@goodandready/dsh-messenger-gateway 0.3.14 → 0.3.18

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 CHANGED
@@ -104,6 +104,12 @@ In addition to Telegram, outbound messages can be dispatched to Discord and Slac
104
104
  - `notifyBridge` — forward non-messenger web session events to a home
105
105
  - Bot token is a DSH secret field — never commit it
106
106
 
107
+ ## Client UI & Settings Card
108
+
109
+ - Mounted into `settings.plugin.item` on the Plugins tab (`key: dsh-messenger-gateway`).
110
+ - Configuration is managed reactively via `settingsScope.bind({ namespace: 'dsh-messenger-gateway' })` with snapshot status checking (`loading`, `unavailable`, `ready`).
111
+ - Protected dictionary registration: duplicate registration attempts on page reload are safely caught with non-fatal warnings, ensuring the settings card never fails to mount.
112
+
107
113
  ## Requirements
108
114
 
109
115
  - DeepSeek Harness web (or compatible) profile
@@ -11,7 +11,7 @@ import { normalizeThreadId, telegramThreadParams } from '../topics.js'
11
11
  import {
12
12
  shouldProcessTelegramMessage, stripBotCommandSuffix,
13
13
  } from '../groups.js'
14
- import { isResendSafeNetworkError, isPollingConflict } from '../telegram-errors.js'
14
+ import { isResendSafeNetworkError, isPollingConflict, computePollBackoffMs } from '../telegram-errors.js'
15
15
 
16
16
  const API = 'https://api.telegram.org'
17
17
  const TELEGRAM_MAX = 4096
@@ -55,6 +55,7 @@ export class TelegramAdapter {
55
55
  this.sendRetryMax = 2
56
56
  this.sendRetryBaseMs = 400
57
57
  this.pollingConflict = false
58
+ this.pollErrorCount = 0
58
59
  this.webhookUrl = String(opts.webhookUrl || '').trim()
59
60
  this.webhookSecret = String(opts.webhookSecret || '').trim()
60
61
  this.offset = 0
@@ -181,9 +182,10 @@ export class TelegramAdapter {
181
182
  if (this.statusIndicator) this.setStatusIndicator(this.statusOffline).catch(() => {})
182
183
  }
183
184
 
184
- schedulePoll() {
185
+ schedulePoll(delayMs) {
185
186
  if (this.stopped) return
186
- this.pollTimer = setTimeout(() => this.poll(), this.pollIntervalMs)
187
+ const delay = delayMs !== undefined ? delayMs : this.pollIntervalMs
188
+ this.pollTimer = setTimeout(() => this.poll(), delay)
187
189
  this.pollTimer.unref?.()
188
190
  }
189
191
 
@@ -196,6 +198,7 @@ export class TelegramAdapter {
196
198
  allowed_updates: ['message', 'callback_query'],
197
199
  })
198
200
  this.pollingConflict = false
201
+ this.pollErrorCount = 0
199
202
  for (const update of updates || []) {
200
203
  this.offset = Math.max(this.offset, update.update_id + 1)
201
204
  await this.dispatchUpdate(update)
@@ -579,6 +582,46 @@ export class TelegramAdapter {
579
582
  return this.sendReply(chatId, undefined, payload, normalizeThreadId(opts.threadId))
580
583
  }
581
584
 
585
+ async probeHealth(timeoutMs = 10000) {
586
+ if (!this.token) {
587
+ return { ok: false, error: 'Telegram bot token is empty' }
588
+ }
589
+ const start = Date.now()
590
+ try {
591
+ const res = await fetch(`${API}/bot${this.token}/getMe`, {
592
+ method: 'POST',
593
+ headers: { 'Content-Type': 'application/json' },
594
+ body: JSON.stringify({}),
595
+ signal: AbortSignal.timeout(Math.max(1000, Number(timeoutMs) || 10000)),
596
+ })
597
+ const latencyMs = Date.now() - start
598
+ const json = await res.json().catch(() => ({}))
599
+ if (!res.ok || json.ok === false) {
600
+ return {
601
+ ok: false,
602
+ latencyMs,
603
+ error: json.description || `HTTP ${res.status}`,
604
+ }
605
+ }
606
+ const me = json.result || {}
607
+ this.botId = Number(me.id) || this.botId
608
+ this.botUsername = String(me.username || this.botUsername)
609
+ return {
610
+ ok: true,
611
+ latencyMs,
612
+ botId: this.botId,
613
+ botUsername: this.botUsername,
614
+ firstName: me.first_name || '',
615
+ }
616
+ } catch (err) {
617
+ return {
618
+ ok: false,
619
+ latencyMs: Date.now() - start,
620
+ error: err instanceof Error ? err.message : String(err),
621
+ }
622
+ }
623
+ }
624
+
582
625
  async createForumTopic(chatId, name, opts = {}) {
583
626
  return this.call('createForumTopic', {
584
627
  chat_id: chatId,