文档索引

点赞与投喂机器人接入说明

面向站外开发者。读完本文即可实现一个会点赞、会投喂的机器人,无需阅读本站源码。

本文是这几个接口的唯一对外口径。字段、限频数值、错误码均照实抄录, 若与源码不符以源码为准(但也请提 issue —— 那就是本文过期了)。

0. 一句话说清

四种「轻互动」,都是一个 POST:

做什么 接口 一句话
给文章点赞 POST /api/blogs/:id/like 切换式:点一次赞、再点一次取消;回你当前状态
给评论点赞 POST /api/comments/:id/like 同上,但不产生通知
给文章投喂鱼干 POST /api/blogs/:id/feed 花自己的鱼干,作者拿 80%,不可撤回
删自己的评论 DELETE /api/comments/:id 软删(§7)

另有两条只有作者看得到的名单(谁赞了、谁喂了):§6。

⚠️ 这一域里只有投喂会动钱。它花的是你账号的真鱼干,且没有撤回接口 (§5)—— 请先读完那一节再让机器人碰它。

📌 本文自包含。账号准备(注册 / 提权)与鉴权(cookie)是全部机器人共用的前置步骤, 三份完整口径见 docs/bot/chat-bot.md §2 与 §3。时间戳的假 Z 全站同一个, 见 docs/bot/comment-bot.md §5。


1. 能力边界(先读这段)

范围 能读 能写 说明
给文章点赞 —— ✅ 需 core+ 登录;切换式
给评论点赞 —— ✅ 需 core+ 登录;切换式,无通知
投喂鱼干给文章 —— ✅ 需 core+ 且未被禁言;花钱、不可撤回
删自己的评论 —— ✅ 需 core+ 登录;软删
读某文章的点赞者 / 投喂者名单 ✅ ❌ 只有作者本人(或管理员),不是公开数据
删别人的评论 ❌ ❌ 那是管理员的路(要带 reason + 写审计日志),机器人没有这条路
看「这篇文章被投喂了多少」 ✅ ❌ 文章详情里的 fish_count(docs/bot/blog-bot.md §9)

机器人的权限边界是三条:core+ 角色、登录状态、是否被禁言。 (禁言只挡投喂与评论相关的写口;点赞不判禁言,见 §3。)

⚠️ 机器人享受不到任何豁免:它同样会被禁言、同样受限频与余额约束。 这是刻意设计 —— 出问题时站长能像处理普通用户一样处理它。


2. 准备账号与鉴权

全部接口需要 core+ 且登录。注册出来是 user,要提权一次: 注册时带有效邀请码(全自动),或让站长执行 npm run cli -- promote-core mybot。 完整流程见 docs/bot/chat-bot.md §2。登录换 cookie:

POST /api/auth/login
Content-Type: application/json

{ "username": "mybot", "password": "********" }

响应会 Set-Cookie: raricy_session=<JWT>(HttpOnly、SameSite=Lax、30 天)。 之后每个请求带上 Cookie: raricy_session=<JWT>。

⚠️ 写请求会校验来源,跨站会被拦成 403 跨源请求被拒绝 (CSRF)。 非浏览器客户端本来就不发 Origin/Referer,天然通过 —— 不要伪造一个。

会话失效后一律 401 请先登录 —— 当成「重新登录」的信号,而不是重试。


3. 文章点赞(切换式)

POST /api/blogs/:id/like
Cookie: raricy_session=<JWT>

没有请求体。 一次调用 = 切换一次。

{ "code": 200, "message": "ok", "liked": true, "likes_count": 13 }
字段 说明
liked 切换之后你的状态:true = 你现在赞了它
likes_count 切换之后的总赞数

⚠️ 这不是「设置成赞」,是「反转」。 盲目重试 = 点了又取消, 而且每次切换都消耗配额(§8)。

⚠️ 而且「我赞过这篇没有」在接口上读不到。 GET /api/blogs/:id 的响应里 没有 liked 字段(docs/bot/blog-bot.md §9 那张清单就是全部),全站也没有 第二个接口下发「某人对某篇文章的点赞态」—— 网页端能显示点亮状态,是因为它在 服务端渲染时直查数据库,那条路站外调用方走不了。 (评论点赞不一样:评论树里每条都带 liked,见 §4。)

所以机器人的正确姿势是自己记账:在你自己那边记下「我赞过哪些 blogId」, 只在账上没有时才切一次;响应里的 liked 就是这笔之后的真实状态,用它回写你的账。 网络超时后先查自己的账,别直接重发 —— 重发的后果是把赞取消掉, 而不是重复点赞(切换式刷不高赞数,只会自己打自己)。

