diff --git a/AGENTS.md b/AGENTS.md index 5010945d..3e6a5509 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -101,7 +101,9 @@ WebUI deployment targets use a reversible presentation overlay. Read [the host a ## Generated files and related changes -OneBot action definitions own validation and catalog metadata. After changing an action name, parameter, result description or execution mode: +OneBot action definitions own validation and catalog metadata. Each bundled action supplies a `returnsSchema` for successful OneBot `data` and at least one `examples` entry with valid parameters and any required identifier sources. Stream schemas describe individual successful frames, not MCP aggregated results. Keep undocumented upstream fields open rather than inventing a structure, and label unsupported or placeholder behavior explicitly. + +After changing an action name, parameter, result description/schema, example or execution mode: 1. Update the owning definition in `packages/onebot/src/actions` and its tests. 2. Run `pnpm --filter @snowluma/mcp gen`. diff --git a/packages/mcp/AGENT_FEATURES.md b/packages/mcp/AGENT_FEATURES.md index bb0a61f9..33f73d4d 100644 --- a/packages/mcp/AGENT_FEATURES.md +++ b/packages/mcp/AGENT_FEATURES.md @@ -5,6 +5,48 @@ and read/write gates. No endpoint is contacted by catalog tools or by a local runtime overview. The action catalog remains a bundled snapshot, not a claim about the connected server's capabilities. +## Action result schemas and examples + +`get_action` and `snowluma://onebot/actions` include `returnsSchema` and +`examples` for every bundled action, including aliases. Both are authored beside +the OneBot action definition and copied into the generated catalog. List/search +responses remain compact; use `get_action` to retrieve the complete contract. + +`returnsSchema` describes successful OneBot **`data`**, not the full OneBot +envelope or the MCP `structuredContent` wrapper. Check `status` and `retcode` +before reading it. For stream actions, the schema describes each successful +frame's `data`; MCP downloads instead return saved-file metadata and generic +streams return a frame collection. Error frames and failed envelopes are not +successful results. Unsupported actions use `{ "not": {} }` because they have no +successful result. Compatibility placeholders explicitly document their limits. + +Each example has `summary`, `params`, and optional `notes` explaining prerequisites +and identifier sources. These are illustrative calls, never run by catalog tools. +Replace example account/group IDs, opaque handles and paths with values from the +current account before execution. Notes identify operations with multiple steps, +such as completing an upload after all chunks have been acknowledged. + +For example, `get_action({ "name": "get_msg" })` documents signed message IDs: + +```json +{ + "action": "get_msg", + "params": { "message_id": -123456 } +} +``` + +Pass this object to `query_action` with a real message ID. Writes and streams use +`invoke_action` in write mode. For automatic upload, pass +`{ "action": "upload_file_stream", "execution": { "input_file": "/allowed/hello.txt" } }` +to `invoke_action`, with a real file inside the configured upload root. Ordinary +action paths refer to the SnowLuma server, whereas `execution.input_file` and MCP +download paths refer to the MCP host. + +Do not substitute zero for a signed message ID. Resource-only forward uploads +and flash shares have a documented `message_id: 0` sentinel; it cannot be used +for message lookup or recall. Also preserve `null` versus `{}`, optional fields, +and decimal strings such as media `file_size` exactly as documented. + ## Structured results Every tool advertises an `outputSchema` and returns `structuredContent` alongside diff --git a/packages/mcp/scripts/gen-catalog.test.ts b/packages/mcp/scripts/gen-catalog.test.ts index e76f39a5..bbfc5bfd 100644 --- a/packages/mcp/scripts/gen-catalog.test.ts +++ b/packages/mcp/scripts/gen-catalog.test.ts @@ -18,6 +18,8 @@ it('generates src/generated/catalog.ts from the live action specs', () => { for (const a of actions) { expect(a.inputSchema).toBeTypeOf('object'); expect(a.name).toBeTruthy(); + expect(a.returnsSchema, `${a.name} result schema`).toBeTypeOf('object'); + expect(a.examples?.length, `${a.name} invocation examples`).toBeGreaterThan(0); } const body = diff --git a/packages/mcp/src/generated/catalog.json b/packages/mcp/src/generated/catalog.json index 3fa63aa1..0f5f8d89 100644 --- a/packages/mcp/src/generated/catalog.json +++ b/packages/mcp/src/generated/catalog.json @@ -4,6 +4,20 @@ "name": "_del_group_notice", "aliases": [], "summary": "删除群公告(fid 或 notice_id 二选一)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除群公告(fid 或 notice_id 二选一)", + "params": { + "group_id": 20001, + "notice_id": "" + }, + "notes": "notice_id 来自 _get_group_notice;删除需要管理权限。" + } + ], "readOnly": false, "params": [ { @@ -63,6 +77,73 @@ "aliases": [], "summary": "获取指定 QQ 号正在使用的个性装扮(挂件/名片/来电/输入状态等)", "returns": "装扮信息;目标未使用任何可查询装扮时 items 为空数组。网络失败、未登录态/风控、页面改版、返回账号与请求不一致时返回失败并附具体原因", + "returnsSchema": { + "type": "object", + "properties": { + "target_uin": { + "type": "string" + }, + "is_svip": { + "type": "boolean" + }, + "avatar_url": { + "type": "string" + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "app_id": { + "type": "integer" + }, + "kind": { + "type": "string" + }, + "item_id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "preview_url": { + "type": "string" + }, + "video_url": { + "type": "string" + }, + "price": { + "type": "number" + } + }, + "required": [ + "app_id", + "kind", + "item_id", + "name", + "preview_url", + "video_url", + "price" + ] + } + } + }, + "required": [ + "target_uin", + "is_svip", + "avatar_url", + "items" + ] + }, + "examples": [ + { + "summary": "获取指定 QQ 号正在使用的个性装扮(挂件/名片/来电/输入状态等)", + "params": { + "user_id": 10001 + }, + "notes": "替换为需要查询的 QQ 号。" + } + ], "readOnly": true, "params": [ { @@ -100,6 +181,124 @@ "aliases": [], "summary": "获取群公告", "returns": "普通公告与新成员公告的合并数组;send_to_new_members 标识后者;图片含 id、url、宽高", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "notice_id": { + "type": "string" + }, + "sender_id": { + "type": "number" + }, + "publish_time": { + "type": "number" + }, + "message": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "image": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "url": { + "type": "string" + }, + "height": { + "type": "number" + }, + "width": { + "type": "number" + } + }, + "required": [ + "id", + "url", + "height", + "width" + ] + } + }, + "images": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "url": { + "type": "string" + }, + "height": { + "type": "number" + }, + "width": { + "type": "number" + } + }, + "required": [ + "id", + "url", + "height", + "width" + ] + }, + "description": "与 image 相同的图片列表,保留两个兼容字段" + } + }, + "required": [ + "text", + "image", + "images" + ] + }, + "settings": { + "description": "服务端公告设置,原样透传,可为 null" + }, + "read_num": { + "type": "number" + }, + "type": { + "type": "number" + }, + "pinned": { + "type": "number" + }, + "send_to_new_members": { + "type": "boolean" + } + }, + "required": [ + "notice_id", + "sender_id", + "publish_time", + "message", + "settings", + "read_num", + "type", + "pinned", + "send_to_new_members" + ] + } + }, + "examples": [ + { + "summary": "获取群公告", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -165,6 +364,15 @@ ] } }, + "examples": [ + { + "summary": "获取机型展示(兼容 mock)", + "params": { + "model": "示例机型" + }, + "notes": "兼容占位接口,仅将传入的 model(或默认值)放进展示结构,不查询设备机型。" + } + ], "readOnly": true, "params": [ { @@ -194,6 +402,17 @@ "name": "_mark_all_as_read", "aliases": [], "summary": "标记全部已读", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "标记全部已读", + "params": {}, + "notes": "会将当前账号的会话标记为已读。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -208,6 +427,22 @@ "name": "_send_group_notice", "aliases": [], "summary": "发送群公告(支持置顶、弹窗、新成员、群昵称引导与确认)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "发送群公告(支持置顶、弹窗、新成员、群昵称引导与确认)", + "params": { + "group_id": 20001, + "content": "欢迎参加本周讨论", + "type": 1, + "pinned": 0 + }, + "notes": "需要群公告管理权限;普通公告 type=1,新成员公告 type=20。" + } + ], "readOnly": false, "params": [ { @@ -382,6 +617,17 @@ "name": "_set_model_show", "aliases": [], "summary": "设置机型展示(占位)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置机型展示(占位)", + "params": {}, + "notes": "兼容占位接口,当前返回成功但不修改机型展示。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -396,6 +642,17 @@ "name": ".get_word_slices", "aliases": [], "summary": "分词(未实现)", + "returnsSchema": { + "not": {}, + "description": "当前始终返回失败,没有成功 data;失败响应 data 为 null,原因见 wording" + }, + "examples": [ + { + "summary": "分词(未实现)", + "params": {}, + "notes": "当前未实现,调用返回失败;不要将其当作可用能力。" + } + ], "readOnly": true, "params": [], "invariants": [], @@ -410,6 +667,26 @@ "name": "add_custom_face", "aliases": [], "summary": "添加收藏表情", + "returnsSchema": { + "type": "object", + "properties": { + "emoji_id": { + "type": "string" + } + }, + "required": [ + "emoji_id" + ] + }, + "examples": [ + { + "summary": "添加收藏表情", + "params": { + "file": "file:///path/to/emoji.png" + }, + "notes": "示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。" + } + ], "readOnly": false, "params": [ { @@ -444,6 +721,20 @@ "name": "add_group_tag", "aliases": [], "summary": "向某个群分类添加资料标签", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "向某个群分类添加资料标签", + "params": { + "group_class": 1, + "name": "学习" + }, + "notes": "替换实际群分类和标签;需要当前账号有管理权限。" + } + ], "readOnly": false, "params": [ { @@ -492,6 +783,17 @@ "name": "bot_exit", "aliases": [], "summary": "退出机器人", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "退出机器人", + "params": {}, + "notes": "会退出 SnowLuma 进程,仅在明确需要停止服务时调用。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -518,6 +820,12 @@ "yes" ] }, + "examples": [ + { + "summary": "基本调用", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -544,6 +852,12 @@ "yes" ] }, + "examples": [ + { + "summary": "基本调用", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -557,6 +871,34 @@ { "name": "cancel_group_album_media_like", "aliases": [], + "returnsSchema": { + "type": "object", + "properties": { + "id": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": [ + "integer", + "null" + ] + } + } + }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001, + "album_id": "", + "batch_id": "1700000000" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 batch_id 必须是实际批次的十进制字符串。" + } + ], "readOnly": false, "params": [ { @@ -632,6 +974,20 @@ "name": "cancel_group_todo", "aliases": [], "summary": "取消群待办", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "取消群待办", + "params": { + "group_id": 20001, + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。 必须是该群的消息。" + } + ], "readOnly": false, "params": [ { @@ -701,6 +1057,13 @@ "level" ] }, + "examples": [ + { + "summary": "检查链接安全性", + "params": {}, + "notes": "兼容占位接口,当前固定返回 level=1,不能用来证明某个链接安全。" + } + ], "readOnly": true, "params": [], "invariants": [], @@ -715,6 +1078,17 @@ "name": "clean_cache", "aliases": [], "summary": "清理缓存", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "清理缓存", + "params": {}, + "notes": "兼容占位接口,当前返回成功但不执行清理。流式临时文件清理请使用 clean_stream_temp_file。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -730,6 +1104,36 @@ "aliases": [], "summary": "清理流式传输临时文件(仅清理 stream 上传/下载目录)", "returns": "{ message, removed }", + "returnsSchema": { + "type": "object", + "properties": { + "message": { + "const": "success" + }, + "removed": { + "type": "integer" + }, + "freed_bytes": { + "type": "integer" + }, + "skipped_active": { + "type": "integer" + } + }, + "required": [ + "message", + "removed", + "freed_bytes", + "skipped_active" + ] + }, + "examples": [ + { + "summary": "清理流式传输临时文件(仅清理 stream 上传/下载目录)", + "params": {}, + "notes": "清理未使用的流式临时文件;不会清理正在传输的项目。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -744,6 +1148,51 @@ "name": "click_inline_keyboard_button", "aliases": [], "summary": "点击内联键盘按钮", + "returnsSchema": { + "type": "object", + "properties": { + "result": { + "type": "number", + "description": "按钮业务结果码,需单独检查" + }, + "errMsg": { + "type": "string" + }, + "status": { + "const": 0 + }, + "promptText": { + "type": "string" + }, + "promptType": { + "const": 0 + }, + "promptIcon": { + "const": 0 + } + }, + "required": [ + "result", + "errMsg", + "status", + "promptText", + "promptType", + "promptIcon" + ] + }, + "examples": [ + { + "summary": "点击内联键盘按钮", + "params": { + "group_id": 20001, + "bot_appid": 10001, + "msg_seq": 42, + "button_id": "", + "callback_data": "" + }, + "notes": "参数必须来自同一条机器人按钮消息;msg_seq 是消息序号,不是 message_id。" + } + ], "readOnly": false, "params": [ { @@ -835,6 +1284,29 @@ "name": "comment_qzone", "aliases": [], "summary": "评论一条说说(QQ 空间,支持纯文字或带图;传 images 自动上传)", + "returnsSchema": { + "type": "object", + "properties": { + "comment_id": { + "type": "string", + "description": "服务端未提供评论 ID 时为空串" + } + }, + "required": [ + "comment_id" + ] + }, + "examples": [ + { + "summary": "评论一条说说(QQ 空间,支持纯文字或带图;传 images 自动上传)", + "params": { + "tid": "", + "target_uin": 10001, + "content": "感谢分享" + }, + "notes": "tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。" + } + ], "readOnly": false, "params": [ { @@ -923,6 +1395,20 @@ "name": "complete_group_todo", "aliases": [], "summary": "完成群待办", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "完成群待办", + "params": { + "group_id": 20001, + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。 必须是该群的消息。" + } + ], "readOnly": false, "params": [ { @@ -979,6 +1465,17 @@ "name": "create_collection", "aliases": [], "summary": "创建收藏(未实现)", + "returnsSchema": { + "not": {}, + "description": "当前始终返回失败,没有成功 data;失败响应 data 为 null,原因见 wording" + }, + "examples": [ + { + "summary": "创建收藏(未实现)", + "params": {}, + "notes": "当前未实现,调用返回失败;不要将其当作可用能力。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -993,6 +1490,37 @@ "name": "create_flash_task", "aliases": [], "summary": "创建闪传任务", + "returnsSchema": { + "type": "object", + "properties": { + "fileset_id": { + "type": "string" + }, + "task_id": { + "type": "string", + "description": "与 fileset_id 相同的兼容字段" + } + }, + "required": [ + "fileset_id", + "task_id" + ] + }, + "examples": [ + { + "summary": "创建闪传任务", + "params": { + "files": [ + { + "file": "file:///path/to/report.pdf", + "name": "报告.pdf" + } + ], + "name": "资料包" + }, + "notes": "示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。 files 不能为空。" + } + ], "readOnly": false, "params": [ { @@ -1042,6 +1570,84 @@ "aliases": [], "summary": "创建群文件夹", "returns": "{ result: { retCode, retMsg }, groupItem: { folderInfo: { folderId, folderName, folderPath, createTime, modifyTime, createUin, modifyUin } } }", + "returnsSchema": { + "type": "object", + "properties": { + "result": { + "type": "object", + "properties": { + "retCode": { + "const": 0 + }, + "retMsg": { + "const": "success" + } + }, + "required": [ + "retCode", + "retMsg" + ] + }, + "groupItem": { + "type": "object", + "properties": { + "folderInfo": { + "type": "object", + "properties": { + "folderId": { + "type": "string" + }, + "folderName": { + "type": "string" + }, + "folderPath": { + "type": "string" + }, + "createTime": { + "type": "integer" + }, + "modifyTime": { + "type": "integer" + }, + "createUin": { + "type": "integer" + }, + "modifyUin": { + "type": "integer" + } + }, + "required": [ + "folderId", + "folderName", + "folderPath", + "createTime", + "modifyTime", + "createUin", + "modifyUin" + ] + } + }, + "required": [ + "folderInfo" + ] + } + }, + "required": [ + "result", + "groupItem" + ] + }, + "examples": [ + { + "summary": "创建群文件夹", + "params": { + "group_id": 20001, + "name": "共享资料", + "parent_id": "/" + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": false, "params": [ { @@ -1105,6 +1711,28 @@ "name": "del_group_album_media", "aliases": [], "summary": "删除群相册图片或视频", + "returnsSchema": { + "type": "object", + "properties": { + "success": { + "const": true + } + }, + "required": [ + "success" + ] + }, + "examples": [ + { + "summary": "删除群相册图片或视频", + "params": { + "group_id": 20001, + "album_id": "", + "lloc": "" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。" + } + ], "readOnly": false, "params": [ { @@ -1171,6 +1799,19 @@ "name": "delete_custom_face", "aliases": [], "summary": "删除收藏表情", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除收藏表情", + "params": { + "emoji_id": "" + }, + "notes": "emoji_id 来自 fetch_custom_face_detail。" + } + ], "readOnly": false, "params": [ { @@ -1203,6 +1844,19 @@ "name": "delete_essence_msg", "aliases": [], "summary": "移除精华消息", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "移除精华消息", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -1241,6 +1895,19 @@ "name": "delete_flash_file", "aliases": [], "summary": "删除闪传文件", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除闪传文件", + "params": { + "fileset_id": "" + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。" + } + ], "readOnly": false, "params": [ { @@ -1273,6 +1940,20 @@ "name": "delete_friend", "aliases": [], "summary": "删除好友", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除好友", + "params": { + "user_id": 10001, + "block": false + }, + "notes": "会删除指定好友;仅在明确需要删除时调用。" + } + ], "readOnly": false, "params": [ { @@ -1322,6 +2003,20 @@ "name": "delete_group_file", "aliases": [], "summary": "删除群文件", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除群文件", + "params": { + "group_id": 20001, + "file_id": "" + }, + "notes": "替换群文件列表中的实际 file_id;需要对应文件操作权限。" + } + ], "readOnly": false, "params": [ { @@ -1374,6 +2069,20 @@ "name": "delete_group_file_folder", "aliases": [], "summary": "删除群文件夹", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除群文件夹", + "params": { + "group_id": 20001, + "folder_id": "" + }, + "notes": "会删除指定群文件夹,确认资源后调用。" + } + ], "readOnly": false, "params": [ { @@ -1424,6 +2133,20 @@ "name": "delete_group_folder", "aliases": [], "summary": "删除群文件夹", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除群文件夹", + "params": { + "group_id": 20001, + "folder_id": "" + }, + "notes": "会删除指定群文件夹,确认资源后调用。" + } + ], "readOnly": false, "params": [ { @@ -1474,6 +2197,20 @@ "name": "delete_group_tag", "aliases": [], "summary": "从某个群分类移除资料标签", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "从某个群分类移除资料标签", + "params": { + "group_class": 1, + "name": "学习" + }, + "notes": "替换实际群分类和标签;需要当前账号有管理权限。" + } + ], "readOnly": false, "params": [ { @@ -1522,6 +2259,19 @@ "name": "delete_msg", "aliases": [], "summary": "撤回消息", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "撤回消息", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -1560,6 +2310,19 @@ "name": "delete_qzone_msg", "aliases": [], "summary": "删除一条说说(QQ 空间,按 tid)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除一条说说(QQ 空间,按 tid)", + "params": { + "tid": "" + }, + "notes": "tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。" + } + ], "readOnly": false, "params": [ { @@ -1593,6 +2356,69 @@ { "name": "do_group_album_comment", "aliases": [], + "returnsSchema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "user": { + "type": "object", + "properties": { + "uin": { + "type": "string" + } + }, + "required": [ + "uin" + ] + }, + "content": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "integer" + }, + "content": { + "type": "string" + } + }, + "required": [ + "type", + "content" + ] + } + }, + "time": { + "type": "string", + "description": "十进制时间字符串" + }, + "clientKey": { + "type": "string" + } + }, + "required": [ + "id", + "user", + "content", + "time", + "clientKey" + ] + }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001, + "album_id": "", + "lloc": "", + "content": "拍得很好" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。" + } + ], "readOnly": false, "params": [ { @@ -1672,6 +2498,28 @@ "aliases": [], "summary": "下载文件(url 或 base64)到 data/downloads", "returns": "{ file }", + "returnsSchema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "description": "保存到 SnowLuma 服务端的本地文件路径" + } + }, + "required": [ + "file" + ] + }, + "examples": [ + { + "summary": "下载文件(url 或 base64)到 data/downloads", + "params": { + "base64": "aGVsbG8=", + "name": "hello.txt" + }, + "notes": "保存到 SnowLuma 服务端临时目录,返回服务端路径;不是 MCP 主机下载路径。" + } + ], "readOnly": false, "params": [ { @@ -1735,6 +2583,118 @@ "aliases": [], "summary": "以流式方式下载图片(缓存图片 id / URL / stream 目录本地文件)", "returns": "流式帧:file_info → file_chunk* → file_complete", + "returnsSchema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "file_info" + }, + "file_name": { + "type": "string" + }, + "file_size": { + "type": "integer", + "description": "字节数,0 可表示未知" + }, + "chunk_size": { + "type": "integer" + } + }, + "required": [ + "type", + "data_type", + "file_name", + "file_size", + "chunk_size" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "file_chunk" + }, + "index": { + "type": "integer" + }, + "data": { + "type": "string", + "description": "该分块的 Base64 数据" + }, + "size": { + "type": "integer" + }, + "progress": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "base64_size": { + "type": "integer" + } + }, + "required": [ + "type", + "data_type", + "index", + "data", + "size", + "progress", + "base64_size" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "response" + }, + "data_type": { + "const": "file_complete" + }, + "file_name": { + "type": "string" + }, + "total_chunks": { + "type": "integer" + }, + "total_bytes": { + "type": "integer" + }, + "message": { + "type": "string" + } + }, + "required": [ + "type", + "data_type", + "file_name", + "total_chunks", + "total_bytes", + "message" + ] + } + ], + "description": "每个成功流式帧的 data;失败帧另由 status/retcode/wording 与 type=error 表示,不是 MCP 保存文件后的工具结果" + }, + "examples": [ + { + "summary": "以流式方式下载图片(缓存图片 id / URL / stream 目录本地文件)", + "params": { + "file_id": "", + "chunk_size": 65536 + }, + "notes": "file_id 来自当前账号图片消息;经 invoke_action 调用。" + } + ], "readOnly": false, "params": [ { @@ -1798,6 +2758,118 @@ "aliases": [], "summary": "以流式方式下载语音(缓存语音 id / URL / stream 目录本地文件)", "returns": "流式帧:file_info → file_chunk* → file_complete", + "returnsSchema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "file_info" + }, + "file_name": { + "type": "string" + }, + "file_size": { + "type": "integer", + "description": "字节数,0 可表示未知" + }, + "chunk_size": { + "type": "integer" + } + }, + "required": [ + "type", + "data_type", + "file_name", + "file_size", + "chunk_size" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "file_chunk" + }, + "index": { + "type": "integer" + }, + "data": { + "type": "string", + "description": "该分块的 Base64 数据" + }, + "size": { + "type": "integer" + }, + "progress": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "base64_size": { + "type": "integer" + } + }, + "required": [ + "type", + "data_type", + "index", + "data", + "size", + "progress", + "base64_size" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "response" + }, + "data_type": { + "const": "file_complete" + }, + "file_name": { + "type": "string" + }, + "total_chunks": { + "type": "integer" + }, + "total_bytes": { + "type": "integer" + }, + "message": { + "type": "string" + } + }, + "required": [ + "type", + "data_type", + "file_name", + "total_chunks", + "total_bytes", + "message" + ] + } + ], + "description": "每个成功流式帧的 data;失败帧另由 status/retcode/wording 与 type=error 表示,不是 MCP 保存文件后的工具结果" + }, + "examples": [ + { + "summary": "以流式方式下载语音(缓存语音 id / URL / stream 目录本地文件)", + "params": { + "file_id": "", + "chunk_size": 65536 + }, + "notes": "file_id 来自当前账号语音消息;经 invoke_action 调用。" + } + ], "readOnly": false, "params": [ { @@ -1861,6 +2933,118 @@ "aliases": [], "summary": "以流式方式下载文件(stream 目录本地文件 / URL / 缓存媒体)", "returns": "流式帧:file_info → file_chunk* → file_complete", + "returnsSchema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "file_info" + }, + "file_name": { + "type": "string" + }, + "file_size": { + "type": "integer", + "description": "字节数,0 可表示未知" + }, + "chunk_size": { + "type": "integer" + } + }, + "required": [ + "type", + "data_type", + "file_name", + "file_size", + "chunk_size" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "file_chunk" + }, + "index": { + "type": "integer" + }, + "data": { + "type": "string", + "description": "该分块的 Base64 数据" + }, + "size": { + "type": "integer" + }, + "progress": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "base64_size": { + "type": "integer" + } + }, + "required": [ + "type", + "data_type", + "index", + "data", + "size", + "progress", + "base64_size" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "response" + }, + "data_type": { + "const": "file_complete" + }, + "file_name": { + "type": "string" + }, + "total_chunks": { + "type": "integer" + }, + "total_bytes": { + "type": "integer" + }, + "message": { + "type": "string" + } + }, + "required": [ + "type", + "data_type", + "file_name", + "total_chunks", + "total_bytes", + "message" + ] + } + ], + "description": "每个成功流式帧的 data;失败帧另由 status/retcode/wording 与 type=error 表示,不是 MCP 保存文件后的工具结果" + }, + "examples": [ + { + "summary": "以流式方式下载文件(stream 目录本地文件 / URL / 缓存媒体)", + "params": { + "file": "https://example.com/report.pdf", + "chunk_size": 65536 + }, + "notes": "替换实际公开可访问地址;经 invoke_action 调用,文件保存到 MCP 下载目录。" + } + ], "readOnly": false, "params": [ { @@ -1924,6 +3108,35 @@ "aliases": [], "summary": "解析闪传文件下载直链(不下载,由调用方实现下载)", "returns": "{ url, file_name, file_size }", + "returnsSchema": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "file_size": { + "type": "number" + } + }, + "required": [ + "url", + "file_name", + "file_size" + ] + }, + "examples": [ + { + "summary": "解析闪传文件下载直链(不下载,由调用方实现下载)", + "params": { + "fileset_id": "", + "file_index": 0 + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。 file_index 从 0 开始。" + } + ], "readOnly": false, "params": [ { @@ -1986,6 +3199,15 @@ "description": "图片 URL 或 emoji_id(取决于 return_type)" } }, + "examples": [ + { + "summary": "获取自定义表情", + "params": { + "count": 10, + "return_type": "url" + } + } + ], "readOnly": true, "params": [ { @@ -2066,6 +3288,14 @@ ] } }, + "examples": [ + { + "summary": "获取自定义表情详情", + "params": { + "count": 10 + } + } + ], "readOnly": true, "params": [ { @@ -2157,6 +3387,17 @@ "isFirstPage" ] }, + "examples": [ + { + "summary": "获取表情回应用户(NapCat 分页)", + "params": { + "message_id": -123456, + "emojiId": "128077", + "count": 10 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。 后续页原样传回上页 cookie。" + } + ], "readOnly": true, "params": [ { @@ -2327,6 +3568,14 @@ } ] }, + "examples": [ + { + "summary": "按编号查询 QQ 系统表情", + "params": { + "face_id": 14 + } + } + ], "readOnly": true, "params": [ { @@ -2394,6 +3643,15 @@ "text" ] }, + "examples": [ + { + "summary": "获取语音转文字结果", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": true, "params": [ { @@ -2444,6 +3702,14 @@ "is_super" ] }, + "examples": [ + { + "summary": "判断 QQ 系统表情是否使用超级表情格式", + "params": { + "face_id": 14 + } + } + ], "readOnly": true, "params": [ { @@ -2604,6 +3870,14 @@ "packs" ] }, + "examples": [ + { + "summary": "获取 QQ 系统表情目录", + "params": { + "refresh": false + } + } + ], "readOnly": true, "params": [ { @@ -2635,6 +3909,33 @@ "name": "forward_friend_single_msg", "aliases": [], "summary": "转发单条消息给好友", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "转发单条消息给好友", + "params": { + "message_id": -123456, + "user_id": 10001 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -2689,6 +3990,33 @@ "name": "forward_group_single_msg", "aliases": [], "summary": "转发单条消息到群", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "转发单条消息到群", + "params": { + "message_id": -123456, + "group_id": 20001 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -2743,6 +4071,19 @@ "name": "friend_poke", "aliases": [], "summary": "好友拍一拍", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "好友拍一拍", + "params": { + "user_id": 10001 + }, + "notes": "戳一戳指定好友。" + } + ], "readOnly": false, "params": [ { @@ -2835,6 +4176,16 @@ ] } }, + "examples": [ + { + "summary": "获取 AI 语音角色", + "params": { + "group_id": 20001, + "chat_type": 1 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -2886,6 +4237,22 @@ "name": "get_ai_record", "aliases": [], "summary": "生成 AI 语音", + "returnsSchema": { + "type": "string", + "description": "生成的下载或分享 URL 字符串,不是含 url 字段的对象" + }, + "examples": [ + { + "summary": "生成 AI 语音", + "params": { + "group_id": 20001, + "character": "", + "text": "你好", + "chat_type": 1 + }, + "notes": "character 原样使用 get_ai_characters 的 character_id;send_group_ai_record 会直接发送语音。" + } + ], "readOnly": false, "params": [ { @@ -2988,6 +4355,13 @@ "keyIndex" ] }, + "examples": [ + { + "summary": "获取 clientkey", + "params": {}, + "notes": "返回值可能包含凭据或临时下载密钥,请勿公开或记录到不受控日志。" + } + ], "readOnly": true, "params": [], "invariants": [], @@ -3189,6 +4563,15 @@ "collectionSearchList" ] }, + "examples": [ + { + "summary": "获取收藏列表", + "params": { + "category": 0, + "count": 20 + } + } + ], "readOnly": true, "params": [ { @@ -3254,6 +4637,15 @@ "cookies" ] }, + "examples": [ + { + "summary": "获取 Cookies", + "params": { + "domain": "qun.qq.com" + }, + "notes": "返回登录凭据,仅在有明确用途时读取,请勿公开。" + } + ], "readOnly": true, "params": [ { @@ -3306,6 +4698,15 @@ "csrf_token" ] }, + "examples": [ + { + "summary": "获取凭证", + "params": { + "domain": "qun.qq.com" + }, + "notes": "返回登录凭据,仅在有明确用途时读取,请勿公开。" + } + ], "readOnly": true, "params": [ { @@ -3348,6 +4749,12 @@ "token" ] }, + "examples": [ + { + "summary": "获取 CSRF 令牌", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -3413,6 +4820,14 @@ ] } }, + "examples": [ + { + "summary": "获取可疑好友申请", + "params": { + "count": 20 + } + } + ], "readOnly": true, "params": [ { @@ -3474,6 +4889,16 @@ "emoji_like_list" ] }, + "examples": [ + { + "summary": "获取表情回应用户", + "params": { + "message_id": -123456, + "emoji_id": "128077" + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": true, "params": [ { @@ -3526,6 +4951,89 @@ "name": "get_essence_msg_list", "aliases": [], "summary": "获取精华消息列表", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "msg_seq": { + "type": "integer" + }, + "msg_random": { + "type": "integer" + }, + "sender_id": { + "type": "integer" + }, + "sender_nick": { + "type": "string" + }, + "sender_time": { + "type": "integer" + }, + "operator_id": { + "type": "integer" + }, + "operator_nick": { + "type": "string" + }, + "operator_time": { + "type": "integer" + }, + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "content": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "消息段类型,例如 text、image、reply" + }, + "data": { + "type": "object", + "description": "字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id", + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ] + } + } + }, + "required": [ + "msg_seq", + "msg_random", + "sender_id", + "sender_nick", + "sender_time", + "operator_id", + "operator_nick", + "operator_time", + "message_id", + "content" + ] + } + }, + "examples": [ + { + "summary": "获取精华消息列表", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -3562,6 +5070,42 @@ "name": "get_file", "aliases": [], "summary": "获取文件信息(仅图片/语音缓存;群文件请用 get_group_file_url)", + "returnsSchema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "description": "媒体地址;图片无地址时可为原文件标识" + }, + "url": { + "type": "string", + "description": "媒体下载地址,图片地址可能为空" + }, + "file_size": { + "type": "string", + "description": "原媒体字节数的十进制字符串" + }, + "file_name": { + "type": "string", + "description": "文件名或缓存标识" + } + }, + "required": [ + "file", + "url", + "file_size", + "file_name" + ] + }, + "examples": [ + { + "summary": "获取文件信息(仅图片/语音缓存;群文件请用 get_group_file_url)", + "params": { + "file_id": "" + }, + "notes": "仅查询已缓存的图片或语音,先尝试图片,再尝试语音。" + } + ], "readOnly": true, "params": [ { @@ -3621,6 +5165,15 @@ "fileset_id" ] }, + "examples": [ + { + "summary": "从 QQ 闪传分享码或官方分享链接获取 fileset_id", + "params": { + "share_code": "" + }, + "notes": "share_code 使用实际闪传分享链接或提取码。" + } + ], "readOnly": true, "params": [ { @@ -3655,6 +5208,66 @@ "name": "get_fileset_info", "aliases": [], "summary": "获取文件集信息", + "returnsSchema": { + "type": "object", + "properties": { + "fileset_id": { + "type": "string" + }, + "file_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fileset_id": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "orig_name": { + "type": "string" + }, + "size": { + "type": "number", + "description": "文件字节数;此接口字段名为 size" + }, + "share_url": { + "type": "string" + }, + "file_id": { + "type": "string" + }, + "download_url": { + "type": "string" + } + }, + "required": [ + "fileset_id", + "file_name", + "orig_name", + "size", + "share_url", + "file_id", + "download_url" + ] + } + } + }, + "required": [ + "fileset_id", + "file_list" + ] + }, + "examples": [ + { + "summary": "获取文件集信息", + "params": { + "fileset_id": "" + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。" + } + ], "readOnly": false, "params": [ { @@ -3687,6 +5300,54 @@ "name": "get_flash_file_list", "aliases": [], "summary": "获取闪传文件列表", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fileset_id": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "orig_name": { + "type": "string" + }, + "size": { + "type": "number", + "description": "文件字节数;此接口字段名为 size" + }, + "share_url": { + "type": "string" + }, + "file_id": { + "type": "string" + }, + "download_url": { + "type": "string" + } + }, + "required": [ + "fileset_id", + "file_name", + "orig_name", + "size", + "share_url", + "file_id", + "download_url" + ] + } + }, + "examples": [ + { + "summary": "获取闪传文件列表", + "params": { + "fileset_id": "" + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。" + } + ], "readOnly": false, "params": [ { @@ -3719,6 +5380,27 @@ "name": "get_flash_file_url", "aliases": [], "summary": "获取闪传文件链接", + "returnsSchema": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": [ + "url" + ] + }, + "examples": [ + { + "summary": "获取闪传文件链接", + "params": { + "fileset_id": "", + "file_index": 0 + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。 file_index 从 0 开始。" + } + ], "readOnly": false, "params": [ { @@ -3779,9 +5461,147 @@ "properties": { "messages": { "type": "array", - "description": "转发消息节点数组", + "description": "转发消息节点数组;节点标识与本地消息 ID 不是同一契约", "items": { - "type": "object" + "type": "object", + "properties": { + "time": { + "type": "integer", + "description": "消息时间,Unix 秒" + }, + "message_type": { + "type": "string", + "enum": [ + "private", + "group" + ] + }, + "sub_type": { + "type": "string", + "description": "消息子类型" + }, + "message_id": { + "type": "integer", + "description": "转发节点原始标识,缺失时为 0" + }, + "real_id": { + "type": "integer", + "description": "与节点 message_id 相同" + }, + "user_id": { + "type": "integer", + "description": "发送者 QQ 号" + }, + "group_id": { + "type": "integer", + "description": "群聊消息的群号,私聊可省略" + }, + "target_id": { + "type": "integer", + "description": "私聊目标 QQ 号(若有)" + }, + "message": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "消息段类型,例如 text、image、reply" + }, + "data": { + "type": "object", + "description": "字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id", + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ] + } + }, + "raw_message": { + "type": "string", + "description": "消息的 CQ 码字符串形式" + }, + "font": { + "type": "integer" + }, + "sender": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "nickname": { + "type": "string" + }, + "card": { + "type": "string" + }, + "sex": { + "type": "string", + "enum": [ + "male", + "female", + "unknown" + ] + }, + "age": { + "type": "integer" + }, + "area": { + "type": "string" + }, + "level": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": true + }, + "self_id": { + "type": "integer" + }, + "post_type": { + "const": "message" + }, + "message_format": { + "const": "array" + }, + "message_seq": { + "type": "integer", + "description": "节点消息序号,缺失时使用节点标识或 0" + } + }, + "additionalProperties": true, + "required": [ + "self_id", + "user_id", + "time", + "message_id", + "message_seq", + "real_id", + "message_type", + "sender", + "raw_message", + "font", + "sub_type", + "message", + "message_format", + "post_type" + ] } } }, @@ -3789,6 +5609,15 @@ "messages" ] }, + "examples": [ + { + "summary": "获取合并转发消息(id 或 message_id)", + "params": { + "id": "" + }, + "notes": "id 是合并转发资源标识,通常来自转发消息段或上传结果 res_id,不是普通数值 message_id。" + } + ], "readOnly": true, "params": [ { @@ -3853,6 +5682,12 @@ ] } }, + "examples": [ + { + "summary": "获取好友列表", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -3873,9 +5708,123 @@ "properties": { "messages": { "type": "array", - "description": "消息事件对象数组", + "description": "消息对象数组;去除 post_type/self_id,附带 real_id", "items": { - "type": "object" + "type": "object", + "properties": { + "time": { + "type": "integer", + "description": "消息时间,Unix 秒" + }, + "message_type": { + "type": "string", + "enum": [ + "private", + "group" + ] + }, + "sub_type": { + "type": "string", + "description": "消息子类型" + }, + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "real_id": { + "type": "integer", + "description": "原始 message_id;旧历史记录缺失时可为 0" + }, + "user_id": { + "type": "integer", + "description": "发送者 QQ 号" + }, + "group_id": { + "type": "integer", + "description": "群聊消息的群号,私聊可省略" + }, + "target_id": { + "type": "integer", + "description": "私聊目标 QQ 号(若有)" + }, + "message": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "消息段类型,例如 text、image、reply" + }, + "data": { + "type": "object", + "description": "字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id", + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ] + } + }, + "raw_message": { + "type": "string", + "description": "消息的 CQ 码字符串形式" + }, + "font": { + "type": "integer" + }, + "sender": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "nickname": { + "type": "string" + }, + "card": { + "type": "string" + }, + "sex": { + "type": "string", + "enum": [ + "male", + "female", + "unknown" + ] + }, + "age": { + "type": "integer" + }, + "area": { + "type": "string" + }, + "level": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true } } }, @@ -3883,6 +5832,16 @@ "messages" ] }, + "examples": [ + { + "summary": "获取好友消息历史(无锚点时从服务器获取最新双向记录)", + "params": { + "user_id": 10001, + "count": 20 + }, + "notes": "省略 message_id 从最新消息开始;只读取当前账号消息。" + } + ], "readOnly": true, "params": [ { @@ -4018,6 +5977,12 @@ ] } }, + "examples": [ + { + "summary": "获取分组好友列表", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -4106,6 +6071,15 @@ "no_code_finger_open" ] }, + "examples": [ + { + "summary": "获取群管理设置的当前值", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4311,6 +6285,15 @@ ] } }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4352,9 +6335,336 @@ "properties": { "mediaList": { "type": "array", - "description": "相册媒体项列表(各项字段不固定)", + "description": "相册图片或视频,未返回的字段可省略或为 null", "items": { - "type": "object" + "type": "object", + "properties": { + "type": { + "type": [ + "integer", + "null" + ] + }, + "image": { + "type": [ + "object", + "null" + ], + "properties": { + "name": { + "type": [ + "string", + "null" + ] + }, + "sloc": { + "type": [ + "string", + "null" + ] + }, + "lloc": { + "type": [ + "string", + "null" + ], + "description": "图片长定位标识,供评论、删除等操作使用" + }, + "photoUrls": { + "type": [ + "array", + "null" + ], + "items": { + "type": "object", + "properties": { + "spec": { + "type": [ + "integer", + "null" + ] + }, + "url": { + "type": [ + "object", + "null" + ], + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "width": { + "type": [ + "integer", + "null" + ] + }, + "height": { + "type": [ + "integer", + "null" + ] + } + } + } + } + } + }, + "defaultUrl": { + "type": [ + "object", + "null" + ], + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "width": { + "type": [ + "integer", + "null" + ] + }, + "height": { + "type": [ + "integer", + "null" + ] + } + } + }, + "isGif": { + "type": [ + "boolean", + "null" + ] + }, + "hasRaw": { + "type": [ + "boolean", + "null" + ] + } + } + }, + "video": { + "type": [ + "object", + "null" + ], + "properties": { + "id": { + "type": [ + "string", + "null" + ], + "description": "视频标识,供评论、删除等操作使用" + }, + "url": { + "type": [ + "string", + "null" + ] + }, + "cover": { + "type": [ + "object", + "null" + ], + "properties": { + "name": { + "type": [ + "string", + "null" + ] + }, + "sloc": { + "type": [ + "string", + "null" + ] + }, + "lloc": { + "type": [ + "string", + "null" + ], + "description": "图片长定位标识,供评论、删除等操作使用" + }, + "photoUrls": { + "type": [ + "array", + "null" + ], + "items": { + "type": "object", + "properties": { + "spec": { + "type": [ + "integer", + "null" + ] + }, + "url": { + "type": [ + "object", + "null" + ], + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "width": { + "type": [ + "integer", + "null" + ] + }, + "height": { + "type": [ + "integer", + "null" + ] + } + } + } + } + } + }, + "defaultUrl": { + "type": [ + "object", + "null" + ], + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "width": { + "type": [ + "integer", + "null" + ] + }, + "height": { + "type": [ + "integer", + "null" + ] + } + } + }, + "isGif": { + "type": [ + "boolean", + "null" + ] + }, + "hasRaw": { + "type": [ + "boolean", + "null" + ] + } + } + }, + "width": { + "type": [ + "integer", + "null" + ] + }, + "height": { + "type": [ + "integer", + "null" + ] + }, + "videoTime": { + "type": [ + "string", + "null" + ], + "description": "时长,十进制字符串" + }, + "videoUrl": { + "type": [ + "array", + "null" + ], + "items": { + "type": "object", + "properties": { + "spec": { + "type": [ + "integer", + "null" + ] + }, + "url": { + "type": [ + "object", + "null" + ], + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "width": { + "type": [ + "integer", + "null" + ] + }, + "height": { + "type": [ + "integer", + "null" + ] + } + } + } + } + } + } + } + }, + "uploader": { + "type": [ + "string", + "null" + ] + }, + "batchId": { + "type": [ + "string", + "null" + ], + "description": "上传批次,十进制字符串" + }, + "uploadTime": { + "type": [ + "string", + "null" + ], + "description": "上传时间,十进制字符串" + } + } } }, "nextAttachInfo": { @@ -4367,6 +6677,17 @@ "nextAttachInfo" ] }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001, + "album_id": "", + "attach_info": "" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 下一页传回 nextAttachInfo。" + } + ], "readOnly": true, "params": [ { @@ -4453,6 +6774,15 @@ "remain_at_all_count_for_uin" ] }, + "examples": [ + { + "summary": "获取群 @全体成员 剩余次数", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4546,6 +6876,15 @@ "group_all_shut" ] }, + "examples": [ + { + "summary": "获取群详细信息", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4621,6 +6960,15 @@ "total_space" ] }, + "examples": [ + { + "summary": "获取群文件系统信息", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4670,6 +7018,16 @@ "url" ] }, + "examples": [ + { + "summary": "获取群文件下载链接", + "params": { + "group_id": 20001, + "file_id": "" + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4841,6 +7199,16 @@ "folders" ] }, + "examples": [ + { + "summary": "获取群子目录文件列表", + "params": { + "group_id": 20001, + "folder_id": "" + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4903,6 +7271,218 @@ "name": "get_group_honor_info", "aliases": [], "summary": "获取群荣誉信息", + "returnsSchema": { + "type": "object", + "properties": { + "group_id": { + "type": "integer" + }, + "current_talkative": { + "anyOf": [ + { + "type": "object", + "properties": { + "user_id": { + "type": [ + "number", + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "user_id", + "nickname", + "avatar", + "description" + ] + }, + { + "type": "null" + } + ] + }, + "talkative_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": [ + "number", + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "user_id", + "nickname", + "avatar", + "description" + ] + } + }, + "performer_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": [ + "number", + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "user_id", + "nickname", + "avatar", + "description" + ] + } + }, + "legend_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": [ + "number", + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "user_id", + "nickname", + "avatar", + "description" + ] + } + }, + "emotion_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": [ + "number", + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "user_id", + "nickname", + "avatar", + "description" + ] + } + }, + "strong_newbie_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": [ + "number", + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "user_id", + "nickname", + "avatar", + "description" + ] + }, + "description": "当前实现始终为空数组" + } + }, + "required": [ + "group_id", + "current_talkative", + "talkative_list", + "performer_list", + "legend_list", + "emotion_list", + "strong_newbie_list" + ] + }, + "examples": [ + { + "summary": "获取群荣誉信息", + "params": { + "group_id": 20001, + "type": "all" + }, + "notes": "未请求的分类仍返回空数组;当前龙王可能为 null。" + } + ], "readOnly": true, "params": [ { @@ -5006,6 +7586,12 @@ ] } }, + "examples": [ + { + "summary": "获取被忽略的入群请求(NapCat)", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -5086,6 +7672,12 @@ ] } }, + "examples": [ + { + "summary": "获取被过滤的入群请求", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -5158,6 +7750,15 @@ "group_all_shut" ] }, + "examples": [ + { + "summary": "获取群信息", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -5264,6 +7865,15 @@ "group_all_shut" ] }, + "examples": [ + { + "summary": "获取群信息(扩展)", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -5372,6 +7982,14 @@ ] } }, + "examples": [ + { + "summary": "获取群列表", + "params": { + "no_cache": false + } + } + ], "readOnly": true, "params": [ { @@ -5511,6 +8129,16 @@ "role" ] }, + "examples": [ + { + "summary": "获取群成员信息", + "params": { + "group_id": 20001, + "user_id": 10001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -5691,6 +8319,15 @@ ] } }, + "examples": [ + { + "summary": "获取群成员列表", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -5746,9 +8383,123 @@ "properties": { "messages": { "type": "array", - "description": "消息事件对象数组", + "description": "消息对象数组;去除 post_type/self_id,附带 real_id", "items": { - "type": "object" + "type": "object", + "properties": { + "time": { + "type": "integer", + "description": "消息时间,Unix 秒" + }, + "message_type": { + "type": "string", + "enum": [ + "private", + "group" + ] + }, + "sub_type": { + "type": "string", + "description": "消息子类型" + }, + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "real_id": { + "type": "integer", + "description": "原始 message_id;旧历史记录缺失时可为 0" + }, + "user_id": { + "type": "integer", + "description": "发送者 QQ 号" + }, + "group_id": { + "type": "integer", + "description": "群聊消息的群号,私聊可省略" + }, + "target_id": { + "type": "integer", + "description": "私聊目标 QQ 号(若有)" + }, + "message": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "消息段类型,例如 text、image、reply" + }, + "data": { + "type": "object", + "description": "字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id", + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ] + } + }, + "raw_message": { + "type": "string", + "description": "消息的 CQ 码字符串形式" + }, + "font": { + "type": "integer" + }, + "sender": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "nickname": { + "type": "string" + }, + "card": { + "type": "string" + }, + "sex": { + "type": "string", + "enum": [ + "male", + "female", + "unknown" + ] + }, + "age": { + "type": "integer" + }, + "area": { + "type": "string" + }, + "level": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true } } }, @@ -5756,6 +8507,16 @@ "messages" ] }, + "examples": [ + { + "summary": "获取群消息历史", + "params": { + "group_id": 20001, + "count": 20 + }, + "notes": "省略 message_id 从最新消息开始;翻页使用上一页实际 message_id,不要自行加减。" + } + ], "readOnly": true, "params": [ { @@ -5949,6 +8710,15 @@ "folders" ] }, + "examples": [ + { + "summary": "获取群根目录文件列表", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -5998,6 +8768,16 @@ "url" ] }, + "examples": [ + { + "summary": "获取群分享链接", + "params": { + "group_id": 20001, + "need_short_url": true + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -6109,6 +8889,15 @@ ] } }, + "examples": [ + { + "summary": "获取群禁言列表", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -6145,6 +8934,41 @@ "name": "get_group_signed_list", "aliases": [], "summary": "获取群今日打卡列表", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "nick": { + "type": "string" + }, + "time": { + "type": "integer" + }, + "rank": { + "type": "integer" + } + }, + "required": [ + "user_id", + "nick", + "time", + "rank" + ] + } + }, + "examples": [ + { + "summary": "获取群今日打卡列表", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -6242,6 +9066,17 @@ ] } }, + "examples": [ + { + "summary": "获取群系统消息", + "params": { + "group_id": 20001, + "only_pending": true, + "count": 20 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -6340,6 +9175,15 @@ "tags" ] }, + "examples": [ + { + "summary": "读取某个群分类下的资料标签", + "params": { + "group_class": 1 + }, + "notes": "group_class 使用实际群分类编号。" + } + ], "readOnly": true, "params": [ { @@ -6375,6 +9219,85 @@ "aliases": [], "summary": "获取群待办列表", "returns": "群待办数组,包含可用于待办操作的消息标识、服务端摘要与时间", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "message_seq": { + "type": "integer" + }, + "message_random": { + "type": "integer" + }, + "message": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "消息段类型,例如 text、image、reply" + }, + "data": { + "type": "object", + "description": "字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id", + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ] + } + }, + { + "type": "null" + } + ], + "description": "本地缓存中的消息段;未缓存时为 null" + }, + "text": { + "type": "string" + }, + "create_time": { + "type": "integer" + }, + "update_time": { + "type": "integer" + } + }, + "required": [ + "message_id", + "message_seq", + "message_random", + "message", + "text", + "create_time", + "update_time" + ] + } + }, + "examples": [ + { + "summary": "获取群待办列表", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -6411,6 +9334,42 @@ "name": "get_image", "aliases": [], "summary": "获取图片信息", + "returnsSchema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "description": "媒体地址;图片无地址时可为原文件标识" + }, + "url": { + "type": "string", + "description": "媒体下载地址,图片地址可能为空" + }, + "file_size": { + "type": "string", + "description": "原媒体字节数的十进制字符串" + }, + "file_name": { + "type": "string", + "description": "文件名或缓存标识" + } + }, + "required": [ + "file", + "url", + "file_size", + "file_name" + ] + }, + "examples": [ + { + "summary": "获取图片信息", + "params": { + "file": "" + }, + "notes": "file 或 file_id 原样取自图片消息段,须在当前账号媒体缓存中存在。" + } + ], "readOnly": true, "params": [ { @@ -6474,6 +9433,12 @@ "nickname" ] }, + "examples": [ + { + "summary": "基本调用", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -6488,6 +9453,60 @@ "name": "get_mini_app_ark", "aliases": [], "summary": "获取小程序卡片 ark", + "returnsSchema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "ver": { + "description": "服务端卡片版本,原样透传" + }, + "prompt": { + "description": "服务端提示文本,原样透传" + }, + "config": { + "description": "服务端卡片配置,原样透传" + }, + "app": { + "description": "服务端应用名,原样透传" + }, + "view": { + "description": "服务端视图,原样透传" + }, + "meta": { + "description": "服务端卡片元数据,原样透传" + }, + "miniappShareOrigin": { + "const": 3 + }, + "miniappOpenRefer": { + "const": "10002" + } + }, + "required": [ + "miniappShareOrigin", + "miniappOpenRefer" + ] + } + }, + "required": [ + "data" + ] + }, + "examples": [ + { + "summary": "获取小程序卡片 ark", + "params": { + "type": "bili", + "title": "示例视频", + "desc": "视频介绍", + "pic_url": "https://example.com/cover.png", + "jump_url": "https://www.bilibili.com/video/" + }, + "notes": "替换为实际分享地址;当前仅支持 bili 和 weibo。返回的 data 是卡片对象,不会自动发送。" + } + ], "readOnly": true, "params": [ { @@ -6582,6 +9601,154 @@ "aliases": [], "summary": "获取消息", "returns": "消息事件对象(去除 post_type/self_id,附带 real_id);逐个尝试刷新图片、语音和视频 URL,成功使用新地址,失败保留该段原地址并继续返回消息;地址更新时同步 raw_message。", + "returnsSchema": { + "allOf": [ + { + "type": "object", + "properties": { + "time": { + "type": "integer", + "description": "消息时间,Unix 秒" + }, + "message_type": { + "type": "string", + "enum": [ + "private", + "group" + ] + }, + "sub_type": { + "type": "string", + "description": "消息子类型" + }, + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "real_id": { + "type": "integer", + "description": "原始 message_id;旧历史记录缺失时可为 0" + }, + "user_id": { + "type": "integer", + "description": "发送者 QQ 号" + }, + "group_id": { + "type": "integer", + "description": "群聊消息的群号,私聊可省略" + }, + "target_id": { + "type": "integer", + "description": "私聊目标 QQ 号(若有)" + }, + "message": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "消息段类型,例如 text、image、reply" + }, + "data": { + "type": "object", + "description": "字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id", + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ] + } + }, + "raw_message": { + "type": "string", + "description": "消息的 CQ 码字符串形式" + }, + "font": { + "type": "integer" + }, + "sender": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "nickname": { + "type": "string" + }, + "card": { + "type": "string" + }, + "sex": { + "type": "string", + "enum": [ + "male", + "female", + "unknown" + ] + }, + "age": { + "type": "integer" + }, + "area": { + "type": "string" + }, + "level": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "real_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "real_id" + ] + } + ], + "description": "消息事件去除 post_type/self_id 并增加 real_id;群聊和私聊字段有所不同" + }, + "examples": [ + { + "summary": "获取消息", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": true, "params": [ { @@ -6667,6 +9834,15 @@ ] } }, + "examples": [ + { + "summary": "获取一条消息的全部表情回应", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": true, "params": [ { @@ -6746,6 +9922,14 @@ "clients" ] }, + "examples": [ + { + "summary": "获取在线客户端", + "params": { + "no_cache": false + } + } + ], "readOnly": true, "params": [ { @@ -6790,6 +9974,16 @@ "url" ] }, + "examples": [ + { + "summary": "获取私聊文件下载链接", + "params": { + "user_id": 10001, + "file_id": "" + }, + "notes": "使用私聊文件消息段中的 file_id,必要时原样传 file_hash。" + } + ], "readOnly": true, "params": [ { @@ -7094,6 +10288,16 @@ "voteInfo" ] }, + "examples": [ + { + "summary": "获取资料点赞", + "params": { + "user_id": 10001, + "start": 0, + "count": 10 + } + } + ], "readOnly": true, "params": [ { @@ -7352,6 +10556,16 @@ "has_more" ] }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001, + "attach_info": "" + }, + "notes": "后续页原样传回上一页的 attach_info;has_more=false 表示结束。" + } + ], "readOnly": true, "params": [ { @@ -7456,6 +10670,16 @@ "has_more" ] }, + "examples": [ + { + "summary": "获取 QQ 空间好友动态(feed);page_num 仅首页可靠,深翻页需时间游标(暂未实现)", + "params": { + "page_num": 1, + "count": 10 + }, + "notes": "目前仅首页可靠,不应假设任意页码均能完整翻页。" + } + ], "readOnly": true, "params": [ { @@ -7605,6 +10829,17 @@ "comments" ] }, + "examples": [ + { + "summary": "获取 QQ 空间说说详情与一页评论(含服务端内联回复,需有访问权限)", + "params": { + "tid": "", + "pos": 0, + "num": 20 + }, + "notes": "tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。 replies 仅为服务端内联回复,不保证完整。" + } + ], "readOnly": true, "params": [ { @@ -7750,6 +10985,16 @@ "msglist" ] }, + "examples": [ + { + "summary": "获取 QQ 空间说说列表(默认机器人自己的空间)", + "params": { + "pos": 0, + "num": 20 + }, + "notes": "默认读取当前账号;指定 target_uin 时需有访问权限。" + } + ], "readOnly": true, "params": [ { @@ -7824,6 +11069,15 @@ "type": "array", "description": "最近会话列表(占位,恒空)" }, + "examples": [ + { + "summary": "获取最近会话(占位)", + "notes": "兼容占位接口,当前始终返回空数组,不能据此判断真实会话列表。", + "params": { + "count": 10 + } + } + ], "readOnly": true, "params": [ { @@ -7856,6 +11110,70 @@ "aliases": [], "summary": "获取语音信息;传 out_format 则服务端转码并附带 base64", "returns": "语音信息(file/url 为向 QQ 获取的当前下载地址);资源元数据缺失或远端获取失败时返回错误。out_format 指定时附带转码后的 base64。", + "returnsSchema": { + "allOf": [ + { + "type": "object", + "properties": { + "file": { + "type": "string", + "description": "媒体地址;图片无地址时可为原文件标识" + }, + "url": { + "type": "string", + "description": "媒体下载地址,图片地址可能为空" + }, + "file_size": { + "type": "string", + "description": "原媒体字节数的十进制字符串" + }, + "file_name": { + "type": "string", + "description": "文件名或缓存标识" + } + }, + "required": [ + "file", + "url", + "file_size", + "file_name" + ] + }, + { + "type": "object", + "properties": { + "out_format": { + "type": "string", + "enum": [ + "mp3", + "amr", + "wma", + "m4a", + "spx", + "ogg", + "wav", + "flac" + ] + }, + "base64": { + "type": "string", + "description": "转换后音频的 Base64,未请求 out_format 时省略" + } + } + } + ], + "description": "未转换时只有媒体信息;指定 out_format 成功转换后同时增加 out_format 和 base64,file_size 仍为原媒体大小" + }, + "examples": [ + { + "summary": "获取语音信息;传 out_format 则服务端转码并附带 base64", + "params": { + "file": "", + "out_format": "mp3" + }, + "notes": "file 来自语音消息段;格式转换依赖服务端可用的转换工具,成功时增加 out_format 和 base64。" + } + ], "readOnly": true, "params": [ { @@ -7943,6 +11261,42 @@ "nc_get_rkey" ], "summary": "获取下载 rkey", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "rkey": { + "type": "string", + "description": "下载密钥,敏感信息" + }, + "type": { + "type": "integer" + }, + "ttl": { + "type": "integer", + "description": "有效时长,秒" + }, + "create_time": { + "type": "integer", + "description": "创建时间,Unix 秒" + } + }, + "required": [ + "rkey", + "type", + "ttl", + "create_time" + ] + } + }, + "examples": [ + { + "summary": "获取下载 rkey", + "params": {}, + "notes": "返回值可能包含凭据或临时下载密钥,请勿公开或记录到不受控日志。" + } + ], "readOnly": true, "params": [], "invariants": [], @@ -7983,6 +11337,13 @@ "name" ] }, + "examples": [ + { + "summary": "获取 rkey 服务器信息", + "params": {}, + "notes": "返回值可能包含凭据或临时下载密钥,请勿公开或记录到不受控日志。" + } + ], "readOnly": true, "params": [], "invariants": [], @@ -7997,6 +11358,19 @@ "name": "get_share_link", "aliases": [], "summary": "获取文件分享链接", + "returnsSchema": { + "type": "string", + "description": "生成的下载或分享 URL 字符串,不是含 url 字段的对象" + }, + "examples": [ + { + "summary": "获取文件分享链接", + "params": { + "fileset_id": "" + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。" + } + ], "readOnly": false, "params": [ { @@ -8051,6 +11425,12 @@ "time" ] }, + "examples": [ + { + "summary": "基本调用", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -8180,6 +11560,15 @@ "long_nick" ] }, + "examples": [ + { + "summary": "获取陌生人信息", + "params": { + "user_id": 10001 + }, + "notes": "替换为需要查询的 QQ 号。" + } + ], "readOnly": true, "params": [ { @@ -8216,6 +11605,20 @@ "name": "get_unidirectional_friend_list", "aliases": [], "summary": "获取单向好友列表", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "description": "服务端列表项原样透传,目前未归一化字段,不能假定与 get_friend_list 相同" + } + }, + "examples": [ + { + "summary": "获取单向好友列表", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -8252,6 +11655,12 @@ "protocol_version" ] }, + "examples": [ + { + "summary": "基本调用", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -8266,6 +11675,20 @@ "name": "group_poke", "aliases": [], "summary": "群拍一拍", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "群拍一拍", + "params": { + "group_id": 20001, + "user_id": 10001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": false, "params": [ { @@ -8320,6 +11743,20 @@ "name": "like_qzone", "aliases": [], "summary": "给一条说说点赞(QQ 空间)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "给一条说说点赞(QQ 空间)", + "params": { + "tid": "", + "target_uin": 10001 + }, + "notes": "tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。" + } + ], "readOnly": false, "params": [ { @@ -8390,6 +11827,51 @@ "name": "list_filesets", "aliases": [], "summary": "列出当前账号的所有闪传文件集", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fileset_id": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "orig_name": { + "type": "string" + }, + "size": { + "type": "number", + "description": "文件字节数;此接口字段名为 size" + }, + "share_url": { + "type": "string" + }, + "file_id": { + "type": "string" + }, + "download_url": { + "type": "string" + } + }, + "required": [ + "fileset_id", + "file_name", + "orig_name", + "size", + "share_url", + "file_id", + "download_url" + ] + } + }, + "examples": [ + { + "summary": "列出当前账号的所有闪传文件集", + "params": {} + } + ], "readOnly": false, "params": [], "invariants": [], @@ -8404,6 +11886,20 @@ "name": "mark_group_msg_as_read", "aliases": [], "summary": "标记群消息已读", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "标记群消息已读", + "params": { + "message_id": -123456, + "group_id": 20001 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -8457,6 +11953,19 @@ "name": "mark_msg_as_read", "aliases": [], "summary": "标记消息已读(群聊/私聊自动路由)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "标记消息已读(群聊/私聊自动路由)", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -8508,6 +12017,20 @@ "name": "mark_private_msg_as_read", "aliases": [], "summary": "标记私聊消息已读", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "标记私聊消息已读", + "params": { + "message_id": -123456, + "user_id": 10001 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -8561,6 +12084,20 @@ "name": "modify_custom_face", "aliases": [], "summary": "修改收藏表情备注", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "修改收藏表情备注", + "params": { + "emoji_id": "", + "desc": "常用表情" + }, + "notes": "emoji_id 来自 fetch_custom_face_detail。" + } + ], "readOnly": false, "params": [ { @@ -8606,6 +12143,19 @@ "name": "move_custom_face_to_front", "aliases": [], "summary": "收藏表情移到最前", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "收藏表情移到最前", + "params": { + "emoji_id": "" + }, + "notes": "emoji_id 来自 fetch_custom_face_detail。" + } + ], "readOnly": false, "params": [ { @@ -8638,6 +12188,22 @@ "name": "move_group_file", "aliases": [], "summary": "移动群文件", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "移动群文件", + "params": { + "group_id": 20001, + "file_id": "", + "parent_directory": "/", + "target_directory": "" + }, + "notes": "目录和文件标识均来自该群文件列表。" + } + ], "readOnly": false, "params": [ { @@ -8723,6 +12289,13 @@ "type": "null", "description": "packet 状态(占位,恒 null)" }, + "examples": [ + { + "summary": "获取 packet 状态(占位)", + "notes": "兼容占位接口,当前始终返回 null,不提供实际收发统计。", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -8755,6 +12328,15 @@ "ext_status" ] }, + "examples": [ + { + "summary": "获取用户在线/扩展状态", + "params": { + "user_id": 10001 + }, + "notes": "替换为需要查询的 QQ 号。" + } + ], "readOnly": true, "params": [ { @@ -8848,6 +12430,15 @@ "language" ] }, + "examples": [ + { + "summary": "OCR 图片(服务端,需图片 URL 或已缓存的图片 file_id)", + "params": { + "image": "https://example.com/image.png" + }, + "notes": "替换为实际可访问的图片地址或已缓存图片标识;不支持本地路径和 base64。" + } + ], "readOnly": true, "params": [ { @@ -8882,6 +12473,20 @@ "name": "rename_flash_file", "aliases": [], "summary": "重命名闪传文件", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "重命名闪传文件", + "params": { + "fileset_id": "", + "new_name": "新资料包" + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。" + } + ], "readOnly": false, "params": [ { @@ -8928,6 +12533,29 @@ "name": "rename_group_file", "aliases": [], "summary": "重命名群文件", + "returnsSchema": { + "type": "object", + "properties": { + "ok": { + "const": true + } + }, + "required": [ + "ok" + ] + }, + "examples": [ + { + "summary": "重命名群文件", + "params": { + "group_id": 20001, + "file_id": "", + "current_parent_directory": "/", + "new_name": "新文件名.pdf" + }, + "notes": "file_id 来自群文件列表,目录必须是文件当前所在目录。" + } + ], "readOnly": false, "params": [ { @@ -9007,6 +12635,21 @@ "name": "rename_group_file_folder", "aliases": [], "summary": "重命名群文件夹", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "重命名群文件夹", + "params": { + "group_id": 20001, + "folder_id": "", + "new_folder_name": "课程资料" + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": false, "params": [ { @@ -9083,6 +12726,27 @@ "name": "request_decrypt_key", "aliases": [], "summary": "请求数据库解密密钥", + "returnsSchema": { + "type": "object", + "properties": { + "db_key": { + "type": "string", + "description": "数据库密钥,敏感信息" + } + }, + "required": [ + "db_key" + ] + }, + "examples": [ + { + "summary": "请求数据库解密密钥", + "params": { + "db_path": "/path/to/account/nt_msg.db" + }, + "notes": "路径位于 SnowLuma 服务端,必须指向当前账号可用的数据库;返回值是敏感密钥。" + } + ], "readOnly": false, "params": [ { @@ -9209,6 +12873,14 @@ "faces" ] }, + "examples": [ + { + "summary": "搜索 QQ 系统表情", + "params": { + "query": "微笑" + } + } + ], "readOnly": true, "params": [ { @@ -9256,6 +12928,15 @@ "arkMsg" ] }, + "examples": [ + { + "summary": "分享用户/群 Ark 卡片(NapCat 标准名)", + "params": { + "user_id": 10001 + }, + "notes": "生成联系人分享卡片;返回卡片数据,不等同于发送到聊天。" + } + ], "readOnly": true, "params": [ { @@ -9316,6 +12997,33 @@ "aliases": [], "summary": "发送账号收藏表情(emoji_id 或 MD5),保留表情显示样式", "returns": "{ message_id: number }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "发送账号收藏表情(emoji_id 或 MD5),保留表情显示样式", + "params": { + "group_id": 20001, + "emoji_id": "" + }, + "notes": "群聊和私聊目标只能提供一个;emoji_id 来自 fetch_custom_face_detail。" + } + ], "readOnly": false, "params": [ { @@ -9404,6 +13112,28 @@ "aliases": [], "summary": "发送闪传消息(私聊或群聊,引用 fileset_id 让对端下载)", "returns": "{ message_id }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "const": 0, + "description": "闪传分享不返回普通消息 ID,0 是占位值,不能用于查询或撤回" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "发送闪传消息(私聊或群聊,引用 fileset_id 让对端下载)", + "params": { + "fileset_id": "", + "user_id": 10001 + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。 会发送分享消息;message_id=0 是占位值,不能用于撤回或查消息。" + } + ], "readOnly": false, "params": [ { @@ -9467,6 +13197,65 @@ "aliases": [], "summary": "发送合并转发(按 message_type/群号自动路由)", "returns": "{ message_id, res_id, forward_id }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "anyOf": [ + { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + { + "const": 0 + } + ], + "description": "已发送时是非零有符号消息 ID;未提供目标、仅上传资源时为 0" + }, + "res_id": { + "type": "string" + }, + "forward_id": { + "type": "string" + } + }, + "required": [ + "message_id", + "res_id", + "forward_id" + ] + }, + "examples": [ + { + "summary": "发送合并转发(按 message_type/群号自动路由)", + "params": { + "group_id": 20001, + "messages": [ + { + "type": "node", + "data": { + "user_id": 10001, + "nickname": "示例用户", + "content": [ + { + "type": "text", + "data": { + "text": "转发内容" + } + } + ] + } + } + ] + }, + "notes": "替换目标群号;nodes 是自定义转发节点数组。send_forward_msg 不提供目标时仅上传资源并返回 message_id=0。" + } + ], "readOnly": false, "params": [ { @@ -9585,6 +13374,35 @@ "aliases": [], "summary": "发送 AI 语音到群", "returns": "{ message_id }:已发送 AI 语音的 OneBot 消息 ID。", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "发送 AI 语音到群", + "params": { + "group_id": 20001, + "character": "", + "text": "你好", + "chat_type": 1 + }, + "notes": "character 原样使用 get_ai_characters 的 character_id;send_group_ai_record 会直接发送语音。" + } + ], "readOnly": false, "params": [ { @@ -9669,6 +13487,15 @@ "type": "string", "description": "群 Ark 卡片 JSON 字符串" }, + "examples": [ + { + "summary": "分享群 Ark 卡片(NapCat 标准名)", + "params": { + "group_id": 20001 + }, + "notes": "生成群分享卡片;返回卡片数据,不等同于发送到聊天。" + } + ], "readOnly": true, "params": [ { @@ -9704,6 +13531,57 @@ "aliases": [], "summary": "发送群合并转发", "returns": "{ message_id, res_id, forward_id }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "res_id": { + "type": "string" + }, + "forward_id": { + "type": "string" + } + }, + "required": [ + "message_id", + "res_id", + "forward_id" + ] + }, + "examples": [ + { + "summary": "发送群合并转发", + "params": { + "group_id": 20001, + "messages": [ + { + "type": "node", + "data": { + "user_id": 10001, + "nickname": "示例用户", + "content": [ + { + "type": "text", + "data": { + "text": "转发内容" + } + } + ] + } + } + ] + }, + "notes": "替换目标群号;nodes 是自定义转发节点数组。send_forward_msg 不提供目标时仅上传资源并返回 message_id=0。" + } + ], "readOnly": false, "params": [ { @@ -9805,6 +13683,40 @@ "aliases": [], "summary": "发送群消息", "returns": "{ message_id: number }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "发送群消息", + "params": { + "group_id": 20001, + "message": [ + { + "type": "text", + "data": { + "text": "你好" + } + } + ] + }, + "notes": "替换目标群号;成功返回的 message_id 可为负值。" + } + ], "readOnly": false, "params": [ { @@ -9866,6 +13778,19 @@ "name": "send_like", "aliases": [], "summary": "点赞", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "点赞", + "params": { + "user_id": 10001, + "times": 1 + } + } + ], "readOnly": false, "params": [ { @@ -9916,6 +13841,34 @@ "aliases": [], "summary": "发送消息(按 message_type/群号 自动路由群聊或私聊)", "returns": "{ message_id: number }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "发送消息(按 message_type/群号 自动路由群聊或私聊)", + "params": { + "message_type": "group", + "group_id": 20001, + "message": "你好" + }, + "notes": "必须提供对应目标;私聊使用 message_type=private 和 user_id。" + } + ], "readOnly": false, "params": [ { @@ -10002,6 +13955,29 @@ ".send_packet" ], "summary": "发送原始 SSO 包(cmd + hex data)", + "returnsSchema": { + "anyOf": [ + { + "type": "string", + "description": "rsp=true 时返回的十六进制响应正文" + }, + { + "type": "null", + "description": "rsp=false 时不等待响应,data 为 null" + } + ] + }, + "examples": [ + { + "summary": "发送原始 SSO 包(cmd + hex data)", + "params": { + "cmd": "", + "data": "", + "rsp": true + }, + "notes": "仅供有协议经验的调用方:cmd 和 data 必须成对替换为已验证的命令与十六进制请求;示例占位符不可直接发送。" + } + ], "readOnly": false, "params": [ { @@ -10060,6 +14036,20 @@ "name": "send_poke", "aliases": [], "summary": "拍一拍(群聊/私聊自动路由)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "拍一拍(群聊/私聊自动路由)", + "params": { + "group_id": 20001, + "user_id": 10001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": false, "params": [ { @@ -10110,6 +14100,57 @@ "aliases": [], "summary": "发送私聊合并转发", "returns": "{ message_id, res_id, forward_id }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "res_id": { + "type": "string" + }, + "forward_id": { + "type": "string" + } + }, + "required": [ + "message_id", + "res_id", + "forward_id" + ] + }, + "examples": [ + { + "summary": "发送私聊合并转发", + "params": { + "user_id": 10001, + "messages": [ + { + "type": "node", + "data": { + "user_id": 10001, + "nickname": "示例用户", + "content": [ + { + "type": "text", + "data": { + "text": "转发内容" + } + } + ] + } + } + ] + }, + "notes": "替换目标好友 QQ 号。" + } + ], "readOnly": false, "params": [ { @@ -10209,6 +14250,33 @@ "aliases": [], "summary": "发送私聊消息", "returns": "{ message_id: number }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "发送私聊消息", + "params": { + "user_id": 10001, + "message": "你好" + }, + "notes": "替换目标好友 QQ 号;临时会话需另传来源 group_id。" + } + ], "readOnly": false, "params": [ { @@ -10281,6 +14349,31 @@ "name": "send_qzone_msg", "aliases": [], "summary": "发表说说(QQ 空间,支持纯文字或带图;传 images 自动上传;可设置查看权限)", + "returnsSchema": { + "type": "object", + "properties": { + "tid": { + "type": "string" + }, + "time": { + "type": "integer" + } + }, + "required": [ + "tid", + "time" + ] + }, + "examples": [ + { + "summary": "发表说说(QQ 空间,支持纯文字或带图;传 images 自动上传;可设置查看权限)", + "params": { + "content": "今天的学习记录", + "ugc_right": 64 + }, + "notes": "会发布说说;ugc_right=64 仅自己可见。16/128 还必须提供 target_uins。" + } + ], "readOnly": false, "params": [ { @@ -10381,6 +14474,19 @@ "returnsSchema": { "type": "null" }, + "examples": [ + { + "summary": "发送图文 Ark 卡片(私聊/群聊)", + "params": { + "group_id": 20001, + "title": "学习资料", + "desc": "本周课程资料", + "jump_url": "https://example.com/course", + "preview_url": "https://example.com/cover.png" + }, + "notes": "替换目标群号及实际链接;会发送分享卡片。" + } + ], "readOnly": false, "params": [ { @@ -10491,6 +14597,20 @@ "name": "set_diy_online_status", "aliases": [], "summary": "设置自定义在线状态", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置自定义在线状态", + "params": { + "face_id": 14, + "face_type": 1, + "wording": "学习中" + } + } + ], "readOnly": false, "params": [ { @@ -10553,6 +14673,20 @@ "name": "set_doubt_friends_add_request", "aliases": [], "summary": "处理可疑好友申请", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "处理可疑好友申请", + "params": { + "flag": "", + "approve": true + }, + "notes": "flag 使用 get_doubt_friends_add_request 返回项的 uid。" + } + ], "readOnly": false, "params": [ { @@ -10598,6 +14732,19 @@ "name": "set_essence_msg", "aliases": [], "summary": "设置精华消息", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置精华消息", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -10636,6 +14783,20 @@ "name": "set_friend_add_request", "aliases": [], "summary": "处理好友添加请求", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "处理好友添加请求", + "params": { + "flag": "", + "approve": true + }, + "notes": "flag 必须原样使用当前账号好友请求事件的 flag,不能根据 QQ 号构造。" + } + ], "readOnly": false, "params": [ { @@ -10681,6 +14842,19 @@ "name": "set_friend_remark", "aliases": [], "summary": "设置好友备注", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置好友备注", + "params": { + "user_id": 10001, + "remark": "同学" + } + } + ], "readOnly": false, "params": [ { @@ -10728,6 +14902,20 @@ "aliases": [], "summary": "移动好友到指定分组", "returns": "成功时返回空数据。", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "移动好友到指定分组", + "params": { + "uin": 10001, + "categoryName": "同学" + }, + "notes": "categoryId 与 categoryName 二选一。" + } + ], "readOnly": false, "params": [ { @@ -10796,6 +14984,22 @@ "name": "set_group_add_option", "aliases": [], "summary": "设置加群选项", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置加群选项", + "params": { + "group_id": 20001, + "add_type": 2, + "group_question": "入群用途?", + "group_answer": "学习交流" + }, + "notes": "修改加群设置前先用 get_group_admin_settings 查看当前状态,并确认目标设置值。" + } + ], "readOnly": false, "params": [ { @@ -10869,6 +15073,21 @@ "name": "set_group_add_request", "aliases": [], "summary": "处理加群请求", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "处理加群请求", + "params": { + "flag": "", + "sub_type": "add", + "approve": true + }, + "notes": "flag 和 sub_type 原样取自同一条群请求事件;邀请事件使用 invite。" + } + ], "readOnly": false, "params": [ { @@ -10949,6 +15168,21 @@ "name": "set_group_admin", "aliases": [], "summary": "设置/取消管理员", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置/取消管理员", + "params": { + "group_id": 20001, + "user_id": 10001, + "enable": true + }, + "notes": "需要群主权限;会授予该成员管理员身份。" + } + ], "readOnly": false, "params": [ { @@ -11015,6 +15249,34 @@ { "name": "set_group_album_media_like", "aliases": [], + "returnsSchema": { + "type": "object", + "properties": { + "id": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": [ + "integer", + "null" + ] + } + } + }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001, + "album_id": "", + "batch_id": "1700000000" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 batch_id 必须是实际批次的十进制字符串。" + } + ], "readOnly": false, "params": [ { @@ -11090,6 +15352,17 @@ "name": "set_group_anonymous", "aliases": [], "summary": "匿名开关(未实现,返回 ok)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "匿名开关(未实现,返回 ok)", + "params": {}, + "notes": "兼容占位接口:当前返回成功但不会修改匿名设置。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -11104,6 +15377,17 @@ "name": "set_group_anonymous_ban", "aliases": [], "summary": "匿名禁言(未实现,返回 ok)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "匿名禁言(未实现,返回 ok)", + "params": {}, + "notes": "兼容占位接口:当前返回成功但不会修改匿名设置。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -11118,6 +15402,21 @@ "name": "set_group_ban", "aliases": [], "summary": "禁言群成员(duration=0 解除)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "禁言群成员(duration=0 解除)", + "params": { + "group_id": 20001, + "user_id": 10001, + "duration": 600 + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。 duration 为秒,0 表示解除禁言。" + } + ], "readOnly": false, "params": [ { @@ -11189,6 +15488,21 @@ "name": "set_group_card", "aliases": [], "summary": "设置群名片(空字符串清除)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群名片(空字符串清除)", + "params": { + "group_id": 20001, + "user_id": 10001, + "card": "示例群名片" + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11256,6 +15570,21 @@ "name": "set_group_kick", "aliases": [], "summary": "踢出群成员", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "踢出群成员", + "params": { + "group_id": 20001, + "user_id": 10001, + "reject_add_request": false + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11323,6 +15652,24 @@ "name": "set_group_kick_members", "aliases": [], "summary": "批量踢出群成员", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "批量踢出群成员", + "params": { + "group_id": 20001, + "user_id": [ + 10001, + 10002 + ], + "reject_add_request": false + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11396,6 +15743,19 @@ "name": "set_group_leave", "aliases": [], "summary": "退群", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "退群", + "params": { + "group_id": 20001 + }, + "notes": "会退出该群,请确认目标群号后调用。" + } + ], "readOnly": false, "params": [ { @@ -11432,6 +15792,20 @@ "name": "set_group_member_invite_policy", "aliases": [], "summary": "设置群成员邀请策略", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群成员邀请策略", + "params": { + "group_id": 20001, + "policy": "require_approval" + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11496,6 +15870,20 @@ "name": "set_group_member_permissions", "aliases": [], "summary": "设置群成员权限(仅群主可改)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群成员权限(仅群主可改)", + "params": { + "group_id": 20001, + "allow_member_upload_album": false + }, + "notes": "至少传一个权限字段;仅修改传入的权限,其余保持原样。" + } + ], "readOnly": false, "params": [ { @@ -11567,6 +15955,20 @@ "name": "set_group_name", "aliases": [], "summary": "设置群名", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群名", + "params": { + "group_id": 20001, + "group_name": "学习交流群" + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11616,6 +16018,20 @@ "name": "set_group_new_member_history_visibility", "aliases": [], "summary": "设置新成员是否可查看历史消息", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置新成员是否可查看历史消息", + "params": { + "group_id": 20001, + "visible": true + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11664,6 +16080,20 @@ "name": "set_group_portrait", "aliases": [], "summary": "设置群头像", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群头像", + "params": { + "group_id": 20001, + "file": "file:///path/to/avatar.png" + }, + "notes": "示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。" + } + ], "readOnly": false, "params": [ { @@ -11716,6 +16146,22 @@ "name": "set_group_reaction", "aliases": [], "summary": "群聊表情回应", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "群聊表情回应", + "params": { + "message_id": -123456, + "group_id": 20001, + "code": "128077", + "is_set": true + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -11796,6 +16242,20 @@ "name": "set_group_remark", "aliases": [], "summary": "设置群备注", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群备注", + "params": { + "group_id": 20001, + "remark": "学习小组" + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": false, "params": [ { @@ -11842,6 +16302,21 @@ "name": "set_group_robot_add_option", "aliases": [], "summary": "设置群机器人加群选项", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群机器人加群选项", + "params": { + "group_id": 20001, + "robot_member_switch": 1, + "robot_member_examine": 1 + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11904,6 +16379,21 @@ "name": "set_group_search", "aliases": [], "summary": "设置群被搜索方式(群指纹 / 群号搜索开关)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群被搜索方式(群指纹 / 群号搜索开关)", + "params": { + "group_id": 20001, + "no_finger_open": 0, + "no_code_finger_open": 0 + }, + "notes": "替换群号;数值含义以当前群设置为准。" + } + ], "readOnly": false, "params": [ { @@ -11968,6 +16458,21 @@ "send_group_sign" ], "summary": "群签到", + "returnsSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "description": "成功时为空对象 {},不是 null" + }, + "examples": [ + { + "summary": "群签到", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": false, "params": [ { @@ -12004,6 +16509,21 @@ "name": "set_group_special_title", "aliases": [], "summary": "设置群头衔", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群头衔", + "params": { + "group_id": 20001, + "user_id": 10001, + "special_title": "学习委员" + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -12071,6 +16591,20 @@ "name": "set_group_todo", "aliases": [], "summary": "设置群待办", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群待办", + "params": { + "group_id": 20001, + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。 必须是该群的消息。" + } + ], "readOnly": false, "params": [ { @@ -12127,6 +16661,20 @@ "name": "set_group_whole_ban", "aliases": [], "summary": "全员禁言开关", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "全员禁言开关", + "params": { + "group_id": 20001, + "enable": true + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -12176,6 +16724,21 @@ "name": "set_input_status", "aliases": [], "summary": "设置输入状态", + "returnsSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "description": "成功时为空对象 {},不是 null" + }, + "examples": [ + { + "summary": "设置输入状态", + "params": { + "user_id": 10001, + "event_type": 0 + } + } + ], "readOnly": false, "params": [ { @@ -12223,6 +16786,21 @@ "name": "set_msg_emoji_like", "aliases": [], "summary": "设置消息表情回应", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置消息表情回应", + "params": { + "message_id": -123456, + "emoji_id": "128077", + "set": true + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -12288,6 +16866,21 @@ "name": "set_online_status", "aliases": [], "summary": "设置在线状态", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置在线状态", + "params": { + "status": 11, + "ext_status": 0, + "battery_status": 100 + }, + "notes": "修改当前账号状态;具体状态码须与目标状态对应。" + } + ], "readOnly": false, "params": [ { @@ -12354,6 +16947,19 @@ "name": "set_qq_avatar", "aliases": [], "summary": "设置 QQ 头像", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置 QQ 头像", + "params": { + "file": "file:///path/to/avatar.png" + }, + "notes": "示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。" + } + ], "readOnly": false, "params": [ { @@ -12388,6 +16994,19 @@ "name": "set_qq_profile", "aliases": [], "summary": "设置 QQ 资料", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置 QQ 资料", + "params": { + "nickname": "示例昵称" + }, + "notes": "会修改当前账号的资料;只传入需要修改的字段。" + } + ], "readOnly": false, "params": [ { @@ -12443,6 +17062,20 @@ "name": "set_qzone_ban", "aliases": [], "summary": "拉黑或解除拉黑某人(修改机器人自身 QQ 空间黑名单;enable=true 拉黑,false 解除)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "拉黑或解除拉黑某人(修改机器人自身 QQ 空间黑名单;enable=true 拉黑,false 解除)", + "params": { + "user_id": 10001, + "enable": true + }, + "notes": "修改当前账号空间黑名单。" + } + ], "readOnly": false, "params": [ { @@ -12507,6 +17140,19 @@ "ugc_right" ] }, + "examples": [ + { + "summary": "修改一条已发说说的查看权限(QQ 空间,按 tid)", + "params": { + "tid": "", + "ugc_right": 16, + "target_uins": [ + 10001 + ] + }, + "notes": "tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。 16 表示仅所选好友可见。" + } + ], "readOnly": false, "params": [ { @@ -12581,6 +17227,17 @@ "name": "set_restart", "aliases": [], "summary": "重启(不支持)", + "returnsSchema": { + "not": {}, + "description": "当前始终返回失败,没有成功 data;失败响应 data 为 null,原因见 wording" + }, + "examples": [ + { + "summary": "重启(不支持)", + "params": {}, + "notes": "当前未实现,调用返回失败;不要将其当作可用能力。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -12595,6 +17252,20 @@ "name": "set_self_longnick", "aliases": [], "summary": "设置个性签名(longNick/long_nick,严格 string)", + "returnsSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "description": "成功时为空对象 {},不是 null" + }, + "examples": [ + { + "summary": "设置个性签名(longNick/long_nick,严格 string)", + "params": { + "long_nick": "学习中" + } + } + ], "readOnly": false, "params": [ { @@ -12638,6 +17309,15 @@ "type": "string", "description": "群 Ark 卡片 JSON 字符串" }, + "examples": [ + { + "summary": "分享群 Ark 卡片", + "params": { + "group_id": 20001 + }, + "notes": "生成群分享卡片;返回卡片数据,不等同于发送到聊天。" + } + ], "readOnly": true, "params": [ { @@ -12685,6 +17365,15 @@ "arkMsg" ] }, + "examples": [ + { + "summary": "分享用户/群 Ark 卡片", + "params": { + "user_id": 10001 + }, + "notes": "生成联系人分享卡片;返回卡片数据,不等同于发送到聊天。" + } + ], "readOnly": true, "params": [ { @@ -12745,6 +17434,58 @@ "aliases": [], "summary": "测试下载流(推送 10 个数据帧,验证流式传输,不触达 QQ)", "returns": "流式帧:data_chunk*10 → data_complete(error=true 时以 error 帧结束)", + "returnsSchema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "data_chunk" + }, + "data": { + "type": "string" + } + }, + "required": [ + "type", + "data_type", + "data" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "response" + }, + "data_type": { + "const": "data_complete" + }, + "data": { + "type": "string" + } + }, + "required": [ + "type", + "data_type", + "data" + ] + } + ], + "description": "成功时 10 个 data_chunk 后接 data_complete;error=true 时最终为失败帧" + }, + "examples": [ + { + "summary": "测试下载流(推送 10 个数据帧,验证流式传输,不触达 QQ)", + "params": { + "error": false + }, + "notes": "经 invoke_action 调用,仅测试传输,不访问 QQ。" + } + ], "readOnly": false, "params": [ { @@ -12778,6 +17519,27 @@ "aliases": [], "summary": "转存群文件", "returns": "{ ok: true }", + "returnsSchema": { + "type": "object", + "properties": { + "ok": { + "const": true + } + }, + "required": [ + "ok" + ] + }, + "examples": [ + { + "summary": "转存群文件", + "params": { + "group_id": 20001, + "file_id": "" + }, + "notes": "替换群文件列表中的实际 file_id;需要对应文件操作权限。" + } + ], "readOnly": false, "params": [ { @@ -12844,6 +17606,16 @@ "words" ] }, + "examples": [ + { + "summary": "英译中", + "params": { + "words": [ + "Hello" + ] + } + } + ], "readOnly": true, "params": [ { @@ -12880,6 +17652,20 @@ "name": "unlike_qzone", "aliases": [], "summary": "取消对一条说说的点赞(QQ 空间;取消赞端点待真机核实)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "取消对一条说说的点赞(QQ 空间;取消赞端点待真机核实)", + "params": { + "tid": "", + "target_uin": 10001 + }, + "notes": "tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。" + } + ], "readOnly": false, "params": [ { @@ -12951,6 +17737,106 @@ "aliases": [], "summary": "以流式分块方式上传文件到机器人本地(返回可用于发送的本地路径)", "returns": "流式帧:分块确认 type=stream、完成 type=response(含 file_path/file_name/file_size/sha256)", + "returnsSchema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "stream_id": { + "type": "string" + }, + "status": { + "enum": [ + "file_created", + "chunk_received" + ] + }, + "received_chunks": { + "type": "integer" + }, + "total_chunks": { + "type": "integer" + } + }, + "required": [ + "type", + "stream_id", + "status", + "received_chunks", + "total_chunks" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "response" + }, + "stream_id": { + "type": "string" + }, + "status": { + "const": "file_complete" + }, + "received_chunks": { + "type": "integer" + }, + "total_chunks": { + "type": "integer" + }, + "file_path": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "file_size": { + "type": "integer" + }, + "sha256": { + "type": "string" + } + }, + "required": [ + "type", + "stream_id", + "status", + "received_chunks", + "total_chunks", + "file_path", + "file_name", + "file_size", + "sha256" + ] + } + ], + "description": "分块确认与完成帧的 data;reset 和其他失败由错误帧返回。必须等到 file_complete 才能使用 file_path" + }, + "examples": [ + { + "summary": "上传分块", + "params": { + "stream_id": "example-upload-001", + "filename": "hello.txt", + "total_chunks": 1, + "file_size": 5, + "chunk_index": 0, + "chunk_data": "aGVsbG8=" + }, + "notes": "第 1 步:每次上传生成新的 stream_id;这里上传 hello 的唯一分块。通过 invoke_action 调用,分块确认不等于整文件完成。" + }, + { + "summary": "完成同一上传", + "params": { + "stream_id": "example-upload-001", + "is_complete": true + }, + "notes": "第 2 步:等待全部分块确认后,用同一个 stream_id 单独请求完成;成功时返回 file_path。MCP 的 invoke_action 也可通过 execution.input_file 自动完成上传。" + } + ], "readOnly": false, "params": [ { @@ -13126,6 +18012,56 @@ "name": "upload_forward_msg", "aliases": [], "summary": "上传转发消息", + "returnsSchema": { + "type": "object", + "properties": { + "res_id": { + "type": "string" + }, + "forward_id": { + "type": "string" + }, + "message_id": { + "const": 0, + "description": "仅上传资源,不代表已发送消息;0 不能用于消息查询或撤回" + }, + "group_id": { + "type": "integer", + "description": "仅请求提供正数 group_id 时出现" + } + }, + "required": [ + "res_id", + "forward_id", + "message_id" + ] + }, + "examples": [ + { + "summary": "上传转发消息", + "params": { + "group_id": 20001, + "messages": [ + { + "type": "node", + "data": { + "user_id": 10001, + "nickname": "示例用户", + "content": [ + { + "type": "text", + "data": { + "text": "转发内容" + } + } + ] + } + } + ] + }, + "notes": "仅上传合并转发资源,不发送聊天消息;message_id=0 是占位值,后续使用 res_id。" + } + ], "readOnly": false, "params": [ { @@ -13181,6 +18117,51 @@ "name": "upload_foward_msg", "aliases": [], "summary": "上传转发消息(别名拼写)", + "returnsSchema": { + "type": "object", + "properties": { + "res_id": { + "type": "string" + }, + "forward_id": { + "type": "string" + }, + "message_id": { + "const": 0, + "description": "仅上传资源,不代表已发送消息;0 不能用于消息查询或撤回" + } + }, + "required": [ + "res_id", + "forward_id", + "message_id" + ] + }, + "examples": [ + { + "summary": "上传转发消息(别名拼写)", + "params": { + "messages": [ + { + "type": "node", + "data": { + "user_id": 10001, + "nickname": "示例用户", + "content": [ + { + "type": "text", + "data": { + "text": "转发内容" + } + } + ] + } + } + ] + }, + "notes": "历史拼写兼容接口,仅上传资源;message_id=0,返回值没有 group_id。" + } + ], "readOnly": false, "params": [ { @@ -13237,6 +18218,28 @@ "aliases": [], "summary": "上传群文件", "returns": "{ file_id: string }", + "returnsSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string" + } + }, + "required": [ + "file_id" + ] + }, + "examples": [ + { + "summary": "上传群文件", + "params": { + "group_id": 20001, + "file": "file:///path/to/report.pdf", + "name": "报告.pdf" + }, + "notes": "示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。" + } + ], "readOnly": false, "params": [ { @@ -13340,6 +18343,22 @@ { "name": "upload_image_to_qun_album", "aliases": [], + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001, + "album_id": "", + "album_name": "活动照片", + "file": "file:///path/to/photo.png" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。" + } + ], "readOnly": false, "params": [ { @@ -13421,6 +18440,28 @@ "aliases": [], "summary": "上传私聊文件", "returns": "{ file_id: string }", + "returnsSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string" + } + }, + "required": [ + "file_id" + ] + }, + "examples": [ + { + "summary": "上传私聊文件", + "params": { + "user_id": 10001, + "file": "file:///path/to/report.pdf", + "name": "报告.pdf" + }, + "notes": "示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。 默认上传并发送给该好友。" + } + ], "readOnly": false, "params": [ { @@ -13510,6 +18551,18 @@ "id" ] }, + "examples": [ + { + "summary": "上传视频到群相册", + "params": { + "group_id": 20001, + "album_id": "", + "album_name": "活动视频", + "file": "file:///path/to/video.mp4" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。" + } + ], "readOnly": false, "params": [ { diff --git a/packages/mcp/src/generated/catalog.ts b/packages/mcp/src/generated/catalog.ts index 70e607d2..85af012b 100644 --- a/packages/mcp/src/generated/catalog.ts +++ b/packages/mcp/src/generated/catalog.ts @@ -7,6 +7,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "_del_group_notice", "aliases": [], "summary": "删除群公告(fid 或 notice_id 二选一)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除群公告(fid 或 notice_id 二选一)", + "params": { + "group_id": 20001, + "notice_id": "" + }, + "notes": "notice_id 来自 _get_group_notice;删除需要管理权限。" + } + ], "readOnly": false, "params": [ { @@ -66,6 +80,73 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "获取指定 QQ 号正在使用的个性装扮(挂件/名片/来电/输入状态等)", "returns": "装扮信息;目标未使用任何可查询装扮时 items 为空数组。网络失败、未登录态/风控、页面改版、返回账号与请求不一致时返回失败并附具体原因", + "returnsSchema": { + "type": "object", + "properties": { + "target_uin": { + "type": "string" + }, + "is_svip": { + "type": "boolean" + }, + "avatar_url": { + "type": "string" + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "app_id": { + "type": "integer" + }, + "kind": { + "type": "string" + }, + "item_id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "preview_url": { + "type": "string" + }, + "video_url": { + "type": "string" + }, + "price": { + "type": "number" + } + }, + "required": [ + "app_id", + "kind", + "item_id", + "name", + "preview_url", + "video_url", + "price" + ] + } + } + }, + "required": [ + "target_uin", + "is_svip", + "avatar_url", + "items" + ] + }, + "examples": [ + { + "summary": "获取指定 QQ 号正在使用的个性装扮(挂件/名片/来电/输入状态等)", + "params": { + "user_id": 10001 + }, + "notes": "替换为需要查询的 QQ 号。" + } + ], "readOnly": true, "params": [ { @@ -103,6 +184,124 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "获取群公告", "returns": "普通公告与新成员公告的合并数组;send_to_new_members 标识后者;图片含 id、url、宽高", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "notice_id": { + "type": "string" + }, + "sender_id": { + "type": "number" + }, + "publish_time": { + "type": "number" + }, + "message": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "image": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "url": { + "type": "string" + }, + "height": { + "type": "number" + }, + "width": { + "type": "number" + } + }, + "required": [ + "id", + "url", + "height", + "width" + ] + } + }, + "images": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "url": { + "type": "string" + }, + "height": { + "type": "number" + }, + "width": { + "type": "number" + } + }, + "required": [ + "id", + "url", + "height", + "width" + ] + }, + "description": "与 image 相同的图片列表,保留两个兼容字段" + } + }, + "required": [ + "text", + "image", + "images" + ] + }, + "settings": { + "description": "服务端公告设置,原样透传,可为 null" + }, + "read_num": { + "type": "number" + }, + "type": { + "type": "number" + }, + "pinned": { + "type": "number" + }, + "send_to_new_members": { + "type": "boolean" + } + }, + "required": [ + "notice_id", + "sender_id", + "publish_time", + "message", + "settings", + "read_num", + "type", + "pinned", + "send_to_new_members" + ] + } + }, + "examples": [ + { + "summary": "获取群公告", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -168,6 +367,15 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取机型展示(兼容 mock)", + "params": { + "model": "示例机型" + }, + "notes": "兼容占位接口,仅将传入的 model(或默认值)放进展示结构,不查询设备机型。" + } + ], "readOnly": true, "params": [ { @@ -197,6 +405,17 @@ export const ACTIONS: CatalogAction[] = [ "name": "_mark_all_as_read", "aliases": [], "summary": "标记全部已读", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "标记全部已读", + "params": {}, + "notes": "会将当前账号的会话标记为已读。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -211,6 +430,22 @@ export const ACTIONS: CatalogAction[] = [ "name": "_send_group_notice", "aliases": [], "summary": "发送群公告(支持置顶、弹窗、新成员、群昵称引导与确认)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "发送群公告(支持置顶、弹窗、新成员、群昵称引导与确认)", + "params": { + "group_id": 20001, + "content": "欢迎参加本周讨论", + "type": 1, + "pinned": 0 + }, + "notes": "需要群公告管理权限;普通公告 type=1,新成员公告 type=20。" + } + ], "readOnly": false, "params": [ { @@ -385,6 +620,17 @@ export const ACTIONS: CatalogAction[] = [ "name": "_set_model_show", "aliases": [], "summary": "设置机型展示(占位)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置机型展示(占位)", + "params": {}, + "notes": "兼容占位接口,当前返回成功但不修改机型展示。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -399,6 +645,17 @@ export const ACTIONS: CatalogAction[] = [ "name": ".get_word_slices", "aliases": [], "summary": "分词(未实现)", + "returnsSchema": { + "not": {}, + "description": "当前始终返回失败,没有成功 data;失败响应 data 为 null,原因见 wording" + }, + "examples": [ + { + "summary": "分词(未实现)", + "params": {}, + "notes": "当前未实现,调用返回失败;不要将其当作可用能力。" + } + ], "readOnly": true, "params": [], "invariants": [], @@ -413,6 +670,26 @@ export const ACTIONS: CatalogAction[] = [ "name": "add_custom_face", "aliases": [], "summary": "添加收藏表情", + "returnsSchema": { + "type": "object", + "properties": { + "emoji_id": { + "type": "string" + } + }, + "required": [ + "emoji_id" + ] + }, + "examples": [ + { + "summary": "添加收藏表情", + "params": { + "file": "file:///path/to/emoji.png" + }, + "notes": "示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。" + } + ], "readOnly": false, "params": [ { @@ -447,6 +724,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "add_group_tag", "aliases": [], "summary": "向某个群分类添加资料标签", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "向某个群分类添加资料标签", + "params": { + "group_class": 1, + "name": "学习" + }, + "notes": "替换实际群分类和标签;需要当前账号有管理权限。" + } + ], "readOnly": false, "params": [ { @@ -495,6 +786,17 @@ export const ACTIONS: CatalogAction[] = [ "name": "bot_exit", "aliases": [], "summary": "退出机器人", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "退出机器人", + "params": {}, + "notes": "会退出 SnowLuma 进程,仅在明确需要停止服务时调用。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -521,6 +823,12 @@ export const ACTIONS: CatalogAction[] = [ "yes" ] }, + "examples": [ + { + "summary": "基本调用", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -547,6 +855,12 @@ export const ACTIONS: CatalogAction[] = [ "yes" ] }, + "examples": [ + { + "summary": "基本调用", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -560,6 +874,34 @@ export const ACTIONS: CatalogAction[] = [ { "name": "cancel_group_album_media_like", "aliases": [], + "returnsSchema": { + "type": "object", + "properties": { + "id": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": [ + "integer", + "null" + ] + } + } + }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001, + "album_id": "", + "batch_id": "1700000000" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 batch_id 必须是实际批次的十进制字符串。" + } + ], "readOnly": false, "params": [ { @@ -635,6 +977,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "cancel_group_todo", "aliases": [], "summary": "取消群待办", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "取消群待办", + "params": { + "group_id": 20001, + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。 必须是该群的消息。" + } + ], "readOnly": false, "params": [ { @@ -704,6 +1060,13 @@ export const ACTIONS: CatalogAction[] = [ "level" ] }, + "examples": [ + { + "summary": "检查链接安全性", + "params": {}, + "notes": "兼容占位接口,当前固定返回 level=1,不能用来证明某个链接安全。" + } + ], "readOnly": true, "params": [], "invariants": [], @@ -718,6 +1081,17 @@ export const ACTIONS: CatalogAction[] = [ "name": "clean_cache", "aliases": [], "summary": "清理缓存", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "清理缓存", + "params": {}, + "notes": "兼容占位接口,当前返回成功但不执行清理。流式临时文件清理请使用 clean_stream_temp_file。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -733,6 +1107,36 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "清理流式传输临时文件(仅清理 stream 上传/下载目录)", "returns": "{ message, removed }", + "returnsSchema": { + "type": "object", + "properties": { + "message": { + "const": "success" + }, + "removed": { + "type": "integer" + }, + "freed_bytes": { + "type": "integer" + }, + "skipped_active": { + "type": "integer" + } + }, + "required": [ + "message", + "removed", + "freed_bytes", + "skipped_active" + ] + }, + "examples": [ + { + "summary": "清理流式传输临时文件(仅清理 stream 上传/下载目录)", + "params": {}, + "notes": "清理未使用的流式临时文件;不会清理正在传输的项目。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -747,6 +1151,51 @@ export const ACTIONS: CatalogAction[] = [ "name": "click_inline_keyboard_button", "aliases": [], "summary": "点击内联键盘按钮", + "returnsSchema": { + "type": "object", + "properties": { + "result": { + "type": "number", + "description": "按钮业务结果码,需单独检查" + }, + "errMsg": { + "type": "string" + }, + "status": { + "const": 0 + }, + "promptText": { + "type": "string" + }, + "promptType": { + "const": 0 + }, + "promptIcon": { + "const": 0 + } + }, + "required": [ + "result", + "errMsg", + "status", + "promptText", + "promptType", + "promptIcon" + ] + }, + "examples": [ + { + "summary": "点击内联键盘按钮", + "params": { + "group_id": 20001, + "bot_appid": 10001, + "msg_seq": 42, + "button_id": "", + "callback_data": "" + }, + "notes": "参数必须来自同一条机器人按钮消息;msg_seq 是消息序号,不是 message_id。" + } + ], "readOnly": false, "params": [ { @@ -838,6 +1287,29 @@ export const ACTIONS: CatalogAction[] = [ "name": "comment_qzone", "aliases": [], "summary": "评论一条说说(QQ 空间,支持纯文字或带图;传 images 自动上传)", + "returnsSchema": { + "type": "object", + "properties": { + "comment_id": { + "type": "string", + "description": "服务端未提供评论 ID 时为空串" + } + }, + "required": [ + "comment_id" + ] + }, + "examples": [ + { + "summary": "评论一条说说(QQ 空间,支持纯文字或带图;传 images 自动上传)", + "params": { + "tid": "", + "target_uin": 10001, + "content": "感谢分享" + }, + "notes": "tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。" + } + ], "readOnly": false, "params": [ { @@ -926,6 +1398,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "complete_group_todo", "aliases": [], "summary": "完成群待办", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "完成群待办", + "params": { + "group_id": 20001, + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。 必须是该群的消息。" + } + ], "readOnly": false, "params": [ { @@ -982,6 +1468,17 @@ export const ACTIONS: CatalogAction[] = [ "name": "create_collection", "aliases": [], "summary": "创建收藏(未实现)", + "returnsSchema": { + "not": {}, + "description": "当前始终返回失败,没有成功 data;失败响应 data 为 null,原因见 wording" + }, + "examples": [ + { + "summary": "创建收藏(未实现)", + "params": {}, + "notes": "当前未实现,调用返回失败;不要将其当作可用能力。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -996,6 +1493,37 @@ export const ACTIONS: CatalogAction[] = [ "name": "create_flash_task", "aliases": [], "summary": "创建闪传任务", + "returnsSchema": { + "type": "object", + "properties": { + "fileset_id": { + "type": "string" + }, + "task_id": { + "type": "string", + "description": "与 fileset_id 相同的兼容字段" + } + }, + "required": [ + "fileset_id", + "task_id" + ] + }, + "examples": [ + { + "summary": "创建闪传任务", + "params": { + "files": [ + { + "file": "file:///path/to/report.pdf", + "name": "报告.pdf" + } + ], + "name": "资料包" + }, + "notes": "示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。 files 不能为空。" + } + ], "readOnly": false, "params": [ { @@ -1045,6 +1573,84 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "创建群文件夹", "returns": "{ result: { retCode, retMsg }, groupItem: { folderInfo: { folderId, folderName, folderPath, createTime, modifyTime, createUin, modifyUin } } }", + "returnsSchema": { + "type": "object", + "properties": { + "result": { + "type": "object", + "properties": { + "retCode": { + "const": 0 + }, + "retMsg": { + "const": "success" + } + }, + "required": [ + "retCode", + "retMsg" + ] + }, + "groupItem": { + "type": "object", + "properties": { + "folderInfo": { + "type": "object", + "properties": { + "folderId": { + "type": "string" + }, + "folderName": { + "type": "string" + }, + "folderPath": { + "type": "string" + }, + "createTime": { + "type": "integer" + }, + "modifyTime": { + "type": "integer" + }, + "createUin": { + "type": "integer" + }, + "modifyUin": { + "type": "integer" + } + }, + "required": [ + "folderId", + "folderName", + "folderPath", + "createTime", + "modifyTime", + "createUin", + "modifyUin" + ] + } + }, + "required": [ + "folderInfo" + ] + } + }, + "required": [ + "result", + "groupItem" + ] + }, + "examples": [ + { + "summary": "创建群文件夹", + "params": { + "group_id": 20001, + "name": "共享资料", + "parent_id": "/" + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": false, "params": [ { @@ -1108,6 +1714,28 @@ export const ACTIONS: CatalogAction[] = [ "name": "del_group_album_media", "aliases": [], "summary": "删除群相册图片或视频", + "returnsSchema": { + "type": "object", + "properties": { + "success": { + "const": true + } + }, + "required": [ + "success" + ] + }, + "examples": [ + { + "summary": "删除群相册图片或视频", + "params": { + "group_id": 20001, + "album_id": "", + "lloc": "" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。" + } + ], "readOnly": false, "params": [ { @@ -1174,6 +1802,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "delete_custom_face", "aliases": [], "summary": "删除收藏表情", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除收藏表情", + "params": { + "emoji_id": "" + }, + "notes": "emoji_id 来自 fetch_custom_face_detail。" + } + ], "readOnly": false, "params": [ { @@ -1206,6 +1847,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "delete_essence_msg", "aliases": [], "summary": "移除精华消息", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "移除精华消息", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -1244,6 +1898,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "delete_flash_file", "aliases": [], "summary": "删除闪传文件", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除闪传文件", + "params": { + "fileset_id": "" + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。" + } + ], "readOnly": false, "params": [ { @@ -1276,6 +1943,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "delete_friend", "aliases": [], "summary": "删除好友", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除好友", + "params": { + "user_id": 10001, + "block": false + }, + "notes": "会删除指定好友;仅在明确需要删除时调用。" + } + ], "readOnly": false, "params": [ { @@ -1325,6 +2006,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "delete_group_file", "aliases": [], "summary": "删除群文件", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除群文件", + "params": { + "group_id": 20001, + "file_id": "" + }, + "notes": "替换群文件列表中的实际 file_id;需要对应文件操作权限。" + } + ], "readOnly": false, "params": [ { @@ -1377,6 +2072,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "delete_group_file_folder", "aliases": [], "summary": "删除群文件夹", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除群文件夹", + "params": { + "group_id": 20001, + "folder_id": "" + }, + "notes": "会删除指定群文件夹,确认资源后调用。" + } + ], "readOnly": false, "params": [ { @@ -1427,6 +2136,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "delete_group_folder", "aliases": [], "summary": "删除群文件夹", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除群文件夹", + "params": { + "group_id": 20001, + "folder_id": "" + }, + "notes": "会删除指定群文件夹,确认资源后调用。" + } + ], "readOnly": false, "params": [ { @@ -1477,6 +2200,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "delete_group_tag", "aliases": [], "summary": "从某个群分类移除资料标签", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "从某个群分类移除资料标签", + "params": { + "group_class": 1, + "name": "学习" + }, + "notes": "替换实际群分类和标签;需要当前账号有管理权限。" + } + ], "readOnly": false, "params": [ { @@ -1525,6 +2262,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "delete_msg", "aliases": [], "summary": "撤回消息", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "撤回消息", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -1563,6 +2313,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "delete_qzone_msg", "aliases": [], "summary": "删除一条说说(QQ 空间,按 tid)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "删除一条说说(QQ 空间,按 tid)", + "params": { + "tid": "" + }, + "notes": "tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。" + } + ], "readOnly": false, "params": [ { @@ -1596,6 +2359,69 @@ export const ACTIONS: CatalogAction[] = [ { "name": "do_group_album_comment", "aliases": [], + "returnsSchema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "user": { + "type": "object", + "properties": { + "uin": { + "type": "string" + } + }, + "required": [ + "uin" + ] + }, + "content": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "integer" + }, + "content": { + "type": "string" + } + }, + "required": [ + "type", + "content" + ] + } + }, + "time": { + "type": "string", + "description": "十进制时间字符串" + }, + "clientKey": { + "type": "string" + } + }, + "required": [ + "id", + "user", + "content", + "time", + "clientKey" + ] + }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001, + "album_id": "", + "lloc": "", + "content": "拍得很好" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。" + } + ], "readOnly": false, "params": [ { @@ -1675,6 +2501,28 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "下载文件(url 或 base64)到 data/downloads", "returns": "{ file }", + "returnsSchema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "description": "保存到 SnowLuma 服务端的本地文件路径" + } + }, + "required": [ + "file" + ] + }, + "examples": [ + { + "summary": "下载文件(url 或 base64)到 data/downloads", + "params": { + "base64": "aGVsbG8=", + "name": "hello.txt" + }, + "notes": "保存到 SnowLuma 服务端临时目录,返回服务端路径;不是 MCP 主机下载路径。" + } + ], "readOnly": false, "params": [ { @@ -1738,6 +2586,118 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "以流式方式下载图片(缓存图片 id / URL / stream 目录本地文件)", "returns": "流式帧:file_info → file_chunk* → file_complete", + "returnsSchema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "file_info" + }, + "file_name": { + "type": "string" + }, + "file_size": { + "type": "integer", + "description": "字节数,0 可表示未知" + }, + "chunk_size": { + "type": "integer" + } + }, + "required": [ + "type", + "data_type", + "file_name", + "file_size", + "chunk_size" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "file_chunk" + }, + "index": { + "type": "integer" + }, + "data": { + "type": "string", + "description": "该分块的 Base64 数据" + }, + "size": { + "type": "integer" + }, + "progress": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "base64_size": { + "type": "integer" + } + }, + "required": [ + "type", + "data_type", + "index", + "data", + "size", + "progress", + "base64_size" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "response" + }, + "data_type": { + "const": "file_complete" + }, + "file_name": { + "type": "string" + }, + "total_chunks": { + "type": "integer" + }, + "total_bytes": { + "type": "integer" + }, + "message": { + "type": "string" + } + }, + "required": [ + "type", + "data_type", + "file_name", + "total_chunks", + "total_bytes", + "message" + ] + } + ], + "description": "每个成功流式帧的 data;失败帧另由 status/retcode/wording 与 type=error 表示,不是 MCP 保存文件后的工具结果" + }, + "examples": [ + { + "summary": "以流式方式下载图片(缓存图片 id / URL / stream 目录本地文件)", + "params": { + "file_id": "", + "chunk_size": 65536 + }, + "notes": "file_id 来自当前账号图片消息;经 invoke_action 调用。" + } + ], "readOnly": false, "params": [ { @@ -1801,6 +2761,118 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "以流式方式下载语音(缓存语音 id / URL / stream 目录本地文件)", "returns": "流式帧:file_info → file_chunk* → file_complete", + "returnsSchema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "file_info" + }, + "file_name": { + "type": "string" + }, + "file_size": { + "type": "integer", + "description": "字节数,0 可表示未知" + }, + "chunk_size": { + "type": "integer" + } + }, + "required": [ + "type", + "data_type", + "file_name", + "file_size", + "chunk_size" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "file_chunk" + }, + "index": { + "type": "integer" + }, + "data": { + "type": "string", + "description": "该分块的 Base64 数据" + }, + "size": { + "type": "integer" + }, + "progress": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "base64_size": { + "type": "integer" + } + }, + "required": [ + "type", + "data_type", + "index", + "data", + "size", + "progress", + "base64_size" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "response" + }, + "data_type": { + "const": "file_complete" + }, + "file_name": { + "type": "string" + }, + "total_chunks": { + "type": "integer" + }, + "total_bytes": { + "type": "integer" + }, + "message": { + "type": "string" + } + }, + "required": [ + "type", + "data_type", + "file_name", + "total_chunks", + "total_bytes", + "message" + ] + } + ], + "description": "每个成功流式帧的 data;失败帧另由 status/retcode/wording 与 type=error 表示,不是 MCP 保存文件后的工具结果" + }, + "examples": [ + { + "summary": "以流式方式下载语音(缓存语音 id / URL / stream 目录本地文件)", + "params": { + "file_id": "", + "chunk_size": 65536 + }, + "notes": "file_id 来自当前账号语音消息;经 invoke_action 调用。" + } + ], "readOnly": false, "params": [ { @@ -1864,6 +2936,118 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "以流式方式下载文件(stream 目录本地文件 / URL / 缓存媒体)", "returns": "流式帧:file_info → file_chunk* → file_complete", + "returnsSchema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "file_info" + }, + "file_name": { + "type": "string" + }, + "file_size": { + "type": "integer", + "description": "字节数,0 可表示未知" + }, + "chunk_size": { + "type": "integer" + } + }, + "required": [ + "type", + "data_type", + "file_name", + "file_size", + "chunk_size" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "file_chunk" + }, + "index": { + "type": "integer" + }, + "data": { + "type": "string", + "description": "该分块的 Base64 数据" + }, + "size": { + "type": "integer" + }, + "progress": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "base64_size": { + "type": "integer" + } + }, + "required": [ + "type", + "data_type", + "index", + "data", + "size", + "progress", + "base64_size" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "response" + }, + "data_type": { + "const": "file_complete" + }, + "file_name": { + "type": "string" + }, + "total_chunks": { + "type": "integer" + }, + "total_bytes": { + "type": "integer" + }, + "message": { + "type": "string" + } + }, + "required": [ + "type", + "data_type", + "file_name", + "total_chunks", + "total_bytes", + "message" + ] + } + ], + "description": "每个成功流式帧的 data;失败帧另由 status/retcode/wording 与 type=error 表示,不是 MCP 保存文件后的工具结果" + }, + "examples": [ + { + "summary": "以流式方式下载文件(stream 目录本地文件 / URL / 缓存媒体)", + "params": { + "file": "https://example.com/report.pdf", + "chunk_size": 65536 + }, + "notes": "替换实际公开可访问地址;经 invoke_action 调用,文件保存到 MCP 下载目录。" + } + ], "readOnly": false, "params": [ { @@ -1927,6 +3111,35 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "解析闪传文件下载直链(不下载,由调用方实现下载)", "returns": "{ url, file_name, file_size }", + "returnsSchema": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "file_size": { + "type": "number" + } + }, + "required": [ + "url", + "file_name", + "file_size" + ] + }, + "examples": [ + { + "summary": "解析闪传文件下载直链(不下载,由调用方实现下载)", + "params": { + "fileset_id": "", + "file_index": 0 + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。 file_index 从 0 开始。" + } + ], "readOnly": false, "params": [ { @@ -1989,6 +3202,15 @@ export const ACTIONS: CatalogAction[] = [ "description": "图片 URL 或 emoji_id(取决于 return_type)" } }, + "examples": [ + { + "summary": "获取自定义表情", + "params": { + "count": 10, + "return_type": "url" + } + } + ], "readOnly": true, "params": [ { @@ -2069,6 +3291,14 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取自定义表情详情", + "params": { + "count": 10 + } + } + ], "readOnly": true, "params": [ { @@ -2160,6 +3390,17 @@ export const ACTIONS: CatalogAction[] = [ "isFirstPage" ] }, + "examples": [ + { + "summary": "获取表情回应用户(NapCat 分页)", + "params": { + "message_id": -123456, + "emojiId": "128077", + "count": 10 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。 后续页原样传回上页 cookie。" + } + ], "readOnly": true, "params": [ { @@ -2330,6 +3571,14 @@ export const ACTIONS: CatalogAction[] = [ } ] }, + "examples": [ + { + "summary": "按编号查询 QQ 系统表情", + "params": { + "face_id": 14 + } + } + ], "readOnly": true, "params": [ { @@ -2397,6 +3646,15 @@ export const ACTIONS: CatalogAction[] = [ "text" ] }, + "examples": [ + { + "summary": "获取语音转文字结果", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": true, "params": [ { @@ -2447,6 +3705,14 @@ export const ACTIONS: CatalogAction[] = [ "is_super" ] }, + "examples": [ + { + "summary": "判断 QQ 系统表情是否使用超级表情格式", + "params": { + "face_id": 14 + } + } + ], "readOnly": true, "params": [ { @@ -2607,6 +3873,14 @@ export const ACTIONS: CatalogAction[] = [ "packs" ] }, + "examples": [ + { + "summary": "获取 QQ 系统表情目录", + "params": { + "refresh": false + } + } + ], "readOnly": true, "params": [ { @@ -2638,6 +3912,33 @@ export const ACTIONS: CatalogAction[] = [ "name": "forward_friend_single_msg", "aliases": [], "summary": "转发单条消息给好友", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "转发单条消息给好友", + "params": { + "message_id": -123456, + "user_id": 10001 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -2692,6 +3993,33 @@ export const ACTIONS: CatalogAction[] = [ "name": "forward_group_single_msg", "aliases": [], "summary": "转发单条消息到群", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "转发单条消息到群", + "params": { + "message_id": -123456, + "group_id": 20001 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -2746,6 +4074,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "friend_poke", "aliases": [], "summary": "好友拍一拍", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "好友拍一拍", + "params": { + "user_id": 10001 + }, + "notes": "戳一戳指定好友。" + } + ], "readOnly": false, "params": [ { @@ -2838,6 +4179,16 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取 AI 语音角色", + "params": { + "group_id": 20001, + "chat_type": 1 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -2889,6 +4240,22 @@ export const ACTIONS: CatalogAction[] = [ "name": "get_ai_record", "aliases": [], "summary": "生成 AI 语音", + "returnsSchema": { + "type": "string", + "description": "生成的下载或分享 URL 字符串,不是含 url 字段的对象" + }, + "examples": [ + { + "summary": "生成 AI 语音", + "params": { + "group_id": 20001, + "character": "", + "text": "你好", + "chat_type": 1 + }, + "notes": "character 原样使用 get_ai_characters 的 character_id;send_group_ai_record 会直接发送语音。" + } + ], "readOnly": false, "params": [ { @@ -2991,6 +4358,13 @@ export const ACTIONS: CatalogAction[] = [ "keyIndex" ] }, + "examples": [ + { + "summary": "获取 clientkey", + "params": {}, + "notes": "返回值可能包含凭据或临时下载密钥,请勿公开或记录到不受控日志。" + } + ], "readOnly": true, "params": [], "invariants": [], @@ -3192,6 +4566,15 @@ export const ACTIONS: CatalogAction[] = [ "collectionSearchList" ] }, + "examples": [ + { + "summary": "获取收藏列表", + "params": { + "category": 0, + "count": 20 + } + } + ], "readOnly": true, "params": [ { @@ -3257,6 +4640,15 @@ export const ACTIONS: CatalogAction[] = [ "cookies" ] }, + "examples": [ + { + "summary": "获取 Cookies", + "params": { + "domain": "qun.qq.com" + }, + "notes": "返回登录凭据,仅在有明确用途时读取,请勿公开。" + } + ], "readOnly": true, "params": [ { @@ -3309,6 +4701,15 @@ export const ACTIONS: CatalogAction[] = [ "csrf_token" ] }, + "examples": [ + { + "summary": "获取凭证", + "params": { + "domain": "qun.qq.com" + }, + "notes": "返回登录凭据,仅在有明确用途时读取,请勿公开。" + } + ], "readOnly": true, "params": [ { @@ -3351,6 +4752,12 @@ export const ACTIONS: CatalogAction[] = [ "token" ] }, + "examples": [ + { + "summary": "获取 CSRF 令牌", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -3416,6 +4823,14 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取可疑好友申请", + "params": { + "count": 20 + } + } + ], "readOnly": true, "params": [ { @@ -3477,6 +4892,16 @@ export const ACTIONS: CatalogAction[] = [ "emoji_like_list" ] }, + "examples": [ + { + "summary": "获取表情回应用户", + "params": { + "message_id": -123456, + "emoji_id": "128077" + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": true, "params": [ { @@ -3529,6 +4954,89 @@ export const ACTIONS: CatalogAction[] = [ "name": "get_essence_msg_list", "aliases": [], "summary": "获取精华消息列表", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "msg_seq": { + "type": "integer" + }, + "msg_random": { + "type": "integer" + }, + "sender_id": { + "type": "integer" + }, + "sender_nick": { + "type": "string" + }, + "sender_time": { + "type": "integer" + }, + "operator_id": { + "type": "integer" + }, + "operator_nick": { + "type": "string" + }, + "operator_time": { + "type": "integer" + }, + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "content": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "消息段类型,例如 text、image、reply" + }, + "data": { + "type": "object", + "description": "字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id", + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ] + } + } + }, + "required": [ + "msg_seq", + "msg_random", + "sender_id", + "sender_nick", + "sender_time", + "operator_id", + "operator_nick", + "operator_time", + "message_id", + "content" + ] + } + }, + "examples": [ + { + "summary": "获取精华消息列表", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -3565,6 +5073,42 @@ export const ACTIONS: CatalogAction[] = [ "name": "get_file", "aliases": [], "summary": "获取文件信息(仅图片/语音缓存;群文件请用 get_group_file_url)", + "returnsSchema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "description": "媒体地址;图片无地址时可为原文件标识" + }, + "url": { + "type": "string", + "description": "媒体下载地址,图片地址可能为空" + }, + "file_size": { + "type": "string", + "description": "原媒体字节数的十进制字符串" + }, + "file_name": { + "type": "string", + "description": "文件名或缓存标识" + } + }, + "required": [ + "file", + "url", + "file_size", + "file_name" + ] + }, + "examples": [ + { + "summary": "获取文件信息(仅图片/语音缓存;群文件请用 get_group_file_url)", + "params": { + "file_id": "" + }, + "notes": "仅查询已缓存的图片或语音,先尝试图片,再尝试语音。" + } + ], "readOnly": true, "params": [ { @@ -3624,6 +5168,15 @@ export const ACTIONS: CatalogAction[] = [ "fileset_id" ] }, + "examples": [ + { + "summary": "从 QQ 闪传分享码或官方分享链接获取 fileset_id", + "params": { + "share_code": "" + }, + "notes": "share_code 使用实际闪传分享链接或提取码。" + } + ], "readOnly": true, "params": [ { @@ -3658,6 +5211,66 @@ export const ACTIONS: CatalogAction[] = [ "name": "get_fileset_info", "aliases": [], "summary": "获取文件集信息", + "returnsSchema": { + "type": "object", + "properties": { + "fileset_id": { + "type": "string" + }, + "file_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fileset_id": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "orig_name": { + "type": "string" + }, + "size": { + "type": "number", + "description": "文件字节数;此接口字段名为 size" + }, + "share_url": { + "type": "string" + }, + "file_id": { + "type": "string" + }, + "download_url": { + "type": "string" + } + }, + "required": [ + "fileset_id", + "file_name", + "orig_name", + "size", + "share_url", + "file_id", + "download_url" + ] + } + } + }, + "required": [ + "fileset_id", + "file_list" + ] + }, + "examples": [ + { + "summary": "获取文件集信息", + "params": { + "fileset_id": "" + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。" + } + ], "readOnly": false, "params": [ { @@ -3690,6 +5303,54 @@ export const ACTIONS: CatalogAction[] = [ "name": "get_flash_file_list", "aliases": [], "summary": "获取闪传文件列表", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fileset_id": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "orig_name": { + "type": "string" + }, + "size": { + "type": "number", + "description": "文件字节数;此接口字段名为 size" + }, + "share_url": { + "type": "string" + }, + "file_id": { + "type": "string" + }, + "download_url": { + "type": "string" + } + }, + "required": [ + "fileset_id", + "file_name", + "orig_name", + "size", + "share_url", + "file_id", + "download_url" + ] + } + }, + "examples": [ + { + "summary": "获取闪传文件列表", + "params": { + "fileset_id": "" + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。" + } + ], "readOnly": false, "params": [ { @@ -3722,6 +5383,27 @@ export const ACTIONS: CatalogAction[] = [ "name": "get_flash_file_url", "aliases": [], "summary": "获取闪传文件链接", + "returnsSchema": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": [ + "url" + ] + }, + "examples": [ + { + "summary": "获取闪传文件链接", + "params": { + "fileset_id": "", + "file_index": 0 + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。 file_index 从 0 开始。" + } + ], "readOnly": false, "params": [ { @@ -3782,9 +5464,147 @@ export const ACTIONS: CatalogAction[] = [ "properties": { "messages": { "type": "array", - "description": "转发消息节点数组", + "description": "转发消息节点数组;节点标识与本地消息 ID 不是同一契约", "items": { - "type": "object" + "type": "object", + "properties": { + "time": { + "type": "integer", + "description": "消息时间,Unix 秒" + }, + "message_type": { + "type": "string", + "enum": [ + "private", + "group" + ] + }, + "sub_type": { + "type": "string", + "description": "消息子类型" + }, + "message_id": { + "type": "integer", + "description": "转发节点原始标识,缺失时为 0" + }, + "real_id": { + "type": "integer", + "description": "与节点 message_id 相同" + }, + "user_id": { + "type": "integer", + "description": "发送者 QQ 号" + }, + "group_id": { + "type": "integer", + "description": "群聊消息的群号,私聊可省略" + }, + "target_id": { + "type": "integer", + "description": "私聊目标 QQ 号(若有)" + }, + "message": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "消息段类型,例如 text、image、reply" + }, + "data": { + "type": "object", + "description": "字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id", + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ] + } + }, + "raw_message": { + "type": "string", + "description": "消息的 CQ 码字符串形式" + }, + "font": { + "type": "integer" + }, + "sender": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "nickname": { + "type": "string" + }, + "card": { + "type": "string" + }, + "sex": { + "type": "string", + "enum": [ + "male", + "female", + "unknown" + ] + }, + "age": { + "type": "integer" + }, + "area": { + "type": "string" + }, + "level": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": true + }, + "self_id": { + "type": "integer" + }, + "post_type": { + "const": "message" + }, + "message_format": { + "const": "array" + }, + "message_seq": { + "type": "integer", + "description": "节点消息序号,缺失时使用节点标识或 0" + } + }, + "additionalProperties": true, + "required": [ + "self_id", + "user_id", + "time", + "message_id", + "message_seq", + "real_id", + "message_type", + "sender", + "raw_message", + "font", + "sub_type", + "message", + "message_format", + "post_type" + ] } } }, @@ -3792,6 +5612,15 @@ export const ACTIONS: CatalogAction[] = [ "messages" ] }, + "examples": [ + { + "summary": "获取合并转发消息(id 或 message_id)", + "params": { + "id": "" + }, + "notes": "id 是合并转发资源标识,通常来自转发消息段或上传结果 res_id,不是普通数值 message_id。" + } + ], "readOnly": true, "params": [ { @@ -3856,6 +5685,12 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取好友列表", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -3876,9 +5711,123 @@ export const ACTIONS: CatalogAction[] = [ "properties": { "messages": { "type": "array", - "description": "消息事件对象数组", + "description": "消息对象数组;去除 post_type/self_id,附带 real_id", "items": { - "type": "object" + "type": "object", + "properties": { + "time": { + "type": "integer", + "description": "消息时间,Unix 秒" + }, + "message_type": { + "type": "string", + "enum": [ + "private", + "group" + ] + }, + "sub_type": { + "type": "string", + "description": "消息子类型" + }, + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "real_id": { + "type": "integer", + "description": "原始 message_id;旧历史记录缺失时可为 0" + }, + "user_id": { + "type": "integer", + "description": "发送者 QQ 号" + }, + "group_id": { + "type": "integer", + "description": "群聊消息的群号,私聊可省略" + }, + "target_id": { + "type": "integer", + "description": "私聊目标 QQ 号(若有)" + }, + "message": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "消息段类型,例如 text、image、reply" + }, + "data": { + "type": "object", + "description": "字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id", + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ] + } + }, + "raw_message": { + "type": "string", + "description": "消息的 CQ 码字符串形式" + }, + "font": { + "type": "integer" + }, + "sender": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "nickname": { + "type": "string" + }, + "card": { + "type": "string" + }, + "sex": { + "type": "string", + "enum": [ + "male", + "female", + "unknown" + ] + }, + "age": { + "type": "integer" + }, + "area": { + "type": "string" + }, + "level": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true } } }, @@ -3886,6 +5835,16 @@ export const ACTIONS: CatalogAction[] = [ "messages" ] }, + "examples": [ + { + "summary": "获取好友消息历史(无锚点时从服务器获取最新双向记录)", + "params": { + "user_id": 10001, + "count": 20 + }, + "notes": "省略 message_id 从最新消息开始;只读取当前账号消息。" + } + ], "readOnly": true, "params": [ { @@ -4021,6 +5980,12 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取分组好友列表", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -4109,6 +6074,15 @@ export const ACTIONS: CatalogAction[] = [ "no_code_finger_open" ] }, + "examples": [ + { + "summary": "获取群管理设置的当前值", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4314,6 +6288,15 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4355,9 +6338,336 @@ export const ACTIONS: CatalogAction[] = [ "properties": { "mediaList": { "type": "array", - "description": "相册媒体项列表(各项字段不固定)", + "description": "相册图片或视频,未返回的字段可省略或为 null", "items": { - "type": "object" + "type": "object", + "properties": { + "type": { + "type": [ + "integer", + "null" + ] + }, + "image": { + "type": [ + "object", + "null" + ], + "properties": { + "name": { + "type": [ + "string", + "null" + ] + }, + "sloc": { + "type": [ + "string", + "null" + ] + }, + "lloc": { + "type": [ + "string", + "null" + ], + "description": "图片长定位标识,供评论、删除等操作使用" + }, + "photoUrls": { + "type": [ + "array", + "null" + ], + "items": { + "type": "object", + "properties": { + "spec": { + "type": [ + "integer", + "null" + ] + }, + "url": { + "type": [ + "object", + "null" + ], + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "width": { + "type": [ + "integer", + "null" + ] + }, + "height": { + "type": [ + "integer", + "null" + ] + } + } + } + } + } + }, + "defaultUrl": { + "type": [ + "object", + "null" + ], + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "width": { + "type": [ + "integer", + "null" + ] + }, + "height": { + "type": [ + "integer", + "null" + ] + } + } + }, + "isGif": { + "type": [ + "boolean", + "null" + ] + }, + "hasRaw": { + "type": [ + "boolean", + "null" + ] + } + } + }, + "video": { + "type": [ + "object", + "null" + ], + "properties": { + "id": { + "type": [ + "string", + "null" + ], + "description": "视频标识,供评论、删除等操作使用" + }, + "url": { + "type": [ + "string", + "null" + ] + }, + "cover": { + "type": [ + "object", + "null" + ], + "properties": { + "name": { + "type": [ + "string", + "null" + ] + }, + "sloc": { + "type": [ + "string", + "null" + ] + }, + "lloc": { + "type": [ + "string", + "null" + ], + "description": "图片长定位标识,供评论、删除等操作使用" + }, + "photoUrls": { + "type": [ + "array", + "null" + ], + "items": { + "type": "object", + "properties": { + "spec": { + "type": [ + "integer", + "null" + ] + }, + "url": { + "type": [ + "object", + "null" + ], + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "width": { + "type": [ + "integer", + "null" + ] + }, + "height": { + "type": [ + "integer", + "null" + ] + } + } + } + } + } + }, + "defaultUrl": { + "type": [ + "object", + "null" + ], + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "width": { + "type": [ + "integer", + "null" + ] + }, + "height": { + "type": [ + "integer", + "null" + ] + } + } + }, + "isGif": { + "type": [ + "boolean", + "null" + ] + }, + "hasRaw": { + "type": [ + "boolean", + "null" + ] + } + } + }, + "width": { + "type": [ + "integer", + "null" + ] + }, + "height": { + "type": [ + "integer", + "null" + ] + }, + "videoTime": { + "type": [ + "string", + "null" + ], + "description": "时长,十进制字符串" + }, + "videoUrl": { + "type": [ + "array", + "null" + ], + "items": { + "type": "object", + "properties": { + "spec": { + "type": [ + "integer", + "null" + ] + }, + "url": { + "type": [ + "object", + "null" + ], + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "width": { + "type": [ + "integer", + "null" + ] + }, + "height": { + "type": [ + "integer", + "null" + ] + } + } + } + } + } + } + } + }, + "uploader": { + "type": [ + "string", + "null" + ] + }, + "batchId": { + "type": [ + "string", + "null" + ], + "description": "上传批次,十进制字符串" + }, + "uploadTime": { + "type": [ + "string", + "null" + ], + "description": "上传时间,十进制字符串" + } + } } }, "nextAttachInfo": { @@ -4370,6 +6680,17 @@ export const ACTIONS: CatalogAction[] = [ "nextAttachInfo" ] }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001, + "album_id": "", + "attach_info": "" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 下一页传回 nextAttachInfo。" + } + ], "readOnly": true, "params": [ { @@ -4456,6 +6777,15 @@ export const ACTIONS: CatalogAction[] = [ "remain_at_all_count_for_uin" ] }, + "examples": [ + { + "summary": "获取群 @全体成员 剩余次数", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4549,6 +6879,15 @@ export const ACTIONS: CatalogAction[] = [ "group_all_shut" ] }, + "examples": [ + { + "summary": "获取群详细信息", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4624,6 +6963,15 @@ export const ACTIONS: CatalogAction[] = [ "total_space" ] }, + "examples": [ + { + "summary": "获取群文件系统信息", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4673,6 +7021,16 @@ export const ACTIONS: CatalogAction[] = [ "url" ] }, + "examples": [ + { + "summary": "获取群文件下载链接", + "params": { + "group_id": 20001, + "file_id": "" + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4844,6 +7202,16 @@ export const ACTIONS: CatalogAction[] = [ "folders" ] }, + "examples": [ + { + "summary": "获取群子目录文件列表", + "params": { + "group_id": 20001, + "folder_id": "" + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -4906,6 +7274,218 @@ export const ACTIONS: CatalogAction[] = [ "name": "get_group_honor_info", "aliases": [], "summary": "获取群荣誉信息", + "returnsSchema": { + "type": "object", + "properties": { + "group_id": { + "type": "integer" + }, + "current_talkative": { + "anyOf": [ + { + "type": "object", + "properties": { + "user_id": { + "type": [ + "number", + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "user_id", + "nickname", + "avatar", + "description" + ] + }, + { + "type": "null" + } + ] + }, + "talkative_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": [ + "number", + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "user_id", + "nickname", + "avatar", + "description" + ] + } + }, + "performer_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": [ + "number", + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "user_id", + "nickname", + "avatar", + "description" + ] + } + }, + "legend_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": [ + "number", + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "user_id", + "nickname", + "avatar", + "description" + ] + } + }, + "emotion_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": [ + "number", + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "user_id", + "nickname", + "avatar", + "description" + ] + } + }, + "strong_newbie_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": [ + "number", + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "user_id", + "nickname", + "avatar", + "description" + ] + }, + "description": "当前实现始终为空数组" + } + }, + "required": [ + "group_id", + "current_talkative", + "talkative_list", + "performer_list", + "legend_list", + "emotion_list", + "strong_newbie_list" + ] + }, + "examples": [ + { + "summary": "获取群荣誉信息", + "params": { + "group_id": 20001, + "type": "all" + }, + "notes": "未请求的分类仍返回空数组;当前龙王可能为 null。" + } + ], "readOnly": true, "params": [ { @@ -5009,6 +7589,12 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取被忽略的入群请求(NapCat)", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -5089,6 +7675,12 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取被过滤的入群请求", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -5161,6 +7753,15 @@ export const ACTIONS: CatalogAction[] = [ "group_all_shut" ] }, + "examples": [ + { + "summary": "获取群信息", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -5267,6 +7868,15 @@ export const ACTIONS: CatalogAction[] = [ "group_all_shut" ] }, + "examples": [ + { + "summary": "获取群信息(扩展)", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -5375,6 +7985,14 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取群列表", + "params": { + "no_cache": false + } + } + ], "readOnly": true, "params": [ { @@ -5514,6 +8132,16 @@ export const ACTIONS: CatalogAction[] = [ "role" ] }, + "examples": [ + { + "summary": "获取群成员信息", + "params": { + "group_id": 20001, + "user_id": 10001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -5694,6 +8322,15 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取群成员列表", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -5749,9 +8386,123 @@ export const ACTIONS: CatalogAction[] = [ "properties": { "messages": { "type": "array", - "description": "消息事件对象数组", + "description": "消息对象数组;去除 post_type/self_id,附带 real_id", "items": { - "type": "object" + "type": "object", + "properties": { + "time": { + "type": "integer", + "description": "消息时间,Unix 秒" + }, + "message_type": { + "type": "string", + "enum": [ + "private", + "group" + ] + }, + "sub_type": { + "type": "string", + "description": "消息子类型" + }, + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "real_id": { + "type": "integer", + "description": "原始 message_id;旧历史记录缺失时可为 0" + }, + "user_id": { + "type": "integer", + "description": "发送者 QQ 号" + }, + "group_id": { + "type": "integer", + "description": "群聊消息的群号,私聊可省略" + }, + "target_id": { + "type": "integer", + "description": "私聊目标 QQ 号(若有)" + }, + "message": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "消息段类型,例如 text、image、reply" + }, + "data": { + "type": "object", + "description": "字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id", + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ] + } + }, + "raw_message": { + "type": "string", + "description": "消息的 CQ 码字符串形式" + }, + "font": { + "type": "integer" + }, + "sender": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "nickname": { + "type": "string" + }, + "card": { + "type": "string" + }, + "sex": { + "type": "string", + "enum": [ + "male", + "female", + "unknown" + ] + }, + "age": { + "type": "integer" + }, + "area": { + "type": "string" + }, + "level": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true } } }, @@ -5759,6 +8510,16 @@ export const ACTIONS: CatalogAction[] = [ "messages" ] }, + "examples": [ + { + "summary": "获取群消息历史", + "params": { + "group_id": 20001, + "count": 20 + }, + "notes": "省略 message_id 从最新消息开始;翻页使用上一页实际 message_id,不要自行加减。" + } + ], "readOnly": true, "params": [ { @@ -5952,6 +8713,15 @@ export const ACTIONS: CatalogAction[] = [ "folders" ] }, + "examples": [ + { + "summary": "获取群根目录文件列表", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -6001,6 +8771,16 @@ export const ACTIONS: CatalogAction[] = [ "url" ] }, + "examples": [ + { + "summary": "获取群分享链接", + "params": { + "group_id": 20001, + "need_short_url": true + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -6112,6 +8892,15 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取群禁言列表", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -6148,6 +8937,41 @@ export const ACTIONS: CatalogAction[] = [ "name": "get_group_signed_list", "aliases": [], "summary": "获取群今日打卡列表", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "nick": { + "type": "string" + }, + "time": { + "type": "integer" + }, + "rank": { + "type": "integer" + } + }, + "required": [ + "user_id", + "nick", + "time", + "rank" + ] + } + }, + "examples": [ + { + "summary": "获取群今日打卡列表", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -6245,6 +9069,17 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取群系统消息", + "params": { + "group_id": 20001, + "only_pending": true, + "count": 20 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -6343,6 +9178,15 @@ export const ACTIONS: CatalogAction[] = [ "tags" ] }, + "examples": [ + { + "summary": "读取某个群分类下的资料标签", + "params": { + "group_class": 1 + }, + "notes": "group_class 使用实际群分类编号。" + } + ], "readOnly": true, "params": [ { @@ -6378,6 +9222,85 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "获取群待办列表", "returns": "群待办数组,包含可用于待办操作的消息标识、服务端摘要与时间", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "message_seq": { + "type": "integer" + }, + "message_random": { + "type": "integer" + }, + "message": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "消息段类型,例如 text、image、reply" + }, + "data": { + "type": "object", + "description": "字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id", + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ] + } + }, + { + "type": "null" + } + ], + "description": "本地缓存中的消息段;未缓存时为 null" + }, + "text": { + "type": "string" + }, + "create_time": { + "type": "integer" + }, + "update_time": { + "type": "integer" + } + }, + "required": [ + "message_id", + "message_seq", + "message_random", + "message", + "text", + "create_time", + "update_time" + ] + } + }, + "examples": [ + { + "summary": "获取群待办列表", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": true, "params": [ { @@ -6414,6 +9337,42 @@ export const ACTIONS: CatalogAction[] = [ "name": "get_image", "aliases": [], "summary": "获取图片信息", + "returnsSchema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "description": "媒体地址;图片无地址时可为原文件标识" + }, + "url": { + "type": "string", + "description": "媒体下载地址,图片地址可能为空" + }, + "file_size": { + "type": "string", + "description": "原媒体字节数的十进制字符串" + }, + "file_name": { + "type": "string", + "description": "文件名或缓存标识" + } + }, + "required": [ + "file", + "url", + "file_size", + "file_name" + ] + }, + "examples": [ + { + "summary": "获取图片信息", + "params": { + "file": "" + }, + "notes": "file 或 file_id 原样取自图片消息段,须在当前账号媒体缓存中存在。" + } + ], "readOnly": true, "params": [ { @@ -6477,6 +9436,12 @@ export const ACTIONS: CatalogAction[] = [ "nickname" ] }, + "examples": [ + { + "summary": "基本调用", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -6491,6 +9456,60 @@ export const ACTIONS: CatalogAction[] = [ "name": "get_mini_app_ark", "aliases": [], "summary": "获取小程序卡片 ark", + "returnsSchema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "ver": { + "description": "服务端卡片版本,原样透传" + }, + "prompt": { + "description": "服务端提示文本,原样透传" + }, + "config": { + "description": "服务端卡片配置,原样透传" + }, + "app": { + "description": "服务端应用名,原样透传" + }, + "view": { + "description": "服务端视图,原样透传" + }, + "meta": { + "description": "服务端卡片元数据,原样透传" + }, + "miniappShareOrigin": { + "const": 3 + }, + "miniappOpenRefer": { + "const": "10002" + } + }, + "required": [ + "miniappShareOrigin", + "miniappOpenRefer" + ] + } + }, + "required": [ + "data" + ] + }, + "examples": [ + { + "summary": "获取小程序卡片 ark", + "params": { + "type": "bili", + "title": "示例视频", + "desc": "视频介绍", + "pic_url": "https://example.com/cover.png", + "jump_url": "https://www.bilibili.com/video/" + }, + "notes": "替换为实际分享地址;当前仅支持 bili 和 weibo。返回的 data 是卡片对象,不会自动发送。" + } + ], "readOnly": true, "params": [ { @@ -6585,6 +9604,154 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "获取消息", "returns": "消息事件对象(去除 post_type/self_id,附带 real_id);逐个尝试刷新图片、语音和视频 URL,成功使用新地址,失败保留该段原地址并继续返回消息;地址更新时同步 raw_message。", + "returnsSchema": { + "allOf": [ + { + "type": "object", + "properties": { + "time": { + "type": "integer", + "description": "消息时间,Unix 秒" + }, + "message_type": { + "type": "string", + "enum": [ + "private", + "group" + ] + }, + "sub_type": { + "type": "string", + "description": "消息子类型" + }, + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "real_id": { + "type": "integer", + "description": "原始 message_id;旧历史记录缺失时可为 0" + }, + "user_id": { + "type": "integer", + "description": "发送者 QQ 号" + }, + "group_id": { + "type": "integer", + "description": "群聊消息的群号,私聊可省略" + }, + "target_id": { + "type": "integer", + "description": "私聊目标 QQ 号(若有)" + }, + "message": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "消息段类型,例如 text、image、reply" + }, + "data": { + "type": "object", + "description": "字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id", + "additionalProperties": true + } + }, + "required": [ + "type", + "data" + ] + } + }, + "raw_message": { + "type": "string", + "description": "消息的 CQ 码字符串形式" + }, + "font": { + "type": "integer" + }, + "sender": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "nickname": { + "type": "string" + }, + "card": { + "type": "string" + }, + "sex": { + "type": "string", + "enum": [ + "male", + "female", + "unknown" + ] + }, + "age": { + "type": "integer" + }, + "area": { + "type": "string" + }, + "level": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": true + } + }, + "additionalProperties": true + }, + { + "type": "object", + "properties": { + "real_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "real_id" + ] + } + ], + "description": "消息事件去除 post_type/self_id 并增加 real_id;群聊和私聊字段有所不同" + }, + "examples": [ + { + "summary": "获取消息", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": true, "params": [ { @@ -6670,6 +9837,15 @@ export const ACTIONS: CatalogAction[] = [ ] } }, + "examples": [ + { + "summary": "获取一条消息的全部表情回应", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": true, "params": [ { @@ -6749,6 +9925,14 @@ export const ACTIONS: CatalogAction[] = [ "clients" ] }, + "examples": [ + { + "summary": "获取在线客户端", + "params": { + "no_cache": false + } + } + ], "readOnly": true, "params": [ { @@ -6793,6 +9977,16 @@ export const ACTIONS: CatalogAction[] = [ "url" ] }, + "examples": [ + { + "summary": "获取私聊文件下载链接", + "params": { + "user_id": 10001, + "file_id": "" + }, + "notes": "使用私聊文件消息段中的 file_id,必要时原样传 file_hash。" + } + ], "readOnly": true, "params": [ { @@ -7097,6 +10291,16 @@ export const ACTIONS: CatalogAction[] = [ "voteInfo" ] }, + "examples": [ + { + "summary": "获取资料点赞", + "params": { + "user_id": 10001, + "start": 0, + "count": 10 + } + } + ], "readOnly": true, "params": [ { @@ -7355,6 +10559,16 @@ export const ACTIONS: CatalogAction[] = [ "has_more" ] }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001, + "attach_info": "" + }, + "notes": "后续页原样传回上一页的 attach_info;has_more=false 表示结束。" + } + ], "readOnly": true, "params": [ { @@ -7459,6 +10673,16 @@ export const ACTIONS: CatalogAction[] = [ "has_more" ] }, + "examples": [ + { + "summary": "获取 QQ 空间好友动态(feed);page_num 仅首页可靠,深翻页需时间游标(暂未实现)", + "params": { + "page_num": 1, + "count": 10 + }, + "notes": "目前仅首页可靠,不应假设任意页码均能完整翻页。" + } + ], "readOnly": true, "params": [ { @@ -7608,6 +10832,17 @@ export const ACTIONS: CatalogAction[] = [ "comments" ] }, + "examples": [ + { + "summary": "获取 QQ 空间说说详情与一页评论(含服务端内联回复,需有访问权限)", + "params": { + "tid": "", + "pos": 0, + "num": 20 + }, + "notes": "tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。 replies 仅为服务端内联回复,不保证完整。" + } + ], "readOnly": true, "params": [ { @@ -7753,6 +10988,16 @@ export const ACTIONS: CatalogAction[] = [ "msglist" ] }, + "examples": [ + { + "summary": "获取 QQ 空间说说列表(默认机器人自己的空间)", + "params": { + "pos": 0, + "num": 20 + }, + "notes": "默认读取当前账号;指定 target_uin 时需有访问权限。" + } + ], "readOnly": true, "params": [ { @@ -7827,6 +11072,15 @@ export const ACTIONS: CatalogAction[] = [ "type": "array", "description": "最近会话列表(占位,恒空)" }, + "examples": [ + { + "summary": "获取最近会话(占位)", + "notes": "兼容占位接口,当前始终返回空数组,不能据此判断真实会话列表。", + "params": { + "count": 10 + } + } + ], "readOnly": true, "params": [ { @@ -7859,6 +11113,70 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "获取语音信息;传 out_format 则服务端转码并附带 base64", "returns": "语音信息(file/url 为向 QQ 获取的当前下载地址);资源元数据缺失或远端获取失败时返回错误。out_format 指定时附带转码后的 base64。", + "returnsSchema": { + "allOf": [ + { + "type": "object", + "properties": { + "file": { + "type": "string", + "description": "媒体地址;图片无地址时可为原文件标识" + }, + "url": { + "type": "string", + "description": "媒体下载地址,图片地址可能为空" + }, + "file_size": { + "type": "string", + "description": "原媒体字节数的十进制字符串" + }, + "file_name": { + "type": "string", + "description": "文件名或缓存标识" + } + }, + "required": [ + "file", + "url", + "file_size", + "file_name" + ] + }, + { + "type": "object", + "properties": { + "out_format": { + "type": "string", + "enum": [ + "mp3", + "amr", + "wma", + "m4a", + "spx", + "ogg", + "wav", + "flac" + ] + }, + "base64": { + "type": "string", + "description": "转换后音频的 Base64,未请求 out_format 时省略" + } + } + } + ], + "description": "未转换时只有媒体信息;指定 out_format 成功转换后同时增加 out_format 和 base64,file_size 仍为原媒体大小" + }, + "examples": [ + { + "summary": "获取语音信息;传 out_format 则服务端转码并附带 base64", + "params": { + "file": "", + "out_format": "mp3" + }, + "notes": "file 来自语音消息段;格式转换依赖服务端可用的转换工具,成功时增加 out_format 和 base64。" + } + ], "readOnly": true, "params": [ { @@ -7946,6 +11264,42 @@ export const ACTIONS: CatalogAction[] = [ "nc_get_rkey" ], "summary": "获取下载 rkey", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "rkey": { + "type": "string", + "description": "下载密钥,敏感信息" + }, + "type": { + "type": "integer" + }, + "ttl": { + "type": "integer", + "description": "有效时长,秒" + }, + "create_time": { + "type": "integer", + "description": "创建时间,Unix 秒" + } + }, + "required": [ + "rkey", + "type", + "ttl", + "create_time" + ] + } + }, + "examples": [ + { + "summary": "获取下载 rkey", + "params": {}, + "notes": "返回值可能包含凭据或临时下载密钥,请勿公开或记录到不受控日志。" + } + ], "readOnly": true, "params": [], "invariants": [], @@ -7986,6 +11340,13 @@ export const ACTIONS: CatalogAction[] = [ "name" ] }, + "examples": [ + { + "summary": "获取 rkey 服务器信息", + "params": {}, + "notes": "返回值可能包含凭据或临时下载密钥,请勿公开或记录到不受控日志。" + } + ], "readOnly": true, "params": [], "invariants": [], @@ -8000,6 +11361,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "get_share_link", "aliases": [], "summary": "获取文件分享链接", + "returnsSchema": { + "type": "string", + "description": "生成的下载或分享 URL 字符串,不是含 url 字段的对象" + }, + "examples": [ + { + "summary": "获取文件分享链接", + "params": { + "fileset_id": "" + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。" + } + ], "readOnly": false, "params": [ { @@ -8054,6 +11428,12 @@ export const ACTIONS: CatalogAction[] = [ "time" ] }, + "examples": [ + { + "summary": "基本调用", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -8183,6 +11563,15 @@ export const ACTIONS: CatalogAction[] = [ "long_nick" ] }, + "examples": [ + { + "summary": "获取陌生人信息", + "params": { + "user_id": 10001 + }, + "notes": "替换为需要查询的 QQ 号。" + } + ], "readOnly": true, "params": [ { @@ -8219,6 +11608,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "get_unidirectional_friend_list", "aliases": [], "summary": "获取单向好友列表", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "description": "服务端列表项原样透传,目前未归一化字段,不能假定与 get_friend_list 相同" + } + }, + "examples": [ + { + "summary": "获取单向好友列表", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -8255,6 +11658,12 @@ export const ACTIONS: CatalogAction[] = [ "protocol_version" ] }, + "examples": [ + { + "summary": "基本调用", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -8269,6 +11678,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "group_poke", "aliases": [], "summary": "群拍一拍", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "群拍一拍", + "params": { + "group_id": 20001, + "user_id": 10001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": false, "params": [ { @@ -8323,6 +11746,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "like_qzone", "aliases": [], "summary": "给一条说说点赞(QQ 空间)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "给一条说说点赞(QQ 空间)", + "params": { + "tid": "", + "target_uin": 10001 + }, + "notes": "tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。" + } + ], "readOnly": false, "params": [ { @@ -8393,6 +11830,51 @@ export const ACTIONS: CatalogAction[] = [ "name": "list_filesets", "aliases": [], "summary": "列出当前账号的所有闪传文件集", + "returnsSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fileset_id": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "orig_name": { + "type": "string" + }, + "size": { + "type": "number", + "description": "文件字节数;此接口字段名为 size" + }, + "share_url": { + "type": "string" + }, + "file_id": { + "type": "string" + }, + "download_url": { + "type": "string" + } + }, + "required": [ + "fileset_id", + "file_name", + "orig_name", + "size", + "share_url", + "file_id", + "download_url" + ] + } + }, + "examples": [ + { + "summary": "列出当前账号的所有闪传文件集", + "params": {} + } + ], "readOnly": false, "params": [], "invariants": [], @@ -8407,6 +11889,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "mark_group_msg_as_read", "aliases": [], "summary": "标记群消息已读", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "标记群消息已读", + "params": { + "message_id": -123456, + "group_id": 20001 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -8460,6 +11956,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "mark_msg_as_read", "aliases": [], "summary": "标记消息已读(群聊/私聊自动路由)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "标记消息已读(群聊/私聊自动路由)", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -8511,6 +12020,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "mark_private_msg_as_read", "aliases": [], "summary": "标记私聊消息已读", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "标记私聊消息已读", + "params": { + "message_id": -123456, + "user_id": 10001 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -8564,6 +12087,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "modify_custom_face", "aliases": [], "summary": "修改收藏表情备注", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "修改收藏表情备注", + "params": { + "emoji_id": "", + "desc": "常用表情" + }, + "notes": "emoji_id 来自 fetch_custom_face_detail。" + } + ], "readOnly": false, "params": [ { @@ -8609,6 +12146,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "move_custom_face_to_front", "aliases": [], "summary": "收藏表情移到最前", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "收藏表情移到最前", + "params": { + "emoji_id": "" + }, + "notes": "emoji_id 来自 fetch_custom_face_detail。" + } + ], "readOnly": false, "params": [ { @@ -8641,6 +12191,22 @@ export const ACTIONS: CatalogAction[] = [ "name": "move_group_file", "aliases": [], "summary": "移动群文件", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "移动群文件", + "params": { + "group_id": 20001, + "file_id": "", + "parent_directory": "/", + "target_directory": "" + }, + "notes": "目录和文件标识均来自该群文件列表。" + } + ], "readOnly": false, "params": [ { @@ -8726,6 +12292,13 @@ export const ACTIONS: CatalogAction[] = [ "type": "null", "description": "packet 状态(占位,恒 null)" }, + "examples": [ + { + "summary": "获取 packet 状态(占位)", + "notes": "兼容占位接口,当前始终返回 null,不提供实际收发统计。", + "params": {} + } + ], "readOnly": true, "params": [], "invariants": [], @@ -8758,6 +12331,15 @@ export const ACTIONS: CatalogAction[] = [ "ext_status" ] }, + "examples": [ + { + "summary": "获取用户在线/扩展状态", + "params": { + "user_id": 10001 + }, + "notes": "替换为需要查询的 QQ 号。" + } + ], "readOnly": true, "params": [ { @@ -8851,6 +12433,15 @@ export const ACTIONS: CatalogAction[] = [ "language" ] }, + "examples": [ + { + "summary": "OCR 图片(服务端,需图片 URL 或已缓存的图片 file_id)", + "params": { + "image": "https://example.com/image.png" + }, + "notes": "替换为实际可访问的图片地址或已缓存图片标识;不支持本地路径和 base64。" + } + ], "readOnly": true, "params": [ { @@ -8885,6 +12476,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "rename_flash_file", "aliases": [], "summary": "重命名闪传文件", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "重命名闪传文件", + "params": { + "fileset_id": "", + "new_name": "新资料包" + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。" + } + ], "readOnly": false, "params": [ { @@ -8931,6 +12536,29 @@ export const ACTIONS: CatalogAction[] = [ "name": "rename_group_file", "aliases": [], "summary": "重命名群文件", + "returnsSchema": { + "type": "object", + "properties": { + "ok": { + "const": true + } + }, + "required": [ + "ok" + ] + }, + "examples": [ + { + "summary": "重命名群文件", + "params": { + "group_id": 20001, + "file_id": "", + "current_parent_directory": "/", + "new_name": "新文件名.pdf" + }, + "notes": "file_id 来自群文件列表,目录必须是文件当前所在目录。" + } + ], "readOnly": false, "params": [ { @@ -9010,6 +12638,21 @@ export const ACTIONS: CatalogAction[] = [ "name": "rename_group_file_folder", "aliases": [], "summary": "重命名群文件夹", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "重命名群文件夹", + "params": { + "group_id": 20001, + "folder_id": "", + "new_folder_name": "课程资料" + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": false, "params": [ { @@ -9086,6 +12729,27 @@ export const ACTIONS: CatalogAction[] = [ "name": "request_decrypt_key", "aliases": [], "summary": "请求数据库解密密钥", + "returnsSchema": { + "type": "object", + "properties": { + "db_key": { + "type": "string", + "description": "数据库密钥,敏感信息" + } + }, + "required": [ + "db_key" + ] + }, + "examples": [ + { + "summary": "请求数据库解密密钥", + "params": { + "db_path": "/path/to/account/nt_msg.db" + }, + "notes": "路径位于 SnowLuma 服务端,必须指向当前账号可用的数据库;返回值是敏感密钥。" + } + ], "readOnly": false, "params": [ { @@ -9212,6 +12876,14 @@ export const ACTIONS: CatalogAction[] = [ "faces" ] }, + "examples": [ + { + "summary": "搜索 QQ 系统表情", + "params": { + "query": "微笑" + } + } + ], "readOnly": true, "params": [ { @@ -9259,6 +12931,15 @@ export const ACTIONS: CatalogAction[] = [ "arkMsg" ] }, + "examples": [ + { + "summary": "分享用户/群 Ark 卡片(NapCat 标准名)", + "params": { + "user_id": 10001 + }, + "notes": "生成联系人分享卡片;返回卡片数据,不等同于发送到聊天。" + } + ], "readOnly": true, "params": [ { @@ -9319,6 +13000,33 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "发送账号收藏表情(emoji_id 或 MD5),保留表情显示样式", "returns": "{ message_id: number }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "发送账号收藏表情(emoji_id 或 MD5),保留表情显示样式", + "params": { + "group_id": 20001, + "emoji_id": "" + }, + "notes": "群聊和私聊目标只能提供一个;emoji_id 来自 fetch_custom_face_detail。" + } + ], "readOnly": false, "params": [ { @@ -9407,6 +13115,28 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "发送闪传消息(私聊或群聊,引用 fileset_id 让对端下载)", "returns": "{ message_id }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "const": 0, + "description": "闪传分享不返回普通消息 ID,0 是占位值,不能用于查询或撤回" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "发送闪传消息(私聊或群聊,引用 fileset_id 让对端下载)", + "params": { + "fileset_id": "", + "user_id": 10001 + }, + "notes": "fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。 会发送分享消息;message_id=0 是占位值,不能用于撤回或查消息。" + } + ], "readOnly": false, "params": [ { @@ -9470,6 +13200,65 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "发送合并转发(按 message_type/群号自动路由)", "returns": "{ message_id, res_id, forward_id }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "anyOf": [ + { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + { + "const": 0 + } + ], + "description": "已发送时是非零有符号消息 ID;未提供目标、仅上传资源时为 0" + }, + "res_id": { + "type": "string" + }, + "forward_id": { + "type": "string" + } + }, + "required": [ + "message_id", + "res_id", + "forward_id" + ] + }, + "examples": [ + { + "summary": "发送合并转发(按 message_type/群号自动路由)", + "params": { + "group_id": 20001, + "messages": [ + { + "type": "node", + "data": { + "user_id": 10001, + "nickname": "示例用户", + "content": [ + { + "type": "text", + "data": { + "text": "转发内容" + } + } + ] + } + } + ] + }, + "notes": "替换目标群号;nodes 是自定义转发节点数组。send_forward_msg 不提供目标时仅上传资源并返回 message_id=0。" + } + ], "readOnly": false, "params": [ { @@ -9588,6 +13377,35 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "发送 AI 语音到群", "returns": "{ message_id }:已发送 AI 语音的 OneBot 消息 ID。", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "发送 AI 语音到群", + "params": { + "group_id": 20001, + "character": "", + "text": "你好", + "chat_type": 1 + }, + "notes": "character 原样使用 get_ai_characters 的 character_id;send_group_ai_record 会直接发送语音。" + } + ], "readOnly": false, "params": [ { @@ -9672,6 +13490,15 @@ export const ACTIONS: CatalogAction[] = [ "type": "string", "description": "群 Ark 卡片 JSON 字符串" }, + "examples": [ + { + "summary": "分享群 Ark 卡片(NapCat 标准名)", + "params": { + "group_id": 20001 + }, + "notes": "生成群分享卡片;返回卡片数据,不等同于发送到聊天。" + } + ], "readOnly": true, "params": [ { @@ -9707,6 +13534,57 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "发送群合并转发", "returns": "{ message_id, res_id, forward_id }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "res_id": { + "type": "string" + }, + "forward_id": { + "type": "string" + } + }, + "required": [ + "message_id", + "res_id", + "forward_id" + ] + }, + "examples": [ + { + "summary": "发送群合并转发", + "params": { + "group_id": 20001, + "messages": [ + { + "type": "node", + "data": { + "user_id": 10001, + "nickname": "示例用户", + "content": [ + { + "type": "text", + "data": { + "text": "转发内容" + } + } + ] + } + } + ] + }, + "notes": "替换目标群号;nodes 是自定义转发节点数组。send_forward_msg 不提供目标时仅上传资源并返回 message_id=0。" + } + ], "readOnly": false, "params": [ { @@ -9808,6 +13686,40 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "发送群消息", "returns": "{ message_id: number }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "发送群消息", + "params": { + "group_id": 20001, + "message": [ + { + "type": "text", + "data": { + "text": "你好" + } + } + ] + }, + "notes": "替换目标群号;成功返回的 message_id 可为负值。" + } + ], "readOnly": false, "params": [ { @@ -9869,6 +13781,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "send_like", "aliases": [], "summary": "点赞", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "点赞", + "params": { + "user_id": 10001, + "times": 1 + } + } + ], "readOnly": false, "params": [ { @@ -9919,6 +13844,34 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "发送消息(按 message_type/群号 自动路由群聊或私聊)", "returns": "{ message_id: number }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "发送消息(按 message_type/群号 自动路由群聊或私聊)", + "params": { + "message_type": "group", + "group_id": 20001, + "message": "你好" + }, + "notes": "必须提供对应目标;私聊使用 message_type=private 和 user_id。" + } + ], "readOnly": false, "params": [ { @@ -10005,6 +13958,29 @@ export const ACTIONS: CatalogAction[] = [ ".send_packet" ], "summary": "发送原始 SSO 包(cmd + hex data)", + "returnsSchema": { + "anyOf": [ + { + "type": "string", + "description": "rsp=true 时返回的十六进制响应正文" + }, + { + "type": "null", + "description": "rsp=false 时不等待响应,data 为 null" + } + ] + }, + "examples": [ + { + "summary": "发送原始 SSO 包(cmd + hex data)", + "params": { + "cmd": "", + "data": "", + "rsp": true + }, + "notes": "仅供有协议经验的调用方:cmd 和 data 必须成对替换为已验证的命令与十六进制请求;示例占位符不可直接发送。" + } + ], "readOnly": false, "params": [ { @@ -10063,6 +14039,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "send_poke", "aliases": [], "summary": "拍一拍(群聊/私聊自动路由)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "拍一拍(群聊/私聊自动路由)", + "params": { + "group_id": 20001, + "user_id": 10001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": false, "params": [ { @@ -10113,6 +14103,57 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "发送私聊合并转发", "returns": "{ message_id, res_id, forward_id }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + }, + "res_id": { + "type": "string" + }, + "forward_id": { + "type": "string" + } + }, + "required": [ + "message_id", + "res_id", + "forward_id" + ] + }, + "examples": [ + { + "summary": "发送私聊合并转发", + "params": { + "user_id": 10001, + "messages": [ + { + "type": "node", + "data": { + "user_id": 10001, + "nickname": "示例用户", + "content": [ + { + "type": "text", + "data": { + "text": "转发内容" + } + } + ] + } + } + ] + }, + "notes": "替换目标好友 QQ 号。" + } + ], "readOnly": false, "params": [ { @@ -10212,6 +14253,33 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "发送私聊消息", "returns": "{ message_id: number }", + "returnsSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647, + "not": { + "const": 0 + }, + "description": "非零有符号 32 位消息 ID,负值合法;必须原样保留" + } + }, + "required": [ + "message_id" + ] + }, + "examples": [ + { + "summary": "发送私聊消息", + "params": { + "user_id": 10001, + "message": "你好" + }, + "notes": "替换目标好友 QQ 号;临时会话需另传来源 group_id。" + } + ], "readOnly": false, "params": [ { @@ -10284,6 +14352,31 @@ export const ACTIONS: CatalogAction[] = [ "name": "send_qzone_msg", "aliases": [], "summary": "发表说说(QQ 空间,支持纯文字或带图;传 images 自动上传;可设置查看权限)", + "returnsSchema": { + "type": "object", + "properties": { + "tid": { + "type": "string" + }, + "time": { + "type": "integer" + } + }, + "required": [ + "tid", + "time" + ] + }, + "examples": [ + { + "summary": "发表说说(QQ 空间,支持纯文字或带图;传 images 自动上传;可设置查看权限)", + "params": { + "content": "今天的学习记录", + "ugc_right": 64 + }, + "notes": "会发布说说;ugc_right=64 仅自己可见。16/128 还必须提供 target_uins。" + } + ], "readOnly": false, "params": [ { @@ -10384,6 +14477,19 @@ export const ACTIONS: CatalogAction[] = [ "returnsSchema": { "type": "null" }, + "examples": [ + { + "summary": "发送图文 Ark 卡片(私聊/群聊)", + "params": { + "group_id": 20001, + "title": "学习资料", + "desc": "本周课程资料", + "jump_url": "https://example.com/course", + "preview_url": "https://example.com/cover.png" + }, + "notes": "替换目标群号及实际链接;会发送分享卡片。" + } + ], "readOnly": false, "params": [ { @@ -10494,6 +14600,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_diy_online_status", "aliases": [], "summary": "设置自定义在线状态", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置自定义在线状态", + "params": { + "face_id": 14, + "face_type": 1, + "wording": "学习中" + } + } + ], "readOnly": false, "params": [ { @@ -10556,6 +14676,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_doubt_friends_add_request", "aliases": [], "summary": "处理可疑好友申请", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "处理可疑好友申请", + "params": { + "flag": "", + "approve": true + }, + "notes": "flag 使用 get_doubt_friends_add_request 返回项的 uid。" + } + ], "readOnly": false, "params": [ { @@ -10601,6 +14735,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_essence_msg", "aliases": [], "summary": "设置精华消息", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置精华消息", + "params": { + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -10639,6 +14786,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_friend_add_request", "aliases": [], "summary": "处理好友添加请求", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "处理好友添加请求", + "params": { + "flag": "", + "approve": true + }, + "notes": "flag 必须原样使用当前账号好友请求事件的 flag,不能根据 QQ 号构造。" + } + ], "readOnly": false, "params": [ { @@ -10684,6 +14845,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_friend_remark", "aliases": [], "summary": "设置好友备注", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置好友备注", + "params": { + "user_id": 10001, + "remark": "同学" + } + } + ], "readOnly": false, "params": [ { @@ -10731,6 +14905,20 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "移动好友到指定分组", "returns": "成功时返回空数据。", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "移动好友到指定分组", + "params": { + "uin": 10001, + "categoryName": "同学" + }, + "notes": "categoryId 与 categoryName 二选一。" + } + ], "readOnly": false, "params": [ { @@ -10799,6 +14987,22 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_add_option", "aliases": [], "summary": "设置加群选项", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置加群选项", + "params": { + "group_id": 20001, + "add_type": 2, + "group_question": "入群用途?", + "group_answer": "学习交流" + }, + "notes": "修改加群设置前先用 get_group_admin_settings 查看当前状态,并确认目标设置值。" + } + ], "readOnly": false, "params": [ { @@ -10872,6 +15076,21 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_add_request", "aliases": [], "summary": "处理加群请求", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "处理加群请求", + "params": { + "flag": "", + "sub_type": "add", + "approve": true + }, + "notes": "flag 和 sub_type 原样取自同一条群请求事件;邀请事件使用 invite。" + } + ], "readOnly": false, "params": [ { @@ -10952,6 +15171,21 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_admin", "aliases": [], "summary": "设置/取消管理员", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置/取消管理员", + "params": { + "group_id": 20001, + "user_id": 10001, + "enable": true + }, + "notes": "需要群主权限;会授予该成员管理员身份。" + } + ], "readOnly": false, "params": [ { @@ -11018,6 +15252,34 @@ export const ACTIONS: CatalogAction[] = [ { "name": "set_group_album_media_like", "aliases": [], + "returnsSchema": { + "type": "object", + "properties": { + "id": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": [ + "integer", + "null" + ] + } + } + }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001, + "album_id": "", + "batch_id": "1700000000" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 batch_id 必须是实际批次的十进制字符串。" + } + ], "readOnly": false, "params": [ { @@ -11093,6 +15355,17 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_anonymous", "aliases": [], "summary": "匿名开关(未实现,返回 ok)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "匿名开关(未实现,返回 ok)", + "params": {}, + "notes": "兼容占位接口:当前返回成功但不会修改匿名设置。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -11107,6 +15380,17 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_anonymous_ban", "aliases": [], "summary": "匿名禁言(未实现,返回 ok)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "匿名禁言(未实现,返回 ok)", + "params": {}, + "notes": "兼容占位接口:当前返回成功但不会修改匿名设置。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -11121,6 +15405,21 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_ban", "aliases": [], "summary": "禁言群成员(duration=0 解除)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "禁言群成员(duration=0 解除)", + "params": { + "group_id": 20001, + "user_id": 10001, + "duration": 600 + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。 duration 为秒,0 表示解除禁言。" + } + ], "readOnly": false, "params": [ { @@ -11192,6 +15491,21 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_card", "aliases": [], "summary": "设置群名片(空字符串清除)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群名片(空字符串清除)", + "params": { + "group_id": 20001, + "user_id": 10001, + "card": "示例群名片" + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11259,6 +15573,21 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_kick", "aliases": [], "summary": "踢出群成员", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "踢出群成员", + "params": { + "group_id": 20001, + "user_id": 10001, + "reject_add_request": false + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11326,6 +15655,24 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_kick_members", "aliases": [], "summary": "批量踢出群成员", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "批量踢出群成员", + "params": { + "group_id": 20001, + "user_id": [ + 10001, + 10002 + ], + "reject_add_request": false + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11399,6 +15746,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_leave", "aliases": [], "summary": "退群", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "退群", + "params": { + "group_id": 20001 + }, + "notes": "会退出该群,请确认目标群号后调用。" + } + ], "readOnly": false, "params": [ { @@ -11435,6 +15795,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_member_invite_policy", "aliases": [], "summary": "设置群成员邀请策略", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群成员邀请策略", + "params": { + "group_id": 20001, + "policy": "require_approval" + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11499,6 +15873,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_member_permissions", "aliases": [], "summary": "设置群成员权限(仅群主可改)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群成员权限(仅群主可改)", + "params": { + "group_id": 20001, + "allow_member_upload_album": false + }, + "notes": "至少传一个权限字段;仅修改传入的权限,其余保持原样。" + } + ], "readOnly": false, "params": [ { @@ -11570,6 +15958,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_name", "aliases": [], "summary": "设置群名", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群名", + "params": { + "group_id": 20001, + "group_name": "学习交流群" + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11619,6 +16021,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_new_member_history_visibility", "aliases": [], "summary": "设置新成员是否可查看历史消息", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置新成员是否可查看历史消息", + "params": { + "group_id": 20001, + "visible": true + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11667,6 +16083,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_portrait", "aliases": [], "summary": "设置群头像", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群头像", + "params": { + "group_id": 20001, + "file": "file:///path/to/avatar.png" + }, + "notes": "示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。" + } + ], "readOnly": false, "params": [ { @@ -11719,6 +16149,22 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_reaction", "aliases": [], "summary": "群聊表情回应", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "群聊表情回应", + "params": { + "message_id": -123456, + "group_id": 20001, + "code": "128077", + "is_set": true + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -11799,6 +16245,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_remark", "aliases": [], "summary": "设置群备注", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群备注", + "params": { + "group_id": 20001, + "remark": "学习小组" + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": false, "params": [ { @@ -11845,6 +16305,21 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_robot_add_option", "aliases": [], "summary": "设置群机器人加群选项", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群机器人加群选项", + "params": { + "group_id": 20001, + "robot_member_switch": 1, + "robot_member_examine": 1 + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -11907,6 +16382,21 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_search", "aliases": [], "summary": "设置群被搜索方式(群指纹 / 群号搜索开关)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群被搜索方式(群指纹 / 群号搜索开关)", + "params": { + "group_id": 20001, + "no_finger_open": 0, + "no_code_finger_open": 0 + }, + "notes": "替换群号;数值含义以当前群设置为准。" + } + ], "readOnly": false, "params": [ { @@ -11971,6 +16461,21 @@ export const ACTIONS: CatalogAction[] = [ "send_group_sign" ], "summary": "群签到", + "returnsSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "description": "成功时为空对象 {},不是 null" + }, + "examples": [ + { + "summary": "群签到", + "params": { + "group_id": 20001 + }, + "notes": "将 group_id 替换为当前账号可访问的群号。" + } + ], "readOnly": false, "params": [ { @@ -12007,6 +16512,21 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_special_title", "aliases": [], "summary": "设置群头衔", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群头衔", + "params": { + "group_id": 20001, + "user_id": 10001, + "special_title": "学习委员" + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -12074,6 +16594,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_todo", "aliases": [], "summary": "设置群待办", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置群待办", + "params": { + "group_id": 20001, + "message_id": -123456 + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。 必须是该群的消息。" + } + ], "readOnly": false, "params": [ { @@ -12130,6 +16664,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_group_whole_ban", "aliases": [], "summary": "全员禁言开关", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "全员禁言开关", + "params": { + "group_id": 20001, + "enable": true + }, + "notes": "替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。" + } + ], "readOnly": false, "params": [ { @@ -12179,6 +16727,21 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_input_status", "aliases": [], "summary": "设置输入状态", + "returnsSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "description": "成功时为空对象 {},不是 null" + }, + "examples": [ + { + "summary": "设置输入状态", + "params": { + "user_id": 10001, + "event_type": 0 + } + } + ], "readOnly": false, "params": [ { @@ -12226,6 +16789,21 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_msg_emoji_like", "aliases": [], "summary": "设置消息表情回应", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置消息表情回应", + "params": { + "message_id": -123456, + "emoji_id": "128077", + "set": true + }, + "notes": "使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。" + } + ], "readOnly": false, "params": [ { @@ -12291,6 +16869,21 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_online_status", "aliases": [], "summary": "设置在线状态", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置在线状态", + "params": { + "status": 11, + "ext_status": 0, + "battery_status": 100 + }, + "notes": "修改当前账号状态;具体状态码须与目标状态对应。" + } + ], "readOnly": false, "params": [ { @@ -12357,6 +16950,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_qq_avatar", "aliases": [], "summary": "设置 QQ 头像", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置 QQ 头像", + "params": { + "file": "file:///path/to/avatar.png" + }, + "notes": "示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。" + } + ], "readOnly": false, "params": [ { @@ -12391,6 +16997,19 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_qq_profile", "aliases": [], "summary": "设置 QQ 资料", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "设置 QQ 资料", + "params": { + "nickname": "示例昵称" + }, + "notes": "会修改当前账号的资料;只传入需要修改的字段。" + } + ], "readOnly": false, "params": [ { @@ -12446,6 +17065,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_qzone_ban", "aliases": [], "summary": "拉黑或解除拉黑某人(修改机器人自身 QQ 空间黑名单;enable=true 拉黑,false 解除)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "拉黑或解除拉黑某人(修改机器人自身 QQ 空间黑名单;enable=true 拉黑,false 解除)", + "params": { + "user_id": 10001, + "enable": true + }, + "notes": "修改当前账号空间黑名单。" + } + ], "readOnly": false, "params": [ { @@ -12510,6 +17143,19 @@ export const ACTIONS: CatalogAction[] = [ "ugc_right" ] }, + "examples": [ + { + "summary": "修改一条已发说说的查看权限(QQ 空间,按 tid)", + "params": { + "tid": "", + "ugc_right": 16, + "target_uins": [ + 10001 + ] + }, + "notes": "tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。 16 表示仅所选好友可见。" + } + ], "readOnly": false, "params": [ { @@ -12584,6 +17230,17 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_restart", "aliases": [], "summary": "重启(不支持)", + "returnsSchema": { + "not": {}, + "description": "当前始终返回失败,没有成功 data;失败响应 data 为 null,原因见 wording" + }, + "examples": [ + { + "summary": "重启(不支持)", + "params": {}, + "notes": "当前未实现,调用返回失败;不要将其当作可用能力。" + } + ], "readOnly": false, "params": [], "invariants": [], @@ -12598,6 +17255,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "set_self_longnick", "aliases": [], "summary": "设置个性签名(longNick/long_nick,严格 string)", + "returnsSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "description": "成功时为空对象 {},不是 null" + }, + "examples": [ + { + "summary": "设置个性签名(longNick/long_nick,严格 string)", + "params": { + "long_nick": "学习中" + } + } + ], "readOnly": false, "params": [ { @@ -12641,6 +17312,15 @@ export const ACTIONS: CatalogAction[] = [ "type": "string", "description": "群 Ark 卡片 JSON 字符串" }, + "examples": [ + { + "summary": "分享群 Ark 卡片", + "params": { + "group_id": 20001 + }, + "notes": "生成群分享卡片;返回卡片数据,不等同于发送到聊天。" + } + ], "readOnly": true, "params": [ { @@ -12688,6 +17368,15 @@ export const ACTIONS: CatalogAction[] = [ "arkMsg" ] }, + "examples": [ + { + "summary": "分享用户/群 Ark 卡片", + "params": { + "user_id": 10001 + }, + "notes": "生成联系人分享卡片;返回卡片数据,不等同于发送到聊天。" + } + ], "readOnly": true, "params": [ { @@ -12748,6 +17437,58 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "测试下载流(推送 10 个数据帧,验证流式传输,不触达 QQ)", "returns": "流式帧:data_chunk*10 → data_complete(error=true 时以 error 帧结束)", + "returnsSchema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "data_type": { + "const": "data_chunk" + }, + "data": { + "type": "string" + } + }, + "required": [ + "type", + "data_type", + "data" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "response" + }, + "data_type": { + "const": "data_complete" + }, + "data": { + "type": "string" + } + }, + "required": [ + "type", + "data_type", + "data" + ] + } + ], + "description": "成功时 10 个 data_chunk 后接 data_complete;error=true 时最终为失败帧" + }, + "examples": [ + { + "summary": "测试下载流(推送 10 个数据帧,验证流式传输,不触达 QQ)", + "params": { + "error": false + }, + "notes": "经 invoke_action 调用,仅测试传输,不访问 QQ。" + } + ], "readOnly": false, "params": [ { @@ -12781,6 +17522,27 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "转存群文件", "returns": "{ ok: true }", + "returnsSchema": { + "type": "object", + "properties": { + "ok": { + "const": true + } + }, + "required": [ + "ok" + ] + }, + "examples": [ + { + "summary": "转存群文件", + "params": { + "group_id": 20001, + "file_id": "" + }, + "notes": "替换群文件列表中的实际 file_id;需要对应文件操作权限。" + } + ], "readOnly": false, "params": [ { @@ -12847,6 +17609,16 @@ export const ACTIONS: CatalogAction[] = [ "words" ] }, + "examples": [ + { + "summary": "英译中", + "params": { + "words": [ + "Hello" + ] + } + } + ], "readOnly": true, "params": [ { @@ -12883,6 +17655,20 @@ export const ACTIONS: CatalogAction[] = [ "name": "unlike_qzone", "aliases": [], "summary": "取消对一条说说的点赞(QQ 空间;取消赞端点待真机核实)", + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "取消对一条说说的点赞(QQ 空间;取消赞端点待真机核实)", + "params": { + "tid": "", + "target_uin": 10001 + }, + "notes": "tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。" + } + ], "readOnly": false, "params": [ { @@ -12954,6 +17740,106 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "以流式分块方式上传文件到机器人本地(返回可用于发送的本地路径)", "returns": "流式帧:分块确认 type=stream、完成 type=response(含 file_path/file_name/file_size/sha256)", + "returnsSchema": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "const": "stream" + }, + "stream_id": { + "type": "string" + }, + "status": { + "enum": [ + "file_created", + "chunk_received" + ] + }, + "received_chunks": { + "type": "integer" + }, + "total_chunks": { + "type": "integer" + } + }, + "required": [ + "type", + "stream_id", + "status", + "received_chunks", + "total_chunks" + ] + }, + { + "type": "object", + "properties": { + "type": { + "const": "response" + }, + "stream_id": { + "type": "string" + }, + "status": { + "const": "file_complete" + }, + "received_chunks": { + "type": "integer" + }, + "total_chunks": { + "type": "integer" + }, + "file_path": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "file_size": { + "type": "integer" + }, + "sha256": { + "type": "string" + } + }, + "required": [ + "type", + "stream_id", + "status", + "received_chunks", + "total_chunks", + "file_path", + "file_name", + "file_size", + "sha256" + ] + } + ], + "description": "分块确认与完成帧的 data;reset 和其他失败由错误帧返回。必须等到 file_complete 才能使用 file_path" + }, + "examples": [ + { + "summary": "上传分块", + "params": { + "stream_id": "example-upload-001", + "filename": "hello.txt", + "total_chunks": 1, + "file_size": 5, + "chunk_index": 0, + "chunk_data": "aGVsbG8=" + }, + "notes": "第 1 步:每次上传生成新的 stream_id;这里上传 hello 的唯一分块。通过 invoke_action 调用,分块确认不等于整文件完成。" + }, + { + "summary": "完成同一上传", + "params": { + "stream_id": "example-upload-001", + "is_complete": true + }, + "notes": "第 2 步:等待全部分块确认后,用同一个 stream_id 单独请求完成;成功时返回 file_path。MCP 的 invoke_action 也可通过 execution.input_file 自动完成上传。" + } + ], "readOnly": false, "params": [ { @@ -13129,6 +18015,56 @@ export const ACTIONS: CatalogAction[] = [ "name": "upload_forward_msg", "aliases": [], "summary": "上传转发消息", + "returnsSchema": { + "type": "object", + "properties": { + "res_id": { + "type": "string" + }, + "forward_id": { + "type": "string" + }, + "message_id": { + "const": 0, + "description": "仅上传资源,不代表已发送消息;0 不能用于消息查询或撤回" + }, + "group_id": { + "type": "integer", + "description": "仅请求提供正数 group_id 时出现" + } + }, + "required": [ + "res_id", + "forward_id", + "message_id" + ] + }, + "examples": [ + { + "summary": "上传转发消息", + "params": { + "group_id": 20001, + "messages": [ + { + "type": "node", + "data": { + "user_id": 10001, + "nickname": "示例用户", + "content": [ + { + "type": "text", + "data": { + "text": "转发内容" + } + } + ] + } + } + ] + }, + "notes": "仅上传合并转发资源,不发送聊天消息;message_id=0 是占位值,后续使用 res_id。" + } + ], "readOnly": false, "params": [ { @@ -13184,6 +18120,51 @@ export const ACTIONS: CatalogAction[] = [ "name": "upload_foward_msg", "aliases": [], "summary": "上传转发消息(别名拼写)", + "returnsSchema": { + "type": "object", + "properties": { + "res_id": { + "type": "string" + }, + "forward_id": { + "type": "string" + }, + "message_id": { + "const": 0, + "description": "仅上传资源,不代表已发送消息;0 不能用于消息查询或撤回" + } + }, + "required": [ + "res_id", + "forward_id", + "message_id" + ] + }, + "examples": [ + { + "summary": "上传转发消息(别名拼写)", + "params": { + "messages": [ + { + "type": "node", + "data": { + "user_id": 10001, + "nickname": "示例用户", + "content": [ + { + "type": "text", + "data": { + "text": "转发内容" + } + } + ] + } + } + ] + }, + "notes": "历史拼写兼容接口,仅上传资源;message_id=0,返回值没有 group_id。" + } + ], "readOnly": false, "params": [ { @@ -13240,6 +18221,28 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "上传群文件", "returns": "{ file_id: string }", + "returnsSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string" + } + }, + "required": [ + "file_id" + ] + }, + "examples": [ + { + "summary": "上传群文件", + "params": { + "group_id": 20001, + "file": "file:///path/to/report.pdf", + "name": "报告.pdf" + }, + "notes": "示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。" + } + ], "readOnly": false, "params": [ { @@ -13343,6 +18346,22 @@ export const ACTIONS: CatalogAction[] = [ { "name": "upload_image_to_qun_album", "aliases": [], + "returnsSchema": { + "type": "null", + "description": "成功时 data 为 null;失败另见响应的 status、retcode 和 wording" + }, + "examples": [ + { + "summary": "基本调用", + "params": { + "group_id": 20001, + "album_id": "", + "album_name": "活动照片", + "file": "file:///path/to/photo.png" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。" + } + ], "readOnly": false, "params": [ { @@ -13424,6 +18443,28 @@ export const ACTIONS: CatalogAction[] = [ "aliases": [], "summary": "上传私聊文件", "returns": "{ file_id: string }", + "returnsSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string" + } + }, + "required": [ + "file_id" + ] + }, + "examples": [ + { + "summary": "上传私聊文件", + "params": { + "user_id": 10001, + "file": "file:///path/to/report.pdf", + "name": "报告.pdf" + }, + "notes": "示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。 默认上传并发送给该好友。" + } + ], "readOnly": false, "params": [ { @@ -13513,6 +18554,18 @@ export const ACTIONS: CatalogAction[] = [ "id" ] }, + "examples": [ + { + "summary": "上传视频到群相册", + "params": { + "group_id": 20001, + "album_id": "", + "album_name": "活动视频", + "file": "file:///path/to/video.mp4" + }, + "notes": "album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。" + } + ], "readOnly": false, "params": [ { diff --git a/packages/mcp/src/tools.ts b/packages/mcp/src/tools.ts index 53a58820..5bf258ce 100644 --- a/packages/mcp/src/tools.ts +++ b/packages/mcp/src/tools.ts @@ -66,7 +66,8 @@ const DOCS_TOOLS: Tool[] = [ }, { name: 'get_action', - description: '获取 action 文档、参数 JSON Schema、约束和返回类型。', + description: + '获取 action 文档、参数与返回数据 JSON Schema、约束和调用示例。示例中的标识和路径需替换;流式返回结构描述单帧数据。', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'action 名(接受别名)' } }, diff --git a/packages/mcp/src/types.ts b/packages/mcp/src/types.ts index 28e7a931..4721c59e 100644 --- a/packages/mcp/src/types.ts +++ b/packages/mcp/src/types.ts @@ -26,8 +26,15 @@ export interface CatalogAction { category?: string; summary?: string; returns?: string; - /** JSON Schema for the action's `data` payload (absent when undocumented). */ + /** Successful OneBot `data`, or one successful stream frame's `data`. + * Does not describe the envelope or MCP's aggregated stream result. */ returnsSchema?: Record; + /** Illustrative OneBot params; identifiers/paths must be replaced before use. */ + examples?: ReadonlyArray<{ + summary: string; + params: Record; + notes?: string; + }>; /** True only for pure data-fetch actions (no side effects). Drives the * read/write tool routing: read-only → query_action, else → invoke_action. * Classified at the source spec by what the action's `run` actually does. */ diff --git a/packages/mcp/tests/action-contracts.test.ts b/packages/mcp/tests/action-contracts.test.ts new file mode 100644 index 00000000..a5677b98 --- /dev/null +++ b/packages/mcp/tests/action-contracts.test.ts @@ -0,0 +1,245 @@ +import { describe, expect, it, vi } from 'vitest'; +import { AjvJsonSchemaValidator } from '@modelcontextprotocol/sdk/validation/ajv-provider.js'; +import { collectActionDocs } from '@snowluma/onebot/action-docs'; +import { ApiHandler, type ApiActionContext } from '@snowluma/onebot/api-handler'; +import type { JsonObject, JsonValue } from '@snowluma/onebot/types'; +import { ACTIONS } from '../src/generated/catalog'; +import { callTool } from '../src/tools'; + +const validator = new AjvJsonSchemaValidator(); + +function resultValidator(name: string) { + const action = ACTIONS.find((entry) => entry.name === name); + + if (!action?.returnsSchema) { + throw new Error(`missing result schema: ${name}`); + } + + return validator.getValidator(action.returnsSchema); +} + +function matchesResult(name: string, data: unknown): void { + const result = resultValidator(name)(data); + expect(result.valid, `${name}: ${result.errorMessage}`).toBe(true); +} + +/** Unexpected dependencies fail rather than contacting an account or a network. */ +function context(values: Record = {}): ApiActionContext { + return new Proxy(values, { + get(target, key) { + if (typeof key === 'string' && Object.hasOwn(target, key)) { + return target[key]; + } + + throw new Error(`unstubbed action context: ${String(key)}`); + }, + }) as unknown as ApiActionContext; +} + +describe('bundled action contracts', () => { + it('matches the owning specs, without relying on a second hand-authored registry', () => { + expect(ACTIONS).toEqual(JSON.parse(JSON.stringify(collectActionDocs()))); + }); + + it.each(ACTIONS)('$name publishes compilable schemas and schema-valid examples', (action) => { + expect(() => resultValidator(action.name)).not.toThrow(); + const validateInput = validator.getValidator(action.inputSchema); + expect(action.examples?.length).toBeGreaterThan(0); + + for (const example of action.examples ?? []) { + const result = validateInput(example.params); + expect(result.valid, `${action.name}: ${result.errorMessage}`).toBe(true); + } + }); + + it('exposes full contracts through get_action and aliases without executing anything', async () => { + const client = { call: vi.fn(), callStream: vi.fn() }; + + for (const action of ACTIONS) { + for (const name of [action.name, ...action.aliases]) { + const result = await callTool('get_action', { name }, { mode: 'docs', client }); + expect(result.structuredContent.result).toEqual(action); + expect(result.content[0]).toEqual({ + type: 'text', + text: JSON.stringify(action, null, 2), + }); + } + } + + expect(client.call).not.toHaveBeenCalled(); + expect(client.callStream).not.toHaveBeenCalled(); + }); + + it.each(['list_actions', 'search_actions'])('keeps %s compact', async (name) => { + const result = await callTool(name, { query: 'send_group_msg' }, { mode: 'docs' }); + const block = result.content[0]; + expect(block.type).toBe('text'); + + if (block.type !== 'text') { + throw new Error('expected text result'); + } + + const payload = JSON.parse(block.text); + expect(payload.actions.length).toBeGreaterThan(0); + + for (const action of payload.actions) { + expect(action).not.toHaveProperty('examples'); + expect(action).not.toHaveProperty('returnsSchema'); + } + }); +}); + +describe('result schemas against actual OneBot handlers', () => { + it('preserves signed message IDs and documents the upload-only sentinel separately', async () => { + const api = new ApiHandler( + context({ + sendGroupMessage: async () => ({ messageId: -123 }), + sendForwardMsg: async () => ({ forwardId: 'example-forward' }), + }), + ); + const send = await api.handle('send_group_msg', { group_id: 20001, message: 'hello' }); + expect(send).toEqual({ status: 'ok', retcode: 0, data: { message_id: -123 } }); + matchesResult('send_group_msg', send.data); + expect(resultValidator('send_group_msg')({ message_id: 0 }).valid).toBe(false); + + for (const name of ['upload_forward_msg', 'upload_foward_msg']) { + const uploaded = await api.handle(name, { + group_id: 20001, + messages: [ + { + type: 'node', + data: { user_id: 10001, nickname: 'example', content: 'hello' }, + }, + ], + }); + expect(uploaded.status).toBe('ok'); + expect(uploaded.data).toMatchObject({ message_id: 0, res_id: 'example-forward' }); + matchesResult(name, uploaded.data); + + if (name === 'upload_forward_msg') { + expect(uploaded.data).toHaveProperty('group_id', 20001); + } else { + expect(uploaded.data).not.toHaveProperty('group_id'); + } + } + }); + + it('distinguishes null, empty objects, and failed unsupported calls', async () => { + const api = new ApiHandler( + context({ + bridge: { + apis: { + friend: { setRemark: async () => {} }, + profile: { setInputStatus: async () => {} }, + }, + }, + }), + ); + const empty = await api.handle('set_friend_remark', { user_id: 10001, remark: 'example' }); + expect(empty.data).toBeNull(); + matchesResult('set_friend_remark', empty.data); + expect(resultValidator('set_friend_remark')({}).valid).toBe(false); + const object = await api.handle('set_input_status', { user_id: 10001 }); + expect(object.data).toEqual({}); + matchesResult('set_input_status', object.data); + expect(resultValidator('set_input_status')(null).valid).toBe(false); + + for (const name of ['set_restart', 'create_collection', '.get_word_slices']) { + const failure = await api.handle(name, {}); + expect(failure.status).toBe('failed'); + expect(resultValidator(name)(failure.data).valid).toBe(false); + } + }); + + it('documents actual media string sizes and optional converted record fields', async () => { + const media = { + file: 'https://example.com/media', + url: 'https://example.com/media', + file_size: '42', + file_name: 'voice.amr', + }; + const api = new ApiHandler( + context({ + getImageInfo: async () => media, + getRecordInfo: async () => media, + bridge: { + apis: { + extras: { convertRecord: async () => ({ base64: 'aGVsbG8=', size: 5 }) }, + }, + }, + }), + ); + + for (const name of ['get_image', 'get_file', 'get_record']) { + const result = await api.handle(name, { file: 'cached-media' }); + expect(result.status).toBe('ok'); + matchesResult(name, result.data); + expect(resultValidator(name)({ ...media, file_size: 42 }).valid).toBe(false); + } + + const converted = await api.handle('get_record', { + file: 'cached-media', + out_format: 'mp3', + }); + expect(converted.data).toEqual({ ...media, out_format: 'mp3', base64: 'aGVsbG8=' }); + matchesResult('get_record', converted.data); + }); + + it('describes get_msg data without the event envelope for both conversation types', async () => { + for (const messageType of ['group', 'private']) { + const event: JsonObject = { + post_type: 'message', + self_id: 10002, + message_type: messageType, + message_id: -123, + user_id: 10001, + time: 1700000000, + message: [{ type: 'text', data: { text: 'hello' } }], + raw_message: 'hello', + ...(messageType === 'group' ? { group_id: 20001 } : { target_id: 10002 }), + }; + const api = new ApiHandler( + context({ + getMessage: async () => event, + refreshMessageMedia: async () => {}, + }), + ); + const result = await api.handle('get_msg', { message_id: -123 }); + expect(result.status).toBe('ok'); + expect(result.data).toMatchObject({ real_id: -123, message_id: -123 }); + expect(result.data).not.toHaveProperty('post_type'); + expect(result.data).not.toHaveProperty('self_id'); + matchesResult('get_msg', result.data); + } + }); + + it('describes successful stream frame data, not MCP aggregate results or failed frames', async () => { + const api = new ApiHandler(context()); + const frames: JsonValue[] = []; + const response = await api.handle( + 'test_download_stream', + { error: false }, + { + send: async (frame) => { + frames.push(frame); + }, + }, + ); + expect(frames).toHaveLength(10); + expect(response.status).toBe('ok'); + + for (const frame of [...frames, response.data]) { + matchesResult('test_download_stream', frame); + } + + expect( + resultValidator('test_download_stream')({ frame_count: 11, frames, terminal: response }) + .valid, + ).toBe(false); + expect( + resultValidator('test_download_stream')({ type: 'error', data_type: 'error' }).valid, + ).toBe(false); + const failure = await api.handle('test_download_stream', { error: true }); + expect(failure.status).toBe('failed'); + }); +}); diff --git a/packages/onebot/src/action-docs.ts b/packages/onebot/src/action-docs.ts index ad8be9d9..2dbce1ad 100644 --- a/packages/onebot/src/action-docs.ts +++ b/packages/onebot/src/action-docs.ts @@ -68,6 +68,21 @@ function renderAction(doc: ActionDoc): string { lines.push('', `**返回:** \`${doc.returns}\``); } + for (const example of doc.examples ?? []) { + lines.push('', `**示例:${example.summary}**`); + + if (example.notes) { + lines.push('', example.notes); + } + + lines.push( + '', + '```json', + JSON.stringify({ action: doc.name, params: example.params }, null, 2), + '```', + ); + } + return lines.join('\n'); } diff --git a/packages/onebot/src/action-kit.ts b/packages/onebot/src/action-kit.ts index 6d05b37c..422302d4 100644 --- a/packages/onebot/src/action-kit.ts +++ b/packages/onebot/src/action-kit.ts @@ -629,6 +629,16 @@ const RULES: RuleBuilders> = { export interface ParamDoc extends FieldDoc { name: string; } + +/** Documentation only: never execute examples while collecting the catalog. */ +export interface ActionExample { + summary: string; + /** OneBot params, also passed as the MCP action tool's `params`. */ + params: JsonObject; + /** Prerequisites, placeholder sources, or ordering within a workflow. */ + notes?: string; +} + export interface ActionDoc { name: string; aliases: string[]; @@ -636,10 +646,11 @@ export interface ActionDoc { category?: string; summary?: string; returns?: string; - /** JSON Schema for the `data` payload inside the OneBot envelope (NOT the - * whole envelope). Optional + authored incrementally; absent ⇒ data shape - * is documented only by the prose `returns`. */ + /** JSON Schema for successful OneBot `data`, not the envelope. For streams, + * describes each successful frame, not an MCP aggregated tool result. + * Optional for third-party specs; the bundled catalog requires coverage. */ returnsSchema?: JsonSchema; + examples?: readonly ActionExample[]; /** True only for pure data-fetch actions (no side effects). Drives the MCP's * read/write tool routing; defaults to false (treated as a write). */ readOnly: boolean; @@ -687,6 +698,7 @@ interface ActionDef { returns?: string; /** JSON Schema for the `data` payload (NOT the envelope). See ActionDoc. */ returnsSchema?: JsonSchema; + examples?: readonly ActionExample[]; /** Mark true ONLY for pure data-fetch actions with no side effects. Default * false = write. Surfaced via describe() into the catalog for the MCP's * read/write routing. Classify by what `run` actually does, not the name. */ @@ -775,6 +787,7 @@ export function defineAction(def: ActionDef): ActionSpec { summary: def.summary, returns: def.returns, returnsSchema: def.returnsSchema, + ...(def.examples ? { examples: def.examples } : {}), readOnly: def.readOnly ?? false, params: entries.map(([name, field]) => ({ name, ...field.doc })), invariants: rules.map((rule) => rule.doc), @@ -797,6 +810,7 @@ interface StreamActionDef { summary?: string; returns?: string; returnsSchema?: JsonSchema; + examples?: readonly ActionExample[]; readOnly?: boolean; params: S; rules?: (r: RuleBuilders>) => readonly CrossFieldRule[]; @@ -823,6 +837,7 @@ export function defineStreamAction(def: StreamActionDef): Str summary: def.summary, returns: def.returns, returnsSchema: def.returnsSchema, + examples: def.examples, readOnly: def.readOnly, params: def.params, rules: def.rules, @@ -865,6 +880,7 @@ interface PresetDef { summary?: string; returns?: string; returnsSchema?: JsonSchema; + examples?: readonly ActionExample[]; readOnly?: boolean; params?: S; rules?: (r: RuleBuilders & Extra>) => readonly CrossFieldRule[]; diff --git a/packages/onebot/src/action-result-schemas.ts b/packages/onebot/src/action-result-schemas.ts new file mode 100644 index 00000000..f900272e --- /dev/null +++ b/packages/onebot/src/action-result-schemas.ts @@ -0,0 +1,126 @@ +import type { JsonSchema } from './action-kit'; + +// Documentation fragments only. They never validate or transform runtime data. +export const messageIdSchema: JsonSchema = { + type: 'integer', + minimum: -2147483648, + maximum: 2147483647, + not: { const: 0 }, + description: '非零有符号 32 位消息 ID,负值合法;必须原样保留', +}; + +export const messageResultSchema: JsonSchema = { + type: 'object', + properties: { message_id: messageIdSchema }, + required: ['message_id'], +}; + +export const messageSegmentSchema: JsonSchema = { + type: 'object', + properties: { + type: { type: 'string', description: '消息段类型,例如 text、image、reply' }, + data: { + type: 'object', + description: '字段随消息段类型变化,例如 text 段包含 text,reply 段包含 id', + additionalProperties: true, + }, + }, + required: ['type', 'data'], +}; + +/** Stored events retain subtype-specific fields; legacy records can omit fields. */ +export const messageDataSchema = { + type: 'object', + properties: { + time: { type: 'integer', description: '消息时间,Unix 秒' }, + message_type: { type: 'string', enum: ['private', 'group'] }, + sub_type: { type: 'string', description: '消息子类型' }, + message_id: messageIdSchema, + real_id: { type: 'integer', description: '原始 message_id;旧历史记录缺失时可为 0' }, + user_id: { type: 'integer', description: '发送者 QQ 号' }, + group_id: { type: 'integer', description: '群聊消息的群号,私聊可省略' }, + target_id: { type: 'integer', description: '私聊目标 QQ 号(若有)' }, + message: { type: 'array', items: messageSegmentSchema }, + raw_message: { type: 'string', description: '消息的 CQ 码字符串形式' }, + font: { type: 'integer' }, + sender: { + type: 'object', + properties: { + user_id: { type: 'integer' }, + nickname: { type: 'string' }, + card: { type: 'string' }, + sex: { type: 'string', enum: ['male', 'female', 'unknown'] }, + age: { type: 'integer' }, + area: { type: 'string' }, + level: { type: 'string' }, + role: { type: 'string', enum: ['owner', 'admin', 'member'] }, + title: { type: 'string' }, + }, + additionalProperties: true, + }, + }, + additionalProperties: true, +} satisfies JsonSchema; + +// Forward nodes carry their original identifiers, not locally hashed message IDs. +export const forwardNodeSchema: JsonSchema = { + ...messageDataSchema, + properties: { + ...messageDataSchema.properties, + self_id: { type: 'integer' }, + post_type: { const: 'message' }, + message_format: { const: 'array' }, + message_id: { type: 'integer', description: '转发节点原始标识,缺失时为 0' }, + message_seq: { type: 'integer', description: '节点消息序号,缺失时使用节点标识或 0' }, + real_id: { type: 'integer', description: '与节点 message_id 相同' }, + }, + required: [ + 'self_id', + 'user_id', + 'time', + 'message_id', + 'message_seq', + 'real_id', + 'message_type', + 'sender', + 'raw_message', + 'font', + 'sub_type', + 'message', + 'message_format', + 'post_type', + ], +}; + +export const mediaInfoSchema: JsonSchema = { + type: 'object', + properties: { + file: { type: 'string', description: '媒体地址;图片无地址时可为原文件标识' }, + url: { type: 'string', description: '媒体下载地址,图片地址可能为空' }, + file_size: { type: 'string', description: '原媒体字节数的十进制字符串' }, + file_name: { type: 'string', description: '文件名或缓存标识' }, + }, + required: ['file', 'url', 'file_size', 'file_name'], +}; + +export const flashFileSchema: JsonSchema = { + type: 'object', + properties: { + fileset_id: { type: 'string' }, + file_name: { type: 'string' }, + orig_name: { type: 'string' }, + size: { type: 'number', description: '文件字节数;此接口字段名为 size' }, + share_url: { type: 'string' }, + file_id: { type: 'string' }, + download_url: { type: 'string' }, + }, + required: [ + 'fileset_id', + 'file_name', + 'orig_name', + 'size', + 'share_url', + 'file_id', + 'download_url', + ], +}; diff --git a/packages/onebot/src/actions/extended.ts b/packages/onebot/src/actions/extended.ts index 84b603b3..e52969aa 100644 --- a/packages/onebot/src/actions/extended.ts +++ b/packages/onebot/src/actions/extended.ts @@ -22,6 +22,15 @@ import { import { defineAction, groupAction, groupUserAction, f, type Field } from '../action-kit'; import { groupInfoReturnsSchema } from './group-info'; import { GROUP_MESSAGE_EVENT, hashMessageIdInt32 } from '../message-id'; +import { + messageIdSchema, + messageDataSchema, + forwardNodeSchema, + messageSegmentSchema, + mediaInfoSchema, + messageResultSchema, + flashFileSchema, +} from '../action-result-schemas'; const DOWNLOAD_FILE_MAX_BYTES = 1024 * 1024 * 1024; // 1 GiB const DEFAULT_DOWNLOAD_FILE_TIMEOUT_MS = 60_000; @@ -547,6 +556,19 @@ export const actions = [ // 赞 defineAction({ name: 'send_like', + examples: [ + { + summary: '点赞', + params: { + user_id: 10001, + times: 1, + }, + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '点赞', params: { user_id: f.userId(), @@ -561,6 +583,19 @@ export const actions = [ // 拍一拍 defineAction({ name: 'friend_poke', + examples: [ + { + summary: '好友拍一拍', + params: { + user_id: 10001, + }, + notes: '戳一戳指定好友。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '好友拍一拍', params: { user_id: f.userId(), @@ -574,6 +609,20 @@ export const actions = [ groupUserAction({ name: 'group_poke', + examples: [ + { + summary: '群拍一拍', + params: { + group_id: 20001, + user_id: 10001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '群拍一拍', run: async (p, ctx) => { await ctx.bridge.apis.interaction.sendPoke(true, p.group_id, p.user_id); @@ -583,6 +632,20 @@ export const actions = [ defineAction({ name: 'send_poke', + examples: [ + { + summary: '拍一拍(群聊/私聊自动路由)', + params: { + group_id: 20001, + user_id: 10001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '拍一拍(群聊/私聊自动路由)', params: { user_id: f.userId(), @@ -602,6 +665,19 @@ export const actions = [ // 精华消息 defineAction({ name: 'set_essence_msg', + examples: [ + { + summary: '设置精华消息', + params: { + message_id: -123456, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置精华消息', params: { message_id: f.messageId() }, run: async (p, ctx) => { @@ -612,6 +688,19 @@ export const actions = [ defineAction({ name: 'delete_essence_msg', + examples: [ + { + summary: '移除精华消息', + params: { + message_id: -123456, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '移除精华消息', params: { message_id: f.messageId() }, run: async (p, ctx) => { @@ -622,6 +711,64 @@ export const actions = [ groupAction({ name: 'get_essence_msg_list', + examples: [ + { + summary: '获取精华消息列表', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], + returnsSchema: { + type: 'array', + items: { + type: 'object', + properties: { + msg_seq: { + type: 'integer', + }, + msg_random: { + type: 'integer', + }, + sender_id: { + type: 'integer', + }, + sender_nick: { + type: 'string', + }, + sender_time: { + type: 'integer', + }, + operator_id: { + type: 'integer', + }, + operator_nick: { + type: 'string', + }, + operator_time: { + type: 'integer', + }, + message_id: messageIdSchema, + content: { + type: 'array', + items: messageSegmentSchema, + }, + }, + required: [ + 'msg_seq', + 'msg_random', + 'sender_id', + 'sender_nick', + 'sender_time', + 'operator_id', + 'operator_nick', + 'operator_time', + 'message_id', + 'content', + ], + }, + }, summary: '获取精华消息列表', readOnly: true, run: async (p, ctx) => { @@ -650,6 +797,22 @@ export const actions = [ // 群聊表情回应 defineAction({ name: 'set_group_reaction', + examples: [ + { + summary: '群聊表情回应', + params: { + message_id: -123456, + group_id: 20001, + code: '128077', + is_set: true, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '群聊表情回应', params: { group_id: f.groupId().optional(), @@ -694,6 +857,19 @@ export const actions = [ // 删除收藏表情(Faceroam.OpReq opType=2) defineAction({ name: 'delete_custom_face', + examples: [ + { + summary: '删除收藏表情', + params: { + emoji_id: '', + }, + notes: 'emoji_id 来自 fetch_custom_face_detail。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '删除收藏表情', params: { emoji_id: f.string({ allowEmpty: false }) }, run: async (p, ctx) => { @@ -705,6 +881,24 @@ export const actions = [ // 添加收藏表情(ImgStore.BDHExpressionRoam + highway HTTP 上传) defineAction({ name: 'add_custom_face', + examples: [ + { + summary: '添加收藏表情', + params: { + file: 'file:///path/to/emoji.png', + }, + notes: '示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + emoji_id: { + type: 'string', + }, + }, + required: ['emoji_id'], + }, summary: '添加收藏表情', params: { file: f.image() }, run: async (p, ctx) => { @@ -716,6 +910,20 @@ export const actions = [ // 修改收藏表情备注(OIDB 0x902e_1 opType=3) defineAction({ name: 'modify_custom_face', + examples: [ + { + summary: '修改收藏表情备注', + params: { + emoji_id: '', + desc: '常用表情', + }, + notes: 'emoji_id 来自 fetch_custom_face_detail。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '修改收藏表情备注', params: { emoji_id: f.string({ allowEmpty: false }), @@ -734,6 +942,19 @@ export const actions = [ // 位置变量,移到其他位置服务端不生效。所以这个 action 只做"移到最前"。 defineAction({ name: 'move_custom_face_to_front', + examples: [ + { + summary: '收藏表情移到最前', + params: { + emoji_id: '', + }, + notes: 'emoji_id 来自 fetch_custom_face_detail。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '收藏表情移到最前', params: { emoji_id: f.string({ allowEmpty: false }) }, run: async (p, ctx) => { @@ -745,6 +966,16 @@ export const actions = [ // 消息历史 groupAction({ name: 'get_group_msg_history', + examples: [ + { + summary: '获取群消息历史', + params: { + group_id: 20001, + count: 20, + }, + notes: '省略 message_id 从最新消息开始;翻页使用上一页实际 message_id,不要自行加减。', + }, + ], summary: '获取群消息历史', readOnly: true, returns: '{ messages }:群消息事件对象数组(每项为 OneBot 消息事件,内部字段不固定)。', @@ -753,8 +984,8 @@ export const actions = [ properties: { messages: { type: 'array', - description: '消息事件对象数组', - items: { type: 'object' }, + description: '消息对象数组;去除 post_type/self_id,附带 real_id', + items: messageDataSchema, }, }, required: ['messages'], @@ -787,6 +1018,16 @@ export const actions = [ defineAction({ name: 'get_friend_msg_history', + examples: [ + { + summary: '获取好友消息历史(无锚点时从服务器获取最新双向记录)', + params: { + user_id: 10001, + count: 20, + }, + notes: '省略 message_id 从最新消息开始;只读取当前账号消息。', + }, + ], summary: '获取好友消息历史(无锚点时从服务器获取最新双向记录)', readOnly: true, returns: '{ messages }:好友消息事件对象数组(每项为 OneBot 消息事件,内部字段不固定)。', @@ -795,8 +1036,8 @@ export const actions = [ properties: { messages: { type: 'array', - description: '消息事件对象数组', - items: { type: 'object' }, + description: '消息对象数组;去除 post_type/self_id,附带 real_id', + items: messageDataSchema, }, }, required: ['messages'], @@ -832,6 +1073,20 @@ export const actions = [ // 标记消息已读 defineAction({ name: 'mark_group_msg_as_read', + examples: [ + { + summary: '标记群消息已读', + params: { + message_id: -123456, + group_id: 20001, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '标记群消息已读', params: { message_id: f.messageId(), @@ -861,6 +1116,20 @@ export const actions = [ defineAction({ name: 'mark_private_msg_as_read', + examples: [ + { + summary: '标记私聊消息已读', + params: { + message_id: -123456, + user_id: 10001, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '标记私聊消息已读', params: { message_id: f.messageId(), @@ -890,6 +1159,19 @@ export const actions = [ defineAction({ name: 'mark_msg_as_read', + examples: [ + { + summary: '标记消息已读(群聊/私聊自动路由)', + params: { + message_id: -123456, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '标记消息已读(群聊/私聊自动路由)', params: { message_id: f.messageId(), @@ -922,6 +1204,22 @@ export const actions = [ // 群公告 groupAction({ name: '_send_group_notice', + examples: [ + { + summary: '发送群公告(支持置顶、弹窗、新成员、群昵称引导与确认)', + params: { + group_id: 20001, + content: '欢迎参加本周讨论', + type: 1, + pinned: 0, + }, + notes: '需要群公告管理权限;普通公告 type=1,新成员公告 type=20。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '发送群公告(支持置顶、弹窗、新成员、群昵称引导与确认)', params: { content: f.string({ allowEmpty: false }).describe('公告正文'), @@ -983,6 +1281,110 @@ export const actions = [ groupAction({ name: '_get_group_notice', + examples: [ + { + summary: '获取群公告', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], + returnsSchema: { + type: 'array', + items: { + type: 'object', + properties: { + notice_id: { + type: 'string', + }, + sender_id: { + type: 'number', + }, + publish_time: { + type: 'number', + }, + message: { + type: 'object', + properties: { + text: { + type: 'string', + }, + image: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + }, + url: { + type: 'string', + }, + height: { + type: 'number', + }, + width: { + type: 'number', + }, + }, + required: ['id', 'url', 'height', 'width'], + }, + }, + images: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + }, + url: { + type: 'string', + }, + height: { + type: 'number', + }, + width: { + type: 'number', + }, + }, + required: ['id', 'url', 'height', 'width'], + }, + description: '与 image 相同的图片列表,保留两个兼容字段', + }, + }, + required: ['text', 'image', 'images'], + }, + settings: { + description: '服务端公告设置,原样透传,可为 null', + }, + read_num: { + type: 'number', + }, + type: { + type: 'number', + }, + pinned: { + type: 'number', + }, + send_to_new_members: { + type: 'boolean', + }, + }, + required: [ + 'notice_id', + 'sender_id', + 'publish_time', + 'message', + 'settings', + 'read_num', + 'type', + 'pinned', + 'send_to_new_members', + ], + }, + }, summary: '获取群公告', returns: '普通公告与新成员公告的合并数组;send_to_new_members 标识后者;图片含 id、url、宽高', @@ -996,6 +1398,52 @@ export const actions = [ // 转发消息。messages/message 互为别名键,两者皆缺省视为未提供。 defineAction({ name: 'upload_forward_msg', + examples: [ + { + summary: '上传转发消息', + params: { + group_id: 20001, + messages: [ + { + type: 'node', + data: { + user_id: 10001, + nickname: '示例用户', + content: [ + { + type: 'text', + data: { + text: '转发内容', + }, + }, + ], + }, + }, + ], + }, + notes: '仅上传合并转发资源,不发送聊天消息;message_id=0 是占位值,后续使用 res_id。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + res_id: { + type: 'string', + }, + forward_id: { + type: 'string', + }, + message_id: { + const: 0, + description: '仅上传资源,不代表已发送消息;0 不能用于消息查询或撤回', + }, + group_id: { + type: 'integer', + description: '仅请求提供正数 group_id 时出现', + }, + }, + required: ['res_id', 'forward_id', 'message_id'], + }, summary: '上传转发消息', params: { messages: forwardMessagesField, @@ -1028,6 +1476,47 @@ export const actions = [ defineAction({ name: 'upload_foward_msg', + examples: [ + { + summary: '上传转发消息(别名拼写)', + params: { + messages: [ + { + type: 'node', + data: { + user_id: 10001, + nickname: '示例用户', + content: [ + { + type: 'text', + data: { + text: '转发内容', + }, + }, + ], + }, + }, + ], + }, + notes: '历史拼写兼容接口,仅上传资源;message_id=0,返回值没有 group_id。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + res_id: { + type: 'string', + }, + forward_id: { + type: 'string', + }, + message_id: { + const: 0, + description: '仅上传资源,不代表已发送消息;0 不能用于消息查询或撤回', + }, + }, + required: ['res_id', 'forward_id', 'message_id'], + }, summary: '上传转发消息(别名拼写)', params: { messages: forwardMessagesField, @@ -1054,6 +1543,16 @@ export const actions = [ // 文件信息 defineAction({ name: 'get_image', + examples: [ + { + summary: '获取图片信息', + params: { + file: '', + }, + notes: 'file 或 file_id 原样取自图片消息段,须在当前账号媒体缓存中存在。', + }, + ], + returnsSchema: mediaInfoSchema, summary: '获取图片信息', readOnly: true, params: { @@ -1079,6 +1578,36 @@ export const actions = [ defineAction({ name: 'get_record', + examples: [ + { + summary: '获取语音信息;传 out_format 则服务端转码并附带 base64', + params: { + file: '', + out_format: 'mp3', + }, + notes: 'file 来自语音消息段;格式转换依赖服务端可用的转换工具,成功时增加 out_format 和 base64。', + }, + ], + returnsSchema: { + allOf: [ + mediaInfoSchema, + { + type: 'object', + properties: { + out_format: { + type: 'string', + enum: ['mp3', 'amr', 'wma', 'm4a', 'spx', 'ogg', 'wav', 'flac'], + }, + base64: { + type: 'string', + description: '转换后音频的 Base64,未请求 out_format 时省略', + }, + }, + }, + ], + description: + '未转换时只有媒体信息;指定 out_format 成功转换后同时增加 out_format 和 base64,file_size 仍为原媒体大小', + }, summary: '获取语音信息;传 out_format 则服务端转码并附带 base64', readOnly: true, returns: @@ -1131,6 +1660,15 @@ export const actions = [ defineAction({ // Primary name matches NapCat for drop-in migration; the rest are aliases. name: ['fetch_ptt_text', 'get_ptt_text', 'get_record_text'], + examples: [ + { + summary: '获取语音转文字结果', + params: { + message_id: -123456, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], summary: '获取语音转文字结果', readOnly: true, returns: '{ text }:语音识别出的文本。', @@ -1152,6 +1690,15 @@ export const actions = [ // Cookie、CSRF 令牌和账号信息 defineAction({ name: 'get_cookies', + examples: [ + { + summary: '获取 Cookies', + params: { + domain: 'qun.qq.com', + }, + notes: '返回登录凭据,仅在有明确用途时读取,请勿公开。', + }, + ], summary: '获取 Cookies', readOnly: true, returns: '{ cookies }:指定域名的 Cookie 字符串。', @@ -1171,6 +1718,12 @@ export const actions = [ defineAction({ name: 'get_csrf_token', + examples: [ + { + summary: '获取 CSRF 令牌', + params: {}, + }, + ], summary: '获取 CSRF 令牌', readOnly: true, returns: '{ token }:CSRF 令牌(bkn,数值)。', @@ -1190,6 +1743,15 @@ export const actions = [ defineAction({ name: 'get_credentials', + examples: [ + { + summary: '获取凭证', + params: { + domain: 'qun.qq.com', + }, + notes: '返回登录凭据,仅在有明确用途时读取,请勿公开。', + }, + ], summary: '获取凭证', readOnly: true, returns: @@ -1213,6 +1775,17 @@ export const actions = [ // 重启和清理缓存 defineAction({ name: 'set_restart', + examples: [ + { + summary: '重启(不支持)', + params: {}, + notes: '当前未实现,调用返回失败;不要将其当作可用能力。', + }, + ], + returnsSchema: { + not: {}, + description: '当前始终返回失败,没有成功 data;失败响应 data 为 null,原因见 wording', + }, summary: '重启(不支持)', params: {}, run: async () => { @@ -1222,6 +1795,17 @@ export const actions = [ defineAction({ name: 'clean_cache', + examples: [ + { + summary: '清理缓存', + params: {}, + notes: '兼容占位接口,当前返回成功但不执行清理。流式临时文件清理请使用 clean_stream_temp_file。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '清理缓存', params: {}, run: async () => { @@ -1232,6 +1816,19 @@ export const actions = [ // 以下接口在方便用户迁移和兼容现有的 OneBot/NapCat 客户端。 defineAction({ name: 'set_friend_remark', + examples: [ + { + summary: '设置好友备注', + params: { + user_id: 10001, + remark: '同学', + }, + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置好友备注', params: { user_id: f.userId(), @@ -1245,6 +1842,20 @@ export const actions = [ defineAction({ name: 'set_group_remark', + examples: [ + { + summary: '设置群备注', + params: { + group_id: 20001, + remark: '学习小组', + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置群备注', params: { group_id: f.groupId(), @@ -1258,6 +1869,21 @@ export const actions = [ defineAction({ name: 'set_msg_emoji_like', + examples: [ + { + summary: '设置消息表情回应', + params: { + message_id: -123456, + emoji_id: '128077', + set: true, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置消息表情回应', params: { message_id: f.messageId(), @@ -1272,6 +1898,17 @@ export const actions = [ defineAction({ name: '_mark_all_as_read', + examples: [ + { + summary: '标记全部已读', + params: {}, + notes: '会将当前账号的会话标记为已读。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '标记全部已读', params: {}, run: async (_p, ctx) => { @@ -1283,6 +1920,15 @@ export const actions = [ groupAction({ name: 'get_group_file_system_info', + examples: [ + { + summary: '获取群文件系统信息', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群文件系统信息', readOnly: true, returns: @@ -1314,6 +1960,13 @@ export const actions = [ defineAction({ name: 'check_url_safely', + examples: [ + { + summary: '检查链接安全性', + params: {}, + notes: '兼容占位接口,当前固定返回 level=1,不能用来证明某个链接安全。', + }, + ], summary: '检查链接安全性', readOnly: true, returns: '{ level }:安全等级(占位实现,恒为 1)。', @@ -1332,6 +1985,19 @@ export const actions = [ defineAction({ name: 'set_qq_profile', + examples: [ + { + summary: '设置 QQ 资料', + params: { + nickname: '示例昵称', + }, + notes: '会修改当前账号的资料;只传入需要修改的字段。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置 QQ 资料', params: { nickname: f.string().optional(), @@ -1346,6 +2012,21 @@ export const actions = [ defineAction({ name: 'set_online_status', + examples: [ + { + summary: '设置在线状态', + params: { + status: 11, + ext_status: 0, + battery_status: 100, + }, + notes: '修改当前账号状态;具体状态码须与目标状态对应。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置在线状态', params: { // 原实现用 asNumber+`status===0` 拒绝,等价于必填非零整数。 @@ -1364,6 +2045,20 @@ export const actions = [ // face_id 和 face_type 可接受数字或数字字符串,以兼容 NapCat;wording 是图标旁展示的可读文本。 defineAction({ name: 'set_diy_online_status', + examples: [ + { + summary: '设置自定义在线状态', + params: { + face_id: 14, + face_type: 1, + wording: '学习中', + }, + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置自定义在线状态', params: { face_id: f.faceId(), @@ -1379,6 +2074,12 @@ export const actions = [ defineAction({ name: 'get_group_ignored_notifies', + examples: [ + { + summary: '获取被过滤的入群请求', + params: {}, + }, + ], summary: '获取被过滤的入群请求', readOnly: true, returns: '被过滤的入群请求数组,每项含群号、申请人、邀请人、留言与处理标记。', @@ -1440,6 +2141,12 @@ export const actions = [ // eventType 已经在当前流水线中编码了请求类别。 defineAction({ name: 'get_group_ignore_add_request', + examples: [ + { + summary: '获取被忽略的入群请求(NapCat)', + params: {}, + }, + ], summary: '获取被忽略的入群请求(NapCat)', readOnly: true, returns: @@ -1499,6 +2206,15 @@ export const actions = [ // 刚下/解的禁言可能在该窗口内未反映,属可接受的低频运维查询取舍。 groupAction({ name: 'get_group_shut_list', + examples: [ + { + summary: '获取群禁言列表', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群禁言列表', readOnly: true, returns: '仍在禁言中的成员数组,每项含 QQ 号、昵称与禁言到期时间戳(秒)。', @@ -1529,6 +2245,36 @@ export const actions = [ // 现有 web cookie/bkn 基建(与群公告/精华同套路),故无需 RE。失败/无打卡返回空表。 groupAction({ name: 'get_group_signed_list', + examples: [ + { + summary: '获取群今日打卡列表', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], + returnsSchema: { + type: 'array', + items: { + type: 'object', + properties: { + user_id: { + type: 'integer', + }, + nick: { + type: 'string', + }, + time: { + type: 'integer', + }, + rank: { + type: 'integer', + }, + }, + required: ['user_id', 'nick', 'time', 'rank'], + }, + }, summary: '获取群今日打卡列表', readOnly: true, run: async (p, ctx) => { @@ -1539,6 +2285,17 @@ export const actions = [ defineAction({ name: 'forward_friend_single_msg', + examples: [ + { + summary: '转发单条消息给好友', + params: { + message_id: -123456, + user_id: 10001, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], + returnsSchema: messageResultSchema, summary: '转发单条消息给好友', params: { message_id: f.messageId(), @@ -1552,6 +2309,17 @@ export const actions = [ defineAction({ name: 'forward_group_single_msg', + examples: [ + { + summary: '转发单条消息到群', + params: { + message_id: -123456, + group_id: 20001, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], + returnsSchema: messageResultSchema, summary: '转发单条消息到群', params: { message_id: f.messageId(), @@ -1565,6 +2333,16 @@ export const actions = [ defineAction({ name: 'get_profile_like', + examples: [ + { + summary: '获取资料点赞', + params: { + user_id: 10001, + start: 0, + count: 10, + }, + }, + ], summary: '获取资料点赞', readOnly: true, returns: '点赞资料:uid、最近点赞时间、收藏与点赞统计及用户明细。', @@ -1619,6 +2397,15 @@ export const actions = [ defineAction({ name: 'fetch_custom_face', + examples: [ + { + summary: '获取自定义表情', + params: { + count: 10, + return_type: 'url', + }, + }, + ], summary: '获取自定义表情', readOnly: true, returns: '字符串数组:return_type=url 时为图片 URL,return_type=id 时为 emoji_id。', @@ -1643,6 +2430,14 @@ export const actions = [ defineAction({ name: 'fetch_custom_face_detail', + examples: [ + { + summary: '获取自定义表情详情', + params: { + count: 10, + }, + }, + ], summary: '获取自定义表情详情', readOnly: true, returns: '自定义表情详情数组,包含资源标识、图片地址、摘要与描述。', @@ -1682,6 +2477,16 @@ export const actions = [ defineAction({ name: 'get_emoji_likes', + examples: [ + { + summary: '获取表情回应用户', + params: { + message_id: -123456, + emoji_id: '128077', + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], summary: '获取表情回应用户', readOnly: true, returns: '{ emoji_like_list }:回应该表情的用户列表(nick_name 恒为空串)。', @@ -1720,6 +2525,17 @@ export const actions = [ defineAction({ name: 'fetch_emoji_like', + examples: [ + { + summary: '获取表情回应用户(NapCat 分页)', + params: { + message_id: -123456, + emojiId: '128077', + count: 10, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。 后续页原样传回上页 cookie。', + }, + ], summary: '获取表情回应用户(NapCat 分页)', readOnly: true, returns: '分页的表情回应用户列表(NapCat 形状),含分页游标 cookie 与首/末页标记。', @@ -1775,6 +2591,15 @@ export const actions = [ defineAction({ name: 'get_msg_emoji_likes', + examples: [ + { + summary: '获取一条消息的全部表情回应', + params: { + message_id: -123456, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], summary: '获取一条消息的全部表情回应', readOnly: true, returns: '该消息上每个表情回应的编号、数量与用户列表。', @@ -1814,6 +2639,12 @@ export const actions = [ defineAction({ name: 'get_friends_with_category', + examples: [ + { + summary: '获取分组好友列表', + params: {}, + }, + ], summary: '获取分组好友列表', readOnly: true, returns: '好友分组数组;每组包含 categoryId、categoryName、categoryMbCount 和 buddyList。', @@ -1863,6 +2694,20 @@ export const actions = [ defineAction({ name: 'set_friends_category', + examples: [ + { + summary: '移动好友到指定分组', + params: { + uin: 10001, + categoryName: '同学', + }, + notes: 'categoryId 与 categoryName 二选一。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '移动好友到指定分组', returns: '成功时返回空数据。', params: { @@ -1891,6 +2736,15 @@ export const actions = [ // 用同名接口给出语义有偏差的近似),接受 count 入参以兼容客户端盲调。 defineAction({ name: 'get_recent_contact', + examples: [ + { + summary: '获取最近会话(占位)', + notes: '兼容占位接口,当前始终返回空数组,不能据此判断真实会话列表。', + params: { + count: 10, + }, + }, + ], summary: '获取最近会话(占位)', readOnly: true, returns: '占位实现,恒返回空数组。', @@ -1905,6 +2759,14 @@ export const actions = [ defineAction({ name: 'get_online_clients', + examples: [ + { + summary: '获取在线客户端', + params: { + no_cache: false, + }, + }, + ], summary: '获取在线客户端', readOnly: true, returns: '{ clients }:QQ 本次会话最近推送的在线设备快照。', @@ -1977,6 +2839,15 @@ export const actions = [ // 'snowluma'),对调用方更有信息量。属有意的行为分歧,仅形状对齐。 defineAction({ name: '_get_model_show', + examples: [ + { + summary: '获取机型展示(兼容 mock)', + params: { + model: '示例机型', + }, + notes: '兼容占位接口,仅将传入的 model(或默认值)放进展示结构,不查询设备机型。', + }, + ], summary: '获取机型展示(兼容 mock)', readOnly: true, returns: '数组,每项含 variants(回显请求的机型名与 need_pay 标记)。', @@ -2011,6 +2882,17 @@ export const actions = [ defineAction({ name: '_set_model_show', + examples: [ + { + summary: '设置机型展示(占位)', + params: {}, + notes: '兼容占位接口,当前返回成功但不修改机型展示。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置机型展示(占位)', params: {}, run: async () => { @@ -2020,6 +2902,15 @@ export const actions = [ groupAction({ name: 'get_group_at_all_remain', + examples: [ + { + summary: '获取群 @全体成员 剩余次数', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群 @全体成员 剩余次数', readOnly: true, returns: @@ -2051,6 +2942,21 @@ export const actions = [ defineAction({ name: 'get_unidirectional_friend_list', + examples: [ + { + summary: '获取单向好友列表', + params: {}, + }, + ], + returnsSchema: { + type: 'array', + items: { + type: 'object', + additionalProperties: true, + description: + '服务端列表项原样透传,目前未归一化字段,不能假定与 get_friend_list 相同', + }, + }, summary: '获取单向好友列表', readOnly: true, params: {}, @@ -2062,6 +2968,13 @@ export const actions = [ defineAction({ name: 'get_clientkey', + examples: [ + { + summary: '获取 clientkey', + params: {}, + notes: '返回值可能包含凭据或临时下载密钥,请勿公开或记录到不受控日志。', + }, + ], summary: '获取 clientkey', readOnly: true, returns: '{ clientKey, expireTime, keyIndex }:clientkey 及其过期时间与索引。', @@ -2096,6 +3009,15 @@ export const actions = [ // which id is present; we mirror that. Returns the server-built ark JSON. defineAction({ name: 'share_peer', + examples: [ + { + summary: '分享用户/群 Ark 卡片', + params: { + user_id: 10001, + }, + notes: '生成联系人分享卡片;返回卡片数据,不等同于发送到聊天。', + }, + ], summary: '分享用户/群 Ark 卡片', readOnly: true, returns: '{ arkMsg }:服务端生成的推荐联系人 Ark 卡片 JSON 字符串。', @@ -2132,6 +3054,15 @@ export const actions = [ }), defineAction({ name: 'send_ark_share', + examples: [ + { + summary: '分享用户/群 Ark 卡片(NapCat 标准名)', + params: { + user_id: 10001, + }, + notes: '生成联系人分享卡片;返回卡片数据,不等同于发送到聊天。', + }, + ], summary: '分享用户/群 Ark 卡片(NapCat 标准名)', readOnly: true, returns: '{ arkMsg }:服务端生成的推荐联系人 Ark 卡片 JSON 字符串。', @@ -2174,6 +3105,19 @@ export const actions = [ // 本 action 发送固定图文 ark 模板,返回 null(无法用于撤回/设精华)。 defineAction({ name: 'send_tuwen_ark', + examples: [ + { + summary: '发送图文 Ark 卡片(私聊/群聊)', + params: { + group_id: 20001, + title: '学习资料', + desc: '本周课程资料', + jump_url: 'https://example.com/course', + preview_url: 'https://example.com/cover.png', + }, + notes: '替换目标群号及实际链接;会发送分享卡片。', + }, + ], summary: '发送图文 Ark 卡片(私聊/群聊)', readOnly: false, returns: 'null', @@ -2228,6 +3172,15 @@ export const actions = [ // confident equivalent. The card may differ slightly from NapCat's. defineAction({ name: 'share_group_ex', + examples: [ + { + summary: '分享群 Ark 卡片', + params: { + group_id: 20001, + }, + notes: '生成群分享卡片;返回卡片数据,不等同于发送到聊天。', + }, + ], summary: '分享群 Ark 卡片', readOnly: true, returns: '服务端生成的群推荐 Ark 卡片 JSON 字符串。', @@ -2239,6 +3192,15 @@ export const actions = [ }), defineAction({ name: 'send_group_ark_share', + examples: [ + { + summary: '分享群 Ark 卡片(NapCat 标准名)', + params: { + group_id: 20001, + }, + notes: '生成群分享卡片;返回卡片数据,不等同于发送到聊天。', + }, + ], summary: '分享群 Ark 卡片(NapCat 标准名)', readOnly: true, returns: '服务端生成的群推荐 Ark 卡片 JSON 字符串。', @@ -2253,6 +3215,14 @@ export const actions = [ // The list item's `uid` is what set_doubt_friends_add_request takes as flag. defineAction({ name: 'get_doubt_friends_add_request', + examples: [ + { + summary: '获取可疑好友申请', + params: { + count: 20, + }, + }, + ], summary: '获取可疑好友申请', readOnly: true, returns: '可疑好友申请数组,每项含 uid(作为处理用 flag)、昵称、来源、留言与申请时间。', @@ -2295,6 +3265,20 @@ export const actions = [ // Approval and rejection use their respective friend API operations. defineAction({ name: 'set_doubt_friends_add_request', + examples: [ + { + summary: '处理可疑好友申请', + params: { + flag: '', + approve: true, + }, + notes: 'flag 使用 get_doubt_friends_add_request 返回项的 uid。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '处理可疑好友申请', params: { flag: f.string({ allowEmpty: false }), @@ -2315,6 +3299,21 @@ export const actions = [ // ext-info (0xf00_3). Omitted params leave the field unchanged. groupAction({ name: 'set_group_robot_add_option', + examples: [ + { + summary: '设置群机器人加群选项', + params: { + group_id: 20001, + robot_member_switch: 1, + robot_member_examine: 1, + }, + notes: '替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置群机器人加群选项', run: async (p, ctx) => { await ctx.bridge.apis.groupAdmin.setRobotAddOption( @@ -2332,6 +3331,15 @@ export const actions = [ defineAction({ name: 'get_collection_list', + examples: [ + { + summary: '获取收藏列表', + params: { + category: 0, + count: 20, + }, + }, + ], summary: '获取收藏列表', readOnly: true, returns: '返回收藏条目、是否还有更多数据及底部时间游标。', @@ -2440,6 +3448,17 @@ export const actions = [ defineAction({ name: 'create_collection', + examples: [ + { + summary: '创建收藏(未实现)', + params: {}, + notes: '当前未实现,调用返回失败;不要将其当作可用能力。', + }, + ], + returnsSchema: { + not: {}, + description: '当前始终返回失败,没有成功 data;失败响应 data 为 null,原因见 wording', + }, summary: '创建收藏(未实现)', params: {}, run: async () => { @@ -2449,6 +3468,17 @@ export const actions = [ defineAction({ name: '.get_word_slices', + examples: [ + { + summary: '分词(未实现)', + params: {}, + notes: '当前未实现,调用返回失败;不要将其当作可用能力。', + }, + ], + returnsSchema: { + not: {}, + description: '当前始终返回失败,没有成功 data;失败响应 data 为 null,原因见 wording', + }, summary: '分词(未实现)', readOnly: true, params: {}, @@ -2459,6 +3489,19 @@ export const actions = [ defineAction({ name: 'set_qq_avatar', + examples: [ + { + summary: '设置 QQ 头像', + params: { + file: 'file:///path/to/avatar.png', + }, + notes: '示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置 QQ 头像', params: { file: f.image() }, run: async (p, ctx) => { @@ -2469,6 +3512,21 @@ export const actions = [ defineAction({ name: 'set_input_status', + examples: [ + { + summary: '设置输入状态', + params: { + user_id: 10001, + event_type: 0, + }, + }, + ], + returnsSchema: { + type: 'object', + properties: {}, + additionalProperties: false, + description: '成功时为空对象 {},不是 null', + }, summary: '设置输入状态', params: { user_id: f.userId(), @@ -2483,6 +3541,15 @@ export const actions = [ defineAction({ name: 'get_group_info_ex', + examples: [ + { + summary: '获取群信息(扩展)', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群信息(扩展)', readOnly: true, returnsSchema: groupInfoReturnsSchema, @@ -2501,6 +3568,15 @@ export const actions = [ defineAction({ name: 'get_group_detail_info', + examples: [ + { + summary: '获取群详细信息', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群详细信息', readOnly: true, returnsSchema: groupInfoReturnsSchema, @@ -2519,6 +3595,25 @@ export const actions = [ defineAction({ name: 'trans_group_file', + examples: [ + { + summary: '转存群文件', + params: { + group_id: 20001, + file_id: '', + }, + notes: '替换群文件列表中的实际 file_id;需要对应文件操作权限。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + ok: { + const: true, + }, + }, + required: ['ok'], + }, summary: '转存群文件', returns: '{ ok: true }', params: { @@ -2543,6 +3638,16 @@ export const actions = [ // 而非武断断言"不支持"。(summary 仅进离线文档,调用方运行时只看到 wording。) defineAction({ name: 'get_file', + examples: [ + { + summary: '获取文件信息(仅图片/语音缓存;群文件请用 get_group_file_url)', + params: { + file_id: '', + }, + notes: '仅查询已缓存的图片或语音,先尝试图片,再尝试语音。', + }, + ], + returnsSchema: mediaInfoSchema, summary: '获取文件信息(仅图片/语音缓存;群文件请用 get_group_file_url)', readOnly: true, params: { @@ -2579,6 +3684,17 @@ export const actions = [ // 机器人生命周期(兼容 NapCat)。 defineAction({ name: 'bot_exit', + examples: [ + { + summary: '退出机器人', + params: {}, + notes: '会退出 SnowLuma 进程,仅在明确需要停止服务时调用。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '退出机器人', params: {}, run: async () => { @@ -2589,6 +3705,13 @@ export const actions = [ defineAction({ name: 'nc_get_packet_status', + examples: [ + { + summary: '获取 packet 状态(占位)', + notes: '兼容占位接口,当前始终返回 null,不提供实际收发统计。', + params: {}, + }, + ], summary: '获取 packet 状态(占位)', readOnly: true, returns: '占位实现,恒返回 null。', @@ -2604,6 +3727,20 @@ export const actions = [ // 添加别名以便遵循 NapCat 文档的客户端无需重写载荷。 groupAction({ name: 'delete_group_folder', + examples: [ + { + summary: '删除群文件夹', + params: { + group_id: 20001, + folder_id: '', + }, + notes: '会删除指定群文件夹,确认资源后调用。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '删除群文件夹', params: { folder_id: f.string({ allowEmpty: false }) }, run: async (p, ctx) => { @@ -2615,6 +3752,15 @@ export const actions = [ // 用户在线/扩展状态(NapCat:nc_get_user_status)。 defineAction({ name: 'nc_get_user_status', + examples: [ + { + summary: '获取用户在线/扩展状态', + params: { + user_id: 10001, + }, + notes: '替换为需要查询的 QQ 号。', + }, + ], summary: '获取用户在线/扩展状态', readOnly: true, returns: '{ status, ext_status }:用户在线状态码与扩展状态码。', @@ -2641,6 +3787,16 @@ export const actions = [ // AI 语音(oidb 0x929D / 0x929B)。 groupAction({ name: 'get_ai_characters', + examples: [ + { + summary: '获取 AI 语音角色', + params: { + group_id: 20001, + chat_type: 1, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取 AI 语音角色', readOnly: true, returns: '按分类分组的 AI 语音角色列表,每组含分类名与角色(id、名称、试听 URL)。', @@ -2685,6 +3841,22 @@ export const actions = [ groupAction({ name: 'get_ai_record', + examples: [ + { + summary: '生成 AI 语音', + params: { + group_id: 20001, + character: '', + text: '你好', + chat_type: 1, + }, + notes: 'character 原样使用 get_ai_characters 的 character_id;send_group_ai_record 会直接发送语音。', + }, + ], + returnsSchema: { + type: 'string', + description: '生成的下载或分享 URL 字符串,不是含 url 字段的对象', + }, summary: '生成 AI 语音', params: { character: f.string({ allowEmpty: false }), @@ -2705,6 +3877,19 @@ export const actions = [ groupAction({ name: 'send_group_ai_record', + examples: [ + { + summary: '发送 AI 语音到群', + params: { + group_id: 20001, + character: '', + text: '你好', + chat_type: 1, + }, + notes: 'character 原样使用 get_ai_characters 的 character_id;send_group_ai_record 会直接发送语音。', + }, + ], + returnsSchema: messageResultSchema, summary: '发送 AI 语音到群', returns: '{ message_id }:已发送 AI 语音的 OneBot 消息 ID。', params: { @@ -2727,6 +3912,25 @@ export const actions = [ defineAction({ name: 'request_decrypt_key', + examples: [ + { + summary: '请求数据库解密密钥', + params: { + db_path: '/path/to/account/nt_msg.db', + }, + notes: '路径位于 SnowLuma 服务端,必须指向当前账号可用的数据库;返回值是敏感密钥。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + db_key: { + type: 'string', + description: '数据库密钥,敏感信息', + }, + }, + required: ['db_key'], + }, summary: '请求数据库解密密钥', params: { db_path: f.string({ allowEmpty: false }) }, run: async (p, ctx) => { @@ -2757,6 +3961,37 @@ export const actions = [ defineAction({ name: ['get_rkey', 'nc_get_rkey'], + examples: [ + { + summary: '获取下载 rkey', + params: {}, + notes: '返回值可能包含凭据或临时下载密钥,请勿公开或记录到不受控日志。', + }, + ], + returnsSchema: { + type: 'array', + items: { + type: 'object', + properties: { + rkey: { + type: 'string', + description: '下载密钥,敏感信息', + }, + type: { + type: 'integer', + }, + ttl: { + type: 'integer', + description: '有效时长,秒', + }, + create_time: { + type: 'integer', + description: '创建时间,Unix 秒', + }, + }, + required: ['rkey', 'type', 'ttl', 'create_time'], + }, + }, summary: '获取下载 rkey', readOnly: true, params: {}, @@ -2770,6 +4005,13 @@ export const actions = [ // 收敛成 NapCat 的 server 形状。expired_time = now + 在场 rkey ttl 的最小值。 defineAction({ name: 'get_rkey_server', + examples: [ + { + summary: '获取 rkey 服务器信息', + params: {}, + notes: '返回值可能包含凭据或临时下载密钥,请勿公开或记录到不受控日志。', + }, + ], summary: '获取 rkey 服务器信息', readOnly: true, returns: @@ -2831,6 +4073,15 @@ export const actions = [ // 上传换 URL,本实现不覆盖(NapCat 走的是 Windows-only 内核 OCR,不可移植)。 defineAction({ name: ['ocr_image', '.ocr_image'], + examples: [ + { + summary: 'OCR 图片(服务端,需图片 URL 或已缓存的图片 file_id)', + params: { + image: 'https://example.com/image.png', + }, + notes: '替换为实际可访问的图片地址或已缓存图片标识;不支持本地路径和 base64。', + }, + ], summary: 'OCR 图片(服务端,需图片 URL 或已缓存的图片 file_id)', readOnly: true, returns: '{ texts, language }:识别文本数组(含置信度与坐标)与识别语言。', @@ -2898,6 +4149,20 @@ export const actions = [ groupAction({ name: '_del_group_notice', + examples: [ + { + summary: '删除群公告(fid 或 notice_id 二选一)', + params: { + group_id: 20001, + notice_id: '', + }, + notes: 'notice_id 来自 _get_group_notice;删除需要管理权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '删除群公告(fid 或 notice_id 二选一)', params: { fid: f.string().optional(), notice_id: f.string().optional() }, run: async (p, ctx) => { @@ -2917,6 +4182,53 @@ export const actions = [ defineAction({ name: 'send_forward_msg', + examples: [ + { + summary: '发送合并转发(按 message_type/群号自动路由)', + params: { + group_id: 20001, + messages: [ + { + type: 'node', + data: { + user_id: 10001, + nickname: '示例用户', + content: [ + { + type: 'text', + data: { + text: '转发内容', + }, + }, + ], + }, + }, + ], + }, + notes: '替换目标群号;nodes 是自定义转发节点数组。send_forward_msg 不提供目标时仅上传资源并返回 message_id=0。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + message_id: { + anyOf: [ + messageIdSchema, + { + const: 0, + }, + ], + description: '已发送时是非零有符号消息 ID;未提供目标、仅上传资源时为 0', + }, + res_id: { + type: 'string', + }, + forward_id: { + type: 'string', + }, + }, + required: ['message_id', 'res_id', 'forward_id'], + }, summary: '发送合并转发(按 message_type/群号自动路由)', returns: '{ message_id, res_id, forward_id }', // group_id/user_id are routing hints, not identity: 0 means "this branch @@ -2977,6 +4289,45 @@ export const actions = [ groupAction({ name: 'send_group_forward_msg', + examples: [ + { + summary: '发送群合并转发', + params: { + group_id: 20001, + messages: [ + { + type: 'node', + data: { + user_id: 10001, + nickname: '示例用户', + content: [ + { + type: 'text', + data: { + text: '转发内容', + }, + }, + ], + }, + }, + ], + }, + notes: '替换目标群号;nodes 是自定义转发节点数组。send_forward_msg 不提供目标时仅上传资源并返回 message_id=0。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + message_id: messageIdSchema, + res_id: { + type: 'string', + }, + forward_id: { + type: 'string', + }, + }, + required: ['message_id', 'res_id', 'forward_id'], + }, summary: '发送群合并转发', returns: '{ message_id, res_id, forward_id }', params: { @@ -3006,6 +4357,45 @@ export const actions = [ defineAction({ name: 'send_private_forward_msg', + examples: [ + { + summary: '发送私聊合并转发', + params: { + user_id: 10001, + messages: [ + { + type: 'node', + data: { + user_id: 10001, + nickname: '示例用户', + content: [ + { + type: 'text', + data: { + text: '转发内容', + }, + }, + ], + }, + }, + ], + }, + notes: '替换目标好友 QQ 号。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + message_id: messageIdSchema, + res_id: { + type: 'string', + }, + forward_id: { + type: 'string', + }, + }, + required: ['message_id', 'res_id', 'forward_id'], + }, summary: '发送私聊合并转发', returns: '{ message_id, res_id, forward_id }', params: { @@ -3036,6 +4426,15 @@ export const actions = [ defineAction({ name: 'get_forward_msg', + examples: [ + { + summary: '获取合并转发消息(id 或 message_id)', + params: { + id: '', + }, + notes: 'id 是合并转发资源标识,通常来自转发消息段或上传结果 res_id,不是普通数值 message_id。', + }, + ], summary: '获取合并转发消息(id 或 message_id)', readOnly: true, returns: @@ -3045,8 +4444,8 @@ export const actions = [ properties: { messages: { type: 'array', - description: '转发消息节点数组', - items: { type: 'object' }, + description: '转发消息节点数组;节点标识与本地消息 ID 不是同一契约', + items: forwardNodeSchema, }, }, required: ['messages'], @@ -3107,6 +4506,26 @@ export const actions = [ defineAction({ name: 'download_file', + examples: [ + { + summary: '下载文件(url 或 base64)到 data/downloads', + params: { + base64: 'aGVsbG8=', + name: 'hello.txt', + }, + notes: '保存到 SnowLuma 服务端临时目录,返回服务端路径;不是 MCP 主机下载路径。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + file: { + type: 'string', + description: '保存到 SnowLuma 服务端的本地文件路径', + }, + }, + required: ['file'], + }, summary: '下载文件(url 或 base64)到 data/downloads', returns: '{ file }', params: { @@ -3167,6 +4586,14 @@ export const actions = [ defineAction({ name: 'translate_en2zh', + examples: [ + { + summary: '英译中', + params: { + words: ['Hello'], + }, + }, + ], summary: '英译中', readOnly: true, returns: '{ words }:与输入等长的中文译文字符串数组。', @@ -3186,6 +4613,20 @@ export const actions = [ defineAction({ name: 'set_self_longnick', + examples: [ + { + summary: '设置个性签名(longNick/long_nick,严格 string)', + params: { + long_nick: '学习中', + }, + }, + ], + returnsSchema: { + type: 'object', + properties: {}, + additionalProperties: false, + description: '成功时为空对象 {},不是 null', + }, summary: '设置个性签名(longNick/long_nick,严格 string)', params: { longNick: optionalExactString(), long_nick: optionalExactString() }, run: async (p, ctx) => { @@ -3202,6 +4643,55 @@ export const actions = [ defineAction({ name: 'get_mini_app_ark', + examples: [ + { + summary: '获取小程序卡片 ark', + params: { + type: 'bili', + title: '示例视频', + desc: '视频介绍', + pic_url: 'https://example.com/cover.png', + jump_url: 'https://www.bilibili.com/video/', + }, + notes: '替换为实际分享地址;当前仅支持 bili 和 weibo。返回的 data 是卡片对象,不会自动发送。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + data: { + type: 'object', + properties: { + ver: { + description: '服务端卡片版本,原样透传', + }, + prompt: { + description: '服务端提示文本,原样透传', + }, + config: { + description: '服务端卡片配置,原样透传', + }, + app: { + description: '服务端应用名,原样透传', + }, + view: { + description: '服务端视图,原样透传', + }, + meta: { + description: '服务端卡片元数据,原样透传', + }, + miniappShareOrigin: { + const: 3, + }, + miniappOpenRefer: { + const: '10002', + }, + }, + required: ['miniappShareOrigin', 'miniappOpenRefer'], + }, + }, + required: ['data'], + }, summary: '获取小程序卡片 ark', readOnly: true, params: { @@ -3232,6 +4722,44 @@ export const actions = [ groupAction({ name: 'click_inline_keyboard_button', + examples: [ + { + summary: '点击内联键盘按钮', + params: { + group_id: 20001, + bot_appid: 10001, + msg_seq: 42, + button_id: '', + callback_data: '', + }, + notes: '参数必须来自同一条机器人按钮消息;msg_seq 是消息序号,不是 message_id。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + result: { + type: 'number', + description: '按钮业务结果码,需单独检查', + }, + errMsg: { + type: 'string', + }, + status: { + const: 0, + }, + promptText: { + type: 'string', + }, + promptType: { + const: 0, + }, + promptIcon: { + const: 0, + }, + }, + required: ['result', 'errMsg', 'status', 'promptText', 'promptType', 'promptIcon'], + }, summary: '点击内联键盘按钮', params: { bot_appid: f.uint(), @@ -3253,6 +4781,21 @@ export const actions = [ groupAction({ name: ['set_group_sign', 'send_group_sign'], + examples: [ + { + summary: '群签到', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], + returnsSchema: { + type: 'object', + properties: {}, + additionalProperties: false, + description: '成功时为空对象 {},不是 null', + }, summary: '群签到', run: async (p, ctx) => { await ctx.bridge.apis.misc.sendGroupSign(p.group_id); @@ -3262,6 +4805,29 @@ export const actions = [ defineAction({ name: ['send_packet', '.send_packet'], + examples: [ + { + summary: '发送原始 SSO 包(cmd + hex data)', + params: { + cmd: '', + data: '', + rsp: true, + }, + notes: '仅供有协议经验的调用方:cmd 和 data 必须成对替换为已验证的命令与十六进制请求;示例占位符不可直接发送。', + }, + ], + returnsSchema: { + anyOf: [ + { + type: 'string', + description: 'rsp=true 时返回的十六进制响应正文', + }, + { + type: 'null', + description: 'rsp=false 时不等待响应,data 为 null', + }, + ], + }, summary: '发送原始 SSO 包(cmd + hex data)', params: { cmd: f.string({ allowEmpty: false }), @@ -3294,6 +4860,60 @@ export const actions = [ groupAction({ name: 'get_group_todo_list', + examples: [ + { + summary: '获取群待办列表', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], + returnsSchema: { + type: 'array', + items: { + type: 'object', + properties: { + message_id: messageIdSchema, + message_seq: { + type: 'integer', + }, + message_random: { + type: 'integer', + }, + message: { + anyOf: [ + { + type: 'array', + items: messageSegmentSchema, + }, + { + type: 'null', + }, + ], + description: '本地缓存中的消息段;未缓存时为 null', + }, + text: { + type: 'string', + }, + create_time: { + type: 'integer', + }, + update_time: { + type: 'integer', + }, + }, + required: [ + 'message_id', + 'message_seq', + 'message_random', + 'message', + 'text', + 'create_time', + 'update_time', + ], + }, + }, summary: '获取群待办列表', returns: '群待办数组,包含可用于待办操作的消息标识、服务端摘要与时间', readOnly: true, @@ -3344,6 +4964,20 @@ export const actions = [ groupAction({ name: 'set_group_todo', + examples: [ + { + summary: '设置群待办', + params: { + group_id: 20001, + message_id: -123456, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。 必须是该群的消息。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置群待办', params: { message_id: f.messageId() }, run: (p, ctx) => groupTodoRun(p, ctx, (g, s) => ctx.bridge.apis.extras.setGroupTodo(g, s)), @@ -3351,6 +4985,20 @@ export const actions = [ groupAction({ name: 'complete_group_todo', + examples: [ + { + summary: '完成群待办', + params: { + group_id: 20001, + message_id: -123456, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。 必须是该群的消息。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '完成群待办', params: { message_id: f.messageId() }, run: (p, ctx) => @@ -3359,6 +5007,20 @@ export const actions = [ groupAction({ name: 'cancel_group_todo', + examples: [ + { + summary: '取消群待办', + params: { + group_id: 20001, + message_id: -123456, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。 必须是该群的消息。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '取消群待办', params: { message_id: f.messageId() }, run: (p, ctx) => @@ -3369,6 +5031,34 @@ export const actions = [ defineAction({ name: 'create_flash_task', + examples: [ + { + summary: '创建闪传任务', + params: { + files: [ + { + file: 'file:///path/to/report.pdf', + name: '报告.pdf', + }, + ], + name: '资料包', + }, + notes: '示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。 files 不能为空。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + fileset_id: { + type: 'string', + }, + task_id: { + type: 'string', + description: '与 fileset_id 相同的兼容字段', + }, + }, + required: ['fileset_id', 'task_id'], + }, summary: '创建闪传任务', params: { // 路径、{ file, name },或它们的数组;name 是该文件在闪传里的显示名 @@ -3394,6 +5084,28 @@ export const actions = [ defineAction({ name: 'get_fileset_info', + examples: [ + { + summary: '获取文件集信息', + params: { + fileset_id: '', + }, + notes: 'fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + fileset_id: { + type: 'string', + }, + file_list: { + type: 'array', + items: flashFileSchema, + }, + }, + required: ['fileset_id', 'file_list'], + }, summary: '获取文件集信息', params: { fileset_id: f.string({ allowEmpty: false }) }, run: async (p, ctx) => { @@ -3407,6 +5119,19 @@ export const actions = [ defineAction({ name: 'get_flash_file_list', + examples: [ + { + summary: '获取闪传文件列表', + params: { + fileset_id: '', + }, + notes: 'fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。', + }, + ], + returnsSchema: { + type: 'array', + items: flashFileSchema, + }, summary: '获取闪传文件列表', params: { fileset_id: f.string({ allowEmpty: false }) }, run: async (p, ctx) => { @@ -3417,6 +5142,16 @@ export const actions = [ defineAction({ name: 'list_filesets', + examples: [ + { + summary: '列出当前账号的所有闪传文件集', + params: {}, + }, + ], + returnsSchema: { + type: 'array', + items: flashFileSchema, + }, summary: '列出当前账号的所有闪传文件集', params: {}, run: async (_p, ctx) => { @@ -3427,6 +5162,25 @@ export const actions = [ defineAction({ name: 'get_flash_file_url', + examples: [ + { + summary: '获取闪传文件链接', + params: { + fileset_id: '', + file_index: 0, + }, + notes: 'fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。 file_index 从 0 开始。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + url: { + type: 'string', + }, + }, + required: ['url'], + }, summary: '获取闪传文件链接', params: { fileset_id: f.string({ allowEmpty: false }), @@ -3444,6 +5198,19 @@ export const actions = [ defineAction({ name: 'get_share_link', + examples: [ + { + summary: '获取文件分享链接', + params: { + fileset_id: '', + }, + notes: 'fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。', + }, + ], + returnsSchema: { + type: 'string', + description: '生成的下载或分享 URL 字符串,不是含 url 字段的对象', + }, summary: '获取文件分享链接', params: { fileset_id: f.string({ allowEmpty: false }) }, run: async (p, ctx) => { @@ -3454,6 +5221,19 @@ export const actions = [ defineAction({ name: 'delete_flash_file', + examples: [ + { + summary: '删除闪传文件', + params: { + fileset_id: '', + }, + notes: 'fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '删除闪传文件', params: { fileset_id: f.string({ allowEmpty: false }) }, run: async (p, ctx) => { @@ -3464,6 +5244,20 @@ export const actions = [ defineAction({ name: 'rename_flash_file', + examples: [ + { + summary: '重命名闪传文件', + params: { + fileset_id: '', + new_name: '新资料包', + }, + notes: 'fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '重命名闪传文件', params: { fileset_id: f.string({ allowEmpty: false }), @@ -3477,6 +5271,31 @@ export const actions = [ defineAction({ name: 'download_fileset', + examples: [ + { + summary: '解析闪传文件下载直链(不下载,由调用方实现下载)', + params: { + fileset_id: '', + file_index: 0, + }, + notes: 'fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。 file_index 从 0 开始。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + url: { + type: 'string', + }, + file_name: { + type: 'string', + }, + file_size: { + type: 'number', + }, + }, + required: ['url', 'file_name', 'file_size'], + }, summary: '解析闪传文件下载直链(不下载,由调用方实现下载)', returns: '{ url, file_name, file_size }', params: { @@ -3499,6 +5318,26 @@ export const actions = [ defineAction({ name: 'send_flash_msg', + examples: [ + { + summary: '发送闪传消息(私聊或群聊,引用 fileset_id 让对端下载)', + params: { + fileset_id: '', + user_id: 10001, + }, + notes: 'fileset_id 来自 create_flash_task、get_fileset_id 或 list_filesets,替换为当前账号可访问的文件集。 会发送分享消息;message_id=0 是占位值,不能用于撤回或查消息。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + message_id: { + const: 0, + description: '闪传分享不返回普通消息 ID,0 是占位值,不能用于查询或撤回', + }, + }, + required: ['message_id'], + }, summary: '发送闪传消息(私聊或群聊,引用 fileset_id 让对端下载)', returns: '{ message_id }', params: { @@ -3522,6 +5361,15 @@ export const actions = [ defineAction({ name: 'get_fileset_id', + examples: [ + { + summary: '从 QQ 闪传分享码或官方分享链接获取 fileset_id', + params: { + share_code: '', + }, + notes: 'share_code 使用实际闪传分享链接或提取码。', + }, + ], summary: '从 QQ 闪传分享码或官方分享链接获取 fileset_id', readOnly: true, returns: '{ fileset_id }:解析出的文件集 ID。', @@ -3546,6 +5394,68 @@ export const actions = [ // 好友个性装扮 defineAction({ name: '_get_friend_dress', + examples: [ + { + summary: '获取指定 QQ 号正在使用的个性装扮(挂件/名片/来电/输入状态等)', + params: { + user_id: 10001, + }, + notes: '替换为需要查询的 QQ 号。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + target_uin: { + type: 'string', + }, + is_svip: { + type: 'boolean', + }, + avatar_url: { + type: 'string', + }, + items: { + type: 'array', + items: { + type: 'object', + properties: { + app_id: { + type: 'integer', + }, + kind: { + type: 'string', + }, + item_id: { + type: 'integer', + }, + name: { + type: 'string', + }, + preview_url: { + type: 'string', + }, + video_url: { + type: 'string', + }, + price: { + type: 'number', + }, + }, + required: [ + 'app_id', + 'kind', + 'item_id', + 'name', + 'preview_url', + 'video_url', + 'price', + ], + }, + }, + }, + required: ['target_uin', 'is_svip', 'avatar_url', 'items'], + }, summary: '获取指定 QQ 号正在使用的个性装扮(挂件/名片/来电/输入状态等)', returns: '装扮信息;目标未使用任何可查询装扮时 items 为空数组。网络失败、未登录态/风控、页面改版、返回账号与请求不一致时返回失败并附具体原因', diff --git a/packages/onebot/src/actions/friend.ts b/packages/onebot/src/actions/friend.ts index d7181af0..fb8dd0d4 100644 --- a/packages/onebot/src/actions/friend.ts +++ b/packages/onebot/src/actions/friend.ts @@ -4,6 +4,12 @@ import { okResponse } from '../types'; export const actions = [ defineAction({ name: 'get_friend_list', + examples: [ + { + summary: '获取好友列表', + params: {}, + }, + ], summary: '获取好友列表', readOnly: true, returns: '好友列表数组,每项含 QQ 号、昵称与备注。', @@ -31,6 +37,15 @@ export const actions = [ defineAction({ name: 'get_stranger_info', + examples: [ + { + summary: '获取陌生人信息', + params: { + user_id: 10001, + }, + notes: '替换为需要查询的 QQ 号。', + }, + ], summary: '获取陌生人信息', readOnly: true, returns: @@ -109,6 +124,20 @@ export const actions = [ defineAction({ name: 'delete_friend', + examples: [ + { + summary: '删除好友', + params: { + user_id: 10001, + block: false, + }, + notes: '会删除指定好友;仅在明确需要删除时调用。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '删除好友', params: { user_id: f.userId().describe('QQ 号'), block: f.bool().default(false) }, run: async (p, ctx) => { diff --git a/packages/onebot/src/actions/group-admin.ts b/packages/onebot/src/actions/group-admin.ts index 3a07fe2c..0df16001 100644 --- a/packages/onebot/src/actions/group-admin.ts +++ b/packages/onebot/src/actions/group-admin.ts @@ -4,6 +4,21 @@ import { okResponse } from '../types'; export const actions = [ groupUserAction({ name: 'set_group_kick', + examples: [ + { + summary: '踢出群成员', + params: { + group_id: 20001, + user_id: 10001, + reject_add_request: false, + }, + notes: '替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '踢出群成员', params: { reject_add_request: f.bool().default(false) }, run: async (p, ctx) => { @@ -18,6 +33,21 @@ export const actions = [ groupAction({ name: 'set_group_kick_members', + examples: [ + { + summary: '批量踢出群成员', + params: { + group_id: 20001, + user_id: [10001, 10002], + reject_add_request: false, + }, + notes: '替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '批量踢出群成员', params: { user_id: f.array(f.memberId()).nonEmpty(), @@ -35,6 +65,21 @@ export const actions = [ groupUserAction({ name: 'set_group_ban', + examples: [ + { + summary: '禁言群成员(duration=0 解除)', + params: { + group_id: 20001, + user_id: 10001, + duration: 600, + }, + notes: '替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。 duration 为秒,0 表示解除禁言。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '禁言群成员(duration=0 解除)', params: { duration: f.duration().default(1800) }, run: async (p, ctx) => { @@ -45,6 +90,20 @@ export const actions = [ groupAction({ name: 'set_group_whole_ban', + examples: [ + { + summary: '全员禁言开关', + params: { + group_id: 20001, + enable: true, + }, + notes: '替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '全员禁言开关', params: { enable: f.bool().default(true) }, run: async (p, ctx) => { @@ -55,6 +114,22 @@ export const actions = [ groupAction({ name: 'set_group_add_option', + examples: [ + { + summary: '设置加群选项', + params: { + group_id: 20001, + add_type: 2, + group_question: '入群用途?', + group_answer: '学习交流', + }, + notes: '修改加群设置前先用 get_group_admin_settings 查看当前状态,并确认目标设置值。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置加群选项', params: { add_type: f.int({ min: 0 }).default(0), @@ -74,6 +149,21 @@ export const actions = [ groupAction({ name: 'set_group_search', + examples: [ + { + summary: '设置群被搜索方式(群指纹 / 群号搜索开关)', + params: { + group_id: 20001, + no_finger_open: 0, + no_code_finger_open: 0, + }, + notes: '替换群号;数值含义以当前群设置为准。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置群被搜索方式(群指纹 / 群号搜索开关)', params: { no_finger_open: f.int({ min: 0 }).optional(), @@ -91,6 +181,20 @@ export const actions = [ groupAction({ name: 'set_group_member_invite_policy', + examples: [ + { + summary: '设置群成员邀请策略', + params: { + group_id: 20001, + policy: 'require_approval', + }, + notes: '替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置群成员邀请策略', params: { policy: f.enum('disabled', 'require_approval', 'no_approval', 'no_approval_under_100'), @@ -103,6 +207,20 @@ export const actions = [ groupAction({ name: 'set_group_new_member_history_visibility', + examples: [ + { + summary: '设置新成员是否可查看历史消息', + params: { + group_id: 20001, + visible: true, + }, + notes: '替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置新成员是否可查看历史消息', params: { visible: f.bool() }, run: async (p, ctx) => { @@ -113,6 +231,20 @@ export const actions = [ groupAction({ name: 'set_group_member_permissions', + examples: [ + { + summary: '设置群成员权限(仅群主可改)', + params: { + group_id: 20001, + allow_member_upload_album: false, + }, + notes: '至少传一个权限字段;仅修改传入的权限,其余保持原样。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置群成员权限(仅群主可改)', params: { allow_member_upload_album: f.bool().optional(), @@ -138,6 +270,15 @@ export const actions = [ groupAction({ name: 'get_group_admin_settings', + examples: [ + { + summary: '获取群管理设置的当前值', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群管理设置的当前值', readOnly: true, returns: '与对应设置接口字段对齐的当前群管理设置。', @@ -210,6 +351,15 @@ export const actions = [ defineAction({ name: 'get_group_tags', + examples: [ + { + summary: '读取某个群分类下的资料标签', + params: { + group_class: 1, + }, + notes: 'group_class 使用实际群分类编号。', + }, + ], summary: '读取某个群分类下的资料标签', readOnly: true, returns: '该分类下的资料标签列表。', @@ -239,6 +389,20 @@ export const actions = [ defineAction({ name: 'add_group_tag', + examples: [ + { + summary: '向某个群分类添加资料标签', + params: { + group_class: 1, + name: '学习', + }, + notes: '替换实际群分类和标签;需要当前账号有管理权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '向某个群分类添加资料标签', params: { group_class: f.int({ min: 0, max: 0xffffffff }), @@ -252,6 +416,20 @@ export const actions = [ defineAction({ name: 'delete_group_tag', + examples: [ + { + summary: '从某个群分类移除资料标签', + params: { + group_class: 1, + name: '学习', + }, + notes: '替换实际群分类和标签;需要当前账号有管理权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '从某个群分类移除资料标签', params: { group_class: f.int({ min: 0, max: 0xffffffff }), @@ -265,6 +443,21 @@ export const actions = [ groupUserAction({ name: 'set_group_admin', + examples: [ + { + summary: '设置/取消管理员', + params: { + group_id: 20001, + user_id: 10001, + enable: true, + }, + notes: '需要群主权限;会授予该成员管理员身份。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置/取消管理员', params: { enable: f.bool().default(true) }, run: async (p, ctx) => { @@ -275,6 +468,21 @@ export const actions = [ groupUserAction({ name: 'set_group_card', + examples: [ + { + summary: '设置群名片(空字符串清除)', + params: { + group_id: 20001, + user_id: 10001, + card: '示例群名片', + }, + notes: '替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置群名片(空字符串清除)', params: { card: f.string().default('') }, run: async (p, ctx) => { @@ -285,6 +493,20 @@ export const actions = [ groupAction({ name: 'set_group_name', + examples: [ + { + summary: '设置群名', + params: { + group_id: 20001, + group_name: '学习交流群', + }, + notes: '替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置群名', params: { group_name: f.string().default('') }, run: async (p, ctx) => { @@ -295,6 +517,19 @@ export const actions = [ groupAction({ name: 'set_group_leave', + examples: [ + { + summary: '退群', + params: { + group_id: 20001, + }, + notes: '会退出该群,请确认目标群号后调用。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '退群', run: async (p, ctx) => { await ctx.bridge.apis.groupAdmin.leave(p.group_id); @@ -304,6 +539,21 @@ export const actions = [ groupUserAction({ name: 'set_group_special_title', + examples: [ + { + summary: '设置群头衔', + params: { + group_id: 20001, + user_id: 10001, + special_title: '学习委员', + }, + notes: '替换群号和成员 QQ 号;操作是否允许取决于当前账号在该群的权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置群头衔', params: { special_title: f.string().default('') }, run: async (p, ctx) => { @@ -319,12 +569,34 @@ export const actions = [ // No-op stubs (accept and ignore any params, as before). defineAction({ name: 'set_group_anonymous', + examples: [ + { + summary: '匿名开关(未实现,返回 ok)', + params: {}, + notes: '兼容占位接口:当前返回成功但不会修改匿名设置。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '匿名开关(未实现,返回 ok)', params: {}, run: () => okResponse(), }), defineAction({ name: 'set_group_anonymous_ban', + examples: [ + { + summary: '匿名禁言(未实现,返回 ok)', + params: {}, + notes: '兼容占位接口:当前返回成功但不会修改匿名设置。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '匿名禁言(未实现,返回 ok)', params: {}, run: () => okResponse(), @@ -332,6 +604,20 @@ export const actions = [ groupAction({ name: 'set_group_portrait', + examples: [ + { + summary: '设置群头像', + params: { + group_id: 20001, + file: 'file:///path/to/avatar.png', + }, + notes: '示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '设置群头像', params: { file: f.image() }, run: async (p, ctx) => { diff --git a/packages/onebot/src/actions/group-album.ts b/packages/onebot/src/actions/group-album.ts index 2143ee80..a63d59e1 100644 --- a/packages/onebot/src/actions/group-album.ts +++ b/packages/onebot/src/actions/group-album.ts @@ -46,9 +46,70 @@ const albumCoverSchema = { required: ['type', 'image'], }; +// Media-list entries are passed through rather than normalized like covers. +// Missing fields can be absent; decoded undefined values become null. +const mediaUrlSchema = { + type: ['object', 'null'], + properties: { + url: { type: ['string', 'null'] }, + width: { type: ['integer', 'null'] }, + height: { type: ['integer', 'null'] }, + }, +}; +const mediaPhotoUrlsSchema = { + type: ['array', 'null'], + items: { + type: 'object', + properties: { spec: { type: ['integer', 'null'] }, url: mediaUrlSchema }, + }, +}; +const mediaImageSchema = { + type: ['object', 'null'], + properties: { + name: { type: ['string', 'null'] }, + sloc: { type: ['string', 'null'] }, + lloc: { type: ['string', 'null'], description: '图片长定位标识,供评论、删除等操作使用' }, + photoUrls: mediaPhotoUrlsSchema, + defaultUrl: mediaUrlSchema, + isGif: { type: ['boolean', 'null'] }, + hasRaw: { type: ['boolean', 'null'] }, + }, +}; +const albumMediaSchema = { + type: 'object', + properties: { + type: { type: ['integer', 'null'] }, + image: mediaImageSchema, + video: { + type: ['object', 'null'], + properties: { + id: { type: ['string', 'null'], description: '视频标识,供评论、删除等操作使用' }, + url: { type: ['string', 'null'] }, + cover: mediaImageSchema, + width: { type: ['integer', 'null'] }, + height: { type: ['integer', 'null'] }, + videoTime: { type: ['string', 'null'], description: '时长,十进制字符串' }, + videoUrl: mediaPhotoUrlsSchema, + }, + }, + uploader: { type: ['string', 'null'] }, + batchId: { type: ['string', 'null'], description: '上传批次,十进制字符串' }, + uploadTime: { type: ['string', 'null'], description: '上传时间,十进制字符串' }, + }, +}; + export const actions = [ groupAction({ name: 'get_group_album_list', + examples: [ + { + summary: '基本调用', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], readOnly: true, returns: '群相册列表数组,每项为一个相册的基本信息。', returnsSchema: { @@ -90,6 +151,16 @@ export const actions = [ // get_qun_album_list — NapCat-compatible view of QQ NT's AlbumService. groupAction({ name: 'get_qun_album_list', + examples: [ + { + summary: '基本调用', + params: { + group_id: 20001, + attach_info: '', + }, + notes: '后续页原样传回上一页的 attach_info;has_more=false 表示结束。', + }, + ], readOnly: true, returns: 'NapCat 风格的相册列表封套:{album_list, attach_info, has_more}。', returnsSchema: { @@ -153,6 +224,22 @@ export const actions = [ groupAction({ name: 'upload_image_to_qun_album', + examples: [ + { + summary: '基本调用', + params: { + group_id: 20001, + album_id: '', + album_name: '活动照片', + file: 'file:///path/to/photo.png', + }, + notes: 'album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, params: { album_id: f.string({ allowEmpty: false }), album_name: f.string({ allowEmpty: false }), @@ -166,6 +253,18 @@ export const actions = [ groupAction({ name: 'upload_video_to_qun_album', + examples: [ + { + summary: '上传视频到群相册', + params: { + group_id: 20001, + album_id: '', + album_name: '活动视频', + file: 'file:///path/to/video.mp4', + }, + notes: 'album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。', + }, + ], summary: '上传视频到群相册', returns: '{ id: string }', returnsSchema: { @@ -193,6 +292,17 @@ export const actions = [ groupAction({ name: 'get_group_album_media_list', + examples: [ + { + summary: '基本调用', + params: { + group_id: 20001, + album_id: '', + attach_info: '', + }, + notes: 'album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 下一页传回 nextAttachInfo。', + }, + ], readOnly: true, returns: '相册图片/视频列表及下一页分页游标;视频项包含 id、url、cover、尺寸、时长和多规格地址。', @@ -201,8 +311,8 @@ export const actions = [ properties: { mediaList: { type: 'array', - description: '相册媒体项列表(各项字段不固定)', - items: { type: 'object' }, + description: '相册图片或视频,未返回的字段可省略或为 null', + items: albumMediaSchema, }, nextAttachInfo: { type: 'string', description: '下一页分页游标(空串表示无更多)' }, }, @@ -224,6 +334,58 @@ export const actions = [ groupAction({ name: 'do_group_album_comment', + examples: [ + { + summary: '基本调用', + params: { + group_id: 20001, + album_id: '', + lloc: '', + content: '拍得很好', + }, + notes: 'album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + id: { + type: 'string', + }, + user: { + type: 'object', + properties: { + uin: { + type: 'string', + }, + }, + required: ['uin'], + }, + content: { + type: 'array', + items: { + type: 'object', + properties: { + type: { + type: 'integer', + }, + content: { + type: 'string', + }, + }, + required: ['type', 'content'], + }, + }, + time: { + type: 'string', + description: '十进制时间字符串', + }, + clientKey: { + type: 'string', + }, + }, + required: ['id', 'user', 'content', 'time', 'clientKey'], + }, params: { album_id: f.string({ allowEmpty: false }), lloc: f.string({ allowEmpty: false }), @@ -242,6 +404,28 @@ export const actions = [ groupAction({ name: 'set_group_album_media_like', + examples: [ + { + summary: '基本调用', + params: { + group_id: 20001, + album_id: '', + batch_id: '1700000000', + }, + notes: 'album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 batch_id 必须是实际批次的十进制字符串。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + id: { + type: ['string', 'null'], + }, + status: { + type: ['integer', 'null'], + }, + }, + }, params: { album_id: f.string({ allowEmpty: false }), batch_id: f.string({ allowEmpty: false }), @@ -262,6 +446,28 @@ export const actions = [ // 取消点赞群相册媒体 groupAction({ name: 'cancel_group_album_media_like', + examples: [ + { + summary: '基本调用', + params: { + group_id: 20001, + album_id: '', + batch_id: '1700000000', + }, + notes: 'album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。 batch_id 必须是实际批次的十进制字符串。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + id: { + type: ['string', 'null'], + }, + status: { + type: ['integer', 'null'], + }, + }, + }, params: { album_id: f.string({ allowEmpty: false }), batch_id: f.string({ allowEmpty: false }), @@ -281,6 +487,26 @@ export const actions = [ groupAction({ name: 'del_group_album_media', + examples: [ + { + summary: '删除群相册图片或视频', + params: { + group_id: 20001, + album_id: '', + lloc: '', + }, + notes: 'album_id 来自 get_group_album_list;lloc 或视频 id、batch_id 来自 get_group_album_media_list。替换群号及对应资源标识。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + success: { + const: true, + }, + }, + required: ['success'], + }, summary: '删除群相册图片或视频', params: { album_id: f.string({ allowEmpty: false }), diff --git a/packages/onebot/src/actions/group-file.ts b/packages/onebot/src/actions/group-file.ts index b18d178b..1d61d5fb 100644 --- a/packages/onebot/src/actions/group-file.ts +++ b/packages/onebot/src/actions/group-file.ts @@ -20,6 +20,26 @@ function busidOr102(raw: unknown): number { export const actions = [ groupAction({ name: 'upload_group_file', + examples: [ + { + summary: '上传群文件', + params: { + group_id: 20001, + file: 'file:///path/to/report.pdf', + name: '报告.pdf', + }, + notes: '示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + file_id: { + type: 'string', + }, + }, + required: ['file_id'], + }, summary: '上传群文件', returns: '{ file_id: string }', // `folder` / `folder_id` are two aliases for one target dir; the first @@ -47,6 +67,26 @@ export const actions = [ defineAction({ name: 'upload_private_file', + examples: [ + { + summary: '上传私聊文件', + params: { + user_id: 10001, + file: 'file:///path/to/report.pdf', + name: '报告.pdf', + }, + notes: '示例路径位于 SnowLuma 服务端,须替换为该服务可读取的实际文件。 默认上传并发送给该好友。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + file_id: { + type: 'string', + }, + }, + required: ['file_id'], + }, summary: '上传私聊文件', returns: '{ file_id: string }', params: { @@ -101,6 +141,16 @@ export const actions = [ groupAction({ name: 'get_group_file_url', + examples: [ + { + summary: '获取群文件下载链接', + params: { + group_id: 20001, + file_id: '', + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群文件下载链接', readOnly: true, returns: '群文件下载链接。', @@ -125,6 +175,15 @@ export const actions = [ groupAction({ name: 'get_group_root_files', + examples: [ + { + summary: '获取群根目录文件列表', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群根目录文件列表', readOnly: true, returns: '群文件系统信息(文件与文件夹列表)。', @@ -180,6 +239,16 @@ export const actions = [ groupAction({ name: 'get_group_files_by_folder', + examples: [ + { + summary: '获取群子目录文件列表', + params: { + group_id: 20001, + folder_id: '', + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群子目录文件列表', readOnly: true, returns: '群文件系统信息(文件与文件夹列表)。', @@ -238,6 +307,20 @@ export const actions = [ groupAction({ name: 'delete_group_file', + examples: [ + { + summary: '删除群文件', + params: { + group_id: 20001, + file_id: '', + }, + notes: '替换群文件列表中的实际 file_id;需要对应文件操作权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '删除群文件', params: { file_id: f.fileId() }, run: async (p, ctx) => { @@ -248,6 +331,22 @@ export const actions = [ groupAction({ name: 'move_group_file', + examples: [ + { + summary: '移动群文件', + params: { + group_id: 20001, + file_id: '', + parent_directory: '/', + target_directory: '', + }, + notes: '目录和文件标识均来自该群文件列表。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '移动群文件', params: { file_id: f.fileId(), @@ -269,6 +368,27 @@ export const actions = [ // (文件当前所在目录)+ new_name。SnowLuma 的 file_id 即原始 fileId,无需 UUID 解码。 groupAction({ name: 'rename_group_file', + examples: [ + { + summary: '重命名群文件', + params: { + group_id: 20001, + file_id: '', + current_parent_directory: '/', + new_name: '新文件名.pdf', + }, + notes: 'file_id 来自群文件列表,目录必须是文件当前所在目录。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + ok: { + const: true, + }, + }, + required: ['ok'], + }, summary: '重命名群文件', params: { file_id: f.fileId(), @@ -290,6 +410,76 @@ export const actions = [ groupAction({ name: 'create_group_file_folder', + examples: [ + { + summary: '创建群文件夹', + params: { + group_id: 20001, + name: '共享资料', + parent_id: '/', + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + result: { + type: 'object', + properties: { + retCode: { + const: 0, + }, + retMsg: { + const: 'success', + }, + }, + required: ['retCode', 'retMsg'], + }, + groupItem: { + type: 'object', + properties: { + folderInfo: { + type: 'object', + properties: { + folderId: { + type: 'string', + }, + folderName: { + type: 'string', + }, + folderPath: { + type: 'string', + }, + createTime: { + type: 'integer', + }, + modifyTime: { + type: 'integer', + }, + createUin: { + type: 'integer', + }, + modifyUin: { + type: 'integer', + }, + }, + required: [ + 'folderId', + 'folderName', + 'folderPath', + 'createTime', + 'modifyTime', + 'createUin', + 'modifyUin', + ], + }, + }, + required: ['folderInfo'], + }, + }, + required: ['result', 'groupItem'], + }, summary: '创建群文件夹', // NapCat-aligned data so callers can read back the new folderId (#195): // { result, groupItem: { folderInfo: { folderId, folderName, … } } }. @@ -311,6 +501,20 @@ export const actions = [ groupAction({ name: 'delete_group_file_folder', + examples: [ + { + summary: '删除群文件夹', + params: { + group_id: 20001, + folder_id: '', + }, + notes: '会删除指定群文件夹,确认资源后调用。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '删除群文件夹', params: { folder_id: f.string({ allowEmpty: false }) }, run: async (p, ctx) => { @@ -321,6 +525,21 @@ export const actions = [ groupAction({ name: 'rename_group_file_folder', + examples: [ + { + summary: '重命名群文件夹', + params: { + group_id: 20001, + folder_id: '', + new_folder_name: '课程资料', + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '重命名群文件夹', // new_folder_name / name are aliases; first non-empty wins and must be // non-empty (legacy `asString(new_folder_name) || asString(name)` + `!newName`). @@ -346,6 +565,16 @@ export const actions = [ defineAction({ name: 'get_private_file_url', + examples: [ + { + summary: '获取私聊文件下载链接', + params: { + user_id: 10001, + file_id: '', + }, + notes: '使用私聊文件消息段中的 file_id,必要时原样传 file_hash。', + }, + ], summary: '获取私聊文件下载链接', readOnly: true, returns: '私聊文件下载链接。', diff --git a/packages/onebot/src/actions/group-info.ts b/packages/onebot/src/actions/group-info.ts index c63cd64c..b2d85d81 100644 --- a/packages/onebot/src/actions/group-info.ts +++ b/packages/onebot/src/actions/group-info.ts @@ -33,6 +33,16 @@ export const groupInfoReturnsSchema = { export const actions = [ groupAction({ name: 'get_group_share_link', + examples: [ + { + summary: '获取群分享链接', + params: { + group_id: 20001, + need_short_url: true, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群分享链接', readOnly: true, params: { @@ -59,6 +69,14 @@ export const actions = [ defineAction({ name: 'get_group_list', + examples: [ + { + summary: '获取群列表', + params: { + no_cache: false, + }, + }, + ], summary: '获取群列表', readOnly: true, returns: '群信息对象数组。', @@ -112,6 +130,15 @@ export const actions = [ groupAction({ name: 'get_group_info', + examples: [ + { + summary: '获取群信息', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群信息', readOnly: true, returns: '群信息对象。', @@ -144,6 +171,15 @@ export const actions = [ groupAction({ name: 'get_group_member_list', + examples: [ + { + summary: '获取群成员列表', + params: { + group_id: 20001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群成员列表', readOnly: true, returns: '群成员信息对象数组。', @@ -225,6 +261,16 @@ export const actions = [ groupUserAction({ name: 'get_group_member_info', + examples: [ + { + summary: '获取群成员信息', + params: { + group_id: 20001, + user_id: 10001, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群成员信息', readOnly: true, returns: '群成员信息对象。', @@ -327,6 +373,164 @@ export const actions = [ groupAction({ name: 'get_group_honor_info', + examples: [ + { + summary: '获取群荣誉信息', + params: { + group_id: 20001, + type: 'all', + }, + notes: '未请求的分类仍返回空数组;当前龙王可能为 null。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + group_id: { + type: 'integer', + }, + current_talkative: { + anyOf: [ + { + type: 'object', + properties: { + user_id: { + type: ['number', 'string', 'null'], + }, + nickname: { + type: 'string', + }, + avatar: { + type: 'string', + }, + description: { + type: 'string', + }, + }, + required: ['user_id', 'nickname', 'avatar', 'description'], + }, + { + type: 'null', + }, + ], + }, + talkative_list: { + type: 'array', + items: { + type: 'object', + properties: { + user_id: { + type: ['number', 'string', 'null'], + }, + nickname: { + type: 'string', + }, + avatar: { + type: 'string', + }, + description: { + type: 'string', + }, + }, + required: ['user_id', 'nickname', 'avatar', 'description'], + }, + }, + performer_list: { + type: 'array', + items: { + type: 'object', + properties: { + user_id: { + type: ['number', 'string', 'null'], + }, + nickname: { + type: 'string', + }, + avatar: { + type: 'string', + }, + description: { + type: 'string', + }, + }, + required: ['user_id', 'nickname', 'avatar', 'description'], + }, + }, + legend_list: { + type: 'array', + items: { + type: 'object', + properties: { + user_id: { + type: ['number', 'string', 'null'], + }, + nickname: { + type: 'string', + }, + avatar: { + type: 'string', + }, + description: { + type: 'string', + }, + }, + required: ['user_id', 'nickname', 'avatar', 'description'], + }, + }, + emotion_list: { + type: 'array', + items: { + type: 'object', + properties: { + user_id: { + type: ['number', 'string', 'null'], + }, + nickname: { + type: 'string', + }, + avatar: { + type: 'string', + }, + description: { + type: 'string', + }, + }, + required: ['user_id', 'nickname', 'avatar', 'description'], + }, + }, + strong_newbie_list: { + type: 'array', + items: { + type: 'object', + properties: { + user_id: { + type: ['number', 'string', 'null'], + }, + nickname: { + type: 'string', + }, + avatar: { + type: 'string', + }, + description: { + type: 'string', + }, + }, + required: ['user_id', 'nickname', 'avatar', 'description'], + }, + description: '当前实现始终为空数组', + }, + }, + required: [ + 'group_id', + 'current_talkative', + 'talkative_list', + 'performer_list', + 'legend_list', + 'emotion_list', + 'strong_newbie_list', + ], + }, summary: '获取群荣誉信息', readOnly: true, params: { type: f.string().optional() }, @@ -360,6 +564,17 @@ export const actions = [ defineAction({ name: 'get_group_system_msg', + examples: [ + { + summary: '获取群系统消息', + params: { + group_id: 20001, + only_pending: true, + count: 20, + }, + notes: '将 group_id 替换为当前账号可访问的群号。', + }, + ], summary: '获取群系统消息', readOnly: true, returns: '群系统消息数组,可按群号或未处理状态过滤。', diff --git a/packages/onebot/src/actions/info.ts b/packages/onebot/src/actions/info.ts index 9eadeea3..bef89fa6 100644 --- a/packages/onebot/src/actions/info.ts +++ b/packages/onebot/src/actions/info.ts @@ -5,6 +5,12 @@ import { okResponse } from '../types'; export const actions = [ defineAction({ name: 'get_login_info', + examples: [ + { + summary: '基本调用', + params: {}, + }, + ], readOnly: true, returns: '当前登录账号的 QQ 号与昵称。', returnsSchema: { @@ -24,6 +30,12 @@ export const actions = [ defineAction({ name: 'get_status', + examples: [ + { + summary: '基本调用', + params: {}, + }, + ], readOnly: true, returns: '运行状态。`online` 表示账号在线;`good` 表示已确认的收发链路健康状态;`time` 为当前账号服务实例运行秒数,与 #sl 同源。', @@ -55,6 +67,12 @@ export const actions = [ defineAction({ name: 'get_version_info', + examples: [ + { + summary: '基本调用', + params: {}, + }, + ], readOnly: true, returns: '实现与协议版本信息。', returnsSchema: { @@ -78,6 +96,12 @@ export const actions = [ defineAction({ name: 'can_send_image', + examples: [ + { + summary: '基本调用', + params: {}, + }, + ], readOnly: true, returns: '能力查询结果。', returnsSchema: { @@ -93,6 +117,12 @@ export const actions = [ defineAction({ name: 'can_send_record', + examples: [ + { + summary: '基本调用', + params: {}, + }, + ], readOnly: true, returns: '能力查询结果。', returnsSchema: { diff --git a/packages/onebot/src/actions/message.ts b/packages/onebot/src/actions/message.ts index f358f183..deffb91c 100644 --- a/packages/onebot/src/actions/message.ts +++ b/packages/onebot/src/actions/message.ts @@ -2,6 +2,7 @@ import { createLogger } from '@snowluma/common/logger'; import type { JsonObject, JsonValue } from '../types'; import { defineAction, groupAction, f, type Field } from '../action-kit'; import { RETCODE, failedResponse, okResponse } from '../types'; +import { messageResultSchema, messageIdSchema, messageDataSchema } from '../action-result-schemas'; /** send_msg only (#426). Some clients send `group_id: ""` on private chats. * Treat that exact empty string as absent. Do not change `f.groupId()`. */ @@ -23,6 +24,17 @@ const log = createLogger('OneBot'); export const actions = [ defineAction({ name: 'send_custom_face', + examples: [ + { + summary: '发送账号收藏表情(emoji_id 或 MD5),保留表情显示样式', + params: { + group_id: 20001, + emoji_id: '', + }, + notes: '群聊和私聊目标只能提供一个;emoji_id 来自 fetch_custom_face_detail。', + }, + ], + returnsSchema: messageResultSchema, summary: '发送账号收藏表情(emoji_id 或 MD5),保留表情显示样式', returns: '{ message_id: number }', params: { @@ -68,6 +80,18 @@ export const actions = [ // validated by the spec; message_type is left lenient for parity. defineAction({ name: 'send_msg', + examples: [ + { + summary: '发送消息(按 message_type/群号 自动路由群聊或私聊)', + params: { + message_type: 'group', + group_id: 20001, + message: '你好', + }, + notes: '必须提供对应目标;私聊使用 message_type=private 和 user_id。', + }, + ], + returnsSchema: messageResultSchema, summary: '发送消息(按 message_type/群号 自动路由群聊或私聊)', returns: '{ message_id: number }', params: { @@ -109,6 +133,17 @@ export const actions = [ defineAction({ name: 'send_private_msg', + examples: [ + { + summary: '发送私聊消息', + params: { + user_id: 10001, + message: '你好', + }, + notes: '替换目标好友 QQ 号;临时会话需另传来源 group_id。', + }, + ], + returnsSchema: messageResultSchema, summary: '发送私聊消息', returns: '{ message_id: number }', params: { @@ -135,6 +170,24 @@ export const actions = [ groupAction({ name: 'send_group_msg', + examples: [ + { + summary: '发送群消息', + params: { + group_id: 20001, + message: [ + { + type: 'text', + data: { + text: '你好', + }, + }, + ], + }, + notes: '替换目标群号;成功返回的 message_id 可为负值。', + }, + ], + returnsSchema: messageResultSchema, summary: '发送群消息', returns: '{ message_id: number }', params: { message: f.message(), auto_escape: f.bool().default(false) }, @@ -146,6 +199,28 @@ export const actions = [ defineAction({ name: 'get_msg', + examples: [ + { + summary: '获取消息', + params: { + message_id: -123456, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], + returnsSchema: { + allOf: [ + messageDataSchema, + { + type: 'object', + properties: { + real_id: messageIdSchema, + }, + required: ['real_id'], + }, + ], + description: '消息事件去除 post_type/self_id 并增加 real_id;群聊和私聊字段有所不同', + }, summary: '获取消息', readOnly: true, returns: @@ -170,6 +245,19 @@ export const actions = [ defineAction({ name: 'delete_msg', + examples: [ + { + summary: '撤回消息', + params: { + message_id: -123456, + }, + notes: '使用消息事件或发送结果中的真实 message_id,负值合法;消息须在当前账号的消息存储中可查询。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '撤回消息', params: { message_id: f.messageId() }, run: async (p, ctx) => { diff --git a/packages/onebot/src/actions/qzone.ts b/packages/onebot/src/actions/qzone.ts index 25a2f2c0..8e5751fa 100644 --- a/packages/onebot/src/actions/qzone.ts +++ b/packages/onebot/src/actions/qzone.ts @@ -41,6 +41,16 @@ export const actions = [ // 复用 qzone.qq.com 的 cookie/g_tk 基建(与群相册同源),纯 web,无 trpc。 defineAction({ name: 'get_qzone_msg_list', + examples: [ + { + summary: '获取 QQ 空间说说列表(默认机器人自己的空间)', + params: { + pos: 0, + num: 20, + }, + notes: '默认读取当前账号;指定 target_uin 时需有访问权限。', + }, + ], readOnly: true, returns: '说说列表对象,含说说总数与本页说说数组。', returnsSchema: { @@ -87,6 +97,17 @@ export const actions = [ defineAction({ name: 'get_qzone_msg_detail', + examples: [ + { + summary: '获取 QQ 空间说说详情与一页评论(含服务端内联回复,需有访问权限)', + params: { + tid: '', + pos: 0, + num: 20, + }, + notes: 'tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。 replies 仅为服务端内联回复,不保证完整。', + }, + ], readOnly: true, summary: '获取 QQ 空间说说详情与一页评论(含服务端内联回复,需有访问权限)', returns: @@ -139,6 +160,17 @@ export const actions = [ // 每条 feed 含结构化字段 + 预渲染 html 原样透传(深度解析交由调用方)。 defineAction({ name: 'get_qzone_feeds', + examples: [ + { + summary: + '获取 QQ 空间好友动态(feed);page_num 仅首页可靠,深翻页需时间游标(暂未实现)', + params: { + page_num: 1, + count: 10, + }, + notes: '目前仅首页可靠,不应假设任意页码均能完整翻页。', + }, + ], readOnly: true, returns: '好友动态对象,含本页 feed 数组与是否有更多页。', returnsSchema: { @@ -187,6 +219,29 @@ export const actions = [ // 注意:发说说为主动行为,高频会被 Qzone 风控,调用方需自行限流。 defineAction({ name: 'send_qzone_msg', + examples: [ + { + summary: + '发表说说(QQ 空间,支持纯文字或带图;传 images 自动上传;可设置查看权限)', + params: { + content: '今天的学习记录', + ugc_right: 64, + }, + notes: '会发布说说;ugc_right=64 仅自己可见。16/128 还必须提供 target_uins。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + tid: { + type: 'string', + }, + time: { + type: 'integer', + }, + }, + required: ['tid', 'time'], + }, summary: '发表说说(QQ 空间,支持纯文字或带图;传 images 自动上传;可设置查看权限)', params: { content: f.string({ allowEmpty: false }).describe('说说正文'), @@ -241,6 +296,19 @@ export const actions = [ // delete_qzone_msg — 删除机器人自己空间的一条说说(按 tid)。写操作。 defineAction({ name: 'delete_qzone_msg', + examples: [ + { + summary: '删除一条说说(QQ 空间,按 tid)', + params: { + tid: '', + }, + notes: 'tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '删除一条说说(QQ 空间,按 tid)', params: { tid: f @@ -257,6 +325,17 @@ export const actions = [ // 写操作;ugc_right/target_uins 语义与 send_qzone_msg 一致。 defineAction({ name: 'set_qzone_msg_right', + examples: [ + { + summary: '修改一条已发说说的查看权限(QQ 空间,按 tid)', + params: { + tid: '', + ugc_right: 16, + target_uins: [10001], + }, + notes: 'tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。 16 表示仅所选好友可见。', + }, + ], returns: '更新后的权限对象。', returnsSchema: { type: 'object', @@ -304,6 +383,20 @@ export const actions = [ // 传真实值更可靠;不传按 0。写操作;高频点赞会被 Qzone 风控,调用方需限流。 defineAction({ name: 'like_qzone', + examples: [ + { + summary: '给一条说说点赞(QQ 空间)', + params: { + tid: '', + target_uin: 10001, + }, + notes: 'tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '给一条说说点赞(QQ 空间)', params: { tid: f.string({ allowEmpty: false }).describe('说说 tid'), @@ -324,6 +417,20 @@ export const actions = [ // 注意:取消赞端点(internal_unlike_app)暂未经真机核实,待抓包确认。 defineAction({ name: 'unlike_qzone', + examples: [ + { + summary: '取消对一条说说的点赞(QQ 空间;取消赞端点待真机核实)', + params: { + tid: '', + target_uin: 10001, + }, + notes: 'tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '取消对一条说说的点赞(QQ 空间;取消赞端点待真机核实)', params: { tid: f.string({ allowEmpty: false }).describe('说说 tid'), @@ -345,6 +452,27 @@ export const actions = [ // 写操作;高频评论会被 Qzone 风控,调用方需限流。 defineAction({ name: 'comment_qzone', + examples: [ + { + summary: '评论一条说说(QQ 空间,支持纯文字或带图;传 images 自动上传)', + params: { + tid: '', + target_uin: 10001, + content: '感谢分享', + }, + notes: 'tid 来自 get_qzone_msg_list;操作需有该说说的访问或管理权限。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + comment_id: { + type: 'string', + description: '服务端未提供评论 ID 时为空串', + }, + }, + required: ['comment_id'], + }, summary: '评论一条说说(QQ 空间,支持纯文字或带图;传 images 自动上传)', params: { tid: f.string({ allowEmpty: false }).describe('说说 tid'), @@ -377,6 +505,21 @@ export const actions = [ // enable=true 拉黑,enable=false 解除拉黑。 defineAction({ name: 'set_qzone_ban', + examples: [ + { + summary: + '拉黑或解除拉黑某人(修改机器人自身 QQ 空间黑名单;enable=true 拉黑,false 解除)', + params: { + user_id: 10001, + enable: true, + }, + notes: '修改当前账号空间黑名单。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '拉黑或解除拉黑某人(修改机器人自身 QQ 空间黑名单;enable=true 拉黑,false 解除)', params: { user_id: f.userId().describe('目标 QQ 号'), diff --git a/packages/onebot/src/actions/request.ts b/packages/onebot/src/actions/request.ts index 19a0b827..6340c723 100644 --- a/packages/onebot/src/actions/request.ts +++ b/packages/onebot/src/actions/request.ts @@ -4,6 +4,20 @@ import { okResponse } from '../types'; export const actions = [ defineAction({ name: 'set_friend_add_request', + examples: [ + { + summary: '处理好友添加请求', + params: { + flag: '', + approve: true, + }, + notes: 'flag 必须原样使用当前账号好友请求事件的 flag,不能根据 QQ 号构造。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '处理好友添加请求', params: { flag: f.string({ allowEmpty: false }), @@ -17,6 +31,21 @@ export const actions = [ defineAction({ name: 'set_group_add_request', + examples: [ + { + summary: '处理加群请求', + params: { + flag: '', + sub_type: 'add', + approve: true, + }, + notes: 'flag 和 sub_type 原样取自同一条群请求事件;邀请事件使用 invite。', + }, + ], + returnsSchema: { + type: 'null', + description: '成功时 data 为 null;失败另见响应的 status、retcode 和 wording', + }, summary: '处理加群请求', params: { // Opaque handle from the original request event; callers must echo it. diff --git a/packages/onebot/src/actions/stream-download.ts b/packages/onebot/src/actions/stream-download.ts index dd5bb34f..83a3645a 100644 --- a/packages/onebot/src/actions/stream-download.ts +++ b/packages/onebot/src/actions/stream-download.ts @@ -28,6 +28,89 @@ import { okResponse, type ApiResponse, type JsonObject, type JsonValue } from '. import { type StreamSink, StreamStatus } from '../streaming'; import { STREAM_ROOT, ensureStreamDirectory, registerActiveStreamItem } from '../stream-storage'; +const downloadFrameSchema = { + oneOf: [ + { + type: 'object', + properties: { + type: { + const: 'stream', + }, + data_type: { + const: 'file_info', + }, + file_name: { + type: 'string', + }, + file_size: { + type: 'integer', + description: '字节数,0 可表示未知', + }, + chunk_size: { + type: 'integer', + }, + }, + required: ['type', 'data_type', 'file_name', 'file_size', 'chunk_size'], + }, + { + type: 'object', + properties: { + type: { + const: 'stream', + }, + data_type: { + const: 'file_chunk', + }, + index: { + type: 'integer', + }, + data: { + type: 'string', + description: '该分块的 Base64 数据', + }, + size: { + type: 'integer', + }, + progress: { + type: 'number', + minimum: 0, + maximum: 100, + }, + base64_size: { + type: 'integer', + }, + }, + required: ['type', 'data_type', 'index', 'data', 'size', 'progress', 'base64_size'], + }, + { + type: 'object', + properties: { + type: { + const: 'response', + }, + data_type: { + const: 'file_complete', + }, + file_name: { + type: 'string', + }, + total_chunks: { + type: 'integer', + }, + total_bytes: { + type: 'integer', + }, + message: { + type: 'string', + }, + }, + required: ['type', 'data_type', 'file_name', 'total_chunks', 'total_bytes', 'message'], + }, + ], + description: + '每个成功流式帧的 data;失败帧另由 status/retcode/wording 与 type=error 表示,不是 MCP 保存文件后的工具结果', +}; + const DEFAULT_CHUNK_BYTES = 64 * 1024; const MAX_CHUNK_REQUEST_BYTES = 16 * 1024 * 1024; // cap the client-requested chunk size const MAX_DOWNLOAD_BYTES = 4 * 1024 * 1024 * 1024; // 4 GiB total @@ -542,6 +625,17 @@ const downloadParams = { const downloadFileStream = defineStreamAction({ name: 'download_file_stream', + examples: [ + { + summary: '以流式方式下载文件(stream 目录本地文件 / URL / 缓存媒体)', + params: { + file: 'https://example.com/report.pdf', + chunk_size: 65536, + }, + notes: '替换实际公开可访问地址;经 invoke_action 调用,文件保存到 MCP 下载目录。', + }, + ], + returnsSchema: downloadFrameSchema, summary: '以流式方式下载文件(stream 目录本地文件 / URL / 缓存媒体)', returns: '流式帧:file_info → file_chunk* → file_complete', params: downloadParams, @@ -550,6 +644,17 @@ const downloadFileStream = defineStreamAction({ const downloadFileImageStream = defineStreamAction({ name: 'download_file_image_stream', + examples: [ + { + summary: '以流式方式下载图片(缓存图片 id / URL / stream 目录本地文件)', + params: { + file_id: '', + chunk_size: 65536, + }, + notes: 'file_id 来自当前账号图片消息;经 invoke_action 调用。', + }, + ], + returnsSchema: downloadFrameSchema, summary: '以流式方式下载图片(缓存图片 id / URL / stream 目录本地文件)', returns: '流式帧:file_info → file_chunk* → file_complete', params: downloadParams, @@ -558,6 +663,17 @@ const downloadFileImageStream = defineStreamAction({ const downloadFileRecordStream = defineStreamAction({ name: 'download_file_record_stream', + examples: [ + { + summary: '以流式方式下载语音(缓存语音 id / URL / stream 目录本地文件)', + params: { + file_id: '', + chunk_size: 65536, + }, + notes: 'file_id 来自当前账号语音消息;经 invoke_action 调用。', + }, + ], + returnsSchema: downloadFrameSchema, summary: '以流式方式下载语音(缓存语音 id / URL / stream 目录本地文件)', returns: '流式帧:file_info → file_chunk* → file_complete', params: downloadParams, @@ -567,6 +683,50 @@ const downloadFileRecordStream = defineStreamAction({ const testDownloadStream = defineStreamAction({ name: 'test_download_stream', + examples: [ + { + summary: '测试下载流(推送 10 个数据帧,验证流式传输,不触达 QQ)', + params: { + error: false, + }, + notes: '经 invoke_action 调用,仅测试传输,不访问 QQ。', + }, + ], + returnsSchema: { + oneOf: [ + { + type: 'object', + properties: { + type: { + const: 'stream', + }, + data_type: { + const: 'data_chunk', + }, + data: { + type: 'string', + }, + }, + required: ['type', 'data_type', 'data'], + }, + { + type: 'object', + properties: { + type: { + const: 'response', + }, + data_type: { + const: 'data_complete', + }, + data: { + type: 'string', + }, + }, + required: ['type', 'data_type', 'data'], + }, + ], + description: '成功时 10 个 data_chunk 后接 data_complete;error=true 时最终为失败帧', + }, summary: '测试下载流(推送 10 个数据帧,验证流式传输,不触达 QQ)', returns: '流式帧:data_chunk*10 → data_complete(error=true 时以 error 帧结束)', params: { error: f.bool().default(false).describe('是否触发测试错误') }, diff --git a/packages/onebot/src/actions/stream-file.ts b/packages/onebot/src/actions/stream-file.ts index 30c6b0e8..d2182f7e 100644 --- a/packages/onebot/src/actions/stream-file.ts +++ b/packages/onebot/src/actions/stream-file.ts @@ -457,6 +457,98 @@ type UploadParams = { const uploadFileStream = defineStreamAction({ name: 'upload_file_stream', + examples: [ + { + summary: '上传分块', + params: { + stream_id: 'example-upload-001', + filename: 'hello.txt', + total_chunks: 1, + file_size: 5, + chunk_index: 0, + chunk_data: 'aGVsbG8=', + }, + notes: '第 1 步:每次上传生成新的 stream_id;这里上传 hello 的唯一分块。通过 invoke_action 调用,分块确认不等于整文件完成。', + }, + { + summary: '完成同一上传', + params: { + stream_id: 'example-upload-001', + is_complete: true, + }, + notes: '第 2 步:等待全部分块确认后,用同一个 stream_id 单独请求完成;成功时返回 file_path。MCP 的 invoke_action 也可通过 execution.input_file 自动完成上传。', + }, + ], + returnsSchema: { + oneOf: [ + { + type: 'object', + properties: { + type: { + const: 'stream', + }, + stream_id: { + type: 'string', + }, + status: { + enum: ['file_created', 'chunk_received'], + }, + received_chunks: { + type: 'integer', + }, + total_chunks: { + type: 'integer', + }, + }, + required: ['type', 'stream_id', 'status', 'received_chunks', 'total_chunks'], + }, + { + type: 'object', + properties: { + type: { + const: 'response', + }, + stream_id: { + type: 'string', + }, + status: { + const: 'file_complete', + }, + received_chunks: { + type: 'integer', + }, + total_chunks: { + type: 'integer', + }, + file_path: { + type: 'string', + }, + file_name: { + type: 'string', + }, + file_size: { + type: 'integer', + }, + sha256: { + type: 'string', + }, + }, + required: [ + 'type', + 'stream_id', + 'status', + 'received_chunks', + 'total_chunks', + 'file_path', + 'file_name', + 'file_size', + 'sha256', + ], + }, + ], + description: + '分块确认与完成帧的 data;reset 和其他失败由错误帧返回。必须等到 file_complete 才能使用 file_path', + }, summary: '以流式分块方式上传文件到机器人本地(返回可用于发送的本地路径)', returns: '流式帧:分块确认 type=stream、完成 type=response(含 file_path/file_name/file_size/sha256)', @@ -496,6 +588,31 @@ function cleanDir(dir: string): number { const cleanStreamTempFile = defineAction({ name: 'clean_stream_temp_file', + examples: [ + { + summary: '清理流式传输临时文件(仅清理 stream 上传/下载目录)', + params: {}, + notes: '清理未使用的流式临时文件;不会清理正在传输的项目。', + }, + ], + returnsSchema: { + type: 'object', + properties: { + message: { + const: 'success', + }, + removed: { + type: 'integer', + }, + freed_bytes: { + type: 'integer', + }, + skipped_active: { + type: 'integer', + }, + }, + required: ['message', 'removed', 'freed_bytes', 'skipped_active'], + }, summary: '清理流式传输临时文件(仅清理 stream 上传/下载目录)', returns: '{ message, removed }', params: {}, diff --git a/packages/onebot/src/actions/system-face.ts b/packages/onebot/src/actions/system-face.ts index 67f73be1..d390b6fb 100644 --- a/packages/onebot/src/actions/system-face.ts +++ b/packages/onebot/src/actions/system-face.ts @@ -62,6 +62,14 @@ const faceSchema = { export const actions = [ defineAction({ name: 'fetch_sys_faces', + examples: [ + { + summary: '获取 QQ 系统表情目录', + params: { + refresh: false, + }, + }, + ], summary: '获取 QQ 系统表情目录', readOnly: true, returns: '按分组返回完整的 QQ 系统表情映射。', @@ -96,6 +104,14 @@ export const actions = [ defineAction({ name: 'fetch_face_entity', + examples: [ + { + summary: '按编号查询 QQ 系统表情', + params: { + face_id: 14, + }, + }, + ], summary: '按编号查询 QQ 系统表情', readOnly: true, returns: '表情详情;编号不存在时返回 null。', @@ -112,6 +128,14 @@ export const actions = [ defineAction({ name: 'search_sys_faces', + examples: [ + { + summary: '搜索 QQ 系统表情', + params: { + query: '微笑', + }, + }, + ], summary: '搜索 QQ 系统表情', readOnly: true, returns: '匹配编号、名称、别名或分组名的表情列表。', @@ -131,6 +155,14 @@ export const actions = [ defineAction({ name: 'fetch_super_face_id', + examples: [ + { + summary: '判断 QQ 系统表情是否使用超级表情格式', + params: { + face_id: 14, + }, + }, + ], summary: '判断 QQ 系统表情是否使用超级表情格式', readOnly: true, returns: '是否使用超级表情格式。', diff --git a/packages/onebot/tests/action-docs.test.ts b/packages/onebot/tests/action-docs.test.ts index eb5da75f..7d35a5ad 100644 --- a/packages/onebot/tests/action-docs.test.ts +++ b/packages/onebot/tests/action-docs.test.ts @@ -2,6 +2,9 @@ // declarative action and renders coherent metadata/markdown. import { describe, it, expect } from 'vitest'; import { collectActionDocs, renderActionDocsMarkdown } from '../src/action-docs'; +import { ACTION_REGISTRY } from '../src/actions'; +import { defineAction, defineStreamAction } from '../src/action-kit'; +import { okResponse } from '../src/types'; describe('action-docs', () => { const docs = collectActionDocs(); @@ -75,6 +78,54 @@ describe('action-docs', () => { expect(md).toContain('# OneBot Actions'); expect(md).toContain('`set_group_ban`'); expect(md).toContain('| 参数 | 类型 | 必填 | 默认 | 说明 |'); + expect(md).toContain('"action": "set_group_ban"'); + expect(md).toContain('"duration": 600'); + }); + + it.each(ACTION_REGISTRY.actions)( + '$canonical examples pass the real parameter parser', + ({ spec, doc }) => { + expect(doc.returnsSchema).toBeTypeOf('object'); + expect(doc.examples?.length).toBeGreaterThan(0); + + // Both built-in spec kinds expose a pure parser; do not call handlers + // here, since examples include writes and process-lifecycle actions. + if (!('parse' in spec) || typeof spec.parse !== 'function') { + throw new Error(`${doc.name} has no pure parameter parser`); + } + + for (const example of doc.examples ?? []) { + expect(example.summary).not.toBe(''); + const keys = new Set(doc.params.map((param) => param.name)); + + for (const key of Object.keys(example.params)) { + expect(keys.has(key), `${doc.name} unknown example param ${key}`).toBe(true); + } + + expect(spec.parse(example.params), `${doc.name}: ${example.summary}`).toMatchObject( + { ok: true }, + ); + } + }, + ); + + it('keeps documentation metadata optional for existing custom specs', () => { + for (const define of [defineAction, defineStreamAction]) { + const spec = define({ name: 'custom', params: {}, run: () => okResponse() }); + expect(spec.describe().examples).toBeUndefined(); + expect(spec.describe().returnsSchema).toBeUndefined(); + expect(spec.parse({})).toMatchObject({ ok: true }); + } + }); + + it('isolates collected examples and schemas from caller mutations', () => { + const copy = collectActionDocs(); + const action = copy.find((doc) => doc.name === 'send_group_msg')!; + action.examples![0].params.group_id = 99999; + action.returnsSchema!.type = 'null'; + const fresh = collectActionDocs().find((doc) => doc.name === action.name)!; + expect(fresh.examples![0].params.group_id).toBe(20001); + expect(fresh.returnsSchema!.type).toBe('object'); }); it('documents custom reply content on every forward entry point', () => { diff --git a/packages/onebot/tests/actions-group-admin.test.ts b/packages/onebot/tests/actions-group-admin.test.ts index a2df79a4..50dfa5d7 100644 --- a/packages/onebot/tests/actions-group-admin.test.ts +++ b/packages/onebot/tests/actions-group-admin.test.ts @@ -403,7 +403,7 @@ describe('set_group_ban describe', () => { expect(doc.summary).toBe('禁言群成员(duration=0 解除)'); expect(doc.readOnly).toBe(false); expect(doc.returns).toBeUndefined(); - expect(doc.returnsSchema).toBeUndefined(); + expect(doc.returnsSchema).toMatchObject({ type: 'null' }); expect(doc.stream).toBeUndefined(); expect(doc.invariants).toEqual([]); expect(doc.params).toEqual([ diff --git a/packages/onebot/tests/actions-request.test.ts b/packages/onebot/tests/actions-request.test.ts index 3c696ed9..0f20b4b6 100644 --- a/packages/onebot/tests/actions-request.test.ts +++ b/packages/onebot/tests/actions-request.test.ts @@ -228,7 +228,7 @@ describe('set_friend_add_request describe', () => { expect(doc.summary).toBe('处理好友添加请求'); expect(doc.readOnly).toBe(false); expect(doc.returns).toBeUndefined(); - expect(doc.returnsSchema).toBeUndefined(); + expect(doc.returnsSchema).toMatchObject({ type: 'null' }); expect(doc.stream).toBeUndefined(); expect(doc.invariants).toEqual([]); expect(doc.params).toEqual([ @@ -526,7 +526,7 @@ describe('set_group_add_request describe', () => { expect(doc.summary).toBe('处理加群请求'); expect(doc.readOnly).toBe(false); expect(doc.returns).toBeUndefined(); - expect(doc.returnsSchema).toBeUndefined(); + expect(doc.returnsSchema).toMatchObject({ type: 'null' }); expect(doc.stream).toBeUndefined(); expect(doc.invariants).toEqual([]); expect(doc.params).toEqual([