@overpod/mcp-telegram 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -444,7 +444,7 @@ server.tool("telegram-get-chat-members", "Get members of a Telegram group or cha
444
444
  return { content: [{ type: "text", text: `Error: ${e.message}` }] };
445
445
  }
446
446
  });
447
- server.tool("telegram-get-profile", "Get detailed profile info of a Telegram user", {
447
+ server.tool("telegram-get-profile", "Get detailed profile info of a Telegram user including bio, birthday, business info and more", {
448
448
  userId: z.string().describe("User ID or username"),
449
449
  }, async ({ userId }) => {
450
450
  const err = await requireConnection();
@@ -459,7 +459,13 @@ server.tool("telegram-get-profile", "Get detailed profile info of a Telegram use
459
459
  ...(profile.phone ? [`Phone: +${profile.phone}`] : []),
460
460
  ...(profile.bio ? [`Bio: ${profile.bio}`] : []),
461
461
  `Photo: ${profile.photo ? "yes" : "no"}`,
462
+ ...(profile.premium ? ["Premium: yes"] : []),
462
463
  ...(profile.lastSeen ? [`Last seen: ${profile.lastSeen}`] : []),
464
+ ...(profile.birthday ? [`Birthday: ${profile.birthday}`] : []),
465
+ ...(profile.commonChatsCount ? [`Common chats: ${profile.commonChatsCount}`] : []),
466
+ ...(profile.personalChannelId ? [`Personal channel ID: ${profile.personalChannelId}`] : []),
467
+ ...(profile.businessLocation ? [`Business location: ${profile.businessLocation}`] : []),
468
+ ...(profile.businessWorkHours ? [`Business hours timezone: ${profile.businessWorkHours}`] : []),
463
469
  ];
464
470
  return { content: [{ type: "text", text: lines.join("\n") }] };
465
471
  }
@@ -467,6 +473,39 @@ server.tool("telegram-get-profile", "Get detailed profile info of a Telegram use
467
473
  return { content: [{ type: "text", text: `Error: ${e.message}` }] };
468
474
  }
469
475
  });
476
+ server.tool("telegram-get-profile-photo", "Download profile photo of a Telegram user, group, or channel. Returns inline image or saves to file", {
477
+ entityId: z.string().describe("User/Chat/Channel ID or username"),
478
+ savePath: z.string().optional().describe("Absolute path to save file. If omitted, returns inline base64 image"),
479
+ size: z
480
+ .enum(["small", "big"])
481
+ .optional()
482
+ .describe("Photo size: 'small' (160x160) or 'big' (640x640). Default: big"),
483
+ }, async ({ entityId, savePath, size }) => {
484
+ const err = await requireConnection();
485
+ if (err)
486
+ return { content: [{ type: "text", text: err }] };
487
+ try {
488
+ const result = await telegram.downloadProfilePhoto(entityId, {
489
+ isBig: size !== "small",
490
+ savePath,
491
+ });
492
+ if (!result) {
493
+ return { content: [{ type: "text", text: "No profile photo found" }] };
494
+ }
495
+ if ("filePath" in result) {
496
+ return { content: [{ type: "text", text: `Downloaded to: ${result.filePath}` }] };
497
+ }
498
+ return {
499
+ content: [
500
+ { type: "image", data: result.buffer.toString("base64"), mimeType: result.mimeType },
501
+ { type: "text", text: `Profile photo (${(result.buffer.length / 1024).toFixed(0)} KB, ${result.mimeType})` },
502
+ ],
503
+ };
504
+ }
505
+ catch (e) {
506
+ return { content: [{ type: "text", text: `Error: ${e.message}` }] };
507
+ }
508
+ });
470
509
  server.tool("telegram-join-chat", "Join a Telegram group or channel by username or invite link", {
471
510
  target: z.string().describe("Username (@group), link (t.me/group), or invite link (t.me/+xxx)"),
472
511
  }, async ({ target }) => {
@@ -166,7 +166,24 @@ export declare class TelegramService {
166
166
  bio?: string;
167
167
  photo: boolean;
168
168
  lastSeen?: string;
169
+ premium?: boolean;
170
+ birthday?: string;
171
+ commonChatsCount?: number;
172
+ personalChannelId?: string;
173
+ businessWorkHours?: string;
174
+ businessLocation?: string;
169
175
  }>;
176
+ downloadProfilePhoto(entityId: string, options?: {
177
+ isBig?: boolean;
178
+ savePath?: string;
179
+ }): Promise<{
180
+ buffer: Buffer;
181
+ mimeType: string;
182
+ } | {
183
+ filePath: string;
184
+ } | null>;
185
+ /** Detect MIME type from buffer magic bytes */
186
+ private detectMimeFromBuffer;
170
187
  sendReaction(chatId: string, messageId: number, emoji?: string): Promise<void>;
171
188
  sendScheduledMessage(chatId: string, text: string, scheduleDate: number, replyTo?: number, parseMode?: "md" | "html"): Promise<void>;
172
189
  createPoll(chatId: string, question: string, answers: string[], options?: {
@@ -819,7 +819,8 @@ export class TelegramService {
819
819
  throw new Error("Entity is not a user");
820
820
  const inputEntity = await this.client.getInputEntity(userId);
821
821
  const fullResult = await this.client.invoke(new Api.users.GetFullUser({ id: inputEntity }));
822
- const bio = fullResult.fullUser.about ?? undefined;
822
+ const full = fullResult.fullUser;
823
+ const bio = full.about ?? undefined;
823
824
  const parts = [entity.firstName, entity.lastName].filter(Boolean);
824
825
  let lastSeen;
825
826
  if (entity.status instanceof Api.UserStatusOnline) {
@@ -837,6 +838,23 @@ export class TelegramService {
837
838
  else if (entity.status instanceof Api.UserStatusLastMonth) {
838
839
  lastSeen = "last month";
839
840
  }
841
+ let birthday;
842
+ if (full.birthday) {
843
+ const b = full.birthday;
844
+ birthday = b.year
845
+ ? `${b.year}-${String(b.month).padStart(2, "0")}-${String(b.day).padStart(2, "0")}`
846
+ : `${String(b.month).padStart(2, "0")}-${String(b.day).padStart(2, "0")}`;
847
+ }
848
+ let businessWorkHours;
849
+ if (full.businessWorkHours) {
850
+ const wh = full.businessWorkHours;
851
+ businessWorkHours = wh.timezoneId ?? "configured";
852
+ }
853
+ let businessLocation;
854
+ if (full.businessLocation) {
855
+ const loc = full.businessLocation;
856
+ businessLocation = loc.address ?? "configured";
857
+ }
840
858
  return {
841
859
  id: entity.id.toString(),
842
860
  name: parts.join(" ") || "Unknown",
@@ -845,8 +863,42 @@ export class TelegramService {
845
863
  bio,
846
864
  photo: !!entity.photo,
847
865
  lastSeen,
866
+ premium: entity.premium || undefined,
867
+ birthday,
868
+ commonChatsCount: full.commonChatsCount || undefined,
869
+ personalChannelId: full.personalChannelId ? full.personalChannelId.toString() : undefined,
870
+ businessWorkHours,
871
+ businessLocation,
848
872
  };
849
873
  }
874
+ async downloadProfilePhoto(entityId, options) {
875
+ if (!this.client || !this.connected)
876
+ throw new Error("Not connected");
877
+ const entity = await this.client.getEntity(entityId);
878
+ const buffer = (await this.client.downloadProfilePhoto(entity, {
879
+ isBig: options?.isBig !== false,
880
+ }));
881
+ if (!buffer || buffer.length === 0)
882
+ return null;
883
+ const mimeType = this.detectMimeFromBuffer(buffer);
884
+ if (options?.savePath) {
885
+ await writeFile(options.savePath, buffer);
886
+ return { filePath: options.savePath };
887
+ }
888
+ return { buffer, mimeType };
889
+ }
890
+ /** Detect MIME type from buffer magic bytes */
891
+ detectMimeFromBuffer(buffer) {
892
+ if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff)
893
+ return "image/jpeg";
894
+ if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47)
895
+ return "image/png";
896
+ if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46)
897
+ return "image/gif";
898
+ if (buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46)
899
+ return "image/webp";
900
+ return "image/jpeg"; // Telegram profile photos are almost always JPEG
901
+ }
850
902
  async sendReaction(chatId, messageId, emoji) {
851
903
  if (!this.client || !this.connected)
852
904
  throw new Error("Not connected");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overpod/mcp-telegram",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "MCP server for Telegram userbot — messages, media, reactions, polls & more. Built on GramJS/MTProto.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",