点赞与投喂机器人接入说明
面向站外开发者。读完本文即可实现一个会点赞、会投喂的机器人,无需阅读本站源码。
本文是这几个接口的唯一对外口径。字段、限频数值、错误码均照实抄录, 若与源码不符以源码为准(但也请提 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 三条硬约束
- 单篇累计上限 5。 你对同一篇文章的投喂加起来不能超过 5
(
amount单次也最多 5,所以理论上一次就能投满)。到顶后回400 投喂已满(单篇文章每人最多投喂 5 条)。 - 不可撤回。 没有「退款」「撤销投喂」接口。投出去就是作者的。
- 被禁言的账号一律 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),只会烧掉你自己的 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 → 退避(点赞那两条额度按小时与按天各一档)。