@overpod/mcp-telegram 1.24.1 → 1.26.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/CHANGELOG.md +67 -1
- package/README.md +45 -13
- package/dist/__tests__/admin-log.test.d.ts +1 -0
- package/dist/__tests__/admin-log.test.js +41 -0
- package/dist/__tests__/approve-join-request.test.d.ts +1 -0
- package/dist/__tests__/approve-join-request.test.js +107 -0
- package/dist/__tests__/boosts.test.d.ts +1 -0
- package/dist/__tests__/boosts.test.js +310 -0
- package/dist/__tests__/broadcast-stats.test.d.ts +1 -0
- package/dist/__tests__/broadcast-stats.test.js +172 -0
- package/dist/__tests__/business-chat-links.test.d.ts +1 -0
- package/dist/__tests__/business-chat-links.test.js +102 -0
- package/dist/__tests__/get-message-buttons.test.d.ts +1 -0
- package/dist/__tests__/get-message-buttons.test.js +122 -0
- package/dist/__tests__/group-calls.test.d.ts +1 -0
- package/dist/__tests__/group-calls.test.js +503 -0
- package/dist/__tests__/inline-query-send.test.d.ts +1 -0
- package/dist/__tests__/inline-query-send.test.js +94 -0
- package/dist/__tests__/inline-query.test.d.ts +1 -0
- package/dist/__tests__/inline-query.test.js +115 -0
- package/dist/__tests__/megagroup-stats.test.d.ts +1 -0
- package/dist/__tests__/megagroup-stats.test.js +166 -0
- package/dist/__tests__/press-button.test.d.ts +1 -0
- package/dist/__tests__/press-button.test.js +123 -0
- package/dist/__tests__/quick-replies.test.d.ts +1 -0
- package/dist/__tests__/quick-replies.test.js +245 -0
- package/dist/__tests__/reactions.test.d.ts +1 -0
- package/dist/__tests__/reactions.test.js +23 -0
- package/dist/__tests__/set-chat-permissions-merge.test.d.ts +1 -0
- package/dist/__tests__/set-chat-permissions-merge.test.js +107 -0
- package/dist/__tests__/set-chat-reactions.test.d.ts +1 -0
- package/dist/__tests__/set-chat-reactions.test.js +129 -0
- package/dist/__tests__/stars-status.test.d.ts +1 -0
- package/dist/__tests__/stars-status.test.js +205 -0
- package/dist/__tests__/stars-transactions.test.d.ts +1 -0
- package/dist/__tests__/stars-transactions.test.js +82 -0
- package/dist/__tests__/stories.test.d.ts +1 -0
- package/dist/__tests__/stories.test.js +361 -0
- package/dist/__tests__/toggle-anti-spam.test.d.ts +1 -0
- package/dist/__tests__/toggle-anti-spam.test.js +80 -0
- package/dist/__tests__/toggle-channel-signatures.test.d.ts +1 -0
- package/dist/__tests__/toggle-channel-signatures.test.js +80 -0
- package/dist/__tests__/toggle-forum-mode.test.d.ts +1 -0
- package/dist/__tests__/toggle-forum-mode.test.js +80 -0
- package/dist/__tests__/toggle-prehistory-hidden.test.d.ts +1 -0
- package/dist/__tests__/toggle-prehistory-hidden.test.js +80 -0
- package/dist/__tests__/updates.test.d.ts +1 -0
- package/dist/__tests__/updates.test.js +221 -0
- package/dist/rate-limiter.d.ts +8 -2
- package/dist/rate-limiter.js +15 -8
- package/dist/telegram-client.d.ts +711 -2
- package/dist/telegram-client.js +2167 -99
- package/dist/tools/account.js +108 -0
- package/dist/tools/boosts.d.ts +3 -0
- package/dist/tools/boosts.js +65 -0
- package/dist/tools/chats.js +388 -1
- package/dist/tools/group-calls.d.ts +4 -0
- package/dist/tools/group-calls.js +77 -0
- package/dist/tools/index.js +10 -0
- package/dist/tools/media.js +120 -1
- package/dist/tools/messages.js +379 -0
- package/dist/tools/quick-replies.d.ts +4 -0
- package/dist/tools/quick-replies.js +58 -0
- package/dist/tools/reactions.js +102 -1
- package/dist/tools/stars.d.ts +4 -0
- package/dist/tools/stars.js +71 -0
- package/dist/tools/stories.d.ts +3 -0
- package/dist/tools/stories.js +107 -0
- package/package.json +1 -1
package/dist/tools/account.js
CHANGED
|
@@ -258,6 +258,98 @@ export function registerAccountTools(server, telegram) {
|
|
|
258
258
|
return fail(e);
|
|
259
259
|
}
|
|
260
260
|
});
|
|
261
|
+
server.registerTool("telegram-save-draft", {
|
|
262
|
+
description: "Save or clear a message draft for a chat. Pass empty text to clear the draft. Optional replyTo sets the message being replied to",
|
|
263
|
+
inputSchema: {
|
|
264
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
265
|
+
text: z.string().describe("Draft text. Empty string clears the draft"),
|
|
266
|
+
replyTo: z.number().int().positive().optional().describe("Message ID this draft replies to"),
|
|
267
|
+
},
|
|
268
|
+
annotations: WRITE,
|
|
269
|
+
}, async ({ chatId, text, replyTo }) => {
|
|
270
|
+
const err = await requireConnection(telegram);
|
|
271
|
+
if (err)
|
|
272
|
+
return fail(new Error(err));
|
|
273
|
+
try {
|
|
274
|
+
await telegram.saveDraft(chatId, text, replyTo);
|
|
275
|
+
return ok(text === "" ? `Draft cleared for ${chatId}` : `Draft saved for ${chatId}`);
|
|
276
|
+
}
|
|
277
|
+
catch (e) {
|
|
278
|
+
return fail(e);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
server.registerTool("telegram-get-drafts", {
|
|
282
|
+
description: "Get all saved message drafts across chats",
|
|
283
|
+
inputSchema: {},
|
|
284
|
+
annotations: READ_ONLY,
|
|
285
|
+
}, async () => {
|
|
286
|
+
const err = await requireConnection(telegram);
|
|
287
|
+
if (err)
|
|
288
|
+
return fail(new Error(err));
|
|
289
|
+
try {
|
|
290
|
+
const drafts = await telegram.getAllDrafts();
|
|
291
|
+
if (drafts.length === 0)
|
|
292
|
+
return ok("No drafts");
|
|
293
|
+
const text = drafts.map((d) => `[${d.chatId}] ${d.chatTitle} (${d.date})\n ${d.text}`).join("\n\n");
|
|
294
|
+
return ok(sanitize(text));
|
|
295
|
+
}
|
|
296
|
+
catch (e) {
|
|
297
|
+
return fail(e);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
server.registerTool("telegram-clear-drafts", {
|
|
301
|
+
description: "Delete saved message drafts. Pass chatId to clear the draft for a single chat. Without chatId, clears drafts in ALL chats — requires confirmAllChats: true",
|
|
302
|
+
inputSchema: {
|
|
303
|
+
chatId: z.string().optional().describe("Chat ID or username. If provided, clears draft only for this chat"),
|
|
304
|
+
confirmAllChats: z
|
|
305
|
+
.boolean()
|
|
306
|
+
.optional()
|
|
307
|
+
.describe("Must be true to wipe drafts across ALL chats when chatId is omitted"),
|
|
308
|
+
},
|
|
309
|
+
annotations: DESTRUCTIVE,
|
|
310
|
+
}, async ({ chatId, confirmAllChats }) => {
|
|
311
|
+
const err = await requireConnection(telegram);
|
|
312
|
+
if (err)
|
|
313
|
+
return fail(new Error(err));
|
|
314
|
+
try {
|
|
315
|
+
if (chatId !== undefined) {
|
|
316
|
+
if (confirmAllChats) {
|
|
317
|
+
return fail(new Error("Pass either chatId or confirmAllChats=true, not both"));
|
|
318
|
+
}
|
|
319
|
+
await telegram.saveDraft(chatId, "");
|
|
320
|
+
return ok(`Draft cleared for ${chatId}`);
|
|
321
|
+
}
|
|
322
|
+
if (!confirmAllChats) {
|
|
323
|
+
return fail(new Error("Refusing to clear drafts in ALL chats without explicit confirmation. Pass chatId for a single chat, or confirmAllChats=true to wipe all drafts"));
|
|
324
|
+
}
|
|
325
|
+
await telegram.clearAllDrafts();
|
|
326
|
+
return ok("All drafts cleared");
|
|
327
|
+
}
|
|
328
|
+
catch (e) {
|
|
329
|
+
return fail(e);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
server.registerTool("telegram-get-saved-dialogs", {
|
|
333
|
+
description: "Get Saved Messages dialogs (Telegram's per-sender grouping of messages forwarded to your Saved Messages)",
|
|
334
|
+
inputSchema: {
|
|
335
|
+
limit: z.number().int().positive().default(20).describe("Max dialogs to return"),
|
|
336
|
+
},
|
|
337
|
+
annotations: READ_ONLY,
|
|
338
|
+
}, async ({ limit }) => {
|
|
339
|
+
const err = await requireConnection(telegram);
|
|
340
|
+
if (err)
|
|
341
|
+
return fail(new Error(err));
|
|
342
|
+
try {
|
|
343
|
+
const dialogs = await telegram.getSavedDialogs(limit);
|
|
344
|
+
if (dialogs.length === 0)
|
|
345
|
+
return ok("No saved dialogs");
|
|
346
|
+
const text = dialogs.map((d) => `[${d.peerId}] ${d.peerTitle} — last msg #${d.lastMsgId}`).join("\n");
|
|
347
|
+
return ok(sanitize(text));
|
|
348
|
+
}
|
|
349
|
+
catch (e) {
|
|
350
|
+
return fail(e);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
261
353
|
server.registerTool("telegram-revoke-invite-link", {
|
|
262
354
|
description: "Revoke an invite link for a group or channel",
|
|
263
355
|
inputSchema: {
|
|
@@ -277,4 +369,20 @@ export function registerAccountTools(server, telegram) {
|
|
|
277
369
|
return fail(e);
|
|
278
370
|
}
|
|
279
371
|
});
|
|
372
|
+
server.registerTool("telegram-get-business-chat-links", {
|
|
373
|
+
description: "List Telegram Business chat links configured for the account (account.GetBusinessChatLinks). Each entry includes the t.me/... link, the prefilled message, optional title (admin-facing label), views count, and entityCount (number of formatting entities in the message). Requires a Telegram Business-enabled account — returns an empty list when the account has none configured. Read-only.",
|
|
374
|
+
inputSchema: {},
|
|
375
|
+
annotations: READ_ONLY,
|
|
376
|
+
}, async () => {
|
|
377
|
+
const err = await requireConnection(telegram);
|
|
378
|
+
if (err)
|
|
379
|
+
return fail(new Error(err));
|
|
380
|
+
try {
|
|
381
|
+
const result = await telegram.getBusinessChatLinks();
|
|
382
|
+
return ok(sanitize(JSON.stringify(result)));
|
|
383
|
+
}
|
|
384
|
+
catch (e) {
|
|
385
|
+
return fail(e);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
280
388
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { fail, ok, READ_ONLY, requireConnection, sanitize } from "./shared.js";
|
|
3
|
+
export function registerBoostTools(server, telegram) {
|
|
4
|
+
server.registerTool("telegram-get-my-boosts", {
|
|
5
|
+
description: "List the user's premium boost slots (premium.GetMyBoosts). Each entry includes slot index, the peer it currently boosts (if any), the date the boost was applied, expiration timestamp, and cooldownUntilDate (when a slot can be reassigned). Premium users have multiple slots; non-Premium users typically have a single slot. Read-only.",
|
|
6
|
+
inputSchema: {},
|
|
7
|
+
annotations: READ_ONLY,
|
|
8
|
+
}, async () => {
|
|
9
|
+
const err = await requireConnection(telegram);
|
|
10
|
+
if (err)
|
|
11
|
+
return fail(new Error(err));
|
|
12
|
+
try {
|
|
13
|
+
const result = await telegram.getMyBoosts();
|
|
14
|
+
return ok(sanitize(JSON.stringify(result)));
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
return fail(e);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
server.registerTool("telegram-get-boosts-status", {
|
|
21
|
+
description: "Fetch the boost status of a channel/supergroup (premium.GetBoostsStatus). Returns current boost level, total boosts, progress to next level (currentLevelBoosts/nextLevelBoosts), giftBoosts, premiumAudience ratio, public boostUrl, and whether the current user is boosting (myBoost + myBoostSlots). Also includes any prepaidGiveaways attached to the chat. Read-only.",
|
|
22
|
+
inputSchema: {
|
|
23
|
+
chat: z.string().describe("Channel or supergroup to query — id, @username, or display name fragment"),
|
|
24
|
+
},
|
|
25
|
+
annotations: READ_ONLY,
|
|
26
|
+
}, async ({ chat }) => {
|
|
27
|
+
const err = await requireConnection(telegram);
|
|
28
|
+
if (err)
|
|
29
|
+
return fail(new Error(err));
|
|
30
|
+
try {
|
|
31
|
+
const result = await telegram.getBoostsStatus(chat);
|
|
32
|
+
return ok(sanitize(JSON.stringify(result)));
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
return fail(e);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
server.registerTool("telegram-get-boosts-list", {
|
|
39
|
+
description: "List the boosts applied to a channel/supergroup (premium.GetBoostsList). Returns paginated boost entries with id, userId (or undefined for anonymous gift boosts), date, expires, flags (gift, giveaway, unclaimed), optional giveawayMsgId, usedGiftSlug, multiplier, and stars. Requires channel admin permissions. Supports pagination via nextOffset and an optional gifts filter to show only gift boosts. Read-only.",
|
|
40
|
+
inputSchema: {
|
|
41
|
+
chat: z.string().describe("Channel or supergroup to query — id, @username, or display name fragment"),
|
|
42
|
+
gifts: z.boolean().optional().describe("If true, return only gift boosts"),
|
|
43
|
+
offset: z.string().optional().describe("Pagination cursor returned as nextOffset from the previous call"),
|
|
44
|
+
limit: z
|
|
45
|
+
.number()
|
|
46
|
+
.int()
|
|
47
|
+
.min(1)
|
|
48
|
+
.max(100)
|
|
49
|
+
.optional()
|
|
50
|
+
.describe("Max boosts to return per page (default 50, max 100)"),
|
|
51
|
+
},
|
|
52
|
+
annotations: READ_ONLY,
|
|
53
|
+
}, async ({ chat, gifts, offset, limit }) => {
|
|
54
|
+
const err = await requireConnection(telegram);
|
|
55
|
+
if (err)
|
|
56
|
+
return fail(new Error(err));
|
|
57
|
+
try {
|
|
58
|
+
const result = await telegram.getBoostsList(chat, { gifts, offset, limit });
|
|
59
|
+
return ok(sanitize(JSON.stringify(result)));
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
return fail(e);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
package/dist/tools/chats.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { fail, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
|
|
2
|
+
import { DESTRUCTIVE, fail, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
|
|
3
3
|
export function registerChatTools(server, telegram) {
|
|
4
4
|
server.registerTool("telegram-list-chats", {
|
|
5
5
|
description: "List Telegram chats with unread counts, type indicators, and contact status",
|
|
@@ -309,6 +309,89 @@ export function registerChatTools(server, telegram) {
|
|
|
309
309
|
return fail(e);
|
|
310
310
|
}
|
|
311
311
|
});
|
|
312
|
+
server.registerTool("telegram-archive-chat", {
|
|
313
|
+
description: "Archive or unarchive a Telegram dialog (moves to/from the Archive folder)",
|
|
314
|
+
inputSchema: {
|
|
315
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
316
|
+
archive: z.boolean().describe("true to archive, false to unarchive"),
|
|
317
|
+
},
|
|
318
|
+
annotations: WRITE,
|
|
319
|
+
}, async ({ chatId, archive }) => {
|
|
320
|
+
const err = await requireConnection(telegram);
|
|
321
|
+
if (err)
|
|
322
|
+
return fail(new Error(err));
|
|
323
|
+
try {
|
|
324
|
+
await telegram.archiveChat(chatId, archive);
|
|
325
|
+
return ok(`${archive ? "Archived" : "Unarchived"} ${chatId}`);
|
|
326
|
+
}
|
|
327
|
+
catch (e) {
|
|
328
|
+
return fail(e);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
server.registerTool("telegram-pin-chat", {
|
|
332
|
+
description: "Pin or unpin a Telegram dialog in the dialog list",
|
|
333
|
+
inputSchema: {
|
|
334
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
335
|
+
pin: z.boolean().describe("true to pin, false to unpin"),
|
|
336
|
+
},
|
|
337
|
+
annotations: WRITE,
|
|
338
|
+
}, async ({ chatId, pin }) => {
|
|
339
|
+
const err = await requireConnection(telegram);
|
|
340
|
+
if (err)
|
|
341
|
+
return fail(new Error(err));
|
|
342
|
+
try {
|
|
343
|
+
await telegram.pinDialog(chatId, pin);
|
|
344
|
+
return ok(`${pin ? "Pinned" : "Unpinned"} ${chatId}`);
|
|
345
|
+
}
|
|
346
|
+
catch (e) {
|
|
347
|
+
return fail(e);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
server.registerTool("telegram-mark-dialog-unread", {
|
|
351
|
+
description: "Mark a Telegram dialog as unread (or clear the unread mark)",
|
|
352
|
+
inputSchema: {
|
|
353
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
354
|
+
unread: z.boolean().describe("true to mark as unread, false to clear the mark"),
|
|
355
|
+
},
|
|
356
|
+
annotations: WRITE,
|
|
357
|
+
}, async ({ chatId, unread }) => {
|
|
358
|
+
const err = await requireConnection(telegram);
|
|
359
|
+
if (err)
|
|
360
|
+
return fail(new Error(err));
|
|
361
|
+
try {
|
|
362
|
+
await telegram.markDialogUnread(chatId, unread);
|
|
363
|
+
return ok(`Marked ${chatId} as ${unread ? "unread" : "read"}`);
|
|
364
|
+
}
|
|
365
|
+
catch (e) {
|
|
366
|
+
return fail(e);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
server.registerTool("telegram-get-admin-log", {
|
|
370
|
+
description: "Get the admin action log (recent event history) of a supergroup or channel. Includes bans, edits, pins, and role changes",
|
|
371
|
+
inputSchema: {
|
|
372
|
+
chatId: z.string().describe("Chat ID or username (supergroup or channel)"),
|
|
373
|
+
limit: z.number().int().min(1).max(100).default(20).describe("Number of events to return (1-100)"),
|
|
374
|
+
q: z.string().optional().describe("Optional text filter for events"),
|
|
375
|
+
},
|
|
376
|
+
annotations: READ_ONLY,
|
|
377
|
+
}, async ({ chatId, limit, q }) => {
|
|
378
|
+
const err = await requireConnection(telegram);
|
|
379
|
+
if (err)
|
|
380
|
+
return fail(new Error(err));
|
|
381
|
+
try {
|
|
382
|
+
const events = await telegram.getAdminLog(chatId, limit, q);
|
|
383
|
+
const text = events
|
|
384
|
+
.map((e) => {
|
|
385
|
+
const details = e.details ? ` — ${e.details}` : "";
|
|
386
|
+
return `[#${e.id}] [${e.date}] ${e.userName}: ${e.action}${details}`;
|
|
387
|
+
})
|
|
388
|
+
.join("\n");
|
|
389
|
+
return ok(sanitize(text) || "No admin log events");
|
|
390
|
+
}
|
|
391
|
+
catch (e) {
|
|
392
|
+
return fail(e);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
312
395
|
server.registerTool("telegram-remove-admin", {
|
|
313
396
|
description: "Remove admin rights from a user in a supergroup or channel",
|
|
314
397
|
inputSchema: {
|
|
@@ -328,4 +411,308 @@ export function registerChatTools(server, telegram) {
|
|
|
328
411
|
return fail(e);
|
|
329
412
|
}
|
|
330
413
|
});
|
|
414
|
+
server.registerTool("telegram-set-chat-permissions", {
|
|
415
|
+
description: "Set the default permissions for all non-admin members of a group, supergroup, or channel. Omitted flags keep their current state; true = allowed, false = denied",
|
|
416
|
+
inputSchema: {
|
|
417
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
418
|
+
sendMessages: z.boolean().optional().describe("Allow sending text messages"),
|
|
419
|
+
sendMedia: z.boolean().optional().describe("Allow sending photos/videos/documents"),
|
|
420
|
+
sendStickers: z.boolean().optional().describe("Allow sending stickers"),
|
|
421
|
+
sendGifs: z.boolean().optional().describe("Allow sending GIFs"),
|
|
422
|
+
sendPolls: z.boolean().optional().describe("Allow sending polls"),
|
|
423
|
+
sendInline: z.boolean().optional().describe("Allow inline bot usage"),
|
|
424
|
+
embedLinks: z.boolean().optional().describe("Allow link previews"),
|
|
425
|
+
changeInfo: z.boolean().optional().describe("Allow changing chat info (title, photo, description)"),
|
|
426
|
+
inviteUsers: z.boolean().optional().describe("Allow inviting new members"),
|
|
427
|
+
pinMessages: z.boolean().optional().describe("Allow pinning messages"),
|
|
428
|
+
},
|
|
429
|
+
annotations: DESTRUCTIVE,
|
|
430
|
+
}, async ({ chatId, ...permissions }) => {
|
|
431
|
+
const err = await requireConnection(telegram);
|
|
432
|
+
if (err)
|
|
433
|
+
return fail(new Error(err));
|
|
434
|
+
try {
|
|
435
|
+
await telegram.setChatPermissions(chatId, permissions);
|
|
436
|
+
const changed = Object.entries(permissions)
|
|
437
|
+
.filter(([, v]) => v !== undefined)
|
|
438
|
+
.map(([k, v]) => `${k}=${v ? "allow" : "deny"}`);
|
|
439
|
+
return ok(changed.length > 0
|
|
440
|
+
? `Updated default permissions for ${chatId}: ${changed.join(", ")}`
|
|
441
|
+
: `No permission changes for ${chatId}`);
|
|
442
|
+
}
|
|
443
|
+
catch (e) {
|
|
444
|
+
return fail(e);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
server.registerTool("telegram-set-slow-mode", {
|
|
448
|
+
description: "Set slow mode for a supergroup (minimum interval between messages per user). Allowed values: 0, 10, 30, 60, 300, 900, 3600 seconds (0 disables slow mode)",
|
|
449
|
+
inputSchema: {
|
|
450
|
+
chatId: z.string().describe("Chat ID or username (supergroup)"),
|
|
451
|
+
seconds: z
|
|
452
|
+
.union([
|
|
453
|
+
z.literal(0),
|
|
454
|
+
z.literal(10),
|
|
455
|
+
z.literal(30),
|
|
456
|
+
z.literal(60),
|
|
457
|
+
z.literal(300),
|
|
458
|
+
z.literal(900),
|
|
459
|
+
z.literal(3600),
|
|
460
|
+
])
|
|
461
|
+
.describe("Interval in seconds: 0 (off), 10, 30, 60, 300, 900, or 3600"),
|
|
462
|
+
},
|
|
463
|
+
annotations: WRITE,
|
|
464
|
+
}, async ({ chatId, seconds }) => {
|
|
465
|
+
const err = await requireConnection(telegram);
|
|
466
|
+
if (err)
|
|
467
|
+
return fail(new Error(err));
|
|
468
|
+
try {
|
|
469
|
+
await telegram.setSlowMode(chatId, seconds);
|
|
470
|
+
return ok(seconds === 0 ? `Disabled slow mode in ${chatId}` : `Set slow mode to ${seconds}s in ${chatId}`);
|
|
471
|
+
}
|
|
472
|
+
catch (e) {
|
|
473
|
+
return fail(e);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
server.registerTool("telegram-toggle-channel-signatures", {
|
|
477
|
+
description: "Enable or disable author signatures on broadcast channel posts. Channel admin required; not supported for supergroups",
|
|
478
|
+
inputSchema: {
|
|
479
|
+
chatId: z.string().describe("Channel ID or username"),
|
|
480
|
+
enabled: z.boolean().describe("true to enable author signatures, false to disable"),
|
|
481
|
+
},
|
|
482
|
+
annotations: WRITE,
|
|
483
|
+
}, async ({ chatId, enabled }) => {
|
|
484
|
+
const err = await requireConnection(telegram);
|
|
485
|
+
if (err)
|
|
486
|
+
return fail(new Error(err));
|
|
487
|
+
try {
|
|
488
|
+
await telegram.toggleChannelSignatures(chatId, enabled);
|
|
489
|
+
return ok(JSON.stringify({ ok: true, signaturesEnabled: enabled }));
|
|
490
|
+
}
|
|
491
|
+
catch (e) {
|
|
492
|
+
return fail(e);
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
server.registerTool("telegram-toggle-anti-spam", {
|
|
496
|
+
description: "Enable or disable aggressive anti-spam filtering in a supergroup. Supergroup only (not broadcast channels); requires admin with ban_users permission",
|
|
497
|
+
inputSchema: {
|
|
498
|
+
chatId: z.string().describe("Supergroup ID or username"),
|
|
499
|
+
enabled: z.boolean().describe("true to enable aggressive anti-spam, false to disable"),
|
|
500
|
+
},
|
|
501
|
+
annotations: WRITE,
|
|
502
|
+
}, async ({ chatId, enabled }) => {
|
|
503
|
+
const err = await requireConnection(telegram);
|
|
504
|
+
if (err)
|
|
505
|
+
return fail(new Error(err));
|
|
506
|
+
try {
|
|
507
|
+
await telegram.toggleAntiSpam(chatId, enabled);
|
|
508
|
+
return ok(`${enabled ? "Enabled" : "Disabled"} aggressive anti-spam in ${chatId}`);
|
|
509
|
+
}
|
|
510
|
+
catch (e) {
|
|
511
|
+
return fail(e);
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
server.registerTool("telegram-toggle-forum-mode", {
|
|
515
|
+
description: "Enable or disable forum/topics mode in a supergroup. Supergroup only; requires creator or admin. " +
|
|
516
|
+
"WARNING: disabling removes ALL existing topics — pass confirm=true to proceed with disable",
|
|
517
|
+
inputSchema: {
|
|
518
|
+
chatId: z.string().describe("Supergroup ID or username"),
|
|
519
|
+
enabled: z.boolean().describe("true to enable forum mode, false to disable"),
|
|
520
|
+
confirm: z
|
|
521
|
+
.boolean()
|
|
522
|
+
.optional()
|
|
523
|
+
.describe("Must be true when disabling (enabled=false) — disabling deletes all existing topics"),
|
|
524
|
+
},
|
|
525
|
+
annotations: DESTRUCTIVE,
|
|
526
|
+
}, async ({ chatId, enabled, confirm }) => {
|
|
527
|
+
const err = await requireConnection(telegram);
|
|
528
|
+
if (err)
|
|
529
|
+
return fail(new Error(err));
|
|
530
|
+
if (!enabled && confirm !== true) {
|
|
531
|
+
return fail(new Error("Disabling forum mode deletes all existing topics. Pass confirm=true to proceed with this destructive action."));
|
|
532
|
+
}
|
|
533
|
+
try {
|
|
534
|
+
await telegram.toggleForumMode(chatId, enabled);
|
|
535
|
+
return ok(`${enabled ? "Enabled" : "Disabled"} forum mode in ${chatId}`);
|
|
536
|
+
}
|
|
537
|
+
catch (e) {
|
|
538
|
+
return fail(e);
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
server.registerTool("telegram-toggle-prehistory-hidden", {
|
|
542
|
+
description: "Toggle pre-history visibility for new members in a supergroup. When hidden=true, new joiners cannot see messages posted before they joined. Supergroup only; requires admin",
|
|
543
|
+
inputSchema: {
|
|
544
|
+
chatId: z.string().describe("Supergroup ID or username"),
|
|
545
|
+
hidden: z.boolean().describe("true to hide prior history from new members, false to make it visible"),
|
|
546
|
+
},
|
|
547
|
+
annotations: WRITE,
|
|
548
|
+
}, async ({ chatId, hidden }) => {
|
|
549
|
+
const err = await requireConnection(telegram);
|
|
550
|
+
if (err)
|
|
551
|
+
return fail(new Error(err));
|
|
552
|
+
try {
|
|
553
|
+
await telegram.togglePrehistoryHidden(chatId, hidden);
|
|
554
|
+
return ok(`${hidden ? "Hid" : "Revealed"} prehistory for new members in ${chatId}`);
|
|
555
|
+
}
|
|
556
|
+
catch (e) {
|
|
557
|
+
return fail(e);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
server.registerTool("telegram-approve-join-request", {
|
|
561
|
+
description: "Approve or deny a pending join request for a supergroup or channel (basic groups are not supported). Admin with invite_users permission required",
|
|
562
|
+
inputSchema: {
|
|
563
|
+
chatId: z.string().describe("Chat ID or username where the join request is pending"),
|
|
564
|
+
userId: z.string().describe("User ID or username of the requesting user"),
|
|
565
|
+
approved: z.boolean().describe("true to approve the join request, false to deny"),
|
|
566
|
+
},
|
|
567
|
+
annotations: WRITE,
|
|
568
|
+
}, async ({ chatId, userId, approved }) => {
|
|
569
|
+
const err = await requireConnection(telegram);
|
|
570
|
+
if (err)
|
|
571
|
+
return fail(new Error(err));
|
|
572
|
+
try {
|
|
573
|
+
await telegram.approveChatJoinRequest(chatId, userId, approved);
|
|
574
|
+
return ok(`${approved ? "Approved" : "Denied"} join request from ${userId} in ${chatId}`);
|
|
575
|
+
}
|
|
576
|
+
catch (e) {
|
|
577
|
+
return fail(e);
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
server.registerTool("telegram-create-topic", {
|
|
581
|
+
description: "Create a new forum topic in a forum-enabled supergroup",
|
|
582
|
+
inputSchema: {
|
|
583
|
+
chatId: z.string().describe("Chat ID or username of the forum supergroup"),
|
|
584
|
+
title: z.string().describe("Topic title"),
|
|
585
|
+
iconColor: z
|
|
586
|
+
.union([
|
|
587
|
+
z.literal(7322096),
|
|
588
|
+
z.literal(16766590),
|
|
589
|
+
z.literal(13338331),
|
|
590
|
+
z.literal(9367192),
|
|
591
|
+
z.literal(16749490),
|
|
592
|
+
z.literal(16225862),
|
|
593
|
+
])
|
|
594
|
+
.optional()
|
|
595
|
+
.describe("Optional icon color (one of 7322096, 16766590, 13338331, 9367192, 16749490, 16225862)"),
|
|
596
|
+
iconEmojiId: z
|
|
597
|
+
.string()
|
|
598
|
+
.regex(/^\d+$/, "iconEmojiId must be a numeric string")
|
|
599
|
+
.optional()
|
|
600
|
+
.describe("Optional custom emoji document ID for the icon (numeric string)"),
|
|
601
|
+
},
|
|
602
|
+
annotations: WRITE,
|
|
603
|
+
}, async ({ chatId, title, iconColor, iconEmojiId }) => {
|
|
604
|
+
const err = await requireConnection(telegram);
|
|
605
|
+
if (err)
|
|
606
|
+
return fail(new Error(err));
|
|
607
|
+
try {
|
|
608
|
+
const topic = await telegram.createForumTopic(chatId, title, iconColor, iconEmojiId);
|
|
609
|
+
return ok(sanitize(`Created topic "${topic.title}" (id=${topic.id}) in ${chatId}`));
|
|
610
|
+
}
|
|
611
|
+
catch (e) {
|
|
612
|
+
return fail(e);
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
server.registerTool("telegram-edit-topic", {
|
|
616
|
+
description: "Edit a forum topic — rename, change icon emoji, open/close, or show/hide",
|
|
617
|
+
inputSchema: {
|
|
618
|
+
chatId: z.string().describe("Chat ID or username of the forum supergroup"),
|
|
619
|
+
topicId: z.number().describe("Topic ID (get from telegram-list-topics)"),
|
|
620
|
+
title: z.string().optional().describe("New topic title"),
|
|
621
|
+
iconEmojiId: z
|
|
622
|
+
.string()
|
|
623
|
+
.regex(/^\d+$/, "iconEmojiId must be a numeric string")
|
|
624
|
+
.optional()
|
|
625
|
+
.describe("New custom emoji document ID for the icon (numeric string)"),
|
|
626
|
+
closed: z.boolean().optional().describe("Close (true) or reopen (false) the topic"),
|
|
627
|
+
hidden: z.boolean().optional().describe("Hide (true) or show (false) the General topic"),
|
|
628
|
+
},
|
|
629
|
+
annotations: WRITE,
|
|
630
|
+
}, async ({ chatId, topicId, title, iconEmojiId, closed, hidden }) => {
|
|
631
|
+
const err = await requireConnection(telegram);
|
|
632
|
+
if (err)
|
|
633
|
+
return fail(new Error(err));
|
|
634
|
+
try {
|
|
635
|
+
await telegram.editForumTopic(chatId, topicId, { title, iconEmojiId, closed, hidden });
|
|
636
|
+
const changes = [];
|
|
637
|
+
if (title !== undefined)
|
|
638
|
+
changes.push(`title="${title}"`);
|
|
639
|
+
if (iconEmojiId !== undefined)
|
|
640
|
+
changes.push(`iconEmojiId=${iconEmojiId}`);
|
|
641
|
+
if (closed !== undefined)
|
|
642
|
+
changes.push(closed ? "closed" : "reopened");
|
|
643
|
+
if (hidden !== undefined)
|
|
644
|
+
changes.push(hidden ? "hidden" : "shown");
|
|
645
|
+
return ok(sanitize(changes.length > 0
|
|
646
|
+
? `Updated topic ${topicId} in ${chatId}: ${changes.join(", ")}`
|
|
647
|
+
: `No changes for topic ${topicId} in ${chatId}`));
|
|
648
|
+
}
|
|
649
|
+
catch (e) {
|
|
650
|
+
return fail(e);
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
server.registerTool("telegram-get-broadcast-stats", {
|
|
654
|
+
description: "Get broadcast channel statistics: followers, views/shares/reactions per post & story, notification percent, recent post interactions. Broadcast channels only (use telegram-get-megagroup-stats for supergroups). Admin rights required; some channels may require Telegram Premium to expose stats",
|
|
655
|
+
inputSchema: {
|
|
656
|
+
chatId: z.string().describe("Broadcast channel ID or username"),
|
|
657
|
+
includeGraphs: z
|
|
658
|
+
.boolean()
|
|
659
|
+
.default(false)
|
|
660
|
+
.describe("Include raw graph data for each series (growth, followers, interactions, etc.). Default false — returns only aggregate numbers + metadata"),
|
|
661
|
+
dark: z.boolean().default(false).describe("Prefer dark-theme palette when Telegram renders graphs"),
|
|
662
|
+
},
|
|
663
|
+
annotations: READ_ONLY,
|
|
664
|
+
}, async ({ chatId, includeGraphs, dark }) => {
|
|
665
|
+
const err = await requireConnection(telegram);
|
|
666
|
+
if (err)
|
|
667
|
+
return fail(new Error(err));
|
|
668
|
+
try {
|
|
669
|
+
const stats = await telegram.getBroadcastStats(chatId, { dark, includeGraphs });
|
|
670
|
+
return ok(sanitize(JSON.stringify(stats)));
|
|
671
|
+
}
|
|
672
|
+
catch (e) {
|
|
673
|
+
return fail(e);
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
server.registerTool("telegram-get-megagroup-stats", {
|
|
677
|
+
description: "Get supergroup statistics: members, messages, viewers, posters (current vs previous period), top posters/admins/inviters. Supergroups only (use telegram-get-broadcast-stats for broadcast channels). Admin rights required. Telegram rate-limits this endpoint to roughly 1 request per 30 minutes per channel — expect FLOOD_WAIT on rapid repeat calls",
|
|
678
|
+
inputSchema: {
|
|
679
|
+
chatId: z.string().describe("Supergroup ID or username"),
|
|
680
|
+
includeGraphs: z
|
|
681
|
+
.boolean()
|
|
682
|
+
.default(false)
|
|
683
|
+
.describe("Include raw graph data for each series (growth, members, messages, actions, top hours, weekdays, etc.). Default false — returns only aggregate numbers + top lists"),
|
|
684
|
+
dark: z.boolean().default(false).describe("Prefer dark-theme palette when Telegram renders graphs"),
|
|
685
|
+
},
|
|
686
|
+
annotations: READ_ONLY,
|
|
687
|
+
}, async ({ chatId, includeGraphs, dark }) => {
|
|
688
|
+
const err = await requireConnection(telegram);
|
|
689
|
+
if (err)
|
|
690
|
+
return fail(new Error(err));
|
|
691
|
+
try {
|
|
692
|
+
const stats = await telegram.getMegagroupStats(chatId, { dark, includeGraphs });
|
|
693
|
+
return ok(sanitize(JSON.stringify(stats)));
|
|
694
|
+
}
|
|
695
|
+
catch (e) {
|
|
696
|
+
return fail(e);
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
server.registerTool("telegram-delete-topic", {
|
|
700
|
+
description: "Delete a forum topic and all its message history",
|
|
701
|
+
inputSchema: {
|
|
702
|
+
chatId: z.string().describe("Chat ID or username of the forum supergroup"),
|
|
703
|
+
topicId: z.number().describe("Topic ID to delete"),
|
|
704
|
+
},
|
|
705
|
+
annotations: DESTRUCTIVE,
|
|
706
|
+
}, async ({ chatId, topicId }) => {
|
|
707
|
+
const err = await requireConnection(telegram);
|
|
708
|
+
if (err)
|
|
709
|
+
return fail(new Error(err));
|
|
710
|
+
try {
|
|
711
|
+
await telegram.deleteForumTopic(chatId, topicId);
|
|
712
|
+
return ok(`Deleted topic ${topicId} in ${chatId}`);
|
|
713
|
+
}
|
|
714
|
+
catch (e) {
|
|
715
|
+
return fail(e);
|
|
716
|
+
}
|
|
717
|
+
});
|
|
331
718
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { TelegramService } from "../telegram-client.js";
|
|
3
|
+
export declare function isGroupCallsEnabled(): boolean;
|
|
4
|
+
export declare function registerGroupCallTools(server: McpServer, telegram: TelegramService): void;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { fail, ok, READ_ONLY, requireConnection, sanitize } from "./shared.js";
|
|
3
|
+
export function isGroupCallsEnabled() {
|
|
4
|
+
return process.env.MCP_TELEGRAM_ENABLE_GROUP_CALLS === "1";
|
|
5
|
+
}
|
|
6
|
+
export function registerGroupCallTools(server, telegram) {
|
|
7
|
+
if (!isGroupCallsEnabled())
|
|
8
|
+
return;
|
|
9
|
+
server.registerTool("telegram-get-group-call", {
|
|
10
|
+
description: "Fetch metadata + an optional initial slice of participants for the active group call (voice/video chat) attached to a chat (phone.GetGroupCall). Returns call info (id, accessHash, participantsCount, title, scheduleDate, recordStartDate, streamDcId, flags) plus a participant slice (peer, date, muted/left/self flags, source, volume, video/presentation indicators) and participantsNextOffset. Pass limit:0 (default) for metadata only. Opt-in: register only when MCP_TELEGRAM_ENABLE_GROUP_CALLS=1. Read-only.",
|
|
11
|
+
inputSchema: {
|
|
12
|
+
chat: z
|
|
13
|
+
.string()
|
|
14
|
+
.describe("Group/supergroup/channel that currently has an active group call — id, @username, or display name fragment"),
|
|
15
|
+
limit: z
|
|
16
|
+
.number()
|
|
17
|
+
.int()
|
|
18
|
+
.min(0)
|
|
19
|
+
.max(500)
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Max participants to include (default 0 — metadata only; use telegram-get-group-call-participants for pagination)"),
|
|
22
|
+
},
|
|
23
|
+
annotations: READ_ONLY,
|
|
24
|
+
}, async ({ chat, limit }) => {
|
|
25
|
+
const err = await requireConnection(telegram);
|
|
26
|
+
if (err)
|
|
27
|
+
return fail(new Error(err));
|
|
28
|
+
try {
|
|
29
|
+
const result = await telegram.getGroupCall(chat, { limit });
|
|
30
|
+
return ok(sanitize(JSON.stringify(result)));
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
return fail(e);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
server.registerTool("telegram-get-group-call-participants", {
|
|
37
|
+
description: "List participants of the active group call (voice/video chat) attached to a chat with pagination (phone.GetGroupParticipants). Looks up the chat's current InputGroupCall automatically, then returns {count, participants[], nextOffset?, version}. Each participant includes peer, date, source, volume, muted/self/left/videoJoined flags, raise-hand rating, about text, and hasVideo/hasPresentation indicators. Pass offset from a prior call's nextOffset to paginate; pass ids (user/channel peers) or sources to filter to specific participants. Opt-in: register only when MCP_TELEGRAM_ENABLE_GROUP_CALLS=1. Read-only.",
|
|
38
|
+
inputSchema: {
|
|
39
|
+
chat: z
|
|
40
|
+
.string()
|
|
41
|
+
.describe("Group/supergroup/channel that currently has an active group call — id, @username, or display name fragment"),
|
|
42
|
+
ids: z
|
|
43
|
+
.array(z.string())
|
|
44
|
+
.max(100)
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Optional list of participant peer ids/@usernames to filter (max 100)"),
|
|
47
|
+
sources: z
|
|
48
|
+
.array(z.number().int())
|
|
49
|
+
.max(100)
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("Optional list of numeric source ids (audio SSRC) to filter participants"),
|
|
52
|
+
offset: z
|
|
53
|
+
.string()
|
|
54
|
+
.optional()
|
|
55
|
+
.describe("Pagination cursor from a previous response's nextOffset; omit for the first page"),
|
|
56
|
+
limit: z
|
|
57
|
+
.number()
|
|
58
|
+
.int()
|
|
59
|
+
.min(1)
|
|
60
|
+
.max(500)
|
|
61
|
+
.optional()
|
|
62
|
+
.describe("Max participants to return in this page (default 100)"),
|
|
63
|
+
},
|
|
64
|
+
annotations: READ_ONLY,
|
|
65
|
+
}, async ({ chat, ids, sources, offset, limit }) => {
|
|
66
|
+
const err = await requireConnection(telegram);
|
|
67
|
+
if (err)
|
|
68
|
+
return fail(new Error(err));
|
|
69
|
+
try {
|
|
70
|
+
const result = await telegram.getGroupCallParticipants(chat, { ids, sources, offset, limit });
|
|
71
|
+
return ok(sanitize(JSON.stringify(result)));
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
return fail(e);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|