接口参考
机器人开放接口:鉴权方式、能力端点与调用规则
机器人程序通过 HTTPS 调用平台开放接口。所有请求都发往 https://2some.ren,鉴权统一用请求头携带运行密钥:
x-api-key: sk_xxxxxxxx验证身份
启动时建议先做一次身份校验,确认密钥有效且确实是机器人密钥:
curl https://2some.ren/api/v1/bots/me \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY"返回的 user.accountType 应为 "bot",user.id 即机器人的 botUserId。
运行密钥只用于 /api/v1 下的公开 Bot 能力,不能调用 /api/auth 的账号、API Key、Passkey 或设备授权控制面;这些管理操作必须使用 human account key。
能力与安装授权
能力先在 Bot 资料中声明,再由每次群聊安装显式授予;运行密钥 scopes 与当前 Bot capability、安装 grant 的交集才是最终权限。当前群聊能力如下:
| Capability | 作用 | 是否附带其他能力 |
|---|---|---|
chat.send | 发送、编辑或撤回机器人自己的消息;Thread 回复还需 chat.read | 不附带 chat.read 或 chat.react |
chat.read | 按安装隐私模式读取群聊与 Thread | 不附带发送或 reaction |
chat.react | 为机器人自己发送且当前安装仍可见的消息添加/取消自己的 reaction | 不附带发送或读取 |
权限不足、安装失效或消息超出隐私范围时,服务端仍是最终裁决者;不要把 Bot roster 的 ConversationMember 角色当作安装授权,也不要把 chat.react 当成可操作任意可见消息的权限。
发布泡泡
需要 bubble.create 能力。
curl -X POST https://2some.ren/api/v1/bubbles \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "你好,泡泡!详情 → https://example.com/article"}'| 字段 | 必填 | 说明 |
|---|---|---|
content | 是* | 纯文本内容,不超过 500 个可见字符;不能与 richContent 同时提交 |
richContent | 是* | 富文本字符串,可用 <sticker:stickerId[:packId]> 内联已审核且可用的贴纸;原始字符串最长 2048 字符、可见内容最长 500 个字符、最多 9 张贴纸;不能与 content 同时提交 |
visibility | 否 | public(默认)/ followers / unlisted |
attachmentIds | 否 | 图片附件 id 数组,最多 9 张(附件必须由当前身份上传并已完成上传) |
aiImageIds | 否 | 当前身份拥有、审核通过、图片已就绪且当前身份有权分享的 AI 作品 id;服务端会复制为 Bubble 图片附件,和 attachmentIds 合计最多 9 张 |
replyToBubbleId | 否 | 回复某条泡泡(可见性会继承被回复的泡泡) |
quoteBubbleId | 否 | 引用某条泡泡 |
quoteArticleId | 否 | 引用某篇文章 |
*
content与richContent二选一(也可以都省略);文本/富文本、attachmentIds、aiImageIds、quoteBubbleId或quoteArticleId至少要有一个。richContent可以只含合法贴纸;replyToBubbleId只指定回复关系,不能单独作为内容。quoteBubbleId和quoteArticleId不能同时提交。公开接口不接受quoteLetterId;转发树洞稿件只能在稿件详情中操作。
机器人发图片需注意
attachmentIds 是接口支持的字段,但它要求先把图片上传拿到附件 id,而上传接口 /api/v1/media/upload-url 目前只对真人会话开放,不接受机器人运行密钥。aiImageIds 不需要再次上传,但只能使用当前身份拥有、审核通过且已就绪、并且仍有分享权限的 AI 作品;richContent 中的贴纸也必须是当前身份可用且已审核的贴纸。没有预先准备这些资源时,机器人实际能稳定发送的是文本、富文本(不含不可用贴纸)、链接、回复和引用;要发普通图片需官方单独开通上传入口。回复和引用对机器人是可用的,适合做「自动回复某话题」「转发引用外部内容并加点评」。
成功返回 { "success": true, "id": "<bubbleId>" },泡泡地址为 https://2some.ren/bubble/<bubbleId>。
内容规则
- 内容会经过平台审核,违规内容发布失败
#词语会成为话题标签;@用户名会真实通知对应用户——转发外部内容时建议把@替换为全角@,避免误打扰- 机器人发布的 public 原创泡泡会进入关注它的人的关注流(并可能触发粉丝推送)。回复不会创建主 Feed 条目;引用和文章评论会保留可发现的 Feed 条目,但纯原创关注流会过滤它们,三者都不会触发粉丝 fan-out 推送。机器人高频发 public 原创泡泡会打扰粉丝,请控制节奏
- 写操作有频率限制,收到
429时请退避重试
读取自己的泡泡
需要 bubble.read 能力。机器人只能读自己发布的内容,常用于「判断哪些内容已经发过」的去重场景。
curl "https://2some.ren/api/v1/bubbles?authorId=<botUserId>&limit=50" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY"| 参数 | 说明 |
|---|---|
authorId | 必填,机器人自己的 botUserId |
limit | 每页条数,最大 50 |
cursor | 翻页游标,取上一页返回的 nextCursor |
返回 { "success": true, "items": [...], "nextCursor": "<游标>" };nextCursor 为 null 时表示没有更多数据。
发现已安装的群聊
机器人运行密钥无需 chat.read 能力也可以查询自身当前可用的安装,用返回的 conversationId 调用群聊接口:
curl "https://2some.ren/api/v1/bots/me/installations" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY"返回 { "success": true, "items": [...] },每项只包含以下低敏字段:
| 字段 | 说明 |
|---|---|
installationId | 该次安装的 id |
conversationId | 已安装群聊的 id |
privacyMode | mention_only / commands_only / read_all |
updatedAt | 安装最后更新时间(UTC ISO 8601) |
接口只返回机器人自己的 active 安装,并排除已归档群聊和成员关系已不存在的记录;不会返回群名、群成员、安装者或 disabled 安装。该端点仅接受机器人运行密钥,账号登录态和账号 API Key 均不可调用。
向群聊发消息
需要 chat.send 能力,且机器人已被安装到该群聊。
curl -X POST "https://2some.ren/api/v1/chat/conversations/<conversationId>/messages" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "今晚 8 点开播,记得来!"}'text 始终按纯文本处理,也是默认和最兼容的写法。要显式发送受限 Markdown,请改传公开 input block;REST 没有顶层 parseMode 字段(Bot SDK 的 parseMode 只是转换该 block 的 convenience):
curl -X POST "https://2some.ren/api/v1/chat/conversations/<conversationId>/messages" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"blocks":[{"type":"rich_text_v1","source":"**重点**:见 [说明](https://example.com)"}]}'群聊消息不止能发纯文本,下面这些字段都可以带:
| 字段 | 必填 | 说明 |
|---|---|---|
text | 否* | 默认的纯文本内容,最长 8000 字,不解析 Markdown 或 HTML |
attachmentIds | 否 | 图片、视频或文件附件 id 数组,最多 9 个(需先上传,见下方提醒) |
aiImageIds | 否 | 当前身份拥有、审核通过、图片已就绪且当前身份有权分享的 AI 作品 id;服务端会复制为聊天图片附件,和 attachmentIds 合计最多 9 个 |
voiceNoteId | 否 | 语音消息 id(不能和 attachmentIds 或 aiImageIds 同时发) |
parentMessageId | 否 | 引用回复某条消息;Bot 同时需要 chat.read,避免用发送权限读取引用上下文 |
threadRootId | 否 | 回复某条 Thread 根消息的 id;Bot 同时需要 chat.read,且 root 必须满足安装隐私模式 |
clientMessageId | 否 | 客户端生成的 UUID;同一会话内重试同一条消息时必须复用,省略后不同请求之间不会去重 |
blocks | 否 | 结构化消息块:text、mention、mention_all、rich_text_v1 或 sticker;贴纸必须单独成条 |
linkCard | 否 | 仅用于正文、text block 中第一个可识别链接,或 rich_text_v1 中第一个安全 link node:url 必须与服务端规范化后的链接完全一致。客户端 card 只作为临时预览;服务端能解析并获取目标时会校正 Bilibili / 2SOMEone 站内实体卡片,失败时可能保留通用卡片或标记获取失败。不能单独作为消息内容 |
asLiveTalk | 否 | 请求将消息标记为当前活跃直播会话的消息;没有活跃会话时不生效,Bot runtime 不允许传 true |
*
text、非空rich_text_v1、attachmentIds、aiImageIds、voiceNoteId或贴纸 block 至少要有一个;目前仅有textblock 不能单独通过 API 发送。parentMessageId、threadRootId和linkCard不能单独作为内容。voiceNoteId不能和任何attachmentIds/aiImageIds同发;两类图片/文件 id 合计最多 9 个。@提及、@所有人、贴纸通过blocks字段发送,其中 @所有人只有群主或管理员能用,贴纸必须单独成条。
如果同时传顶层 text 和 blocks,消息正文以 blocks 为准,顶层 text 不会再作为 fallback 写入;顶层 text 仍会经过 8000 字符的请求校验。要发送带格式的正文,只传 rich_text_v1 block 最清楚。
受限 Markdown 与兼容读取
公开写入形态严格限定为 { "type": "rich_text_v1", "source": string }。支持粗体、斜体、删除线、行内代码、代码块、引用、有序/无序列表、换行和 HTTP/HTTPS 链接;不支持的 Markdown 语法会按输入中的字面文本保留,不会按该语法生成格式化节点。javascript:、data: 和自定义 scheme 不会成为链接,也会按字面文本保留。@用户名 不会从 Markdown 推断为通知对象,需要通知时仍应使用顶层 mention / mention_all block。
任意 HTML、CSS、JavaScript、DOM、iframe 或 WebView 正文都不受支持,也不会执行。服务端会从 source 生成 { plainText, nodes } 和消息的纯文本 text;plainText、nodes 和 text 都是派生结果,客户端不得提交,也不要把顶层 text 当作 fallback(若同传,按上面的 blocks 优先规则处理)。带 plainText、nodes 或其他额外字段的 rich_text_v1 input block 会被拒绝。
读取时,x-chat-message-blocks 是读取方声明的完整 capability 集合。当前已知类型为 text、mention、mention_all、sticker、custom_emoji、rich_text_v1 和 unsupported;只声明 rich_text_v1 不等于声明其它类型,未声明的 block 会按 legacy/unsupported 规则处理。只有显式声明 rich_text_v1 的消费者会获得完整 { type, source, plainText, nodes } block,例如:
curl "https://2some.ren/api/v1/chat/conversations/<conversationId>/messages" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY" \
-H "x-chat-message-blocks: text,mention,mention_all,sticker,custom_emoji,rich_text_v1,unsupported"未带 capability(包括 Bot SDK 默认读取)的 legacy 消费者会按顶层顺序把每个富文本 block 降级为真实 { type: "text", content: plainText },相邻 Mention 仍保留;链接 fallback 会保留可访问 URL。服务端不会用“请更新客户端”占位替换真实正文;legacy 消费者也不能用 fallback 纯文本静默覆盖原富文本,编辑会返回 409 升级提示。
单条消息最多 64 个公开 blocks;消息内所有 block 的 authoring 内容总长和派生 plainText 总长分别不得超过 8000 字符(两项独立计算,不是相加)。服务端会把一个消息内全部 rich_text_v1 block 的 AST 节点累计限制在 1024 个以内,嵌套深度最多 8 层;Markdown 链接的原始值与规范化结果都不能超过 2048 字符,且只有有效的 HTTP/HTTPS URL 才会生成链接节点。超过字符数、block 数、AST 节点数或嵌套深度上限的请求会被拒绝;超长、危险协议或无效 scheme 的链接不会成为链接,而是按字面文本保留。不会执行 HTML、CSS、JavaScript、iframe 或 WebView。
机器人发图片 / 语音需注意
attachmentIds / voiceNoteId 需要先上传媒体拿到 id,而上传接口暂不对机器人运行密钥开放;aiImageIds 不需要再次上传,但只能使用当前 Bot 自己拥有、审核通过且已就绪、并且仍有分享权限的 AI 作品。没有预先开通媒体、可分享 AI 作品或可用贴纸时,机器人在群里实际能稳定发的是文本、结构化文本、回复(parentMessageId)、Thread 回复和链接卡片;要发普通图片 / 视频 / 文件或语音需官方单独开通上传入口。接口仍支持 aiImageIds、贴纸和其他结构化 block,能否使用取决于资源状态与权限。
读取群聊与 Thread
需要 chat.read 能力,且机器人已安装到目标会话。服务端会严格执行安装时的隐私模式:
mention_only 返回提及机器人的顶层消息、机器人自己在可见会话中的消息,以及可见提及 root 下的 Thread 上下文;commands_only 返回顶层命令消息、机器人自己在可见会话中的消息,以及可见命令 root 下的 Thread 上下文,read_all 返回常规可见性过滤范围内的群聊历史(仍受拉黑、举报、内部消息、管理员受众、LiveTalk 和归档状态等规则约束)。机器人自己发在已隐藏 Thread root 下的回复也不会绕过 root 隐私边界。普通隐藏 Thread 中单独 @Bot 的回复不会反向开放此前隐藏的 root;只有 root 先满足隐私模式时,Thread 回复才会进入上下文。Bot runtime 目前只支持已安装且未归档的 Group,不适用于私信、Channel 或客服会话。
curl "https://2some.ren/api/v1/chat/conversations/<conversationId>/messages?after=100&limit=50&order=asc" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY"
curl "https://2some.ren/api/v1/chat/conversations/<conversationId>/messages/<threadRootId>/thread?after=100&order=asc" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY"两条接口都支持正整数 before / after 游标;limit 最终限制为 1~200。非法游标会返回 400,不会静默回到默认页。传 after 且省略 order 时按升序取游标之后最早的一页;传 before 且省略 order 时,按降序取游标之前最近的一页(SDK 对 before 单独调用会显式传 order=desc)。显式 order 只决定从哪一端取这一页窗口,不改变响应排列:服务端返回的 items 始终按消息 id/时间升序排列。未传游标时,顶层消息接口返回最新窗口,Thread 接口返回按时间正序的回复。两条接口当前都返回 { "success": true, "items": [...] },不会返回 nextCursor;客户端应把本页最大消息 id 保存为下一次 after,直到返回空页。before 与 after 不建议同时传;需要组合条件时请显式指定 order。Thread 回复可用 threadRootId 继续发送,但同时要求 chat.send 与 chat.read;root 必须是同一会话中可见、未撤回的顶层消息,不能是 LiveTalk 消息。机器人只能编辑、撤回和 reaction 自己发送的消息:编辑 / 撤回使用 chat.send,reaction 使用独立的 chat.react;写接口只返回自身消息的最小 receipt,完整消息及引用上下文仍需 chat.read 后调用读取接口。
Bot 的消息响应会把 threadReplyCount 固定为 0、threadLastReplyAt 固定为 null。这两个持久聚合无法按每个 Bot 的拉黑、举报、内部消息、管理员受众和安装隐私重新计算,因此不能用来判断 Thread 是否有隐藏活动;需要处理已知 Thread 时请分页读取其可见回复。
对 Bot 而言,不存在或因安装隐私模式不可见的 Thread root 会与“可见但暂无回复”统一返回 200 和空 items,不会通过 404 暴露 root 是否存在;请把它视为不可用上下文,不要据此创建跨会话映射。
列表的 items 可能包含已撤回消息的 tombstone(例如历史页或 Bot 自己的消息)。这类项仍保留 id、发送者和时间等元数据,并以 deletedAt(以及可选的 deletedReason)标记撤回;text 为 null,blocks、attachments、linkCards、reactions 为空。不要把 text === null 当成网络缺失,请以 deletedAt 判断消息状态。
单条消息与自己的消息操作
读取单条消息(需要 chat.read):
curl "https://2some.ren/api/v1/chat/messages/<messageId>" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY"消息必须位于机器人已安装并获隐私模式允许的会话中;不存在或被隐私模式过滤的消息按 404 处理。已撤回的消息仍可能返回一个不含正文的 tombstone(deletedAt 有值);已撤回的 Thread root 不能再读取其回复。编辑和撤回需要 chat.send;reaction 使用独立的 chat.react。这些写操作都只能操作机器人自己发送的消息:
# 编辑(发送后 15 分钟内)
curl -X PATCH "https://2some.ren/api/v1/chat/messages/<messageId>" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY" -H "Content-Type: application/json" \
-d '{"text":"更新后的内容"}'
# 编辑为受限 Markdown(同样只提交 source)
curl -X PATCH "https://2some.ren/api/v1/chat/messages/<messageId>" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY" -H "Content-Type: application/json" \
-d '{"blocks":[{"type":"rich_text_v1","source":"**更新后的内容**"}]}'
# 撤回(发送后 2 分钟内)
curl -X DELETE "https://2some.ren/api/v1/chat/messages/<messageId>" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY"
# 添加 / 取消自己的 reaction(需要独立的 chat.react;不会授予 chat.send 或 chat.read)
curl -X POST "https://2some.ren/api/v1/chat/messages/<messageId>/reactions" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY" -H "Content-Type: application/json" \
-d '{"emojiCode":"👍"}'
curl -X DELETE "https://2some.ren/api/v1/chat/messages/<messageId>/reactions?emojiCode=👍" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY"编辑 / 撤回成功返回 { "success": true, "message": { "id", "conversationId", "clientMessageId", "createdAt", "editedAt", "deletedAt" } } 的最小 receipt;reaction 成功只返回 { "success": true }。chat.react 是独立 capability,不隐式授予 chat.send 或 chat.read;Bot 只能对自己发送的消息添加或取消自己的 reaction,不能操作其他发送者的消息。after 游标只覆盖新建消息,不能补回离线期间发生的编辑、撤回或 reaction 变更。反应型 Bot 默认用 webhook + GET /api/v1/bots/me/events 作为 inbox(当前会在斜杠命令命中时叫醒)。需要监控会话里更多可见消息时,继续用 GET .../messages / SDK iterateChatMessagePages(chat.read);拉消息限额按主人当前 VIP,见下方 429。
事件 inbox 与 Webhook 叫醒
操作步骤、验签与限额见 Webhook 与轮询。下面是接口契约。
反应型机器人被斜杠命令点到时,平台会把一条 interaction.created 写入事件表,并在主人已登记 active HTTPS 回调时签名 POST 过去。Webhook 丢失后,用 runtime key 拉同一条事件:
curl "https://2some.ren/api/v1/bots/me/events?limit=100" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY" \
-H "x-2someone-bot-runtime: 1"新轮换的 runtime key 自动带 events.read;这不是群安装授权。wait 非 0 会返回 400(当前 Route 不做 long poll)。登记回调是主人操作(Web 设置或 2s1 bot endpoint create),群管理员安装时看不到回调 URL,也没有名为 Webhook 的勾选。验证请求是签名 POST { type: "endpoint.verify", challenge },对端须 2xx 且回显同一个 challenge。投递请求头包含 x-webhook-timestamp、x-webhook-endpoint-id、x-webhook-signature(v1: + Ed25519),签名内容为 timestamp.endpointId.body。
# 主人登记 HTTPS 回调(human account key)
curl -X POST "https://2some.ren/api/v1/bots/<botUserId>/endpoint" \
-H "x-api-key: $TWOSOMEONE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/bot/events","idempotencyKey":"<uuid>"}'
# 查看当前回调与验签公钥
curl "https://2some.ren/api/v1/bots/<botUserId>/endpoint" \
-H "x-api-key: $TWOSOMEONE_API_KEY"
# 暂停出站(需带当前 configRevision;事件仍可 pull)
curl -X PATCH "https://2some.ren/api/v1/bots/<botUserId>/endpoint" \
-H "x-api-key: $TWOSOMEONE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status":"paused","expectedConfigRevision":"<revision>","idempotencyKey":"<uuid>"}'Thread 已读与关注状态
Bot 也可以用 chat.read 更新自己在可见 Thread上的已读水位或关注状态;这两个端点不需要 chat.send。它们只接受机器人已安装、未归档 Group 中满足安装隐私模式的顶层 Thread root。
# 把 Thread 已读位置推进到某条回复
curl -X POST "https://2some.ren/api/v1/chat/conversations/<conversationId>/threads/<threadRootId>/read" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY" -H "Content-Type: application/json" \
-d '{"upToMessageId":12345}'
# 关注 / 取消关注一个 Thread
curl -X POST "https://2some.ren/api/v1/chat/conversations/<conversationId>/threads/<threadRootId>/follow" \
-H "x-api-key: $TWOSOMEONE_BOT_API_KEY" -H "Content-Type: application/json" \
-d '{"followed":true}'upToMessageId 必须是正安全整数;服务端只会把水位向前推进到该 Thread 中实际可见的回复,不会因传入更大的 id 越过可见内容。已读成功返回 { "success": true, "updated": boolean, "lastReadMessageId": number | null };关注成功返回 { "success": true, "followed": boolean }。对不存在或因 privacy mode 不可见的 root,Bot 两个端点都保持成功形态(已读返回 updated: false,关注回显请求值)且不写入状态,不要用响应判断 root 是否存在;真人会话对这类 root 返回 404。这些状态目前没有对应的 SDK 方法,需直接调用 REST。
当前关注字段还不是 tri-state:首次创建已读状态会按 followed: true 保存;即使随后写入 false,root 作者、被提及者或参与者仍可能因 Activity 的自动资格继续看到该 Thread。完整的“显式取消关注覆盖自动资格”尚未实现。
clientMessageId 幂等边界
同一会话内使用相同 clientMessageId、相同发送者和相同 parentMessageId / threadRootId 重试,会返回第一次写入的消息,不会重复创建。幂等校验不会比较正文、blocks、附件、语音或链接卡片等 payload;同一 UUID 和同一上下文即使改了内容,仍返回第一次结果。要发送新内容请换 UUID,或在允许的编辑窗口内修改原消息。重放仍会重新检查当前 Bot 安装状态和 Thread root 可见性;权限已撤销或 root 已失效时不会返回旧正文。若该 id 已被其他发送者使用,或同一发送者改用了不同的 parent / Thread 上下文,服务端会返回 409;此时应生成新的 UUID。省略 clientMessageId 的请求不会跨请求去重。
常见错误
下表针对机器人 runtime 发布接口(发泡泡 / 发消息):
| 状态码 | 含义 | 处理建议 |
|---|---|---|
401 | 未提供、无效或已被轮换的密钥;部分鉴权入口也会把当前 capability / key scope 不足视为未授权 | 检查运行密钥和 capability;新增能力后按创建页说明轮换运行密钥 |
403 | 身份有效,但会话成员关系、安装隐私模式或其他业务策略不允许该操作 | 检查安装状态、会话权限和消息规则;最终以响应的 error 字段为准 |
400 | 参数错误(Bubble 文本超 500 字、Chat 文本超 8000 字、内容违规、附件超 9 张等) | 按返回的 error 字段修正 |
409 | 同一会话内的 clientMessageId 已被其他发送者占用,或同一发送者改变了 parent / Thread 上下文 | 生成新的 UUID;不要重试当前 id |
429 | code: rate_limited。可能是整把运行密钥限额(自助默认 100 次/分钟),也可能是 GET .../messages / Thread 的单独拉消息限额(默认 5 次/分钟,VIP 1 为 30,VIP 2 为 60,按主人当前 VIP 即时生效)。响应带 Retry-After 与 X-RateLimit-* | 读 Retry-After 后重试;反应型默认走 webhook / 事件 feed。拉消息分页每一页都计数,跨会话共用同一只桶 |
创建机器人(用账号密钥调管理接口)时,若用户名已被占用会返回 409。
完整示例
一个最小可运行的 Node.js 机器人(Node 20+,零依赖)。保存为 bot.mjs,然后 TWOSOMEONE_BOT_API_KEY=sk_xxx node bot.mjs 运行:
// bot.mjs — 顶层 await 需要 ESM,文件后缀用 .mjs(或在 package.json 设 "type": "module")
const BASE = "https://2some.ren";
const KEY = process.env.TWOSOMEONE_BOT_API_KEY;
async function api(path, init = {}) {
const res = await fetch(`${BASE}${path}`, {
...init,
headers: { "x-api-key": KEY, "Content-Type": "application/json", ...init.headers },
});
const body = await res.json();
if (!res.ok || body.success === false) throw new Error(body.error ?? `HTTP ${res.status}`);
return body;
}
// 验证身份
const { user } = await api("/api/v1/bots/me");
if (user?.accountType !== "bot") throw new Error("不是机器人密钥");
// 发一条泡泡
const { id } = await api("/api/v1/bubbles", {
method: "POST",
body: JSON.stringify({ content: `大家好,我是 ${user.username} 🤖` }),
});
console.log(`已发布:${BASE}/bubble/${id}`);更完整的实战范例(RSS 拉取、无状态去重、GitHub Actions 免服务器部署)见官方模板仓库 leaperone/2someone-news-bot。模板仓库的 README/SKILL 当前仍有旧的 /api/auth/get-session 身份调用,Fork 后请先改为 GET /api/v1/bots/me 并校验 accountType === "bot",不能原样运行。