diff --git a/scripts/gen_skill_shortcut_sections.py b/scripts/gen_skill_shortcut_sections.py index 88609b49..53191b60 100644 --- a/scripts/gen_skill_shortcut_sections.py +++ b/scripts/gen_skill_shortcut_sections.py @@ -46,7 +46,28 @@ MONO_START = "" MONO_END = "" PRODUCT_START = "" PRODUCT_END = "" -DISCOVERY_ONLY_SERVICES = {"chat"} +MONO_COMPACT_SERVICES = {"chat"} +MULTI_DISCOVERY_ONLY_SERVICES: set[str] = set() +MULTI_COMPACT_TABLE_SERVICES = {"chat"} + +# Keep the Chinese intent cue while shortening long, mechanical chat details. +# Short Chinese descriptions are already token-efficient and stay source-owned. +CHAT_COMPACT_DESCRIPTIONS = { + "+at-me": "查最近 @我;auto window, project sender/time/content/chat.", + "+bot-find": "搜索全部机器人;include others/official, return DM-ready openDingTalkId.", + "+broadcast": "多人单聊群发;resolve userId and send individually.", + "+chat-audit-join": "审批入群验证;approve/reject/delete/ignore/block.", + "+chat-members-list": "列出群成员;group users/bots, resolve chat name.", + "+chat-messages": "拉取会话消息;group/DM, project sender/text/time.", + "+conversation-clear-messages": "清空本人会话记录;current-user view only, irreversible.", + "+conversation-mark-read": "标记消息已读;includes all earlier messages.", + "+conversation-set-top": "批量置顶/取消;max 10 chats.", + "+messages-resource-download": "安全下载消息资源;image/video/audio/file.", + "+messages-update-card": "流式更新卡片;final `--flow-status` must be 3.", + "+send-to-group": "按群名发消息;resolve openConversationId.", + "+thread-replies": "拉取话题全部回复;project sender/text/time.", + "+unread-chats": "列出未读会话;project name/count/chat ID.", +} def md_escape(value: Any) -> str: @@ -92,7 +113,7 @@ def mono_overview(items: list[dict[str, Any]]) -> str: skill = "—" if path: skill = next((part for part in path.parts if part.startswith("dingtalk-")), path.parent.name) - compact = " --compact" if service in DISCOVERY_ONLY_SERVICES else "" + compact = " --compact" if service in MONO_COMPACT_SERVICES else "" rows.append(f"| `{md_escape(service)}` | {count} | `{md_escape(skill)}` | `dws shortcut list --service {md_escape(service)}{compact} --format json` |") body = "\n".join(rows) return f"""{MONO_START} @@ -107,7 +128,7 @@ def mono_overview(items: list[dict[str, Any]]) -> str: def product_section(service: str, rows: list[dict[str, Any]]) -> str: - if service in DISCOVERY_ONLY_SERVICES: + if service in MULTI_DISCOVERY_ONLY_SERVICES: return f"""{PRODUCT_START} ## Shortcut 发现与选择 @@ -119,6 +140,32 @@ def product_section(service: str, rows: list[dict[str, Any]]) -> str: - 选中后用 `dws schema --cli-path "" --format json` 读取完整契约;若该 leaf 暂未进入 Schema,则重新运行不带 `--compact` 的 catalog 命令并选取同一 `cli_path`。 - 执行前始终用 `dws --help` 核对当前 Cobra flags;无匹配项时回退到意图表、reference 和原子命令。 - `confirmation=user_required` 时先征得用户确认,再添加 `--yes`;Schema、catalog 与 Help 冲突时采用更安全的解释并报告契约漂移。 +{PRODUCT_END}""" + + if service in MULTI_COMPACT_TABLE_SERVICES: + table = [] + for item in rows: + description = CHAT_COMPACT_DESCRIPTIONS.get( + item["command"], item["desc"] + ) + table.append( + f"| `{md_escape(item['command'])}` | {md_escape(description)} |" + ) + high_risk = [ + f"`{md_escape(item['command'])}`" + for item in rows + if item["risk"] == "high-risk-write" + ] + return f"""{PRODUCT_START} +## Shortcuts(无专用脚本/recipe 时优先) + +Complete public catalog index; every command uses the `dws {service}` prefix. Each row keeps a Chinese intent cue. After selection, read the leaf Schema for parameters, constraints, risk, and confirmation. See “Shortcut 执行契约” below. + +| Shortcut | 适用场景 | +|---|---| +{os.linesep.join(table)} + +`risk=high-risk-write`(高风险): {", ".join(high_risk)}. Confirmation follows leaf Schema `confirmation` and the runtime gate; never infer it from risk. {PRODUCT_END}""" table = [] @@ -150,7 +197,7 @@ def update_product_skills(items: list[dict[str, Any]]) -> None: for item in items: by_service[item["service"]].append(item) for service, path in SERVICE_TO_SKILL.items(): - if service not in by_service and service not in DISCOVERY_ONLY_SERVICES: + if service not in by_service and service not in MULTI_DISCOVERY_ONLY_SERVICES: continue if not path.exists(): raise RuntimeError(f"skill file not found for {service}: {path}") diff --git a/skills/multi/dingtalk-chat/SKILL.md b/skills/multi/dingtalk-chat/SKILL.md index 4826c036..27b3cddf 100644 --- a/skills/multi/dingtalk-chat/SKILL.md +++ b/skills/multi/dingtalk-chat/SKILL.md @@ -11,141 +11,335 @@ metadata: # 钉钉群聊 / 消息 Skill -## 前置条件 — 执行操作前必读 +## Preconditions -> **CRITICAL — 执行任何 `dws` 操作前,MUST 先用 Read 工具完整读取 [`dws-shared`](../dws-shared/SKILL.md)。**该轻量文件包含全局执行契约、安全底线及 shared references 的按需加载导航;不要预加载其全部 references。 +> **CRITICAL — Before any `dws` operation, MUST fully read [`dws-shared`](../dws-shared/SKILL.md).** It defines the global execution contract, safety floor, and on-demand shared-reference routing. Do not preload all of its references. -> 命令参考:[chat.md](references/chat.md);表情:[chat-emoji-list.md](references/chat-emoji-list.md);剧本:[01-messaging.md](references/01-messaging.md)。 +> Command reference: [chat.md](references/chat.md); emoji list: [chat-emoji-list.md](references/chat-emoji-list.md); workflows: [01-messaging.md](references/01-messaging.md). -## Shortcut 发现与选择 +## Shortcuts(无专用脚本/recipe 时优先) -本产品提供公开内建 `+shortcut`,但本 Skill 不静态枚举,避免与当前 CLI catalog 漂移。 +Complete public catalog index; every command uses the `dws chat` prefix. Each row keeps a Chinese intent cue. After selection, read the leaf Schema for parameters, constraints, risk, and confirmation. See “Shortcut 执行契约” below. -- 路由优先级:精确脚本 / recipe > 匹配的公开 shortcut > reference 中的原子命令。 -- 无精确脚本 / recipe 覆盖,或准备手工组合多个原子命令前,必须先运行 `dws shortcut list --service chat --compact --format json`;当前 CLI 不接受 `--compact` 时去掉该 flag 重试。 -- 必须依据返回的 `intent`、`description` 和 `cli_path` 选择,不得猜测 `+` 命令名。 -- 选中后用 `dws schema --cli-path "" --format json` 读取完整契约;若该 leaf 暂未进入 Schema,则重新运行不带 `--compact` 的 catalog 命令并选取同一 `cli_path`。 -- 执行前始终用 `dws --help` 核对当前 Cobra flags;无匹配项时回退到意图表、reference 和原子命令。 -- `confirmation=user_required` 时先征得用户确认,再添加 `--yes`;Schema、catalog 与 Help 冲突时采用更安全的解释并报告契约漂移。 +| Shortcut | 适用场景 | +|---|---| +| `+at-me` | 查最近 @我;auto window, project sender/time/content/chat. | +| `+bot-find` | 搜索全部机器人;include others/official, return DM-ready openDingTalkId. | +| `+bot-search` | 搜索当前用户自己创建的机器人 | +| `+broadcast` | 多人单聊群发;resolve userId and send individually. | +| `+category-add-conversation` | 将会话移动到指定的自定义分组中 | +| `+category-create` | 创建用户自定义会话分组 | +| `+category-delete` | 删除用户自定义会话分组 | +| `+category-list` | 获取用户自定义会话分组 | +| `+category-list-conversations` | 拉取指定自定义会话分组下的会话 | +| `+category-remove-conversation` | 将会话从指定的自定义分组中移出 | +| `+category-rename` | 更新用户自定义会话分组的名称 | +| `+chat-add-bot` | 将机器人添加到群中 | +| `+chat-audit-join` | 审批入群验证;approve/reject/delete/ignore/block. | +| `+chat-bots` | 查看群内所有机器人 | +| `+chat-create` | 以当前用户身份创建钉钉群聊 | +| `+chat-dismiss` | 解散群聊(不可逆,需群主权限) | +| `+chat-get-by-id` | 根据群号获取群聊信息 | +| `+chat-invite-url` | 获取群邀请链接 | +| `+chat-list-all` | 分页拉取我加入的所有群列表 | +| `+chat-list-join-requests` | 分页拉取入群验证记录 | +| `+chat-list-mine` | 拉取我创建/管理的群 | +| `+chat-members-get` | 根据成员 openDingTalkId 批量查询群成员详情 | +| `+chat-members-list` | 列出群成员;group users/bots, resolve chat name. | +| `+chat-messages` | 拉取会话消息;group/DM, project sender/text/time. | +| `+chat-mute` | 全员禁言 / 取消全员禁言 | +| `+chat-mute-member` | 指定群成员禁言 / 取消禁言 | +| `+chat-quit` | 退出群聊 | +| `+chat-remove-bot` | 从群内移除机器人 | +| `+chat-role-add` | 添加群身份 | +| `+chat-role-list` | 拉取会话的群身份列表 | +| `+chat-role-query-user` | 查询群成员的群身份 | +| `+chat-role-remove` | 删除群身份 | +| `+chat-role-remove-user` | 移除用户的指定群身份 | +| `+chat-role-set-user` | 设置用户的群身份(覆盖该用户的全部群身份) | +| `+chat-role-update` | 更新群身份名称 | +| `+chat-search` | 按关键词搜索群聊 | +| `+chat-set-admin` | 设置 / 取消群管理员 | +| `+chat-set-history` | 设置新成员入群可查看历史消息范围 | +| `+chat-transfer-owner` | 转让群主 | +| `+chat-update` | 更新群名称(仅名称,不支持 description) | +| `+chat-update-alias` | 设置群备注(仅自己可见) | +| `+chat-update-icon` | 更新群头像 | +| `+chat-update-nick` | 设置当前用户在群内的群昵称 | +| `+chat-update-settings` | 更新群设置(settingKey + status) | +| `+conversation-clear-all-red-point` | 清除所有会话红点(全部已读) | +| `+conversation-clear-messages` | 清空本人会话记录;current-user view only, irreversible. | +| `+conversation-clear-red-point` | 清除会话红点 | +| `+conversation-hide` | 会话列表中隐藏会话(收到新消息会重新出现) | +| `+conversation-info` | 获取会话信息(群聊传 --group,单聊传 --open-dingtalk-id) | +| `+conversation-list` | 分页获取当前用户的全部会话列表(单聊+群聊) | +| `+conversation-list-top` | 拉取置顶会话列表,可只看群聊或单聊 | +| `+conversation-mark-read` | 标记消息已读;includes all earlier messages. | +| `+conversation-mark-unread` | 标记会话为未读 | +| `+conversation-mute` | 会话消息免打扰(支持单聊/群聊) | +| `+conversation-set-top` | 批量置顶/取消;max 10 chats. | +| `+dm` | 按姓名直接给某人发单聊消息(自动解析 userId) | +| `+feed-group-query-item` | 在会话分组结果中按会话 ID 精确查询多项 | +| `+flag-cancel` | 取消收藏一条或多条消息(最多 10 条) | +| `+flag-create` | 收藏一条或多条消息(最多 10 条) | +| `+flag-list` | 分页查询当前用户收藏的消息 | +| `+group-members` | 按群名列出群成员(自动搜群解析 openConversationId) | +| `+messages-add-emoji` | 对消息添加 emoji 表情回应 | +| `+messages-add-text-emotion` | 对消息添加文字表情回应 | +| `+messages-batch-recall-by-bot` | 机器人撤回单聊消息 | +| `+messages-batch-send-by-bot` | 机器人批量向用户发送单聊 Markdown 消息 | +| `+messages-combine-forward` | 合并转发多条消息 | +| `+messages-create-text-emotion` | 创建文字表情(获取 emotionId) | +| `+messages-forward` | 转发单条消息 | +| `+messages-forward-topic` | 转发话题消息到目标会话 | +| `+messages-list` | 拉取群聊会话消息 | +| `+messages-list-direct` | 拉取单聊会话消息 | +| `+messages-list-pin` | 拉取会话中钉住的消息列表 | +| `+messages-list-unread-conversations` | 获取有未读消息的会话列表 | +| `+messages-mget` | 根据消息 ID 批量查询消息(最多 50 条) | +| `+messages-query-send-status` | 查询消息发送状态 | +| `+messages-read-status` | 查询消息的已读/未读状态 | +| `+messages-recall` | 撤回当前用户发送的消息 | +| `+messages-recall-by-bot` | 机器人撤回群消息 | +| `+messages-remove-emoji` | 移除消息的 emoji 表情回应 | +| `+messages-remove-text-emotion` | 移除消息的文字表情回应 | +| `+messages-reply` | 以当前用户身份引用回复消息(自动补全原发送者) | +| `+messages-resource-download` | 安全下载消息资源;image/video/audio/file. | +| `+messages-resource-url` | 获取消息资源(图片/视频/语音)下载链接 | +| `+messages-send` | 统一发送文本、Markdown、当前用户文件或已有 mediaId 图片 | +| `+messages-send-by-bot` | 机器人向群聊发送 Markdown 消息 | +| `+messages-send-by-webhook` | 自定义机器人 Webhook 发送群消息 | +| `+messages-send-card` | 创建流式卡片,可在同一次调用中写入内容并结束 | +| `+messages-set-pin` | 钉住消息(Pin) | +| `+messages-set-top` | 置顶消息 | +| `+messages-unset-pin` | 取消钉住消息(Unpin) | +| `+messages-unset-top` | 取消置顶消息 | +| `+messages-update-card` | 流式更新卡片;final `--flow-status` must be 3. | +| `+my-groups` | 列出我加入的群,可按类型过滤并投影关键字段 | +| `+search-msg` | 多维搜索消息,可全量翻页并批量富化详情 | +| `+send-to-group` | 按群名发消息;resolve openConversationId. | +| `+thread-replies` | 拉取话题全部回复;project sender/text/time. | +| `+unread-chats` | 列出未读会话;project name/count/chat ID. | + +`risk=high-risk-write`(高风险): `+category-delete`, `+chat-dismiss`, `+chat-remove-bot`, `+chat-role-remove`, `+conversation-clear-messages`. Confirmation follows leaf Schema `confirmation` and the runtime gate; never infer it from risk. +## Shortcut 执行契约 + +This is the only Shortcut entry point for multi/chat; `references/` documents +atomic commands only. Routing priority: +exact script/recipe > matching public Shortcut > atomic command. + +- Select a real `cli_path` from the table or + `dws shortcut list --service chat --format json`; never guess names. +- Read `dws schema --cli-path "" --format json`, then verify flags + with leaf `--help`. If absent from Schema, use the same path in the full Catalog. +- For `confirmation=user_required`, confirm before adding `--yes`. On source + conflict, use the safer interpretation and report it. + +### 高频选择 + +| 用户意图 | 首选 Shortcut | 关键边界 | +|---|---|---| +| 当前用户 / 应用机器人 / Webhook 发消息 | `+messages-send --as user|bot|webhook` | Never mix identities | +| 按姓名发单聊 / 按群名发群消息 | `+dm` / `+send-to-group` | Resolves real person/chat IDs | +| 拉单个群聊或单聊消息 | `+chat-messages` | Choose one of `--group`, `--user`, `--open-dingtalk-id` | +| 组合搜索消息 | `+search-msg` | Keyword, sender, @, chat, type, time, `--page-all` | +| 查询 @ 我的消息 | `+at-me` | Defaults to 7 days; window and pagination are adjustable | +| 按消息 ID 批量取详情与 reaction | `+messages-mget` | Max 50; supports `--no-reactions` | +| 读取已知 thread/topic 的全部回复 | `+thread-replies` | `--group` required; choose thread or topic ID | +| 下载单个 mediaId/fileId | `+messages-resource-download` | mediaId needs message/chat context; fileId does not | +| 创建并按需立即更新流式卡片 | `+messages-send-card` | Choose one target; `--content` controls immediate update | +| 清空会话消息 / 删除分组 / 解散群 | `+conversation-clear-messages` / `+category-delete` / `+chat-dismiss` | 高风险; add `--yes` only after Schema confirmation | + +### 统一发送 + +Before sending, verify identity, target, body, title, mentions, message type, +and attachment path; ask if ambiguous. Reuse the same `--idempotency-key` on retry. + +```bash +dws chat +messages-send --as user --chat-id --text "内容" --idempotency-key --format json +dws chat +messages-send --as user --open-dingtalk-id --msg-type file --file ./report.pdf --idempotency-key --format json +dws chat +messages-send --as bot --robot-code --chat-id --markdown "## 标题 + +正文" --format json +dws chat +messages-send --as webhook --webhook-token --title "告警" --text "内容" --at-all --format json +``` + +- `user` supports text, Markdown, existing mediaId images, and local files. + Audio/video use `--file` and are sent as files. +- `bot` supports group or batch-DM text/Markdown. A webhook token selects its + chat. Neither identity supports files. +- The Shortcut normalizes @ placeholders. Pass only required `--at-*` / + `--at-all`; never construct `@10` manually. +- Newlines in `--text` / `--markdown` must be real `U+000A`; separate Markdown + paragraphs with a blank line. + +### 查询、资源与卡片 + +- `+search-msg --page-all` paginates and enriches in batches. On partial + failure, preserve results and return a per-item ledger. +- `+at-me`、`+chat-messages`、`+messages-mget`、`+search-msg`、`+thread-replies` + support `--download-resources --output-dir ./downloads [--overwrite]`. + For nested resources, prefer the child `messageId` in `resourceRefs`; + inherit the parent only if the chat ID is missing. +- These queries and `+messages-resource-download` are `read/not_required`; + never pass `--yes`. Output must be a relative path inside the working + directory without `..`; pass `--overwrite` only when explicitly requested. +- Accept reviewed DingTalk/public OSS HTTPS URLs only. Validate every redirect + and never forward headers across hosts. +- `+messages-send-card` 的 `--group`、`--receiver`、`--receiver-open-dingtalk-id` + are mutually exclusive. With `--content`, update immediately; otherwise + return `bizId`. The final `+messages-update-card` call must use `--flow-status 3`. + +### Shortcut 错误处理 + +If a leaf is missing or parameters are invalid, check the full Catalog, +Schema, and `--help`, then correct once. Re-extract IDs from actual output. +Stop and report insufficient permission, unresolved ambiguity, no result, or +contract conflict. + ## 渐进加载与一级路由 -决定回退到原子命令后,先按一级分支定位能力;兄弟命令容易混淆时读取 -[intent-guide.md](references/intent-guide.md),再从 -[chat.md 的命令索引表](references/chat.md#命令索引表) 找到叶子。执行前用 -`dws schema --cli-path "chat " --format json` 读取选择、参数、约束和确认语义, -并以当前叶子的 `--help` 核对 Cobra flags。 +After choosing atomic fallback, select a top-level branch. If sibling commands +are ambiguous, read [intent-guide.md](references/intent-guide.md), then locate +the leaf in [chat.md](references/chat.md#命令索引表). Before execution, read +`dws schema --cli-path "chat " --format json` for selection, parameters, +constraints, and confirmation; verify Cobra flags with leaf `--help`. ```text dws chat -├── message # 发送、查询、搜索、回复、转发、撤回、卡片、表情 -├── group* / group-role # 群、成员、设置、禁言、公告、群身份 -├── bot # 机器人搜索 -├── category # 会话分组 -├── search # 群搜索与共同群 -├── conversation-info / list-* # 会话信息与会话列表 +├── message # send, list, search, reply, forward, recall, cards, reactions +├── group* / group-role # chats, members, settings, mute, notice, roles +├── bot # bot search +├── category # conversation categories +├── search # chat and common-chat search +├── conversation-info / list-* # conversation info and lists ├── mute* / hide / set-top -├── mark-* / clear-* # 已读、未读、红点与消息清理 +├── mark-* / clear-* # read/unread, red dots, message clearing ├── text / chmod / data-auth ├── +shortcut └── scripts ``` -分支细节按需读取:[消息](references/chat/chat-message.md)、 -[群与成员](references/chat/chat-group.md)、[机器人与 Webhook](references/chat/chat-bot.md)、 -[会话状态与分组](references/chat/chat-conversation.md)。 +Load branch details on demand: [消息](references/chat/chat-message.md), +[群与成员](references/chat/chat-group.md), +[机器人与 Webhook](references/chat/chat-bot.md), and +[会话状态与分组](references/chat/chat-conversation.md). ## 核心意图表 -本表只负责身份、会话类型、操作类型和三个专用脚本的消歧。除精确命中的脚本外, -表中命令均为未匹配公开 shortcut 时的原子回退;不得跳过上方 Shortcut 发现流程。 +Use this table only to disambiguate identity, chat type, operation, and three +dedicated scripts. Except for exact script matches, commands are atomic +fallbacks used only when no public Shortcut matches. | 用户说 | 分支 / 原子回退 | |---|---| -| “发给某人” | 当前用户单聊:先解析 `openDingTalkId` / `userId`,再用 `message send` | -| “发到某群” | 当前用户群聊:先 `chat search`,再用 `message send --group` | -| “用应用机器人发” | `message send-by-bot`,不要用当前用户身份代发 | -| “Webhook 推送” | `message send-by-webhook`;需要 @ 时正文与 @ 参数必须对应 | -| “拉某个会话的消息” | `message list`,先定位会话并确定时间边界 | -| “搜消息关键词 / 组合搜索” | 简单关键词可用 `message search`;发送者、@、多会话条件用 `search-advanced` | -| “撤回用户消息 / 机器人消息” | `message recall` / `recall-by-bot`,两者所需消息标识不同 | +| “发给某人” | Current-user DM: resolve `openDingTalkId` / `userId`, then `message send` | +| “发到某群” | Current-user group message: `chat search`, then `message send --group` | +| “用应用机器人发” | Use `message send-by-bot`; never impersonate the current user | +| “Webhook 推送” | Use `message send-by-webhook`; mention text and @ flags must agree | +| “拉某个会话的消息” | Use `message list`; locate the chat and set time bounds first | +| “搜消息关键词 / 组合搜索” | Use `message search` for keywords; `search-advanced` for sender/@/multi-chat filters | +| “撤回用户消息 / 机器人消息” | Use `message recall` / `recall-by-bot`; their message IDs differ | | “群消息翻页导出” | `python3 scripts/chat_export_messages.py` | | “查和某人的聊天记录” | `python3 scripts/chat_history_with_user.py` | | “机器人多群广播” | `python3 scripts/bot_broadcast.py` | -脚本路径相对于本 Skill 根目录;命中脚本时先确认 `python3` 可用,不可用则按 -[01-messaging.md](references/01-messaging.md) 中对应场景的原子命令备选流程执行。 +Script paths are relative to this Skill. For a script match, verify `python3`; +if unavailable, use the matching atomic fallback in +[01-messaging.md](references/01-messaging.md). ## 关键 SOP -以下 SOP 约束身份、ID、时间边界和成功判断,不覆盖 Shortcut 路由优先级。匹配 -shortcut 时按其 leaf 契约执行;未匹配时再使用下面的原子命令。所有结构化调用带 -`--format json`;下游 ID 必须取自真实返回。 +These SOPs constrain identity, IDs, time bounds, and success checks without +overriding Shortcut priority. Follow a matching leaf; otherwise use the atomic +commands below. Add `--format json` to structured calls and take downstream +IDs from actual output. ### 发消息 -1. 人名先用 `dws aisearch person --keyword "<姓名>" --dimension name` 取 - `openDingTalkId`(优先)或 `userId`;群名先用 `dws chat search --query "<群名>"` - 取 `openConversationId`。 -2. 未匹配 shortcut 时,单聊使用 `message send --open-dingtalk-id`(必要时用 `--user`);群聊使用 - `message send --group`。本地文件、音频、视频使用 `--msg-type file|audio|video --file-path`。 -3. 按结构化返回判断成功;`openTaskId` 只能查询发送状态,不是撤回所需消息 ID。 +1. Resolve a person with + `dws aisearch person --keyword "<姓名>" --dimension name`; prefer + `openDingTalkId`, otherwise `userId`. Resolve a chat with + `dws chat search --query "<群名>"` to get `openConversationId`. +2. Without a matching Shortcut, use `message send --open-dingtalk-id` + (`--user` if needed) for DMs and `message send --group` for groups. Local + files/audio/video use `--msg-type file|audio|video --file-path`. +3. Judge success from structured output. `openTaskId` queries send status; it + is not the message ID required for recall. ### 建群或拉人 -1. 对每个成员先用 `aisearch person` 取得真实 `userId`,不要把姓名直接作为成员参数。 -2. 未匹配 shortcut 时,建群使用 `group create --name "<群名>" --users `;已有群加成员使用 - `group members add --id --users `。 -3. 建群后从返回提取 `openConversationId`;群名或成员有歧义时先让用户确认。 +1. Resolve every member to a real `userId` with `aisearch person`; never pass + names as member arguments. +2. Without a matching Shortcut, create with + `group create --name "<群名>" --users ` or add members with + `group members add --id --users `. +3. Extract `openConversationId` from creation output. Confirm ambiguous chat + names or members. ### 拉取或撤回消息 -1. 群名先用 `chat search` 定位;单聊对象先解析 `userId` 或 `openDingTalkId`。 -2. 未匹配 shortcut 时,`message list` 必须使用用户时间范围或明确收敛后的时间边界。 -3. 仅在用户明确要求撤回时,从消息列表取得真实 `openMessageId`,再调用 - `message recall --conversation-id --msg-id `。 +1. Resolve a group with `chat search`; resolve a DM target to `userId` or + `openDingTalkId`. +2. Without a matching Shortcut, run `message list` only with the user's time + range or an explicitly narrowed boundary. +3. Only on an explicit recall request, take the real `openMessageId` from the + message list, then call + `message recall --conversation-id --msg-id `. ## 低频操作原子回退入口 -这里只负责未匹配公开 shortcut 后的原子命令定位;具体 flags、风险和确认以 leaf -Schema、`--help` 与对应 reference 为准。 +Use this table only to locate atomic fallbacks when no public Shortcut matches. +Follow leaf Schema, `--help`, and the linked reference for flags, risk, and +confirmation. | 用户意图 | 原子回退 | 关键边界 | |---|---|---| -| 收藏 / 取消收藏 / 收藏列表 | `message add-favorite` / `remove-favorite` / `list-favorites` | 只改变个人收藏,不撤回原消息 | -| 编辑已发送消息 | `message edit` | 修改现有消息,不是重新发送 | -| 普通群升级为外部群 | `group upgrade-to-external` | 不可逆,执行前读取 leaf Schema 的确认语义 | -| 设置或清除群昵称 | `group update-nick` | 省略 `--nick` 表示清除当前用户群昵称 | -| 查会话所属分组 / 批量查分组 | `category list-by-conv` / `batch-info` | 与列出全部分组不同 | -| 特别关注人的消息 | `message list-focused` | 查消息;“我关注了谁”走 `dingtalk-contact` | -| 我和某人的共同群 | `search-common` | 包含“我”时先用 `contact user get-self` 取本人昵称;按 `nextCursor` 翻页 | -| 群公告 / 群身份 | `group notice *` / `group-role *` | 公告与企业公告不同;群身份不是管理员角色 | -| 消息置顶 / Pin / 会话置顶 | `message set-top-msg` / `set-pin-msg` / `chat set-top` | 三者作用对象不同 | -| 查未读会话 / 谁看了消息 | `message list-unread-conversations` / `message read-status` | 前者查会话,后者查指定消息的阅读人员 | -| 标未读 / 标已读 / 清红点 / 全部已读 | `mark-unread` / `mark-read` / `clear-red-point` / `clear-all-red-point` | 修改会话或消息状态,不是只读查询 | -| 行为授权 / 跨组织聊天数据授权 | `chmod` / `data-auth cross-org` | 前者只授权具体操作,后者只授权跨组织数据读取;都不执行目标操作 | -| 退群 / 解散群 | `group quit` / `group dismiss` | 前者只退出当前用户,后者解散整个群 | +| 收藏 / 取消收藏 / 收藏列表 | `message add-favorite` / `remove-favorite` / `list-favorites` | Changes personal favorites; does not recall | +| 编辑已发送消息 | `message edit` | Edits the existing message; does not resend | +| 普通群升级为外部群 | `group upgrade-to-external` | 不可逆; read leaf confirmation before execution | +| 设置或清除群昵称 | `group update-nick` | Omit `--nick` to clear the current user's chat nickname | +| 查会话所属分组 / 批量查分组 | `category list-by-conv` / `batch-info` | Different from listing every category | +| 特别关注人的消息 | `message list-focused` | Lists messages; “我关注了谁” routes to `dingtalk-contact` | +| 我和某人的共同群 | `search-common` | For “我”, get self nickname via `contact user get-self`; paginate by `nextCursor` | +| 群公告 / 群身份 | `group notice *` / `group-role *` | Chat notices differ from company notices; identity is not admin role | +| 消息置顶 / Pin / 会话置顶 | `message set-top-msg` / `set-pin-msg` / `chat set-top` | Three different target types | +| 查未读会话 / 谁看了消息 | `message list-unread-conversations` / `message read-status` | First lists chats; second lists readers of one message | +| 标未读 / 标已读 / 清红点 / 全部已读 | `mark-unread` / `mark-read` / `clear-red-point` / `clear-all-red-point` | Mutates chat/message state; not read-only | +| 行为授权 / 跨组织聊天数据授权 | `chmod` / `data-auth cross-org` | Grants action or cross-org read access; executes neither target action | +| 退群 / 解散群 | `group quit` / `group dismiss` | First exits current user; second dismisses the entire chat | ## 跨命令关键边界 -- 当前用户、应用机器人、Webhook 三种发送身份不能混用。 -- `message list` 拉取指定会话;关键词与组合条件使用 `search` / `search-advanced`。 -- `openTaskId` 不是 `openMessageId`;审批、日历、待办等产品对象 ID 也不能代替消息 ID。 -- 消息置顶、消息 Pin、会话置顶是三种不同操作。 -- 参数、风险与确认以 leaf Schema 和当前 `--help` 为准,不从其他安全字段推断。 +- Never mix current-user, app-bot, and Webhook identities. +- `message list` reads one chat; use `search` / `search-advanced` for keywords + and combined filters. +- `openTaskId` is not `openMessageId`; approval, calendar, todo, and other + product IDs cannot replace a message ID. +- Message top, message Pin, and conversation top are distinct operations. +- Follow leaf Schema and current `--help` for parameters, risk, and + confirmation; never infer across safety fields. ## Workflow 与错误导航 -- 复杂消息流程读取 [01-messaging.md](references/01-messaging.md)。 -- 新人接待读取 [01-onboarding.md](references/workflows/01-onboarding.md)。 -- 命令返回错误时读取 [chat-error-recovery.md](references/chat-error-recovery.md),只修正一次; - 权限不足、目标仍有歧义或重新搜索无结果时停止并报告。 +- For complex messaging, read [01-messaging.md](references/01-messaging.md). +- For onboarding, read + [01-onboarding.md](references/workflows/01-onboarding.md). +- On command error, read + [chat-error-recovery.md](references/chat-error-recovery.md) and correct + once. Stop on insufficient permission, unresolved ambiguity, or no result + after re-search. ## 跨产品协作 -- 收件人是人名 → 先用 `dingtalk-contact` 或 `dingtalk-aisearch` 拿 `openDingTalkId` / `userId` -- 要发本地图片/文件 → 直接用 `dws chat message send --msg-type file --file-path <本地路径>`;图片会作为可下载的文件附件发送,不会内联渲染。只有上游已提供有效 mediaId 时才用 `--msg-type image --media-id`,DWS CLI 不能把本地文件转换成 mediaId -- 紧急升级(应用内/短信/电话)→ 切到 `dingtalk-ding` -- 发邮件 → 切到 `dingtalk-mail` +- 收件人是人名 → use `dingtalk-contact` or `dingtalk-aisearch` to get + `openDingTalkId` / `userId`. +- 要发本地图片/文件 → use + `dws chat message send --msg-type file --file-path <本地路径>`. Images are + downloadable attachments, not inline. Use `--msg-type image --media-id` + only with an existing valid mediaId; DWS cannot convert local files to mediaId. +- 紧急升级(应用内/短信/电话)→ route to `dingtalk-ding`. +- 发邮件 → route to `dingtalk-mail`. diff --git a/skills/multi/dingtalk-chat/references/01-messaging.md b/skills/multi/dingtalk-chat/references/01-messaging.md index 4ec0810f..a1e6f738 100644 --- a/skills/multi/dingtalk-chat/references/01-messaging.md +++ b/skills/multi/dingtalk-chat/references/01-messaging.md @@ -7,7 +7,7 @@ | query-group-chat | **优先**:`python3 scripts/chat_export_messages.py --query "<群名>" --time "" [--no-forward] [--limit N] [--output messages.json]`(自动搜群+翻页+导出)
备选:1. `chat search --query "<群名>"` → 取 `openConversationId`
2. `chat message list --group --time "" --direction newer` → 取消息列表
3. **翻页**:`hasMore=true` 时取本页最后 `createTime` 作为下次 `--time`,重复至 `hasMore=false`
4. `--direction older` 拉给定时间**之前**的消息
5. 合并全部消息后总结 | | query-private-chat | **优先**:`python3 scripts/chat_history_with_user.py --name "<姓名>" --time "" [--no-forward] [--limit N] [--output messages.json]`(自动搜人+翻页+导出)
备选:1. `aisearch person --keyword "<姓名>" --dimension name` → 取 `userId`
2. `chat message list --user --time "" --direction newer` → 取消息列表
3. **翻页**:同 query-group-chat
4. 合并全部消息后总结 | | escalate-ding | 三级升级:
1. `ding message send --robot-code --type app --users --content "<内容>"`(必填项见 `dingtalk-misc` 的 `references/ding.md`)
2. `chat message send --group --text "<内容>"` 群里提醒(可选 `--title` / `@` 见 [chat.md](./chat.md))
3. `todo task create --title "<标题>" --executors --priority 40` 建紧急待办
前置:`aisearch person --keyword "<姓名>" --dimension name` → 取 `userId`;`chat search --query "<群名>"` → 取 `openConversationId` | -| send-by-bot | **多群批量优先**:`python3 scripts/bot_broadcast.py --robot-code --chats , --title "<标题>" --text "<内容>"`
单群:1. `chat bot search` → 取 `robotCode`
2. `chat search --query "<群名>"` → 取 `openConversationId`
3. `chat +messages-send --as bot --robot-code --chat-id --title "<标题>" --text "<内容>"`;需要原子返回时回退 `message send-by-bot` | -| forward-message | 1. `chat search --query "<群名>"` → 取 `openConversationId` → `chat +chat-messages --group --time "<起始时间>"` 拉源消息
2. `aisearch person --keyword "<姓名>" --dimension name` → 取 `openDingTalkId`(推荐);或 `chat search --query "<群名>"` → 取目标 `openConversationId`
3. `chat +messages-send --as user --open-dingtalk-id --text "<内容>"`(推荐)或 `--chat-id ` 发送。仅当无法获取 openDingTalkId 时才用 `--user `(备选) | +| send-by-bot | **多群批量优先**:`python3 scripts/bot_broadcast.py --robot-code --chats , --title "<标题>" --text "<内容>"`
单群:1. `chat bot search` → 取 `robotCode`
2. `chat search --query "<群名>"` → 取 `openConversationId`
3. `chat message send-by-bot --robot-code --group --title "<标题>" --text "<内容>"` | +| forward-message | 1. `chat search --query "<群名>"` → 取 `openConversationId` → `chat message list --group --time "<起始时间>"` 拉源消息
2. `aisearch person --keyword "<姓名>" --dimension name` → 取 `openDingTalkId`(推荐);或 `chat search --query "<群名>"` → 取目标 `openConversationId`
3. `chat message send --open-dingtalk-id --text "<内容>"`(推荐)或 `--group ` 发送。仅当无法获取 openDingTalkId 时才用 `--user `(备选) | | search-common-group | `chat search-common --nicks "<昵称1>,<昵称2>" --limit 20 --cursor 0`(`--match-mode AND`=全在/`OR`=任一在,翻页:`hasMore=true` 时用 `nextCursor`)
用户说"我和XX的共同群" → nicks 包含"我"时,需先 `contact user get-self` 取自己昵称再拼接 | | focus-messages | **零参数一行命令**:`chat message list-focused --limit 50`(拉特别关注人发的消息聚合)
触发 query:`"我特别关注的人最近发了什么消息"`、`"关注的人最近聊了啥"`、`"星标联系人最近的动态"`
**强消歧**:query 含动词【发/说/聊/讲】或名词【消息/聊天/动态】 → **必须**走本命令,**不要**先去拉 `contact relation list-my-followings`;仅当用户终点是"人员列表"(如"我关注了谁")才走 `relation list-my-followings`(详见 `dingtalk-contact` 易混淆硬规则)
翻页:`hasMore=true` 时用 `nextCursor` 作为下次 `--cursor`
按人精控(可选):先 `contact relation list-my-followings` 取 `openDingTalkId`,再 `chat message list-by-sender --sender-open-dingtalk-id --start --end ` | diff --git a/skills/multi/dingtalk-chat/references/chat-error-recovery.md b/skills/multi/dingtalk-chat/references/chat-error-recovery.md index e26cd009..0e5cce2a 100644 --- a/skills/multi/dingtalk-chat/references/chat-error-recovery.md +++ b/skills/multi/dingtalk-chat/references/chat-error-recovery.md @@ -15,10 +15,9 @@ chat 命令由 Agent 执行时统一加 `--format json`。失败时优先读取 | `openConversationId is required` / 群不存在 | 群 ID 错误或缺失 | 执行 `chat search --query "<群名>" --format json`,从返回中取真实 `openConversationId` | | `userId is required` / 收件人无效 | 未先把人名解析为 ID | 执行 `aisearch person` 或 `contact user search`,取 `userId` / `openDingTalkId` | | 群成员命令提示 flag 不存在 | 把消息参数用于成员管理 | 改用 `group members --id `;不要臆造 `members list` | -| 消息搜索结果不符合关键词条件 | 错用了按时间拉取 | 优先改用 `chat +search-msg`;需要原始返回或未覆盖字段时才回退 `message search-advanced` | -| Shortcut leaf Schema 未找到 | 该公开 Shortcut 尚在 Schema 排除列表 | 重新读取不带 `--compact` 的 Shortcut Catalog,定位同一 `cli_path`,再用叶子 `--help` 核对 flags;不要猜命令或参数 | +| 消息搜索结果不符合关键词条件 | 错用了按时间拉取 | 改用 `message search-advanced` | | `confirmation_required` | 危险操作尚未通过运行时确认 | 停止并向用户说明目标与影响;只有用户明确同意后才用原命令加 `--yes` 重试一次 | -| 消息 ID / 卡片 bizId 无效 | 使用了占位符或产品对象 ID | 从源会话消息或 `+messages-send-card` / `message send-card` 的真实返回中重新提取,不自行构造 | +| 消息 ID / 卡片 bizId 无效 | 使用了占位符或产品对象 ID | 从源会话消息或 `message send-card` 的真实返回中重新提取,不自行构造 | | Webhook Token 无效 | token 错误或失效 | 停止并报告用户,要求确认 Webhook Token | | 添加/移除群成员失败 | ID 错误或无权限 | 先确认成员 ID;当前用户无管理权限则停止并报告 | | 机器人无法添加到群 | 当前用户非群管理员或机器人不可用 | 停止并报告用户,不改用普通用户身份代发 | @@ -33,7 +32,7 @@ chat 命令由 Agent 执行时统一加 `--format json`。失败时优先读取 - 配额超限(例如群成员达到上限)。 - 搜索返回多个同名用户或群,目标存在歧义。 - 不可逆操作尚未获得用户明确确认。 -- Shortcut Catalog、Schema 与叶子 Help 冲突;采用更安全解释并报告契约漂移,不尝试绕过。 +- Schema 与叶子 Help 冲突;采用更安全解释并报告契约漂移,不尝试绕过。 报告时包含原始错误、已验证的目标与仍缺少的信息。 @@ -42,6 +41,6 @@ chat 命令由 Agent 执行时统一加 `--format json`。失败时优先读取 1. `chat search --format json` 返回的 `openConversationId` 直接传给下一步群聊参数。 2. `contact user search` / `aisearch person` 返回的 `openDingTalkId`、`userId` 分别传给对应 flag。 3. `chat message list` / `search` 返回的 `openMessageId` 必须与所属 `openConversationId` 同源,再用于回复、转发、收藏、表情或置顶。 -4. `chat +messages-send-card` 未传 `--content`,或原子 `chat message send-card` 创建成功后返回的 `bizId`,只用于后续 `message update-card`。 +4. `chat message send-card` 创建成功后返回的 `bizId` 只用于后续 `message update-card`。 不要自行构造 ID,也不要假设不同命令返回的字段可以互换。 diff --git a/skills/multi/dingtalk-chat/references/chat.md b/skills/multi/dingtalk-chat/references/chat.md index 0b157bc7..70724996 100644 --- a/skills/multi/dingtalk-chat/references/chat.md +++ b/skills/multi/dingtalk-chat/references/chat.md @@ -4,42 +4,20 @@ > 命令别名:`dws im` 等价于 `dws chat`。 -## Shortcut 优先路由 - -精确脚本 / recipe 未覆盖时,常见 Agent 意图优先使用公开 `+` Shortcut;原子命令保留给需要特定原始返回结构、兼容参数或 Shortcut 未覆盖字段的场景。先尝试 `dws shortcut list --service chat --compact --format json` 动态发现;当前 CLI 报 `unknown flag: --compact` 时,立即去掉 `--compact` 重试。选中后读取 `dws schema --cli-path "" --format json`;若 leaf 暂未进入 Schema,则以完整 Catalog 中同一 `cli_path` 的契约为准,最后用 `dws --help` 核对真实 flags。 - -| 意图 | 首选 | -|---|---| -| 以 current-user / bot / webhook 身份发消息 | `dws chat +messages-send --as ...` | -| 拉取单个群聊或单聊的消息 | `dws chat +chat-messages ...` | -| 按关键词、发送者、@对象、会话、类型或时间组合搜索 | `dws chat +search-msg ...` | -| 查询 @ 我的消息 | `dws chat +at-me ...` | -| 根据消息 ID 批量取详情与 reaction | `dws chat +messages-mget ...` | -| 读取已知 thread/topic 的全部回复 | `dws chat +thread-replies ...` | -| 下载单个 mediaId/fileId | `dws chat +messages-resource-download ...` | -| 创建并按需立即更新流式卡片 | `dws chat +messages-send-card ...` | - -- `+messages-send` 只暴露下层真实支持的身份能力:user 支持文本/Markdown、已有 mediaId 图片、本地文件和幂等键;bot 支持群聊或批量单聊文本/Markdown;webhook 的目标由 token 所在群决定。 -- `+messages-send` 自动规范化并补齐对应身份的 @ 占位符。声明 `--at-*` / `--at-all` 即可,不要为统一 Shortcut 手工拼 `@10`。 -- `+search-msg --page-all` 连续翻页并默认按消息 ID 批量富化;续页或富化失败会保留已取得结果并返回逐项失败 ledger。 -- `+at-me`、`+chat-messages`、`+messages-mget`、`+search-msg`、`+thread-replies` 可用 `--download-resources` 下载资源。引用、回复、合并转发中的资源使用结果 `resourceRefs` 自带的子消息 `messageId`;仅当子消息缺会话 ID 时继承父消息 `openConversationId`。 -- 上述五个查询 Shortcut 与 `+messages-resource-download` 都是 `read/not_required`,不要添加 `--yes`。下载只允许工作目录内相对路径、默认不覆盖并原子落盘;需要覆盖时必须由用户显式传 `--overwrite`。 -- 下载器只接受经审查的钉钉与公网 OSS HTTPS 地址并逐跳校验重定向;跨主机时不会转发下层提供的请求头。 - ## 适用范围与安全硬约束 `chat` 覆盖钉钉会话、群聊、群成员、会话消息、机器人消息、Webhook、会话状态和群身份管理。 **发消息前参数审查(必须执行)**: -- 发消息类命令包括 `chat +messages-send`、`+messages-send-card`,以及原子 `message send`、`send-by-bot`、`send-by-webhook`、`send-card`、`reply`、`forward`。 +- 发消息类原子命令包括 `message send`、`send-by-bot`、`send-by-webhook`、`send-card`、`reply`、`forward`。 - 执行前必须逐项核对接收对象、群/人、消息内容、@ 对象、消息类型、附件路径,确认全部来自用户原始需求。 - 用户没说发给谁、群名/人名可能匹配多个对象、消息文本由 Agent 组织、是否 @ 某人不明确时,必须先确认,严禁自行补全。 -- 重试发送时,Shortcut 复用同一个 `--idempotency-key`,原子命令复用同一个 `--uuid`,避免重复投递。 +- 重试 `message send` 时复用同一个 `--uuid`,避免重复投递。 **文件、图片、音频、视频发送硬规则**: -- 新场景发送本地文件、音频、视频优先用 `dws chat +messages-send --as user --msg-type file|audio|video --file <本地路径>`;原子回退用 `message send --file-path`。两条链路都由 CLI 完成上传和发送,`audio` / `video` 底层按 `file` 处理。 +- 发送本地文件、音频、视频使用 `message send --msg-type file|audio|video --file-path <本地路径>`;CLI 完成上传和发送,`audio` / `video` 底层按 `file` 处理。 - 不要先走 `dt_media_upload`、`extract_media_id.py`、`drive upload`;`--msg-type image --media-id` 只保留给已有 mediaId 的旧链路。 **Markdown 换行硬规则**: @@ -84,7 +62,7 @@ dws chat bot --help | 群成员昵称 | `dws chat group members --id --format json` | 返回中包含成员在群里的昵称/展示名;不要只查 contact 个人名 | | 消息 `openMessageId` / `openMsgId` | `dws chat message list ... --format json` | 撤回、已读状态、回复、转发、表情回应 | | 话题 `openConvThreadId` | `dws chat message list ... --format json` | 拉话题回复或往话题内回复;禁止自行拼接 | -| 卡片 `bizId` | `dws chat +messages-send-card ... --format json`(不传 `--content`)或原子 `message send-card` | `message update-card --biz-id` | +| 卡片 `bizId` | `dws chat message send-card ... --format json` | `message update-card --biz-id` | ## 易混淆路由 @@ -92,14 +70,14 @@ dws chat bot --help |------------|----------|------------| | “置顶会话” | `chat set-top` 或 `chat list-top-conversations` | 不要用 `message set-top-msg` | | “置顶某条消息” | `chat message set-top-msg` | 不要用 `chat set-top` | -| “机器人发消息” | 优先 `chat +messages-send --as bot`;原子回退 `chat message send-by-bot` | 不要用当前用户身份代发 | +| “机器人发消息” | `chat message send-by-bot` | 不要用当前用户身份代发 | | “给机器人发单聊” | `chat bot find` 取 openDingTalkId → `message send --open-dingtalk-id` | 不要用 `bot search`,它没有 openDingTalkId | -| “发图片/文件/音频/视频” | 优先 `+messages-send --as user --msg-type file|audio|video --file`;原子回退用 `message send --file-path`;已有图片 mediaId 才用 `image` | 不要先 `drive upload` 或 `dt_media_upload` | +| “发图片/文件/音频/视频” | `message send --msg-type file|audio|video --file-path`;已有图片 mediaId 才用 `image` | 不要先 `drive upload` 或 `dt_media_upload` | | “特别关注的人最近发了什么” | `message list-focused` | 不要先查 `contact relation list-my-followings` | | “某人发给我的消息” | `message list-by-sender` | 不要只查单聊,结果应覆盖群聊+单聊 | -| “搜索消息” | 优先 `chat +search-msg`;需要原始返回或未覆盖字段时回退 `message search-advanced` | 不要在可组合条件下退回简单 `message search` | -| “消息转待办” | 先用 `chat +chat-messages` / `+search-msg` 取消息内容,再调用 `dws todo task create` | 不要在 chat 内寻找“转待办”命令 | -| “消息转日程” | 先用 `chat +chat-messages` / `+search-msg` 取消息内容,再调用 `dws calendar event create` | 不要在 chat 内寻找“转日程”命令 | +| “搜索消息” | `message search-advanced` | 不要在可组合条件下退回简单 `message search` | +| “消息转待办” | 先用 `message list` / `search-advanced` 取消息内容,再调用 `dws todo task create` | 不要在 chat 内寻找“转待办”命令 | +| “消息转日程” | 先用 `message list` / `search-advanced` 取消息内容,再调用 `dws calendar event create` | 不要在 chat 内寻找“转日程”命令 | | “共同群” | `chat search-common --nicks` | 不要分别搜索群再手工求交 | | “拉取群文件 / 群文件列表” | `chat conversation-info --group ` 取 `spaceId` → `dws drive list --space-id ` | 不要用 `chat file upload`,它只负责上传 | | “清空会话聊天记录” | `chat clear-messages` | 不要理解为删除群消息;它只影响当前用户视角 | @@ -109,11 +87,11 @@ dws chat bot --help 执行任何发送类命令前,至少检查: 1. 接收对象明确:群聊必须有 `openConversationId`,单聊必须有 `userId` 或 `openDingTalkId`。 -2. 身份明确:统一 Shortcut 显式传 `--as user/bot/webhook`;原子回退分别使用 `message send`、`send-by-bot`、`send-by-webhook`。 +2. 身份明确:当前用户、应用机器人、Webhook 分别使用 `message send`、`send-by-bot`、`send-by-webhook`。 3. 内容明确:消息正文、标题、附件、卡片内容都能从用户原始需求中找到依据。 4. @ 对象明确:`--at-all`、`--at-user-ids`、`--at-open-dingtalk-ids` 不可自行追加。 -5. 文件路径明确:统一 Shortcut 用 `--file`,原子回退用 `--file-path`;并确认路径是用户提供或当前任务生成的目标文件。 -6. 重试策略明确:Shortcut 复用同一个 `--idempotency-key`,原子命令复用同一个 `--uuid`,避免重复投递。 +5. 文件路径明确:使用 `--file-path`,并确认路径是用户提供或当前任务生成的目标文件。 +6. 重试策略明确:`message send` 复用同一个 `--uuid`,避免重复投递。 ## 高频一跳命令 @@ -125,19 +103,19 @@ dws chat bot --help | 建群 | `dws chat group create --name "群名" --users userId1,userId2 --format json` | | 看群成员 | `dws chat group members --id --format json` | | 看群内昵称 | `dws chat group members --id --format json` | -| 发群文字 | `dws chat +messages-send --as user --chat-id --text "内容" --idempotency-key --format json` | -| 发单聊文字 | `dws chat +messages-send --as user --user --text "内容" --idempotency-key --format json` | -| 发本地文件 | `dws chat +messages-send --as user --chat-id --msg-type file --file ./file.pdf --format json` | -| 发音频/视频 | `dws chat +messages-send --as user --chat-id --msg-type audio --file ./voice.mp3 --format json` / `--msg-type video --file ./demo.mp4` | +| 发群文字 | `dws chat message send --group --text "内容" --uuid --format json` | +| 发单聊文字 | `dws chat message send --user --text "内容" --uuid --format json` | +| 发本地文件 | `dws chat message send --group --msg-type file --file-path ./file.pdf --format json` | +| 发音频/视频 | `dws chat message send --group --msg-type audio --file-path ./voice.mp3 --format json` / `--msg-type video --file-path ./demo.mp4` | | 发位置/名片 | `dws chat message send --group --msg-type location ...` / `--msg-type profile --contact-id ` | -| 拉群消息 | `dws chat +chat-messages --group --time "2026-03-10 00:00:00" --direction older --format json` | -| 搜消息 | `dws chat +search-msg --query "关键词" --start --end --format json` | -| 查 @ 我的消息 | `dws chat +at-me --days 7 --format json` | +| 拉群消息 | `dws chat message list --group --time "2026-03-10 00:00:00" --direction older --format json` | +| 搜消息 | `dws chat message search-advanced --query "关键词" --start --end --format json` | +| 查 @ 我的消息 | `dws chat message list-mentions --start --end --format json` | | 查未读会话 | `dws chat message list-unread-conversations --count 20 --format json` | | 撤回自己消息 | `dws chat message recall --conversation-id --msg-id --format json` | | 查我的机器人 | `dws chat bot search --name "机器人名" --format json` | -| 机器人发群消息 | `dws chat +messages-send --as bot --robot-code --chat-id --title "标题" --markdown "## 标题\n\n正文" --format json` | -| Webhook 告警 | `dws chat +messages-send --as webhook --webhook-token --title "告警" --text "内容" --format json` | +| 机器人发群消息 | `dws chat message send-by-bot --robot-code --group --title "标题" --text "## 标题

