wechat-robot-skills/skills/send-complex-message/SKILL.md
2026-09-12 17:26:47 +08:00

12 KiB
Raw Blame History

name description
send-complex-message 在当前微信会话中发送纯文本、引用回复或群聊艾特/@/提及消息。用户要求发送一段文字、仅@成员或@所有人、引用某条消息回复、引用时同时@成员时使用;文本和引用支持私聊及群聊,可在发送后结束当前 Agent 对话。

Send Complex Message Skill

描述

本技能是当前微信会话中纯文本、艾特和引用回复的统一发送入口。仅艾特时不需要正文;引用消息必须有正文,也可以附带成员艾特参数。艾特支持指定一个或多个成员,也支持微信原生的 @所有人。

技能脚本位于 scripts/send_complex_message.py,统一调用客户端的 /message/send/refermessage 接口。refer_message_id 是可选参数:不传时由客户端调用普通文本消息方法,传入时发送引用消息。指定成员时,根据昵称或备注查询当前群内未退群成员;@所有人时使用客户端协议值 notify@all,不要把 @昵称 或 @所有人 当普通正文拼接。

本技能不带引用 ID 时通过普通文本消息实现原生艾特,支持只艾特而不附加正文。带引用 ID 时,客户端接收 at 并显示艾特名称,但尚未实现引用消息中的原生艾特提醒,不能把引用发送成功表述为已经提醒成员。

触发条件

  • 用户要求将一段话分成多条发送。
  • 用户要求「引用这条消息回复」「引用我刚才的话说收到」「回复我引用的那条消息」。
  • 需要发送微信原生引用回复,而不是在普通文本中复述原文。
  • 需要艾特、@、提及某个群成员或多个群成员。
  • 用户要求「帮我艾特下 xxx」「@ 一下 xxx」「提一下 xxx 和 yyy」。
  • 用户要求「@所有人」「提醒全体成员」「通知群里所有人」。
  • 需要在群聊里点名提醒某人。
  • 其它时候不应该使用本技能

不带艾特的纯文本和引用回复可用于私聊和群聊;只要提供艾特参数,ROBOT_FROM_WX_ID 就必须是群聊 ID。

入参规范

refer_message_id 不全局必填,只在用户明确要求引用时传入,值为 messages.id。仅文本、仅艾特时省略该参数,不查询引用目标,也不从环境变量自动补齐引用 ID。

发送方式 艾特参数 refer_message_id 正文 content
仅艾特 指定成员或 all: true 不传 可省略或为空
仅文本 不传 不传 必填且不能是纯空白
仅引用 不传 传入 messages.id 必填且不能是纯空白
引用并艾特 指定成员或 all: true 传入 messages.id 必填且不能是纯空白

不引用时也支持正文加艾特。下方 schema 没有全局必填字段,组合校验由 schema 和脚本共同约束。

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": [],
  "properties": {
    "mention": {
      "type": "string",
      "description": "要艾特的群成员昵称或备注。按用户原话提取,不要改写。"
    },
    "mentions": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "要艾特的多个群成员昵称或备注。"
    },
    "all": {
      "type": "boolean",
      "description": "是否 @所有人。用户明确要求 @所有人或通知全体成员时设为 true,不能与 mention/mentions 同时使用。"
    },
    "content": {
      "type": "string",
      "description": "要发送的文本内容。纯文本和引用回复必须提供非空正文;只艾特不附加正文时可以省略或为空字符串。"
    },
    "refer_message_id": {
      "type": "integer",
      "minimum": 1,
      "maximum": 9223372036854775807,
      "description": "可选,仅引用时传入被引用消息的数据库主键 messages.id。仅文本、仅艾特时省略;传入后 content 必须为非空白文本。"
    },
    "ended": {
      "type": "boolean",
      "description": "是否结束当前对话。当 Agent 已经完成消息发送、要说的话已说完、要做的事已做完时,设置为 true。"
    }
  },
  "anyOf": [
    {
      "required": ["content"],
      "properties": { "content": { "pattern": "\\S" } }
    },
    {
      "required": ["mention"],
      "properties": { "mention": { "pattern": "\\S" } }
    },
    {
      "required": ["mentions"],
      "properties": {
        "mentions": { "contains": { "type": "string", "pattern": "\\S" } }
      }
    },
    {
      "required": ["all"],
      "properties": { "all": { "const": true } }
    }
  ],
  "dependencies": {
    "refer_message_id": {
      "required": ["content"],
      "properties": { "content": { "minLength": 1, "pattern": "\\S" } }
    }
  },
  "additionalProperties": false
}

对应命令行参数:

  • --refer-message-id <messages.id> 可选,传入后发送引用消息,必须同时提供非空 --content
  • --mention <昵称或备注> 指定成员时使用,可重复传入
  • --mentions <JSON数组> 指定成员时可选,用于一次传入多个昵称或备注
  • --all(也支持 --mention-all)可选,用于真正 @所有人,不能与 mention 参数同用
  • --content <文本内容> 纯文本和引用消息必填,单独艾特时可省略或为空
  • --ended 可选标志。当 Agent 已完成消息发送、要说的话已说完时传入。

引用消息的选择

  • 仅在用户要求引用时选择消息,传给脚本的引用 ID 只使用 messages.id。
  • 引用当前用户触发本次对话的消息时,使用环境变量 ROBOT_MESSAGE_ID 中的消息主键。
  • 用户要求回复他引用的原消息时,使用 ROBOT_REF_MESSAGE_ID 中的消息主键;该值为空或 0 表示没有引用目标,不能改为引用当前消息。
  • 引用其他历史消息时,从当前机器人数据库 messages 表查找,限定 from_wxid = ROBOT_FROM_WX_ID,按用户描述确认原消息后取 id。不要使用 msg_id、client_msg_id 或 XML 中的 svrid。
  • 引用目标不明确时先确认,不能猜测消息 ID。不要因为上下文存在引用消息就自动发送引用回复。
  • 脚本接收明确的 --refer-message-id,不会自动选择最近一条消息;仅引用且不指定成员时不需要查询成员表。

