@elizaos/plugin-telegram 2.0.0-alpha.5 → 2.0.0-alpha.537

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/dist/index.js ADDED
@@ -0,0 +1,3157 @@
1
+ import {
2
+ TelegramAccountAuthSession,
3
+ clearTelegramAccountAuthState,
4
+ clearTelegramAccountSession,
5
+ defaultTelegramAccountDeviceModel,
6
+ defaultTelegramAccountSystemVersion,
7
+ loadTelegramAccountSessionString,
8
+ resolveTelegramAccountSessionFile,
9
+ saveTelegramAccountSessionString,
10
+ telegramAccountAuthStateExists,
11
+ telegramAccountSessionExists
12
+ } from "./chunk-ORNHNB7E.js";
13
+
14
+ // src/account-setup-routes.ts
15
+ function getSetupService(runtime) {
16
+ return runtime.getService("connector-setup");
17
+ }
18
+ var telegramAccountAuthSession = null;
19
+ async function stopTelegramAccountAuthSession() {
20
+ if (telegramAccountAuthSession) {
21
+ try {
22
+ await telegramAccountAuthSession.stop();
23
+ } catch {
24
+ }
25
+ telegramAccountAuthSession = null;
26
+ }
27
+ }
28
+ function readConnectorConfig(config) {
29
+ const connectors = config.connectors;
30
+ const raw = connectors?.telegramAccount;
31
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
32
+ return {};
33
+ }
34
+ return raw;
35
+ }
36
+ function hasConfiguredTelegramAccount(connConfig) {
37
+ return Boolean(
38
+ typeof connConfig.phone === "string" && connConfig.phone.trim() && (typeof connConfig.appId === "string" || typeof connConfig.appId === "number") && typeof connConfig.appHash === "string" && connConfig.appHash.trim() && typeof connConfig.deviceModel === "string" && connConfig.deviceModel.trim() && typeof connConfig.systemVersion === "string" && connConfig.systemVersion.trim() && connConfig.enabled !== false
39
+ );
40
+ }
41
+ function resolveConfiguredPhone(runtime, connConfig) {
42
+ if (typeof connConfig.phone === "string" && connConfig.phone.trim().length > 0) {
43
+ return connConfig.phone.trim();
44
+ }
45
+ const setting = runtime.getSetting("TELEGRAM_ACCOUNT_PHONE");
46
+ return typeof setting === "string" && setting.trim().length > 0 ? setting.trim() : null;
47
+ }
48
+ function resolveService(runtime) {
49
+ const service = runtime.getService("telegram-account");
50
+ return service ?? null;
51
+ }
52
+ function isServiceConnected(service) {
53
+ if (!service) {
54
+ return false;
55
+ }
56
+ if (typeof service.isConnected === "function") {
57
+ return service.isConnected();
58
+ }
59
+ const withFlags = service;
60
+ if (typeof withFlags.isServiceConnected === "function") {
61
+ return withFlags.isServiceConnected();
62
+ }
63
+ return withFlags.connected === true;
64
+ }
65
+ function statusFromState(runtime, config) {
66
+ const connectorConfig = readConnectorConfig(config);
67
+ const configured = hasConfiguredTelegramAccount(connectorConfig);
68
+ const sessExists = telegramAccountSessionExists();
69
+ const authSnapshot = telegramAccountAuthSession?.getSnapshot() ?? null;
70
+ const service = resolveService(runtime);
71
+ const serviceConnected = isServiceConnected(service);
72
+ const serviceAccount = typeof service?.getAccountSummary === "function" ? service.getAccountSummary() : null;
73
+ const fallbackPhone = resolveConfiguredPhone(runtime, connectorConfig);
74
+ let status = authSnapshot?.status ?? (serviceConnected ? "connected" : configured || sessExists ? "configured" : "idle");
75
+ if (serviceConnected && status === "configured") {
76
+ status = "connected";
77
+ }
78
+ return {
79
+ available: true,
80
+ status,
81
+ configured,
82
+ sessionExists: sessExists,
83
+ serviceConnected,
84
+ restartRequired: status === "configured" && !serviceConnected,
85
+ hasAppCredentials: Boolean(
86
+ (typeof connectorConfig.appId === "string" || typeof connectorConfig.appId === "number") && typeof connectorConfig.appHash === "string" && connectorConfig.appHash.trim().length > 0
87
+ ),
88
+ phone: authSnapshot?.phone ?? fallbackPhone,
89
+ isCodeViaApp: authSnapshot?.isCodeViaApp ?? false,
90
+ account: authSnapshot?.account ?? serviceAccount ?? null,
91
+ error: authSnapshot?.error ?? null
92
+ };
93
+ }
94
+ function ensureConnectorBlock(config) {
95
+ if (!config.connectors) {
96
+ config.connectors = {};
97
+ }
98
+ const connectors = config.connectors;
99
+ if (!connectors.telegramAccount || typeof connectors.telegramAccount !== "object" || Array.isArray(connectors.telegramAccount)) {
100
+ connectors.telegramAccount = {};
101
+ }
102
+ return connectors.telegramAccount;
103
+ }
104
+ function createSessionOptions(config) {
105
+ const connectorConfig = readConnectorConfig(config);
106
+ return {
107
+ deviceModel: typeof connectorConfig.deviceModel === "string" && connectorConfig.deviceModel.trim().length > 0 ? connectorConfig.deviceModel.trim() : defaultTelegramAccountDeviceModel(),
108
+ systemVersion: typeof connectorConfig.systemVersion === "string" && connectorConfig.systemVersion.trim().length > 0 ? connectorConfig.systemVersion.trim() : defaultTelegramAccountSystemVersion()
109
+ };
110
+ }
111
+ function ensureAuthSession(config) {
112
+ if (telegramAccountAuthSession) {
113
+ return telegramAccountAuthSession;
114
+ }
115
+ if (!telegramAccountAuthStateExists()) {
116
+ return null;
117
+ }
118
+ telegramAccountAuthSession = new TelegramAccountAuthSession(
119
+ createSessionOptions(config)
120
+ );
121
+ return telegramAccountAuthSession;
122
+ }
123
+ async function handleStatus(_req, res, runtime) {
124
+ const setupService = getSetupService(runtime);
125
+ const config = setupService?.getConfig() ?? {};
126
+ ensureAuthSession(config);
127
+ res.status(200).json(statusFromState(runtime, config));
128
+ }
129
+ async function handleAuthStart(req, res, runtime) {
130
+ const body = req.body ?? {};
131
+ const setupService = getSetupService(runtime);
132
+ const config = setupService?.getConfig() ?? {};
133
+ const connectorConfig = readConnectorConfig(config);
134
+ const phone = typeof body.phone === "string" && body.phone.trim() || resolveConfiguredPhone(runtime, connectorConfig);
135
+ if (!phone) {
136
+ res.status(400).json({ error: "telegram phone number is required" });
137
+ return;
138
+ }
139
+ await telegramAccountAuthSession?.stop();
140
+ telegramAccountAuthSession = new TelegramAccountAuthSession(
141
+ createSessionOptions(config)
142
+ );
143
+ const credentials = hasConfiguredTelegramAccount(connectorConfig) && (typeof connectorConfig.appId === "string" || typeof connectorConfig.appId === "number") && typeof connectorConfig.appHash === "string" ? {
144
+ apiId: Number(connectorConfig.appId),
145
+ apiHash: connectorConfig.appHash
146
+ } : null;
147
+ try {
148
+ await telegramAccountAuthSession.start({ phone, credentials });
149
+ const resolved = telegramAccountAuthSession.getResolvedConnectorConfig();
150
+ if (resolved && setupService) {
151
+ setupService.updateConfig((cfg) => {
152
+ Object.assign(ensureConnectorBlock(cfg), resolved);
153
+ });
154
+ }
155
+ const freshConfig = setupService?.getConfig() ?? config;
156
+ res.status(200).json(statusFromState(runtime, freshConfig));
157
+ } catch (error) {
158
+ res.status(500).json({
159
+ error: error instanceof Error ? error.message : String(error)
160
+ });
161
+ }
162
+ }
163
+ async function handleAuthSubmit(req, res, runtime) {
164
+ const body = req.body ?? {};
165
+ const setupService = getSetupService(runtime);
166
+ const config = setupService?.getConfig() ?? {};
167
+ if (!ensureAuthSession(config)) {
168
+ res.status(400).json({ error: "telegram login session has not been started" });
169
+ return;
170
+ }
171
+ if (!telegramAccountAuthSession) {
172
+ res.status(400).json({ error: "telegram login session has not been started" });
173
+ return;
174
+ }
175
+ try {
176
+ await telegramAccountAuthSession.submit(body);
177
+ const resolved = telegramAccountAuthSession.getResolvedConnectorConfig();
178
+ if (resolved && setupService) {
179
+ setupService.updateConfig((cfg) => {
180
+ Object.assign(ensureConnectorBlock(cfg), resolved);
181
+ });
182
+ }
183
+ const freshConfig = setupService?.getConfig() ?? config;
184
+ res.status(200).json(statusFromState(runtime, freshConfig));
185
+ } catch (error) {
186
+ res.status(500).json({
187
+ error: error instanceof Error ? error.message : String(error)
188
+ });
189
+ }
190
+ }
191
+ async function handleDisconnect(_req, res, runtime) {
192
+ await telegramAccountAuthSession?.stop();
193
+ telegramAccountAuthSession = null;
194
+ clearTelegramAccountAuthState();
195
+ clearTelegramAccountSession();
196
+ const service = resolveService(runtime);
197
+ if (typeof service?.stop === "function") {
198
+ await service.stop();
199
+ }
200
+ const setupService = getSetupService(runtime);
201
+ if (setupService) {
202
+ setupService.updateConfig((cfg) => {
203
+ const connectors = cfg.connectors;
204
+ if (connectors?.telegramAccount) {
205
+ delete connectors.telegramAccount;
206
+ }
207
+ });
208
+ }
209
+ const config = setupService?.getConfig() ?? {};
210
+ res.status(200).json({
211
+ ok: true,
212
+ ...statusFromState(runtime, config)
213
+ });
214
+ }
215
+ var telegramAccountRoutes = [
216
+ {
217
+ type: "GET",
218
+ path: "/api/telegram-account/status",
219
+ handler: handleStatus,
220
+ rawPath: true
221
+ },
222
+ {
223
+ type: "POST",
224
+ path: "/api/telegram-account/auth/start",
225
+ handler: handleAuthStart,
226
+ rawPath: true
227
+ },
228
+ {
229
+ type: "POST",
230
+ path: "/api/telegram-account/auth/submit",
231
+ handler: handleAuthSubmit,
232
+ rawPath: true
233
+ },
234
+ {
235
+ type: "POST",
236
+ path: "/api/telegram-account/disconnect",
237
+ handler: handleDisconnect,
238
+ rawPath: true
239
+ }
240
+ ];
241
+
242
+ // src/constants.ts
243
+ var MESSAGE_CONSTANTS = {
244
+ MAX_MESSAGES: 50,
245
+ RECENT_MESSAGE_COUNT: 5,
246
+ CHAT_HISTORY_COUNT: 10,
247
+ DEFAULT_SIMILARITY_THRESHOLD: 0.6,
248
+ DEFAULT_SIMILARITY_THRESHOLD_FOLLOW_UPS: 0.4,
249
+ INTEREST_DECAY_TIME: 5 * 60 * 1e3,
250
+ // 5 minutes
251
+ PARTIAL_INTEREST_DECAY: 3 * 60 * 1e3
252
+ // 3 minutes
253
+ };
254
+ var TELEGRAM_SERVICE_NAME = "telegram";
255
+
256
+ // src/messageManager.ts
257
+ import {
258
+ ChannelType,
259
+ createUniqueUuid,
260
+ EventType,
261
+ lifeOpsPassiveConnectorsEnabled,
262
+ logger as logger2,
263
+ ModelType,
264
+ ServiceType
265
+ } from "@elizaos/core";
266
+ import fs from "fs";
267
+ import { Markup as Markup2 } from "telegraf";
268
+
269
+ // src/utils.ts
270
+ import { logger } from "@elizaos/core";
271
+ import { Markup } from "telegraf";
272
+ var TELEGRAM_RESERVED_REGEX = /([_*[\]()~`>#+\-=|{}.!\\])/g;
273
+ function escapePlainText(text) {
274
+ if (!text) {
275
+ return "";
276
+ }
277
+ return text.replace(TELEGRAM_RESERVED_REGEX, "\\$1");
278
+ }
279
+ function escapePlainTextPreservingBlockquote(text) {
280
+ if (!text) {
281
+ return "";
282
+ }
283
+ return text.split("\n").map((line) => {
284
+ const match = line.match(/^(>+\s?)(.*)$/);
285
+ if (match) {
286
+ return match[1] + escapePlainText(match[2]);
287
+ }
288
+ return escapePlainText(line);
289
+ }).join("\n");
290
+ }
291
+ function escapeCode(text) {
292
+ if (!text) {
293
+ return "";
294
+ }
295
+ return text.replace(/([`\\])/g, "\\$1");
296
+ }
297
+ function escapeUrl(url) {
298
+ if (!url) {
299
+ return "";
300
+ }
301
+ return url.replace(/([)\\])/g, "\\$1");
302
+ }
303
+ function convertMarkdownToTelegram(markdown) {
304
+ const replacements = [];
305
+ function storeReplacement(formatted) {
306
+ const placeholder = `\0${replacements.length}\0`;
307
+ replacements.push(formatted);
308
+ return placeholder;
309
+ }
310
+ let converted = markdown;
311
+ converted = converted.replace(
312
+ /```(\w+)?\n([\s\S]*?)```/g,
313
+ (_match, lang, code) => {
314
+ const escapedCode = escapeCode(code);
315
+ const formatted = `\`\`\`${lang || ""}
316
+ ${escapedCode}\`\`\``;
317
+ return storeReplacement(formatted);
318
+ }
319
+ );
320
+ converted = converted.replace(/`([^`]+)`/g, (_match, code) => {
321
+ const escapedCode = escapeCode(code);
322
+ const formatted = `\`${escapedCode}\``;
323
+ return storeReplacement(formatted);
324
+ });
325
+ converted = converted.replace(
326
+ /$begin:math:display$([^$end:math:display$]+)]$begin:math:text$([^)]+)$end:math:text$/g,
327
+ (_match, text, url) => {
328
+ const formattedText = escapePlainText(text);
329
+ const escapedURL = escapeUrl(url);
330
+ const formatted = `[${formattedText}](${escapedURL})`;
331
+ return storeReplacement(formatted);
332
+ }
333
+ );
334
+ converted = converted.replace(/\*\*([^*]+)\*\*/g, (_match, content) => {
335
+ const formattedContent = escapePlainText(content);
336
+ const formatted = `*${formattedContent}*`;
337
+ return storeReplacement(formatted);
338
+ });
339
+ converted = converted.replace(/~~([^~]+)~~/g, (_match, content) => {
340
+ const formattedContent = escapePlainText(content);
341
+ const formatted = `~${formattedContent}~`;
342
+ return storeReplacement(formatted);
343
+ });
344
+ converted = converted.replace(
345
+ /(?<!\*)\*([^*\n]+)\*(?!\*)/g,
346
+ (_match, content) => {
347
+ const formattedContent = escapePlainText(content);
348
+ const formatted = `_${formattedContent}_`;
349
+ return storeReplacement(formatted);
350
+ }
351
+ );
352
+ converted = converted.replace(/_([^_\n]+)_/g, (_match, content) => {
353
+ const formattedContent = escapePlainText(content);
354
+ const formatted = `_${formattedContent}_`;
355
+ return storeReplacement(formatted);
356
+ });
357
+ converted = converted.replace(
358
+ /^(#{1,6})\s*(.*)$/gm,
359
+ (_match, _hashes, headerContent) => {
360
+ const formatted = `*${escapePlainText(headerContent.trim())}*`;
361
+ return storeReplacement(formatted);
362
+ }
363
+ );
364
+ const NULL_CHAR = String.fromCharCode(0);
365
+ const PLACEHOLDER_PATTERN = new RegExp(`(${NULL_CHAR}\\d+${NULL_CHAR})`, "g");
366
+ const PLACEHOLDER_TEST = new RegExp(`^${NULL_CHAR}\\d+${NULL_CHAR}$`);
367
+ const PLACEHOLDER_REPLACE = new RegExp(`${NULL_CHAR}(\\d+)${NULL_CHAR}`, "g");
368
+ const finalEscaped = converted.split(PLACEHOLDER_PATTERN).map((segment) => {
369
+ if (PLACEHOLDER_TEST.test(segment)) {
370
+ return segment;
371
+ } else {
372
+ return escapePlainTextPreservingBlockquote(segment);
373
+ }
374
+ }).join("");
375
+ const finalResult = finalEscaped.replace(PLACEHOLDER_REPLACE, (_, index) => {
376
+ return replacements[Number.parseInt(index, 10)];
377
+ });
378
+ return finalResult;
379
+ }
380
+ function convertToTelegramButtons(buttons) {
381
+ if (!buttons) {
382
+ return [];
383
+ }
384
+ const telegramButtons = [];
385
+ for (const button of buttons) {
386
+ if (!button?.text || !button.url) {
387
+ logger.warn(
388
+ { src: "plugin:telegram", button },
389
+ "Invalid button configuration, skipping"
390
+ );
391
+ continue;
392
+ }
393
+ let telegramButton;
394
+ switch (button.kind) {
395
+ case "login":
396
+ telegramButton = Markup.button.login(button.text, button.url);
397
+ break;
398
+ case "url":
399
+ telegramButton = Markup.button.url(button.text, button.url);
400
+ break;
401
+ default:
402
+ logger.warn(
403
+ { src: "plugin:telegram", buttonKind: button.kind },
404
+ "Unknown button kind, treating as URL button"
405
+ );
406
+ telegramButton = Markup.button.url(button.text, button.url);
407
+ break;
408
+ }
409
+ telegramButtons.push(telegramButton);
410
+ }
411
+ return telegramButtons;
412
+ }
413
+ function cleanText(text) {
414
+ if (!text) {
415
+ return "";
416
+ }
417
+ return text.split("\0").join("");
418
+ }
419
+
420
+ // src/messageManager.ts
421
+ var MAX_MESSAGE_LENGTH = 4096;
422
+ var getChannelType = (chat) => {
423
+ const chatType = chat.type;
424
+ switch (chatType) {
425
+ case "private":
426
+ return ChannelType.DM;
427
+ case "group":
428
+ case "supergroup":
429
+ case "channel":
430
+ return ChannelType.GROUP;
431
+ default:
432
+ throw new Error(`Unrecognized Telegram chat type: ${String(chatType)}`);
433
+ }
434
+ };
435
+ var MessageManager = class {
436
+ bot;
437
+ runtime;
438
+ /**
439
+ * Constructor for creating a new instance of a BotAgent.
440
+ *
441
+ * @param {Telegraf<Context>} bot - The Telegraf instance used for interacting with the bot platform.
442
+ * @param {IAgentRuntime} runtime - The runtime environment for the agent.
443
+ */
444
+ constructor(bot, runtime) {
445
+ this.bot = bot;
446
+ this.runtime = runtime;
447
+ }
448
+ /**
449
+ * Process an image from a Telegram message to extract the image URL and description.
450
+ *
451
+ * @param {Message} message - The Telegram message object containing the image.
452
+ * @returns {Promise<{ description: string } | null>} The description of the processed image or null if no image found.
453
+ */
454
+ async processImage(message) {
455
+ try {
456
+ let imageUrl = null;
457
+ logger2.debug(
458
+ {
459
+ src: "plugin:telegram",
460
+ agentId: this.runtime.agentId,
461
+ messageId: message.message_id
462
+ },
463
+ "Processing image from message"
464
+ );
465
+ if ("photo" in message && message.photo?.length > 0) {
466
+ const photo = message.photo[message.photo.length - 1];
467
+ const fileLink = await this.bot.telegram.getFileLink(photo.file_id);
468
+ imageUrl = fileLink.toString();
469
+ } else if ("document" in message && message.document?.mime_type?.startsWith("image/") && !message.document?.mime_type?.startsWith("application/pdf")) {
470
+ const fileLink = await this.bot.telegram.getFileLink(
471
+ message.document.file_id
472
+ );
473
+ imageUrl = fileLink.toString();
474
+ }
475
+ if (imageUrl) {
476
+ const { title, description } = await this.runtime.useModel(
477
+ ModelType.IMAGE_DESCRIPTION,
478
+ imageUrl
479
+ );
480
+ return { description: `[Image: ${title}
481
+ ${description}]` };
482
+ }
483
+ } catch (error) {
484
+ logger2.error(
485
+ {
486
+ src: "plugin:telegram",
487
+ agentId: this.runtime.agentId,
488
+ error: error instanceof Error ? error.message : String(error)
489
+ },
490
+ "Error processing image"
491
+ );
492
+ }
493
+ return null;
494
+ }
495
+ /**
496
+ * Process a document from a Telegram message to extract the document URL and description.
497
+ * Handles PDFs and other document types by converting them to text when possible.
498
+ *
499
+ * @param {Message} message - The Telegram message object containing the document.
500
+ * @returns {Promise<{ description: string } | null>} The description of the processed document or null if no document found.
501
+ */
502
+ async processDocument(message) {
503
+ try {
504
+ if (!("document" in message) || !message.document) {
505
+ return null;
506
+ }
507
+ const document = message.document;
508
+ const fileLink = await this.bot.telegram.getFileLink(document.file_id);
509
+ const documentUrl = fileLink.toString();
510
+ logger2.debug(
511
+ {
512
+ src: "plugin:telegram",
513
+ agentId: this.runtime.agentId,
514
+ fileName: document.file_name,
515
+ mimeType: document.mime_type,
516
+ fileSize: document.file_size
517
+ },
518
+ "Processing document"
519
+ );
520
+ const documentProcessor = this.getDocumentProcessor(document.mime_type);
521
+ if (documentProcessor) {
522
+ return await documentProcessor(document, documentUrl);
523
+ }
524
+ return {
525
+ title: `Document: ${document.file_name || "Unknown Document"}`,
526
+ fullText: "",
527
+ formattedDescription: `[Document: ${document.file_name || "Unknown Document"}
528
+ Type: ${document.mime_type || "unknown"}
529
+ Size: ${document.file_size || 0} bytes]`,
530
+ fileName: document.file_name || "Unknown Document",
531
+ mimeType: document.mime_type,
532
+ fileSize: document.file_size
533
+ };
534
+ } catch (error) {
535
+ logger2.error(
536
+ {
537
+ src: "plugin:telegram",
538
+ agentId: this.runtime.agentId,
539
+ error: error instanceof Error ? error.message : String(error)
540
+ },
541
+ "Error processing document"
542
+ );
543
+ return null;
544
+ }
545
+ }
546
+ /**
547
+ * Get the appropriate document processor based on MIME type.
548
+ */
549
+ getDocumentProcessor(mimeType) {
550
+ if (!mimeType) {
551
+ return null;
552
+ }
553
+ const processors = {
554
+ "application/pdf": this.processPdfDocument.bind(this),
555
+ "text/": this.processTextDocument.bind(this),
556
+ // covers text/plain, text/csv, text/markdown, etc.
557
+ "application/json": this.processTextDocument.bind(this)
558
+ };
559
+ for (const [pattern, processor] of Object.entries(processors)) {
560
+ if (mimeType.startsWith(pattern)) {
561
+ return processor;
562
+ }
563
+ }
564
+ return null;
565
+ }
566
+ /**
567
+ * Process PDF documents by converting them to text.
568
+ */
569
+ async processPdfDocument(document, documentUrl) {
570
+ try {
571
+ const pdfService = this.runtime.getService(
572
+ ServiceType.PDF
573
+ );
574
+ if (!pdfService) {
575
+ logger2.warn(
576
+ { src: "plugin:telegram", agentId: this.runtime.agentId },
577
+ "PDF service not available, using fallback"
578
+ );
579
+ return {
580
+ title: `PDF Document: ${document.file_name || "Unknown Document"}`,
581
+ fullText: "",
582
+ formattedDescription: `[PDF Document: ${document.file_name || "Unknown Document"}
583
+ Size: ${document.file_size || 0} bytes
584
+ Unable to extract text content]`,
585
+ fileName: document.file_name || "Unknown Document",
586
+ mimeType: document.mime_type,
587
+ fileSize: document.file_size
588
+ };
589
+ }
590
+ const response = await fetch(documentUrl);
591
+ if (!response.ok) {
592
+ throw new Error(`Failed to fetch PDF: ${response.status}`);
593
+ }
594
+ const pdfBuffer = await response.arrayBuffer();
595
+ const text = await pdfService.convertPdfToText(Buffer.from(pdfBuffer));
596
+ logger2.debug(
597
+ {
598
+ src: "plugin:telegram",
599
+ agentId: this.runtime.agentId,
600
+ fileName: document.file_name,
601
+ charactersExtracted: text.length
602
+ },
603
+ "PDF processed successfully"
604
+ );
605
+ return {
606
+ title: document.file_name || "Unknown Document",
607
+ fullText: text,
608
+ formattedDescription: `[PDF Document: ${document.file_name || "Unknown Document"}
609
+ Size: ${document.file_size || 0} bytes
610
+ Text extracted successfully: ${text.length} characters]`,
611
+ fileName: document.file_name || "Unknown Document",
612
+ mimeType: document.mime_type,
613
+ fileSize: document.file_size
614
+ };
615
+ } catch (error) {
616
+ logger2.error(
617
+ {
618
+ src: "plugin:telegram",
619
+ agentId: this.runtime.agentId,
620
+ fileName: document.file_name,
621
+ error: error instanceof Error ? error.message : String(error)
622
+ },
623
+ "Error processing PDF document"
624
+ );
625
+ return {
626
+ title: `PDF Document: ${document.file_name || "Unknown Document"}`,
627
+ fullText: "",
628
+ formattedDescription: `[PDF Document: ${document.file_name || "Unknown Document"}
629
+ Size: ${document.file_size || 0} bytes
630
+ Error: Unable to extract text content]`,
631
+ fileName: document.file_name || "Unknown Document",
632
+ mimeType: document.mime_type,
633
+ fileSize: document.file_size
634
+ };
635
+ }
636
+ }
637
+ /**
638
+ * Process text documents by fetching their content.
639
+ */
640
+ async processTextDocument(document, documentUrl) {
641
+ try {
642
+ const response = await fetch(documentUrl);
643
+ if (!response.ok) {
644
+ throw new Error(`Failed to fetch text document: ${response.status}`);
645
+ }
646
+ const text = await response.text();
647
+ logger2.debug(
648
+ {
649
+ src: "plugin:telegram",
650
+ agentId: this.runtime.agentId,
651
+ fileName: document.file_name,
652
+ charactersExtracted: text.length
653
+ },
654
+ "Text document processed successfully"
655
+ );
656
+ return {
657
+ title: document.file_name || "Unknown Document",
658
+ fullText: text,
659
+ formattedDescription: `[Text Document: ${document.file_name || "Unknown Document"}
660
+ Size: ${document.file_size || 0} bytes
661
+ Text extracted successfully: ${text.length} characters]`,
662
+ fileName: document.file_name || "Unknown Document",
663
+ mimeType: document.mime_type,
664
+ fileSize: document.file_size
665
+ };
666
+ } catch (error) {
667
+ logger2.error(
668
+ {
669
+ src: "plugin:telegram",
670
+ agentId: this.runtime.agentId,
671
+ fileName: document.file_name,
672
+ error: error instanceof Error ? error.message : String(error)
673
+ },
674
+ "Error processing text document"
675
+ );
676
+ return {
677
+ title: `Text Document: ${document.file_name || "Unknown Document"}`,
678
+ fullText: "",
679
+ formattedDescription: `[Text Document: ${document.file_name || "Unknown Document"}
680
+ Size: ${document.file_size || 0} bytes
681
+ Error: Unable to read content]`,
682
+ fileName: document.file_name || "Unknown Document",
683
+ mimeType: document.mime_type,
684
+ fileSize: document.file_size
685
+ };
686
+ }
687
+ }
688
+ /**
689
+ * Processes the message content, documents, and images to generate
690
+ * processed content and media attachments.
691
+ *
692
+ * @param {Message} message The message to process
693
+ * @returns {Promise<{ processedContent: string; attachments: Media[] }>} Processed content and media attachments
694
+ */
695
+ async processMessage(message) {
696
+ let processedContent = "";
697
+ const attachments = [];
698
+ if ("text" in message && message.text) {
699
+ processedContent = message.text;
700
+ } else if ("caption" in message && message.caption) {
701
+ processedContent = message.caption;
702
+ }
703
+ if ("document" in message && message.document) {
704
+ const document = message.document;
705
+ const documentInfo = await this.processDocument(message);
706
+ if (documentInfo) {
707
+ try {
708
+ const fileLink = await this.bot.telegram.getFileLink(
709
+ document.file_id
710
+ );
711
+ const title = documentInfo.title;
712
+ const fullText = documentInfo.fullText;
713
+ if (fullText) {
714
+ const documentContent = `
715
+
716
+ --- DOCUMENT CONTENT ---
717
+ Title: ${title}
718
+
719
+ Full Content:
720
+ ${fullText}
721
+ --- END DOCUMENT ---
722
+
723
+ `;
724
+ processedContent += documentContent;
725
+ }
726
+ attachments.push({
727
+ id: document.file_id,
728
+ url: fileLink.toString(),
729
+ title,
730
+ source: document.mime_type?.startsWith("application/pdf") ? "PDF" : "Document",
731
+ description: documentInfo.formattedDescription,
732
+ text: fullText
733
+ });
734
+ logger2.debug(
735
+ {
736
+ src: "plugin:telegram",
737
+ agentId: this.runtime.agentId,
738
+ fileName: documentInfo.fileName
739
+ },
740
+ "Document processed successfully"
741
+ );
742
+ } catch (error) {
743
+ logger2.error(
744
+ {
745
+ src: "plugin:telegram",
746
+ agentId: this.runtime.agentId,
747
+ fileName: documentInfo.fileName,
748
+ error: error instanceof Error ? error.message : String(error)
749
+ },
750
+ "Error processing document"
751
+ );
752
+ attachments.push({
753
+ id: document.file_id,
754
+ url: "",
755
+ title: `Document: ${documentInfo.fileName}`,
756
+ source: "Document",
757
+ description: `Document processing failed: ${documentInfo.fileName}`,
758
+ text: `Document: ${documentInfo.fileName}
759
+ Size: ${documentInfo.fileSize || 0} bytes
760
+ Type: ${documentInfo.mimeType || "unknown"}`
761
+ });
762
+ }
763
+ } else {
764
+ attachments.push({
765
+ id: document.file_id,
766
+ url: "",
767
+ title: `Document: ${document.file_name || "Unknown Document"}`,
768
+ source: "Document",
769
+ description: `Document: ${document.file_name || "Unknown Document"}`,
770
+ text: `Document: ${document.file_name || "Unknown Document"}
771
+ Size: ${document.file_size || 0} bytes
772
+ Type: ${document.mime_type || "unknown"}`
773
+ });
774
+ }
775
+ }
776
+ if ("photo" in message && message.photo?.length > 0) {
777
+ const imageInfo = await this.processImage(message);
778
+ if (imageInfo) {
779
+ const photo = message.photo[message.photo.length - 1];
780
+ const fileLink = await this.bot.telegram.getFileLink(photo.file_id);
781
+ attachments.push({
782
+ id: photo.file_id,
783
+ url: fileLink.toString(),
784
+ title: "Image Attachment",
785
+ source: "Image",
786
+ description: imageInfo.description,
787
+ text: imageInfo.description
788
+ });
789
+ }
790
+ }
791
+ logger2.debug(
792
+ {
793
+ src: "plugin:telegram",
794
+ agentId: this.runtime.agentId,
795
+ hasContent: !!processedContent,
796
+ attachmentsCount: attachments.length
797
+ },
798
+ "Message processed"
799
+ );
800
+ return { processedContent, attachments };
801
+ }
802
+ /**
803
+ * Sends a message in chunks, handling attachments and splitting the message if necessary
804
+ *
805
+ * @param {Context} ctx - The context object representing the current state of the bot
806
+ * @param {TelegramContent} content - The content of the message to be sent
807
+ * @param {number} [replyToMessageId] - The ID of the message to reply to, if any
808
+ * @returns {Promise<Message.TextMessage[]>} - An array of TextMessage objects representing the messages sent
809
+ */
810
+ async sendMessageInChunks(ctx, content, replyToMessageId) {
811
+ if (content.attachments && content.attachments.length > 0) {
812
+ content.attachments.map(async (attachment) => {
813
+ const typeMap = {
814
+ "image/gif": "animation" /* ANIMATION */,
815
+ image: "photo" /* PHOTO */,
816
+ doc: "document" /* DOCUMENT */,
817
+ video: "video" /* VIDEO */,
818
+ audio: "audio" /* AUDIO */
819
+ };
820
+ let mediaType;
821
+ for (const prefix in typeMap) {
822
+ if (attachment.contentType?.startsWith(prefix)) {
823
+ mediaType = typeMap[prefix];
824
+ break;
825
+ }
826
+ }
827
+ if (!mediaType) {
828
+ throw new Error(
829
+ `Unsupported Telegram attachment content type: ${attachment.contentType}`
830
+ );
831
+ }
832
+ await this.sendMedia(
833
+ ctx,
834
+ attachment.url,
835
+ mediaType,
836
+ attachment.description
837
+ );
838
+ });
839
+ return [];
840
+ } else {
841
+ const chunks = this.splitMessage(content.text ?? "");
842
+ const sentMessages = [];
843
+ const telegramButtons = convertToTelegramButtons(content.buttons ?? []);
844
+ if (!ctx.chat) {
845
+ logger2.error(
846
+ { src: "plugin:telegram", agentId: this.runtime.agentId },
847
+ "sendMessageInChunks: ctx.chat is undefined"
848
+ );
849
+ return [];
850
+ }
851
+ await ctx.telegram.sendChatAction(ctx.chat.id, "typing");
852
+ for (let i = 0; i < chunks.length; i++) {
853
+ const chunk = convertMarkdownToTelegram(chunks[i]);
854
+ if (!ctx.chat) {
855
+ logger2.error(
856
+ { src: "plugin:telegram", agentId: this.runtime.agentId },
857
+ "sendMessageInChunks loop: ctx.chat is undefined"
858
+ );
859
+ continue;
860
+ }
861
+ const sentMessage = await ctx.telegram.sendMessage(
862
+ ctx.chat.id,
863
+ chunk,
864
+ {
865
+ reply_parameters: i === 0 && replyToMessageId ? { message_id: replyToMessageId } : void 0,
866
+ parse_mode: "MarkdownV2",
867
+ ...Markup2.inlineKeyboard(telegramButtons)
868
+ }
869
+ );
870
+ sentMessages.push(sentMessage);
871
+ }
872
+ return sentMessages;
873
+ }
874
+ }
875
+ /**
876
+ * Sends media to a chat using the Telegram API.
877
+ *
878
+ * @param {Context} ctx - The context object containing information about the current chat.
879
+ * @param {string} mediaPath - The path to the media to be sent, either a URL or a local file path.
880
+ * @param {MediaType} type - The type of media being sent (PHOTO, VIDEO, DOCUMENT, AUDIO, or ANIMATION).
881
+ * @param {string} [caption] - Optional caption for the media being sent.
882
+ *
883
+ * @returns {Promise<void>} A Promise that resolves when the media is successfully sent.
884
+ */
885
+ async sendMedia(ctx, mediaPath, type, caption) {
886
+ try {
887
+ const isUrl = /^(http|https):\/\//.test(mediaPath);
888
+ const sendFunctionMap = {
889
+ ["photo" /* PHOTO */]: ctx.telegram.sendPhoto.bind(ctx.telegram),
890
+ ["video" /* VIDEO */]: ctx.telegram.sendVideo.bind(ctx.telegram),
891
+ ["document" /* DOCUMENT */]: ctx.telegram.sendDocument.bind(ctx.telegram),
892
+ ["audio" /* AUDIO */]: ctx.telegram.sendAudio.bind(ctx.telegram),
893
+ ["animation" /* ANIMATION */]: ctx.telegram.sendAnimation.bind(ctx.telegram)
894
+ };
895
+ const sendFunction = sendFunctionMap[type];
896
+ if (!sendFunction) {
897
+ throw new Error(`Unsupported media type: ${type}`);
898
+ }
899
+ if (!ctx.chat) {
900
+ throw new Error("sendMedia: ctx.chat is undefined");
901
+ }
902
+ if (isUrl) {
903
+ await sendFunction(ctx.chat.id, mediaPath, { caption });
904
+ } else {
905
+ if (!fs.existsSync(mediaPath)) {
906
+ throw new Error(`File not found at path: ${mediaPath}`);
907
+ }
908
+ const fileStream = fs.createReadStream(mediaPath);
909
+ try {
910
+ if (!ctx.chat) {
911
+ throw new Error("sendMedia (file): ctx.chat is undefined");
912
+ }
913
+ await sendFunction(ctx.chat.id, { source: fileStream }, { caption });
914
+ } finally {
915
+ fileStream.destroy();
916
+ }
917
+ }
918
+ logger2.debug(
919
+ {
920
+ src: "plugin:telegram",
921
+ agentId: this.runtime.agentId,
922
+ mediaType: type,
923
+ mediaPath
924
+ },
925
+ "Media sent successfully"
926
+ );
927
+ } catch (error) {
928
+ logger2.error(
929
+ {
930
+ src: "plugin:telegram",
931
+ agentId: this.runtime.agentId,
932
+ mediaType: type,
933
+ mediaPath,
934
+ error: error instanceof Error ? error.message : String(error)
935
+ },
936
+ "Failed to send media"
937
+ );
938
+ throw error;
939
+ }
940
+ }
941
+ /**
942
+ * Splits a given text into an array of strings based on the maximum message length.
943
+ *
944
+ * @param {string} text - The text to split into chunks.
945
+ * @returns {string[]} An array of strings with each element representing a chunk of the original text.
946
+ */
947
+ splitMessage(text) {
948
+ const chunks = [];
949
+ if (!text) {
950
+ return chunks;
951
+ }
952
+ let currentChunk = "";
953
+ const lines = text.split("\n");
954
+ for (const line of lines) {
955
+ if (currentChunk.length + line.length + 1 <= MAX_MESSAGE_LENGTH) {
956
+ currentChunk += (currentChunk ? "\n" : "") + line;
957
+ } else {
958
+ if (currentChunk) {
959
+ chunks.push(currentChunk);
960
+ }
961
+ currentChunk = line;
962
+ }
963
+ }
964
+ if (currentChunk) {
965
+ chunks.push(currentChunk);
966
+ }
967
+ return chunks;
968
+ }
969
+ /**
970
+ * Handle incoming messages from Telegram and process them accordingly.
971
+ * @param {Context} ctx - The context object containing information about the message.
972
+ * @returns {Promise<void>}
973
+ */
974
+ async handleMessage(ctx) {
975
+ if (!ctx.message || !ctx.from) {
976
+ return;
977
+ }
978
+ const message = ctx.message;
979
+ try {
980
+ const telegramUserId = ctx.from.id.toString();
981
+ const entityId = createUniqueUuid(this.runtime, telegramUserId);
982
+ const threadId = "is_topic_message" in message && message.is_topic_message ? message.message_thread_id?.toString() : void 0;
983
+ if (!ctx.chat) {
984
+ logger2.error(
985
+ { src: "plugin:telegram", agentId: this.runtime.agentId },
986
+ "handleMessage: ctx.chat is undefined"
987
+ );
988
+ return;
989
+ }
990
+ const telegramRoomid = threadId ? `${ctx.chat.id}-${threadId}` : ctx.chat.id.toString();
991
+ const telegramChatId = ctx.chat.id.toString();
992
+ const roomId = createUniqueUuid(this.runtime, telegramRoomid);
993
+ const worldId = createUniqueUuid(this.runtime, telegramChatId);
994
+ const telegramMessageId = message.message_id.toString();
995
+ const messageId = createUniqueUuid(this.runtime, telegramMessageId);
996
+ const { processedContent, attachments } = await this.processMessage(message);
997
+ const cleanedContent = cleanText(processedContent);
998
+ const cleanedAttachments = attachments.map((att) => ({
999
+ ...att,
1000
+ text: cleanText(att.text),
1001
+ description: cleanText(att.description),
1002
+ title: cleanText(att.title)
1003
+ }));
1004
+ if (!cleanedContent && cleanedAttachments.length === 0) {
1005
+ return;
1006
+ }
1007
+ const chat = message.chat;
1008
+ const channelType = getChannelType(chat);
1009
+ await this.runtime.ensureConnection({
1010
+ entityId,
1011
+ roomId,
1012
+ roomName: "title" in chat && typeof chat.title === "string" && chat.title || "first_name" in chat && typeof chat.first_name === "string" && chat.first_name || "username" in chat && typeof chat.username === "string" && chat.username || telegramRoomid,
1013
+ userName: ctx.from.username,
1014
+ name: ctx.from.first_name,
1015
+ userId: telegramUserId,
1016
+ source: "telegram",
1017
+ channelId: telegramRoomid,
1018
+ type: channelType,
1019
+ worldId,
1020
+ worldName: telegramRoomid
1021
+ });
1022
+ const memory = {
1023
+ id: messageId,
1024
+ entityId,
1025
+ agentId: this.runtime.agentId,
1026
+ roomId,
1027
+ content: {
1028
+ text: cleanedContent || " ",
1029
+ attachments: cleanedAttachments,
1030
+ source: "telegram",
1031
+ channelType,
1032
+ inReplyTo: "reply_to_message" in message && message.reply_to_message ? createUniqueUuid(
1033
+ this.runtime,
1034
+ message.reply_to_message.message_id.toString()
1035
+ ) : void 0
1036
+ },
1037
+ metadata: {
1038
+ type: "message",
1039
+ source: "telegram",
1040
+ provider: "telegram",
1041
+ timestamp: message.date * 1e3,
1042
+ entityName: ctx.from.first_name,
1043
+ entityUserName: ctx.from.username,
1044
+ fromBot: ctx.from.is_bot,
1045
+ fromId: telegramUserId,
1046
+ sourceId: entityId,
1047
+ chatType: chat.type,
1048
+ messageIdFull: telegramMessageId,
1049
+ sender: {
1050
+ id: telegramUserId,
1051
+ name: ctx.from.first_name,
1052
+ username: ctx.from.username
1053
+ },
1054
+ telegram: {
1055
+ chatId: telegramChatId,
1056
+ messageId: telegramMessageId,
1057
+ threadId
1058
+ },
1059
+ telegramUserId,
1060
+ telegramChatId
1061
+ },
1062
+ createdAt: message.date * 1e3
1063
+ };
1064
+ const callback = async (content, _actionName) => {
1065
+ try {
1066
+ if (!content.text) {
1067
+ return [];
1068
+ }
1069
+ let sentMessages = false;
1070
+ if (content?.channelType === "DM") {
1071
+ sentMessages = [];
1072
+ if (ctx.from) {
1073
+ const res = await this.bot.telegram.sendMessage(
1074
+ ctx.from.id,
1075
+ content.text
1076
+ );
1077
+ sentMessages.push(res);
1078
+ }
1079
+ } else {
1080
+ sentMessages = await this.sendMessageInChunks(
1081
+ ctx,
1082
+ content,
1083
+ message.message_id
1084
+ );
1085
+ }
1086
+ if (!Array.isArray(sentMessages)) {
1087
+ return [];
1088
+ }
1089
+ const memories = [];
1090
+ for (let i = 0; i < sentMessages.length; i++) {
1091
+ const sentMessage = sentMessages[i];
1092
+ const responseMemory = {
1093
+ id: createUniqueUuid(
1094
+ this.runtime,
1095
+ sentMessage.message_id.toString()
1096
+ ),
1097
+ entityId: this.runtime.agentId,
1098
+ agentId: this.runtime.agentId,
1099
+ roomId,
1100
+ content: {
1101
+ ...content,
1102
+ source: "telegram",
1103
+ text: sentMessage.text,
1104
+ inReplyTo: messageId,
1105
+ channelType
1106
+ },
1107
+ metadata: {
1108
+ type: "message",
1109
+ source: "telegram",
1110
+ provider: "telegram",
1111
+ timestamp: sentMessage.date * 1e3,
1112
+ fromBot: true,
1113
+ fromId: this.runtime.agentId,
1114
+ sourceId: this.runtime.agentId,
1115
+ chatType: chat.type,
1116
+ messageIdFull: sentMessage.message_id.toString(),
1117
+ telegram: {
1118
+ chatId: sentMessage.chat.id,
1119
+ messageId: sentMessage.message_id.toString(),
1120
+ threadId
1121
+ }
1122
+ },
1123
+ createdAt: sentMessage.date * 1e3
1124
+ };
1125
+ await this.runtime.createMemory(responseMemory, "messages");
1126
+ memories.push(responseMemory);
1127
+ }
1128
+ return memories;
1129
+ } catch (error) {
1130
+ logger2.error(
1131
+ {
1132
+ src: "plugin:telegram",
1133
+ agentId: this.runtime.agentId,
1134
+ error: error instanceof Error ? error.message : String(error)
1135
+ },
1136
+ "Error in message callback"
1137
+ );
1138
+ return [];
1139
+ }
1140
+ };
1141
+ const telegramAutoReplyRaw = this.runtime.getSetting(
1142
+ "TELEGRAM_AUTO_REPLY"
1143
+ );
1144
+ const telegramAutoReply = !lifeOpsPassiveConnectorsEnabled(this.runtime) && (telegramAutoReplyRaw === true || telegramAutoReplyRaw === "true");
1145
+ if (!telegramAutoReply) {
1146
+ try {
1147
+ await this.runtime.createMemory(memory, "messages");
1148
+ } catch (persistError) {
1149
+ logger2.warn(
1150
+ {
1151
+ src: "plugin:telegram",
1152
+ agentId: this.runtime.agentId,
1153
+ error: persistError instanceof Error ? persistError.message : String(persistError)
1154
+ },
1155
+ "Failed to persist inbound memory while auto-reply is disabled"
1156
+ );
1157
+ }
1158
+ logger2.debug(
1159
+ { src: "plugin:telegram", agentId: this.runtime.agentId },
1160
+ "Auto-reply disabled (TELEGRAM_AUTO_REPLY=false); message ingested without response"
1161
+ );
1162
+ } else if (this.runtime.messageService) {
1163
+ await this.runtime.messageService.handleMessage(
1164
+ this.runtime,
1165
+ memory,
1166
+ callback
1167
+ );
1168
+ } else {
1169
+ logger2.error(
1170
+ { src: "plugin:telegram", agentId: this.runtime.agentId },
1171
+ "Message service is not available"
1172
+ );
1173
+ throw new Error(
1174
+ "Message service is not initialized. Ensure the message service is properly configured."
1175
+ );
1176
+ }
1177
+ } catch (error) {
1178
+ logger2.error(
1179
+ {
1180
+ src: "plugin:telegram",
1181
+ agentId: this.runtime.agentId,
1182
+ chatId: ctx.chat?.id,
1183
+ messageId: ctx.message?.message_id,
1184
+ from: ctx.from?.username || ctx.from?.id,
1185
+ error: error instanceof Error ? error.message : String(error)
1186
+ },
1187
+ "Error handling Telegram message"
1188
+ );
1189
+ throw error;
1190
+ }
1191
+ }
1192
+ /**
1193
+ * Handles the reaction event triggered by a user reacting to a message.
1194
+ * @param {NarrowedContext<Context<Update>, Update.MessageReactionUpdate>} ctx The context of the message reaction update
1195
+ * @returns {Promise<void>} A Promise that resolves when the reaction handling is complete
1196
+ */
1197
+ async handleReaction(ctx) {
1198
+ if (!ctx.update.message_reaction || !ctx.from) {
1199
+ return;
1200
+ }
1201
+ const reaction = ctx.update.message_reaction;
1202
+ const reactedToMessageId = reaction.message_id;
1203
+ const originalMessagePlaceholder = {
1204
+ message_id: reactedToMessageId,
1205
+ chat: reaction.chat,
1206
+ from: ctx.from,
1207
+ date: Math.floor(Date.now() / 1e3)
1208
+ };
1209
+ const reactionType = reaction.new_reaction[0].type;
1210
+ const reactionEmoji = reaction.new_reaction[0].type;
1211
+ try {
1212
+ const entityId = createUniqueUuid(
1213
+ this.runtime,
1214
+ ctx.from.id.toString()
1215
+ );
1216
+ const roomId = createUniqueUuid(this.runtime, ctx.chat.id.toString());
1217
+ const reactionId = createUniqueUuid(
1218
+ this.runtime,
1219
+ `${reaction.message_id}-${ctx.from.id}-${Date.now()}`
1220
+ );
1221
+ const memory = {
1222
+ id: reactionId,
1223
+ entityId,
1224
+ agentId: this.runtime.agentId,
1225
+ roomId,
1226
+ content: {
1227
+ channelType: getChannelType(reaction.chat),
1228
+ text: `Reacted with: ${reactionType === "emoji" ? reactionEmoji : reactionType}`,
1229
+ source: "telegram",
1230
+ inReplyTo: createUniqueUuid(
1231
+ this.runtime,
1232
+ reaction.message_id.toString()
1233
+ )
1234
+ },
1235
+ createdAt: Date.now()
1236
+ };
1237
+ const callback = async (content) => {
1238
+ try {
1239
+ const replyText = content.text ?? "";
1240
+ const sentMessage = await ctx.reply(replyText);
1241
+ const responseMemory = {
1242
+ id: createUniqueUuid(
1243
+ this.runtime,
1244
+ sentMessage.message_id.toString()
1245
+ ),
1246
+ entityId: this.runtime.agentId,
1247
+ agentId: this.runtime.agentId,
1248
+ roomId,
1249
+ content: {
1250
+ ...content,
1251
+ inReplyTo: reactionId
1252
+ },
1253
+ createdAt: sentMessage.date * 1e3
1254
+ };
1255
+ return [responseMemory];
1256
+ } catch (error) {
1257
+ logger2.error(
1258
+ {
1259
+ src: "plugin:telegram",
1260
+ agentId: this.runtime.agentId,
1261
+ error: error instanceof Error ? error.message : String(error)
1262
+ },
1263
+ "Error in reaction callback"
1264
+ );
1265
+ return [];
1266
+ }
1267
+ };
1268
+ this.runtime.emitEvent(EventType.REACTION_RECEIVED, {
1269
+ runtime: this.runtime,
1270
+ message: memory,
1271
+ callback,
1272
+ source: "telegram",
1273
+ ctx,
1274
+ originalMessage: originalMessagePlaceholder,
1275
+ // Cast needed due to placeholder
1276
+ reactionString: reactionType === "emoji" ? reactionEmoji : reactionType,
1277
+ originalReaction: reaction.new_reaction[0]
1278
+ });
1279
+ this.runtime.emitEvent("TELEGRAM_REACTION_RECEIVED" /* REACTION_RECEIVED */, {
1280
+ runtime: this.runtime,
1281
+ message: memory,
1282
+ callback,
1283
+ source: "telegram",
1284
+ ctx,
1285
+ originalMessage: originalMessagePlaceholder,
1286
+ // Cast needed due to placeholder
1287
+ reactionString: reactionType === "emoji" ? reactionEmoji : reactionType,
1288
+ originalReaction: reaction.new_reaction[0]
1289
+ });
1290
+ } catch (error) {
1291
+ logger2.error(
1292
+ {
1293
+ src: "plugin:telegram",
1294
+ agentId: this.runtime.agentId,
1295
+ error: error instanceof Error ? error.message : String(error)
1296
+ },
1297
+ "Error handling reaction"
1298
+ );
1299
+ }
1300
+ }
1301
+ /**
1302
+ * Sends a message to a Telegram chat and emits appropriate events
1303
+ * @param {number | string} chatId - The Telegram chat ID to send the message to
1304
+ * @param {Content} content - The content to send
1305
+ * @param {number} [replyToMessageId] - Optional message ID to reply to
1306
+ * @returns {Promise<Message.TextMessage[]>} The sent messages
1307
+ */
1308
+ async sendMessage(chatId, content, replyToMessageId) {
1309
+ try {
1310
+ const ctx = {
1311
+ chat: { id: chatId },
1312
+ telegram: this.bot.telegram
1313
+ };
1314
+ const sentMessages = await this.sendMessageInChunks(
1315
+ ctx,
1316
+ content,
1317
+ replyToMessageId
1318
+ );
1319
+ if (!sentMessages?.length) {
1320
+ return [];
1321
+ }
1322
+ const roomId = createUniqueUuid(this.runtime, chatId.toString());
1323
+ const memories = [];
1324
+ for (const sentMessage of sentMessages) {
1325
+ const memory = {
1326
+ id: createUniqueUuid(this.runtime, sentMessage.message_id.toString()),
1327
+ entityId: this.runtime.agentId,
1328
+ agentId: this.runtime.agentId,
1329
+ roomId,
1330
+ content: {
1331
+ ...content,
1332
+ text: sentMessage.text,
1333
+ source: "telegram",
1334
+ channelType: getChannelType({
1335
+ id: typeof chatId === "string" ? Number.parseInt(chatId, 10) : chatId,
1336
+ type: "private"
1337
+ // Default to private, will be overridden if in context
1338
+ })
1339
+ },
1340
+ createdAt: sentMessage.date * 1e3
1341
+ };
1342
+ await this.runtime.createMemory(memory, "messages");
1343
+ memories.push(memory);
1344
+ }
1345
+ if (memories.length > 0) {
1346
+ const firstMemory = memories[0];
1347
+ this.runtime.emitEvent(EventType.MESSAGE_SENT, {
1348
+ runtime: this.runtime,
1349
+ message: firstMemory,
1350
+ source: "telegram"
1351
+ });
1352
+ const telegramMessageSentPayload = {
1353
+ runtime: this.runtime,
1354
+ source: "telegram",
1355
+ originalMessages: sentMessages,
1356
+ chatId,
1357
+ message: firstMemory
1358
+ };
1359
+ this.runtime.emitEvent(
1360
+ "TELEGRAM_MESSAGE_SENT" /* MESSAGE_SENT */,
1361
+ telegramMessageSentPayload
1362
+ );
1363
+ }
1364
+ return sentMessages;
1365
+ } catch (error) {
1366
+ logger2.error(
1367
+ {
1368
+ src: "plugin:telegram",
1369
+ agentId: this.runtime.agentId,
1370
+ chatId,
1371
+ error: error instanceof Error ? error.message : String(error)
1372
+ },
1373
+ "Error sending message to Telegram"
1374
+ );
1375
+ return [];
1376
+ }
1377
+ }
1378
+ };
1379
+
1380
+ // src/owner-pairing-service.ts
1381
+ import { logger as logger3, Service } from "@elizaos/core";
1382
+ var TELEGRAM_OWNER_PAIRING_SERVICE_TYPE = "OWNER_PAIRING_TELEGRAM";
1383
+ var RATE_LIMIT_MAX_ATTEMPTS = 5;
1384
+ var RATE_LIMIT_WINDOW_MS = 6e4;
1385
+ async function auditEmit(runtime, action, outcome, metadata) {
1386
+ try {
1387
+ await runtime.emitEvent(
1388
+ ["AUTH_AUDIT"],
1389
+ {
1390
+ runtime,
1391
+ action,
1392
+ outcome,
1393
+ metadata,
1394
+ source: "telegram"
1395
+ }
1396
+ );
1397
+ } catch {
1398
+ }
1399
+ }
1400
+ var pairAttempts = /* @__PURE__ */ new Map();
1401
+ function isRateLimited(userId) {
1402
+ const now = Date.now();
1403
+ const windowStart = now - RATE_LIMIT_WINDOW_MS;
1404
+ const attempts = (pairAttempts.get(userId) ?? []).filter(
1405
+ (ts) => ts > windowStart
1406
+ );
1407
+ pairAttempts.set(userId, attempts);
1408
+ if (attempts.length >= RATE_LIMIT_MAX_ATTEMPTS) {
1409
+ return true;
1410
+ }
1411
+ attempts.push(now);
1412
+ pairAttempts.set(userId, attempts);
1413
+ return false;
1414
+ }
1415
+ function isValidPairCode(code) {
1416
+ return /^\d{6}$/.test(code.trim());
1417
+ }
1418
+ function resolveVerifyService(runtime) {
1419
+ try {
1420
+ const svc = runtime.getService("OWNER_BIND_VERIFY");
1421
+ if (svc && typeof svc === "object" && typeof svc.verifyOwnerBindFromConnector === "function") {
1422
+ return svc;
1423
+ }
1424
+ } catch {
1425
+ }
1426
+ return null;
1427
+ }
1428
+ function resolveDisplayHandle(from) {
1429
+ if (from.username) {
1430
+ return `@${from.username}`;
1431
+ }
1432
+ if (from.first_name) {
1433
+ return from.first_name;
1434
+ }
1435
+ return String(from.id);
1436
+ }
1437
+ async function handleElizaPairCommand(ctx, runtime) {
1438
+ const from = ctx.from;
1439
+ if (!from) {
1440
+ return;
1441
+ }
1442
+ const userId = String(from.id);
1443
+ const displayHandle = resolveDisplayHandle(from);
1444
+ if (isRateLimited(userId)) {
1445
+ logger3.warn(
1446
+ { src: "plugin:telegram:owner-pairing", userId },
1447
+ "Rate limit hit for /eliza_pair"
1448
+ );
1449
+ await auditEmit(
1450
+ runtime,
1451
+ "auth.owner.pair.telegram.rate_limited",
1452
+ "failure",
1453
+ { externalId: userId }
1454
+ );
1455
+ await ctx.reply(
1456
+ "Too many pairing attempts. Please wait a moment before trying again."
1457
+ );
1458
+ return;
1459
+ }
1460
+ const message = ctx.message;
1461
+ const rawText = message && "text" in message ? message.text : void 0;
1462
+ let code = null;
1463
+ if (typeof rawText === "string") {
1464
+ const parts = rawText.trim().split(/\s+/);
1465
+ if (parts.length >= 2) {
1466
+ code = parts[1] ?? null;
1467
+ }
1468
+ }
1469
+ if (!code?.trim()) {
1470
+ await ctx.reply(
1471
+ "Usage: /eliza\\_pair <code> \u2014 enter the 6-digit code shown in the Eliza dashboard."
1472
+ );
1473
+ return;
1474
+ }
1475
+ code = code.trim();
1476
+ if (!isValidPairCode(code)) {
1477
+ await ctx.reply(
1478
+ "The pairing code must be exactly 6 digits. Check the Eliza dashboard and try again."
1479
+ );
1480
+ return;
1481
+ }
1482
+ const verifySvc = resolveVerifyService(runtime);
1483
+ if (!verifySvc) {
1484
+ logger3.error(
1485
+ { src: "plugin:telegram:owner-pairing", userId },
1486
+ "OWNER_BIND_VERIFY service not available \u2014 cannot complete pairing"
1487
+ );
1488
+ await auditEmit(
1489
+ runtime,
1490
+ "auth.owner.pair.telegram.service_unavailable",
1491
+ "failure",
1492
+ { externalId: userId }
1493
+ );
1494
+ await ctx.reply(
1495
+ "Eliza could not reach the pairing service right now. Please try again in a moment."
1496
+ );
1497
+ return;
1498
+ }
1499
+ let result;
1500
+ try {
1501
+ result = await verifySvc.verifyOwnerBindFromConnector({
1502
+ connector: "telegram",
1503
+ externalId: userId,
1504
+ displayHandle,
1505
+ code
1506
+ });
1507
+ } catch (err) {
1508
+ logger3.error(
1509
+ {
1510
+ src: "plugin:telegram:owner-pairing",
1511
+ userId,
1512
+ error: err instanceof Error ? err.message : String(err)
1513
+ },
1514
+ "verifyOwnerBindFromConnector threw unexpectedly"
1515
+ );
1516
+ await auditEmit(
1517
+ runtime,
1518
+ "auth.owner.pair.telegram.verify_error",
1519
+ "failure",
1520
+ { externalId: userId }
1521
+ );
1522
+ await ctx.reply(
1523
+ "Something went wrong while verifying the pairing code. Please try again."
1524
+ );
1525
+ return;
1526
+ }
1527
+ if (result.success) {
1528
+ logger3.info(
1529
+ { src: "plugin:telegram:owner-pairing", userId, displayHandle },
1530
+ "Owner pairing completed successfully"
1531
+ );
1532
+ await auditEmit(runtime, "auth.owner.pair.telegram.success", "success", {
1533
+ externalId: userId,
1534
+ displayHandle
1535
+ });
1536
+ await ctx.reply("Paired with Eliza. You can now log in via Telegram.");
1537
+ } else {
1538
+ logger3.warn(
1539
+ {
1540
+ src: "plugin:telegram:owner-pairing",
1541
+ userId,
1542
+ backendError: result.error
1543
+ },
1544
+ "Owner pairing rejected by backend"
1545
+ );
1546
+ await auditEmit(runtime, "auth.owner.pair.telegram.failure", "failure", {
1547
+ externalId: userId
1548
+ });
1549
+ await ctx.reply(
1550
+ "Pair code invalid or expired. Check the Eliza dashboard for a fresh code."
1551
+ );
1552
+ }
1553
+ }
1554
+ var TelegramOwnerPairingServiceImpl = class _TelegramOwnerPairingServiceImpl extends Service {
1555
+ static serviceType = TELEGRAM_OWNER_PAIRING_SERVICE_TYPE;
1556
+ capabilityDescription = "Handles Telegram-side owner pairing (command code verification) and DM login-link delivery for Eliza remote auth";
1557
+ static async start(runtime) {
1558
+ const service = new _TelegramOwnerPairingServiceImpl(runtime);
1559
+ if (resolveVerifyService(runtime)) {
1560
+ service.registerPairCommand(runtime);
1561
+ logger3.info(
1562
+ {
1563
+ src: "plugin:telegram:owner-pairing",
1564
+ agentId: runtime.agentId
1565
+ },
1566
+ "TelegramOwnerPairingService started; /eliza_pair command registered"
1567
+ );
1568
+ } else {
1569
+ logger3.info(
1570
+ {
1571
+ src: "plugin:telegram:owner-pairing",
1572
+ agentId: runtime.agentId
1573
+ },
1574
+ "TelegramOwnerPairingService started without /eliza_pair because OWNER_BIND_VERIFY is not registered"
1575
+ );
1576
+ }
1577
+ return service;
1578
+ }
1579
+ async stop() {
1580
+ pairAttempts.clear();
1581
+ }
1582
+ /**
1583
+ * Registers the /eliza_pair command with the active Telegraf bot instance
1584
+ * by looking up the TelegramService from the runtime service registry.
1585
+ * Called during `start`; it is safe to call this before or after the bot
1586
+ * has finished initialising because Telegraf accepts handler registration
1587
+ * at any point before `launch()`.
1588
+ *
1589
+ * If the TelegramService is unavailable, the command is not registered.
1590
+ */
1591
+ registerPairCommand(runtime) {
1592
+ const telegramSvc = runtime.getService(TELEGRAM_SERVICE_NAME);
1593
+ if (!telegramSvc || typeof telegramSvc !== "object") {
1594
+ logger3.warn(
1595
+ { src: "plugin:telegram:owner-pairing", agentId: runtime.agentId },
1596
+ "TelegramService unavailable during owner-pairing start; /eliza_pair command not registered"
1597
+ );
1598
+ return;
1599
+ }
1600
+ const bot = "bot" in telegramSvc ? telegramSvc.bot : null;
1601
+ if (!bot || typeof bot.command !== "function") {
1602
+ logger3.warn(
1603
+ { src: "plugin:telegram:owner-pairing", agentId: runtime.agentId },
1604
+ "Telegraf bot instance not available \u2014 /eliza_pair will not be registered"
1605
+ );
1606
+ return;
1607
+ }
1608
+ const telegrafBot = bot;
1609
+ telegrafBot.command("eliza_pair", async (ctx) => {
1610
+ await handleElizaPairCommand(ctx, runtime);
1611
+ });
1612
+ logger3.debug(
1613
+ { src: "plugin:telegram:owner-pairing", agentId: runtime.agentId },
1614
+ "/eliza_pair command registered with Telegraf bot"
1615
+ );
1616
+ }
1617
+ async sendOwnerLoginDmLink(params) {
1618
+ const { externalId, link } = params;
1619
+ const telegramSvc = this.runtime.getService(
1620
+ TELEGRAM_SERVICE_NAME
1621
+ );
1622
+ const bot = telegramSvc && typeof telegramSvc === "object" && "bot" in telegramSvc ? telegramSvc.bot : null;
1623
+ if (!bot || typeof bot.telegram !== "object") {
1624
+ throw new Error(
1625
+ "Telegram bot is not available \u2014 cannot send DM login link"
1626
+ );
1627
+ }
1628
+ const telegrafBot = bot;
1629
+ const chatId = Number(externalId);
1630
+ if (!Number.isFinite(chatId) || chatId <= 0) {
1631
+ throw new Error(
1632
+ `Invalid Telegram externalId "${externalId}" \u2014 must be a positive numeric user ID`
1633
+ );
1634
+ }
1635
+ const message = `Click to log in to Eliza: ${link}
1636
+
1637
+ _This link expires in 5 minutes. Do not share it._`;
1638
+ try {
1639
+ await telegrafBot.telegram.sendMessage(chatId, message, {
1640
+ parse_mode: "Markdown"
1641
+ });
1642
+ logger3.info(
1643
+ { src: "plugin:telegram:owner-pairing", externalId },
1644
+ "Login DM link sent"
1645
+ );
1646
+ } catch (err) {
1647
+ throw new Error(
1648
+ `Failed to send DM login link to Telegram user ${externalId}: ${err instanceof Error ? err.message : String(err)}`
1649
+ );
1650
+ }
1651
+ }
1652
+ };
1653
+
1654
+ // src/service.ts
1655
+ import {
1656
+ ChannelType as ChannelType2,
1657
+ createUniqueUuid as createUniqueUuid2,
1658
+ EventType as EventType2,
1659
+ logger as logger4,
1660
+ Role,
1661
+ Service as Service2
1662
+ } from "@elizaos/core";
1663
+ import { Telegraf } from "telegraf";
1664
+ var CANONICAL_OWNER_SETTING_KEYS = ["ELIZA_ADMIN_ENTITY_ID"];
1665
+ function resolveTelegramBotToken(runtime) {
1666
+ const fromRuntime = runtime.getSetting("TELEGRAM_BOT_TOKEN");
1667
+ if (typeof fromRuntime === "string" && fromRuntime.trim()) {
1668
+ return fromRuntime.trim();
1669
+ }
1670
+ const fromEnv = process.env.TELEGRAM_BOT_TOKEN;
1671
+ return typeof fromEnv === "string" && fromEnv.trim() ? fromEnv.trim() : null;
1672
+ }
1673
+ var ACTIVE_TELEGRAM_POLLERS = /* @__PURE__ */ new Map();
1674
+ function getCanonicalOwnerId(runtime) {
1675
+ for (const key of CANONICAL_OWNER_SETTING_KEYS) {
1676
+ const value = runtime.getSetting(key);
1677
+ if (typeof value !== "string") {
1678
+ continue;
1679
+ }
1680
+ const trimmed = value.trim();
1681
+ if (trimmed.length > 0) {
1682
+ return trimmed;
1683
+ }
1684
+ }
1685
+ return null;
1686
+ }
1687
+ function getTelegramChatDisplayName(chat, fallback) {
1688
+ if (!chat) {
1689
+ return fallback;
1690
+ }
1691
+ if ("title" in chat && typeof chat.title === "string" && chat.title.trim()) {
1692
+ return chat.title;
1693
+ }
1694
+ if ("first_name" in chat && typeof chat.first_name === "string" && chat.first_name.trim()) {
1695
+ return chat.first_name;
1696
+ }
1697
+ if ("username" in chat && typeof chat.username === "string" && chat.username.trim()) {
1698
+ return chat.username;
1699
+ }
1700
+ return fallback;
1701
+ }
1702
+ var TelegramService = class _TelegramService extends Service2 {
1703
+ static serviceType = TELEGRAM_SERVICE_NAME;
1704
+ capabilityDescription = "The agent is able to send and receive messages on telegram";
1705
+ bot;
1706
+ messageManager;
1707
+ options;
1708
+ knownChats = /* @__PURE__ */ new Map();
1709
+ syncedEntityIds = /* @__PURE__ */ new Set();
1710
+ botToken;
1711
+ /**
1712
+ * Constructor for TelegramService class.
1713
+ * @param {IAgentRuntime} runtime - The runtime object for the agent.
1714
+ */
1715
+ constructor(runtime) {
1716
+ super(runtime);
1717
+ if (!runtime) {
1718
+ this.bot = null;
1719
+ this.messageManager = null;
1720
+ this.botToken = null;
1721
+ return;
1722
+ }
1723
+ logger4.debug(
1724
+ { src: "plugin:telegram", agentId: runtime.agentId },
1725
+ "Constructing TelegramService"
1726
+ );
1727
+ const botToken = resolveTelegramBotToken(runtime);
1728
+ this.botToken = botToken;
1729
+ if (!botToken) {
1730
+ logger4.warn(
1731
+ { src: "plugin:telegram", agentId: runtime.agentId },
1732
+ "Bot token not provided, Telegram functionality unavailable"
1733
+ );
1734
+ this.bot = null;
1735
+ this.messageManager = null;
1736
+ return;
1737
+ }
1738
+ const configuredApiRoot = runtime.getSetting("TELEGRAM_API_ROOT");
1739
+ const apiRoot = typeof configuredApiRoot === "string" && configuredApiRoot.length > 0 ? configuredApiRoot : process.env.TELEGRAM_API_ROOT || "https://api.telegram.org";
1740
+ this.options = {
1741
+ telegram: { apiRoot }
1742
+ };
1743
+ try {
1744
+ this.bot = new Telegraf(botToken, this.options);
1745
+ this.messageManager = new MessageManager(this.bot, this.runtime);
1746
+ logger4.debug(
1747
+ { src: "plugin:telegram", agentId: runtime.agentId },
1748
+ "TelegramService constructor completed"
1749
+ );
1750
+ } catch (error) {
1751
+ logger4.error(
1752
+ {
1753
+ src: "plugin:telegram",
1754
+ agentId: runtime.agentId,
1755
+ error: error instanceof Error ? error.message : String(error)
1756
+ },
1757
+ "Failed to initialize Telegram bot"
1758
+ );
1759
+ this.bot = null;
1760
+ this.messageManager = null;
1761
+ }
1762
+ }
1763
+ /**
1764
+ * Starts the Telegram service for the given runtime.
1765
+ *
1766
+ * @param {IAgentRuntime} runtime - The agent runtime to start the Telegram service for.
1767
+ * @returns {Promise<TelegramService>} A promise that resolves with the initialized TelegramService.
1768
+ */
1769
+ static async start(runtime) {
1770
+ const service = new _TelegramService(runtime);
1771
+ if (!service.bot) {
1772
+ logger4.warn(
1773
+ { src: "plugin:telegram", agentId: runtime.agentId },
1774
+ "Service started without bot functionality"
1775
+ );
1776
+ return service;
1777
+ }
1778
+ const maxRetries = 5;
1779
+ let retryCount = 0;
1780
+ let lastError = null;
1781
+ while (retryCount < maxRetries) {
1782
+ try {
1783
+ logger4.info(
1784
+ {
1785
+ src: "plugin:telegram",
1786
+ agentId: runtime.agentId,
1787
+ agentName: runtime.character.name
1788
+ },
1789
+ "Starting Telegram bot"
1790
+ );
1791
+ await service.initializeBot();
1792
+ service.setupMiddlewares();
1793
+ service.setupMessageHandlers();
1794
+ const bot = service.bot;
1795
+ if (!bot) {
1796
+ throw new Error("Telegram bot was not initialized");
1797
+ }
1798
+ await bot.telegram.getMe();
1799
+ logger4.success(
1800
+ {
1801
+ src: "plugin:telegram",
1802
+ agentId: runtime.agentId,
1803
+ agentName: runtime.character.name
1804
+ },
1805
+ "Telegram bot started successfully"
1806
+ );
1807
+ return service;
1808
+ } catch (error) {
1809
+ lastError = error instanceof Error ? error : new Error(String(error));
1810
+ logger4.error(
1811
+ {
1812
+ src: "plugin:telegram",
1813
+ agentId: runtime.agentId,
1814
+ attempt: retryCount + 1,
1815
+ error: lastError.message
1816
+ },
1817
+ "Initialization attempt failed"
1818
+ );
1819
+ retryCount++;
1820
+ if (retryCount < maxRetries) {
1821
+ const delay = 2 ** retryCount * 1e3;
1822
+ logger4.info(
1823
+ {
1824
+ src: "plugin:telegram",
1825
+ agentId: runtime.agentId,
1826
+ delaySeconds: delay / 1e3
1827
+ },
1828
+ "Retrying initialization"
1829
+ );
1830
+ await new Promise((resolve) => setTimeout(resolve, delay));
1831
+ }
1832
+ }
1833
+ }
1834
+ logger4.error(
1835
+ {
1836
+ src: "plugin:telegram",
1837
+ agentId: runtime.agentId,
1838
+ maxRetries,
1839
+ error: lastError?.message
1840
+ },
1841
+ "Initialization failed after all attempts"
1842
+ );
1843
+ return service;
1844
+ }
1845
+ /**
1846
+ * Stops the agent runtime.
1847
+ * @param {IAgentRuntime} runtime - The agent runtime to stop
1848
+ */
1849
+ static async stop(runtime) {
1850
+ const tgClient = await runtime.getService(TELEGRAM_SERVICE_NAME);
1851
+ if (tgClient) {
1852
+ await tgClient.stop();
1853
+ }
1854
+ }
1855
+ /**
1856
+ * Asynchronously stops the bot.
1857
+ *
1858
+ * @returns A Promise that resolves once the bot has stopped.
1859
+ */
1860
+ async stop() {
1861
+ const bot = this.bot;
1862
+ if (!bot) {
1863
+ return;
1864
+ }
1865
+ bot.stop("service-stop");
1866
+ if (this.botToken) {
1867
+ const active = ACTIVE_TELEGRAM_POLLERS.get(this.botToken);
1868
+ if (active?.bot === bot) {
1869
+ ACTIVE_TELEGRAM_POLLERS.delete(this.botToken);
1870
+ }
1871
+ }
1872
+ }
1873
+ /**
1874
+ * Initializes the Telegram bot by launching it, getting bot info, and setting up message manager.
1875
+ * @returns {Promise<void>} A Promise that resolves when the initialization is complete.
1876
+ */
1877
+ async initializeBot() {
1878
+ const bot = this.bot;
1879
+ if (!bot) {
1880
+ throw new Error("Telegram bot is not initialized");
1881
+ }
1882
+ const botToken = this.botToken;
1883
+ if (botToken) {
1884
+ const active = ACTIVE_TELEGRAM_POLLERS.get(botToken);
1885
+ if (active && active.bot !== bot) {
1886
+ logger4.warn(
1887
+ {
1888
+ src: "plugin:telegram",
1889
+ agentId: this.runtime.agentId,
1890
+ previousAgentId: active.agentId
1891
+ },
1892
+ "Stopping existing Telegram poller before launching a new one"
1893
+ );
1894
+ try {
1895
+ active.bot.stop("replaced-by-new-runtime");
1896
+ } catch (error) {
1897
+ logger4.warn(
1898
+ {
1899
+ src: "plugin:telegram",
1900
+ agentId: this.runtime.agentId,
1901
+ error: error instanceof Error ? error.message : String(error)
1902
+ },
1903
+ "Failed to stop previous Telegram poller cleanly"
1904
+ );
1905
+ }
1906
+ ACTIVE_TELEGRAM_POLLERS.delete(botToken);
1907
+ await new Promise((resolve) => setTimeout(resolve, 300));
1908
+ }
1909
+ }
1910
+ bot.start((ctx) => {
1911
+ const slashStartPayload = {
1912
+ ctx,
1913
+ runtime: this.runtime,
1914
+ source: "telegram"
1915
+ };
1916
+ this.runtime.emitEvent(
1917
+ "TELEGRAM_SLASH_START" /* SLASH_START */,
1918
+ slashStartPayload
1919
+ );
1920
+ });
1921
+ await bot.launch({
1922
+ dropPendingUpdates: true,
1923
+ allowedUpdates: ["message", "message_reaction"]
1924
+ });
1925
+ if (botToken) {
1926
+ ACTIVE_TELEGRAM_POLLERS.set(botToken, {
1927
+ bot,
1928
+ agentId: this.runtime.agentId
1929
+ });
1930
+ }
1931
+ const botInfo = await bot.telegram.getMe();
1932
+ logger4.debug(
1933
+ {
1934
+ src: "plugin:telegram",
1935
+ agentId: this.runtime.agentId,
1936
+ botId: botInfo.id,
1937
+ botUsername: botInfo.username
1938
+ },
1939
+ "Bot info retrieved"
1940
+ );
1941
+ process.once("SIGINT", () => bot.stop("SIGINT"));
1942
+ process.once("SIGTERM", () => bot.stop("SIGTERM"));
1943
+ }
1944
+ /**
1945
+ * Sets up the middleware chain for preprocessing messages before they reach handlers.
1946
+ * This critical method establishes a sequential processing pipeline that:
1947
+ *
1948
+ * 1. Authorization - Verifies if a chat is allowed to interact with the bot based on configured settings
1949
+ * 2. Chat Discovery - Ensures chat entities and worlds exist in the runtime, creating them if needed
1950
+ * 3. Forum Topics - Handles Telegram forum topics as separate rooms for better conversation management
1951
+ * 4. Entity Synchronization - Ensures message senders are properly synchronized as entities
1952
+ *
1953
+ * The middleware chain runs in sequence for each message, with each step potentially
1954
+ * enriching the context or stopping processing if conditions aren't met.
1955
+ * This preprocessing is essential for maintaining consistent state before message handlers execute.
1956
+ *
1957
+ * @private
1958
+ */
1959
+ setupMiddlewares() {
1960
+ this.bot?.use(this.authorizationMiddleware.bind(this));
1961
+ this.bot?.use(this.chatAndEntityMiddleware.bind(this));
1962
+ }
1963
+ /**
1964
+ * Authorization middleware - checks if chat is allowed to interact with the bot
1965
+ * based on the TELEGRAM_ALLOWED_CHATS configuration.
1966
+ *
1967
+ * @param {Context} ctx - The context of the incoming update
1968
+ * @param {Function} next - The function to call to proceed to the next middleware
1969
+ * @returns {Promise<void>}
1970
+ * @private
1971
+ */
1972
+ async authorizationMiddleware(ctx, next) {
1973
+ if (!await this.isGroupAuthorized(ctx)) {
1974
+ logger4.debug(
1975
+ {
1976
+ src: "plugin:telegram",
1977
+ agentId: this.runtime.agentId,
1978
+ chatId: ctx.chat?.id
1979
+ },
1980
+ "Chat not authorized, skipping"
1981
+ );
1982
+ return;
1983
+ }
1984
+ await next();
1985
+ }
1986
+ /**
1987
+ * Chat and entity management middleware - handles new chats, forum topics, and entity synchronization.
1988
+ * This middleware implements decision logic to determine which operations are needed based on
1989
+ * the chat type and whether we've seen this chat before.
1990
+ *
1991
+ * @param {Context} ctx - The context of the incoming update
1992
+ * @param {Function} next - The function to call to proceed to the next middleware
1993
+ * @returns {Promise<void>}
1994
+ * @private
1995
+ */
1996
+ async chatAndEntityMiddleware(ctx, next) {
1997
+ if (!ctx.chat) {
1998
+ return next();
1999
+ }
2000
+ const chatId = ctx.chat.id.toString();
2001
+ if (!this.knownChats.has(chatId)) {
2002
+ await this.handleNewChat(ctx);
2003
+ return next();
2004
+ }
2005
+ await this.processExistingChat(ctx);
2006
+ await next();
2007
+ }
2008
+ /**
2009
+ * Process an existing chat based on chat type and message properties.
2010
+ * Different chat types require different processing steps.
2011
+ *
2012
+ * @param {Context} ctx - The context of the incoming update
2013
+ * @returns {Promise<void>}
2014
+ * @private
2015
+ */
2016
+ async processExistingChat(ctx) {
2017
+ if (!ctx.chat) {
2018
+ return;
2019
+ }
2020
+ const chat = ctx.chat;
2021
+ if (chat.type === "supergroup" && chat.is_forum && ctx.message?.message_thread_id) {
2022
+ try {
2023
+ await this.handleForumTopic(ctx);
2024
+ } catch (error) {
2025
+ logger4.error(
2026
+ {
2027
+ src: "plugin:telegram",
2028
+ agentId: this.runtime.agentId,
2029
+ chatId: chat.id,
2030
+ error: error instanceof Error ? error.message : String(error)
2031
+ },
2032
+ "Error handling forum topic"
2033
+ );
2034
+ }
2035
+ }
2036
+ if (ctx.from && ctx.chat.type !== "private") {
2037
+ await this.syncEntity(ctx);
2038
+ }
2039
+ }
2040
+ /**
2041
+ * Sets up message and reaction handlers for the bot.
2042
+ * Configures event handlers to process incoming messages and reactions.
2043
+ *
2044
+ * @private
2045
+ */
2046
+ setupMessageHandlers() {
2047
+ this.bot?.on("message", async (ctx) => {
2048
+ try {
2049
+ await this.messageManager?.handleMessage(ctx);
2050
+ } catch (error) {
2051
+ logger4.error(
2052
+ {
2053
+ src: "plugin:telegram",
2054
+ agentId: this.runtime.agentId,
2055
+ error: error instanceof Error ? error.message : String(error)
2056
+ },
2057
+ "Error handling message"
2058
+ );
2059
+ }
2060
+ });
2061
+ this.bot?.on("message_reaction", async (ctx) => {
2062
+ try {
2063
+ await this.messageManager?.handleReaction(ctx);
2064
+ } catch (error) {
2065
+ logger4.error(
2066
+ {
2067
+ src: "plugin:telegram",
2068
+ agentId: this.runtime.agentId,
2069
+ error: error instanceof Error ? error.message : String(error)
2070
+ },
2071
+ "Error handling reaction"
2072
+ );
2073
+ }
2074
+ });
2075
+ }
2076
+ /**
2077
+ * Checks if a group is authorized, based on the TELEGRAM_ALLOWED_CHATS setting.
2078
+ * @param {Context} ctx - The context of the incoming update.
2079
+ * @returns {Promise<boolean>} A Promise that resolves with a boolean indicating if the group is authorized.
2080
+ */
2081
+ async isGroupAuthorized(ctx) {
2082
+ const chatId = ctx.chat?.id.toString();
2083
+ if (!chatId) {
2084
+ return false;
2085
+ }
2086
+ const allowedChats = this.runtime.getSetting("TELEGRAM_ALLOWED_CHATS");
2087
+ if (!allowedChats) {
2088
+ return true;
2089
+ }
2090
+ try {
2091
+ const allowedChatsList = JSON.parse(allowedChats);
2092
+ return allowedChatsList.includes(chatId);
2093
+ } catch (error) {
2094
+ logger4.error(
2095
+ {
2096
+ src: "plugin:telegram",
2097
+ agentId: this.runtime.agentId,
2098
+ error: error instanceof Error ? error.message : String(error)
2099
+ },
2100
+ "Error parsing TELEGRAM_ALLOWED_CHATS"
2101
+ );
2102
+ return false;
2103
+ }
2104
+ }
2105
+ /**
2106
+ * Synchronizes an entity from a message context with the runtime system.
2107
+ * This method handles three cases:
2108
+ * 1. Message sender - most common case
2109
+ * 2. New chat member - when a user joins the chat
2110
+ * 3. Left chat member - when a user leaves the chat
2111
+ *
2112
+ * @param {Context} ctx - The context of the incoming update
2113
+ * @returns {Promise<void>}
2114
+ * @private
2115
+ */
2116
+ async syncEntity(ctx) {
2117
+ if (!ctx.chat) {
2118
+ return;
2119
+ }
2120
+ const chat = ctx.chat;
2121
+ const chatId = chat.id.toString();
2122
+ const worldId = createUniqueUuid2(this.runtime, chatId);
2123
+ const roomId = createUniqueUuid2(
2124
+ this.runtime,
2125
+ ctx.message?.message_thread_id ? `${ctx.chat.id}-${ctx.message.message_thread_id}` : ctx.chat.id.toString()
2126
+ );
2127
+ await this.syncMessageSender(ctx, worldId, roomId, chatId);
2128
+ await this.syncNewChatMember(ctx, worldId, roomId, chatId);
2129
+ await this.syncLeftChatMember(ctx);
2130
+ }
2131
+ /**
2132
+ * Synchronizes the message sender entity with the runtime system.
2133
+ * This is the most common entity sync case.
2134
+ *
2135
+ * @param {Context} ctx - The context of the incoming update
2136
+ * @param {UUID} worldId - The ID of the world
2137
+ * @param {UUID} roomId - The ID of the room
2138
+ * @param {string} chatId - The ID of the chat
2139
+ * @returns {Promise<void>}
2140
+ * @private
2141
+ */
2142
+ async syncMessageSender(ctx, worldId, roomId, chatId) {
2143
+ if (ctx.from) {
2144
+ const telegramId = ctx.from.id.toString();
2145
+ const entityId = createUniqueUuid2(this.runtime, telegramId);
2146
+ if (this.syncedEntityIds.has(entityId)) {
2147
+ return;
2148
+ }
2149
+ await this.runtime.ensureConnection({
2150
+ entityId,
2151
+ roomId,
2152
+ roomName: getTelegramChatDisplayName(ctx.chat, chatId),
2153
+ userName: ctx.from.username,
2154
+ userId: telegramId,
2155
+ name: ctx.from.first_name || ctx.from.username || "Unknown User",
2156
+ source: "telegram",
2157
+ channelId: chatId,
2158
+ type: ChannelType2.GROUP,
2159
+ worldId
2160
+ });
2161
+ this.syncedEntityIds.add(entityId);
2162
+ }
2163
+ }
2164
+ /**
2165
+ * Synchronizes a new chat member entity with the runtime system.
2166
+ * Triggered when a user joins the chat.
2167
+ *
2168
+ * @param {Context} ctx - The context of the incoming update
2169
+ * @param {UUID} worldId - The ID of the world
2170
+ * @param {UUID} roomId - The ID of the room
2171
+ * @param {string} chatId - The ID of the chat
2172
+ * @returns {Promise<void>}
2173
+ * @private
2174
+ */
2175
+ async syncNewChatMember(ctx, worldId, roomId, chatId) {
2176
+ if (ctx.message && "new_chat_members" in ctx.message) {
2177
+ for (const newMember of ctx.message.new_chat_members) {
2178
+ const telegramId = newMember.id.toString();
2179
+ const entityId = createUniqueUuid2(this.runtime, telegramId);
2180
+ if (this.syncedEntityIds.has(entityId)) {
2181
+ continue;
2182
+ }
2183
+ await this.runtime.ensureConnection({
2184
+ entityId,
2185
+ roomId,
2186
+ roomName: getTelegramChatDisplayName(ctx.chat, chatId),
2187
+ userName: newMember.username,
2188
+ userId: telegramId,
2189
+ name: newMember.first_name || newMember.username || "Unknown User",
2190
+ source: "telegram",
2191
+ channelId: chatId,
2192
+ type: ChannelType2.GROUP,
2193
+ worldId
2194
+ });
2195
+ this.syncedEntityIds.add(entityId);
2196
+ const entityJoinedPayload = {
2197
+ runtime: this.runtime,
2198
+ entityId,
2199
+ worldId,
2200
+ source: "telegram",
2201
+ telegramUser: {
2202
+ id: newMember.id,
2203
+ username: newMember.username,
2204
+ first_name: newMember.first_name
2205
+ }
2206
+ };
2207
+ this.runtime.emitEvent(
2208
+ "TELEGRAM_ENTITY_JOINED" /* ENTITY_JOINED */,
2209
+ entityJoinedPayload
2210
+ );
2211
+ }
2212
+ }
2213
+ }
2214
+ /**
2215
+ * Updates entity status when a user leaves the chat.
2216
+ *
2217
+ * @param {Context} ctx - The context of the incoming update
2218
+ * @returns {Promise<void>}
2219
+ * @private
2220
+ */
2221
+ async syncLeftChatMember(ctx) {
2222
+ if (ctx.message && "left_chat_member" in ctx.message) {
2223
+ const leftMember = ctx.message.left_chat_member;
2224
+ const telegramId = leftMember.id.toString();
2225
+ const entityId = createUniqueUuid2(this.runtime, telegramId);
2226
+ const existingEntity = await this.runtime.getEntityById(entityId);
2227
+ if (existingEntity) {
2228
+ existingEntity.metadata = {
2229
+ ...existingEntity.metadata,
2230
+ status: "INACTIVE",
2231
+ leftAt: Date.now()
2232
+ };
2233
+ await this.runtime.updateEntity(existingEntity);
2234
+ }
2235
+ }
2236
+ }
2237
+ /**
2238
+ * Handles forum topics by creating appropriate rooms in the runtime system.
2239
+ * This enables proper conversation management for Telegram's forum feature.
2240
+ *
2241
+ * @param {Context} ctx - The context of the incoming update
2242
+ * @returns {Promise<void>}
2243
+ * @private
2244
+ */
2245
+ async handleForumTopic(ctx) {
2246
+ if (!ctx.chat || !ctx.message?.message_thread_id) {
2247
+ return;
2248
+ }
2249
+ const chat = ctx.chat;
2250
+ const chatId = chat.id.toString();
2251
+ const worldId = createUniqueUuid2(this.runtime, chatId);
2252
+ const room = await this.buildForumTopicRoom(ctx, worldId);
2253
+ if (!room) {
2254
+ return;
2255
+ }
2256
+ await this.runtime.ensureRoomExists(room);
2257
+ }
2258
+ /**
2259
+ * Builds entity for message sender
2260
+ */
2261
+ buildMsgSenderEntity(from) {
2262
+ if (!from) {
2263
+ return null;
2264
+ }
2265
+ const userId = createUniqueUuid2(this.runtime, from.id.toString());
2266
+ const telegramId = from.id.toString();
2267
+ return {
2268
+ id: userId,
2269
+ agentId: this.runtime.agentId,
2270
+ names: [from.first_name || from.username || "Unknown User"],
2271
+ metadata: {
2272
+ telegram: {
2273
+ id: telegramId,
2274
+ username: from.username,
2275
+ name: from.first_name || from.username || "Unknown User"
2276
+ }
2277
+ }
2278
+ };
2279
+ }
2280
+ /**
2281
+ * Handles new chat discovery and emits WORLD_JOINED event.
2282
+ * This is a critical function that ensures new chats are properly
2283
+ * registered in the runtime system and appropriate events are emitted.
2284
+ *
2285
+ * @param {Context} ctx - The context of the incoming update
2286
+ * @returns {Promise<void>}
2287
+ * @private
2288
+ */
2289
+ async handleNewChat(ctx) {
2290
+ if (!ctx.chat) {
2291
+ return;
2292
+ }
2293
+ const chat = ctx.chat;
2294
+ const chatId = chat.id.toString();
2295
+ this.knownChats.set(chatId, chat);
2296
+ const { chatTitle, channelType } = this.getChatTypeInfo(chat);
2297
+ const worldId = createUniqueUuid2(this.runtime, chatId);
2298
+ const existingWorld = await this.runtime.getWorld(worldId);
2299
+ if (existingWorld) {
2300
+ return;
2301
+ }
2302
+ const userId = ctx.from ? createUniqueUuid2(this.runtime, ctx.from.id.toString()) : null;
2303
+ let admins = [];
2304
+ let owner = null;
2305
+ if (chat.type === "group" || chat.type === "supergroup" || chat.type === "channel") {
2306
+ try {
2307
+ const chatAdmins = await ctx.getChatAdministrators();
2308
+ admins = chatAdmins;
2309
+ const foundOwner = admins.find(
2310
+ (admin) => admin.status === "creator"
2311
+ );
2312
+ owner = foundOwner || null;
2313
+ } catch (error) {
2314
+ logger4.warn(
2315
+ {
2316
+ src: "plugin:telegram",
2317
+ agentId: this.runtime.agentId,
2318
+ chatId,
2319
+ error: error instanceof Error ? error.message : String(error)
2320
+ },
2321
+ "Could not get chat administrators"
2322
+ );
2323
+ }
2324
+ }
2325
+ const canonicalOwnerId = getCanonicalOwnerId(this.runtime);
2326
+ let ownerId = canonicalOwnerId ?? userId;
2327
+ if (!canonicalOwnerId && owner) {
2328
+ ownerId = createUniqueUuid2(this.runtime, String(owner.user.id));
2329
+ }
2330
+ const world = {
2331
+ id: worldId,
2332
+ name: chatTitle,
2333
+ agentId: this.runtime.agentId,
2334
+ messageServerId: chatId,
2335
+ metadata: {
2336
+ source: "telegram",
2337
+ ...ownerId && { ownership: { ownerId } },
2338
+ roles: ownerId ? {
2339
+ [ownerId]: Role.OWNER
2340
+ } : {},
2341
+ chatType: chat.type,
2342
+ isForumEnabled: chat.type === "supergroup" && chat.is_forum
2343
+ }
2344
+ };
2345
+ await this.runtime.ensureWorldExists(world);
2346
+ const generalRoom = {
2347
+ id: createUniqueUuid2(this.runtime, chatId),
2348
+ name: chatTitle,
2349
+ source: "telegram",
2350
+ type: channelType,
2351
+ channelId: chatId,
2352
+ serverId: chatId,
2353
+ worldId
2354
+ };
2355
+ await this.runtime.ensureRoomExists(generalRoom);
2356
+ const rooms = [generalRoom];
2357
+ if (chat.type === "supergroup" && chat.is_forum && ctx.message?.message_thread_id) {
2358
+ const topicRoom = await this.buildForumTopicRoom(ctx, worldId);
2359
+ if (topicRoom) {
2360
+ rooms.push(topicRoom);
2361
+ await this.runtime.ensureRoomExists(topicRoom);
2362
+ }
2363
+ }
2364
+ const entities = await this.buildStandardizedEntities(chat);
2365
+ if (ctx.from) {
2366
+ const senderEntity = this.buildMsgSenderEntity(ctx.from);
2367
+ if (senderEntity?.id && !entities.some((e) => e.id === senderEntity.id)) {
2368
+ entities.push(senderEntity);
2369
+ this.syncedEntityIds.add(senderEntity.id);
2370
+ }
2371
+ }
2372
+ await this.batchProcessEntities(
2373
+ entities,
2374
+ generalRoom.id,
2375
+ generalRoom.name || generalRoom.channelId || chatId,
2376
+ generalRoom.channelId || chatId,
2377
+ generalRoom.type,
2378
+ worldId
2379
+ );
2380
+ const telegramWorldPayload = {
2381
+ runtime: this.runtime,
2382
+ world,
2383
+ rooms,
2384
+ entities,
2385
+ source: "telegram",
2386
+ chat,
2387
+ botUsername: this.bot?.botInfo?.username
2388
+ };
2389
+ if (chat.type !== "private") {
2390
+ await this.runtime.emitEvent(
2391
+ "TELEGRAM_WORLD_JOINED" /* WORLD_JOINED */,
2392
+ telegramWorldPayload
2393
+ );
2394
+ }
2395
+ await this.runtime.emitEvent(EventType2.WORLD_JOINED, {
2396
+ runtime: this.runtime,
2397
+ world,
2398
+ rooms,
2399
+ entities,
2400
+ source: "telegram"
2401
+ });
2402
+ }
2403
+ /**
2404
+ * Processes entities in batches to prevent overwhelming the system.
2405
+ *
2406
+ * @param {Entity[]} entities - The entities to process
2407
+ * @param {UUID} roomId - The ID of the room to connect entities to
2408
+ * @param {string} channelId - The channel ID
2409
+ * @param {ChannelType} roomType - The type of the room
2410
+ * @param {UUID} worldId - The ID of the world
2411
+ * @returns {Promise<void>}
2412
+ * @private
2413
+ */
2414
+ async batchProcessEntities(entities, roomId, roomName, channelId, roomType, worldId) {
2415
+ const batchSize = 50;
2416
+ for (let i = 0; i < entities.length; i += batchSize) {
2417
+ const entityBatch = entities.slice(i, i + batchSize);
2418
+ await Promise.all(
2419
+ entityBatch.map(async (entity) => {
2420
+ try {
2421
+ if (entity.id) {
2422
+ const telegramMetadata = entity.metadata?.telegram;
2423
+ await this.runtime.ensureConnection({
2424
+ entityId: entity.id,
2425
+ roomId,
2426
+ roomName,
2427
+ userName: telegramMetadata?.username,
2428
+ name: telegramMetadata?.name,
2429
+ userId: telegramMetadata?.id,
2430
+ source: "telegram",
2431
+ channelId,
2432
+ type: roomType,
2433
+ worldId
2434
+ });
2435
+ } else {
2436
+ logger4.warn(
2437
+ {
2438
+ src: "plugin:telegram",
2439
+ agentId: this.runtime.agentId,
2440
+ entityNames: entity.names
2441
+ },
2442
+ "Skipping entity sync due to missing ID"
2443
+ );
2444
+ }
2445
+ } catch (err) {
2446
+ const telegramMetadata = entity.metadata?.telegram;
2447
+ logger4.warn(
2448
+ {
2449
+ src: "plugin:telegram",
2450
+ agentId: this.runtime.agentId,
2451
+ username: telegramMetadata?.username,
2452
+ error: err instanceof Error ? err.message : String(err)
2453
+ },
2454
+ "Failed to sync user"
2455
+ );
2456
+ }
2457
+ })
2458
+ );
2459
+ if (i + batchSize < entities.length) {
2460
+ await new Promise((resolve) => setTimeout(resolve, 500));
2461
+ }
2462
+ }
2463
+ }
2464
+ /**
2465
+ * Gets chat title and channel type based on Telegram chat type.
2466
+ * Maps Telegram-specific chat types to standardized system types.
2467
+ *
2468
+ * @param {any} chat - The Telegram chat object
2469
+ * @returns {Object} Object containing chatTitle and channelType
2470
+ * @private
2471
+ */
2472
+ getChatTypeInfo(chat) {
2473
+ const chatType = chat.type;
2474
+ let chatTitle;
2475
+ let channelType;
2476
+ switch (chatType) {
2477
+ case "private":
2478
+ chatTitle = `Chat with ${chat.first_name || "Unknown User"}`;
2479
+ channelType = ChannelType2.DM;
2480
+ break;
2481
+ case "group":
2482
+ chatTitle = chat.title || "Unknown Group";
2483
+ channelType = ChannelType2.GROUP;
2484
+ break;
2485
+ case "supergroup":
2486
+ chatTitle = chat.title || "Unknown Supergroup";
2487
+ channelType = ChannelType2.GROUP;
2488
+ break;
2489
+ case "channel":
2490
+ chatTitle = chat.title || "Unknown Channel";
2491
+ channelType = ChannelType2.FEED;
2492
+ break;
2493
+ default:
2494
+ throw new Error(`Unrecognized Telegram chat type: ${String(chatType)}`);
2495
+ }
2496
+ return { chatTitle, channelType };
2497
+ }
2498
+ /**
2499
+ * Builds standardized entity representations from Telegram chat data.
2500
+ * Transforms Telegram-specific user data into system-standard Entity objects.
2501
+ *
2502
+ * @param {any} chat - The Telegram chat object
2503
+ * @returns {Promise<Entity[]>} Array of standardized Entity objects
2504
+ * @private
2505
+ */
2506
+ async buildStandardizedEntities(chat) {
2507
+ const entities = [];
2508
+ try {
2509
+ if (chat.type === "private" && chat.id) {
2510
+ const userId = createUniqueUuid2(
2511
+ this.runtime,
2512
+ chat.id.toString()
2513
+ );
2514
+ entities.push({
2515
+ id: userId,
2516
+ names: [chat.first_name || "Unknown User"],
2517
+ agentId: this.runtime.agentId,
2518
+ metadata: {
2519
+ telegram: {
2520
+ id: chat.id.toString(),
2521
+ username: chat.username || "unknown",
2522
+ name: chat.first_name || "Unknown User"
2523
+ },
2524
+ source: "telegram"
2525
+ }
2526
+ });
2527
+ this.syncedEntityIds.add(userId);
2528
+ } else if (chat.type === "group" || chat.type === "supergroup") {
2529
+ try {
2530
+ const admins = await this.bot?.telegram.getChatAdministrators(
2531
+ chat.id
2532
+ );
2533
+ if (admins && admins.length > 0) {
2534
+ for (const admin of admins) {
2535
+ const userId = createUniqueUuid2(
2536
+ this.runtime,
2537
+ admin.user.id.toString()
2538
+ );
2539
+ entities.push({
2540
+ id: userId,
2541
+ names: [
2542
+ admin.user.first_name || admin.user.username || "Unknown Admin"
2543
+ ],
2544
+ agentId: this.runtime.agentId,
2545
+ metadata: {
2546
+ telegram: {
2547
+ id: admin.user.id.toString(),
2548
+ username: admin.user.username || "unknown",
2549
+ name: admin.user.first_name || "Unknown Admin",
2550
+ isAdmin: true,
2551
+ adminTitle: admin.custom_title || (admin.status === "creator" ? "Owner" : "Admin")
2552
+ },
2553
+ source: "telegram",
2554
+ roles: [admin.status === "creator" ? Role.OWNER : Role.ADMIN]
2555
+ }
2556
+ });
2557
+ this.syncedEntityIds.add(userId);
2558
+ }
2559
+ }
2560
+ } catch (error) {
2561
+ logger4.warn(
2562
+ {
2563
+ src: "plugin:telegram",
2564
+ agentId: this.runtime.agentId,
2565
+ chatId: chat.id,
2566
+ error: error instanceof Error ? error.message : String(error)
2567
+ },
2568
+ "Could not fetch administrators"
2569
+ );
2570
+ }
2571
+ }
2572
+ } catch (error) {
2573
+ logger4.error(
2574
+ {
2575
+ src: "plugin:telegram",
2576
+ agentId: this.runtime.agentId,
2577
+ error: error instanceof Error ? error.message : String(error)
2578
+ },
2579
+ "Error building standardized entities"
2580
+ );
2581
+ }
2582
+ return entities;
2583
+ }
2584
+ /**
2585
+ * Extracts and builds the room object for a forum topic from a message context.
2586
+ * This refactored method can be used both in middleware and when handling new chats.
2587
+ *
2588
+ * @param {Context} ctx - The context of the incoming update
2589
+ * @param {UUID} worldId - The ID of the world the topic belongs to
2590
+ * @returns {Promise<Room | null>} A Promise that resolves with the room or null if not a topic
2591
+ * @private
2592
+ */
2593
+ async buildForumTopicRoom(ctx, worldId) {
2594
+ if (!ctx.chat || !ctx.message?.message_thread_id) {
2595
+ return null;
2596
+ }
2597
+ if (ctx.chat.type !== "supergroup" || !ctx.chat.is_forum) {
2598
+ return null;
2599
+ }
2600
+ const chat = ctx.chat;
2601
+ const chatId = chat.id.toString();
2602
+ const threadId = ctx.message.message_thread_id.toString();
2603
+ const roomId = createUniqueUuid2(
2604
+ this.runtime,
2605
+ `${chatId}-${threadId}`
2606
+ );
2607
+ try {
2608
+ const replyMessage = JSON.parse(JSON.stringify(ctx.message));
2609
+ let topicName = `Topic #${threadId}`;
2610
+ if (replyMessage && typeof replyMessage === "object" && "forum_topic_created" in replyMessage && replyMessage.forum_topic_created) {
2611
+ const topicCreated = replyMessage.forum_topic_created;
2612
+ if (topicCreated && typeof topicCreated === "object" && "name" in topicCreated) {
2613
+ topicName = topicCreated.name;
2614
+ }
2615
+ } else if (replyMessage && typeof replyMessage === "object" && "reply_to_message" in replyMessage && replyMessage.reply_to_message && typeof replyMessage.reply_to_message === "object" && "forum_topic_created" in replyMessage.reply_to_message && replyMessage.reply_to_message.forum_topic_created) {
2616
+ const topicCreated = replyMessage.reply_to_message.forum_topic_created;
2617
+ if (topicCreated && typeof topicCreated === "object" && "name" in topicCreated) {
2618
+ topicName = topicCreated.name;
2619
+ }
2620
+ }
2621
+ const room = {
2622
+ id: roomId,
2623
+ name: topicName,
2624
+ source: "telegram",
2625
+ type: ChannelType2.GROUP,
2626
+ channelId: `${chatId}-${threadId}`,
2627
+ serverId: chatId,
2628
+ worldId,
2629
+ metadata: {
2630
+ threadId,
2631
+ isForumTopic: true,
2632
+ parentChatId: chatId
2633
+ }
2634
+ };
2635
+ return room;
2636
+ } catch (error) {
2637
+ logger4.error(
2638
+ {
2639
+ src: "plugin:telegram",
2640
+ agentId: this.runtime.agentId,
2641
+ chatId,
2642
+ threadId,
2643
+ error: error instanceof Error ? error.message : String(error)
2644
+ },
2645
+ "Error building forum topic room"
2646
+ );
2647
+ return null;
2648
+ }
2649
+ }
2650
+ static registerSendHandlers(runtime, serviceInstance) {
2651
+ if (serviceInstance?.bot) {
2652
+ runtime.registerSendHandler(
2653
+ "telegram",
2654
+ serviceInstance.handleSendMessage.bind(serviceInstance)
2655
+ );
2656
+ logger4.info(
2657
+ { src: "plugin:telegram", agentId: runtime.agentId },
2658
+ "Registered send handler"
2659
+ );
2660
+ } else {
2661
+ logger4.warn(
2662
+ { src: "plugin:telegram", agentId: runtime.agentId },
2663
+ "Cannot register send handler, bot not initialized"
2664
+ );
2665
+ }
2666
+ }
2667
+ async handleSendMessage(runtime, target, content) {
2668
+ if (!this.bot || !this.messageManager) {
2669
+ logger4.error(
2670
+ { src: "plugin:telegram", agentId: runtime.agentId },
2671
+ "Bot not initialized, cannot send messages"
2672
+ );
2673
+ throw new Error(
2674
+ "Telegram bot is not initialized. Please provide TELEGRAM_BOT_TOKEN."
2675
+ );
2676
+ }
2677
+ let chatId;
2678
+ if (target.channelId) {
2679
+ chatId = target.channelId;
2680
+ } else if (target.roomId) {
2681
+ const room = await runtime.getRoom(target.roomId);
2682
+ chatId = room?.channelId;
2683
+ if (!chatId) {
2684
+ throw new Error(
2685
+ `Could not resolve Telegram chat ID from roomId ${target.roomId}`
2686
+ );
2687
+ }
2688
+ } else if (target.entityId) {
2689
+ const entity = await runtime.getEntityById(target.entityId);
2690
+ if (!entity) {
2691
+ throw new Error(`Entity ${target.entityId} not found`);
2692
+ }
2693
+ const telegramMeta = entity.metadata?.telegram;
2694
+ const telegramId = telegramMeta?.id;
2695
+ if (!telegramId) {
2696
+ logger4.error(
2697
+ {
2698
+ src: "plugin:telegram",
2699
+ agentId: runtime.agentId,
2700
+ entityId: target.entityId
2701
+ },
2702
+ "Entity has no telegram.id in metadata \u2014 cannot send DM without Telegram user ID"
2703
+ );
2704
+ throw new Error(
2705
+ `Entity ${target.entityId} has no telegram.id in metadata \u2014 cannot send DM without Telegram user ID`
2706
+ );
2707
+ }
2708
+ chatId = telegramId;
2709
+ } else {
2710
+ throw new Error(
2711
+ "Telegram SendHandler requires channelId, roomId, or entityId."
2712
+ );
2713
+ }
2714
+ if (!chatId) {
2715
+ throw new Error(
2716
+ `Could not determine target Telegram chat ID for target: ${JSON.stringify(target)}`
2717
+ );
2718
+ }
2719
+ try {
2720
+ await this.messageManager.sendMessage(chatId, content);
2721
+ logger4.info(
2722
+ { src: "plugin:telegram", agentId: runtime.agentId, chatId },
2723
+ "Message sent"
2724
+ );
2725
+ } catch (error) {
2726
+ logger4.error(
2727
+ {
2728
+ src: "plugin:telegram",
2729
+ agentId: runtime.agentId,
2730
+ chatId,
2731
+ error: error instanceof Error ? error.message : String(error)
2732
+ },
2733
+ "Error sending message"
2734
+ );
2735
+ throw error;
2736
+ }
2737
+ }
2738
+ };
2739
+
2740
+ // src/setup-routes.ts
2741
+ import { logger as logger5 } from "@elizaos/core";
2742
+ var TELEGRAM_API_BASE = "https://api.telegram.org";
2743
+ function getSetupService2(runtime) {
2744
+ return runtime.getService("connector-setup");
2745
+ }
2746
+ async function readJsonBody(req) {
2747
+ return req.body ?? null;
2748
+ }
2749
+ async function handleValidateToken(req, res, runtime) {
2750
+ const body = await readJsonBody(req);
2751
+ const token = typeof body?.token === "string" ? body.token.trim() : "";
2752
+ if (!token) {
2753
+ res.status(200).json({ ok: false, error: "token is required" });
2754
+ return;
2755
+ }
2756
+ if (!/^\d+:[A-Za-z0-9_-]{30,}$/.test(token)) {
2757
+ res.status(200).json({
2758
+ ok: false,
2759
+ error: "Token format invalid. Expected format: 123456:ABC-DEF..."
2760
+ });
2761
+ return;
2762
+ }
2763
+ try {
2764
+ const apiRes = await fetch(`${TELEGRAM_API_BASE}/bot${token}/getMe`, {
2765
+ signal: AbortSignal.timeout(1e4)
2766
+ });
2767
+ if (!apiRes.ok) {
2768
+ res.status(200).json({
2769
+ ok: false,
2770
+ error: `Telegram API returned ${apiRes.status}. Check that the token is correct.`
2771
+ });
2772
+ return;
2773
+ }
2774
+ const data = await apiRes.json();
2775
+ if (!data.ok || !data.result) {
2776
+ res.status(200).json({
2777
+ ok: false,
2778
+ error: "Telegram API returned unexpected response"
2779
+ });
2780
+ return;
2781
+ }
2782
+ const bot = data.result;
2783
+ const setupService = getSetupService2(runtime);
2784
+ if (setupService) {
2785
+ setupService.updateConfig((config) => {
2786
+ if (!config.connectors) {
2787
+ config.connectors = {};
2788
+ }
2789
+ const connectors = config.connectors;
2790
+ if (!connectors.telegram || typeof connectors.telegram !== "object") {
2791
+ connectors.telegram = {};
2792
+ }
2793
+ connectors.telegram.botToken = token;
2794
+ });
2795
+ setupService.setOwnerContact({
2796
+ source: "telegram",
2797
+ channelId: String(bot.id)
2798
+ });
2799
+ setupService.registerEscalationChannel("telegram");
2800
+ } else {
2801
+ logger5.warn(
2802
+ "[telegram-setup] connector-setup service not available \u2014 token saved to runtime only"
2803
+ );
2804
+ }
2805
+ res.status(200).json({
2806
+ ok: true,
2807
+ bot: {
2808
+ id: bot.id,
2809
+ username: bot.username,
2810
+ firstName: bot.first_name
2811
+ }
2812
+ });
2813
+ } catch (err) {
2814
+ const message = err instanceof Error ? err.message : String(err);
2815
+ res.status(200).json({
2816
+ ok: false,
2817
+ error: `Failed to reach Telegram API: ${message}`
2818
+ });
2819
+ }
2820
+ }
2821
+ async function handleStatus2(_req, res, runtime) {
2822
+ const setupService = getSetupService2(runtime);
2823
+ let hasToken = false;
2824
+ if (setupService) {
2825
+ const config = setupService.getConfig();
2826
+ const connectors = config.connectors ?? {};
2827
+ const tgConfig = connectors.telegram;
2828
+ hasToken = Boolean(tgConfig?.botToken);
2829
+ }
2830
+ if (!hasToken) {
2831
+ hasToken = Boolean(runtime.getSetting("TELEGRAM_BOT_TOKEN"));
2832
+ }
2833
+ const service = runtime.getService("telegram");
2834
+ const connected = Boolean(service);
2835
+ res.status(200).json({
2836
+ available: true,
2837
+ hasToken,
2838
+ connected
2839
+ });
2840
+ }
2841
+ async function handleDisconnect2(_req, res, runtime) {
2842
+ const setupService = getSetupService2(runtime);
2843
+ if (setupService) {
2844
+ setupService.updateConfig((config) => {
2845
+ const connectors = config.connectors ?? {};
2846
+ const tgConfig = connectors.telegram;
2847
+ if (tgConfig) {
2848
+ delete tgConfig.botToken;
2849
+ }
2850
+ });
2851
+ }
2852
+ res.status(200).json({ ok: true });
2853
+ }
2854
+ var telegramSetupRoutes = [
2855
+ {
2856
+ type: "POST",
2857
+ path: "/api/telegram-setup/validate-token",
2858
+ handler: handleValidateToken,
2859
+ rawPath: true
2860
+ },
2861
+ {
2862
+ type: "GET",
2863
+ path: "/api/telegram-setup/status",
2864
+ handler: handleStatus2,
2865
+ rawPath: true
2866
+ },
2867
+ {
2868
+ type: "POST",
2869
+ path: "/api/telegram-setup/disconnect",
2870
+ handler: handleDisconnect2,
2871
+ rawPath: true
2872
+ }
2873
+ ];
2874
+
2875
+ // src/tests.ts
2876
+ import { logger as logger6 } from "@elizaos/core";
2877
+ var TEST_IMAGE_URL = "https://github.com/elizaOS/awesome-eliza/blob/main/assets/eliza-logo.jpg?raw=true";
2878
+ var TelegramTestSuite = class {
2879
+ name = "telegram";
2880
+ telegramClient = null;
2881
+ bot = null;
2882
+ messageManager = null;
2883
+ tests;
2884
+ /**
2885
+ * Constructor for initializing a set of test cases for a Telegram bot.
2886
+ *
2887
+ * @constructor
2888
+ * @property {Array<Object>} tests - An array of test cases with name and corresponding test functions.
2889
+ * @property {string} tests.name - The name of the test case.
2890
+ * @property {function} tests.fn - The test function to be executed.
2891
+ */
2892
+ constructor() {
2893
+ this.tests = [
2894
+ {
2895
+ name: "Initialize and Validate Telegram Bot Connection",
2896
+ fn: this.testCreatingTelegramBot.bind(this)
2897
+ },
2898
+ {
2899
+ name: "Send Basic Text Message to Telegram Chat",
2900
+ fn: this.testSendingTextMessage.bind(this)
2901
+ },
2902
+ {
2903
+ name: "Send Text Message with an Image Attachment",
2904
+ fn: this.testSendingMessageWithAttachment.bind(this)
2905
+ },
2906
+ {
2907
+ name: "Handle and Process Incoming Telegram Messages",
2908
+ fn: this.testHandlingMessage.bind(this)
2909
+ },
2910
+ {
2911
+ name: "Process and Validate Image Attachments in Incoming Messages",
2912
+ fn: this.testProcessingImages.bind(this)
2913
+ }
2914
+ ];
2915
+ }
2916
+ /**
2917
+ * Retrieves the Telegram test chat ID from environment variables.
2918
+ *
2919
+ * Reference on getting the Telegram chat ID:
2920
+ * https://stackoverflow.com/a/32572159
2921
+ */
2922
+ /**
2923
+ * Validates the chat ID by checking if it is set in the runtime settings or environment variables.
2924
+ * If not set, an error is thrown with a message instructing to provide a valid chat ID.
2925
+ * @param {IAgentRuntime} runtime - The runtime object that provides access to the settings and environment variables.
2926
+ * @throws {Error} If TELEGRAM_TEST_CHAT_ID is not set in the runtime settings or environment variables.
2927
+ * @returns {string} The validated chat ID.
2928
+ */
2929
+ validateChatId(runtime) {
2930
+ const testChatId = runtime.getSetting("TELEGRAM_TEST_CHAT_ID") || process.env.TELEGRAM_TEST_CHAT_ID;
2931
+ if (!testChatId || typeof testChatId === "boolean") {
2932
+ throw new Error(
2933
+ "TELEGRAM_TEST_CHAT_ID is not set. Please provide a valid chat ID in the environment variables."
2934
+ );
2935
+ }
2936
+ return testChatId;
2937
+ }
2938
+ async getChatInfo(runtime) {
2939
+ try {
2940
+ const chatId = this.validateChatId(runtime);
2941
+ if (!this.bot) {
2942
+ throw new Error("Bot is not initialized.");
2943
+ }
2944
+ const chat = await this.bot.telegram.getChat(chatId);
2945
+ logger6.debug({ src: "plugin:telegram", chatId }, "Fetched real chat");
2946
+ return chat;
2947
+ } catch (error) {
2948
+ throw new Error(`Error fetching real Telegram chat: ${error}`);
2949
+ }
2950
+ }
2951
+ async testCreatingTelegramBot(runtime) {
2952
+ this.telegramClient = runtime.getService("telegram");
2953
+ if (!this.telegramClient?.messageManager) {
2954
+ throw new Error(
2955
+ "Telegram service or message manager not initialized - check TELEGRAM_BOT_TOKEN"
2956
+ );
2957
+ }
2958
+ this.bot = this.telegramClient.messageManager.bot;
2959
+ this.messageManager = this.telegramClient.messageManager;
2960
+ logger6.debug(
2961
+ { src: "plugin:telegram" },
2962
+ "Telegram bot initialized successfully"
2963
+ );
2964
+ }
2965
+ async testSendingTextMessage(runtime) {
2966
+ try {
2967
+ if (!this.bot) {
2968
+ throw new Error("Bot not initialized.");
2969
+ }
2970
+ const chatId = this.validateChatId(runtime);
2971
+ await this.bot.telegram.sendMessage(chatId, "Testing Telegram message!");
2972
+ logger6.debug(
2973
+ { src: "plugin:telegram", chatId },
2974
+ "Message sent successfully"
2975
+ );
2976
+ } catch (error) {
2977
+ throw new Error(`Error sending Telegram message: ${error}`);
2978
+ }
2979
+ }
2980
+ async testSendingMessageWithAttachment(runtime) {
2981
+ try {
2982
+ if (!this.messageManager) {
2983
+ throw new Error("MessageManager not initialized.");
2984
+ }
2985
+ if (!this.bot) {
2986
+ throw new Error("Bot not initialized.");
2987
+ }
2988
+ const chat = await this.getChatInfo(runtime);
2989
+ const mockContext = {
2990
+ chat,
2991
+ from: { id: 123, username: "TestUser" },
2992
+ telegram: this.bot.telegram
2993
+ };
2994
+ const messageContent = {
2995
+ text: "Here is an image attachment:",
2996
+ attachments: [
2997
+ {
2998
+ id: "123",
2999
+ title: "Sample Image",
3000
+ source: TEST_IMAGE_URL,
3001
+ text: "Sample Image",
3002
+ url: TEST_IMAGE_URL,
3003
+ contentType: "image/png",
3004
+ description: "Sample Image"
3005
+ }
3006
+ ]
3007
+ };
3008
+ await this.messageManager.sendMessageInChunks(
3009
+ mockContext,
3010
+ messageContent
3011
+ );
3012
+ logger6.success(
3013
+ { src: "plugin:telegram" },
3014
+ "Message with image attachment sent successfully"
3015
+ );
3016
+ } catch (error) {
3017
+ throw new Error(
3018
+ `Error sending Telegram message with attachment: ${error}`
3019
+ );
3020
+ }
3021
+ }
3022
+ async testHandlingMessage(runtime) {
3023
+ try {
3024
+ if (!this.bot) {
3025
+ throw new Error("Bot not initialized.");
3026
+ }
3027
+ if (!this.messageManager) {
3028
+ throw new Error("MessageManager not initialized.");
3029
+ }
3030
+ const chat = await this.getChatInfo(runtime);
3031
+ const mockContext = {
3032
+ chat,
3033
+ from: {
3034
+ id: 123,
3035
+ username: "TestUser",
3036
+ is_bot: false,
3037
+ first_name: "Test",
3038
+ last_name: "User"
3039
+ },
3040
+ message: {
3041
+ message_id: 12345,
3042
+ text: `@${this.bot.botInfo?.username}! Hello!`,
3043
+ date: Math.floor(Date.now() / 1e3),
3044
+ chat
3045
+ },
3046
+ telegram: this.bot.telegram
3047
+ };
3048
+ try {
3049
+ await this.messageManager.handleMessage(mockContext);
3050
+ } catch (error) {
3051
+ throw new Error(`Error handling Telegram message: ${error}`);
3052
+ }
3053
+ } catch (error) {
3054
+ throw new Error(`Error handling Telegram message: ${error}`);
3055
+ }
3056
+ }
3057
+ async testProcessingImages(runtime) {
3058
+ try {
3059
+ if (!this.bot) {
3060
+ throw new Error("Bot not initialized.");
3061
+ }
3062
+ if (!this.messageManager) {
3063
+ throw new Error("MessageManager not initialized.");
3064
+ }
3065
+ const chatId = this.validateChatId(runtime);
3066
+ const fileId = await this.getFileId(String(chatId), TEST_IMAGE_URL);
3067
+ const mockMessage = {
3068
+ message_id: 12345,
3069
+ chat: { id: chatId, type: "private" },
3070
+ date: Math.floor(Date.now() / 1e3),
3071
+ photo: [
3072
+ {
3073
+ file_id: fileId,
3074
+ file_unique_id: `unique_${fileId}`,
3075
+ width: 100,
3076
+ height: 100
3077
+ }
3078
+ ],
3079
+ text: `@${this.bot.botInfo?.username}!`
3080
+ };
3081
+ const result = await this.messageManager.processImage(
3082
+ mockMessage
3083
+ );
3084
+ if (!result?.description) {
3085
+ throw new Error(
3086
+ "Error processing Telegram image or description not found"
3087
+ );
3088
+ }
3089
+ const { description } = result;
3090
+ logger6.debug(
3091
+ { src: "plugin:telegram", description },
3092
+ "Processing Telegram image successfully"
3093
+ );
3094
+ } catch (error) {
3095
+ throw new Error(`Error processing Telegram image: ${error}`);
3096
+ }
3097
+ }
3098
+ async getFileId(chatId, imageUrl) {
3099
+ try {
3100
+ if (!this.bot) {
3101
+ throw new Error("Bot is not initialized.");
3102
+ }
3103
+ const message = await this.bot.telegram.sendPhoto(chatId, imageUrl);
3104
+ if (message.photo.length === 0) {
3105
+ throw new Error("No photo received in the message response.");
3106
+ }
3107
+ return message.photo[message.photo.length - 1].file_id;
3108
+ } catch (error) {
3109
+ logger6.error(
3110
+ {
3111
+ src: "plugin:telegram",
3112
+ chatId,
3113
+ error: error instanceof Error ? error.message : String(error)
3114
+ },
3115
+ "Error sending image"
3116
+ );
3117
+ throw error;
3118
+ }
3119
+ }
3120
+ };
3121
+
3122
+ // src/index.ts
3123
+ var telegramPlugin = {
3124
+ name: TELEGRAM_SERVICE_NAME,
3125
+ description: "Telegram client plugin",
3126
+ // TelegramService must come before TelegramOwnerPairingServiceImpl so the
3127
+ // bot instance exists when the pairing service registers its command.
3128
+ services: [TelegramService, TelegramOwnerPairingServiceImpl],
3129
+ routes: [...telegramSetupRoutes, ...telegramAccountRoutes],
3130
+ tests: [new TelegramTestSuite()],
3131
+ // Self-declared auto-enable: activate when the "telegram" connector is
3132
+ // configured in eliza.json / eliza.json. The hardcoded CONNECTOR_PLUGINS
3133
+ // map in plugin-auto-enable.ts still serves as a fallback.
3134
+ autoEnable: {
3135
+ connectorKeys: ["telegram"]
3136
+ }
3137
+ };
3138
+ var index_default = telegramPlugin;
3139
+ export {
3140
+ MessageManager,
3141
+ TELEGRAM_OWNER_PAIRING_SERVICE_TYPE,
3142
+ TelegramAccountAuthSession,
3143
+ TelegramOwnerPairingServiceImpl,
3144
+ TelegramService,
3145
+ clearTelegramAccountAuthState,
3146
+ clearTelegramAccountSession,
3147
+ index_default as default,
3148
+ defaultTelegramAccountDeviceModel,
3149
+ defaultTelegramAccountSystemVersion,
3150
+ loadTelegramAccountSessionString,
3151
+ resolveTelegramAccountSessionFile,
3152
+ saveTelegramAccountSessionString,
3153
+ stopTelegramAccountAuthSession,
3154
+ telegramAccountAuthStateExists,
3155
+ telegramAccountSessionExists
3156
+ };
3157
+ //# sourceMappingURL=index.js.map