正文" --format json` | +| Webhook 告警 | `dws chat message send-by-webhook --token --title "告警" --text "内容" --format json` | | 查单聊会话 ID | `dws chat conversation-info --user --format json` | | 查群钉盘空间 | `dws chat conversation-info --group --format json` | | 发群公告 | `dws chat group notice create --group --content "公告内容" --format json` | @@ -250,26 +228,26 @@ dws chat bot --help | 用户说 | 路由 | |--------|------| -| “发群消息 / 发到某群” | 优先 `chat +messages-send --as user --chat-id`,先用 `chat search` 找 `openConversationId` | -| “发单聊 / 发给某人” | 优先 `chat +messages-send --as user --user/--open-dingtalk-id`,人员信息见 `dingtalk-contact` | -| “发图片 / 发文件 / 发截图 / 发音频 / 发视频” | 优先 `chat +messages-send --as user --msg-type file|audio|video --file <本地路径>`;原子回退用 `message send --file-path` | +| “发群消息 / 发到某群” | `chat message send --group`,先用 `chat search` 找 `openConversationId` | +| “发单聊 / 发给某人” | `chat message send --user/--open-dingtalk-id`,人员信息见 `dingtalk-contact` | +| “发图片 / 发文件 / 发截图 / 发音频 / 发视频” | `chat message send --msg-type file|audio|video --file-path <本地路径>` | | “发位置 / 分享地址” | `chat message send --msg-type location`,需经用户确认经纬度和地址名 | | “发联系人名片 / 分享联系人” | `chat message send --msg-type profile --contact-id ` | -| “机器人发消息 / 机器人群发” | 单次优先 `chat +messages-send --as bot`;多群广播优先专用脚本;原子回退 `chat message send-by-bot` | +| “机器人发消息 / 机器人群发” | 单次用 `chat message send-by-bot`;多群广播优先专用脚本 | | “撤回我发的消息” | `chat message recall` | | “撤回机器人发的消息” | `chat message recall-by-bot` | -| “查某群聊天记录” | 单次优先 `chat +chat-messages --group`;完整导出优先专用脚本 | -| “查和某人的单聊记录” | 单次优先 `chat +chat-messages --user/--open-dingtalk-id`;完整历史优先专用脚本 | +| “查某群聊天记录” | 单次用 `chat message list --group`;完整导出优先专用脚本 | +| “查和某人的单聊记录” | 单次用 `chat message list-direct --user/--open-dingtalk-id`;完整历史优先专用脚本 | | “某人发给我的消息 / 指定发送者消息” | `chat message list-by-sender`,跨单聊和群聊 | | “我今天/最近所有消息” | `chat message list-all --start --end ` | -| “@我的消息” | 优先 `chat +at-me`;组合过滤用 `chat +search-msg --at-me` | +| “@我的消息” | `chat message list-mentions`;组合过滤用 `chat message search-advanced --at-me` | | “特别关注的人最近发了什么” | `chat message list-focused`,不要先查关注人员列表 | -| “搜索消息里的关键词 / 多维度搜索” | 首选 `chat +search-msg`;原始接口字段未覆盖时回退 `chat message search-advanced` | -| “只搜文件消息 / 只搜群聊 / 只看机器人消息” | `chat +search-msg --message-type file --conversation-type group_chat --only-robot` 按需组合 | +| “搜索消息里的关键词 / 多维度搜索” | `chat message search-advanced` | +| “只搜文件消息 / 只搜群聊 / 只看机器人消息” | `chat message search-advanced --message-type file --search-conv-type group_chat --only-robot-messages` 按需组合 | | “翻译这段文字” | `chat text translate --query <文本> --to <语言代码>` | | “把这条消息转待办 / 消息里提到的事项建待办” | 先取消息内容和相关人员,再按 `dingtalk-todo` 调 `dws todo task create`;标题、执行人、截止时间必须来自消息或用户确认 | | “把这条消息转日程 / 消息里约的会建日程” | 先取消息内容、时间、地点、参会人,再按 `dingtalk-calendar` 调 `dws calendar event create`;时间不明确必须先确认 | -| “话题回复 / 往话题里回复” | 已知 thread/topic 时优先 `chat +thread-replies` 读取;发送回复仍用 `message send --group ` | +| “话题回复 / 往话题里回复” | `message list-topic-replies` 读取;发送回复用 `message send --group ` | | “消息已读未读 / 谁看了消息” | `chat message read-status` | | “置顶某条消息 / 取消消息置顶” | `chat message set-top-msg` / `unset-top-msg` | @@ -299,7 +277,7 @@ dws chat bot --help | “查看我的机器人 / 我创建的机器人” | `chat bot search` | | “找机器人 / 搜索全部可用机器人 / 给机器人发单聊” | `chat bot find`,需要 `openDingTalkId` | | “加机器人到群 / 从群移除机器人 / 群里有哪些机器人” | `group members add-bot` / `remove-bot` / `group bots` | -| “Webhook 发告警” | 优先 `chat +messages-send --as webhook`;原子回退 `chat message send-by-webhook` | +| “Webhook 发告警” | `chat message send-by-webhook` | ### 会话状态 @@ -312,7 +290,7 @@ dws chat bot --help | “隐藏会话” | `chat hide` | | “清红点 / 全部已读” | `chat clear-red-point` / `chat clear-all-red-point` | | “标记未读 / 标记已读” | `chat mark-unread` / `chat mark-read` | -| “清空聊天记录” | 优先 `chat +conversation-clear-messages`;确认用户同意后才执行并传 `--yes`;原子回退 `chat clear-messages` | +| “清空聊天记录” | `chat clear-messages`;按 leaf Schema 的确认语义执行 | | “会话分组” | `chat category *` | | “智能分组 / 按关键词或成员自动分组” | `chat category create-smart` | | “群文件 / 拉取群文件列表” | `chat conversation-info --group ` 取 `spaceId`,再按 `dingtalk-drive` 调 `dws drive list --space-id ` | @@ -390,8 +368,7 @@ dws chat bot --help - `chat group quit` 退出群聊,需传 --group(openConversationId) - `chat group update-icon` 更新群头像,需传 --group(openConversationId)和由可信上游提供的有效 --icon-media-id(mediaId);DWS CLI 不能从本地图片生成该 ID - `chat group update-settings` 更新群设置,需传 --group(openConversationId)、--setting-key(设置项 key)、--status(0=关闭 1=开启) -- `chat +messages-send-card` 的 `--group`、`--receiver`、`--receiver-open-dingtalk-id` 三选一;传 `--content` 时创建并立即更新,不传时返回 `bizId` 供后续 `message update-card` 使用;`--flow-status` 范围 1–5,默认 3 -- 原子 `chat message send-card` 创建卡片后仍用 `message update-card --biz-id` 更新内容 +- `chat message send-card` 的 `--group`、`--receiver`、`--receiver-open-dingtalk-id` 三选一;创建后用 `message update-card --biz-id` 更新内容 - `chat message update-card` 流式更新卡片内容,需传 --biz-id(创建卡片返回的业务 ID)、--content、--flow-status - `chat message list-by-ids` 根据消息 ID 批量查询,--msg-ids 逗号分隔,最多 50 条 - `chat message add-emoji` / `remove-emoji` 需传 --group(openConversationId)、--msg-id(openMsgId)、--emoji(表情名称) @@ -423,7 +400,7 @@ dws chat bot --help - [contact](../../dingtalk-contact/references/contact.md) — 搜索人员,获取 `userId` / `openDingTalkId`。 - [todo](../../dingtalk-todo/references/todo.md) — 消息转待办时使用;从 chat 消息内容提取标题、执行人、截止时间后调用 `dws todo task create`。 - [calendar](../../dingtalk-calendar/references/calendar.md) — 消息转日程时使用;从 chat 消息内容提取标题、开始/结束时间、地点、参会人后调用 `dws calendar event create`。 -- [drive](../../dingtalk-drive/references/drive.md) — 云盘/群文件管理;拉群文件先用 `chat conversation-info` 取 `spaceId`,再用 `dws drive list --space-id `;chat 纯文件/音视频发送优先用 `chat +messages-send --as user --msg-type file|audio|video --file`。 +- [drive](../../dingtalk-drive/references/drive.md) — 云盘/群文件管理;拉群文件先用 `chat conversation-info` 取 `spaceId`,再用 `dws drive list --space-id `;chat 纯文件/音视频发送使用 `chat message send --msg-type file|audio|video --file-path`。 - [aisearch](../../dingtalk-aisearch/references/aisearch.md) — 可用于人员、行为、历史信息搜索,但消息发送与会话管理仍走 `chat`。 - [ding](../../dingtalk-misc/references/ding.md) — DING 通知与升级提醒,不等价于群聊消息。 diff --git a/skills/multi/dingtalk-chat/references/chat/chat-bot.md b/skills/multi/dingtalk-chat/references/chat/chat-bot.md index a80f91a8..3500234e 100644 --- a/skills/multi/dingtalk-chat/references/chat/chat-bot.md +++ b/skills/multi/dingtalk-chat/references/chat/chat-bot.md @@ -8,8 +8,8 @@ ## 必读约束 -- 用户明确要求“用机器人/机器人身份/robot”发送时,单次发送优先用 `chat +messages-send --as bot`,原子回退用 `chat message send-by-bot`;严禁改用当前用户身份。 -- 用户明确要求 Webhook 时,优先用 `chat +messages-send --as webhook --webhook-token ...`,原子回退用 `chat message send-by-webhook`。 +- 用户明确要求“用机器人/机器人身份/robot”发送时使用 `chat message send-by-bot`;严禁改用当前用户身份。 +- 用户明确要求 Webhook 时使用 `chat message send-by-webhook`。 - `chat bot search` 只返回我创建的机器人,没有 `openDingTalkId`;给机器人发单聊必须用 `chat bot find`。 - 机器人发群消息前需确认机器人已在群中;报“机器人不存在”时先 `group members add-bot`。 - `send-by-bot --text` 支持 Markdown;需要稳定换行时用空行分隔段落。若以转义形式组织文本,写 `\n\n`,不要只写 `\n`。 @@ -17,19 +17,6 @@ ## 命令明细 -### 统一发送 Shortcut - -```bash -# 机器人群聊或批量单聊 -dws chat +messages-send --as bot --robot-code --chat-id --markdown "## 日报\n\n正文" -dws chat +messages-send --as bot --robot-code --users userId1,userId2 --text "请提交周报" - -# Webhook;目标群由 token 决定 -dws chat +messages-send --as webhook --webhook-token --title "告警" --text "CPU 超 90%" --at-all -``` - -bot/webhook 仅支持文本或 Markdown,不支持 `--file`、`--file-path`、`--media-id`。Shortcut 会按身份规范化并补齐 @ 占位符,声明 `--at-*` / `--at-all` 即可;需要底层原始返回或兼容参数时再用下面的原子命令。 - ### 机器人搜索 | 命令 | 范围 | 返回 openDingTalkId | 典型触发词 | @@ -51,7 +38,11 @@ dws chat bot find --query "日报" --limit 20 --cursor ```bash # 群聊 -dws chat message send-by-bot --robot-code --group --title "日报" --text "## 今日完成\n\n- 事项 A\n\n- 事项 B" +dws chat message send-by-bot --robot-code --group --title "日报" --text "## 今日完成 + +- 事项 A + +- 事项 B" # 单聊 userId dws chat message send-by-bot --robot-code --users userId1,userId2 --title "提醒" --text "请提交周报" @@ -119,7 +110,11 @@ dws chat group members remove-bot --id --bot-id ```bash dws chat bot search --name "日报" --format json -dws chat +messages-send --as bot --robot-code --chat-id --title "日报" --markdown "## 今日完成\n\n- 事项 A\n\n- 事项 B" --format json +dws chat message send-by-bot --robot-code --group --title "日报" --text "## 今日完成 + +- 事项 A + +- 事项 B" --format json dws chat message recall-by-bot --robot-code --group --keys --format json ``` @@ -128,7 +123,7 @@ dws chat message recall-by-bot --robot-code --group --robot-code --format json -dws chat +messages-send --as bot --robot-code --chat-id --title "通知" --text "内容" --format json +dws chat message send-by-bot --robot-code --group --title "通知" --text "内容" --format json ``` ### 给机器人发单聊 @@ -142,7 +137,7 @@ dws chat message send --open-dingtalk-id --text "你好" --form ```bash dws aisearch person --keyword "张三" --dimension name --format json -dws chat +messages-send --as bot --robot-code --chat-id --at-user-ids userId1 --title "提醒" --text "请查收" --format json +dws chat message send-by-bot --robot-code --group --at-user-ids userId1 --title "提醒" --text "@userId1 请查收" --format json ``` ## 常见错误与回退 diff --git a/skills/multi/dingtalk-chat/references/chat/chat-conversation.md b/skills/multi/dingtalk-chat/references/chat/chat-conversation.md index 161b0219..0c6c549f 100644 --- a/skills/multi/dingtalk-chat/references/chat/chat-conversation.md +++ b/skills/multi/dingtalk-chat/references/chat/chat-conversation.md @@ -67,9 +67,7 @@ dws chat hide --conversation-id ```bash dws chat mark-unread --conversation-id dws chat mark-read --conversation-id --message-id -# 用户确认后执行;Shortcut 暂无 leaf Schema 时按 Catalog + Help 核对 -dws chat +conversation-clear-messages --conversation-id --yes -# 原子回退 +# 按 leaf Schema 的确认语义执行 dws chat clear-messages --conversation-id --yes ``` @@ -95,9 +93,7 @@ dws chat category batch-info --category-ids 123,456 dws chat category create --title "工作群" dws chat category create-smart --name "重点群" --keywords "重点,项目" --members openDingTalkId1,openDingTalkId2 dws chat category add-conv --group --category-ids 123,456 -# 用户确认后执行 -dws chat +category-delete --category-id --yes -# 原子回退 +# 按 leaf Schema 的确认语义执行 dws chat category delete --category-id --yes ``` diff --git a/skills/multi/dingtalk-chat/references/chat/chat-group.md b/skills/multi/dingtalk-chat/references/chat/chat-group.md index d9d7ed55..6c16634e 100644 --- a/skills/multi/dingtalk-chat/references/chat/chat-group.md +++ b/skills/multi/dingtalk-chat/references/chat/chat-group.md @@ -128,7 +128,9 @@ dws chat group user-settings set --items '[{"openConversationId":"cid1","top":tr ```bash dws chat group notice create --group --content "今晚 22 点系统维护,请提前保存工作内容" -dws chat group notice create --group --content "# 重要通知\n\n请大家查收" --sticky --send-ding +dws chat group notice create --group --content "# 重要通知 + +请大家查收" --sticky --send-ding dws chat group notice create --group --content "明早九点例会" --run-at "2026-07-03T09:00:00+08:00" dws chat group notice list --group --limit 20 --cursor dws chat group notice get --group --notice-id @@ -195,7 +197,9 @@ dws chat group members add --id --users userId3,userId4 --f ```bash dws chat group share-invite --source --target --format json -dws chat group notice create --group --content "# 项目公告\n\n请大家关注最新安排" --send-ding --format json +dws chat group notice create --group --content "# 项目公告 + +请大家关注最新安排" --send-ding --format json ``` ### 设置管理员并禁言成员 diff --git a/skills/multi/dingtalk-chat/references/chat/chat-message.md b/skills/multi/dingtalk-chat/references/chat/chat-message.md index 3515b671..ab7ac2cc 100644 --- a/skills/multi/dingtalk-chat/references/chat/chat-message.md +++ b/skills/multi/dingtalk-chat/references/chat/chat-message.md @@ -10,28 +10,11 @@ - 发消息前必须核对接收对象、消息内容、@ 对象、附件路径和消息类型;不明确时先问用户。 - `--group`、`--user`、`--open-dingtalk-id` 通常互斥,群聊用 `--group`,单聊用 `--user` 或 `--open-dingtalk-id`。 -- 发送本地文件、音频、视频优先用 `chat +messages-send --as user --msg-type file|audio|video --file `;原子回退用 `chat message send --file-path`。`audio` / `video` 底层按 `file` 链路发送;已有 mediaId 的图片可用 `--msg-type image --media-id`。 +- 发送本地文件、音频、视频使用 `chat message send --msg-type file|audio|video --file-path `。`audio` / `video` 底层按 `file` 链路发送;已有 mediaId 的图片可用 `--msg-type image --media-id`。 - 发送位置消息前必须确认纬度、经度、地址名称;地图缩略图需先通过旧媒体上传链路拿到 mediaId。 - 分享联系人名片前必须确认联系人 `openDingTalkId`,不要把 userId 直接当 `--contact-id`。 - 消息内容按 Markdown 渲染,换行必须是真实换行符;需要换行效果时用空行、行尾两个空格或 `
`。 -- Shortcut 建议带 `--idempotency-key`,原子 `message send` 建议带 `--uuid`;失败重试复用同一个值。 - -## Shortcut 首选 - -先按 [chat.md 的 Shortcut 优先路由](../chat.md#shortcut-优先路由) 动态发现并读取当前契约;下文原子命令用于 Shortcut 未覆盖字段或需要原始返回结构时回退。 - -| 场景 | 首选 Shortcut | 关键边界 | -|---|---|---| -| 普通发送 | `+messages-send` | `--as user/bot/webhook`;仅 user 支持已有 mediaId 图片和本地文件,bot/webhook 只发文本或 Markdown | -| 单会话消息 | `+chat-messages` | `--group` / `--user` / `--open-dingtalk-id` 三选一;`--time` 可省略,`--direction` 为 `newer/older` | -| 组合搜索 | `+search-msg` | 支持关键词、发送者、@、会话、消息类型和时间组合;`--page-all` 自动续页 | -| @ 我的消息 | `+at-me` | 默认最近 7 天,可用 `--days`、`--limit`、`--cursor` | -| 已知消息 ID 批量查询 | `+messages-mget --msg-ids ...` | 同时返回消息详情和 reaction,可用 `--no-reactions` | -| 已知 thread/topic 全部回复 | `+thread-replies` | `--group` 必填,`--thread-id` / `--topic-id` 二选一 | -| 单资源下载 | `+messages-resource-download` | mediaId 需要消息和会话上下文;fileId 不需要 | -| 流式卡片 | `+messages-send-card` | 目标三选一;`--content` 可在创建后立即完成一次更新 | - -`+at-me`、`+chat-messages`、`+messages-mget`、`+search-msg`、`+thread-replies` 都支持 `--download-resources --output-dir ./downloads [--overwrite]`。它们是读操作,不要添加 `--yes`。 +- `message send` 建议带 `--uuid`;失败重试复用同一个值。 ## 命令明细 @@ -107,12 +90,7 @@ dws chat message send --group --msg-type profile --contact- ### 搜索消息 -常见组合搜索优先使用 `+search-msg`;需要 Shortcut 未覆盖的原始字段或底层返回结构时,再用 `message search-advanced`。后者是 `message search` 的严格超集。 - -```bash -dws chat +search-msg --query "周报" --senders --days 3 --page-all --format json -dws chat +search-msg --group --message-type file --download-resources --output-dir ./downloads --format json -``` +组合搜索使用 `message search-advanced`。它是 `message search` 的严格超集。 ```bash dws chat message search-advanced --query "周报" --start "2026-04-01T00:00:00+08:00" --end "2026-04-15T00:00:00+08:00" @@ -179,17 +157,10 @@ dws chat message edit --group --msg-id --co 话题完整读取流程: -1. 已知 thread/topic ID 时优先执行 `dws chat +thread-replies --group --thread-id ` 或 `--topic-id `。 -2. 未知 ID 时先拉主消息;如果返回 `openConvThreadId`,再将其作为 `--topic-id`。需要原始返回结构时回退 `message list-topic-replies`。 +1. 已知 thread/topic ID 时执行 `message list-topic-replies --group --topic-id `。 +2. 未知 ID 时先拉主消息;如果返回 `openConvThreadId`,再将其作为 `--topic-id`。 -Shortcut 可创建卡片并按需立即更新: - -```bash -dws chat +messages-send-card --group --content "任务已完成" --flow-status 3 -dws chat +messages-send-card --receiver -``` - -`--group`、`--receiver`(userId,会先解析联系人)和 `--receiver-open-dingtalk-id` 三选一。传 `--content` 时创建后立即更新;不传时返回 `bizId`,再用原子更新命令继续流式输出: +原子卡片接口先创建,再用返回的 `bizId` 继续流式更新: ```bash dws chat message send-card --group @@ -252,19 +223,7 @@ dws chat text translate --query "Bonjour" --to ja_JP ### 文件与媒体 -读取消息时优先在查询 Shortcut 上加 `--download-resources`;只下载一个已知资源时用: - -```bash -# mediaId:必须同时提供来源消息和会话 -dws chat +messages-resource-download --type mediaId --resource-id --message-id --open-conversation-id --output ./downloads/ - -# fileId:不需要消息上下文 -dws chat +messages-resource-download --type fileId --resource-id --output ./downloads/ -``` - -`--type` 默认 `mediaId`。`--output` 必须是工作目录内相对路径,不能包含 `..`;默认拒绝覆盖,只有用户明确要求时才传 `--overwrite`。该 Shortcut 暂无 leaf Schema 时,从完整 Shortcut Catalog 读取同一 `cli_path`,再用 `--help` 核对 flags。 - -需要原始下载接口时回退 `dws chat message download-media`: +使用原子下载接口 `dws chat message download-media`: ```bash dws chat message download-media --type mediaId --resource-id --message-id --open-conversation-id --output ./downloads/ @@ -304,7 +263,7 @@ dws chat message search-advanced --message-type file --search-conv-type group_ch - 发送目标不唯一:先确认群/人;群用 `chat search`,单聊用 `aisearch person` + `conversation-info`。 - `unknown flag`:立即执行对应命令 `--help`,不要猜参数。 -- 文件/音视频发送失败:确认本地路径可读且在允许的工作目录范围内;优先 `+messages-send --as user --msg-type file|audio|video --file`,原子回退用 `message send --file-path`。 +- 文件/音视频发送失败:确认本地路径可读且在允许的工作目录范围内,并使用 `message send --msg-type file|audio|video --file-path`。 - 位置消息参数不完整:先确认经纬度、地址名称和缩略图 mediaId。 - 名片发送失败:确认 `--contact-id` 是 openDingTalkId,不是 userId。 - 话题回复缺失:检查是否只拉了主消息,需继续用 `list-topic-replies`。 diff --git a/skills/multi/dingtalk-chat/references/chat/chat-workflows.md b/skills/multi/dingtalk-chat/references/chat/chat-workflows.md index 961a06cd..221da4d2 100644 --- a/skills/multi/dingtalk-chat/references/chat/chat-workflows.md +++ b/skills/multi/dingtalk-chat/references/chat/chat-workflows.md @@ -30,7 +30,7 @@ dws chat search --query "项目冲刺" --format json # 2. 拉取群消息 -dws chat +chat-messages --group --time "2025-03-01 00:00:00" --direction older --format json +dws chat message list --group --time "2025-03-01 00:00:00" --direction older --format json # 3. 拉未读会话 dws chat message list-unread-conversations --count 20 --format json @@ -40,21 +40,21 @@ dws chat message list-unread-conversations --count 20 --format json ```bash # 个人身份发群消息 -dws chat +messages-send --as user --chat-id --title "周报提醒" --text "请大家本周五前提交周报" --idempotency-key --format json +dws chat message send --group --title "周报提醒" --text "请大家本周五前提交周报" --uuid --format json # 个人身份发单聊 -dws chat +messages-send --as user --user --text "你好" --idempotency-key --format json -dws chat +messages-send --as user --open-dingtalk-id --text "你好" --idempotency-key --format json +dws chat message send --user --text "你好" --uuid --format json +dws chat message send --open-dingtalk-id --text "你好" --uuid --format json # 发送图片/文件/音频/视频 -dws chat +messages-send --as user --chat-id --msg-type file --file ./screenshot.png --format json -dws chat +messages-send --as user --open-dingtalk-id --msg-type file --file ./report.pdf --format json -dws chat +messages-send --as user --chat-id --msg-type audio --file ./voice.mp3 --format json -dws chat +messages-send --as user --chat-id --msg-type video --file ./demo.mp4 --format json +dws chat message send --group --msg-type file --file-path ./screenshot.png --format json +dws chat message send --open-dingtalk-id --msg-type file --file-path ./report.pdf --format json +dws chat message send --group --msg-type audio --file-path ./voice.mp3 --format json +dws chat message send --group --msg-type video --file-path ./demo.mp4 --format json # 文件后补文字说明 -dws chat +messages-send --as user --open-dingtalk-id --msg-type file --file ./screenshot.png --format json -dws chat +messages-send --as user --open-dingtalk-id --text "这是本周数据汇总" --format json +dws chat message send --open-dingtalk-id --msg-type file --file-path ./screenshot.png --format json +dws chat message send --open-dingtalk-id --text "这是本周数据汇总" --format json ``` ### 机器人消息 @@ -63,19 +63,23 @@ dws chat +messages-send --as user --open-dingtalk-id --text " # 查我的机器人,提取 robotCode dws chat bot search --name "日报" --format json -# 机器人发群消息,提取 processQueryKey;Markdown 稳定换行用 \n\n,不要只用 \n -dws chat +messages-send --as bot --robot-code --chat-id --title "日报" --markdown "## 今日完成\n\n- 事项 A\n\n- 事项 B" --format json +# 机器人发群消息,提取 processQueryKey;命令中使用真实空行,模型内部转义表示才写 \n\n +dws chat message send-by-bot --robot-code --group --title "日报" --text "## 今日完成 + +- 事项 A + +- 事项 B" --format json # 撤回机器人消息 dws chat message recall-by-bot --robot-code --group --keys --format json # 机器人不在群内时先邀请 dws chat group members add-bot --id --robot-code --format json -dws chat +messages-send --as bot --robot-code --chat-id --title "通知" --text "内容" --format json +dws chat message send-by-bot --robot-code --group --title "通知" --text "内容" --format json # 给机器人发单聊,必须用 find 拿 openDingTalkId dws chat bot find --query "玉澜" --format json -dws chat +messages-send --as user --open-dingtalk-id --text "你好" --format json +dws chat message send --open-dingtalk-id --text "你好" --format json ``` ### 机器人 @ 指定人 @@ -83,29 +87,29 @@ dws chat +messages-send --as user --open-dingtalk-id --text " ```bash dws aisearch person --keyword "张三" --dimension name --format json -dws chat +messages-send --as bot --robot-code --chat-id \ +dws chat message send-by-bot --robot-code --group \ --at-user-ids userId1,userId2 \ - --title "提醒" --text "请查收本周报告" --format json + --title "提醒" --text "@userId1 @userId2 请查收本周报告" --format json -dws chat +messages-send --as bot --robot-code --chat-id \ +dws chat message send-by-bot --robot-code --group \ --at-open-dingtalk-ids openDingtalkId1,openDingtalkId2 \ - --title "提醒" --text "请查收本周报告" --format json + --title "提醒" --text "@openDingtalkId1 @openDingtalkId2 请查收本周报告" --format json -dws chat +messages-send --as bot --robot-code --chat-id \ - --at-all --title "通知" --text "请所有人注意" --format json +dws chat message send-by-bot --robot-code --group \ + --at-all --title "通知" --text "@all 请所有人注意" --format json ``` ### Webhook 告警 ```bash -dws chat +messages-send --as webhook --webhook-token --title "告警" --text "CPU 超 90%" --at-all --format json +dws chat message send-by-webhook --token --title "告警" --text "@10 CPU 超 90%" --at-all --format json ``` ### 话题完整读取与回复 ```bash -dws chat +chat-messages --group --time "2026-03-10 00:00:00" --direction older --format json -dws chat +thread-replies --group --topic-id --format json +dws chat message list --group --time "2026-03-10 00:00:00" --direction older --format json +dws chat message list-topic-replies --group --topic-id --format json dws chat message send --group --text "回复话题内容" --format json ``` @@ -116,7 +120,9 @@ dws chat message send --group --text "回复话题内容" --f dws chat group share-invite --source --target --format json # 发布群公告,Markdown 段落换行用空行 -dws chat group notice create --group --content "# 重要通知\n\n请大家查收" --send-ding --format json +dws chat group notice create --group --content "# 重要通知 + +请大家查收" --send-ding --format json # 修改公告前先列表查询 notice-id/dataId dws chat group notice list --group --format json @@ -135,12 +141,12 @@ dws chat text translate --query "你好世界" --to en_US --format json | 操作 | 从返回中提取 | 用于 | |------|-------------|------| -| `chat search` | `openConversationId` | `+messages-send`、`+chat-messages`、`group members`、`set-top`、`group-mute` | +| `chat search` | `openConversationId` | `message send`、`message list`、`group members`、`set-top`、`group-mute` | | `chat group create` | `openConversationId` | 新群后续发消息、成员管理、群设置 | | `chat group get-by-group-id` | `openConversationId` | 将数字群号转为可用会话 ID | | `chat conversation-info` | `openConversationId` | 单聊消息搜索、转发、会话状态操作 | -| `aisearch person` | `userId` | `+messages-send --user/--users`、`--at-user-ids`、`list-by-sender --sender-user-id` | -| `aisearch person` | `openDingTalkId` | `+messages-send --open-dingtalk-id/--open-dingtalk-ids`、`--at-open-dingtalk-ids` | +| `aisearch person` | `userId` | `message send --user`、`send-by-bot --users/--at-user-ids`、`list-by-sender --sender-user-id` | +| `aisearch person` | `openDingTalkId` | `message send --open-dingtalk-id`、`send-by-bot --open-dingtalk-ids/--at-open-dingtalk-ids` | | `aisearch person` | `userId` | 可替代人员搜索结果,继续用于 userId 参数 | | `chat bot search` | `robotCode` | `send-by-bot`、`recall-by-bot` | | `chat bot find` | `openDingTalkId` | 给机器人发单聊 | @@ -160,7 +166,7 @@ dws chat text translate --query "你好世界" --to en_US --format json | `chat message list/search-advanced` | `openMessageId` / `openMsgId` | `list-emotion-replies --msg-ids` | | `chat category list` | `categoryId` | `category list-conversations/add-conv/remove-conv --category-id(s)` | | `chat category create-smart` | 智能分组 ID/规则结果 | 后续查看或调整智能分组 | -| `chat +messages-send-card`(无 `--content`)/ `chat message send-card` | `bizId` | `message update-card --biz-id` | +| `chat message send-card` | `bizId` | `message update-card --biz-id` | ## 自动化脚本 @@ -175,4 +181,4 @@ dws chat text translate --query "你好世界" --to en_US --format json - `chat bot search` 没有 openDingTalkId:改用 `chat bot find`。 - 群号是纯数字:先 `chat group get-by-group-id`。 - 翻页不要自造 cursor:使用响应中的 `nextCursor` 原值。 -- 文件/音视频发送不要走旧上传链路:优先 `+messages-send --as user --msg-type file|audio|video --file`;原子回退用 `message send --file-path`。 +- 文件/音视频发送不要走旧上传链路;使用 `message send --msg-type file|audio|video --file-path`。 diff --git a/test/scripts/chat_skill_scripts_test.py b/test/scripts/chat_skill_scripts_test.py index fe90eea4..98db0cfd 100644 --- a/test/scripts/chat_skill_scripts_test.py +++ b/test/scripts/chat_skill_scripts_test.py @@ -4,6 +4,7 @@ import contextlib import importlib.util import io +import json import re import types import unittest @@ -32,27 +33,82 @@ def load_script(name: str): class ChatSkillScriptPathTest(unittest.TestCase): - def test_shortcuts_use_progressive_discovery_without_static_catalog(self): + def test_shortcuts_are_self_contained_in_multi_skill(self): text = (MULTI_ROOT / 'SKILL.md').read_text(encoding='utf-8') block = text.split('', 1)[1].split( '', 1 )[0] - self.assertNotRegex(block, r'\|\s*`dws chat \+') + catalog = json.loads( + (ROOT / 'docs' / 'shortcut-public-catalog.json').read_text( + encoding='utf-8' + ) + ) + chat_rows = [ + row for row in catalog['results'] if row.get('service') == 'chat' + ] + table_rows = re.findall( + r'\|\s*`(\+[a-z0-9-]+)`\s*\|\s*([^|\n]+)\s*\|', + block, + ) + self.assertEqual( + {row['command'] for row in chat_rows}, + {command for command, _ in table_rows}, + ) + self.assertTrue(all(description.strip() for _, description in table_rows)) + self.assertTrue( + all( + re.search(r'[\u4e00-\u9fff]', description) + for _, description in table_rows + ), + 'every shortcut description must retain a Chinese intent cue', + ) + self.assertIn('| Shortcut | 适用场景 |', block) + self.assertNotIn('| Shortcut | 风险 |', block) + self.assertNotIn('dws chat +', block) for required in ( - '精确脚本 / recipe > 匹配的公开 shortcut > reference 中的原子命令', - 'dws shortcut list --service chat --compact --format json', - '不接受 `--compact` 时去掉该 flag 重试', - '不得猜测 `+` 命令名', - '若该 leaf 暂未进入 Schema', - 'dws --help', - 'confirmation=user_required', + '+messages-send', + '+chat-messages', + '+search-msg', + '+conversation-clear-messages', ): self.assertIn(required, block) - - def test_chat_references_track_remote_shortcut_contract(self): - chat_ref = (MULTI_ROOT / 'references' / 'chat.md').read_text( - encoding='utf-8' + high_risk = { + row['command'] + for row in chat_rows + if row.get('risk') == 'high-risk-write' + } + high_risk_line = next( + line for line in block.splitlines() + if line.startswith('`risk=high-risk-write`') ) + self.assertEqual( + high_risk, + set(re.findall(r'`(\+[a-z0-9-]+)`', high_risk_line)), + ) + for required in ( + 'This is the only Shortcut entry point for multi/chat', + 'exact script/recipe > matching public Shortcut > atomic command', + 'dws shortcut list --service chat --format json', + 'never guess names', + 'confirmation=user_required', + '--idempotency-key', + '--download-resources', + '--receiver-open-dingtalk-id', + ): + self.assertIn(required, text) + + def test_chat_references_are_atomic_only(self): + references = list((MULTI_ROOT / 'references').rglob('*.md')) + self.assertTrue(references) + for path in references: + text = path.read_text(encoding='utf-8') + with self.subTest(path=path.relative_to(MULTI_ROOT)): + self.assertNotRegex(text, r'(?i)\bshortcut\b') + self.assertNotRegex(text, r'\+[a-z][a-z0-9-]+') + + chat_ref = ( + MULTI_ROOT / 'references' / 'chat.md' + ).read_text(encoding='utf-8') message_ref = ( MULTI_ROOT / 'references' / 'chat' / 'chat-message.md' ).read_text(encoding='utf-8') @@ -61,28 +117,42 @@ class ChatSkillScriptPathTest(unittest.TestCase): ).read_text(encoding='utf-8') for required in ( - 'unknown flag: --compact', - '若 leaf 暂未进入 Schema', - '+messages-send --as ', - '+messages-resource-download', + 'message send --group', + 'message search-advanced', + 'message send-by-bot', + 'message send-by-webhook', ): self.assertIn(required, chat_ref) for required in ( - '--receiver-open-dingtalk-id', - '--content', + 'message send-card', + 'message update-card', '--flow-status', - '--download-resources', - '--overwrite', - 'fileId:不需要消息上下文', + 'message download-media', + '--file-path', ): self.assertIn(required, message_ref) for required in ( - '+conversation-clear-messages', - '+category-delete', - '确认后才传 `--yes`', + 'clear-messages', + 'category delete', + '按 leaf Schema 的确认语义执行', ): self.assertIn(required, conversation_ref) + def test_documented_chat_commands_do_not_pass_literal_newline_escapes(self): + docs = [MULTI_ROOT / 'SKILL.md'] + docs.extend((MULTI_ROOT / 'references').rglob('*.md')) + offenders = [] + for path in docs: + for line_number, line in enumerate( + path.read_text(encoding='utf-8').splitlines(), + start=1, + ): + if 'dws chat ' in line and r'\n' in line: + offenders.append( + f'{path.relative_to(ROOT)}:{line_number}: {line}' + ) + self.assertEqual([], offenders) + def test_markdown_python_links_resolve(self): link_re = re.compile(r'\[[^\]]+\.py\]\(([^)]+\.py)\)') docs = list(MULTI_ROOT.rglob('*.md'))