@elizaos/plugin-telegram 1.0.0-beta.7 → 1.0.0
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/LICENSE +1 -1
- package/README.md +56 -0
- package/dist/index.d.ts +328 -0
- package/dist/index.js +774 -289
- package/dist/index.js.map +1 -1
- package/package.json +9 -3
package/dist/index.js
CHANGED
|
@@ -52,29 +52,126 @@ import {
|
|
|
52
52
|
createUniqueUuid,
|
|
53
53
|
logger
|
|
54
54
|
} from "@elizaos/core";
|
|
55
|
+
import { Markup as Markup2 } from "telegraf";
|
|
55
56
|
|
|
56
57
|
// src/utils.ts
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
import { Markup } from "telegraf";
|
|
59
|
+
var TELEGRAM_RESERVED_REGEX = /([_*[\]()~`>#+\-=|{}.!\\])/g;
|
|
60
|
+
function escapePlainText(text) {
|
|
61
|
+
if (!text) return "";
|
|
62
|
+
return text.replace(TELEGRAM_RESERVED_REGEX, "\\$1");
|
|
63
|
+
}
|
|
64
|
+
function escapePlainTextPreservingBlockquote(text) {
|
|
65
|
+
if (!text) return "";
|
|
66
|
+
return text.split("\n").map((line) => {
|
|
67
|
+
const match = line.match(/^(>+\s?)(.*)$/);
|
|
68
|
+
if (match) {
|
|
69
|
+
return match[1] + escapePlainText(match[2]);
|
|
70
|
+
}
|
|
71
|
+
return escapePlainText(line);
|
|
72
|
+
}).join("\n");
|
|
73
|
+
}
|
|
74
|
+
function escapeCode(text) {
|
|
75
|
+
if (!text) return "";
|
|
76
|
+
return text.replace(/([`\\])/g, "\\$1");
|
|
77
|
+
}
|
|
78
|
+
function escapeUrl(url) {
|
|
79
|
+
if (!url) return "";
|
|
80
|
+
return url.replace(/([)\\])/g, "\\$1");
|
|
81
|
+
}
|
|
82
|
+
function convertMarkdownToTelegram(markdown) {
|
|
83
|
+
const replacements = [];
|
|
84
|
+
function storeReplacement(formatted) {
|
|
85
|
+
const placeholder = `\0${replacements.length}\0`;
|
|
86
|
+
replacements.push(formatted);
|
|
87
|
+
return placeholder;
|
|
60
88
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
89
|
+
let converted = markdown;
|
|
90
|
+
converted = converted.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
|
|
91
|
+
const escapedCode = escapeCode(code);
|
|
92
|
+
const formatted = "```" + (lang || "") + "\n" + escapedCode + "```";
|
|
93
|
+
return storeReplacement(formatted);
|
|
94
|
+
});
|
|
95
|
+
converted = converted.replace(/`([^`]+)`/g, (match, code) => {
|
|
96
|
+
const escapedCode = escapeCode(code);
|
|
97
|
+
const formatted = "`" + escapedCode + "`";
|
|
98
|
+
return storeReplacement(formatted);
|
|
99
|
+
});
|
|
100
|
+
converted = converted.replace(
|
|
101
|
+
/$begin:math:display$([^$end:math:display$]+)]$begin:math:text$([^)]+)$end:math:text$/g,
|
|
102
|
+
(match, text, url) => {
|
|
103
|
+
const formattedText = escapePlainText(text);
|
|
104
|
+
const escapedURL = escapeUrl(url);
|
|
105
|
+
const formatted = `[${formattedText}](${escapedURL})`;
|
|
106
|
+
return storeReplacement(formatted);
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
converted = converted.replace(/\*\*([^*]+)\*\*/g, (match, content) => {
|
|
110
|
+
const formattedContent = escapePlainText(content);
|
|
111
|
+
const formatted = `*${formattedContent}*`;
|
|
112
|
+
return storeReplacement(formatted);
|
|
113
|
+
});
|
|
114
|
+
converted = converted.replace(/~~([^~]+)~~/g, (match, content) => {
|
|
115
|
+
const formattedContent = escapePlainText(content);
|
|
116
|
+
const formatted = `~${formattedContent}~`;
|
|
117
|
+
return storeReplacement(formatted);
|
|
118
|
+
});
|
|
119
|
+
converted = converted.replace(/(?<!\*)\*([^*\n]+)\*(?!\*)/g, (match, content) => {
|
|
120
|
+
const formattedContent = escapePlainText(content);
|
|
121
|
+
const formatted = `_${formattedContent}_`;
|
|
122
|
+
return storeReplacement(formatted);
|
|
123
|
+
});
|
|
124
|
+
converted = converted.replace(/_([^_\n]+)_/g, (match, content) => {
|
|
125
|
+
const formattedContent = escapePlainText(content);
|
|
126
|
+
const formatted = `_${formattedContent}_`;
|
|
127
|
+
return storeReplacement(formatted);
|
|
128
|
+
});
|
|
129
|
+
converted = converted.replace(/^(#{1,6})\s*(.*)$/gm, (match, hashes, headerContent) => {
|
|
130
|
+
const formatted = `*${escapePlainText(headerContent.trim())}*`;
|
|
131
|
+
return storeReplacement(formatted);
|
|
132
|
+
});
|
|
133
|
+
const NULL_CHAR = String.fromCharCode(0);
|
|
134
|
+
const PLACEHOLDER_PATTERN = new RegExp(`(${NULL_CHAR}\\d+${NULL_CHAR})`, "g");
|
|
135
|
+
const PLACEHOLDER_TEST = new RegExp(`^${NULL_CHAR}\\d+${NULL_CHAR}$`);
|
|
136
|
+
const PLACEHOLDER_REPLACE = new RegExp(`${NULL_CHAR}(\\d+)${NULL_CHAR}`, "g");
|
|
137
|
+
const finalEscaped = converted.split(PLACEHOLDER_PATTERN).map((segment) => {
|
|
138
|
+
if (PLACEHOLDER_TEST.test(segment)) {
|
|
139
|
+
return segment;
|
|
140
|
+
} else {
|
|
141
|
+
return escapePlainTextPreservingBlockquote(segment);
|
|
65
142
|
}
|
|
66
|
-
return part.replace(/`.*?`/g, (match) => match).replace(/([*_`\\])/g, "\\$1");
|
|
67
143
|
}).join("");
|
|
144
|
+
const finalResult = finalEscaped.replace(PLACEHOLDER_REPLACE, (_, index) => {
|
|
145
|
+
return replacements[parseInt(index)];
|
|
146
|
+
});
|
|
147
|
+
return finalResult;
|
|
148
|
+
}
|
|
149
|
+
function convertToTelegramButtons(buttons) {
|
|
150
|
+
if (!buttons) return [];
|
|
151
|
+
return buttons.map((button) => {
|
|
152
|
+
switch (button.kind) {
|
|
153
|
+
case "login":
|
|
154
|
+
return Markup.button.login(button.text, button.url);
|
|
155
|
+
case "url":
|
|
156
|
+
return Markup.button.url(button.text, button.url);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
68
159
|
}
|
|
69
160
|
|
|
70
161
|
// src/messageManager.ts
|
|
71
162
|
import fs from "node:fs";
|
|
72
163
|
var MAX_MESSAGE_LENGTH = 4096;
|
|
73
164
|
var getChannelType = (chat) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
165
|
+
switch (chat.type) {
|
|
166
|
+
case "private":
|
|
167
|
+
return ChannelType.DM;
|
|
168
|
+
case "group":
|
|
169
|
+
case "supergroup":
|
|
170
|
+
case "channel":
|
|
171
|
+
return ChannelType.GROUP;
|
|
172
|
+
default:
|
|
173
|
+
throw new Error(`Unrecognized Telegram chat type: ${chat.type}`);
|
|
174
|
+
}
|
|
78
175
|
};
|
|
79
176
|
var MessageManager = class {
|
|
80
177
|
bot;
|
|
@@ -97,15 +194,14 @@ var MessageManager = class {
|
|
|
97
194
|
* @returns {Promise<{ description: string } | null>} The description of the processed image or null if no image found.
|
|
98
195
|
*/
|
|
99
196
|
async processImage(message) {
|
|
100
|
-
var _a, _b, _c;
|
|
101
197
|
try {
|
|
102
198
|
let imageUrl = null;
|
|
103
199
|
logger.info(`Telegram Message: ${message}`);
|
|
104
|
-
if ("photo" in message &&
|
|
200
|
+
if ("photo" in message && message.photo?.length > 0) {
|
|
105
201
|
const photo = message.photo[message.photo.length - 1];
|
|
106
202
|
const fileLink = await this.bot.telegram.getFileLink(photo.file_id);
|
|
107
203
|
imageUrl = fileLink.toString();
|
|
108
|
-
} else if ("document" in message &&
|
|
204
|
+
} else if ("document" in message && message.document?.mime_type?.startsWith("image/")) {
|
|
109
205
|
const fileLink = await this.bot.telegram.getFileLink(message.document.file_id);
|
|
110
206
|
imageUrl = fileLink.toString();
|
|
111
207
|
}
|
|
@@ -127,7 +223,7 @@ ${description}]` };
|
|
|
127
223
|
* Sends a message in chunks, handling attachments and splitting the message if necessary
|
|
128
224
|
*
|
|
129
225
|
* @param {Context} ctx - The context object representing the current state of the bot
|
|
130
|
-
* @param {
|
|
226
|
+
* @param {TelegramContent} content - The content of the message to be sent
|
|
131
227
|
* @param {number} [replyToMessageId] - The ID of the message to reply to, if any
|
|
132
228
|
* @returns {Promise<Message.TextMessage[]>} - An array of TextMessage objects representing the messages sent
|
|
133
229
|
*/
|
|
@@ -143,7 +239,7 @@ ${description}]` };
|
|
|
143
239
|
};
|
|
144
240
|
let mediaType = void 0;
|
|
145
241
|
for (const prefix in typeMap) {
|
|
146
|
-
if (attachment.contentType
|
|
242
|
+
if (attachment.contentType?.startsWith(prefix)) {
|
|
147
243
|
mediaType = typeMap[prefix];
|
|
148
244
|
break;
|
|
149
245
|
}
|
|
@@ -155,14 +251,26 @@ ${description}]` };
|
|
|
155
251
|
}
|
|
156
252
|
await this.sendMedia(ctx, attachment.url, mediaType, attachment.description);
|
|
157
253
|
});
|
|
254
|
+
return [];
|
|
158
255
|
} else {
|
|
159
|
-
const chunks = this.splitMessage(content.text);
|
|
256
|
+
const chunks = this.splitMessage(content.text ?? "");
|
|
160
257
|
const sentMessages = [];
|
|
258
|
+
const telegramButtons = convertToTelegramButtons(content.buttons ?? []);
|
|
259
|
+
if (!ctx.chat) {
|
|
260
|
+
logger.error("sendMessageInChunks: ctx.chat is undefined");
|
|
261
|
+
return [];
|
|
262
|
+
}
|
|
263
|
+
await ctx.telegram.sendChatAction(ctx.chat.id, "typing");
|
|
161
264
|
for (let i = 0; i < chunks.length; i++) {
|
|
162
|
-
const chunk =
|
|
265
|
+
const chunk = convertMarkdownToTelegram(chunks[i]);
|
|
266
|
+
if (!ctx.chat) {
|
|
267
|
+
logger.error("sendMessageInChunks loop: ctx.chat is undefined");
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
163
270
|
const sentMessage = await ctx.telegram.sendMessage(ctx.chat.id, chunk, {
|
|
164
271
|
reply_parameters: i === 0 && replyToMessageId ? { message_id: replyToMessageId } : void 0,
|
|
165
|
-
parse_mode: "
|
|
272
|
+
parse_mode: "MarkdownV2",
|
|
273
|
+
...Markup2.inlineKeyboard(telegramButtons)
|
|
166
274
|
});
|
|
167
275
|
sentMessages.push(sentMessage);
|
|
168
276
|
}
|
|
@@ -193,6 +301,9 @@ ${description}]` };
|
|
|
193
301
|
if (!sendFunction) {
|
|
194
302
|
throw new Error(`Unsupported media type: ${type}`);
|
|
195
303
|
}
|
|
304
|
+
if (!ctx.chat) {
|
|
305
|
+
throw new Error("sendMedia: ctx.chat is undefined");
|
|
306
|
+
}
|
|
196
307
|
if (isUrl) {
|
|
197
308
|
await sendFunction(ctx.chat.id, mediaPath, { caption });
|
|
198
309
|
} else {
|
|
@@ -201,6 +312,9 @@ ${description}]` };
|
|
|
201
312
|
}
|
|
202
313
|
const fileStream = fs.createReadStream(mediaPath);
|
|
203
314
|
try {
|
|
315
|
+
if (!ctx.chat) {
|
|
316
|
+
throw new Error("sendMedia (file): ctx.chat is undefined");
|
|
317
|
+
}
|
|
204
318
|
await sendFunction(ctx.chat.id, { source: fileStream }, { caption });
|
|
205
319
|
} finally {
|
|
206
320
|
fileStream.destroy();
|
|
@@ -210,8 +324,10 @@ ${description}]` };
|
|
|
210
324
|
`${type.charAt(0).toUpperCase() + type.slice(1)} sent successfully: ${mediaPath}`
|
|
211
325
|
);
|
|
212
326
|
} catch (error) {
|
|
213
|
-
|
|
214
|
-
logger.
|
|
327
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
328
|
+
logger.error(`Failed to send ${type}. Path: ${mediaPath}. Error: ${errorMessage}`, {
|
|
329
|
+
originalError: error
|
|
330
|
+
});
|
|
215
331
|
throw error;
|
|
216
332
|
}
|
|
217
333
|
}
|
|
@@ -224,6 +340,7 @@ ${description}]` };
|
|
|
224
340
|
*/
|
|
225
341
|
splitMessage(text) {
|
|
226
342
|
const chunks = [];
|
|
343
|
+
if (!text) return chunks;
|
|
227
344
|
let currentChunk = "";
|
|
228
345
|
const lines = text.split("\n");
|
|
229
346
|
for (const line of lines) {
|
|
@@ -244,14 +361,20 @@ ${description}]` };
|
|
|
244
361
|
* @returns {Promise<void>}
|
|
245
362
|
*/
|
|
246
363
|
async handleMessage(ctx) {
|
|
247
|
-
var _a, _b, _c, _d, _e, _f;
|
|
248
364
|
if (!ctx.message || !ctx.from) return;
|
|
249
365
|
const message = ctx.message;
|
|
250
366
|
try {
|
|
251
367
|
const entityId = createUniqueUuid(this.runtime, ctx.from.id.toString());
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
368
|
+
const threadId = "is_topic_message" in message && message.is_topic_message ? message.message_thread_id?.toString() : void 0;
|
|
369
|
+
if (!ctx.chat) {
|
|
370
|
+
logger.error("handleMessage: ctx.chat is undefined");
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const roomId = createUniqueUuid(
|
|
374
|
+
this.runtime,
|
|
375
|
+
threadId ? `${ctx.chat.id}-${threadId}` : ctx.chat.id.toString()
|
|
376
|
+
);
|
|
377
|
+
const messageId = createUniqueUuid(this.runtime, message?.message_id?.toString());
|
|
255
378
|
const imageInfo = await this.processImage(message);
|
|
256
379
|
let messageText = "";
|
|
257
380
|
if ("text" in message && message.text) {
|
|
@@ -263,34 +386,6 @@ ${description}]` };
|
|
|
263
386
|
if (!fullText) return;
|
|
264
387
|
const chat = message.chat;
|
|
265
388
|
const channelType = getChannelType(chat);
|
|
266
|
-
const worldId = createUniqueUuid(
|
|
267
|
-
this.runtime,
|
|
268
|
-
chat.type === "private" ? `private_${chat.id}` : chat.id.toString()
|
|
269
|
-
);
|
|
270
|
-
const worldName = chat.type === "supergroup" ? chat.title : chat.type === "channel" ? chat.title : chat.type === "private" ? `Chat with ${chat.first_name || "Unknown"}` : "Telegram";
|
|
271
|
-
const roomName = chat.type === "private" ? chat.first_name : chat.type === "supergroup" ? chat.title : chat.type === "channel" ? chat.title : chat.type === "group" ? chat.title : "Unknown Group";
|
|
272
|
-
await this.runtime.ensureConnection({
|
|
273
|
-
entityId,
|
|
274
|
-
roomId,
|
|
275
|
-
userName,
|
|
276
|
-
name: userName,
|
|
277
|
-
source: "telegram",
|
|
278
|
-
channelId: ctx.chat.id.toString(),
|
|
279
|
-
serverId: chat.type === "private" ? void 0 : chat.id.toString(),
|
|
280
|
-
// Only set serverId for non-private chats
|
|
281
|
-
type: channelType,
|
|
282
|
-
worldId
|
|
283
|
-
});
|
|
284
|
-
const room = {
|
|
285
|
-
id: roomId,
|
|
286
|
-
name: roomName,
|
|
287
|
-
source: "telegram",
|
|
288
|
-
type: channelType,
|
|
289
|
-
channelId: ctx.chat.id.toString(),
|
|
290
|
-
serverId: chat.type === "private" ? void 0 : chat.id.toString(),
|
|
291
|
-
worldId
|
|
292
|
-
};
|
|
293
|
-
await this.runtime.ensureRoomExists(room);
|
|
294
389
|
const memory = {
|
|
295
390
|
id: messageId,
|
|
296
391
|
entityId,
|
|
@@ -306,6 +401,7 @@ ${description}]` };
|
|
|
306
401
|
};
|
|
307
402
|
const callback = async (content, _files) => {
|
|
308
403
|
try {
|
|
404
|
+
if (!content.text) return [];
|
|
309
405
|
const sentMessages = await this.sendMessageInChunks(ctx, content, message.message_id);
|
|
310
406
|
if (!sentMessages) return [];
|
|
311
407
|
const memories = [];
|
|
@@ -319,6 +415,7 @@ ${description}]` };
|
|
|
319
415
|
roomId,
|
|
320
416
|
content: {
|
|
321
417
|
...content,
|
|
418
|
+
source: "telegram",
|
|
322
419
|
text: sentMessage.text,
|
|
323
420
|
inReplyTo: messageId,
|
|
324
421
|
channelType
|
|
@@ -351,27 +448,33 @@ ${description}]` };
|
|
|
351
448
|
} catch (error) {
|
|
352
449
|
logger.error("Error handling Telegram message:", {
|
|
353
450
|
error,
|
|
354
|
-
chatId:
|
|
355
|
-
messageId:
|
|
356
|
-
from:
|
|
451
|
+
chatId: ctx.chat?.id,
|
|
452
|
+
messageId: ctx.message?.message_id,
|
|
453
|
+
from: ctx.from?.username || ctx.from?.id
|
|
357
454
|
});
|
|
358
455
|
throw error;
|
|
359
456
|
}
|
|
360
457
|
}
|
|
361
458
|
/**
|
|
362
459
|
* Handles the reaction event triggered by a user reacting to a message.
|
|
363
|
-
*
|
|
460
|
+
* @param {NarrowedContext<Context<Update>, Update.MessageReactionUpdate>} ctx The context of the message reaction update
|
|
364
461
|
* @returns {Promise<void>} A Promise that resolves when the reaction handling is complete
|
|
365
462
|
*/
|
|
366
463
|
async handleReaction(ctx) {
|
|
367
464
|
if (!ctx.update.message_reaction || !ctx.from) return;
|
|
368
465
|
const reaction = ctx.update.message_reaction;
|
|
466
|
+
const reactedToMessageId = reaction.message_id;
|
|
467
|
+
const originalMessagePlaceholder = {
|
|
468
|
+
message_id: reactedToMessageId,
|
|
469
|
+
chat: reaction.chat,
|
|
470
|
+
from: ctx.from,
|
|
471
|
+
date: Math.floor(Date.now() / 1e3)
|
|
472
|
+
};
|
|
369
473
|
const reactionType = reaction.new_reaction[0].type;
|
|
370
474
|
const reactionEmoji = reaction.new_reaction[0].type;
|
|
371
475
|
try {
|
|
372
476
|
const entityId = createUniqueUuid(this.runtime, ctx.from.id.toString());
|
|
373
477
|
const roomId = createUniqueUuid(this.runtime, ctx.chat.id.toString());
|
|
374
|
-
const worldId = createUniqueUuid(this.runtime, ctx.chat.id.toString());
|
|
375
478
|
const reactionId = createUniqueUuid(
|
|
376
479
|
this.runtime,
|
|
377
480
|
`${reaction.message_id}-${ctx.from.id}-${Date.now()}`
|
|
@@ -391,7 +494,8 @@ ${description}]` };
|
|
|
391
494
|
};
|
|
392
495
|
const callback = async (content) => {
|
|
393
496
|
try {
|
|
394
|
-
const
|
|
497
|
+
const replyText = content.text ?? "";
|
|
498
|
+
const sentMessage = await ctx.reply(replyText);
|
|
395
499
|
const responseMemory = {
|
|
396
500
|
id: createUniqueUuid(this.runtime, sentMessage.message_id.toString()),
|
|
397
501
|
entityId: this.runtime.agentId,
|
|
@@ -413,7 +517,12 @@ ${description}]` };
|
|
|
413
517
|
runtime: this.runtime,
|
|
414
518
|
message: memory,
|
|
415
519
|
callback,
|
|
416
|
-
source: "telegram"
|
|
520
|
+
source: "telegram",
|
|
521
|
+
ctx,
|
|
522
|
+
originalMessage: originalMessagePlaceholder,
|
|
523
|
+
// Cast needed due to placeholder
|
|
524
|
+
reactionString: reactionType === "emoji" ? reactionEmoji : reactionType,
|
|
525
|
+
originalReaction: reaction.new_reaction[0]
|
|
417
526
|
});
|
|
418
527
|
this.runtime.emitEvent("TELEGRAM_REACTION_RECEIVED" /* REACTION_RECEIVED */, {
|
|
419
528
|
runtime: this.runtime,
|
|
@@ -421,11 +530,14 @@ ${description}]` };
|
|
|
421
530
|
callback,
|
|
422
531
|
source: "telegram",
|
|
423
532
|
ctx,
|
|
533
|
+
originalMessage: originalMessagePlaceholder,
|
|
534
|
+
// Cast needed due to placeholder
|
|
424
535
|
reactionString: reactionType === "emoji" ? reactionEmoji : reactionType,
|
|
425
536
|
originalReaction: reaction.new_reaction[0]
|
|
426
537
|
});
|
|
427
538
|
} catch (error) {
|
|
428
|
-
|
|
539
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
540
|
+
logger.error("Error handling reaction:", { error: errorMessage, originalError: error });
|
|
429
541
|
}
|
|
430
542
|
}
|
|
431
543
|
/**
|
|
@@ -446,7 +558,7 @@ ${description}]` };
|
|
|
446
558
|
content,
|
|
447
559
|
replyToMessageId
|
|
448
560
|
);
|
|
449
|
-
if (!
|
|
561
|
+
if (!sentMessages?.length) return [];
|
|
450
562
|
const roomId = createUniqueUuid(this.runtime, chatId.toString());
|
|
451
563
|
const memories = [];
|
|
452
564
|
for (const sentMessage of sentMessages) {
|
|
@@ -472,7 +584,9 @@ ${description}]` };
|
|
|
472
584
|
}
|
|
473
585
|
this.runtime.emitEvent(EventType.MESSAGE_SENT, {
|
|
474
586
|
runtime: this.runtime,
|
|
475
|
-
|
|
587
|
+
message: {
|
|
588
|
+
content
|
|
589
|
+
},
|
|
476
590
|
roomId,
|
|
477
591
|
source: "telegram"
|
|
478
592
|
});
|
|
@@ -482,7 +596,11 @@ ${description}]` };
|
|
|
482
596
|
});
|
|
483
597
|
return sentMessages;
|
|
484
598
|
} catch (error) {
|
|
485
|
-
|
|
599
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
600
|
+
logger.error("Error sending message to Telegram:", {
|
|
601
|
+
error: errorMessage,
|
|
602
|
+
originalError: error
|
|
603
|
+
});
|
|
486
604
|
return [];
|
|
487
605
|
}
|
|
488
606
|
}
|
|
@@ -496,6 +614,7 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
496
614
|
messageManager;
|
|
497
615
|
options;
|
|
498
616
|
knownChats = /* @__PURE__ */ new Map();
|
|
617
|
+
syncedEntityIds = /* @__PURE__ */ new Set();
|
|
499
618
|
/**
|
|
500
619
|
* Constructor for TelegramService class.
|
|
501
620
|
* @param {IAgentRuntime} runtime - The runtime object for the agent.
|
|
@@ -532,6 +651,7 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
532
651
|
);
|
|
533
652
|
logger2.log("\u{1F680} Starting Telegram bot...");
|
|
534
653
|
await service.initializeBot();
|
|
654
|
+
service.setupMiddlewares();
|
|
535
655
|
service.setupMessageHandlers();
|
|
536
656
|
await service.bot.telegram.getMe();
|
|
537
657
|
return service;
|
|
@@ -549,7 +669,7 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
549
669
|
}
|
|
550
670
|
}
|
|
551
671
|
throw new Error(
|
|
552
|
-
`Telegram initialization failed after ${maxRetries} attempts. Last error: ${lastError
|
|
672
|
+
`Telegram initialization failed after ${maxRetries} attempts. Last error: ${lastError?.message}`
|
|
553
673
|
);
|
|
554
674
|
}
|
|
555
675
|
/**
|
|
@@ -584,18 +704,113 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
584
704
|
process.once("SIGINT", () => this.bot.stop("SIGINT"));
|
|
585
705
|
process.once("SIGTERM", () => this.bot.stop("SIGTERM"));
|
|
586
706
|
}
|
|
707
|
+
/**
|
|
708
|
+
* Sets up the middleware chain for preprocessing messages before they reach handlers.
|
|
709
|
+
* This critical method establishes a sequential processing pipeline that:
|
|
710
|
+
*
|
|
711
|
+
* 1. Authorization - Verifies if a chat is allowed to interact with the bot based on configured settings
|
|
712
|
+
* 2. Chat Discovery - Ensures chat entities and worlds exist in the runtime, creating them if needed
|
|
713
|
+
* 3. Forum Topics - Handles Telegram forum topics as separate rooms for better conversation management
|
|
714
|
+
* 4. Entity Synchronization - Ensures message senders are properly synchronized as entities
|
|
715
|
+
*
|
|
716
|
+
* The middleware chain runs in sequence for each message, with each step potentially
|
|
717
|
+
* enriching the context or stopping processing if conditions aren't met.
|
|
718
|
+
* This preprocessing is essential for maintaining consistent state before message handlers execute.
|
|
719
|
+
*
|
|
720
|
+
* @private
|
|
721
|
+
*/
|
|
722
|
+
setupMiddlewares() {
|
|
723
|
+
this.bot.use(this.authorizationMiddleware.bind(this));
|
|
724
|
+
this.bot.use(this.chatAndEntityMiddleware.bind(this));
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Authorization middleware - checks if chat is allowed to interact with the bot
|
|
728
|
+
* based on the TELEGRAM_ALLOWED_CHATS configuration.
|
|
729
|
+
*
|
|
730
|
+
* @param {Context} ctx - The context of the incoming update
|
|
731
|
+
* @param {Function} next - The function to call to proceed to the next middleware
|
|
732
|
+
* @returns {Promise<void>}
|
|
733
|
+
* @private
|
|
734
|
+
*/
|
|
735
|
+
async authorizationMiddleware(ctx, next) {
|
|
736
|
+
if (!await this.isGroupAuthorized(ctx)) {
|
|
737
|
+
logger2.debug("Chat not authorized, skipping message processing");
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
await next();
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Chat and entity management middleware - handles new chats, forum topics, and entity synchronization.
|
|
744
|
+
* This middleware implements decision logic to determine which operations are needed based on
|
|
745
|
+
* the chat type and whether we've seen this chat before.
|
|
746
|
+
*
|
|
747
|
+
* @param {Context} ctx - The context of the incoming update
|
|
748
|
+
* @param {Function} next - The function to call to proceed to the next middleware
|
|
749
|
+
* @returns {Promise<void>}
|
|
750
|
+
* @private
|
|
751
|
+
*/
|
|
752
|
+
async chatAndEntityMiddleware(ctx, next) {
|
|
753
|
+
if (!ctx.chat) return next();
|
|
754
|
+
const chatId = ctx.chat.id.toString();
|
|
755
|
+
if (!this.knownChats.has(chatId)) {
|
|
756
|
+
await this.handleNewChat(ctx);
|
|
757
|
+
return next();
|
|
758
|
+
}
|
|
759
|
+
await this.processExistingChat(ctx);
|
|
760
|
+
await next();
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Process an existing chat based on chat type and message properties.
|
|
764
|
+
* Different chat types require different processing steps.
|
|
765
|
+
*
|
|
766
|
+
* @param {Context} ctx - The context of the incoming update
|
|
767
|
+
* @returns {Promise<void>}
|
|
768
|
+
* @private
|
|
769
|
+
*/
|
|
770
|
+
async processExistingChat(ctx) {
|
|
771
|
+
if (!ctx.chat) return;
|
|
772
|
+
const chat = ctx.chat;
|
|
773
|
+
if (chat.type === "supergroup" && chat.is_forum && ctx.message?.message_thread_id) {
|
|
774
|
+
try {
|
|
775
|
+
await this.handleForumTopic(ctx);
|
|
776
|
+
} catch (error) {
|
|
777
|
+
logger2.error(`Error handling forum topic: ${error}`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
if (ctx.from && ctx.chat.type !== "private") {
|
|
781
|
+
await this.syncEntity(ctx);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Sets up message and reaction handlers for the bot.
|
|
786
|
+
* Configures event handlers to process incoming messages and reactions.
|
|
787
|
+
*
|
|
788
|
+
* @private
|
|
789
|
+
*/
|
|
790
|
+
setupMessageHandlers() {
|
|
791
|
+
this.bot.on("message", async (ctx) => {
|
|
792
|
+
try {
|
|
793
|
+
await this.messageManager.handleMessage(ctx);
|
|
794
|
+
} catch (error) {
|
|
795
|
+
logger2.error("Error handling message:", error);
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
this.bot.on("message_reaction", async (ctx) => {
|
|
799
|
+
try {
|
|
800
|
+
await this.messageManager.handleReaction(ctx);
|
|
801
|
+
} catch (error) {
|
|
802
|
+
logger2.error("Error handling reaction:", error);
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
}
|
|
587
806
|
/**
|
|
588
807
|
* Checks if a group is authorized, based on the TELEGRAM_ALLOWED_CHATS setting.
|
|
589
808
|
* @param {Context} ctx - The context of the incoming update.
|
|
590
809
|
* @returns {Promise<boolean>} A Promise that resolves with a boolean indicating if the group is authorized.
|
|
591
810
|
*/
|
|
592
811
|
async isGroupAuthorized(ctx) {
|
|
593
|
-
|
|
594
|
-
const chatId = (_a = ctx.chat) == null ? void 0 : _a.id.toString();
|
|
812
|
+
const chatId = ctx.chat?.id.toString();
|
|
595
813
|
if (!chatId) return false;
|
|
596
|
-
if (!this.knownChats.has(chatId)) {
|
|
597
|
-
await this.handleNewChat(ctx);
|
|
598
|
-
}
|
|
599
814
|
const allowedChats = this.runtime.getSetting("TELEGRAM_ALLOWED_CHATS");
|
|
600
815
|
if (!allowedChats) {
|
|
601
816
|
return true;
|
|
@@ -609,39 +824,199 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
609
824
|
}
|
|
610
825
|
}
|
|
611
826
|
/**
|
|
612
|
-
*
|
|
827
|
+
* Synchronizes an entity from a message context with the runtime system.
|
|
828
|
+
* This method handles three cases:
|
|
829
|
+
* 1. Message sender - most common case
|
|
830
|
+
* 2. New chat member - when a user joins the chat
|
|
831
|
+
* 3. Left chat member - when a user leaves the chat
|
|
832
|
+
*
|
|
833
|
+
* @param {Context} ctx - The context of the incoming update
|
|
834
|
+
* @returns {Promise<void>}
|
|
835
|
+
* @private
|
|
836
|
+
*/
|
|
837
|
+
async syncEntity(ctx) {
|
|
838
|
+
if (!ctx.chat) return;
|
|
839
|
+
const chat = ctx.chat;
|
|
840
|
+
const chatId = chat.id.toString();
|
|
841
|
+
const worldId = createUniqueUuid2(this.runtime, chatId);
|
|
842
|
+
const roomId = createUniqueUuid2(
|
|
843
|
+
this.runtime,
|
|
844
|
+
ctx.message?.message_thread_id ? `${ctx.chat.id}-${ctx.message.message_thread_id}` : ctx.chat.id.toString()
|
|
845
|
+
);
|
|
846
|
+
await this.syncMessageSender(ctx, worldId, roomId, chatId);
|
|
847
|
+
await this.syncNewChatMember(ctx, worldId, roomId, chatId);
|
|
848
|
+
await this.syncLeftChatMember(ctx);
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Synchronizes the message sender entity with the runtime system.
|
|
852
|
+
* This is the most common entity sync case.
|
|
853
|
+
*
|
|
854
|
+
* @param {Context} ctx - The context of the incoming update
|
|
855
|
+
* @param {UUID} worldId - The ID of the world
|
|
856
|
+
* @param {UUID} roomId - The ID of the room
|
|
857
|
+
* @param {string} chatId - The ID of the chat
|
|
858
|
+
* @returns {Promise<void>}
|
|
859
|
+
* @private
|
|
860
|
+
*/
|
|
861
|
+
async syncMessageSender(ctx, worldId, roomId, chatId) {
|
|
862
|
+
if (ctx.from && !this.syncedEntityIds.has(ctx.from.id.toString())) {
|
|
863
|
+
const telegramId = ctx.from.id.toString();
|
|
864
|
+
const entityId = createUniqueUuid2(this.runtime, telegramId);
|
|
865
|
+
await this.runtime.ensureConnection({
|
|
866
|
+
entityId,
|
|
867
|
+
roomId,
|
|
868
|
+
userName: ctx.from.username,
|
|
869
|
+
userId: telegramId,
|
|
870
|
+
name: ctx.from.first_name || ctx.from.username || "Unknown User",
|
|
871
|
+
source: "telegram",
|
|
872
|
+
channelId: chatId,
|
|
873
|
+
serverId: chatId,
|
|
874
|
+
type: ChannelType2.GROUP,
|
|
875
|
+
worldId
|
|
876
|
+
});
|
|
877
|
+
this.syncedEntityIds.add(entityId);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Synchronizes a new chat member entity with the runtime system.
|
|
882
|
+
* Triggered when a user joins the chat.
|
|
883
|
+
*
|
|
884
|
+
* @param {Context} ctx - The context of the incoming update
|
|
885
|
+
* @param {UUID} worldId - The ID of the world
|
|
886
|
+
* @param {UUID} roomId - The ID of the room
|
|
887
|
+
* @param {string} chatId - The ID of the chat
|
|
888
|
+
* @returns {Promise<void>}
|
|
889
|
+
* @private
|
|
890
|
+
*/
|
|
891
|
+
async syncNewChatMember(ctx, worldId, roomId, chatId) {
|
|
892
|
+
if (ctx.message && "new_chat_member" in ctx.message) {
|
|
893
|
+
const newMember = ctx.message.new_chat_member;
|
|
894
|
+
const telegramId = newMember.id.toString();
|
|
895
|
+
const entityId = createUniqueUuid2(this.runtime, telegramId);
|
|
896
|
+
if (this.syncedEntityIds.has(telegramId)) return;
|
|
897
|
+
await this.runtime.ensureConnection({
|
|
898
|
+
entityId,
|
|
899
|
+
roomId,
|
|
900
|
+
userName: newMember.username,
|
|
901
|
+
userId: telegramId,
|
|
902
|
+
name: newMember.first_name || newMember.username || "Unknown User",
|
|
903
|
+
source: "telegram",
|
|
904
|
+
channelId: chatId,
|
|
905
|
+
serverId: chatId,
|
|
906
|
+
type: ChannelType2.GROUP,
|
|
907
|
+
worldId
|
|
908
|
+
});
|
|
909
|
+
this.syncedEntityIds.add(entityId);
|
|
910
|
+
this.runtime.emitEvent(["TELEGRAM_ENTITY_JOINED" /* ENTITY_JOINED */], {
|
|
911
|
+
runtime: this.runtime,
|
|
912
|
+
entityId,
|
|
913
|
+
worldId,
|
|
914
|
+
newMember,
|
|
915
|
+
ctx
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Updates entity status when a user leaves the chat.
|
|
921
|
+
*
|
|
922
|
+
* @param {Context} ctx - The context of the incoming update
|
|
923
|
+
* @returns {Promise<void>}
|
|
924
|
+
* @private
|
|
925
|
+
*/
|
|
926
|
+
async syncLeftChatMember(ctx) {
|
|
927
|
+
if (ctx.message && "left_chat_member" in ctx.message) {
|
|
928
|
+
const leftMember = ctx.message.left_chat_member;
|
|
929
|
+
const telegramId = leftMember.id.toString();
|
|
930
|
+
const entityId = createUniqueUuid2(this.runtime, telegramId);
|
|
931
|
+
const existingEntity = await this.runtime.getEntityById(entityId);
|
|
932
|
+
if (existingEntity) {
|
|
933
|
+
existingEntity.metadata = {
|
|
934
|
+
...existingEntity.metadata,
|
|
935
|
+
status: "INACTIVE",
|
|
936
|
+
leftAt: Date.now()
|
|
937
|
+
};
|
|
938
|
+
await this.runtime.updateEntity(existingEntity);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Handles forum topics by creating appropriate rooms in the runtime system.
|
|
944
|
+
* This enables proper conversation management for Telegram's forum feature.
|
|
945
|
+
*
|
|
946
|
+
* @param {Context} ctx - The context of the incoming update
|
|
947
|
+
* @returns {Promise<void>}
|
|
948
|
+
* @private
|
|
949
|
+
*/
|
|
950
|
+
async handleForumTopic(ctx) {
|
|
951
|
+
if (!ctx.chat || !ctx.message?.message_thread_id) return;
|
|
952
|
+
const chat = ctx.chat;
|
|
953
|
+
const chatId = chat.id.toString();
|
|
954
|
+
const worldId = createUniqueUuid2(this.runtime, chatId);
|
|
955
|
+
const room = await this.buildForumTopicRoom(ctx, worldId);
|
|
956
|
+
if (!room) return;
|
|
957
|
+
await this.runtime.ensureRoomExists(room);
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Builds entity for message sender
|
|
961
|
+
*/
|
|
962
|
+
buildMsgSenderEntity(from) {
|
|
963
|
+
if (!from) return null;
|
|
964
|
+
const userId = createUniqueUuid2(this.runtime, from.id.toString());
|
|
965
|
+
const telegramId = from.id.toString();
|
|
966
|
+
return {
|
|
967
|
+
id: userId,
|
|
968
|
+
agentId: this.runtime.agentId,
|
|
969
|
+
names: [from.first_name || from.username || "Unknown User"],
|
|
970
|
+
metadata: {
|
|
971
|
+
telegram: {
|
|
972
|
+
id: telegramId,
|
|
973
|
+
username: from.username,
|
|
974
|
+
name: from.first_name || from.username || "Unknown User"
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Handles new chat discovery and emits WORLD_JOINED event.
|
|
981
|
+
* This is a critical function that ensures new chats are properly
|
|
982
|
+
* registered in the runtime system and appropriate events are emitted.
|
|
983
|
+
*
|
|
613
984
|
* @param {Context} ctx - The context of the incoming update
|
|
985
|
+
* @returns {Promise<void>}
|
|
986
|
+
* @private
|
|
614
987
|
*/
|
|
615
988
|
async handleNewChat(ctx) {
|
|
616
989
|
if (!ctx.chat) return;
|
|
617
990
|
const chat = ctx.chat;
|
|
618
991
|
const chatId = chat.id.toString();
|
|
619
992
|
this.knownChats.set(chatId, chat);
|
|
620
|
-
|
|
621
|
-
let channelType;
|
|
622
|
-
switch (chat.type) {
|
|
623
|
-
case "private":
|
|
624
|
-
chatTitle = `Chat with ${chat.first_name || "Unknown User"}`;
|
|
625
|
-
channelType = ChannelType2.DM;
|
|
626
|
-
break;
|
|
627
|
-
case "group":
|
|
628
|
-
chatTitle = chat.title || "Unknown Group";
|
|
629
|
-
channelType = ChannelType2.GROUP;
|
|
630
|
-
break;
|
|
631
|
-
case "supergroup":
|
|
632
|
-
chatTitle = chat.title || "Unknown Supergroup";
|
|
633
|
-
channelType = ChannelType2.GROUP;
|
|
634
|
-
break;
|
|
635
|
-
case "channel":
|
|
636
|
-
chatTitle = chat.title || "Unknown Channel";
|
|
637
|
-
channelType = ChannelType2.FEED;
|
|
638
|
-
break;
|
|
639
|
-
default:
|
|
640
|
-
chatTitle = "Unknown Chat";
|
|
641
|
-
channelType = ChannelType2.GROUP;
|
|
642
|
-
}
|
|
993
|
+
const { chatTitle, channelType } = this.getChatTypeInfo(chat);
|
|
643
994
|
const worldId = createUniqueUuid2(this.runtime, chatId);
|
|
644
|
-
const
|
|
995
|
+
const existingWorld = await this.runtime.getWorld(worldId);
|
|
996
|
+
if (existingWorld) {
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
const userId = ctx.from ? createUniqueUuid2(this.runtime, ctx.from.id.toString()) : null;
|
|
1000
|
+
let admins = [];
|
|
1001
|
+
let owner = null;
|
|
1002
|
+
if (chat.type === "group" || chat.type === "supergroup" || chat.type === "channel") {
|
|
1003
|
+
try {
|
|
1004
|
+
const chatAdmins = await ctx.getChatAdministrators();
|
|
1005
|
+
admins = chatAdmins;
|
|
1006
|
+
const foundOwner = admins.find(
|
|
1007
|
+
(admin) => admin.status === "creator"
|
|
1008
|
+
);
|
|
1009
|
+
owner = foundOwner || null;
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
logger2.warn(
|
|
1012
|
+
`Could not get chat administrators: ${error instanceof Error ? error.message : String(error)}`
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
let ownerId = userId;
|
|
1017
|
+
if (owner) {
|
|
1018
|
+
ownerId = createUniqueUuid2(this.runtime, String(owner.user.id));
|
|
1019
|
+
}
|
|
645
1020
|
const world = {
|
|
646
1021
|
id: worldId,
|
|
647
1022
|
name: chatTitle,
|
|
@@ -649,14 +1024,17 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
649
1024
|
serverId: chatId,
|
|
650
1025
|
metadata: {
|
|
651
1026
|
source: "telegram",
|
|
652
|
-
ownership: { ownerId
|
|
653
|
-
roles: {
|
|
654
|
-
[
|
|
655
|
-
}
|
|
1027
|
+
...ownerId && { ownership: { ownerId } },
|
|
1028
|
+
roles: ownerId ? {
|
|
1029
|
+
[ownerId]: Role.OWNER
|
|
1030
|
+
} : {},
|
|
1031
|
+
chatType: chat.type,
|
|
1032
|
+
isForumEnabled: chat.type === "supergroup" && chat.is_forum
|
|
656
1033
|
}
|
|
657
1034
|
};
|
|
658
|
-
|
|
659
|
-
|
|
1035
|
+
await this.runtime.ensureWorldExists(world);
|
|
1036
|
+
const generalRoom = {
|
|
1037
|
+
id: createUniqueUuid2(this.runtime, chatId),
|
|
660
1038
|
name: chatTitle,
|
|
661
1039
|
source: "telegram",
|
|
662
1040
|
type: channelType,
|
|
@@ -664,204 +1042,289 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
664
1042
|
serverId: chatId,
|
|
665
1043
|
worldId
|
|
666
1044
|
};
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
metadata: {
|
|
675
|
-
telegram: {
|
|
676
|
-
id: chat.id.toString(),
|
|
677
|
-
username: chat.username || "unknown",
|
|
678
|
-
name: chat.first_name || "Unknown User"
|
|
679
|
-
},
|
|
680
|
-
source: "telegram"
|
|
681
|
-
}
|
|
682
|
-
});
|
|
683
|
-
} else if (chat.type === "group" || chat.type === "supergroup") {
|
|
684
|
-
try {
|
|
685
|
-
const admins = await this.bot.telegram.getChatAdministrators(chat.id);
|
|
686
|
-
if (admins && admins.length > 0) {
|
|
687
|
-
for (const admin of admins) {
|
|
688
|
-
const userId = createUniqueUuid2(this.runtime, admin.user.id.toString());
|
|
689
|
-
users.push({
|
|
690
|
-
id: userId,
|
|
691
|
-
names: [admin.user.first_name || admin.user.username || "Unknown Admin"],
|
|
692
|
-
agentId: this.runtime.agentId,
|
|
693
|
-
metadata: {
|
|
694
|
-
telegram: {
|
|
695
|
-
id: admin.user.id.toString(),
|
|
696
|
-
username: admin.user.username || "unknown",
|
|
697
|
-
name: admin.user.first_name || "Unknown Admin",
|
|
698
|
-
isAdmin: true,
|
|
699
|
-
adminTitle: admin.custom_title || (admin.status === "creator" ? "Owner" : "Admin")
|
|
700
|
-
},
|
|
701
|
-
source: "telegram",
|
|
702
|
-
roles: [admin.status === "creator" ? Role.OWNER : Role.ADMIN]
|
|
703
|
-
}
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
try {
|
|
708
|
-
const chatInfo = await this.bot.telegram.getChat(chat.id);
|
|
709
|
-
if (chatInfo && "member_count" in chatInfo) {
|
|
710
|
-
world.metadata.memberCount = chatInfo.member_count;
|
|
711
|
-
}
|
|
712
|
-
} catch (countError) {
|
|
713
|
-
logger2.warn(`Could not get member count for chat ${chatId}: ${countError}`);
|
|
714
|
-
}
|
|
715
|
-
} catch (error) {
|
|
716
|
-
logger2.warn(`Could not fetch administrators for chat ${chatId}: ${error}`);
|
|
1045
|
+
await this.runtime.ensureRoomExists(generalRoom);
|
|
1046
|
+
const rooms = [generalRoom];
|
|
1047
|
+
if (chat.type === "supergroup" && chat.is_forum && ctx.message?.message_thread_id) {
|
|
1048
|
+
const topicRoom = await this.buildForumTopicRoom(ctx, worldId);
|
|
1049
|
+
if (topicRoom) {
|
|
1050
|
+
rooms.push(topicRoom);
|
|
1051
|
+
await this.runtime.ensureRoomExists(topicRoom);
|
|
717
1052
|
}
|
|
718
1053
|
}
|
|
719
|
-
const
|
|
1054
|
+
const entities = await this.buildStandardizedEntities(chat);
|
|
1055
|
+
if (ctx.from) {
|
|
1056
|
+
const senderEntity = this.buildMsgSenderEntity(ctx.from);
|
|
1057
|
+
if (senderEntity && senderEntity.id && !entities.some((e) => e.id === senderEntity.id)) {
|
|
1058
|
+
entities.push(senderEntity);
|
|
1059
|
+
this.syncedEntityIds.add(senderEntity.id);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
await this.batchProcessEntities(
|
|
1063
|
+
entities,
|
|
1064
|
+
generalRoom.id,
|
|
1065
|
+
generalRoom.channelId,
|
|
1066
|
+
generalRoom.serverId,
|
|
1067
|
+
generalRoom.type,
|
|
1068
|
+
worldId
|
|
1069
|
+
);
|
|
1070
|
+
const telegramWorldPayload = {
|
|
720
1071
|
runtime: this.runtime,
|
|
721
1072
|
world,
|
|
722
|
-
rooms
|
|
723
|
-
entities
|
|
724
|
-
source: "telegram"
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
...worldPayload,
|
|
728
|
-
chat
|
|
1073
|
+
rooms,
|
|
1074
|
+
entities,
|
|
1075
|
+
source: "telegram",
|
|
1076
|
+
chat,
|
|
1077
|
+
botUsername: this.bot.botInfo.username
|
|
729
1078
|
};
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
if (chat.type === "group" || chat.type === "supergroup") {
|
|
733
|
-
this.setupEntityTracking(chat.id);
|
|
1079
|
+
if (chat.type !== "private") {
|
|
1080
|
+
await this.runtime.emitEvent("TELEGRAM_WORLD_JOINED" /* WORLD_JOINED */, telegramWorldPayload);
|
|
734
1081
|
}
|
|
1082
|
+
await this.runtime.emitEvent(EventType2.WORLD_JOINED, {
|
|
1083
|
+
runtime: this.runtime,
|
|
1084
|
+
world,
|
|
1085
|
+
rooms,
|
|
1086
|
+
entities,
|
|
1087
|
+
source: "telegram"
|
|
1088
|
+
});
|
|
735
1089
|
}
|
|
736
1090
|
/**
|
|
737
|
-
*
|
|
1091
|
+
* Processes entities in batches to prevent overwhelming the system.
|
|
738
1092
|
*
|
|
1093
|
+
* @param {Entity[]} entities - The entities to process
|
|
1094
|
+
* @param {UUID} roomId - The ID of the room to connect entities to
|
|
1095
|
+
* @param {string} channelId - The channel ID
|
|
1096
|
+
* @param {string} serverId - The server ID
|
|
1097
|
+
* @param {ChannelType} roomType - The type of the room
|
|
1098
|
+
* @param {UUID} worldId - The ID of the world
|
|
1099
|
+
* @returns {Promise<void>}
|
|
739
1100
|
* @private
|
|
740
|
-
* @returns {void}
|
|
741
1101
|
*/
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
1102
|
+
async batchProcessEntities(entities, roomId, channelId, serverId, roomType, worldId) {
|
|
1103
|
+
const batchSize = 50;
|
|
1104
|
+
for (let i = 0; i < entities.length; i += batchSize) {
|
|
1105
|
+
const entityBatch = entities.slice(i, i + batchSize);
|
|
1106
|
+
await Promise.all(
|
|
1107
|
+
entityBatch.map(async (entity) => {
|
|
1108
|
+
try {
|
|
1109
|
+
if (entity.id) {
|
|
1110
|
+
await this.runtime.ensureConnection({
|
|
1111
|
+
entityId: entity.id,
|
|
1112
|
+
roomId,
|
|
1113
|
+
userName: entity.metadata?.telegram?.username,
|
|
1114
|
+
name: entity.metadata?.telegram?.name,
|
|
1115
|
+
userId: entity.metadata?.telegram?.id,
|
|
1116
|
+
source: "telegram",
|
|
1117
|
+
channelId,
|
|
1118
|
+
serverId,
|
|
1119
|
+
type: roomType,
|
|
1120
|
+
worldId
|
|
1121
|
+
});
|
|
1122
|
+
} else {
|
|
1123
|
+
logger2.warn(
|
|
1124
|
+
`Skipping entity sync due to missing ID: ${JSON.stringify(entity.names)}`
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
} catch (err) {
|
|
1128
|
+
logger2.warn(`Failed to sync user ${entity.metadata?.telegram?.username}: ${err}`);
|
|
1129
|
+
}
|
|
1130
|
+
})
|
|
1131
|
+
);
|
|
1132
|
+
if (i + batchSize < entities.length) {
|
|
1133
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
757
1134
|
}
|
|
758
|
-
}
|
|
1135
|
+
}
|
|
759
1136
|
}
|
|
760
1137
|
/**
|
|
761
|
-
*
|
|
762
|
-
*
|
|
1138
|
+
* Gets chat title and channel type based on Telegram chat type.
|
|
1139
|
+
* Maps Telegram-specific chat types to standardized system types.
|
|
1140
|
+
*
|
|
1141
|
+
* @param {any} chat - The Telegram chat object
|
|
1142
|
+
* @returns {Object} Object containing chatTitle and channelType
|
|
1143
|
+
* @private
|
|
763
1144
|
*/
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
1145
|
+
getChatTypeInfo(chat) {
|
|
1146
|
+
let chatTitle;
|
|
1147
|
+
let channelType;
|
|
1148
|
+
switch (chat.type) {
|
|
1149
|
+
case "private":
|
|
1150
|
+
chatTitle = `Chat with ${chat.first_name || "Unknown User"}`;
|
|
1151
|
+
channelType = ChannelType2.DM;
|
|
1152
|
+
break;
|
|
1153
|
+
case "group":
|
|
1154
|
+
chatTitle = chat.title || "Unknown Group";
|
|
1155
|
+
channelType = ChannelType2.GROUP;
|
|
1156
|
+
break;
|
|
1157
|
+
case "supergroup":
|
|
1158
|
+
chatTitle = chat.title || "Unknown Supergroup";
|
|
1159
|
+
channelType = ChannelType2.GROUP;
|
|
1160
|
+
break;
|
|
1161
|
+
case "channel":
|
|
1162
|
+
chatTitle = chat.title || "Unknown Channel";
|
|
1163
|
+
channelType = ChannelType2.FEED;
|
|
1164
|
+
break;
|
|
1165
|
+
default:
|
|
1166
|
+
chatTitle = "Unknown Chat";
|
|
1167
|
+
channelType = ChannelType2.GROUP;
|
|
1168
|
+
}
|
|
1169
|
+
return { chatTitle, channelType };
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Builds standardized entity representations from Telegram chat data.
|
|
1173
|
+
* Transforms Telegram-specific user data into system-standard Entity objects.
|
|
1174
|
+
*
|
|
1175
|
+
* @param {any} chat - The Telegram chat object
|
|
1176
|
+
* @returns {Promise<Entity[]>} Array of standardized Entity objects
|
|
1177
|
+
* @private
|
|
1178
|
+
*/
|
|
1179
|
+
async buildStandardizedEntities(chat) {
|
|
1180
|
+
const entities = [];
|
|
1181
|
+
try {
|
|
1182
|
+
if (chat.type === "private" && chat.id) {
|
|
1183
|
+
const userId = createUniqueUuid2(this.runtime, chat.id.toString());
|
|
1184
|
+
entities.push({
|
|
1185
|
+
id: userId,
|
|
1186
|
+
names: [chat.first_name || "Unknown User"],
|
|
1187
|
+
agentId: this.runtime.agentId,
|
|
796
1188
|
metadata: {
|
|
797
|
-
|
|
1189
|
+
telegram: {
|
|
1190
|
+
id: chat.id.toString(),
|
|
1191
|
+
username: chat.username || "unknown",
|
|
1192
|
+
name: chat.first_name || "Unknown User"
|
|
1193
|
+
},
|
|
1194
|
+
source: "telegram"
|
|
798
1195
|
}
|
|
799
|
-
};
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
1196
|
+
});
|
|
1197
|
+
this.syncedEntityIds.add(userId);
|
|
1198
|
+
} else if (chat.type === "group" || chat.type === "supergroup") {
|
|
1199
|
+
try {
|
|
1200
|
+
const admins = await this.bot.telegram.getChatAdministrators(chat.id);
|
|
1201
|
+
if (admins && admins.length > 0) {
|
|
1202
|
+
for (const admin of admins) {
|
|
1203
|
+
const userId = createUniqueUuid2(this.runtime, admin.user.id.toString());
|
|
1204
|
+
entities.push({
|
|
1205
|
+
id: userId,
|
|
1206
|
+
names: [admin.user.first_name || admin.user.username || "Unknown Admin"],
|
|
1207
|
+
agentId: this.runtime.agentId,
|
|
1208
|
+
metadata: {
|
|
1209
|
+
telegram: {
|
|
1210
|
+
id: admin.user.id.toString(),
|
|
1211
|
+
username: admin.user.username || "unknown",
|
|
1212
|
+
name: admin.user.first_name || "Unknown Admin",
|
|
1213
|
+
isAdmin: true,
|
|
1214
|
+
adminTitle: admin.custom_title || (admin.status === "creator" ? "Owner" : "Admin")
|
|
1215
|
+
},
|
|
1216
|
+
source: "telegram",
|
|
1217
|
+
roles: [admin.status === "creator" ? Role.OWNER : Role.ADMIN]
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
this.syncedEntityIds.add(userId);
|
|
1221
|
+
}
|
|
806
1222
|
}
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
logger2.info(
|
|
811
|
-
`Tracked new Telegram entity: ${ctx.from.username || ctx.from.first_name || entityId}`
|
|
812
|
-
);
|
|
813
|
-
} catch (error) {
|
|
814
|
-
logger2.error(`Error syncing new Telegram entity ${entityId} from chat ${chatId}:`, error);
|
|
1223
|
+
} catch (error) {
|
|
1224
|
+
logger2.warn(`Could not fetch administrators for chat ${chat.id}: ${error}`);
|
|
1225
|
+
}
|
|
815
1226
|
}
|
|
816
|
-
})
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
username: leftUser.username,
|
|
852
|
-
first_name: leftUser.first_name
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
this.runtime.emitEvent(EventType2.ENTITY_LEFT, entityLeftPayload);
|
|
856
|
-
this.runtime.emitEvent("TELEGRAM_ENTITY_LEFT" /* ENTITY_LEFT */, telegramEntityLeftPayload);
|
|
857
|
-
logger2.info(
|
|
858
|
-
`Entity ${leftUser.username || leftUser.first_name || leftUser.id} left chat ${chatId}`
|
|
859
|
-
);
|
|
1227
|
+
} catch (error) {
|
|
1228
|
+
logger2.error(
|
|
1229
|
+
`Error building standardized entities: ${error instanceof Error ? error.message : String(error)}`
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
return entities;
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Extracts and builds the room object for a forum topic from a message context.
|
|
1236
|
+
* This refactored method can be used both in middleware and when handling new chats.
|
|
1237
|
+
*
|
|
1238
|
+
* @param {Context} ctx - The context of the incoming update
|
|
1239
|
+
* @param {UUID} worldId - The ID of the world the topic belongs to
|
|
1240
|
+
* @returns {Promise<Room | null>} A Promise that resolves with the room or null if not a topic
|
|
1241
|
+
* @private
|
|
1242
|
+
*/
|
|
1243
|
+
async buildForumTopicRoom(ctx, worldId) {
|
|
1244
|
+
if (!ctx.chat || !ctx.message?.message_thread_id) return null;
|
|
1245
|
+
if (ctx.chat.type !== "supergroup" || !ctx.chat.is_forum) return null;
|
|
1246
|
+
const chat = ctx.chat;
|
|
1247
|
+
const chatId = chat.id.toString();
|
|
1248
|
+
const threadId = ctx.message.message_thread_id.toString();
|
|
1249
|
+
const roomId = createUniqueUuid2(this.runtime, `${chatId}-${threadId}`);
|
|
1250
|
+
try {
|
|
1251
|
+
const replyMessage = JSON.parse(JSON.stringify(ctx.message));
|
|
1252
|
+
let topicName = `Topic #${threadId}`;
|
|
1253
|
+
if (replyMessage && typeof replyMessage === "object" && "forum_topic_created" in replyMessage && replyMessage.forum_topic_created) {
|
|
1254
|
+
const topicCreated = replyMessage.forum_topic_created;
|
|
1255
|
+
if (topicCreated && typeof topicCreated === "object" && "name" in topicCreated) {
|
|
1256
|
+
topicName = topicCreated.name;
|
|
1257
|
+
}
|
|
1258
|
+
} 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) {
|
|
1259
|
+
const topicCreated = replyMessage.reply_to_message.forum_topic_created;
|
|
1260
|
+
if (topicCreated && typeof topicCreated === "object" && "name" in topicCreated) {
|
|
1261
|
+
topicName = topicCreated.name;
|
|
860
1262
|
}
|
|
861
|
-
} catch (error) {
|
|
862
|
-
logger2.error(`Error handling Telegram entity leaving chat ${chatId}:`, error);
|
|
863
1263
|
}
|
|
864
|
-
|
|
1264
|
+
const room = {
|
|
1265
|
+
id: roomId,
|
|
1266
|
+
name: topicName,
|
|
1267
|
+
source: "telegram",
|
|
1268
|
+
type: ChannelType2.GROUP,
|
|
1269
|
+
channelId: `${chatId}-${threadId}`,
|
|
1270
|
+
serverId: chatId,
|
|
1271
|
+
worldId,
|
|
1272
|
+
metadata: {
|
|
1273
|
+
threadId,
|
|
1274
|
+
isForumTopic: true,
|
|
1275
|
+
parentChatId: chatId
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
return room;
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
logger2.error(
|
|
1281
|
+
`Error building forum topic room: ${error instanceof Error ? error.message : String(error)}`
|
|
1282
|
+
);
|
|
1283
|
+
return null;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
static registerSendHandlers(runtime, serviceInstance) {
|
|
1287
|
+
if (serviceInstance) {
|
|
1288
|
+
runtime.registerSendHandler(
|
|
1289
|
+
"telegram",
|
|
1290
|
+
serviceInstance.handleSendMessage.bind(serviceInstance)
|
|
1291
|
+
);
|
|
1292
|
+
logger2.info("[Telegram] Registered send handler.");
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
async handleSendMessage(runtime, target, content) {
|
|
1296
|
+
let chatId;
|
|
1297
|
+
if (target.channelId) {
|
|
1298
|
+
chatId = target.channelId;
|
|
1299
|
+
} else if (target.roomId) {
|
|
1300
|
+
const room = await runtime.getRoom(target.roomId);
|
|
1301
|
+
chatId = room?.channelId;
|
|
1302
|
+
if (!chatId)
|
|
1303
|
+
throw new Error(`Could not resolve Telegram chat ID from roomId ${target.roomId}`);
|
|
1304
|
+
} else if (target.entityId) {
|
|
1305
|
+
logger2.error("[Telegram SendHandler] Sending DMs via entityId not implemented yet.");
|
|
1306
|
+
throw new Error("Sending DMs via entityId is not yet supported for Telegram.");
|
|
1307
|
+
} else {
|
|
1308
|
+
throw new Error("Telegram SendHandler requires channelId, roomId, or entityId.");
|
|
1309
|
+
}
|
|
1310
|
+
if (!chatId) {
|
|
1311
|
+
throw new Error(
|
|
1312
|
+
`Could not determine target Telegram chat ID for target: ${JSON.stringify(target)}`
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
1315
|
+
try {
|
|
1316
|
+
await this.messageManager.sendMessage(chatId, content);
|
|
1317
|
+
logger2.info(`[Telegram SendHandler] Message sent to chat ID: ${chatId}`);
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
logger2.error(
|
|
1320
|
+
`[Telegram SendHandler] Error sending message: ${error instanceof Error ? error.message : String(error)}`,
|
|
1321
|
+
{
|
|
1322
|
+
target,
|
|
1323
|
+
content
|
|
1324
|
+
}
|
|
1325
|
+
);
|
|
1326
|
+
throw error;
|
|
1327
|
+
}
|
|
865
1328
|
}
|
|
866
1329
|
};
|
|
867
1330
|
|
|
@@ -931,6 +1394,9 @@ var TelegramTestSuite = class {
|
|
|
931
1394
|
async getChatInfo(runtime) {
|
|
932
1395
|
try {
|
|
933
1396
|
const chatId = this.validateChatId(runtime);
|
|
1397
|
+
if (!this.bot) {
|
|
1398
|
+
throw new Error("Bot is not initialized.");
|
|
1399
|
+
}
|
|
934
1400
|
const chat = await this.bot.telegram.getChat(chatId);
|
|
935
1401
|
logger3.log(`Fetched real chat: ${JSON.stringify(chat)}`);
|
|
936
1402
|
return chat;
|
|
@@ -957,6 +1423,7 @@ var TelegramTestSuite = class {
|
|
|
957
1423
|
async testSendingMessageWithAttachment(runtime) {
|
|
958
1424
|
try {
|
|
959
1425
|
if (!this.messageManager) throw new Error("MessageManager not initialized.");
|
|
1426
|
+
if (!this.bot) throw new Error("Bot not initialized.");
|
|
960
1427
|
const chat = await this.getChatInfo(runtime);
|
|
961
1428
|
const mockContext = {
|
|
962
1429
|
chat,
|
|
@@ -984,8 +1451,9 @@ var TelegramTestSuite = class {
|
|
|
984
1451
|
}
|
|
985
1452
|
}
|
|
986
1453
|
async testHandlingMessage(runtime) {
|
|
987
|
-
var _a;
|
|
988
1454
|
try {
|
|
1455
|
+
if (!this.bot) throw new Error("Bot not initialized.");
|
|
1456
|
+
if (!this.messageManager) throw new Error("MessageManager not initialized.");
|
|
989
1457
|
const chat = await this.getChatInfo(runtime);
|
|
990
1458
|
const mockContext = {
|
|
991
1459
|
chat,
|
|
@@ -998,7 +1466,7 @@ var TelegramTestSuite = class {
|
|
|
998
1466
|
},
|
|
999
1467
|
message: {
|
|
1000
1468
|
message_id: void 0,
|
|
1001
|
-
text: `@${
|
|
1469
|
+
text: `@${this.bot.botInfo?.username}! Hello!`,
|
|
1002
1470
|
date: Math.floor(Date.now() / 1e3),
|
|
1003
1471
|
chat
|
|
1004
1472
|
},
|
|
@@ -1014,21 +1482,30 @@ var TelegramTestSuite = class {
|
|
|
1014
1482
|
}
|
|
1015
1483
|
}
|
|
1016
1484
|
async testProcessingImages(runtime) {
|
|
1017
|
-
var _a;
|
|
1018
1485
|
try {
|
|
1486
|
+
if (!this.bot) throw new Error("Bot not initialized.");
|
|
1487
|
+
if (!this.messageManager) throw new Error("MessageManager not initialized.");
|
|
1019
1488
|
const chatId = this.validateChatId(runtime);
|
|
1020
1489
|
const fileId = await this.getFileId(chatId, TEST_IMAGE_URL);
|
|
1021
1490
|
const mockMessage = {
|
|
1022
|
-
message_id:
|
|
1023
|
-
chat: { id: chatId },
|
|
1491
|
+
message_id: 12345,
|
|
1492
|
+
chat: { id: chatId, type: "private" },
|
|
1024
1493
|
date: Math.floor(Date.now() / 1e3),
|
|
1025
|
-
photo: [
|
|
1026
|
-
|
|
1494
|
+
photo: [
|
|
1495
|
+
{
|
|
1496
|
+
file_id: fileId,
|
|
1497
|
+
file_unique_id: `unique_${fileId}`,
|
|
1498
|
+
width: 100,
|
|
1499
|
+
height: 100
|
|
1500
|
+
}
|
|
1501
|
+
],
|
|
1502
|
+
text: `@${this.bot.botInfo?.username}!`
|
|
1027
1503
|
};
|
|
1028
|
-
const
|
|
1029
|
-
if (!description) {
|
|
1030
|
-
throw new Error("Error processing Telegram image");
|
|
1504
|
+
const result = await this.messageManager.processImage(mockMessage);
|
|
1505
|
+
if (!result || !result.description) {
|
|
1506
|
+
throw new Error("Error processing Telegram image or description not found");
|
|
1031
1507
|
}
|
|
1508
|
+
const { description } = result;
|
|
1032
1509
|
logger3.log(`Processing Telegram image successfully: ${description}`);
|
|
1033
1510
|
} catch (error) {
|
|
1034
1511
|
throw new Error(`Error processing Telegram image: ${error}`);
|
|
@@ -1036,7 +1513,13 @@ var TelegramTestSuite = class {
|
|
|
1036
1513
|
}
|
|
1037
1514
|
async getFileId(chatId, imageUrl) {
|
|
1038
1515
|
try {
|
|
1516
|
+
if (!this.bot) {
|
|
1517
|
+
throw new Error("Bot is not initialized.");
|
|
1518
|
+
}
|
|
1039
1519
|
const message = await this.bot.telegram.sendPhoto(chatId, imageUrl);
|
|
1520
|
+
if (!message.photo || message.photo.length === 0) {
|
|
1521
|
+
throw new Error("No photo received in the message response.");
|
|
1522
|
+
}
|
|
1040
1523
|
return message.photo[message.photo.length - 1].file_id;
|
|
1041
1524
|
} catch (error) {
|
|
1042
1525
|
logger3.error(`Error sending image: ${error}`);
|
|
@@ -1054,6 +1537,8 @@ var telegramPlugin = {
|
|
|
1054
1537
|
};
|
|
1055
1538
|
var index_default = telegramPlugin;
|
|
1056
1539
|
export {
|
|
1540
|
+
MessageManager,
|
|
1541
|
+
TelegramService,
|
|
1057
1542
|
index_default as default
|
|
1058
1543
|
};
|
|
1059
1544
|
//# sourceMappingURL=index.js.map
|