@elizaos/plugin-telegram 1.0.0-beta.3 → 1.0.0-beta.32
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 +59 -0
- package/dist/index.js +535 -246
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
} from "@elizaos/core";
|
|
55
55
|
|
|
56
56
|
// src/utils.ts
|
|
57
|
+
import { Markup } from "telegraf";
|
|
57
58
|
function escapeMarkdown(text) {
|
|
58
59
|
if (text.startsWith("```") && text.endsWith("```")) {
|
|
59
60
|
return text;
|
|
@@ -66,8 +67,20 @@ function escapeMarkdown(text) {
|
|
|
66
67
|
return part.replace(/`.*?`/g, (match) => match).replace(/([*_`\\])/g, "\\$1");
|
|
67
68
|
}).join("");
|
|
68
69
|
}
|
|
70
|
+
function convertToTelegramButtons(buttons) {
|
|
71
|
+
if (!buttons) return [];
|
|
72
|
+
return buttons.map((button) => {
|
|
73
|
+
switch (button.kind) {
|
|
74
|
+
case "login":
|
|
75
|
+
return Markup.button.login(button.text, button.url);
|
|
76
|
+
case "url":
|
|
77
|
+
return Markup.button.url(button.text, button.url);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
69
81
|
|
|
70
82
|
// src/messageManager.ts
|
|
83
|
+
import { Markup as Markup2 } from "telegraf";
|
|
71
84
|
import fs from "node:fs";
|
|
72
85
|
var MAX_MESSAGE_LENGTH = 4096;
|
|
73
86
|
var getChannelType = (chat) => {
|
|
@@ -127,7 +140,7 @@ ${description}]` };
|
|
|
127
140
|
* Sends a message in chunks, handling attachments and splitting the message if necessary
|
|
128
141
|
*
|
|
129
142
|
* @param {Context} ctx - The context object representing the current state of the bot
|
|
130
|
-
* @param {
|
|
143
|
+
* @param {TelegramContent} content - The content of the message to be sent
|
|
131
144
|
* @param {number} [replyToMessageId] - The ID of the message to reply to, if any
|
|
132
145
|
* @returns {Promise<Message.TextMessage[]>} - An array of TextMessage objects representing the messages sent
|
|
133
146
|
*/
|
|
@@ -158,11 +171,13 @@ ${description}]` };
|
|
|
158
171
|
} else {
|
|
159
172
|
const chunks = this.splitMessage(content.text);
|
|
160
173
|
const sentMessages = [];
|
|
174
|
+
const telegramButtons = convertToTelegramButtons(content.buttons ?? []);
|
|
161
175
|
for (let i = 0; i < chunks.length; i++) {
|
|
162
176
|
const chunk = escapeMarkdown(chunks[i]);
|
|
163
177
|
const sentMessage = await ctx.telegram.sendMessage(ctx.chat.id, chunk, {
|
|
164
178
|
reply_parameters: i === 0 && replyToMessageId ? { message_id: replyToMessageId } : void 0,
|
|
165
|
-
parse_mode: "Markdown"
|
|
179
|
+
parse_mode: "Markdown",
|
|
180
|
+
...Markup2.inlineKeyboard(telegramButtons)
|
|
166
181
|
});
|
|
167
182
|
sentMessages.push(sentMessage);
|
|
168
183
|
}
|
|
@@ -249,8 +264,11 @@ ${description}]` };
|
|
|
249
264
|
const message = ctx.message;
|
|
250
265
|
try {
|
|
251
266
|
const entityId = createUniqueUuid(this.runtime, ctx.from.id.toString());
|
|
252
|
-
const
|
|
253
|
-
const roomId = createUniqueUuid(
|
|
267
|
+
const threadId = "is_topic_message" in message && message.is_topic_message ? (_a = message.message_thread_id) == null ? void 0 : _a.toString() : void 0;
|
|
268
|
+
const roomId = createUniqueUuid(
|
|
269
|
+
this.runtime,
|
|
270
|
+
threadId ? `${ctx.chat.id}-${threadId}` : ctx.chat.id.toString()
|
|
271
|
+
);
|
|
254
272
|
const messageId = createUniqueUuid(this.runtime, (_b = message == null ? void 0 : message.message_id) == null ? void 0 : _b.toString());
|
|
255
273
|
const imageInfo = await this.processImage(message);
|
|
256
274
|
let messageText = "";
|
|
@@ -263,34 +281,6 @@ ${description}]` };
|
|
|
263
281
|
if (!fullText) return;
|
|
264
282
|
const chat = message.chat;
|
|
265
283
|
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
284
|
const memory = {
|
|
295
285
|
id: messageId,
|
|
296
286
|
entityId,
|
|
@@ -306,6 +296,7 @@ ${description}]` };
|
|
|
306
296
|
};
|
|
307
297
|
const callback = async (content, _files) => {
|
|
308
298
|
try {
|
|
299
|
+
if (!content.text) return [];
|
|
309
300
|
const sentMessages = await this.sendMessageInChunks(ctx, content, message.message_id);
|
|
310
301
|
if (!sentMessages) return [];
|
|
311
302
|
const memories = [];
|
|
@@ -360,7 +351,7 @@ ${description}]` };
|
|
|
360
351
|
}
|
|
361
352
|
/**
|
|
362
353
|
* Handles the reaction event triggered by a user reacting to a message.
|
|
363
|
-
*
|
|
354
|
+
* @param {NarrowedContext<Context<Update>, Update.MessageReactionUpdate>} ctx The context of the message reaction update
|
|
364
355
|
* @returns {Promise<void>} A Promise that resolves when the reaction handling is complete
|
|
365
356
|
*/
|
|
366
357
|
async handleReaction(ctx) {
|
|
@@ -371,7 +362,6 @@ ${description}]` };
|
|
|
371
362
|
try {
|
|
372
363
|
const entityId = createUniqueUuid(this.runtime, ctx.from.id.toString());
|
|
373
364
|
const roomId = createUniqueUuid(this.runtime, ctx.chat.id.toString());
|
|
374
|
-
const worldId = createUniqueUuid(this.runtime, ctx.chat.id.toString());
|
|
375
365
|
const reactionId = createUniqueUuid(
|
|
376
366
|
this.runtime,
|
|
377
367
|
`${reaction.message_id}-${ctx.from.id}-${Date.now()}`
|
|
@@ -472,7 +462,9 @@ ${description}]` };
|
|
|
472
462
|
}
|
|
473
463
|
this.runtime.emitEvent(EventType.MESSAGE_SENT, {
|
|
474
464
|
runtime: this.runtime,
|
|
475
|
-
|
|
465
|
+
message: {
|
|
466
|
+
content
|
|
467
|
+
},
|
|
476
468
|
roomId,
|
|
477
469
|
source: "telegram"
|
|
478
470
|
});
|
|
@@ -496,6 +488,7 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
496
488
|
messageManager;
|
|
497
489
|
options;
|
|
498
490
|
knownChats = /* @__PURE__ */ new Map();
|
|
491
|
+
syncedEntityIds = /* @__PURE__ */ new Set();
|
|
499
492
|
/**
|
|
500
493
|
* Constructor for TelegramService class.
|
|
501
494
|
* @param {IAgentRuntime} runtime - The runtime object for the agent.
|
|
@@ -532,6 +525,7 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
532
525
|
);
|
|
533
526
|
logger2.log("\u{1F680} Starting Telegram bot...");
|
|
534
527
|
await service.initializeBot();
|
|
528
|
+
service.setupMiddlewares();
|
|
535
529
|
service.setupMessageHandlers();
|
|
536
530
|
await service.bot.telegram.getMe();
|
|
537
531
|
return service;
|
|
@@ -584,6 +578,106 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
584
578
|
process.once("SIGINT", () => this.bot.stop("SIGINT"));
|
|
585
579
|
process.once("SIGTERM", () => this.bot.stop("SIGTERM"));
|
|
586
580
|
}
|
|
581
|
+
/**
|
|
582
|
+
* Sets up the middleware chain for preprocessing messages before they reach handlers.
|
|
583
|
+
* This critical method establishes a sequential processing pipeline that:
|
|
584
|
+
*
|
|
585
|
+
* 1. Authorization - Verifies if a chat is allowed to interact with the bot based on configured settings
|
|
586
|
+
* 2. Chat Discovery - Ensures chat entities and worlds exist in the runtime, creating them if needed
|
|
587
|
+
* 3. Forum Topics - Handles Telegram forum topics as separate rooms for better conversation management
|
|
588
|
+
* 4. Entity Synchronization - Ensures message senders are properly synchronized as entities
|
|
589
|
+
*
|
|
590
|
+
* The middleware chain runs in sequence for each message, with each step potentially
|
|
591
|
+
* enriching the context or stopping processing if conditions aren't met.
|
|
592
|
+
* This preprocessing is essential for maintaining consistent state before message handlers execute.
|
|
593
|
+
*
|
|
594
|
+
* @private
|
|
595
|
+
*/
|
|
596
|
+
setupMiddlewares() {
|
|
597
|
+
this.bot.use(this.authorizationMiddleware.bind(this));
|
|
598
|
+
this.bot.use(this.chatAndEntityMiddleware.bind(this));
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Authorization middleware - checks if chat is allowed to interact with the bot
|
|
602
|
+
* based on the TELEGRAM_ALLOWED_CHATS configuration.
|
|
603
|
+
*
|
|
604
|
+
* @param {Context} ctx - The context of the incoming update
|
|
605
|
+
* @param {Function} next - The function to call to proceed to the next middleware
|
|
606
|
+
* @returns {Promise<void>}
|
|
607
|
+
* @private
|
|
608
|
+
*/
|
|
609
|
+
async authorizationMiddleware(ctx, next) {
|
|
610
|
+
if (!await this.isGroupAuthorized(ctx)) {
|
|
611
|
+
logger2.debug("Chat not authorized, skipping message processing");
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
await next();
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Chat and entity management middleware - handles new chats, forum topics, and entity synchronization.
|
|
618
|
+
* This middleware implements decision logic to determine which operations are needed based on
|
|
619
|
+
* the chat type and whether we've seen this chat before.
|
|
620
|
+
*
|
|
621
|
+
* @param {Context} ctx - The context of the incoming update
|
|
622
|
+
* @param {Function} next - The function to call to proceed to the next middleware
|
|
623
|
+
* @returns {Promise<void>}
|
|
624
|
+
* @private
|
|
625
|
+
*/
|
|
626
|
+
async chatAndEntityMiddleware(ctx, next) {
|
|
627
|
+
if (!ctx.chat) return next();
|
|
628
|
+
const chatId = ctx.chat.id.toString();
|
|
629
|
+
if (!this.knownChats.has(chatId)) {
|
|
630
|
+
await this.handleNewChat(ctx);
|
|
631
|
+
return next();
|
|
632
|
+
}
|
|
633
|
+
await this.processExistingChat(ctx);
|
|
634
|
+
await next();
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Process an existing chat based on chat type and message properties.
|
|
638
|
+
* Different chat types require different processing steps.
|
|
639
|
+
*
|
|
640
|
+
* @param {Context} ctx - The context of the incoming update
|
|
641
|
+
* @returns {Promise<void>}
|
|
642
|
+
* @private
|
|
643
|
+
*/
|
|
644
|
+
async processExistingChat(ctx) {
|
|
645
|
+
var _a;
|
|
646
|
+
if (!ctx.chat) return;
|
|
647
|
+
const chat = ctx.chat;
|
|
648
|
+
if (chat.type === "supergroup" && chat.is_forum && ((_a = ctx.message) == null ? void 0 : _a.message_thread_id)) {
|
|
649
|
+
try {
|
|
650
|
+
await this.handleForumTopic(ctx);
|
|
651
|
+
} catch (error) {
|
|
652
|
+
logger2.error(`Error handling forum topic: ${error}`);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (ctx.from && ctx.chat.type !== "private") {
|
|
656
|
+
await this.syncEntity(ctx);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Sets up message and reaction handlers for the bot.
|
|
661
|
+
* Configures event handlers to process incoming messages and reactions.
|
|
662
|
+
*
|
|
663
|
+
* @private
|
|
664
|
+
*/
|
|
665
|
+
setupMessageHandlers() {
|
|
666
|
+
this.bot.on("message", async (ctx) => {
|
|
667
|
+
try {
|
|
668
|
+
await this.messageManager.handleMessage(ctx);
|
|
669
|
+
} catch (error) {
|
|
670
|
+
logger2.error("Error handling message:", error);
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
this.bot.on("message_reaction", async (ctx) => {
|
|
674
|
+
try {
|
|
675
|
+
await this.messageManager.handleReaction(ctx);
|
|
676
|
+
} catch (error) {
|
|
677
|
+
logger2.error("Error handling reaction:", error);
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
}
|
|
587
681
|
/**
|
|
588
682
|
* Checks if a group is authorized, based on the TELEGRAM_ALLOWED_CHATS setting.
|
|
589
683
|
* @param {Context} ctx - The context of the incoming update.
|
|
@@ -593,9 +687,6 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
593
687
|
var _a;
|
|
594
688
|
const chatId = (_a = ctx.chat) == null ? void 0 : _a.id.toString();
|
|
595
689
|
if (!chatId) return false;
|
|
596
|
-
if (!this.knownChats.has(chatId)) {
|
|
597
|
-
await this.handleNewChat(ctx);
|
|
598
|
-
}
|
|
599
690
|
const allowedChats = this.runtime.getSetting("TELEGRAM_ALLOWED_CHATS");
|
|
600
691
|
if (!allowedChats) {
|
|
601
692
|
return true;
|
|
@@ -609,39 +700,196 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
609
700
|
}
|
|
610
701
|
}
|
|
611
702
|
/**
|
|
612
|
-
*
|
|
703
|
+
* Synchronizes an entity from a message context with the runtime system.
|
|
704
|
+
* This method handles three cases:
|
|
705
|
+
* 1. Message sender - most common case
|
|
706
|
+
* 2. New chat member - when a user joins the chat
|
|
707
|
+
* 3. Left chat member - when a user leaves the chat
|
|
708
|
+
*
|
|
709
|
+
* @param {Context} ctx - The context of the incoming update
|
|
710
|
+
* @returns {Promise<void>}
|
|
711
|
+
* @private
|
|
712
|
+
*/
|
|
713
|
+
async syncEntity(ctx) {
|
|
714
|
+
var _a;
|
|
715
|
+
if (!ctx.chat) return;
|
|
716
|
+
const chat = ctx.chat;
|
|
717
|
+
const chatId = chat.id.toString();
|
|
718
|
+
const worldId = createUniqueUuid2(this.runtime, chatId);
|
|
719
|
+
const roomId = createUniqueUuid2(
|
|
720
|
+
this.runtime,
|
|
721
|
+
((_a = ctx.message) == null ? void 0 : _a.message_thread_id) ? `${ctx.chat.id}-${ctx.message.message_thread_id}` : ctx.chat.id.toString()
|
|
722
|
+
);
|
|
723
|
+
await this.syncMessageSender(ctx, worldId, roomId, chatId);
|
|
724
|
+
await this.syncNewChatMember(ctx, worldId, roomId, chatId);
|
|
725
|
+
await this.syncLeftChatMember(ctx);
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Synchronizes the message sender entity with the runtime system.
|
|
729
|
+
* This is the most common entity sync case.
|
|
730
|
+
*
|
|
731
|
+
* @param {Context} ctx - The context of the incoming update
|
|
732
|
+
* @param {UUID} worldId - The ID of the world
|
|
733
|
+
* @param {UUID} roomId - The ID of the room
|
|
734
|
+
* @param {string} chatId - The ID of the chat
|
|
735
|
+
* @returns {Promise<void>}
|
|
736
|
+
* @private
|
|
737
|
+
*/
|
|
738
|
+
async syncMessageSender(ctx, worldId, roomId, chatId) {
|
|
739
|
+
if (ctx.from && !this.syncedEntityIds.has(ctx.from.id.toString())) {
|
|
740
|
+
const telegramId = ctx.from.id.toString();
|
|
741
|
+
const entityId = createUniqueUuid2(this.runtime, telegramId);
|
|
742
|
+
await this.runtime.ensureConnection({
|
|
743
|
+
entityId,
|
|
744
|
+
roomId,
|
|
745
|
+
userName: ctx.from.username,
|
|
746
|
+
userId: telegramId,
|
|
747
|
+
name: ctx.from.first_name || ctx.from.username || "Unknown User",
|
|
748
|
+
source: "telegram",
|
|
749
|
+
channelId: chatId,
|
|
750
|
+
serverId: chatId,
|
|
751
|
+
type: ChannelType2.GROUP,
|
|
752
|
+
worldId
|
|
753
|
+
});
|
|
754
|
+
this.syncedEntityIds.add(entityId);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Synchronizes a new chat member entity with the runtime system.
|
|
759
|
+
* Triggered when a user joins the chat.
|
|
760
|
+
*
|
|
761
|
+
* @param {Context} ctx - The context of the incoming update
|
|
762
|
+
* @param {UUID} worldId - The ID of the world
|
|
763
|
+
* @param {UUID} roomId - The ID of the room
|
|
764
|
+
* @param {string} chatId - The ID of the chat
|
|
765
|
+
* @returns {Promise<void>}
|
|
766
|
+
* @private
|
|
767
|
+
*/
|
|
768
|
+
async syncNewChatMember(ctx, worldId, roomId, chatId) {
|
|
769
|
+
if (ctx.message && "new_chat_member" in ctx.message) {
|
|
770
|
+
const newMember = ctx.message.new_chat_member;
|
|
771
|
+
const telegramId = newMember.id.toString();
|
|
772
|
+
const entityId = createUniqueUuid2(this.runtime, telegramId);
|
|
773
|
+
if (this.syncedEntityIds.has(telegramId)) return;
|
|
774
|
+
await this.runtime.ensureConnection({
|
|
775
|
+
entityId,
|
|
776
|
+
roomId,
|
|
777
|
+
userName: newMember.username,
|
|
778
|
+
userId: telegramId,
|
|
779
|
+
name: newMember.first_name || newMember.username || "Unknown User",
|
|
780
|
+
source: "telegram",
|
|
781
|
+
channelId: chatId,
|
|
782
|
+
serverId: chatId,
|
|
783
|
+
type: ChannelType2.GROUP,
|
|
784
|
+
worldId
|
|
785
|
+
});
|
|
786
|
+
this.syncedEntityIds.add(entityId);
|
|
787
|
+
this.runtime.emitEvent(["TELEGRAM_ENTITY_JOINED" /* ENTITY_JOINED */], {
|
|
788
|
+
runtime: this.runtime,
|
|
789
|
+
entityId,
|
|
790
|
+
worldId,
|
|
791
|
+
newMember,
|
|
792
|
+
ctx
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Updates entity status when a user leaves the chat.
|
|
798
|
+
*
|
|
799
|
+
* @param {Context} ctx - The context of the incoming update
|
|
800
|
+
* @returns {Promise<void>}
|
|
801
|
+
* @private
|
|
802
|
+
*/
|
|
803
|
+
async syncLeftChatMember(ctx) {
|
|
804
|
+
if (ctx.message && "left_chat_member" in ctx.message) {
|
|
805
|
+
const leftMember = ctx.message.left_chat_member;
|
|
806
|
+
const telegramId = leftMember.id.toString();
|
|
807
|
+
const entityId = createUniqueUuid2(this.runtime, telegramId);
|
|
808
|
+
const existingEntity = await this.runtime.getEntityById(entityId);
|
|
809
|
+
if (existingEntity) {
|
|
810
|
+
existingEntity.metadata = {
|
|
811
|
+
...existingEntity.metadata,
|
|
812
|
+
status: "INACTIVE",
|
|
813
|
+
leftAt: Date.now()
|
|
814
|
+
};
|
|
815
|
+
await this.runtime.updateEntity(existingEntity);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Handles forum topics by creating appropriate rooms in the runtime system.
|
|
821
|
+
* This enables proper conversation management for Telegram's forum feature.
|
|
822
|
+
*
|
|
823
|
+
* @param {Context} ctx - The context of the incoming update
|
|
824
|
+
* @returns {Promise<void>}
|
|
825
|
+
* @private
|
|
826
|
+
*/
|
|
827
|
+
async handleForumTopic(ctx) {
|
|
828
|
+
var _a;
|
|
829
|
+
if (!ctx.chat || !((_a = ctx.message) == null ? void 0 : _a.message_thread_id)) return;
|
|
830
|
+
const chat = ctx.chat;
|
|
831
|
+
const chatId = chat.id.toString();
|
|
832
|
+
const worldId = createUniqueUuid2(this.runtime, chatId);
|
|
833
|
+
const room = await this.buildForumTopicRoom(ctx, worldId);
|
|
834
|
+
if (!room) return;
|
|
835
|
+
await this.runtime.ensureRoomExists(room);
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Builds entity for message sender
|
|
839
|
+
*/
|
|
840
|
+
buildMsgSenderEntity(from) {
|
|
841
|
+
if (!from) return null;
|
|
842
|
+
const userId = createUniqueUuid2(this.runtime, from.id.toString());
|
|
843
|
+
const telegramId = from.id.toString();
|
|
844
|
+
return {
|
|
845
|
+
id: userId,
|
|
846
|
+
agentId: this.runtime.agentId,
|
|
847
|
+
names: [from.first_name || from.username || "Unknown User"],
|
|
848
|
+
metadata: {
|
|
849
|
+
telegram: {
|
|
850
|
+
id: telegramId,
|
|
851
|
+
username: from.username,
|
|
852
|
+
name: from.first_name || from.username || "Unknown User"
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Handles new chat discovery and emits WORLD_JOINED event.
|
|
859
|
+
* This is a critical function that ensures new chats are properly
|
|
860
|
+
* registered in the runtime system and appropriate events are emitted.
|
|
861
|
+
*
|
|
613
862
|
* @param {Context} ctx - The context of the incoming update
|
|
863
|
+
* @returns {Promise<void>}
|
|
864
|
+
* @private
|
|
614
865
|
*/
|
|
615
866
|
async handleNewChat(ctx) {
|
|
867
|
+
var _a;
|
|
616
868
|
if (!ctx.chat) return;
|
|
617
869
|
const chat = ctx.chat;
|
|
618
870
|
const chatId = chat.id.toString();
|
|
619
871
|
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
|
-
}
|
|
872
|
+
const { chatTitle, channelType } = this.getChatTypeInfo(chat);
|
|
643
873
|
const worldId = createUniqueUuid2(this.runtime, chatId);
|
|
644
|
-
const
|
|
874
|
+
const existingWorld = await this.runtime.getWorld(worldId);
|
|
875
|
+
if (existingWorld) {
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
const userId = ctx.from ? createUniqueUuid2(this.runtime, ctx.from.id.toString()) : null;
|
|
879
|
+
let admins = [];
|
|
880
|
+
let owner = null;
|
|
881
|
+
if (chat.type === "group" || chat.type === "supergroup" || chat.type === "channel") {
|
|
882
|
+
try {
|
|
883
|
+
admins = await ctx.getChatAdministrators();
|
|
884
|
+
owner = admins.find((admin) => admin.status === "creator");
|
|
885
|
+
} catch (error) {
|
|
886
|
+
logger2.warn(`Could not get chat administrators: ${error.message}`);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
let ownerId = userId;
|
|
890
|
+
if (owner) {
|
|
891
|
+
ownerId = createUniqueUuid2(this.runtime, String(owner.user.id));
|
|
892
|
+
}
|
|
645
893
|
const world = {
|
|
646
894
|
id: worldId,
|
|
647
895
|
name: chatTitle,
|
|
@@ -649,14 +897,17 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
649
897
|
serverId: chatId,
|
|
650
898
|
metadata: {
|
|
651
899
|
source: "telegram",
|
|
652
|
-
ownership: { ownerId
|
|
653
|
-
roles: {
|
|
654
|
-
[
|
|
655
|
-
}
|
|
900
|
+
ownership: { ownerId },
|
|
901
|
+
roles: ownerId ? {
|
|
902
|
+
[ownerId]: Role.OWNER
|
|
903
|
+
} : {},
|
|
904
|
+
chatType: chat.type,
|
|
905
|
+
isForumEnabled: chat.type === "supergroup" && chat.is_forum
|
|
656
906
|
}
|
|
657
907
|
};
|
|
658
|
-
|
|
659
|
-
|
|
908
|
+
await this.runtime.ensureWorldExists(world);
|
|
909
|
+
const generalRoom = {
|
|
910
|
+
id: createUniqueUuid2(this.runtime, chatId),
|
|
660
911
|
name: chatTitle,
|
|
661
912
|
source: "telegram",
|
|
662
913
|
type: channelType,
|
|
@@ -664,204 +915,242 @@ var TelegramService = class _TelegramService extends Service {
|
|
|
664
915
|
serverId: chatId,
|
|
665
916
|
worldId
|
|
666
917
|
};
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
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}`);
|
|
918
|
+
await this.runtime.ensureRoomExists(generalRoom);
|
|
919
|
+
const rooms = [generalRoom];
|
|
920
|
+
if (chat.type === "supergroup" && chat.is_forum && ((_a = ctx.message) == null ? void 0 : _a.message_thread_id)) {
|
|
921
|
+
const topicRoom = await this.buildForumTopicRoom(ctx, worldId);
|
|
922
|
+
if (topicRoom) {
|
|
923
|
+
rooms.push(topicRoom);
|
|
924
|
+
}
|
|
925
|
+
await this.runtime.ensureRoomExists(topicRoom);
|
|
926
|
+
}
|
|
927
|
+
const entities = await this.buildStandardizedEntities(chat);
|
|
928
|
+
if (ctx.from) {
|
|
929
|
+
const senderEntity = this.buildMsgSenderEntity(ctx.from);
|
|
930
|
+
if (senderEntity && !entities.some((e) => e.id === senderEntity.id)) {
|
|
931
|
+
entities.push(senderEntity);
|
|
932
|
+
this.syncedEntityIds.add(senderEntity.id);
|
|
717
933
|
}
|
|
718
934
|
}
|
|
719
|
-
|
|
935
|
+
await this.batchProcessEntities(
|
|
936
|
+
entities,
|
|
937
|
+
generalRoom.id,
|
|
938
|
+
generalRoom.channelId,
|
|
939
|
+
generalRoom.serverId,
|
|
940
|
+
generalRoom.type,
|
|
941
|
+
worldId
|
|
942
|
+
);
|
|
943
|
+
const telegramWorldPayload = {
|
|
720
944
|
runtime: this.runtime,
|
|
721
945
|
world,
|
|
722
|
-
rooms
|
|
723
|
-
entities
|
|
724
|
-
source: "telegram"
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
...worldPayload,
|
|
728
|
-
chat
|
|
946
|
+
rooms,
|
|
947
|
+
entities,
|
|
948
|
+
source: "telegram",
|
|
949
|
+
chat,
|
|
950
|
+
botUsername: this.bot.botInfo.username
|
|
729
951
|
};
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
if (chat.type === "group" || chat.type === "supergroup") {
|
|
733
|
-
this.setupEntityTracking(chat.id);
|
|
952
|
+
if (chat.type !== "private") {
|
|
953
|
+
await this.runtime.emitEvent("TELEGRAM_WORLD_JOINED" /* WORLD_JOINED */, telegramWorldPayload);
|
|
734
954
|
}
|
|
955
|
+
await this.runtime.emitEvent(EventType2.WORLD_JOINED, {
|
|
956
|
+
runtime: this.runtime,
|
|
957
|
+
world,
|
|
958
|
+
rooms,
|
|
959
|
+
entities,
|
|
960
|
+
source: "telegram"
|
|
961
|
+
});
|
|
735
962
|
}
|
|
736
963
|
/**
|
|
737
|
-
*
|
|
964
|
+
* Processes entities in batches to prevent overwhelming the system.
|
|
738
965
|
*
|
|
966
|
+
* @param {Entity[]} entities - The entities to process
|
|
967
|
+
* @param {UUID} roomId - The ID of the room to connect entities to
|
|
968
|
+
* @param {string} channelId - The channel ID
|
|
969
|
+
* @param {string} serverId - The server ID
|
|
970
|
+
* @param {ChannelType} roomType - The type of the room
|
|
971
|
+
* @param {UUID} worldId - The ID of the world
|
|
972
|
+
* @returns {Promise<void>}
|
|
739
973
|
* @private
|
|
740
|
-
* @returns {void}
|
|
741
974
|
*/
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
975
|
+
async batchProcessEntities(entities, roomId, channelId, serverId, roomType, worldId) {
|
|
976
|
+
const batchSize = 50;
|
|
977
|
+
for (let i = 0; i < entities.length; i += batchSize) {
|
|
978
|
+
const entityBatch = entities.slice(i, i + batchSize);
|
|
979
|
+
await Promise.all(
|
|
980
|
+
entityBatch.map(async (entity) => {
|
|
981
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
982
|
+
try {
|
|
983
|
+
await this.runtime.ensureConnection({
|
|
984
|
+
entityId: entity.id,
|
|
985
|
+
roomId,
|
|
986
|
+
userName: (_b = (_a = entity.metadata) == null ? void 0 : _a.telegram) == null ? void 0 : _b.username,
|
|
987
|
+
name: (_d = (_c = entity.metadata) == null ? void 0 : _c.telegram) == null ? void 0 : _d.name,
|
|
988
|
+
userId: (_f = (_e = entity.metadata) == null ? void 0 : _e.telegram) == null ? void 0 : _f.id,
|
|
989
|
+
source: "telegram",
|
|
990
|
+
channelId,
|
|
991
|
+
serverId,
|
|
992
|
+
type: roomType,
|
|
993
|
+
worldId
|
|
994
|
+
});
|
|
995
|
+
} catch (err) {
|
|
996
|
+
logger2.warn(`Failed to sync user ${(_h = (_g = entity.metadata) == null ? void 0 : _g.telegram) == null ? void 0 : _h.username}: ${err}`);
|
|
997
|
+
}
|
|
998
|
+
})
|
|
999
|
+
);
|
|
1000
|
+
if (i + batchSize < entities.length) {
|
|
1001
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
757
1002
|
}
|
|
758
|
-
}
|
|
1003
|
+
}
|
|
759
1004
|
}
|
|
760
1005
|
/**
|
|
761
|
-
*
|
|
762
|
-
*
|
|
1006
|
+
* Gets chat title and channel type based on Telegram chat type.
|
|
1007
|
+
* Maps Telegram-specific chat types to standardized system types.
|
|
1008
|
+
*
|
|
1009
|
+
* @param {any} chat - The Telegram chat object
|
|
1010
|
+
* @returns {Object} Object containing chatTitle and channelType
|
|
1011
|
+
* @private
|
|
763
1012
|
*/
|
|
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
|
-
|
|
1013
|
+
getChatTypeInfo(chat) {
|
|
1014
|
+
let chatTitle;
|
|
1015
|
+
let channelType;
|
|
1016
|
+
switch (chat.type) {
|
|
1017
|
+
case "private":
|
|
1018
|
+
chatTitle = `Chat with ${chat.first_name || "Unknown User"}`;
|
|
1019
|
+
channelType = ChannelType2.DM;
|
|
1020
|
+
break;
|
|
1021
|
+
case "group":
|
|
1022
|
+
chatTitle = chat.title || "Unknown Group";
|
|
1023
|
+
channelType = ChannelType2.GROUP;
|
|
1024
|
+
break;
|
|
1025
|
+
case "supergroup":
|
|
1026
|
+
chatTitle = chat.title || "Unknown Supergroup";
|
|
1027
|
+
channelType = ChannelType2.GROUP;
|
|
1028
|
+
break;
|
|
1029
|
+
case "channel":
|
|
1030
|
+
chatTitle = chat.title || "Unknown Channel";
|
|
1031
|
+
channelType = ChannelType2.FEED;
|
|
1032
|
+
break;
|
|
1033
|
+
default:
|
|
1034
|
+
chatTitle = "Unknown Chat";
|
|
1035
|
+
channelType = ChannelType2.GROUP;
|
|
1036
|
+
}
|
|
1037
|
+
return { chatTitle, channelType };
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Builds standardized entity representations from Telegram chat data.
|
|
1041
|
+
* Transforms Telegram-specific user data into system-standard Entity objects.
|
|
1042
|
+
*
|
|
1043
|
+
* @param {any} chat - The Telegram chat object
|
|
1044
|
+
* @returns {Promise<Entity[]>} Array of standardized Entity objects
|
|
1045
|
+
* @private
|
|
1046
|
+
*/
|
|
1047
|
+
async buildStandardizedEntities(chat) {
|
|
1048
|
+
const entities = [];
|
|
1049
|
+
try {
|
|
1050
|
+
if (chat.type === "private" && chat.id) {
|
|
1051
|
+
const userId = createUniqueUuid2(this.runtime, chat.id.toString());
|
|
1052
|
+
entities.push({
|
|
1053
|
+
id: userId,
|
|
1054
|
+
names: [chat.first_name || "Unknown User"],
|
|
1055
|
+
agentId: this.runtime.agentId,
|
|
796
1056
|
metadata: {
|
|
797
|
-
|
|
1057
|
+
telegram: {
|
|
1058
|
+
id: chat.id.toString(),
|
|
1059
|
+
username: chat.username || "unknown",
|
|
1060
|
+
name: chat.first_name || "Unknown User"
|
|
1061
|
+
},
|
|
1062
|
+
source: "telegram"
|
|
798
1063
|
}
|
|
799
|
-
};
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
1064
|
+
});
|
|
1065
|
+
this.syncedEntityIds.add(userId);
|
|
1066
|
+
} else if (chat.type === "group" || chat.type === "supergroup") {
|
|
1067
|
+
try {
|
|
1068
|
+
const admins = await this.bot.telegram.getChatAdministrators(chat.id);
|
|
1069
|
+
if (admins && admins.length > 0) {
|
|
1070
|
+
for (const admin of admins) {
|
|
1071
|
+
const userId = createUniqueUuid2(this.runtime, admin.user.id.toString());
|
|
1072
|
+
entities.push({
|
|
1073
|
+
id: userId,
|
|
1074
|
+
names: [admin.user.first_name || admin.user.username || "Unknown Admin"],
|
|
1075
|
+
agentId: this.runtime.agentId,
|
|
1076
|
+
metadata: {
|
|
1077
|
+
telegram: {
|
|
1078
|
+
id: admin.user.id.toString(),
|
|
1079
|
+
username: admin.user.username || "unknown",
|
|
1080
|
+
name: admin.user.first_name || "Unknown Admin",
|
|
1081
|
+
isAdmin: true,
|
|
1082
|
+
adminTitle: admin.custom_title || (admin.status === "creator" ? "Owner" : "Admin")
|
|
1083
|
+
},
|
|
1084
|
+
source: "telegram",
|
|
1085
|
+
roles: [admin.status === "creator" ? Role.OWNER : Role.ADMIN]
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
this.syncedEntityIds.add(userId);
|
|
1089
|
+
}
|
|
806
1090
|
}
|
|
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);
|
|
1091
|
+
} catch (error) {
|
|
1092
|
+
logger2.warn(`Could not fetch administrators for chat ${chat.id}: ${error}`);
|
|
1093
|
+
}
|
|
815
1094
|
}
|
|
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
|
-
|
|
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
|
-
);
|
|
1095
|
+
} catch (error) {
|
|
1096
|
+
logger2.error(
|
|
1097
|
+
`Error building standardized entities: ${error instanceof Error ? error.message : String(error)}`
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
return entities;
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Extracts and builds the room object for a forum topic from a message context.
|
|
1104
|
+
* This refactored method can be used both in middleware and when handling new chats.
|
|
1105
|
+
*
|
|
1106
|
+
* @param {Context} ctx - The context of the incoming update
|
|
1107
|
+
* @param {UUID} worldId - The ID of the world the topic belongs to
|
|
1108
|
+
* @returns {Promise<Room | null>} A Promise that resolves with the room or null if not a topic
|
|
1109
|
+
* @private
|
|
1110
|
+
*/
|
|
1111
|
+
async buildForumTopicRoom(ctx, worldId) {
|
|
1112
|
+
var _a;
|
|
1113
|
+
if (!ctx.chat || !((_a = ctx.message) == null ? void 0 : _a.message_thread_id)) return null;
|
|
1114
|
+
if (ctx.chat.type !== "supergroup" || !ctx.chat.is_forum) return null;
|
|
1115
|
+
const chat = ctx.chat;
|
|
1116
|
+
const chatId = chat.id.toString();
|
|
1117
|
+
const threadId = ctx.message.message_thread_id.toString();
|
|
1118
|
+
const roomId = createUniqueUuid2(this.runtime, `${chatId}-${threadId}`);
|
|
1119
|
+
try {
|
|
1120
|
+
const replyMessage = JSON.parse(JSON.stringify(ctx.message));
|
|
1121
|
+
let topicName = `Topic #${threadId}`;
|
|
1122
|
+
if (replyMessage && typeof replyMessage === "object" && "forum_topic_created" in replyMessage && replyMessage.forum_topic_created) {
|
|
1123
|
+
const topicCreated = replyMessage.forum_topic_created;
|
|
1124
|
+
if (topicCreated && typeof topicCreated === "object" && "name" in topicCreated) {
|
|
1125
|
+
topicName = topicCreated.name;
|
|
1126
|
+
}
|
|
1127
|
+
} 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) {
|
|
1128
|
+
const topicCreated = replyMessage.reply_to_message.forum_topic_created;
|
|
1129
|
+
if (topicCreated && typeof topicCreated === "object" && "name" in topicCreated) {
|
|
1130
|
+
topicName = topicCreated.name;
|
|
860
1131
|
}
|
|
861
|
-
} catch (error) {
|
|
862
|
-
logger2.error(`Error handling Telegram entity leaving chat ${chatId}:`, error);
|
|
863
1132
|
}
|
|
864
|
-
|
|
1133
|
+
const room = {
|
|
1134
|
+
id: roomId,
|
|
1135
|
+
name: topicName,
|
|
1136
|
+
source: "telegram",
|
|
1137
|
+
type: ChannelType2.GROUP,
|
|
1138
|
+
channelId: `${chatId}-${threadId}`,
|
|
1139
|
+
serverId: chatId,
|
|
1140
|
+
worldId,
|
|
1141
|
+
metadata: {
|
|
1142
|
+
threadId,
|
|
1143
|
+
isForumTopic: true,
|
|
1144
|
+
parentChatId: chatId
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
return room;
|
|
1148
|
+
} catch (error) {
|
|
1149
|
+
logger2.error(
|
|
1150
|
+
`Error building forum topic room: ${error instanceof Error ? error.message : String(error)}`
|
|
1151
|
+
);
|
|
1152
|
+
return null;
|
|
1153
|
+
}
|
|
865
1154
|
}
|
|
866
1155
|
};
|
|
867
1156
|
|