@elizaos/plugin-telegram 1.0.0-alpha.2 → 1.0.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,10 +1,28 @@
1
1
  // src/index.ts
2
2
  import {
3
+ ChannelType as ChannelType2,
4
+ EventTypes as EventTypes2,
5
+ Role,
3
6
  Service,
7
+ createUniqueUuid as createUniqueUuid2,
4
8
  logger as logger3
5
9
  } from "@elizaos/core";
6
10
  import { Telegraf } from "telegraf";
7
11
 
12
+ // src/constants.ts
13
+ var MESSAGE_CONSTANTS = {
14
+ MAX_MESSAGES: 50,
15
+ RECENT_MESSAGE_COUNT: 5,
16
+ CHAT_HISTORY_COUNT: 10,
17
+ DEFAULT_SIMILARITY_THRESHOLD: 0.6,
18
+ DEFAULT_SIMILARITY_THRESHOLD_FOLLOW_UPS: 0.4,
19
+ INTEREST_DECAY_TIME: 5 * 60 * 1e3,
20
+ // 5 minutes
21
+ PARTIAL_INTEREST_DECAY: 3 * 60 * 1e3
22
+ // 3 minutes
23
+ };
24
+ var TELEGRAM_SERVICE_NAME = "telegram";
25
+
8
26
  // ../../node_modules/zod/lib/index.mjs
9
27
  var util;
10
28
  (function(util2) {
@@ -4074,10 +4092,10 @@ ${errorMessages}`
4074
4092
  // src/messageManager.ts
4075
4093
  import {
4076
4094
  ChannelType,
4077
- createUniqueUuid,
4078
- logger,
4095
+ EventTypes,
4079
4096
  ModelTypes,
4080
- Role
4097
+ createUniqueUuid,
4098
+ logger
4081
4099
  } from "@elizaos/core";
4082
4100
 
4083
4101
  // src/utils.ts
@@ -4106,11 +4124,23 @@ var getChannelType = (chat) => {
4106
4124
  var MessageManager = class {
4107
4125
  bot;
4108
4126
  runtime;
4127
+ /**
4128
+ * Constructor for creating a new instance of a BotAgent.
4129
+ *
4130
+ * @param {Telegraf<Context>} bot - The Telegraf instance used for interacting with the bot platform.
4131
+ * @param {IAgentRuntime} runtime - The runtime environment for the agent.
4132
+ */
4109
4133
  constructor(bot, runtime) {
4110
4134
  this.bot = bot;
4111
4135
  this.runtime = runtime;
4112
4136
  }
4113
4137
  // Process image messages and generate descriptions
4138
+ /**
4139
+ * Process an image from a Telegram message to extract the image URL and description.
4140
+ *
4141
+ * @param {Message} message - The Telegram message object containing the image.
4142
+ * @returns {Promise<{ description: string } | null>} The description of the processed image or null if no image found.
4143
+ */
4114
4144
  async processImage(message) {
4115
4145
  var _a, _b, _c;
4116
4146
  try {
@@ -4140,6 +4170,14 @@ ${description}]` };
4140
4170
  return null;
4141
4171
  }
4142
4172
  // Send long messages in chunks
4173
+ /**
4174
+ * Sends a message in chunks, handling attachments and splitting the message if necessary
4175
+ *
4176
+ * @param {Context} ctx - The context object representing the current state of the bot
4177
+ * @param {Content} content - The content of the message to be sent
4178
+ * @param {number} [replyToMessageId] - The ID of the message to reply to, if any
4179
+ * @returns {Promise<Message.TextMessage[]>} - An array of TextMessage objects representing the messages sent
4180
+ */
4143
4181
  async sendMessageInChunks(ctx, content, replyToMessageId) {
4144
4182
  if (content.attachments && content.attachments.length > 0) {
4145
4183
  content.attachments.map(async (attachment) => {
@@ -4187,6 +4225,16 @@ ${description}]` };
4187
4225
  return sentMessages;
4188
4226
  }
4189
4227
  }
4228
+ /**
4229
+ * Sends media to a chat using the Telegram API.
4230
+ *
4231
+ * @param {Context} ctx - The context object containing information about the current chat.
4232
+ * @param {string} mediaPath - The path to the media to be sent, either a URL or a local file path.
4233
+ * @param {MediaType} type - The type of media being sent (PHOTO, VIDEO, DOCUMENT, AUDIO, or ANIMATION).
4234
+ * @param {string} [caption] - Optional caption for the media being sent.
4235
+ *
4236
+ * @returns {Promise<void>} A Promise that resolves when the media is successfully sent.
4237
+ */
4190
4238
  async sendMedia(ctx, mediaPath, type, caption) {
4191
4239
  try {
4192
4240
  const isUrl = /^(http|https):\/\//.test(mediaPath);
@@ -4226,6 +4274,12 @@ ${description}]` };
4226
4274
  }
4227
4275
  }
4228
4276
  // Split message into smaller parts
