news 2026/9/10 17:42:21

OpenHuman Rewards Referrals 完整解析:推荐奖励、优惠券兑换、Discord 社区成就与邀请码机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenHuman Rewards Referrals 完整解析:推荐奖励、优惠券兑换、Discord 社区成就与邀请码机制

OpenHuman Rewards & Referrals 完整解析:推荐奖励、优惠券兑换、Discord 社区成就与邀请码机制

【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman

本文以 OpenHuman 的官方功能文档(gitbooks/features/rewards-and-referrals.md)为主线,结合仓库中 Rust 核心(src/openhuman/hosted/referral/)与 React 前端(app/src/pages/Rewards.tsxapp/src/pages/Invites.tsxapp/src/services/api/)的源码实现,系统讲解应用内“奖励与推荐”体系的三个松散耦合的成长机制:推荐计划(Referrals)促销优惠券(Coupons)社区奖励(Community Rewards),外加独立的邀请码(Invite Codes)管理。读完本文,你将掌握各功能面的数据模型、状态机、前后端调用链与底层 RPC 适配层原理,并能据此排查“本地会话下功能不可用”“WebView fetch 失败”“角色未分配”等实际现象。

一、功能概览:三个机制共用一个界面

OpenHuman 将三类“增长玩法”捆绑在同一个Rewards界面下,它们业务上彼此独立,只是共享一个入口:

机制作用
推荐计划(Referral program)分享你的推荐码,好友完成转化后你获得信用额度(credit)
促销优惠券(Promo coupons)兑换促销码获得促销信用(promotional credit),与推荐奖励相互独立
社区奖励(Community rewards)绑定 Discord 账号,随使用量里程碑解锁角色与奖励

邀请码管理则独立于 Rewards 界面,位于单独的/invites页面。

一个关键前提:以上所有功能都要求已登录的后端会话(signed-in backend session)。在纯本地会话(local-only session)下,Rewards 页面只显示一个引导登录的空状态(empty state),所有这些功能离线不可用。对应前端实现见 Rewards.tsx:当isLocalSessionToken(coreSnapshot.sessionToken)为真时,直接渲染EmptyStateCard,提示文案由 i18n 键rewards.localUnavailable提供,并引导用户跳转/settings/account

二、Rewards 界面:三个视图与地址化导航

功能文档描述/rewards拥有三个 chip 标签页,中间默认选中Rewards(社区)。从当前仓库源码看,这一交互已演化为更彻底的形式:三个表面不再是同一页面上的标签,而是各有一个侧边栏条目(sidebar entry)的独立视图,并用 URL 参数?view=作为地址,从而支持刷新后状态保持与直接链接跳转。

// app/src/pages/Rewards.tsx type RewardsView = 'rewards' | 'referrals' | 'redeem'; const VIEWS: readonly RewardsView[] = ['rewards', 'referrals', 'redeem'] as const;
视图?view=侧边栏图标对应内容
Rewards(社区)缺省 /?view=rewardsGiftDiscord 连接、进度环、可解锁的社区角色
Referrals(推荐)?view=referralsUsers推荐码、收益、被推荐用户活动
Coupons(优惠券)?view=redeemTicket兑换促销码、兑换历史

视图切换通过setView写入 URL:view === 'rewards'时删除view参数,其他视图则写入对应值(Rewards.tsx)。未识别或缺失的?view=一律回落(fallback)到rewards,保证侧边栏始终恰好高亮一个条目。

另外,Rewards 页面注册了一个oauth:success事件监听:任何 OAuth 连接(如 Discord)完成后,深链监听器会派发该事件,页面随即静默刷新快照,使 Discord 用户名与连接状态实时更新(Rewards.tsx)。

三、Referrals 推荐计划

3.1 推荐码与分享

每个账号拥有唯一推荐码。你可以复制它,或使用Share(优先调用系统原生分享面板,失败时回退到剪贴板)发送一条预填充消息,内容包含你的推荐码与应用下载链接。

3.2 数据展示:四个磁贴 + 活动表

Referrals 标签页展示四块信息:

  • 你的推荐码(code)
  • 累计收益(total earned,USD)
  • 待转化推荐(pending referrals)
  • 已完成推荐(completed referrals)

