@overpod/mcp-telegram 1.28.1 → 1.32.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 +153 -0
- package/README.md +21 -8
- package/dist/telegram-client.d.ts +265 -4
- package/dist/telegram-client.js +1111 -5
- package/dist/telegram-helpers.d.ts +111 -1
- package/dist/telegram-helpers.js +270 -0
- package/dist/tools/account.js +221 -6
- package/dist/tools/business.d.ts +3 -0
- package/dist/tools/business.js +333 -0
- package/dist/tools/fact-check.d.ts +3 -0
- package/dist/tools/fact-check.js +72 -0
- package/dist/tools/index.js +8 -0
- package/dist/tools/messages.js +183 -4
- package/dist/tools/reactions.js +62 -0
- package/dist/tools/send-media.d.ts +3 -0
- package/dist/tools/send-media.js +259 -0
- package/dist/tools/shared.d.ts +28 -0
- package/dist/tools/shared.js +59 -0
- package/dist/tools/stories.js +303 -1
- package/dist/tools/transcribe.d.ts +3 -0
- package/dist/tools/transcribe.js +75 -0
- package/package.json +1 -1
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { fail, ok, READ_ONLY, requireConnection, WRITE } from "./shared.js";
|
|
3
|
+
const AUDIENCE = z.enum(["all_new", "contacts_only", "non_contacts", "existing_only"]).default("all_new");
|
|
4
|
+
export function registerBusinessTools(server, telegram) {
|
|
5
|
+
server.registerTool("telegram-get-business-chat-links", {
|
|
6
|
+
description: "List Telegram Business chat links configured for the account. Each entry includes the t.me/... link, the prefilled message, optional title (admin-facing label), views count, and entityCount. Requires Telegram Business — returns empty list when none configured.",
|
|
7
|
+
inputSchema: {},
|
|
8
|
+
annotations: READ_ONLY,
|
|
9
|
+
}, async () => {
|
|
10
|
+
const err = await requireConnection(telegram);
|
|
11
|
+
if (err)
|
|
12
|
+
return fail(new Error(err));
|
|
13
|
+
try {
|
|
14
|
+
const result = await telegram.getBusinessChatLinks();
|
|
15
|
+
return ok(JSON.stringify(result));
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
return fail(e);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
server.registerTool("telegram-create-business-chat-link", {
|
|
22
|
+
description: "Create a Telegram Business chat link (t.me/m/... deep-link that opens a chat with you pre-filled with a message). Returns JSON with link, slug, message, title, and views. Requires Telegram Business subscription.",
|
|
23
|
+
inputSchema: {
|
|
24
|
+
message: z.string().min(1).max(4096).describe("Pre-filled message text shown to users who click the link"),
|
|
25
|
+
title: z.string().max(32).optional().describe("Admin-facing label (not visible to visitors, max 32 chars)"),
|
|
26
|
+
parseMode: z.enum(["md", "html"]).optional().describe("Format message as Markdown or HTML"),
|
|
27
|
+
},
|
|
28
|
+
annotations: WRITE,
|
|
29
|
+
}, async ({ message, title, parseMode }) => {
|
|
30
|
+
const err = await requireConnection(telegram);
|
|
31
|
+
if (err)
|
|
32
|
+
return fail(new Error(err));
|
|
33
|
+
try {
|
|
34
|
+
const result = await telegram.createBusinessChatLink({ message, title, parseMode });
|
|
35
|
+
return ok(JSON.stringify(result));
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
return fail(e);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
server.registerTool("telegram-edit-business-chat-link", {
|
|
42
|
+
description: "Edit an existing Telegram Business chat link by its slug (the trailing segment after t.me/m/). Returns JSON with updated fields. Requires Telegram Business subscription.",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
slug: z.string().min(1).describe("Link slug — the last path segment of t.me/m/<slug>"),
|
|
45
|
+
message: z.string().min(1).max(4096).describe("New pre-filled message text"),
|
|
46
|
+
title: z.string().max(32).optional().describe("New admin-facing label (max 32 chars)"),
|
|
47
|
+
parseMode: z.enum(["md", "html"]).optional().describe("Format message as Markdown or HTML"),
|
|
48
|
+
},
|
|
49
|
+
annotations: WRITE,
|
|
50
|
+
}, async ({ slug, message, title, parseMode }) => {
|
|
51
|
+
const err = await requireConnection(telegram);
|
|
52
|
+
if (err)
|
|
53
|
+
return fail(new Error(err));
|
|
54
|
+
try {
|
|
55
|
+
const result = await telegram.editBusinessChatLink({ slug, message, title, parseMode });
|
|
56
|
+
return ok(JSON.stringify(result));
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
return fail(e);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
server.registerTool("telegram-delete-business-chat-link", {
|
|
63
|
+
description: "Delete a Telegram Business chat link by its slug. Requires Telegram Business subscription.",
|
|
64
|
+
inputSchema: {
|
|
65
|
+
slug: z.string().min(1).describe("Link slug to delete (from t.me/m/<slug>)"),
|
|
66
|
+
},
|
|
67
|
+
annotations: WRITE,
|
|
68
|
+
}, async ({ slug }) => {
|
|
69
|
+
const err = await requireConnection(telegram);
|
|
70
|
+
if (err)
|
|
71
|
+
return fail(new Error(err));
|
|
72
|
+
try {
|
|
73
|
+
await telegram.deleteBusinessChatLink(slug);
|
|
74
|
+
return ok(`Business chat link '${slug}' deleted`);
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
return fail(e);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
server.registerTool("telegram-resolve-business-chat-link", {
|
|
81
|
+
description: "Resolve a Telegram Business chat link by slug to see whose chat it opens and the pre-filled message. Returns JSON with peerId, peerType, message, and entityCount.",
|
|
82
|
+
inputSchema: {
|
|
83
|
+
slug: z.string().min(1).describe("Link slug to resolve (from t.me/m/<slug>)"),
|
|
84
|
+
},
|
|
85
|
+
annotations: READ_ONLY,
|
|
86
|
+
}, async ({ slug }) => {
|
|
87
|
+
const err = await requireConnection(telegram);
|
|
88
|
+
if (err)
|
|
89
|
+
return fail(new Error(err));
|
|
90
|
+
try {
|
|
91
|
+
const result = await telegram.resolveBusinessChatLink(slug);
|
|
92
|
+
return ok(JSON.stringify(result));
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
return fail(e);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
server.registerTool("telegram-set-business-hours", {
|
|
99
|
+
description: "Set Telegram Business work hours — days and time ranges when your business is open. Requires Telegram Business subscription. Pass clear=true to disable the work hours display entirely.",
|
|
100
|
+
inputSchema: {
|
|
101
|
+
timezone: z
|
|
102
|
+
.string()
|
|
103
|
+
.optional()
|
|
104
|
+
.describe("IANA timezone ID (e.g. 'Europe/Moscow', 'America/New_York'). Required when setting schedule."),
|
|
105
|
+
openNow: z
|
|
106
|
+
.boolean()
|
|
107
|
+
.optional()
|
|
108
|
+
.describe("Manually override current open/closed status. Omit to derive from schedule."),
|
|
109
|
+
schedule: z
|
|
110
|
+
.array(z.object({
|
|
111
|
+
day: z.enum(["mon", "tue", "wed", "thu", "fri", "sat", "sun"]).describe("Day of week"),
|
|
112
|
+
openFrom: z
|
|
113
|
+
.string()
|
|
114
|
+
.regex(/^\d{2}:\d{2}$/)
|
|
115
|
+
.describe("Opening time in 24h HH:MM format"),
|
|
116
|
+
openTo: z
|
|
117
|
+
.string()
|
|
118
|
+
.regex(/^\d{2}:\d{2}$/)
|
|
119
|
+
.describe("Closing time in 24h HH:MM. Use '24:00' for end of day."),
|
|
120
|
+
}))
|
|
121
|
+
.optional()
|
|
122
|
+
.describe("Weekly schedule. Multiple ranges per day are allowed."),
|
|
123
|
+
clear: z.boolean().optional().describe("Pass true to remove business hours entirely"),
|
|
124
|
+
},
|
|
125
|
+
annotations: WRITE,
|
|
126
|
+
}, async ({ timezone, openNow, schedule, clear }) => {
|
|
127
|
+
const err = await requireConnection(telegram);
|
|
128
|
+
if (err)
|
|
129
|
+
return fail(new Error(err));
|
|
130
|
+
if (!clear && (!timezone || !schedule?.length)) {
|
|
131
|
+
return fail(new Error("timezone and schedule are required when not clearing"));
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
await telegram.setBusinessWorkHours({ timezone, openNow, schedule, clear });
|
|
135
|
+
if (clear)
|
|
136
|
+
return ok("Business hours cleared");
|
|
137
|
+
return ok(`Business hours set: ${schedule?.length} range(s) in ${timezone}`);
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
return fail(e);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
server.registerTool("telegram-set-business-location", {
|
|
144
|
+
description: "Set Telegram Business physical location (address + optional geo coordinates). Requires Telegram Business subscription. Pass clear=true to remove.",
|
|
145
|
+
inputSchema: {
|
|
146
|
+
address: z.string().min(1).max(512).optional().describe("Street address text"),
|
|
147
|
+
latitude: z.number().min(-90).max(90).optional().describe("Geo latitude (-90 to 90)"),
|
|
148
|
+
longitude: z.number().min(-180).max(180).optional().describe("Geo longitude (-180 to 180)"),
|
|
149
|
+
clear: z.boolean().optional().describe("Pass true to remove business location"),
|
|
150
|
+
},
|
|
151
|
+
annotations: WRITE,
|
|
152
|
+
}, async ({ address, latitude, longitude, clear }) => {
|
|
153
|
+
const err = await requireConnection(telegram);
|
|
154
|
+
if (err)
|
|
155
|
+
return fail(new Error(err));
|
|
156
|
+
if (!clear && !address) {
|
|
157
|
+
return fail(new Error("address is required when not clearing"));
|
|
158
|
+
}
|
|
159
|
+
if ((latitude === undefined) !== (longitude === undefined)) {
|
|
160
|
+
return fail(new Error("latitude and longitude must both be set or both omitted"));
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
await telegram.setBusinessLocation({ address, latitude, longitude, clear });
|
|
164
|
+
if (clear)
|
|
165
|
+
return ok("Business location cleared");
|
|
166
|
+
const geo = latitude !== undefined ? ` @ ${latitude},${longitude}` : "";
|
|
167
|
+
return ok(`Business location set: ${address}${geo}`);
|
|
168
|
+
}
|
|
169
|
+
catch (e) {
|
|
170
|
+
return fail(e);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
server.registerTool("telegram-set-business-greeting", {
|
|
174
|
+
description: "Set Telegram Business greeting message — auto-reply sent to new conversations using a quick reply shortcut as template. Requires Telegram Business subscription. Pass clear=true to disable.",
|
|
175
|
+
inputSchema: {
|
|
176
|
+
shortcutId: z
|
|
177
|
+
.number()
|
|
178
|
+
.int()
|
|
179
|
+
.positive()
|
|
180
|
+
.optional()
|
|
181
|
+
.describe("Quick reply shortcut ID (from telegram-get-quick-replies) used as the greeting template"),
|
|
182
|
+
audience: AUDIENCE.describe("Who receives the greeting: all_new (new contacts+non-contacts), contacts_only, non_contacts, existing_only"),
|
|
183
|
+
includeUsers: z.array(z.string()).optional().describe("Additional usernames/IDs to always include"),
|
|
184
|
+
excludeUsers: z
|
|
185
|
+
.array(z.string())
|
|
186
|
+
.optional()
|
|
187
|
+
.describe("Usernames/IDs to exclude — overrides audience. Cannot be combined with includeUsers."),
|
|
188
|
+
noActivityDays: z
|
|
189
|
+
.number()
|
|
190
|
+
.int()
|
|
191
|
+
.min(1)
|
|
192
|
+
.max(365)
|
|
193
|
+
.default(7)
|
|
194
|
+
.describe("Send greeting if user has been inactive for N days"),
|
|
195
|
+
clear: z.boolean().optional().describe("Pass true to disable greeting message"),
|
|
196
|
+
},
|
|
197
|
+
annotations: WRITE,
|
|
198
|
+
}, async ({ shortcutId, audience, includeUsers, excludeUsers, noActivityDays, clear }) => {
|
|
199
|
+
const err = await requireConnection(telegram);
|
|
200
|
+
if (err)
|
|
201
|
+
return fail(new Error(err));
|
|
202
|
+
if (!clear && shortcutId === undefined) {
|
|
203
|
+
return fail(new Error("shortcutId is required when not clearing"));
|
|
204
|
+
}
|
|
205
|
+
if (includeUsers?.length && excludeUsers?.length) {
|
|
206
|
+
return fail(new Error("includeUsers and excludeUsers cannot both be set"));
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
await telegram.setBusinessGreeting({
|
|
210
|
+
shortcutId,
|
|
211
|
+
audience,
|
|
212
|
+
includeUsers,
|
|
213
|
+
excludeUsers,
|
|
214
|
+
noActivityDays,
|
|
215
|
+
clear,
|
|
216
|
+
});
|
|
217
|
+
if (clear)
|
|
218
|
+
return ok("Business greeting cleared");
|
|
219
|
+
return ok(`Business greeting set: shortcut=${shortcutId} audience=${audience} noActivityDays=${noActivityDays}`);
|
|
220
|
+
}
|
|
221
|
+
catch (e) {
|
|
222
|
+
return fail(e);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
server.registerTool("telegram-set-business-away", {
|
|
226
|
+
description: "Set Telegram Business away message — auto-reply when you are offline or outside work hours. Uses a quick reply shortcut as template. Requires Telegram Business subscription. Pass clear=true to disable.",
|
|
227
|
+
inputSchema: {
|
|
228
|
+
shortcutId: z
|
|
229
|
+
.number()
|
|
230
|
+
.int()
|
|
231
|
+
.positive()
|
|
232
|
+
.optional()
|
|
233
|
+
.describe("Quick reply shortcut ID used as the away message template"),
|
|
234
|
+
schedule: z
|
|
235
|
+
.enum(["always", "outside_hours", "custom"])
|
|
236
|
+
.default("outside_hours")
|
|
237
|
+
.describe("When to send: always (any time offline), outside_hours (based on business hours), custom (time range)"),
|
|
238
|
+
customFrom: z.number().int().positive().optional().describe("For schedule=custom: Unix timestamp range start"),
|
|
239
|
+
customTo: z.number().int().positive().optional().describe("For schedule=custom: Unix timestamp range end"),
|
|
240
|
+
offlineOnly: z
|
|
241
|
+
.boolean()
|
|
242
|
+
.default(true)
|
|
243
|
+
.describe("Send only when you appear offline (true) or regardless of online status (false)"),
|
|
244
|
+
audience: AUDIENCE.describe("Who receives the away message"),
|
|
245
|
+
includeUsers: z.array(z.string()).optional(),
|
|
246
|
+
excludeUsers: z.array(z.string()).optional(),
|
|
247
|
+
clear: z.boolean().optional().describe("Pass true to disable away message"),
|
|
248
|
+
},
|
|
249
|
+
annotations: WRITE,
|
|
250
|
+
}, async ({ shortcutId, schedule, customFrom, customTo, offlineOnly, audience, includeUsers, excludeUsers, clear, }) => {
|
|
251
|
+
const err = await requireConnection(telegram);
|
|
252
|
+
if (err)
|
|
253
|
+
return fail(new Error(err));
|
|
254
|
+
if (!clear && shortcutId === undefined) {
|
|
255
|
+
return fail(new Error("shortcutId is required when not clearing"));
|
|
256
|
+
}
|
|
257
|
+
if (schedule === "custom" && (customFrom === undefined || customTo === undefined)) {
|
|
258
|
+
return fail(new Error("customFrom and customTo are required when schedule=custom"));
|
|
259
|
+
}
|
|
260
|
+
if (includeUsers?.length && excludeUsers?.length) {
|
|
261
|
+
return fail(new Error("includeUsers and excludeUsers cannot both be set"));
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
await telegram.setBusinessAway({
|
|
265
|
+
shortcutId,
|
|
266
|
+
schedule,
|
|
267
|
+
customFrom,
|
|
268
|
+
customTo,
|
|
269
|
+
offlineOnly,
|
|
270
|
+
audience,
|
|
271
|
+
includeUsers,
|
|
272
|
+
excludeUsers,
|
|
273
|
+
clear,
|
|
274
|
+
});
|
|
275
|
+
if (clear)
|
|
276
|
+
return ok("Business away message cleared");
|
|
277
|
+
return ok(`Business away message set: shortcut=${shortcutId} schedule=${schedule}`);
|
|
278
|
+
}
|
|
279
|
+
catch (e) {
|
|
280
|
+
return fail(e);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
server.registerTool("telegram-set-business-intro", {
|
|
284
|
+
description: "Set Telegram Business intro card — title and description shown to new users opening your chat, with an optional sticker. Requires Telegram Business subscription. Pass clear=true to remove.",
|
|
285
|
+
inputSchema: {
|
|
286
|
+
title: z.string().min(1).max(32).optional().describe("Intro title (max 32 chars)"),
|
|
287
|
+
description: z.string().min(1).max(70).optional().describe("Intro description (max 70 chars)"),
|
|
288
|
+
stickerId: z
|
|
289
|
+
.string()
|
|
290
|
+
.optional()
|
|
291
|
+
.describe("Sticker document ID (stringified long) — optional illustrative sticker. Requires stickerAccessHash and stickerFileReference."),
|
|
292
|
+
stickerAccessHash: z
|
|
293
|
+
.string()
|
|
294
|
+
.optional()
|
|
295
|
+
.describe("Access hash of the sticker document (required with stickerId)"),
|
|
296
|
+
stickerFileReference: z
|
|
297
|
+
.string()
|
|
298
|
+
.regex(/^[\da-fA-F]{2,}$/)
|
|
299
|
+
.refine((v) => v.length % 2 === 0, "must be even-length hex")
|
|
300
|
+
.optional()
|
|
301
|
+
.describe("Hex-encoded file_reference bytes (required with stickerId)"),
|
|
302
|
+
clear: z.boolean().optional().describe("Pass true to remove the intro card"),
|
|
303
|
+
},
|
|
304
|
+
annotations: WRITE,
|
|
305
|
+
}, async ({ title, description, stickerId, stickerAccessHash, stickerFileReference, clear }) => {
|
|
306
|
+
const err = await requireConnection(telegram);
|
|
307
|
+
if (err)
|
|
308
|
+
return fail(new Error(err));
|
|
309
|
+
if (!clear && (!title || !description)) {
|
|
310
|
+
return fail(new Error("title and description are required when not clearing"));
|
|
311
|
+
}
|
|
312
|
+
const stickerFields = [stickerId, stickerAccessHash, stickerFileReference].filter(Boolean);
|
|
313
|
+
if (stickerFields.length > 0 && stickerFields.length < 3) {
|
|
314
|
+
return fail(new Error("stickerId, stickerAccessHash, and stickerFileReference must all be set together"));
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
await telegram.setBusinessIntro({
|
|
318
|
+
title,
|
|
319
|
+
description,
|
|
320
|
+
stickerId,
|
|
321
|
+
stickerAccessHash,
|
|
322
|
+
stickerFileReference,
|
|
323
|
+
clear,
|
|
324
|
+
});
|
|
325
|
+
if (clear)
|
|
326
|
+
return ok("Business intro cleared");
|
|
327
|
+
return ok(`Business intro set: "${title}"`);
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
return fail(e);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { DESTRUCTIVE, fail, ok, READ_ONLY, requireConnection, sanitizeInputText, WRITE } from "./shared.js";
|
|
3
|
+
export function registerFactCheckTools(server, telegram) {
|
|
4
|
+
server.registerTool("telegram-get-fact-check", {
|
|
5
|
+
description: "Get fact-check annotations on channel messages. Fact-checks are added by independent fact-checkers in supported countries. Most messages will show no fact-check.",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
chatId: z.string().describe("Chat ID or username (channel)"),
|
|
8
|
+
messageIds: z
|
|
9
|
+
.array(z.number().int().positive())
|
|
10
|
+
.min(1)
|
|
11
|
+
.max(100)
|
|
12
|
+
.describe("Message IDs to get fact-checks for (1-100)"),
|
|
13
|
+
},
|
|
14
|
+
annotations: READ_ONLY,
|
|
15
|
+
}, async ({ chatId, messageIds }) => {
|
|
16
|
+
const err = await requireConnection(telegram);
|
|
17
|
+
if (err)
|
|
18
|
+
return fail(new Error(err));
|
|
19
|
+
try {
|
|
20
|
+
const result = await telegram.getFactCheck(chatId, messageIds);
|
|
21
|
+
return ok(JSON.stringify(result));
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
return fail(e);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
server.registerTool("telegram-edit-fact-check", {
|
|
28
|
+
description: "Add or update a fact-check annotation. Requires fact-checker privileges (limited to independent verifiers in supported countries).",
|
|
29
|
+
inputSchema: {
|
|
30
|
+
chatId: z.string().describe("Chat ID or username (channel)"),
|
|
31
|
+
messageId: z.number().int().positive().describe("Message ID to annotate"),
|
|
32
|
+
text: z
|
|
33
|
+
.string()
|
|
34
|
+
.transform(sanitizeInputText)
|
|
35
|
+
.pipe(z.string().min(1).max(1024))
|
|
36
|
+
.describe("Fact-check annotation text (1-1024 chars)"),
|
|
37
|
+
parseMode: z.enum(["md", "html"]).optional().describe("Text format (currently ignored — plain text only)"),
|
|
38
|
+
},
|
|
39
|
+
annotations: WRITE,
|
|
40
|
+
}, async ({ chatId, messageId, text, parseMode }) => {
|
|
41
|
+
const err = await requireConnection(telegram);
|
|
42
|
+
if (err)
|
|
43
|
+
return fail(new Error(err));
|
|
44
|
+
try {
|
|
45
|
+
await telegram.editFactCheck(chatId, messageId, text, { parseMode });
|
|
46
|
+
const preview = `${text.slice(0, 80)}${text.length > 80 ? "..." : ""}`;
|
|
47
|
+
return ok(`Fact-check set on message #${messageId} in ${chatId}: "${preview}"`);
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
return fail(e);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
server.registerTool("telegram-delete-fact-check", {
|
|
54
|
+
description: "Remove a fact-check annotation. Requires fact-checker privileges.",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
chatId: z.string().describe("Chat ID or username (channel)"),
|
|
57
|
+
messageId: z.number().int().positive().describe("Message ID whose fact-check to remove"),
|
|
58
|
+
},
|
|
59
|
+
annotations: DESTRUCTIVE,
|
|
60
|
+
}, async ({ chatId, messageId }) => {
|
|
61
|
+
const err = await requireConnection(telegram);
|
|
62
|
+
if (err)
|
|
63
|
+
return fail(new Error(err));
|
|
64
|
+
try {
|
|
65
|
+
await telegram.deleteFactCheck(chatId, messageId);
|
|
66
|
+
return ok(`Removed fact-check from message #${messageId} in ${chatId}`);
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
return fail(e);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
package/dist/tools/index.js
CHANGED
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
import { registerAccountTools } from "./account.js";
|
|
2
2
|
import { registerAuthTools } from "./auth.js";
|
|
3
3
|
import { registerBoostTools } from "./boosts.js";
|
|
4
|
+
import { registerBusinessTools } from "./business.js";
|
|
4
5
|
import { registerChatTools } from "./chats.js";
|
|
5
6
|
import { registerContactTools } from "./contacts.js";
|
|
6
7
|
import { registerExtraTools } from "./extras.js";
|
|
8
|
+
import { registerFactCheckTools } from "./fact-check.js";
|
|
7
9
|
import { registerGroupCallTools } from "./group-calls.js";
|
|
8
10
|
import { registerMediaTools } from "./media.js";
|
|
9
11
|
import { registerMessageTools } from "./messages.js";
|
|
10
12
|
import { registerQuickRepliesTools } from "./quick-replies.js";
|
|
11
13
|
import { registerReactionTools } from "./reactions.js";
|
|
14
|
+
import { registerSendMediaTools } from "./send-media.js";
|
|
12
15
|
import { registerStarsTools } from "./stars.js";
|
|
13
16
|
import { registerStickerTools } from "./stickers.js";
|
|
14
17
|
import { registerStoryTools } from "./stories.js";
|
|
18
|
+
import { registerTranscribeTools } from "./transcribe.js";
|
|
15
19
|
export function registerTools(server, telegram) {
|
|
16
20
|
registerAuthTools(server, telegram);
|
|
17
21
|
registerMessageTools(server, telegram);
|
|
18
22
|
registerChatTools(server, telegram);
|
|
19
23
|
registerMediaTools(server, telegram);
|
|
24
|
+
registerSendMediaTools(server, telegram);
|
|
20
25
|
registerContactTools(server, telegram);
|
|
21
26
|
registerReactionTools(server, telegram);
|
|
27
|
+
registerTranscribeTools(server, telegram);
|
|
28
|
+
registerFactCheckTools(server, telegram);
|
|
22
29
|
registerExtraTools(server, telegram);
|
|
23
30
|
registerAccountTools(server, telegram);
|
|
31
|
+
registerBusinessTools(server, telegram);
|
|
24
32
|
registerStickerTools(server, telegram);
|
|
25
33
|
registerStoryTools(server, telegram);
|
|
26
34
|
registerBoostTools(server, telegram);
|
package/dist/tools/messages.js
CHANGED
|
@@ -1,22 +1,34 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { DESTRUCTIVE, fail, formatReactions, ok, READ_ONLY, requireConnection, WRITE } from "./shared.js";
|
|
2
|
+
import { DESTRUCTIVE, fail, formatReactions, ok, READ_ONLY, requireConnection, sanitizeInputText, WRITE, } from "./shared.js";
|
|
3
3
|
export function registerMessageTools(server, telegram) {
|
|
4
4
|
server.registerTool("telegram-send-message", {
|
|
5
5
|
description: "Send a message to a Telegram chat",
|
|
6
6
|
inputSchema: {
|
|
7
7
|
chatId: z.string().describe("Chat ID or username (e.g. @username or numeric ID)"),
|
|
8
|
-
text: z.string().describe("Message text"),
|
|
8
|
+
text: z.string().transform(sanitizeInputText).describe("Message text"),
|
|
9
9
|
replyTo: z.number().optional().describe("Message ID to reply to"),
|
|
10
10
|
parseMode: z.enum(["md", "html"]).optional().describe("Message format: md (Markdown) or html"),
|
|
11
11
|
topicId: z.number().optional().describe("Forum topic ID to send message into (for groups with Topics enabled)"),
|
|
12
|
+
quoteText: z
|
|
13
|
+
.string()
|
|
14
|
+
.transform(sanitizeInputText)
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("Optional excerpt from the replied-to message to show as a quote above your reply. " +
|
|
17
|
+
"Requires `replyTo` to be set. Must be a verbatim substring of the original message text."),
|
|
18
|
+
effect: z
|
|
19
|
+
.string()
|
|
20
|
+
.regex(/^\d{1,19}$/)
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Optional message effect ID (numeric string, up to 19 digits). Premium animated effect attached to the message."),
|
|
12
23
|
},
|
|
13
24
|
annotations: WRITE,
|
|
14
|
-
}, async ({ chatId, text, replyTo, parseMode, topicId }) => {
|
|
25
|
+
}, async ({ chatId, text, replyTo, parseMode, topicId, quoteText, effect }) => {
|
|
15
26
|
const err = await requireConnection(telegram);
|
|
16
27
|
if (err)
|
|
17
28
|
return fail(new Error(err));
|
|
18
29
|
try {
|
|
19
|
-
const
|
|
30
|
+
const extra = quoteText || effect ? { quoteText, effect } : undefined;
|
|
31
|
+
const result = await telegram.sendMessage(chatId, text, replyTo, parseMode, topicId, extra);
|
|
20
32
|
const dest = topicId ? `topic ${topicId} in ${chatId}` : chatId;
|
|
21
33
|
const messageId = result?.id;
|
|
22
34
|
const idInfo = messageId ? ` [#${messageId}]` : "";
|
|
@@ -586,4 +598,171 @@ export function registerMessageTools(server, telegram) {
|
|
|
586
598
|
return fail(e);
|
|
587
599
|
}
|
|
588
600
|
});
|
|
601
|
+
server.registerTool("telegram-get-discussion-message", {
|
|
602
|
+
description: "For a channel post with comments enabled, returns the linked discussion-group info: discussionGroupId, discussionMsgId, unreadCount, readInboxMaxId, readOutboxMaxId, topMessage. Use discussionGroupId + discussionMsgId with telegram-send-message (replyTo=discussionMsgId) to post a comment.",
|
|
603
|
+
inputSchema: {
|
|
604
|
+
chatId: z.string().describe("Channel ID or @username that contains the post"),
|
|
605
|
+
messageId: z.number().int().positive().describe("ID of the channel post to get discussion info for"),
|
|
606
|
+
},
|
|
607
|
+
annotations: READ_ONLY,
|
|
608
|
+
}, async ({ chatId, messageId }) => {
|
|
609
|
+
const err = await requireConnection(telegram);
|
|
610
|
+
if (err)
|
|
611
|
+
return fail(new Error(err));
|
|
612
|
+
try {
|
|
613
|
+
const result = await telegram.getDiscussionMessage(chatId, messageId);
|
|
614
|
+
return ok(JSON.stringify(result));
|
|
615
|
+
}
|
|
616
|
+
catch (e) {
|
|
617
|
+
return fail(e);
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
server.registerTool("telegram-get-groups-for-discussion", {
|
|
621
|
+
description: "List groups that can be linked as a discussion group to a channel you admin. Helper for channel admins setting up comment threads.",
|
|
622
|
+
inputSchema: {},
|
|
623
|
+
annotations: READ_ONLY,
|
|
624
|
+
}, async () => {
|
|
625
|
+
const err = await requireConnection(telegram);
|
|
626
|
+
if (err)
|
|
627
|
+
return fail(new Error(err));
|
|
628
|
+
try {
|
|
629
|
+
const result = await telegram.getGroupsForDiscussion();
|
|
630
|
+
return ok(JSON.stringify(result));
|
|
631
|
+
}
|
|
632
|
+
catch (e) {
|
|
633
|
+
return fail(e);
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
server.registerTool("telegram-get-message-read-participants", {
|
|
637
|
+
description: "List who has read a message in a small group (≤100 members, ≤7 days old). Returns readers with userId, readAt timestamp. Does NOT work for channels or groups over 100 members (CHAT_TOO_BIG).",
|
|
638
|
+
inputSchema: {
|
|
639
|
+
chatId: z.string().describe("Group chat ID or @username"),
|
|
640
|
+
messageId: z.number().int().positive().describe("ID of the message to check read status for"),
|
|
641
|
+
},
|
|
642
|
+
annotations: READ_ONLY,
|
|
643
|
+
}, async ({ chatId, messageId }) => {
|
|
644
|
+
const err = await requireConnection(telegram);
|
|
645
|
+
if (err)
|
|
646
|
+
return fail(new Error(err));
|
|
647
|
+
try {
|
|
648
|
+
const result = await telegram.getMessageReadParticipants(chatId, messageId);
|
|
649
|
+
return ok(JSON.stringify(result));
|
|
650
|
+
}
|
|
651
|
+
catch (e) {
|
|
652
|
+
return fail(e);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
// ─── Poll interaction ──────────────────────────────────────────────────────
|
|
656
|
+
server.registerTool("telegram-vote-poll", {
|
|
657
|
+
description: "Vote in a poll by option index (single or multi-choice). Empty array retracts vote.",
|
|
658
|
+
inputSchema: {
|
|
659
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
660
|
+
messageId: z.number().int().positive().describe("Message ID of the poll"),
|
|
661
|
+
optionIndexes: z
|
|
662
|
+
.array(z.number().int().min(0).max(9))
|
|
663
|
+
.min(0)
|
|
664
|
+
.max(10)
|
|
665
|
+
.describe("Zero-based option indexes. Empty [] retracts vote."),
|
|
666
|
+
},
|
|
667
|
+
annotations: WRITE,
|
|
668
|
+
}, async ({ chatId, messageId, optionIndexes }) => {
|
|
669
|
+
const err = await requireConnection(telegram);
|
|
670
|
+
if (err)
|
|
671
|
+
return fail(new Error(err));
|
|
672
|
+
try {
|
|
673
|
+
const result = await telegram.sendPollVote(chatId, messageId, optionIndexes);
|
|
674
|
+
if (optionIndexes.length === 0) {
|
|
675
|
+
return ok(`Retracted vote from poll #${messageId}`);
|
|
676
|
+
}
|
|
677
|
+
return ok(`Voted in poll #${messageId}: option(s) ${result.chosenLabels.join(", ")} | Total: ${result.totalVoters} voters`);
|
|
678
|
+
}
|
|
679
|
+
catch (e) {
|
|
680
|
+
return fail(e);
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
server.registerTool("telegram-get-poll-results", {
|
|
684
|
+
description: "Get aggregated poll results: vote counts, percentages, quiz answer status",
|
|
685
|
+
inputSchema: {
|
|
686
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
687
|
+
messageId: z.number().int().positive().describe("Message ID of the poll"),
|
|
688
|
+
},
|
|
689
|
+
annotations: READ_ONLY,
|
|
690
|
+
}, async ({ chatId, messageId }) => {
|
|
691
|
+
const err = await requireConnection(telegram);
|
|
692
|
+
if (err)
|
|
693
|
+
return fail(new Error(err));
|
|
694
|
+
try {
|
|
695
|
+
const result = await telegram.getPollResults(chatId, messageId);
|
|
696
|
+
return ok(JSON.stringify(result));
|
|
697
|
+
}
|
|
698
|
+
catch (e) {
|
|
699
|
+
return fail(e);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
server.registerTool("telegram-get-poll-voters", {
|
|
703
|
+
description: "List users who voted for specific poll options (public polls only, paginated)",
|
|
704
|
+
inputSchema: {
|
|
705
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
706
|
+
messageId: z.number().int().positive().describe("Message ID of the poll"),
|
|
707
|
+
optionIndex: z
|
|
708
|
+
.number()
|
|
709
|
+
.int()
|
|
710
|
+
.min(0)
|
|
711
|
+
.max(9)
|
|
712
|
+
.optional()
|
|
713
|
+
.describe("Zero-based option index to filter by. Omit to get all voters"),
|
|
714
|
+
limit: z.number().int().min(1).max(100).default(20).describe("Max voters to return"),
|
|
715
|
+
offset: z.string().optional().describe("Pagination offset from previous call"),
|
|
716
|
+
},
|
|
717
|
+
annotations: READ_ONLY,
|
|
718
|
+
}, async ({ chatId, messageId, optionIndex, limit, offset }) => {
|
|
719
|
+
const err = await requireConnection(telegram);
|
|
720
|
+
if (err)
|
|
721
|
+
return fail(new Error(err));
|
|
722
|
+
try {
|
|
723
|
+
const result = await telegram.getPollVoters(chatId, messageId, { optionIndex, limit, offset });
|
|
724
|
+
return ok(JSON.stringify(result));
|
|
725
|
+
}
|
|
726
|
+
catch (e) {
|
|
727
|
+
return fail(e);
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
server.registerTool("telegram-close-poll", {
|
|
731
|
+
description: "Close a poll permanently. This is a one-way operation — closed polls cannot be reopened.",
|
|
732
|
+
inputSchema: {
|
|
733
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
734
|
+
messageId: z.number().int().positive().describe("Message ID of the poll to close"),
|
|
735
|
+
},
|
|
736
|
+
annotations: WRITE,
|
|
737
|
+
}, async ({ chatId, messageId }) => {
|
|
738
|
+
const err = await requireConnection(telegram);
|
|
739
|
+
if (err)
|
|
740
|
+
return fail(new Error(err));
|
|
741
|
+
try {
|
|
742
|
+
const result = await telegram.closePoll(chatId, messageId);
|
|
743
|
+
return ok(`Closed poll #${messageId} (final: ${result.totalVoters} voters)`);
|
|
744
|
+
}
|
|
745
|
+
catch (e) {
|
|
746
|
+
return fail(e);
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
server.registerTool("telegram-get-outbox-read-date", {
|
|
750
|
+
description: "Get when your recipient read your outgoing message in a private chat. Returns null/Not read yet if unread. Errors if the other side disabled read receipts (YOUR_PRIVACY_RESTRICTED / USER_PRIVACY_RESTRICTED).",
|
|
751
|
+
inputSchema: {
|
|
752
|
+
chatId: z.string().describe("Private chat ID or @username of the recipient"),
|
|
753
|
+
messageId: z.number().int().positive().describe("ID of your outgoing message"),
|
|
754
|
+
},
|
|
755
|
+
annotations: READ_ONLY,
|
|
756
|
+
}, async ({ chatId, messageId }) => {
|
|
757
|
+
const err = await requireConnection(telegram);
|
|
758
|
+
if (err)
|
|
759
|
+
return fail(new Error(err));
|
|
760
|
+
try {
|
|
761
|
+
const result = await telegram.getOutboxReadDate(chatId, messageId);
|
|
762
|
+
return ok(result.readAt ? `Read at ${result.readAt}` : "Not read yet");
|
|
763
|
+
}
|
|
764
|
+
catch (e) {
|
|
765
|
+
return fail(e);
|
|
766
|
+
}
|
|
767
|
+
});
|
|
589
768
|
}
|