@markusylisiurunen/tau 0.3.2 → 0.3.4
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 +13 -55
- package/dist/core/cli.js +2 -2
- package/dist/core/cli.js.map +1 -1
- package/dist/core/config/index.js.map +1 -1
- package/dist/core/config/schema.js +0 -159
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/{async/telegram.js → telegram/adapter.js} +256 -329
- package/dist/core/telegram/adapter.js.map +1 -0
- package/dist/core/telegram/cli.js +148 -0
- package/dist/core/telegram/cli.js.map +1 -0
- package/dist/core/telegram/config.js +241 -0
- package/dist/core/telegram/config.js.map +1 -0
- package/dist/core/telegram/index.js +4 -0
- package/dist/core/telegram/index.js.map +1 -0
- package/dist/core/telegram/runtime.js +96 -0
- package/dist/core/telegram/runtime.js.map +1 -0
- package/dist/core/{async → telegram}/session_manager.js +22 -22
- package/dist/core/telegram/session_manager.js.map +1 -0
- package/dist/core/telegram/workspace.js.map +1 -0
- package/dist/core/version.js +1 -1
- package/dist/main.js +7 -7
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
- package/dist/core/async/cli.js +0 -557
- package/dist/core/async/cli.js.map +0 -1
- package/dist/core/async/cron.js +0 -370
- package/dist/core/async/cron.js.map +0 -1
- package/dist/core/async/daemon_runtime.js +0 -120
- package/dist/core/async/daemon_runtime.js.map +0 -1
- package/dist/core/async/http_protocol.js +0 -177
- package/dist/core/async/http_protocol.js.map +0 -1
- package/dist/core/async/http_server.js +0 -278
- package/dist/core/async/http_server.js.map +0 -1
- package/dist/core/async/index.js +0 -10
- package/dist/core/async/index.js.map +0 -1
- package/dist/core/async/server_config.js +0 -420
- package/dist/core/async/server_config.js.map +0 -1
- package/dist/core/async/session_manager.js.map +0 -1
- package/dist/core/async/telegram.js.map +0 -1
- package/dist/core/async/workspace.js.map +0 -1
- /package/dist/core/{async → telegram}/workspace.js +0 -0
|
@@ -4,7 +4,7 @@ import { basename, extname, join } from "node:path";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { transcribeAudio } from "../utils/speech_to_text.js";
|
|
6
6
|
import { formatZodError } from "../utils/zod.js";
|
|
7
|
-
import {
|
|
7
|
+
import { createScopedTelegramSessionManager, TelegramSessionManagerError, } from "./session_manager.js";
|
|
8
8
|
const DEFAULT_POLL_INTERVAL_MS = 1000;
|
|
9
9
|
const DEFAULT_REQUEST_TIMEOUT_SECONDS = 30;
|
|
10
10
|
const MAX_COMMAND_PREVIEW_CHARS = 128;
|
|
@@ -17,19 +17,24 @@ const DEFAULT_TELEGRAM_DOCUMENT_MIME_TYPE = "application/octet-stream";
|
|
|
17
17
|
const MESSAGE_QUEUED_REACTION_EMOJI = "👀";
|
|
18
18
|
const MESSAGE_QUEUED_REACTION_DELAY_MS = 1000;
|
|
19
19
|
const TELEGRAM_MAX_MESSAGE_BYTES = 4096;
|
|
20
|
+
const TELEGRAM_MAX_RICH_MESSAGE_BYTES = 32 * 1024;
|
|
20
21
|
const TELEGRAM_MESSAGE_BYTE_BUFFER_RATIO = 0.95;
|
|
21
22
|
const TELEGRAM_SAFE_MESSAGE_BYTES = Math.floor(TELEGRAM_MAX_MESSAGE_BYTES * TELEGRAM_MESSAGE_BYTE_BUFFER_RATIO);
|
|
23
|
+
const TELEGRAM_RICH_MESSAGE_SAFE_BYTES = Math.floor(TELEGRAM_MAX_RICH_MESSAGE_BYTES * TELEGRAM_MESSAGE_BYTE_BUFFER_RATIO);
|
|
22
24
|
const TELEGRAM_MESSAGE_SPLIT_DELAY_MS = 1000;
|
|
25
|
+
const TELEGRAM_DRAFT_MAX_CHARS = 4096;
|
|
26
|
+
const TELEGRAM_DRAFT_THINKING_MARKDOWN = "<tg-thinking>Thinking...</tg-thinking>";
|
|
27
|
+
const TELEGRAM_DRAFT_PREAMBLE_VISIBLE_MS = 10_000;
|
|
28
|
+
const TELEGRAM_DRAFT_REFRESH_MS = 20_000;
|
|
29
|
+
const TELEGRAM_TYPING_REFRESH_MS = 4000;
|
|
23
30
|
const ABORTED = Symbol("aborted");
|
|
24
31
|
const CALLBACK_ACTION_PREFIX = "tau:action:";
|
|
25
|
-
const CALLBACK_USE_PREFIX = "tau:use:";
|
|
26
|
-
const MAX_SESSION_PREVIEW_CHARS = 64;
|
|
27
32
|
const MAX_TELEGRAM_ATTACHMENTS_PER_TURN = 10;
|
|
28
33
|
const MAX_TELEGRAM_ATTACHMENT_FILE_BYTES = 20 * 1024 * 1024;
|
|
29
34
|
const MAX_TELEGRAM_ATTACHMENT_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
30
35
|
const MAX_TELEGRAM_GROUP_PENDING_MESSAGES = 50;
|
|
31
36
|
const TELEGRAM_ATTACHMENT_TEMP_DIR_PREFIX = "tau-telegram-attachments-";
|
|
32
|
-
const NO_ACTIVE_SESSION_MESSAGE = "no active session. use /new
|
|
37
|
+
const NO_ACTIVE_SESSION_MESSAGE = "no active session. use /new";
|
|
33
38
|
const SUPPORTED_TEXT_ATTACHMENT_EXTENSIONS = new Set([
|
|
34
39
|
".txt",
|
|
35
40
|
".md",
|
|
@@ -98,11 +103,6 @@ async function sweepStaleTelegramAttachmentTempDirs() {
|
|
|
98
103
|
return;
|
|
99
104
|
}
|
|
100
105
|
}
|
|
101
|
-
const QUICK_ACTION_ROWS = [
|
|
102
|
-
["new", "sessions", "status"],
|
|
103
|
-
["interrupt", "close"],
|
|
104
|
-
["quiet", "verbose"],
|
|
105
|
-
];
|
|
106
106
|
function splitCommandText(text) {
|
|
107
107
|
return text
|
|
108
108
|
.trim()
|
|
@@ -196,15 +196,15 @@ function truncateText(text, maxChars) {
|
|
|
196
196
|
}
|
|
197
197
|
return `${trimmed.slice(0, maxChars - 1)}…`;
|
|
198
198
|
}
|
|
199
|
-
function
|
|
200
|
-
if (Buffer.byteLength(text, "utf8") <=
|
|
199
|
+
function splitTelegramTextByBytes(text, maxBytes) {
|
|
200
|
+
if (Buffer.byteLength(text, "utf8") <= maxBytes) {
|
|
201
201
|
return [text];
|
|
202
202
|
}
|
|
203
203
|
const chunks = [];
|
|
204
204
|
let currentChunk = "";
|
|
205
205
|
for (const character of text) {
|
|
206
206
|
const nextChunk = `${currentChunk}${character}`;
|
|
207
|
-
if (Buffer.byteLength(nextChunk, "utf8") <=
|
|
207
|
+
if (Buffer.byteLength(nextChunk, "utf8") <= maxBytes) {
|
|
208
208
|
currentChunk = nextChunk;
|
|
209
209
|
continue;
|
|
210
210
|
}
|
|
@@ -220,6 +220,12 @@ function splitTelegramMessage(text) {
|
|
|
220
220
|
}
|
|
221
221
|
return chunks;
|
|
222
222
|
}
|
|
223
|
+
function splitTelegramMessage(text) {
|
|
224
|
+
return splitTelegramTextByBytes(text, TELEGRAM_SAFE_MESSAGE_BYTES);
|
|
225
|
+
}
|
|
226
|
+
function splitTelegramRichMessage(text) {
|
|
227
|
+
return splitTelegramTextByBytes(text, TELEGRAM_RICH_MESSAGE_SAFE_BYTES);
|
|
228
|
+
}
|
|
223
229
|
function normalizeSizeBytes(value) {
|
|
224
230
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
225
231
|
return undefined;
|
|
@@ -478,6 +484,30 @@ function createTelegramApi(botToken) {
|
|
|
478
484
|
...(options?.replyMarkup ? { reply_markup: options.replyMarkup } : {}),
|
|
479
485
|
}, z.unknown());
|
|
480
486
|
},
|
|
487
|
+
async sendRichMessage(chatId, markdown, options) {
|
|
488
|
+
await callTelegramMethod("sendRichMessage", {
|
|
489
|
+
chat_id: chatId,
|
|
490
|
+
rich_message: {
|
|
491
|
+
markdown,
|
|
492
|
+
},
|
|
493
|
+
...(options?.replyMarkup ? { reply_markup: options.replyMarkup } : {}),
|
|
494
|
+
}, z.unknown());
|
|
495
|
+
},
|
|
496
|
+
async sendRichMessageDraft(chatId, draftId, markdown) {
|
|
497
|
+
await callTelegramMethod("sendRichMessageDraft", {
|
|
498
|
+
chat_id: chatId,
|
|
499
|
+
draft_id: draftId,
|
|
500
|
+
rich_message: {
|
|
501
|
+
markdown,
|
|
502
|
+
},
|
|
503
|
+
}, TelegramAckResultSchema);
|
|
504
|
+
},
|
|
505
|
+
async sendChatAction(chatId, action) {
|
|
506
|
+
await callTelegramMethod("sendChatAction", {
|
|
507
|
+
chat_id: chatId,
|
|
508
|
+
action,
|
|
509
|
+
}, TelegramAckResultSchema);
|
|
510
|
+
},
|
|
481
511
|
async downloadFile(fileId) {
|
|
482
512
|
const parsed = await callTelegramMethod("getFile", {
|
|
483
513
|
file_id: fileId,
|
|
@@ -514,7 +544,7 @@ function createTelegramApi(botToken) {
|
|
|
514
544
|
},
|
|
515
545
|
};
|
|
516
546
|
}
|
|
517
|
-
class
|
|
547
|
+
class TelegramAdapterImpl {
|
|
518
548
|
projects;
|
|
519
549
|
defaultProjectId;
|
|
520
550
|
systemMessage;
|
|
@@ -540,7 +570,9 @@ class AsyncTelegramAdapterImpl {
|
|
|
540
570
|
activeSessionsByChat = new Map();
|
|
541
571
|
sessionsByChat = new Map();
|
|
542
572
|
chatsBySession = new Map();
|
|
543
|
-
|
|
573
|
+
chatTypesByChat = new Map();
|
|
574
|
+
typingIntervalsBySessionChat = new Map();
|
|
575
|
+
draftStatesBySessionChat = new Map();
|
|
544
576
|
lastCommandBySession = new Map();
|
|
545
577
|
lastAssistantMessageBySession = new Map();
|
|
546
578
|
latestAssistantMessageByRun = new Map();
|
|
@@ -612,6 +644,10 @@ class AsyncTelegramAdapterImpl {
|
|
|
612
644
|
try {
|
|
613
645
|
await this.loopPromise;
|
|
614
646
|
await this.waitForInFlightUpdateTasks();
|
|
647
|
+
for (const sessionId of Array.from(this.chatsBySession.keys())) {
|
|
648
|
+
this.stopTypingIndicators(sessionId);
|
|
649
|
+
this.clearSessionDrafts(sessionId);
|
|
650
|
+
}
|
|
615
651
|
for (const sessionId of Array.from(this.attachmentTempDirsBySession.keys())) {
|
|
616
652
|
await this.cleanupSessionAttachmentTempDirs(sessionId);
|
|
617
653
|
}
|
|
@@ -627,38 +663,13 @@ class AsyncTelegramAdapterImpl {
|
|
|
627
663
|
}
|
|
628
664
|
createCommandDefinitions() {
|
|
629
665
|
return [
|
|
630
|
-
{
|
|
631
|
-
command: "/help",
|
|
632
|
-
description: "show supported commands",
|
|
633
|
-
usage: "/help",
|
|
634
|
-
handler: async (chatId) => this.handleHelp(chatId),
|
|
635
|
-
},
|
|
636
666
|
{
|
|
637
667
|
command: "/new",
|
|
638
668
|
description: "start a new session",
|
|
639
|
-
usage: "/new
|
|
669
|
+
usage: "/new",
|
|
640
670
|
callbackAction: "new",
|
|
641
671
|
handler: async (chatId, args, sourceMessageId) => this.handleNew(chatId, args, sourceMessageId),
|
|
642
672
|
},
|
|
643
|
-
{
|
|
644
|
-
command: "/projects",
|
|
645
|
-
description: "list configured projects",
|
|
646
|
-
usage: "/projects",
|
|
647
|
-
handler: async (chatId) => this.handleProjects(chatId),
|
|
648
|
-
},
|
|
649
|
-
{
|
|
650
|
-
command: "/use",
|
|
651
|
-
description: "switch active session",
|
|
652
|
-
usage: "/use <sessionId|prefix|index>",
|
|
653
|
-
handler: async (chatId, args) => this.handleUse(chatId, args),
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
command: "/sessions",
|
|
657
|
-
description: "list sessions",
|
|
658
|
-
usage: "/sessions",
|
|
659
|
-
callbackAction: "sessions",
|
|
660
|
-
handler: async (chatId) => this.handleSessions(chatId),
|
|
661
|
-
},
|
|
662
673
|
{
|
|
663
674
|
command: "/status",
|
|
664
675
|
description: "show active session status",
|
|
@@ -673,33 +684,9 @@ class AsyncTelegramAdapterImpl {
|
|
|
673
684
|
callbackAction: "interrupt",
|
|
674
685
|
handler: async (chatId) => this.handleInterrupt(chatId),
|
|
675
686
|
},
|
|
676
|
-
{
|
|
677
|
-
command: "/close",
|
|
678
|
-
description: "close session(s)",
|
|
679
|
-
usage: "/close [<sessionId>|all]",
|
|
680
|
-
callbackAction: "close",
|
|
681
|
-
handler: async (chatId, args) => this.handleClose(chatId, args),
|
|
682
|
-
},
|
|
683
|
-
{
|
|
684
|
-
command: "/verbose",
|
|
685
|
-
description: "stream progress updates",
|
|
686
|
-
usage: "/verbose",
|
|
687
|
-
callbackAction: "verbose",
|
|
688
|
-
handler: async (chatId) => this.handleVerbosityCommand(chatId, "verbose"),
|
|
689
|
-
},
|
|
690
|
-
{
|
|
691
|
-
command: "/quiet",
|
|
692
|
-
description: "only send final assistant message",
|
|
693
|
-
usage: "/quiet",
|
|
694
|
-
callbackAction: "quiet",
|
|
695
|
-
handler: async (chatId) => this.handleVerbosityCommand(chatId, "quiet"),
|
|
696
|
-
},
|
|
697
687
|
];
|
|
698
688
|
}
|
|
699
689
|
async syncCommands() {
|
|
700
|
-
if (!this.api.setCommands) {
|
|
701
|
-
return;
|
|
702
|
-
}
|
|
703
690
|
const commands = this.commandDefinitions.map((definition) => ({
|
|
704
691
|
command: definition.command.slice(1),
|
|
705
692
|
description: definition.description,
|
|
@@ -837,6 +824,7 @@ class AsyncTelegramAdapterImpl {
|
|
|
837
824
|
if (!chat) {
|
|
838
825
|
return;
|
|
839
826
|
}
|
|
827
|
+
this.chatTypesByChat.set(chat.id, chat.type);
|
|
840
828
|
if (chat.type === "private") {
|
|
841
829
|
await this.handlePrivateMessage(chat.id, message);
|
|
842
830
|
return;
|
|
@@ -1277,7 +1265,7 @@ class AsyncTelegramAdapterImpl {
|
|
|
1277
1265
|
if (!this.enforceChatOwnership) {
|
|
1278
1266
|
return this.sessionManager;
|
|
1279
1267
|
}
|
|
1280
|
-
return
|
|
1268
|
+
return createScopedTelegramSessionManager({
|
|
1281
1269
|
sessionManager: this.sessionManager,
|
|
1282
1270
|
ownerId: this.ownerIdForChat(chatId),
|
|
1283
1271
|
allowedProjectIds: this.allowedProjectIds,
|
|
@@ -1289,20 +1277,12 @@ class AsyncTelegramAdapterImpl {
|
|
|
1289
1277
|
const args = parts.slice(1);
|
|
1290
1278
|
const handler = this.commandHandlers.get(command);
|
|
1291
1279
|
if (!handler) {
|
|
1292
|
-
await this.reply(chatId, "unsupported command.
|
|
1280
|
+
await this.reply(chatId, "unsupported command. supported commands: /new, /status, /interrupt");
|
|
1293
1281
|
return;
|
|
1294
1282
|
}
|
|
1295
1283
|
await handler(chatId, args, sourceMessageId);
|
|
1296
1284
|
}
|
|
1297
1285
|
async handleCallback(chatId, callbackData) {
|
|
1298
|
-
if (callbackData.startsWith(CALLBACK_USE_PREFIX)) {
|
|
1299
|
-
const sessionId = callbackData.slice(CALLBACK_USE_PREFIX.length).trim();
|
|
1300
|
-
if (!sessionId) {
|
|
1301
|
-
return false;
|
|
1302
|
-
}
|
|
1303
|
-
await this.handleUse(chatId, [sessionId]);
|
|
1304
|
-
return true;
|
|
1305
|
-
}
|
|
1306
1286
|
if (!callbackData.startsWith(CALLBACK_ACTION_PREFIX)) {
|
|
1307
1287
|
return false;
|
|
1308
1288
|
}
|
|
@@ -1314,77 +1294,29 @@ class AsyncTelegramAdapterImpl {
|
|
|
1314
1294
|
await handler(chatId, []);
|
|
1315
1295
|
return true;
|
|
1316
1296
|
}
|
|
1317
|
-
async handleHelp(chatId) {
|
|
1318
|
-
const lines = [
|
|
1319
|
-
"commands:",
|
|
1320
|
-
...this.commandDefinitions.map((definition) => definition.usage),
|
|
1321
|
-
"",
|
|
1322
|
-
"tip: use /sessions and tap a session button to switch quickly",
|
|
1323
|
-
];
|
|
1324
|
-
await this.reply(chatId, lines.join("\n"), {
|
|
1325
|
-
replyMarkup: this.buildQuickActionsKeyboard(),
|
|
1326
|
-
});
|
|
1327
|
-
}
|
|
1328
1297
|
resolveNewCommand(args) {
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
if (this.defaultProjectId) {
|
|
1332
|
-
if (!this.projects[this.defaultProjectId]) {
|
|
1333
|
-
return {
|
|
1334
|
-
error: `telegram.<botId>.defaultProjectId '${this.defaultProjectId}' is not configured`,
|
|
1335
|
-
};
|
|
1336
|
-
}
|
|
1337
|
-
return { projectId: this.defaultProjectId };
|
|
1338
|
-
}
|
|
1339
|
-
if (projectIds.length === 1) {
|
|
1340
|
-
return { projectId: projectIds[0] };
|
|
1341
|
-
}
|
|
1342
|
-
if (projectIds.length === 0) {
|
|
1343
|
-
return { error: "no async projects configured" };
|
|
1344
|
-
}
|
|
1345
|
-
return {
|
|
1346
|
-
error: "missing project id. usage: /new [projectId]. use /projects to list options",
|
|
1347
|
-
};
|
|
1348
|
-
};
|
|
1349
|
-
if (args.length > 1) {
|
|
1350
|
-
return { error: "usage: /new [projectId]" };
|
|
1298
|
+
if (args.length > 0) {
|
|
1299
|
+
return { error: "usage: /new" };
|
|
1351
1300
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
if (!
|
|
1355
|
-
return { error: "usage: /new [projectId]" };
|
|
1356
|
-
}
|
|
1357
|
-
if (!this.projects[projectId]) {
|
|
1301
|
+
const projectIds = Object.keys(this.projects);
|
|
1302
|
+
if (this.defaultProjectId) {
|
|
1303
|
+
if (!this.projects[this.defaultProjectId]) {
|
|
1358
1304
|
return {
|
|
1359
|
-
error: `
|
|
1305
|
+
error: `telegram.<botId>.defaultProjectId '${this.defaultProjectId}' is not configured`,
|
|
1360
1306
|
};
|
|
1361
1307
|
}
|
|
1362
|
-
return { projectId };
|
|
1308
|
+
return { projectId: this.defaultProjectId };
|
|
1363
1309
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
};
|
|
1310
|
+
if (projectIds.length === 1) {
|
|
1311
|
+
return { projectId: projectIds[0] };
|
|
1312
|
+
}
|
|
1313
|
+
if (projectIds.length === 0) {
|
|
1314
|
+
return { error: "no telegram projects configured" };
|
|
1369
1315
|
}
|
|
1370
1316
|
return {
|
|
1371
|
-
|
|
1317
|
+
error: "multiple projects configured. set defaultProjectId for this bot before using /new",
|
|
1372
1318
|
};
|
|
1373
1319
|
}
|
|
1374
|
-
async handleProjects(chatId) {
|
|
1375
|
-
const entries = Object.entries(this.projects).sort(([left], [right]) => left.localeCompare(right));
|
|
1376
|
-
if (entries.length === 0) {
|
|
1377
|
-
await this.reply(chatId, "no async projects configured");
|
|
1378
|
-
return;
|
|
1379
|
-
}
|
|
1380
|
-
const lines = entries.map(([projectId, project]) => {
|
|
1381
|
-
if (!project.description) {
|
|
1382
|
-
return projectId;
|
|
1383
|
-
}
|
|
1384
|
-
return `${projectId}: ${project.description}`;
|
|
1385
|
-
});
|
|
1386
|
-
await this.reply(chatId, [`projects:`, ...lines].join("\n"));
|
|
1387
|
-
}
|
|
1388
1320
|
async handleNew(chatId, args, sourceMessageId) {
|
|
1389
1321
|
const parsed = this.resolveNewCommand(args);
|
|
1390
1322
|
if ("error" in parsed) {
|
|
@@ -1393,134 +1325,27 @@ class AsyncTelegramAdapterImpl {
|
|
|
1393
1325
|
}
|
|
1394
1326
|
try {
|
|
1395
1327
|
const sessionManager = this.getSessionManagerForChat(chatId);
|
|
1328
|
+
const previousSession = this.getActiveSession(chatId);
|
|
1329
|
+
if (previousSession) {
|
|
1330
|
+
await sessionManager.closeSession(previousSession.id);
|
|
1331
|
+
this.clearClosedSession(previousSession.id);
|
|
1332
|
+
}
|
|
1396
1333
|
const session = await sessionManager.createSession({
|
|
1397
1334
|
projectId: parsed.projectId,
|
|
1398
1335
|
});
|
|
1399
1336
|
this.setActiveSession(chatId, session.id);
|
|
1400
|
-
this.sessionVerbosityBySession.set(session.id, "quiet");
|
|
1401
1337
|
void this.reactToQueuedMessage(chatId, sourceMessageId);
|
|
1402
1338
|
}
|
|
1403
1339
|
catch (error) {
|
|
1404
1340
|
await this.reply(chatId, this.formatManagerError(error));
|
|
1405
1341
|
}
|
|
1406
1342
|
}
|
|
1407
|
-
async handleUse(chatId, args) {
|
|
1408
|
-
const selector = args[0]?.trim();
|
|
1409
|
-
if (!selector) {
|
|
1410
|
-
await this.reply(chatId, "usage: /use <sessionId|prefix|index>");
|
|
1411
|
-
return;
|
|
1412
|
-
}
|
|
1413
|
-
const sessionManager = this.getSessionManagerForChat(chatId);
|
|
1414
|
-
const sessions = sessionManager.listSessions();
|
|
1415
|
-
const selectedSession = this.resolveSessionSelector(selector, sessions, sessionManager);
|
|
1416
|
-
if ("error" in selectedSession) {
|
|
1417
|
-
await this.reply(chatId, selectedSession.error);
|
|
1418
|
-
return;
|
|
1419
|
-
}
|
|
1420
|
-
this.setActiveSession(chatId, selectedSession.session.id);
|
|
1421
|
-
await this.reply(chatId, `switched to ${formatTelegramSessionName(selectedSession.session)}.`);
|
|
1422
|
-
}
|
|
1423
|
-
resolveSessionSelector(selector, sessions, sessionManager) {
|
|
1424
|
-
const exactSession = sessionManager.getSession(selector);
|
|
1425
|
-
if (exactSession) {
|
|
1426
|
-
return { session: exactSession };
|
|
1427
|
-
}
|
|
1428
|
-
if (/^\d+$/.test(selector)) {
|
|
1429
|
-
const index = Number.parseInt(selector, 10);
|
|
1430
|
-
if (index <= 0 || index > sessions.length) {
|
|
1431
|
-
return {
|
|
1432
|
-
error: sessions.length === 0
|
|
1433
|
-
? "no sessions"
|
|
1434
|
-
: `session index '${selector}' is out of range (1-${sessions.length})`,
|
|
1435
|
-
};
|
|
1436
|
-
}
|
|
1437
|
-
const indexedSession = sessions[index - 1];
|
|
1438
|
-
if (!indexedSession) {
|
|
1439
|
-
return { error: "session index lookup failed" };
|
|
1440
|
-
}
|
|
1441
|
-
return { session: indexedSession };
|
|
1442
|
-
}
|
|
1443
|
-
const prefixMatches = sessions.filter((session) => session.id.startsWith(selector));
|
|
1444
|
-
if (prefixMatches.length === 1) {
|
|
1445
|
-
const [prefixMatch] = prefixMatches;
|
|
1446
|
-
if (!prefixMatch) {
|
|
1447
|
-
return { error: "session prefix lookup failed" };
|
|
1448
|
-
}
|
|
1449
|
-
return { session: prefixMatch };
|
|
1450
|
-
}
|
|
1451
|
-
if (prefixMatches.length > 1) {
|
|
1452
|
-
return {
|
|
1453
|
-
error: `session prefix '${selector}' is ambiguous: ${prefixMatches.map((session) => session.id).join(", ")}`,
|
|
1454
|
-
};
|
|
1455
|
-
}
|
|
1456
|
-
return { error: `session '${selector}' not found` };
|
|
1457
|
-
}
|
|
1458
|
-
async handleSessions(chatId) {
|
|
1459
|
-
const sessionManager = this.getSessionManagerForChat(chatId);
|
|
1460
|
-
const sessions = sessionManager.listSessions();
|
|
1461
|
-
const lines = this.formatSessions(chatId, sessions);
|
|
1462
|
-
await this.reply(chatId, lines.join("\n"), {
|
|
1463
|
-
replyMarkup: this.buildSessionsKeyboard(chatId, sessions),
|
|
1464
|
-
});
|
|
1465
|
-
}
|
|
1466
|
-
formatSessions(chatId, sessions) {
|
|
1467
|
-
if (sessions.length === 0) {
|
|
1468
|
-
return ["no sessions"];
|
|
1469
|
-
}
|
|
1470
|
-
const activeSessionId = this.activeSessionsByChat.get(chatId);
|
|
1471
|
-
return [
|
|
1472
|
-
"sessions:",
|
|
1473
|
-
...sessions.map((session, index) => {
|
|
1474
|
-
const marker = session.id === activeSessionId ? "*" : "-";
|
|
1475
|
-
const preview = this.formatSessionPreview(session.id);
|
|
1476
|
-
return `${index + 1}. ${marker} ${session.id} ${session.projectId} ${session.state}${preview ? ` · ${preview}` : ""}`;
|
|
1477
|
-
}),
|
|
1478
|
-
];
|
|
1479
|
-
}
|
|
1480
|
-
formatSessionPreview(sessionId) {
|
|
1481
|
-
const previews = [];
|
|
1482
|
-
const lastCommand = this.lastCommandBySession.get(sessionId);
|
|
1483
|
-
if (lastCommand) {
|
|
1484
|
-
previews.push(`$ ${truncateText(lastCommand, MAX_SESSION_PREVIEW_CHARS)}`);
|
|
1485
|
-
}
|
|
1486
|
-
const lastAssistantMessage = this.lastAssistantMessageBySession.get(sessionId);
|
|
1487
|
-
if (lastAssistantMessage) {
|
|
1488
|
-
previews.push(truncateText(lastAssistantMessage, MAX_SESSION_PREVIEW_CHARS));
|
|
1489
|
-
}
|
|
1490
|
-
if (previews.length === 0) {
|
|
1491
|
-
return undefined;
|
|
1492
|
-
}
|
|
1493
|
-
return previews.join(" | ");
|
|
1494
|
-
}
|
|
1495
|
-
buildSessionsKeyboard(chatId, sessions) {
|
|
1496
|
-
const activeSessionId = this.activeSessionsByChat.get(chatId);
|
|
1497
|
-
const inlineKeyboard = sessions.map((session, index) => [
|
|
1498
|
-
{
|
|
1499
|
-
text: `${index + 1}. ${session.id}${session.id === activeSessionId ? " *" : ""}`,
|
|
1500
|
-
callback_data: `${CALLBACK_USE_PREFIX}${session.id}`,
|
|
1501
|
-
},
|
|
1502
|
-
]);
|
|
1503
|
-
inlineKeyboard.push(...this.buildQuickActionsKeyboard().inline_keyboard);
|
|
1504
|
-
return {
|
|
1505
|
-
inline_keyboard: inlineKeyboard,
|
|
1506
|
-
};
|
|
1507
|
-
}
|
|
1508
|
-
buildQuickActionsKeyboard() {
|
|
1509
|
-
return {
|
|
1510
|
-
inline_keyboard: QUICK_ACTION_ROWS.map((row) => row.map((action) => ({
|
|
1511
|
-
text: `/${action}`,
|
|
1512
|
-
callback_data: `${CALLBACK_ACTION_PREFIX}${action}`,
|
|
1513
|
-
}))),
|
|
1514
|
-
};
|
|
1515
|
-
}
|
|
1516
1343
|
async handleStatus(chatId) {
|
|
1517
1344
|
const session = await this.requireActiveSession(chatId);
|
|
1518
1345
|
if (!session) {
|
|
1519
1346
|
return;
|
|
1520
1347
|
}
|
|
1521
|
-
await this.reply(chatId, formatSessionStatus(session)
|
|
1522
|
-
replyMarkup: this.buildQuickActionsKeyboard(),
|
|
1523
|
-
});
|
|
1348
|
+
await this.reply(chatId, formatSessionStatus(session));
|
|
1524
1349
|
}
|
|
1525
1350
|
async handleInterrupt(chatId) {
|
|
1526
1351
|
const session = await this.requireActiveSession(chatId);
|
|
@@ -1540,57 +1365,6 @@ class AsyncTelegramAdapterImpl {
|
|
|
1540
1365
|
await this.reply(chatId, this.formatManagerError(error));
|
|
1541
1366
|
}
|
|
1542
1367
|
}
|
|
1543
|
-
async handleClose(chatId, args) {
|
|
1544
|
-
if (args.length > 1) {
|
|
1545
|
-
await this.reply(chatId, "usage: /close [<sessionId>|all]");
|
|
1546
|
-
return;
|
|
1547
|
-
}
|
|
1548
|
-
const target = args[0]?.trim();
|
|
1549
|
-
if (target === "all") {
|
|
1550
|
-
try {
|
|
1551
|
-
const sessionManager = this.getSessionManagerForChat(chatId);
|
|
1552
|
-
const closed = await sessionManager.closeInactiveSessions();
|
|
1553
|
-
for (const session of closed) {
|
|
1554
|
-
this.clearClosedSession(session.id);
|
|
1555
|
-
}
|
|
1556
|
-
if (closed.length === 0) {
|
|
1557
|
-
await this.reply(chatId, "there are no idle sessions to close.");
|
|
1558
|
-
return;
|
|
1559
|
-
}
|
|
1560
|
-
const label = closed.length === 1 ? "session" : "sessions";
|
|
1561
|
-
await this.reply(chatId, `closed ${closed.length} idle ${label}.`);
|
|
1562
|
-
}
|
|
1563
|
-
catch (error) {
|
|
1564
|
-
await this.reply(chatId, this.formatManagerError(error));
|
|
1565
|
-
}
|
|
1566
|
-
return;
|
|
1567
|
-
}
|
|
1568
|
-
const sessionId = target ?? this.getActiveSession(chatId)?.id;
|
|
1569
|
-
if (!sessionId) {
|
|
1570
|
-
await this.reply(chatId, NO_ACTIVE_SESSION_MESSAGE);
|
|
1571
|
-
return;
|
|
1572
|
-
}
|
|
1573
|
-
try {
|
|
1574
|
-
const sessionManager = this.getSessionManagerForChat(chatId);
|
|
1575
|
-
const closed = await sessionManager.closeSession(sessionId);
|
|
1576
|
-
this.clearClosedSession(closed.id);
|
|
1577
|
-
await this.reply(chatId, `closed ${formatTelegramSessionName(closed)}.`);
|
|
1578
|
-
}
|
|
1579
|
-
catch (error) {
|
|
1580
|
-
await this.reply(chatId, this.formatManagerError(error));
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
async handleVerbosityCommand(chatId, verbosity) {
|
|
1584
|
-
const session = await this.requireActiveSession(chatId);
|
|
1585
|
-
if (!session) {
|
|
1586
|
-
return;
|
|
1587
|
-
}
|
|
1588
|
-
this.sessionVerbosityBySession.set(session.id, verbosity);
|
|
1589
|
-
const message = verbosity === "quiet"
|
|
1590
|
-
? `${formatTelegramSessionName(session)} is now quiet.`
|
|
1591
|
-
: `${formatTelegramSessionName(session)} will now show progress updates.`;
|
|
1592
|
-
await this.reply(chatId, message);
|
|
1593
|
-
}
|
|
1594
1368
|
async handleMessage(chatId, text, sourceMessageId) {
|
|
1595
1369
|
const session = await this.requireActiveOrSingleSession(chatId);
|
|
1596
1370
|
if (!session) {
|
|
@@ -1893,6 +1667,8 @@ class AsyncTelegramAdapterImpl {
|
|
|
1893
1667
|
}
|
|
1894
1668
|
}
|
|
1895
1669
|
clearClosedSession(sessionId) {
|
|
1670
|
+
this.stopTypingIndicators(sessionId);
|
|
1671
|
+
this.clearSessionDrafts(sessionId);
|
|
1896
1672
|
for (const [chatId, activeSessionId] of this.activeSessionsByChat) {
|
|
1897
1673
|
if (activeSessionId === sessionId) {
|
|
1898
1674
|
this.activeSessionsByChat.delete(chatId);
|
|
@@ -1902,17 +1678,13 @@ class AsyncTelegramAdapterImpl {
|
|
|
1902
1678
|
for (const chatId of chatIds) {
|
|
1903
1679
|
this.unlinkChatFromSession(chatId, sessionId);
|
|
1904
1680
|
}
|
|
1905
|
-
this.sessionVerbosityBySession.delete(sessionId);
|
|
1906
1681
|
this.lastCommandBySession.delete(sessionId);
|
|
1907
1682
|
this.lastAssistantMessageBySession.delete(sessionId);
|
|
1908
1683
|
this.latestAssistantMessageByRun.delete(sessionId);
|
|
1909
1684
|
this.clearSessionAttachments(sessionId);
|
|
1910
1685
|
}
|
|
1911
|
-
|
|
1912
|
-
return
|
|
1913
|
-
}
|
|
1914
|
-
isVerboseSession(sessionId) {
|
|
1915
|
-
return this.getSessionVerbosity(sessionId) === "verbose";
|
|
1686
|
+
isVerboseSession(_sessionId) {
|
|
1687
|
+
return false;
|
|
1916
1688
|
}
|
|
1917
1689
|
onSessionEvent(event) {
|
|
1918
1690
|
if (event.type === "session-progress") {
|
|
@@ -1930,6 +1702,8 @@ class AsyncTelegramAdapterImpl {
|
|
|
1930
1702
|
}
|
|
1931
1703
|
if (event.state === "running") {
|
|
1932
1704
|
this.latestAssistantMessageByRun.delete(event.sessionId);
|
|
1705
|
+
this.startTypingIndicators(event.sessionId);
|
|
1706
|
+
this.showDraftThinking(event.sessionId);
|
|
1933
1707
|
if (this.isVerboseSession(event.sessionId)) {
|
|
1934
1708
|
this.notifyLifecycle(event.sessionId, event.projectId, "started");
|
|
1935
1709
|
}
|
|
@@ -1937,6 +1711,8 @@ class AsyncTelegramAdapterImpl {
|
|
|
1937
1711
|
}
|
|
1938
1712
|
if (event.state === "failed") {
|
|
1939
1713
|
this.latestAssistantMessageByRun.delete(event.sessionId);
|
|
1714
|
+
this.stopTypingIndicators(event.sessionId);
|
|
1715
|
+
this.clearSessionDrafts(event.sessionId);
|
|
1940
1716
|
this.notifyLifecycle(event.sessionId, event.projectId, "failed");
|
|
1941
1717
|
return;
|
|
1942
1718
|
}
|
|
@@ -1945,6 +1721,8 @@ class AsyncTelegramAdapterImpl {
|
|
|
1945
1721
|
return;
|
|
1946
1722
|
}
|
|
1947
1723
|
if (event.state === "waiting-input" && event.previousState === "running") {
|
|
1724
|
+
this.stopTypingIndicators(event.sessionId);
|
|
1725
|
+
this.clearSessionDrafts(event.sessionId);
|
|
1948
1726
|
if (this.isVerboseSession(event.sessionId)) {
|
|
1949
1727
|
this.notifyLifecycle(event.sessionId, event.projectId, "finished");
|
|
1950
1728
|
return;
|
|
@@ -1952,7 +1730,7 @@ class AsyncTelegramAdapterImpl {
|
|
|
1952
1730
|
const message = this.latestAssistantMessageByRun.get(event.sessionId);
|
|
1953
1731
|
this.latestAssistantMessageByRun.delete(event.sessionId);
|
|
1954
1732
|
if (message) {
|
|
1955
|
-
this.notifySession(event.sessionId, message);
|
|
1733
|
+
this.notifySession(event.sessionId, message, { rich: true });
|
|
1956
1734
|
}
|
|
1957
1735
|
}
|
|
1958
1736
|
}
|
|
@@ -1985,6 +1763,7 @@ class AsyncTelegramAdapterImpl {
|
|
|
1985
1763
|
}
|
|
1986
1764
|
this.lastAssistantMessageBySession.set(event.sessionId, event.progress.text);
|
|
1987
1765
|
this.latestAssistantMessageByRun.set(event.sessionId, event.progress.text);
|
|
1766
|
+
this.showDraftPreamble(event.sessionId, event.progress.text);
|
|
1988
1767
|
if (!isVerbose) {
|
|
1989
1768
|
return;
|
|
1990
1769
|
}
|
|
@@ -2002,19 +1781,161 @@ class AsyncTelegramAdapterImpl {
|
|
|
2002
1781
|
}
|
|
2003
1782
|
this.notifySession(sessionId, [formatSessionHeadline(sessionId, stateLabel), `project: ${projectId}`].join("\n"));
|
|
2004
1783
|
}
|
|
2005
|
-
notifySession(sessionId, text) {
|
|
1784
|
+
notifySession(sessionId, text, options = {}) {
|
|
2006
1785
|
const chatIds = this.chatsBySession.get(sessionId);
|
|
2007
1786
|
if (!chatIds || chatIds.size === 0) {
|
|
2008
1787
|
return;
|
|
2009
1788
|
}
|
|
2010
1789
|
for (const chatId of chatIds) {
|
|
2011
|
-
void this.reply(chatId, text)
|
|
1790
|
+
void this.reply(chatId, text, { rich: options.rich }).catch((error) => {
|
|
1791
|
+
this.log("warn", "failed to send telegram notification", {
|
|
1792
|
+
chatId,
|
|
1793
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
1794
|
+
});
|
|
1795
|
+
});
|
|
2012
1796
|
}
|
|
2013
1797
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
1798
|
+
startTypingIndicators(sessionId) {
|
|
1799
|
+
const chatIds = this.chatsBySession.get(sessionId);
|
|
1800
|
+
if (!chatIds) {
|
|
2016
1801
|
return;
|
|
2017
1802
|
}
|
|
1803
|
+
for (const chatId of chatIds) {
|
|
1804
|
+
const key = this.getSessionChatKey(sessionId, chatId);
|
|
1805
|
+
if (this.typingIntervalsBySessionChat.has(key)) {
|
|
1806
|
+
continue;
|
|
1807
|
+
}
|
|
1808
|
+
const sendTyping = () => {
|
|
1809
|
+
void this.api.sendChatAction(chatId, "typing").catch((error) => {
|
|
1810
|
+
this.log("warn", "failed to send telegram typing action", {
|
|
1811
|
+
chatId,
|
|
1812
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
1813
|
+
});
|
|
1814
|
+
});
|
|
1815
|
+
};
|
|
1816
|
+
sendTyping();
|
|
1817
|
+
const interval = setInterval(sendTyping, TELEGRAM_TYPING_REFRESH_MS);
|
|
1818
|
+
interval.unref?.();
|
|
1819
|
+
this.typingIntervalsBySessionChat.set(key, interval);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
stopTypingIndicators(sessionId) {
|
|
1823
|
+
const chatIds = this.chatsBySession.get(sessionId);
|
|
1824
|
+
if (!chatIds) {
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
for (const chatId of chatIds) {
|
|
1828
|
+
const key = this.getSessionChatKey(sessionId, chatId);
|
|
1829
|
+
const interval = this.typingIntervalsBySessionChat.get(key);
|
|
1830
|
+
if (interval) {
|
|
1831
|
+
clearInterval(interval);
|
|
1832
|
+
this.typingIntervalsBySessionChat.delete(key);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
showDraftThinking(sessionId) {
|
|
1837
|
+
const chatIds = this.chatsBySession.get(sessionId);
|
|
1838
|
+
if (!chatIds) {
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
for (const chatId of chatIds) {
|
|
1842
|
+
if (this.chatTypesByChat.get(chatId) !== "private") {
|
|
1843
|
+
continue;
|
|
1844
|
+
}
|
|
1845
|
+
void this.updateMessageDraft(sessionId, chatId, TELEGRAM_DRAFT_THINKING_MARKDOWN);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
showDraftPreamble(sessionId, text) {
|
|
1849
|
+
const chatIds = this.chatsBySession.get(sessionId);
|
|
1850
|
+
if (!chatIds) {
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
for (const chatId of chatIds) {
|
|
1854
|
+
if (this.chatTypesByChat.get(chatId) !== "private") {
|
|
1855
|
+
continue;
|
|
1856
|
+
}
|
|
1857
|
+
const key = this.getSessionChatKey(sessionId, chatId);
|
|
1858
|
+
const state = this.getDraftState(key);
|
|
1859
|
+
if (state.preambleTimeout) {
|
|
1860
|
+
clearTimeout(state.preambleTimeout);
|
|
1861
|
+
}
|
|
1862
|
+
void this.updateMessageDraft(sessionId, chatId, truncateText(text, TELEGRAM_DRAFT_MAX_CHARS));
|
|
1863
|
+
const timeout = setTimeout(() => {
|
|
1864
|
+
const currentState = this.draftStatesBySessionChat.get(key);
|
|
1865
|
+
if (currentState === state) {
|
|
1866
|
+
void this.updateMessageDraft(sessionId, chatId, TELEGRAM_DRAFT_THINKING_MARKDOWN);
|
|
1867
|
+
}
|
|
1868
|
+
}, TELEGRAM_DRAFT_PREAMBLE_VISIBLE_MS);
|
|
1869
|
+
timeout.unref?.();
|
|
1870
|
+
state.preambleTimeout = timeout;
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
async updateMessageDraft(sessionId, chatId, markdown) {
|
|
1874
|
+
const key = this.getSessionChatKey(sessionId, chatId);
|
|
1875
|
+
const state = this.getDraftState(key);
|
|
1876
|
+
state.markdown = markdown;
|
|
1877
|
+
this.startMessageDraftRefresh(sessionId, chatId, state);
|
|
1878
|
+
await this.sendMessageDraft(chatId, state);
|
|
1879
|
+
}
|
|
1880
|
+
startMessageDraftRefresh(sessionId, chatId, state) {
|
|
1881
|
+
if (state.refreshInterval) {
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1884
|
+
const interval = setInterval(() => {
|
|
1885
|
+
const currentState = this.draftStatesBySessionChat.get(this.getSessionChatKey(sessionId, chatId));
|
|
1886
|
+
if (currentState !== state) {
|
|
1887
|
+
clearInterval(interval);
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1890
|
+
void this.sendMessageDraft(chatId, currentState);
|
|
1891
|
+
}, TELEGRAM_DRAFT_REFRESH_MS);
|
|
1892
|
+
interval.unref?.();
|
|
1893
|
+
state.refreshInterval = interval;
|
|
1894
|
+
}
|
|
1895
|
+
async sendMessageDraft(chatId, state) {
|
|
1896
|
+
try {
|
|
1897
|
+
await this.api.sendRichMessageDraft(chatId, state.draftId, state.markdown);
|
|
1898
|
+
}
|
|
1899
|
+
catch (error) {
|
|
1900
|
+
this.log("warn", "failed to update telegram message draft", {
|
|
1901
|
+
chatId,
|
|
1902
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
1903
|
+
});
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
clearSessionDrafts(sessionId) {
|
|
1907
|
+
const chatIds = this.chatsBySession.get(sessionId);
|
|
1908
|
+
if (!chatIds) {
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
for (const chatId of chatIds) {
|
|
1912
|
+
const key = this.getSessionChatKey(sessionId, chatId);
|
|
1913
|
+
const state = this.draftStatesBySessionChat.get(key);
|
|
1914
|
+
if (state?.preambleTimeout) {
|
|
1915
|
+
clearTimeout(state.preambleTimeout);
|
|
1916
|
+
}
|
|
1917
|
+
if (state?.refreshInterval) {
|
|
1918
|
+
clearInterval(state.refreshInterval);
|
|
1919
|
+
}
|
|
1920
|
+
this.draftStatesBySessionChat.delete(key);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
getDraftState(key) {
|
|
1924
|
+
const existing = this.draftStatesBySessionChat.get(key);
|
|
1925
|
+
if (existing) {
|
|
1926
|
+
return existing;
|
|
1927
|
+
}
|
|
1928
|
+
const state = { draftId: this.createDraftId(), markdown: TELEGRAM_DRAFT_THINKING_MARKDOWN };
|
|
1929
|
+
this.draftStatesBySessionChat.set(key, state);
|
|
1930
|
+
return state;
|
|
1931
|
+
}
|
|
1932
|
+
createDraftId() {
|
|
1933
|
+
return Math.floor(Math.random() * 2_000_000_000) + 1;
|
|
1934
|
+
}
|
|
1935
|
+
getSessionChatKey(sessionId, chatId) {
|
|
1936
|
+
return `${sessionId}:${chatId}`;
|
|
1937
|
+
}
|
|
1938
|
+
async reactToQueuedMessage(chatId, messageId) {
|
|
2018
1939
|
if (typeof messageId !== "number" || !Number.isInteger(messageId) || messageId <= 0) {
|
|
2019
1940
|
return;
|
|
2020
1941
|
}
|
|
@@ -2034,9 +1955,6 @@ class AsyncTelegramAdapterImpl {
|
|
|
2034
1955
|
}
|
|
2035
1956
|
}
|
|
2036
1957
|
async answerCallbackQuery(callbackQueryId, text) {
|
|
2037
|
-
if (!this.api.answerCallbackQuery) {
|
|
2038
|
-
return;
|
|
2039
|
-
}
|
|
2040
1958
|
const trimmedCallbackQueryId = callbackQueryId?.trim();
|
|
2041
1959
|
if (!trimmedCallbackQueryId) {
|
|
2042
1960
|
return;
|
|
@@ -2058,26 +1976,35 @@ class AsyncTelegramAdapterImpl {
|
|
|
2058
1976
|
return formatSessionHeadline(sessionId, "message queued");
|
|
2059
1977
|
}
|
|
2060
1978
|
formatManagerError(error) {
|
|
2061
|
-
if (error instanceof
|
|
1979
|
+
if (error instanceof TelegramSessionManagerError) {
|
|
2062
1980
|
return error.message;
|
|
2063
1981
|
}
|
|
2064
1982
|
return error instanceof Error ? error.message : String(error);
|
|
2065
1983
|
}
|
|
2066
1984
|
async reply(chatId, text, options) {
|
|
2067
|
-
|
|
1985
|
+
if (options?.rich === true) {
|
|
1986
|
+
await this.replyWithRichMessage(chatId, text, options.replyMarkup);
|
|
1987
|
+
return;
|
|
1988
|
+
}
|
|
1989
|
+
await this.replyWithPlainMessage(chatId, text, options?.replyMarkup);
|
|
1990
|
+
}
|
|
1991
|
+
async replyWithRichMessage(chatId, text, replyMarkup) {
|
|
1992
|
+
const chunks = splitTelegramRichMessage(text);
|
|
2068
1993
|
for (const [index, chunk] of chunks.entries()) {
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
catch (error) {
|
|
2075
|
-
this.log("warn", "failed to send telegram message", {
|
|
2076
|
-
chatId,
|
|
2077
|
-
cause: error instanceof Error ? error.message : String(error),
|
|
2078
|
-
});
|
|
2079
|
-
return;
|
|
1994
|
+
await this.api.sendRichMessage(chatId, chunk, {
|
|
1995
|
+
replyMarkup: index === chunks.length - 1 ? replyMarkup : undefined,
|
|
1996
|
+
});
|
|
1997
|
+
if (index < chunks.length - 1) {
|
|
1998
|
+
await this.wait(TELEGRAM_MESSAGE_SPLIT_DELAY_MS);
|
|
2080
1999
|
}
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
async replyWithPlainMessage(chatId, text, replyMarkup) {
|
|
2003
|
+
const chunks = splitTelegramMessage(text);
|
|
2004
|
+
for (const [index, chunk] of chunks.entries()) {
|
|
2005
|
+
await this.api.sendMessage(chatId, chunk, {
|
|
2006
|
+
replyMarkup: index === chunks.length - 1 ? replyMarkup : undefined,
|
|
2007
|
+
});
|
|
2081
2008
|
if (index < chunks.length - 1) {
|
|
2082
2009
|
await this.wait(TELEGRAM_MESSAGE_SPLIT_DELAY_MS);
|
|
2083
2010
|
}
|
|
@@ -2101,14 +2028,14 @@ class AsyncTelegramAdapterImpl {
|
|
|
2101
2028
|
});
|
|
2102
2029
|
}
|
|
2103
2030
|
}
|
|
2104
|
-
export async function
|
|
2031
|
+
export async function startTelegramAdapter(options) {
|
|
2105
2032
|
await sweepStaleTelegramAttachmentTempDirs();
|
|
2106
2033
|
const api = options.api ?? createTelegramApi(options.botToken);
|
|
2107
2034
|
const botUsername = normalizeTelegramUsername((await api.getMe()).username);
|
|
2108
2035
|
if (!botUsername) {
|
|
2109
2036
|
throw new Error("telegram bot username is missing");
|
|
2110
2037
|
}
|
|
2111
|
-
const adapter = new
|
|
2038
|
+
const adapter = new TelegramAdapterImpl({
|
|
2112
2039
|
...options,
|
|
2113
2040
|
api,
|
|
2114
2041
|
botUsername,
|
|
@@ -2117,4 +2044,4 @@ export async function startAsyncTelegramAdapter(options) {
|
|
|
2117
2044
|
close: () => adapter.close(),
|
|
2118
2045
|
};
|
|
2119
2046
|
}
|
|
2120
|
-
//# sourceMappingURL=
|
|
2047
|
+
//# sourceMappingURL=adapter.js.map
|