情形 返回
未登录 / 非 core+ 401 / 403 需要核心用户权限
文章不存在 / 已被软删 404 文章不存在
触发限频 429 操作过于频繁,请稍后再试

📌 顺序:先判文章存不存在,再扣限频。所以刷一个不存在的 id 不会烧掉你的点赞额度 —— 但也没有任何理由去刷。

3.1 通知:一人一篇文章最多一条

点赞生效时(liked 变 true)会给文章作者发一条站内通知(action 文章点赞):

  • 自己赞自己不发;
  • 取消再点亮也不发第二条 —— 服务端记着一个「这条点赞记录发过通知没有」的标记, 取消点赞不碰它。所以「反复点赞取消」刷不出通知,只会烧配额;
  • 通知失败不会回滚点赞(钱与状态优先)。

⚠️ 机器人被禁言时仍然点得动赞(点赞不判禁言)。但别拿它当绕过手段 —— 通知照发,作者看得见是谁。


4. 评论点赞(切换式,无通知)

POST /api/comments/:id/like
Cookie: raricy_session=<JWT>

同样没有请求体,返回形状与文章点赞一致:

{ "code": 200, "message": "ok", "liked": true, "likes_count": 4 }
情形 返回
未登录 / 非 core+ 401 / 403 需要核心用户权限
评论不存在 / 已删除 404 评论不存在或已删除
触发限频 429 操作过于频繁,请稍后再试

✅ 评论点赞不产生任何通知。 这一条与文章点赞不同(那边会通知作者), 所以「反复点评论赞」打扰不到人 —— 但它照样吃配额。

📌 评论点赞是可以读到状态的(文章点赞不行,见 §3):GET /api/blogs/:id/comments 的每条评论都带 liked(随查看者而变,见 docs/bot/comment-bot.md §10.2)。 所以评论这边能做到「先读,再决定要不要切」,不必自己记账。


5. 投喂鱼干(花钱,不可撤回)

POST /api/blogs/:id/feed
Cookie: raricy_session=<JWT>
Content-Type: application/json

{ "amount": 1 }
字段 类型 必填 说明
amount number 是 1–5 的整数

成功:

{
  "code": 200,
  "message": "投喂成功!",
  "fed_total": 3,
  "remaining": 2,
  "fish_count": 27,
  "balance": 38.5,
  "author_income": 0.8
}
字段 说明
fed_total 你对这篇文章的累计投喂(含本次)
remaining 你还能给它投多少(累计上限 5)
fish_count 这篇文章收到的总投喂
balance 你投喂之后的余额
author_income 作者这次实际到手的鱼干(= amount × 0.8)。amount 只收 1–5 的整数,所以它恒是 0.8 / 1.6 / 2.4 / 3.2 / 4 之一,精确值,没有舍入

💰 结算规则:你付全额,作者拿 80%。差额不流向任何人(不是手续费, 是分成口径)。余额不足则整笔失败,没有「先欠着」这种事。

5.1 三条硬约束

  1. 单篇累计上限 5。 你对同一篇文章的投喂加起来不能超过 5 (amount 单次也最多 5,所以理论上一次就能投满)。到顶后回 400 投喂已满(单篇文章每人最多投喂 5 条)。
  2. 不可撤回。 没有「退款」「撤销投喂」接口。投出去就是作者的。
  3. 被禁言的账号一律 403(你已被禁言,暂时无法投喂)—— 与点赞不同,投喂判禁言。

5.2 会被拒绝的情形

情形 返回
未登录 / 非 core+ 401 / 403 需要核心用户权限
账号被禁言 403 你已被禁言,暂时无法投喂
请求体不是 JSON 400 请求体格式错误
amount 不是 1–5 的整数 400 投喂数量需为 1~5 的整数
余额不够 400 小鱼干不足
累计超上限 400 投喂已满(单篇文章每人最多投喂 5 条)
文章不存在 / 已被软删 404 文章不存在
本站故障 500 服务器错误 —— 别当成「这笔没发生」,也别急着重发(见下)

⚠️ amount 只收整数。 1.0 可以(它就是 1),0.5 不行; 鱼干本身可以有小数(别人转给你的分成余额常常是 3.8 这样), 但投喂的数量必须是整数。

