@elizaos/plugin-telegram 1.0.0-alpha.4 → 1.0.0-alpha.41

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