4277
+ /**
4278
+ * Splits a given text into an array of strings based on the maximum message length.
4279
+ *
4280
+ * @param {string} text - The text to split into chunks.
4281
+ * @returns {string[]} An array of strings with each element representing a chunk of the original text.
4282
+ */
4229
4283
  splitMessage(text) {
4230
4284
  const chunks = [];
4231
4285
  let currentChunk = "";
@@ -4242,6 +4296,11 @@ ${description}]` };
4242
4296
  return chunks;
4243
4297
  }
4244
4298
  // Main handler for incoming messages
4299
+ /**
4300
+ * Handle incoming messages from Telegram and process them accordingly.
4301
+ * @param {Context} ctx - The context object containing information about the message.
4302
+ * @returns {Promise<void>}
4303
+ */
4245
4304
  async handleMessage(ctx) {
4246
4305
  var _a, _b;
4247
4306
  if (!ctx.message || !ctx.from) return;
@@ -4267,28 +4326,11 @@ ${description}]` };
4267
4326
  }
4268
4327
  const fullText = imageInfo ? `${messageText} ${imageInfo.description}` : messageText;
4269
4328
  if (!fullText) return;
4270
- const memory = {
4271
- id: messageId,
4272
- entityId,
4273
- agentId: this.runtime.agentId,
4274
- roomId,
4275
- content: {
4276
- text: fullText,
4277
- source: "telegram",
4278
- channelType: getChannelType(message.chat),
4279
- // name: userName,
4280
- // userName: userName,
4281
- // Safely access reply_to_message with type guard
4282
- inReplyTo: "reply_to_message" in message && message.reply_to_message ? createUniqueUuid(
4283
- this.runtime,
4284
- message.reply_to_message.message_id.toString()
4285
- ) : void 0
4286
- },
4287
- createdAt: message.date * 1e3
4288
- };
4289
4329
  const chat = message.chat;
4290
- const worldName = chat.type === "supergroup" ? chat.title : chat.type === "channel" ? chat.title : "undefined";
4330
+ const channelType = getChannelType(chat);
4331
+ const worldName = chat.type === "supergroup" ? chat.title : chat.type === "channel" ? chat.title : chat.type === "private" ? `Chat with ${chat.first_name || "Unknown"}` : "Telegram";
4291
4332
  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";
4333
+ const worldId = createUniqueUuid(this.runtime, chat.id.toString());
4292
4334
  await this.runtime.ensureConnection({
4293
4335
  entityId,
4294
4336
  roomId,
@@ -4297,10 +4339,9 @@ ${description}]` };
4297
4339
  source: "telegram",
4298
4340
  channelId: ctx.chat.id.toString(),
4299
4341
  serverId: chat.id.toString(),
4300
- type: getChannelType(chat)
4342
+ type: channelType,
4343
+ worldId
4301
4344
  });
4302
- const channelType = getChannelType(chat);
4303
- const worldId = createUniqueUuid(this.runtime, chat.id.toString());
4304
4345
  const room = {
4305
4346
  id: roomId,
4306
4347
  name: roomName,
@@ -4310,24 +4351,23 @@ ${description}]` };
4310
4351
  serverId: ctx.chat.id.toString(),
4311
4352
  worldId
4312
4353
  };
4313
- const ownerId = chat.id;
4314
- if (channelType === ChannelType.GROUP) {
4315
- await this.runtime.ensureWorldExists({
4316
- id: worldId,
4317
- name: worldName,
4318
- serverId: chat.id.toString(),
4319
- agentId: this.runtime.agentId,
4320
- metadata: {
4321
- ownership: chat.type === "supergroup" ? { ownerId: chat.id.toString() } : void 0,
4322
- roles: {
4323
- // TODO: chat.id is probably wrong key for this
4324
- [ownerId]: Role.OWNER
4325
- }
4326
- }
4327
- });
4328
- room.worldId = worldId;
4329
- }
4330
4354
  await this.runtime.ensureRoomExists(room);
4355
+ const memory = {
4356
+ id: messageId,
4357
+ entityId,
4358
+ agentId: this.runtime.agentId,
4359
+ roomId,
4360
+ content: {
4361
+ text: fullText,
4362
+ source: "telegram",
4363
+ channelType,
4364
+ inReplyTo: "reply_to_message" in message && message.reply_to_message ? createUniqueUuid(
4365
+ this.runtime,
4366
+ message.reply_to_message.message_id.toString()
4367
+ ) : void 0
4368
+ },
4369
+ createdAt: message.date * 1e3
4370
+ };
4331
4371
  const callback = async (content, _files) => {
4332
4372
  try {
4333
4373
  const sentMessages = await this.sendMessageInChunks(
@@ -4352,7 +4392,7 @@ ${description}]` };
4352
4392
  ...content,
4353
4393
  text: sentMessage.text,
4354
4394
  inReplyTo: messageId,
4355
- channelType: getChannelType(message.chat)
4395
+ channelType
4356
4396
  },
4357
4397
  createdAt: sentMessage.date * 1e3
4358
4398
  };
@@ -4366,11 +4406,23 @@ ${description}]` };
4366
4406
  }
4367
4407
  };