⚠️ 500 / 超时之后别急着原样重发。 投喂没有幂等键参数(没有去重), 而调用方无法从状态码区分「没成交」与「成交了但响应丢了」(后者:响应在 服务器提交事务之后才丢)—— 那一次到底扣没扣,只有查了才知道。 正确做法与转账是同一条纪律(见 docs/bot/fish-bot.md §1 硬约束 2): 先查流水确认刚才那笔到底发生了没有(POST /api/fish/market/transactions 传 type: "feed_all",见 docs/bot/fish-bot.md §3.3;也可以先查余额), 再决定要不要重发。别无脑重试 —— 重试 = 再投一笔。

⚠️ 同理,不要为了「确保成功」重复提交:每次成功返回 200 都是一笔真实投喂, 第二笔会同时花掉你的余额、给作者多刷一条通知,还会占掉单篇累计 5 条的额度。

5.3 投喂会给作者发通知

投喂成功后作者收到一条站内通知(action 文章投喂, 文案形如「你的文章《标题》收到了 N 条小鱼干投喂!」)。自投喂(作者给自己投)不发。

⚠️ 通知在钱结算完之后才发,通知失败不会退回鱼干。 所以「没收到通知」不代表「没投成功」—— 查余额或流水(docs/bot/fish-bot.md §3.3)。

⚠️ 每次投喂都是一条通知。别用「投 1 条 × 5 次」代替「一次投 5」 —— 前者会给作者刷出 5 条通知,且吃 5 倍配额。


6. 名单:谁赞了 / 谁喂了(只有作者看得到)

GET /api/blogs/:id/likers?offset=0&limit=50
GET /api/blogs/:id/feeders?offset=0&limit=50
Cookie: raricy_session=<JWT>

权限两层:core+ 档位 且(文章作者本人 或 管理员)。 不是你的文章 → 403 无权查看;文章不存在 → 404 文章不存在。

查询参数 说明
offset 从第几条开始,默认 0
limit 每页条数,默认 50,服务端夹到 1–200

📌 解析不出来的参数一律回落默认值,不报错(offset=abc 等价于 0)。

6.1 两条名单的字段名不一样(容易踩)

likers 的数组键是 users(不是 likers):

{
  "code": 200,
  "message": "获取成功",
  "users": [
    { "id": "u_xxx", "username": "alice", "avatar_url": "/api/avatar/u_xxx", "liked_at": "2026-09-19 20:31:05" }
  ],
  "total": 13, "offset": 0, "limit": 50
}

feeders 的数组键是 feeders,字段是 snake_case 且带金额:

{
  "code": 200,
  "message": "ok",
  "feeders": [
    { "user_id": "u_xxx", "username": "alice", "avatar_path": null, "amount": 3 }
  ],
  "total": 5, "offset": 0, "limit": 50
}
差异 likers feeders
数组键 users feeders
用户 id 字段 id user_id
头像字段 avatar_url(永远是 /api/avatar/<id>) avatar_path(可能是 null)
时间 liked_at("YYYY-MM-DD HH:MM:SS",UTC+8 墙上时间) 无
排序 点赞时间倒序(新的在前) 投喂量倒序(多的在前)

⚠️ 这不是笔误,是两段独立实现的历史形状,照实收着。写机器人时别假设 「同一个域字段名一致」—— 请按本节逐一取字段。


7. 删自己的评论

DELETE /api/comments/:id
Cookie: raricy_session=<JWT>
Content-Type: application/json

{ "reason": "可选,作者删自己的评论不需要" }
  • 作者本人删自己的 → 成功,reason 可以不传;
  • 管理员删别人的 → 必须给 reason(1–500 字),且会写审计日志 (那是 /audit 公示与用户申诉的数据来源)。机器人一般用不到这条;
  • 成功:{ "code": 200, "message": "删除成功" }
情形 返回
未登录 / 非 core+ 401 / 403 需要核心用户权限
评论不存在 / 已删过 404 评论不存在或已删除
不是你的评论(且你又不是管理员) 403 无权删除该评论
管理员删他人但没给 reason 400 请提供删除原因
reason 超过 500 字 400 删除原因过长(最多500字)

⚠️ 软删除:不物理抹除。别人再拉到这棵评论树时,正文会被换成 [该评论已删除];没有回复的评论会整条从楼中楼里消失(有回复的保留占位)。 所以「删了重发一条一模一样的」在读者侧看得到痕迹。

⚠️ 被删除的评论不能再回复(parent_id 指向它会回 400 父评论不存在或已删除,见 docs/bot/comment-bot.md §7.3)。


8. 限频

规则 额度 适用
文章点赞 300 次 / 小时、1500 次 / 天 POST /api/blogs/:id/like
评论点赞 300 次 / 小时、1500 次 / 天 POST /api/comments/:id/like
投喂 无限频(RULES 里没有) POST /api/blogs/:id/feed
名单 无限频 §6 两条
删自己的评论 无限频 §7

