@elizaos/plugin-telegram 2.0.0-beta.1 → 2.0.3-beta.3

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 DELETED
@@ -1,4167 +0,0 @@
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-KFEFF6RI.js";
13
-
14
- // src/index.ts
15
- import { getConnectorAccountManager, logger as logger7 } from "@elizaos/core";
16
-
17
- // src/account-setup-routes.ts
18
- function isConnectorSetupService(service) {
19
- if (!service || typeof service !== "object") {
20
- return false;
21
- }
22
- const candidate = service;
23
- return typeof candidate.getConfig === "function" && typeof candidate.updateConfig === "function" && typeof candidate.persistConfig === "function";
24
- }
25
- function getSetupService(runtime) {
26
- const service = runtime.getService("connector-setup");
27
- return isConnectorSetupService(service) ? service : null;
28
- }
29
- var telegramAccountAuthSession = null;
30
- async function stopTelegramAccountAuthSession() {
31
- if (telegramAccountAuthSession) {
32
- try {
33
- await telegramAccountAuthSession.stop();
34
- } catch {
35
- }
36
- telegramAccountAuthSession = null;
37
- }
38
- }
39
- function readConnectorConfig(config) {
40
- const connectors = config.connectors;
41
- const raw = connectors?.telegramAccount;
42
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
43
- return {};
44
- }
45
- return raw;
46
- }
47
- function hasConfiguredTelegramAccount(connConfig) {
48
- return Boolean(
49
- 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
50
- );
51
- }
52
- function resolveConfiguredPhone(runtime, connConfig) {
53
- if (typeof connConfig.phone === "string" && connConfig.phone.trim().length > 0) {
54
- return connConfig.phone.trim();
55
- }
56
- const setting = runtime.getSetting("TELEGRAM_ACCOUNT_PHONE");
57
- return typeof setting === "string" && setting.trim().length > 0 ? setting.trim() : null;
58
- }
59
- function resolveService(runtime) {
60
- const service = runtime.getService("telegram-account");
61
- return service ?? null;
62
- }
63
- function isServiceConnected(service) {
64
- if (!service) {
65
- return false;
66
- }
67
- if (typeof service.isConnected === "function") {
68
- return service.isConnected();
69
- }
70
- const withFlags = service;
71
- if (typeof withFlags.isServiceConnected === "function") {
72
- return withFlags.isServiceConnected();
73
- }
74
- return withFlags.connected === true;
75
- }
76
- function statusFromState(runtime, config) {
77
- const connectorConfig = readConnectorConfig(config);
78
- const configured = hasConfiguredTelegramAccount(connectorConfig);
79
- const sessExists = telegramAccountSessionExists();
80
- const authSnapshot = telegramAccountAuthSession?.getSnapshot() ?? null;
81
- const service = resolveService(runtime);
82
- const serviceConnected = isServiceConnected(service);
83
- const serviceAccount = typeof service?.getAccountSummary === "function" ? service.getAccountSummary() : null;
84
- const fallbackPhone = resolveConfiguredPhone(runtime, connectorConfig);
85
- let status = authSnapshot?.status ?? (serviceConnected ? "connected" : configured || sessExists ? "configured" : "idle");
86
- if (serviceConnected && status === "configured") {
87
- status = "connected";
88
- }
89
- return {
90
- available: true,
91
- status,
92
- configured,
93
- sessionExists: sessExists,
94
- serviceConnected,
95
- restartRequired: status === "configured" && !serviceConnected,
96
- hasAppCredentials: Boolean(
97
- (typeof connectorConfig.appId === "string" || typeof connectorConfig.appId === "number") && typeof connectorConfig.appHash === "string" && connectorConfig.appHash.trim().length > 0
98
- ),
99
- phone: authSnapshot?.phone ?? fallbackPhone,
100
- isCodeViaApp: authSnapshot?.isCodeViaApp ?? false,
101
- account: authSnapshot?.account ?? serviceAccount ?? null,
102
- error: authSnapshot?.error ?? null
103
- };
104
- }
105
- function ensureConnectorBlock(config) {
106
- if (!config.connectors) {
107
- config.connectors = {};
108
- }
109
- const connectors = config.connectors;
110
- if (!connectors.telegramAccount || typeof connectors.telegramAccount !== "object" || Array.isArray(connectors.telegramAccount)) {
111
- connectors.telegramAccount = {};
112
- }
113
- return connectors.telegramAccount;
114
- }
115
- function createSessionOptions(config) {
116
- const connectorConfig = readConnectorConfig(config);
117
- return {
118
- deviceModel: typeof connectorConfig.deviceModel === "string" && connectorConfig.deviceModel.trim().length > 0 ? connectorConfig.deviceModel.trim() : defaultTelegramAccountDeviceModel(),
119
- systemVersion: typeof connectorConfig.systemVersion === "string" && connectorConfig.systemVersion.trim().length > 0 ? connectorConfig.systemVersion.trim() : defaultTelegramAccountSystemVersion()
120
- };
121
- }
122
- function ensureAuthSession(config) {
123
- if (telegramAccountAuthSession) {
124
- return telegramAccountAuthSession;
125
- }
126
- if (!telegramAccountAuthStateExists()) {
127
- return null;
128
- }
129
- telegramAccountAuthSession = new TelegramAccountAuthSession(
130
- createSessionOptions(config)
131
- );
132
- return telegramAccountAuthSession;
133
- }
134
- async function handleStatus(_req, res, runtime) {
135
- const setupService = getSetupService(runtime);
136
- const config = setupService?.getConfig() ?? {};
137
- ensureAuthSession(config);
138
- res.status(200).json(statusFromState(runtime, config));
139
- }
140
- async function handleAuthStart(req, res, runtime) {
141
- const body = req.body ?? {};
142
- const setupService = getSetupService(runtime);
143
- const config = setupService?.getConfig() ?? {};
144
- const connectorConfig = readConnectorConfig(config);
145
- const phone = typeof body.phone === "string" && body.phone.trim() || resolveConfiguredPhone(runtime, connectorConfig);
146
- if (!phone) {
147
- res.status(400).json({ error: "telegram phone number is required" });
148
- return;
149
- }
150
- await telegramAccountAuthSession?.stop();
151
- telegramAccountAuthSession = new TelegramAccountAuthSession(
152
- createSessionOptions(config)
153
- );
154
- const credentials = hasConfiguredTelegramAccount(connectorConfig) && (typeof connectorConfig.appId === "string" || typeof connectorConfig.appId === "number") && typeof connectorConfig.appHash === "string" ? {
155
- apiId: Number(connectorConfig.appId),
156
- apiHash: connectorConfig.appHash
157
- } : null;
158
- try {
159
- await telegramAccountAuthSession.start({ phone, credentials });
160
- const resolved = telegramAccountAuthSession.getResolvedConnectorConfig();
161
- if (resolved && setupService) {
162
- setupService.updateConfig((cfg) => {
163
- Object.assign(ensureConnectorBlock(cfg), resolved);
164
- });
165
- }
166
- const freshConfig = setupService?.getConfig() ?? config;
167
- res.status(200).json(statusFromState(runtime, freshConfig));
168
- } catch (error) {
169
- res.status(500).json({
170
- error: error instanceof Error ? error.message : String(error)
171
- });
172
- }
173
- }
174
- async function handleAuthSubmit(req, res, runtime) {
175
- const body = req.body ?? {};
176
- const setupService = getSetupService(runtime);
177
- const config = setupService?.getConfig() ?? {};
178
- if (!ensureAuthSession(config)) {
179
- res.status(400).json({ error: "telegram login session has not been started" });
180
- return;
181
- }
182
- if (!telegramAccountAuthSession) {
183
- res.status(400).json({ error: "telegram login session has not been started" });
184
- return;
185
- }
186
- try {
187
- await telegramAccountAuthSession.submit(body);
188
- const resolved = telegramAccountAuthSession.getResolvedConnectorConfig();
189
- if (resolved && setupService) {
190
- setupService.updateConfig((cfg) => {
191
- Object.assign(ensureConnectorBlock(cfg), resolved);
192
- });
193
- }
194
- const freshConfig = setupService?.getConfig() ?? config;
195
- res.status(200).json(statusFromState(runtime, freshConfig));
196
- } catch (error) {
197
- res.status(500).json({
198
- error: error instanceof Error ? error.message : String(error)
199
- });
200
- }
201
- }
202
- async function handleDisconnect(_req, res, runtime) {
203
- await telegramAccountAuthSession?.stop();
204
- telegramAccountAuthSession = null;
205
- clearTelegramAccountAuthState();
206
- clearTelegramAccountSession();
207
- const service = resolveService(runtime);
208
- if (typeof service?.stop === "function") {
209
- await service.stop();
210
- }
211
- const setupService = getSetupService(runtime);
212
- if (setupService) {
213
- setupService.updateConfig((cfg) => {
214
- const connectors = cfg.connectors;
215
- if (connectors?.telegramAccount) {
216
- delete connectors.telegramAccount;
217
- }
218
- });
219
- }
220
- const config = setupService?.getConfig() ?? {};
221
- res.status(200).json({
222
- ok: true,
223
- ...statusFromState(runtime, config)
224
- });
225
- }
226
- var telegramAccountRoutes = [
227
- {
228
- type: "GET",
229
- path: "/api/telegram-account/status",
230
- handler: handleStatus,
231
- rawPath: true
232
- },
233
- {
234
- type: "POST",
235
- path: "/api/telegram-account/auth/start",
236
- handler: handleAuthStart,
237
- rawPath: true
238
- },
239
- {
240
- type: "POST",
241
- path: "/api/telegram-account/auth/submit",
242
- handler: handleAuthSubmit,
243
- rawPath: true
244
- },
245
- {
246
- type: "POST",
247
- path: "/api/telegram-account/disconnect",
248
- handler: handleDisconnect,
249
- rawPath: true
250
- }
251
- ];
252
-
253
- // src/accounts.ts
254
- var DEFAULT_ACCOUNT_ID = "default";
255
- function readNonEmptyString(value) {
256
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
257
- }
258
- function normalizeTelegramAccountId(accountId) {
259
- return readNonEmptyString(accountId) ?? DEFAULT_ACCOUNT_ID;
260
- }
261
- function getTelegramMultiAccountConfig(runtime) {
262
- const characterTelegram = runtime.character?.settings?.telegram;
263
- return {
264
- enabled: characterTelegram?.enabled,
265
- botToken: characterTelegram?.botToken,
266
- apiRoot: characterTelegram?.apiRoot,
267
- accounts: characterTelegram?.accounts
268
- };
269
- }
270
- function listTelegramAccountIds(runtime) {
271
- const accounts = getTelegramMultiAccountConfig(runtime).accounts;
272
- if (!accounts || typeof accounts !== "object") {
273
- return [DEFAULT_ACCOUNT_ID];
274
- }
275
- const ids = Object.keys(accounts).filter(Boolean);
276
- return ids.length > 0 ? ids.slice().sort((a, b) => a.localeCompare(b)) : [DEFAULT_ACCOUNT_ID];
277
- }
278
- function resolveDefaultTelegramAccountId(runtime) {
279
- const ids = listTelegramAccountIds(runtime);
280
- return ids.includes(DEFAULT_ACCOUNT_ID) ? DEFAULT_ACCOUNT_ID : ids[0] ?? DEFAULT_ACCOUNT_ID;
281
- }
282
- function getAccountConfig(runtime, accountId) {
283
- const accounts = getTelegramMultiAccountConfig(runtime).accounts;
284
- return accounts && typeof accounts === "object" ? accounts[accountId] : void 0;
285
- }
286
- function resolveTelegramBotToken(runtime, accountId, merged) {
287
- const configToken = readNonEmptyString(merged.botToken);
288
- if (configToken) {
289
- return configToken;
290
- }
291
- if (accountId !== DEFAULT_ACCOUNT_ID) {
292
- return void 0;
293
- }
294
- return readNonEmptyString(runtime.getSetting("TELEGRAM_BOT_TOKEN")) ?? readNonEmptyString(process.env.TELEGRAM_BOT_TOKEN);
295
- }
296
- function resolveTelegramAccount(runtime, accountId) {
297
- const normalizedAccountId = normalizeTelegramAccountId(accountId);
298
- const multi = getTelegramMultiAccountConfig(runtime);
299
- const accountConfig = getAccountConfig(runtime, normalizedAccountId) ?? {};
300
- const merged = {
301
- enabled: multi.enabled,
302
- botToken: multi.botToken,
303
- apiRoot: multi.apiRoot,
304
- ...accountConfig
305
- };
306
- const apiRoot = readNonEmptyString(merged.apiRoot) ?? readNonEmptyString(runtime.getSetting("TELEGRAM_API_ROOT")) ?? readNonEmptyString(process.env.TELEGRAM_API_ROOT) ?? "https://api.telegram.org";
307
- return {
308
- accountId: normalizedAccountId,
309
- enabled: multi.enabled !== false && merged.enabled !== false,
310
- name: readNonEmptyString(merged.name),
311
- botToken: resolveTelegramBotToken(runtime, normalizedAccountId, merged),
312
- apiRoot,
313
- config: merged
314
- };
315
- }
316
- function listEnabledTelegramAccounts(runtime) {
317
- return listTelegramAccountIds(runtime).map((accountId) => resolveTelegramAccount(runtime, accountId)).filter((account) => account.enabled && account.botToken);
318
- }
319
-
320
- // src/constants.ts
321
- var MESSAGE_CONSTANTS = {
322
- MAX_MESSAGES: 50,
323
- RECENT_MESSAGE_COUNT: 5,
324
- CHAT_HISTORY_COUNT: 10,
325
- DEFAULT_SIMILARITY_THRESHOLD: 0.6,
326
- DEFAULT_SIMILARITY_THRESHOLD_FOLLOW_UPS: 0.4,
327
- INTEREST_DECAY_TIME: 5 * 60 * 1e3,
328
- // 5 minutes
329
- PARTIAL_INTEREST_DECAY: 3 * 60 * 1e3
330
- // 3 minutes
331
- };
332
- var TELEGRAM_SERVICE_NAME = "telegram";
333
-
334
- // src/connector-account-provider.ts
335
- function nowMs() {
336
- return Date.now();
337
- }
338
- function synthesizeAccount(accountId, name, externalId) {
339
- return {
340
- id: accountId,
341
- provider: TELEGRAM_SERVICE_NAME,
342
- label: name ?? `Telegram (${accountId})`,
343
- role: "AGENT",
344
- purpose: ["messaging"],
345
- accessGate: "open",
346
- status: "connected",
347
- externalId,
348
- displayHandle: name,
349
- createdAt: nowMs(),
350
- updatedAt: nowMs(),
351
- metadata: {
352
- synthesized: true,
353
- source: "env"
354
- }
355
- };
356
- }
357
- function createTelegramConnectorAccountProvider(runtime) {
358
- return {
359
- provider: TELEGRAM_SERVICE_NAME,
360
- label: "Telegram",
361
- listAccounts: async (manager) => {
362
- const persisted = await manager.getStorage().listAccounts(TELEGRAM_SERVICE_NAME);
363
- const persistedById = new Map(persisted.map((a) => [a.id, a]));
364
- const enabled = listEnabledTelegramAccounts(runtime);
365
- const synthesized = enabled.filter((account) => !persistedById.has(account.accountId)).map(
366
- (account) => synthesizeAccount(account.accountId, account.name, void 0)
367
- );
368
- if (synthesized.length === 0 && persisted.length === 0) {
369
- const fallback = resolveTelegramAccount(runtime, DEFAULT_ACCOUNT_ID);
370
- if (fallback.botToken) {
371
- synthesized.push(
372
- synthesizeAccount(DEFAULT_ACCOUNT_ID, fallback.name, void 0)
373
- );
374
- }
375
- }
376
- return [...persisted, ...synthesized];
377
- },
378
- createAccount: async (input) => {
379
- return {
380
- ...input,
381
- provider: TELEGRAM_SERVICE_NAME,
382
- role: input.role ?? "AGENT",
383
- purpose: input.purpose ?? ["messaging"],
384
- accessGate: input.accessGate ?? "open",
385
- status: input.status ?? "connected"
386
- };
387
- },
388
- patchAccount: async (_accountId, patch) => {
389
- return { ...patch, provider: TELEGRAM_SERVICE_NAME };
390
- },
391
- deleteAccount: async () => {
392
- }
393
- // Telegram bots use a long-lived bot token; no OAuth flow exists.
394
- // startOAuth / completeOAuth are intentionally omitted.
395
- };
396
- }
397
-
398
- // src/messageManager.ts
399
- import fs from "fs";
400
- import {
401
- ChannelType,
402
- createUniqueUuid,
403
- EventType,
404
- lifeOpsPassiveConnectorsEnabled,
405
- logger as logger2,
406
- ModelType,
407
- ServiceType
408
- } from "@elizaos/core";
409
- import { Markup as Markup2 } from "telegraf";
410
-
411
- // src/utils.ts
412
- import { logger } from "@elizaos/core";
413
- import { Markup } from "telegraf";
414
- var TELEGRAM_RESERVED_REGEX = /([_*[\]()~`>#+\-=|{}.!\\])/g;
415
- function escapePlainText(text) {
416
- if (!text) {
417
- return "";
418
- }
419
- return text.replace(TELEGRAM_RESERVED_REGEX, "\\$1");
420
- }
421
- function escapePlainTextPreservingBlockquote(text) {
422
- if (!text) {
423
- return "";
424
- }
425
- return text.split("\n").map((line) => {
426
- const match = line.match(/^(>+\s?)(.*)$/);
427
- if (match) {
428
- return match[1] + escapePlainText(match[2]);
429
- }
430
- return escapePlainText(line);
431
- }).join("\n");
432
- }
433
- function escapeCode(text) {
434
- if (!text) {
435
- return "";
436
- }
437
- return text.replace(/([`\\])/g, "\\$1");
438
- }
439
- function escapeUrl(url) {
440
- if (!url) {
441
- return "";
442
- }
443
- return url.replace(/([)\\])/g, "\\$1");
444
- }
445
- function convertMarkdownToTelegram(markdown) {
446
- const replacements = [];
447
- function storeReplacement(formatted) {
448
- const placeholder = `\0${replacements.length}\0`;
449
- replacements.push(formatted);
450
- return placeholder;
451
- }
452
- let converted = markdown;
453
- converted = converted.replace(
454
- /```(\w+)?\n([\s\S]*?)```/g,
455
- (_match, lang, code) => {
456
- const escapedCode = escapeCode(code);
457
- const formatted = `\`\`\`${lang || ""}
458
- ${escapedCode}\`\`\``;
459
- return storeReplacement(formatted);
460
- }
461
- );
462
- converted = converted.replace(/`([^`]+)`/g, (_match, code) => {
463
- const escapedCode = escapeCode(code);
464
- const formatted = `\`${escapedCode}\``;
465
- return storeReplacement(formatted);
466
- });
467
- converted = converted.replace(
468
- /$begin:math:display$([^$end:math:display$]+)]$begin:math:text$([^)]+)$end:math:text$/g,
469
- (_match, text, url) => {
470
- const formattedText = escapePlainText(text);
471
- const escapedURL = escapeUrl(url);
472
- const formatted = `[${formattedText}](${escapedURL})`;
473
- return storeReplacement(formatted);
474
- }
475
- );
476
- converted = converted.replace(/\*\*([^*]+)\*\*/g, (_match, content) => {
477
- const formattedContent = escapePlainText(content);
478
- const formatted = `*${formattedContent}*`;
479
- return storeReplacement(formatted);
480
- });
481
- converted = converted.replace(/~~([^~]+)~~/g, (_match, content) => {
482
- const formattedContent = escapePlainText(content);
483
- const formatted = `~${formattedContent}~`;
484
- return storeReplacement(formatted);
485
- });
486
- converted = converted.replace(
487
- /(?<!\*)\*([^*\n]+)\*(?!\*)/g,
488
- (_match, content) => {
489
- const formattedContent = escapePlainText(content);
490
- const formatted = `_${formattedContent}_`;
491
- return storeReplacement(formatted);
492
- }
493
- );
494
- converted = converted.replace(/_([^_\n]+)_/g, (_match, content) => {
495
- const formattedContent = escapePlainText(content);
496
- const formatted = `_${formattedContent}_`;
497
- return storeReplacement(formatted);
498
- });
499
- converted = converted.replace(
500
- /^(#{1,6})\s*(.*)$/gm,
501
- (_match, _hashes, headerContent) => {
502
- const formatted = `*${escapePlainText(headerContent.trim())}*`;
503
- return storeReplacement(formatted);
504
- }
505
- );
506
- const NULL_CHAR = String.fromCharCode(0);
507
- const PLACEHOLDER_PATTERN = new RegExp(`(${NULL_CHAR}\\d+${NULL_CHAR})`, "g");
508
- const PLACEHOLDER_TEST = new RegExp(`^${NULL_CHAR}\\d+${NULL_CHAR}$`);
509
- const PLACEHOLDER_REPLACE = new RegExp(`${NULL_CHAR}(\\d+)${NULL_CHAR}`, "g");
510
- const finalEscaped = converted.split(PLACEHOLDER_PATTERN).map((segment) => {
511
- if (PLACEHOLDER_TEST.test(segment)) {
512
- return segment;
513
- } else {
514
- return escapePlainTextPreservingBlockquote(segment);
515
- }
516
- }).join("");
517
- const finalResult = finalEscaped.replace(PLACEHOLDER_REPLACE, (_, index) => {
518
- return replacements[Number.parseInt(index, 10)];
519
- });
520
- return finalResult;
521
- }
522
- function convertToTelegramButtons(buttons) {
523
- if (!buttons) {
524
- return [];
525
- }
526
- const telegramButtons = [];
527
- for (const button of buttons) {
528
- if (!button?.text || !button.url) {
529
- logger.warn(
530
- { src: "plugin:telegram", button },
531
- "Invalid button configuration, skipping"
532
- );
533
- continue;
534
- }
535
- let telegramButton;
536
- switch (button.kind) {
537
- case "login":
538
- telegramButton = Markup.button.login(button.text, button.url);
539
- break;
540
- case "url":
541
- telegramButton = Markup.button.url(button.text, button.url);
542
- break;
543
- default:
544
- logger.warn(
545
- { src: "plugin:telegram", buttonKind: button.kind },
546
- "Unknown button kind, treating as URL button"
547
- );
548
- telegramButton = Markup.button.url(button.text, button.url);
549
- break;
550
- }
551
- telegramButtons.push(telegramButton);
552
- }
553
- return telegramButtons;
554
- }
555
- function cleanText(text) {
556
- if (!text) {
557
- return "";
558
- }
559
- return text.split("\0").join("");
560
- }
561
-
562
- // src/messageManager.ts
563
- var MAX_MESSAGE_LENGTH = 4096;
564
- function isPdfTextService(service) {
565
- return typeof service === "object" && service !== null && typeof service.convertPdfToText === "function";
566
- }
567
- var getChannelType = (chat) => {
568
- const chatType = chat.type;
569
- switch (chatType) {
570
- case "private":
571
- return ChannelType.DM;
572
- case "group":
573
- case "supergroup":
574
- case "channel":
575
- return ChannelType.GROUP;
576
- default:
577
- throw new Error(`Unrecognized Telegram chat type: ${String(chatType)}`);
578
- }
579
- };
580
- var MessageManager = class {
581
- bot;
582
- runtime;
583
- accountId;
584
- /**
585
- * Constructor for creating a new instance of a BotAgent.
586
- *
587
- * @param {Telegraf<Context>} bot - The Telegraf instance used for interacting with the bot platform.
588
- * @param {IAgentRuntime} runtime - The runtime environment for the agent.
589
- */
590
- constructor(bot, runtime, accountId = "default") {
591
- this.bot = bot;
592
- this.runtime = runtime;
593
- this.accountId = accountId;
594
- }
595
- scopedTelegramKey(key) {
596
- return this.accountId === "default" ? key : `${this.accountId}:${key}`;
597
- }
598
- /**
599
- * Process an image from a Telegram message to extract the image URL and description.
600
- *
601
- * @param {Message} message - The Telegram message object containing the image.
602
- * @returns {Promise<{ description: string } | null>} The description of the processed image or null if no image found.
603
- */
604
- async processImage(message) {
605
- try {
606
- let imageUrl = null;
607
- logger2.debug(
608
- {
609
- src: "plugin:telegram",
610
- agentId: this.runtime.agentId,
611
- messageId: message.message_id
612
- },
613
- "Processing image from message"
614
- );
615
- if ("photo" in message && message.photo?.length > 0) {
616
- const photo = message.photo[message.photo.length - 1];
617
- const fileLink = await this.bot.telegram.getFileLink(photo.file_id);
618
- imageUrl = fileLink.toString();
619
- } else if ("document" in message && message.document?.mime_type?.startsWith("image/") && !message.document?.mime_type?.startsWith("application/pdf")) {
620
- const fileLink = await this.bot.telegram.getFileLink(
621
- message.document.file_id
622
- );
623
- imageUrl = fileLink.toString();
624
- }
625
- if (imageUrl) {
626
- const { title, description } = await this.runtime.useModel(
627
- ModelType.IMAGE_DESCRIPTION,
628
- imageUrl
629
- );
630
- return { description: `[Image: ${title}
631
- ${description}]` };
632
- }
633
- } catch (error) {
634
- logger2.error(
635
- {
636
- src: "plugin:telegram",
637
- agentId: this.runtime.agentId,
638
- error: error instanceof Error ? error.message : String(error)
639
- },
640
- "Error processing image"
641
- );
642
- }
643
- return null;
644
- }
645
- /**
646
- * Process a document from a Telegram message to extract the document URL and description.
647
- * Handles PDFs and other document types by converting them to text when possible.
648
- *
649
- * @param {Message} message - The Telegram message object containing the document.
650
- * @returns {Promise<{ description: string } | null>} The description of the processed document or null if no document found.
651
- */
652
- async processDocument(message) {
653
- try {
654
- if (!("document" in message) || !message.document) {
655
- return null;
656
- }
657
- const document = message.document;
658
- const fileLink = await this.bot.telegram.getFileLink(document.file_id);
659
- const documentUrl = fileLink.toString();
660
- logger2.debug(
661
- {
662
- src: "plugin:telegram",
663
- agentId: this.runtime.agentId,
664
- fileName: document.file_name,
665
- mimeType: document.mime_type,
666
- fileSize: document.file_size
667
- },
668
- "Processing document"
669
- );
670
- const documentProcessor = this.getDocumentProcessor(document.mime_type);
671
- if (documentProcessor) {
672
- return await documentProcessor(document, documentUrl);
673
- }
674
- return {
675
- title: `Document: ${document.file_name || "Unknown Document"}`,
676
- fullText: "",
677
- formattedDescription: `[Document: ${document.file_name || "Unknown Document"}
678
- Type: ${document.mime_type || "unknown"}
679
- Size: ${document.file_size || 0} bytes]`,
680
- fileName: document.file_name || "Unknown Document",
681
- mimeType: document.mime_type,
682
- fileSize: document.file_size
683
- };
684
- } catch (error) {
685
- logger2.error(
686
- {
687
- src: "plugin:telegram",
688
- agentId: this.runtime.agentId,
689
- error: error instanceof Error ? error.message : String(error)
690
- },
691
- "Error processing document"
692
- );
693
- return null;
694
- }
695
- }
696
- /**
697
- * Get the appropriate document processor based on MIME type.
698
- */
699
- getDocumentProcessor(mimeType) {
700
- if (!mimeType) {
701
- return null;
702
- }
703
- const processors = {
704
- "application/pdf": this.processPdfDocument.bind(this),
705
- "text/": this.processTextDocument.bind(this),
706
- // covers text/plain, text/csv, text/markdown, etc.
707
- "application/json": this.processTextDocument.bind(this)
708
- };
709
- for (const [pattern, processor] of Object.entries(processors)) {
710
- if (mimeType.startsWith(pattern)) {
711
- return processor;
712
- }
713
- }
714
- return null;
715
- }
716
- /**
717
- * Process PDF documents by converting them to text.
718
- */
719
- async processPdfDocument(document, documentUrl) {
720
- try {
721
- const pdfServiceCandidate = this.runtime.getService(ServiceType.PDF);
722
- const pdfService = isPdfTextService(pdfServiceCandidate) ? pdfServiceCandidate : null;
723
- if (!pdfService) {
724
- logger2.warn(
725
- { src: "plugin:telegram", agentId: this.runtime.agentId },
726
- "PDF service not available, using fallback"
727
- );
728
- return {
729
- title: `PDF Document: ${document.file_name || "Unknown Document"}`,
730
- fullText: "",
731
- formattedDescription: `[PDF Document: ${document.file_name || "Unknown Document"}
732
- Size: ${document.file_size || 0} bytes
733
- Unable to extract text content]`,
734
- fileName: document.file_name || "Unknown Document",
735
- mimeType: document.mime_type,
736
- fileSize: document.file_size
737
- };
738
- }
739
- const response = await fetch(documentUrl);
740
- if (!response.ok) {
741
- throw new Error(`Failed to fetch PDF: ${response.status}`);
742
- }
743
- const pdfBuffer = await response.arrayBuffer();
744
- const text = await pdfService.convertPdfToText(Buffer.from(pdfBuffer));
745
- logger2.debug(
746
- {
747
- src: "plugin:telegram",
748
- agentId: this.runtime.agentId,
749
- fileName: document.file_name,
750
- charactersExtracted: text.length
751
- },
752
- "PDF processed successfully"
753
- );
754
- return {
755
- title: document.file_name || "Unknown Document",
756
- fullText: text,
757
- formattedDescription: `[PDF Document: ${document.file_name || "Unknown Document"}
758
- Size: ${document.file_size || 0} bytes
759
- Text extracted successfully: ${text.length} characters]`,
760
- fileName: document.file_name || "Unknown Document",
761
- mimeType: document.mime_type,
762
- fileSize: document.file_size
763
- };
764
- } catch (error) {
765
- logger2.error(
766
- {
767
- src: "plugin:telegram",
768
- agentId: this.runtime.agentId,
769
- fileName: document.file_name,
770
- error: error instanceof Error ? error.message : String(error)
771
- },
772
- "Error processing PDF document"
773
- );
774
- return {
775
- title: `PDF Document: ${document.file_name || "Unknown Document"}`,
776
- fullText: "",
777
- formattedDescription: `[PDF Document: ${document.file_name || "Unknown Document"}
778
- Size: ${document.file_size || 0} bytes
779
- Error: Unable to extract text content]`,
780
- fileName: document.file_name || "Unknown Document",
781
- mimeType: document.mime_type,
782
- fileSize: document.file_size
783
- };
784
- }
785
- }
786
- /**
787
- * Process text documents by fetching their content.
788
- */
789
- async processTextDocument(document, documentUrl) {
790
- try {
791
- const response = await fetch(documentUrl);
792
- if (!response.ok) {
793
- throw new Error(`Failed to fetch text document: ${response.status}`);
794
- }
795
- const text = await response.text();
796
- logger2.debug(
797
- {
798
- src: "plugin:telegram",
799
- agentId: this.runtime.agentId,
800
- fileName: document.file_name,
801
- charactersExtracted: text.length
802
- },
803
- "Text document processed successfully"
804
- );
805
- return {
806
- title: document.file_name || "Unknown Document",
807
- fullText: text,
808
- formattedDescription: `[Text Document: ${document.file_name || "Unknown Document"}
809
- Size: ${document.file_size || 0} bytes
810
- Text extracted successfully: ${text.length} characters]`,
811
- fileName: document.file_name || "Unknown Document",
812
- mimeType: document.mime_type,
813
- fileSize: document.file_size
814
- };
815
- } catch (error) {
816
- logger2.error(
817
- {
818
- src: "plugin:telegram",
819
- agentId: this.runtime.agentId,
820
- fileName: document.file_name,
821
- error: error instanceof Error ? error.message : String(error)
822
- },
823
- "Error processing text document"
824
- );
825
- return {
826
- title: `Text Document: ${document.file_name || "Unknown Document"}`,
827
- fullText: "",
828
- formattedDescription: `[Text Document: ${document.file_name || "Unknown Document"}
829
- Size: ${document.file_size || 0} bytes
830
- Error: Unable to read content]`,
831
- fileName: document.file_name || "Unknown Document",
832
- mimeType: document.mime_type,
833
- fileSize: document.file_size
834
- };
835
- }
836
- }
837
- /**
838
- * Processes the message content, documents, and images to generate
839
- * processed content and media attachments.
840
- *
841
- * @param {Message} message The message to process
842
- * @returns {Promise<{ processedContent: string; attachments: Media[] }>} Processed content and media attachments
843
- */
844
- async processMessage(message) {
845
- let processedContent = "";
846
- const attachments = [];
847
- if ("text" in message && message.text) {
848
- processedContent = message.text;
849
- } else if ("caption" in message && message.caption) {
850
- processedContent = message.caption;
851
- }
852
- if ("document" in message && message.document) {
853
- const document = message.document;
854
- const documentInfo = await this.processDocument(message);
855
- if (documentInfo) {
856
- try {
857
- const fileLink = await this.bot.telegram.getFileLink(
858
- document.file_id
859
- );
860
- const title = documentInfo.title;
861
- const fullText = documentInfo.fullText;
862
- if (fullText) {
863
- const documentContent = `
864
-
865
- --- DOCUMENT CONTENT ---
866
- Title: ${title}
867
-
868
- Full Content:
869
- ${fullText}
870
- --- END DOCUMENT ---
871
-
872
- `;
873
- processedContent += documentContent;
874
- }
875
- attachments.push({
876
- id: document.file_id,
877
- url: fileLink.toString(),
878
- title,
879
- source: document.mime_type?.startsWith("application/pdf") ? "PDF" : "Document",
880
- description: documentInfo.formattedDescription,
881
- text: fullText
882
- });
883
- logger2.debug(
884
- {
885
- src: "plugin:telegram",
886
- agentId: this.runtime.agentId,
887
- fileName: documentInfo.fileName
888
- },
889
- "Document processed successfully"
890
- );
891
- } catch (error) {
892
- logger2.error(
893
- {
894
- src: "plugin:telegram",
895
- agentId: this.runtime.agentId,
896
- fileName: documentInfo.fileName,
897
- error: error instanceof Error ? error.message : String(error)
898
- },
899
- "Error processing document"
900
- );
901
- attachments.push({
902
- id: document.file_id,
903
- url: "",
904
- title: `Document: ${documentInfo.fileName}`,
905
- source: "Document",
906
- description: `Document processing failed: ${documentInfo.fileName}`,
907
- text: `Document: ${documentInfo.fileName}
908
- Size: ${documentInfo.fileSize || 0} bytes
909
- Type: ${documentInfo.mimeType || "unknown"}`
910
- });
911
- }
912
- } else {
913
- attachments.push({
914
- id: document.file_id,
915
- url: "",
916
- title: `Document: ${document.file_name || "Unknown Document"}`,
917
- source: "Document",
918
- description: `Document: ${document.file_name || "Unknown Document"}`,
919
- text: `Document: ${document.file_name || "Unknown Document"}
920
- Size: ${document.file_size || 0} bytes
921
- Type: ${document.mime_type || "unknown"}`
922
- });
923
- }
924
- }
925
- if ("photo" in message && message.photo?.length > 0) {
926
- const imageInfo = await this.processImage(message);
927
- if (imageInfo) {
928
- const photo = message.photo[message.photo.length - 1];
929
- const fileLink = await this.bot.telegram.getFileLink(photo.file_id);
930
- attachments.push({
931
- id: photo.file_id,
932
- url: fileLink.toString(),
933
- title: "Image Attachment",
934
- source: "Image",
935
- description: imageInfo.description,
936
- text: imageInfo.description
937
- });
938
- }
939
- }
940
- logger2.debug(
941
- {
942
- src: "plugin:telegram",
943
- agentId: this.runtime.agentId,
944
- hasContent: !!processedContent,
945
- attachmentsCount: attachments.length
946
- },
947
- "Message processed"
948
- );
949
- return { processedContent, attachments };
950
- }
951
- /**
952
- * Sends a message in chunks, handling attachments and splitting the message if necessary
953
- *
954
- * @param {Context} ctx - The context object representing the current state of the bot
955
- * @param {TelegramContent} content - The content of the message to be sent
956
- * @param {number} [replyToMessageId] - The ID of the message to reply to, if any
957
- * @returns {Promise<Message.TextMessage[]>} - An array of TextMessage objects representing the messages sent
958
- */
959
- async sendMessageInChunks(ctx, content, replyToMessageId, messageThreadId) {
960
- if (content.attachments && content.attachments.length > 0) {
961
- content.attachments.map(async (attachment) => {
962
- const typeMap = {
963
- "image/gif": "animation" /* ANIMATION */,
964
- image: "photo" /* PHOTO */,
965
- doc: "document" /* DOCUMENT */,
966
- video: "video" /* VIDEO */,
967
- audio: "audio" /* AUDIO */
968
- };
969
- let mediaType;
970
- for (const prefix in typeMap) {
971
- if (attachment.contentType?.startsWith(prefix)) {
972
- mediaType = typeMap[prefix];
973
- break;
974
- }
975
- }
976
- if (!mediaType) {
977
- throw new Error(
978
- `Unsupported Telegram attachment content type: ${attachment.contentType}`
979
- );
980
- }
981
- await this.sendMedia(
982
- ctx,
983
- attachment.url,
984
- mediaType,
985
- attachment.description
986
- );
987
- });
988
- return [];
989
- } else {
990
- const chunks = this.splitMessage(content.text ?? "");
991
- const sentMessages = [];
992
- const telegramButtons = convertToTelegramButtons(content.buttons ?? []);
993
- if (!ctx.chat) {
994
- logger2.error(
995
- { src: "plugin:telegram", agentId: this.runtime.agentId },
996
- "sendMessageInChunks: ctx.chat is undefined"
997
- );
998
- return [];
999
- }
1000
- await ctx.telegram.sendChatAction(ctx.chat.id, "typing");
1001
- for (let i = 0; i < chunks.length; i++) {
1002
- const chunk = convertMarkdownToTelegram(chunks[i]);
1003
- if (!ctx.chat) {
1004
- logger2.error(
1005
- { src: "plugin:telegram", agentId: this.runtime.agentId },
1006
- "sendMessageInChunks loop: ctx.chat is undefined"
1007
- );
1008
- continue;
1009
- }
1010
- const sentMessage = await ctx.telegram.sendMessage(
1011
- ctx.chat.id,
1012
- chunk,
1013
- {
1014
- reply_parameters: i === 0 && replyToMessageId ? { message_id: replyToMessageId } : void 0,
1015
- message_thread_id: messageThreadId,
1016
- parse_mode: "MarkdownV2",
1017
- ...Markup2.inlineKeyboard(telegramButtons)
1018
- }
1019
- );
1020
- sentMessages.push(sentMessage);
1021
- }
1022
- return sentMessages;
1023
- }
1024
- }
1025
- /**
1026
- * Sends media to a chat using the Telegram API.
1027
- *
1028
- * @param {Context} ctx - The context object containing information about the current chat.
1029
- * @param {string} mediaPath - The path to the media to be sent, either a URL or a local file path.
1030
- * @param {MediaType} type - The type of media being sent (PHOTO, VIDEO, DOCUMENT, AUDIO, or ANIMATION).
1031
- * @param {string} [caption] - Optional caption for the media being sent.
1032
- *
1033
- * @returns {Promise<void>} A Promise that resolves when the media is successfully sent.
1034
- */
1035
- async sendMedia(ctx, mediaPath, type, caption) {
1036
- try {
1037
- const isUrl = /^(http|https):\/\//.test(mediaPath);
1038
- const sendFunctionMap = {
1039
- ["photo" /* PHOTO */]: ctx.telegram.sendPhoto.bind(ctx.telegram),
1040
- ["video" /* VIDEO */]: ctx.telegram.sendVideo.bind(ctx.telegram),
1041
- ["document" /* DOCUMENT */]: ctx.telegram.sendDocument.bind(ctx.telegram),
1042
- ["audio" /* AUDIO */]: ctx.telegram.sendAudio.bind(ctx.telegram),
1043
- ["animation" /* ANIMATION */]: ctx.telegram.sendAnimation.bind(ctx.telegram)
1044
- };
1045
- const sendFunction = sendFunctionMap[type];
1046
- if (!sendFunction) {
1047
- throw new Error(`Unsupported media type: ${type}`);
1048
- }
1049
- if (!ctx.chat) {
1050
- throw new Error("sendMedia: ctx.chat is undefined");
1051
- }
1052
- if (isUrl) {
1053
- await sendFunction(ctx.chat.id, mediaPath, { caption });
1054
- } else {
1055
- if (!fs.existsSync(mediaPath)) {
1056
- throw new Error(`File not found at path: ${mediaPath}`);
1057
- }
1058
- const fileStream = fs.createReadStream(mediaPath);
1059
- try {
1060
- if (!ctx.chat) {
1061
- throw new Error("sendMedia (file): ctx.chat is undefined");
1062
- }
1063
- await sendFunction(ctx.chat.id, { source: fileStream }, { caption });
1064
- } finally {
1065
- fileStream.destroy();
1066
- }
1067
- }
1068
- logger2.debug(
1069
- {
1070
- src: "plugin:telegram",
1071
- agentId: this.runtime.agentId,
1072
- mediaType: type,
1073
- mediaPath
1074
- },
1075
- "Media sent successfully"
1076
- );
1077
- } catch (error) {
1078
- logger2.error(
1079
- {
1080
- src: "plugin:telegram",
1081
- agentId: this.runtime.agentId,
1082
- mediaType: type,
1083
- mediaPath,
1084
- error: error instanceof Error ? error.message : String(error)
1085
- },
1086
- "Failed to send media"
1087
- );
1088
- throw error;
1089
- }
1090
- }
1091
- /**
1092
- * Splits a given text into an array of strings based on the maximum message length.
1093
- *
1094
- * @param {string} text - The text to split into chunks.
1095
- * @returns {string[]} An array of strings with each element representing a chunk of the original text.
1096
- */
1097
- splitMessage(text) {
1098
- const chunks = [];
1099
- if (!text) {
1100
- return chunks;
1101
- }
1102
- let currentChunk = "";
1103
- const lines = text.split("\n");
1104
- for (const line of lines) {
1105
- if (currentChunk.length + line.length + 1 <= MAX_MESSAGE_LENGTH) {
1106
- currentChunk += (currentChunk ? "\n" : "") + line;
1107
- } else {
1108
- if (currentChunk) {
1109
- chunks.push(currentChunk);
1110
- }
1111
- currentChunk = line;
1112
- }
1113
- }
1114
- if (currentChunk) {
1115
- chunks.push(currentChunk);
1116
- }
1117
- return chunks;
1118
- }
1119
- /**
1120
- * Handle incoming messages from Telegram and process them accordingly.
1121
- * @param {Context} ctx - The context object containing information about the message.
1122
- * @returns {Promise<void>}
1123
- */
1124
- async handleMessage(ctx) {
1125
- if (!ctx.message || !ctx.from) {
1126
- return;
1127
- }
1128
- const message = ctx.message;
1129
- try {
1130
- const telegramUserId = ctx.from.id.toString();
1131
- const entityId = createUniqueUuid(
1132
- this.runtime,
1133
- this.scopedTelegramKey(telegramUserId)
1134
- );
1135
- const threadId = "is_topic_message" in message && message.is_topic_message ? message.message_thread_id?.toString() : void 0;
1136
- if (!ctx.chat) {
1137
- logger2.error(
1138
- { src: "plugin:telegram", agentId: this.runtime.agentId },
1139
- "handleMessage: ctx.chat is undefined"
1140
- );
1141
- return;
1142
- }
1143
- const telegramRoomid = threadId ? `${ctx.chat.id}-${threadId}` : ctx.chat.id.toString();
1144
- const telegramChatId = ctx.chat.id.toString();
1145
- const scopedRoomKey = this.scopedTelegramKey(telegramRoomid);
1146
- const scopedChatKey = this.scopedTelegramKey(telegramChatId);
1147
- const roomId = createUniqueUuid(this.runtime, scopedRoomKey);
1148
- const worldId = createUniqueUuid(this.runtime, scopedChatKey);
1149
- const telegramMessageId = message.message_id.toString();
1150
- const messageId = createUniqueUuid(
1151
- this.runtime,
1152
- this.scopedTelegramKey(telegramMessageId)
1153
- );
1154
- const { processedContent, attachments } = await this.processMessage(message);
1155
- const cleanedContent = cleanText(processedContent);
1156
- const cleanedAttachments = attachments.map((att) => ({
1157
- ...att,
1158
- text: cleanText(att.text),
1159
- description: cleanText(att.description),
1160
- title: cleanText(att.title)
1161
- }));
1162
- if (!cleanedContent && cleanedAttachments.length === 0) {
1163
- return;
1164
- }
1165
- const chat = message.chat;
1166
- const channelType = getChannelType(chat);
1167
- await this.runtime.ensureConnection({
1168
- entityId,
1169
- roomId,
1170
- 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,
1171
- userName: ctx.from.username,
1172
- name: ctx.from.first_name,
1173
- userId: telegramUserId,
1174
- source: "telegram",
1175
- channelId: telegramRoomid,
1176
- type: channelType,
1177
- worldId,
1178
- worldName: telegramRoomid
1179
- });
1180
- const memory = {
1181
- id: messageId,
1182
- entityId,
1183
- agentId: this.runtime.agentId,
1184
- roomId,
1185
- content: {
1186
- text: cleanedContent || " ",
1187
- attachments: cleanedAttachments,
1188
- source: "telegram",
1189
- metadata: { accountId: this.accountId },
1190
- channelType,
1191
- inReplyTo: "reply_to_message" in message && message.reply_to_message ? createUniqueUuid(
1192
- this.runtime,
1193
- this.scopedTelegramKey(
1194
- message.reply_to_message.message_id.toString()
1195
- )
1196
- ) : void 0
1197
- },
1198
- metadata: {
1199
- type: "message",
1200
- source: "telegram",
1201
- accountId: this.accountId,
1202
- provider: "telegram",
1203
- timestamp: message.date * 1e3,
1204
- entityName: ctx.from.first_name,
1205
- entityUserName: ctx.from.username,
1206
- fromBot: ctx.from.is_bot,
1207
- fromId: telegramUserId,
1208
- sourceId: entityId,
1209
- chatType: chat.type,
1210
- messageIdFull: telegramMessageId,
1211
- sender: {
1212
- id: telegramUserId,
1213
- name: ctx.from.first_name,
1214
- username: ctx.from.username
1215
- },
1216
- telegram: {
1217
- chatId: telegramChatId,
1218
- messageId: telegramMessageId,
1219
- threadId
1220
- },
1221
- telegramUserId,
1222
- telegramChatId
1223
- },
1224
- createdAt: message.date * 1e3
1225
- };
1226
- const callback = async (content, _actionName) => {
1227
- try {
1228
- if (!content.text) {
1229
- return [];
1230
- }
1231
- let sentMessages = false;
1232
- if (content?.channelType === "DM") {
1233
- sentMessages = [];
1234
- if (ctx.from) {
1235
- const res = await this.bot.telegram.sendMessage(
1236
- ctx.from.id,
1237
- content.text
1238
- );
1239
- sentMessages.push(res);
1240
- }
1241
- } else {
1242
- sentMessages = await this.sendMessageInChunks(
1243
- ctx,
1244
- content,
1245
- message.message_id
1246
- );
1247
- }
1248
- if (!Array.isArray(sentMessages)) {
1249
- return [];
1250
- }
1251
- const memories = [];
1252
- for (let i = 0; i < sentMessages.length; i++) {
1253
- const sentMessage = sentMessages[i];
1254
- const responseMemory = {
1255
- id: createUniqueUuid(
1256
- this.runtime,
1257
- this.scopedTelegramKey(sentMessage.message_id.toString())
1258
- ),
1259
- entityId: this.runtime.agentId,
1260
- agentId: this.runtime.agentId,
1261
- roomId,
1262
- content: {
1263
- ...content,
1264
- source: "telegram",
1265
- text: sentMessage.text,
1266
- inReplyTo: messageId,
1267
- channelType,
1268
- metadata: { accountId: this.accountId }
1269
- },
1270
- metadata: {
1271
- type: "message",
1272
- source: "telegram",
1273
- accountId: this.accountId,
1274
- provider: "telegram",
1275
- timestamp: sentMessage.date * 1e3,
1276
- fromBot: true,
1277
- fromId: this.runtime.agentId,
1278
- sourceId: this.runtime.agentId,
1279
- chatType: chat.type,
1280
- messageIdFull: sentMessage.message_id.toString(),
1281
- telegram: {
1282
- chatId: sentMessage.chat.id,
1283
- messageId: sentMessage.message_id.toString(),
1284
- threadId
1285
- }
1286
- },
1287
- createdAt: sentMessage.date * 1e3
1288
- };
1289
- await this.runtime.createMemory(responseMemory, "messages");
1290
- memories.push(responseMemory);
1291
- }
1292
- return memories;
1293
- } catch (error) {
1294
- logger2.error(
1295
- {
1296
- src: "plugin:telegram",
1297
- agentId: this.runtime.agentId,
1298
- error: error instanceof Error ? error.message : String(error)
1299
- },
1300
- "Error in message callback"
1301
- );
1302
- return [];
1303
- }
1304
- };
1305
- const telegramAutoReplyRaw = this.runtime.getSetting(
1306
- "TELEGRAM_AUTO_REPLY"
1307
- );
1308
- const telegramAutoReply = !lifeOpsPassiveConnectorsEnabled(this.runtime) && (telegramAutoReplyRaw === true || telegramAutoReplyRaw === "true");
1309
- if (!telegramAutoReply) {
1310
- try {
1311
- await this.runtime.createMemory(memory, "messages");
1312
- } catch (persistError) {
1313
- logger2.warn(
1314
- {
1315
- src: "plugin:telegram",
1316
- agentId: this.runtime.agentId,
1317
- error: persistError instanceof Error ? persistError.message : String(persistError)
1318
- },
1319
- "Failed to persist inbound memory while auto-reply is disabled"
1320
- );
1321
- }
1322
- logger2.debug(
1323
- { src: "plugin:telegram", agentId: this.runtime.agentId },
1324
- "Auto-reply disabled (TELEGRAM_AUTO_REPLY=false); message ingested without response"
1325
- );
1326
- } else if (this.runtime.messageService) {
1327
- await this.runtime.messageService.handleMessage(
1328
- this.runtime,
1329
- memory,
1330
- callback
1331
- );
1332
- } else {
1333
- logger2.error(
1334
- { src: "plugin:telegram", agentId: this.runtime.agentId },
1335
- "Message service is not available"
1336
- );
1337
- throw new Error(
1338
- "Message service is not initialized. Ensure the message service is properly configured."
1339
- );
1340
- }
1341
- } catch (error) {
1342
- logger2.error(
1343
- {
1344
- src: "plugin:telegram",
1345
- agentId: this.runtime.agentId,
1346
- chatId: ctx.chat?.id,
1347
- messageId: ctx.message?.message_id,
1348
- from: ctx.from?.username || ctx.from?.id,
1349
- error: error instanceof Error ? error.message : String(error)
1350
- },
1351
- "Error handling Telegram message"
1352
- );
1353
- throw error;
1354
- }
1355
- }
1356
- /**
1357
- * Handles the reaction event triggered by a user reacting to a message.
1358
- * @param {NarrowedContext<Context<Update>, Update.MessageReactionUpdate>} ctx The context of the message reaction update
1359
- * @returns {Promise<void>} A Promise that resolves when the reaction handling is complete
1360
- */
1361
- async handleReaction(ctx) {
1362
- if (!ctx.update.message_reaction || !ctx.from) {
1363
- return;
1364
- }
1365
- const reaction = ctx.update.message_reaction;
1366
- const reactedToMessageId = reaction.message_id;
1367
- const originalMessagePlaceholder = {
1368
- message_id: reactedToMessageId,
1369
- chat: reaction.chat,
1370
- from: ctx.from,
1371
- date: Math.floor(Date.now() / 1e3)
1372
- };
1373
- const reactionType = reaction.new_reaction[0].type;
1374
- const reactionEmoji = reaction.new_reaction[0].type;
1375
- try {
1376
- const entityId = createUniqueUuid(
1377
- this.runtime,
1378
- this.scopedTelegramKey(ctx.from.id.toString())
1379
- );
1380
- const roomId = createUniqueUuid(
1381
- this.runtime,
1382
- this.scopedTelegramKey(ctx.chat.id.toString())
1383
- );
1384
- const reactionId = createUniqueUuid(
1385
- this.runtime,
1386
- this.scopedTelegramKey(
1387
- `${reaction.message_id}-${ctx.from.id}-${Date.now()}`
1388
- )
1389
- );
1390
- const memory = {
1391
- id: reactionId,
1392
- entityId,
1393
- agentId: this.runtime.agentId,
1394
- roomId,
1395
- content: {
1396
- channelType: getChannelType(reaction.chat),
1397
- text: `Reacted with: ${reactionType === "emoji" ? reactionEmoji : reactionType}`,
1398
- source: "telegram",
1399
- inReplyTo: createUniqueUuid(
1400
- this.runtime,
1401
- this.scopedTelegramKey(reaction.message_id.toString())
1402
- ),
1403
- metadata: { accountId: this.accountId }
1404
- },
1405
- metadata: {
1406
- type: "custom",
1407
- eventType: "reaction",
1408
- source: "telegram",
1409
- accountId: this.accountId,
1410
- provider: "telegram",
1411
- telegram: {
1412
- chatId: reaction.chat.id.toString(),
1413
- messageId: reaction.message_id.toString()
1414
- }
1415
- },
1416
- createdAt: Date.now()
1417
- };
1418
- const callback = async (content) => {
1419
- try {
1420
- const replyText = content.text ?? "";
1421
- const sentMessage = await ctx.reply(replyText);
1422
- const responseMemory = {
1423
- id: createUniqueUuid(
1424
- this.runtime,
1425
- this.scopedTelegramKey(sentMessage.message_id.toString())
1426
- ),
1427
- entityId: this.runtime.agentId,
1428
- agentId: this.runtime.agentId,
1429
- roomId,
1430
- content: {
1431
- ...content,
1432
- inReplyTo: reactionId,
1433
- metadata: { accountId: this.accountId }
1434
- },
1435
- metadata: {
1436
- type: "message",
1437
- source: "telegram",
1438
- accountId: this.accountId,
1439
- provider: "telegram"
1440
- },
1441
- createdAt: sentMessage.date * 1e3
1442
- };
1443
- return [responseMemory];
1444
- } catch (error) {
1445
- logger2.error(
1446
- {
1447
- src: "plugin:telegram",
1448
- agentId: this.runtime.agentId,
1449
- error: error instanceof Error ? error.message : String(error)
1450
- },
1451
- "Error in reaction callback"
1452
- );
1453
- return [];
1454
- }
1455
- };
1456
- this.runtime.emitEvent(EventType.REACTION_RECEIVED, {
1457
- runtime: this.runtime,
1458
- message: memory,
1459
- callback,
1460
- source: "telegram",
1461
- accountId: this.accountId,
1462
- metadata: { accountId: this.accountId },
1463
- ctx,
1464
- originalMessage: originalMessagePlaceholder,
1465
- // Cast needed due to placeholder
1466
- reactionString: reactionType === "emoji" ? reactionEmoji : reactionType,
1467
- originalReaction: reaction.new_reaction[0]
1468
- });
1469
- this.runtime.emitEvent("TELEGRAM_REACTION_RECEIVED" /* REACTION_RECEIVED */, {
1470
- runtime: this.runtime,
1471
- message: memory,
1472
- callback,
1473
- source: "telegram",
1474
- accountId: this.accountId,
1475
- metadata: { accountId: this.accountId },
1476
- ctx,
1477
- originalMessage: originalMessagePlaceholder,
1478
- // Cast needed due to placeholder
1479
- reactionString: reactionType === "emoji" ? reactionEmoji : reactionType,
1480
- originalReaction: reaction.new_reaction[0]
1481
- });
1482
- } catch (error) {
1483
- logger2.error(
1484
- {
1485
- src: "plugin:telegram",
1486
- agentId: this.runtime.agentId,
1487
- error: error instanceof Error ? error.message : String(error)
1488
- },
1489
- "Error handling reaction"
1490
- );
1491
- }
1492
- }
1493
- /**
1494
- * Sends a message to a Telegram chat and emits appropriate events
1495
- * @param {number | string} chatId - The Telegram chat ID to send the message to
1496
- * @param {Content} content - The content to send
1497
- * @param {number} [replyToMessageId] - Optional message ID to reply to
1498
- * @returns {Promise<Message.TextMessage[]>} The sent messages
1499
- */
1500
- async sendMessage(chatId, content, replyToMessageId, messageThreadId) {
1501
- try {
1502
- const ctx = {
1503
- chat: { id: chatId },
1504
- telegram: this.bot.telegram
1505
- };
1506
- const sentMessages = await this.sendMessageInChunks(
1507
- ctx,
1508
- content,
1509
- replyToMessageId,
1510
- messageThreadId
1511
- );
1512
- if (!sentMessages?.length) {
1513
- return [];
1514
- }
1515
- const roomKey = messageThreadId ? `${chatId.toString()}-${messageThreadId}` : chatId.toString();
1516
- const roomId = createUniqueUuid(
1517
- this.runtime,
1518
- this.scopedTelegramKey(roomKey)
1519
- );
1520
- const memories = [];
1521
- const contentMetadata = content.metadata && typeof content.metadata === "object" && !Array.isArray(content.metadata) ? content.metadata : {};
1522
- for (const sentMessage of sentMessages) {
1523
- const memory = {
1524
- id: createUniqueUuid(
1525
- this.runtime,
1526
- this.scopedTelegramKey(sentMessage.message_id.toString())
1527
- ),
1528
- entityId: this.runtime.agentId,
1529
- agentId: this.runtime.agentId,
1530
- roomId,
1531
- content: {
1532
- ...content,
1533
- text: sentMessage.text,
1534
- source: "telegram",
1535
- metadata: { ...contentMetadata, accountId: this.accountId },
1536
- channelType: getChannelType({
1537
- id: typeof chatId === "string" ? Number.parseInt(chatId, 10) : chatId,
1538
- type: "private"
1539
- // Default to private, will be overridden if in context
1540
- }),
1541
- ...messageThreadId ? {
1542
- metadata: {
1543
- ...contentMetadata,
1544
- accountId: this.accountId,
1545
- threadId: messageThreadId
1546
- }
1547
- } : {}
1548
- },
1549
- metadata: {
1550
- type: "message",
1551
- source: "telegram",
1552
- accountId: this.accountId,
1553
- provider: "telegram",
1554
- fromBot: true,
1555
- fromId: this.runtime.agentId,
1556
- sourceId: this.runtime.agentId,
1557
- messageIdFull: sentMessage.message_id.toString(),
1558
- telegram: {
1559
- chatId: sentMessage.chat.id.toString(),
1560
- messageId: sentMessage.message_id.toString(),
1561
- threadId: messageThreadId?.toString()
1562
- }
1563
- },
1564
- createdAt: sentMessage.date * 1e3
1565
- };
1566
- await this.runtime.createMemory(memory, "messages");
1567
- memories.push(memory);
1568
- }
1569
- if (memories.length > 0) {
1570
- const firstMemory = memories[0];
1571
- this.runtime.emitEvent(EventType.MESSAGE_SENT, {
1572
- runtime: this.runtime,
1573
- message: firstMemory,
1574
- source: "telegram",
1575
- accountId: this.accountId,
1576
- metadata: { accountId: this.accountId }
1577
- });
1578
- const telegramMessageSentPayload = {
1579
- runtime: this.runtime,
1580
- source: "telegram",
1581
- accountId: this.accountId,
1582
- metadata: { accountId: this.accountId },
1583
- originalMessages: sentMessages,
1584
- chatId,
1585
- message: firstMemory
1586
- };
1587
- this.runtime.emitEvent(
1588
- "TELEGRAM_MESSAGE_SENT" /* MESSAGE_SENT */,
1589
- telegramMessageSentPayload
1590
- );
1591
- }
1592
- return sentMessages;
1593
- } catch (error) {
1594
- logger2.error(
1595
- {
1596
- src: "plugin:telegram",
1597
- agentId: this.runtime.agentId,
1598
- chatId,
1599
- error: error instanceof Error ? error.message : String(error)
1600
- },
1601
- "Error sending message to Telegram"
1602
- );
1603
- return [];
1604
- }
1605
- }
1606
- };
1607
-
1608
- // src/owner-pairing-service.ts
1609
- import { logger as logger3, Service } from "@elizaos/core";
1610
- var TELEGRAM_OWNER_PAIRING_SERVICE_TYPE = "OWNER_PAIRING_TELEGRAM";
1611
- var RATE_LIMIT_MAX_ATTEMPTS = 5;
1612
- var RATE_LIMIT_WINDOW_MS = 6e4;
1613
- async function auditEmit(runtime, action, outcome, metadata) {
1614
- try {
1615
- await runtime.emitEvent(
1616
- ["AUTH_AUDIT"],
1617
- {
1618
- runtime,
1619
- action,
1620
- outcome,
1621
- metadata,
1622
- source: "telegram"
1623
- }
1624
- );
1625
- } catch {
1626
- }
1627
- }
1628
- var pairAttempts = /* @__PURE__ */ new Map();
1629
- function isRateLimited(userId) {
1630
- const now = Date.now();
1631
- const windowStart = now - RATE_LIMIT_WINDOW_MS;
1632
- const attempts = (pairAttempts.get(userId) ?? []).filter(
1633
- (ts) => ts > windowStart
1634
- );
1635
- pairAttempts.set(userId, attempts);
1636
- if (attempts.length >= RATE_LIMIT_MAX_ATTEMPTS) {
1637
- return true;
1638
- }
1639
- attempts.push(now);
1640
- pairAttempts.set(userId, attempts);
1641
- return false;
1642
- }
1643
- function isValidPairCode(code) {
1644
- return /^\d{6}$/.test(code.trim());
1645
- }
1646
- function resolveVerifyService(runtime) {
1647
- try {
1648
- const svc = runtime.getService("OWNER_BIND_VERIFY");
1649
- if (svc && typeof svc === "object" && typeof svc.verifyOwnerBindFromConnector === "function") {
1650
- return svc;
1651
- }
1652
- } catch {
1653
- }
1654
- return null;
1655
- }
1656
- function resolveDisplayHandle(from) {
1657
- if (from.username) {
1658
- return `@${from.username}`;
1659
- }
1660
- if (from.first_name) {
1661
- return from.first_name;
1662
- }
1663
- return String(from.id);
1664
- }
1665
- async function handleElizaPairCommand(ctx, runtime) {
1666
- const from = ctx.from;
1667
- if (!from) {
1668
- return;
1669
- }
1670
- const userId = String(from.id);
1671
- const displayHandle = resolveDisplayHandle(from);
1672
- if (isRateLimited(userId)) {
1673
- logger3.warn(
1674
- { src: "plugin:telegram:owner-pairing", userId },
1675
- "Rate limit hit for /eliza_pair"
1676
- );
1677
- await auditEmit(
1678
- runtime,
1679
- "auth.owner.pair.telegram.rate_limited",
1680
- "failure",
1681
- { externalId: userId }
1682
- );
1683
- await ctx.reply(
1684
- "Too many pairing attempts. Please wait a moment before trying again."
1685
- );
1686
- return;
1687
- }
1688
- const message = ctx.message;
1689
- const rawText = message && "text" in message ? message.text : void 0;
1690
- let code = null;
1691
- if (typeof rawText === "string") {
1692
- const parts = rawText.trim().split(/\s+/);
1693
- if (parts.length >= 2) {
1694
- code = parts[1] ?? null;
1695
- }
1696
- }
1697
- if (!code?.trim()) {
1698
- await ctx.reply(
1699
- "Usage: /eliza\\_pair <code> \u2014 enter the 6-digit code shown in the Eliza dashboard."
1700
- );
1701
- return;
1702
- }
1703
- code = code.trim();
1704
- if (!isValidPairCode(code)) {
1705
- await ctx.reply(
1706
- "The pairing code must be exactly 6 digits. Check the Eliza dashboard and try again."
1707
- );
1708
- return;
1709
- }
1710
- const verifySvc = resolveVerifyService(runtime);
1711
- if (!verifySvc) {
1712
- logger3.error(
1713
- { src: "plugin:telegram:owner-pairing", userId },
1714
- "OWNER_BIND_VERIFY service not available \u2014 cannot complete pairing"
1715
- );
1716
- await auditEmit(
1717
- runtime,
1718
- "auth.owner.pair.telegram.service_unavailable",
1719
- "failure",
1720
- { externalId: userId }
1721
- );
1722
- await ctx.reply(
1723
- "Eliza could not reach the pairing service right now. Please try again in a moment."
1724
- );
1725
- return;
1726
- }
1727
- let result;
1728
- try {
1729
- result = await verifySvc.verifyOwnerBindFromConnector({
1730
- connector: "telegram",
1731
- externalId: userId,
1732
- displayHandle,
1733
- code
1734
- });
1735
- } catch (err) {
1736
- logger3.error(
1737
- {
1738
- src: "plugin:telegram:owner-pairing",
1739
- userId,
1740
- error: err instanceof Error ? err.message : String(err)
1741
- },
1742
- "verifyOwnerBindFromConnector threw unexpectedly"
1743
- );
1744
- await auditEmit(
1745
- runtime,
1746
- "auth.owner.pair.telegram.verify_error",
1747
- "failure",
1748
- { externalId: userId }
1749
- );
1750
- await ctx.reply(
1751
- "Something went wrong while verifying the pairing code. Please try again."
1752
- );
1753
- return;
1754
- }
1755
- if (result.success) {
1756
- logger3.info(
1757
- { src: "plugin:telegram:owner-pairing", userId, displayHandle },
1758
- "Owner pairing completed successfully"
1759
- );
1760
- await auditEmit(runtime, "auth.owner.pair.telegram.success", "success", {
1761
- externalId: userId,
1762
- displayHandle
1763
- });
1764
- await ctx.reply("Paired with Eliza. You can now log in via Telegram.");
1765
- } else {
1766
- logger3.warn(
1767
- {
1768
- src: "plugin:telegram:owner-pairing",
1769
- userId,
1770
- backendError: result.error
1771
- },
1772
- "Owner pairing rejected by backend"
1773
- );
1774
- await auditEmit(runtime, "auth.owner.pair.telegram.failure", "failure", {
1775
- externalId: userId
1776
- });
1777
- await ctx.reply(
1778
- "Pair code invalid or expired. Check the Eliza dashboard for a fresh code."
1779
- );
1780
- }
1781
- }
1782
- var TelegramOwnerPairingServiceImpl = class _TelegramOwnerPairingServiceImpl extends Service {
1783
- static serviceType = TELEGRAM_OWNER_PAIRING_SERVICE_TYPE;
1784
- capabilityDescription = "Handles Telegram-side owner pairing (command code verification) and DM login-link delivery for Eliza remote auth";
1785
- static async start(runtime) {
1786
- const service = new _TelegramOwnerPairingServiceImpl(runtime);
1787
- if (resolveVerifyService(runtime)) {
1788
- service.registerPairCommand(runtime);
1789
- logger3.info(
1790
- {
1791
- src: "plugin:telegram:owner-pairing",
1792
- agentId: runtime.agentId
1793
- },
1794
- "TelegramOwnerPairingService started; /eliza_pair command registered"
1795
- );
1796
- } else {
1797
- logger3.info(
1798
- {
1799
- src: "plugin:telegram:owner-pairing",
1800
- agentId: runtime.agentId
1801
- },
1802
- "TelegramOwnerPairingService started without /eliza_pair because OWNER_BIND_VERIFY is not registered"
1803
- );
1804
- }
1805
- return service;
1806
- }
1807
- async stop() {
1808
- pairAttempts.clear();
1809
- }
1810
- /**
1811
- * Registers the /eliza_pair command with the active Telegraf bot instance
1812
- * by looking up the TelegramService from the runtime service registry.
1813
- * Called during `start`; it is safe to call this before or after the bot
1814
- * has finished initialising because Telegraf accepts handler registration
1815
- * at any point before `launch()`.
1816
- *
1817
- * If the TelegramService is unavailable, the command is not registered.
1818
- */
1819
- registerPairCommand(runtime) {
1820
- const telegramSvc = runtime.getService(TELEGRAM_SERVICE_NAME);
1821
- if (!telegramSvc || typeof telegramSvc !== "object") {
1822
- logger3.warn(
1823
- { src: "plugin:telegram:owner-pairing", agentId: runtime.agentId },
1824
- "TelegramService unavailable during owner-pairing start; /eliza_pair command not registered"
1825
- );
1826
- return;
1827
- }
1828
- const bot = "bot" in telegramSvc ? telegramSvc.bot : null;
1829
- if (!bot || typeof bot.command !== "function") {
1830
- logger3.warn(
1831
- { src: "plugin:telegram:owner-pairing", agentId: runtime.agentId },
1832
- "Telegraf bot instance not available \u2014 /eliza_pair will not be registered"
1833
- );
1834
- return;
1835
- }
1836
- const telegrafBot = bot;
1837
- telegrafBot.command("eliza_pair", async (ctx) => {
1838
- await handleElizaPairCommand(ctx, runtime);
1839
- });
1840
- logger3.debug(
1841
- { src: "plugin:telegram:owner-pairing", agentId: runtime.agentId },
1842
- "/eliza_pair command registered with Telegraf bot"
1843
- );
1844
- }
1845
- async sendOwnerLoginDmLink(params) {
1846
- const { externalId, link } = params;
1847
- const telegramSvc = this.runtime.getService(
1848
- TELEGRAM_SERVICE_NAME
1849
- );
1850
- const bot = telegramSvc && typeof telegramSvc === "object" && "bot" in telegramSvc ? telegramSvc.bot : null;
1851
- if (!bot || typeof bot.telegram !== "object") {
1852
- throw new Error(
1853
- "Telegram bot is not available \u2014 cannot send DM login link"
1854
- );
1855
- }
1856
- const telegrafBot = bot;
1857
- const chatId = Number(externalId);
1858
- if (!Number.isFinite(chatId) || chatId <= 0) {
1859
- throw new Error(
1860
- `Invalid Telegram externalId "${externalId}" \u2014 must be a positive numeric user ID`
1861
- );
1862
- }
1863
- const message = `Click to log in to Eliza: ${link}
1864
-
1865
- _This link expires in 5 minutes. Do not share it._`;
1866
- try {
1867
- await telegrafBot.telegram.sendMessage(chatId, message, {
1868
- parse_mode: "Markdown"
1869
- });
1870
- logger3.info(
1871
- { src: "plugin:telegram:owner-pairing", externalId },
1872
- "Login DM link sent"
1873
- );
1874
- } catch (err) {
1875
- throw new Error(
1876
- `Failed to send DM login link to Telegram user ${externalId}: ${err instanceof Error ? err.message : String(err)}`
1877
- );
1878
- }
1879
- }
1880
- };
1881
-
1882
- // src/service.ts
1883
- import {
1884
- ChannelType as ChannelType2,
1885
- createUniqueUuid as createUniqueUuid2,
1886
- EventType as EventType2,
1887
- logger as logger4,
1888
- Role,
1889
- Service as Service2
1890
- } from "@elizaos/core";
1891
- import { Telegraf } from "telegraf";
1892
- var CANONICAL_OWNER_SETTING_KEYS = ["ELIZA_ADMIN_ENTITY_ID"];
1893
- var TELEGRAM_CONNECTOR_CONTEXTS = ["social", "connectors"];
1894
- var TELEGRAM_CONNECTOR_CAPABILITIES = [
1895
- "send_message",
1896
- "resolve_targets",
1897
- "list_rooms",
1898
- "chat_context",
1899
- "user_context"
1900
- ];
1901
- var TELEGRAM_CHAT_ID_PATTERN = /^-?\d+$/;
1902
- var TELEGRAM_THREADED_CHANNEL_PATTERN = /^(-?\d+)-(\d+)$/;
1903
- function normalizeConnectorLimit(limit, fallback = 50) {
1904
- if (!Number.isFinite(limit) || !limit || limit <= 0) {
1905
- return fallback;
1906
- }
1907
- return Math.min(Math.floor(limit), 200);
1908
- }
1909
- function filterMemoriesByQuery(memories, query, limit) {
1910
- const normalized = query.trim().toLowerCase();
1911
- if (!normalized) {
1912
- return memories.slice(0, limit);
1913
- }
1914
- return memories.filter((memory) => {
1915
- const text = typeof memory.content?.text === "string" ? memory.content.text : "";
1916
- return text.toLowerCase().includes(normalized);
1917
- }).slice(0, limit);
1918
- }
1919
- var ACTIVE_TELEGRAM_POLLERS = /* @__PURE__ */ new Map();
1920
- function getCanonicalOwnerId(runtime) {
1921
- for (const key of CANONICAL_OWNER_SETTING_KEYS) {
1922
- const value = runtime.getSetting(key);
1923
- if (typeof value !== "string") {
1924
- continue;
1925
- }
1926
- const trimmed = value.trim();
1927
- if (trimmed.length > 0) {
1928
- return trimmed;
1929
- }
1930
- }
1931
- return null;
1932
- }
1933
- function getTelegramChatDisplayName(chat, fallback) {
1934
- if (!chat) {
1935
- return fallback;
1936
- }
1937
- if ("title" in chat && typeof chat.title === "string" && chat.title.trim()) {
1938
- return chat.title;
1939
- }
1940
- if ("first_name" in chat && typeof chat.first_name === "string" && chat.first_name.trim()) {
1941
- return chat.first_name;
1942
- }
1943
- if ("username" in chat && typeof chat.username === "string" && chat.username.trim()) {
1944
- return chat.username;
1945
- }
1946
- return fallback;
1947
- }
1948
- function normalizeTelegramConnectorQuery(value) {
1949
- return value.trim().replace(/^@/, "").toLowerCase();
1950
- }
1951
- function scoreTelegramConnectorMatch(query, id, labels) {
1952
- if (!query) {
1953
- return 0.45;
1954
- }
1955
- if (id.toLowerCase() === query) {
1956
- return 1;
1957
- }
1958
- let bestScore = 0;
1959
- for (const label of labels) {
1960
- const normalized = label?.trim().replace(/^@/, "").toLowerCase();
1961
- if (!normalized) {
1962
- continue;
1963
- }
1964
- if (normalized === query) {
1965
- bestScore = Math.max(bestScore, 0.95);
1966
- } else if (normalized.startsWith(query)) {
1967
- bestScore = Math.max(bestScore, 0.85);
1968
- } else if (normalized.includes(query)) {
1969
- bestScore = Math.max(bestScore, 0.7);
1970
- }
1971
- }
1972
- return bestScore;
1973
- }
1974
- function parseTelegramTargetParts(channelId, explicitThreadId) {
1975
- const explicitThreadNumber = explicitThreadId && /^\d+$/.test(explicitThreadId) ? Number.parseInt(explicitThreadId, 10) : void 0;
1976
- const threadedMatch = channelId.match(TELEGRAM_THREADED_CHANNEL_PATTERN);
1977
- if (threadedMatch) {
1978
- return {
1979
- chatId: threadedMatch[1],
1980
- threadId: explicitThreadNumber ?? Number.parseInt(threadedMatch[2], 10)
1981
- };
1982
- }
1983
- return { chatId: channelId, threadId: explicitThreadNumber };
1984
- }
1985
- function telegramChatKind(chat) {
1986
- if (chat.type === "private") {
1987
- return "user";
1988
- }
1989
- if (chat.type === "channel") {
1990
- return "channel";
1991
- }
1992
- return "group";
1993
- }
1994
- var TelegramService = class _TelegramService extends Service2 {
1995
- static serviceType = TELEGRAM_SERVICE_NAME;
1996
- capabilityDescription = "The agent is able to send and receive messages on telegram";
1997
- bot = null;
1998
- messageManager = null;
1999
- knownChats = /* @__PURE__ */ new Map();
2000
- syncedEntityIds = /* @__PURE__ */ new Set();
2001
- botToken;
2002
- defaultAccountId = DEFAULT_ACCOUNT_ID;
2003
- accountStates = /* @__PURE__ */ new Map();
2004
- /**
2005
- * Constructor for TelegramService class.
2006
- * @param {IAgentRuntime} runtime - The runtime object for the agent.
2007
- */
2008
- constructor(runtime) {
2009
- super(runtime);
2010
- if (!runtime) {
2011
- this.bot = null;
2012
- this.messageManager = null;
2013
- this.botToken = null;
2014
- return;
2015
- }
2016
- logger4.debug(
2017
- { src: "plugin:telegram", agentId: runtime.agentId },
2018
- "Constructing TelegramService"
2019
- );
2020
- this.defaultAccountId = resolveDefaultTelegramAccountId(runtime);
2021
- const account = resolveTelegramAccount(runtime, this.defaultAccountId);
2022
- this.botToken = account.botToken ?? null;
2023
- if (!account.botToken) {
2024
- logger4.warn(
2025
- {
2026
- src: "plugin:telegram",
2027
- agentId: runtime.agentId,
2028
- accountId: account.accountId
2029
- },
2030
- "Bot token not provided, Telegram functionality unavailable"
2031
- );
2032
- this.bot = null;
2033
- this.messageManager = null;
2034
- return;
2035
- }
2036
- try {
2037
- const state = this.createAccountRuntime(account);
2038
- this.setDefaultAccountState(state);
2039
- logger4.debug(
2040
- {
2041
- src: "plugin:telegram",
2042
- agentId: runtime.agentId,
2043
- accountId: account.accountId
2044
- },
2045
- "TelegramService constructor completed"
2046
- );
2047
- } catch (error) {
2048
- logger4.error(
2049
- {
2050
- src: "plugin:telegram",
2051
- agentId: runtime.agentId,
2052
- error: error instanceof Error ? error.message : String(error)
2053
- },
2054
- "Failed to initialize Telegram bot"
2055
- );
2056
- this.bot = null;
2057
- this.messageManager = null;
2058
- }
2059
- }
2060
- createAccountRuntime(account) {
2061
- if (!account.botToken) {
2062
- throw new Error(`Telegram account ${account.accountId} has no bot token`);
2063
- }
2064
- const bot = new Telegraf(account.botToken, {
2065
- telegram: { apiRoot: account.apiRoot }
2066
- });
2067
- const messageManager = new MessageManager(
2068
- bot,
2069
- this.runtime,
2070
- account.accountId
2071
- );
2072
- return {
2073
- accountId: account.accountId,
2074
- account,
2075
- bot,
2076
- messageManager
2077
- };
2078
- }
2079
- setDefaultAccountState(state) {
2080
- this.accountStates.set(state.accountId, state);
2081
- if (state.accountId === this.defaultAccountId || !this.bot) {
2082
- this.bot = state.bot;
2083
- this.messageManager = state.messageManager;
2084
- this.botToken = state.account.botToken ?? null;
2085
- }
2086
- }
2087
- getDefaultAccountState() {
2088
- if (!(this.accountStates instanceof Map) || this.accountStates.size === 0) {
2089
- return null;
2090
- }
2091
- return this.accountStates.get(this.defaultAccountId) ?? this.accountStates.values().next().value ?? null;
2092
- }
2093
- getAccountState(accountId) {
2094
- if (!(this.accountStates instanceof Map) || this.accountStates.size === 0) {
2095
- return null;
2096
- }
2097
- if (accountId) {
2098
- return this.accountStates.get(normalizeTelegramAccountId(accountId)) ?? null;
2099
- }
2100
- return this.getDefaultAccountState();
2101
- }
2102
- getAccountIds() {
2103
- if (this.accountStates instanceof Map && this.accountStates.size > 0) {
2104
- return Array.from(this.accountStates.keys());
2105
- }
2106
- return [normalizeTelegramAccountId(this.defaultAccountId)];
2107
- }
2108
- resolveAccountIdFromContext(context, target) {
2109
- const scopedTarget = target;
2110
- const scopedContext = context;
2111
- return scopedTarget?.accountId ?? scopedContext?.target?.accountId ?? scopedContext?.accountId ?? scopedContext?.account?.accountId ?? void 0;
2112
- }
2113
- async resolveAccountIdForTarget(runtime, target, fallback) {
2114
- const direct = target?.accountId ?? fallback?.accountId;
2115
- if (direct) {
2116
- return normalizeTelegramAccountId(direct);
2117
- }
2118
- const roomId = target?.roomId ?? fallback?.roomId;
2119
- if (roomId && typeof runtime.getRoom === "function") {
2120
- const room = await runtime.getRoom(roomId);
2121
- const metadata = room?.metadata;
2122
- if (typeof metadata?.accountId === "string" && metadata.accountId.trim()) {
2123
- return normalizeTelegramAccountId(metadata.accountId);
2124
- }
2125
- const telegram = metadata?.telegram && typeof metadata.telegram === "object" ? metadata.telegram : void 0;
2126
- if (typeof telegram?.accountId === "string" && telegram.accountId.trim()) {
2127
- return normalizeTelegramAccountId(telegram.accountId);
2128
- }
2129
- }
2130
- return normalizeTelegramAccountId(this.defaultAccountId);
2131
- }
2132
- scopedTelegramKey(key, accountId) {
2133
- const normalized = normalizeTelegramAccountId(
2134
- accountId ?? this.defaultAccountId
2135
- );
2136
- return normalized === DEFAULT_ACCOUNT_ID ? key : `${normalized}:${key}`;
2137
- }
2138
- knownChatKeyMatchesAccount(key, chat, accountId) {
2139
- return key === this.scopedTelegramKey(chat.id.toString(), accountId);
2140
- }
2141
- /**
2142
- * Starts the Telegram service for the given runtime.
2143
- *
2144
- * @param {IAgentRuntime} runtime - The agent runtime to start the Telegram service for.
2145
- * @returns {Promise<TelegramService>} A promise that resolves with the initialized TelegramService.
2146
- */
2147
- static async start(runtime) {
2148
- const service = new _TelegramService(runtime);
2149
- for (const account of listEnabledTelegramAccounts(runtime)) {
2150
- if (!service.getAccountState(account.accountId)) {
2151
- service.setDefaultAccountState(service.createAccountRuntime(account));
2152
- }
2153
- }
2154
- if (!service.bot) {
2155
- logger4.warn(
2156
- { src: "plugin:telegram", agentId: runtime.agentId },
2157
- "Service started without bot functionality"
2158
- );
2159
- return service;
2160
- }
2161
- const maxRetries = 5;
2162
- for (const state of service.accountStates.values()) {
2163
- let retryCount = 0;
2164
- let lastError = null;
2165
- while (retryCount < maxRetries) {
2166
- try {
2167
- logger4.info(
2168
- {
2169
- src: "plugin:telegram",
2170
- agentId: runtime.agentId,
2171
- agentName: runtime.character.name,
2172
- accountId: state.accountId
2173
- },
2174
- "Starting Telegram bot"
2175
- );
2176
- await service.initializeBot(state);
2177
- service.setupMiddlewares(state);
2178
- service.setupMessageHandlers(state);
2179
- await state.bot.telegram.getMe();
2180
- logger4.success(
2181
- {
2182
- src: "plugin:telegram",
2183
- agentId: runtime.agentId,
2184
- agentName: runtime.character.name,
2185
- accountId: state.accountId
2186
- },
2187
- "Telegram bot started successfully"
2188
- );
2189
- break;
2190
- } catch (error) {
2191
- lastError = error instanceof Error ? error : new Error(String(error));
2192
- logger4.error(
2193
- {
2194
- src: "plugin:telegram",
2195
- agentId: runtime.agentId,
2196
- accountId: state.accountId,
2197
- attempt: retryCount + 1,
2198
- error: lastError.message
2199
- },
2200
- "Initialization attempt failed"
2201
- );
2202
- retryCount++;
2203
- if (retryCount < maxRetries) {
2204
- const delay = 2 ** retryCount * 1e3;
2205
- logger4.info(
2206
- {
2207
- src: "plugin:telegram",
2208
- agentId: runtime.agentId,
2209
- accountId: state.accountId,
2210
- delaySeconds: delay / 1e3
2211
- },
2212
- "Retrying initialization"
2213
- );
2214
- await new Promise((resolve) => setTimeout(resolve, delay));
2215
- }
2216
- }
2217
- }
2218
- if (retryCount >= maxRetries) {
2219
- logger4.error(
2220
- {
2221
- src: "plugin:telegram",
2222
- agentId: runtime.agentId,
2223
- accountId: state.accountId,
2224
- maxRetries,
2225
- error: lastError?.message
2226
- },
2227
- "Initialization failed after all attempts"
2228
- );
2229
- }
2230
- }
2231
- return service;
2232
- }
2233
- /**
2234
- * Stops the agent runtime.
2235
- * @param {IAgentRuntime} runtime - The agent runtime to stop
2236
- */
2237
- static async stop(runtime) {
2238
- const tgClient = await runtime.getService(TELEGRAM_SERVICE_NAME);
2239
- if (tgClient) {
2240
- await tgClient.stop();
2241
- }
2242
- }
2243
- /**
2244
- * Asynchronously stops the bot.
2245
- *
2246
- * @returns A Promise that resolves once the bot has stopped.
2247
- */
2248
- async stop() {
2249
- const states = this.accountStates instanceof Map ? Array.from(this.accountStates.values()) : [];
2250
- if (states.length > 0) {
2251
- for (const state of states) {
2252
- state.bot.stop("service-stop");
2253
- const token = state.account.botToken;
2254
- if (token) {
2255
- const active = ACTIVE_TELEGRAM_POLLERS.get(token);
2256
- if (active?.bot === state.bot) {
2257
- ACTIVE_TELEGRAM_POLLERS.delete(token);
2258
- }
2259
- }
2260
- }
2261
- return;
2262
- }
2263
- const bot = this.bot;
2264
- if (bot) {
2265
- bot.stop("service-stop");
2266
- if (this.botToken) {
2267
- const active = ACTIVE_TELEGRAM_POLLERS.get(this.botToken);
2268
- if (active?.bot === bot) {
2269
- ACTIVE_TELEGRAM_POLLERS.delete(this.botToken);
2270
- }
2271
- }
2272
- }
2273
- }
2274
- /**
2275
- * Initializes the Telegram bot by launching it, getting bot info, and setting up message manager.
2276
- * @returns {Promise<void>} A Promise that resolves when the initialization is complete.
2277
- */
2278
- async initializeBot(state) {
2279
- const activeState = state ?? this.getDefaultAccountState();
2280
- const bot = activeState?.bot ?? this.bot;
2281
- if (!bot) {
2282
- throw new Error("Telegram bot is not initialized");
2283
- }
2284
- const botToken = activeState?.account.botToken ?? this.botToken;
2285
- const accountId = activeState?.accountId ?? this.defaultAccountId;
2286
- if (botToken) {
2287
- const active = ACTIVE_TELEGRAM_POLLERS.get(botToken);
2288
- if (active && active.bot !== bot) {
2289
- logger4.warn(
2290
- {
2291
- src: "plugin:telegram",
2292
- agentId: this.runtime.agentId,
2293
- accountId,
2294
- previousAgentId: active.agentId
2295
- },
2296
- "Stopping existing Telegram poller before launching a new one"
2297
- );
2298
- try {
2299
- active.bot.stop("replaced-by-new-runtime");
2300
- } catch (error) {
2301
- logger4.warn(
2302
- {
2303
- src: "plugin:telegram",
2304
- agentId: this.runtime.agentId,
2305
- accountId,
2306
- error: error instanceof Error ? error.message : String(error)
2307
- },
2308
- "Failed to stop previous Telegram poller cleanly"
2309
- );
2310
- }
2311
- ACTIVE_TELEGRAM_POLLERS.delete(botToken);
2312
- await new Promise((resolve) => setTimeout(resolve, 300));
2313
- }
2314
- }
2315
- bot.start((ctx) => {
2316
- const slashStartPayload = {
2317
- ctx,
2318
- runtime: this.runtime,
2319
- source: "telegram",
2320
- accountId,
2321
- metadata: { accountId }
2322
- };
2323
- this.runtime.emitEvent(
2324
- "TELEGRAM_SLASH_START" /* SLASH_START */,
2325
- slashStartPayload
2326
- );
2327
- });
2328
- await bot.launch({
2329
- dropPendingUpdates: true,
2330
- allowedUpdates: ["message", "message_reaction"]
2331
- });
2332
- if (botToken) {
2333
- ACTIVE_TELEGRAM_POLLERS.set(botToken, {
2334
- bot,
2335
- agentId: this.runtime.agentId,
2336
- accountId
2337
- });
2338
- }
2339
- const botInfo = await bot.telegram.getMe();
2340
- logger4.debug(
2341
- {
2342
- src: "plugin:telegram",
2343
- agentId: this.runtime.agentId,
2344
- accountId,
2345
- botId: botInfo.id,
2346
- botUsername: botInfo.username
2347
- },
2348
- "Bot info retrieved"
2349
- );
2350
- process.once("SIGINT", () => bot.stop("SIGINT"));
2351
- process.once("SIGTERM", () => bot.stop("SIGTERM"));
2352
- }
2353
- /**
2354
- * Sets up the middleware chain for preprocessing messages before they reach handlers.
2355
- * This critical method establishes a sequential processing pipeline that:
2356
- *
2357
- * 1. Authorization - Verifies if a chat is allowed to interact with the bot based on configured settings
2358
- * 2. Chat Discovery - Ensures chat entities and worlds exist in the runtime, creating them if needed
2359
- * 3. Forum Topics - Handles Telegram forum topics as separate rooms for better conversation management
2360
- * 4. Entity Synchronization - Ensures message senders are properly synchronized as entities
2361
- *
2362
- * The middleware chain runs in sequence for each message, with each step potentially
2363
- * enriching the context or stopping processing if conditions aren't met.
2364
- * This preprocessing is essential for maintaining consistent state before message handlers execute.
2365
- *
2366
- * @private
2367
- */
2368
- setupMiddlewares(state) {
2369
- const bot = state?.bot ?? this.bot;
2370
- const accountId = state?.accountId ?? this.defaultAccountId;
2371
- bot?.use((ctx, next) => this.authorizationMiddleware(ctx, next, accountId));
2372
- bot?.use((ctx, next) => this.chatAndEntityMiddleware(ctx, next, accountId));
2373
- }
2374
- /**
2375
- * Authorization middleware - checks if chat is allowed to interact with the bot
2376
- * based on the TELEGRAM_ALLOWED_CHATS configuration.
2377
- *
2378
- * @param {Context} ctx - The context of the incoming update
2379
- * @param {Function} next - The function to call to proceed to the next middleware
2380
- * @returns {Promise<void>}
2381
- * @private
2382
- */
2383
- async authorizationMiddleware(ctx, next, accountId = this.defaultAccountId) {
2384
- if (!await this.isGroupAuthorized(ctx, accountId)) {
2385
- logger4.debug(
2386
- {
2387
- src: "plugin:telegram",
2388
- agentId: this.runtime.agentId,
2389
- accountId,
2390
- chatId: ctx.chat?.id
2391
- },
2392
- "Chat not authorized, skipping"
2393
- );
2394
- return;
2395
- }
2396
- await next();
2397
- }
2398
- /**
2399
- * Chat and entity management middleware - handles new chats, forum topics, and entity synchronization.
2400
- * This middleware implements decision logic to determine which operations are needed based on
2401
- * the chat type and whether we've seen this chat before.
2402
- *
2403
- * @param {Context} ctx - The context of the incoming update
2404
- * @param {Function} next - The function to call to proceed to the next middleware
2405
- * @returns {Promise<void>}
2406
- * @private
2407
- */
2408
- async chatAndEntityMiddleware(ctx, next, accountId = this.defaultAccountId) {
2409
- if (!ctx.chat) {
2410
- return next();
2411
- }
2412
- const chatId = ctx.chat.id.toString();
2413
- if (!this.knownChats.has(this.scopedTelegramKey(chatId, accountId))) {
2414
- await this.handleNewChat(ctx, accountId);
2415
- return next();
2416
- }
2417
- await this.processExistingChat(ctx, accountId);
2418
- await next();
2419
- }
2420
- /**
2421
- * Process an existing chat based on chat type and message properties.
2422
- * Different chat types require different processing steps.
2423
- *
2424
- * @param {Context} ctx - The context of the incoming update
2425
- * @returns {Promise<void>}
2426
- * @private
2427
- */
2428
- async processExistingChat(ctx, accountId = this.defaultAccountId) {
2429
- if (!ctx.chat) {
2430
- return;
2431
- }
2432
- const chat = ctx.chat;
2433
- if (chat.type === "supergroup" && chat.is_forum && ctx.message?.message_thread_id) {
2434
- try {
2435
- await this.handleForumTopic(ctx, accountId);
2436
- } catch (error) {
2437
- logger4.error(
2438
- {
2439
- src: "plugin:telegram",
2440
- agentId: this.runtime.agentId,
2441
- accountId,
2442
- chatId: chat.id,
2443
- error: error instanceof Error ? error.message : String(error)
2444
- },
2445
- "Error handling forum topic"
2446
- );
2447
- }
2448
- }
2449
- if (ctx.from && ctx.chat.type !== "private") {
2450
- await this.syncEntity(ctx, accountId);
2451
- }
2452
- }
2453
- /**
2454
- * Sets up message and reaction handlers for the bot.
2455
- * Configures event handlers to process incoming messages and reactions.
2456
- *
2457
- * @private
2458
- */
2459
- setupMessageHandlers(state) {
2460
- const bot = state?.bot ?? this.bot;
2461
- const messageManager = state?.messageManager ?? this.messageManager;
2462
- const accountId = state?.accountId ?? this.defaultAccountId;
2463
- bot?.on("message", async (ctx) => {
2464
- try {
2465
- await messageManager?.handleMessage(ctx);
2466
- } catch (error) {
2467
- logger4.error(
2468
- {
2469
- src: "plugin:telegram",
2470
- agentId: this.runtime.agentId,
2471
- accountId,
2472
- error: error instanceof Error ? error.message : String(error)
2473
- },
2474
- "Error handling message"
2475
- );
2476
- }
2477
- });
2478
- bot?.on("message_reaction", async (ctx) => {
2479
- try {
2480
- await messageManager?.handleReaction(ctx);
2481
- } catch (error) {
2482
- logger4.error(
2483
- {
2484
- src: "plugin:telegram",
2485
- agentId: this.runtime.agentId,
2486
- accountId,
2487
- error: error instanceof Error ? error.message : String(error)
2488
- },
2489
- "Error handling reaction"
2490
- );
2491
- }
2492
- });
2493
- }
2494
- /**
2495
- * Checks if a group is authorized, based on the TELEGRAM_ALLOWED_CHATS setting.
2496
- * @param {Context} ctx - The context of the incoming update.
2497
- * @returns {Promise<boolean>} A Promise that resolves with a boolean indicating if the group is authorized.
2498
- */
2499
- async isGroupAuthorized(ctx, accountId = this.defaultAccountId) {
2500
- const chatId = ctx.chat?.id.toString();
2501
- if (!chatId) {
2502
- return false;
2503
- }
2504
- const accountAllowedChats = this.getAccountState(accountId)?.account.config.allowedChats;
2505
- if (accountAllowedChats?.length) {
2506
- return accountAllowedChats.includes(chatId);
2507
- }
2508
- const allowedChats = this.runtime.getSetting("TELEGRAM_ALLOWED_CHATS");
2509
- if (!allowedChats) {
2510
- return true;
2511
- }
2512
- try {
2513
- const allowedChatsList = JSON.parse(allowedChats);
2514
- return allowedChatsList.includes(chatId);
2515
- } catch (error) {
2516
- logger4.error(
2517
- {
2518
- src: "plugin:telegram",
2519
- agentId: this.runtime.agentId,
2520
- accountId,
2521
- error: error instanceof Error ? error.message : String(error)
2522
- },
2523
- "Error parsing TELEGRAM_ALLOWED_CHATS"
2524
- );
2525
- return false;
2526
- }
2527
- }
2528
- /**
2529
- * Synchronizes an entity from a message context with the runtime system.
2530
- * This method handles three cases:
2531
- * 1. Message sender - most common case
2532
- * 2. New chat member - when a user joins the chat
2533
- * 3. Left chat member - when a user leaves the chat
2534
- *
2535
- * @param {Context} ctx - The context of the incoming update
2536
- * @returns {Promise<void>}
2537
- * @private
2538
- */
2539
- async syncEntity(ctx, accountId = this.defaultAccountId) {
2540
- if (!ctx.chat) {
2541
- return;
2542
- }
2543
- const chat = ctx.chat;
2544
- const chatId = chat.id.toString();
2545
- const worldId = createUniqueUuid2(
2546
- this.runtime,
2547
- this.scopedTelegramKey(chatId, accountId)
2548
- );
2549
- const roomId = createUniqueUuid2(
2550
- this.runtime,
2551
- this.scopedTelegramKey(
2552
- ctx.message?.message_thread_id ? `${ctx.chat.id}-${ctx.message.message_thread_id}` : ctx.chat.id.toString(),
2553
- accountId
2554
- )
2555
- );
2556
- await this.syncMessageSender(ctx, worldId, roomId, chatId, accountId);
2557
- await this.syncNewChatMember(ctx, worldId, roomId, chatId, accountId);
2558
- await this.syncLeftChatMember(ctx, accountId);
2559
- }
2560
- /**
2561
- * Synchronizes the message sender entity with the runtime system.
2562
- * This is the most common entity sync case.
2563
- *
2564
- * @param {Context} ctx - The context of the incoming update
2565
- * @param {UUID} worldId - The ID of the world
2566
- * @param {UUID} roomId - The ID of the room
2567
- * @param {string} chatId - The ID of the chat
2568
- * @returns {Promise<void>}
2569
- * @private
2570
- */
2571
- async syncMessageSender(ctx, worldId, roomId, chatId, accountId = this.defaultAccountId) {
2572
- if (ctx.from) {
2573
- const telegramId = ctx.from.id.toString();
2574
- const entityId = createUniqueUuid2(
2575
- this.runtime,
2576
- this.scopedTelegramKey(telegramId, accountId)
2577
- );
2578
- if (this.syncedEntityIds.has(entityId)) {
2579
- return;
2580
- }
2581
- await this.runtime.ensureConnection({
2582
- entityId,
2583
- roomId,
2584
- roomName: getTelegramChatDisplayName(ctx.chat, chatId),
2585
- userName: ctx.from.username,
2586
- userId: telegramId,
2587
- name: ctx.from.first_name || ctx.from.username || "Unknown User",
2588
- source: "telegram",
2589
- channelId: chatId,
2590
- type: ChannelType2.GROUP,
2591
- worldId
2592
- });
2593
- this.syncedEntityIds.add(entityId);
2594
- }
2595
- }
2596
- /**
2597
- * Synchronizes a new chat member entity with the runtime system.
2598
- * Triggered when a user joins the chat.
2599
- *
2600
- * @param {Context} ctx - The context of the incoming update
2601
- * @param {UUID} worldId - The ID of the world
2602
- * @param {UUID} roomId - The ID of the room
2603
- * @param {string} chatId - The ID of the chat
2604
- * @returns {Promise<void>}
2605
- * @private
2606
- */
2607
- async syncNewChatMember(ctx, worldId, roomId, chatId, accountId = this.defaultAccountId) {
2608
- if (ctx.message && "new_chat_members" in ctx.message) {
2609
- for (const newMember of ctx.message.new_chat_members) {
2610
- const telegramId = newMember.id.toString();
2611
- const entityId = createUniqueUuid2(
2612
- this.runtime,
2613
- this.scopedTelegramKey(telegramId, accountId)
2614
- );
2615
- if (this.syncedEntityIds.has(entityId)) {
2616
- continue;
2617
- }
2618
- await this.runtime.ensureConnection({
2619
- entityId,
2620
- roomId,
2621
- roomName: getTelegramChatDisplayName(ctx.chat, chatId),
2622
- userName: newMember.username,
2623
- userId: telegramId,
2624
- name: newMember.first_name || newMember.username || "Unknown User",
2625
- source: "telegram",
2626
- channelId: chatId,
2627
- type: ChannelType2.GROUP,
2628
- worldId
2629
- });
2630
- this.syncedEntityIds.add(entityId);
2631
- const entityJoinedPayload = {
2632
- runtime: this.runtime,
2633
- entityId,
2634
- worldId,
2635
- source: "telegram",
2636
- accountId,
2637
- metadata: { accountId },
2638
- telegramUser: {
2639
- id: newMember.id,
2640
- username: newMember.username,
2641
- first_name: newMember.first_name
2642
- }
2643
- };
2644
- this.runtime.emitEvent(
2645
- "TELEGRAM_ENTITY_JOINED" /* ENTITY_JOINED */,
2646
- entityJoinedPayload
2647
- );
2648
- }
2649
- }
2650
- }
2651
- /**
2652
- * Updates entity status when a user leaves the chat.
2653
- *
2654
- * @param {Context} ctx - The context of the incoming update
2655
- * @returns {Promise<void>}
2656
- * @private
2657
- */
2658
- async syncLeftChatMember(ctx, accountId = this.defaultAccountId) {
2659
- if (ctx.message && "left_chat_member" in ctx.message) {
2660
- const leftMember = ctx.message.left_chat_member;
2661
- const telegramId = leftMember.id.toString();
2662
- const entityId = createUniqueUuid2(
2663
- this.runtime,
2664
- this.scopedTelegramKey(telegramId, accountId)
2665
- );
2666
- const existingEntity = await this.runtime.getEntityById(entityId);
2667
- if (existingEntity) {
2668
- existingEntity.metadata = {
2669
- ...existingEntity.metadata,
2670
- accountId,
2671
- status: "INACTIVE",
2672
- leftAt: Date.now()
2673
- };
2674
- await this.runtime.updateEntity(existingEntity);
2675
- }
2676
- }
2677
- }
2678
- /**
2679
- * Handles forum topics by creating appropriate rooms in the runtime system.
2680
- * This enables proper conversation management for Telegram's forum feature.
2681
- *
2682
- * @param {Context} ctx - The context of the incoming update
2683
- * @returns {Promise<void>}
2684
- * @private
2685
- */
2686
- async handleForumTopic(ctx, accountId = this.defaultAccountId) {
2687
- if (!ctx.chat || !ctx.message?.message_thread_id) {
2688
- return;
2689
- }
2690
- const chat = ctx.chat;
2691
- const chatId = chat.id.toString();
2692
- const worldId = createUniqueUuid2(
2693
- this.runtime,
2694
- this.scopedTelegramKey(chatId, accountId)
2695
- );
2696
- const room = await this.buildForumTopicRoom(ctx, worldId, accountId);
2697
- if (!room) {
2698
- return;
2699
- }
2700
- await this.runtime.ensureRoomExists(room);
2701
- }
2702
- /**
2703
- * Builds entity for message sender
2704
- */
2705
- buildMsgSenderEntity(from, accountId = this.defaultAccountId) {
2706
- if (!from) {
2707
- return null;
2708
- }
2709
- const userId = createUniqueUuid2(
2710
- this.runtime,
2711
- this.scopedTelegramKey(from.id.toString(), accountId)
2712
- );
2713
- const telegramId = from.id.toString();
2714
- return {
2715
- id: userId,
2716
- agentId: this.runtime.agentId,
2717
- names: [from.first_name || from.username || "Unknown User"],
2718
- metadata: {
2719
- source: "telegram",
2720
- accountId,
2721
- telegram: {
2722
- accountId,
2723
- id: telegramId,
2724
- username: from.username,
2725
- name: from.first_name || from.username || "Unknown User"
2726
- }
2727
- }
2728
- };
2729
- }
2730
- /**
2731
- * Handles new chat discovery and emits WORLD_JOINED event.
2732
- * This is a critical function that ensures new chats are properly
2733
- * registered in the runtime system and appropriate events are emitted.
2734
- *
2735
- * @param {Context} ctx - The context of the incoming update
2736
- * @returns {Promise<void>}
2737
- * @private
2738
- */
2739
- async handleNewChat(ctx, accountId = this.defaultAccountId) {
2740
- if (!ctx.chat) {
2741
- return;
2742
- }
2743
- const chat = ctx.chat;
2744
- const chatId = chat.id.toString();
2745
- this.knownChats.set(this.scopedTelegramKey(chatId, accountId), chat);
2746
- const { chatTitle, channelType } = this.getChatTypeInfo(chat);
2747
- const worldId = createUniqueUuid2(
2748
- this.runtime,
2749
- this.scopedTelegramKey(chatId, accountId)
2750
- );
2751
- const existingWorld = await this.runtime.getWorld(worldId);
2752
- if (existingWorld) {
2753
- return;
2754
- }
2755
- const userId = ctx.from ? createUniqueUuid2(
2756
- this.runtime,
2757
- this.scopedTelegramKey(ctx.from.id.toString(), accountId)
2758
- ) : null;
2759
- let admins = [];
2760
- let owner = null;
2761
- if (chat.type === "group" || chat.type === "supergroup" || chat.type === "channel") {
2762
- try {
2763
- const chatAdmins = await ctx.getChatAdministrators();
2764
- admins = chatAdmins;
2765
- const foundOwner = admins.find(
2766
- (admin) => admin.status === "creator"
2767
- );
2768
- owner = foundOwner || null;
2769
- } catch (error) {
2770
- logger4.warn(
2771
- {
2772
- src: "plugin:telegram",
2773
- agentId: this.runtime.agentId,
2774
- chatId,
2775
- error: error instanceof Error ? error.message : String(error)
2776
- },
2777
- "Could not get chat administrators"
2778
- );
2779
- }
2780
- }
2781
- const canonicalOwnerId = getCanonicalOwnerId(this.runtime);
2782
- let ownerId = canonicalOwnerId ?? userId;
2783
- if (!canonicalOwnerId && owner) {
2784
- ownerId = createUniqueUuid2(
2785
- this.runtime,
2786
- this.scopedTelegramKey(String(owner.user.id), accountId)
2787
- );
2788
- }
2789
- const world = {
2790
- id: worldId,
2791
- name: chatTitle,
2792
- agentId: this.runtime.agentId,
2793
- messageServerId: chatId,
2794
- metadata: {
2795
- source: "telegram",
2796
- accountId,
2797
- ...ownerId && { ownership: { ownerId } },
2798
- roles: ownerId ? {
2799
- [ownerId]: Role.OWNER
2800
- } : {},
2801
- chatType: chat.type,
2802
- isForumEnabled: chat.type === "supergroup" && chat.is_forum
2803
- }
2804
- };
2805
- await this.runtime.ensureWorldExists(world);
2806
- const generalRoom = {
2807
- id: createUniqueUuid2(
2808
- this.runtime,
2809
- this.scopedTelegramKey(chatId, accountId)
2810
- ),
2811
- name: chatTitle,
2812
- source: "telegram",
2813
- type: channelType,
2814
- channelId: chatId,
2815
- serverId: chatId,
2816
- worldId,
2817
- metadata: {
2818
- source: "telegram",
2819
- accountId,
2820
- telegram: { accountId, chatId }
2821
- }
2822
- };
2823
- await this.runtime.ensureRoomExists(generalRoom);
2824
- const rooms = [generalRoom];
2825
- if (chat.type === "supergroup" && chat.is_forum && ctx.message?.message_thread_id) {
2826
- const topicRoom = await this.buildForumTopicRoom(ctx, worldId, accountId);
2827
- if (topicRoom) {
2828
- rooms.push(topicRoom);
2829
- await this.runtime.ensureRoomExists(topicRoom);
2830
- }
2831
- }
2832
- const entities = await this.buildStandardizedEntities(chat, accountId);
2833
- if (ctx.from) {
2834
- const senderEntity = this.buildMsgSenderEntity(ctx.from, accountId);
2835
- if (senderEntity?.id && !entities.some((e) => e.id === senderEntity.id)) {
2836
- entities.push(senderEntity);
2837
- this.syncedEntityIds.add(senderEntity.id);
2838
- }
2839
- }
2840
- await this.batchProcessEntities(
2841
- entities,
2842
- generalRoom.id,
2843
- generalRoom.name || generalRoom.channelId || chatId,
2844
- generalRoom.channelId || chatId,
2845
- generalRoom.type,
2846
- worldId,
2847
- accountId
2848
- );
2849
- const telegramWorldPayload = {
2850
- runtime: this.runtime,
2851
- world,
2852
- rooms,
2853
- entities,
2854
- source: "telegram",
2855
- accountId,
2856
- metadata: { accountId },
2857
- chat,
2858
- botUsername: this.getAccountState(accountId)?.bot.botInfo?.username
2859
- };
2860
- if (chat.type !== "private") {
2861
- await this.runtime.emitEvent(
2862
- "TELEGRAM_WORLD_JOINED" /* WORLD_JOINED */,
2863
- telegramWorldPayload
2864
- );
2865
- }
2866
- await this.runtime.emitEvent(EventType2.WORLD_JOINED, {
2867
- runtime: this.runtime,
2868
- world,
2869
- rooms,
2870
- entities,
2871
- source: "telegram",
2872
- accountId,
2873
- metadata: { accountId }
2874
- });
2875
- }
2876
- /**
2877
- * Processes entities in batches to prevent overwhelming the system.
2878
- *
2879
- * @param {Entity[]} entities - The entities to process
2880
- * @param {UUID} roomId - The ID of the room to connect entities to
2881
- * @param {string} channelId - The channel ID
2882
- * @param {ChannelType} roomType - The type of the room
2883
- * @param {UUID} worldId - The ID of the world
2884
- * @returns {Promise<void>}
2885
- * @private
2886
- */
2887
- async batchProcessEntities(entities, roomId, roomName, channelId, roomType, worldId, accountId = this.defaultAccountId) {
2888
- const batchSize = 50;
2889
- for (let i = 0; i < entities.length; i += batchSize) {
2890
- const entityBatch = entities.slice(i, i + batchSize);
2891
- await Promise.all(
2892
- entityBatch.map(async (entity) => {
2893
- try {
2894
- if (entity.id) {
2895
- const telegramMetadata = entity.metadata?.telegram;
2896
- await this.runtime.ensureConnection({
2897
- entityId: entity.id,
2898
- roomId,
2899
- roomName,
2900
- userName: telegramMetadata?.username,
2901
- name: telegramMetadata?.name,
2902
- userId: telegramMetadata?.id,
2903
- source: "telegram",
2904
- channelId,
2905
- type: roomType,
2906
- worldId
2907
- });
2908
- } else {
2909
- logger4.warn(
2910
- {
2911
- src: "plugin:telegram",
2912
- agentId: this.runtime.agentId,
2913
- accountId,
2914
- entityNames: entity.names
2915
- },
2916
- "Skipping entity sync due to missing ID"
2917
- );
2918
- }
2919
- } catch (err) {
2920
- const telegramMetadata = entity.metadata?.telegram;
2921
- logger4.warn(
2922
- {
2923
- src: "plugin:telegram",
2924
- agentId: this.runtime.agentId,
2925
- accountId,
2926
- username: telegramMetadata?.username,
2927
- error: err instanceof Error ? err.message : String(err)
2928
- },
2929
- "Failed to sync user"
2930
- );
2931
- }
2932
- })
2933
- );
2934
- if (i + batchSize < entities.length) {
2935
- await new Promise((resolve) => setTimeout(resolve, 500));
2936
- }
2937
- }
2938
- }
2939
- /**
2940
- * Gets chat title and channel type based on Telegram chat type.
2941
- * Maps Telegram-specific chat types to standardized system types.
2942
- *
2943
- * @param {any} chat - The Telegram chat object
2944
- * @returns {Object} Object containing chatTitle and channelType
2945
- * @private
2946
- */
2947
- getChatTypeInfo(chat) {
2948
- const chatType = chat.type;
2949
- let chatTitle;
2950
- let channelType;
2951
- switch (chatType) {
2952
- case "private":
2953
- chatTitle = `Chat with ${chat.first_name || "Unknown User"}`;
2954
- channelType = ChannelType2.DM;
2955
- break;
2956
- case "group":
2957
- chatTitle = chat.title || "Unknown Group";
2958
- channelType = ChannelType2.GROUP;
2959
- break;
2960
- case "supergroup":
2961
- chatTitle = chat.title || "Unknown Supergroup";
2962
- channelType = ChannelType2.GROUP;
2963
- break;
2964
- case "channel":
2965
- chatTitle = chat.title || "Unknown Channel";
2966
- channelType = ChannelType2.FEED;
2967
- break;
2968
- default:
2969
- throw new Error(`Unrecognized Telegram chat type: ${String(chatType)}`);
2970
- }
2971
- return { chatTitle, channelType };
2972
- }
2973
- /**
2974
- * Builds standardized entity representations from Telegram chat data.
2975
- * Transforms Telegram-specific user data into system-standard Entity objects.
2976
- *
2977
- * @param {any} chat - The Telegram chat object
2978
- * @returns {Promise<Entity[]>} Array of standardized Entity objects
2979
- * @private
2980
- */
2981
- async buildStandardizedEntities(chat, accountId = this.defaultAccountId) {
2982
- const entities = [];
2983
- try {
2984
- if (chat.type === "private" && chat.id) {
2985
- const userId = createUniqueUuid2(
2986
- this.runtime,
2987
- this.scopedTelegramKey(chat.id.toString(), accountId)
2988
- );
2989
- entities.push({
2990
- id: userId,
2991
- names: [chat.first_name || "Unknown User"],
2992
- agentId: this.runtime.agentId,
2993
- metadata: {
2994
- telegram: {
2995
- accountId,
2996
- id: chat.id.toString(),
2997
- username: chat.username || "unknown",
2998
- name: chat.first_name || "Unknown User"
2999
- },
3000
- source: "telegram",
3001
- accountId
3002
- }
3003
- });
3004
- this.syncedEntityIds.add(userId);
3005
- } else if (chat.type === "group" || chat.type === "supergroup") {
3006
- try {
3007
- const admins = await this.getAccountState(
3008
- accountId
3009
- )?.bot.telegram.getChatAdministrators(chat.id);
3010
- if (admins && admins.length > 0) {
3011
- for (const admin of admins) {
3012
- const userId = createUniqueUuid2(
3013
- this.runtime,
3014
- this.scopedTelegramKey(admin.user.id.toString(), accountId)
3015
- );
3016
- entities.push({
3017
- id: userId,
3018
- names: [
3019
- admin.user.first_name || admin.user.username || "Unknown Admin"
3020
- ],
3021
- agentId: this.runtime.agentId,
3022
- metadata: {
3023
- telegram: {
3024
- accountId,
3025
- id: admin.user.id.toString(),
3026
- username: admin.user.username || "unknown",
3027
- name: admin.user.first_name || "Unknown Admin",
3028
- isAdmin: true,
3029
- adminTitle: admin.custom_title || (admin.status === "creator" ? "Owner" : "Admin")
3030
- },
3031
- source: "telegram",
3032
- accountId,
3033
- roles: [admin.status === "creator" ? Role.OWNER : Role.ADMIN]
3034
- }
3035
- });
3036
- this.syncedEntityIds.add(userId);
3037
- }
3038
- }
3039
- } catch (error) {
3040
- logger4.warn(
3041
- {
3042
- src: "plugin:telegram",
3043
- agentId: this.runtime.agentId,
3044
- chatId: chat.id,
3045
- error: error instanceof Error ? error.message : String(error)
3046
- },
3047
- "Could not fetch administrators"
3048
- );
3049
- }
3050
- }
3051
- } catch (error) {
3052
- logger4.error(
3053
- {
3054
- src: "plugin:telegram",
3055
- agentId: this.runtime.agentId,
3056
- error: error instanceof Error ? error.message : String(error)
3057
- },
3058
- "Error building standardized entities"
3059
- );
3060
- }
3061
- return entities;
3062
- }
3063
- /**
3064
- * Extracts and builds the room object for a forum topic from a message context.
3065
- * This refactored method can be used both in middleware and when handling new chats.
3066
- *
3067
- * @param {Context} ctx - The context of the incoming update
3068
- * @param {UUID} worldId - The ID of the world the topic belongs to
3069
- * @returns {Promise<Room | null>} A Promise that resolves with the room or null if not a topic
3070
- * @private
3071
- */
3072
- async buildForumTopicRoom(ctx, worldId, accountId = this.defaultAccountId) {
3073
- if (!ctx.chat || !ctx.message?.message_thread_id) {
3074
- return null;
3075
- }
3076
- if (ctx.chat.type !== "supergroup" || !ctx.chat.is_forum) {
3077
- return null;
3078
- }
3079
- const chat = ctx.chat;
3080
- const chatId = chat.id.toString();
3081
- const threadId = ctx.message.message_thread_id.toString();
3082
- const roomId = createUniqueUuid2(
3083
- this.runtime,
3084
- this.scopedTelegramKey(`${chatId}-${threadId}`, accountId)
3085
- );
3086
- try {
3087
- const replyMessage = JSON.parse(JSON.stringify(ctx.message));
3088
- let topicName = `Topic #${threadId}`;
3089
- if (replyMessage && typeof replyMessage === "object" && "forum_topic_created" in replyMessage && replyMessage.forum_topic_created) {
3090
- const topicCreated = replyMessage.forum_topic_created;
3091
- if (topicCreated && typeof topicCreated === "object" && "name" in topicCreated) {
3092
- topicName = topicCreated.name;
3093
- }
3094
- } 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) {
3095
- const topicCreated = replyMessage.reply_to_message.forum_topic_created;
3096
- if (topicCreated && typeof topicCreated === "object" && "name" in topicCreated) {
3097
- topicName = topicCreated.name;
3098
- }
3099
- }
3100
- const room = {
3101
- id: roomId,
3102
- name: topicName,
3103
- source: "telegram",
3104
- type: ChannelType2.GROUP,
3105
- channelId: `${chatId}-${threadId}`,
3106
- serverId: chatId,
3107
- worldId,
3108
- metadata: {
3109
- source: "telegram",
3110
- accountId,
3111
- threadId,
3112
- isForumTopic: true,
3113
- parentChatId: chatId,
3114
- telegram: {
3115
- accountId,
3116
- chatId,
3117
- threadId
3118
- }
3119
- }
3120
- };
3121
- return room;
3122
- } catch (error) {
3123
- logger4.error(
3124
- {
3125
- src: "plugin:telegram",
3126
- agentId: this.runtime.agentId,
3127
- accountId,
3128
- chatId,
3129
- threadId,
3130
- error: error instanceof Error ? error.message : String(error)
3131
- },
3132
- "Error building forum topic room"
3133
- );
3134
- return null;
3135
- }
3136
- }
3137
- buildConnectorChatTarget(chat, score = 0.5, threadId, accountId = this.defaultAccountId) {
3138
- const chatId = chat.id.toString();
3139
- const roomKey = threadId ? `${chatId}-${threadId}` : chatId;
3140
- const roomId = createUniqueUuid2(
3141
- this.runtime,
3142
- this.scopedTelegramKey(roomKey, accountId)
3143
- );
3144
- const label = getTelegramChatDisplayName(chat, chatId);
3145
- return {
3146
- target: {
3147
- source: "telegram",
3148
- accountId,
3149
- roomId,
3150
- channelId: roomKey,
3151
- threadId: threadId?.toString()
3152
- },
3153
- label,
3154
- kind: threadId ? "thread" : telegramChatKind(chat),
3155
- description: threadId && "title" in chat ? `Telegram topic ${threadId} in ${chat.title}` : `Telegram ${chat.type}`,
3156
- score,
3157
- contexts: ["social", "connectors"],
3158
- metadata: {
3159
- accountId,
3160
- telegramChatId: chatId,
3161
- telegramThreadId: threadId,
3162
- telegramChatType: chat.type,
3163
- username: "username" in chat ? chat.username : void 0,
3164
- title: "title" in chat ? chat.title : void 0
3165
- }
3166
- };
3167
- }
3168
- buildConnectorRoomTarget(room, score = 0.5) {
3169
- if (room.source !== "telegram" || !room.channelId) {
3170
- return null;
3171
- }
3172
- const metadata = room.metadata;
3173
- const accountId = typeof metadata?.accountId === "string" && metadata.accountId.trim() ? normalizeTelegramAccountId(metadata.accountId) : normalizeTelegramAccountId(this.defaultAccountId);
3174
- const threadId = typeof metadata?.threadId === "string" ? metadata.threadId : typeof room.channelId === "string" ? parseTelegramTargetParts(room.channelId).threadId?.toString() : void 0;
3175
- return {
3176
- target: {
3177
- source: "telegram",
3178
- accountId,
3179
- roomId: room.id,
3180
- channelId: room.channelId,
3181
- threadId
3182
- },
3183
- label: room.name || room.channelId,
3184
- kind: threadId ? "thread" : "group",
3185
- description: threadId ? `Telegram topic ${threadId}` : "Telegram chat room",
3186
- score,
3187
- contexts: ["social", "connectors"],
3188
- metadata: {
3189
- accountId,
3190
- telegramChatId: room.channelId,
3191
- telegramThreadId: threadId,
3192
- roomName: room.name
3193
- }
3194
- };
3195
- }
3196
- dedupeConnectorTargets(targets) {
3197
- const byKey = /* @__PURE__ */ new Map();
3198
- for (const target of targets) {
3199
- const key = [
3200
- target.target.accountId ?? "",
3201
- target.kind ?? "target",
3202
- target.target.channelId ?? "",
3203
- target.target.entityId ?? "",
3204
- target.target.threadId ?? ""
3205
- ].join(":");
3206
- const existing = byKey.get(key);
3207
- if (!existing || (target.score ?? 0) > (existing.score ?? 0)) {
3208
- byKey.set(key, target);
3209
- }
3210
- }
3211
- return Array.from(byKey.values()).sort(
3212
- (a, b) => (b.score ?? 0) - (a.score ?? 0)
3213
- );
3214
- }
3215
- async getTelegramChatForTarget(chatId, accountId = this.defaultAccountId) {
3216
- const known = this.knownChats.get(this.scopedTelegramKey(String(chatId), accountId)) ?? (normalizeTelegramAccountId(accountId) === DEFAULT_ACCOUNT_ID ? this.knownChats.get(String(chatId)) : void 0);
3217
- if (known) {
3218
- return known;
3219
- }
3220
- const bot = this.getAccountState(accountId)?.bot ?? this.bot;
3221
- if (!bot) {
3222
- return null;
3223
- }
3224
- try {
3225
- const chat = await bot.telegram.getChat(chatId);
3226
- this.knownChats.set(
3227
- this.scopedTelegramKey(String(chat.id), accountId),
3228
- chat
3229
- );
3230
- return chat;
3231
- } catch {
3232
- return null;
3233
- }
3234
- }
3235
- async resolveConnectorTargets(query, context) {
3236
- const normalizedQuery = normalizeTelegramConnectorQuery(query);
3237
- const targets = [];
3238
- const explicitAccountId = this.resolveAccountIdFromContext(
3239
- context,
3240
- context.target
3241
- );
3242
- const accountIds = explicitAccountId ? [normalizeTelegramAccountId(explicitAccountId)] : this.getAccountIds();
3243
- for (const accountId of accountIds) {
3244
- for (const [key, chat] of this.knownChats.entries()) {
3245
- if (!this.knownChatKeyMatchesAccount(key, chat, accountId)) {
3246
- continue;
3247
- }
3248
- const score = scoreTelegramConnectorMatch(
3249
- normalizedQuery,
3250
- chat.id.toString(),
3251
- [
3252
- "title" in chat ? chat.title : void 0,
3253
- "username" in chat ? chat.username : void 0,
3254
- "first_name" in chat ? chat.first_name : void 0,
3255
- "last_name" in chat ? chat.last_name : void 0
3256
- ]
3257
- );
3258
- if (score <= 0) {
3259
- continue;
3260
- }
3261
- targets.push(
3262
- this.buildConnectorChatTarget(chat, score, void 0, accountId)
3263
- );
3264
- }
3265
- }
3266
- if (normalizedQuery && (TELEGRAM_CHAT_ID_PATTERN.test(normalizedQuery) || query.trim().startsWith("@"))) {
3267
- const lookup = TELEGRAM_CHAT_ID_PATTERN.test(normalizedQuery) ? normalizedQuery : query.trim();
3268
- const accountId = accountIds[0] ?? this.defaultAccountId;
3269
- const chat = await this.getTelegramChatForTarget(lookup, accountId);
3270
- if (chat) {
3271
- targets.push(
3272
- this.buildConnectorChatTarget(chat, 1, void 0, accountId)
3273
- );
3274
- }
3275
- }
3276
- const room = context.roomId && typeof context.runtime.getRoom === "function" ? await context.runtime.getRoom(context.roomId) : null;
3277
- if (room) {
3278
- const roomTarget = this.buildConnectorRoomTarget(room, 0.6);
3279
- if (roomTarget) {
3280
- targets.push(roomTarget);
3281
- }
3282
- }
3283
- return this.dedupeConnectorTargets(targets).slice(0, 25);
3284
- }
3285
- async listConnectorRooms(context) {
3286
- const explicitAccountId = this.resolveAccountIdFromContext(
3287
- context,
3288
- context.target
3289
- );
3290
- const accountIds = explicitAccountId ? [normalizeTelegramAccountId(explicitAccountId)] : this.getAccountIds();
3291
- const targets = [];
3292
- for (const accountId of accountIds) {
3293
- for (const [key, chat] of this.knownChats.entries()) {
3294
- if (!this.knownChatKeyMatchesAccount(key, chat, accountId)) {
3295
- continue;
3296
- }
3297
- targets.push(
3298
- this.buildConnectorChatTarget(chat, 0.5, void 0, accountId)
3299
- );
3300
- }
3301
- }
3302
- const room = context.roomId && typeof context.runtime.getRoom === "function" ? await context.runtime.getRoom(context.roomId) : null;
3303
- if (room) {
3304
- const roomTarget = this.buildConnectorRoomTarget(room, 0.7);
3305
- if (roomTarget) {
3306
- targets.push(roomTarget);
3307
- }
3308
- }
3309
- return this.dedupeConnectorTargets(targets).slice(0, 50);
3310
- }
3311
- async listRecentConnectorTargets(context) {
3312
- return this.listConnectorRooms(context);
3313
- }
3314
- async fetchConnectorMessages(context, params = {}) {
3315
- const limit = normalizeConnectorLimit(params.limit);
3316
- const target = params.target ?? context.target;
3317
- const accountId = await this.resolveAccountIdForTarget(
3318
- context.runtime,
3319
- target,
3320
- { accountId: context.accountId }
3321
- );
3322
- if (target?.roomId) {
3323
- const memories = await context.runtime.getMemories({
3324
- tableName: "messages",
3325
- roomId: target.roomId,
3326
- limit,
3327
- orderBy: "createdAt",
3328
- orderDirection: "desc"
3329
- });
3330
- return memories.filter((memory) => {
3331
- const metadata = memory.metadata;
3332
- return !metadata?.accountId || metadata.accountId === accountId;
3333
- });
3334
- }
3335
- const targets = (await this.listRecentConnectorTargets(context)).slice(
3336
- 0,
3337
- 10
3338
- );
3339
- const roomIds = Array.from(
3340
- new Set(
3341
- targets.map((candidate) => candidate.target.roomId).filter((roomId) => Boolean(roomId))
3342
- )
3343
- );
3344
- const chunks = await Promise.all(
3345
- roomIds.map(
3346
- (roomId) => context.runtime.getMemories({
3347
- tableName: "messages",
3348
- roomId,
3349
- limit,
3350
- orderBy: "createdAt",
3351
- orderDirection: "desc"
3352
- })
3353
- )
3354
- );
3355
- return chunks.flat().filter((memory) => {
3356
- const metadata = memory.metadata;
3357
- return !metadata?.accountId || metadata.accountId === accountId;
3358
- }).sort((left, right) => (right.createdAt ?? 0) - (left.createdAt ?? 0)).slice(0, limit);
3359
- }
3360
- async searchConnectorMessages(context, params) {
3361
- const limit = normalizeConnectorLimit(params.limit);
3362
- const messages = await this.fetchConnectorMessages(context, {
3363
- target: params.target ?? context.target,
3364
- limit: Math.max(limit, 100)
3365
- });
3366
- return filterMemoriesByQuery(messages, params.query, limit);
3367
- }
3368
- async getConnectorChatContext(target, context) {
3369
- const room = target.roomId && typeof context.runtime.getRoom === "function" ? await context.runtime.getRoom(target.roomId) : null;
3370
- const accountId = await this.resolveAccountIdForTarget(
3371
- context.runtime,
3372
- target,
3373
- {
3374
- accountId: context.accountId,
3375
- roomId: target.roomId ?? context.roomId
3376
- }
3377
- );
3378
- const channelId = target.channelId ?? room?.channelId;
3379
- if (!channelId) {
3380
- return null;
3381
- }
3382
- const parts = parseTelegramTargetParts(channelId, target.threadId);
3383
- const chat = await this.getTelegramChatForTarget(parts.chatId, accountId);
3384
- const roomId = target.roomId ?? room?.id ?? createUniqueUuid2(
3385
- this.runtime,
3386
- this.scopedTelegramKey(
3387
- parts.threadId ? `${parts.chatId}-${parts.threadId}` : String(parts.chatId),
3388
- accountId
3389
- )
3390
- );
3391
- const memories = await context.runtime.getMemories({
3392
- tableName: "messages",
3393
- roomId,
3394
- count: 10,
3395
- orderBy: "createdAt",
3396
- orderDirection: "desc"
3397
- });
3398
- const recentMessages = memories.slice().reverse().map((memory) => ({
3399
- entityId: memory.entityId,
3400
- name: typeof memory.content?.name === "string" ? memory.content.name : void 0,
3401
- text: memory.content?.text ?? "",
3402
- timestamp: memory.createdAt,
3403
- metadata: {
3404
- memoryId: memory.id,
3405
- accountId,
3406
- source: memory.content?.source
3407
- }
3408
- })).filter((message) => message.text.trim().length > 0);
3409
- return {
3410
- target: {
3411
- source: "telegram",
3412
- accountId,
3413
- roomId,
3414
- channelId,
3415
- threadId: parts.threadId?.toString()
3416
- },
3417
- label: room?.name || (chat ? getTelegramChatDisplayName(chat, String(parts.chatId)) : channelId),
3418
- summary: chat ? `Telegram ${chat.type}` : void 0,
3419
- recentMessages,
3420
- metadata: {
3421
- accountId,
3422
- telegramChatId: String(parts.chatId),
3423
- telegramThreadId: parts.threadId,
3424
- telegramChatType: chat?.type
3425
- }
3426
- };
3427
- }
3428
- async getConnectorUserContext(entityId, context) {
3429
- const accountId = normalizeTelegramAccountId(
3430
- context.target?.accountId ?? context.accountId ?? context.account?.accountId ?? this.defaultAccountId
3431
- );
3432
- const entity = typeof context.runtime.getEntityById === "function" ? await context.runtime.getEntityById(String(entityId)) : null;
3433
- const telegramMetadata = entity?.metadata?.telegram && typeof entity.metadata.telegram === "object" ? entity.metadata.telegram : null;
3434
- const telegramId = typeof telegramMetadata?.id === "number" || typeof telegramMetadata?.id === "string" ? telegramMetadata.id : TELEGRAM_CHAT_ID_PATTERN.test(String(entityId)) ? entityId : null;
3435
- if (!telegramId) {
3436
- return null;
3437
- }
3438
- const chat = await this.getTelegramChatForTarget(telegramId, accountId);
3439
- const aliases = [
3440
- entity?.names?.[0],
3441
- chat && "username" in chat ? chat.username : void 0,
3442
- chat && "first_name" in chat ? chat.first_name : void 0,
3443
- chat && "last_name" in chat ? chat.last_name : void 0
3444
- ].filter((value) => Boolean(value));
3445
- return {
3446
- entityId,
3447
- label: aliases[0] ?? String(telegramId),
3448
- aliases,
3449
- handles: { telegram: String(telegramId) },
3450
- metadata: {
3451
- accountId,
3452
- telegramId: String(telegramId),
3453
- telegramChatType: chat?.type,
3454
- username: chat && "username" in chat ? chat.username : void 0
3455
- }
3456
- };
3457
- }
3458
- static registerSendHandlers(runtime, serviceInstance) {
3459
- if (serviceInstance?.bot) {
3460
- const registerConnector = (accountId) => {
3461
- const normalizedAccountId = accountId ? normalizeTelegramAccountId(accountId) : void 0;
3462
- const state = normalizedAccountId ? serviceInstance.getAccountState(normalizedAccountId) : serviceInstance.getDefaultAccountState();
3463
- const sendHandler = async (handlerRuntime, target, content) => {
3464
- await serviceInstance.handleSendMessage(
3465
- handlerRuntime,
3466
- normalizedAccountId && !target.accountId ? { ...target, accountId: normalizedAccountId } : target,
3467
- content
3468
- );
3469
- return void 0;
3470
- };
3471
- const withContextAccount = (context) => normalizedAccountId && !context.accountId ? {
3472
- ...context,
3473
- accountId: normalizedAccountId,
3474
- account: context.account ?? {
3475
- source: "telegram",
3476
- accountId: normalizedAccountId,
3477
- label: state?.account.name ?? normalizedAccountId
3478
- }
3479
- } : context;
3480
- const registration = {
3481
- source: "telegram",
3482
- ...normalizedAccountId ? { accountId: normalizedAccountId } : {},
3483
- ...normalizedAccountId ? {
3484
- account: {
3485
- source: "telegram",
3486
- accountId: normalizedAccountId,
3487
- label: state?.account.name ?? normalizedAccountId,
3488
- authMethod: "BOT_TOKEN"
3489
- }
3490
- } : {},
3491
- label: state?.account.name ? `Telegram (${state.account.name})` : "Telegram",
3492
- description: "Telegram connector for sending messages to chats, topics, and users.",
3493
- capabilities: [...TELEGRAM_CONNECTOR_CAPABILITIES],
3494
- supportedTargetKinds: ["channel", "group", "thread", "user"],
3495
- contexts: [...TELEGRAM_CONNECTOR_CONTEXTS],
3496
- metadata: {
3497
- service: TELEGRAM_SERVICE_NAME,
3498
- ...normalizedAccountId ? { accountId: normalizedAccountId } : {}
3499
- },
3500
- resolveTargets: (query, context) => serviceInstance.resolveConnectorTargets(
3501
- query,
3502
- withContextAccount(context)
3503
- ),
3504
- listRecentTargets: (context) => serviceInstance.listRecentConnectorTargets(
3505
- withContextAccount(context)
3506
- ),
3507
- listRooms: (context) => serviceInstance.listConnectorRooms(withContextAccount(context)),
3508
- fetchMessages: (context, params) => {
3509
- const readParams = params ?? {};
3510
- return serviceInstance.fetchConnectorMessages(
3511
- withContextAccount(context),
3512
- {
3513
- ...readParams,
3514
- target: normalizedAccountId && readParams.target && !readParams.target.accountId ? {
3515
- ...readParams.target,
3516
- accountId: normalizedAccountId
3517
- } : readParams.target
3518
- }
3519
- );
3520
- },
3521
- searchMessages: (context, params) => serviceInstance.searchConnectorMessages(
3522
- withContextAccount(context),
3523
- {
3524
- ...params,
3525
- target: normalizedAccountId && params.target && !params.target.accountId ? {
3526
- ...params.target,
3527
- accountId: normalizedAccountId
3528
- } : params.target
3529
- }
3530
- ),
3531
- getChatContext: (target, context) => serviceInstance.getConnectorChatContext(
3532
- normalizedAccountId && !target.accountId ? { ...target, accountId: normalizedAccountId } : target,
3533
- withContextAccount(context)
3534
- ),
3535
- getUserContext: (entityId, context) => serviceInstance.getConnectorUserContext(
3536
- entityId,
3537
- withContextAccount(context)
3538
- ),
3539
- getUser: async (handlerRuntime, params) => {
3540
- const userParams = params;
3541
- const entityId = String(
3542
- userParams.entityId ?? userParams.userId ?? userParams.username ?? userParams.handle ?? ""
3543
- ).trim();
3544
- if (!entityId) {
3545
- return null;
3546
- }
3547
- const entity = typeof handlerRuntime.getEntityById === "function" ? await handlerRuntime.getEntityById(entityId).catch(() => null) : null;
3548
- if (entity) {
3549
- return entity;
3550
- }
3551
- const context = await serviceInstance.getConnectorUserContext(
3552
- entityId,
3553
- {
3554
- runtime: handlerRuntime,
3555
- accountId: normalizedAccountId
3556
- }
3557
- );
3558
- if (!context) {
3559
- return null;
3560
- }
3561
- return {
3562
- id: createUniqueUuid2(
3563
- handlerRuntime,
3564
- serviceInstance.scopedTelegramKey(
3565
- `telegram:${context.handles?.telegram ?? entityId}`,
3566
- normalizedAccountId
3567
- )
3568
- ),
3569
- names: context.aliases?.length ? context.aliases : [context.label ?? entityId],
3570
- agentId: handlerRuntime.agentId,
3571
- metadata: {
3572
- ...context.metadata,
3573
- ...normalizedAccountId ? { accountId: normalizedAccountId } : {}
3574
- }
3575
- };
3576
- },
3577
- sendHandler
3578
- };
3579
- runtime.registerMessageConnector(registration);
3580
- };
3581
- if (typeof runtime.registerMessageConnector === "function") {
3582
- registerConnector();
3583
- for (const accountId of serviceInstance.getAccountIds()) {
3584
- registerConnector(accountId);
3585
- }
3586
- } else {
3587
- runtime.registerSendHandler(
3588
- "telegram",
3589
- serviceInstance.handleSendMessage.bind(serviceInstance)
3590
- );
3591
- }
3592
- logger4.info(
3593
- { src: "plugin:telegram", agentId: runtime.agentId },
3594
- "Registered Telegram message connector"
3595
- );
3596
- } else {
3597
- logger4.warn(
3598
- { src: "plugin:telegram", agentId: runtime.agentId },
3599
- "Cannot register send handler, bot not initialized"
3600
- );
3601
- }
3602
- }
3603
- async handleSendMessage(runtime, target, content) {
3604
- const accountId = await this.resolveAccountIdForTarget(runtime, target);
3605
- const accountState = this.getAccountState(accountId);
3606
- const messageManager = accountState?.messageManager ?? this.messageManager;
3607
- const bot = accountState?.bot ?? this.bot;
3608
- if (!bot || !messageManager) {
3609
- logger4.error(
3610
- { src: "plugin:telegram", agentId: runtime.agentId, accountId },
3611
- "Bot not initialized, cannot send messages"
3612
- );
3613
- throw new Error(
3614
- "Telegram bot is not initialized. Please provide TELEGRAM_BOT_TOKEN."
3615
- );
3616
- }
3617
- let chatId;
3618
- let threadId;
3619
- if (target.channelId) {
3620
- const parts = parseTelegramTargetParts(target.channelId, target.threadId);
3621
- chatId = parts.chatId;
3622
- threadId = parts.threadId;
3623
- } else if (target.roomId) {
3624
- const room = await runtime.getRoom(target.roomId);
3625
- const metadata = room?.metadata;
3626
- const metadataThreadId = typeof metadata?.threadId === "string" ? metadata.threadId : void 0;
3627
- if (room?.channelId) {
3628
- const parts = parseTelegramTargetParts(
3629
- room.channelId,
3630
- metadataThreadId
3631
- );
3632
- chatId = parts.chatId;
3633
- threadId = parts.threadId;
3634
- }
3635
- if (!chatId) {
3636
- throw new Error(
3637
- `Could not resolve Telegram chat ID from roomId ${target.roomId}`
3638
- );
3639
- }
3640
- } else if (target.entityId) {
3641
- const entity = await runtime.getEntityById(target.entityId);
3642
- if (!entity) {
3643
- throw new Error(`Entity ${target.entityId} not found`);
3644
- }
3645
- const telegramMeta = entity.metadata?.telegram;
3646
- const entityAccountId = typeof entity.metadata?.accountId === "string" ? entity.metadata.accountId : typeof telegramMeta?.accountId === "string" ? telegramMeta.accountId : void 0;
3647
- if (entityAccountId && normalizeTelegramAccountId(entityAccountId) !== accountId) {
3648
- throw new Error(
3649
- `Entity ${target.entityId} is linked to Telegram account ${entityAccountId}, not ${accountId}`
3650
- );
3651
- }
3652
- const telegramId = telegramMeta?.id;
3653
- if (!telegramId) {
3654
- logger4.error(
3655
- {
3656
- src: "plugin:telegram",
3657
- agentId: runtime.agentId,
3658
- accountId,
3659
- entityId: target.entityId
3660
- },
3661
- "Entity has no telegram.id in metadata \u2014 cannot send DM without Telegram user ID"
3662
- );
3663
- throw new Error(
3664
- `Entity ${target.entityId} has no telegram.id in metadata \u2014 cannot send DM without Telegram user ID`
3665
- );
3666
- }
3667
- chatId = telegramId;
3668
- if (target.threadId && /^\d+$/.test(target.threadId)) {
3669
- threadId = Number.parseInt(target.threadId, 10);
3670
- }
3671
- } else {
3672
- throw new Error(
3673
- "Telegram SendHandler requires channelId, roomId, or entityId."
3674
- );
3675
- }
3676
- if (!chatId) {
3677
- throw new Error(
3678
- `Could not determine target Telegram chat ID for target: ${JSON.stringify(target)}`
3679
- );
3680
- }
3681
- try {
3682
- await messageManager.sendMessage(
3683
- chatId,
3684
- {
3685
- ...content,
3686
- metadata: {
3687
- ...content.metadata && typeof content.metadata === "object" ? content.metadata : {},
3688
- accountId
3689
- }
3690
- },
3691
- void 0,
3692
- threadId
3693
- );
3694
- logger4.info(
3695
- {
3696
- src: "plugin:telegram",
3697
- agentId: runtime.agentId,
3698
- accountId,
3699
- chatId,
3700
- threadId
3701
- },
3702
- "Message sent"
3703
- );
3704
- } catch (error) {
3705
- logger4.error(
3706
- {
3707
- src: "plugin:telegram",
3708
- agentId: runtime.agentId,
3709
- accountId,
3710
- chatId,
3711
- error: error instanceof Error ? error.message : String(error)
3712
- },
3713
- "Error sending message"
3714
- );
3715
- throw error;
3716
- }
3717
- }
3718
- };
3719
-
3720
- // src/setup-routes.ts
3721
- import { logger as logger5 } from "@elizaos/core";
3722
- var TELEGRAM_API_BASE = "https://api.telegram.org";
3723
- function isConnectorSetupService2(service) {
3724
- if (!service || typeof service !== "object") {
3725
- return false;
3726
- }
3727
- const candidate = service;
3728
- return typeof candidate.getConfig === "function" && typeof candidate.updateConfig === "function" && typeof candidate.persistConfig === "function" && typeof candidate.registerEscalationChannel === "function" && typeof candidate.setOwnerContact === "function";
3729
- }
3730
- function getSetupService2(runtime) {
3731
- const service = runtime.getService("connector-setup");
3732
- return isConnectorSetupService2(service) ? service : null;
3733
- }
3734
- async function readJsonBody(req) {
3735
- return req.body ?? null;
3736
- }
3737
- async function handleValidateToken(req, res, runtime) {
3738
- const body = await readJsonBody(req);
3739
- const token = typeof body?.token === "string" ? body.token.trim() : "";
3740
- if (!token) {
3741
- res.status(200).json({ ok: false, error: "token is required" });
3742
- return;
3743
- }
3744
- if (!/^\d+:[A-Za-z0-9_-]{30,}$/.test(token)) {
3745
- res.status(200).json({
3746
- ok: false,
3747
- error: "Token format invalid. Expected format: 123456:ABC-DEF..."
3748
- });
3749
- return;
3750
- }
3751
- try {
3752
- const apiRes = await fetch(`${TELEGRAM_API_BASE}/bot${token}/getMe`, {
3753
- signal: AbortSignal.timeout(1e4)
3754
- });
3755
- if (!apiRes.ok) {
3756
- res.status(200).json({
3757
- ok: false,
3758
- error: `Telegram API returned ${apiRes.status}. Check that the token is correct.`
3759
- });
3760
- return;
3761
- }
3762
- const data = await apiRes.json();
3763
- if (!data.ok || !data.result) {
3764
- res.status(200).json({
3765
- ok: false,
3766
- error: "Telegram API returned unexpected response"
3767
- });
3768
- return;
3769
- }
3770
- const bot = data.result;
3771
- const setupService = getSetupService2(runtime);
3772
- if (setupService) {
3773
- setupService.updateConfig((config) => {
3774
- if (!config.connectors) {
3775
- config.connectors = {};
3776
- }
3777
- const connectors = config.connectors;
3778
- if (!connectors.telegram || typeof connectors.telegram !== "object") {
3779
- connectors.telegram = {};
3780
- }
3781
- connectors.telegram.botToken = token;
3782
- });
3783
- setupService.setOwnerContact({
3784
- source: "telegram",
3785
- channelId: String(bot.id)
3786
- });
3787
- setupService.registerEscalationChannel("telegram");
3788
- } else {
3789
- logger5.warn(
3790
- "[telegram-setup] connector-setup service not available \u2014 token saved to runtime only"
3791
- );
3792
- }
3793
- res.status(200).json({
3794
- ok: true,
3795
- bot: {
3796
- id: bot.id,
3797
- username: bot.username,
3798
- firstName: bot.first_name
3799
- }
3800
- });
3801
- } catch (err) {
3802
- const message = err instanceof Error ? err.message : String(err);
3803
- res.status(200).json({
3804
- ok: false,
3805
- error: `Failed to reach Telegram API: ${message}`
3806
- });
3807
- }
3808
- }
3809
- async function handleStatus2(_req, res, runtime) {
3810
- const setupService = getSetupService2(runtime);
3811
- let hasToken = false;
3812
- if (setupService) {
3813
- const config = setupService.getConfig();
3814
- const connectors = config.connectors ?? {};
3815
- const tgConfig = connectors.telegram;
3816
- hasToken = Boolean(tgConfig?.botToken);
3817
- }
3818
- if (!hasToken) {
3819
- hasToken = Boolean(runtime.getSetting("TELEGRAM_BOT_TOKEN"));
3820
- }
3821
- const service = runtime.getService("telegram");
3822
- const connected = Boolean(service);
3823
- res.status(200).json({
3824
- available: true,
3825
- hasToken,
3826
- connected
3827
- });
3828
- }
3829
- async function handleDisconnect2(_req, res, runtime) {
3830
- const setupService = getSetupService2(runtime);
3831
- if (setupService) {
3832
- setupService.updateConfig((config) => {
3833
- const connectors = config.connectors ?? {};
3834
- const tgConfig = connectors.telegram;
3835
- if (tgConfig) {
3836
- delete tgConfig.botToken;
3837
- }
3838
- });
3839
- }
3840
- res.status(200).json({ ok: true });
3841
- }
3842
- var telegramSetupRoutes = [
3843
- {
3844
- type: "POST",
3845
- path: "/api/telegram-setup/validate-token",
3846
- handler: handleValidateToken,
3847
- rawPath: true
3848
- },
3849
- {
3850
- type: "GET",
3851
- path: "/api/telegram-setup/status",
3852
- handler: handleStatus2,
3853
- rawPath: true
3854
- },
3855
- {
3856
- type: "POST",
3857
- path: "/api/telegram-setup/disconnect",
3858
- handler: handleDisconnect2,
3859
- rawPath: true
3860
- }
3861
- ];
3862
-
3863
- // src/tests.ts
3864
- import { logger as logger6 } from "@elizaos/core";
3865
- var TEST_IMAGE_URL = "https://github.com/elizaOS/awesome-eliza/blob/main/assets/eliza-logo.jpg?raw=true";
3866
- var TelegramTestSuite = class {
3867
- name = "telegram";
3868
- telegramClient = null;
3869
- bot = null;
3870
- messageManager = null;
3871
- tests;
3872
- /**
3873
- * Constructor for initializing a set of test cases for a Telegram bot.
3874
- *
3875
- * @constructor
3876
- * @property {Array<Object>} tests - An array of test cases with name and corresponding test functions.
3877
- * @property {string} tests.name - The name of the test case.
3878
- * @property {function} tests.fn - The test function to be executed.
3879
- */
3880
- constructor() {
3881
- this.tests = [
3882
- {
3883
- name: "Initialize and Validate Telegram Bot Connection",
3884
- fn: this.testCreatingTelegramBot.bind(this)
3885
- },
3886
- {
3887
- name: "Send Basic Text Message to Telegram Chat",
3888
- fn: this.testSendingTextMessage.bind(this)
3889
- },
3890
- {
3891
- name: "Send Text Message with an Image Attachment",
3892
- fn: this.testSendingMessageWithAttachment.bind(this)
3893
- },
3894
- {
3895
- name: "Handle and Process Incoming Telegram Messages",
3896
- fn: this.testHandlingMessage.bind(this)
3897
- },
3898
- {
3899
- name: "Process and Validate Image Attachments in Incoming Messages",
3900
- fn: this.testProcessingImages.bind(this)
3901
- }
3902
- ];
3903
- }
3904
- /**
3905
- * Retrieves the Telegram test chat ID from environment variables.
3906
- *
3907
- * Reference on getting the Telegram chat ID:
3908
- * https://stackoverflow.com/a/32572159
3909
- */
3910
- /**
3911
- * Validates the chat ID by checking if it is set in the runtime settings or environment variables.
3912
- * If not set, an error is thrown with a message instructing to provide a valid chat ID.
3913
- * @param {IAgentRuntime} runtime - The runtime object that provides access to the settings and environment variables.
3914
- * @throws {Error} If TELEGRAM_TEST_CHAT_ID is not set in the runtime settings or environment variables.
3915
- * @returns {string} The validated chat ID.
3916
- */
3917
- validateChatId(runtime) {
3918
- const testChatId = runtime.getSetting("TELEGRAM_TEST_CHAT_ID") || process.env.TELEGRAM_TEST_CHAT_ID;
3919
- if (!testChatId || typeof testChatId === "boolean") {
3920
- throw new Error(
3921
- "TELEGRAM_TEST_CHAT_ID is not set. Please provide a valid chat ID in the environment variables."
3922
- );
3923
- }
3924
- return testChatId;
3925
- }
3926
- async getChatInfo(runtime) {
3927
- try {
3928
- const chatId = this.validateChatId(runtime);
3929
- if (!this.bot) {
3930
- throw new Error("Bot is not initialized.");
3931
- }
3932
- const chat = await this.bot.telegram.getChat(chatId);
3933
- logger6.debug({ src: "plugin:telegram", chatId }, "Fetched real chat");
3934
- return chat;
3935
- } catch (error) {
3936
- throw new Error(`Error fetching real Telegram chat: ${error}`);
3937
- }
3938
- }
3939
- async testCreatingTelegramBot(runtime) {
3940
- this.telegramClient = runtime.getService("telegram");
3941
- if (!this.telegramClient?.messageManager) {
3942
- throw new Error(
3943
- "Telegram service or message manager not initialized - check TELEGRAM_BOT_TOKEN"
3944
- );
3945
- }
3946
- this.bot = this.telegramClient.messageManager.bot;
3947
- this.messageManager = this.telegramClient.messageManager;
3948
- logger6.debug(
3949
- { src: "plugin:telegram" },
3950
- "Telegram bot initialized successfully"
3951
- );
3952
- }
3953
- async testSendingTextMessage(runtime) {
3954
- try {
3955
- if (!this.bot) {
3956
- throw new Error("Bot not initialized.");
3957
- }
3958
- const chatId = this.validateChatId(runtime);
3959
- await this.bot.telegram.sendMessage(chatId, "Testing Telegram message!");
3960
- logger6.debug(
3961
- { src: "plugin:telegram", chatId },
3962
- "Message sent successfully"
3963
- );
3964
- } catch (error) {
3965
- throw new Error(`Error sending Telegram message: ${error}`);
3966
- }
3967
- }
3968
- async testSendingMessageWithAttachment(runtime) {
3969
- try {
3970
- if (!this.messageManager) {
3971
- throw new Error("MessageManager not initialized.");
3972
- }
3973
- if (!this.bot) {
3974
- throw new Error("Bot not initialized.");
3975
- }
3976
- const chat = await this.getChatInfo(runtime);
3977
- const mockContext = {
3978
- chat,
3979
- from: { id: 123, username: "TestUser" },
3980
- telegram: this.bot.telegram
3981
- };
3982
- const messageContent = {
3983
- text: "Here is an image attachment:",
3984
- attachments: [
3985
- {
3986
- id: "123",
3987
- title: "Sample Image",
3988
- source: TEST_IMAGE_URL,
3989
- text: "Sample Image",
3990
- url: TEST_IMAGE_URL,
3991
- contentType: "image/png",
3992
- description: "Sample Image"
3993
- }
3994
- ]
3995
- };
3996
- await this.messageManager.sendMessageInChunks(
3997
- mockContext,
3998
- messageContent
3999
- );
4000
- logger6.success(
4001
- { src: "plugin:telegram" },
4002
- "Message with image attachment sent successfully"
4003
- );
4004
- } catch (error) {
4005
- throw new Error(
4006
- `Error sending Telegram message with attachment: ${error}`
4007
- );
4008
- }
4009
- }
4010
- async testHandlingMessage(runtime) {
4011
- try {
4012
- if (!this.bot) {
4013
- throw new Error("Bot not initialized.");
4014
- }
4015
- if (!this.messageManager) {
4016
- throw new Error("MessageManager not initialized.");
4017
- }
4018
- const chat = await this.getChatInfo(runtime);
4019
- const mockContext = {
4020
- chat,
4021
- from: {
4022
- id: 123,
4023
- username: "TestUser",
4024
- is_bot: false,
4025
- first_name: "Test",
4026
- last_name: "User"
4027
- },
4028
- message: {
4029
- message_id: 12345,
4030
- text: `@${this.bot.botInfo?.username}! Hello!`,
4031
- date: Math.floor(Date.now() / 1e3),
4032
- chat
4033
- },
4034
- telegram: this.bot.telegram
4035
- };
4036
- try {
4037
- await this.messageManager.handleMessage(mockContext);
4038
- } catch (error) {
4039
- throw new Error(`Error handling Telegram message: ${error}`);
4040
- }
4041
- } catch (error) {
4042
- throw new Error(`Error handling Telegram message: ${error}`);
4043
- }
4044
- }
4045
- async testProcessingImages(runtime) {
4046
- try {
4047
- if (!this.bot) {
4048
- throw new Error("Bot not initialized.");
4049
- }
4050
- if (!this.messageManager) {
4051
- throw new Error("MessageManager not initialized.");
4052
- }
4053
- const chatId = this.validateChatId(runtime);
4054
- const fileId = await this.getFileId(String(chatId), TEST_IMAGE_URL);
4055
- const mockMessage = {
4056
- message_id: 12345,
4057
- chat: { id: chatId, type: "private" },
4058
- date: Math.floor(Date.now() / 1e3),
4059
- photo: [
4060
- {
4061
- file_id: fileId,
4062
- file_unique_id: `unique_${fileId}`,
4063
- width: 100,
4064
- height: 100
4065
- }
4066
- ],
4067
- text: `@${this.bot.botInfo?.username}!`
4068
- };
4069
- const result = await this.messageManager.processImage(
4070
- mockMessage
4071
- );
4072
- if (!result?.description) {
4073
- throw new Error(
4074
- "Error processing Telegram image or description not found"
4075
- );
4076
- }
4077
- const { description } = result;
4078
- logger6.debug(
4079
- { src: "plugin:telegram", description },
4080
- "Processing Telegram image successfully"
4081
- );
4082
- } catch (error) {
4083
- throw new Error(`Error processing Telegram image: ${error}`);
4084
- }
4085
- }
4086
- async getFileId(chatId, imageUrl) {
4087
- try {
4088
- if (!this.bot) {
4089
- throw new Error("Bot is not initialized.");
4090
- }
4091
- const message = await this.bot.telegram.sendPhoto(chatId, imageUrl);
4092
- if (message.photo.length === 0) {
4093
- throw new Error("No photo received in the message response.");
4094
- }
4095
- return message.photo[message.photo.length - 1].file_id;
4096
- } catch (error) {
4097
- logger6.error(
4098
- {
4099
- src: "plugin:telegram",
4100
- chatId,
4101
- error: error instanceof Error ? error.message : String(error)
4102
- },
4103
- "Error sending image"
4104
- );
4105
- throw error;
4106
- }
4107
- }
4108
- };
4109
-
4110
- // src/index.ts
4111
- var telegramPlugin = {
4112
- name: TELEGRAM_SERVICE_NAME,
4113
- description: "Telegram client plugin",
4114
- // TelegramService must come before TelegramOwnerPairingServiceImpl so the
4115
- // bot instance exists when the pairing service registers its command.
4116
- services: [TelegramService, TelegramOwnerPairingServiceImpl],
4117
- routes: [...telegramSetupRoutes, ...telegramAccountRoutes],
4118
- tests: [new TelegramTestSuite()],
4119
- // Self-declared auto-enable: activate when the "telegram" connector is
4120
- // configured in eliza.json / eliza.json. The hardcoded CONNECTOR_PLUGINS
4121
- // map in plugin-auto-enable.ts still serves as a fallback.
4122
- autoEnable: {
4123
- connectorKeys: ["telegram"]
4124
- },
4125
- init: async (_config, runtime) => {
4126
- try {
4127
- const manager = getConnectorAccountManager(runtime);
4128
- manager.registerProvider(createTelegramConnectorAccountProvider(runtime));
4129
- } catch (err) {
4130
- logger7.warn(
4131
- {
4132
- src: "plugin:telegram",
4133
- err: err instanceof Error ? err.message : String(err)
4134
- },
4135
- "Failed to register Telegram provider with ConnectorAccountManager"
4136
- );
4137
- }
4138
- }
4139
- };
4140
- var index_default = telegramPlugin;
4141
- export {
4142
- DEFAULT_ACCOUNT_ID,
4143
- MessageManager,
4144
- TELEGRAM_OWNER_PAIRING_SERVICE_TYPE,
4145
- TelegramAccountAuthSession,
4146
- TelegramOwnerPairingServiceImpl,
4147
- TelegramService,
4148
- clearTelegramAccountAuthState,
4149
- clearTelegramAccountSession,
4150
- createTelegramConnectorAccountProvider,
4151
- index_default as default,
4152
- defaultTelegramAccountDeviceModel,
4153
- defaultTelegramAccountSystemVersion,
4154
- getTelegramMultiAccountConfig,
4155
- listEnabledTelegramAccounts,
4156
- listTelegramAccountIds,
4157
- loadTelegramAccountSessionString,
4158
- normalizeTelegramAccountId,
4159
- resolveDefaultTelegramAccountId,
4160
- resolveTelegramAccount,
4161
- resolveTelegramAccountSessionFile,
4162
- saveTelegramAccountSessionString,
4163
- stopTelegramAccountAuthSession,
4164
- telegramAccountAuthStateExists,
4165
- telegramAccountSessionExists
4166
- };
4167
- //# sourceMappingURL=index.js.map