@overpod/mcp-telegram 1.9.0 → 1.10.1
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 +44 -5
- package/dist/telegram-client.d.ts +17 -0
- package/dist/telegram-client.js +53 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -165,7 +165,7 @@ server.tool("telegram-read-messages", "Read recent messages from a Telegram chat
|
|
|
165
165
|
try {
|
|
166
166
|
const messages = await telegram.getMessages(chatId, limit, offsetId, minDate, maxDate);
|
|
167
167
|
const text = messages
|
|
168
|
-
.map((m) => `[${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}`)
|
|
168
|
+
.map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}`)
|
|
169
169
|
.join("\n\n");
|
|
170
170
|
return { content: [{ type: "text", text: text || "No messages" }] };
|
|
171
171
|
}
|
|
@@ -203,7 +203,7 @@ server.tool("telegram-search-global", "Search messages globally across all publi
|
|
|
203
203
|
try {
|
|
204
204
|
const messages = await telegram.searchGlobal(query, limit, minDate, maxDate);
|
|
205
205
|
const text = messages
|
|
206
|
-
.map((m) => `[${m.date}] [${m.chat.type === "channel" ? "C" : m.chat.type === "group" ? "G" : "P"} ${m.chat.name}${m.chat.username ? ` @${m.chat.username}` : ""}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}`)
|
|
206
|
+
.map((m) => `[#${m.id}] [${m.date}] [${m.chat.type === "channel" ? "C" : m.chat.type === "group" ? "G" : "P"} ${m.chat.name}${m.chat.username ? ` @${m.chat.username}` : ""}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}`)
|
|
207
207
|
.join("\n\n");
|
|
208
208
|
return { content: [{ type: "text", text: text || "No messages found" }] };
|
|
209
209
|
}
|
|
@@ -224,7 +224,7 @@ server.tool("telegram-search-messages", "Search messages in a Telegram chat by t
|
|
|
224
224
|
try {
|
|
225
225
|
const messages = await telegram.searchMessages(chatId, query, limit, minDate, maxDate);
|
|
226
226
|
const text = messages
|
|
227
|
-
.map((m) => `[${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}`)
|
|
227
|
+
.map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}`)
|
|
228
228
|
.join("\n\n");
|
|
229
229
|
return { content: [{ type: "text", text: text || "No messages found" }] };
|
|
230
230
|
}
|
|
@@ -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 }) => {
|
|
@@ -664,7 +703,7 @@ server.tool("telegram-read-topic-messages", "Read messages from a specific forum
|
|
|
664
703
|
try {
|
|
665
704
|
const messages = await telegram.getTopicMessages(chatId, topicId, limit, offsetId);
|
|
666
705
|
const text = messages
|
|
667
|
-
.map((m) => `[${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}`)
|
|
706
|
+
.map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}`)
|
|
668
707
|
.join("\n\n");
|
|
669
708
|
return { content: [{ type: "text", text: text || "No messages in this topic" }] };
|
|
670
709
|
}
|
|
@@ -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?: {
|
package/dist/telegram-client.js
CHANGED
|
@@ -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
|
|
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