4368
4408
  this.runtime.emitEvent(
4369
- ["TELEGRAM_MESSAGE_RECEIVED", "MESSAGE_RECEIVED"],
4409
+ EventTypes.MESSAGE_RECEIVED,
4410
+ {
4411
+ runtime: this.runtime,
4412
+ message: memory,
4413
+ callback,
4414
+ source: "telegram"
4415
+ }
4416
+ );
4417
+ this.runtime.emitEvent(
4418
+ "TELEGRAM_MESSAGE_RECEIVED" /* MESSAGE_RECEIVED */,
4370
4419
  {
4371
4420
  runtime: this.runtime,
4372
4421
  message: memory,
4373
- callback
4422
+ callback,
4423
+ source: "telegram",
4424
+ ctx,
4425
+ originalMessage: message
4374
4426
  }
4375
4427
  );
4376
4428
  } catch (error) {
@@ -4379,6 +4431,11 @@ ${description}]` };
4379
4431
  throw error;
4380
4432
  }
4381
4433
  }
4434
+ /**
4435
+ * Handles the reaction event triggered by a user reacting to a message.
4436
+ * * @param {NarrowedContext<Context<Update>, Update.MessageReactionUpdate>} ctx The context of the message reaction update
4437
+ * @returns {Promise<void>} A Promise that resolves when the reaction handling is complete
4438
+ */
4382
4439
  async handleReaction(ctx) {
4383
4440
  if (!ctx.update.message_reaction || !ctx.from) return;
4384
4441
  const reaction = ctx.update.message_reaction;
@@ -4390,6 +4447,7 @@ ${description}]` };
4390
4447
  ctx.from.id.toString()
4391
4448
  );
4392
4449
  const roomId = createUniqueUuid(this.runtime, ctx.chat.id.toString());
4450
+ const worldId = createUniqueUuid(this.runtime, ctx.chat.id.toString());
4393
4451
  const reactionId = createUniqueUuid(
4394
4452
  this.runtime,
4395
4453
  `${reaction.message_id}-${ctx.from.id}-${Date.now()}`
@@ -4403,8 +4461,6 @@ ${description}]` };
4403
4461
  channelType: getChannelType(reaction.chat),
4404
4462
  text: `Reacted with: ${reactionType === "emoji" ? reactionEmoji : reactionType}`,
4405
4463
  source: "telegram",
4406
- // name: ctx.from.first_name,
4407
- // userName: ctx.from.username,
4408
4464
  inReplyTo: createUniqueUuid(
4409
4465
  this.runtime,
4410
4466
  reaction.message_id.toString()
@@ -4412,7 +4468,6 @@ ${description}]` };
4412
4468
  },
4413
4469
  createdAt: Date.now()
4414
4470
  };
