@jeik/dingtalk-connector 0.8.21-fix1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +686 -0
- package/LICENSE +21 -0
- package/README.en.md +181 -0
- package/README.md +221 -0
- package/bin/dingtalk-connector.js +858 -0
- package/bin/wizard-config.mjs +110 -0
- package/dist/accounts-BAzdqkAV.mjs +268 -0
- package/dist/accounts-BQptOmgB.mjs +2 -0
- package/dist/chunk-upload-BBQgGtcZ.mjs +193 -0
- package/dist/chunk-upload-DaLXXZH3.mjs +2 -0
- package/dist/common-C8pYKU_y.mjs +2 -0
- package/dist/common-Dt9n6fQN.mjs +101 -0
- package/dist/connection-DHHFFNQJ.mjs +423 -0
- package/dist/entry-bundled.d.mts +16 -0
- package/dist/entry-bundled.mjs +31 -0
- package/dist/game-xiyou-CqHt-6Q1.mjs +4271 -0
- package/dist/gateway-methods-C4tcgI7P.mjs +771 -0
- package/dist/gateway-methods-Ci31A3vg.mjs +2 -0
- package/dist/http-client-CpnJHB89.mjs +2 -0
- package/dist/http-client-DFWZgO1n.mjs +33 -0
- package/dist/index.d.mts +193 -0
- package/dist/index.mjs +45 -0
- package/dist/logger-BmJkQkm1.mjs +2 -0
- package/dist/logger-mZ9OSbmD.mjs +58 -0
- package/dist/media-C_SVin7s.mjs +2 -0
- package/dist/media-cz72EVS3.mjs +509 -0
- package/dist/message-handler-DESzFFDc.mjs +1971 -0
- package/dist/messaging-B6l1sRvX.mjs +1044 -0
- package/dist/runtime-DUgpo5zC.mjs +1422 -0
- package/dist/session-DJ4jYqPv.mjs +114 -0
- package/dist/utils-Bjh4r_qS.mjs +4 -0
- package/dist/utils-CIfI_3Jh.mjs +63 -0
- package/dist/utils-legacy-CALCPP1t.mjs +230 -0
- package/dist/utils-legacy-CFYDBM4r.mjs +3 -0
- package/docs/DEAP_AGENT_GUIDE.en.md +115 -0
- package/docs/DEAP_AGENT_GUIDE.md +115 -0
- package/docs/DINGTALK_MANUAL_SETUP.md +50 -0
- package/docs/MULTI_AGENT_SETUP.md +306 -0
- package/docs/RELEASE_NOTES_V0.7.10.md +40 -0
- package/docs/RELEASE_NOTES_V0.7.2.md +143 -0
- package/docs/RELEASE_NOTES_V0.7.3.md +149 -0
- package/docs/RELEASE_NOTES_V0.7.4.md +206 -0
- package/docs/RELEASE_NOTES_V0.7.5.md +267 -0
- package/docs/RELEASE_NOTES_V0.7.6.md +219 -0
- package/docs/RELEASE_NOTES_V0.7.7.md +122 -0
- package/docs/RELEASE_NOTES_V0.7.8.md +101 -0
- package/docs/RELEASE_NOTES_V0.7.9.md +65 -0
- package/docs/RELEASE_NOTES_V0.8.0.md +53 -0
- package/docs/RELEASE_NOTES_V0.8.1.md +47 -0
- package/docs/RELEASE_NOTES_V0.8.10.md +49 -0
- package/docs/RELEASE_NOTES_V0.8.11.md +51 -0
- package/docs/RELEASE_NOTES_V0.8.12.md +63 -0
- package/docs/RELEASE_NOTES_V0.8.13-beta.0.md +69 -0
- package/docs/RELEASE_NOTES_V0.8.13.md +62 -0
- package/docs/RELEASE_NOTES_V0.8.14.md +86 -0
- package/docs/RELEASE_NOTES_V0.8.16.md +40 -0
- package/docs/RELEASE_NOTES_V0.8.17.md +87 -0
- package/docs/RELEASE_NOTES_V0.8.18.md +64 -0
- package/docs/RELEASE_NOTES_V0.8.19.md +62 -0
- package/docs/RELEASE_NOTES_V0.8.2.md +55 -0
- package/docs/RELEASE_NOTES_V0.8.20.md +49 -0
- package/docs/RELEASE_NOTES_V0.8.3.md +63 -0
- package/docs/RELEASE_NOTES_V0.8.4.md +45 -0
- package/docs/RELEASE_NOTES_V0.8.7.md +49 -0
- package/docs/RELEASE_NOTES_V0.8.8.md +63 -0
- package/docs/RELEASE_NOTES_V0.8.9.md +81 -0
- package/docs/RELEASE_NOTES_v0.7.0.md +142 -0
- package/docs/RELEASE_NOTES_v0.7.1.md +74 -0
- package/docs/TROUBLESHOOTING.md +122 -0
- package/index.ts +77 -0
- package/openclaw.plugin.json +551 -0
- package/package.json +147 -0
- package/skills/dingtalk-channel-rules/SKILL.md +91 -0
- package/skills/dingtalk-troubleshoot/SKILL.md +93 -0
- package/skills/dws-cli/SKILL.md +129 -0
- package/skills/dws-cli/references/error-codes.md +95 -0
- package/skills/dws-cli/references/field-rules.md +105 -0
- package/skills/dws-cli/references/global-reference.md +104 -0
- package/skills/dws-cli/references/intent-guide.md +114 -0
- package/skills/dws-cli/references/products/aitable.md +452 -0
- package/skills/dws-cli/references/products/attendance.md +93 -0
- package/skills/dws-cli/references/products/calendar.md +217 -0
- package/skills/dws-cli/references/products/chat.md +292 -0
- package/skills/dws-cli/references/products/contact.md +108 -0
- package/skills/dws-cli/references/products/ding.md +57 -0
- package/skills/dws-cli/references/products/report.md +162 -0
- package/skills/dws-cli/references/products/simple.md +128 -0
- package/skills/dws-cli/references/products/todo.md +138 -0
- package/skills/dws-cli/references/products/workbench.md +39 -0
- package/skills/dws-cli/references/recovery-guide.md +94 -0
- package/src/channel.ts +588 -0
- package/src/config/accounts.ts +242 -0
- package/src/config/schema.ts +180 -0
- package/src/core/connection.ts +741 -0
- package/src/core/message-handler.ts +1788 -0
- package/src/core/provider.ts +111 -0
- package/src/core/state.ts +54 -0
- package/src/device-auth-config.ts +14 -0
- package/src/device-auth.ts +197 -0
- package/src/directory.ts +95 -0
- package/src/docs.ts +293 -0
- package/src/game-xiyou/achievement-engine.ts +252 -0
- package/src/game-xiyou/bounty-system.ts +315 -0
- package/src/game-xiyou/commands.ts +223 -0
- package/src/game-xiyou/drop-engine.ts +241 -0
- package/src/game-xiyou/encounter-system.ts +135 -0
- package/src/game-xiyou/escape-engine.ts +164 -0
- package/src/game-xiyou/exp-calculator.ts +139 -0
- package/src/game-xiyou/index.ts +479 -0
- package/src/game-xiyou/level-system.ts +91 -0
- package/src/game-xiyou/monster-pool.ts +180 -0
- package/src/game-xiyou/pity-counter.ts +114 -0
- package/src/game-xiyou/random-event-engine.ts +648 -0
- package/src/game-xiyou/renderer.ts +679 -0
- package/src/game-xiyou/storage.ts +218 -0
- package/src/game-xiyou/treasure-system.ts +105 -0
- package/src/game-xiyou/types.ts +582 -0
- package/src/game-xiyou/uid-resolver.ts +49 -0
- package/src/gateway-methods.ts +740 -0
- package/src/onboarding.ts +553 -0
- package/src/policy.ts +32 -0
- package/src/probe.ts +210 -0
- package/src/reply-dispatcher.ts +874 -0
- package/src/runtime.ts +32 -0
- package/src/sdk/helpers.ts +322 -0
- package/src/sdk/types.ts +519 -0
- package/src/secret-input.ts +19 -0
- package/src/services/media/audio.ts +54 -0
- package/src/services/media/chunk-upload.ts +296 -0
- package/src/services/media/common.ts +155 -0
- package/src/services/media/file.ts +75 -0
- package/src/services/media/image.ts +81 -0
- package/src/services/media/index.ts +10 -0
- package/src/services/media/video.ts +162 -0
- package/src/services/media.ts +1143 -0
- package/src/services/messaging/card.ts +604 -0
- package/src/services/messaging/index.ts +18 -0
- package/src/services/messaging/mentions.ts +267 -0
- package/src/services/messaging/send.ts +141 -0
- package/src/services/messaging.ts +1191 -0
- package/src/services/reply-markers.ts +55 -0
- package/src/targets.ts +45 -0
- package/src/types/index.ts +59 -0
- package/src/types/pdf-parse.d.ts +3 -0
- package/src/utils/agent.ts +63 -0
- package/src/utils/async.ts +51 -0
- package/src/utils/constants.ts +27 -0
- package/src/utils/http-client.ts +38 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/logger.ts +78 -0
- package/src/utils/session.ts +147 -0
- package/src/utils/token.ts +93 -0
- package/src/utils/utils-legacy.ts +454 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 多机器人 @ 提及解析器
|
|
3
|
+
*
|
|
4
|
+
* 目的:在多 Agent / 多 bot 群场景下,让 AI 写 "@dev-agent / @开发助手机器人"
|
|
5
|
+
* 这样的自然语言时,connector 能自动把它们替换成钉钉识别的
|
|
6
|
+
* `@$:LWCP_v1:$xxxxx`(chatbotUserId 加密 ID),并补上 `at.atDingtalkIds`。
|
|
7
|
+
*
|
|
8
|
+
* 解析来源:`channels.dingtalk-connector.accounts` 下配置的所有 bot。
|
|
9
|
+
* 每个 bot 提供 3 类别名:
|
|
10
|
+
* 1. accountId(如 `dev-bot`)
|
|
11
|
+
* 2. 配置里的友好名 name(如 `开发助手机器人`)
|
|
12
|
+
* 3. 通过 bindings 反查的 agentId(如 `dev-agent`)
|
|
13
|
+
*
|
|
14
|
+
* 设计原则:
|
|
15
|
+
* - 不改变原始 AI 文本里不相关的 @ 内容(例如 @all、@某个手机号)
|
|
16
|
+
* - 只替换能明确对应到某个 bot 的 token
|
|
17
|
+
* - 幂等:已经是 `@$:LWCP_v1:$xxx` 格式的文本不会被二次替换
|
|
18
|
+
*/
|
|
19
|
+
import type { DingtalkAccountConfig, DingtalkConfig } from "../../types/index.ts";
|
|
20
|
+
|
|
21
|
+
/** 单个 bot 的 @ 解析表项 */
|
|
22
|
+
export interface BotMentionEntry {
|
|
23
|
+
accountId: string;
|
|
24
|
+
/** 机器人在钉钉侧的加密用户 ID(`$:LWCP_v1:$xxx`),没填则为 undefined */
|
|
25
|
+
chatbotUserId?: string;
|
|
26
|
+
/** 配置里的友好名(`accounts.<id>.name`) */
|
|
27
|
+
name?: string;
|
|
28
|
+
/** 通过 bindings 绑定的 agentId 列表(1 个 bot 通常绑 1 个 agent) */
|
|
29
|
+
agentIds: string[];
|
|
30
|
+
/** 所有候选别名的去重集合(含 accountId / name / agentIds) */
|
|
31
|
+
aliases: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface BuildMentionTableOptions {
|
|
35
|
+
/** 额外别名映射:key 为 alias,value 为 accountId。用于调用方临时补充(例如 agent prompt 里的缩写) */
|
|
36
|
+
extraAliases?: Record<string, string>;
|
|
37
|
+
/**
|
|
38
|
+
* 是否允许把“裸别名”(例如 `dev-agent`,前面没有 `@`)识别为 mention 目标。
|
|
39
|
+
* 启用后不会直接改写原文,仅会把对应 chatbotUserId 注入 `injectedChatbotUserIds`,
|
|
40
|
+
* 由上层在发送前自动追加 `@<chatbotUserId>` 到文末以触发钉钉真实 @。
|
|
41
|
+
*/
|
|
42
|
+
detectBareAliases?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 从全局 cfg 里构建「bot 别名 → chatbotUserId」的解析表。
|
|
47
|
+
*
|
|
48
|
+
* 会同时扫描:
|
|
49
|
+
* - `channels.dingtalk-connector.accounts.*`:accountId + name + chatbotUserId
|
|
50
|
+
* - `bindings[]`:根据 `match.accountId` 反查 agentId
|
|
51
|
+
*/
|
|
52
|
+
export function buildBotMentionTable(
|
|
53
|
+
cfg: any,
|
|
54
|
+
options: BuildMentionTableOptions = {},
|
|
55
|
+
): BotMentionEntry[] {
|
|
56
|
+
const root = cfg?.channels?.["dingtalk-connector"] as DingtalkConfig | undefined;
|
|
57
|
+
const accountsMap = (root?.accounts as Record<string, DingtalkAccountConfig | undefined>) || {};
|
|
58
|
+
|
|
59
|
+
const byAccountId = new Map<string, BotMentionEntry>();
|
|
60
|
+
for (const [accountId, acct] of Object.entries(accountsMap)) {
|
|
61
|
+
if (!acct) continue;
|
|
62
|
+
byAccountId.set(accountId, {
|
|
63
|
+
accountId,
|
|
64
|
+
chatbotUserId: (acct as any).chatbotUserId?.trim?.() || undefined,
|
|
65
|
+
name: (acct as any).name?.trim?.() || undefined,
|
|
66
|
+
agentIds: [],
|
|
67
|
+
aliases: [],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const bindings = (cfg as any)?.bindings;
|
|
72
|
+
if (Array.isArray(bindings)) {
|
|
73
|
+
for (const b of bindings) {
|
|
74
|
+
const match = b?.match;
|
|
75
|
+
if (!match) continue;
|
|
76
|
+
if (match.channel && match.channel !== "dingtalk-connector") continue;
|
|
77
|
+
const accountId = match.accountId;
|
|
78
|
+
const agentId = b.agentId;
|
|
79
|
+
if (typeof accountId !== "string" || typeof agentId !== "string") continue;
|
|
80
|
+
const entry = byAccountId.get(accountId);
|
|
81
|
+
if (!entry) continue;
|
|
82
|
+
if (!entry.agentIds.includes(agentId)) {
|
|
83
|
+
entry.agentIds.push(agentId);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const extraMap = new Map<string, string>();
|
|
89
|
+
if (options.extraAliases) {
|
|
90
|
+
for (const [alias, accountId] of Object.entries(options.extraAliases)) {
|
|
91
|
+
if (alias && accountId) {
|
|
92
|
+
extraMap.set(alias.toLowerCase(), accountId);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const entry of byAccountId.values()) {
|
|
98
|
+
const aliasSet = new Set<string>();
|
|
99
|
+
aliasSet.add(entry.accountId);
|
|
100
|
+
if (entry.name) aliasSet.add(entry.name);
|
|
101
|
+
for (const aid of entry.agentIds) aliasSet.add(aid);
|
|
102
|
+
for (const [alias, accountId] of extraMap.entries()) {
|
|
103
|
+
if (accountId === entry.accountId) aliasSet.add(alias);
|
|
104
|
+
}
|
|
105
|
+
entry.aliases = Array.from(aliasSet);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return Array.from(byAccountId.values());
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** chatbotUserId 加密 ID 的正则(用于检测文本里已经写成加密形式的 @) */
|
|
112
|
+
const CHATBOT_ID_PATTERN = /\$:LWCP_v1:\$[A-Za-z0-9+/=]+/g;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 把一批 accountId 解析成对应的 chatbotUserId 数组。
|
|
116
|
+
* 找不到 chatbotUserId 的账号会被跳过,并通过 `missing` 报告,方便上层 log 警告。
|
|
117
|
+
*/
|
|
118
|
+
export function resolveAtAccountIdsToChatbotUserIds(
|
|
119
|
+
cfg: any,
|
|
120
|
+
atAccountIds: string[] | undefined,
|
|
121
|
+
): { resolved: string[]; missing: string[] } {
|
|
122
|
+
if (!atAccountIds || atAccountIds.length === 0) {
|
|
123
|
+
return { resolved: [], missing: [] };
|
|
124
|
+
}
|
|
125
|
+
const table = buildBotMentionTable(cfg);
|
|
126
|
+
const byAccountId = new Map(table.map((e) => [e.accountId, e]));
|
|
127
|
+
const resolved: string[] = [];
|
|
128
|
+
const missing: string[] = [];
|
|
129
|
+
for (const id of atAccountIds) {
|
|
130
|
+
if (!id) continue;
|
|
131
|
+
const entry = byAccountId.get(id);
|
|
132
|
+
if (entry?.chatbotUserId) {
|
|
133
|
+
resolved.push(entry.chatbotUserId);
|
|
134
|
+
} else {
|
|
135
|
+
missing.push(id);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { resolved, missing };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 对文本中的 @ 别名做自动替换:
|
|
143
|
+
* 1. `@<alias>` → `@<chatbotUserId>`(alias 命中某个 bot 时)
|
|
144
|
+
* 2. 已经是 `@$:LWCP_v1:$xxx` 形式的 @ 原样保留
|
|
145
|
+
*
|
|
146
|
+
* 返回:
|
|
147
|
+
* - `text`:替换后的文本
|
|
148
|
+
* - `injectedChatbotUserIds`:本次替换中涉及到的 chatbotUserId 列表(调用方可合并到 atDingtalkIds)
|
|
149
|
+
*/
|
|
150
|
+
export function substituteBotMentions(
|
|
151
|
+
text: string,
|
|
152
|
+
cfg: any,
|
|
153
|
+
options: BuildMentionTableOptions = {},
|
|
154
|
+
): { text: string; injectedChatbotUserIds: string[] } {
|
|
155
|
+
if (!text || typeof text !== "string") {
|
|
156
|
+
return { text: text ?? "", injectedChatbotUserIds: [] };
|
|
157
|
+
}
|
|
158
|
+
const table = buildBotMentionTable(cfg, options);
|
|
159
|
+
|
|
160
|
+
// 别名 → chatbotUserId 查找表(不区分大小写,长别名优先匹配)
|
|
161
|
+
const aliasToChatbotUserId = new Map<string, string>();
|
|
162
|
+
for (const entry of table) {
|
|
163
|
+
if (!entry.chatbotUserId) continue;
|
|
164
|
+
for (const alias of entry.aliases) {
|
|
165
|
+
const key = alias.toLowerCase();
|
|
166
|
+
if (!aliasToChatbotUserId.has(key)) {
|
|
167
|
+
aliasToChatbotUserId.set(key, entry.chatbotUserId);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (aliasToChatbotUserId.size === 0) {
|
|
173
|
+
return { text, injectedChatbotUserIds: [] };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 按别名长度降序替换,避免 "dev-agent" 被短别名 "dev" 先匹配掉
|
|
177
|
+
const aliases = Array.from(aliasToChatbotUserId.keys()).sort(
|
|
178
|
+
(a, b) => b.length - a.length,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const injected = new Set<string>();
|
|
182
|
+
let out = text;
|
|
183
|
+
|
|
184
|
+
for (const alias of aliases) {
|
|
185
|
+
const chatbotUserId = aliasToChatbotUserId.get(alias)!;
|
|
186
|
+
const escaped = alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
187
|
+
// 前置允许:开头 / 空白 / 标点;尾随允许:结尾 / 空白 / 标点(但不能是 id 字符)
|
|
188
|
+
const pattern = new RegExp(
|
|
189
|
+
`@(${escaped})(?![A-Za-z0-9_\\u4e00-\\u9fff\\-])`,
|
|
190
|
+
"gi",
|
|
191
|
+
);
|
|
192
|
+
out = out.replace(pattern, (match, _matched, offset: number) => {
|
|
193
|
+
// 跳过已经在 chatbotUserId 里的片段(保险起见)
|
|
194
|
+
const before = out.slice(Math.max(0, offset - 1), offset);
|
|
195
|
+
if (before === "$") return match;
|
|
196
|
+
injected.add(chatbotUserId);
|
|
197
|
+
return `@${chatbotUserId}`;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 可选兜底:识别裸别名(无 @ 前缀),并注入对应 chatbotUserId。
|
|
202
|
+
// 说明:不少模型会输出“已拉上 dev-agent review”而不是“@dev-agent ...”,
|
|
203
|
+
// 该兜底可强制触发真实 mention(通过发送层追加 @<chatbotUserId>)。
|
|
204
|
+
if (options.detectBareAliases) {
|
|
205
|
+
for (const alias of aliases) {
|
|
206
|
+
const chatbotUserId = aliasToChatbotUserId.get(alias)!;
|
|
207
|
+
const escaped = alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
208
|
+
const pattern = new RegExp(
|
|
209
|
+
`(?<![@A-Za-z0-9_\\u4e00-\\u9fff\\-])(${escaped})(?![A-Za-z0-9_\\u4e00-\\u9fff\\-])`,
|
|
210
|
+
"gi",
|
|
211
|
+
);
|
|
212
|
+
if (pattern.test(out)) {
|
|
213
|
+
injected.add(chatbotUserId);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 把文本里用户已经写好的 `@$:LWCP_v1:$xxx` 也收集起来
|
|
219
|
+
const rawIds = out.match(CHATBOT_ID_PATTERN) || [];
|
|
220
|
+
for (const id of rawIds) injected.add(id);
|
|
221
|
+
|
|
222
|
+
return { text: out, injectedChatbotUserIds: Array.from(injected) };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* 高层入口:同时处理显式 `atAccountIds` 与文本里的自然语言 @。
|
|
227
|
+
*
|
|
228
|
+
* 用于 `dingtalk-connector.send*` 系列 Gateway 方法,在调 `sendProactive` 前把最终
|
|
229
|
+
* 的 `content / atDingtalkIds` 准备好。
|
|
230
|
+
*/
|
|
231
|
+
export function prepareMultiBotMentions(params: {
|
|
232
|
+
cfg: any;
|
|
233
|
+
content: string;
|
|
234
|
+
atAccountIds?: string[];
|
|
235
|
+
atDingtalkIds?: string[];
|
|
236
|
+
/** 额外别名:agent prompt 里有时会用缩写/昵称指代某个 bot */
|
|
237
|
+
extraAliases?: Record<string, string>;
|
|
238
|
+
}): {
|
|
239
|
+
content: string;
|
|
240
|
+
atDingtalkIds: string[];
|
|
241
|
+
/** atAccountIds 里那些没在 accounts.*.chatbotUserId 里配出来的 id,用于 log 警告 */
|
|
242
|
+
missingAccountIds: string[];
|
|
243
|
+
} {
|
|
244
|
+
const { cfg, content, atAccountIds, atDingtalkIds = [], extraAliases } = params;
|
|
245
|
+
|
|
246
|
+
const explicit = resolveAtAccountIdsToChatbotUserIds(cfg, atAccountIds);
|
|
247
|
+
const substituted = substituteBotMentions(content, cfg, { extraAliases });
|
|
248
|
+
|
|
249
|
+
const merged = new Set<string>();
|
|
250
|
+
for (const id of atDingtalkIds) if (id) merged.add(id);
|
|
251
|
+
for (const id of explicit.resolved) merged.add(id);
|
|
252
|
+
for (const id of substituted.injectedChatbotUserIds) merged.add(id);
|
|
253
|
+
|
|
254
|
+
// 文本尾巴确保带 `@<chatbotUserId>`。buildMsgPayload 还会补一次,保证万无一失
|
|
255
|
+
let finalContent = substituted.text;
|
|
256
|
+
for (const id of explicit.resolved) {
|
|
257
|
+
if (!finalContent.includes(`@${id}`)) {
|
|
258
|
+
finalContent = `${finalContent} @${id}`;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
content: finalContent,
|
|
264
|
+
atDingtalkIds: Array.from(merged),
|
|
265
|
+
missingAccountIds: explicit.missing,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 消息发送基础模块
|
|
3
|
+
* 支持 Markdown、文本、链接等消息类型
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DingtalkConfig } from '../../types/index.ts';
|
|
7
|
+
import { DINGTALK_API, getAccessToken } from '../../utils/token.ts';
|
|
8
|
+
import { dingtalkHttp } from '../../utils/http-client.ts';
|
|
9
|
+
|
|
10
|
+
/** 消息类型枚举 */
|
|
11
|
+
export type DingTalkMsgType = 'text' | 'markdown' | 'link' | 'actionCard' | 'image';
|
|
12
|
+
|
|
13
|
+
/** 主动发送消息的结果 */
|
|
14
|
+
export interface SendResult {
|
|
15
|
+
ok: boolean;
|
|
16
|
+
processQueryKey?: string;
|
|
17
|
+
cardInstanceId?: string;
|
|
18
|
+
error?: string;
|
|
19
|
+
usedAICard?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** 主动发送选项 */
|
|
23
|
+
export interface ProactiveSendOptions {
|
|
24
|
+
msgType?: DingTalkMsgType;
|
|
25
|
+
replyToId?: string;
|
|
26
|
+
title?: string;
|
|
27
|
+
log?: any;
|
|
28
|
+
useAICard?: boolean;
|
|
29
|
+
fallbackToNormal?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 发送 Markdown 消息
|
|
34
|
+
*/
|
|
35
|
+
export async function sendMarkdownMessage(
|
|
36
|
+
config: DingtalkConfig,
|
|
37
|
+
sessionWebhook: string,
|
|
38
|
+
title: string,
|
|
39
|
+
markdown: string,
|
|
40
|
+
options: any = {},
|
|
41
|
+
): Promise<any> {
|
|
42
|
+
const token = await getAccessToken(config);
|
|
43
|
+
let text = markdown;
|
|
44
|
+
if (options.atUserId) text = `${text} @${options.atUserId}`;
|
|
45
|
+
|
|
46
|
+
const body: any = {
|
|
47
|
+
msgtype: 'markdown',
|
|
48
|
+
markdown: {
|
|
49
|
+
title,
|
|
50
|
+
text: text,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
if (options.atUserId) {
|
|
55
|
+
body.at = {
|
|
56
|
+
userIds: [options.atUserId],
|
|
57
|
+
isAtAll: false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const resp = await dingtalkHttp.post(sessionWebhook, body, {
|
|
62
|
+
headers: {
|
|
63
|
+
'x-acs-dingtalk-access-token': token,
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return resp.data;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 发送文本消息
|
|
73
|
+
*/
|
|
74
|
+
export async function sendTextMessage(
|
|
75
|
+
config: DingtalkConfig,
|
|
76
|
+
sessionWebhook: string,
|
|
77
|
+
content: string,
|
|
78
|
+
options: any = {},
|
|
79
|
+
): Promise<any> {
|
|
80
|
+
const token = await getAccessToken(config);
|
|
81
|
+
let text = content;
|
|
82
|
+
if (options.atUserId) text = `${text} @${options.atUserId}`;
|
|
83
|
+
|
|
84
|
+
const body: any = {
|
|
85
|
+
msgtype: 'text',
|
|
86
|
+
text: {
|
|
87
|
+
content: text,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (options.atUserId) {
|
|
92
|
+
body.at = {
|
|
93
|
+
userIds: [options.atUserId],
|
|
94
|
+
isAtAll: false,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const resp = await dingtalkHttp.post(sessionWebhook, body, {
|
|
99
|
+
headers: {
|
|
100
|
+
'x-acs-dingtalk-access-token': token,
|
|
101
|
+
'Content-Type': 'application/json',
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return resp.data;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 发送链接消息
|
|
110
|
+
*/
|
|
111
|
+
export async function sendLinkMessage(
|
|
112
|
+
config: DingtalkConfig,
|
|
113
|
+
sessionWebhook: string,
|
|
114
|
+
params: {
|
|
115
|
+
title: string;
|
|
116
|
+
text: string;
|
|
117
|
+
picUrl?: string;
|
|
118
|
+
messageUrl: string;
|
|
119
|
+
},
|
|
120
|
+
): Promise<any> {
|
|
121
|
+
const token = await getAccessToken(config);
|
|
122
|
+
|
|
123
|
+
const body = {
|
|
124
|
+
msgtype: 'link',
|
|
125
|
+
link: {
|
|
126
|
+
title: params.title,
|
|
127
|
+
text: params.text,
|
|
128
|
+
picUrl: params.picUrl,
|
|
129
|
+
messageUrl: params.messageUrl,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const resp = await dingtalkHttp.post(sessionWebhook, body, {
|
|
134
|
+
headers: {
|
|
135
|
+
'x-acs-dingtalk-access-token': token,
|
|
136
|
+
'Content-Type': 'application/json',
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return resp.data;
|
|
141
|
+
}
|