@huo15/dingtalk-connector-pro 1.0.4 → 1.0.7
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/README.en.md +106 -384
- package/README.md +14 -18
- package/dist/index.js +17 -0
- package/dist/openclaw.plugin.json +498 -0
- package/dist/package.json +91 -0
- package/dist/src/channel.js +415 -0
- package/dist/src/config/accounts.js +182 -0
- package/dist/src/config/schema.js +135 -0
- package/dist/src/core/connection.js +561 -0
- package/dist/src/core/message-handler.js +1422 -0
- package/dist/src/core/provider.js +59 -0
- package/dist/src/core/state.js +49 -0
- package/dist/src/directory.js +53 -0
- package/dist/src/docs.js +209 -0
- package/dist/src/gateway-methods.js +360 -0
- package/dist/src/onboarding.js +337 -0
- package/dist/src/policy.js +15 -0
- package/dist/src/probe.js +144 -0
- package/dist/src/reply-dispatcher.js +435 -0
- package/dist/src/runtime.js +26 -0
- package/dist/src/sdk/helpers.js +237 -0
- package/dist/src/sdk/types.js +13 -0
- package/dist/src/secret-input.js +13 -0
- package/dist/src/services/media/audio.js +40 -0
- package/dist/src/services/media/chunk-upload.js +211 -0
- package/dist/src/services/media/common.js +120 -0
- package/dist/src/services/media/file.js +54 -0
- package/dist/src/services/media/image.js +59 -0
- package/dist/src/services/media/index.js +9 -0
- package/dist/src/services/media/video.js +133 -0
- package/dist/src/services/media.js +889 -0
- package/dist/src/services/messaging/card.js +234 -0
- package/dist/src/services/messaging/index.js +8 -0
- package/dist/src/services/messaging/send.js +85 -0
- package/dist/src/services/messaging.js +680 -0
- package/dist/src/targets.js +38 -0
- package/dist/src/types/index.js +1 -0
- package/dist/src/utils/agent.js +55 -0
- package/dist/src/utils/async.js +40 -0
- package/dist/src/utils/constants.js +24 -0
- package/dist/src/utils/http-client.js +33 -0
- package/dist/src/utils/index.js +7 -0
- package/dist/src/utils/logger.js +76 -0
- package/dist/src/utils/session.js +95 -0
- package/dist/src/utils/token.js +71 -0
- package/dist/src/utils/utils-legacy.js +393 -0
- package/index.ts +3 -3
- package/openclaw.plugin.json +1 -1
- package/package.json +16 -5
- package/src/channel.js +415 -0
- package/src/channel.ts +12 -12
- package/src/config/accounts.js +182 -0
- package/src/config/accounts.ts +2 -2
- package/src/config/schema.js +135 -0
- package/src/config/schema.ts +2 -2
- package/src/core/connection.js +561 -0
- package/src/core/connection.ts +2 -2
- package/src/core/message-handler.js +1422 -0
- package/src/core/message-handler.ts +12 -12
- package/src/core/provider.js +59 -0
- package/src/core/provider.ts +4 -4
- package/src/core/state.js +49 -0
- package/src/directory.js +53 -0
- package/src/directory.ts +2 -2
- package/src/docs.js +209 -0
- package/src/docs.ts +3 -3
- package/src/gateway-methods.js +360 -0
- package/src/gateway-methods.ts +5 -5
- package/src/onboarding.js +337 -0
- package/src/onboarding.ts +4 -4
- package/src/policy.js +15 -0
- package/src/policy.ts +1 -1
- package/src/probe.js +144 -0
- package/src/probe.ts +2 -2
- package/src/reply-dispatcher.js +435 -0
- package/src/reply-dispatcher.ts +9 -9
- package/src/runtime.js +26 -0
- package/src/sdk/helpers.js +237 -0
- package/src/sdk/helpers.ts +1 -1
- package/src/sdk/types.js +13 -0
- package/src/secret-input.js +13 -0
- package/src/secret-input.ts +1 -1
- package/src/services/media/audio.js +40 -0
- package/src/services/media/audio.ts +2 -2
- package/src/services/media/chunk-upload.js +211 -0
- package/src/services/media/chunk-upload.ts +2 -2
- package/src/services/media/common.js +120 -0
- package/src/services/media/common.ts +3 -3
- package/src/services/media/file.js +54 -0
- package/src/services/media/file.ts +2 -2
- package/src/services/media/image.js +59 -0
- package/src/services/media/image.ts +2 -2
- package/src/services/media/index.js +9 -0
- package/src/services/media/index.ts +6 -6
- package/src/services/media/video.js +133 -0
- package/src/services/media/video.ts +2 -2
- package/src/services/media.js +889 -0
- package/src/services/media.ts +12 -12
- package/src/services/messaging/card.js +234 -0
- package/src/services/messaging/card.ts +3 -3
- package/src/services/messaging/index.js +8 -0
- package/src/services/messaging/index.ts +3 -3
- package/src/services/messaging/send.js +85 -0
- package/src/services/messaging/send.ts +3 -3
- package/src/services/messaging.js +680 -0
- package/src/services/messaging.ts +8 -8
- package/src/targets.js +38 -0
- package/src/targets.ts +1 -1
- package/src/types/index.js +1 -0
- package/src/types/index.ts +1 -1
- package/src/utils/agent.js +55 -0
- package/src/utils/async.js +40 -0
- package/src/utils/constants.js +24 -0
- package/src/utils/http-client.js +33 -0
- package/src/utils/http-client.ts +1 -1
- package/src/utils/index.js +7 -0
- package/src/utils/index.ts +4 -4
- package/src/utils/logger.js +76 -0
- package/src/utils/session.js +95 -0
- package/src/utils/session.ts +1 -1
- package/src/utils/token.js +71 -0
- package/src/utils/token.ts +2 -2
- package/src/utils/utils-legacy.js +393 -0
- package/src/utils/utils-legacy.ts +8 -8
- package/CHANGELOG.md +0 -485
- package/SKILL.md +0 -40
- package/_meta.json +0 -4
- package/docs/AGENT_ROUTING.md +0 -335
- package/docs/DEAP_AGENT_GUIDE.en.md +0 -115
- package/docs/DEAP_AGENT_GUIDE.md +0 -115
- package/docs/images/dingtalk.svg +0 -1
- package/docs/images/image-1.png +0 -0
- package/docs/images/image-2.png +0 -0
- package/docs/images/image-3.png +0 -0
- package/docs/images/image-4.png +0 -0
- package/docs/images/image-5.png +0 -0
- package/docs/images/image-6.png +0 -0
- package/docs/images/image-7.png +0 -0
- package/install-beta.sh +0 -438
- package/install-npm.sh +0 -167
- package/src/hooks/init.ts +0 -16
- package/tsconfig.json +0 -20
package/src/services/media.ts
CHANGED
|
@@ -8,9 +8,9 @@ import * as path from 'path';
|
|
|
8
8
|
// form-data 是 CJS 模块,静态 import 可确保 jiti/ESM 环境下 CJS 互操作行为稳定,
|
|
9
9
|
// 避免动态 import 时 .default 偶发为 undefined 导致 "Cannot read properties of undefined (reading 'registry')"
|
|
10
10
|
import FormData from 'form-data';
|
|
11
|
-
import type { DingtalkConfig } from '../types/index.
|
|
12
|
-
import { DINGTALK_OAPI, getOapiAccessToken } from '../utils/index.
|
|
13
|
-
import { dingtalkHttp, dingtalkOapiHttp } from '../utils/http-client.
|
|
11
|
+
import type { DingtalkConfig } from '../types/index.js';
|
|
12
|
+
import { DINGTALK_OAPI, getOapiAccessToken } from '../utils/index.js';
|
|
13
|
+
import { dingtalkHttp, dingtalkOapiHttp } from '../utils/http-client.js';
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
/** 文本文件扩展名 */
|
|
@@ -716,7 +716,7 @@ async function sendVideoMessage(
|
|
|
716
716
|
metadata?: { duration: number; width: number; height: number },
|
|
717
717
|
): Promise<void> {
|
|
718
718
|
try {
|
|
719
|
-
const token = await (await import('../utils/index.
|
|
719
|
+
const token = await (await import('../utils/index.js')).getAccessToken(config);
|
|
720
720
|
|
|
721
721
|
// 钉钉视频消息格式(sessionWebhook 模式)
|
|
722
722
|
const videoMessage = {
|
|
@@ -759,8 +759,8 @@ export async function sendVideoProactive(
|
|
|
759
759
|
log?: any,
|
|
760
760
|
): Promise<void> {
|
|
761
761
|
try {
|
|
762
|
-
const token = await (await import('../utils/index.
|
|
763
|
-
const { DINGTALK_API } = await import('../utils/index.
|
|
762
|
+
const token = await (await import('../utils/index.js')).getAccessToken(config);
|
|
763
|
+
const { DINGTALK_API } = await import('../utils/index.js');
|
|
764
764
|
|
|
765
765
|
// 钉钉普通消息 API 的视频消息格式
|
|
766
766
|
const msgParam = {
|
|
@@ -819,7 +819,7 @@ async function sendAudioMessage(
|
|
|
819
819
|
durationMs?: number,
|
|
820
820
|
): Promise<void> {
|
|
821
821
|
try {
|
|
822
|
-
const token = await (await import('../utils/index.
|
|
822
|
+
const token = await (await import('../utils/index.js')).getAccessToken(config);
|
|
823
823
|
|
|
824
824
|
// 钉钉语音消息格式
|
|
825
825
|
const actualDuration = (durationMs && durationMs > 0) ? durationMs.toString() : '60000';
|
|
@@ -862,8 +862,8 @@ export async function sendAudioProactive(
|
|
|
862
862
|
durationMs?: number,
|
|
863
863
|
): Promise<void> {
|
|
864
864
|
try {
|
|
865
|
-
const token = await (await import('../utils/index.
|
|
866
|
-
const { DINGTALK_API } = await import('../utils/index.
|
|
865
|
+
const token = await (await import('../utils/index.js')).getAccessToken(config);
|
|
866
|
+
const { DINGTALK_API } = await import('../utils/index.js');
|
|
867
867
|
|
|
868
868
|
// 钉钉普通消息 API 的音频消息格式
|
|
869
869
|
const actualDuration = (durationMs && durationMs > 0) ? durationMs.toString() : '60000';
|
|
@@ -915,7 +915,7 @@ async function sendFileMessage(
|
|
|
915
915
|
log?: any,
|
|
916
916
|
): Promise<void> {
|
|
917
917
|
try {
|
|
918
|
-
const token = await (await import('../utils/index.
|
|
918
|
+
const token = await (await import('../utils/index.js')).getAccessToken(config);
|
|
919
919
|
|
|
920
920
|
const fileMessage = {
|
|
921
921
|
msgtype: 'file',
|
|
@@ -956,8 +956,8 @@ export async function sendFileProactive(
|
|
|
956
956
|
log?: any,
|
|
957
957
|
): Promise<void> {
|
|
958
958
|
try {
|
|
959
|
-
const token = await (await import('../utils/index.
|
|
960
|
-
const { DINGTALK_API } = await import('../utils/index.
|
|
959
|
+
const token = await (await import('../utils/index.js')).getAccessToken(config);
|
|
960
|
+
const { DINGTALK_API } = await import('../utils/index.js');
|
|
961
961
|
|
|
962
962
|
// 钉钉普通消息 API 的文件消息格式
|
|
963
963
|
const msgParam = {
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Card 流式响应模块
|
|
3
|
+
* 支持 AI Card 创建、流式更新、完成
|
|
4
|
+
*/
|
|
5
|
+
import { DINGTALK_API, getAccessToken } from "../../utils/token.js";
|
|
6
|
+
import { dingtalkHttp } from "../../utils/http-client.js";
|
|
7
|
+
// ============ 常量 ============
|
|
8
|
+
const AI_CARD_TEMPLATE_ID = "02fcf2f4-5e02-4a85-b672-46d1f715543e.schema";
|
|
9
|
+
/** AI Card 状态 */
|
|
10
|
+
const AICardStatus = {
|
|
11
|
+
PROCESSING: "1",
|
|
12
|
+
INPUTING: "2",
|
|
13
|
+
FINISHED: "3",
|
|
14
|
+
EXECUTING: "4",
|
|
15
|
+
FAILED: "5",
|
|
16
|
+
};
|
|
17
|
+
// ============ Markdown 格式修正 ============
|
|
18
|
+
/**
|
|
19
|
+
* 确保 Markdown 表格前有空行,否则钉钉无法正确渲染表格
|
|
20
|
+
*/
|
|
21
|
+
function ensureTableBlankLines(text) {
|
|
22
|
+
const lines = text.split("\n");
|
|
23
|
+
const result = [];
|
|
24
|
+
const tableDividerRegex = /^\s*\|?\s*:?-+:?\s*(\|?\s*:?-+:?\s*)+\|?\s*$/;
|
|
25
|
+
const tableRowRegex = /^\s*\|?.*\|.*\|?\s*$/;
|
|
26
|
+
const isDivider = (line) => line &&
|
|
27
|
+
typeof line === "string" &&
|
|
28
|
+
line.includes("|") &&
|
|
29
|
+
tableDividerRegex.test(line);
|
|
30
|
+
for (let i = 0; i < lines.length; i++) {
|
|
31
|
+
const currentLine = lines[i];
|
|
32
|
+
const nextLine = lines[i + 1] ?? "";
|
|
33
|
+
if (tableRowRegex.test(currentLine) &&
|
|
34
|
+
isDivider(nextLine) &&
|
|
35
|
+
i > 0 &&
|
|
36
|
+
lines[i - 1].trim() !== "" &&
|
|
37
|
+
!tableRowRegex.test(lines[i - 1])) {
|
|
38
|
+
result.push("");
|
|
39
|
+
}
|
|
40
|
+
result.push(currentLine);
|
|
41
|
+
}
|
|
42
|
+
return result.join("\n");
|
|
43
|
+
}
|
|
44
|
+
// ============ AI Card 相关 ============
|
|
45
|
+
/**
|
|
46
|
+
* 构建卡片投放请求体
|
|
47
|
+
*/
|
|
48
|
+
export function buildDeliverBody(cardInstanceId, target, robotCode) {
|
|
49
|
+
const base = { outTrackId: cardInstanceId, userIdType: 1 };
|
|
50
|
+
if (target.type === "group") {
|
|
51
|
+
return {
|
|
52
|
+
...base,
|
|
53
|
+
openSpaceId: `dtv1.card//IM_GROUP.${target.openConversationId}`,
|
|
54
|
+
imGroupOpenDeliverModel: {
|
|
55
|
+
robotCode,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
...base,
|
|
61
|
+
openSpaceId: `dtv1.card//IM_ROBOT.${target.userId}`,
|
|
62
|
+
imRobotOpenDeliverModel: {
|
|
63
|
+
spaceType: 'IM_ROBOT',
|
|
64
|
+
robotCode,
|
|
65
|
+
extension: {
|
|
66
|
+
dynamicSummary: 'true',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 通用 AI Card 创建函数
|
|
73
|
+
*/
|
|
74
|
+
export async function createAICardForTarget(config, target, log) {
|
|
75
|
+
const targetDesc = target.type === "group"
|
|
76
|
+
? `群聊 ${target.openConversationId}`
|
|
77
|
+
: `用户 ${target.userId}`;
|
|
78
|
+
try {
|
|
79
|
+
const token = await getAccessToken(config);
|
|
80
|
+
const cardInstanceId = `card_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
81
|
+
log?.info?.(`[DingTalk][AICard] 开始创建卡片:${targetDesc}, outTrackId=${cardInstanceId}`);
|
|
82
|
+
// 1. 创建卡片实例
|
|
83
|
+
const createBody = {
|
|
84
|
+
cardTemplateId: AI_CARD_TEMPLATE_ID,
|
|
85
|
+
outTrackId: cardInstanceId,
|
|
86
|
+
cardData: {
|
|
87
|
+
cardParamMap: {
|
|
88
|
+
config: JSON.stringify({ autoLayout: true }),
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
callbackType: "STREAM",
|
|
92
|
+
imGroupOpenSpaceModel: { supportForward: true },
|
|
93
|
+
imRobotOpenSpaceModel: { supportForward: true },
|
|
94
|
+
};
|
|
95
|
+
const createResp = await dingtalkHttp.post(`${DINGTALK_API}/v1.0/card/instances`, createBody, {
|
|
96
|
+
headers: {
|
|
97
|
+
"x-acs-dingtalk-access-token": token,
|
|
98
|
+
"Content-Type": "application/json",
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
// 2. 投放卡片
|
|
102
|
+
const deliverBody = buildDeliverBody(cardInstanceId, target, String(config.clientId ?? ""));
|
|
103
|
+
const deliverResp = await dingtalkHttp.post(`${DINGTALK_API}/v1.0/card/instances/deliver`, deliverBody, {
|
|
104
|
+
headers: {
|
|
105
|
+
"x-acs-dingtalk-access-token": token,
|
|
106
|
+
"Content-Type": "application/json",
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
// 记录 token 过期时间(钉钉 token 有效期 2 小时)
|
|
110
|
+
const tokenExpireTime = Date.now() + 2 * 60 * 60 * 1000;
|
|
111
|
+
return { cardInstanceId, accessToken: token, tokenExpireTime, inputingStarted: false };
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
log?.error?.(`[DingTalk][AICard] 创建卡片失败 (${targetDesc}): ${err.message}`);
|
|
115
|
+
if (err.response) {
|
|
116
|
+
log?.error?.(`[DingTalk][AICard] 错误响应:status=${err.response.status}`);
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* 确保 Token 有效(自动刷新过期的 Token)
|
|
123
|
+
*/
|
|
124
|
+
async function ensureValidToken(card, config) {
|
|
125
|
+
// 如果 token 即将过期(提前 5 分钟刷新)
|
|
126
|
+
if (Date.now() > card.tokenExpireTime - 5 * 60 * 1000) {
|
|
127
|
+
const newToken = await getAccessToken(config);
|
|
128
|
+
card.accessToken = newToken;
|
|
129
|
+
card.tokenExpireTime = Date.now() + 2 * 60 * 60 * 1000;
|
|
130
|
+
}
|
|
131
|
+
return card.accessToken;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 流式更新 AI Card 内容
|
|
135
|
+
*/
|
|
136
|
+
export async function streamAICard(card, content, finished = false, config, log) {
|
|
137
|
+
// 确保 token 有效
|
|
138
|
+
if (config) {
|
|
139
|
+
await ensureValidToken(card, config);
|
|
140
|
+
}
|
|
141
|
+
if (!card.inputingStarted) {
|
|
142
|
+
const statusBody = {
|
|
143
|
+
outTrackId: card.cardInstanceId,
|
|
144
|
+
cardData: {
|
|
145
|
+
cardParamMap: {
|
|
146
|
+
flowStatus: AICardStatus.INPUTING,
|
|
147
|
+
msgContent: content,
|
|
148
|
+
staticMsgContent: "",
|
|
149
|
+
sys_full_json_obj: JSON.stringify({
|
|
150
|
+
order: ["msgContent"],
|
|
151
|
+
}),
|
|
152
|
+
config: JSON.stringify({ autoLayout: true }),
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
try {
|
|
157
|
+
const statusResp = await dingtalkHttp.put(`${DINGTALK_API}/v1.0/card/instances`, statusBody, {
|
|
158
|
+
headers: {
|
|
159
|
+
"x-acs-dingtalk-access-token": card.accessToken,
|
|
160
|
+
"Content-Type": "application/json",
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
log?.info?.(`[DingTalk][AICard] INPUTING 响应:status=${statusResp.status}`);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
log?.error?.(`[DingTalk][AICard] INPUTING 切换失败:${err.message}`);
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
card.inputingStarted = true;
|
|
170
|
+
}
|
|
171
|
+
const fixedContent = ensureTableBlankLines(content);
|
|
172
|
+
const body = {
|
|
173
|
+
outTrackId: card.cardInstanceId,
|
|
174
|
+
guid: `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
175
|
+
key: "msgContent",
|
|
176
|
+
content: fixedContent,
|
|
177
|
+
isFull: true,
|
|
178
|
+
isFinalize: finished,
|
|
179
|
+
isError: false,
|
|
180
|
+
};
|
|
181
|
+
log?.info?.(`[DingTalk][AICard] PUT /v1.0/card/streaming contentLen=${content.length} isFinalize=${finished}`);
|
|
182
|
+
try {
|
|
183
|
+
const streamResp = await dingtalkHttp.put(`${DINGTALK_API}/v1.0/card/streaming`, body, {
|
|
184
|
+
headers: {
|
|
185
|
+
"x-acs-dingtalk-access-token": card.accessToken,
|
|
186
|
+
"Content-Type": "application/json",
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
log?.info?.(`[DingTalk][AICard] streaming 响应:status=${streamResp.status}`);
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
log?.error?.(`[DingTalk][AICard] streaming 更新失败:${err.message}`);
|
|
193
|
+
throw err;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* 完成 AI Card
|
|
198
|
+
*/
|
|
199
|
+
export async function finishAICard(card, content, config, log) {
|
|
200
|
+
// 确保 token 有效
|
|
201
|
+
if (config) {
|
|
202
|
+
await ensureValidToken(card, config);
|
|
203
|
+
}
|
|
204
|
+
const fixedContent = ensureTableBlankLines(content);
|
|
205
|
+
log?.info?.(`[DingTalk][AICard] 开始 finish,最终内容长度=${fixedContent.length}`);
|
|
206
|
+
await streamAICard(card, fixedContent, true, log);
|
|
207
|
+
const body = {
|
|
208
|
+
outTrackId: card.cardInstanceId,
|
|
209
|
+
cardData: {
|
|
210
|
+
cardParamMap: {
|
|
211
|
+
flowStatus: AICardStatus.FINISHED,
|
|
212
|
+
msgContent: fixedContent,
|
|
213
|
+
staticMsgContent: "",
|
|
214
|
+
sys_full_json_obj: JSON.stringify({
|
|
215
|
+
order: ["msgContent"],
|
|
216
|
+
}),
|
|
217
|
+
config: JSON.stringify({ autoLayout: true }),
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
cardUpdateOptions: { updateCardDataByKey: true },
|
|
221
|
+
};
|
|
222
|
+
try {
|
|
223
|
+
const finishResp = await dingtalkHttp.put(`${DINGTALK_API}/v1.0/card/instances`, body, {
|
|
224
|
+
headers: {
|
|
225
|
+
"x-acs-dingtalk-access-token": card.accessToken,
|
|
226
|
+
"Content-Type": "application/json",
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
log?.info?.(`[DingTalk][AICard] FINISHED 响应:status=${finishResp.status}`);
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
log?.error?.(`[DingTalk][AICard] FINISHED 更新失败:${err.message}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* 支持 AI Card 创建、流式更新、完成
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { DingtalkConfig } from "../../types/index.
|
|
7
|
-
import { DINGTALK_API, getAccessToken } from "../../utils/token.
|
|
8
|
-
import { dingtalkHttp } from "../../utils/http-client.
|
|
6
|
+
import type { DingtalkConfig } from "../../types/index.js";
|
|
7
|
+
import { DINGTALK_API, getAccessToken } from "../../utils/token.js";
|
|
8
|
+
import { dingtalkHttp } from "../../utils/http-client.js";
|
|
9
9
|
|
|
10
10
|
// ============ 常量 ============
|
|
11
11
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 消息发送模块统一导出
|
|
3
|
+
*/
|
|
4
|
+
export * from './send.js';
|
|
5
|
+
export * from './card.js';
|
|
6
|
+
// 兼容旧实现(`src/services/messaging.ts`)中仍被外部调用的 API。
|
|
7
|
+
// 注意:这里只显式导出函数,避免与 `send.ts/card.ts` 的类型/常量命名冲突。
|
|
8
|
+
export { sendMessage, sendProactive, sendToUser, sendToGroup, sendTextToDingTalk, sendMediaToDingTalk, } from '../messaging.js';
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* 消息发送模块统一导出
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export * from './send.
|
|
6
|
-
export * from './card.
|
|
5
|
+
export * from './send.js';
|
|
6
|
+
export * from './card.js';
|
|
7
7
|
|
|
8
8
|
// 兼容旧实现(`src/services/messaging.ts`)中仍被外部调用的 API。
|
|
9
9
|
// 注意:这里只显式导出函数,避免与 `send.ts/card.ts` 的类型/常量命名冲突。
|
|
@@ -14,4 +14,4 @@ export {
|
|
|
14
14
|
sendToGroup,
|
|
15
15
|
sendTextToDingTalk,
|
|
16
16
|
sendMediaToDingTalk,
|
|
17
|
-
} from '../messaging.
|
|
17
|
+
} from '../messaging.js';
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 消息发送基础模块
|
|
3
|
+
* 支持 Markdown、文本、链接等消息类型
|
|
4
|
+
*/
|
|
5
|
+
import { getAccessToken } from '../../utils/token.js';
|
|
6
|
+
import { dingtalkHttp } from '../../utils/http-client.js';
|
|
7
|
+
/**
|
|
8
|
+
* 发送 Markdown 消息
|
|
9
|
+
*/
|
|
10
|
+
export async function sendMarkdownMessage(config, sessionWebhook, title, markdown, options = {}) {
|
|
11
|
+
const token = await getAccessToken(config);
|
|
12
|
+
let text = markdown;
|
|
13
|
+
if (options.atUserId)
|
|
14
|
+
text = `${text} @${options.atUserId}`;
|
|
15
|
+
const body = {
|
|
16
|
+
msgtype: 'markdown',
|
|
17
|
+
markdown: {
|
|
18
|
+
title,
|
|
19
|
+
text: text,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
if (options.atUserId) {
|
|
23
|
+
body.at = {
|
|
24
|
+
userIds: [options.atUserId],
|
|
25
|
+
isAtAll: false,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const resp = await dingtalkHttp.post(sessionWebhook, body, {
|
|
29
|
+
headers: {
|
|
30
|
+
'x-acs-dingtalk-access-token': token,
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
return resp.data;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 发送文本消息
|
|
38
|
+
*/
|
|
39
|
+
export async function sendTextMessage(config, sessionWebhook, content, options = {}) {
|
|
40
|
+
const token = await getAccessToken(config);
|
|
41
|
+
let text = content;
|
|
42
|
+
if (options.atUserId)
|
|
43
|
+
text = `${text} @${options.atUserId}`;
|
|
44
|
+
const body = {
|
|
45
|
+
msgtype: 'text',
|
|
46
|
+
text: {
|
|
47
|
+
content: text,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
if (options.atUserId) {
|
|
51
|
+
body.at = {
|
|
52
|
+
userIds: [options.atUserId],
|
|
53
|
+
isAtAll: false,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const resp = await dingtalkHttp.post(sessionWebhook, body, {
|
|
57
|
+
headers: {
|
|
58
|
+
'x-acs-dingtalk-access-token': token,
|
|
59
|
+
'Content-Type': 'application/json',
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
return resp.data;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 发送链接消息
|
|
66
|
+
*/
|
|
67
|
+
export async function sendLinkMessage(config, sessionWebhook, params) {
|
|
68
|
+
const token = await getAccessToken(config);
|
|
69
|
+
const body = {
|
|
70
|
+
msgtype: 'link',
|
|
71
|
+
link: {
|
|
72
|
+
title: params.title,
|
|
73
|
+
text: params.text,
|
|
74
|
+
picUrl: params.picUrl,
|
|
75
|
+
messageUrl: params.messageUrl,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
const resp = await dingtalkHttp.post(sessionWebhook, body, {
|
|
79
|
+
headers: {
|
|
80
|
+
'x-acs-dingtalk-access-token': token,
|
|
81
|
+
'Content-Type': 'application/json',
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
return resp.data;
|
|
85
|
+
}
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* 支持 Markdown、文本、链接等消息类型
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { DingtalkConfig } from '../../types/index.
|
|
7
|
-
import { DINGTALK_API, getAccessToken } from '../../utils/token.
|
|
8
|
-
import { dingtalkHttp } from '../../utils/http-client.
|
|
6
|
+
import type { DingtalkConfig } from '../../types/index.js';
|
|
7
|
+
import { DINGTALK_API, getAccessToken } from '../../utils/token.js';
|
|
8
|
+
import { dingtalkHttp } from '../../utils/http-client.js';
|
|
9
9
|
|
|
10
10
|
/** 消息类型枚举 */
|
|
11
11
|
export type DingTalkMsgType = 'text' | 'markdown' | 'link' | 'actionCard' | 'image';
|