@xmanrui/dsh-im 1.4.0 → 2.0.0
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 +18 -6
- package/README.md +18 -6
- package/lib/index.js +206 -176
- package/package.json +1 -1
- package/plugin-src/host/index.mjs +2 -0
- package/src/channels/dingtalk/config-store.mjs +5 -3
- package/src/channels/dingtalk/dingtalk-api.mjs +105 -82
- package/src/channels/dingtalk/dingtalk-bridge.mjs +79 -110
- package/src/channels/dingtalk/dingtalk-card-stream.mjs +3 -1
- package/src/channels/dingtalk/dingtalk-controller.mjs +24 -23
- package/src/channels/dingtalk/dingtalk-runtime.mjs +4 -3
- package/src/channels/dingtalk/state-store.mjs +3 -1
- package/src/channels/discord/config-store.mjs +2 -1
- package/src/channels/discord/discord-api.mjs +25 -1
- package/src/channels/discord/discord-runtime.mjs +237 -11
- package/src/channels/feishu/bridge.mjs +169 -180
- package/src/channels/feishu/feishu-cards.mjs +62 -54
- package/src/channels/feishu/feishu-channel.mjs +57 -21
- package/src/channels/feishu/feishu-runtime.mjs +10 -9
- package/src/channels/feishu/message-utils.mjs +4 -3
- package/src/channels/office/office-job-executor.mjs +15 -14
- package/src/channels/office/office-runtime.mjs +6 -5
- package/src/channels/qq/config-store.mjs +3 -1
- package/src/channels/qq/markdown-reply.mjs +4 -2
- package/src/channels/qq/qq-bridge.mjs +149 -130
- package/src/channels/qq/qq-controller.mjs +17 -16
- package/src/channels/qq/qq-runtime.mjs +3 -2
- package/src/channels/shared/agent-preset.mjs +3 -1
- package/src/channels/shared/compact-command.mjs +15 -14
- package/src/channels/shared/connection-test.mjs +6 -4
- package/src/channels/shared/control-command.mjs +13 -11
- package/src/channels/shared/editable-message-stream.mjs +4 -2
- package/src/channels/shared/harness-approval.mjs +23 -21
- package/src/channels/shared/harness-client.mjs +3 -2
- package/src/channels/shared/harness-question.mjs +8 -6
- package/src/channels/shared/i18n-en/dingtalk.mjs +52 -0
- package/src/channels/shared/i18n-en/discord.mjs +8 -0
- package/src/channels/shared/i18n-en/feishu.mjs +217 -0
- package/src/channels/shared/i18n-en/office.mjs +23 -0
- package/src/channels/shared/i18n-en/qq.mjs +54 -0
- package/src/channels/shared/i18n-en/shared-a.mjs +91 -0
- package/src/channels/shared/i18n-en/shared-b.mjs +215 -0
- package/src/channels/shared/i18n-en/shared-c.mjs +99 -0
- package/src/channels/shared/i18n-en/slack.mjs +27 -0
- package/src/channels/shared/i18n-en/telegram.mjs +21 -0
- package/src/channels/shared/i18n-en/wecom.mjs +38 -0
- package/src/channels/shared/i18n-en/weixin.mjs +80 -0
- package/src/channels/shared/i18n-en/whatsapp.mjs +17 -0
- package/src/channels/shared/i18n-en.mjs +35 -0
- package/src/channels/shared/i18n.mjs +35 -0
- package/src/channels/shared/image-prompt.mjs +17 -15
- package/src/channels/shared/inbound-file.mjs +5 -3
- package/src/channels/shared/model-command.mjs +35 -31
- package/src/channels/shared/preset-command.mjs +41 -38
- package/src/channels/shared/semantic/artifact-delivery.mjs +170 -0
- package/src/channels/shared/semantic/artifact.mjs +2 -2
- package/src/channels/shared/semantic/delivery.mjs +40 -0
- package/src/channels/shared/text-harness-bridge.mjs +154 -136
- package/src/channels/shared/token-bot-controller.mjs +32 -11
- package/src/channels/shared/workspace-command.mjs +69 -58
- package/src/channels/slack/config-store.mjs +3 -1
- package/src/channels/slack/slack-api.mjs +6 -5
- package/src/channels/slack/slack-controller.mjs +10 -9
- package/src/channels/slack/slack-runtime.mjs +16 -4
- package/src/channels/telegram/config-store.mjs +2 -1
- package/src/channels/telegram/telegram-api.mjs +111 -19
- package/src/channels/telegram/telegram-rich-message.mjs +147 -0
- package/src/channels/telegram/telegram-runtime.mjs +383 -5
- package/src/channels/wecom/config-store.mjs +3 -1
- package/src/channels/wecom/wecom-bridge.mjs +154 -153
- package/src/channels/wecom/wecom-controller.mjs +20 -19
- package/src/channels/wecom/wecom-runtime.mjs +2 -1
- package/src/channels/weixin/config-store.mjs +2 -1
- package/src/channels/weixin/weixin-api.mjs +140 -107
- package/src/channels/weixin/weixin-bridge.mjs +86 -123
- package/src/channels/weixin/weixin-controller.mjs +22 -21
- package/src/channels/weixin/weixin-runtime.mjs +3 -2
- package/src/channels/whatsapp/config-store.mjs +3 -1
- package/src/channels/whatsapp/whatsapp-bridge.mjs +3 -1
- package/src/channels/whatsapp/whatsapp-controller.mjs +15 -14
- package/src/channels/whatsapp/whatsapp-runtime.mjs +47 -13
- package/src/channels/whatsapp/whatsapp-web-session.mjs +3 -1
|
@@ -38,14 +38,9 @@ import {
|
|
|
38
38
|
} from '../shared/preset-command.mjs';
|
|
39
39
|
import { runWorkspaceCommand, resolveSessionListWorkspace, workspacePathSnapshot } from '../shared/workspace-command.mjs';
|
|
40
40
|
import { askInWorkspaceSession } from '../shared/workspace-session.mjs';
|
|
41
|
+
import { deliverOutboundArtifacts } from '../shared/semantic/artifact-delivery.mjs';
|
|
41
42
|
import {
|
|
42
|
-
materializeOutboundArtifact,
|
|
43
|
-
releaseOutboundArtifact,
|
|
44
|
-
} from '../shared/semantic/artifact.mjs';
|
|
45
|
-
import {
|
|
46
|
-
createArtifactFailureReceipt,
|
|
47
43
|
createDeliveryReceipt,
|
|
48
|
-
mergeDeliveryReceipts,
|
|
49
44
|
} from '../shared/semantic/delivery.mjs';
|
|
50
45
|
import {
|
|
51
46
|
MENU_PAGE_SIZE,
|
|
@@ -56,13 +51,15 @@ import {
|
|
|
56
51
|
watchListCard,
|
|
57
52
|
workspaceListCard,
|
|
58
53
|
} from './feishu-cards.mjs';
|
|
54
|
+
import { t } from '../shared/i18n.mjs';
|
|
59
55
|
import { MAX_WATCHES_PER_KEY } from './state-store.mjs';
|
|
60
56
|
import {
|
|
61
57
|
FEISHU_GROUP_RESPONSE_MODES,
|
|
62
58
|
normalizeFeishuGroupResponseMode,
|
|
63
59
|
} from './group-response-mode.mjs';
|
|
64
60
|
|
|
65
|
-
|
|
61
|
+
// Lazily evaluated: t() must run after setImHostLanguage, not at import time.
|
|
62
|
+
const INTERACTION_RESOLVED_TEXT = () => t('这个问题已在其他客户端处理,无需再次回答。');
|
|
66
63
|
const RESOLVED_REPLY_TTL_MS = 30 * 60_000;
|
|
67
64
|
|
|
68
65
|
const MENU_COMMAND = /^\/m(?:enu)?$/i;
|
|
@@ -92,34 +89,37 @@ const REPAIR_URL_HOSTS = new Set([
|
|
|
92
89
|
'open.larksuite.com',
|
|
93
90
|
]);
|
|
94
91
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
92
|
+
// Built lazily: t() must run after setImHostLanguage, not at import time.
|
|
93
|
+
function helpText() {
|
|
94
|
+
return [
|
|
95
|
+
t('北汇星河 AIOS 已连接 DeepSeek Harness。'),
|
|
96
|
+
'',
|
|
97
|
+
t('直接发送文字、图片或文件即可继续当前会话。'),
|
|
98
|
+
t('/new 开启一个全新会话'),
|
|
99
|
+
t('/compact 压缩当前会话的较早上下文'),
|
|
100
|
+
t('/workspace 工作区绝对路径 切换工作区'),
|
|
101
|
+
t('/workspacelist 列出工作区绝对路径'),
|
|
102
|
+
t('/sessionlist [工作区序号或绝对路径] 列出会话 ID 和标题'),
|
|
103
|
+
t('/session Session ID 或当前工作区序号 将当前聊天绑定到指定会话'),
|
|
104
|
+
t('/models 按序号列出所有可用模型'),
|
|
105
|
+
t('/model [序号或完整模型ID] 查看或切换当前会话模型'),
|
|
106
|
+
t('示例:先发 /models,再发 /model 2'),
|
|
107
|
+
t('/presetlist 按序号列出可用 Agent Preset'),
|
|
108
|
+
t('/preset [序号或完整ID] 查看或设置当前机器人 Agent Preset'),
|
|
109
|
+
t('纯数字 ID:/preset id:<ID>'),
|
|
110
|
+
t('/preset --default 跟随 Host 默认'),
|
|
111
|
+
t('/stop 停止当前任务'),
|
|
112
|
+
t('/steer 补充指令 纠偏当前任务'),
|
|
113
|
+
t('/status 检查连接状态'),
|
|
114
|
+
t('/repair 修复卡片按钮回调'),
|
|
115
|
+
t('/m(或 /menu) 打开交互卡片菜单'),
|
|
116
|
+
t('/watch [Session ID 或序号] 关注会话,任务完成自动推送'),
|
|
117
|
+
t('/unwatch [Session ID 或序号] 取消关注'),
|
|
118
|
+
t('/watchlist 查看关注列表'),
|
|
119
|
+
t('/archived on|off 会话列表是否包含归档会话'),
|
|
120
|
+
t('/help 显示本帮助'),
|
|
121
|
+
].join('\n');
|
|
122
|
+
}
|
|
123
123
|
|
|
124
124
|
const ARCHIVED_COMMAND = /^\/archived(?:\s+(on|off))?$/i;
|
|
125
125
|
|
|
@@ -127,43 +127,43 @@ const ARCHIVED_COMMAND = /^\/archived(?:\s+(on|off))?$/i;
|
|
|
127
127
|
function safeErrorText(error) {
|
|
128
128
|
switch (error?.code) {
|
|
129
129
|
case 'workspace-not-absolute':
|
|
130
|
-
return '工作区必须是绝对路径。';
|
|
130
|
+
return t('工作区必须是绝对路径。');
|
|
131
131
|
case 'workspace-not-found':
|
|
132
|
-
return '工作区路径不存在。';
|
|
132
|
+
return t('工作区路径不存在。');
|
|
133
133
|
case 'workspace-not-directory':
|
|
134
|
-
return '工作区路径必须指向一个目录。';
|
|
134
|
+
return t('工作区路径必须指向一个目录。');
|
|
135
135
|
case 'workspace-bot-not-found':
|
|
136
|
-
return '机器人正在移除或已重新接入,无法操作原会话的工作区。';
|
|
136
|
+
return t('机器人正在移除或已重新接入,无法操作原会话的工作区。');
|
|
137
137
|
default:
|
|
138
|
-
return '操作失败,请稍后重试。';
|
|
138
|
+
return t('操作失败,请稍后重试。');
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
function artifactFailureText(fileName, error) {
|
|
143
|
-
const name = String(fileName ?? '结果文件').replace(/[\r\n]+/g, ' ').trim() || '结果文件';
|
|
143
|
+
const name = String(fileName ?? t('结果文件')).replace(/[\r\n]+/g, ' ').trim() || t('结果文件');
|
|
144
144
|
switch (error?.code) {
|
|
145
145
|
case 'artifact-permission-required':
|
|
146
|
-
return
|
|
146
|
+
return t('结果文件「{name}」已生成,但机器人缺少飞书文件上传权限。请为应用添加 im:resource 并完成必要审批后重试。', { name });
|
|
147
147
|
case 'artifact-too-large':
|
|
148
|
-
return
|
|
148
|
+
return t('结果文件「{name}」超过飞书 30 MB 上限,未发送。', { name });
|
|
149
149
|
case 'artifact-empty':
|
|
150
|
-
return
|
|
150
|
+
return t('结果文件「{name}」为空,飞书不允许发送空文件。', { name });
|
|
151
151
|
case 'artifact-changed':
|
|
152
152
|
case 'artifact-invalid':
|
|
153
153
|
case 'artifact-unavailable':
|
|
154
|
-
return
|
|
154
|
+
return t('结果文件「{name}」暂时无法读取或准备发送,请确认文件仍可访问后重试。', { name });
|
|
155
155
|
case 'artifact-rate-limited':
|
|
156
|
-
return
|
|
156
|
+
return t('结果文件「{name}」暂时被飞书限流,未能发送,请稍后重试。', { name });
|
|
157
157
|
case 'artifact-delivery-uncertain':
|
|
158
|
-
return
|
|
158
|
+
return t('结果文件「{name}」发送结果未能确认,请先检查聊天内是否已收到,不要立即重试。', { name });
|
|
159
159
|
default:
|
|
160
|
-
return
|
|
160
|
+
return t('结果文件「{name}」已生成,但暂时未能发送,请稍后重试。', { name });
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
function answerTextForDelivery(answer, artifacts) {
|
|
165
165
|
if (typeof answer === 'string' && answer.trim()) return answer;
|
|
166
|
-
return artifacts.length > 0 ? '结果文件已生成。' : answer;
|
|
166
|
+
return artifacts.length > 0 ? t('结果文件已生成。') : answer;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
function nonEmptyString(value) {
|
|
@@ -616,7 +616,7 @@ export class FeishuHarnessBridge {
|
|
|
616
616
|
event.message.chat_id,
|
|
617
617
|
inboundFileUserMessage(error)
|
|
618
618
|
?? imagePromptUserMessage(error)
|
|
619
|
-
?? '处理失败,请稍后重试。如果问题持续,请在 DeepSeek Harness 的飞书插件页面检查连接状态。',
|
|
619
|
+
?? t('处理失败,请稍后重试。如果问题持续,请在 DeepSeek Harness 的飞书插件页面检查连接状态。'),
|
|
620
620
|
).catch(() => undefined);
|
|
621
621
|
}
|
|
622
622
|
|
|
@@ -680,7 +680,7 @@ export class FeishuHarnessBridge {
|
|
|
680
680
|
const hasFiles = hasInboundFiles(message);
|
|
681
681
|
const commandText = event.message.message_type === 'text' && !hasImages && !hasFiles ? text : null;
|
|
682
682
|
if (!text && !hasImages && !hasFiles) {
|
|
683
|
-
await this.#send(event.message.chat_id, '目前支持文字、图片和文件消息。');
|
|
683
|
+
await this.#send(event.message.chat_id, t('目前支持文字、图片和文件消息。'));
|
|
684
684
|
return;
|
|
685
685
|
}
|
|
686
686
|
|
|
@@ -689,7 +689,7 @@ export class FeishuHarnessBridge {
|
|
|
689
689
|
return;
|
|
690
690
|
}
|
|
691
691
|
if (commandText === '/help') {
|
|
692
|
-
await this.#send(event.message.chat_id,
|
|
692
|
+
await this.#send(event.message.chat_id, helpText());
|
|
693
693
|
return;
|
|
694
694
|
}
|
|
695
695
|
if (MENU_COMMAND.test(commandText)) {
|
|
@@ -699,12 +699,12 @@ export class FeishuHarnessBridge {
|
|
|
699
699
|
}
|
|
700
700
|
if (commandText === '/new') {
|
|
701
701
|
await this.#state.clearSession(key);
|
|
702
|
-
await this.#send(event.message.chat_id, '已开启全新 Harness 会话。');
|
|
702
|
+
await this.#send(event.message.chat_id, t('已开启全新 Harness 会话。'));
|
|
703
703
|
return;
|
|
704
704
|
}
|
|
705
705
|
if (commandText === '/status') {
|
|
706
706
|
await this.#harness.ensureRunning({ signal: this.#signal });
|
|
707
|
-
await this.#send(event.message.chat_id, '飞书机器人与 DeepSeek Harness 连接正常。');
|
|
707
|
+
await this.#send(event.message.chat_id, t('飞书机器人与 DeepSeek Harness 连接正常。'));
|
|
708
708
|
return;
|
|
709
709
|
}
|
|
710
710
|
if (SESSION_LIST_PREFIX.test(commandText)) {
|
|
@@ -734,7 +734,7 @@ export class FeishuHarnessBridge {
|
|
|
734
734
|
const match = ARCHIVED_COMMAND.exec(commandText);
|
|
735
735
|
const value = match[1]?.toLowerCase();
|
|
736
736
|
if (value !== 'on' && value !== 'off') {
|
|
737
|
-
await this.#send(event.message.chat_id, '用法:/archived on(包含归档会话)或 /archived off(隐藏归档会话)');
|
|
737
|
+
await this.#send(event.message.chat_id, t('用法:/archived on(包含归档会话)或 /archived off(隐藏归档会话)'));
|
|
738
738
|
return;
|
|
739
739
|
}
|
|
740
740
|
if (typeof this.#state?.setIncludeArchivedSessions === 'function') {
|
|
@@ -742,7 +742,7 @@ export class FeishuHarnessBridge {
|
|
|
742
742
|
}
|
|
743
743
|
await this.#send(
|
|
744
744
|
event.message.chat_id,
|
|
745
|
-
value === 'on' ? '已开启:会话列表包含归档会话。' : '已关闭:会话列表隐藏归档会话。',
|
|
745
|
+
value === 'on' ? t('已开启:会话列表包含归档会话。') : t('已关闭:会话列表隐藏归档会话。'),
|
|
746
746
|
);
|
|
747
747
|
return;
|
|
748
748
|
}
|
|
@@ -799,7 +799,7 @@ export class FeishuHarnessBridge {
|
|
|
799
799
|
// and number replies because callback buttons are the capability being fixed.
|
|
800
800
|
async #handleRepairCommand(event, commandText) {
|
|
801
801
|
if (event?.message?.chat_type !== 'p2p') {
|
|
802
|
-
await this.#send(event.message.chat_id, '为避免授权链接暴露,请私聊机器人发送 /repair。');
|
|
802
|
+
await this.#send(event.message.chat_id, t('为避免授权链接暴露,请私聊机器人发送 /repair。'));
|
|
803
803
|
return;
|
|
804
804
|
}
|
|
805
805
|
const actorOpenId = strictSenderOpenId(event);
|
|
@@ -807,19 +807,19 @@ export class FeishuHarnessBridge {
|
|
|
807
807
|
await this.#send(
|
|
808
808
|
event.message.chat_id,
|
|
809
809
|
this.#repairOwnerOpenIds.size === 0
|
|
810
|
-
? '当前机器人没有可验证的接入者身份,不能从聊天发起修复;请先在插件页设置管理员。'
|
|
811
|
-
: '此操作只能由机器人接入者在私聊中发起,未进行任何修改。',
|
|
810
|
+
? t('当前机器人没有可验证的接入者身份,不能从聊天发起修复;请先在插件页设置管理员。')
|
|
811
|
+
: t('此操作只能由机器人接入者在私聊中发起,未进行任何修改。'),
|
|
812
812
|
);
|
|
813
813
|
return;
|
|
814
814
|
}
|
|
815
815
|
if (!this.#repair) {
|
|
816
|
-
await this.#send(event.message.chat_id, '当前 Host 版本暂不支持聊天内修复,请先更新插件。');
|
|
816
|
+
await this.#send(event.message.chat_id, t('当前 Host 版本暂不支持聊天内修复,请先更新插件。'));
|
|
817
817
|
return;
|
|
818
818
|
}
|
|
819
819
|
|
|
820
820
|
const parsed = REPAIR_COMMAND.exec(commandText);
|
|
821
821
|
if (!parsed) {
|
|
822
|
-
await this.#send(event.message.chat_id, '用法:/repair、/repair qr、/repair status、/repair cancel 或 /repair verify');
|
|
822
|
+
await this.#send(event.message.chat_id, t('用法:/repair、/repair qr、/repair status、/repair cancel 或 /repair verify'));
|
|
823
823
|
return;
|
|
824
824
|
}
|
|
825
825
|
const operation = parsed[1]?.toLowerCase() ?? 'start';
|
|
@@ -833,12 +833,12 @@ export class FeishuHarnessBridge {
|
|
|
833
833
|
if (!attempt) {
|
|
834
834
|
await this.#send(
|
|
835
835
|
chatId,
|
|
836
|
-
'当前 Runtime 没有可恢复的修复任务记录(机器人可能刚完成密钥更新并重启)。本命令不会启动新的授权;请查看机器人发送的验证结果,确认上一次任务已结束后再发送 /repair。',
|
|
836
|
+
t('当前 Runtime 没有可恢复的修复任务记录(机器人可能刚完成密钥更新并重启)。本命令不会启动新的授权;请查看机器人发送的验证结果,确认上一次任务已结束后再发送 /repair。'),
|
|
837
837
|
);
|
|
838
838
|
return;
|
|
839
839
|
}
|
|
840
840
|
if (attempt.actorOpenId !== actorOpenId) {
|
|
841
|
-
await this.#send(chatId, '另一位管理员正在修复该机器人,本次不会显示其授权信息。');
|
|
841
|
+
await this.#send(chatId, t('另一位管理员正在修复该机器人,本次不会显示其授权信息。'));
|
|
842
842
|
return;
|
|
843
843
|
}
|
|
844
844
|
if (operation === 'cancel') {
|
|
@@ -848,7 +848,7 @@ export class FeishuHarnessBridge {
|
|
|
848
848
|
snapshot = repairSnapshot(result, { botId: this.#botId });
|
|
849
849
|
attempt.snapshot = snapshot;
|
|
850
850
|
} catch {
|
|
851
|
-
await this.#send(chatId, '暂时无法取消修复任务,请稍后重试。');
|
|
851
|
+
await this.#send(chatId, t('暂时无法取消修复任务,请稍后重试。'));
|
|
852
852
|
return;
|
|
853
853
|
}
|
|
854
854
|
if (snapshot.state === 'cancelled') {
|
|
@@ -863,7 +863,7 @@ export class FeishuHarnessBridge {
|
|
|
863
863
|
try {
|
|
864
864
|
snapshot = await this.#refreshRepairAttempt(attempt);
|
|
865
865
|
} catch {
|
|
866
|
-
await this.#send(chatId, '暂时无法查询修复状态,请稍后重试。');
|
|
866
|
+
await this.#send(chatId, t('暂时无法查询修复状态,请稍后重试。'));
|
|
867
867
|
return;
|
|
868
868
|
}
|
|
869
869
|
if (operation === 'qr') {
|
|
@@ -892,7 +892,7 @@ export class FeishuHarnessBridge {
|
|
|
892
892
|
const previous = this.#repairAttempt;
|
|
893
893
|
if (previous && REPAIR_ACTIVE_STATES.has(previous.snapshot.state)) {
|
|
894
894
|
if (previous.actorOpenId !== actorOpenId) {
|
|
895
|
-
await this.#send(chatId, '另一位管理员正在修复该机器人,本次不会显示其授权信息。');
|
|
895
|
+
await this.#send(chatId, t('另一位管理员正在修复该机器人,本次不会显示其授权信息。'));
|
|
896
896
|
return;
|
|
897
897
|
}
|
|
898
898
|
try {
|
|
@@ -902,7 +902,7 @@ export class FeishuHarnessBridge {
|
|
|
902
902
|
return;
|
|
903
903
|
}
|
|
904
904
|
} catch {
|
|
905
|
-
await this.#send(chatId, '暂时无法查询修复状态,请稍后重试。');
|
|
905
|
+
await this.#send(chatId, t('暂时无法查询修复状态,请稍后重试。'));
|
|
906
906
|
return;
|
|
907
907
|
}
|
|
908
908
|
}
|
|
@@ -916,7 +916,7 @@ export class FeishuHarnessBridge {
|
|
|
916
916
|
}), { botId: this.#botId });
|
|
917
917
|
snapshot = await this.#waitForRepairLink(snapshot, { actorOpenId, chatId });
|
|
918
918
|
} catch {
|
|
919
|
-
await this.#send(chatId, '修复流程暂时失败,现有机器人连接不受影响;请稍后发送 /repair 重试。');
|
|
919
|
+
await this.#send(chatId, t('修复流程暂时失败,现有机器人连接不受影响;请稍后发送 /repair 重试。'));
|
|
920
920
|
return;
|
|
921
921
|
}
|
|
922
922
|
const attempt = {
|
|
@@ -937,7 +937,7 @@ export class FeishuHarnessBridge {
|
|
|
937
937
|
} catch {
|
|
938
938
|
attempt.stopped = true;
|
|
939
939
|
await this.#repair.cancel(this.#repairArgs(attempt)).catch(() => undefined);
|
|
940
|
-
await this.#send(chatId, '飞书返回了无法安全验证的授权链接,已中止本次修复。');
|
|
940
|
+
await this.#send(chatId, t('飞书返回了无法安全验证的授权链接,已中止本次修复。'));
|
|
941
941
|
return;
|
|
942
942
|
}
|
|
943
943
|
}
|
|
@@ -950,7 +950,7 @@ export class FeishuHarnessBridge {
|
|
|
950
950
|
}
|
|
951
951
|
if (!attempt.verificationUrl) {
|
|
952
952
|
attempt.stopped = true;
|
|
953
|
-
await this.#send(chatId, '飞书未返回授权链接,已中止本次修复。');
|
|
953
|
+
await this.#send(chatId, t('飞书未返回授权链接,已中止本次修复。'));
|
|
954
954
|
return;
|
|
955
955
|
}
|
|
956
956
|
await this.#sendRepairLink(chatId, attempt.verificationUrl, snapshot);
|
|
@@ -1005,7 +1005,7 @@ export class FeishuHarnessBridge {
|
|
|
1005
1005
|
attempt.announcedSaving = true;
|
|
1006
1006
|
await this.#send(
|
|
1007
1007
|
attempt.chatId,
|
|
1008
|
-
'授权已确认,正在发送并等待测试按钮回调;收到真实回调后才会完成。',
|
|
1008
|
+
t('授权已确认,正在发送并等待测试按钮回调;收到真实回调后才会完成。'),
|
|
1009
1009
|
);
|
|
1010
1010
|
}
|
|
1011
1011
|
if (REPAIR_TERMINAL_STATES.has(snapshot.state)) {
|
|
@@ -1026,7 +1026,7 @@ export class FeishuHarnessBridge {
|
|
|
1026
1026
|
this.#logger.warn?.('[dsh-feishu] callback repair status monitoring failed');
|
|
1027
1027
|
await this.#send(
|
|
1028
1028
|
attempt.chatId,
|
|
1029
|
-
'修复状态查询中断,现有机器人连接不受影响;发送 /repair status 重试查询。',
|
|
1029
|
+
t('修复状态查询中断,现有机器人连接不受影响;发送 /repair status 重试查询。'),
|
|
1030
1030
|
).catch(() => undefined);
|
|
1031
1031
|
});
|
|
1032
1032
|
}
|
|
@@ -1035,16 +1035,16 @@ export class FeishuHarnessBridge {
|
|
|
1035
1035
|
const remaining = snapshot.remainingSeconds
|
|
1036
1036
|
?? (snapshot.expiresAt ? Math.max(0, Math.ceil((snapshot.expiresAt - Date.now()) / 1000)) : null);
|
|
1037
1037
|
const expiry = remaining === null
|
|
1038
|
-
? '链接为短期有效'
|
|
1039
|
-
:
|
|
1038
|
+
? t('链接为短期有效')
|
|
1039
|
+
: t('链接约 {minutes} 分钟后过期', { minutes: Math.max(1, Math.ceil(remaining / 60)) });
|
|
1040
1040
|
await this.#send(chatId, [
|
|
1041
|
-
existing ? '已有一个修复任务在等待授权。' : '🔧 准备修复卡片按钮。',
|
|
1042
|
-
'本次只会增量添加 card.action.trigger。请核对确认页只显示这一项;若出现其他权限或事件,请取消。',
|
|
1041
|
+
existing ? t('已有一个修复任务在等待授权。') : t('🔧 准备修复卡片按钮。'),
|
|
1042
|
+
t('本次只会增量添加 card.action.trigger。请核对确认页只显示这一项;若出现其他权限或事件,请取消。'),
|
|
1043
1043
|
'',
|
|
1044
|
-
'当前设备直接打开:',
|
|
1044
|
+
t('当前设备直接打开:'),
|
|
1045
1045
|
url,
|
|
1046
1046
|
'',
|
|
1047
|
-
|
|
1047
|
+
t('若要用另一台设备扫码,发送 /repair qr。{expiry}。', { expiry }),
|
|
1048
1048
|
].join('\n'));
|
|
1049
1049
|
}
|
|
1050
1050
|
|
|
@@ -1060,9 +1060,12 @@ export class FeishuHarnessBridge {
|
|
|
1060
1060
|
if (!imageKey) throw new Error('Feishu QR upload returned no image key');
|
|
1061
1061
|
const remaining = snapshot.remainingSeconds
|
|
1062
1062
|
?? (snapshot.expiresAt ? Math.max(0, Math.ceil((snapshot.expiresAt - Date.now()) / 1000)) : null);
|
|
1063
|
+
const remainingText = remaining === null
|
|
1064
|
+
? ''
|
|
1065
|
+
: t('(剩余约 {minutes} 分钟)', { minutes: Math.max(1, Math.ceil(remaining / 60)) });
|
|
1063
1066
|
await this.#send(
|
|
1064
1067
|
chatId,
|
|
1065
|
-
|
|
1068
|
+
t('请用另一台设备扫码完成授权{remaining}。', { remaining: remainingText }),
|
|
1066
1069
|
);
|
|
1067
1070
|
const response = await this.#client.im.v1.message.create({
|
|
1068
1071
|
params: { receive_id_type: 'chat_id' },
|
|
@@ -1074,40 +1077,40 @@ export class FeishuHarnessBridge {
|
|
|
1074
1077
|
});
|
|
1075
1078
|
if (response?.code && response.code !== 0) throw new Error('Feishu QR message send failed');
|
|
1076
1079
|
} catch {
|
|
1077
|
-
await this.#send(chatId,
|
|
1080
|
+
await this.#send(chatId, t('二维码暂时无法发送,请直接打开授权链接:\n{url}', { url }));
|
|
1078
1081
|
}
|
|
1079
1082
|
}
|
|
1080
1083
|
|
|
1081
1084
|
#repairStatusText(snapshot, { verificationFocused = false } = {}) {
|
|
1082
1085
|
if (snapshot.state === 'succeeded') {
|
|
1083
|
-
return '✅ 修复完成:已实测收到 card.action.trigger,菜单按钮现在可用。';
|
|
1086
|
+
return t('✅ 修复完成:已实测收到 card.action.trigger,菜单按钮现在可用。');
|
|
1084
1087
|
}
|
|
1085
1088
|
if (snapshot.state === 'expired' || snapshot.error?.code === 'expired_token') {
|
|
1086
|
-
return '授权链接已过期;平台未返回成功结果,无法确认已修复。发送 /repair 生成新链接。';
|
|
1089
|
+
return t('授权链接已过期;平台未返回成功结果,无法确认已修复。发送 /repair 生成新链接。');
|
|
1087
1090
|
}
|
|
1088
1091
|
if (snapshot.state === 'cancelled' || snapshot.error?.code === 'abort') {
|
|
1089
|
-
return '已取消本次修复授权,未确认完成修复。';
|
|
1092
|
+
return t('已取消本次修复授权,未确认完成修复。');
|
|
1090
1093
|
}
|
|
1091
1094
|
if (snapshot.error?.code === 'access_denied') {
|
|
1092
|
-
return '你已取消或拒绝授权,没有确认修复;发送 /repair 可重试。';
|
|
1095
|
+
return t('你已取消或拒绝授权,没有确认修复;发送 /repair 可重试。');
|
|
1093
1096
|
}
|
|
1094
1097
|
if (snapshot.error?.code === 'card_action_probe_timeout'
|
|
1095
1098
|
|| snapshot.error?.code === 'card-action-probe-timeout') {
|
|
1096
|
-
return '授权已提交,但未收到测试按钮回调。可能尚未点击或配置仍在传播;稍后发送 /repair verify 查询,不要盲目重复授权。';
|
|
1099
|
+
return t('授权已提交,但未收到测试按钮回调。可能尚未点击或配置仍在传播;稍后发送 /repair verify 查询,不要盲目重复授权。');
|
|
1097
1100
|
}
|
|
1098
1101
|
if (snapshot.state === 'error') {
|
|
1099
|
-
return '修复流程暂时失败,现有机器人连接不受影响;发送 /repair 可重试。';
|
|
1102
|
+
return t('修复流程暂时失败,现有机器人连接不受影响;发送 /repair 可重试。');
|
|
1100
1103
|
}
|
|
1101
1104
|
if (snapshot.state === 'saving') {
|
|
1102
|
-
return '授权已确认,正在等待专用测试按钮的真实回调;回调到达前不会宣告成功。';
|
|
1105
|
+
return t('授权已确认,正在等待专用测试按钮的真实回调;回调到达前不会宣告成功。');
|
|
1103
1106
|
}
|
|
1104
1107
|
if (verificationFocused) {
|
|
1105
|
-
return '授权尚未完成,暂时不能验证卡片按钮。请先打开授权链接并确认。';
|
|
1108
|
+
return t('授权尚未完成,暂时不能验证卡片按钮。请先打开授权链接并确认。');
|
|
1106
1109
|
}
|
|
1107
1110
|
const remaining = snapshot.remainingSeconds === null
|
|
1108
1111
|
? ''
|
|
1109
|
-
:
|
|
1110
|
-
return
|
|
1112
|
+
: t(',剩余约 {minutes} 分钟', { minutes: Math.max(1, Math.ceil(snapshot.remainingSeconds / 60)) });
|
|
1113
|
+
return t('修复任务正在等待授权{remaining}。发送 /repair qr 可获取二维码,/repair cancel 可取消。', { remaining });
|
|
1111
1114
|
}
|
|
1112
1115
|
|
|
1113
1116
|
/**
|
|
@@ -1139,7 +1142,7 @@ export class FeishuHarnessBridge {
|
|
|
1139
1142
|
// restart) or never came from us: nudge instead of staying silent.
|
|
1140
1143
|
const chatId = nonEmptyString(event?.context?.open_chat_id);
|
|
1141
1144
|
if (chatId) {
|
|
1142
|
-
this.#send(chatId, '这个菜单已过期,请回复 /m 重新打开。').catch(() => undefined);
|
|
1145
|
+
this.#send(chatId, t('这个菜单已过期,请回复 /m 重新打开。')).catch(() => undefined);
|
|
1143
1146
|
}
|
|
1144
1147
|
return Promise.resolve();
|
|
1145
1148
|
}
|
|
@@ -1166,12 +1169,12 @@ export class FeishuHarnessBridge {
|
|
|
1166
1169
|
}
|
|
1167
1170
|
if (action === 'new') {
|
|
1168
1171
|
await this.#state.clearSession(key);
|
|
1169
|
-
await this.#send(chatId, '已开启全新 Harness 会话。');
|
|
1172
|
+
await this.#send(chatId, t('已开启全新 Harness 会话。'));
|
|
1170
1173
|
return;
|
|
1171
1174
|
}
|
|
1172
1175
|
if (action === 'status') {
|
|
1173
1176
|
await this.#harness.ensureRunning({ signal: this.#signal });
|
|
1174
|
-
await this.#send(chatId, '飞书机器人与 DeepSeek Harness 连接正常。');
|
|
1177
|
+
await this.#send(chatId, t('飞书机器人与 DeepSeek Harness 连接正常。'));
|
|
1175
1178
|
return;
|
|
1176
1179
|
}
|
|
1177
1180
|
if (action === 'help') {
|
|
@@ -1218,7 +1221,7 @@ export class FeishuHarnessBridge {
|
|
|
1218
1221
|
if (menu.kind === 'menu') {
|
|
1219
1222
|
const action = ['sessions', 'workspaces', 'new', 'status', 'help', 'repair', 'watchlist'][number - 1];
|
|
1220
1223
|
if (!action) {
|
|
1221
|
-
await this.#send(chatId, '菜单没有这个编号,回复 /m 重新打开。');
|
|
1224
|
+
await this.#send(chatId, t('菜单没有这个编号,回复 /m 重新打开。'));
|
|
1222
1225
|
return;
|
|
1223
1226
|
}
|
|
1224
1227
|
if (action === 'repair') {
|
|
@@ -1231,7 +1234,7 @@ export class FeishuHarnessBridge {
|
|
|
1231
1234
|
if (menu.kind === 'sessions') {
|
|
1232
1235
|
const session = menu.sessions[number - 1];
|
|
1233
1236
|
if (!session?.sessionId) {
|
|
1234
|
-
await this.#send(chatId,
|
|
1237
|
+
await this.#send(chatId, t('本页只有 {count} 个会话,回复 /sessionlist 重新查看。', { count: menu.sessions.length }));
|
|
1235
1238
|
return;
|
|
1236
1239
|
}
|
|
1237
1240
|
// The number label sits on the session (bind) button of the row.
|
|
@@ -1241,7 +1244,7 @@ export class FeishuHarnessBridge {
|
|
|
1241
1244
|
if (menu.kind === 'workspaces') {
|
|
1242
1245
|
const workspace = menu.paths[number - 1];
|
|
1243
1246
|
if (!workspace) {
|
|
1244
|
-
await this.#send(chatId,
|
|
1247
|
+
await this.#send(chatId, t('只有 {count} 个工作区,回复 /workspacelist 重新查看。', { count: menu.paths.length }));
|
|
1245
1248
|
return;
|
|
1246
1249
|
}
|
|
1247
1250
|
await this.#handleCardAction(`workspace:${workspace}`, { chatId, key });
|
|
@@ -1250,7 +1253,7 @@ export class FeishuHarnessBridge {
|
|
|
1250
1253
|
if (menu.kind === 'watches') {
|
|
1251
1254
|
const entry = menu.entries[number - 1];
|
|
1252
1255
|
if (!entry?.sessionId) {
|
|
1253
|
-
await this.#send(chatId,
|
|
1256
|
+
await this.#send(chatId, t('关注列表只有 {count} 个会话。', { count: menu.entries.length }));
|
|
1254
1257
|
return;
|
|
1255
1258
|
}
|
|
1256
1259
|
await this.#handleCardAction(`unwatch:${entry.sessionId}`, { chatId, key });
|
|
@@ -1276,7 +1279,7 @@ export class FeishuHarnessBridge {
|
|
|
1276
1279
|
const sessions = this.#visibleSessions(Array.isArray(listed?.sessions) ? listed.sessions : []);
|
|
1277
1280
|
const workspace = listed?.workspace ?? resolved.workspace;
|
|
1278
1281
|
if (sessions.length === 0) {
|
|
1279
|
-
await this.#send(chatId,
|
|
1282
|
+
await this.#send(chatId, t('工作区:{workspace}\n该工作区暂无会话。', { workspace }));
|
|
1280
1283
|
return;
|
|
1281
1284
|
}
|
|
1282
1285
|
const pageCount = Math.ceil(sessions.length / MENU_PAGE_SIZE);
|
|
@@ -1301,7 +1304,7 @@ export class FeishuHarnessBridge {
|
|
|
1301
1304
|
);
|
|
1302
1305
|
} catch (error) {
|
|
1303
1306
|
this.#logger.warn?.('[dsh-feishu] session list failed:', error.message);
|
|
1304
|
-
await this.#send(chatId, '暂时无法获取会话列表,请稍后重试。');
|
|
1307
|
+
await this.#send(chatId, t('暂时无法获取会话列表,请稍后重试。'));
|
|
1305
1308
|
}
|
|
1306
1309
|
}
|
|
1307
1310
|
|
|
@@ -1312,26 +1315,26 @@ export class FeishuHarnessBridge {
|
|
|
1312
1315
|
await this.#sendCard(chatId, workspaceListCard(paths, current), { key });
|
|
1313
1316
|
} catch (error) {
|
|
1314
1317
|
this.#logger.warn?.('[dsh-feishu] workspace list failed:', error.message);
|
|
1315
|
-
await this.#send(chatId, '暂时无法获取工作区列表,请稍后重试。');
|
|
1318
|
+
await this.#send(chatId, t('暂时无法获取工作区列表,请稍后重试。'));
|
|
1316
1319
|
}
|
|
1317
1320
|
}
|
|
1318
1321
|
|
|
1319
1322
|
async #bindSession(key, chatId, sessionId) {
|
|
1320
1323
|
try {
|
|
1321
1324
|
const bound = await this.#harness.bindWorkspaceSession(key, sessionId);
|
|
1322
|
-
const title = String(bound?.title ?? '').replace(/\s+/gu, ' ').trim() || '暂无标题';
|
|
1323
|
-
await this.#send(chatId,
|
|
1325
|
+
const title = String(bound?.title ?? '').replace(/\s+/gu, ' ').trim() || t('暂无标题');
|
|
1326
|
+
await this.#send(chatId, t('已绑定会话「{title}」\nID:{id}', { title, id: bound?.sessionId ?? sessionId }));
|
|
1324
1327
|
} catch (error) {
|
|
1325
|
-
await this.#send(chatId,
|
|
1328
|
+
await this.#send(chatId, t('绑定失败:{message}', { message: safeErrorText(error) }));
|
|
1326
1329
|
}
|
|
1327
1330
|
}
|
|
1328
1331
|
|
|
1329
1332
|
async #switchWorkspace(key, chatId, workspace) {
|
|
1330
1333
|
try {
|
|
1331
1334
|
const current = await this.#harness.switchWorkspace(workspace);
|
|
1332
|
-
await this.#send(chatId,
|
|
1335
|
+
await this.#send(chatId, t('工作区已切换为:{workspace}', { workspace: current }));
|
|
1333
1336
|
} catch (error) {
|
|
1334
|
-
await this.#send(chatId,
|
|
1337
|
+
await this.#send(chatId, t('切换失败:{message}', { message: safeErrorText(error) }));
|
|
1335
1338
|
}
|
|
1336
1339
|
}
|
|
1337
1340
|
|
|
@@ -1403,7 +1406,7 @@ export class FeishuHarnessBridge {
|
|
|
1403
1406
|
*/
|
|
1404
1407
|
async #resolveWatchTarget(target) {
|
|
1405
1408
|
if (typeof target !== 'string' || target === '') {
|
|
1406
|
-
return { error: '用法:/watch <Session ID 或当前工作区序号>' };
|
|
1409
|
+
return { error: t('用法:/watch <Session ID 或当前工作区序号>') };
|
|
1407
1410
|
}
|
|
1408
1411
|
const numeric = /^\d{1,4}$/.test(target) ? Number(target) : null;
|
|
1409
1412
|
const currentPath = typeof this.#harness?.currentWorkspace === 'function'
|
|
@@ -1414,13 +1417,13 @@ export class FeishuHarnessBridge {
|
|
|
1414
1417
|
return Array.isArray(listed?.sessions) ? listed.sessions : [];
|
|
1415
1418
|
};
|
|
1416
1419
|
if (numeric !== null) {
|
|
1417
|
-
if (!currentPath) return { error: '当前机器人没有可用的工作区,无法按序号解析会话。' };
|
|
1420
|
+
if (!currentPath) return { error: t('当前机器人没有可用的工作区,无法按序号解析会话。') };
|
|
1418
1421
|
const sessions = this.#visibleSessions(await listSessions(currentPath));
|
|
1419
1422
|
const session = sessions[numeric - 1];
|
|
1420
1423
|
if (!session?.sessionId) {
|
|
1421
|
-
return { error:
|
|
1424
|
+
return { error: t('当前工作区只有 {count} 个会话。', { count: sessions.length }) };
|
|
1422
1425
|
}
|
|
1423
|
-
return { sessionId: session.sessionId, title: session.title ?? '暂无标题' };
|
|
1426
|
+
return { sessionId: session.sessionId, title: session.title ?? t('暂无标题') };
|
|
1424
1427
|
}
|
|
1425
1428
|
const extraPaths = typeof this.#harness?.listWorkspaces === 'function'
|
|
1426
1429
|
? (await this.#harness.listWorkspaces()).filter((path) => path !== currentPath)
|
|
@@ -1429,9 +1432,9 @@ export class FeishuHarnessBridge {
|
|
|
1429
1432
|
for (const workspace of paths) {
|
|
1430
1433
|
const sessions = await listSessions(workspace);
|
|
1431
1434
|
const session = sessions.find((candidate) => candidate.sessionId === target);
|
|
1432
|
-
if (session) return { sessionId: target, title: session.title ?? '暂无标题' };
|
|
1435
|
+
if (session) return { sessionId: target, title: session.title ?? t('暂无标题') };
|
|
1433
1436
|
}
|
|
1434
|
-
return { error: '没有找到这个会话,请用 /sessionlist 查看可用会话。' };
|
|
1437
|
+
return { error: t('没有找到这个会话,请用 /sessionlist 查看可用会话。') };
|
|
1435
1438
|
}
|
|
1436
1439
|
|
|
1437
1440
|
async #latestSessionSeq(sessionId) {
|
|
@@ -1448,14 +1451,14 @@ export class FeishuHarnessBridge {
|
|
|
1448
1451
|
async #runWatch(key, chatId, target) {
|
|
1449
1452
|
this.#ensureEventWatcher();
|
|
1450
1453
|
if (typeof this.#state?.setWatch !== 'function') {
|
|
1451
|
-
await this.#send(chatId, '当前状态存储不支持关注。');
|
|
1454
|
+
await this.#send(chatId, t('当前状态存储不支持关注。'));
|
|
1452
1455
|
return;
|
|
1453
1456
|
}
|
|
1454
1457
|
let resolved;
|
|
1455
1458
|
try {
|
|
1456
1459
|
resolved = await this.#resolveWatchTarget(target);
|
|
1457
1460
|
} catch (error) {
|
|
1458
|
-
await this.#send(chatId,
|
|
1461
|
+
await this.#send(chatId, t('无法解析会话:{message}', { message: safeErrorText(error) }));
|
|
1459
1462
|
return;
|
|
1460
1463
|
}
|
|
1461
1464
|
if (resolved.error) {
|
|
@@ -1465,7 +1468,7 @@ export class FeishuHarnessBridge {
|
|
|
1465
1468
|
const existing = this.#state.watchEntries?.(key) ?? [];
|
|
1466
1469
|
const existingEntry = existing.find((entry) => entry.sessionId === resolved.sessionId);
|
|
1467
1470
|
if (!existingEntry && existing.length >= MAX_WATCHES_PER_KEY) {
|
|
1468
|
-
await this.#send(chatId,
|
|
1471
|
+
await this.#send(chatId, t('每个聊天最多关注 {count} 个会话。', { count: MAX_WATCHES_PER_KEY }));
|
|
1469
1472
|
return;
|
|
1470
1473
|
}
|
|
1471
1474
|
try {
|
|
@@ -1478,10 +1481,10 @@ export class FeishuHarnessBridge {
|
|
|
1478
1481
|
chatId,
|
|
1479
1482
|
lastSeq,
|
|
1480
1483
|
});
|
|
1481
|
-
await this.#send(chatId,
|
|
1484
|
+
await this.#send(chatId, t('已关注会话「{title}」,任务完成会推送结果。', { title: String(resolved.title).replace(/\s+/gu, ' ') }));
|
|
1482
1485
|
await this.#queueEventTask(() => this.#compensateSession(resolved.sessionId));
|
|
1483
1486
|
} catch (error) {
|
|
1484
|
-
await this.#send(chatId,
|
|
1487
|
+
await this.#send(chatId, t('关注失败:{message}', { message: safeErrorText(error) }));
|
|
1485
1488
|
}
|
|
1486
1489
|
}
|
|
1487
1490
|
|
|
@@ -1492,15 +1495,15 @@ export class FeishuHarnessBridge {
|
|
|
1492
1495
|
? entries[Number(target) - 1]
|
|
1493
1496
|
: entries.find((candidate) => candidate.sessionId === target);
|
|
1494
1497
|
if (!entry) {
|
|
1495
|
-
await this.#send(chatId, '关注列表里没有这个会话,回复 /watchlist 查看。');
|
|
1498
|
+
await this.#send(chatId, t('关注列表里没有这个会话,回复 /watchlist 查看。'));
|
|
1496
1499
|
return;
|
|
1497
1500
|
}
|
|
1498
1501
|
try {
|
|
1499
1502
|
await this.#state.removeWatch(key, entry.sessionId);
|
|
1500
1503
|
this.#failedWatchSeqs.delete(`${key}\0${entry.sessionId}`);
|
|
1501
|
-
await this.#send(chatId,
|
|
1504
|
+
await this.#send(chatId, t('已取消关注「{title}」。', { title: String(entry.title ?? '').replace(/\s+/gu, ' ') }));
|
|
1502
1505
|
} catch (error) {
|
|
1503
|
-
await this.#send(chatId,
|
|
1506
|
+
await this.#send(chatId, t('取消失败:{message}', { message: safeErrorText(error) }));
|
|
1504
1507
|
}
|
|
1505
1508
|
}
|
|
1506
1509
|
|
|
@@ -1641,64 +1644,50 @@ export class FeishuHarnessBridge {
|
|
|
1641
1644
|
}
|
|
1642
1645
|
|
|
1643
1646
|
async #deliverArtifacts(chatId, replyTo, artifacts = [], baseReceipt) {
|
|
1644
|
-
const
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
)
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
artifactId: artifact?.artifactId ?? 'unknown',
|
|
1677
|
-
deliveryId: artifact?.deliveryKey ?? artifact?.artifactId ?? 'unknown',
|
|
1678
|
-
error,
|
|
1679
|
-
providerMessageIds: noticeMessageId ? [noticeMessageId] : [],
|
|
1680
|
-
}));
|
|
1681
|
-
} finally {
|
|
1682
|
-
releaseOutboundArtifact(artifact);
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
if (receipts.length === 0) {
|
|
1647
|
+
const delivery = await deliverOutboundArtifacts({
|
|
1648
|
+
artifacts,
|
|
1649
|
+
baseReceipt,
|
|
1650
|
+
deliveryId: baseReceipt?.deliveryId ?? artifacts[0]?.deliveryKey ?? replyTo,
|
|
1651
|
+
aggregatePresentation: baseReceipt ? 'feishu-text-and-files' : 'feishu-files',
|
|
1652
|
+
channelKey: 'feishu',
|
|
1653
|
+
signal: this.#signal,
|
|
1654
|
+
sendImage: typeof this.#channel?.sendImage === 'function'
|
|
1655
|
+
? (file) => this.#channel.sendImage(chatId, file, {
|
|
1656
|
+
replyTo,
|
|
1657
|
+
signal: this.#signal,
|
|
1658
|
+
})
|
|
1659
|
+
: undefined,
|
|
1660
|
+
sendFile: typeof this.#channel?.sendFile === 'function'
|
|
1661
|
+
? (file) => this.#channel.sendFile(chatId, file, {
|
|
1662
|
+
replyTo,
|
|
1663
|
+
signal: this.#signal,
|
|
1664
|
+
})
|
|
1665
|
+
: undefined,
|
|
1666
|
+
sendFailureNotice: async (artifact, error) => ({
|
|
1667
|
+
messageId: await this.#send(
|
|
1668
|
+
chatId,
|
|
1669
|
+
artifactFailureText(artifact?.fileName, error),
|
|
1670
|
+
),
|
|
1671
|
+
}),
|
|
1672
|
+
logger: this.#logger,
|
|
1673
|
+
});
|
|
1674
|
+
this.#status.artifactsSent = (this.#status.artifactsSent ?? 0)
|
|
1675
|
+
+ delivery.artifactsSent;
|
|
1676
|
+
this.#status.artifactSendErrors = (this.#status.artifactSendErrors ?? 0)
|
|
1677
|
+
+ delivery.artifactSendErrors;
|
|
1678
|
+
if (!delivery.receipt) {
|
|
1686
1679
|
return {
|
|
1687
1680
|
receipt: createDeliveryReceipt({
|
|
1688
1681
|
deliveryId: replyTo,
|
|
1689
1682
|
presentation: 'feishu-files',
|
|
1690
1683
|
}),
|
|
1691
|
-
failureNoticeVisible,
|
|
1684
|
+
failureNoticeVisible: delivery.failureNoticeVisible,
|
|
1692
1685
|
};
|
|
1693
1686
|
}
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
:
|
|
1697
|
-
|
|
1698
|
-
presentation: baseReceipt ? 'feishu-text-and-files' : 'feishu-files',
|
|
1699
|
-
receipts,
|
|
1700
|
-
});
|
|
1701
|
-
return { receipt, failureNoticeVisible };
|
|
1687
|
+
return {
|
|
1688
|
+
receipt: delivery.receipt,
|
|
1689
|
+
failureNoticeVisible: delivery.failureNoticeVisible,
|
|
1690
|
+
};
|
|
1702
1691
|
}
|
|
1703
1692
|
|
|
1704
1693
|
async #answerWithStream(event, key, message) {
|
|
@@ -1896,18 +1885,18 @@ export class FeishuHarnessBridge {
|
|
|
1896
1885
|
|
|
1897
1886
|
const text = extractText(event);
|
|
1898
1887
|
if (!text) {
|
|
1899
|
-
await this.#send(event.message.chat_id, '请用文字回答当前问题。');
|
|
1888
|
+
await this.#send(event.message.chat_id, t('请用文字回答当前问题。'));
|
|
1900
1889
|
return;
|
|
1901
1890
|
}
|
|
1902
1891
|
|
|
1903
1892
|
const pending = this.#pendingInteractions.get(key);
|
|
1904
1893
|
if (!pending || pending !== expected || pending.submitting) {
|
|
1905
1894
|
if (this.#isResolvedQuestionReply(event, key)) {
|
|
1906
|
-
await this.#send(event.message.chat_id, INTERACTION_RESOLVED_TEXT).catch(() => undefined);
|
|
1895
|
+
await this.#send(event.message.chat_id, INTERACTION_RESOLVED_TEXT()).catch(() => undefined);
|
|
1907
1896
|
return;
|
|
1908
1897
|
}
|
|
1909
1898
|
if (claimed && (!pending || pending !== expected)) {
|
|
1910
|
-
await this.#send(event.message.chat_id, INTERACTION_RESOLVED_TEXT);
|
|
1899
|
+
await this.#send(event.message.chat_id, INTERACTION_RESOLVED_TEXT());
|
|
1911
1900
|
return;
|
|
1912
1901
|
}
|
|
1913
1902
|
return this.#enqueueMessage(event, messageId, key, processingReaction, {
|
|
@@ -1965,7 +1954,7 @@ export class FeishuHarnessBridge {
|
|
|
1965
1954
|
if (error?.code === 'interaction-not-pending') {
|
|
1966
1955
|
this.#rememberResolvedInteraction(key, pending);
|
|
1967
1956
|
this.#clearPendingInteraction(key, pending.interactionId);
|
|
1968
|
-
await this.#send(event.message.chat_id, INTERACTION_RESOLVED_TEXT).catch(() => undefined);
|
|
1957
|
+
await this.#send(event.message.chat_id, INTERACTION_RESOLVED_TEXT()).catch(() => undefined);
|
|
1969
1958
|
return;
|
|
1970
1959
|
}
|
|
1971
1960
|
pending.submitting = false;
|
|
@@ -1973,7 +1962,7 @@ export class FeishuHarnessBridge {
|
|
|
1973
1962
|
pending.index -= 1;
|
|
1974
1963
|
this.#status.lastError = '回答提交失败。';
|
|
1975
1964
|
this.#logger.error?.('[dsh-feishu] failed to answer a Harness interaction');
|
|
1976
|
-
await this.#send(event.message.chat_id, '回答提交失败,请重新发送当前问题的答案。')
|
|
1965
|
+
await this.#send(event.message.chat_id, t('回答提交失败,请重新发送当前问题的答案。'))
|
|
1977
1966
|
.catch(() => undefined);
|
|
1978
1967
|
}
|
|
1979
1968
|
}
|
|
@@ -2018,7 +2007,7 @@ export class FeishuHarnessBridge {
|
|
|
2018
2007
|
});
|
|
2019
2008
|
await this.#send(
|
|
2020
2009
|
chatId,
|
|
2021
|
-
'检测到这个 Session 中遗留的待回答问题,已安全取消并继续处理你刚才的消息。',
|
|
2010
|
+
t('检测到这个 Session 中遗留的待回答问题,已安全取消并继续处理你刚才的消息。'),
|
|
2022
2011
|
).catch(() => undefined);
|
|
2023
2012
|
return;
|
|
2024
2013
|
}
|
|
@@ -2120,7 +2109,7 @@ export class FeishuHarnessBridge {
|
|
|
2120
2109
|
await this.#state.markSeen(messageId);
|
|
2121
2110
|
this.#status.lastMessageAt = new Date().toISOString();
|
|
2122
2111
|
this.#status.messagesReceived += 1;
|
|
2123
|
-
await this.#send(event.message.chat_id, INTERACTION_RESOLVED_TEXT).catch(() => undefined);
|
|
2112
|
+
await this.#send(event.message.chat_id, INTERACTION_RESOLVED_TEXT()).catch(() => undefined);
|
|
2124
2113
|
}
|
|
2125
2114
|
|
|
2126
2115
|
#takePendingInteraction(key, interactionId) {
|
|
@@ -2160,10 +2149,10 @@ export class FeishuHarnessBridge {
|
|
|
2160
2149
|
#progressText(update) {
|
|
2161
2150
|
if (update.type === 'text' && update.text) return update.text;
|
|
2162
2151
|
if (update.type === 'tool') {
|
|
2163
|
-
if (update.name === 'web_search') return '_正在搜索网络并整理信息…_';
|
|
2164
|
-
return
|
|
2152
|
+
if (update.name === 'web_search') return t('_正在搜索网络并整理信息…_');
|
|
2153
|
+
return t('_正在使用 {name}…_', { name: update.name || t('工具') });
|
|
2165
2154
|
}
|
|
2166
|
-
return
|
|
2155
|
+
return t('_{text}_', { text: update.text || t('正在处理…') });
|
|
2167
2156
|
}
|
|
2168
2157
|
|
|
2169
2158
|
async #addReaction(messageId, emojiType) {
|