下方是一张活动表,列出每条被推荐记录:被推荐人的掩码身份(masked identity,如j***@gmail.com)、状态徽章、奖励金额与时间戳。掩码优先取自后端下发的referredUserMasked字段,若后端只提供referredDisplayName或用户 id,前端归一化逻辑也会兜底处理(见 referralApi.ts)。

数据模型定义在 app/src/types/referral.ts:ReferralStats包含referralCodereferralLinktotalstotalRewardUsd/pendingCount/convertedCount)、referrals行数组、appliedReferralCodecanApplyReferral

3.3 推荐状态机

Referral 状态含义
Joined(加入)被推荐用户已注册但尚未转化
Completed(完成)被推荐用户已转化,推荐奖励已入账
Expired(过期)关系失效(预留状态,由后端驱动)

注意 UI 层与后端的措辞差异:后端只区分pending | convertedexpired为预留值;前端归一化函数把后端的joined映射为pendingcompleted/complete映射为converted(referralApi.ts)。因此文档表格里的 “Joined / Completed / Expired” 本质上是归一化后的 UI 状态名。

3.4 应用他人推荐码(Apply)

如果你是被他人推荐且仍符合资格的用户,界面会显示一个apply 表单让你输入对方的推荐码。资格判断(canApplyReferral)完全由后端决定——典型规则是:尚未订阅、且尚未应用过任何推荐码的用户才有资格。一旦应用成功,表单会被“已关联推荐码”的确认信息替换。

金额、转化规则与资格判定全部服务端强制(server-side enforced)。桌面端核心在这里只是一个薄适配层(thin adapter)。

3.5 底层原理:referral 域是“无状态 RPC 适配器”

