@goodandready/dsh-messenger-gateway 0.4.3 → 0.4.5

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,126 +1,126 @@
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
- { command: 'update', description: 'Check or install plugin update: /update [now]' },
34
- ]
35
-
36
- export function normalizeTelegramCommands(commands) {
37
- const source = Array.isArray(commands) && commands.length ? commands : DEFAULT_TELEGRAM_COMMANDS
38
- const out = []
39
- const seen = new Set()
40
- for (const raw of source) {
41
- if (!raw || typeof raw !== 'object') continue
42
- let command = String(raw.command || '').trim().replace(/^\//, '')
43
- const description = String(raw.description || '').trim()
44
- if (!command || !description) continue
45
- command = command.slice(0, 32).toLowerCase()
46
- if (seen.has(command)) continue
47
- seen.add(command)
48
- out.push({ command, description: description.slice(0, 256) })
49
- }
50
- return out.length ? out : [...DEFAULT_TELEGRAM_COMMANDS]
51
- }
52
-
53
- export function mergeDynamicCommands(baseCommands = DEFAULT_TELEGRAM_COMMANDS, dynamicCommands = [], maxTotal = 100) {
54
- const normalizedBase = normalizeTelegramCommands(baseCommands)
55
- const out = [...normalizedBase]
56
- const seen = new Set(out.map((c) => c.command))
57
-
58
- for (const item of dynamicCommands || []) {
59
- if (out.length >= maxTotal) break
60
- if (!item || typeof item !== 'object') continue
61
- let command = String(item.command || item.name || '').trim().replace(/^\//, '').toLowerCase()
62
- // Telegram format: lowercase alphanumeric and underscore only, 1-32 chars
63
- command = command.replace(/[^a-z0-9_]/g, '_').slice(0, 32)
64
- if (!command || seen.has(command)) continue
65
- let description = String(item.description || item.title || `Skill ${command}`).trim()
66
- if (!description) description = `Skill ${command}`
67
- description = description.slice(0, 256)
68
- seen.add(command)
69
- out.push({ command, description })
70
- }
71
-
72
- return out.slice(0, maxTotal)
73
- }
74
-
75
- export function buildQuickActionsKeyboard() {
76
- return {
77
- keyboard: [
78
- [{ text: '🔄 /new' }, { text: '🛑 /stop' }],
79
- [{ text: '🎙️ /voice' }, { text: '📊 /status' }],
80
- ],
81
- resize_keyboard: true,
82
- }
83
- }
84
-
85
- export const REMOVE_REPLY_KEYBOARD = { remove_keyboard: true }
86
-
87
- export const HELP_TEXT = [
88
- '📖 <b>Messenger Gateway Help:</b>',
89
- '',
90
- '💬 <b>Session & Chat:</b>',
91
- '• /help — show this command reference',
92
- '• /new — start fresh session',
93
- '• /stop — interrupt current response',
94
- '• /model — interactive model selector (/model list)',
95
- '• /role [name] — switch persona or role (/role list)',
96
- '• /bind [role] — bind persona to topic / chat',
97
- '• /preset [name] — bind preset to topic / chat',
98
- '• /lang [en|zh] — switch user language',
99
- '• /rewind [N] — rewind last N turns',
100
- '• /fork — fork session into new branch',
101
- '• /export — export history to Markdown',
102
- '',
103
- '🛠️ <b>Tools, Files & Cron:</b>',
104
- '• /skills / /tools — list active tools & skills',
105
- '• /files [dir] — workspace file explorer',
106
- '• /get <path> — download file from workspace',
107
- '• /remind <time> <text> — set reminder (/remind 10m check deploy)',
108
- '• /cron <interval> <prompt> — recurring autonomous task',
109
- '',
110
- '⚙️ <b>Settings & Stats:</b>',
111
- '• /status — gateway and active model status',
112
- '• /top — system resource usage (RAM, uptime)',
113
- '• /keyboard on|off — quick action keyboard',
114
- '• /voice on|off|status — voice replies preference',
115
- '• /tts on|off|status — speech synthesis in this chat',
116
- '• /mute / /unmute — mute notifications in this chat',
117
- '',
118
- '🔒 <b>Access & Channels:</b>',
119
- '• /whoami — your messenger user ID',
120
- '• /pair CODE — approve pairing code',
121
- '• /sethome [name] — set home notification channel',
122
- '• /setalert — set alert channel',
123
- ].join('\n')
124
-
125
-
126
-
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
+ { command: 'update', description: 'Check or install plugin update: /update [now]' },
34
+ ]
35
+
36
+ export function normalizeTelegramCommands(commands) {
37
+ const source = Array.isArray(commands) && commands.length ? commands : DEFAULT_TELEGRAM_COMMANDS
38
+ const out = []
39
+ const seen = new Set()
40
+ for (const raw of source) {
41
+ if (!raw || typeof raw !== 'object') continue
42
+ let command = String(raw.command || '').trim().replace(/^\//, '')
43
+ const description = String(raw.description || '').trim()
44
+ if (!command || !description) continue
45
+ command = command.slice(0, 32).toLowerCase()
46
+ if (seen.has(command)) continue
47
+ seen.add(command)
48
+ out.push({ command, description: description.slice(0, 256) })
49
+ }
50
+ return out.length ? out : [...DEFAULT_TELEGRAM_COMMANDS]
51
+ }
52
+
53
+ export function mergeDynamicCommands(baseCommands = DEFAULT_TELEGRAM_COMMANDS, dynamicCommands = [], maxTotal = 100) {
54
+ const normalizedBase = normalizeTelegramCommands(baseCommands)
55
+ const out = [...normalizedBase]
56
+ const seen = new Set(out.map((c) => c.command))
57
+
58
+ for (const item of dynamicCommands || []) {
59
+ if (out.length >= maxTotal) break
60
+ if (!item || typeof item !== 'object') continue
61
+ let command = String(item.command || item.name || '').trim().replace(/^\//, '').toLowerCase()
62
+ // Telegram format: lowercase alphanumeric and underscore only, 1-32 chars
63
+ command = command.replace(/[^a-z0-9_]/g, '_').slice(0, 32)
64
+ if (!command || seen.has(command)) continue
65
+ let description = String(item.description || item.title || `Skill ${command}`).trim()
66
+ if (!description) description = `Skill ${command}`
67
+ description = description.slice(0, 256)
68
+ seen.add(command)
69
+ out.push({ command, description })
70
+ }
71
+
72
+ return out.slice(0, maxTotal)
73
+ }
74
+
75
+ export function buildQuickActionsKeyboard() {
76
+ return {
77
+ keyboard: [
78
+ [{ text: '🔄 /new' }, { text: '🛑 /stop' }],
79
+ [{ text: '🎙️ /voice' }, { text: '📊 /status' }],
80
+ ],
81
+ resize_keyboard: true,
82
+ }
83
+ }
84
+
85
+ export const REMOVE_REPLY_KEYBOARD = { remove_keyboard: true }
86
+
87
+ export const HELP_TEXT = [
88
+ '📖 <b>Messenger Gateway Help:</b>',
89
+ '',
90
+ '💬 <b>Session & Chat:</b>',
91
+ '• /help — show this command reference',
92
+ '• /new — start fresh session',
93
+ '• /stop — interrupt current response',
94
+ '• /model — interactive model selector (/model list)',
95
+ '• /role [name] — switch persona or role (/role list)',
96
+ '• /bind [role] — bind persona to topic / chat',
97
+ '• /preset [name] — bind preset to topic / chat',
98
+ '• /lang [en|zh] — switch user language',
99
+ '• /rewind [N] — rewind last N turns',
100
+ '• /fork — fork session into new branch',
101
+ '• /export — export history to Markdown',
102
+ '',
103
+ '🛠️ <b>Tools, Files & Cron:</b>',
104
+ '• /skills / /tools — list active tools & skills',
105
+ '• /files [dir] — workspace file explorer',
106
+ '• /get <path> — download file from workspace',
107
+ '• /remind <time> <text> — set reminder (/remind 10m check deploy)',
108
+ '• /cron <interval> <prompt> — recurring autonomous task',
109
+ '',
110
+ '⚙️ <b>Settings & Stats:</b>',
111
+ '• /status — gateway and active model status',
112
+ '• /top — system resource usage (RAM, uptime)',
113
+ '• /keyboard on|off — quick action keyboard',
114
+ '• /voice on|off|status — voice replies preference',
115
+ '• /tts on|off|status — speech synthesis in this chat',
116
+ '• /mute / /unmute — mute notifications in this chat',
117
+ '',
118
+ '🔒 <b>Access & Channels:</b>',
119
+ '• /whoami — your messenger user ID',
120
+ '• /pair CODE — approve pairing code',
121
+ '• /sethome [name] — set home notification channel',
122
+ '• /setalert — set alert channel',
123
+ ].join('\n')
124
+
125
+
126
+
@@ -6,7 +6,7 @@ import { stripImageUrls } from './outbound.js'
6
6
  export function collectDynamicSkills(ctx) {
7
7
  const skills = []
8
8
  try {
9
- const skillsService = ctx.get?.('skills') || gw.ctx.skills
9
+ const skillsService = ctx.get?.('skills') || ctx?.skills
10
10
  const allSkills = skillsService?.list?.() || []
11
11
  for (const s of allSkills) {
12
12
  if (!s || !s.name) continue
@@ -17,7 +17,7 @@ export function collectDynamicSkills(ctx) {
17
17
  })
18
18
  }
19
19
  } catch (err) {
20
- ctx.logger?.debug?.(`Failed to collect dynamic skills: ${err?.message || err}`)
20
+ ctx?.logger?.debug?.(`Failed to collect dynamic skills: ${err?.message || err}`)
21
21
  }
22
22
  return skills
23
23
  }
@@ -29,9 +29,9 @@ export async function syncTelegramCommands(gw) {
29
29
  const merged = mergeDynamicCommands(baseCommands, dynamicSkills, 100)
30
30
  try {
31
31
  await tgAdapter.registerCommands(merged)
32
- ctx.logger?.info?.(`Synced ${merged.length} Telegram commands (including ${dynamicSkills.length} dynamic skills)`)
32
+ gw.ctx?.logger?.info?.(`Synced ${merged.length} Telegram commands (including ${dynamicSkills.length} dynamic skills)`)
33
33
  } catch (err) {
34
- ctx.logger?.warn?.(`Failed to sync Telegram commands: ${err?.message || err}`)
34
+ gw.ctx?.logger?.warn?.(`Failed to sync Telegram commands: ${err?.message || err}`)
35
35
  }
36
36
  }
37
37
  export async function mirrorSessionToForumTopic(gw, session) {
@@ -60,9 +60,9 @@ export async function mirrorSessionToForumTopic(gw, session) {
60
60
 
61
61
  const text = t('mirror.created', { sessionId, title }, 'en')
62
62
  await tgAdapter.sendTo(forumChatId, { text }, { threadId })
63
- ctx.logger?.info?.(`Mirrored session ${sessionId} to Telegram forum topic ${threadId} in ${forumChatId}`)
63
+ gw.ctx?.logger?.info?.(`Mirrored session ${sessionId} to Telegram forum topic ${threadId} in ${forumChatId}`)
64
64
  } catch (err) {
65
- ctx.logger?.warn?.(`Failed to mirror session ${sessionId} to forum topic: ${err?.message || err}`)
65
+ gw.ctx?.logger?.warn?.(`Failed to mirror session ${sessionId} to forum topic: ${err?.message || err}`)
66
66
  }
67
67
  }
68
68
  export async function relayTurnToForumMirror(gw, session, event) {
@@ -4,7 +4,8 @@ import {
4
4
  releaseCallbacks, buildMultiSelectKeyboard, indexCallbacks, REMOVE_KEYBOARD
5
5
  } from './ask.js'
6
6
  import {
7
- listModelCatalog, buildProvidersKeyboard, buildModelsKeyboard, storeModelSelection
7
+ listModelCatalog, buildProvidersKeyboard, buildModelsKeyboard, storeModelSelection,
8
+ getStoredModelSelection
8
9
  } from './models.js'
9
10
 
10
11
  export async function handleGatewayCallback(gw, cb) {
@@ -1,3 +1,6 @@
1
+ import { resolve, dirname } from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { ensureContentArray } from './content-guard.js'
1
4
  import { t } from './locales/index.js'
2
5
  import { REMOVE_REPLY_KEYBOARD, buildQuickActionsKeyboard } from './adapters/telegram.js'
3
6
  import { getPersona, listPersonas } from './personas.js'