@goodandready/dsh-messenger-gateway 0.3.19 → 0.3.21
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.md +40 -2
- package/README.ru.md +10 -0
- package/README.zh.md +10 -0
- package/lib/adapters/discord.js +65 -11
- package/lib/adapters/slack.js +72 -12
- package/lib/adapters/telegram.js +708 -690
- package/lib/alerts.js +10 -10
- package/lib/artifacts.js +117 -117
- package/lib/ask.js +151 -151
- package/lib/client.js +104 -104
- package/lib/commands.js +73 -47
- package/lib/config.js +104 -98
- package/lib/content-guard.js +14 -14
- package/lib/documents.js +9 -9
- package/lib/file-manager.js +9 -9
- package/lib/gateway.js +1737 -1406
- package/lib/index.js +500 -488
- package/lib/locales/en.js +114 -0
- package/lib/locales/index.js +21 -0
- package/lib/locales/zh.js +114 -0
- package/lib/models.js +1 -1
- package/lib/personas.js +67 -14
- package/lib/photos.js +2 -2
- package/lib/scheduler.js +159 -142
- package/lib/stream.js +4 -2
- package/lib/telegram-errors.js +0 -0
- package/lib/text.js +39 -39
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -116,107 +116,107 @@ window.__ModuleLoader__.load({
|
|
|
116
116
|
'stats.active_chats': 'Active Chats in Memory',
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
const
|
|
119
|
+
const zh = {
|
|
120
120
|
title: 'Messenger gateway',
|
|
121
|
-
description: 'Telegram
|
|
122
|
-
'settings.loading': '
|
|
123
|
-
'settings.retry': '
|
|
124
|
-
'settings.unavailable': '
|
|
125
|
-
'header.title': '
|
|
126
|
-
'header.sub': '
|
|
127
|
-
'badge.online': 'Telegram
|
|
128
|
-
'badge.running': '
|
|
129
|
-
'badge.stopped': '
|
|
130
|
-
'badge.token_ok': '
|
|
131
|
-
'badge.token_missing': '
|
|
132
|
-
'badge.pairing_count': '
|
|
133
|
-
'badge.transport': '
|
|
134
|
-
save: '
|
|
135
|
-
saved: '
|
|
136
|
-
saving: '
|
|
137
|
-
showAdvanced: '⚙️
|
|
138
|
-
hideAdvanced: '
|
|
139
|
-
telegram: '
|
|
140
|
-
telegramSub: '
|
|
141
|
-
agent: '
|
|
142
|
-
agentSub: '
|
|
143
|
-
media: '
|
|
144
|
-
mediaSub: '
|
|
145
|
-
tts: '
|
|
146
|
-
ttsSub: '
|
|
147
|
-
advanced: '
|
|
148
|
-
botToken: '
|
|
149
|
-
botTokenHint: '
|
|
150
|
-
allowedIds: '
|
|
151
|
-
allowedIdsHint: 'ID
|
|
152
|
-
textFormat: '
|
|
153
|
-
textFormatHtml: 'Markdown → Telegram HTML (
|
|
154
|
-
textFormatPlain: '
|
|
155
|
-
disableTelegram: '
|
|
156
|
-
enableTelegram: '
|
|
157
|
-
provider: '
|
|
158
|
-
providerHint: '
|
|
159
|
-
model: '
|
|
160
|
-
modelHint: '
|
|
161
|
-
photoOnly: '
|
|
162
|
-
photoPrompt: '
|
|
163
|
-
photoRun: '
|
|
164
|
-
instruction: '
|
|
165
|
-
instructionHint: '
|
|
166
|
-
instructionPlaceholder: '
|
|
167
|
-
maxMessageLength: '
|
|
168
|
-
turnTimeout: '
|
|
169
|
-
maxDocMb: '
|
|
170
|
-
maxImageMb: '
|
|
171
|
-
maxTextKb: '
|
|
172
|
-
ttsEnable: '
|
|
173
|
-
ttsMaxChars: '
|
|
174
|
-
pollTimeout: '
|
|
175
|
-
pollInterval: '
|
|
176
|
-
idleTimeout: '
|
|
177
|
-
pairing: '
|
|
178
|
-
pairingSub: '
|
|
179
|
-
pairingEmpty: '
|
|
180
|
-
pairingApprove: '
|
|
181
|
-
pairingReject: '
|
|
182
|
-
pairingRefresh: '
|
|
183
|
-
groupsEnable: '
|
|
184
|
-
groupMention: '
|
|
185
|
-
reactionsEnable: '
|
|
186
|
-
progressEnable: '
|
|
187
|
-
statusIndicator: '
|
|
188
|
-
statusIndicatorHint: '
|
|
189
|
-
transport: '
|
|
190
|
-
transportPoll: '
|
|
121
|
+
description: 'Telegram 机器人:文本、语音、图片、文档和交互式询问。',
|
|
122
|
+
'settings.loading': '正在加载 Messenger gateway 设置…',
|
|
123
|
+
'settings.retry': '重试',
|
|
124
|
+
'settings.unavailable': '设置不可用(主机命名空间未就绪)。',
|
|
125
|
+
'header.title': 'Telegram 与多消息平台网关',
|
|
126
|
+
'header.sub': 'DeepSeek Harness 与 Telegram 之间的桥梁:会话、转向提示、论坛话题、内联按钮和可选语音。',
|
|
127
|
+
'badge.online': 'Telegram 在线 (@{username})',
|
|
128
|
+
'badge.running': '运行中',
|
|
129
|
+
'badge.stopped': '已停止',
|
|
130
|
+
'badge.token_ok': '令牌有效 ✓',
|
|
131
|
+
'badge.token_missing': '缺少令牌',
|
|
132
|
+
'badge.pairing_count': '配对: {count}',
|
|
133
|
+
'badge.transport': '传输方式: {mode}',
|
|
134
|
+
save: '保存更改',
|
|
135
|
+
saved: '设置保存成功',
|
|
136
|
+
saving: '保存中…',
|
|
137
|
+
showAdvanced: '⚙️ 高级连接与轮询设置',
|
|
138
|
+
hideAdvanced: '隐藏高级设置',
|
|
139
|
+
telegram: 'Telegram 机器人设置',
|
|
140
|
+
telegramSub: '主机器人凭证、白名单与消息发送设置。',
|
|
141
|
+
agent: '智能体与推理行为',
|
|
142
|
+
agentSub: '模型路由、附加指令与轮次超时设置。',
|
|
143
|
+
media: '媒体与文件限制',
|
|
144
|
+
mediaSub: '文档提取与图片上传限制。',
|
|
145
|
+
tts: '语音回复 (dsh-tts)',
|
|
146
|
+
ttsSub: '为 Telegram 语音消息合成语音回复。',
|
|
147
|
+
advanced: '连接与轮询内部设置',
|
|
148
|
+
botToken: 'Telegram 机器人令牌',
|
|
149
|
+
botTokenHint: '从 @BotFather 获取。留空保持现有配置。',
|
|
150
|
+
allowedIds: '允许的 Telegram 用户 ID',
|
|
151
|
+
allowedIdsHint: '逗号分隔的用户 ID 列表。留空允许所有用户(启用配对请求)。',
|
|
152
|
+
textFormat: '文本格式化渲染',
|
|
153
|
+
textFormatHtml: 'Markdown → Telegram HTML (推荐)',
|
|
154
|
+
textFormatPlain: '纯文本 (无格式化)',
|
|
155
|
+
disableTelegram: '停用 Telegram 机器人',
|
|
156
|
+
enableTelegram: '启用 Telegram 机器人',
|
|
157
|
+
provider: '提供商覆盖',
|
|
158
|
+
providerHint: '留空使用 DSH 默认提供商',
|
|
159
|
+
model: '模型覆盖',
|
|
160
|
+
modelHint: '留空使用 DSH 默认模型',
|
|
161
|
+
photoOnly: '无附言图片处理方式',
|
|
162
|
+
photoPrompt: '等待问题 (推荐)',
|
|
163
|
+
photoRun: '立即回复',
|
|
164
|
+
instruction: '额外智能体指令',
|
|
165
|
+
instructionHint: '添加到每条来自网关的消息前',
|
|
166
|
+
instructionPlaceholder: '留空 = 内置指令',
|
|
167
|
+
maxMessageLength: '最大回复长度 (字符)',
|
|
168
|
+
turnTimeout: '智能体轮次超时 (秒)',
|
|
169
|
+
maxDocMb: '最大文档大小 (MB)',
|
|
170
|
+
maxImageMb: '最大图片大小 (MB)',
|
|
171
|
+
maxTextKb: '文档提取最大文本量 (KB)',
|
|
172
|
+
ttsEnable: '启用语音回复 (需 dsh-tts)',
|
|
173
|
+
ttsMaxChars: '每条语音消息最大字数',
|
|
174
|
+
pollTimeout: '长轮询超时 (秒)',
|
|
175
|
+
pollInterval: '长轮询间隔 (毫秒)',
|
|
176
|
+
idleTimeout: '空闲会话超时 (秒)',
|
|
177
|
+
pairing: '配对请求与授权',
|
|
178
|
+
pairingSub: '受限白名单下授权新用户。',
|
|
179
|
+
pairingEmpty: '暂无待处理配对码',
|
|
180
|
+
pairingApprove: '批准',
|
|
181
|
+
pairingReject: '拒绝',
|
|
182
|
+
pairingRefresh: '刷新列表',
|
|
183
|
+
groupsEnable: '在群组和超级群组中响应',
|
|
184
|
+
groupMention: '群组中需 @提及、回复或 /命令',
|
|
185
|
+
reactionsEnable: '处理中添加 👀 表情反应',
|
|
186
|
+
progressEnable: '进度状态提示 (思考中 → 删除 → 最终回复)',
|
|
187
|
+
statusIndicator: '机器人状态描述指示器 (Online/Offline)',
|
|
188
|
+
statusIndicatorHint: '通过 Telegram API 设置机器人简介(所有人可见)。',
|
|
189
|
+
transport: 'Telegram 传输模式',
|
|
190
|
+
transportPoll: '长轮询 (getUpdates)',
|
|
191
191
|
transportWebhook: 'Webhook (HTTPS)',
|
|
192
|
-
webhookUrl: '
|
|
193
|
-
webhookUrlHint: '
|
|
194
|
-
webhookSecret: '
|
|
195
|
-
webhookSecretHint: '
|
|
196
|
-
sessionScope: '
|
|
197
|
-
sessionScopeChat: '
|
|
198
|
-
sessionScopeUser: '
|
|
199
|
-
voiceMode: '
|
|
200
|
-
voiceModeMirror: '
|
|
201
|
-
voiceModeAlways: '
|
|
202
|
-
voiceModeOff: '
|
|
203
|
-
notifyBridge: '
|
|
204
|
-
notifyBridgeHint: '
|
|
205
|
-
notifyHome: '
|
|
206
|
-
quickActions: '
|
|
207
|
-
artifactPreviews: '
|
|
208
|
-
'diag.title': '⚡
|
|
209
|
-
'diag.desc': '
|
|
210
|
-
'diag.smoke_btn': '
|
|
211
|
-
'diag.smoke_testing': '
|
|
212
|
-
'diag.smoke_ok': '
|
|
213
|
-
'diag.smoke_err': '
|
|
214
|
-
'stats.title': '📊
|
|
215
|
-
'stats.desc': '
|
|
216
|
-
'stats.sent': '
|
|
217
|
-
'stats.errors': '
|
|
218
|
-
'stats.uptime': '
|
|
219
|
-
'stats.active_chats': '
|
|
192
|
+
webhookUrl: '公开 Webhook HTTPS 地址',
|
|
193
|
+
webhookUrlHint: 'Telegram 投递更新的公开端点',
|
|
194
|
+
webhookSecret: 'Webhook 密钥令牌',
|
|
195
|
+
webhookSecretHint: '留空保持当前配置的密钥',
|
|
196
|
+
sessionScope: '群组会话范围',
|
|
197
|
+
sessionScopeChat: '每个聊天/话题共享',
|
|
198
|
+
sessionScopeUser: '每用户独立会话 (推荐)',
|
|
199
|
+
voiceMode: '语音回复模式',
|
|
200
|
+
voiceModeMirror: '镜像输入语音 (仅语音输入时回复语音)',
|
|
201
|
+
voiceModeAlways: '始终合成语音回复',
|
|
202
|
+
voiceModeOff: '关闭 (除非通过 /voice 启用)',
|
|
203
|
+
notifyBridge: '通知网桥 → Telegram Home',
|
|
204
|
+
notifyBridgeHint: '将 Web 会话的 task_done/error 事件转发至指定频道/话题',
|
|
205
|
+
notifyHome: '目标频道名称 (Home)',
|
|
206
|
+
quickActions: '快捷操作键盘 (/new, /stop, /voice, /status)',
|
|
207
|
+
artifactPreviews: '在 Telegram 中预览图表与表格',
|
|
208
|
+
'diag.title': '⚡ 诊断与连通性检查 (Smoke test)',
|
|
209
|
+
'diag.desc': '检查 Telegram Bot API 连接、测量延迟并验证机器人名称。',
|
|
210
|
+
'diag.smoke_btn': '测试 Telegram 连接 (Ping)',
|
|
211
|
+
'diag.smoke_testing': '正在连接 Telegram API…',
|
|
212
|
+
'diag.smoke_ok': 'Telegram API 连接成功!延迟: {latency} 毫秒 (@{username}, ID: {id})',
|
|
213
|
+
'diag.smoke_err': '连通性检查失败: {error}',
|
|
214
|
+
'stats.title': '📊 网关遥测与计数器',
|
|
215
|
+
'stats.desc': '运行中网关进程的实时统计。',
|
|
216
|
+
'stats.sent': '已发送消息',
|
|
217
|
+
'stats.errors': '发送错误数',
|
|
218
|
+
'stats.uptime': '运行时间',
|
|
219
|
+
'stats.active_chats': '内存中活跃聊天数',
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
function useActiveLocale(ctx) {
|
|
@@ -234,7 +234,7 @@ window.__ModuleLoader__.load({
|
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
function makeT(locale) {
|
|
237
|
-
const DICT = String(locale || '').startsWith('
|
|
237
|
+
const DICT = String(locale || '').startsWith('zh') ? zh : en
|
|
238
238
|
return (key, params) => {
|
|
239
239
|
let text = DICT[key] || en[key] || key
|
|
240
240
|
if (params && typeof params === 'object') {
|
|
@@ -1243,20 +1243,20 @@ window.__ModuleLoader__.load({
|
|
|
1243
1243
|
try {
|
|
1244
1244
|
const loc = ctx?.get?.('locale') || ctx?.locale
|
|
1245
1245
|
if (loc?.register) {
|
|
1246
|
-
loc.register(NS, { en,
|
|
1246
|
+
loc.register(NS, { en, zh })
|
|
1247
1247
|
}
|
|
1248
1248
|
} catch (taken) {
|
|
1249
|
-
console.warn('[dsh-messenger-gateway]
|
|
1249
|
+
console.warn('[dsh-messenger-gateway] dictionary already registered:', taken && taken.message)
|
|
1250
1250
|
}
|
|
1251
1251
|
}, 'dsh-messenger-gateway: dictionaries')
|
|
1252
1252
|
} else {
|
|
1253
1253
|
const loc = ctx?.get?.('locale') || ctx?.locale
|
|
1254
1254
|
if (loc?.register) {
|
|
1255
|
-
loc.register(NS, { en,
|
|
1255
|
+
loc.register(NS, { en, zh })
|
|
1256
1256
|
}
|
|
1257
1257
|
}
|
|
1258
1258
|
} catch (err) {
|
|
1259
|
-
console.warn('[dsh-messenger-gateway]
|
|
1259
|
+
console.warn('[dsh-messenger-gateway] dictionary registration error:', err && err.message)
|
|
1260
1260
|
}
|
|
1261
1261
|
|
|
1262
1262
|
if (typeof ctx.effect === 'function') {
|
package/lib/commands.js
CHANGED
|
@@ -1,47 +1,73 @@
|
|
|
1
|
-
export const DEFAULT_TELEGRAM_COMMANDS = [
|
|
2
|
-
{ command: 'start', description: '
|
|
3
|
-
{ command: 'help', description: '
|
|
4
|
-
{ command: 'new', description: '
|
|
5
|
-
{ command: 'whoami', description: '
|
|
6
|
-
{ command: 'stop', description: '
|
|
7
|
-
{ command: 'pair', description: '
|
|
8
|
-
{ command: 'sethome', description: '
|
|
9
|
-
{ command: 'home', description: '
|
|
10
|
-
{ command: 'model', description: '
|
|
11
|
-
{ command: 'top', description: '
|
|
12
|
-
{ command: 'topic', description: '
|
|
13
|
-
{ command: 'status', description: '
|
|
14
|
-
{ command: 'setalert', description: '
|
|
15
|
-
{ command: 'alert', description: '
|
|
16
|
-
{ command: 'keyboard', description: '
|
|
17
|
-
{ command: 'role', description: '
|
|
18
|
-
{ command: '
|
|
19
|
-
{ command: '
|
|
20
|
-
{ command: '
|
|
21
|
-
{ command: '
|
|
22
|
-
{ command: '
|
|
23
|
-
{ command: '
|
|
24
|
-
{ command: '
|
|
25
|
-
{ command: '
|
|
26
|
-
{ command: '
|
|
27
|
-
{ command: '
|
|
28
|
-
{ command: '
|
|
29
|
-
{ command: '
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (!
|
|
41
|
-
command = command.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
1
|
+
export const DEFAULT_TELEGRAM_COMMANDS = [
|
|
2
|
+
{ command: 'start', description: 'Connect and show greeting' },
|
|
3
|
+
{ command: 'help', description: 'Show commands help' },
|
|
4
|
+
{ command: 'new', description: 'Start a new agent session' },
|
|
5
|
+
{ command: 'whoami', description: 'Show your messenger user ID' },
|
|
6
|
+
{ command: 'stop', description: 'Interrupt current turn' },
|
|
7
|
+
{ command: 'pair', description: 'Approve pairing code: /pair CODE' },
|
|
8
|
+
{ command: 'sethome', description: 'Set home: /sethome [name]' },
|
|
9
|
+
{ command: 'home', description: 'List registered home channels' },
|
|
10
|
+
{ command: 'model', description: 'Interactive model selector' },
|
|
11
|
+
{ command: 'top', description: 'System resources: CPU, RAM, uptime' },
|
|
12
|
+
{ command: 'topic', description: 'Create forum topic: /topic <name>' },
|
|
13
|
+
{ command: 'status', description: 'Gateway and bot status' },
|
|
14
|
+
{ command: 'setalert', description: 'Designate channel for alerts' },
|
|
15
|
+
{ command: 'alert', description: 'Alert channel status: /alert [test]' },
|
|
16
|
+
{ command: 'keyboard', description: 'Toggle quick actions: /keyboard on|off' },
|
|
17
|
+
{ command: 'role', description: 'Switch persona: /role [name]' },
|
|
18
|
+
{ command: 'bind', description: 'Bind preset/persona to forum topic: /bind <name>' },
|
|
19
|
+
{ command: 'preset', description: 'Show or switch agent preset: /preset [name]' },
|
|
20
|
+
{ command: 'cron', description: 'Manage recurring reports: /cron' },
|
|
21
|
+
{ command: 'skills', description: 'List active tools & skills' },
|
|
22
|
+
{ command: 'tools', description: 'List active tools' },
|
|
23
|
+
{ command: 'fork', description: 'Fork session into new branch' },
|
|
24
|
+
{ command: 'export', description: 'Export history to Markdown' },
|
|
25
|
+
{ command: 'rewind', description: 'Rewind turns: /rewind [N]' },
|
|
26
|
+
{ command: 'files', description: 'Workspace file explorer: /files [dir]' },
|
|
27
|
+
{ command: 'get', description: 'Download file to messenger: /get <path>' },
|
|
28
|
+
{ command: 'remind', description: 'Set reminder: /remind <time> <text>' },
|
|
29
|
+
{ command: 'voice', description: 'Voice reply mode: /voice on|off|status' },
|
|
30
|
+
{ command: 'tts', description: 'Speech in this chat: /tts on|off|status' },
|
|
31
|
+
{ command: 'mute', description: 'Mute notifications in this chat' },
|
|
32
|
+
{ command: 'unmute', description: 'Unmute notifications in this chat' },
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
export function normalizeTelegramCommands(commands) {
|
|
36
|
+
const source = Array.isArray(commands) && commands.length ? commands : DEFAULT_TELEGRAM_COMMANDS
|
|
37
|
+
const out = []
|
|
38
|
+
const seen = new Set()
|
|
39
|
+
for (const raw of source) {
|
|
40
|
+
if (!raw || typeof raw !== 'object') continue
|
|
41
|
+
let command = String(raw.command || '').trim().replace(/^\//, '')
|
|
42
|
+
const description = String(raw.description || '').trim()
|
|
43
|
+
if (!command || !description) continue
|
|
44
|
+
command = command.slice(0, 32).toLowerCase()
|
|
45
|
+
if (seen.has(command)) continue
|
|
46
|
+
seen.add(command)
|
|
47
|
+
out.push({ command, description: description.slice(0, 256) })
|
|
48
|
+
}
|
|
49
|
+
return out.length ? out : [...DEFAULT_TELEGRAM_COMMANDS]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function mergeDynamicCommands(baseCommands = DEFAULT_TELEGRAM_COMMANDS, dynamicCommands = [], maxTotal = 100) {
|
|
53
|
+
const normalizedBase = normalizeTelegramCommands(baseCommands)
|
|
54
|
+
const out = [...normalizedBase]
|
|
55
|
+
const seen = new Set(out.map((c) => c.command))
|
|
56
|
+
|
|
57
|
+
for (const item of dynamicCommands || []) {
|
|
58
|
+
if (out.length >= maxTotal) break
|
|
59
|
+
if (!item || typeof item !== 'object') continue
|
|
60
|
+
let command = String(item.command || item.name || '').trim().replace(/^\//, '').toLowerCase()
|
|
61
|
+
// Telegram format: lowercase alphanumeric and underscore only, 1-32 chars
|
|
62
|
+
command = command.replace(/[^a-z0-9_]/g, '_').slice(0, 32)
|
|
63
|
+
if (!command || seen.has(command)) continue
|
|
64
|
+
let description = String(item.description || item.title || `Skill ${command}`).trim()
|
|
65
|
+
if (!description) description = `Skill ${command}`
|
|
66
|
+
description = description.slice(0, 256)
|
|
67
|
+
seen.add(command)
|
|
68
|
+
out.push({ command, description })
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return out.slice(0, maxTotal)
|
|
72
|
+
}
|
|
73
|
+
|
package/lib/config.js
CHANGED
|
@@ -1,98 +1,104 @@
|
|
|
1
|
-
import z from '@deepseek-ai/schemastery'
|
|
2
|
-
|
|
3
|
-
const HomeEntry = z.object({
|
|
4
|
-
name: z.string().default('default'),
|
|
5
|
-
chatId: z.union([z.number(), z.string()]),
|
|
6
|
-
threadId: z.number().default(0),
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
export const PluginConfig = z.object({
|
|
10
|
-
enabled: z.boolean().default(true),
|
|
11
|
-
internalBaseURL: z.string().default('http://127.0.0.1:3080'),
|
|
12
|
-
telegram: z.object({
|
|
13
|
-
enabled: z.boolean().default(false),
|
|
14
|
-
botToken: z.string().role('secret').default(''),
|
|
15
|
-
allowedUserIds: z.array(z.number()).default([]),
|
|
16
|
-
pollTimeoutSeconds: z.number().default(50),
|
|
17
|
-
pollIntervalMs: z.number().default(500),
|
|
18
|
-
commands: z.array(z.object({
|
|
19
|
-
command: z.string(),
|
|
20
|
-
description: z.string(),
|
|
21
|
-
})).default([]),
|
|
22
|
-
textFormat: z.union([z.const('html'), z.const('plain')]).default('html').description('html: Markdown→Telegram HTML; plain:
|
|
23
|
-
homeChatId: z.union([z.number(), z.string()]).default('').description('Legacy default home chatId'),
|
|
24
|
-
homeThreadId: z.number().default(0),
|
|
25
|
-
homes: z.array(HomeEntry).default([]).description('Named homes (forum topics / channels)'),
|
|
26
|
-
pairingEnabled: z.boolean().default(true).description('Issue pairing codes to unknown users when allowlist is non-empty'),
|
|
27
|
-
streaming: z.boolean().default(false).description('If true, edit one Telegram message while tokens arrive; default off'),
|
|
28
|
-
streamEditIntervalMs: z.number().default(1200),
|
|
29
|
-
progressEnabled: z.boolean().default(true),
|
|
30
|
-
approvalsEnabled: z.boolean().default(true),
|
|
31
|
-
groupsEnabled: z.boolean().default(true).description('Process group/supergroup messages'),
|
|
32
|
-
groupRequireMention: z.boolean().default(true).description('In groups, only respond to @mention, reply-to-bot, or /commands'),
|
|
33
|
-
reactionsEnabled: z.boolean().default(true).description('React with 👀 while processing a turn'),
|
|
34
|
-
statusIndicator: z.boolean().default(false).description('Opt-in: set bot short description to Online/Offline (bots have no presence)'),
|
|
35
|
-
statusOnline: z.string().default('Online'),
|
|
36
|
-
statusOffline: z.string().default('Offline'),
|
|
37
|
-
transport: z.union([z.const('poll'), z.const('webhook')]).default('poll'),
|
|
38
|
-
webhookUrl: z.string().default(''),
|
|
39
|
-
webhookSecret: z.string().role('secret').default(''),
|
|
40
|
-
webhookPath: z.string().default('/dsh-messenger-gateway/telegram/webhook'),
|
|
41
|
-
voiceMode: z.union([z.const('mirror'), z.const('always'), z.const('off')]).default('mirror')
|
|
42
|
-
.description('mirror: TTS when inbound was voice; always/off override (per-user /voice wins)'),
|
|
43
|
-
quickActions: z.boolean().default(false).description('Show quick actions keyboard in Telegram (/new, /stop, /voice, /status)'),
|
|
44
|
-
artifactPreviews: z.boolean().default(true).description('Render diagrams and formatted tables as previews'),
|
|
45
|
-
notifyBridge: z.object({
|
|
46
|
-
enabled: z.boolean().default(false),
|
|
47
|
-
events: z.array(z.string()).default(['task_done', 'error']),
|
|
48
|
-
home: z.string().default('default'),
|
|
49
|
-
excludeSessionPrefixes: z.array(z.string()).default(['msgw-']),
|
|
50
|
-
}).default({ enabled: false }),
|
|
51
|
-
alerts: z.object({
|
|
52
|
-
enabled: z.boolean().default(false),
|
|
53
|
-
chatId: z.union([z.number(), z.string()]).default(''),
|
|
54
|
-
threadId: z.number().default(0),
|
|
55
|
-
home: z.string().default(''),
|
|
56
|
-
events: z.array(z.string()).default(['error', 'pairing']),
|
|
57
|
-
}).default({ enabled: false }),
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}),
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
1
|
+
import z from '@deepseek-ai/schemastery'
|
|
2
|
+
|
|
3
|
+
const HomeEntry = z.object({
|
|
4
|
+
name: z.string().default('default'),
|
|
5
|
+
chatId: z.union([z.number(), z.string()]),
|
|
6
|
+
threadId: z.number().default(0),
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
export const PluginConfig = z.object({
|
|
10
|
+
enabled: z.boolean().default(true),
|
|
11
|
+
internalBaseURL: z.string().default('http://127.0.0.1:3080'),
|
|
12
|
+
telegram: z.object({
|
|
13
|
+
enabled: z.boolean().default(false),
|
|
14
|
+
botToken: z.string().role('secret').default(''),
|
|
15
|
+
allowedUserIds: z.array(z.number()).default([]),
|
|
16
|
+
pollTimeoutSeconds: z.number().default(50),
|
|
17
|
+
pollIntervalMs: z.number().default(500),
|
|
18
|
+
commands: z.array(z.object({
|
|
19
|
+
command: z.string(),
|
|
20
|
+
description: z.string(),
|
|
21
|
+
})).default([]),
|
|
22
|
+
textFormat: z.union([z.const('html'), z.const('plain')]).default('html').description('html: Markdown→Telegram HTML; plain: unformatted plain text'),
|
|
23
|
+
homeChatId: z.union([z.number(), z.string()]).default('').description('Legacy default home chatId'),
|
|
24
|
+
homeThreadId: z.number().default(0),
|
|
25
|
+
homes: z.array(HomeEntry).default([]).description('Named homes (forum topics / channels)'),
|
|
26
|
+
pairingEnabled: z.boolean().default(true).description('Issue pairing codes to unknown users when allowlist is non-empty'),
|
|
27
|
+
streaming: z.boolean().default(false).description('If true, edit one Telegram message while tokens arrive; default off'),
|
|
28
|
+
streamEditIntervalMs: z.number().default(1200),
|
|
29
|
+
progressEnabled: z.boolean().default(true),
|
|
30
|
+
approvalsEnabled: z.boolean().default(true),
|
|
31
|
+
groupsEnabled: z.boolean().default(true).description('Process group/supergroup messages'),
|
|
32
|
+
groupRequireMention: z.boolean().default(true).description('In groups, only respond to @mention, reply-to-bot, or /commands'),
|
|
33
|
+
reactionsEnabled: z.boolean().default(true).description('React with 👀 while processing a turn'),
|
|
34
|
+
statusIndicator: z.boolean().default(false).description('Opt-in: set bot short description to Online/Offline (bots have no presence)'),
|
|
35
|
+
statusOnline: z.string().default('Online'),
|
|
36
|
+
statusOffline: z.string().default('Offline'),
|
|
37
|
+
transport: z.union([z.const('poll'), z.const('webhook')]).default('poll'),
|
|
38
|
+
webhookUrl: z.string().default(''),
|
|
39
|
+
webhookSecret: z.string().role('secret').default(''),
|
|
40
|
+
webhookPath: z.string().default('/dsh-messenger-gateway/telegram/webhook'),
|
|
41
|
+
voiceMode: z.union([z.const('mirror'), z.const('always'), z.const('off')]).default('mirror')
|
|
42
|
+
.description('mirror: TTS when inbound was voice; always/off override (per-user /voice wins)'),
|
|
43
|
+
quickActions: z.boolean().default(false).description('Show quick actions keyboard in Telegram (/new, /stop, /voice, /status)'),
|
|
44
|
+
artifactPreviews: z.boolean().default(true).description('Render diagrams and formatted tables as previews'),
|
|
45
|
+
notifyBridge: z.object({
|
|
46
|
+
enabled: z.boolean().default(false),
|
|
47
|
+
events: z.array(z.string()).default(['task_done', 'error']),
|
|
48
|
+
home: z.string().default('default'),
|
|
49
|
+
excludeSessionPrefixes: z.array(z.string()).default(['msgw-']),
|
|
50
|
+
}).default({ enabled: false }),
|
|
51
|
+
alerts: z.object({
|
|
52
|
+
enabled: z.boolean().default(false),
|
|
53
|
+
chatId: z.union([z.number(), z.string()]).default(''),
|
|
54
|
+
threadId: z.number().default(0),
|
|
55
|
+
home: z.string().default(''),
|
|
56
|
+
events: z.array(z.string()).default(['error', 'pairing']),
|
|
57
|
+
}).default({ enabled: false }),
|
|
58
|
+
forumMirror: z.object({
|
|
59
|
+
enabled: z.boolean().default(false),
|
|
60
|
+
chatId: z.union([z.number(), z.string()]).default(''),
|
|
61
|
+
}).default({ enabled: false }),
|
|
62
|
+
forumMirrorChatId: z.union([z.number(), z.string()]).default(''),
|
|
63
|
+
forumMirrorEnabled: z.boolean().default(false),
|
|
64
|
+
}),
|
|
65
|
+
discord: z.object({
|
|
66
|
+
enabled: z.boolean().default(false),
|
|
67
|
+
botToken: z.string().role('secret').default(''),
|
|
68
|
+
webhookUrl: z.string().default(''),
|
|
69
|
+
}).default({ enabled: false }),
|
|
70
|
+
slack: z.object({
|
|
71
|
+
enabled: z.boolean().default(false),
|
|
72
|
+
botToken: z.string().role('secret').default(''),
|
|
73
|
+
webhookUrl: z.string().default(''),
|
|
74
|
+
}).default({ enabled: false }),
|
|
75
|
+
webhooks: z.object({
|
|
76
|
+
enabled: z.boolean().default(true),
|
|
77
|
+
secret: z.string().role('secret').default(''),
|
|
78
|
+
}).default({ enabled: true }),
|
|
79
|
+
media: z.object({
|
|
80
|
+
cacheDir: z.string().default(''),
|
|
81
|
+
maxDocBytes: z.number().default(20 * 1024 * 1024),
|
|
82
|
+
maxTextInjectBytes: z.number().default(100 * 1024),
|
|
83
|
+
maxImageBytes: z.number().default(20 * 1024 * 1024),
|
|
84
|
+
}),
|
|
85
|
+
tts: z.object({
|
|
86
|
+
enabled: z.boolean().default(false),
|
|
87
|
+
maxChars: z.number().default(4000),
|
|
88
|
+
voiceSummary: z.boolean().default(false).description('Speak concise TL;DR summary while full text is sent to chat'),
|
|
89
|
+
}),
|
|
90
|
+
agent: z.object({
|
|
91
|
+
provider: z.string().default(''),
|
|
92
|
+
model: z.string().default(''),
|
|
93
|
+
cwd: z.string().default(''),
|
|
94
|
+
instructionPrefix: z.string().default(''),
|
|
95
|
+
maxMessageLength: z.number().default(4000),
|
|
96
|
+
idleTimeoutMs: z.number().default(3_600_000),
|
|
97
|
+
turnTimeoutMs: z.number().default(600_000),
|
|
98
|
+
photoOnlyMode: z.union([z.const('prompt'), z.const('run')]).default('prompt'),
|
|
99
|
+
sessionScope: z.union([z.const('chat'), z.const('user')]).default('user')
|
|
100
|
+
.description('In groups: user = per-user session; chat = shared session for the chat/topic'),
|
|
101
|
+
}),
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
export const Config = PluginConfig
|
package/lib/content-guard.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Normalize any content value to ContentBlock[].
|
|
3
|
-
* DSH core (dsh-llm) calls content.some() without Array.isArray guard,
|
|
4
|
-
* so we must ensure every message we create or copy has array content.
|
|
5
|
-
* Refs: #51
|
|
6
|
-
*/
|
|
7
|
-
export function ensureContentArray(content) {
|
|
8
|
-
if (Array.isArray(content)) return content
|
|
9
|
-
if (typeof content === 'string') {
|
|
10
|
-
return content ? [{ type: 'text', text: content }] : [{ type: 'text', text: '(
|
|
11
|
-
}
|
|
12
|
-
if (content && typeof content === 'object' && content.type) return [content]
|
|
13
|
-
return [{ type: 'text', text: String(content ?? '(
|
|
14
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Normalize any content value to ContentBlock[].
|
|
3
|
+
* DSH core (dsh-llm) calls content.some() without Array.isArray guard,
|
|
4
|
+
* so we must ensure every message we create or copy has array content.
|
|
5
|
+
* Refs: #51
|
|
6
|
+
*/
|
|
7
|
+
export function ensureContentArray(content) {
|
|
8
|
+
if (Array.isArray(content)) return content
|
|
9
|
+
if (typeof content === 'string') {
|
|
10
|
+
return content ? [{ type: 'text', text: content }] : [{ type: 'text', text: '(empty message)' }]
|
|
11
|
+
}
|
|
12
|
+
if (content && typeof content === 'object' && content.type) return [content]
|
|
13
|
+
return [{ type: 'text', text: String(content ?? '(empty message)') }]
|
|
14
|
+
}
|