@goodandready/dsh-messenger-gateway 0.3.21 → 0.3.22
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/adapters/discord.js +8 -0
- package/lib/adapters/slack.js +6 -0
- package/lib/adapters/telegram.js +712 -708
- package/lib/client.js +320 -1
- package/lib/commands.js +74 -73
- package/lib/config.js +104 -104
- package/lib/content-guard.js +14 -14
- package/lib/gateway.js +1772 -1737
- package/lib/index.js +532 -500
- package/lib/locales/en.js +123 -114
- package/lib/locales/index.js +21 -21
- package/lib/locales/zh.js +123 -114
- package/lib/scheduler.js +159 -159
- package/lib/telegram-errors.js +0 -0
- package/lib/text.js +39 -39
- package/lib/updater.js +304 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -114,6 +114,43 @@ window.__ModuleLoader__.load({
|
|
|
114
114
|
'stats.errors': 'Delivery Errors',
|
|
115
115
|
'stats.uptime': 'Gateway Uptime',
|
|
116
116
|
'stats.active_chats': 'Active Chats in Memory',
|
|
117
|
+
// Updater
|
|
118
|
+
'updater.title': 'Plugin Self-Updater',
|
|
119
|
+
'updater.desc': 'Check npmjs registry for updates and upgrade in-place.',
|
|
120
|
+
'updater.current': 'Current version: {version}',
|
|
121
|
+
'updater.checking': 'Checking for updates…',
|
|
122
|
+
'updater.available': 'Update available: v{version}',
|
|
123
|
+
'updater.upToDate': 'Up to date',
|
|
124
|
+
'updater.btn_check': 'Check for Updates',
|
|
125
|
+
'updater.btn_update': 'Update to v{version}',
|
|
126
|
+
'updater.updating': 'Updating…',
|
|
127
|
+
'updater.success': 'Updated to v{version}! Please restart DSH service if required.',
|
|
128
|
+
'updater.failed': 'Update failed: {error}',
|
|
129
|
+
'updater.check_failed': 'Update check failed: {error}',
|
|
130
|
+
// Discord
|
|
131
|
+
discord: 'Discord Gateway',
|
|
132
|
+
discordSub: 'Discord bot and outbound webhook delivery settings.',
|
|
133
|
+
enableDiscord: 'Enable Discord',
|
|
134
|
+
disableDiscord: 'Disable Discord',
|
|
135
|
+
discordToken: 'Discord Bot Token',
|
|
136
|
+
discordTokenHint: 'From Discord Developer Portal. Leave empty to keep existing.',
|
|
137
|
+
discordWebhook: 'Discord Webhook URL',
|
|
138
|
+
discordWebhookHint: 'Incoming webhook for sending messages/notifications.',
|
|
139
|
+
discordChannels: 'Allowed Discord Channel IDs',
|
|
140
|
+
discordChannelsHint: 'Comma-separated channel IDs. Leave empty to allow all configured.',
|
|
141
|
+
// Slack
|
|
142
|
+
slack: 'Slack Gateway',
|
|
143
|
+
slackSub: 'Slack bot and incoming webhook settings.',
|
|
144
|
+
enableSlack: 'Enable Slack',
|
|
145
|
+
disableSlack: 'Disable Slack',
|
|
146
|
+
slackToken: 'Slack Bot Token (xoxb-…)',
|
|
147
|
+
slackTokenHint: 'Bot User OAuth Token. Leave empty to keep existing.',
|
|
148
|
+
slackWebhook: 'Slack Webhook URL',
|
|
149
|
+
slackWebhookHint: 'Incoming webhook URL for channel notifications.',
|
|
150
|
+
slackDefaultChannel: 'Slack Default Channel',
|
|
151
|
+
slackDefaultChannelHint: 'Default channel or member ID for outbound messages (e.g. #general or C12345).',
|
|
152
|
+
// Cron badge
|
|
153
|
+
'badge.cron_count': 'Cron: {count}',
|
|
117
154
|
}
|
|
118
155
|
|
|
119
156
|
const zh = {
|
|
@@ -217,6 +254,43 @@ window.__ModuleLoader__.load({
|
|
|
217
254
|
'stats.errors': '发送错误数',
|
|
218
255
|
'stats.uptime': '运行时间',
|
|
219
256
|
'stats.active_chats': '内存中活跃聊天数',
|
|
257
|
+
// Updater
|
|
258
|
+
'updater.title': '插件自更新',
|
|
259
|
+
'updater.desc': '从 npmjs 仓库检查更新并直接就地升级。',
|
|
260
|
+
'updater.current': '当前版本: {version}',
|
|
261
|
+
'updater.checking': '正在检查更新…',
|
|
262
|
+
'updater.available': '发现新版本: v{version}',
|
|
263
|
+
'updater.upToDate': '已是最新版本',
|
|
264
|
+
'updater.btn_check': '检查更新',
|
|
265
|
+
'updater.btn_update': '更新至 v{version}',
|
|
266
|
+
'updater.updating': '正在更新…',
|
|
267
|
+
'updater.success': '已更新至 v{version}!如需生效请重启 DSH 服务。',
|
|
268
|
+
'updater.failed': '更新失败: {error}',
|
|
269
|
+
'updater.check_failed': '检查更新失败: {error}',
|
|
270
|
+
// Discord
|
|
271
|
+
discord: 'Discord 网关',
|
|
272
|
+
discordSub: 'Discord 机器人与出站 Webhook 设置。',
|
|
273
|
+
enableDiscord: '启用 Discord',
|
|
274
|
+
disableDiscord: '停用 Discord',
|
|
275
|
+
discordToken: 'Discord 机器人令牌',
|
|
276
|
+
discordTokenHint: '从 Discord 开发者平台获取。留空保持现有配置。',
|
|
277
|
+
discordWebhook: 'Discord Webhook URL',
|
|
278
|
+
discordWebhookHint: '用于发送消息或通知的传入 Webhook。',
|
|
279
|
+
discordChannels: '允许的 Discord 频道 ID',
|
|
280
|
+
discordChannelsHint: '逗号分隔的频道 ID。留空允许所有已配置频道。',
|
|
281
|
+
// Slack
|
|
282
|
+
slack: 'Slack 网关',
|
|
283
|
+
slackSub: 'Slack 机器人与 Webhook 设置。',
|
|
284
|
+
enableSlack: '启用 Slack',
|
|
285
|
+
disableSlack: '停用 Slack',
|
|
286
|
+
slackToken: 'Slack 机器人令牌 (xoxb-…)',
|
|
287
|
+
slackTokenHint: 'Bot 用户 OAuth 令牌。留空保持现有配置。',
|
|
288
|
+
slackWebhook: 'Slack Webhook URL',
|
|
289
|
+
slackWebhookHint: '用于频道通知的传入 Webhook URL。',
|
|
290
|
+
slackDefaultChannel: 'Slack 默认频道',
|
|
291
|
+
slackDefaultChannelHint: '出站消息的默认频道或成员 ID (例如 #general 或 C12345)。',
|
|
292
|
+
// Cron badge
|
|
293
|
+
'badge.cron_count': '定时任务: {count}',
|
|
220
294
|
}
|
|
221
295
|
|
|
222
296
|
function useActiveLocale(ctx) {
|
|
@@ -369,11 +443,13 @@ window.__ModuleLoader__.load({
|
|
|
369
443
|
)
|
|
370
444
|
}
|
|
371
445
|
|
|
372
|
-
const SETTINGS_KEYS = ['enabled', 'telegram', 'agent', 'media', 'tts']
|
|
446
|
+
const SETTINGS_KEYS = ['enabled', 'telegram', 'discord', 'slack', 'agent', 'media', 'tts']
|
|
373
447
|
|
|
374
448
|
function draftFromStored(s) {
|
|
375
449
|
const src = (s && typeof s === 'object') ? s : {}
|
|
376
450
|
const t = src.telegram || {}
|
|
451
|
+
const d = src.discord || {}
|
|
452
|
+
const sl = src.slack || {}
|
|
377
453
|
const a = src.agent || {}
|
|
378
454
|
const m = src.media || {}
|
|
379
455
|
const tts = src.tts || {}
|
|
@@ -411,6 +487,18 @@ window.__ModuleLoader__.load({
|
|
|
411
487
|
botToken: t.botToken || '',
|
|
412
488
|
webhookSecret: t.webhookSecret || '',
|
|
413
489
|
},
|
|
490
|
+
discord: {
|
|
491
|
+
enabled: d.enabled === true,
|
|
492
|
+
botToken: d.botToken || '',
|
|
493
|
+
webhookUrl: d.webhookUrl || '',
|
|
494
|
+
allowedChannelIds: Array.isArray(d.allowedChannelIds) ? d.allowedChannelIds : [],
|
|
495
|
+
},
|
|
496
|
+
slack: {
|
|
497
|
+
enabled: sl.enabled === true,
|
|
498
|
+
botToken: sl.botToken || '',
|
|
499
|
+
webhookUrl: sl.webhookUrl || '',
|
|
500
|
+
defaultChannel: sl.defaultChannel || '',
|
|
501
|
+
},
|
|
414
502
|
agent: {
|
|
415
503
|
provider: a.provider || '',
|
|
416
504
|
model: a.model || '',
|
|
@@ -441,6 +529,18 @@ window.__ModuleLoader__.load({
|
|
|
441
529
|
const [token, setToken] = React.useState('')
|
|
442
530
|
const [webhookSecret, setWebhookSecret] = React.useState('')
|
|
443
531
|
const [allowText, setAllowText] = React.useState('')
|
|
532
|
+
const [discordToken, setDiscordToken] = React.useState('')
|
|
533
|
+
const [slackToken, setSlackToken] = React.useState('')
|
|
534
|
+
const [discordChannelsText, setDiscordChannelsText] = React.useState('')
|
|
535
|
+
const [updateState, setUpdateState] = React.useState({
|
|
536
|
+
checking: false,
|
|
537
|
+
updating: false,
|
|
538
|
+
currentVersion: '',
|
|
539
|
+
latestVersion: '',
|
|
540
|
+
updateAvailable: false,
|
|
541
|
+
error: '',
|
|
542
|
+
notice: '',
|
|
543
|
+
})
|
|
444
544
|
const [pending, setPending] = React.useState([])
|
|
445
545
|
const [status, setStatus] = React.useState(null)
|
|
446
546
|
const [smokeResult, setSmokeResult] = React.useState(null)
|
|
@@ -472,6 +572,7 @@ window.__ModuleLoader__.load({
|
|
|
472
572
|
if (snapStatus === 'ready' && cfg === null) {
|
|
473
573
|
setCfg(draftFromStored(stored))
|
|
474
574
|
setAllowText(idsToText(stored?.telegram?.allowedUserIds))
|
|
575
|
+
setDiscordChannelsText(idsToText(stored?.discord?.allowedChannelIds))
|
|
475
576
|
}
|
|
476
577
|
}, [snapStatus, stored, cfg])
|
|
477
578
|
|
|
@@ -503,6 +604,61 @@ window.__ModuleLoader__.load({
|
|
|
503
604
|
return () => clearInterval(timer)
|
|
504
605
|
}, [loadStatus, loadPairing])
|
|
505
606
|
|
|
607
|
+
const loadUpdateStatus = React.useCallback(async () => {
|
|
608
|
+
setUpdateState((s) => ({ ...s, checking: true, error: '' }))
|
|
609
|
+
try {
|
|
610
|
+
const res = await fetch(`${ROUTE_PREFIX}/update`, { cache: 'no-store' })
|
|
611
|
+
const data = await res.json().catch(() => ({}))
|
|
612
|
+
if (res.ok && data.ok) {
|
|
613
|
+
setUpdateState((s) => ({
|
|
614
|
+
...s,
|
|
615
|
+
checking: false,
|
|
616
|
+
currentVersion: data.currentVersion || '',
|
|
617
|
+
latestVersion: data.latestVersion || '',
|
|
618
|
+
updateAvailable: !!data.updateAvailable,
|
|
619
|
+
}))
|
|
620
|
+
} else {
|
|
621
|
+
setUpdateState((s) => ({ ...s, checking: false, error: data.error || `HTTP ${res.status}` }))
|
|
622
|
+
}
|
|
623
|
+
} catch (e) {
|
|
624
|
+
setUpdateState((s) => ({ ...s, checking: false, error: String(e.message || e) }))
|
|
625
|
+
}
|
|
626
|
+
}, [])
|
|
627
|
+
|
|
628
|
+
const handleTriggerUpdate = async () => {
|
|
629
|
+
if (updateState.updating) return
|
|
630
|
+
setUpdateState((s) => ({ ...s, updating: true, error: '', notice: '' }))
|
|
631
|
+
try {
|
|
632
|
+
const res = await fetch(`${ROUTE_PREFIX}/update`, {
|
|
633
|
+
method: 'POST',
|
|
634
|
+
credentials: 'same-origin',
|
|
635
|
+
headers: {
|
|
636
|
+
'Content-Type': 'application/json',
|
|
637
|
+
'x-dsh-plugin-update': '1',
|
|
638
|
+
},
|
|
639
|
+
})
|
|
640
|
+
const data = await res.json().catch(() => ({}))
|
|
641
|
+
if (!res.ok || !data.ok) throw new Error(data.error || `HTTP ${res.status}`)
|
|
642
|
+
setUpdateState((s) => ({
|
|
643
|
+
...s,
|
|
644
|
+
updating: false,
|
|
645
|
+
updateAvailable: false,
|
|
646
|
+
currentVersion: data.updatedVersion || s.latestVersion,
|
|
647
|
+
notice: t('updater.success', { version: data.updatedVersion || s.latestVersion }),
|
|
648
|
+
}))
|
|
649
|
+
} catch (e) {
|
|
650
|
+
setUpdateState((s) => ({
|
|
651
|
+
...s,
|
|
652
|
+
updating: false,
|
|
653
|
+
error: t('updater.failed', { error: String(e.message || e) }),
|
|
654
|
+
}))
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
React.useEffect(() => {
|
|
659
|
+
loadUpdateStatus()
|
|
660
|
+
}, [loadUpdateStatus])
|
|
661
|
+
|
|
506
662
|
const handleSmoke = async () => {
|
|
507
663
|
setBusy('smoke')
|
|
508
664
|
setErr('')
|
|
@@ -531,6 +687,8 @@ window.__ModuleLoader__.load({
|
|
|
531
687
|
...base,
|
|
532
688
|
...patch,
|
|
533
689
|
telegram: { ...base.telegram, ...(patch.telegram || {}) },
|
|
690
|
+
discord: { ...base.discord, ...(patch.discord || {}) },
|
|
691
|
+
slack: { ...base.slack, ...(patch.slack || {}) },
|
|
534
692
|
tts: { ...base.tts, ...(patch.tts || {}) },
|
|
535
693
|
agent: { ...base.agent, ...(patch.agent || {}) },
|
|
536
694
|
media: { ...base.media, ...(patch.media || {}) },
|
|
@@ -546,6 +704,9 @@ window.__ModuleLoader__.load({
|
|
|
546
704
|
if (token.trim()) next.telegram = { ...next.telegram, botToken: token.trim() }
|
|
547
705
|
if (webhookSecret.trim()) next.telegram = { ...next.telegram, webhookSecret: webhookSecret.trim() }
|
|
548
706
|
next.telegram.allowedUserIds = textToIds(allowText)
|
|
707
|
+
if (discordToken.trim()) next.discord = { ...next.discord, botToken: discordToken.trim() }
|
|
708
|
+
if (slackToken.trim()) next.slack = { ...next.slack, botToken: slackToken.trim() }
|
|
709
|
+
next.discord.allowedChannelIds = textToIds(discordChannelsText)
|
|
549
710
|
|
|
550
711
|
const broken = []
|
|
551
712
|
for (const key of SETTINGS_KEYS) {
|
|
@@ -564,6 +725,8 @@ window.__ModuleLoader__.load({
|
|
|
564
725
|
setCfg(next)
|
|
565
726
|
setToken('')
|
|
566
727
|
setWebhookSecret('')
|
|
728
|
+
setDiscordToken('')
|
|
729
|
+
setSlackToken('')
|
|
567
730
|
setMsg(t('saved'))
|
|
568
731
|
setTimeout(() => setMsg(''), 3000)
|
|
569
732
|
await loadPairing()
|
|
@@ -628,6 +791,13 @@ window.__ModuleLoader__.load({
|
|
|
628
791
|
{ className: 'msgw-badge msgw-badge-warn' },
|
|
629
792
|
t('badge.pairing_count', { count: pendingCount })
|
|
630
793
|
)
|
|
794
|
+
: null,
|
|
795
|
+
(status?.scheduledCount > 0)
|
|
796
|
+
? React.createElement(
|
|
797
|
+
'span',
|
|
798
|
+
{ className: 'msgw-badge msgw-badge-ok' },
|
|
799
|
+
t('badge.cron_count', { count: status.scheduledCount })
|
|
800
|
+
)
|
|
631
801
|
: null
|
|
632
802
|
),
|
|
633
803
|
React.createElement('div', { className: 'msgw-page-sub' }, t('header.sub'))
|
|
@@ -637,6 +807,60 @@ window.__ModuleLoader__.load({
|
|
|
637
807
|
err ? React.createElement('div', { className: 'msgw-alert-err' }, err) : null,
|
|
638
808
|
msg ? React.createElement('div', { className: 'msgw-alert-ok' }, msg) : null,
|
|
639
809
|
|
|
810
|
+
// Self-Updater Card
|
|
811
|
+
React.createElement(
|
|
812
|
+
'div',
|
|
813
|
+
{ className: 'msgw-section-card' },
|
|
814
|
+
React.createElement(
|
|
815
|
+
'div',
|
|
816
|
+
{ className: 'msgw-section-title' },
|
|
817
|
+
React.createElement('span', null, `🚀 ${t('updater.title')}`),
|
|
818
|
+
React.createElement(
|
|
819
|
+
'div',
|
|
820
|
+
{ className: 'msgw-row' },
|
|
821
|
+
React.createElement(
|
|
822
|
+
'button',
|
|
823
|
+
{
|
|
824
|
+
type: 'button',
|
|
825
|
+
className: 'msgw-btn msgw-btn-mini',
|
|
826
|
+
disabled: updateState.checking || updateState.updating,
|
|
827
|
+
onClick: loadUpdateStatus,
|
|
828
|
+
},
|
|
829
|
+
updateState.checking ? t('updater.checking') : t('updater.btn_check')
|
|
830
|
+
),
|
|
831
|
+
updateState.updateAvailable
|
|
832
|
+
? React.createElement(
|
|
833
|
+
'button',
|
|
834
|
+
{
|
|
835
|
+
type: 'button',
|
|
836
|
+
className: 'msgw-btn msgw-btn-mini msgw-btn-primary',
|
|
837
|
+
disabled: updateState.updating,
|
|
838
|
+
onClick: handleTriggerUpdate,
|
|
839
|
+
},
|
|
840
|
+
updateState.updating ? t('updater.updating') : t('updater.btn_update', { version: updateState.latestVersion })
|
|
841
|
+
)
|
|
842
|
+
: null
|
|
843
|
+
)
|
|
844
|
+
),
|
|
845
|
+
React.createElement('div', { className: 'msgw-section-desc' }, t('updater.desc')),
|
|
846
|
+
React.createElement(
|
|
847
|
+
'div',
|
|
848
|
+
{ className: 'msgw-row', style: { fontSize: '13px' } },
|
|
849
|
+
React.createElement('span', { style: { color: 'var(--dsw-alias-label-secondary)' } },
|
|
850
|
+
t('updater.current', { version: updateState.currentVersion || status?.version || '...' })
|
|
851
|
+
),
|
|
852
|
+
updateState.checking
|
|
853
|
+
? React.createElement('span', { className: 'msgw-badge msgw-badge-warn' }, t('updater.checking'))
|
|
854
|
+
: updateState.updateAvailable
|
|
855
|
+
? React.createElement('span', { className: 'msgw-badge msgw-badge-warn' }, t('updater.available', { version: updateState.latestVersion }))
|
|
856
|
+
: updateState.currentVersion && !updateState.error
|
|
857
|
+
? React.createElement('span', { className: 'msgw-badge msgw-badge-ok' }, `✓ ${t('updater.upToDate')}`)
|
|
858
|
+
: null
|
|
859
|
+
),
|
|
860
|
+
updateState.notice ? React.createElement('div', { className: 'msgw-alert-ok' }, updateState.notice) : null,
|
|
861
|
+
updateState.error ? React.createElement('div', { className: 'msgw-alert-err' }, updateState.error) : null
|
|
862
|
+
),
|
|
863
|
+
|
|
640
864
|
// Card 1: Telegram Connection & Bot
|
|
641
865
|
React.createElement(
|
|
642
866
|
'div',
|
|
@@ -949,6 +1173,101 @@ window.__ModuleLoader__.load({
|
|
|
949
1173
|
)
|
|
950
1174
|
),
|
|
951
1175
|
|
|
1176
|
+
// Card: Discord Gateway
|
|
1177
|
+
React.createElement(
|
|
1178
|
+
'div',
|
|
1179
|
+
{ className: 'msgw-section-card' },
|
|
1180
|
+
React.createElement(
|
|
1181
|
+
'div',
|
|
1182
|
+
{ className: 'msgw-section-title' },
|
|
1183
|
+
React.createElement('span', null, `🎮 ${t('discord')}`),
|
|
1184
|
+
React.createElement(
|
|
1185
|
+
'button',
|
|
1186
|
+
{
|
|
1187
|
+
type: 'button',
|
|
1188
|
+
className: `msgw-btn msgw-btn-mini ${cfg.discord?.enabled ? 'msgw-btn-danger' : 'msgw-btn-primary'}`,
|
|
1189
|
+
disabled: !!busy,
|
|
1190
|
+
onClick: () => save({ discord: { enabled: !cfg.discord?.enabled } }),
|
|
1191
|
+
},
|
|
1192
|
+
cfg.discord?.enabled ? t('disableDiscord') : t('enableDiscord')
|
|
1193
|
+
)
|
|
1194
|
+
),
|
|
1195
|
+
React.createElement('div', { className: 'msgw-section-desc' }, t('discordSub')),
|
|
1196
|
+
React.createElement(Field, { label: t('discordToken'), hint: t('discordTokenHint') },
|
|
1197
|
+
React.createElement('input', {
|
|
1198
|
+
type: 'password',
|
|
1199
|
+
className: 'msgw-input',
|
|
1200
|
+
value: discordToken,
|
|
1201
|
+
placeholder: (status?.config?.discord?.botTokenConfigured || cfg.discord?.botTokenConfigured) ? '••••••••••••••••' : 'Discord Bot Token',
|
|
1202
|
+
onChange: (e) => setDiscordToken(e.target.value),
|
|
1203
|
+
})
|
|
1204
|
+
),
|
|
1205
|
+
React.createElement(Field, { label: t('discordWebhook'), hint: t('discordWebhookHint') },
|
|
1206
|
+
React.createElement('input', {
|
|
1207
|
+
className: 'msgw-input',
|
|
1208
|
+
value: cfg.discord?.webhookUrl || '',
|
|
1209
|
+
placeholder: 'https://discord.com/api/webhooks/...',
|
|
1210
|
+
onChange: (e) => setCfg({ ...cfg, discord: { ...cfg.discord, webhookUrl: e.target.value } }),
|
|
1211
|
+
})
|
|
1212
|
+
),
|
|
1213
|
+
React.createElement(Field, { label: t('discordChannels'), hint: t('discordChannelsHint') },
|
|
1214
|
+
React.createElement('textarea', {
|
|
1215
|
+
className: 'msgw-textarea',
|
|
1216
|
+
value: discordChannelsText,
|
|
1217
|
+
onChange: (e) => setDiscordChannelsText(e.target.value),
|
|
1218
|
+
rows: 2,
|
|
1219
|
+
placeholder: '123456789012345678, 987654321098765432',
|
|
1220
|
+
})
|
|
1221
|
+
)
|
|
1222
|
+
),
|
|
1223
|
+
|
|
1224
|
+
// Card: Slack Gateway
|
|
1225
|
+
React.createElement(
|
|
1226
|
+
'div',
|
|
1227
|
+
{ className: 'msgw-section-card' },
|
|
1228
|
+
React.createElement(
|
|
1229
|
+
'div',
|
|
1230
|
+
{ className: 'msgw-section-title' },
|
|
1231
|
+
React.createElement('span', null, `💼 ${t('slack')}`),
|
|
1232
|
+
React.createElement(
|
|
1233
|
+
'button',
|
|
1234
|
+
{
|
|
1235
|
+
type: 'button',
|
|
1236
|
+
className: `msgw-btn msgw-btn-mini ${cfg.slack?.enabled ? 'msgw-btn-danger' : 'msgw-btn-primary'}`,
|
|
1237
|
+
disabled: !!busy,
|
|
1238
|
+
onClick: () => save({ slack: { enabled: !cfg.slack?.enabled } }),
|
|
1239
|
+
},
|
|
1240
|
+
cfg.slack?.enabled ? t('disableSlack') : t('enableSlack')
|
|
1241
|
+
)
|
|
1242
|
+
),
|
|
1243
|
+
React.createElement('div', { className: 'msgw-section-desc' }, t('slackSub')),
|
|
1244
|
+
React.createElement(Field, { label: t('slackToken'), hint: t('slackTokenHint') },
|
|
1245
|
+
React.createElement('input', {
|
|
1246
|
+
type: 'password',
|
|
1247
|
+
className: 'msgw-input',
|
|
1248
|
+
value: slackToken,
|
|
1249
|
+
placeholder: (status?.config?.slack?.botTokenConfigured || cfg.slack?.botTokenConfigured) ? '••••••••••••••••' : 'xoxb-...',
|
|
1250
|
+
onChange: (e) => setSlackToken(e.target.value),
|
|
1251
|
+
})
|
|
1252
|
+
),
|
|
1253
|
+
React.createElement(Field, { label: t('slackWebhook'), hint: t('slackWebhookHint') },
|
|
1254
|
+
React.createElement('input', {
|
|
1255
|
+
className: 'msgw-input',
|
|
1256
|
+
value: cfg.slack?.webhookUrl || '',
|
|
1257
|
+
placeholder: 'https://hooks.slack.com/services/...',
|
|
1258
|
+
onChange: (e) => setCfg({ ...cfg, slack: { ...cfg.slack, webhookUrl: e.target.value } }),
|
|
1259
|
+
})
|
|
1260
|
+
),
|
|
1261
|
+
React.createElement(Field, { label: t('slackDefaultChannel'), hint: t('slackDefaultChannelHint') },
|
|
1262
|
+
React.createElement('input', {
|
|
1263
|
+
className: 'msgw-input',
|
|
1264
|
+
value: cfg.slack?.defaultChannel || '',
|
|
1265
|
+
placeholder: '#general or C12345678',
|
|
1266
|
+
onChange: (e) => setCfg({ ...cfg, slack: { ...cfg.slack, defaultChannel: e.target.value } }),
|
|
1267
|
+
})
|
|
1268
|
+
)
|
|
1269
|
+
),
|
|
1270
|
+
|
|
952
1271
|
// Card 6: Pairing Requests
|
|
953
1272
|
React.createElement(
|
|
954
1273
|
'div',
|
package/lib/commands.js
CHANGED
|
@@ -1,73 +1,74 @@
|
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
command
|
|
45
|
-
|
|
46
|
-
seen.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
description =
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
+
|