超限一律 429。

⚠️ 文章点赞与评论点赞各自独立:它们是两个桶,互不挤占。 但每一次切换都计数 —— 切换式接口最容易被写成「点两次抵消」, 所以要确保「先读再切」,而不是「盲切 + 重试」。

⚠️ 投喂没有限频,但那不等于可以随便投。 它唯一的闸门是你的余额与 单篇累计 5 条。一个失控的循环能在几秒内把余额搬空,且不可撤回 —— 机器人侧请自己做节流,并在启动前确认这次真的要投。

📌 投喂是转账之外唯一能把鱼干推给别人的路径之一,但它只推给 「文章作者」这一确定对象,所以没有像转账那样的 30 次/时配额。


9. 礼仪与约定

技术之外,三点请配合:

  1. 点赞是社交信号,不是同步工具。 用机器人「把看过的文章全赞一遍」会让作者 收到一串通知(每篇一条),这是骚扰。请只赞你真的读过并认可的内容。
  2. 投喂是钱。 它不可撤回、会给作者发通知、且花的是真余额。 请像对待转账一样对待它:先确认对象与金额,再发。
  3. 别拿切换接口刷数字。 「反复点赞取消」既刷不出通知(服务端只发一条), 也刷不出赞数(取消就是减 1),只会烧掉你自己的 300/时,并留下可疑的操作记录。

10. 排错速查

现象 多半是
点了赞,作者没收到通知 自赞;或这条点赞记录以前发过(一人一篇文章只发一条,§3.1)
想「确保赞上」,结果取消掉了 切换式的固有语义。没有读口能告诉你赞过没有,只能自己记账(§3)
找不到 blog.liked 它不存在(§3)。文章点赞态不下发;评论的 liked 才有
403 无权查看 名单只有作者本人或管理员能看,不是公开数据(§6)
404 评论不存在或已删除 评论已被作者/管理员软删;点赞与回复都会撞这个
400 投喂数量需为 1~5 的整数 传了小数(0.5)或超出区间的数
400 小鱼干不足 余额不够。先查余额(docs/bot/fish-bot.md §3.2)
400 投喂已满(单篇文章每人最多投喂 5 条) 你对这篇的累计已达 5,这不是错误,是上限
500 服务器错误 本站故障。状态码分不出「没成交」与「成交了但响应丢了」 —— 先查流水 / 余额确认这笔扣没扣,再决定要不要重发(§5.2)
投喂成功但作者没收到通知 通知在结算之后发且失败不回滚;以余额与流水为准
likers 里找不到 likers 字段 它的数组键叫 users(§6.1)
时间差 8 小时 假的 Z,见 docs/bot/comment-bot.md §5

11. 最小可用流程

const BASE = 'https://raricy.com';
const cookie = 'raricy_session=<JWT>';          // 登录见 chat-bot.md §3
const H = { cookie, 'content-type': 'application/json' };

// ── 点赞:接口不告诉你「赞过没有」,所以**自己记账** ──────────────────
// ⚠️ 别抄「读详情看 liked」—— `GET /api/blogs/:id` 没有那个字段(§3)
const myLikedBlogIds = new Set();                 // 你自己维护,持久化到磁盘/数据库
if (!myLikedBlogIds.has(blogId)) {
  const r = await fetch(`${BASE}/api/blogs/${blogId}/like`, { method: 'POST', headers: { cookie } });
  const out = await r.json();
  if (out.liked) myLikedBlogIds.add(blogId);      // 以响应为准回写自己的账
  console.log(out.liked, out.likes_count);        // true = 现在赞上了
}

// ── 投喂:确认金额,一次投完(别拆成 5 次,会刷 5 条通知)──────────
const bal = await (await fetch(`${BASE}/api/fish/market/balance`, {
  method: 'POST', headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ username: 'mybot', password: process.env.BOT_PASSWORD }),
})).json();
if (bal.balance >= 5) {
  const r = await fetch(`${BASE}/api/blogs/${blogId}/feed`, {
    method: 'POST', headers: H, body: JSON.stringify({ amount: 5 }),
  });
  const out = await r.json();
  console.log(r.status, out.message, out.balance);   // 5xx → 先查流水/余额确认扣没扣,再决定是否重发
}

断线 / 401 → 重新登录。429 → 退避(点赞那两条额度按小时与按天各一档)。

本页内容来自仓库 docs/bot/like-feed-bot.md·在 GitHub 上查看