{"_id":"@dongkangxin/dingtalk-plugin","_rev":"2-dd765bdb2420071d4cb34b9a873e9104","name":"@dongkangxin/dingtalk-plugin","dist-tags":{"latest":"3.1.5"},"versions":{"3.1.4":{"name":"@dongkangxin/dingtalk-plugin","version":"3.1.4","keywords":["bot","channel","clawdbot","dingtalk","openclaw","stream","钉钉"],"author":{"url":"http://github.com/soimy","name":"YM Shen","email":"soimy@163.com"},"license":"MIT","_id":"@dongkangxin/dingtalk-plugin@3.1.4","maintainers":[{"name":"dongkangxin","email":"dongkangxin@outlook.com"}],"homepage":"http://gitlab.alibaba-inc.com/trip/openclaw-channel-dingtalk","dist":{"shasum":"358b22883613d5ef452969c63d93b3728af425a7","tarball":"https://registry.npmjs.org/@dongkangxin/dingtalk-plugin/-/dingtalk-plugin-3.1.4.tgz","fileCount":26,"integrity":"sha512-YMGA2l8AhpqTIK688xgZWT/YYEDio+yL+2zkKxbyCPeYvtBwNFRLijRnE8EqcHGIYAbOOFb/VsQta9CDJ0GeNg==","signatures":[{"sig":"MEUCIFnEs0GCYnpeY45lm246NGZ5KhpSFrFxXTVb6Kp2sLkyAiEAkGMNF+3VIdEWkrtJV9vdNUW7Qr7gJ10dqekC+pwzJSo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":155867},"main":"index.ts","type":"module","gitHead":"4ec22c1c73fe7fdaf54d596d4c82b6d60749a2e2","scripts":{"lint":"oxlint --type-aware index.ts src","test":"vitest run","format":"oxfmt --write package.json tsconfig.json index.ts src/*.ts","lint:fix":"oxlint --type-aware --fix index.ts src && pnpm format","type-check":"tsc -p tsconfig.json","format:check":"oxfmt --check package.json tsconfig.json index.ts src/*.ts","test:coverage":"vitest run --coverage","prepublishOnly":"npm run type-check && npm run lint","publish:public":"npm publish --registry=https://registry.npmjs.org --access public","publish:alibaba":"npm publish --registry=http://registry.npm.alibaba-inc.com"},"_npmUser":{"name":"dongkangxin","email":"dongkangxin@outlook.com"},"openclaw":{"channel":{"id":"dingtalk","blurb":"钉钉企业内部机器人，使用 Stream 模式，无需公网 IP。","label":"DingTalk","order":70,"aliases":["dd","ding"],"docsPath":"/channels/dingtalk","docsLabel":"dingtalk","selectionLabel":"DingTalk (钉钉)"},"install":{"npmSpec":"@fengri/dingtalk","localPath":".","defaultChoice":"npm"},"channels":["dingtalk"],"extensions":["./index.ts"],"installDependencies":true},"repository":{"url":"git+http://gitlab.alibaba-inc.com/trip/openclaw-channel-dingtalk.git","type":"git"},"_npmVersion":"10.9.4","description":"DingTalk (钉钉) channel plugin for OpenClaw","directories":{},"_nodeVersion":"22.21.1","dependencies":{"ws":"^8.19.0","zod":"^4.3.6","axios":"^1.6.0","@types/ws":"^8.18.1","form-data":"^4.0.0","dingtalk-stream":"^2.1.4"},"publishConfig":{"access":"public","registry":"ttps://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"oxfmt":"0.34.0","oxlint":"^1.49.0","vitest":"^3.2.4","typescript":"^5.3.0","@types/node":"^25.2.0","oxlint-tsgolint":"^0.15.0","@vitest/coverage-v8":"^3.2.4"},"peerDependencies":{"openclaw":">=2026.2.13"},"_npmOperationalInternal":{"tmp":"tmp/dingtalk-plugin_3.1.4_1772793108849_0.8277886351570158","host":"s3://npm-registry-packages-npm-production"}},"3.1.5":{"name":"@dongkangxin/dingtalk-plugin","version":"3.1.5","description":"DingTalk (钉钉) channel plugin for OpenClaw","keywords":["bot","channel","clawdbot","dingtalk","openclaw","stream","钉钉"],"homepage":"http://gitlab.alibaba-inc.com/trip/openclaw-channel-dingtalk","license":"MIT","author":{"name":"YM Shen","email":"soimy@163.com","url":"http://github.com/soimy"},"repository":{"type":"git","url":"git+http://gitlab.alibaba-inc.com/trip/openclaw-channel-dingtalk.git"},"type":"module","main":"index.ts","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"scripts":{"format":"oxfmt --write package.json tsconfig.json index.ts src/*.ts","format:check":"oxfmt --check package.json tsconfig.json index.ts src/*.ts","lint":"oxlint --type-aware index.ts src","lint:fix":"oxlint --type-aware --fix index.ts src && pnpm format","test":"vitest run","test:coverage":"vitest run --coverage","type-check":"tsc -p tsconfig.json","prepublishOnly":"npm run type-check && npm run lint","publish":"npm publish --access public --ignore-scripts    "},"dependencies":{"@types/ws":"^8.18.1","axios":"^1.6.0","dingtalk-stream":"^2.1.4","form-data":"^4.0.0","ws":"^8.19.0","zod":"^4.3.6"},"devDependencies":{"@types/node":"^25.2.0","@vitest/coverage-v8":"^3.2.4","oxfmt":"0.34.0","oxlint":"^1.49.0","oxlint-tsgolint":"^0.16.0","typescript":"^5.3.0","vitest":"^3.2.4"},"peerDependencies":{"openclaw":">=2026.2.13"},"openclaw":{"extensions":["./index.ts"],"channels":["dingtalk"],"installDependencies":true,"channel":{"id":"dingtalk","label":"DingTalk","selectionLabel":"DingTalk (钉钉)","docsPath":"/channels/dingtalk","docsLabel":"dingtalk","blurb":"钉钉企业内部机器人，使用 Stream 模式，无需公网 IP。","order":70,"aliases":["dd","ding"]},"install":{"npmSpec":"@fengri/dingtalk","localPath":".","defaultChoice":"npm"}},"_id":"@dongkangxin/dingtalk-plugin@3.1.5","gitHead":"c7f98b0fe75bb01e785f3a06e5cfa9310655646f","_nodeVersion":"22.21.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-8XtdIxfq7kmY1aEp4klygb89HIfoyLi+/uNr1imoScQote+4xWpI3FVlASZ7qXr7mMtDCFI/FDVDDWX1m3I/Lw==","shasum":"486138e0ba17905fcfe78a9f7c21a15110e2557b","tarball":"https://registry.npmjs.org/@dongkangxin/dingtalk-plugin/-/dingtalk-plugin-3.1.5.tgz","fileCount":26,"unpackedSize":153840,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC0sWNeDAGNo5l8otYptPrxETKPa7y5IF1xozw/wqQSygIhAIB5j4VHgVyKWfM4oQUlGQ0F5JYLjYMWBlchnetM1JcW"}]},"_npmUser":{"name":"dongkangxin","email":"dongkangxin@outlook.com"},"directories":{},"maintainers":[{"name":"dongkangxin","email":"dongkangxin@outlook.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dingtalk-plugin_3.1.5_1773023152706_0.970307064124224"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-06T10:31:48.718Z","modified":"2026-03-09T02:25:53.016Z","3.1.4":"2026-03-06T10:31:49.011Z","3.1.5":"2026-03-09T02:25:52.875Z"},"author":{"name":"YM Shen","email":"soimy@163.com","url":"http://github.com/soimy"},"license":"MIT","homepage":"http://gitlab.alibaba-inc.com/trip/openclaw-channel-dingtalk","keywords":["bot","channel","clawdbot","dingtalk","openclaw","stream","钉钉"],"repository":{"type":"git","url":"git+http://gitlab.alibaba-inc.com/trip/openclaw-channel-dingtalk.git"},"description":"DingTalk (钉钉) channel plugin for OpenClaw","maintainers":[{"name":"dongkangxin","email":"dongkangxin@outlook.com"}],"readme":"# DingTalk Channel for OpenClaw\n\n钉钉企业内部机器人 Channel 插件，使用 Stream 模式（无需公网 IP）。\n\n## 功能特性\n\n- ✅ **Stream 模式** — WebSocket 长连接，无需公网 IP 或 Webhook\n- ✅ **私聊支持** — 直接与机器人对话\n- ✅ **群聊支持** — 在群里 @机器人\n- ✅ **多种消息类型** — 文本、图片、语音（自带识别）、视频、文件\n- ✅ **Markdown 回复** — 支持富文本格式回复\n- ✅ **互动卡片** — 支持流式更新，适用于 AI 实时输出\n- ✅ **完整 AI 对话** — 接入 Clawdbot 消息处理管道\n\n## 安装\n\n### 方法 A：通过 Git URL 安装（推荐）\n\n直接从 GitLab 仓库安装：\n\n```bash\nopenclaw plugins install git+http://gitlab.alibaba-inc.com/trip/openclaw-channel-dingtalk.git\n```\n\n### 方法 B：通过 npm 包安装\n\n如果已发布到 npm registry：\n\n```bash\nopenclaw plugins install @fengri/dingtalk\n```\n\n### 方法 C：通过本地源码安装\n\n如果你想对插件进行二次开发，可以先克隆仓库：\n\n```bash\n# 1. 克隆仓库\ngit clone http://gitlab.alibaba-inc.com/trip/openclaw-channel-dingtalk.git\ncd openclaw-channel-dingtalk\n\n# 2. 安装依赖 (必需)\nnpm install\n\n# 3. 以链接模式安装 (方便修改代码后实时生效)\nopenclaw plugins install -l .\n```\n\n### 方法 D：手动安装\n\n1. 将本目录下载或复制到 `~/.openclaw/extensions/dingtalk`。\n2. 确保包含 `index.ts`, `openclaw.plugin.json` 和 `package.json`。\n3. 运行 `openclaw plugins list` 确认 `dingtalk` 已显示在列表中。\n\n### 安装后必做：配置插件信任白名单（`plugins.allow`）\n\n从 OpenClaw 新版本开始，如果发现了非内置插件且 `plugins.allow` 为空，会提示：\n\n```text\n[plugins] plugins.allow is empty; discovered non-bundled plugins may auto-load ...\n```\n\n这是一条安全告警（不是安装失败），建议显式写入你信任的插件 id。\n\n#### 步骤 1：确认插件 id\n\n本插件 id 固定为：`dingtalk`（定义于 `openclaw.plugin.json`）。\n\n也可用下面命令查看已发现插件：\n\n```bash\nopenclaw plugins list\n```\n\n#### 步骤 2：在 `~/.openclaw/openclaw.json` 添加 `plugins.allow`\n\n```json5\n{\n  \"plugins\": {\n    \"enabled\": true,\n    \"allow\": [\"dingtalk\"]\n  }\n}\n```\n\n如果你还有其他已安装且需要启用的插件，请一并加入，例如：\n\n```json5\n{\n  \"plugins\": {\n    \"allow\": [\"dingtalk\", \"telegram\", \"voice-call\"]\n  }\n}\n```\n\n#### 步骤 3：重启 Gateway\n\n```bash\nopenclaw gateway restart\n```\n\n> 注意：如果你之前已经配置过 `plugins.allow`，但没有 `dingtalk`，那么插件不会被加载。请把 `dingtalk` 加入该列表。\n\n## 更新\n\n`openclaw plugins update` 使用插件 id（不是 npm 包名），并且仅适用于 npm 安装来源。\n\n如果你是通过 npm 安装本插件：\n\n```bash\nopenclaw plugins update dingtalk\n```\n\n如果你是本地源码/链接安装（`openclaw plugins install -l .`），请在插件目录更新代码后重启 Gateway：\n\n```bash\ngit pull\nopenclaw gateway restart\n```\n\n## 配置\n\nOpenClaw 支持**交互式配置**和**手动配置文件**两种方式。\n\n### 方法 1：交互式配置（推荐）\n\n使用 OpenClaw 命令行向导式配置插件参数：\n\n```bash\n# 方式 A：使用 onboard 命令\nopenclaw onboard\n\n# 方式 B：直接配置 channels 部分\nopenclaw configure --section channels\n```\n\n交互式配置流程：\n\n1. **选择插件** — 在插件列表中选择 `dingtalk` 或 `DingTalk (钉钉)`\n2. **Client ID** — 输入钉钉应用的 AppKey\n3. **Client Secret** — 输入钉钉应用的 AppSecret\n4. **完整配置** — 可选配置 Robot Code、Corp ID、Agent ID（推荐）\n5. **卡片模式** — 可选启用 AI 互动卡片模式\n   - 如启用，需输入 Card Template ID 和 Card Template Key\n6. **私聊策略** — 选择 `open`（开放）或 `allowlist`（白名单）\n7. **群聊策略** — 选择 `open`（开放）或 `allowlist`（白名单）\n\n> 所有的参数参考下文中的钉钉开发者平台配置指南\n\n配置完成后会自动保存并重启 Gateway。\n\n---\n\n#### 钉钉开发者平台配置指南\n\n##### 1. 创建钉钉应用\n\n1. 访问 [钉钉开发者后台](https://open-dev.dingtalk.com/)\n2. 创建企业内部应用\n3. 添加「机器人」能力\n4. 配置消息接收模式为 **Stream 模式**\n5. 发布应用\n\n##### 2. 配置权限管理\n\n在应用的权限管理页面，需要开启以下权限：\n\n- ✅ **Card.Instance.Write** — 创建和投放卡片实例\n- ✅ **Card.Streaming.Write** — 对卡片进行流式更新\n\n**步骤：**\n\n1. 进入应用 → 权限管理\n2. 搜索「Card」相关权限\n3. 勾选上述两个权限\n4. 保存权限配置\n\n##### 3. 建立卡片模板(可选)\n\n**步骤：**\n\n1. 访问 [钉钉卡片平台](https://open-dev.dingtalk.com/fe/card)\n2. 进入「我的模板」\n3. 点击「创建模板」\n4. 卡片模板场景选择 **「AI 卡片」**\n5. 按需设计卡片排版,点击保存并发布\n6. 记下模板中定义的内容字段名称\n7. 复制模板 ID（格式如：`xxxxx-xxxxx-xxxxx.schema`）\n8. 将 templateId 配置到 `openclaw.json` 的 `cardTemplateId` 字段\n9. 或在OpenClaw控制台的Channel标签->Dingtalk配置面板-> Card Template Id填入\n10. 将记下的内容字段变量名配置到 `openclaw.json` 的 `cardTemplateKey` 字段\n11. 或在OpenClaw控制台的Channel标签->Dingtalk配置面板-> Card Template Key填入\n\n**说明：**\n\n- 使用 DingTalk 官方 AI 卡片模板时，`cardTemplateKey` 默认为 `'msgContent'`，无需修改\n- 如果您创建自定义卡片模板，需要确保模板中包含相应的内容字段，并将 `cardTemplateKey` 配置为该字段名称\n\n##### 4. 获取凭证\n\n从开发者后台获取：\n\n- **Client ID** (AppKey)\n- **Client Secret** (AppSecret)\n- **Robot Code** (与 Client ID 相同)\n- **Corp ID** (企业 ID)\n- **Agent ID** (应用 ID)\n\n### 方法 2：手动配置文件\n\n在 `~/.openclaw/openclaw.json` 中添加（仅作参考，交互式配置会自动生成）：\n\n> 至少包含 `plugins.allow` 和 `channels.dingtalk` 两部分，内容参考上文钉钉开发者配置指南\n\n```json5\n{\n  \"plugins\": {\n    \"enabled\": true,\n    \"allow\": [\"dingtalk\"]\n  },\n\n  ...\n  \"channels\": {\n    \"telegram\": { ... },\n\n    \"dingtalk\": {\n      \"enabled\": true,\n      \"clientId\": \"dingxxxxxx\",\n      \"clientSecret\": \"your-app-secret\",\n      \"robotCode\": \"dingxxxxxx\",\n      \"corpId\": \"dingxxxxxx\",\n      \"agentId\": \"123456789\",\n      \"dmPolicy\": \"open\",\n      \"groupPolicy\": \"open\",\n      \"debug\": false,\n      \"messageType\": \"markdown\", // 或 \"card\"\n      // 仅card需要配置\n      \"cardTemplateId\": \"你复制的模板ID\",\n      \"cardTemplateKey\": \"你模板的内容变量\"\n    }\n  },\n  ...\n}\n```\n\n最后重启 Gateway\n\n> 使用交互式配置时，Gateway 会自动重启。使用手动配置时需要手动执行：\n\n```bash\nopenclaw gateway restart\n```\n\n## 配置选项\n\n| 选项                    | 类型     | 默认值       | 说明                                        |\n| ----------------------- | -------- | ------------ | ------------------------------------------- |\n| `enabled`               | boolean  | `true`       | 是否启用                                    |\n| `clientId`              | string   | 必填         | 应用的 AppKey                               |\n| `clientSecret`          | string   | 必填         | 应用的 AppSecret                            |\n| `robotCode`             | string   | -            | 机器人代码（用于下载媒体和发送卡片）        |\n| `corpId`                | string   | -            | 企业 ID                                     |\n| `agentId`               | string   | -            | 应用 ID                                     |\n| `dmPolicy`              | string   | `\"open\"`     | 私聊策略：open/pairing/allowlist            |\n| `groupPolicy`           | string   | `\"open\"`     | 群聊策略：open/allowlist                    |\n| `allowFrom`             | string[] | `[]`         | 允许的发送者 ID 列表                        |\n| `messageType`           | string   | `\"markdown\"` | 消息类型：markdown/card                     |\n| `cardTemplateId`        | string   |              | AI 互动卡片模板 ID（仅当 messageType=card） |\n| `cardTemplateKey`       | string   | `\"content\"`  | 卡片模板内容字段键（仅当 messageType=card） |\n| `debug`                 | boolean  | `false`      | 是否开启调试日志                            |\n| `maxConnectionAttempts` | number   | `10`         | 最大连接尝试次数                            |\n| `initialReconnectDelay` | number   | `1000`       | 初始重连延迟（毫秒）                        |\n| `maxReconnectDelay`     | number   | `60000`      | 最大重连延迟（毫秒）                        |\n| `reconnectJitter`       | number   | `0.3`        | 重连延迟抖动因子（0-1）                     |\n\n### 连接鲁棒性配置\n\n为提高连接稳定性，插件支持以下高级配置：\n\n- **maxConnectionAttempts**: 连接失败后的最大重试次数，超过后将停止尝试并报警。\n- **initialReconnectDelay**: 第一次重连的初始延迟（毫秒），后续重连会按指数增长。\n- **maxReconnectDelay**: 重连延迟的上限（毫秒），防止等待时间过长。\n- **reconnectJitter**: 延迟抖动因子，在延迟基础上增加随机变化（±30%），避免多个客户端同时重连。\n\n重连延迟计算公式：`delay = min(initialDelay × 2^attempt, maxDelay) × (1 ± jitter)`\n\n示例延迟序列（默认配置）：~1s, ~2s, ~4s, ~8s, ~16s, ~32s, ~60s（达到上限）\n\n更多详情请参阅 [CONNECTION_ROBUSTNESS.md](./CONNECTION_ROBUSTNESS.md)。\n\n## 安全策略\n\n### 私聊策略 (dmPolicy)\n\n- `open` — 任何人都可以私聊机器人\n- `pairing` — 新用户需要通过配对码验证\n- `allowlist` — 只有 allowFrom 列表中的用户可以使用\n\n### 群聊策略 (groupPolicy)\n\n- `open` — 任何群都可以 @机器人\n- `allowlist` — 只有配置的群可以使用\n\n## 消息类型支持\n\n### 接收\n\n| 类型   | 支持 | 说明                 |\n| ------ | ---- | -------------------- |\n| 文本   | ✅   | 完整支持             |\n| 富文本 | ✅   | 提取文本内容         |\n| 图片   | ✅   | 下载并传递给 AI      |\n| 语音   | ✅   | 使用钉钉语音识别结果 |\n| 视频   | ✅   | 下载并传递给 AI      |\n| 文件   | ✅   | 下载并传递给 AI      |\n\n### 发送\n\n| 类型     | 支持 | 说明                             |\n| -------- | ---- | -------------------------------- |\n| 文本     | ✅   | 完整支持                         |\n| Markdown | ✅   | 自动检测或手动指定               |\n| 互动卡片 | ✅   | 支持流式更新，适用于 AI 实时输出 |\n| 图片     | ⏳   | 需要通过媒体上传 API             |\n\n## API 消耗说明\n\n### Text/Markdown 模式\n\n| 操作       | API 调用次数 | 说明                                                                         |\n| ---------- | ------------ | ---------------------------------------------------------------------------- |\n| 获取 Token | 1            | 共享/缓存（60 秒检查过期一次）                                               |\n| 发送消息   | 1            | 使用 `/v1.0/robot/oToMessages/batchSend` 或 `/v1.0/robot/groupMessages/send` |\n| **总计**   | **2**        | 每条回复 1 次                                                                |\n\n### Card（AI 互动卡片）模式\n\n| 阶段         | API 调用               | 说明                                                |\n| ------------ | ---------------------- | --------------------------------------------------- |\n| **创建卡片** | 1                      | `POST /v1.0/card/instances/createAndDeliver`        |\n| **流式更新** | M                      | M = 回复块数量，每块一次 `PUT /v1.0/card/streaming` |\n| **完成卡片** | 包含在最后一次流更新中 | 使用 `isFinalize=true` 标记                         |\n| **总计**     | **1 + M**              | M = Agent 产生的回复块数                            |\n\n### 典型场景成本对比\n\n| 场景             | Text/Markdown | Card | 节省   |\n| ---------------- | ------------- | ---- | ------ |\n| 简短回复（1 块） | 2             | 2    | ✓ 相同 |\n| 中等回复（5 块） | 6             | 6    | ✓ 相同 |\n| 长回复（10 块）  | 12            | 11   | ✓ 1 次 |\n\n### 优化策略\n\n**降低 API 调用的方法：**\n\n1. **合并回复块** — 通过调整 Agent 输出配置，减少块数量\n2. **使用缓存** — Token 自动缓存（60 秒），无需每次都获取\n3. **Buffer 模式** — 使用 `dispatchReplyWithBufferedBlockDispatcher` 合并多个小块\n\n**成本建议：**\n\n- ✅ **推荐** — Card 模式：流式体验更好，成本与 Text/Markdown 相当或更低\n- ⚠️ **谨慎** — 频繁调用需要监测配额，建议使用钉钉开发者后台查看 API 调用量\n\n## 消息类型选择\n\n插件支持两种消息回复类型，可通过 `messageType` 配置：\n\n### 1. markdown（Markdown 格式）**【默认】**\n\n- 支持富文本格式（标题、粗体、列表等）\n- 自动检测消息是否包含 Markdown 语法\n- 适用于大多数场景\n\n### 2. card（AI 互动卡片）\n\n- 支持流式更新（实时显示 AI 生成内容）\n- 更好的视觉呈现和交互体验\n- 支持 Markdown 格式渲染\n- 通过 `cardTemplateId` 指定模板\n- 通过 `cardTemplateKey` 指定内容字段\n- **适用于 AI 对话场景**\n- 支持在卡片中实时显示 AI 思考过程（推理流）和工具执行结果\n\n**AI Card API 特性：**\n当配置 `messageType: 'card'` 时：\n\n1. 使用 `/v1.0/card/instances/createAndDeliver` 创建并投放卡片\n2. 使用 `/v1.0/card/streaming` 实现真正的流式更新\n3. 自动状态管理（PROCESSING → INPUTING → FINISHED）\n4. 更稳定的流式体验，无需手动节流\n\n### AI 思考过程与工具执行显示（AI Card 模式）\n\n当 `messageType` 为 `card` 时，插件可以在卡片中实时展示 AI 的推理过程（🤔 思考中）和工具调用结果（🛠️ 工具执行）。这两项功能通过**对话级命令**控制，无需修改配置文件：\n\n| 功能              | 对话命令              | 说明                               |\n| ----------------- | --------------------- | ---------------------------------- |\n| 显示 AI 推理流    | `/reasoning stream`   | 开启后，AI 思考内容实时更新到卡片  |\n| 显示工具执行结果  | `/verbose on`         | 开启后，工具调用结果实时更新到卡片 |\n| 关闭 AI 推理流    | `/reasoning off`      | 关闭推理流显示                     |\n| 关闭工具执行显示  | `/verbose off`        | 关闭工具执行结果显示               |\n\n**显示格式：**\n\n- 思考内容以 `🤔 **思考中**` 为标题，正文以 `>` 引用块展示，最多显示前 500 个字符\n- 工具结果以 `🛠️ **工具执行**` 为标题，正文以 `>` 引用块展示，最多显示前 500 个字符\n\n> **注意：** 推理流和工具执行均会产生额外的卡片流式更新 API 调用，在 AI 推理步骤较多时可能显著增加 API 消耗，建议按需开启。\n\n**配置示例：**\n\n```json5\n{\n  messageType: 'card', // 启用 AI 互动卡片模式\n  cardTemplateId: '382e4302-551d-4880-bf29-a30acfab2e71.schema', // AI 卡片模板 ID（默认值）\n  cardTemplateKey: 'msgContent', // 卡片内容字段键（默认值：msgContent）\n}\n```\n\n> **注意**：`cardTemplateKey` 应与您的卡片模板中定义的字段名称一致。默认值为 `'msgContent'`，适用于 DingTalk 官方 AI 卡片模板。如果您使用自定义模板，请根据模板定义的字段名称进行配置。\n\n## 使用示例\n\n配置完成后，直接在钉钉中：\n\n1. **私聊机器人** — 找到机器人，发送消息\n2. **群聊 @机器人** — 在群里 @机器人名称 + 消息\n\n## 故障排除\n\n### 收不到消息\n\n1. 确认应用已发布\n2. 确认消息接收模式是 Stream\n3. 检查 Gateway 日志：`openclaw logs | grep dingtalk`\n\n### 群消息无响应\n\n1. 确认机器人已添加到群\n2. 确认正确 @机器人（使用机器人名称）\n3. 确认群是企业内部群\n\n### 连接失败\n\n1. 检查 clientId 和 clientSecret 是否正确\n2. 确认网络可以访问钉钉 API\n\n### 错误 payload 日志规范（`[ErrorPayload]`）\n\n为便于快速定位 4xx/5xx 参数问题，插件会在 API 错误分支输出统一格式日志：\n\n- 通用前缀：`[DingTalk][ErrorPayload][<scope>]`\n- AI Card 前缀：`[DingTalk][AICard][ErrorPayload][<scope>]`\n- 内容格式：`code=<...> message=<...> payload=<...>`（同时保留脱敏后的完整 payload）\n\n常见 scope 示例：\n\n- `send.proactiveMessage` / `send.proactiveMedia` / `send.message`\n- `outbound.sendText` / `outbound.sendMedia`\n- `inbound.downloadMedia` / `inbound.cardFinalize`\n- `card.create` / `card.stream` / `card.stream.retryAfterRefresh`\n- `retry.beforeDecision`\n\n排查建议：\n\n```bash\nopenclaw logs | grep \"\\[ErrorPayload\\]\"\n```\n\n如果你看到 `code=invalidParameter`，通常优先检查请求 payload 的必填字段（例如 `robotCode`、`userIds`、`msgKey`、`msgParam`）是否完整且格式正确。\n\n## 开发指南\n\n### 首次设置\n\n1. 克隆仓库并安装依赖\n\n```bash\ngit clone http://gitlab.alibaba-inc.com/trip/openclaw-channel-dingtalk.git\ncd openclaw-channel-dingtalk\nnpm install\n```\n\n2. 验证开发环境\n\n```bash\nnpm run type-check              # TypeScript 类型检查\nnpm run lint                    # ESLint 代码检查\n```\n\n### 常用命令\n\n| 命令                 | 说明                |\n| -------------------- | ------------------- |\n| `npm run type-check` | TypeScript 类型检查 |\n| `npm run lint`       | ESLint 代码检查     |\n| `npm run lint:fix`   | 自动修复格式问题    |\n\n### 项目结构\n\n```\nsrc/\n  channel.ts           - 插件定义和辅助函数（535 行）\n  runtime.ts           - 运行时管理（14 行）\n  types.ts             - 类型定义（30+ interfaces）\n\nindex.ts              - 插件注册（29 行）\nutils.ts              - 工具函数（110 行）\n\nopenclaw.plugin.json  - 插件配置\npackage.json          - 项目配置\nREADME.md             - 本文件\n```\n\n### 代码质量\n\n- **TypeScript**: 严格模式，0 错误\n- **ESLint**: 自动检查和修复\n- **Type Safety**: 完整的类型注解（30+ 接口）\n\n### 类型系统\n\n核心类型定义在 `src/types.ts` 中，包括：\n\n```typescript\n// 配置\nDingTalkConfig; // 插件配置\nDingTalkChannelConfig; // 多账户配置\n\n// 消息处理\nDingTalkInboundMessage; // 收到的钉钉消息\nMessageContent; // 解析后的消息内容\nHandleDingTalkMessageParams; // 消息处理参数\n\n// AI 互动卡片\nAICardInstance; // AI 卡片实例\nAICardCreateAndDeliverRequest; // 创建并投放卡片请求\nAICardStreamingRequest; // 流式更新请求\nAICardStatus; // 卡片状态常量\n\n// 工具函数类型\nLogger; // 日志接口\nRetryOptions; // 重试选项\nMediaFile; // 下载的媒体文件\n```\n\n### 公开 API\n\n插件导出以下低级 API 函数，可用于自定义集成：\n\n```typescript\n// 文本/Markdown 消息\nsendBySession(config, sessionWebhook, text, options); // 通过会话发送\n\n// AI 互动卡片\ncreateAICard(config, conversationId, data, log); // 创建并投放 AI 卡片\nstreamAICard(card, content, finished, log); // 流式更新卡片内容\nfinishAICard(card, content, log); // 完成并关闭卡片\n\n// 自动模式选择\nsendMessage(config, conversationId, text, options); // 根据配置自动选择（含卡片/文本回退）\n\n// 认证\ngetAccessToken(config, log); // 获取访问令牌\n```\n\n**使用示例：**\n\n```typescript\nimport { createAICard, streamAICard, finishAICard } from './src/channel';\n\n// 创建 AI 卡片\nconst card = await createAICard(config, conversationId, messageData, log);\n\n// 流式更新内容\nfor (const chunk of aiResponseChunks) {\n  await streamAICard(card, currentText + chunk, false, log);\n}\n\n// 完成并关闭卡片\nawait finishAICard(card, finalText, log);\n```\n\n### 架构\n\n插件遵循 Telegram 参考实现的架构模式：\n\n- **index.ts**: 最小化插件注册入口\n- **src/channel.ts**: 所有 DingTalk 特定的逻辑（API、消息处理、配置等）\n- **src/runtime.ts**: 运行时管理（getter/setter）\n- **src/types.ts**: 类型定义\n- **utils.ts**: 通用工具函数\n\n## 测试\n\n项目已基于 Vitest 初始化自动化测试，目录结构如下：\n\n```text\ntests/\n  unit/\n    sign.test.ts               # HmacSHA256 + Base64 签名测试\n    message-transform.test.ts  # 文本/Markdown 消息转换测试\n  integration/\n    send-lifecycle.test.ts     # 插件 outbound.sendText 生命周期适配测试\n```\n\n### 运行测试\n\n```bash\n# 安装依赖（pnpm）\npnpm install\n\n# 运行全部测试\npnpm test\n\n# 生成覆盖率报告（coverage/）\npnpm test:coverage\n```\n\n### Mock 约束\n\n- 所有测试中的网络请求均通过 `vi.mock('axios')` 拦截，禁止真实调用钉钉 API。\n- 集成测试通过模块 mock 隔离 `openclaw/plugin-sdk`、`dingtalk-stream` 等外部依赖。\n\n## 许可\n\nMIT\n","readmeFilename":"README.md"}