@overpod/mcp-telegram 1.11.1 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2 -736
- package/dist/telegram-client.js +1 -1
- package/dist/tools/auth.d.ts +3 -0
- package/dist/tools/auth.js +72 -0
- package/dist/tools/chats.d.ts +3 -0
- package/dist/tools/chats.js +121 -0
- package/dist/tools/contacts.d.ts +3 -0
- package/dist/tools/contacts.js +135 -0
- package/dist/tools/extras.d.ts +3 -0
- package/dist/tools/extras.js +155 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.js +16 -0
- package/dist/tools/media.d.ts +3 -0
- package/dist/tools/media.js +84 -0
- package/dist/tools/messages.d.ts +3 -0
- package/dist/tools/messages.js +208 -0
- package/dist/tools/reactions.d.ts +3 -0
- package/dist/tools/reactions.js +63 -0
- package/dist/tools/shared.d.ts +40 -0
- package/dist/tools/shared.js +30 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -8,8 +8,8 @@ console.log = (...args) => {
|
|
|
8
8
|
import "dotenv/config";
|
|
9
9
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
10
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11
|
-
import { z } from "zod";
|
|
12
11
|
import { TelegramService } from "./telegram-client.js";
|
|
12
|
+
import { registerTools } from "./tools/index.js";
|
|
13
13
|
// Telegram API credentials from env
|
|
14
14
|
const API_ID = Number(process.env.TELEGRAM_API_ID);
|
|
15
15
|
const API_HASH = process.env.TELEGRAM_API_HASH;
|
|
@@ -18,745 +18,11 @@ if (!API_ID || !API_HASH) {
|
|
|
18
18
|
process.exit(1);
|
|
19
19
|
}
|
|
20
20
|
const telegram = new TelegramService(API_ID, API_HASH);
|
|
21
|
-
/** Remove unpaired UTF-16 surrogates that break JSON serialization */
|
|
22
|
-
function sanitize(text) {
|
|
23
|
-
return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
|
|
24
|
-
}
|
|
25
|
-
/** Format reactions array into compact text like: [👍×5 ❤️×3(me) 🔥×1] */
|
|
26
|
-
function formatReactions(reactions) {
|
|
27
|
-
if (!reactions?.length)
|
|
28
|
-
return "";
|
|
29
|
-
const parts = reactions.map((r) => `${r.emoji}×${r.count}${r.me ? "(me)" : ""}`);
|
|
30
|
-
return ` [${parts.join(" ")}]`;
|
|
31
|
-
}
|
|
32
21
|
const server = new McpServer({
|
|
33
22
|
name: "mcp-telegram",
|
|
34
23
|
version: "1.0.0",
|
|
35
24
|
});
|
|
36
|
-
|
|
37
|
-
async function requireConnection() {
|
|
38
|
-
if (await telegram.ensureConnected())
|
|
39
|
-
return null;
|
|
40
|
-
const reason = telegram.lastError ? ` ${telegram.lastError}` : "";
|
|
41
|
-
return `Not connected to Telegram.${reason} Run telegram-login first.`;
|
|
42
|
-
}
|
|
43
|
-
// --- Tools ---
|
|
44
|
-
server.tool("telegram-status", "Check Telegram connection status", {}, async () => {
|
|
45
|
-
if (await telegram.ensureConnected()) {
|
|
46
|
-
try {
|
|
47
|
-
const me = await telegram.getMe();
|
|
48
|
-
return {
|
|
49
|
-
content: [
|
|
50
|
-
{
|
|
51
|
-
type: "text",
|
|
52
|
-
text: `Connected as ${me.firstName ?? ""} (@${me.username ?? "unknown"}, id: ${me.id})`,
|
|
53
|
-
},
|
|
54
|
-
],
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
return { content: [{ type: "text", text: "Connected, but failed to get user info" }] };
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
const reason = telegram.lastError ? ` Reason: ${telegram.lastError}` : "";
|
|
62
|
-
return {
|
|
63
|
-
content: [{ type: "text", text: `Not connected.${reason} Use telegram-login to authenticate via QR code.` }],
|
|
64
|
-
};
|
|
65
|
-
});
|
|
66
|
-
server.tool("telegram-login", "Login to Telegram via QR code. Returns QR image. IMPORTANT: pass the entire result to user without modifications.", {}, async () => {
|
|
67
|
-
let qrDataUrl = "";
|
|
68
|
-
let qrRawUrl = "";
|
|
69
|
-
const loginPromise = telegram.startQrLogin((dataUrl) => {
|
|
70
|
-
qrDataUrl = dataUrl;
|
|
71
|
-
}, (url) => {
|
|
72
|
-
qrRawUrl = url;
|
|
73
|
-
});
|
|
74
|
-
// Wait for first QR to be generated
|
|
75
|
-
const startTime = Date.now();
|
|
76
|
-
while (!qrDataUrl && Date.now() - startTime < 15000) {
|
|
77
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
78
|
-
}
|
|
79
|
-
if (!qrDataUrl) {
|
|
80
|
-
return { content: [{ type: "text", text: "Failed to generate QR code" }] };
|
|
81
|
-
}
|
|
82
|
-
// Login continues in background
|
|
83
|
-
loginPromise.then((result) => {
|
|
84
|
-
if (result.success) {
|
|
85
|
-
console.error("[mcp-telegram] Login successful");
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
console.error(`[mcp-telegram] Login failed: ${result.message}`);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
// Return as MCP image content + text with fallback options
|
|
92
|
-
const base64 = qrDataUrl.replace(/^data:image\/png;base64,/, "");
|
|
93
|
-
const qrApiUrl = qrRawUrl
|
|
94
|
-
? `https://api.qrserver.com/v1/create-qr-code/?size=256x256&data=${encodeURIComponent(qrRawUrl)}`
|
|
95
|
-
: "";
|
|
96
|
-
const instructions = [
|
|
97
|
-
"Scan this QR code in Telegram: **Settings → Devices → Link Desktop Device**.",
|
|
98
|
-
"",
|
|
99
|
-
qrApiUrl ? `If the QR image is not visible, open this link in your browser:\n${qrApiUrl}` : "",
|
|
100
|
-
"",
|
|
101
|
-
"After scanning, run **telegram-status** to verify the connection.",
|
|
102
|
-
]
|
|
103
|
-
.filter(Boolean)
|
|
104
|
-
.join("\n");
|
|
105
|
-
return {
|
|
106
|
-
content: [
|
|
107
|
-
{
|
|
108
|
-
type: "image",
|
|
109
|
-
data: base64,
|
|
110
|
-
mimeType: "image/png",
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
type: "text",
|
|
114
|
-
text: instructions,
|
|
115
|
-
},
|
|
116
|
-
],
|
|
117
|
-
};
|
|
118
|
-
});
|
|
119
|
-
server.tool("telegram-send-message", "Send a message to a Telegram chat", {
|
|
120
|
-
chatId: z.string().describe("Chat ID or username (e.g. @username or numeric ID)"),
|
|
121
|
-
text: z.string().describe("Message text"),
|
|
122
|
-
replyTo: z.number().optional().describe("Message ID to reply to"),
|
|
123
|
-
parseMode: z.enum(["md", "html"]).optional().describe("Message format: md (Markdown) or html"),
|
|
124
|
-
topicId: z.number().optional().describe("Forum topic ID to send message into (for groups with Topics enabled)"),
|
|
125
|
-
}, async ({ chatId, text, replyTo, parseMode, topicId }) => {
|
|
126
|
-
const err = await requireConnection();
|
|
127
|
-
if (err)
|
|
128
|
-
return { content: [{ type: "text", text: err }] };
|
|
129
|
-
try {
|
|
130
|
-
await telegram.sendMessage(chatId, text, replyTo, parseMode, topicId);
|
|
131
|
-
const dest = topicId ? `topic ${topicId} in ${chatId}` : chatId;
|
|
132
|
-
return { content: [{ type: "text", text: `Message sent to ${dest}` }] };
|
|
133
|
-
}
|
|
134
|
-
catch (e) {
|
|
135
|
-
return { content: [{ type: "text", text: `Send error: ${e.message}` }] };
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
server.tool("telegram-list-chats", "List Telegram chats", {
|
|
139
|
-
limit: z.number().default(20).describe("Number of chats to return"),
|
|
140
|
-
offsetDate: z.number().optional().describe("Unix timestamp offset for pagination"),
|
|
141
|
-
filterType: z
|
|
142
|
-
.enum(["private", "group", "channel", "contact_requests"])
|
|
143
|
-
.optional()
|
|
144
|
-
.describe("Filter by chat type. 'contact_requests' shows only private chats from non-contacts"),
|
|
145
|
-
}, async ({ limit, offsetDate, filterType }) => {
|
|
146
|
-
const err = await requireConnection();
|
|
147
|
-
if (err)
|
|
148
|
-
return { content: [{ type: "text", text: err }] };
|
|
149
|
-
try {
|
|
150
|
-
const dialogs = await telegram.getDialogs(limit, offsetDate, filterType);
|
|
151
|
-
const text = dialogs
|
|
152
|
-
.map((d) => {
|
|
153
|
-
const prefix = d.type === "group" ? "G" : d.type === "channel" ? "C" : "P";
|
|
154
|
-
const botTag = d.isBot ? " [bot]" : "";
|
|
155
|
-
const contactTag = d.type === "private" && d.isContact === false ? " [not in contacts]" : "";
|
|
156
|
-
const unread = d.unreadCount > 0 ? ` [${d.unreadCount} unread]` : "";
|
|
157
|
-
return `${prefix} ${d.name} (${d.id})${botTag}${contactTag}${unread}`;
|
|
158
|
-
})
|
|
159
|
-
.join("\n");
|
|
160
|
-
return { content: [{ type: "text", text: sanitize(text) || "No chats" }] };
|
|
161
|
-
}
|
|
162
|
-
catch (e) {
|
|
163
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
server.tool("telegram-read-messages", "Read recent messages from a Telegram chat", {
|
|
167
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
168
|
-
limit: z.number().default(10).describe("Number of messages to return"),
|
|
169
|
-
offsetId: z.number().optional().describe("Message ID to start from (for pagination)"),
|
|
170
|
-
minDate: z.number().optional().describe("Unix timestamp: only messages after this date"),
|
|
171
|
-
maxDate: z.number().optional().describe("Unix timestamp: only messages before this date"),
|
|
172
|
-
}, async ({ chatId, limit, offsetId, minDate, maxDate }) => {
|
|
173
|
-
const err = await requireConnection();
|
|
174
|
-
if (err)
|
|
175
|
-
return { content: [{ type: "text", text: err }] };
|
|
176
|
-
try {
|
|
177
|
-
const messages = await telegram.getMessages(chatId, limit, offsetId, minDate, maxDate);
|
|
178
|
-
const text = messages
|
|
179
|
-
.map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
|
|
180
|
-
.join("\n\n");
|
|
181
|
-
return { content: [{ type: "text", text: sanitize(text) || "No messages" }] };
|
|
182
|
-
}
|
|
183
|
-
catch (e) {
|
|
184
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
server.tool("telegram-search-chats", "Search for Telegram chats/users/channels by name or username", {
|
|
188
|
-
query: z.string().describe("Search query (name or username)"),
|
|
189
|
-
limit: z.number().default(10).describe("Max results"),
|
|
190
|
-
}, async ({ query, limit }) => {
|
|
191
|
-
const err = await requireConnection();
|
|
192
|
-
if (err)
|
|
193
|
-
return { content: [{ type: "text", text: err }] };
|
|
194
|
-
try {
|
|
195
|
-
const results = await telegram.searchChats(query, limit);
|
|
196
|
-
const text = results
|
|
197
|
-
.map((c) => `${c.type === "group" ? "G" : c.type === "channel" ? "C" : "P"} ${c.name}${c.username ? ` (@${c.username})` : ""} (${c.id})${c.membersCount ? ` [${c.membersCount} members]` : ""}${c.description ? ` — ${c.description.split("\n")[0].slice(0, 100)}` : ""}`)
|
|
198
|
-
.join("\n");
|
|
199
|
-
return { content: [{ type: "text", text: sanitize(text) || "No results" }] };
|
|
200
|
-
}
|
|
201
|
-
catch (e) {
|
|
202
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
server.tool("telegram-search-global", "Search messages globally across all public Telegram chats and channels", {
|
|
206
|
-
query: z.string().describe("Search text"),
|
|
207
|
-
limit: z.number().default(20).describe("Max results"),
|
|
208
|
-
minDate: z.number().optional().describe("Unix timestamp: only messages after this date"),
|
|
209
|
-
maxDate: z.number().optional().describe("Unix timestamp: only messages before this date"),
|
|
210
|
-
}, async ({ query, limit, minDate, maxDate }) => {
|
|
211
|
-
const err = await requireConnection();
|
|
212
|
-
if (err)
|
|
213
|
-
return { content: [{ type: "text", text: err }] };
|
|
214
|
-
try {
|
|
215
|
-
const messages = await telegram.searchGlobal(query, limit, minDate, maxDate);
|
|
216
|
-
const text = messages
|
|
217
|
-
.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}` : ""}]` : ""}${formatReactions(m.reactions)}`)
|
|
218
|
-
.join("\n\n");
|
|
219
|
-
return { content: [{ type: "text", text: sanitize(text) || "No messages found" }] };
|
|
220
|
-
}
|
|
221
|
-
catch (e) {
|
|
222
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
server.tool("telegram-search-messages", "Search messages in a Telegram chat by text", {
|
|
226
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
227
|
-
query: z.string().describe("Search text"),
|
|
228
|
-
limit: z.number().default(20).describe("Max results"),
|
|
229
|
-
minDate: z.number().optional().describe("Unix timestamp: only messages after this date"),
|
|
230
|
-
maxDate: z.number().optional().describe("Unix timestamp: only messages before this date"),
|
|
231
|
-
}, async ({ chatId, query, limit, minDate, maxDate }) => {
|
|
232
|
-
const err = await requireConnection();
|
|
233
|
-
if (err)
|
|
234
|
-
return { content: [{ type: "text", text: err }] };
|
|
235
|
-
try {
|
|
236
|
-
const messages = await telegram.searchMessages(chatId, query, limit, minDate, maxDate);
|
|
237
|
-
const text = messages
|
|
238
|
-
.map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
|
|
239
|
-
.join("\n\n");
|
|
240
|
-
return { content: [{ type: "text", text: sanitize(text) || "No messages found" }] };
|
|
241
|
-
}
|
|
242
|
-
catch (e) {
|
|
243
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
server.tool("telegram-get-unread", "Get unread Telegram chats", {
|
|
247
|
-
limit: z.number().default(20).describe("Number of unread chats to return"),
|
|
248
|
-
}, async ({ limit }) => {
|
|
249
|
-
const err = await requireConnection();
|
|
250
|
-
if (err)
|
|
251
|
-
return { content: [{ type: "text", text: err }] };
|
|
252
|
-
try {
|
|
253
|
-
const dialogs = await telegram.getUnreadDialogs(limit);
|
|
254
|
-
const text = dialogs
|
|
255
|
-
.map((d) => {
|
|
256
|
-
const prefix = d.type === "group" ? "G" : d.type === "channel" ? "C" : "P";
|
|
257
|
-
const botTag = d.isBot ? " [bot]" : "";
|
|
258
|
-
const contactTag = d.type === "private" && d.isContact === false ? " [not in contacts]" : "";
|
|
259
|
-
const forumTag = d.forum ? " [forum]" : "";
|
|
260
|
-
let line = `${prefix} ${d.name} (${d.id})${botTag}${contactTag}${forumTag} [${d.unreadCount} unread]`;
|
|
261
|
-
if (d.topics && d.topics.length > 0) {
|
|
262
|
-
const topicLines = d.topics.map((t) => ` # ${t.title} [${t.unreadCount} unread]`);
|
|
263
|
-
line += `\n${topicLines.join("\n")}`;
|
|
264
|
-
}
|
|
265
|
-
return line;
|
|
266
|
-
})
|
|
267
|
-
.join("\n");
|
|
268
|
-
return { content: [{ type: "text", text: sanitize(text) || "No unread chats" }] };
|
|
269
|
-
}
|
|
270
|
-
catch (e) {
|
|
271
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
server.tool("telegram-mark-as-read", "Mark a Telegram chat as read", {
|
|
275
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
276
|
-
}, async ({ chatId }) => {
|
|
277
|
-
const err = await requireConnection();
|
|
278
|
-
if (err)
|
|
279
|
-
return { content: [{ type: "text", text: err }] };
|
|
280
|
-
try {
|
|
281
|
-
await telegram.markAsRead(chatId);
|
|
282
|
-
return { content: [{ type: "text", text: `Marked ${chatId} as read` }] };
|
|
283
|
-
}
|
|
284
|
-
catch (e) {
|
|
285
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
server.tool("telegram-forward-message", "Forward messages between Telegram chats", {
|
|
289
|
-
fromChatId: z.string().describe("Source chat ID or username"),
|
|
290
|
-
toChatId: z.string().describe("Destination chat ID or username"),
|
|
291
|
-
messageIds: z.array(z.number()).describe("Array of message IDs to forward"),
|
|
292
|
-
}, async ({ fromChatId, toChatId, messageIds }) => {
|
|
293
|
-
const err = await requireConnection();
|
|
294
|
-
if (err)
|
|
295
|
-
return { content: [{ type: "text", text: err }] };
|
|
296
|
-
try {
|
|
297
|
-
await telegram.forwardMessage(fromChatId, toChatId, messageIds);
|
|
298
|
-
return {
|
|
299
|
-
content: [
|
|
300
|
-
{ type: "text", text: `Forwarded ${messageIds.length} message(s) from ${fromChatId} to ${toChatId}` },
|
|
301
|
-
],
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
catch (e) {
|
|
305
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
306
|
-
}
|
|
307
|
-
});
|
|
308
|
-
server.tool("telegram-edit-message", "Edit a sent message in Telegram", {
|
|
309
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
310
|
-
messageId: z.number().describe("ID of the message to edit"),
|
|
311
|
-
text: z.string().describe("New message text"),
|
|
312
|
-
}, async ({ chatId, messageId, text }) => {
|
|
313
|
-
const err = await requireConnection();
|
|
314
|
-
if (err)
|
|
315
|
-
return { content: [{ type: "text", text: err }] };
|
|
316
|
-
try {
|
|
317
|
-
await telegram.editMessage(chatId, messageId, text);
|
|
318
|
-
return { content: [{ type: "text", text: `Message ${messageId} edited in ${chatId}` }] };
|
|
319
|
-
}
|
|
320
|
-
catch (e) {
|
|
321
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
server.tool("telegram-delete-message", "Delete messages in a Telegram chat", {
|
|
325
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
326
|
-
messageIds: z.array(z.number()).describe("Array of message IDs to delete"),
|
|
327
|
-
}, async ({ chatId, messageIds }) => {
|
|
328
|
-
const err = await requireConnection();
|
|
329
|
-
if (err)
|
|
330
|
-
return { content: [{ type: "text", text: err }] };
|
|
331
|
-
try {
|
|
332
|
-
await telegram.deleteMessages(chatId, messageIds);
|
|
333
|
-
return { content: [{ type: "text", text: `Deleted ${messageIds.length} message(s) in ${chatId}` }] };
|
|
334
|
-
}
|
|
335
|
-
catch (e) {
|
|
336
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
server.tool("telegram-get-chat-info", "Get detailed info about a Telegram chat", {
|
|
340
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
341
|
-
}, async ({ chatId }) => {
|
|
342
|
-
const err = await requireConnection();
|
|
343
|
-
if (err)
|
|
344
|
-
return { content: [{ type: "text", text: err }] };
|
|
345
|
-
try {
|
|
346
|
-
const info = await telegram.getChatInfo(chatId);
|
|
347
|
-
const lines = [
|
|
348
|
-
`Name: ${info.name}`,
|
|
349
|
-
`ID: ${info.id}`,
|
|
350
|
-
`Type: ${info.type}`,
|
|
351
|
-
...(info.forum ? ["Forum: yes"] : []),
|
|
352
|
-
...(info.username ? [`Username: @${info.username}`] : []),
|
|
353
|
-
...(info.description ? [`Description: ${info.description}`] : []),
|
|
354
|
-
...(info.membersCount != null ? [`Members: ${info.membersCount}`] : []),
|
|
355
|
-
];
|
|
356
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
357
|
-
}
|
|
358
|
-
catch (e) {
|
|
359
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
server.tool("telegram-send-file", "Send a file to a Telegram chat", {
|
|
363
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
364
|
-
filePath: z.string().describe("Absolute path to file"),
|
|
365
|
-
caption: z.string().optional().describe("File caption"),
|
|
366
|
-
}, async ({ chatId, filePath, caption }) => {
|
|
367
|
-
const err = await requireConnection();
|
|
368
|
-
if (err)
|
|
369
|
-
return { content: [{ type: "text", text: err }] };
|
|
370
|
-
try {
|
|
371
|
-
await telegram.sendFile(chatId, filePath, caption);
|
|
372
|
-
return { content: [{ type: "text", text: `File sent to ${chatId}` }] };
|
|
373
|
-
}
|
|
374
|
-
catch (e) {
|
|
375
|
-
return { content: [{ type: "text", text: `Send file error: ${e.message}` }] };
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
server.tool("telegram-download-media", "Download media from a Telegram message", {
|
|
379
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
380
|
-
messageId: z.number().describe("Message ID containing media"),
|
|
381
|
-
downloadPath: z.string().describe("Absolute path to save file"),
|
|
382
|
-
}, async ({ chatId, messageId, downloadPath }) => {
|
|
383
|
-
const err = await requireConnection();
|
|
384
|
-
if (err)
|
|
385
|
-
return { content: [{ type: "text", text: err }] };
|
|
386
|
-
try {
|
|
387
|
-
const path = await telegram.downloadMedia(chatId, messageId, downloadPath);
|
|
388
|
-
return { content: [{ type: "text", text: `Media downloaded to ${path}` }] };
|
|
389
|
-
}
|
|
390
|
-
catch (e) {
|
|
391
|
-
return { content: [{ type: "text", text: `Download error: ${e.message}` }] };
|
|
392
|
-
}
|
|
393
|
-
});
|
|
394
|
-
server.tool("telegram-pin-message", "Pin a message in a Telegram chat", {
|
|
395
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
396
|
-
messageId: z.number().describe("Message ID to pin"),
|
|
397
|
-
silent: z.boolean().default(false).describe("Pin without notification"),
|
|
398
|
-
}, async ({ chatId, messageId, silent }) => {
|
|
399
|
-
const err = await requireConnection();
|
|
400
|
-
if (err)
|
|
401
|
-
return { content: [{ type: "text", text: err }] };
|
|
402
|
-
try {
|
|
403
|
-
await telegram.pinMessage(chatId, messageId, silent);
|
|
404
|
-
return { content: [{ type: "text", text: `Message ${messageId} pinned in ${chatId}` }] };
|
|
405
|
-
}
|
|
406
|
-
catch (e) {
|
|
407
|
-
return { content: [{ type: "text", text: `Pin error: ${e.message}` }] };
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
server.tool("telegram-unpin-message", "Unpin a message in a Telegram chat", {
|
|
411
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
412
|
-
messageId: z.number().describe("Message ID to unpin"),
|
|
413
|
-
}, async ({ chatId, messageId }) => {
|
|
414
|
-
const err = await requireConnection();
|
|
415
|
-
if (err)
|
|
416
|
-
return { content: [{ type: "text", text: err }] };
|
|
417
|
-
try {
|
|
418
|
-
await telegram.unpinMessage(chatId, messageId);
|
|
419
|
-
return { content: [{ type: "text", text: `Message ${messageId} unpinned in ${chatId}` }] };
|
|
420
|
-
}
|
|
421
|
-
catch (e) {
|
|
422
|
-
return { content: [{ type: "text", text: `Unpin error: ${e.message}` }] };
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
server.tool("telegram-get-contacts", "Get Telegram contacts list", {
|
|
426
|
-
limit: z.number().default(50).describe("Number of contacts to return"),
|
|
427
|
-
}, async ({ limit }) => {
|
|
428
|
-
const err = await requireConnection();
|
|
429
|
-
if (err)
|
|
430
|
-
return { content: [{ type: "text", text: err }] };
|
|
431
|
-
try {
|
|
432
|
-
const contacts = await telegram.getContacts(limit);
|
|
433
|
-
const text = contacts
|
|
434
|
-
.map((c) => `P ${c.name}${c.username ? ` (@${c.username})` : ""} (${c.id})${c.phone ? ` +${c.phone}` : ""}`)
|
|
435
|
-
.join("\n");
|
|
436
|
-
return { content: [{ type: "text", text: sanitize(text) || "No contacts" }] };
|
|
437
|
-
}
|
|
438
|
-
catch (e) {
|
|
439
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
server.tool("telegram-get-chat-members", "Get members of a Telegram group or channel", {
|
|
443
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
444
|
-
limit: z.number().default(50).describe("Number of members to return"),
|
|
445
|
-
}, async ({ chatId, limit }) => {
|
|
446
|
-
const err = await requireConnection();
|
|
447
|
-
if (err)
|
|
448
|
-
return { content: [{ type: "text", text: err }] };
|
|
449
|
-
try {
|
|
450
|
-
const members = await telegram.getChatMembers(chatId, limit);
|
|
451
|
-
const text = members.map((m) => `${m.name}${m.username ? ` (@${m.username})` : ""} (${m.id})`).join("\n");
|
|
452
|
-
return { content: [{ type: "text", text: sanitize(text) || "No members found" }] };
|
|
453
|
-
}
|
|
454
|
-
catch (e) {
|
|
455
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
server.tool("telegram-get-profile", "Get detailed profile info of a Telegram user including bio, birthday, business info and more", {
|
|
459
|
-
userId: z.string().describe("User ID or username"),
|
|
460
|
-
}, async ({ userId }) => {
|
|
461
|
-
const err = await requireConnection();
|
|
462
|
-
if (err)
|
|
463
|
-
return { content: [{ type: "text", text: err }] };
|
|
464
|
-
try {
|
|
465
|
-
const profile = await telegram.getProfile(userId);
|
|
466
|
-
const lines = [
|
|
467
|
-
`Name: ${profile.name}`,
|
|
468
|
-
`ID: ${profile.id}`,
|
|
469
|
-
...(profile.username ? [`Username: @${profile.username}`] : []),
|
|
470
|
-
...(profile.phone ? [`Phone: +${profile.phone}`] : []),
|
|
471
|
-
...(profile.bio ? [`Bio: ${profile.bio}`] : []),
|
|
472
|
-
`Photo: ${profile.photo ? "yes" : "no"}`,
|
|
473
|
-
...(profile.premium ? ["Premium: yes"] : []),
|
|
474
|
-
...(profile.lastSeen ? [`Last seen: ${profile.lastSeen}`] : []),
|
|
475
|
-
...(profile.birthday ? [`Birthday: ${profile.birthday}`] : []),
|
|
476
|
-
...(profile.commonChatsCount ? [`Common chats: ${profile.commonChatsCount}`] : []),
|
|
477
|
-
...(profile.personalChannelId ? [`Personal channel ID: ${profile.personalChannelId}`] : []),
|
|
478
|
-
...(profile.businessLocation ? [`Business location: ${profile.businessLocation}`] : []),
|
|
479
|
-
...(profile.businessWorkHours ? [`Business hours timezone: ${profile.businessWorkHours}`] : []),
|
|
480
|
-
];
|
|
481
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
482
|
-
}
|
|
483
|
-
catch (e) {
|
|
484
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
485
|
-
}
|
|
486
|
-
});
|
|
487
|
-
server.tool("telegram-get-profile-photo", "Download profile photo of a Telegram user, group, or channel. Returns inline image or saves to file", {
|
|
488
|
-
entityId: z.string().describe("User/Chat/Channel ID or username"),
|
|
489
|
-
savePath: z.string().optional().describe("Absolute path to save file. If omitted, returns inline base64 image"),
|
|
490
|
-
size: z
|
|
491
|
-
.enum(["small", "big"])
|
|
492
|
-
.optional()
|
|
493
|
-
.describe("Photo size: 'small' (160x160) or 'big' (640x640). Default: big"),
|
|
494
|
-
}, async ({ entityId, savePath, size }) => {
|
|
495
|
-
const err = await requireConnection();
|
|
496
|
-
if (err)
|
|
497
|
-
return { content: [{ type: "text", text: err }] };
|
|
498
|
-
try {
|
|
499
|
-
const result = await telegram.downloadProfilePhoto(entityId, {
|
|
500
|
-
isBig: size !== "small",
|
|
501
|
-
savePath,
|
|
502
|
-
});
|
|
503
|
-
if (!result) {
|
|
504
|
-
return { content: [{ type: "text", text: "No profile photo found" }] };
|
|
505
|
-
}
|
|
506
|
-
if ("filePath" in result) {
|
|
507
|
-
return { content: [{ type: "text", text: `Downloaded to: ${result.filePath}` }] };
|
|
508
|
-
}
|
|
509
|
-
return {
|
|
510
|
-
content: [
|
|
511
|
-
{ type: "image", data: result.buffer.toString("base64"), mimeType: result.mimeType },
|
|
512
|
-
{ type: "text", text: `Profile photo (${(result.buffer.length / 1024).toFixed(0)} KB, ${result.mimeType})` },
|
|
513
|
-
],
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
catch (e) {
|
|
517
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
518
|
-
}
|
|
519
|
-
});
|
|
520
|
-
server.tool("telegram-join-chat", "Join a Telegram group or channel by username or invite link", {
|
|
521
|
-
target: z.string().describe("Username (@group), link (t.me/group), or invite link (t.me/+xxx)"),
|
|
522
|
-
}, async ({ target }) => {
|
|
523
|
-
const err = await requireConnection();
|
|
524
|
-
if (err)
|
|
525
|
-
return { content: [{ type: "text", text: err }] };
|
|
526
|
-
try {
|
|
527
|
-
const result = await telegram.joinChat(target);
|
|
528
|
-
return {
|
|
529
|
-
content: [
|
|
530
|
-
{
|
|
531
|
-
type: "text",
|
|
532
|
-
text: `Joined ${result.type}: ${result.title} (ID: ${result.id})`,
|
|
533
|
-
},
|
|
534
|
-
],
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
catch (e) {
|
|
538
|
-
return {
|
|
539
|
-
content: [{ type: "text", text: `Error: ${e.message}` }],
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
server.tool("telegram-send-reaction", "Send emoji reaction(s) to a message. Supports multiple reactions and adding to existing ones. Omit emoji to remove all reactions", {
|
|
544
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
545
|
-
messageId: z.number().describe("Message ID to react to"),
|
|
546
|
-
emoji: z
|
|
547
|
-
.union([z.string(), z.array(z.string())])
|
|
548
|
-
.optional()
|
|
549
|
-
.describe("Reaction emoji(s): single '👍' or array ['👍','🔥']. Omit to remove all reactions"),
|
|
550
|
-
addToExisting: z
|
|
551
|
-
.boolean()
|
|
552
|
-
.default(false)
|
|
553
|
-
.describe("If true, add reaction(s) to existing ones instead of replacing"),
|
|
554
|
-
}, async ({ chatId, messageId, emoji, addToExisting }) => {
|
|
555
|
-
const err = await requireConnection();
|
|
556
|
-
if (err)
|
|
557
|
-
return { content: [{ type: "text", text: err }] };
|
|
558
|
-
try {
|
|
559
|
-
const updated = await telegram.sendReaction(chatId, messageId, emoji, addToExisting);
|
|
560
|
-
const emojiStr = Array.isArray(emoji) ? emoji.join("") : emoji;
|
|
561
|
-
const action = emoji ? `Reacted ${emojiStr} to` : "Removed reactions from";
|
|
562
|
-
const reactionsInfo = updated
|
|
563
|
-
? ` | Reactions: ${updated.map((r) => `${r.emoji}×${r.count}${r.me ? "(me)" : ""}`).join(" ")}`
|
|
564
|
-
: "";
|
|
565
|
-
return { content: [{ type: "text", text: `${action} message ${messageId} in ${chatId}${reactionsInfo}` }] };
|
|
566
|
-
}
|
|
567
|
-
catch (e) {
|
|
568
|
-
return { content: [{ type: "text", text: `Reaction error: ${e.message}` }] };
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
server.tool("telegram-get-reactions", "Get detailed reaction info for a message: which reactions, counts, and who reacted (when visible)", {
|
|
572
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
573
|
-
messageId: z.number().describe("Message ID to get reactions for"),
|
|
574
|
-
}, async ({ chatId, messageId }) => {
|
|
575
|
-
const err = await requireConnection();
|
|
576
|
-
if (err)
|
|
577
|
-
return { content: [{ type: "text", text: err }] };
|
|
578
|
-
try {
|
|
579
|
-
const result = await telegram.getMessageReactions(chatId, messageId);
|
|
580
|
-
if (result.reactions.length === 0) {
|
|
581
|
-
return { content: [{ type: "text", text: `No reactions on message ${messageId}` }] };
|
|
582
|
-
}
|
|
583
|
-
const lines = result.reactions.map((r) => {
|
|
584
|
-
const usersStr = r.users.length > 0 ? `: ${r.users.map((u) => u.name).join(", ")}` : "";
|
|
585
|
-
return `${r.emoji} × ${r.count}${usersStr}`;
|
|
586
|
-
});
|
|
587
|
-
lines.push(`\nTotal: ${result.total} reactions`);
|
|
588
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
589
|
-
}
|
|
590
|
-
catch (e) {
|
|
591
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
592
|
-
}
|
|
593
|
-
});
|
|
594
|
-
server.tool("telegram-send-scheduled", "Send a scheduled message to a Telegram chat. The message will be delivered at the specified time by Telegram servers", {
|
|
595
|
-
chatId: z.string().describe("Chat ID or username (use 'me' or 'self' for Saved Messages)"),
|
|
596
|
-
text: z.string().describe("Message text"),
|
|
597
|
-
scheduleDate: z.number().describe("Unix timestamp when to send the message (must be in the future)"),
|
|
598
|
-
replyTo: z.number().optional().describe("Message ID to reply to"),
|
|
599
|
-
parseMode: z.enum(["md", "html"]).optional().describe("Message format: md (Markdown) or html"),
|
|
600
|
-
}, async ({ chatId, text, scheduleDate, replyTo, parseMode }) => {
|
|
601
|
-
const err = await requireConnection();
|
|
602
|
-
if (err)
|
|
603
|
-
return { content: [{ type: "text", text: err }] };
|
|
604
|
-
// Resolve 'me'/'self' to Saved Messages
|
|
605
|
-
let target = chatId;
|
|
606
|
-
if (target === "me" || target === "self") {
|
|
607
|
-
try {
|
|
608
|
-
const me = await telegram.getMe();
|
|
609
|
-
target = me.id;
|
|
610
|
-
}
|
|
611
|
-
catch {
|
|
612
|
-
return { content: [{ type: "text", text: "Failed to resolve Saved Messages" }] };
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
try {
|
|
616
|
-
await telegram.sendScheduledMessage(target, text, scheduleDate, replyTo, parseMode);
|
|
617
|
-
const date = new Date(scheduleDate * 1000).toISOString();
|
|
618
|
-
return { content: [{ type: "text", text: `Message scheduled for ${date} in ${chatId}` }] };
|
|
619
|
-
}
|
|
620
|
-
catch (e) {
|
|
621
|
-
return { content: [{ type: "text", text: `Schedule error: ${e.message}` }] };
|
|
622
|
-
}
|
|
623
|
-
});
|
|
624
|
-
server.tool("telegram-create-poll", "Create a poll in a Telegram chat", {
|
|
625
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
626
|
-
question: z.string().describe("Poll question"),
|
|
627
|
-
answers: z.array(z.string()).min(2).max(10).describe("Answer options (2-10)"),
|
|
628
|
-
multipleChoice: z.boolean().default(false).describe("Allow multiple answers"),
|
|
629
|
-
quiz: z.boolean().default(false).describe("Quiz mode (one correct answer)"),
|
|
630
|
-
correctAnswer: z.number().optional().describe("Index of correct answer (0-based, required for quiz mode)"),
|
|
631
|
-
}, async ({ chatId, question, answers, multipleChoice, quiz, correctAnswer }) => {
|
|
632
|
-
const err = await requireConnection();
|
|
633
|
-
if (err)
|
|
634
|
-
return { content: [{ type: "text", text: err }] };
|
|
635
|
-
try {
|
|
636
|
-
const msgId = await telegram.createPoll(chatId, question, answers, { multipleChoice, quiz, correctAnswer });
|
|
637
|
-
return { content: [{ type: "text", text: `Poll created in ${chatId}${msgId ? ` (message #${msgId})` : ""}` }] };
|
|
638
|
-
}
|
|
639
|
-
catch (e) {
|
|
640
|
-
return { content: [{ type: "text", text: `Poll error: ${e.message}` }] };
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
|
-
server.tool("telegram-get-contact-requests", "Get incoming messages from non-contacts (contact requests). Shows who messaged you without being in your contacts, with message preview", {
|
|
644
|
-
limit: z.number().default(20).describe("Number of contact requests to return"),
|
|
645
|
-
}, async ({ limit }) => {
|
|
646
|
-
const err = await requireConnection();
|
|
647
|
-
if (err)
|
|
648
|
-
return { content: [{ type: "text", text: err }] };
|
|
649
|
-
try {
|
|
650
|
-
const requests = await telegram.getContactRequests(limit);
|
|
651
|
-
if (requests.length === 0) {
|
|
652
|
-
return { content: [{ type: "text", text: "No contact requests" }] };
|
|
653
|
-
}
|
|
654
|
-
const text = requests
|
|
655
|
-
.map((r) => {
|
|
656
|
-
const tag = r.isBot ? "[bot]" : "[user]";
|
|
657
|
-
const username = r.username ? ` @${r.username}` : "";
|
|
658
|
-
const unread = r.unreadCount > 0 ? ` [${r.unreadCount} unread]` : "";
|
|
659
|
-
const preview = r.lastMessage ? `\n > ${r.lastMessage.slice(0, 100)}` : "";
|
|
660
|
-
return `${tag} ${r.name}${username} (${r.id})${unread}${preview}`;
|
|
661
|
-
})
|
|
662
|
-
.join("\n");
|
|
663
|
-
return { content: [{ type: "text", text: sanitize(text) }] };
|
|
664
|
-
}
|
|
665
|
-
catch (e) {
|
|
666
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
667
|
-
}
|
|
668
|
-
});
|
|
669
|
-
server.tool("telegram-add-contact", "Add a user to your Telegram contacts. Use this to accept contact requests from non-contacts", {
|
|
670
|
-
userId: z.string().describe("User ID or username to add"),
|
|
671
|
-
firstName: z.string().describe("First name for the contact"),
|
|
672
|
-
lastName: z.string().optional().describe("Last name for the contact"),
|
|
673
|
-
phone: z.string().optional().describe("Phone number for the contact"),
|
|
674
|
-
}, async ({ userId, firstName, lastName, phone }) => {
|
|
675
|
-
const err = await requireConnection();
|
|
676
|
-
if (err)
|
|
677
|
-
return { content: [{ type: "text", text: err }] };
|
|
678
|
-
try {
|
|
679
|
-
await telegram.addContact(userId, firstName, lastName, phone);
|
|
680
|
-
return {
|
|
681
|
-
content: [{ type: "text", text: `Contact added: ${firstName}${lastName ? ` ${lastName}` : ""} (${userId})` }],
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
catch (e) {
|
|
685
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
686
|
-
}
|
|
687
|
-
});
|
|
688
|
-
server.tool("telegram-block-user", "Block a Telegram user. Blocked users cannot send you messages", {
|
|
689
|
-
userId: z.string().describe("User ID or username to block"),
|
|
690
|
-
}, async ({ userId }) => {
|
|
691
|
-
const err = await requireConnection();
|
|
692
|
-
if (err)
|
|
693
|
-
return { content: [{ type: "text", text: err }] };
|
|
694
|
-
try {
|
|
695
|
-
await telegram.blockUser(userId);
|
|
696
|
-
return { content: [{ type: "text", text: `User blocked: ${userId}` }] };
|
|
697
|
-
}
|
|
698
|
-
catch (e) {
|
|
699
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
700
|
-
}
|
|
701
|
-
});
|
|
702
|
-
server.tool("telegram-report-spam", "Report a chat as spam to Telegram", {
|
|
703
|
-
chatId: z.string().describe("Chat ID or username to report"),
|
|
704
|
-
}, async ({ chatId }) => {
|
|
705
|
-
const err = await requireConnection();
|
|
706
|
-
if (err)
|
|
707
|
-
return { content: [{ type: "text", text: err }] };
|
|
708
|
-
try {
|
|
709
|
-
await telegram.reportSpam(chatId);
|
|
710
|
-
return { content: [{ type: "text", text: `Reported as spam: ${chatId}` }] };
|
|
711
|
-
}
|
|
712
|
-
catch (e) {
|
|
713
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
714
|
-
}
|
|
715
|
-
});
|
|
716
|
-
server.tool("telegram-list-topics", "List forum topics in a Telegram group with Topics enabled. Shows topic names, unread counts, and status", {
|
|
717
|
-
chatId: z.string().describe("Chat ID or username of a group with Topics enabled"),
|
|
718
|
-
limit: z.number().default(100).describe("Max topics to return"),
|
|
719
|
-
}, async ({ chatId, limit }) => {
|
|
720
|
-
const err = await requireConnection();
|
|
721
|
-
if (err)
|
|
722
|
-
return { content: [{ type: "text", text: err }] };
|
|
723
|
-
try {
|
|
724
|
-
const topics = await telegram.getForumTopics(chatId, limit);
|
|
725
|
-
const text = topics
|
|
726
|
-
.map((t) => {
|
|
727
|
-
const flags = [t.pinned ? "pinned" : "", t.closed ? "closed" : ""].filter(Boolean).join(", ");
|
|
728
|
-
const flagStr = flags ? ` [${flags}]` : "";
|
|
729
|
-
const unread = t.unreadCount > 0 ? ` [${t.unreadCount} unread]` : "";
|
|
730
|
-
return `# ${t.title} (id: ${t.id})${flagStr}${unread}`;
|
|
731
|
-
})
|
|
732
|
-
.join("\n");
|
|
733
|
-
return { content: [{ type: "text", text: sanitize(text) || "No topics found" }] };
|
|
734
|
-
}
|
|
735
|
-
catch (e) {
|
|
736
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
737
|
-
}
|
|
738
|
-
});
|
|
739
|
-
server.tool("telegram-read-topic-messages", "Read messages from a specific forum topic in a Telegram group", {
|
|
740
|
-
chatId: z.string().describe("Chat ID or username"),
|
|
741
|
-
topicId: z.number().describe("Topic ID (get from telegram-list-topics)"),
|
|
742
|
-
limit: z.number().default(20).describe("Number of messages to return"),
|
|
743
|
-
offsetId: z.number().optional().describe("Message ID to start from (for pagination)"),
|
|
744
|
-
}, async ({ chatId, topicId, limit, offsetId }) => {
|
|
745
|
-
const err = await requireConnection();
|
|
746
|
-
if (err)
|
|
747
|
-
return { content: [{ type: "text", text: err }] };
|
|
748
|
-
try {
|
|
749
|
-
const messages = await telegram.getTopicMessages(chatId, topicId, limit, offsetId);
|
|
750
|
-
const text = messages
|
|
751
|
-
.map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
|
|
752
|
-
.join("\n\n");
|
|
753
|
-
return { content: [{ type: "text", text: sanitize(text) || "No messages in this topic" }] };
|
|
754
|
-
}
|
|
755
|
-
catch (e) {
|
|
756
|
-
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
757
|
-
}
|
|
758
|
-
});
|
|
759
|
-
// --- Start ---
|
|
25
|
+
registerTools(server, telegram);
|
|
760
26
|
async function main() {
|
|
761
27
|
// Try to auto-connect with saved session
|
|
762
28
|
await telegram.loadSession();
|