@goodandready/dsh-messenger-gateway 0.3.19 → 0.3.20

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/lib/commands.js CHANGED
@@ -1,47 +1,70 @@
1
- export const DEFAULT_TELEGRAM_COMMANDS = [
2
- { command: 'start', description: 'Подключение и приветствие' },
3
- { command: 'help', description: 'Список команд' },
4
- { command: 'new', description: 'Новая сессия агента' },
5
- { command: 'whoami', description: 'Показать ваш Telegram id' },
6
- { command: 'stop', description: 'Прервать текущий ответ' },
7
- { command: 'pair', description: 'Одобрить код сопряжения: /pair CODE' },
8
- { command: 'sethome', description: 'Home: /sethome [name]' },
9
- { command: 'home', description: 'Список home-каналов' },
10
- { command: 'model', description: 'Интерактивный выбор или смена модели' },
11
- { command: 'top', description: 'Ресурсы сервера: CPU, RAM, uptime, сессии' },
12
- { command: 'topic', description: 'Создать новый форум-топик под задачу: /topic <имя>' },
13
- { command: 'status', description: 'Статус шлюза' },
14
- { command: 'setalert', description: 'Назначить этот чат каналом алертов' },
15
- { command: 'alert', description: 'Статус канала алертов: /alert [test]' },
16
- { command: 'keyboard', description: 'Клавиатура быстрых действий: /keyboard on|off' },
17
- { command: 'role', description: 'Роль и персона агента: /role [name]' },
18
- { command: 'skills', description: 'Список активных инструментов и навыков' },
19
- { command: 'tools', description: 'Список активных инструментов' },
20
- { command: 'fork', description: 'Форк текущей сессии в новую ветку' },
21
- { command: 'export', description: 'Экспорт истории диалога в Markdown' },
22
- { command: 'rewind', description: 'Откат сообщений: /rewind [N]' },
23
- { command: 'files', description: 'Файловый менеджер рабочей папки: /files [dir]' },
24
- { command: 'get', description: 'Скачать файл в Telegram: /get <path>' },
25
- { command: 'remind', description: 'Напоминание: /remind <время> <текст>' },
26
- { command: 'voice', description: 'Голосовые ответы: /voice on|off|status' },
27
- { command: 'tts', description: 'Озвучка в этом чате: /tts on|off|status' },
28
- { command: 'mute', description: 'Не присылать уведомления в этот чат' },
29
- { command: 'unmute', description: 'Вернуть уведомления в этот чат' },
30
- ]
31
-
32
- export function normalizeTelegramCommands(commands) {
33
- const source = Array.isArray(commands) && commands.length ? commands : DEFAULT_TELEGRAM_COMMANDS
34
- const out = []
35
- const seen = new Set()
36
- for (const raw of source) {
37
- if (!raw || typeof raw !== 'object') continue
38
- let command = String(raw.command || '').trim().replace(/^\//, '')
39
- const description = String(raw.description || '').trim()
40
- if (!command || !description) continue
41
- command = command.slice(0, 32).toLowerCase()
42
- if (seen.has(command)) continue
43
- seen.add(command)
44
- out.push({ command, description: description.slice(0, 256) })
45
- }
46
- return out.length ? out : [...DEFAULT_TELEGRAM_COMMANDS]
47
- }
1
+ export const DEFAULT_TELEGRAM_COMMANDS = [
2
+ { command: 'start', description: 'Подключение и приветствие' },
3
+ { command: 'help', description: 'Список команд' },
4
+ { command: 'new', description: 'Новая сессия агента' },
5
+ { command: 'whoami', description: 'Показать ваш Telegram id' },
6
+ { command: 'stop', description: 'Прервать текущий ответ' },
7
+ { command: 'pair', description: 'Одобрить код сопряжения: /pair CODE' },
8
+ { command: 'sethome', description: 'Home: /sethome [name]' },
9
+ { command: 'home', description: 'Список home-каналов' },
10
+ { command: 'model', description: 'Интерактивный выбор или смена модели' },
11
+ { command: 'top', description: 'Ресурсы сервера: CPU, RAM, uptime, сессии' },
12
+ { command: 'topic', description: 'Создать новый форум-топик под задачу: /topic <имя>' },
13
+ { command: 'status', description: 'Статус шлюза' },
14
+ { command: 'setalert', description: 'Назначить этот чат каналом алертов' },
15
+ { command: 'alert', description: 'Статус канала алертов: /alert [test]' },
16
+ { command: 'keyboard', description: 'Клавиатура быстрых действий: /keyboard on|off' },
17
+ { command: 'role', description: 'Роль и персона агента: /role [name]' },
18
+ { command: 'skills', description: 'Список активных инструментов и навыков' },
19
+ { command: 'tools', description: 'Список активных инструментов' },
20
+ { command: 'fork', description: 'Форк текущей сессии в новую ветку' },
21
+ { command: 'export', description: 'Экспорт истории диалога в Markdown' },
22
+ { command: 'rewind', description: 'Откат сообщений: /rewind [N]' },
23
+ { command: 'files', description: 'Файловый менеджер рабочей папки: /files [dir]' },
24
+ { command: 'get', description: 'Скачать файл в Telegram: /get <path>' },
25
+ { command: 'remind', description: 'Напоминание: /remind <время> <текст>' },
26
+ { command: 'voice', description: 'Голосовые ответы: /voice on|off|status' },
27
+ { command: 'tts', description: 'Озвучка в этом чате: /tts on|off|status' },
28
+ { command: 'mute', description: 'Не присылать уведомления в этот чат' },
29
+ { command: 'unmute', description: 'Вернуть уведомления в этот чат' },
30
+ ]
31
+
32
+ export function normalizeTelegramCommands(commands) {
33
+ const source = Array.isArray(commands) && commands.length ? commands : DEFAULT_TELEGRAM_COMMANDS
34
+ const out = []
35
+ const seen = new Set()
36
+ for (const raw of source) {
37
+ if (!raw || typeof raw !== 'object') continue
38
+ let command = String(raw.command || '').trim().replace(/^\//, '')
39
+ const description = String(raw.description || '').trim()
40
+ if (!command || !description) continue
41
+ command = command.slice(0, 32).toLowerCase()
42
+ if (seen.has(command)) continue
43
+ seen.add(command)
44
+ out.push({ command, description: description.slice(0, 256) })
45
+ }
46
+ return out.length ? out : [...DEFAULT_TELEGRAM_COMMANDS]
47
+ }
48
+
49
+ export function mergeDynamicCommands(baseCommands = DEFAULT_TELEGRAM_COMMANDS, dynamicCommands = [], maxTotal = 100) {
50
+ const normalizedBase = normalizeTelegramCommands(baseCommands)
51
+ const out = [...normalizedBase]
52
+ const seen = new Set(out.map((c) => c.command))
53
+
54
+ for (const item of dynamicCommands || []) {
55
+ if (out.length >= maxTotal) break
56
+ if (!item || typeof item !== 'object') continue
57
+ let command = String(item.command || item.name || '').trim().replace(/^\//, '').toLowerCase()
58
+ // Telegram format: lowercase alphanumeric and underscore only, 1-32 chars
59
+ command = command.replace(/[^a-z0-9_]/g, '_').slice(0, 32)
60
+ if (!command || seen.has(command)) continue
61
+ let description = String(item.description || item.title || `Скилл ${command}`).trim()
62
+ if (!description) description = `Скилл ${command}`
63
+ description = description.slice(0, 256)
64
+ seen.add(command)
65
+ out.push({ command, description })
66
+ }
67
+
68
+ return out.slice(0, maxTotal)
69
+ }
70
+
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
- discord: z.object({
60
- enabled: z.boolean().default(false),
61
- botToken: z.string().role('secret').default(''),
62
- webhookUrl: z.string().default(''),
63
- }).default({ enabled: false }),
64
- slack: z.object({
65
- enabled: z.boolean().default(false),
66
- botToken: z.string().role('secret').default(''),
67
- webhookUrl: z.string().default(''),
68
- }).default({ enabled: false }),
69
- webhooks: z.object({
70
- enabled: z.boolean().default(true),
71
- secret: z.string().role('secret').default(''),
72
- }).default({ enabled: true }),
73
- media: z.object({
74
- cacheDir: z.string().default(''),
75
- maxDocBytes: z.number().default(20 * 1024 * 1024),
76
- maxTextInjectBytes: z.number().default(100 * 1024),
77
- maxImageBytes: z.number().default(20 * 1024 * 1024),
78
- }),
79
- tts: z.object({
80
- enabled: z.boolean().default(false),
81
- maxChars: z.number().default(4000),
82
- voiceSummary: z.boolean().default(false).description('Speak concise TL;DR summary while full text is sent to chat'),
83
- }),
84
- agent: z.object({
85
- provider: z.string().default(''),
86
- model: z.string().default(''),
87
- cwd: z.string().default(''),
88
- instructionPrefix: z.string().default(''),
89
- maxMessageLength: z.number().default(4000),
90
- idleTimeoutMs: z.number().default(3_600_000),
91
- turnTimeoutMs: z.number().default(600_000),
92
- photoOnlyMode: z.union([z.const('prompt'), z.const('run')]).default('prompt'),
93
- sessionScope: z.union([z.const('chat'), z.const('user')]).default('user')
94
- .description('In groups: user = per-user session; chat = shared session for the chat/topic'),
95
- }),
96
- })
97
-
98
- export const Config = PluginConfig
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
+ 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