成员匹配规则

仅指定成员时执行以下匹配;--all 不查询成员表:

  1. 只在当前群聊 ROBOT_FROM_WX_ID 对应的 chat_room_members 记录中查找。
  2. 只匹配 is_leaved 为空或 0 的成员,已经退群的成员不能被艾特。
  3. 使用用户给出的昵称或备注做模糊查询,字段优先级为 remark,然后是 nickname。
  4. 查询到候选成员后,优先选择 remark 完全等于输入值的成员。
  5. 如果没有完全相等的 remark,选择 nickname 完全等于输入值的成员。
  6. 如果没有完全相等结果,选择第一个 remark 包含输入值的成员。
  7. 如果仍未命中,选择第一个 nickname 包含输入值的成员。

执行步骤

  1. 判断用户需要纯文本、仅艾特、引用回复,还是引用时同时艾特。纯文本和引用回复必须准备非空正文 content;引用时按上面的规则确定 refer_message_id。
  2. 如需指定成员,把用户原话中的昵称或备注写入 mention/mentions;@所有人时设置 all: true 并使用 --all。
  3. 在该技能目录执行脚本,例如:

仅艾特某人,不附加正文:

python3 scripts/send_complex_message.py --mention '张三' --ended

仅发送文本,不艾特、不引用:

python3 scripts/send_complex_message.py --content '收到,我来处理' --ended

引用当前用户消息回复:

python3 scripts/send_complex_message.py --refer-message-id "$ROBOT_MESSAGE_ID" --content '收到,我来处理' --ended

回复用户引用的原消息(先确认 ROBOT_REF_MESSAGE_ID 大于 0):

python3 scripts/send_complex_message.py --refer-message-id "$ROBOT_REF_MESSAGE_ID" --content '同意这个安排'

引用已确认的历史消息并附带成员显示(当前客户端不产生原生艾特提醒):

python3 scripts/send_complex_message.py --refer-message-id 12345 --mention '张三' --content '请看一下这个'

艾特群成员并附加正文:

python3 scripts/send_complex_message.py --mention '张三' --content '看一下这个'

用户要求 @所有人时传 --all,不要把“所有人”当成员昵称查询:

python3 scripts/send_complex_message.py --all --content '请大家查看群公告'

当 Agent 认为任务已完成、对话可以结束时,加上 --ended 标志:

python3 scripts/send_complex_message.py --mention '张三' --content '看一下这个' --ended
  1. 指定成员时,脚本查询数据库表 chat_room_members 并解析微信 ID;--all 时跳过数据库查询,使用 at: ["notify@all"]。如果指定成员未命中,可以查询记忆里是否记录了对方的别称。
  2. 脚本通过 X-Private-Token 请求头传递环境变量 ROBOT_CLIENT_PRIVATE_TOKEN,统一调用 POST http://127.0.0.1:{ROBOT_WECHAT_CLIENT_PORT}/api/v1/robot/message/send/refermessage。请求体包含 to_wxid、content、at;只有引用时才添加 refer_message_id。不引用时,客户端直接转入普通文本发送方法;纯文本的 at 为空数组,仅艾特的 content 为空字符串。

to_wxid 固定取当前会话 ROBOT_FROM_WX_ID。技能在客户端内执行,无需携带管理后台的机器人实例 query id。

校验规则

  • ROBOT_FROM_WX_ID 必须配置;带艾特时必须以 @chatroom 结尾。
  • 没有艾特且正文为空或纯空白时不能发送;空的成员名称、空成员数组和 all: false 都不算有效艾特。
  • 仅艾特时,content 可直接为空,由客户端根据 at 生成艾特正文,不添加额外话语。
  • 传入 refer_message_id 时,必须是 int64 范围内的正整数,引用正文不能是空字符串或纯空白;不引用时直接省略 ID,不能为满足校验而猜测或自动填入消息 ID。
  • --all 必须独占,不能再指定成员。
  • 每个要艾特的人都必须能在当前群内匹配到未退群成员。
  • 如果同一个微信 ID 被多个昵称命中,只会艾特一次。

依赖安装

  • 指定成员、需要查询数据库时,脚本会自动创建虚拟环境并安装依赖;纯文本、仅引用或 --all 不需要安装数据库依赖。
  • 如需手动重新安装,可执行:python3 scripts/bootstrap.py

ended 行为

  • 当 --ended 传入且客户端返回业务状态 code: 200 时,脚本在正常输出末尾追加打印独立一行 ended。
  • ended 字符串必须位于输出的最末尾,前面不能跟其他字符。
  • Agent 检测到输出以 ended 结尾时,会自动退出 Agent 循环。

回复要求

  • 成功时,脚本输出「文本消息发送成功」「引用消息发送成功」「艾特消息发送成功」或「艾特所有人消息发送成功」,表示消息已通过客户端接口直接发送,不要再重复发送正文。
  • HTTP 200 不等于业务成功,必须检查响应中的 code。当前引用接口成功时可能返回 data: null,不能因没有消息对象重发。
  • 如果传入 --ended,输出末尾会追加 ended,Agent 会自动结束对话。
  • 失败时,返回脚本输出的具体错误信息,不输出 ended。引用消息不存在、原消息内容不完整或原发送人不存在时,按客户端错误说明原因;请求超时或发送结果不确定时,不自动重试,以免重复发送。