@zhin.js/adapter-dingtalk 1.1.15 → 1.1.17
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 +46 -0
- package/agents/dingtalk/messages.js +32 -0
- package/agents/dingtalk/messages.ts +32 -0
- package/agents/dingtalk/skills/dingtalk/tools/create_chat/index.js +2 -1
- package/agents/dingtalk/skills/dingtalk/tools/create_chat/index.ts +2 -1
- package/agents/dingtalk/skills/dingtalk/tools/send_work_notice/index.js +2 -1
- package/agents/dingtalk/skills/dingtalk/tools/send_work_notice/index.ts +2 -1
- package/agents/dingtalk/skills/dingtalk-group-admin/tools/add_chat_members/index.js +2 -1
- package/agents/dingtalk/skills/dingtalk-group-admin/tools/add_chat_members/index.ts +2 -1
- package/agents/dingtalk/skills/dingtalk-group-admin/tools/update_chat/index.js +2 -1
- package/agents/dingtalk/skills/dingtalk-group-admin/tools/update_chat/index.ts +2 -1
- package/commands/dingtalk/endpoint/definition.js +3 -3
- package/commands/dingtalk/endpoint/definition.ts +3 -3
- package/lib/cards.d.ts +2 -1
- package/lib/cards.js +3 -2
- package/lib/endpoint.d.ts +1 -1
- package/lib/endpoint.js +3 -3
- package/lib/i18n.d.ts +1 -0
- package/lib/i18n.js +6 -0
- package/lib/protocol.d.ts +2 -5
- package/lib/protocol.js +6 -5
- package/package.json +16 -16
- package/schema.json +41 -6
- package/src/cards.ts +4 -2
- package/src/endpoint.ts +3 -3
- package/src/i18n.ts +7 -0
- package/src/protocol.ts +7 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# @zhin.js/adapter-dingtalk
|
|
2
2
|
|
|
3
|
+
## 1.1.17
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [f67883e]
|
|
8
|
+
- Updated dependencies [e23cabc]
|
|
9
|
+
- Updated dependencies [4d728c0]
|
|
10
|
+
- Updated dependencies [eb8feeb]
|
|
11
|
+
- Updated dependencies [24a2b71]
|
|
12
|
+
- Updated dependencies [d0e1fac]
|
|
13
|
+
- Updated dependencies [1ca669a]
|
|
14
|
+
- Updated dependencies [3771f7a]
|
|
15
|
+
- Updated dependencies [4f96ad5]
|
|
16
|
+
- @zhin.js/core@1.1.52
|
|
17
|
+
- @zhin.js/adapter@1.1.24
|
|
18
|
+
- @zhin.js/tool@1.1.11
|
|
19
|
+
- @zhin.js/host-http@1.1.11
|
|
20
|
+
- @zhin.js/im-contract@1.1.8
|
|
21
|
+
|
|
22
|
+
## 1.1.16
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- 53bc6a0: 适配器配置标签、端点命令和工具反馈支持中英文;复用当前操作语言,保持协议值、用户内容、身份、权限与发送链路不变。Sandbox 展示使用项目语言,QQ 绑定与交互状态保留完整会话身份。
|
|
27
|
+
- Updated dependencies [334e92d]
|
|
28
|
+
- Updated dependencies [f3b7c9f]
|
|
29
|
+
- Updated dependencies [658a5fb]
|
|
30
|
+
- Updated dependencies [144a17b]
|
|
31
|
+
- Updated dependencies [18296cf]
|
|
32
|
+
- Updated dependencies [08e64de]
|
|
33
|
+
- Updated dependencies [9cade30]
|
|
34
|
+
- Updated dependencies [53bc6a0]
|
|
35
|
+
- Updated dependencies [7595136]
|
|
36
|
+
- Updated dependencies [66b6404]
|
|
37
|
+
- Updated dependencies [adb4af2]
|
|
38
|
+
- Updated dependencies [69f9ef6]
|
|
39
|
+
- Updated dependencies [17d1301]
|
|
40
|
+
- @zhin.js/adapter@1.1.23
|
|
41
|
+
- @zhin.js/host-http@1.1.10
|
|
42
|
+
- @zhin.js/core@1.1.51
|
|
43
|
+
- @zhin.js/tool@1.1.10
|
|
44
|
+
- @zhin.js/skill@1.1.10
|
|
45
|
+
- @zhin.js/im-contract@1.1.7
|
|
46
|
+
- @zhin.js/agent-feature@1.1.10
|
|
47
|
+
- @zhin.js/feature-kit@1.1.9
|
|
48
|
+
|
|
3
49
|
## 1.1.15
|
|
4
50
|
|
|
5
51
|
### Patch Changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { createMessages } from 'zhin.js/i18n';
|
|
3
|
+
export const messages = createMessages({
|
|
4
|
+
"createFailed": {
|
|
5
|
+
"zh-CN": "创建失败",
|
|
6
|
+
"en": "Creation failed"
|
|
7
|
+
},
|
|
8
|
+
"chatCreated": {
|
|
9
|
+
"zh-CN": "群聊创建成功: {p0}",
|
|
10
|
+
"en": "Group chat created: {p0}"
|
|
11
|
+
},
|
|
12
|
+
"sendFailed": {
|
|
13
|
+
"zh-CN": "发送失败",
|
|
14
|
+
"en": "Delivery failed"
|
|
15
|
+
},
|
|
16
|
+
"workNoticeSent": {
|
|
17
|
+
"zh-CN": "工作通知已发送",
|
|
18
|
+
"en": "Work notification sent"
|
|
19
|
+
},
|
|
20
|
+
"addFailed": {
|
|
21
|
+
"zh-CN": "添加失败",
|
|
22
|
+
"en": "Could not add members"
|
|
23
|
+
},
|
|
24
|
+
"membersAdded": {
|
|
25
|
+
"zh-CN": "成员添加成功",
|
|
26
|
+
"en": "Members added"
|
|
27
|
+
},
|
|
28
|
+
"chatUpdated": {
|
|
29
|
+
"zh-CN": "群聊设置已更新",
|
|
30
|
+
"en": "Group chat settings updated"
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createMessages } from 'zhin.js/i18n';
|
|
2
|
+
|
|
3
|
+
export const messages = createMessages({
|
|
4
|
+
"createFailed": {
|
|
5
|
+
"zh-CN": "创建失败",
|
|
6
|
+
"en": "Creation failed"
|
|
7
|
+
},
|
|
8
|
+
"chatCreated": {
|
|
9
|
+
"zh-CN": "群聊创建成功: {p0}",
|
|
10
|
+
"en": "Group chat created: {p0}"
|
|
11
|
+
},
|
|
12
|
+
"sendFailed": {
|
|
13
|
+
"zh-CN": "发送失败",
|
|
14
|
+
"en": "Delivery failed"
|
|
15
|
+
},
|
|
16
|
+
"workNoticeSent": {
|
|
17
|
+
"zh-CN": "工作通知已发送",
|
|
18
|
+
"en": "Work notification sent"
|
|
19
|
+
},
|
|
20
|
+
"addFailed": {
|
|
21
|
+
"zh-CN": "添加失败",
|
|
22
|
+
"en": "Could not add members"
|
|
23
|
+
},
|
|
24
|
+
"membersAdded": {
|
|
25
|
+
"zh-CN": "成员添加成功",
|
|
26
|
+
"en": "Members added"
|
|
27
|
+
},
|
|
28
|
+
"chatUpdated": {
|
|
29
|
+
"zh-CN": "群聊设置已更新",
|
|
30
|
+
"en": "Group chat settings updated"
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { messages as t } from "../../../../messages.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
import { platformPermission } from '@zhin.js/permission';
|
|
@@ -15,6 +16,6 @@ export default defineAgentTool({
|
|
|
15
16
|
async execute({ name, owner, members }, context) {
|
|
16
17
|
const endpoint = context.$client;
|
|
17
18
|
const chatId = await endpoint.createChat(name, owner, members.split(','));
|
|
18
|
-
return { success: !!chatId, chat_id: chatId, message: chatId ?
|
|
19
|
+
return { success: !!chatId, chat_id: chatId, message: chatId ? t(context.locale, 'chatCreated', { p0: chatId }) : t(context.locale, 'createFailed') };
|
|
19
20
|
},
|
|
20
21
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages as t } from "../../../../messages.js";
|
|
1
2
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
import { platformPermission } from '@zhin.js/permission';
|
|
@@ -15,7 +16,7 @@ export default defineAgentTool<{ name: string; owner: string; members: string }>
|
|
|
15
16
|
async execute({ name, owner, members }: { name: string; owner: string; members: string }, context) {
|
|
16
17
|
const endpoint = context.$client;
|
|
17
18
|
const chatId = await endpoint.createChat(name, owner, members.split(','));
|
|
18
|
-
return { success: !!chatId, chat_id: chatId, message: chatId ?
|
|
19
|
+
return { success: !!chatId, chat_id: chatId, message: chatId ? t(context.locale, 'chatCreated', { p0: chatId }) : t(context.locale, 'createFailed') };
|
|
19
20
|
},
|
|
20
21
|
});
|
|
21
22
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { messages as t } from "../../../../messages.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
export default defineAgentTool({
|
|
@@ -13,6 +14,6 @@ export default defineAgentTool({
|
|
|
13
14
|
const endpoint = context.$client;
|
|
14
15
|
const msgContent = { msgtype: 'text', text: { content } };
|
|
15
16
|
const success = await endpoint.sendWorkNotice(user_ids.split(','), msgContent);
|
|
16
|
-
return { success, message: success ? '
|
|
17
|
+
return { success, message: success ? t(context.locale, 'workNoticeSent') : t(context.locale, 'sendFailed') };
|
|
17
18
|
},
|
|
18
19
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages as t } from "../../../../messages.js";
|
|
1
2
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
export default defineAgentTool<{ user_ids: string; content: string }>({
|
|
@@ -12,7 +13,7 @@ export default defineAgentTool<{ user_ids: string; content: string }>({
|
|
|
12
13
|
const endpoint = context.$client;
|
|
13
14
|
const msgContent = { msgtype: 'text', text: { content } };
|
|
14
15
|
const success = await endpoint.sendWorkNotice(user_ids.split(','), msgContent);
|
|
15
|
-
return { success, message: success ? '
|
|
16
|
+
return { success, message: success ? t(context.locale, 'workNoticeSent') : t(context.locale, 'sendFailed') };
|
|
16
17
|
},
|
|
17
18
|
});
|
|
18
19
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { messages as t } from "../../../../messages.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
import { platformPermission } from '@zhin.js/permission';
|
|
@@ -15,6 +16,6 @@ export default defineAgentTool({
|
|
|
15
16
|
async execute({ chat_id, user_ids }, context) {
|
|
16
17
|
const endpoint = context.$client;
|
|
17
18
|
const success = await endpoint.updateChat(chat_id, { add_useridlist: user_ids.split(',') });
|
|
18
|
-
return { success, message: success ? '
|
|
19
|
+
return { success, message: success ? t(context.locale, 'membersAdded') : t(context.locale, 'addFailed') };
|
|
19
20
|
},
|
|
20
21
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages as t } from "../../../../messages.js";
|
|
1
2
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
import { platformPermission } from '@zhin.js/permission';
|
|
@@ -15,7 +16,7 @@ export default defineAgentTool<{ chat_id: string; user_ids: string }>({
|
|
|
15
16
|
async execute({ chat_id, user_ids }: { chat_id: string; user_ids: string }, context) {
|
|
16
17
|
const endpoint = context.$client;
|
|
17
18
|
const success = await endpoint.updateChat(chat_id, { add_useridlist: user_ids.split(',') });
|
|
18
|
-
return { success, message: success ? '
|
|
19
|
+
return { success, message: success ? t(context.locale, 'membersAdded') : t(context.locale, 'addFailed') };
|
|
19
20
|
},
|
|
20
21
|
});
|
|
21
22
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { messages as t } from "../../../../messages.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
export default defineAgentTool({
|
|
@@ -25,6 +26,6 @@ export default defineAgentTool({
|
|
|
25
26
|
if (remove_members)
|
|
26
27
|
options.del_useridlist = remove_members.split(',').map((s) => s.trim());
|
|
27
28
|
await endpoint.updateChat(chat_id, options);
|
|
28
|
-
return { success: true, message: '
|
|
29
|
+
return { success: true, message: t(context.locale, 'chatUpdated') };
|
|
29
30
|
},
|
|
30
31
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages as t } from "../../../../messages.js";
|
|
1
2
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
export default defineAgentTool<{ chat_id: string; name?: string; owner?: string; add_members?: string; remove_members?: string }>({
|
|
@@ -20,7 +21,7 @@ export default defineAgentTool<{ chat_id: string; name?: string; owner?: string;
|
|
|
20
21
|
if (add_members) options.add_useridlist = add_members.split(',').map((s: string) => s.trim());
|
|
21
22
|
if (remove_members) options.del_useridlist = remove_members.split(',').map((s: string) => s.trim());
|
|
22
23
|
await endpoint.updateChat(chat_id, options);
|
|
23
|
-
return { success: true, message: '
|
|
24
|
+
return { success: true, message: t(context.locale, 'chatUpdated') };
|
|
24
25
|
},
|
|
25
26
|
});
|
|
26
27
|
|
|
@@ -10,9 +10,9 @@ export const dingtalkEndpointCommands = createEndpointCommands({
|
|
|
10
10
|
adapterKey: 'dingtalk',
|
|
11
11
|
adapterDisplayName: 'DingTalk',
|
|
12
12
|
fields: [
|
|
13
|
-
{ key: 'appKey', required: true, env: true, description: '钉钉 AppKey' },
|
|
14
|
-
{ key: 'appSecret', required: true, env: true, description: '钉钉 AppSecret' },
|
|
15
|
-
{ key: 'robotCode', required: true, env: true, description: '机器人 Code(出站 senderStaffId)' },
|
|
13
|
+
{ key: 'appKey', required: true, env: true, description: '钉钉 AppKey', descriptionI18n: { en: "DingTalk app key" } },
|
|
14
|
+
{ key: 'appSecret', required: true, env: true, description: '钉钉 AppSecret', descriptionI18n: { en: "DingTalk app secret" } },
|
|
15
|
+
{ key: 'robotCode', required: true, env: true, description: '机器人 Code(出站 senderStaffId)', descriptionI18n: { en: "Robot code (outbound senderStaffId)" } },
|
|
16
16
|
],
|
|
17
17
|
running: (use) => use(dingtalkRuntimeStateToken).endpoints.values(),
|
|
18
18
|
describeEntry: (entry) => `appKey: ${String(entry.appKey)}`,
|
|
@@ -10,9 +10,9 @@ export const dingtalkEndpointCommands = createEndpointCommands({
|
|
|
10
10
|
adapterKey: 'dingtalk',
|
|
11
11
|
adapterDisplayName: 'DingTalk',
|
|
12
12
|
fields: [
|
|
13
|
-
{ key: 'appKey', required: true, env: true, description: '钉钉 AppKey' },
|
|
14
|
-
{ key: 'appSecret', required: true, env: true, description: '钉钉 AppSecret' },
|
|
15
|
-
{ key: 'robotCode', required: true, env: true, description: '机器人 Code(出站 senderStaffId)' },
|
|
13
|
+
{ key: 'appKey', required: true, env: true, description: '钉钉 AppKey', descriptionI18n: { en: "DingTalk app key" } },
|
|
14
|
+
{ key: 'appSecret', required: true, env: true, description: '钉钉 AppSecret', descriptionI18n: { en: "DingTalk app secret" } },
|
|
15
|
+
{ key: 'robotCode', required: true, env: true, description: '机器人 Code(出站 senderStaffId)', descriptionI18n: { en: "Robot code (outbound senderStaffId)" } },
|
|
16
16
|
],
|
|
17
17
|
running: (use) => use(dingtalkRuntimeStateToken).endpoints.values(),
|
|
18
18
|
describeEntry: (entry) => `appKey: ${String(entry.appKey)}`,
|
package/lib/cards.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Locale } from 'zhin.js/i18n';
|
|
1
2
|
import { type ConversationRef } from '@zhin.js/im-contract';
|
|
2
3
|
import type { DingTalkMessage, ResolvedDingTalkConfig } from './protocol.js';
|
|
3
4
|
export interface DingTalkCardCallback {
|
|
@@ -24,7 +25,7 @@ export declare class DingTalkCards {
|
|
|
24
25
|
#private;
|
|
25
26
|
remember(conversation: ConversationRef, event: DingTalkMessage): void;
|
|
26
27
|
clear(): void;
|
|
27
|
-
prepare(config: ResolvedDingTalkConfig, conversation: ConversationRef, payload: unknown): {
|
|
28
|
+
prepare(config: ResolvedDingTalkConfig, conversation: ConversationRef, payload: unknown, locale?: Locale): {
|
|
28
29
|
id: `${string}-${string}-${string}-${string}-${string}`;
|
|
29
30
|
record: CardRecord;
|
|
30
31
|
epoch: number;
|
package/lib/cards.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages as t } from './i18n.js';
|
|
1
2
|
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
3
4
|
/** Endpoint-owned, bounded correlation. Cards from old generations deliberately cannot dispatch. */
|
|
@@ -12,7 +13,7 @@ export class DingTalkCards {
|
|
|
12
13
|
this.#contexts.delete(this.#contexts.keys().next().value);
|
|
13
14
|
}
|
|
14
15
|
clear() { this.#epoch++; this.#records.clear(); this.#contexts.clear(); }
|
|
15
|
-
prepare(config, conversation, payload) {
|
|
16
|
+
prepare(config, conversation, payload, locale) {
|
|
16
17
|
const segments = Array.isArray(payload) ? payload : [payload];
|
|
17
18
|
const keyboard = segments.filter(item => item && typeof item === 'object' && item.type === 'keyboard');
|
|
18
19
|
if (!keyboard.length)
|
|
@@ -42,7 +43,7 @@ export class DingTalkCards {
|
|
|
42
43
|
return String(item.data?.text ?? item.data?.content ?? '');
|
|
43
44
|
throw new EndpointDeliveryError('invalid_payload', 'DingTalk interactive cards accept text/markdown and keyboard only', 'not_sent');
|
|
44
45
|
}).join('');
|
|
45
|
-
const cardParamMap = { title: '
|
|
46
|
+
const cardParamMap = { title: t(locale, 'message'), text };
|
|
46
47
|
for (let i = 0; i < count; i++) {
|
|
47
48
|
cardParamMap[`button${i}_label`] = buttons[i]?.label ?? '';
|
|
48
49
|
cardParamMap[`button${i}_payload`] = buttons[i]?.payload ?? '';
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -70,7 +70,7 @@ export declare class DingTalkEndpoint extends Endpoint<DingTalkClient> {
|
|
|
70
70
|
open(): void;
|
|
71
71
|
close(): void;
|
|
72
72
|
stop(): Promise<void>;
|
|
73
|
-
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
73
|
+
send({ conversation, payload, locale }: EndpointSendRequest): Promise<string>;
|
|
74
74
|
admitCard(event: DingTalkCardCallback, id: string): Promise<void>;
|
|
75
75
|
/** Test / internal: admit a parsed event when open (non-webhook path). */
|
|
76
76
|
admit(event: DingTalkEvent | DingTalkMessage): Promise<void>;
|
package/lib/endpoint.js
CHANGED
|
@@ -105,9 +105,9 @@ export class DingTalkEndpoint extends Endpoint {
|
|
|
105
105
|
this.#started = false;
|
|
106
106
|
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
107
107
|
}
|
|
108
|
-
async send({ conversation, payload }) {
|
|
108
|
+
async send({ conversation, payload, locale }) {
|
|
109
109
|
try {
|
|
110
|
-
const card = this.#cards.prepare(this.config, conversation, payload);
|
|
110
|
+
const card = this.#cards.prepare(this.config, conversation, payload, locale);
|
|
111
111
|
if (card) {
|
|
112
112
|
let stage = 'token';
|
|
113
113
|
let httpStatus;
|
|
@@ -151,7 +151,7 @@ export class DingTalkEndpoint extends Endpoint {
|
|
|
151
151
|
throw error;
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
|
-
const content = formatOutboundBody(payload);
|
|
154
|
+
const content = formatOutboundBody(payload, locale);
|
|
155
155
|
const session = this.#sessionWebhooks.get(conversation.id);
|
|
156
156
|
const sessionWebhook = session && session.expiresAt > Date.now() ? session.url : undefined;
|
|
157
157
|
if (session && !sessionWebhook)
|
package/lib/i18n.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const messages: (locale: import("zhin.js").Locale | undefined, key: "openLink" | "link" | "message", values?: import("zhin.js").MessageValues) => string;
|
package/lib/i18n.js
ADDED
package/lib/protocol.d.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* DingTalk protocol helpers — no legacy Adapter/Endpoint / segment-mapper.
|
|
3
|
-
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
4
|
-
*/
|
|
1
|
+
import type { Locale } from 'zhin.js/i18n';
|
|
5
2
|
import type { IncomingMessage } from 'node:http';
|
|
6
3
|
import { type ConversationRef } from '@zhin.js/im-contract';
|
|
7
4
|
/** One endpoint config after AdapterIndex expands `plugins.<instanceKey>.endpoints`. */
|
|
@@ -131,7 +128,7 @@ export declare function verifySignature(appSecret: string, timestamp: string, si
|
|
|
131
128
|
* 钉钉机器人图片使用官方 Markdown HTTP(S) 图片链接,保留同载荷文本和图片顺序。
|
|
132
129
|
* 本地媒体上传、原生引用、音频/视频/文件未接线,在请求前明确拒绝。
|
|
133
130
|
*/
|
|
134
|
-
export declare function formatOutboundBody(payload: unknown): DingTalkSendBody;
|
|
131
|
+
export declare function formatOutboundBody(payload: unknown, locale?: Locale): DingTalkSendBody;
|
|
135
132
|
export declare function headerValue(headers: IncomingMessage['headers'], name: string): string;
|
|
136
133
|
export declare function readTextBody(request: IncomingMessage, options?: {
|
|
137
134
|
readonly limit?: number;
|
package/lib/protocol.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages as t } from './i18n.js';
|
|
1
2
|
/**
|
|
2
3
|
* DingTalk protocol helpers — no legacy Adapter/Endpoint / segment-mapper.
|
|
3
4
|
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
@@ -143,7 +144,7 @@ export function verifySignature(appSecret, timestamp, sign) {
|
|
|
143
144
|
* 钉钉机器人图片使用官方 Markdown HTTP(S) 图片链接,保留同载荷文本和图片顺序。
|
|
144
145
|
* 本地媒体上传、原生引用、音频/视频/文件未接线,在请求前明确拒绝。
|
|
145
146
|
*/
|
|
146
|
-
export function formatOutboundBody(payload) {
|
|
147
|
+
export function formatOutboundBody(payload, locale) {
|
|
147
148
|
if (typeof payload === 'string') {
|
|
148
149
|
return { msgtype: 'text', text: { content: payload } };
|
|
149
150
|
}
|
|
@@ -199,7 +200,7 @@ export function formatOutboundBody(payload) {
|
|
|
199
200
|
throw new EndpointDeliveryError('invalid_payload', 'DingTalk share requires an HTTP(S) URL', 'not_sent');
|
|
200
201
|
if (media)
|
|
201
202
|
throw new EndpointDeliveryError('invalid_payload', 'DingTalk accepts one link card per message', 'not_sent');
|
|
202
|
-
media = { msgtype: 'link', link: { title: String(data.title ?? '
|
|
203
|
+
media = { msgtype: 'link', link: { title: String(data.title ?? t(locale, 'link')), text: String(data.description ?? data.content ?? ''), messageUrl: url.toString(), ...(typeof data.image === 'string' ? { picUrl: data.image } : {}) } };
|
|
203
204
|
break;
|
|
204
205
|
}
|
|
205
206
|
case 'keyboard':
|
|
@@ -216,7 +217,7 @@ export function formatOutboundBody(payload) {
|
|
|
216
217
|
}
|
|
217
218
|
if (!['https:', 'http:'].includes(url.protocol))
|
|
218
219
|
throw new EndpointDeliveryError('invalid_payload', 'DingTalk image requires an HTTP(S) URL', 'not_sent');
|
|
219
|
-
markdownTitle ??= '
|
|
220
|
+
markdownTitle ??= t(locale, 'message');
|
|
220
221
|
textParts.push(`\n\s]/g, character => encodeURIComponent(character))})\n`);
|
|
221
222
|
break;
|
|
222
223
|
}
|
|
@@ -227,7 +228,7 @@ export function formatOutboundBody(payload) {
|
|
|
227
228
|
case 'file':
|
|
228
229
|
throw new EndpointDeliveryError('unsupported_operation', 'DingTalk audio/video/file delivery is not implemented', 'not_sent');
|
|
229
230
|
case 'markdown':
|
|
230
|
-
markdownTitle ??= String(data.title || '
|
|
231
|
+
markdownTitle ??= String(data.title || t(locale, 'message'));
|
|
231
232
|
textParts.push(String(data.content ?? data.text ?? ''));
|
|
232
233
|
break;
|
|
233
234
|
case 'link':
|
|
@@ -235,7 +236,7 @@ export function formatOutboundBody(payload) {
|
|
|
235
236
|
media = {
|
|
236
237
|
msgtype: 'link',
|
|
237
238
|
link: {
|
|
238
|
-
title: String(data.title || '
|
|
239
|
+
title: String(data.title || t(locale, 'link')),
|
|
239
240
|
text: String(data.text ?? data.content ?? ''),
|
|
240
241
|
messageUrl: typeof data.url === 'string' ? data.url : undefined,
|
|
241
242
|
picUrl: typeof data.picUrl === 'string' ? data.picUrl : undefined,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-dingtalk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.17",
|
|
4
4
|
"description": "Zhin.js DingTalk (钉钉) adapter for Plugin Runtime (HTTP webhook)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -33,24 +33,24 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"ws": "^8.18.3",
|
|
36
|
-
"@zhin.js/adapter": "1.1.
|
|
37
|
-
"@zhin.js/agent-feature": "1.1.
|
|
38
|
-
"@zhin.js/core": "1.1.
|
|
39
|
-
"@zhin.js/feature-kit": "1.1.
|
|
40
|
-
"@zhin.js/host-http": "1.1.
|
|
41
|
-
"@zhin.js/im-contract": "1.1.
|
|
36
|
+
"@zhin.js/adapter": "1.1.24",
|
|
37
|
+
"@zhin.js/agent-feature": "1.1.10",
|
|
38
|
+
"@zhin.js/core": "1.1.52",
|
|
39
|
+
"@zhin.js/feature-kit": "1.1.9",
|
|
40
|
+
"@zhin.js/host-http": "1.1.11",
|
|
41
|
+
"@zhin.js/im-contract": "1.1.8",
|
|
42
42
|
"@zhin.js/logger": "1.1.2",
|
|
43
|
-
"@zhin.js/skill": "1.1.
|
|
44
|
-
"@zhin.js/tool": "1.1.
|
|
43
|
+
"@zhin.js/skill": "1.1.10",
|
|
44
|
+
"@zhin.js/tool": "1.1.11"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"zod": "^4.0.0",
|
|
48
|
-
"@zhin.js/adapter": "^1.1.
|
|
49
|
-
"@zhin.js/command": "^1.1.
|
|
50
|
-
"@zhin.js/core": "^1.1.
|
|
51
|
-
"@zhin.js/host-http": "^1.1.
|
|
52
|
-
"@zhin.js/permission": "^1.1.
|
|
53
|
-
"zhin.js": "^1.1.
|
|
48
|
+
"@zhin.js/adapter": "^1.1.24",
|
|
49
|
+
"@zhin.js/command": "^1.1.11",
|
|
50
|
+
"@zhin.js/core": "^1.1.52",
|
|
51
|
+
"@zhin.js/host-http": "^1.1.11",
|
|
52
|
+
"@zhin.js/permission": "^1.1.9",
|
|
53
|
+
"zhin.js": "^1.1.17"
|
|
54
54
|
},
|
|
55
55
|
"peerDependenciesMeta": {
|
|
56
56
|
"@zhin.js/command": {
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"vitest": "^4.1.10",
|
|
70
70
|
"zod": "^4.4.3",
|
|
71
71
|
"@types/ws": "^8.18.1",
|
|
72
|
-
"zhin.js": "1.1.
|
|
72
|
+
"zhin.js": "1.1.17"
|
|
73
73
|
},
|
|
74
74
|
"files": [
|
|
75
75
|
"adapters",
|
package/schema.json
CHANGED
|
@@ -12,7 +12,12 @@
|
|
|
12
12
|
"string",
|
|
13
13
|
"number"
|
|
14
14
|
],
|
|
15
|
-
"description": "框架 master(DingTalk userid;AI/工具权限、endpoint 管理)。endpoints[i].master 可逐项覆盖"
|
|
15
|
+
"description": "框架 master(DingTalk userid;AI/工具权限、endpoint 管理)。endpoints[i].master 可逐项覆盖",
|
|
16
|
+
"x-i18n": {
|
|
17
|
+
"en": {
|
|
18
|
+
"description": "Framework masters (DingTalk userid; AI/tool permissions and endpoint management). endpoints[i].master can override this value."
|
|
19
|
+
}
|
|
20
|
+
}
|
|
16
21
|
},
|
|
17
22
|
"trusted": {
|
|
18
23
|
"type": "array",
|
|
@@ -23,7 +28,12 @@
|
|
|
23
28
|
],
|
|
24
29
|
"description": "Trusted DingTalk userid"
|
|
25
30
|
},
|
|
26
|
-
"description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加"
|
|
31
|
+
"description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加",
|
|
32
|
+
"x-i18n": {
|
|
33
|
+
"en": {
|
|
34
|
+
"description": "Framework trusted users, below master authority. endpoints[i].trusted can add users."
|
|
35
|
+
}
|
|
36
|
+
}
|
|
27
37
|
},
|
|
28
38
|
"endpoints": {
|
|
29
39
|
"type": "array",
|
|
@@ -38,7 +48,12 @@
|
|
|
38
48
|
"string",
|
|
39
49
|
"number"
|
|
40
50
|
],
|
|
41
|
-
"description": "本 endpoint 的框架 master(DingTalk userid);覆盖顶层 master"
|
|
51
|
+
"description": "本 endpoint 的框架 master(DingTalk userid);覆盖顶层 master",
|
|
52
|
+
"x-i18n": {
|
|
53
|
+
"en": {
|
|
54
|
+
"description": "Framework masters for this endpoint (DingTalk userid), overriding the top-level master list."
|
|
55
|
+
}
|
|
56
|
+
}
|
|
42
57
|
},
|
|
43
58
|
"trusted": {
|
|
44
59
|
"type": "array",
|
|
@@ -49,7 +64,12 @@
|
|
|
49
64
|
],
|
|
50
65
|
"description": "Trusted DingTalk userid"
|
|
51
66
|
},
|
|
52
|
-
"description": "本 endpoint 的 trusted 列表"
|
|
67
|
+
"description": "本 endpoint 的 trusted 列表",
|
|
68
|
+
"x-i18n": {
|
|
69
|
+
"en": {
|
|
70
|
+
"description": "Trusted users for this endpoint."
|
|
71
|
+
}
|
|
72
|
+
}
|
|
53
73
|
},
|
|
54
74
|
"appKey": {
|
|
55
75
|
"type": "string",
|
|
@@ -116,7 +136,12 @@
|
|
|
116
136
|
"port",
|
|
117
137
|
"serverName"
|
|
118
138
|
],
|
|
119
|
-
"description": "故障验收专用:TCP固定127.0.0.1,保留真实WSS TLS身份"
|
|
139
|
+
"description": "故障验收专用:TCP固定127.0.0.1,保留真实WSS TLS身份",
|
|
140
|
+
"x-i18n": {
|
|
141
|
+
"en": {
|
|
142
|
+
"description": "Fault-injection tests only: pin TCP to 127.0.0.1 while preserving the actual WSS TLS identity."
|
|
143
|
+
}
|
|
144
|
+
}
|
|
120
145
|
}
|
|
121
146
|
},
|
|
122
147
|
"required": [
|
|
@@ -124,12 +149,22 @@
|
|
|
124
149
|
"appKey",
|
|
125
150
|
"appSecret"
|
|
126
151
|
]
|
|
152
|
+
},
|
|
153
|
+
"x-i18n": {
|
|
154
|
+
"en": {
|
|
155
|
+
"description": "Multiple accounts: mount multiple endpoints in one plugin instance. Each entry uses the top-level fields; id is required and other fields override defaults."
|
|
156
|
+
}
|
|
127
157
|
}
|
|
128
158
|
},
|
|
129
159
|
"commandPrefix": {
|
|
130
160
|
"type": "string",
|
|
131
161
|
"default": "",
|
|
132
|
-
"description": "命令前缀(默认 '' 无前缀,任意文本按命令匹配;如 '/' 要求 / 开头)。endpoints[i] 可逐项覆盖"
|
|
162
|
+
"description": "命令前缀(默认 '' 无前缀,任意文本按命令匹配;如 '/' 要求 / 开头)。endpoints[i] 可逐项覆盖",
|
|
163
|
+
"x-i18n": {
|
|
164
|
+
"en": {
|
|
165
|
+
"description": "Command prefix. The default empty string allows unprefixed commands; '/' requires a leading slash. Each endpoints[i] can override it."
|
|
166
|
+
}
|
|
167
|
+
}
|
|
133
168
|
},
|
|
134
169
|
"mode": {
|
|
135
170
|
"type": "string",
|
package/src/cards.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Locale } from 'zhin.js/i18n';
|
|
2
|
+
import { messages as t } from './i18n.js';
|
|
1
3
|
import { randomUUID } from 'node:crypto';
|
|
2
4
|
import { EndpointDeliveryError, type ConversationRef } from '@zhin.js/im-contract';
|
|
3
5
|
import type { DingTalkMessage, ResolvedDingTalkConfig } from './protocol.js';
|
|
@@ -14,7 +16,7 @@ export class DingTalkCards {
|
|
|
14
16
|
while (this.#contexts.size > 1024) this.#contexts.delete(this.#contexts.keys().next().value!);
|
|
15
17
|
}
|
|
16
18
|
clear(): void { this.#epoch++; this.#records.clear(); this.#contexts.clear(); }
|
|
17
|
-
prepare(config: ResolvedDingTalkConfig, conversation: ConversationRef, payload: unknown) {
|
|
19
|
+
prepare(config: ResolvedDingTalkConfig, conversation: ConversationRef, payload: unknown, locale?: Locale) {
|
|
18
20
|
const segments = Array.isArray(payload) ? payload : [payload];
|
|
19
21
|
const keyboard = segments.filter(item => item && typeof item === 'object' && item.type === 'keyboard');
|
|
20
22
|
if (!keyboard.length) return undefined;
|
|
@@ -35,7 +37,7 @@ export class DingTalkCards {
|
|
|
35
37
|
if (item?.type === 'text' || item?.type === 'markdown') return String(item.data?.text ?? item.data?.content ?? '');
|
|
36
38
|
throw new EndpointDeliveryError('invalid_payload', 'DingTalk interactive cards accept text/markdown and keyboard only', 'not_sent');
|
|
37
39
|
}).join('');
|
|
38
|
-
const cardParamMap: Record<string, string> = { title: '
|
|
40
|
+
const cardParamMap: Record<string, string> = { title: t(locale, 'message'), text };
|
|
39
41
|
for (let i = 0; i < count; i++) { cardParamMap[`button${i}_label`] = buttons[i]?.label ?? ''; cardParamMap[`button${i}_payload`] = buttons[i]?.payload ?? ''; cardParamMap[`button${i}_visible`] = buttons[i] ? 'true' : 'false'; }
|
|
40
42
|
const id = randomUUID();
|
|
41
43
|
const record: CardRecord = { conversation, spaceType, spaceId, staffId: context.senderStaffId, senderId: context.senderId, corpId: context.senderCorpId, payloads: new Set(buttons.map((button: any) => button.payload)), expiresAt: Date.now() + 3_600_000 };
|
package/src/endpoint.ts
CHANGED
|
@@ -169,9 +169,9 @@ export class DingTalkEndpoint extends Endpoint<DingTalkClient> {
|
|
|
169
169
|
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
172
|
+
async send({ conversation, payload, locale }: EndpointSendRequest): Promise<string> {
|
|
173
173
|
try {
|
|
174
|
-
const card = this.#cards.prepare(this.config, conversation, payload);
|
|
174
|
+
const card = this.#cards.prepare(this.config, conversation, payload, locale);
|
|
175
175
|
if (card) {
|
|
176
176
|
let stage = 'token';
|
|
177
177
|
let httpStatus: number | undefined;
|
|
@@ -202,7 +202,7 @@ export class DingTalkEndpoint extends Endpoint<DingTalkClient> {
|
|
|
202
202
|
throw error;
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
|
-
const content = formatOutboundBody(payload);
|
|
205
|
+
const content = formatOutboundBody(payload, locale);
|
|
206
206
|
const session = this.#sessionWebhooks.get(conversation.id);
|
|
207
207
|
const sessionWebhook = session && session.expiresAt > Date.now() ? session.url : undefined;
|
|
208
208
|
if (session && !sessionWebhook) this.#sessionWebhooks.delete(conversation.id);
|
package/src/i18n.ts
ADDED
package/src/protocol.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { messages as t } from './i18n.js';
|
|
2
|
+
import type { Locale } from 'zhin.js/i18n';
|
|
1
3
|
/**
|
|
2
4
|
* DingTalk protocol helpers — no legacy Adapter/Endpoint / segment-mapper.
|
|
3
5
|
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
@@ -250,7 +252,7 @@ export function verifySignature(
|
|
|
250
252
|
* 钉钉机器人图片使用官方 Markdown HTTP(S) 图片链接,保留同载荷文本和图片顺序。
|
|
251
253
|
* 本地媒体上传、原生引用、音频/视频/文件未接线,在请求前明确拒绝。
|
|
252
254
|
*/
|
|
253
|
-
export function formatOutboundBody(payload: unknown): DingTalkSendBody {
|
|
255
|
+
export function formatOutboundBody(payload: unknown, locale?: Locale): DingTalkSendBody {
|
|
254
256
|
if (typeof payload === 'string') {
|
|
255
257
|
return { msgtype: 'text', text: { content: payload } };
|
|
256
258
|
}
|
|
@@ -303,7 +305,7 @@ export function formatOutboundBody(payload: unknown): DingTalkSendBody {
|
|
|
303
305
|
try { url = new URL(String(data.url ?? '')); } catch { throw new EndpointDeliveryError('invalid_payload', 'DingTalk share requires an HTTP(S) URL', 'not_sent'); }
|
|
304
306
|
if (!['https:', 'http:'].includes(url.protocol)) throw new EndpointDeliveryError('invalid_payload', 'DingTalk share requires an HTTP(S) URL', 'not_sent');
|
|
305
307
|
if (media) throw new EndpointDeliveryError('invalid_payload', 'DingTalk accepts one link card per message', 'not_sent');
|
|
306
|
-
media = { msgtype: 'link', link: { title: String(data.title ?? '
|
|
308
|
+
media = { msgtype: 'link', link: { title: String(data.title ?? t(locale, 'link')), text: String(data.description ?? data.content ?? ''), messageUrl: url.toString(), ...(typeof data.image === 'string' ? { picUrl: data.image } : {}) } };
|
|
307
309
|
break;
|
|
308
310
|
}
|
|
309
311
|
case 'keyboard':
|
|
@@ -314,7 +316,7 @@ export function formatOutboundBody(payload: unknown): DingTalkSendBody {
|
|
|
314
316
|
let url: URL;
|
|
315
317
|
try { url = new URL(ref.value); } catch { throw new EndpointDeliveryError('invalid_payload', 'DingTalk image requires an HTTP(S) URL', 'not_sent'); }
|
|
316
318
|
if (!['https:', 'http:'].includes(url.protocol)) throw new EndpointDeliveryError('invalid_payload', 'DingTalk image requires an HTTP(S) URL', 'not_sent');
|
|
317
|
-
markdownTitle ??= '
|
|
319
|
+
markdownTitle ??= t(locale, 'message');
|
|
318
320
|
textParts.push(`\n\s]/g, character => encodeURIComponent(character))})\n`);
|
|
319
321
|
break;
|
|
320
322
|
}
|
|
@@ -325,7 +327,7 @@ export function formatOutboundBody(payload: unknown): DingTalkSendBody {
|
|
|
325
327
|
case 'file':
|
|
326
328
|
throw new EndpointDeliveryError('unsupported_operation', 'DingTalk audio/video/file delivery is not implemented', 'not_sent');
|
|
327
329
|
case 'markdown':
|
|
328
|
-
markdownTitle ??= String(data.title || '
|
|
330
|
+
markdownTitle ??= String(data.title || t(locale, 'message'));
|
|
329
331
|
textParts.push(String(data.content ?? data.text ?? ''));
|
|
330
332
|
break;
|
|
331
333
|
case 'link':
|
|
@@ -333,7 +335,7 @@ export function formatOutboundBody(payload: unknown): DingTalkSendBody {
|
|
|
333
335
|
media = {
|
|
334
336
|
msgtype: 'link',
|
|
335
337
|
link: {
|
|
336
|
-
title: String(data.title || '
|
|
338
|
+
title: String(data.title || t(locale, 'link')),
|
|
337
339
|
text: String(data.text ?? data.content ?? ''),
|
|
338
340
|
messageUrl: typeof data.url === 'string' ? data.url : undefined,
|
|
339
341
|
picUrl: typeof data.picUrl === 'string' ? data.picUrl : undefined,
|