@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/reactions.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { fail, ok, READ_ONLY, requireConnection, WRITE } from "./shared.js";
|
|
2
|
+
import { fail, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
|
|
3
3
|
export function registerReactionTools(server, telegram) {
|
|
4
4
|
server.registerTool("telegram-send-reaction", {
|
|
5
5
|
description: "Send emoji reaction(s) to a message. Supports multiple reactions and adding to existing ones. Omit emoji to remove all reactions",
|
|
@@ -60,4 +60,105 @@ export function registerReactionTools(server, telegram) {
|
|
|
60
60
|
return fail(e);
|
|
61
61
|
}
|
|
62
62
|
});
|
|
63
|
+
server.registerTool("telegram-set-default-reaction", {
|
|
64
|
+
description: "Set the default emoji reaction used in quick-reaction menus across Telegram",
|
|
65
|
+
inputSchema: {
|
|
66
|
+
emoji: z.string().min(1).max(8).describe("Emoji character (e.g. 👍 ❤️ 🔥)"),
|
|
67
|
+
},
|
|
68
|
+
annotations: WRITE,
|
|
69
|
+
}, async ({ emoji }) => {
|
|
70
|
+
const err = await requireConnection(telegram);
|
|
71
|
+
if (err)
|
|
72
|
+
return fail(new Error(err));
|
|
73
|
+
try {
|
|
74
|
+
await telegram.setDefaultReaction(emoji);
|
|
75
|
+
return ok(`Default reaction set to ${sanitize(emoji)}`);
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
return fail(e);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
server.registerTool("telegram-get-top-reactions", {
|
|
82
|
+
description: "Get the list of most popular emoji reactions available on Telegram",
|
|
83
|
+
inputSchema: {
|
|
84
|
+
limit: z.number().int().min(1).max(100).default(20).describe("Max number of reactions to return"),
|
|
85
|
+
},
|
|
86
|
+
annotations: READ_ONLY,
|
|
87
|
+
}, async ({ limit }) => {
|
|
88
|
+
const err = await requireConnection(telegram);
|
|
89
|
+
if (err)
|
|
90
|
+
return fail(new Error(err));
|
|
91
|
+
try {
|
|
92
|
+
const reactions = await telegram.getTopReactions(limit);
|
|
93
|
+
if (reactions.length === 0)
|
|
94
|
+
return ok("No top reactions available");
|
|
95
|
+
return ok(sanitize(reactions.map((r) => r.emoji).join(" ")));
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
return fail(e);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
server.registerTool("telegram-set-chat-reactions", {
|
|
102
|
+
description: "Set which reactions are available in a chat. type='all' allows all standard emoji (set allowCustom=true to also permit custom emoji for Premium users), type='some' restricts to a specific emoji list, type='none' disables reactions entirely. Requires admin",
|
|
103
|
+
inputSchema: {
|
|
104
|
+
chatId: z.string().describe("Chat ID or username (group, supergroup, or channel)"),
|
|
105
|
+
reactions: z
|
|
106
|
+
.discriminatedUnion("type", [
|
|
107
|
+
z.object({
|
|
108
|
+
type: z.literal("all"),
|
|
109
|
+
allowCustom: z
|
|
110
|
+
.boolean()
|
|
111
|
+
.optional()
|
|
112
|
+
.describe("If true, also allow custom emoji reactions (requires Premium users)"),
|
|
113
|
+
}),
|
|
114
|
+
z.object({
|
|
115
|
+
type: z.literal("some"),
|
|
116
|
+
emoji: z
|
|
117
|
+
.array(z.string().min(1).max(8))
|
|
118
|
+
.min(1)
|
|
119
|
+
.max(100)
|
|
120
|
+
.describe("List of allowed reaction emoji (e.g. ['👍','❤️','🔥'])"),
|
|
121
|
+
}),
|
|
122
|
+
z.object({ type: z.literal("none") }),
|
|
123
|
+
])
|
|
124
|
+
.describe("Reaction policy for the chat"),
|
|
125
|
+
},
|
|
126
|
+
annotations: WRITE,
|
|
127
|
+
}, async ({ chatId, reactions }) => {
|
|
128
|
+
const err = await requireConnection(telegram);
|
|
129
|
+
if (err)
|
|
130
|
+
return fail(new Error(err));
|
|
131
|
+
try {
|
|
132
|
+
await telegram.setChatAvailableReactions(chatId, reactions);
|
|
133
|
+
const summary = reactions.type === "all"
|
|
134
|
+
? `all reactions${reactions.allowCustom ? " (incl. custom)" : ""}`
|
|
135
|
+
: reactions.type === "none"
|
|
136
|
+
? "no reactions"
|
|
137
|
+
: `${reactions.emoji.length} reaction(s): ${sanitize(reactions.emoji.join(" "))}`;
|
|
138
|
+
return ok(`Set ${summary} for ${chatId}`);
|
|
139
|
+
}
|
|
140
|
+
catch (e) {
|
|
141
|
+
return fail(e);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
server.registerTool("telegram-get-recent-reactions", {
|
|
145
|
+
description: "Get the list of emoji reactions the current account used recently",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
limit: z.number().int().min(1).max(100).default(20).describe("Max number of reactions to return"),
|
|
148
|
+
},
|
|
149
|
+
annotations: READ_ONLY,
|
|
150
|
+
}, async ({ limit }) => {
|
|
151
|
+
const err = await requireConnection(telegram);
|
|
152
|
+
if (err)
|
|
153
|
+
return fail(new Error(err));
|
|
154
|
+
try {
|
|
155
|
+
const reactions = await telegram.getRecentReactions(limit);
|
|
156
|
+
if (reactions.length === 0)
|
|
157
|
+
return ok("No recent reactions");
|
|
158
|
+
return ok(sanitize(reactions.map((r) => r.emoji).join(" ")));
|
|
159
|
+
}
|
|
160
|
+
catch (e) {
|
|
161
|
+
return fail(e);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
63
164
|
}
|
|
@@ -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 isStarsEnabled(): boolean;
|
|
4
|
+
export declare function registerStarsTools(server: McpServer, telegram: TelegramService): void;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { fail, ok, READ_ONLY, requireConnection, sanitize } from "./shared.js";
|
|
3
|
+
export function isStarsEnabled() {
|
|
4
|
+
return process.env.MCP_TELEGRAM_ENABLE_STARS === "1";
|
|
5
|
+
}
|
|
6
|
+
export function registerStarsTools(server, telegram) {
|
|
7
|
+
if (!isStarsEnabled())
|
|
8
|
+
return;
|
|
9
|
+
server.registerTool("telegram-get-stars-status", {
|
|
10
|
+
description: "Fetch the current Telegram Stars balance and recent activity for a peer (payments.GetStarsStatus). Pass 'me' / '@me' (or the user's own id) to inspect your own wallet, or a bot/channel peer you own/administrate. Returns {balance:{amount,nanos}, subscriptions?[], subscriptionsNextOffset?, subscriptionsMissingBalance?, history?[], nextOffset?}. Each transaction has id, stars, date, peer (kind: appStore|playMarket|premiumBot|fragment|ads|api|peer|unsupported), and flags (refund/pending/failed/gift/reaction). Use telegram-get-stars-transactions for full paginated history. Opt-in: register only when MCP_TELEGRAM_ENABLE_STARS=1. Read-only.",
|
|
11
|
+
inputSchema: {
|
|
12
|
+
peer: z
|
|
13
|
+
.string()
|
|
14
|
+
.describe("Peer whose Stars wallet to inspect — 'me'/'@me' or user id for self, or @username/id for a bot/channel you control"),
|
|
15
|
+
},
|
|
16
|
+
annotations: READ_ONLY,
|
|
17
|
+
}, async ({ peer }) => {
|
|
18
|
+
const err = await requireConnection(telegram);
|
|
19
|
+
if (err)
|
|
20
|
+
return fail(new Error(err));
|
|
21
|
+
try {
|
|
22
|
+
const result = await telegram.getStarsStatus(peer);
|
|
23
|
+
return ok(sanitize(JSON.stringify(result)));
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
return fail(e);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
server.registerTool("telegram-get-stars-transactions", {
|
|
30
|
+
description: "Fetch a paginated Telegram Stars transaction history for a peer (payments.GetStarsTransactions). Pass 'me'/'@me' (or your own user id) for your wallet, or a bot/channel peer you own/administrate. Filters: inbound (credits only), outbound (debits only), ascending (chronological; default descending), subscriptionId (scope to a single subscription). Paginate with offset (cursor string from a prior response's nextOffset) and limit (default 50). Returns {balance, history[], nextOffset?, subscriptions?[], ...} — same shape as telegram-get-stars-status but focused on transactions. Opt-in: register only when MCP_TELEGRAM_ENABLE_STARS=1. Read-only.",
|
|
31
|
+
inputSchema: {
|
|
32
|
+
peer: z
|
|
33
|
+
.string()
|
|
34
|
+
.describe("Peer whose Stars transactions to fetch — 'me'/'@me' or user id for self, or @username/id for a bot/channel you control"),
|
|
35
|
+
inbound: z.boolean().optional().describe("If true, return only inbound (credit) transactions"),
|
|
36
|
+
outbound: z.boolean().optional().describe("If true, return only outbound (debit) transactions"),
|
|
37
|
+
ascending: z
|
|
38
|
+
.boolean()
|
|
39
|
+
.optional()
|
|
40
|
+
.describe("If true, return transactions in chronological (oldest first) order; default: newest first"),
|
|
41
|
+
subscriptionId: z.string().optional().describe("If set, filter transactions to a single subscription id"),
|
|
42
|
+
offset: z
|
|
43
|
+
.string()
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("Pagination cursor — pass the nextOffset from a previous response; empty/undefined for first page"),
|
|
46
|
+
limit: z.number().int().min(1).max(100).optional().describe("Max transactions per page (default 50, max 100)"),
|
|
47
|
+
},
|
|
48
|
+
annotations: READ_ONLY,
|
|
49
|
+
}, async ({ peer, inbound, outbound, ascending, subscriptionId, offset, limit }) => {
|
|
50
|
+
const err = await requireConnection(telegram);
|
|
51
|
+
if (err)
|
|
52
|
+
return fail(new Error(err));
|
|
53
|
+
if (inbound && outbound) {
|
|
54
|
+
return fail(new Error("inbound and outbound are mutually exclusive — provide at most one"));
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const result = await telegram.getStarsTransactions(peer, {
|
|
58
|
+
inbound,
|
|
59
|
+
outbound,
|
|
60
|
+
ascending,
|
|
61
|
+
subscriptionId,
|
|
62
|
+
offset,
|
|
63
|
+
limit,
|
|
64
|
+
});
|
|
65
|
+
return ok(sanitize(JSON.stringify(result)));
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
return fail(e);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { fail, ok, READ_ONLY, requireConnection, sanitize } from "./shared.js";
|
|
3
|
+
export function registerStoryTools(server, telegram) {
|
|
4
|
+
server.registerTool("telegram-get-all-stories", {
|
|
5
|
+
description: "Fetch active stories from contacts/channels the user follows. Pagination via 'next' + 'state' — pass the returned state back on the next call with next:true to load more. Use hidden:true to read stories from muted/archived peers. Returns compact story metadata (id, date, expireDate, caption, mediaType, counters) without raw media blobs.",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
next: z.boolean().optional().describe("Load the next page (use with state from a prior response)"),
|
|
8
|
+
hidden: z.boolean().optional().describe("Fetch stories from hidden/archived peers instead of the main feed"),
|
|
9
|
+
state: z.string().optional().describe("Pagination state token returned by a previous call"),
|
|
10
|
+
},
|
|
11
|
+
annotations: READ_ONLY,
|
|
12
|
+
}, async ({ next, hidden, state }) => {
|
|
13
|
+
const err = await requireConnection(telegram);
|
|
14
|
+
if (err)
|
|
15
|
+
return fail(new Error(err));
|
|
16
|
+
if (next === true && !state) {
|
|
17
|
+
return fail(new Error("'state' is required when 'next' is true — use the state token from a prior telegram-get-all-stories response"));
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const result = await telegram.getAllStories({ next, hidden, state });
|
|
21
|
+
return ok(sanitize(JSON.stringify(result)));
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
return fail(e);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
server.registerTool("telegram-get-peer-stories", {
|
|
28
|
+
description: "Fetch currently active stories posted by a specific peer (user/channel). Returns compact story metadata (id, date, expireDate, caption, mediaType, counters) with media type className only — no raw media blobs. Use telegram-download-media with the story id if you need media bytes.",
|
|
29
|
+
inputSchema: {
|
|
30
|
+
chat: z
|
|
31
|
+
.string()
|
|
32
|
+
.describe("Peer to fetch stories from — user/channel id, @username, phone, or display name fragment"),
|
|
33
|
+
},
|
|
34
|
+
annotations: READ_ONLY,
|
|
35
|
+
}, async ({ chat }) => {
|
|
36
|
+
const err = await requireConnection(telegram);
|
|
37
|
+
if (err)
|
|
38
|
+
return fail(new Error(err));
|
|
39
|
+
try {
|
|
40
|
+
const result = await telegram.getPeerStories(chat);
|
|
41
|
+
if (result === null)
|
|
42
|
+
return ok("No active stories found for the specified peer");
|
|
43
|
+
return ok(sanitize(JSON.stringify(result)));
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
return fail(e);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
server.registerTool("telegram-get-stories-by-id", {
|
|
50
|
+
description: "Fetch specific stories from a peer by their numeric IDs. Useful for retrieving archived/pinned stories outside the active feed. Returns compact story metadata and optional pinnedToTop list. Pass up to ~100 ids per request.",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
chat: z
|
|
53
|
+
.string()
|
|
54
|
+
.describe("Peer to fetch stories from — user/channel id, @username, phone, or display name fragment"),
|
|
55
|
+
ids: z.array(z.number().int().positive()).min(1).max(100).describe("Story IDs to fetch (1–100 per request)"),
|
|
56
|
+
},
|
|
57
|
+
annotations: READ_ONLY,
|
|
58
|
+
}, async ({ chat, ids }) => {
|
|
59
|
+
const err = await requireConnection(telegram);
|
|
60
|
+
if (err)
|
|
61
|
+
return fail(new Error(err));
|
|
62
|
+
try {
|
|
63
|
+
const result = await telegram.getStoriesById(chat, ids);
|
|
64
|
+
return ok(sanitize(JSON.stringify(result)));
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
return fail(e);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
server.registerTool("telegram-get-story-views", {
|
|
71
|
+
description: "List viewers of one of YOUR stories (stories.GetStoryViewsList). Returns per-viewer entries (user id, view date, their reaction emoji if any), plus totals (viewsCount, forwardsCount, reactionsCount) and nextOffset for pagination. Pass your own user id (numeric) or @username as the peer — this only works for stories you posted. Some accounts (non-Premium, old stories) may not get a full viewer list — a Premium hint is surfaced on typical errors.",
|
|
72
|
+
inputSchema: {
|
|
73
|
+
chat: z.string().describe("Peer owning the story — usually 'me' or your own user id/@username"),
|
|
74
|
+
storyId: z.number().int().positive().describe("Story ID to fetch viewers for"),
|
|
75
|
+
q: z.string().optional().describe("Filter viewers by name substring"),
|
|
76
|
+
justContacts: z.boolean().optional().describe("Return only contacts"),
|
|
77
|
+
reactionsFirst: z.boolean().optional().describe("Sort viewers who reacted first"),
|
|
78
|
+
forwardsFirst: z.boolean().optional().describe("Sort forwards/reposts first"),
|
|
79
|
+
offset: z.string().optional().describe("Pagination offset from a previous response's nextOffset"),
|
|
80
|
+
limit: z.number().int().min(1).max(100).optional().describe("Max viewers to return (default 50, max 100)"),
|
|
81
|
+
},
|
|
82
|
+
annotations: READ_ONLY,
|
|
83
|
+
}, async ({ chat, storyId, q, justContacts, reactionsFirst, forwardsFirst, offset, limit }) => {
|
|
84
|
+
const err = await requireConnection(telegram);
|
|
85
|
+
if (err)
|
|
86
|
+
return fail(new Error(err));
|
|
87
|
+
try {
|
|
88
|
+
const result = await telegram.getStoryViewsList(chat, {
|
|
89
|
+
id: storyId,
|
|
90
|
+
q,
|
|
91
|
+
justContacts,
|
|
92
|
+
reactionsFirst,
|
|
93
|
+
forwardsFirst,
|
|
94
|
+
offset,
|
|
95
|
+
limit,
|
|
96
|
+
});
|
|
97
|
+
return ok(sanitize(JSON.stringify(result)));
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
const msg = e.message ?? "";
|
|
101
|
+
if (/PREMIUM|PAYMENT_REQUIRED/i.test(msg)) {
|
|
102
|
+
return fail(new Error("story view stats may require Telegram Premium"));
|
|
103
|
+
}
|
|
104
|
+
return fail(e);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
package/package.json
CHANGED