4415
- await this.runtime.getMemoryManager("messages").createMemory(memory);
4416
4471
  const callback = async (content) => {
4417
4472
  try {
4418
4473
  const sentMessage = await ctx.reply(content.text);
@@ -4437,17 +4492,94 @@ ${description}]` };
4437
4492
  }
4438
4493
  };
4439
4494
  this.runtime.emitEvent(
4440
- ["TELEGRAM_REACTION_RECEIVED", "REACTION_RECEIVED"],
4495
+ EventTypes.REACTION_RECEIVED,
4496
+ {
4497
+ runtime: this.runtime,
4498
+ message: memory,
4499
+ callback,
4500
+ source: "telegram"
4501
+ }
4502
+ );
4503
+ this.runtime.emitEvent(
4504
+ "TELEGRAM_REACTION_RECEIVED" /* REACTION_RECEIVED */,
4441
4505
  {
4442
4506
  runtime: this.runtime,
4443
4507
  message: memory,
4444
- callback
4508
+ callback,
4509
+ source: "telegram",
4510
+ ctx,
4511
+ reactionString: reactionType === "emoji" ? reactionEmoji : reactionType,
4512
+ originalReaction: reaction.new_reaction[0]
4445
4513
  }
4446
4514
  );
4447
4515
  } catch (error) {
4448
4516
  logger.error("Error handling reaction:", error);
4449
4517
  }
4450
4518
  }
4519
+ /**
4520
+ * Sends a message to a Telegram chat and emits appropriate events
4521
+ * @param {number | string} chatId - The Telegram chat ID to send the message to
4522
+ * @param {Content} content - The content to send
4523
+ * @param {number} [replyToMessageId] - Optional message ID to reply to
4524
+ * @returns {Promise<Message.TextMessage[]>} The sent messages
4525
+ */
4526
+ async sendMessage(chatId, content, replyToMessageId) {
4527
+ try {
4528
+ const ctx = {
4529
+ chat: { id: chatId },
4530
+ telegram: this.bot.telegram
4531
+ };
4532
+ const sentMessages = await this.sendMessageInChunks(
4533
+ ctx,
4534
+ content,
4535
+ replyToMessageId
4536
+ );
4537
+ if (!(sentMessages == null ? void 0 : sentMessages.length)) return [];
4538
+ const roomId = createUniqueUuid(this.runtime, chatId.toString());
4539
+ const memories = [];
4540
+ for (const sentMessage of sentMessages) {
4541
+ const memory = {
4542
+ id: createUniqueUuid(this.runtime, sentMessage.message_id.toString()),
4543
+ entityId: this.runtime.agentId,
4544
+ agentId: this.runtime.agentId,
4545
+ roomId,
4546
+ content: {
4547
+ ...content,
4548
+ text: sentMessage.text,
4549
+ source: "telegram",
4550
+ channelType: getChannelType({
4551
+ id: typeof chatId === "string" ? Number.parseInt(chatId, 10) : chatId,
4552
+ type: "private"
4553
+ // Default to private, will be overridden if in context
4554
+ })
4555
+ },
4556
+ createdAt: sentMessage.date * 1e3
4557
+ };
4558
+ await this.runtime.getMemoryManager("messages").createMemory(memory);
4559
+ memories.push(memory);
4560
+ }
4561
+ this.runtime.emitEvent(
4562
+ EventTypes.MESSAGE_SENT,
4563
+ {
4564
+ runtime: this.runtime,
4565
+ messages: memories,
4566
+ roomId,
4567
+ source: "telegram"
4568
+ }
4569
+ );
4570
+ this.runtime.emitEvent(
4571
+ "TELEGRAM_MESSAGE_SENT" /* MESSAGE_SENT */,
4572
+ {
4573
+ originalMessages: sentMessages,
4574
+ chatId
4575
+ }
4576
+ );
4577
+ return sentMessages;
4578
+ } catch (error) {
4579
+ logger.error("Error sending message to Telegram:", error);
4580
+ return [];
4581
+ }
4582
+ }
4451
4583
  };
4452
4584
 
4453
4585
  // src/tests.ts
@@ -4459,6 +4591,14 @@ var TelegramTestSuite = class {
4459
4591
  bot = null;
4460
4592
  messageManager = null;
4461
4593
  tests;
4594
+ /**
4595
+ * Constructor for initializing a set of test cases for a Telegram bot.
4596
+ *
4597
+ * @constructor
4598
+ * @property {Array<Object>} tests - An array of test cases with name and corresponding test functions.
4599
+ * @property {string} tests.name - The name of the test case.
4600
+ * @property {function} tests.fn - The test function to be executed.
4601
+ */
4462
4602
  constructor() {
4463
4603
  this.tests = [
4464
4604
  {
@@ -4489,6 +4629,13 @@ var TelegramTestSuite = class {
4489
4629
  * Reference on getting the Telegram chat ID:
4490
4630
  * https://stackoverflow.com/a/32572159
4491
4631
  */
4632
+ /**
4633
+ * Validates the chat ID by checking if it is set in the runtime settings or environment variables.
4634
+ * If not set, an error is thrown with a message instructing to provide a valid chat ID.
4635
+ * @param {IAgentRuntime} runtime - The runtime object that provides access to the settings and environment variables.
4636
+ * @throws {Error} If TELEGRAM_TEST_CHAT_ID is not set in the runtime settings or environment variables.
4637
+ * @returns {string} The validated chat ID.
4638
+ */
4492
4639
  validateChatId(runtime) {
4493
4640
  const testChatId = runtime.getSetting("TELEGRAM_TEST_CHAT_ID") || process.env.TELEGRAM_TEST_CHAT_ID;
4494
4641
  if (!testChatId) {
@@ -4531,13 +4678,17 @@ var TelegramTestSuite = class {
4531
4678
  const chat = await this.getChatInfo(runtime);
4532
4679
  const mockContext = {
4533
4680
  chat,
4534
- from: { id: "mock-user-id", username: "TestUser" },
4681
+ from: { id: 123, username: "TestUser" },
4535
4682
  telegram: this.bot.telegram
4536
4683
  };
4537
4684
  const messageContent = {
4538
4685
  text: "Here is an image attachment:",
4539
4686
  attachments: [
4540
4687
  {
4688
+ id: "123",
4689
+ title: "Sample Image",
4690
+ source: TEST_IMAGE_URL,
4691
+ text: "Sample Image",
4541
4692
  url: TEST_IMAGE_URL,
4542
4693
  contentType: "image/png",
4543
4694
  description: "Sample Image"
@@ -4561,12 +4712,19 @@ var TelegramTestSuite = class {
4561
4712
  const chat = await this.getChatInfo(runtime);
4562
4713
  const mockContext = {
4563
4714
  chat,
4564
- from: { id: "mock-user-id", username: "TestUser" },
4715
+ from: {
4716
+ id: 123,
4717
+ username: "TestUser",
4718
+ is_bot: false,
4719
+ first_name: "Test",
4720
+ last_name: "User"
4721
+ },
4565
4722
  message: {
4566
4723
  message_id: void 0,
4567
4724
  text: `@${(_a = this.bot.botInfo) == null ? void 0 : _a.username}! Hello!`,
4568
4725
  date: Math.floor(Date.now() / 1e3),
4569
4726
  chat
4727
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
4570
4728
  },
4571
4729
  telegram: this.bot.telegram
4572
4730
  };
@@ -4586,13 +4744,12 @@ var TelegramTestSuite = class {
4586
4744
  const fileId = await this.getFileId(chatId, TEST_IMAGE_URL);
4587
4745
  const mockMessage = {
4588
4746
  message_id: void 0,
4747
+ chat: { id: chatId },
4589
4748
  date: Math.floor(Date.now() / 1e3),
4590
4749
  photo: [{ file_id: fileId }],
4591
4750
  text: `@${(_a = this.bot.botInfo) == null ? void 0 : _a.username}!`
4592
4751
  };
4593
- const { description } = await this.messageManager.processImage(
4594
- mockMessage
4595
- );
4752
+ const { description } = await this.messageManager.processImage(mockMessage);
4596
4753
  if (!description) {
4597
4754
  throw new Error("Error processing Telegram image");
4598
4755
  }
@@ -4612,20 +4769,6 @@ var TelegramTestSuite = class {
4612
4769
  }
4613
4770
  };
4614
4771
 
4615
- // src/constants.ts
4616
- var MESSAGE_CONSTANTS = {
4617
- MAX_MESSAGES: 50,
4618
- RECENT_MESSAGE_COUNT: 5,
4619
- CHAT_HISTORY_COUNT: 10,
4620
- DEFAULT_SIMILARITY_THRESHOLD: 0.6,
4621
- DEFAULT_SIMILARITY_THRESHOLD_FOLLOW_UPS: 0.4,
4622
- INTEREST_DECAY_TIME: 5 * 60 * 1e3,
4623
- // 5 minutes
4624
- PARTIAL_INTEREST_DECAY: 3 * 60 * 1e3
4625
- // 3 minutes
4626
- };
4627
- var TELEGRAM_SERVICE_NAME = "telegram";
4628
-
4629
4772
  // src/index.ts
4630
4773
  var TelegramService = class _TelegramService extends Service {
4631
4774
  static serviceType = TELEGRAM_SERVICE_NAME;
@@ -4633,6 +4776,11 @@ var TelegramService = class _TelegramService extends Service {
4633
4776
  bot;
4634
4777
  messageManager;
4635
4778
  options;
4779
+ knownChats = /* @__PURE__ */ new Map();
4780
+ /**
4781
+ * Constructor for TelegramService class.
4782
+ * @param {IAgentRuntime} runtime - The runtime object for the agent.
4783
+ */
4636
4784
  constructor(runtime) {
4637
4785
  super(runtime);
4638
4786
  logger3.log("\u{1F4F1} Constructing new TelegramService...");
@@ -4646,68 +4794,234 @@ var TelegramService = class _TelegramService extends Service {
4646
4794
  this.messageManager = new MessageManager(this.bot, this.runtime);
4647
4795
  logger3.log("\u2705 TelegramService constructor completed");
4648
4796
  }
4797
+ /**
4798
+ * Starts the Telegram service for the given runtime.
4799
+ *
4800
+ * @param {IAgentRuntime} runtime - The agent runtime to start the Telegram service for.
4801
+ * @returns {Promise<TelegramService>} A promise that resolves with the initialized TelegramService.
4802
+ */
4649
4803
  static async start(runtime) {
4650
4804
  await validateTelegramConfig(runtime);
4651
- const tg = new _TelegramService(runtime);
4652
- logger3.success(
4653
- `\u2705 Telegram client successfully started for character ${runtime.character.name}`
4654
- );
4655
- logger3.log("\u{1F680} Starting Telegram bot...");
4656
- try {
4657
- await tg.initializeBot();
4658
- tg.setupMessageHandlers();
4659
- } catch (error) {
4660
- logger3.error("\u274C Failed to launch Telegram bot:", error);
4661
- throw error;
4805
+ const maxRetries = 5;
4806
+ let retryCount = 0;
4807
+ let lastError = null;
4808
+ while (retryCount < maxRetries) {
4809
+ try {
4810
+ const service = new _TelegramService(runtime);
4811
+ logger3.success(
4812
+ `\u2705 Telegram client successfully started for character ${runtime.character.name}`
4813
+ );
4814
+ logger3.log("\u{1F680} Starting Telegram bot...");
4815
+ await service.initializeBot();
4816
+ service.setupMessageHandlers();
4817
+ await service.bot.telegram.getMe();
4818
+ return service;
4819
+ } catch (error) {
4820
+ lastError = error instanceof Error ? error : new Error(String(error));
4821
+ logger3.error(`Telegram initialization attempt ${retryCount + 1} failed: ${lastError.message}`);
4822
+ retryCount++;
4823
+ if (retryCount < maxRetries) {
4824
+ const delay = 2 ** retryCount * 1e3;
4825
+ logger3.info(`Retrying Telegram initialization in ${delay / 1e3} seconds...`);
4826
+ await new Promise((resolve) => setTimeout(resolve, delay));
4827
+ }
4828
+ }
4662
4829
  }
4663
- return tg;
4830
+ throw new Error(`Telegram initialization failed after ${maxRetries} attempts. Last error: ${lastError == null ? void 0 : lastError.message}`);
4664
4831
  }
4832
+ /**
4833
+ * Stops the agent runtime.
4834
+ * @param {IAgentRuntime} runtime - The agent runtime to stop
4835
+ */
4665
4836
  static async stop(runtime) {
4666
4837
  const tgClient = runtime.getService(TELEGRAM_SERVICE_NAME);
4667
4838
  if (tgClient) {
4668
4839
  await tgClient.stop();
4669
4840
  }
4670
4841
  }
4842
+ /**
4843
+ * Asynchronously stops the bot.
4844
+ *
4845
+ * @returns A Promise that resolves once the bot has stopped.
4846
+ */
4671
4847
  async stop() {
4672
4848
  this.bot.stop();
4673
4849
  }
4850
+ /**
4851
+ * Initializes the Telegram bot by launching it, getting bot info, and setting up message manager.
4852
+ * @returns {Promise<void>} A Promise that resolves when the initialization is complete.
4853
+ */
4674
4854
  async initializeBot() {
4675
4855
  this.bot.launch({
4676
4856
  dropPendingUpdates: true,
4677
4857
  allowedUpdates: ["message", "message_reaction"]
4678
4858
  });
4679
- logger3.log("\u2728 Telegram bot successfully launched and is running!");
4680
4859
  const botInfo = await this.bot.telegram.getMe();
4681
- this.bot.botInfo = botInfo;
4682
- logger3.success(`Bot username: @${botInfo.username}`);
4683
- this.messageManager.bot = this.bot;
4860
+ logger3.log(`Bot info: ${JSON.stringify(botInfo)}`);
4861
+ process.once("SIGINT", () => this.bot.stop("SIGINT"));
4862
+ process.once("SIGTERM", () => this.bot.stop("SIGTERM"));
4684
4863
  }
4864
+ /**
4865
+ * Checks if a group is authorized, based on the TELEGRAM_ALLOWED_CHATS setting.
4866
+ * @param {Context} ctx - The context of the incoming update.
4867
+ * @returns {Promise<boolean>} A Promise that resolves with a boolean indicating if the group is authorized.
4868
+ */
4685
4869
  async isGroupAuthorized(ctx) {
4686
- var _a, _b, _c;
4687
- const config = (_a = this.runtime.character.settings) == null ? void 0 : _a.telegram;
4688
- if (((_b = ctx.from) == null ? void 0 : _b.id) === ((_c = ctx.botInfo) == null ? void 0 : _c.id)) {
4689
- return false;
4870
+ var _a;
4871
+ const chatId = (_a = ctx.chat) == null ? void 0 : _a.id.toString();
4872
+ if (!chatId) return false;
4873
+ if (!this.knownChats.has(chatId)) {
4874
+ await this.handleNewChat(ctx);
4690
4875
  }
4691
- if (!(config == null ? void 0 : config.shouldOnlyJoinInAllowedGroups)) {
4876
+ const allowedChats = this.runtime.getSetting("TELEGRAM_ALLOWED_CHATS");
4877
+ if (!allowedChats) {
4692
4878
  return true;
4693
4879
  }
4694
- const allowedGroups = config.allowedGroupIds || [];
4695
- const currentGroupId = ctx.chat.id.toString();
4696
- if (!allowedGroups.includes(currentGroupId)) {
4697
- logger3.info(`Unauthorized group detected: ${currentGroupId}`);
4880
+ try {
4881
+ const allowedChatsList = JSON.parse(allowedChats);
4882
+ return allowedChatsList.includes(chatId);
4883
+ } catch (error) {
4884
+ logger3.error("Error parsing TELEGRAM_ALLOWED_CHATS:", error);
4885
+ return false;
4886
+ }
4887
+ }
4888
+ /**
4889
+ * Handles new chat discovery and emits WORLD_JOINED event
4890
+ * @param {Context} ctx - The context of the incoming update
4891
+ */
4892
+ async handleNewChat(ctx) {
4893
+ if (!ctx.chat) return;
4894
+ const chat = ctx.chat;
4895
+ const chatId = chat.id.toString();
4896
+ this.knownChats.set(chatId, chat);
4897
+ let chatTitle;
4898
+ let channelType;
4899
+ switch (chat.type) {
4900
+ case "private":
4901
+ chatTitle = `Chat with ${chat.first_name || "Unknown User"}`;
4902
+ channelType = ChannelType2.DM;
4903
+ break;
4904
+ case "group":
4905
+ chatTitle = chat.title || "Unknown Group";
4906
+ channelType = ChannelType2.GROUP;
4907
+ break;
4908
+ case "supergroup":
4909
+ chatTitle = chat.title || "Unknown Supergroup";
4910
+ channelType = ChannelType2.GROUP;
4911
+ break;
4912
+ case "channel":
4913
+ chatTitle = chat.title || "Unknown Channel";
4914
+ channelType = ChannelType2.FEED;
4915
+ break;
4916
+ default:
4917
+ chatTitle = "Unknown Chat";
4918
+ channelType = ChannelType2.GROUP;
4919
+ }
4920
+ const worldId = createUniqueUuid2(this.runtime, chatId);
4921
+ const roomId = createUniqueUuid2(this.runtime, chatId);
4922
+ const world = {
4923
+ id: worldId,
4924
+ name: chatTitle,
4925
+ agentId: this.runtime.agentId,
4926
+ serverId: chatId,
4927
+ metadata: {
4928
+ source: "telegram",
4929
+ ownership: { ownerId: chatId },
4930
+ roles: {
4931
+ [chatId]: Role.OWNER
4932
+ }
4933
+ }
4934
+ };
4935
+ const room = {
4936
+ id: roomId,
4937
+ name: chatTitle,
4938
+ source: "telegram",
4939
+ type: channelType,
4940
+ channelId: chatId,
4941
+ serverId: chatId,
4942
+ worldId
4943
+ };
4944
+ const users = [];
4945
+ if (chat.type === "private" && chat.id) {
4946
+ const userId = createUniqueUuid2(this.runtime, chat.id.toString());
4947
+ users.push({
4948
+ id: userId,
4949
+ names: [chat.first_name || "Unknown User"],
4950
+ agentId: this.runtime.agentId,
4951
+ metadata: {
4952
+ telegram: {
4953
+ id: chat.id.toString(),
4954
+ username: chat.username || "unknown",
4955
+ name: chat.first_name || "Unknown User"
4956
+ },
4957
+ source: "telegram"
4958
+ }
4959
+ });
4960
+ } else if (chat.type === "group" || chat.type === "supergroup") {
4698
4961
  try {
4699
- await ctx.reply("Not authorized. Leaving.");
4700
- await ctx.leaveChat();
4962
+ const admins = await this.bot.telegram.getChatAdministrators(chat.id);
4963
+ if (admins && admins.length > 0) {
4964
+ for (const admin of admins) {
4965
+ const userId = createUniqueUuid2(this.runtime, admin.user.id.toString());
4966
+ users.push({
4967
+ id: userId,
4968
+ names: [admin.user.first_name || admin.user.username || "Unknown Admin"],
4969
+ agentId: this.runtime.agentId,
4970
+ metadata: {
4971
+ telegram: {
4972
+ id: admin.user.id.toString(),
4973
+ username: admin.user.username || "unknown",
4974
+ name: admin.user.first_name || "Unknown Admin",
4975
+ isAdmin: true,
4976
+ adminTitle: admin.custom_title || (admin.status === "creator" ? "Owner" : "Admin")
4977
+ },
4978
+ source: "telegram",
4979
+ roles: [admin.status === "creator" ? Role.OWNER : Role.ADMIN]
4980
+ }
4981
+ });
4982
+ }
4983
+ }
4984
+ try {
4985
+ const chatInfo = await this.bot.telegram.getChat(chat.id);
4986
+ if (chatInfo && "member_count" in chatInfo) {
4987
+ world.metadata.memberCount = chatInfo.member_count;
4988
+ }
4989
+ } catch (countError) {
4990
+ logger3.warn(`Could not get member count for chat ${chatId}: ${countError}`);
4991
+ }
4701
4992
  } catch (error) {
4702
- logger3.error(
4703
- `Error leaving unauthorized group ${currentGroupId}:`,
4704
- error
4705
- );
4993
+ logger3.warn(`Could not fetch administrators for chat ${chatId}: ${error}`);
4706
4994
  }
4707
- return false;
4708
4995
  }
4709
- return true;
4996
+ const worldPayload = {
4997
+ runtime: this.runtime,
4998
+ world,
4999
+ rooms: [room],
5000
+ entities: users,
5001
+ source: "telegram"
5002
+ };
5003
+ const telegramWorldPayload = {
5004
+ ...worldPayload,
5005
+ chat
5006
+ };
5007
+ this.runtime.emitEvent(
5008
+ EventTypes2.WORLD_JOINED,
5009
+ worldPayload
5010
+ );
5011
+ this.runtime.emitEvent(
5012
+ "TELEGRAM_WORLD_JOINED" /* WORLD_JOINED */,
5013
+ telegramWorldPayload
5014
+ );
5015
+ if (chat.type === "group" || chat.type === "supergroup") {
5016
+ this.setupEntityTracking(chat.id);
5017
+ }
4710
5018
  }
5019
+ /**
5020
+ * Sets up message and reaction handlers for the bot.
5021
+ *
5022
+ * @private
5023
+ * @returns {void}
5024
+ */
4711
5025
  setupMessageHandlers() {
4712
5026
  this.bot.on("message", async (ctx) => {
4713
5027
  try {
@@ -4726,6 +5040,120 @@ var TelegramService = class _TelegramService extends Service {
4726
5040
  }
4727
5041
  });
4728
5042
  }
5043
+ /**
5044
+ * Sets up tracking for new entities in a group chat to sync them as entities
5045
+ * @param {number} chatId - The Telegram chat ID to track entities for
5046
+ */
5047
+ setupEntityTracking(chatId) {
5048
+ const syncedEntityIds = /* @__PURE__ */ new Set();
5049
+ this.bot.on("message", async (ctx) => {
5050
+ if (!ctx.chat || ctx.chat.id !== chatId || !ctx.from) return;
5051
+ const entityId = ctx.from.id.toString();
5052
+ if (syncedEntityIds.has(entityId)) return;
5053
+ syncedEntityIds.add(entityId);
5054
+ const entityUuid = createUniqueUuid2(this.runtime, entityId);
5055
+ const worldId = createUniqueUuid2(this.runtime, chatId.toString());
5056
+ const chatIdStr = chatId.toString();
5057
+ try {
5058
+ await this.runtime.ensureConnection({
5059
+ entityId: entityUuid,
5060
+ roomId: createUniqueUuid2(this.runtime, chatIdStr),
5061
+ userName: ctx.from.username || ctx.from.first_name || "Unknown Entity",
5062
+ name: ctx.from.first_name || ctx.from.username || "Unknown Entity",
5063
+ source: "telegram",
5064
+ channelId: chatIdStr,
5065
+ serverId: chatIdStr,
5066
+ type: ChannelType2.GROUP,
5067
+ worldId
5068
+ });
5069
+ const entityJoinedPayload = {
5070
+ runtime: this.runtime,
5071
+ entityId: entityUuid,
5072
+ entity: {
5073
+ id: entityId,
5074
+ username: ctx.from.username || ctx.from.first_name || "Unknown Entity",
5075
+ displayName: ctx.from.first_name || ctx.from.username || "Unknown Entity"
5076
+ },
5077
+ worldId,
5078
+ source: "telegram",
5079
+ metadata: {
5080
+ joinedAt: Date.now()
5081
+ }
5082
+ };
5083
+ const telegramEntityJoinedPayload = {
5084
+ ...entityJoinedPayload,
5085
+ telegramUser: {
5086
+ id: ctx.from.id,
5087
+ username: ctx.from.username,
5088
+ first_name: ctx.from.first_name
5089
+ }
5090
+ };
5091
+ this.runtime.emitEvent(
5092
+ EventTypes2.ENTITY_JOINED,
5093
+ entityJoinedPayload
5094
+ );
5095
+ this.runtime.emitEvent(
5096
+ "TELEGRAM_ENTITY_JOINED" /* ENTITY_JOINED */,
5097
+ telegramEntityJoinedPayload
5098
+ );
5099
+ logger3.info(`Tracked new Telegram entity: ${ctx.from.username || ctx.from.first_name || entityId}`);
5100
+ } catch (error) {
5101
+ logger3.error(`Error syncing new Telegram entity ${entityId} from chat ${chatId}:`, error);
5102
+ }
5103
+ });
5104
+ this.bot.on("left_chat_member", async (ctx) => {
5105
+ var _a, _b;
5106
+ if (!((_a = ctx.message) == null ? void 0 : _a.left_chat_member) || ((_b = ctx.chat) == null ? void 0 : _b.id) !== chatId) return;
5107
+ const leftUser = ctx.message.left_chat_member;
5108
+ const entityId = createUniqueUuid2(this.runtime, leftUser.id.toString());
5109
+ const chatIdStr = chatId.toString();
5110
+ const worldId = createUniqueUuid2(this.runtime, chatIdStr);
5111
+ try {
5112
+ const entity = await this.runtime.getEntityById(entityId);
5113
+ if (entity) {
5114
+ entity.metadata = {
5115
+ ...entity.metadata,
5116
+ status: "INACTIVE",
5117
+ leftAt: Date.now()
5118
+ };
5119
+ await this.runtime.updateEntity(entity);
5120
+ const entityLeftPayload = {
5121
+ runtime: this.runtime,
5122
+ entityId,
5123
+ entity: {
5124
+ id: leftUser.id.toString(),
5125
+ username: leftUser.username || leftUser.first_name || "Unknown Entity",
5126
+ displayName: leftUser.first_name || leftUser.username || "Unknown Entity"
5127
+ },
5128
+ worldId,
5129
+ source: "telegram",
5130
+ metadata: {
5131
+ leftAt: Date.now()
5132
+ }
5133
+ };
5134
+ const telegramEntityLeftPayload = {
5135
+ ...entityLeftPayload,
5136
+ telegramUser: {
5137
+ id: leftUser.id,
5138
+ username: leftUser.username,
5139
+ first_name: leftUser.first_name
5140
+ }
5141
+ };
5142
+ this.runtime.emitEvent(
5143
+ EventTypes2.ENTITY_LEFT,
5144
+ entityLeftPayload
5145
+ );
5146
+ this.runtime.emitEvent(
5147
+ "TELEGRAM_ENTITY_LEFT" /* ENTITY_LEFT */,
5148
+ telegramEntityLeftPayload
5149
+ );
5150
+ logger3.info(`Entity ${leftUser.username || leftUser.first_name || leftUser.id} left chat ${chatId}`);
5151
+ }
5152
+ } catch (error) {
5153
+ logger3.error(`Error handling Telegram entity leaving chat ${chatId}:`, error);
5154
+ }
5155
+ });
5156
+ }
4729
5157
  };
4730
5158
  var telegramPlugin = {
4731
5159
  name: TELEGRAM_SERVICE_NAME,