邀请码
后台「邀请码」页面上要做的事只有三件:定好注册模式和「每邀请 N 人奖励 M 张」这条规则、给一个合作方发一个专属码、以及需要的时候批量生成一批码。账户自己领到的东西——个人邀请码,或者别人给的一个码——在设置页的「邀请」卡片里,终端里对应 me invite 和 me invite claim。
一、注册模式与邀请奖励
页面上只有一个「注册模式」下拉框,实际写的是两个设置项:
| 模式 | registration.enabled | invites.required | 效果 |
|---|---|---|---|
| 开放 (open) | true | false | 谁都能注册;带邀请码也认,邀请码的分组和奖励照常生效 |
| 仅限邀请 (invite) | true | true | 必须带一个有效邀请码才能注册 |
| 关闭 (closed) | false | — | 不接受任何新注册 |
其余的注册规则(邮箱域名白名单、每 IP 注册数、注册频率、模型审核)不受这个开关影响,见注册与安全和系统设置。
「仅限邀请」下,第三方登录(GitHub / Google)如果对应的是一个还不存在的账户,不会直接开号:会先停在只剩邀请码这一两项的补充表单上,和邮箱、QQ 缺失时的表现一致,见第三方登录。已经绑定过的账户照常登录,不受影响。
「允许账户使用个人邀请码」打开后,每个账户会有一个属于自己的邀请码(第一次打开设置里的「邀请」卡片或运行 me invite 时自动生成),可以在设置 → 邀请里复制码或链接分享给朋友。「每个账户可邀请上限」达到后,这个账户的个人码就不能再用来注册了(返回 invite_invalid);0 表示不限。关闭这个开关会同时让已经发出去的个人码失效,还没结算的奖励也按 disabled 结算,不只是不再生成新码。
「邀请奖励」现在是每邀请满 N 人,奖励一次 M 张重置卡,三个数分别设置:
| 设置项 | 含义 | 取值范围 | 默认 |
|---|---|---|---|
invites.reward_every | 每邀请满几个人才发一次奖励 | 1-1000 | 1(即每邀请一人都发,原来的行为) |
invites.reward_cards | 每次奖励发放的重置卡数量,0 表示不发放奖励 | 0-100 | 0 |
invites.reward_card_days | 这些卡的有效天数 | 1-3650 | 30 |
比如 reward_every=3、reward_cards=2:一个账户的第 3、6、9…个成功邀请各发一次 2 张卡;第 1、2、4、5 个邀请照样被计入总数,只是那一次不发奖励。
一次邀请要「成功」(被计入总数),被邀请人需要:账户处于活跃状态、没有被注册审查模型限制、并且在本站要求邮箱验证时已经完成验证——不要求验证的实例在注册那一刻就结算,要求验证的实例会在完成验证后再结算一次。注册时被审查模型限制的账户,限制解除后由后台每 10 分钟一次的例行清理补上结算(只追溯最近 30 天内的邀请)。以下情况邀请人这次邀请不计入总数,会被如实记录而不是悄悄跳过:
| 记录原因 | 触发条件 |
|---|---|
same_ip | 被邀请人的注册 IP 和邀请人的注册 IP 相同,或和邀请人当前任意一个在线会话的 IP 相同 |
limit | 邀请人已经达到「每个账户可邀请上限」 |
disabled | 结算时「邀请奖励」数量为 0,或「允许用户邀请」已关闭 |
inviter_gone | 邀请人账户在结算前已被删除 |
inviter_disabled | 邀请人账户在结算前已被禁用 |
每一次被计入总数的邀请(不管这次是不是刚好撞上发奖门槛)都会给邀请人发一条通知,点开跳转到设置 → 邀请:内容包含目前累计计入的邀请数、发奖门槛、这次拿到的卡数(可能是 0)、以及距下次发奖还差几个人。奖励只会真正发一次:即使注册和邮件验证两次都触发了结算检查,也只有第一次真正生效。
二、给合作方一个专属码
给合作方一个好记的固定链接,在「邀请码」页面点「新增合作方邀请码」,或者终端 invite create --partner:
| 参数 | 含义 |
|---|---|
| 合作方名称 | 1-60 字符,只用于运营方自己在列表里认出这是哪一个合作方,不会出现在链接里 |
| 自定义码 | 必填,合作方链接里那段好记的文字,例如 PARTNERX |
| 可使用次数 | 通常填 0,表示不限次数 |
| 分组 / 分组天数 | 通过这个链接注册或领取的账户会进入的用户组,天数可以是一个区间,比如 3 到 7,让每个人各自随机分到 3-7 天中的一个天数 |
| 老用户也能领取 | 默认开启:已经有账号的人也能在设置页用这同一个码领取一次;关掉后这个码就只能用来注册新账号 |
worked example:合作方「XX」要一个码,注册和老用户都能用,给 3-7 天的 Go 会员,不限次数:
invite create --partner "XX" --code PARTNERX --uses 0 --group Go --group-days 3-7
# 只想让它对新注册生效,不给老用户用:加一个 --no-existing
invite create --partner "XX" --code PARTNERX --uses 0 --group Go --group-days 3-7 --no-existing链接的形式是 站点地址/register?invite= 加上码本身,例如 https://你的域名/register?invite=PARTNERX。把这个链接交给合作方,或者只给他们那个码,让对方自己拼。一个已经登录的人打开这个链接会被带到设置 → 邀请,邀请码框里已经填好这个码,等他自己点「领取」——不会替他自动提交,改变用户组是要本人确认的事。
要看这个码带来了多少注册、多少老用户领取,在邀请码列表里点开这个码,或者在终端用 invite uses PARTNERX(via 列区分 register 和 claim);邀请码页面「合作方邀请码」卡片和终端 invite stats 都会列出每个合作方码目前的注册数和领取数。合作方码没有所有者,不计入任何账户的个人邀请奖励。
三、批量码
给不需要专属名字、只是要发一批码的场景,在「生成邀请码」里填数量(1-500,只能生成随机码):
| 参数 | 含义 |
|---|---|
| 生成数量 | 一次生成多少个,1-500;批量只能生成随机码 |
| 自定义码 | 只在数量为 1 时可填,也可以借此生成一个可自定义文字、但不算合作方(不出现在合作方列表里)的单个码 |
| 可使用次数 | 这个码总共能被注册多少次,0 表示不限次数;默认 1 |
| 失效时间 | 从现在起多少天后这个码本身停止可用;不填表示永不失效 |
| 分组 / 分组天数 | 同上一节 |
| 备注 | 给运营自己看的一行说明 |
随机生成的码是 8 个字符,取自不含 0 1 I L O U 的字母数字表,界面上以 XXXX-XXXX 的形式显示和复制;自定义码是 4-32 位字母和数字,大小写和连字符在录入时会被折叠掉(abcd-1234、ABCD1234 是同一个码),显示时保持你输入的样子。批量生成的一批码在关闭这个面板后就不会再整批出现,请当场复制。批量码默认不能被已有账户领取;终端 invite create(不带 --partner)也可以显式带上 allow_existing(见下)单独打开这一点。
老用户领取一个码
设置页「邀请」卡片最上面永远有一个「使用邀请码」的输入框,不管本站是否开着个人邀请码——它和个人码是两件事:任何登录账户都可以在这里,或者用终端 me invite claim <码>,去领取一个合作方或者显式打开了「老用户也能领取」的批量码。个人码永远不能被这样领取。
领取的规则:
- 如果账户当前处于默认注册分组(或没有分组),领取后会进入这个码的分组,到期时间是现在起加上抽到的天数。
- 如果账户已经在这个码的分组里、还带着一个到期时间(正在试用),领取会在原到期时间基础上顺延,不会缩短也不会重置。
- 如果账户已经永久在这个分组,或者在其他任何分组,领取会被拒绝——永远不会降级或替换掉账户已有的分组。
- 每个账户对每个码只能领取一次,往后再领会被拒绝,不管上一次领没领成功过。
一个还没登录的人打开 /register?invite=CODE 会照常走注册流程;已经登录的人打开同一个链接会被自动带到设置页的这个输入框,并且把码填好等他自己确认。
撤销
点开一个码,「撤销」会让它停止接受新的注册和领取;已经通过它注册或领取的账户,其分组成员身份和已经发放的奖励都不受影响,不会被追回。撤销是幂等的——对已撤销的码再撤销一次不会改变原来的撤销时间。
列表可以按「来源」(batch 批量 / partner 合作方 / personal 个人,也支持旧的 admin = 批量+合作方、user = 个人 两个别名,或者 all)和状态(有效 / 已用完 / 已过期 / 已撤销)筛选,也可以按码本身或所有者用户名搜索。
权限
管理这些接口需要新的 invites 后台权限,在「用户」页面编辑管理员的权限列表里授予。只有 invites 权限的管理员也能改本页的设置:invites.* 和注册方式用到的 registration.enabled,但改不了其他任何设置;账户自己的邀请码和领取入口(GET/POST /api/profile/invites*)不需要任何后台权限,任何登录账户都能读写自己的那一份。
终端
invite list [--kind batch|partner|personal|all] [--status active|used_up|expired|revoked] [--search 文本]
invite create [--count N] [--code 自定义码] [--uses N] [--days N | --expires YYYY-MM-DD] [--group 分组] [--group-days N|N-M] [--note 备注]
invite create --partner 名称 --code 自定义码 --group 分组 --group-days N-M [--uses N] [--expires YYYY-MM-DD] [--no-existing]
invite revoke <码|id> --yes
invite uses <码|id>
invite stats
me invite
me invite regenerate --yes
me invite claim <码>需要 invites 权限的是 invite * 这一组;me invite、me invite regenerate、me invite claim 任何登录账户都能用,只作用于自己的账户。--kind 兼容旧的 admin(批量+合作方)、user(个人)两个别名。--expires 写的日期当天全天有效(按服务器时区),到第二天零点失效;--uses 0 表示不限次数;--partner 必须搭配 --code 和 --group。示例:
invite create --count 20 --uses 1 --note '9 月活动批次'
invite create --partner 'XX' --code PARTNERX --uses 0 --group Go --group-days 3-7
invite revoke PARTNERX --yes
invite uses PARTNERX --json
me invite claim PARTNERX
me invite regenerate --yes失败与拒绝
| 场景 | 状态码 | 错误码 |
|---|---|---|
| 仅限邀请模式下注册没带邀请码 | 400 | invite_required |
| 注册用的邀请码不存在、已撤销、已过期、已用完,或它的个人码所有者账户已被禁用或删除 | 400 | invite_invalid |
| 自定义码格式不对(生成时校验,不是注册/领取时) | 400 | invite_code_format |
| 自定义码和已存在的码重名 | 409 | invite_code_taken |
| 生成合作方码时缺少名称、自定义码或分组之一 | 400 | invite_partner_fields |
关闭了个人邀请码开关后调用 POST /api/profile/invites/regenerate | 400 | invites_disabled |
| 领取的码不存在、已撤销、已过期、已用完、是个人码,或没打开「老用户也能领取」 | 400 | invite_invalid |
| 这个账户已经领取过这个码 | 409 | invite_claimed |
| 领取会替换或降级账户已有的分组 | 409 | invite_group_conflict |
| 领取失败次数超过限制(每账户每小时 10 次失败尝试) | 429 | too_many_attempts(带 Retry-After 响应头) |
| 注册或第三方补全时,同一地址猜错邀请码超过 30 次(见下文) | 429 | too_many_attempts(带 Retry-After 响应头,1 至 2 秒) |
领取失败的限流只数「猜码」——不存在、格式不对或已经失效的码;已经领取过 (invite_claimed) 和分组冲突 (invite_group_conflict) 都是账户拿着一个本身有效的码, 只是撞上了一个确定的状态, 不计入失败次数, 免得一次重复提交或者手滑点两下就被当成暴力枚举。和注册一样,invite_invalid 不区分具体原因——不存在、已撤销、已过期、已用完、是个人码、没打开领取权限,回复都完全一样,避免让人靠试探邀请码本身猜出更多信息。
注册时填错的邀请码也计入猜码的限制,第三方登录补全注册表单里填错的码同样计入。这个限制和领取的次数分开,按地址计算:IPv4 按单个地址,IPv6 按整个 /64。同一地址猜错超过 30 次以后,之后带邀请码的注册或补全请求都会被拒绝(429,too_many_attempts,Retry-After 为 1 至 2 秒),填的是正确的码也一样。等待时间不会随次数增加,直到该地址最后一次计数的猜错之后约 30 分钟,限制才解除。同时还在处理中的带邀请码请求也占这个名额:同一地址若有很多人在同一时刻注册,超出的那几个会被同样暂时拒绝,过一两秒再试即可。不带邀请码的注册不受这个限制;猜中的码不计次数,其他地址的注册不受影响。