@dsh-overdrive/gateway 0.1.3 → 0.1.4
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/dist/adapters/dingtalk.d.ts +21 -0
- package/dist/adapters/dingtalk.js +85 -12
- package/dist/adapters/dingtalk.js.map +1 -1
- package/dist/adapters/feishu.d.ts +9 -0
- package/dist/adapters/feishu.js +65 -11
- package/dist/adapters/feishu.js.map +1 -1
- package/dist/adapters/telegram.d.ts +7 -1
- package/dist/adapters/telegram.js +17 -4
- package/dist/adapters/telegram.js.map +1 -1
- package/dist/adapters/wecom.d.ts +2 -0
- package/dist/adapters/wecom.js +12 -9
- package/dist/adapters/wecom.js.map +1 -1
- package/dist/adapters/whatsapp.d.ts +1 -1
- package/dist/adapters/whatsapp.js +9 -12
- package/dist/adapters/whatsapp.js.map +1 -1
- package/dist/asr.d.ts +19 -0
- package/dist/asr.js +63 -0
- package/dist/asr.js.map +1 -0
- package/dist/commands.d.ts +5 -0
- package/dist/commands.js +7 -0
- package/dist/commands.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +33 -1
- package/dist/index.js.map +1 -1
- package/dist/pending-buttons.d.ts +19 -0
- package/dist/pending-buttons.js +40 -0
- package/dist/pending-buttons.js.map +1 -0
- package/package.json +2 -2
- package/src/adapters/dingtalk.ts +86 -11
- package/src/adapters/feishu.ts +64 -10
- package/src/adapters/telegram.ts +22 -5
- package/src/adapters/wecom.ts +11 -9
- package/src/adapters/whatsapp.ts +9 -12
- package/src/asr.ts +83 -0
- package/src/commands.ts +8 -1
- package/src/config.ts +6 -0
- package/src/index.ts +35 -1
- package/src/pending-buttons.ts +45 -0
- package/test/adapters.dingtalk.test.ts +34 -1
- package/test/adapters.feishu.test.ts +37 -1
- package/test/asr.test.ts +77 -0
- package/test/commands.test.ts +6 -0
- package/test/pending-buttons.test.ts +61 -0
|
@@ -15,6 +15,27 @@ export declare function buildReplyBody(text: string): {
|
|
|
15
15
|
};
|
|
16
16
|
export declare function buildNumberedText(text: string, buttons: OutboundButton[]): string;
|
|
17
17
|
export declare function matchNumberedButton(text: string, buttons: OutboundButton[]): OutboundButton | undefined;
|
|
18
|
+
/** 按钮 id("approve:<reqId>" / "reject:<reqId>")→ 卡片回调载荷 JSON 字符串。 */
|
|
19
|
+
export declare function buttonCallbackData(button: OutboundButton): string;
|
|
20
|
+
/** 钉钉 actionCard 消息体(Roadmap v0.2)。按钮回调经 TOPIC_CARD 走 Stream 返回。 */
|
|
21
|
+
export declare function buildActionCard(text: string, buttons: OutboundButton[]): {
|
|
22
|
+
msgtype: 'actionCard';
|
|
23
|
+
actionCard: {
|
|
24
|
+
title: string;
|
|
25
|
+
text: string;
|
|
26
|
+
btnOrientation: string;
|
|
27
|
+
btns: Array<{
|
|
28
|
+
title: string;
|
|
29
|
+
actionURL: string;
|
|
30
|
+
}>;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* 钉钉卡片回调载荷 → 按钮 id("approve:<reqId>")。
|
|
35
|
+
* Stream 模式下回调 JSON 的字段名(cardCallbackData / params / cardActionData)在不同卡片版本有差异,
|
|
36
|
+
* 这里做多路径深度兜底解析;真机验证后可按实际字段收敛。找不到返回 null。
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseCardCallback(raw: unknown): string | null;
|
|
18
39
|
export interface DingTalkAdapterOptions {
|
|
19
40
|
clientId: string;
|
|
20
41
|
clientSecret: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PendingButtons } from '../pending-buttons.js';
|
|
2
|
+
import { DWClient, TOPIC_CARD, TOPIC_ROBOT } from 'dingtalk-stream-sdk-nodejs';
|
|
2
3
|
export function parseBotMessage(data) {
|
|
3
4
|
if (data.msgtype !== 'text' || !data.text?.content)
|
|
4
5
|
return null;
|
|
@@ -26,6 +27,58 @@ export function matchNumberedButton(text, buttons) {
|
|
|
26
27
|
return undefined;
|
|
27
28
|
return buttons[n - 1];
|
|
28
29
|
}
|
|
30
|
+
/** 按钮 id("approve:<reqId>" / "reject:<reqId>")→ 卡片回调载荷 JSON 字符串。 */
|
|
31
|
+
export function buttonCallbackData(button) {
|
|
32
|
+
const idx = button.id.indexOf(':');
|
|
33
|
+
return JSON.stringify({
|
|
34
|
+
action: idx >= 0 ? button.id.slice(0, idx) : button.id,
|
|
35
|
+
reqId: idx >= 0 ? button.id.slice(idx + 1) : '',
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/** 钉钉 actionCard 消息体(Roadmap v0.2)。按钮回调经 TOPIC_CARD 走 Stream 返回。 */
|
|
39
|
+
export function buildActionCard(text, buttons) {
|
|
40
|
+
return {
|
|
41
|
+
msgtype: 'actionCard',
|
|
42
|
+
actionCard: {
|
|
43
|
+
title: '需要批准',
|
|
44
|
+
text,
|
|
45
|
+
btnOrientation: '1',
|
|
46
|
+
btns: buttons.map((b) => ({
|
|
47
|
+
title: b.label,
|
|
48
|
+
actionURL: `dingtalk://dingtalkclient/action/openapp?cardCallbackData=${encodeURIComponent(buttonCallbackData(b))}`,
|
|
49
|
+
})),
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 钉钉卡片回调载荷 → 按钮 id("approve:<reqId>")。
|
|
55
|
+
* Stream 模式下回调 JSON 的字段名(cardCallbackData / params / cardActionData)在不同卡片版本有差异,
|
|
56
|
+
* 这里做多路径深度兜底解析;真机验证后可按实际字段收敛。找不到返回 null。
|
|
57
|
+
*/
|
|
58
|
+
export function parseCardCallback(raw) {
|
|
59
|
+
const visit = (obj, depth) => {
|
|
60
|
+
if (depth > 5 || !obj || typeof obj !== 'object')
|
|
61
|
+
return null;
|
|
62
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
63
|
+
if (typeof value === 'string' && (key === 'cardCallbackData' || key === 'params' || key === 'cardActionData')) {
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(value);
|
|
66
|
+
if ((parsed.action === 'approve' || parsed.action === 'reject') && typeof parsed.reqId === 'string' && parsed.reqId) {
|
|
67
|
+
return `${parsed.action}:${parsed.reqId}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
/* 该字段不是 JSON 载荷,继续往下找 */
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const found = visit(value, depth + 1);
|
|
75
|
+
if (found)
|
|
76
|
+
return found;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
};
|
|
80
|
+
return visit(raw, 0);
|
|
81
|
+
}
|
|
29
82
|
export class DingTalkAdapter {
|
|
30
83
|
opts;
|
|
31
84
|
id = 'dingtalk';
|
|
@@ -33,7 +86,7 @@ export class DingTalkAdapter {
|
|
|
33
86
|
connected = false;
|
|
34
87
|
messageCb;
|
|
35
88
|
replyCb;
|
|
36
|
-
pendingButtons = new
|
|
89
|
+
pendingButtons = new PendingButtons();
|
|
37
90
|
/** conversationId → 最近的 sessionWebhook(回复通道,过期由钉钉侧管理) */
|
|
38
91
|
webhooks = new Map();
|
|
39
92
|
constructor(opts) {
|
|
@@ -54,17 +107,26 @@ export class DingTalkAdapter {
|
|
|
54
107
|
if (!parsed)
|
|
55
108
|
return;
|
|
56
109
|
this.webhooks.set(parsed.chatId, parsed.sessionWebhook);
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
this.pendingButtons.delete(parsed.chatId);
|
|
62
|
-
this.replyCb?.(button.id);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
110
|
+
const button = this.pendingButtons.match(parsed.chatId, parsed.text);
|
|
111
|
+
if (button) {
|
|
112
|
+
this.replyCb?.(button.id);
|
|
113
|
+
return;
|
|
65
114
|
}
|
|
66
115
|
this.messageCb?.({ chatId: parsed.chatId, userId: parsed.userId, text: parsed.text });
|
|
67
116
|
});
|
|
117
|
+
// 原生 actionCard 按钮回调(Stream 模式,Roadmap v0.2)
|
|
118
|
+
client.registerCallbackListener(TOPIC_CARD, (msg) => {
|
|
119
|
+
let data;
|
|
120
|
+
try {
|
|
121
|
+
data = JSON.parse(msg.data);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const buttonId = parseCardCallback(data);
|
|
127
|
+
if (buttonId)
|
|
128
|
+
this.replyCb?.(buttonId);
|
|
129
|
+
});
|
|
68
130
|
await client.connect();
|
|
69
131
|
this.connected = true;
|
|
70
132
|
console.log('[dingtalk] 钉钉 Stream 已连接');
|
|
@@ -73,8 +135,19 @@ export class DingTalkAdapter {
|
|
|
73
135
|
const webhook = this.webhooks.get(chatId);
|
|
74
136
|
if (!webhook)
|
|
75
137
|
throw new Error(`钉钉会话 ${chatId} 无可用 sessionWebhook(先让用户发一条消息)`);
|
|
76
|
-
if (payload.buttons?.length)
|
|
77
|
-
this.pendingButtons.set(chatId, payload.buttons);
|
|
138
|
+
if (payload.buttons?.length) {
|
|
139
|
+
this.pendingButtons.set(chatId, payload.buttons); // 卡片之外仍支持编号回复兜底
|
|
140
|
+
const res = await fetch(webhook, {
|
|
141
|
+
method: 'POST',
|
|
142
|
+
headers: { 'content-type': 'application/json' },
|
|
143
|
+
body: JSON.stringify(buildActionCard(payload.text, payload.buttons)),
|
|
144
|
+
});
|
|
145
|
+
if (!res.ok) {
|
|
146
|
+
const body = await res.text();
|
|
147
|
+
throw new Error(`钉钉回发失败 ${res.status}: ${body.slice(0, 200)}`);
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
78
151
|
const text = buildNumberedText(payload.text, payload.buttons ?? []);
|
|
79
152
|
const res = await fetch(webhook, {
|
|
80
153
|
method: 'POST',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dingtalk.js","sourceRoot":"","sources":["../../src/adapters/dingtalk.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAqB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"dingtalk.js","sourceRoot":"","sources":["../../src/adapters/dingtalk.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,6 +17,15 @@ export interface FeishuReceivePayload {
|
|
|
17
17
|
export declare function parseFeishuTextMessage(payload: FeishuReceivePayload): NormalizedMessage | null;
|
|
18
18
|
export declare function buildNumberedText(text: string, buttons: OutboundButton[]): string;
|
|
19
19
|
export declare function matchNumberedButton(text: string, buttons: OutboundButton[]): OutboundButton | undefined;
|
|
20
|
+
/** 按钮 id("approve:<reqId>" / "reject:<reqId>")→ 卡片按钮 value。 */
|
|
21
|
+
export declare function buttonValue(button: OutboundButton): {
|
|
22
|
+
action: string;
|
|
23
|
+
reqId: string;
|
|
24
|
+
};
|
|
25
|
+
/** 交互卡片 JSON(msg_type: interactive)。原生按钮点击走 card.action.trigger 回调。 */
|
|
26
|
+
export declare function buildApprovalCard(text: string, buttons: OutboundButton[]): string;
|
|
27
|
+
/** 卡片回调 value → 按钮 id("approve:<reqId>");缺字段返回 null。 */
|
|
28
|
+
export declare function cardActionToButtonId(value: unknown): string | null;
|
|
20
29
|
export interface FeishuAdapterOptions {
|
|
21
30
|
appId: string;
|
|
22
31
|
appSecret: string;
|
package/dist/adapters/feishu.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import lark from '@larksuiteoapi/node-sdk';
|
|
2
|
+
import { PendingButtons } from '../pending-buttons.js';
|
|
2
3
|
// @larksuiteoapi/node-sdk 是 CommonJS 包(main=lib/index.js,无 "type":"module"):
|
|
3
4
|
// Node 原生 ESM 下必须 default 导入后解构(同 M2b 的 @slack/bolt 处理)。
|
|
4
5
|
const { Client, WSClient, EventDispatcher } = lark;
|
|
@@ -32,6 +33,41 @@ export function matchNumberedButton(text, buttons) {
|
|
|
32
33
|
return undefined;
|
|
33
34
|
return buttons[n - 1];
|
|
34
35
|
}
|
|
36
|
+
/** 按钮 id("approve:<reqId>" / "reject:<reqId>")→ 卡片按钮 value。 */
|
|
37
|
+
export function buttonValue(button) {
|
|
38
|
+
const idx = button.id.indexOf(':');
|
|
39
|
+
return {
|
|
40
|
+
action: idx >= 0 ? button.id.slice(0, idx) : button.id,
|
|
41
|
+
reqId: idx >= 0 ? button.id.slice(idx + 1) : '',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** 交互卡片 JSON(msg_type: interactive)。原生按钮点击走 card.action.trigger 回调。 */
|
|
45
|
+
export function buildApprovalCard(text, buttons) {
|
|
46
|
+
const actions = buttons.map((b) => ({
|
|
47
|
+
tag: 'button',
|
|
48
|
+
text: { tag: 'plain_text', content: b.label },
|
|
49
|
+
type: b.id.startsWith('approve:') ? 'primary' : 'default',
|
|
50
|
+
value: buttonValue(b),
|
|
51
|
+
}));
|
|
52
|
+
const card = {
|
|
53
|
+
config: { wide_screen_mode: true },
|
|
54
|
+
header: { title: { tag: 'plain_text', content: text.slice(0, 60) }, template: 'blue' },
|
|
55
|
+
elements: [
|
|
56
|
+
{ tag: 'div', text: { tag: 'lark_md', content: text } },
|
|
57
|
+
{ tag: 'action', actions },
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
return JSON.stringify(card);
|
|
61
|
+
}
|
|
62
|
+
/** 卡片回调 value → 按钮 id("approve:<reqId>");缺字段返回 null。 */
|
|
63
|
+
export function cardActionToButtonId(value) {
|
|
64
|
+
if (!value || typeof value !== 'object')
|
|
65
|
+
return null;
|
|
66
|
+
const { action, reqId } = value;
|
|
67
|
+
if ((action !== 'approve' && action !== 'reject') || typeof reqId !== 'string' || !reqId)
|
|
68
|
+
return null;
|
|
69
|
+
return `${action}:${reqId}`;
|
|
70
|
+
}
|
|
35
71
|
export class FeishuAdapter {
|
|
36
72
|
opts;
|
|
37
73
|
id = 'feishu';
|
|
@@ -40,7 +76,7 @@ export class FeishuAdapter {
|
|
|
40
76
|
connected = false;
|
|
41
77
|
messageCb;
|
|
42
78
|
replyCb;
|
|
43
|
-
pendingButtons = new
|
|
79
|
+
pendingButtons = new PendingButtons();
|
|
44
80
|
/** chatId → 最近一条入站消息的 message_id(send 优先 reply,缺失则 create 兜底) */
|
|
45
81
|
lastMessageIds = new Map();
|
|
46
82
|
constructor(opts) {
|
|
@@ -58,17 +94,19 @@ export class FeishuAdapter {
|
|
|
58
94
|
if (!normalized)
|
|
59
95
|
return;
|
|
60
96
|
const chatId = normalized.chatId;
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
this.pendingButtons.delete(chatId);
|
|
66
|
-
this.replyCb?.(button.id);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
97
|
+
const button = this.pendingButtons.match(chatId, normalized.text);
|
|
98
|
+
if (button) {
|
|
99
|
+
this.replyCb?.(button.id);
|
|
100
|
+
return;
|
|
69
101
|
}
|
|
70
102
|
this.messageCb?.(normalized);
|
|
71
103
|
},
|
|
104
|
+
// 原生交互卡片按钮回调 → 审批应答(Roadmap v0.2)
|
|
105
|
+
'card.action.trigger': async (data) => {
|
|
106
|
+
const buttonId = cardActionToButtonId(data?.action?.value);
|
|
107
|
+
if (buttonId)
|
|
108
|
+
this.replyCb?.(buttonId);
|
|
109
|
+
},
|
|
72
110
|
});
|
|
73
111
|
this.ws = new WSClient({
|
|
74
112
|
appId: this.opts.appId,
|
|
@@ -80,8 +118,24 @@ export class FeishuAdapter {
|
|
|
80
118
|
console.log('[feishu] 飞书长连接已建立');
|
|
81
119
|
}
|
|
82
120
|
async send(chatId, payload) {
|
|
83
|
-
if (payload.buttons?.length)
|
|
84
|
-
this.pendingButtons.set(chatId, payload.buttons);
|
|
121
|
+
if (payload.buttons?.length) {
|
|
122
|
+
this.pendingButtons.set(chatId, payload.buttons); // 卡片之外仍支持编号回复兜底
|
|
123
|
+
const content = buildApprovalCard(payload.text, payload.buttons);
|
|
124
|
+
const messageId = this.lastMessageIds.get(chatId);
|
|
125
|
+
if (messageId) {
|
|
126
|
+
await this.client.im.message.reply({
|
|
127
|
+
path: { message_id: messageId },
|
|
128
|
+
data: { msg_type: 'interactive', content },
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
await this.client.im.message.create({
|
|
133
|
+
params: { receive_id_type: 'chat_id' },
|
|
134
|
+
data: { receive_id: chatId, msg_type: 'interactive', content },
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
85
139
|
const text = buildNumberedText(payload.text, payload.buttons ?? []);
|
|
86
140
|
const content = JSON.stringify({ text });
|
|
87
141
|
const messageId = this.lastMessageIds.get(chatId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feishu.js","sourceRoot":"","sources":["../../src/adapters/feishu.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"feishu.js","sourceRoot":"","sources":["../../src/adapters/feishu.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,9 +14,11 @@ export interface RawTelegramMessage {
|
|
|
14
14
|
}>;
|
|
15
15
|
voice?: {
|
|
16
16
|
file_id?: string;
|
|
17
|
+
mime_type?: string;
|
|
17
18
|
};
|
|
18
19
|
audio?: {
|
|
19
20
|
file_id?: string;
|
|
21
|
+
mime_type?: string;
|
|
20
22
|
};
|
|
21
23
|
video?: {
|
|
22
24
|
file_id?: string;
|
|
@@ -30,6 +32,10 @@ export interface RawTelegramMessage {
|
|
|
30
32
|
export declare function telegramPhotoFileId(photo: Array<{
|
|
31
33
|
file_id?: string;
|
|
32
34
|
}>): string | undefined;
|
|
35
|
+
/** 纯函数:语音消息的 file_id(voice 或 audio);无返回 undefined。 */
|
|
36
|
+
export declare function telegramVoiceFileId(raw: RawTelegramMessage): string | undefined;
|
|
37
|
+
/** 纯函数:语音消息的 MIME(voice 优先,audio 兜底)。 */
|
|
38
|
+
export declare function telegramVoiceMime(raw: RawTelegramMessage): string | undefined;
|
|
33
39
|
/** 纯函数:Telegram 文件下载 URL 模板。file_path 需 getFile(file_id) 换取(真实调用在 adapter 薄层)。 */
|
|
34
40
|
export declare function telegramImageUrl(token: string, filePath: string): string;
|
|
35
41
|
export declare function normalizeTelegramMessage(raw: RawTelegramMessage): NormalizedMessage | null;
|
|
@@ -46,7 +52,7 @@ export declare class TelegramAdapter implements Adapter {
|
|
|
46
52
|
private replyCb?;
|
|
47
53
|
constructor(opts: TelegramAdapterOptions);
|
|
48
54
|
connect(): Promise<void>;
|
|
49
|
-
/** 薄层:photo → getFile(file_id) 换 file_path → 下载 URL 填充 msg.media.url(纯函数只做模板)。 */
|
|
55
|
+
/** 薄层:photo/voice/audio → getFile(file_id) 换 file_path → 下载 URL 填充 msg.media.url(纯函数只做模板)。 */
|
|
50
56
|
private handleMessage;
|
|
51
57
|
send(chatId: string, payload: OutboundPayload): Promise<void>;
|
|
52
58
|
onMessage(cb: (msg: NormalizedMessage) => void): void;
|
|
@@ -3,6 +3,14 @@ import { Bot, InlineKeyboard } from 'grammy';
|
|
|
3
3
|
export function telegramPhotoFileId(photo) {
|
|
4
4
|
return photo[photo.length - 1]?.file_id;
|
|
5
5
|
}
|
|
6
|
+
/** 纯函数:语音消息的 file_id(voice 或 audio);无返回 undefined。 */
|
|
7
|
+
export function telegramVoiceFileId(raw) {
|
|
8
|
+
return raw.message?.voice?.file_id ?? raw.message?.audio?.file_id;
|
|
9
|
+
}
|
|
10
|
+
/** 纯函数:语音消息的 MIME(voice 优先,audio 兜底)。 */
|
|
11
|
+
export function telegramVoiceMime(raw) {
|
|
12
|
+
return raw.message?.voice?.mime_type ?? raw.message?.audio?.mime_type;
|
|
13
|
+
}
|
|
6
14
|
/** 纯函数:Telegram 文件下载 URL 模板。file_path 需 getFile(file_id) 换取(真实调用在 adapter 薄层)。 */
|
|
7
15
|
export function telegramImageUrl(token, filePath) {
|
|
8
16
|
return `https://api.telegram.org/file/bot${token}/${filePath}`;
|
|
@@ -17,8 +25,9 @@ export function normalizeTelegramMessage(raw) {
|
|
|
17
25
|
let media;
|
|
18
26
|
if (msg.photo?.length)
|
|
19
27
|
media = { kind: 'image' }; // url 由 adapter getFile 薄层填充
|
|
20
|
-
else if (msg.voice || msg.audio)
|
|
21
|
-
media = { kind: 'voice' };
|
|
28
|
+
else if (msg.voice || msg.audio) {
|
|
29
|
+
media = { kind: 'voice', mime: telegramVoiceMime(raw) }; // url 由 adapter getFile 薄层填充(ASR 用)
|
|
30
|
+
}
|
|
22
31
|
else if (msg.video)
|
|
23
32
|
media = { kind: 'video' };
|
|
24
33
|
else if (msg.document)
|
|
@@ -59,12 +68,16 @@ export class TelegramAdapter {
|
|
|
59
68
|
this.bot.catch((err) => console.error('[telegram]', err));
|
|
60
69
|
void this.bot.start(); // 长轮询(自托管无需 webhook)
|
|
61
70
|
}
|
|
62
|
-
/** 薄层:photo → getFile(file_id) 换 file_path → 下载 URL 填充 msg.media.url(纯函数只做模板)。 */
|
|
71
|
+
/** 薄层:photo/voice/audio → getFile(file_id) 换 file_path → 下载 URL 填充 msg.media.url(纯函数只做模板)。 */
|
|
63
72
|
async handleMessage(raw) {
|
|
64
73
|
const msg = normalizeTelegramMessage(raw);
|
|
65
74
|
if (!msg)
|
|
66
75
|
return;
|
|
67
|
-
const fileId = msg.media?.kind === 'image'
|
|
76
|
+
const fileId = msg.media?.kind === 'image'
|
|
77
|
+
? telegramPhotoFileId(raw.message?.photo ?? [])
|
|
78
|
+
: msg.media?.kind === 'voice'
|
|
79
|
+
? telegramVoiceFileId(raw)
|
|
80
|
+
: undefined;
|
|
68
81
|
if (fileId) {
|
|
69
82
|
try {
|
|
70
83
|
const file = await this.bot.api.getFile(fileId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../src/adapters/telegram.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAmB7C,wDAAwD;AACxD,MAAM,UAAU,mBAAmB,CAAC,KAAkC;IACpE,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC;AAC1C,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,QAAgB;IAC9D,OAAO,oCAAoC,KAAK,IAAI,QAAQ,EAAE,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,GAAuB;IAC9D,IAAI,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC1E,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC;IACxB,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;IAC3C,IAAI,KAAiC,CAAC;IACtC,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM;QAAE,KAAK,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,6BAA6B;SAC1E,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK;
|
|
1
|
+
{"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../src/adapters/telegram.ts"],"names":[],"mappings":"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"}
|
package/dist/adapters/wecom.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ export declare class WeComAdapter implements Adapter {
|
|
|
29
29
|
private messageCb?;
|
|
30
30
|
private replyCb?;
|
|
31
31
|
private readonly pendingButtons;
|
|
32
|
+
/** access_token 缓存:企业微信 token 有效期 7200s,且有获取频率限制,必须复用。 */
|
|
33
|
+
private tokenCache?;
|
|
32
34
|
constructor(opts: WeComAdapterOptions);
|
|
33
35
|
connect(): Promise<void>;
|
|
34
36
|
private route;
|
package/dist/adapters/wecom.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash, createDecipheriv, createCipheriv, randomBytes } from 'node:crypto';
|
|
2
2
|
import { createServer } from 'node:http';
|
|
3
|
+
import { PendingButtons } from '../pending-buttons.js';
|
|
3
4
|
// ── 纯函数:AES-256-CBC 加解密(企业微信协议)──────────────────
|
|
4
5
|
export function deriveAesKey(encodingAESKey) {
|
|
5
6
|
return Buffer.from(encodingAESKey + '=', 'base64'); // 43 位 + '=' → 32 字节
|
|
@@ -79,7 +80,9 @@ export class WeComAdapter {
|
|
|
79
80
|
connected = false;
|
|
80
81
|
messageCb;
|
|
81
82
|
replyCb;
|
|
82
|
-
pendingButtons = new
|
|
83
|
+
pendingButtons = new PendingButtons();
|
|
84
|
+
/** access_token 缓存:企业微信 token 有效期 7200s,且有获取频率限制,必须复用。 */
|
|
85
|
+
tokenCache;
|
|
83
86
|
constructor(opts) {
|
|
84
87
|
this.opts = opts;
|
|
85
88
|
}
|
|
@@ -127,14 +130,10 @@ export class WeComAdapter {
|
|
|
127
130
|
res.end('success'); // 先应答,避免企业微信重试
|
|
128
131
|
if (!normalized)
|
|
129
132
|
return;
|
|
130
|
-
const
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
this.pendingButtons.delete(normalized.chatId);
|
|
135
|
-
this.replyCb?.(button.id);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
133
|
+
const button = this.pendingButtons.match(normalized.chatId, normalized.text);
|
|
134
|
+
if (button) {
|
|
135
|
+
this.replyCb?.(button.id);
|
|
136
|
+
return;
|
|
138
137
|
}
|
|
139
138
|
this.messageCb?.(normalized);
|
|
140
139
|
}
|
|
@@ -158,10 +157,14 @@ export class WeComAdapter {
|
|
|
158
157
|
throw new Error(`企业微信发送失败: ${res.errcode} ${res.errmsg}`);
|
|
159
158
|
}
|
|
160
159
|
async fetchAccessToken() {
|
|
160
|
+
if (this.tokenCache && this.tokenCache.expiresAt > Date.now())
|
|
161
|
+
return this.tokenCache.token;
|
|
161
162
|
const url = `https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${encodeURIComponent(this.opts.corpId)}&corpsecret=${encodeURIComponent(this.opts.secret)}`;
|
|
162
163
|
const data = (await fetch(url).then((r) => r.json()));
|
|
163
164
|
if (!data.access_token)
|
|
164
165
|
throw new Error(`企业微信 token 获取失败: ${data.errcode}`);
|
|
166
|
+
// 官方有效期 7200s;留 200s 余量,避免临界过期
|
|
167
|
+
this.tokenCache = { token: data.access_token, expiresAt: Date.now() + 7_000_000 };
|
|
165
168
|
return data.access_token;
|
|
166
169
|
}
|
|
167
170
|
onMessage(cb) { this.messageCb = cb; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wecom.js","sourceRoot":"","sources":["../../src/adapters/wecom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EAAE,YAAY,EAA6C,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"wecom.js","sourceRoot":"","sources":["../../src/adapters/wecom.ts"],"names":[],"mappings":"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"}
|
|
@@ -68,7 +68,7 @@ export declare class WhatsAppAdapter implements Adapter {
|
|
|
68
68
|
private connected;
|
|
69
69
|
private messageCb?;
|
|
70
70
|
private replyCb?;
|
|
71
|
-
/** chatId → 当前 pending 按钮(编号回复 → 按钮 id
|
|
71
|
+
/** chatId → 当前 pending 按钮(编号回复 → 按钮 id,带 TTL 防过期误吞) */
|
|
72
72
|
private readonly pendingButtons;
|
|
73
73
|
constructor(opts: WhatsAppAdapterOptions);
|
|
74
74
|
connect(): Promise<void>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import makeWASocket, { useMultiFileAuthState, DisconnectReason, generateWAMessageFromContent, } from '@whiskeysockets/baileys';
|
|
2
2
|
import * as qrcode from 'qrcode-terminal';
|
|
3
3
|
import pino from 'pino';
|
|
4
|
+
import { PendingButtons } from '../pending-buttons.js';
|
|
4
5
|
export function extractWhatsAppText(raw) {
|
|
5
6
|
const msg = raw.message;
|
|
6
7
|
if (!msg)
|
|
@@ -95,8 +96,8 @@ export class WhatsAppAdapter {
|
|
|
95
96
|
connected = false;
|
|
96
97
|
messageCb;
|
|
97
98
|
replyCb;
|
|
98
|
-
/** chatId → 当前 pending 按钮(编号回复 → 按钮 id
|
|
99
|
-
pendingButtons = new
|
|
99
|
+
/** chatId → 当前 pending 按钮(编号回复 → 按钮 id,带 TTL 防过期误吞) */
|
|
100
|
+
pendingButtons = new PendingButtons();
|
|
100
101
|
constructor(opts) {
|
|
101
102
|
this.opts = opts;
|
|
102
103
|
}
|
|
@@ -138,22 +139,18 @@ export class WhatsAppAdapter {
|
|
|
138
139
|
if (buttonId) {
|
|
139
140
|
const chatId = waRaw.key?.remoteJid;
|
|
140
141
|
if (chatId)
|
|
141
|
-
this.pendingButtons.
|
|
142
|
+
this.pendingButtons.consume(chatId);
|
|
142
143
|
this.replyCb?.(buttonId);
|
|
143
144
|
continue;
|
|
144
145
|
}
|
|
145
146
|
const normalized = normalizeWhatsAppMessage(waRaw);
|
|
146
147
|
if (!normalized)
|
|
147
148
|
continue;
|
|
148
|
-
// 编号回复兜底:若该 chat 有 pending
|
|
149
|
-
const
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
this.pendingButtons.delete(normalized.msg.chatId);
|
|
154
|
-
this.replyCb?.(button.id);
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
149
|
+
// 编号回复兜底:若该 chat 有 pending 按钮且消息是数字(TTL 内),转成按钮点击
|
|
150
|
+
const button = this.pendingButtons.match(normalized.msg.chatId, normalized.msg.text);
|
|
151
|
+
if (button) {
|
|
152
|
+
this.replyCb?.(button.id);
|
|
153
|
+
continue;
|
|
157
154
|
}
|
|
158
155
|
this.messageCb?.(normalized.msg);
|
|
159
156
|
}
|