@wrongstack/telegram 0.293.0 → 0.295.1
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 +1 -1
- package/dist/api-client.d.ts +2 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/bot-queue.d.ts +16 -1
- package/dist/bot-queue.d.ts.map +1 -1
- package/dist/bot.d.ts +8 -1
- package/dist/bot.d.ts.map +1 -1
- package/dist/config-classifier.d.ts +1 -52
- package/dist/config-classifier.d.ts.map +1 -1
- package/dist/config.d.ts +110 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +335 -119
- package/dist/index.js.map +3 -3
- package/dist/manifest.d.ts +35 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/notification-channel.d.ts +2 -2
- package/dist/notification-channel.d.ts.map +1 -1
- package/dist/outbound-queue.d.ts +1 -1
- package/dist/outbound-queue.d.ts.map +1 -1
- package/dist/poll-lock.d.ts +1 -1
- package/dist/poll-lock.d.ts.map +1 -1
- package/dist/rate-limiter.d.ts +36 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/security/outbound.d.ts.map +1 -1
- package/dist/slash-commands/index.d.ts +2 -1
- package/dist/slash-commands/index.d.ts.map +1 -1
- package/dist/tools/telegram-approve.d.ts +6 -3
- package/dist/tools/telegram-approve.d.ts.map +1 -1
- package/dist/tools/telegram-read.d.ts +1 -1
- package/dist/tools/telegram-read.d.ts.map +1 -1
- package/dist/tools/telegram-send.d.ts +1 -1
- package/dist/tools/telegram-send.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/inbox-cursor-store.d.ts +0 -54
- package/dist/inbox-cursor-store.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { expectDefined as expectDefined2 } from "@wrongstack/core";
|
|
2
|
+
import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
3
3
|
|
|
4
4
|
// src/api-client.ts
|
|
5
5
|
var TelegramApiClientError = class extends Error {
|
|
@@ -163,30 +163,34 @@ var TelegramApiClient = class {
|
|
|
163
163
|
});
|
|
164
164
|
}
|
|
165
165
|
sendMessage(chatId, text, opts) {
|
|
166
|
+
const body = {
|
|
167
|
+
chat_id: String(chatId),
|
|
168
|
+
text,
|
|
169
|
+
disable_web_page_preview: true
|
|
170
|
+
};
|
|
171
|
+
if (opts?.parseMode) body.parse_mode = opts.parseMode;
|
|
166
172
|
return this.request("sendMessage", {
|
|
167
|
-
body
|
|
168
|
-
chat_id: String(chatId),
|
|
169
|
-
text,
|
|
170
|
-
disable_web_page_preview: true
|
|
171
|
-
},
|
|
173
|
+
body,
|
|
172
174
|
signal: composedSignal(opts?.signal)
|
|
173
175
|
});
|
|
174
176
|
}
|
|
175
177
|
sendMessageWithKeyboard(chatId, text, buttons, opts) {
|
|
178
|
+
const body = {
|
|
179
|
+
chat_id: String(chatId),
|
|
180
|
+
text,
|
|
181
|
+
disable_web_page_preview: true,
|
|
182
|
+
reply_markup: {
|
|
183
|
+
inline_keyboard: [
|
|
184
|
+
buttons.map((button) => ({
|
|
185
|
+
text: button.text,
|
|
186
|
+
callback_data: button.callback_data
|
|
187
|
+
}))
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
if (opts?.parseMode) body.parse_mode = opts.parseMode;
|
|
176
192
|
return this.request("sendMessage", {
|
|
177
|
-
body
|
|
178
|
-
chat_id: String(chatId),
|
|
179
|
-
text,
|
|
180
|
-
disable_web_page_preview: true,
|
|
181
|
-
reply_markup: {
|
|
182
|
-
inline_keyboard: [
|
|
183
|
-
buttons.map((button) => ({
|
|
184
|
-
text: button.text,
|
|
185
|
-
callback_data: button.callback_data
|
|
186
|
-
}))
|
|
187
|
-
]
|
|
188
|
-
}
|
|
189
|
-
},
|
|
193
|
+
body,
|
|
190
194
|
signal: composedSignal(opts?.signal)
|
|
191
195
|
});
|
|
192
196
|
}
|
|
@@ -286,6 +290,7 @@ var TelegramBot = class _TelegramBot {
|
|
|
286
290
|
/** Single-poller election across wstack instances sharing this token. */
|
|
287
291
|
lock;
|
|
288
292
|
standbyRetryMs;
|
|
293
|
+
getParseMode;
|
|
289
294
|
standbyTimer = null;
|
|
290
295
|
standbyAnnounced = false;
|
|
291
296
|
// Circular buffer for incoming messages
|
|
@@ -306,6 +311,7 @@ var TelegramBot = class _TelegramBot {
|
|
|
306
311
|
this.offsetStore = opts.offsetStore;
|
|
307
312
|
this.lock = opts.lock;
|
|
308
313
|
this.standbyRetryMs = opts.standbyRetryMs ?? 15e3;
|
|
314
|
+
this.getParseMode = opts.getParseMode;
|
|
309
315
|
if (this.lock) {
|
|
310
316
|
this.lock.onLost = () => this.handleLockLost();
|
|
311
317
|
}
|
|
@@ -432,7 +438,8 @@ var TelegramBot = class _TelegramBot {
|
|
|
432
438
|
try {
|
|
433
439
|
const timeout = AbortSignal.timeout(1e4);
|
|
434
440
|
const result = await this.api.sendMessage(chatId, text, {
|
|
435
|
-
signal: signal ? AbortSignal.any([signal, timeout]) : timeout
|
|
441
|
+
signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
|
|
442
|
+
parseMode: this.getParseMode?.()
|
|
436
443
|
});
|
|
437
444
|
return { ok: true, result };
|
|
438
445
|
} catch (err) {
|
|
@@ -468,7 +475,8 @@ var TelegramBot = class _TelegramBot {
|
|
|
468
475
|
try {
|
|
469
476
|
const timeout = AbortSignal.timeout(1e4);
|
|
470
477
|
const result = await this.api.sendMessageWithKeyboard(chatId, text, buttons, {
|
|
471
|
-
signal: signal ? AbortSignal.any([signal, timeout]) : timeout
|
|
478
|
+
signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
|
|
479
|
+
parseMode: this.getParseMode?.()
|
|
472
480
|
});
|
|
473
481
|
return { ok: true, result };
|
|
474
482
|
} catch (err) {
|
|
@@ -529,7 +537,11 @@ var TelegramBot = class _TelegramBot {
|
|
|
529
537
|
for (const upd of updates) {
|
|
530
538
|
this.offset = upd.update_id + 1;
|
|
531
539
|
if (upd.callback_query) {
|
|
532
|
-
void this.dispatchCallback(upd.callback_query)
|
|
540
|
+
void this.dispatchCallback(upd.callback_query).catch(
|
|
541
|
+
(err) => this.log.debug(
|
|
542
|
+
`Callback dispatch failed: ${err instanceof Error ? err.message : String(err)}`
|
|
543
|
+
)
|
|
544
|
+
);
|
|
533
545
|
continue;
|
|
534
546
|
}
|
|
535
547
|
const raw = upd.message ?? upd.edited_message;
|
|
@@ -572,7 +584,11 @@ var TelegramBot = class _TelegramBot {
|
|
|
572
584
|
const denialReason = this.inboundDenialReason(userId, chatId);
|
|
573
585
|
if (denialReason === "user") {
|
|
574
586
|
this.log.debug(`Ignoring message from user ${userId ?? "unknown"} (not in allowedUsers)`);
|
|
575
|
-
void this.sendMessage(chatId, "\u26D4 You are not authorized to interact with this bot.")
|
|
587
|
+
void this.sendMessage(chatId, "\u26D4 You are not authorized to interact with this bot.").catch(
|
|
588
|
+
(err) => this.log.debug(
|
|
589
|
+
`Failed to send denial notice: ${err instanceof Error ? err.message : String(err)}`
|
|
590
|
+
)
|
|
591
|
+
);
|
|
576
592
|
return;
|
|
577
593
|
}
|
|
578
594
|
if (denialReason === "chat") {
|
|
@@ -736,7 +752,11 @@ var TelegramBot = class _TelegramBot {
|
|
|
736
752
|
request.promptMessageId = promptMessageId;
|
|
737
753
|
const pending = request.pendingCallbacks.splice(0);
|
|
738
754
|
for (const callback of pending) {
|
|
739
|
-
void this.dispatchCallback(callback)
|
|
755
|
+
void this.dispatchCallback(callback).catch(
|
|
756
|
+
(err) => this.log.debug(
|
|
757
|
+
`Callback dispatch failed: ${err instanceof Error ? err.message : String(err)}`
|
|
758
|
+
)
|
|
759
|
+
);
|
|
740
760
|
}
|
|
741
761
|
return true;
|
|
742
762
|
}
|
|
@@ -802,7 +822,9 @@ function truncateForTelegram(text, maxLen = 4e3) {
|
|
|
802
822
|
}
|
|
803
823
|
|
|
804
824
|
// src/config.ts
|
|
825
|
+
import { resolvePluginConfig } from "@wrongstack/core/plugin";
|
|
805
826
|
var PLUGIN_NAME = "telegram";
|
|
827
|
+
var PLUGIN_CONFIG_ALIASES = ["@wrongstack/telegram"];
|
|
806
828
|
var INBOUND_MODES = ["disabled", "paired", "allowlist", "public"];
|
|
807
829
|
var DEFAULT_CONFIG = {
|
|
808
830
|
inboundMode: "disabled",
|
|
@@ -816,7 +838,32 @@ var DEFAULT_CONFIG = {
|
|
|
816
838
|
maxMessageLength: 4e3,
|
|
817
839
|
singleInstanceLock: true,
|
|
818
840
|
outboundQueuePerChat: 32,
|
|
819
|
-
outboundQueueConcurrency: 4
|
|
841
|
+
outboundQueueConcurrency: 4,
|
|
842
|
+
allowGroupApprovals: false,
|
|
843
|
+
rateLimitTokensPerSecond: 0.33,
|
|
844
|
+
rateLimitBurst: 4,
|
|
845
|
+
parseMode: ""
|
|
846
|
+
};
|
|
847
|
+
var TELEGRAM_CONFIG_FIELDS = {
|
|
848
|
+
botToken: { lifecycle: "restart", secret: true },
|
|
849
|
+
notifyChatId: { lifecycle: "restart" },
|
|
850
|
+
inboundMode: { lifecycle: "hot" },
|
|
851
|
+
allowedUsers: { lifecycle: "hot" },
|
|
852
|
+
allowedChats: { lifecycle: "hot" },
|
|
853
|
+
allowedOutboundChats: { lifecycle: "hot" },
|
|
854
|
+
allowGroupApprovals: { lifecycle: "hot" },
|
|
855
|
+
pollIntervalSec: { lifecycle: "hot" },
|
|
856
|
+
notifyOnSessionEnd: { lifecycle: "hot" },
|
|
857
|
+
longToolThresholdMs: { lifecycle: "hot" },
|
|
858
|
+
notifyOnDelegate: { lifecycle: "hot" },
|
|
859
|
+
maxMessageLength: { lifecycle: "hot" },
|
|
860
|
+
offsetStoragePath: { lifecycle: "immutable" },
|
|
861
|
+
singleInstanceLock: { lifecycle: "restart" },
|
|
862
|
+
outboundQueuePerChat: { lifecycle: "restart" },
|
|
863
|
+
outboundQueueConcurrency: { lifecycle: "restart" },
|
|
864
|
+
rateLimitTokensPerSecond: { lifecycle: "hot", description: "Per-chat rate limit (tokens/sec)" },
|
|
865
|
+
rateLimitBurst: { lifecycle: "hot", description: "Per-chat rate limit burst size" },
|
|
866
|
+
parseMode: { lifecycle: "hot", description: "Telegram parse mode: HTML, MarkdownV2, or empty for plain text" }
|
|
820
867
|
};
|
|
821
868
|
var telegramConfigSchema = {
|
|
822
869
|
type: "object",
|
|
@@ -857,6 +904,7 @@ var telegramConfigSchema = {
|
|
|
857
904
|
longToolThresholdMs: { type: "integer", minimum: 0 },
|
|
858
905
|
notifyOnDelegate: { type: "boolean" },
|
|
859
906
|
maxMessageLength: { type: "integer", minimum: 100, maximum: 4096 },
|
|
907
|
+
offsetStoragePath: { type: "string" },
|
|
860
908
|
singleInstanceLock: {
|
|
861
909
|
type: "boolean",
|
|
862
910
|
description: "Elect a single getUpdates poller per bot token across wstack instances (default true)"
|
|
@@ -872,24 +920,37 @@ var telegramConfigSchema = {
|
|
|
872
920
|
minimum: 1,
|
|
873
921
|
maximum: 64,
|
|
874
922
|
description: "Maximum concurrent outbound sends across all chats (default 4)"
|
|
923
|
+
},
|
|
924
|
+
allowGroupApprovals: { type: "boolean" },
|
|
925
|
+
rateLimitTokensPerSecond: {
|
|
926
|
+
type: "number",
|
|
927
|
+
minimum: 0.1,
|
|
928
|
+
maximum: 100,
|
|
929
|
+
description: "Per-chat rate limit in tokens per second (default: 0.33 \u224820 msg/min)"
|
|
930
|
+
},
|
|
931
|
+
rateLimitBurst: {
|
|
932
|
+
type: "integer",
|
|
933
|
+
minimum: 1,
|
|
934
|
+
maximum: 100,
|
|
935
|
+
description: "Per-chat burst size (default: 1)"
|
|
936
|
+
},
|
|
937
|
+
parseMode: {
|
|
938
|
+
type: "string",
|
|
939
|
+
enum: ["", "HTML", "MarkdownV2"],
|
|
940
|
+
description: "Telegram parse mode: HTML, MarkdownV2, or empty for plain text"
|
|
875
941
|
}
|
|
876
942
|
},
|
|
877
943
|
required: ["botToken"]
|
|
878
944
|
};
|
|
879
945
|
function readTelegramConfig(api) {
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
const
|
|
886
|
-
const extensionOpts = extensions?.[PLUGIN_NAME];
|
|
887
|
-
const opts = {
|
|
888
|
-
...legacyOpts ?? entryOpts,
|
|
889
|
-
...extensionOpts ?? {}
|
|
890
|
-
};
|
|
946
|
+
const resolution = resolvePluginConfig({
|
|
947
|
+
name: PLUGIN_NAME,
|
|
948
|
+
aliases: PLUGIN_CONFIG_ALIASES,
|
|
949
|
+
config: api.config
|
|
950
|
+
});
|
|
951
|
+
const opts = resolution.options;
|
|
891
952
|
const inboundMode = resolveInboundMode(opts, {
|
|
892
|
-
configured:
|
|
953
|
+
configured: resolution.configured,
|
|
893
954
|
warn: api.log?.warn.bind(api.log)
|
|
894
955
|
});
|
|
895
956
|
return {
|
|
@@ -898,6 +959,9 @@ function readTelegramConfig(api) {
|
|
|
898
959
|
inboundMode
|
|
899
960
|
};
|
|
900
961
|
}
|
|
962
|
+
function readTelegramConfigFromConfig(cfg) {
|
|
963
|
+
return readTelegramConfig({ config: cfg });
|
|
964
|
+
}
|
|
901
965
|
function resolveInboundMode(opts, migration) {
|
|
902
966
|
if (opts.inboundMode !== void 0) {
|
|
903
967
|
if (!INBOUND_MODES.includes(opts.inboundMode)) {
|
|
@@ -927,13 +991,6 @@ function resolveInboundMode(opts, migration) {
|
|
|
927
991
|
function hasEntries(values) {
|
|
928
992
|
return Array.isArray(values) && values.length > 0;
|
|
929
993
|
}
|
|
930
|
-
function pluginOptionsFromEntries(entries) {
|
|
931
|
-
if (!Array.isArray(entries)) return void 0;
|
|
932
|
-
const found = entries.find(
|
|
933
|
-
(entry) => typeof entry === "object" && entry !== null && "name" in entry && (entry.name === "@wrongstack/telegram" || entry.name === PLUGIN_NAME)
|
|
934
|
-
);
|
|
935
|
-
return found?.options && typeof found.options === "object" ? found.options : void 0;
|
|
936
|
-
}
|
|
937
994
|
|
|
938
995
|
// src/redact.ts
|
|
939
996
|
var SENSITIVE_FLAG_PATTERNS = [
|
|
@@ -965,7 +1022,7 @@ function redactSecrets(text) {
|
|
|
965
1022
|
delim = "=";
|
|
966
1023
|
delimIdx = eq;
|
|
967
1024
|
} else if (sp !== -1) {
|
|
968
|
-
delim = match[sp]
|
|
1025
|
+
delim = match[sp];
|
|
969
1026
|
delimIdx = sp;
|
|
970
1027
|
}
|
|
971
1028
|
if (delim !== null && delimIdx >= 0) {
|
|
@@ -1143,6 +1200,7 @@ var PollLock = class {
|
|
|
1143
1200
|
const raw = readFileSync(this.lockPath, "utf8");
|
|
1144
1201
|
const parsed = JSON.parse(raw);
|
|
1145
1202
|
if (typeof parsed.id !== "string" || typeof parsed.pid !== "number") return null;
|
|
1203
|
+
if (!Number.isFinite(parsed.heartbeatAt)) return null;
|
|
1146
1204
|
return parsed;
|
|
1147
1205
|
} catch {
|
|
1148
1206
|
return null;
|
|
@@ -1246,7 +1304,8 @@ var OffsetStore = class {
|
|
|
1246
1304
|
};
|
|
1247
1305
|
|
|
1248
1306
|
// src/security/outbound.ts
|
|
1249
|
-
import { DefaultSecretScrubber
|
|
1307
|
+
import { DefaultSecretScrubber } from "@wrongstack/core/security";
|
|
1308
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
1250
1309
|
var TELEGRAM_APPROVAL_CAPABILITY = "net.outbound.telegram.approval";
|
|
1251
1310
|
var secretScrubber = new DefaultSecretScrubber();
|
|
1252
1311
|
var RAW_TELEGRAM_BOT_TOKEN = /(?<![A-Za-z0-9])\d{5,15}:[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g;
|
|
@@ -1288,7 +1347,7 @@ function scrubTelegramOutboundText(text) {
|
|
|
1288
1347
|
}
|
|
1289
1348
|
|
|
1290
1349
|
// src/slash-commands/index.ts
|
|
1291
|
-
import { expectDefined } from "@wrongstack/core";
|
|
1350
|
+
import { expectDefined } from "@wrongstack/core/utils";
|
|
1292
1351
|
function tgHealthCommand(bot, cfg) {
|
|
1293
1352
|
return {
|
|
1294
1353
|
name: "telegram-health",
|
|
@@ -1427,7 +1486,7 @@ function makeTelegramApproveTool(opts) {
|
|
|
1427
1486
|
const timeoutMs = Math.min(Math.max(input.timeout_ms ?? 6e4, 1e3), 6e5);
|
|
1428
1487
|
const configuredUserIds = opts.getAllowedUserIds?.().map(String) ?? [];
|
|
1429
1488
|
const isGroup = String(chatId).startsWith("-");
|
|
1430
|
-
if (isGroup && (opts.
|
|
1489
|
+
if (isGroup && (opts.getAllowGroupApprovals?.() !== true || configuredUserIds.length === 0)) {
|
|
1431
1490
|
throw new Error("Telegram group approvals require explicit per-user configuration.");
|
|
1432
1491
|
}
|
|
1433
1492
|
const expectedUserIds = configuredUserIds.length > 0 ? configuredUserIds : [String(chatId)];
|
|
@@ -1449,7 +1508,7 @@ _Reply by tapping a button. Auto-denies in ${Math.round(timeoutMs / 1e3)}s._`;
|
|
|
1449
1508
|
sessionId: ctx?.session.id ?? "unknown-session",
|
|
1450
1509
|
expectedChatId: chatId,
|
|
1451
1510
|
expectedUserIds,
|
|
1452
|
-
allowGroup: isGroup && opts.
|
|
1511
|
+
allowGroup: isGroup && opts.getAllowGroupApprovals?.() === true,
|
|
1453
1512
|
expiresAt: Date.now() + timeoutMs,
|
|
1454
1513
|
signal: toolOpts?.signal
|
|
1455
1514
|
});
|
|
@@ -1500,8 +1559,8 @@ var OutboundQueue = class {
|
|
|
1500
1559
|
#failed = 0;
|
|
1501
1560
|
#resolvers = /* @__PURE__ */ new Map();
|
|
1502
1561
|
constructor(opts) {
|
|
1503
|
-
const maxPerChat = opts.maxPerChat ?? DEFAULT_MAX_PER_CHAT;
|
|
1504
|
-
const maxConcurrency = opts.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY;
|
|
1562
|
+
const maxPerChat = Math.max(1, opts.maxPerChat ?? DEFAULT_MAX_PER_CHAT);
|
|
1563
|
+
const maxConcurrency = Math.max(1, opts.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY);
|
|
1505
1564
|
this.#opts = {
|
|
1506
1565
|
maxPerChat,
|
|
1507
1566
|
maxConcurrency,
|
|
@@ -1529,18 +1588,19 @@ var OutboundQueue = class {
|
|
|
1529
1588
|
}
|
|
1530
1589
|
if (entry.kind === "notification") {
|
|
1531
1590
|
if (lane.pending.length >= this.#opts.maxPerChat) {
|
|
1532
|
-
const
|
|
1533
|
-
if (
|
|
1591
|
+
const dropIndex = lane.pending.findIndex((e) => e.kind === "notification");
|
|
1592
|
+
if (dropIndex === -1) {
|
|
1534
1593
|
this.#dropped += 1;
|
|
1535
|
-
const droppedResolver = this.#resolvers.get(dropped.id);
|
|
1536
|
-
if (droppedResolver) {
|
|
1537
|
-
this.#resolvers.delete(dropped.id);
|
|
1538
|
-
droppedResolver.resolve(void 0);
|
|
1539
|
-
}
|
|
1540
1594
|
this.#opts.log?.debug(
|
|
1541
|
-
`Telegram outbound queue dropped
|
|
1595
|
+
`Telegram outbound queue dropped an incoming notification for chat ${entry.chatId} (per-chat limit ${this.#opts.maxPerChat}; only manual entries pending)`
|
|
1542
1596
|
);
|
|
1597
|
+
return Promise.resolve(void 0);
|
|
1543
1598
|
}
|
|
1599
|
+
const dropped = lane.pending.splice(dropIndex, 1)[0];
|
|
1600
|
+
this.#dropped += 1;
|
|
1601
|
+
this.#opts.log?.debug(
|
|
1602
|
+
`Telegram outbound queue dropped a notification for chat ${dropped.chatId} (per-chat limit ${this.#opts.maxPerChat})`
|
|
1603
|
+
);
|
|
1544
1604
|
}
|
|
1545
1605
|
} else if (lane.pending.length + (lane.running ? 1 : 0) >= this.#opts.maxPerChat) {
|
|
1546
1606
|
return Promise.reject(
|
|
@@ -1632,15 +1692,6 @@ var OutboundQueue = class {
|
|
|
1632
1692
|
async #run(entry) {
|
|
1633
1693
|
const key = String(entry.chatId);
|
|
1634
1694
|
const lane = this.#lanes.get(key);
|
|
1635
|
-
if (!lane) {
|
|
1636
|
-
const resolver = this.#resolvers.get(entry.id);
|
|
1637
|
-
if (resolver) {
|
|
1638
|
-
this.#resolvers.delete(entry.id);
|
|
1639
|
-
resolver.resolve(void 0);
|
|
1640
|
-
}
|
|
1641
|
-
this.#active -= 1;
|
|
1642
|
-
return;
|
|
1643
|
-
}
|
|
1644
1695
|
try {
|
|
1645
1696
|
const result = await this.#opts.send(entry.chatId, entry.text);
|
|
1646
1697
|
this.#sent += 1;
|
|
@@ -1655,7 +1706,7 @@ var OutboundQueue = class {
|
|
|
1655
1706
|
if (resolver) {
|
|
1656
1707
|
this.#resolvers.delete(entry.id);
|
|
1657
1708
|
resolver.reject(err);
|
|
1658
|
-
} else
|
|
1709
|
+
} else {
|
|
1659
1710
|
this.#opts.log?.debug(
|
|
1660
1711
|
`Telegram outbound queue notification failed for chat ${entry.chatId}: ${err.message}`
|
|
1661
1712
|
);
|
|
@@ -1663,31 +1714,115 @@ var OutboundQueue = class {
|
|
|
1663
1714
|
} finally {
|
|
1664
1715
|
this.#active -= 1;
|
|
1665
1716
|
lane.running = false;
|
|
1717
|
+
if (lane.pending.length === 0) {
|
|
1718
|
+
this.#lanes.delete(key);
|
|
1719
|
+
}
|
|
1666
1720
|
this.#schedule();
|
|
1667
1721
|
}
|
|
1668
1722
|
}
|
|
1669
1723
|
};
|
|
1670
1724
|
|
|
1725
|
+
// src/rate-limiter.ts
|
|
1726
|
+
function createTokenBucket(opts) {
|
|
1727
|
+
const tokensPerSecond = opts?.tokensPerSecond ?? 0.33;
|
|
1728
|
+
const burst = opts?.burst ?? 4;
|
|
1729
|
+
const refillIntervalMs = 1e3 / tokensPerSecond;
|
|
1730
|
+
let tokens = burst;
|
|
1731
|
+
let lastRefill = Date.now();
|
|
1732
|
+
return { waitForToken, fill, isFull };
|
|
1733
|
+
async function waitForToken(timeoutMs) {
|
|
1734
|
+
const deadline = timeoutMs !== void 0 ? Date.now() + timeoutMs : Infinity;
|
|
1735
|
+
while (true) {
|
|
1736
|
+
refill();
|
|
1737
|
+
if (tokens >= 1) {
|
|
1738
|
+
tokens -= 1;
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1741
|
+
const now = Date.now();
|
|
1742
|
+
if (now >= deadline) {
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
const nextRefill = lastRefill + refillIntervalMs;
|
|
1746
|
+
const delay = Math.min(
|
|
1747
|
+
Math.max(nextRefill - now, 0),
|
|
1748
|
+
deadline - now,
|
|
1749
|
+
5e3
|
|
1750
|
+
// safety cap: never sleep longer than 5s
|
|
1751
|
+
);
|
|
1752
|
+
await sleep(delay);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
function refill() {
|
|
1756
|
+
const now = Date.now();
|
|
1757
|
+
const elapsed = now - lastRefill;
|
|
1758
|
+
if (elapsed <= 0) return;
|
|
1759
|
+
const newTokens = elapsed / 1e3 * tokensPerSecond;
|
|
1760
|
+
tokens = Math.min(burst, tokens + newTokens);
|
|
1761
|
+
lastRefill = now;
|
|
1762
|
+
}
|
|
1763
|
+
function fill() {
|
|
1764
|
+
refill();
|
|
1765
|
+
return tokens;
|
|
1766
|
+
}
|
|
1767
|
+
function isFull() {
|
|
1768
|
+
refill();
|
|
1769
|
+
return tokens >= burst;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
function sleep(ms) {
|
|
1773
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1671
1776
|
// src/bot-queue.ts
|
|
1777
|
+
var BUCKET_SWEEP_INTERVAL_MS = 6e4;
|
|
1672
1778
|
var TelegramBotOutbound = class {
|
|
1673
1779
|
#queue;
|
|
1674
1780
|
#bot;
|
|
1675
1781
|
#log;
|
|
1782
|
+
#buckets = /* @__PURE__ */ new Map();
|
|
1783
|
+
#getRateTokensPerSecond;
|
|
1784
|
+
#getRateBurst;
|
|
1785
|
+
#bucketSweepTimer;
|
|
1676
1786
|
#stopped = false;
|
|
1677
1787
|
constructor(opts) {
|
|
1678
1788
|
this.#bot = opts.bot;
|
|
1679
1789
|
this.#log = opts.log;
|
|
1790
|
+
this.#getRateTokensPerSecond = opts.getRateLimitTokensPerSecond ?? (() => 0.33);
|
|
1791
|
+
this.#getRateBurst = opts.getRateLimitBurst ?? (() => 4);
|
|
1680
1792
|
this.#queue = new OutboundQueue({
|
|
1681
1793
|
maxPerChat: opts.maxPerChat,
|
|
1682
1794
|
maxConcurrency: opts.maxConcurrency,
|
|
1683
|
-
send: (chatId, text) => this.#
|
|
1684
|
-
if (!res.ok) {
|
|
1685
|
-
throw new Error(`Telegram outbound send returned ok=false for chat ${chatId}`);
|
|
1686
|
-
}
|
|
1687
|
-
return res;
|
|
1688
|
-
}),
|
|
1795
|
+
send: (chatId, text) => this.#rateLimitedSend(chatId, text),
|
|
1689
1796
|
log: opts.log
|
|
1690
1797
|
});
|
|
1798
|
+
this.#bucketSweepTimer = setInterval(() => this.#sweepIdleBuckets(), BUCKET_SWEEP_INTERVAL_MS);
|
|
1799
|
+
this.#bucketSweepTimer.unref?.();
|
|
1800
|
+
}
|
|
1801
|
+
#sweepIdleBuckets() {
|
|
1802
|
+
for (const [key, bucket] of this.#buckets) {
|
|
1803
|
+
if (bucket.isFull()) this.#buckets.delete(key);
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
/** Per-chat rate-limited send: waits for a token, then delegates to the bot.
|
|
1807
|
+
* Rate-limit values are resolved lazily when a new bucket is created via
|
|
1808
|
+
* the constructor getters so a live config change applies to subsequent
|
|
1809
|
+
* chats without rebuilding the queue. */
|
|
1810
|
+
async #rateLimitedSend(chatId, text) {
|
|
1811
|
+
const key = String(chatId);
|
|
1812
|
+
let bucket = this.#buckets.get(key);
|
|
1813
|
+
if (!bucket) {
|
|
1814
|
+
bucket = createTokenBucket({
|
|
1815
|
+
tokensPerSecond: this.#getRateTokensPerSecond(),
|
|
1816
|
+
burst: this.#getRateBurst()
|
|
1817
|
+
});
|
|
1818
|
+
this.#buckets.set(key, bucket);
|
|
1819
|
+
}
|
|
1820
|
+
await bucket.waitForToken(5e3);
|
|
1821
|
+
const res = await this.#bot.sendMessage(chatId, text);
|
|
1822
|
+
if (!res.ok) {
|
|
1823
|
+
throw new Error(`Telegram outbound send returned ok=false for chat ${chatId}`);
|
|
1824
|
+
}
|
|
1825
|
+
return res;
|
|
1691
1826
|
}
|
|
1692
1827
|
/** Manual send (telegram_send tool, /telegram:send): never silently dropped. */
|
|
1693
1828
|
async sendManual(chatId, text) {
|
|
@@ -1711,17 +1846,14 @@ var TelegramBotOutbound = class {
|
|
|
1711
1846
|
return;
|
|
1712
1847
|
}
|
|
1713
1848
|
const entry = { chatId, text, kind: "notification" };
|
|
1714
|
-
this.#queue.enqueue(entry)
|
|
1715
|
-
this.#log.debug(
|
|
1716
|
-
`Telegram outbound notification enqueue rejected for chat ${chatId}: ${err.message}`
|
|
1717
|
-
);
|
|
1718
|
-
});
|
|
1849
|
+
void this.#queue.enqueue(entry);
|
|
1719
1850
|
}
|
|
1720
1851
|
stats() {
|
|
1721
1852
|
return this.#queue.stats();
|
|
1722
1853
|
}
|
|
1723
1854
|
async stop() {
|
|
1724
1855
|
this.#stopped = true;
|
|
1856
|
+
clearInterval(this.#bucketSweepTimer);
|
|
1725
1857
|
await this.#queue.stop();
|
|
1726
1858
|
}
|
|
1727
1859
|
};
|
|
@@ -1868,7 +2000,7 @@ function makeTelegramReadTool(opts) {
|
|
|
1868
2000
|
}
|
|
1869
2001
|
|
|
1870
2002
|
// src/tools/telegram-send.ts
|
|
1871
|
-
import { ToolCapabilities } from "@wrongstack/core";
|
|
2003
|
+
import { ToolCapabilities } from "@wrongstack/core/security";
|
|
1872
2004
|
function makeTelegramSendTool(opts) {
|
|
1873
2005
|
return {
|
|
1874
2006
|
name: "telegram_send",
|
|
@@ -1912,6 +2044,19 @@ function makeTelegramSendTool(opts) {
|
|
|
1912
2044
|
};
|
|
1913
2045
|
}
|
|
1914
2046
|
|
|
2047
|
+
// src/config-classifier.ts
|
|
2048
|
+
import { diffPluginConfig } from "@wrongstack/core/plugin";
|
|
2049
|
+
function diffConfigKeys(previous, next) {
|
|
2050
|
+
return diffPluginConfig(
|
|
2051
|
+
previous,
|
|
2052
|
+
next,
|
|
2053
|
+
TELEGRAM_CONFIG_FIELDS
|
|
2054
|
+
).map((change) => ({
|
|
2055
|
+
key: change.key,
|
|
2056
|
+
classification: change.lifecycle === "hot" ? "hot" : "restart-required"
|
|
2057
|
+
}));
|
|
2058
|
+
}
|
|
2059
|
+
|
|
1915
2060
|
// src/index.ts
|
|
1916
2061
|
var teardownState = null;
|
|
1917
2062
|
var DENY_ALL_INBOUND = "__wrongstack_telegram_inbound_disabled__";
|
|
@@ -1958,22 +2103,25 @@ function registerCommand(api, command, cleanups) {
|
|
|
1958
2103
|
api.slashCommands.unregister(`${PLUGIN_NAME}:${command.name}`);
|
|
1959
2104
|
});
|
|
1960
2105
|
}
|
|
2106
|
+
function telegramDefaultConfig() {
|
|
2107
|
+
return structuredClone(DEFAULT_CONFIG);
|
|
2108
|
+
}
|
|
1961
2109
|
function telegramFromConfig(cfg) {
|
|
1962
|
-
const
|
|
2110
|
+
const tg = readTelegramConfigFromConfig(cfg);
|
|
1963
2111
|
return {
|
|
1964
|
-
notifyChatId:
|
|
1965
|
-
allowedOutboundChats:
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2112
|
+
notifyChatId: tg.notifyChatId !== void 0 ? String(tg.notifyChatId) : void 0,
|
|
2113
|
+
allowedOutboundChats: [...tg.allowedOutboundChats ?? []],
|
|
2114
|
+
allowedUserIds: [...tg.allowedUsers ?? []],
|
|
2115
|
+
allowGroupApprovals: tg.allowGroupApprovals ?? false,
|
|
2116
|
+
notifyOnSessionEnd: tg.notifyOnSessionEnd ?? false,
|
|
2117
|
+
notifyOnDelegate: tg.notifyOnDelegate ?? true,
|
|
2118
|
+
longToolThresholdMs: tg.longToolThresholdMs ?? 3e4,
|
|
2119
|
+
maxMessageLength: tg.maxMessageLength ?? 4e3,
|
|
2120
|
+
outboundQueuePerChat: tg.outboundQueuePerChat ?? 32,
|
|
2121
|
+
outboundQueueConcurrency: tg.outboundQueueConcurrency ?? 4,
|
|
2122
|
+
rateLimitTokensPerSecond: tg.rateLimitTokensPerSecond ?? 0.33,
|
|
2123
|
+
rateLimitBurst: tg.rateLimitBurst ?? 1,
|
|
2124
|
+
parseMode: tg.parseMode ?? ""
|
|
1977
2125
|
};
|
|
1978
2126
|
}
|
|
1979
2127
|
var plugin = {
|
|
@@ -1986,31 +2134,29 @@ var plugin = {
|
|
|
1986
2134
|
slashCommands: true,
|
|
1987
2135
|
pipelines: []
|
|
1988
2136
|
},
|
|
2137
|
+
configAliases: [...PLUGIN_CONFIG_ALIASES],
|
|
2138
|
+
configFields: TELEGRAM_CONFIG_FIELDS,
|
|
1989
2139
|
configSchema: telegramConfigSchema,
|
|
1990
|
-
defaultConfig:
|
|
1991
|
-
allowedOutboundChats: [],
|
|
1992
|
-
pollIntervalSec: 2,
|
|
1993
|
-
notifyOnSessionEnd: false,
|
|
1994
|
-
longToolThresholdMs: 3e4,
|
|
1995
|
-
maxMessageLength: 4e3
|
|
1996
|
-
},
|
|
2140
|
+
defaultConfig: telegramDefaultConfig(),
|
|
1997
2141
|
async setup(api) {
|
|
1998
2142
|
const log = api.log;
|
|
1999
2143
|
disposeRuntime(log);
|
|
2000
2144
|
const cfg = readTelegramConfig(api);
|
|
2001
2145
|
log.info("Starting Telegram plugin...");
|
|
2002
|
-
const rawCfg = cfg;
|
|
2003
2146
|
const runtimeCfg = {
|
|
2004
2147
|
notifyChatId: cfg.notifyChatId,
|
|
2005
2148
|
allowedOutboundChats: [...cfg.allowedOutboundChats ?? []],
|
|
2006
2149
|
allowedUserIds: [...cfg.allowedUsers ?? []],
|
|
2007
|
-
allowGroupApprovals:
|
|
2150
|
+
allowGroupApprovals: cfg.allowGroupApprovals ?? false,
|
|
2008
2151
|
notifyOnSessionEnd: cfg.notifyOnSessionEnd ?? false,
|
|
2009
2152
|
notifyOnDelegate: cfg.notifyOnDelegate ?? true,
|
|
2010
2153
|
longToolThresholdMs: cfg.longToolThresholdMs ?? 3e4,
|
|
2011
2154
|
maxMessageLength: cfg.maxMessageLength ?? 4e3,
|
|
2012
2155
|
outboundQueuePerChat: cfg.outboundQueuePerChat ?? 32,
|
|
2013
|
-
outboundQueueConcurrency: cfg.outboundQueueConcurrency ?? 4
|
|
2156
|
+
outboundQueueConcurrency: cfg.outboundQueueConcurrency ?? 4,
|
|
2157
|
+
rateLimitTokensPerSecond: cfg.rateLimitTokensPerSecond ?? 0.33,
|
|
2158
|
+
rateLimitBurst: cfg.rateLimitBurst ?? 1,
|
|
2159
|
+
parseMode: cfg.parseMode ?? ""
|
|
2014
2160
|
};
|
|
2015
2161
|
const lock = cfg.singleInstanceLock === false ? void 0 : new PollLock(lockPathForToken(cfg.botToken), { log });
|
|
2016
2162
|
const offsetStore = cfg.offsetStoragePath === "" ? void 0 : new OffsetStore({ token: cfg.botToken, path: cfg.offsetStoragePath });
|
|
@@ -2022,8 +2168,24 @@ var plugin = {
|
|
|
2022
2168
|
log,
|
|
2023
2169
|
offsetStore,
|
|
2024
2170
|
lock,
|
|
2171
|
+
getParseMode: () => runtimeCfg.parseMode,
|
|
2025
2172
|
onMessage(msg) {
|
|
2026
2173
|
api.emitCustom("telegram:message_received", msg);
|
|
2174
|
+
const mailbox = api.mailbox;
|
|
2175
|
+
if (mailbox) {
|
|
2176
|
+
mailbox.send({
|
|
2177
|
+
from: "telegram",
|
|
2178
|
+
to: "leader",
|
|
2179
|
+
type: "note",
|
|
2180
|
+
subject: scrubTelegramOutboundText(
|
|
2181
|
+
`\u{1F4E8} Telegram from ${msg.userName ?? `user_${msg.userId ?? "unknown"}`}`
|
|
2182
|
+
),
|
|
2183
|
+
body: scrubTelegramOutboundText(msg.text),
|
|
2184
|
+
priority: "low"
|
|
2185
|
+
}).catch((err) => {
|
|
2186
|
+
log.debug(`Telegram\u2192mailbox bridge delivery failed: ${err.message}`);
|
|
2187
|
+
});
|
|
2188
|
+
}
|
|
2027
2189
|
log.info(`\u{1F4E8} Telegram message received (${Math.min(bot.bufferCount, 50)} unread)`);
|
|
2028
2190
|
}
|
|
2029
2191
|
});
|
|
@@ -2042,7 +2204,9 @@ var plugin = {
|
|
|
2042
2204
|
bot,
|
|
2043
2205
|
log,
|
|
2044
2206
|
maxPerChat: runtimeCfg.outboundQueuePerChat,
|
|
2045
|
-
maxConcurrency: runtimeCfg.outboundQueueConcurrency
|
|
2207
|
+
maxConcurrency: runtimeCfg.outboundQueueConcurrency,
|
|
2208
|
+
getRateLimitTokensPerSecond: () => runtimeCfg.rateLimitTokensPerSecond,
|
|
2209
|
+
getRateLimitBurst: () => runtimeCfg.rateLimitBurst
|
|
2046
2210
|
});
|
|
2047
2211
|
cleanups.push(() => {
|
|
2048
2212
|
void outbound.stop();
|
|
@@ -2060,7 +2224,7 @@ var plugin = {
|
|
|
2060
2224
|
getDefaultChatId: () => runtimeCfg.notifyChatId,
|
|
2061
2225
|
getAllowedOutboundChatIds: () => runtimeCfg.allowedOutboundChats,
|
|
2062
2226
|
getAllowedUserIds: () => runtimeCfg.allowedUserIds,
|
|
2063
|
-
|
|
2227
|
+
getAllowGroupApprovals: () => runtimeCfg.allowGroupApprovals,
|
|
2064
2228
|
maxMessageLength: runtimeCfg.maxMessageLength,
|
|
2065
2229
|
log
|
|
2066
2230
|
});
|
|
@@ -2128,6 +2292,8 @@ var plugin = {
|
|
|
2128
2292
|
source: "session.end"
|
|
2129
2293
|
}).then((r) => {
|
|
2130
2294
|
if (!r.ok) log.warn(`session.ended notification delivery failed: ${r.error ?? "unknown"}`);
|
|
2295
|
+
}).catch((err) => {
|
|
2296
|
+
log.debug(`session.ended notification delivery threw: ${err.message}`);
|
|
2131
2297
|
});
|
|
2132
2298
|
})
|
|
2133
2299
|
);
|
|
@@ -2148,6 +2314,8 @@ var plugin = {
|
|
|
2148
2314
|
source: "tool.exec"
|
|
2149
2315
|
}).then((r) => {
|
|
2150
2316
|
if (!r.ok) log.warn(`tool.executed notification delivery failed: ${r.error ?? "unknown"}`);
|
|
2317
|
+
}).catch((err) => {
|
|
2318
|
+
log.debug(`tool.executed notification delivery threw: ${err.message}`);
|
|
2151
2319
|
});
|
|
2152
2320
|
})
|
|
2153
2321
|
);
|
|
@@ -2168,33 +2336,81 @@ var plugin = {
|
|
|
2168
2336
|
source: "delegate.completed"
|
|
2169
2337
|
}).then((r) => {
|
|
2170
2338
|
if (!r.ok) log.warn(`delegate.completed notification delivery failed: ${r.error ?? "unknown"}`);
|
|
2339
|
+
}).catch((err) => {
|
|
2340
|
+
log.debug(`delegate.completed notification delivery threw: ${err.message}`);
|
|
2171
2341
|
});
|
|
2172
2342
|
})
|
|
2173
2343
|
);
|
|
2174
2344
|
const unlistenConfig = api.onConfigChange((next, prev) => {
|
|
2345
|
+
const nextTg = readTelegramConfigFromConfig(next);
|
|
2346
|
+
const prevTg = readTelegramConfigFromConfig(prev);
|
|
2347
|
+
const changedKeys = diffConfigKeys(prevTg, nextTg);
|
|
2348
|
+
const hotKeys = changedKeys.filter((c) => c.classification === "hot").map((c) => c.key);
|
|
2349
|
+
const restartKeys = changedKeys.filter((c) => c.classification === "restart-required").map((c) => c.key);
|
|
2175
2350
|
const fresh = telegramFromConfig(next);
|
|
2176
2351
|
const was = telegramFromConfig(prev);
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2352
|
+
const hotSet = new Set(hotKeys);
|
|
2353
|
+
const HOT_APPLIERS = /* @__PURE__ */ new Map([
|
|
2354
|
+
["allowedOutboundChats", (r, f) => {
|
|
2355
|
+
r.allowedOutboundChats = f.allowedOutboundChats;
|
|
2356
|
+
}],
|
|
2357
|
+
["allowedUsers", (r, f) => {
|
|
2358
|
+
r.allowedUserIds = f.allowedUserIds;
|
|
2359
|
+
}],
|
|
2360
|
+
["notifyOnSessionEnd", (r, f) => {
|
|
2361
|
+
r.notifyOnSessionEnd = f.notifyOnSessionEnd;
|
|
2362
|
+
}],
|
|
2363
|
+
["notifyOnDelegate", (r, f) => {
|
|
2364
|
+
r.notifyOnDelegate = f.notifyOnDelegate;
|
|
2365
|
+
}],
|
|
2366
|
+
["longToolThresholdMs", (r, f) => {
|
|
2367
|
+
r.longToolThresholdMs = f.longToolThresholdMs;
|
|
2368
|
+
}],
|
|
2369
|
+
["maxMessageLength", (r, f) => {
|
|
2370
|
+
r.maxMessageLength = f.maxMessageLength;
|
|
2371
|
+
}],
|
|
2372
|
+
["allowGroupApprovals", (r, f) => {
|
|
2373
|
+
r.allowGroupApprovals = f.allowGroupApprovals;
|
|
2374
|
+
}],
|
|
2375
|
+
["rateLimitTokensPerSecond", (r, f) => {
|
|
2376
|
+
r.rateLimitTokensPerSecond = f.rateLimitTokensPerSecond;
|
|
2377
|
+
}],
|
|
2378
|
+
["rateLimitBurst", (r, f) => {
|
|
2379
|
+
r.rateLimitBurst = f.rateLimitBurst;
|
|
2380
|
+
}],
|
|
2381
|
+
["parseMode", (r, f) => {
|
|
2382
|
+
r.parseMode = f.parseMode;
|
|
2383
|
+
}]
|
|
2384
|
+
]);
|
|
2385
|
+
for (const [key, apply] of HOT_APPLIERS) {
|
|
2386
|
+
if (hotSet.has(key)) apply(runtimeCfg, fresh);
|
|
2387
|
+
}
|
|
2388
|
+
if (hotSet.has("maxMessageLength") && fresh.maxMessageLength !== was.maxMessageLength) {
|
|
2389
|
+
notifyChannel = runtimeCfg.notifyChatId !== void 0 ? new TelegramNotificationChannel({
|
|
2187
2390
|
bot,
|
|
2188
|
-
chatId:
|
|
2391
|
+
chatId: runtimeCfg.notifyChatId,
|
|
2189
2392
|
maxMessageLength: fresh.maxMessageLength,
|
|
2190
2393
|
enqueueNotification: (chatId, text) => outbound.enqueueNotification(chatId, text),
|
|
2191
2394
|
log
|
|
2192
2395
|
}) : void 0;
|
|
2193
2396
|
}
|
|
2194
|
-
|
|
2397
|
+
if (restartKeys.length > 0) {
|
|
2398
|
+
log.warn(
|
|
2399
|
+
"Telegram config changed restart-required keys \u2014 restart the plugin for these to take effect",
|
|
2400
|
+
{ restartKeys, hotKeys }
|
|
2401
|
+
);
|
|
2402
|
+
api.emitCustom("telegram:restart_required", {
|
|
2403
|
+
keys: restartKeys,
|
|
2404
|
+
message: `Restart required for: ${restartKeys.join(", ")}`
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
log.debug("Telegram config updated", {
|
|
2408
|
+
hotApplied: hotKeys,
|
|
2409
|
+
restartRequired: restartKeys,
|
|
2195
2410
|
notifyOnSessionEnd: runtimeCfg.notifyOnSessionEnd,
|
|
2196
2411
|
notifyOnDelegate: runtimeCfg.notifyOnDelegate,
|
|
2197
2412
|
longToolThresholdMs: runtimeCfg.longToolThresholdMs,
|
|
2413
|
+
parseMode: runtimeCfg.parseMode,
|
|
2198
2414
|
notifyChatId: runtimeCfg.notifyChatId ?? "not set"
|
|
2199
2415
|
});
|
|
2200
2416
|
});
|