从源码结构看,referral 域(src/openhuman/hosted/referral/)不持有任何业务逻辑、状态或自有 schema——它只是一个无状态 RPC 适配器,用带认证的reqwest请求调用托管后端(hosted backend)的/referral/*接口,并把原始data负载原样返回给 CLI / JSON-RPC 客户端。

它之所以存在,是因为桌面 WebView 的fetch到后端可能抛出笼统的 “Load failed”(源于 CORS / TLS / WebKit 限制),因此这些调用复用了与 billing 域相同的服务端reqwest路径。这一点在 ops.rs 的模块注释和 README.md 中均有明确说明。

对外暴露两个 RPC 方法(注册见 src/core/all.rs):

RPC 方法后端调用用途输入
openhuman.referral_get_stats(即referral.get_statsGET /referral/stats拉取推荐码、链接、总额与被推荐人列表
openhuman.referral_claim(即referral.claimPOST /referral/claim应用推荐码(可带设备指纹作为滥用信号)code(必填)、deviceFingerprint(可选)

RPC schema 定义于 schemas.rs:referral_get_stats输出stats(JSON,来自后端data字段);referral_claim输出result(JSON),入参通过ReferralClaimParams以 camelCase 反序列化(device_fingerprint字段带#[serde(default)])。未知方法名会返回一个unknown占位 schema 且只含error输出。

关键实现细节(ops.rs):

  • require_token私有助手从凭据库读取会话令牌:先get_session_token,然后trim并拒绝空串,否则fail closed,返回错误"no backend session token; run auth_store_session first"——两个方法在没有会话时都以此方式失败。
  • get_stats通过effective_backend_api_url(&config.api_url)解析有效后端地址,构造BackendOAuthClient后执行authed_json(GET, "/referral/stats")
  • claim_referral构造请求体时会对codetrim,对deviceFingerprint同样 trim 并丢弃纯空白字符串后才转发;schema handler(handle_referral_claim)侧也做了一遍防御性的重复过滤。

前端 referralApi.ts 通过callCoreCommand('openhuman.referral_get_stats')/callCoreCommand('openhuman.referral_claim', { code, deviceFingerprint })调用上述 RPC。设备指纹由 deviceFingerprint.ts 提供:以openhuman_device_fingerprint_v1为键存入localStorage,优先使用crypto.randomUUID(),不可用时回退到时间戳 + 随机串,是一个稳定的匿名标识(stable anonymous id),供后端做滥用信号分析。

四、Coupons 优惠券兑换

Coupons 标签页用于兑换**促销码(promo codes)**换取促销信用,与推荐奖励相互独立。界面包含两块统计磁贴:

  • 促销信用余额(promo credit balance,USD)
  • 已兑换码数量(count of redeemed codes)

输入一个码并兑换即可;兑换结果要么立即生效(applied),要么在依赖后续操作时先被接受为pending(待定)。下方是一张近期兑换记录表,列出每个码、奖励金额、状态与兑换时间。

Coupon 状态含义
Applied(已应用)已兑现——信用已进入你的账户
Pending action(待触发)条件性优惠券,等待某个触发动作
Redeemed(已兑换)已被接受,但尚未兑现

五、Community Rewards 与 Discord

5.1 进度环与角色奖励

Rewards(社区)标签页将使用量“游戏化”。一个**进度环(progress ring)**展示你已解锁成就数占总成就数的比例;**角色与奖励(roles & rewards)**列表则描述每个里程碑(部分里程碑附带可选的 USD 信用)。页面底部用状态徽章(当前连续天数 current streak、累计 token 数)汇总你的活动。

5.2 围绕 Discord 的三个操作

奖励以Discord 角色的形式发放,因此该标签页围绕“绑定 Discord 账号”构建:

  1. Connect Discord:执行 OAuth 授权流程(openhuman.auth.oauth_connect,provider 为discord);成功后快照刷新并显示你的 Discord 用户名。
  2. Join Discord:打开社区服务器邀请链接。
  3. Disconnect:解除账号绑定(清除存储的 Discord ID,操作幂等)。

从 rewardsApi.ts 可以看到对应的 HTTP 接口:GET /rewards/me拉取快照(15 秒超时,REWARDS_SNAPSHOT_TIMEOUT_MS = 15_000),POST /rewards/claim(body 为{ rewardType })领取奖励,DELETE /rewards/discord解绑 Discord。

5.3 快照数据模型与角色状态

GET /rewards/me的快照结构定义在 app/src/types/rewards.ts,前端归一化逻辑见 rewardsApi.ts:

  • discordlinkeddiscordIdusernameinviteUrlmembershipStatus
  • summaryunlockedCounttotalCountassignedDiscordRoleCountclaimableCount(后端不支持领取功能时该字段缺省)、planFREE | BASIC | PRO)、hasActiveSubscription
  • metricscurrentStreakDayslongestStreakDayscumulativeTokensfeaturesUsedCounttrackedFeaturesCountlastEvaluatedAtlastSyncedAt
  • achievements:成就数组,含titledescriptionunlockedprogressLabelroleIddiscordRoleStatuscreditAmountUsdrewardTokensrewardRecurring,以及领取相关的claimable / claimed / claimedAt / claimPeriod(后四者带缺省值,保证旧后端返回的快照依然合法)。

绑定完成后,每个已解锁成就都会展示其 Discord 角色分配状态:

角色状态含义
Assigned(已分配)角色已在服务器上授予
Pending(待分配)已解锁,但角色尚未分配
Join to claim(加入后领取)已绑定但未加入服务器——加入即可获得角色

底层角色状态枚举更细:assigned | not_assigned | not_linked | not_in_guild | not_configured | unavailable(rewards.ts);成员状态为member | not_in_guild | not_linked | unavailable。如果你已解锁带角色的成就但未加入服务器,会出现一条claim banner提示你加入领取。POST /rewards/claim的返回(RewardsClaimResult)还携带alreadyClaimed(幂等重复领取标识)、tokensamountUsdnewPromoBalanceUsd(领取后新的促销余额),说明部分成就是“可领取的月度/一次性 token 或 USD 信用”。

5.4 与 GitHub 贡献者奖励的边界

基于 GitHub 的贡献者奖励是独立机制:一个 GitHub Actions 工作流会在贡献者首个 PR 合并后发布 Discord/周边(merch)邀请评论。它不属于应用内 Rewards 界面,也不使用应用内的 GitHub OAuth。

也就是说,仓库里的contributor-rewards工作流与应用内奖励体系完全解耦,不要混为一谈。

六、Invite Codes 邀请码(/invites

/invites页面与推荐码是两码事。它管理的是门控新用户注册的个人邀请码(invite codes):

  • Redeem(兑换):如果你尚未被邀请,输入一个邀请码来抢占名额。
  • Your invite codes(我的邀请码):发给你的一批邀请码列表。每行展示等宽字体(monospace)的码、复制按钮与enabled/disabled状态。当currentUses >= maxUses(使用次数耗尽)时,该码自动翻转为禁用,并显示是谁兑换了它。

数据模型见 app/src/types/invite.ts:

export interface InviteCode { _id: string; code: string; owner: string; type: 'USER' | 'CAMPAIGN'; // 邀请码类型 maxUses: number; // 最大可用次数 currentUses: number; // 当前已用次数 usageHistory: UsageHistoryEntry[]; // 谁在何时兑换 isActive: boolean; createdAt: string; }
  • typeUSER(用户发放)或CAMPAIGN(活动发放);
  • maxUses/currentUses:使用额度计数器,currentUses >= maxUses即失效;
  • usageHistory:兑换记录,每项包含兑换用户信息(userId及其username/firstName等)与usedAt时间戳。

页面实现(Invites.tsx)中,已兑尽的邀请码显示disabled徽章,未兑尽则显示enabled徽章,并有剪贴板反馈(useClipboardFeedback)。

七、本地会话与后端会话的行为差异

汇总一张表,便于快速判断你在哪种会话下会遇到什么:

场景表现
本地会话(local-only)打开/rewards显示空状态卡片,提示需登录,CTA 跳转/settings/account
已登录后端会话,但未存储会话令牌时调用 referral RPCfail closed,错误"no backend session token; run auth_store_session first"
Discord 未绑定成就的discordRoleStatusnot_linked,需先执行 OAuth 连接
已绑定但未加入服务器角色状态为not_in_guild,出现 claim banner 提示加入

八、总结与延伸阅读

OpenHuman 的奖励与推荐体系在架构上刻意保持“薄客户端、厚服务端”:业务规则(金额、转化条件、资格、角色分配)全部由托管后端裁决,桌面端只做两件事——React 前端负责交互与展示,Rust core 中的 referral 域以无状态 RPC 适配器方式代理请求以规避 WebViewfetch的 “Load failed” 问题。这种设计既保证了规则可以随时在服务端调整,也让离线/本地模式下的降级路径非常清晰(直接空状态 + 引导登录)。

想继续深入,可以阅读:

  • Billing & usage——推荐、优惠券与成就信用最终在哪里被消费;
  • Welcome(文档首页);
  • referral 域实现:README.md、ops.rs、schemas.rs;
  • 前端页面与组件:Rewards.tsx、Invites.tsx、RewardsCommunityTab.tsx、ReferralRewardsSection.tsx、RewardsCouponSection.tsx;
  • 类型与 API 层:referral.ts、rewards.ts、invite.ts、referralApi.ts、rewardsApi.ts;
  • 设备指纹:deviceFingerprint.ts。

【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 17:40:24

CANN/GE ES包生成CMake指南

add_es_library 使用指南 【免费下载链接】ge GE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、T…

作者头像 李华
网站建设 2026/9/10 17:39:20

FlyEnv实战:多语言多版本本地开发环境管理指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 17:36:48

西门子Smart200与V90伺服Profinet三轴控制实战

1. 项目概述 西门子Smart200 PLC与V90伺服驱动器通过Profinet(PN)网络实现3轴控制,是工业自动化领域常见的运动控制解决方案。这套系统在包装机械、数控机床、自动化生产线等场景中应用广泛。作为一名在工控领域摸爬滚打多年的工程师&#xf…

作者头像 李华
网站建设 2026/9/10 17:36:04

Matlab从零实现C4.5决策树:增益率计算与剪枝全流程

简介:本资源是一份面向计算机、电子信息工程及数学等专业本科生的机器学习课程实践材料,聚焦决策树C4.5算法原理与Matlab实现,适用于课程设计、期末大作业或毕业设计参考。压缩包共14个文件,含7个.mat数据文件(如train…

作者头像 李华