@elizaos/plugin-telegram 1.0.2 → 1.0.9
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/constants.d.ts +10 -0
- package/dist/environment.d.ts +21 -0
- package/dist/index.d.ts +5 -327
- package/dist/index.js +107 -60
- package/dist/index.js.map +1 -1
- package/dist/messageManager.d.ts +87 -0
- package/dist/service.d.ts +219 -0
- package/dist/tests.d.ts +52 -0
- package/dist/types.d.ts +99 -0
- package/dist/utils.d.ts +29 -0
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -23,27 +23,6 @@ import {
|
|
|
23
23
|
} from "@elizaos/core";
|
|
24
24
|
import { Telegraf } from "telegraf";
|
|
25
25
|
|
|
26
|
-
// src/environment.ts
|
|
27
|
-
import { z } from "zod";
|
|
28
|
-
var telegramEnvSchema = z.object({
|
|
29
|
-
TELEGRAM_BOT_TOKEN: z.string().min(1, "Telegram bot token is required")
|
|
30
|
-
});
|
|
31
|
-
async function validateTelegramConfig(runtime) {
|
|
32
|
-
try {
|
|
33
|
-
const config = {
|
|
34
|
-
TELEGRAM_BOT_TOKEN: runtime.getSetting("TELEGRAM_BOT_TOKEN") || process.env.TELEGRAM_BOT_TOKEN
|
|
35
|
-
};
|
|
36
|
-
return telegramEnvSchema.parse(config);
|
|
37
|
-
} catch (error) {
|
|
38
|
-
if (error instanceof z.ZodError) {
|
|
39
|
-
const errorMessages = error.errors.map((err) => `${err.path.join(".")}: ${err.message}`).join("\n");
|
|
40
|
-
throw new Error(`Telegram configuration validation failed:
|
|
41
|
-
${errorMessages}`);
|
|
42
|
-
}
|
|
43
|
-
throw error;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
26
|
// src/messageManager.ts
|
|
48
27
|
import {
|
|
49
28
|
ChannelType,
|
|
@@ -87,46 +66,46 @@ function convertMarkdownToTelegram(markdown) {
|
|
|
87
66
|
return placeholder;
|
|
88
67
|
}
|
|
89
68
|
let converted = markdown;
|
|
90
|
-
converted = converted.replace(/```(\w+)?\n([\s\S]*?)```/g, (
|
|
69
|
+
converted = converted.replace(/```(\w+)?\n([\s\S]*?)```/g, (_match, lang, code) => {
|
|
91
70
|
const escapedCode = escapeCode(code);
|
|
92
71
|
const formatted = "```" + (lang || "") + "\n" + escapedCode + "```";
|
|
93
72
|
return storeReplacement(formatted);
|
|
94
73
|
});
|
|
95
|
-
converted = converted.replace(/`([^`]+)`/g, (
|
|
74
|
+
converted = converted.replace(/`([^`]+)`/g, (_match, code) => {
|
|
96
75
|
const escapedCode = escapeCode(code);
|
|
97
76
|
const formatted = "`" + escapedCode + "`";
|
|
98
77
|
return storeReplacement(formatted);
|
|
99
78
|
});
|
|
100
79
|
converted = converted.replace(
|
|
101
80
|
/$begin:math:display$([^$end:math:display$]+)]$begin:math:text$([^)]+)$end:math:text$/g,
|
|
102
|
-
(
|
|
81
|
+
(_match, text, url) => {
|
|
103
82
|
const formattedText = escapePlainText(text);
|
|
104
83
|
const escapedURL = escapeUrl(url);
|
|
105
84
|
const formatted = `[${formattedText}](${escapedURL})`;
|
|
106
85
|
return storeReplacement(formatted);
|
|
107
86
|
}
|
|
108
87
|
);
|
|
109
|
-
converted = converted.replace(/\*\*([^*]+)\*\*/g, (
|
|
88
|
+
converted = converted.replace(/\*\*([^*]+)\*\*/g, (_match, content) => {
|
|
110
89
|
const formattedContent = escapePlainText(content);
|
|
111
90
|
const formatted = `*${formattedContent}*`;
|
|
112
91
|
return storeReplacement(formatted);
|
|
113
92
|
});
|
|
114
|
-
converted = converted.replace(/~~([^~]+)~~/g, (
|
|
93
|
+
converted = converted.replace(/~~([^~]+)~~/g, (_match, content) => {
|
|
115
94
|
const formattedContent = escapePlainText(content);
|
|
116
95
|
const formatted = `~${formattedContent}~`;
|
|
117
96
|
return storeReplacement(formatted);
|
|
118
97
|
});
|
|
119
|
-
converted = converted.replace(/(?<!\*)\*([^*\n]+)\*(?!\*)/g, (
|
|
98
|
+
converted = converted.replace(/(?<!\*)\*([^*\n]+)\*(?!\*)/g, (_match, content) => {
|
|
120
99
|
const formattedContent = escapePlainText(content);
|
|
121
100
|
const formatted = `_${formattedContent}_`;
|
|
122
101
|
return storeReplacement(formatted);
|
|
123
102
|
});
|
|
124
|
-
converted = converted.replace(/_([^_\n]+)_/g, (
|
|
103
|
+
converted = converted.replace(/_([^_\n]+)_/g, (_match, content) => {
|
|
125
104
|
const formattedContent = escapePlainText(content);
|
|
126
105
|
const formatted = `_${formattedContent}_`;
|
|
127
106
|
return storeReplacement(formatted);
|
|
128
107
|
});
|
|
129
|
-
converted = converted.replace(/^(#{1,6})\s*(.*)$/gm, (
|
|
108
|
+
converted = converted.replace(/^(#{1,6})\s*(.*)$/gm, (_match, _hashes, headerContent) => {
|
|
130
109
|
const formatted = `*${escapePlainText(headerContent.trim())}*`;
|
|
131
110
|
return storeReplacement(formatted);
|
|
132
111
|
});
|
|
@@ -196,7 +175,7 @@ var MessageManager = class {
|
|
|
196
175
|
async processImage(message) {
|
|
197
176
|
try {
|
|
198
177
|
let imageUrl = null;
|
|
199
|
-
logger.info(`Telegram Message: ${message}`);
|
|
178
|
+
logger.info(`Telegram Message: ${JSON.stringify(message, null, 2)}`);
|
|
200
179
|
if ("photo" in message && message.photo?.length > 0) {
|
|
201
180
|
const photo = message.photo[message.photo.length - 1];
|
|
202
181
|
const fileLink = await this.bot.telegram.getFileLink(photo.file_id);
|
|
@@ -370,10 +349,8 @@ ${description}]` };
|
|
|
370
349
|
logger.error("handleMessage: ctx.chat is undefined");
|
|
371
350
|
return;
|
|
372
351
|
}
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
threadId ? `${ctx.chat.id}-${threadId}` : ctx.chat.id.toString()
|
|
376
|
-
);
|
|
352
|
+
const telegramRoomid = threadId ? `${ctx.chat.id}-${threadId}` : ctx.chat.id.toString();
|
|
353
|
+
const roomId = createUniqueUuid(this.runtime, telegramRoomid);
|
|
377
354
|
const messageId = createUniqueUuid(this.runtime, message?.message_id?.toString());
|
|
378
355
|
const imageInfo = await this.processImage(message);
|
|
379
356
|
let messageText = "";
|
|
@@ -386,6 +363,19 @@ ${description}]` };
|
|
|
386
363
|
if (!fullText) return;
|
|
387
364
|
const chat = message.chat;
|
|
388
365
|
const channelType = getChannelType(chat);
|
|
366
|
+
const sourceId = createUniqueUuid(this.runtime, "" + chat.id);
|
|
367
|
+
await this.runtime.ensureConnection({
|
|
368
|
+
entityId,
|
|
369
|
+
roomId,
|
|
370
|
+
userName: ctx.from.username,
|
|
371
|
+
name: ctx.from.first_name,
|
|
372
|
+
source: "telegram",
|
|
373
|
+
channelId: telegramRoomid,
|
|
374
|
+
serverId: void 0,
|
|
375
|
+
type: channelType,
|
|
376
|
+
worldId: createUniqueUuid(this.runtime, roomId),
|
|
377
|
+
worldName: telegramRoomid
|
|
378
|
+
});
|
|
389
379
|
const memory = {
|
|
390
380
|
id: messageId,
|
|
391
381
|
entityId,
|
|
@@ -393,21 +383,45 @@ ${description}]` };
|
|
|
393
383
|
roomId,
|
|
394
384
|
content: {
|
|
395
385
|
text: fullText,
|
|
386
|
+
// attachments?
|
|
396
387
|
source: "telegram",
|
|
388
|
+
// url?
|
|
397
389
|
channelType,
|
|
398
390
|
inReplyTo: "reply_to_message" in message && message.reply_to_message ? createUniqueUuid(this.runtime, message.reply_to_message.message_id.toString()) : void 0
|
|
399
391
|
},
|
|
392
|
+
metadata: {
|
|
393
|
+
entityName: ctx.from.first_name,
|
|
394
|
+
entityUserName: ctx.from.username,
|
|
395
|
+
fromBot: ctx.from.is_bot,
|
|
396
|
+
// include very technical/exact reference to this user for security reasons
|
|
397
|
+
// don't remove or change this, spartan needs this
|
|
398
|
+
fromId: chat.id,
|
|
399
|
+
sourceId,
|
|
400
|
+
// why message? all Memories contain content (which is basically a message)
|
|
401
|
+
// what are the other types? see MemoryType
|
|
402
|
+
type: "message"
|
|
403
|
+
// MemoryType.MESSAGE
|
|
404
|
+
// scope: `shared`, `private`, or `room`
|
|
405
|
+
},
|
|
400
406
|
createdAt: message.date * 1e3
|
|
401
407
|
};
|
|
402
408
|
const callback = async (content, _files) => {
|
|
403
409
|
try {
|
|
404
410
|
if (!content.text) return [];
|
|
405
|
-
|
|
406
|
-
if (
|
|
411
|
+
let sentMessages = false;
|
|
412
|
+
if (content?.channelType === "DM") {
|
|
413
|
+
sentMessages = [];
|
|
414
|
+
if (ctx.from) {
|
|
415
|
+
const res = await this.bot.telegram.sendMessage(ctx.from.id, content.text);
|
|
416
|
+
sentMessages.push(res);
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
sentMessages = await this.sendMessageInChunks(ctx, content, message.message_id);
|
|
420
|
+
}
|
|
421
|
+
if (!Array.isArray(sentMessages)) return [];
|
|
407
422
|
const memories = [];
|
|
408
423
|
for (let i = 0; i < sentMessages.length; i++) {
|
|
409
424
|
const sentMessage = sentMessages[i];
|
|
410
|
-
const _isLastMessage = i === sentMessages.length - 1;
|
|
411
425
|
const responseMemory = {
|
|
412
426
|
id: createUniqueUuid(this.runtime, sentMessage.message_id.toString()),
|
|
413
427
|
entityId: this.runtime.agentId,
|
|
@@ -622,15 +636,27 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
622
636
|
constructor(runtime) {
|
|
623
637
|
super(runtime);
|
|
624
638
|
logger2.log("\u{1F4F1} Constructing new TelegramService...");
|
|
639
|
+
const botToken = runtime.getSetting("TELEGRAM_BOT_TOKEN");
|
|
640
|
+
if (!botToken || botToken.trim() === "") {
|
|
641
|
+
logger2.warn("Telegram Bot Token not provided - Telegram functionality will be unavailable");
|
|
642
|
+
this.bot = null;
|
|
643
|
+
this.messageManager = null;
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
625
646
|
this.options = {
|
|
626
647
|
telegram: {
|
|
627
648
|
apiRoot: runtime.getSetting("TELEGRAM_API_ROOT") || process.env.TELEGRAM_API_ROOT || "https://api.telegram.org"
|
|
628
649
|
}
|
|
629
650
|
};
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
651
|
+
try {
|
|
652
|
+
this.bot = new Telegraf(botToken, this.options);
|
|
653
|
+
this.messageManager = new MessageManager(this.bot, this.runtime);
|
|
654
|
+
logger2.log("\u2705 TelegramService constructor completed");
|
|
655
|
+
} catch (error) {
|
|
656
|
+
logger2.error(`Error initializing Telegram bot: ${error instanceof Error ? error.message : String(error)}`);
|
|
657
|
+
this.bot = null;
|
|
658
|
+
this.messageManager = null;
|
|
659
|
+
}
|
|
634
660
|
}
|
|
635
661
|
/**
|
|
636
662
|
* Starts the Telegram service for the given runtime.
|
|
@@ -639,13 +665,16 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
639
665
|
* @returns {Promise<TelegramService>} A promise that resolves with the initialized TelegramService.
|
|
640
666
|
*/
|
|
641
667
|
static async start(runtime) {
|
|
642
|
-
|
|
668
|
+
const service = new _TelegramService(runtime);
|
|
669
|
+
if (!service.bot) {
|
|
670
|
+
logger2.warn("Telegram service started without bot functionality - no bot token provided");
|
|
671
|
+
return service;
|
|
672
|
+
}
|
|
643
673
|
const maxRetries = 5;
|
|
644
674
|
let retryCount = 0;
|
|
645
675
|
let lastError = null;
|
|
646
676
|
while (retryCount < maxRetries) {
|
|
647
677
|
try {
|
|
648
|
-
const service = new _TelegramService(runtime);
|
|
649
678
|
logger2.success(
|
|
650
679
|
`\u2705 Telegram client successfully started for character ${runtime.character.name}`
|
|
651
680
|
);
|
|
@@ -668,9 +697,10 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
668
697
|
}
|
|
669
698
|
}
|
|
670
699
|
}
|
|
671
|
-
|
|
672
|
-
`Telegram initialization failed after ${maxRetries} attempts. Last error: ${lastError?.message}
|
|
700
|
+
logger2.error(
|
|
701
|
+
`Telegram initialization failed after ${maxRetries} attempts. Last error: ${lastError?.message}. Service will continue without Telegram functionality.`
|
|
673
702
|
);
|
|
703
|
+
return service;
|
|
674
704
|
}
|
|
675
705
|
/**
|
|
676
706
|
* Stops the agent runtime.
|
|
@@ -688,21 +718,27 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
688
718
|
* @returns A Promise that resolves once the bot has stopped.
|
|
689
719
|
*/
|
|
690
720
|
async stop() {
|
|
691
|
-
this.bot
|
|
721
|
+
this.bot?.stop();
|
|
692
722
|
}
|
|
693
723
|
/**
|
|
694
724
|
* Initializes the Telegram bot by launching it, getting bot info, and setting up message manager.
|
|
695
725
|
* @returns {Promise<void>} A Promise that resolves when the initialization is complete.
|
|
696
726
|
*/
|
|
697
727
|
async initializeBot() {
|
|
698
|
-
this.bot
|
|
728
|
+
this.bot?.start((ctx) => {
|
|
729
|
+
this.runtime.emitEvent(["TELEGRAM_SLASH_START" /* SLASH_START */], {
|
|
730
|
+
// we don't need this
|
|
731
|
+
ctx
|
|
732
|
+
});
|
|
733
|
+
});
|
|
734
|
+
this.bot?.launch({
|
|
699
735
|
dropPendingUpdates: true,
|
|
700
736
|
allowedUpdates: ["message", "message_reaction"]
|
|
701
737
|
});
|
|
702
738
|
const botInfo = await this.bot.telegram.getMe();
|
|
703
739
|
logger2.log(`Bot info: ${JSON.stringify(botInfo)}`);
|
|
704
|
-
process.once("SIGINT", () => this.bot
|
|
705
|
-
process.once("SIGTERM", () => this.bot
|
|
740
|
+
process.once("SIGINT", () => this.bot?.stop("SIGINT"));
|
|
741
|
+
process.once("SIGTERM", () => this.bot?.stop("SIGTERM"));
|
|
706
742
|
}
|
|
707
743
|
/**
|
|
708
744
|
* Sets up the middleware chain for preprocessing messages before they reach handlers.
|
|
@@ -720,8 +756,8 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
720
756
|
* @private
|
|
721
757
|
*/
|
|
722
758
|
setupMiddlewares() {
|
|
723
|
-
this.bot
|
|
724
|
-
this.bot
|
|
759
|
+
this.bot?.use(this.authorizationMiddleware.bind(this));
|
|
760
|
+
this.bot?.use(this.chatAndEntityMiddleware.bind(this));
|
|
725
761
|
}
|
|
726
762
|
/**
|
|
727
763
|
* Authorization middleware - checks if chat is allowed to interact with the bot
|
|
@@ -788,14 +824,14 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
788
824
|
* @private
|
|
789
825
|
*/
|
|
790
826
|
setupMessageHandlers() {
|
|
791
|
-
this.bot
|
|
827
|
+
this.bot?.on("message", async (ctx) => {
|
|
792
828
|
try {
|
|
793
829
|
await this.messageManager.handleMessage(ctx);
|
|
794
830
|
} catch (error) {
|
|
795
831
|
logger2.error("Error handling message:", error);
|
|
796
832
|
}
|
|
797
833
|
});
|
|
798
|
-
this.bot
|
|
834
|
+
this.bot?.on("message_reaction", async (ctx) => {
|
|
799
835
|
try {
|
|
800
836
|
await this.messageManager.handleReaction(ctx);
|
|
801
837
|
} catch (error) {
|
|
@@ -1074,7 +1110,7 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1074
1110
|
entities,
|
|
1075
1111
|
source: "telegram",
|
|
1076
1112
|
chat,
|
|
1077
|
-
botUsername: this.bot
|
|
1113
|
+
botUsername: this.bot?.botInfo?.username
|
|
1078
1114
|
};
|
|
1079
1115
|
if (chat.type !== "private") {
|
|
1080
1116
|
await this.runtime.emitEvent("TELEGRAM_WORLD_JOINED" /* WORLD_JOINED */, telegramWorldPayload);
|
|
@@ -1107,12 +1143,13 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1107
1143
|
entityBatch.map(async (entity) => {
|
|
1108
1144
|
try {
|
|
1109
1145
|
if (entity.id) {
|
|
1146
|
+
const telegramMetadata = entity.metadata?.telegram;
|
|
1110
1147
|
await this.runtime.ensureConnection({
|
|
1111
1148
|
entityId: entity.id,
|
|
1112
1149
|
roomId,
|
|
1113
|
-
userName:
|
|
1114
|
-
name:
|
|
1115
|
-
userId:
|
|
1150
|
+
userName: telegramMetadata?.username,
|
|
1151
|
+
name: telegramMetadata?.name,
|
|
1152
|
+
userId: telegramMetadata?.id,
|
|
1116
1153
|
source: "telegram",
|
|
1117
1154
|
channelId,
|
|
1118
1155
|
serverId,
|
|
@@ -1125,7 +1162,8 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1125
1162
|
);
|
|
1126
1163
|
}
|
|
1127
1164
|
} catch (err) {
|
|
1128
|
-
|
|
1165
|
+
const telegramMetadata = entity.metadata?.telegram;
|
|
1166
|
+
logger2.warn(`Failed to sync user ${telegramMetadata?.username}: ${err}`);
|
|
1129
1167
|
}
|
|
1130
1168
|
})
|
|
1131
1169
|
);
|
|
@@ -1197,7 +1235,7 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1197
1235
|
this.syncedEntityIds.add(userId);
|
|
1198
1236
|
} else if (chat.type === "group" || chat.type === "supergroup") {
|
|
1199
1237
|
try {
|
|
1200
|
-
const admins = await this.bot
|
|
1238
|
+
const admins = await this.bot?.telegram.getChatAdministrators(chat.id);
|
|
1201
1239
|
if (admins && admins.length > 0) {
|
|
1202
1240
|
for (const admin of admins) {
|
|
1203
1241
|
const userId = createUniqueUuid2(this.runtime, admin.user.id.toString());
|
|
@@ -1284,15 +1322,21 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
1284
1322
|
}
|
|
1285
1323
|
}
|
|
1286
1324
|
static registerSendHandlers(runtime, serviceInstance) {
|
|
1287
|
-
if (serviceInstance) {
|
|
1325
|
+
if (serviceInstance && serviceInstance.bot) {
|
|
1288
1326
|
runtime.registerSendHandler(
|
|
1289
1327
|
"telegram",
|
|
1290
1328
|
serviceInstance.handleSendMessage.bind(serviceInstance)
|
|
1291
1329
|
);
|
|
1292
1330
|
logger2.info("[Telegram] Registered send handler.");
|
|
1331
|
+
} else {
|
|
1332
|
+
logger2.warn("[Telegram] Cannot register send handler - bot not initialized.");
|
|
1293
1333
|
}
|
|
1294
1334
|
}
|
|
1295
1335
|
async handleSendMessage(runtime, target, content) {
|
|
1336
|
+
if (!this.bot || !this.messageManager) {
|
|
1337
|
+
logger2.error("[Telegram SendHandler] Bot not initialized - cannot send messages.");
|
|
1338
|
+
throw new Error("Telegram bot is not initialized. Please provide TELEGRAM_BOT_TOKEN.");
|
|
1339
|
+
}
|
|
1296
1340
|
let chatId;
|
|
1297
1341
|
if (target.channelId) {
|
|
1298
1342
|
chatId = target.channelId;
|
|
@@ -1406,6 +1450,9 @@ var TelegramTestSuite = class {
|
|
|
1406
1450
|
}
|
|
1407
1451
|
async testCreatingTelegramBot(runtime) {
|
|
1408
1452
|
this.telegramClient = runtime.getService("telegram");
|
|
1453
|
+
if (!this.telegramClient || !this.telegramClient.messageManager) {
|
|
1454
|
+
throw new Error("Telegram service or message manager not initialized - check TELEGRAM_BOT_TOKEN");
|
|
1455
|
+
}
|
|
1409
1456
|
this.bot = this.telegramClient.messageManager.bot;
|
|
1410
1457
|
this.messageManager = this.telegramClient.messageManager;
|
|
1411
1458
|
logger3.debug("Telegram bot initialized successfully.");
|