@mseep/telegram-api-mcp 0.1.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/LICENSE +21 -0
- package/README.md +247 -0
- package/dist/circuit-breaker.d.ts +34 -0
- package/dist/circuit-breaker.d.ts.map +1 -0
- package/dist/circuit-breaker.js +64 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +22 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/method-registry.d.ts +78 -0
- package/dist/method-registry.d.ts.map +1 -0
- package/dist/method-registry.js +117 -0
- package/dist/method-registry.js.map +1 -0
- package/dist/methods/bot.d.ts +3 -0
- package/dist/methods/bot.d.ts.map +1 -0
- package/dist/methods/bot.js +258 -0
- package/dist/methods/bot.js.map +1 -0
- package/dist/methods/business.d.ts +3 -0
- package/dist/methods/business.d.ts.map +1 -0
- package/dist/methods/business.js +137 -0
- package/dist/methods/business.js.map +1 -0
- package/dist/methods/chat.d.ts +3 -0
- package/dist/methods/chat.d.ts.map +1 -0
- package/dist/methods/chat.js +458 -0
- package/dist/methods/chat.js.map +1 -0
- package/dist/methods/editing.d.ts +3 -0
- package/dist/methods/editing.d.ts.map +1 -0
- package/dist/methods/editing.js +153 -0
- package/dist/methods/editing.js.map +1 -0
- package/dist/methods/forum.d.ts +3 -0
- package/dist/methods/forum.d.ts.map +1 -0
- package/dist/methods/forum.js +184 -0
- package/dist/methods/forum.js.map +1 -0
- package/dist/methods/forwarding.d.ts +3 -0
- package/dist/methods/forwarding.d.ts.map +1 -0
- package/dist/methods/forwarding.js +84 -0
- package/dist/methods/forwarding.js.map +1 -0
- package/dist/methods/games.d.ts +3 -0
- package/dist/methods/games.d.ts.map +1 -0
- package/dist/methods/games.js +43 -0
- package/dist/methods/games.js.map +1 -0
- package/dist/methods/gifts.d.ts +3 -0
- package/dist/methods/gifts.d.ts.map +1 -0
- package/dist/methods/gifts.js +87 -0
- package/dist/methods/gifts.js.map +1 -0
- package/dist/methods/index.d.ts +13 -0
- package/dist/methods/index.d.ts.map +1 -0
- package/dist/methods/index.js +57 -0
- package/dist/methods/index.js.map +1 -0
- package/dist/methods/inline.d.ts +3 -0
- package/dist/methods/inline.d.ts.map +1 -0
- package/dist/methods/inline.js +32 -0
- package/dist/methods/inline.js.map +1 -0
- package/dist/methods/managed-bots.d.ts +3 -0
- package/dist/methods/managed-bots.d.ts.map +1 -0
- package/dist/methods/managed-bots.js +33 -0
- package/dist/methods/managed-bots.js.map +1 -0
- package/dist/methods/messages.d.ts +3 -0
- package/dist/methods/messages.d.ts.map +1 -0
- package/dist/methods/messages.js +357 -0
- package/dist/methods/messages.js.map +1 -0
- package/dist/methods/other.d.ts +3 -0
- package/dist/methods/other.d.ts.map +1 -0
- package/dist/methods/other.js +247 -0
- package/dist/methods/other.js.map +1 -0
- package/dist/methods/passport.d.ts +3 -0
- package/dist/methods/passport.d.ts.map +1 -0
- package/dist/methods/passport.js +15 -0
- package/dist/methods/passport.js.map +1 -0
- package/dist/methods/payments.d.ts +3 -0
- package/dist/methods/payments.d.ts.map +1 -0
- package/dist/methods/payments.js +128 -0
- package/dist/methods/payments.js.map +1 -0
- package/dist/methods/stickers.d.ts +3 -0
- package/dist/methods/stickers.d.ts.map +1 -0
- package/dist/methods/stickers.js +208 -0
- package/dist/methods/stickers.js.map +1 -0
- package/dist/methods/stories.d.ts +3 -0
- package/dist/methods/stories.d.ts.map +1 -0
- package/dist/methods/stories.js +58 -0
- package/dist/methods/stories.js.map +1 -0
- package/dist/methods/updates.d.ts +3 -0
- package/dist/methods/updates.d.ts.map +1 -0
- package/dist/methods/updates.js +48 -0
- package/dist/methods/updates.js.map +1 -0
- package/dist/post-log.d.ts +14 -0
- package/dist/post-log.d.ts.map +1 -0
- package/dist/post-log.js +48 -0
- package/dist/post-log.js.map +1 -0
- package/dist/rate-limiter.d.ts +25 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +93 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +287 -0
- package/dist/server.js.map +1 -0
- package/dist/telegram-client.d.ts +28 -0
- package/dist/telegram-client.d.ts.map +1 -0
- package/dist/telegram-client.js +254 -0
- package/dist/telegram-client.js.map +1 -0
- package/dist/trail.d.ts +86 -0
- package/dist/trail.d.ts.map +1 -0
- package/dist/trail.js +185 -0
- package/dist/trail.js.map +1 -0
- package/package.json +61 -0
- package/server.json +18 -0
- package/smithery.yaml +27 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { join, dirname } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { TelegramClient, TelegramApiError } from "./telegram-client.js";
|
|
7
|
+
import { buildZodSchema } from "./method-registry.js";
|
|
8
|
+
import { allMethods, searchMethods, findMethodByApiName } from "./methods/index.js";
|
|
9
|
+
import { CircuitOpenError } from "./circuit-breaker.js";
|
|
10
|
+
import { Trail } from "./trail.js";
|
|
11
|
+
/** Pre-built Zod schemas for all methods (built once, reused on every call). */
|
|
12
|
+
const schemaCache = new Map();
|
|
13
|
+
/** Module-level TRAIL instance for auto-logging in callTelegram. */
|
|
14
|
+
let trailInstance = null;
|
|
15
|
+
function getSchema(method) {
|
|
16
|
+
let schema = schemaCache.get(method.apiMethod);
|
|
17
|
+
if (!schema) {
|
|
18
|
+
schema = buildZodSchema(method.params);
|
|
19
|
+
schemaCache.set(method.apiMethod, schema);
|
|
20
|
+
}
|
|
21
|
+
return schema;
|
|
22
|
+
}
|
|
23
|
+
export async function startServer(config) {
|
|
24
|
+
const client = new TelegramClient(config);
|
|
25
|
+
const server = new McpServer({
|
|
26
|
+
name: "telegram-api-mcp",
|
|
27
|
+
version: "0.1.0",
|
|
28
|
+
});
|
|
29
|
+
// Log before connect (connect blocks on stdio transport)
|
|
30
|
+
log("info", `Starting server in ${config.metaMode ? "meta" : "standard"} mode with ${allMethods.length} methods`);
|
|
31
|
+
if (config.metaMode) {
|
|
32
|
+
registerMetaTools(server, client);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
registerAllTools(server, client);
|
|
36
|
+
}
|
|
37
|
+
trailInstance = registerTrailTools(server);
|
|
38
|
+
registerDownloadTool(server, client);
|
|
39
|
+
const shutdown = () => {
|
|
40
|
+
client.destroy();
|
|
41
|
+
process.exit(0);
|
|
42
|
+
};
|
|
43
|
+
process.on("SIGINT", shutdown);
|
|
44
|
+
process.on("SIGTERM", shutdown);
|
|
45
|
+
const transport = new StdioServerTransport();
|
|
46
|
+
await server.connect(transport);
|
|
47
|
+
}
|
|
48
|
+
// ─── Standard Mode ──────────────────────────────────────────────────────
|
|
49
|
+
function registerAllTools(server, client) {
|
|
50
|
+
for (const method of allMethods) {
|
|
51
|
+
const zodSchema = getSchema(method);
|
|
52
|
+
const annotations = method.annotations || { destructiveHint: false };
|
|
53
|
+
server.tool(method.toolName, method.description, zodSchema.shape, annotations, async (params) => {
|
|
54
|
+
return callTelegram(client, method, params);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// ─── Meta Mode ──────────────────────────────────────────────────────────
|
|
59
|
+
function registerMetaTools(server, client) {
|
|
60
|
+
server.tool("telegram_find", "Search Telegram Bot API methods by keyword. Returns matching methods with their parameters. Use this to discover available methods before calling them.", {
|
|
61
|
+
query: z.string().optional().describe("Search keyword (e.g. 'send photo', 'ban', 'poll', 'sticker')"),
|
|
62
|
+
category: z.string().optional().describe("Filter by category: messages, media, polls, chat, members, invite, forum, stickers, inline, payments, business, stories, gifts, games, bot, updates, editing, forwarding, managed_bots, passport, other"),
|
|
63
|
+
}, async (params) => {
|
|
64
|
+
let results = params.query ? searchMethods(params.query) : allMethods;
|
|
65
|
+
if (params.category) {
|
|
66
|
+
results = results.filter((m) => m.category === params.category);
|
|
67
|
+
}
|
|
68
|
+
if (results.length === 0) {
|
|
69
|
+
return { content: [{ type: "text", text: "No methods found. Try a different keyword." }] };
|
|
70
|
+
}
|
|
71
|
+
const text = results
|
|
72
|
+
.slice(0, 20)
|
|
73
|
+
.map((m) => {
|
|
74
|
+
const required = m.params.filter((p) => p.required).map((p) => p.name);
|
|
75
|
+
const optional = m.params.filter((p) => !p.required).map((p) => p.name).slice(0, 5);
|
|
76
|
+
return [
|
|
77
|
+
`**${m.apiMethod}** (tool: ${m.toolName}) [${m.category}]`,
|
|
78
|
+
` ${m.description}`,
|
|
79
|
+
` Required: ${required.join(", ") || "none"}`,
|
|
80
|
+
` Optional: ${optional.join(", ")}${m.params.filter((p) => !p.required).length > 5 ? " ..." : ""}`,
|
|
81
|
+
` Returns: ${m.returns}`,
|
|
82
|
+
].join("\n");
|
|
83
|
+
})
|
|
84
|
+
.join("\n\n");
|
|
85
|
+
return {
|
|
86
|
+
content: [{
|
|
87
|
+
type: "text",
|
|
88
|
+
text: `Found ${results.length} method(s):\n\n${text}${results.length > 20 ? `\n\n... and ${results.length - 20} more. Narrow your search.` : ""}`,
|
|
89
|
+
}],
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
server.tool("telegram_call", "Call any Telegram Bot API method. Use telegram_find first to discover method names and required parameters.", {
|
|
93
|
+
method: z.string().describe("API method name (e.g. sendMessage, banChatMember)"),
|
|
94
|
+
params: z.record(z.unknown()).optional().describe("Method parameters as JSON object"),
|
|
95
|
+
}, async (args) => {
|
|
96
|
+
const methodDef = findMethodByApiName(args.method);
|
|
97
|
+
if (!methodDef) {
|
|
98
|
+
return {
|
|
99
|
+
content: [{ type: "text", text: `Unknown method: "${args.method}". Use telegram_find to search for the correct method name. Method names are camelCase (e.g. sendMessage, not send_message).` }],
|
|
100
|
+
isError: true,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return executeMethod(client, methodDef, (args.params || {}));
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
// ─── TRAIL ─────────────────────────────────────────────────────────────
|
|
107
|
+
function registerTrailTools(server) {
|
|
108
|
+
const dataDir = join(dirname(fileURLToPath(import.meta.url)), "..", "data");
|
|
109
|
+
const trail = new Trail(dataDir, "telegram-mcp");
|
|
110
|
+
server.tool("get_trail", "Query the TRAIL content log. Check what content was posted, failed, or skipped across pipelines.", {
|
|
111
|
+
content_id: z.string().optional().describe("Filter by content ID (exact or prefix like 'civitai:image:')"),
|
|
112
|
+
action: z.union([z.string(), z.array(z.string())]).optional().describe("Filter by action (posted, failed, skipped, etc.)"),
|
|
113
|
+
requester: z.string().optional().describe("Filter by workflow/task ID"),
|
|
114
|
+
trace_id: z.string().optional().describe("Filter by pipeline trace ID"),
|
|
115
|
+
since: z.string().optional().describe("ISO 8601 timestamp — only entries after this time"),
|
|
116
|
+
limit: z.number().int().min(0).max(500).optional().describe("Max entries, newest first (default: 50, 0 = all)"),
|
|
117
|
+
offset: z.number().int().min(0).optional().describe("Entries to skip for pagination (default: 0)"),
|
|
118
|
+
}, { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }, async (params) => {
|
|
119
|
+
const { entries, total } = await trail.query({
|
|
120
|
+
content_id: params.content_id,
|
|
121
|
+
action: params.action,
|
|
122
|
+
requester: params.requester,
|
|
123
|
+
trace_id: params.trace_id,
|
|
124
|
+
since: params.since,
|
|
125
|
+
limit: params.limit ?? 50,
|
|
126
|
+
offset: params.offset,
|
|
127
|
+
});
|
|
128
|
+
if (entries.length === 0) {
|
|
129
|
+
return { content: [{ type: "text", text: "No trail entries found." }] };
|
|
130
|
+
}
|
|
131
|
+
const lines = entries.map((e) => {
|
|
132
|
+
const d = e.details ?? {};
|
|
133
|
+
const detailParts = Object.entries(d)
|
|
134
|
+
.filter(([, v]) => typeof v !== "object")
|
|
135
|
+
.map(([k, v]) => `${k}=${v}`);
|
|
136
|
+
const detailStr = detailParts.length ? ` — ${detailParts.join(", ")}` : "";
|
|
137
|
+
return `[${e.timestamp}] ${e.action} ${e.content_id} (req: ${e.requester})${detailStr}`;
|
|
138
|
+
});
|
|
139
|
+
return { content: [{ type: "text", text: `TRAIL Log (${entries.length}/${total}):\n\n${lines.join("\n")}` }] };
|
|
140
|
+
});
|
|
141
|
+
server.tool("mark_trail", "Write an entry to the TRAIL content log. Use to explicitly record content actions.", {
|
|
142
|
+
content_id: z.string().describe("Content ID in format source:type:id"),
|
|
143
|
+
action: z.string().describe("Action: fetched, selected, posted, failed, skipped"),
|
|
144
|
+
requester: z.string().describe("Workflow/task ID"),
|
|
145
|
+
details: z.record(z.unknown()).optional().describe("Platform-specific data"),
|
|
146
|
+
trace_id: z.string().optional().describe("Pipeline trace ID"),
|
|
147
|
+
tags: z.array(z.string()).optional().describe("Labels for filtering"),
|
|
148
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, async (params) => {
|
|
149
|
+
await trail.append(params.content_id, params.action, params.requester, {
|
|
150
|
+
details: params.details,
|
|
151
|
+
trace_id: params.trace_id,
|
|
152
|
+
tags: params.tags,
|
|
153
|
+
});
|
|
154
|
+
return { content: [{ type: "text", text: `Trail entry recorded: ${params.action} ${params.content_id} (req: ${params.requester})` }] };
|
|
155
|
+
});
|
|
156
|
+
server.tool("get_trail_stats", "Get summary statistics from the TRAIL content log.", {
|
|
157
|
+
requester: z.string().optional().describe("Filter by workflow/task ID"),
|
|
158
|
+
since: z.string().optional().describe("ISO 8601 timestamp — only count entries after this"),
|
|
159
|
+
}, { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }, async (params) => {
|
|
160
|
+
const stats = await trail.stats(params.requester, params.since);
|
|
161
|
+
const actionLines = Object.entries(stats.by_action)
|
|
162
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
163
|
+
.map(([act, count]) => ` ${act}: ${count}`)
|
|
164
|
+
.join("\n");
|
|
165
|
+
const text = [
|
|
166
|
+
`TRAIL Statistics:`,
|
|
167
|
+
`Total entries: ${stats.total_entries}`,
|
|
168
|
+
`Unique content IDs: ${stats.unique_content_ids}`,
|
|
169
|
+
`First entry: ${stats.first_entry ?? "N/A"}`,
|
|
170
|
+
`Last entry: ${stats.last_entry ?? "N/A"}`,
|
|
171
|
+
`By action:\n${actionLines}`,
|
|
172
|
+
].join("\n");
|
|
173
|
+
return { content: [{ type: "text", text }] };
|
|
174
|
+
});
|
|
175
|
+
return trail;
|
|
176
|
+
}
|
|
177
|
+
// ─── Download ───────────────────────────────────────────────────────────
|
|
178
|
+
function registerDownloadTool(server, client) {
|
|
179
|
+
const defaultDir = process.env.TELEGRAM_DOWNLOAD_DIR || (process.platform === "win32"
|
|
180
|
+
? `${process.env.USERPROFILE || "C:\\Users\\user"}\\Downloads\\telegram-mcp`
|
|
181
|
+
: `${process.env.HOME || "/tmp"}/telegram-mcp-downloads`);
|
|
182
|
+
server.tool("download_file", "Download a file from Telegram by file_id. Saves locally and returns the path. Use after receiving a message with a photo, video, document, voice, etc. Max 20MB (Telegram Bot API limit).", {
|
|
183
|
+
file_id: z.string().describe("File ID from a message (photo[-1].file_id, document.file_id, video.file_id, etc.)"),
|
|
184
|
+
dest_dir: z.string().optional().describe(`Directory to save to (default: ${defaultDir})`),
|
|
185
|
+
}, { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }, async (params) => {
|
|
186
|
+
try {
|
|
187
|
+
const dir = params.dest_dir || defaultDir;
|
|
188
|
+
const localPath = await client.downloadFile(params.file_id, dir);
|
|
189
|
+
return {
|
|
190
|
+
content: [{ type: "text", text: `Downloaded to: ${localPath}` }],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
return {
|
|
195
|
+
content: [{ type: "text", text: `Download failed: ${error.message}` }],
|
|
196
|
+
isError: true,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
// ─── Execution ──────────────────────────────────────────────────────────
|
|
202
|
+
/** For meta-mode: validate then call. */
|
|
203
|
+
async function executeMethod(client, method, params) {
|
|
204
|
+
const schema = getSchema(method);
|
|
205
|
+
const parseResult = schema.safeParse(params);
|
|
206
|
+
if (!parseResult.success) {
|
|
207
|
+
const errors = parseResult.error.issues
|
|
208
|
+
.map((i) => ` ${i.path.join(".")}: ${i.message}`)
|
|
209
|
+
.join("\n");
|
|
210
|
+
const required = method.params.filter((p) => p.required).map((p) => p.name);
|
|
211
|
+
return {
|
|
212
|
+
content: [{ type: "text", text: `Validation error for ${method.apiMethod}:\n${errors}\n\nRequired params: ${required.join(", ") || "none"}. Use telegram_find(query: "${method.apiMethod}") to see full parameter list.` }],
|
|
213
|
+
isError: true,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return callTelegram(client, method, parseResult.data);
|
|
217
|
+
}
|
|
218
|
+
/** Methods that produce posts/messages worth logging. */
|
|
219
|
+
const LOGGED_METHODS = new Set([
|
|
220
|
+
"sendMessage", "sendPhoto", "sendVideo", "sendDocument", "sendAudio",
|
|
221
|
+
"sendAnimation", "sendVoice", "sendVideoNote", "sendMediaGroup",
|
|
222
|
+
"sendPaidMedia", "sendSticker", "sendLocation", "sendVenue", "sendContact",
|
|
223
|
+
"sendPoll", "sendDice", "sendChecklist", "sendGame", "sendInvoice",
|
|
224
|
+
"sendGift", "sendMessageDraft", "forwardMessage", "forwardMessages",
|
|
225
|
+
"copyMessage", "copyMessages", "postStory",
|
|
226
|
+
]);
|
|
227
|
+
/** For standard mode: already validated by SDK, just call. */
|
|
228
|
+
async function callTelegram(client, method, params) {
|
|
229
|
+
try {
|
|
230
|
+
const result = await client.call(method.apiMethod, params);
|
|
231
|
+
// Auto-log send/forward/copy/post calls
|
|
232
|
+
if (LOGGED_METHODS.has(method.apiMethod) && result != null) {
|
|
233
|
+
const r = (Array.isArray(result) ? result[0] : result);
|
|
234
|
+
// TRAIL auto-logging
|
|
235
|
+
const _trail = params._trail;
|
|
236
|
+
const trailCid = (_trail?.content_id ?? params.content_id);
|
|
237
|
+
const trailReq = (_trail?.requester ?? params.requester);
|
|
238
|
+
if (trailCid && trailReq && trailInstance) {
|
|
239
|
+
trailInstance.append(trailCid, "posted", trailReq, {
|
|
240
|
+
details: {
|
|
241
|
+
platform: "telegram",
|
|
242
|
+
platform_id: String(r?.message_id ?? ""),
|
|
243
|
+
chat_id: String(params.chat_id ?? ""),
|
|
244
|
+
},
|
|
245
|
+
trace_id: (_trail?.trace_id ?? params.trace_id),
|
|
246
|
+
}).catch((err) => {
|
|
247
|
+
log("warn", `trail write failed: ${err.message}`);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const text = formatResult(method.apiMethod, result);
|
|
252
|
+
return { content: [{ type: "text", text }] };
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
const message = error instanceof CircuitOpenError
|
|
256
|
+
? error.message
|
|
257
|
+
: error instanceof TelegramApiError
|
|
258
|
+
? error.message
|
|
259
|
+
: `Unexpected error: ${error.message}`;
|
|
260
|
+
return {
|
|
261
|
+
content: [{ type: "text", text: message }],
|
|
262
|
+
isError: true,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const MAX_RESPONSE_LENGTH = 100_000;
|
|
267
|
+
function formatResult(method, result) {
|
|
268
|
+
if (result === true)
|
|
269
|
+
return `${method}: Success`;
|
|
270
|
+
let text;
|
|
271
|
+
if (typeof result === "object" && result !== null) {
|
|
272
|
+
text = JSON.stringify(result, null, 2);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
text = String(result);
|
|
276
|
+
}
|
|
277
|
+
// Truncate to prevent filling context window
|
|
278
|
+
if (text.length > MAX_RESPONSE_LENGTH) {
|
|
279
|
+
text = text.slice(0, MAX_RESPONSE_LENGTH) + `\n\n... [truncated, ${text.length} chars total]`;
|
|
280
|
+
}
|
|
281
|
+
return text;
|
|
282
|
+
}
|
|
283
|
+
function log(level, msg) {
|
|
284
|
+
const ts = new Date().toISOString();
|
|
285
|
+
process.stderr.write(`[${ts}] [${level.toUpperCase()}] ${msg}\n`);
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Config } from "./config.js";
|
|
2
|
+
export declare class TelegramClient {
|
|
3
|
+
private baseUrl;
|
|
4
|
+
private token;
|
|
5
|
+
private rateLimiter;
|
|
6
|
+
private circuitBreaker;
|
|
7
|
+
private config;
|
|
8
|
+
private cleanupInterval;
|
|
9
|
+
constructor(config: Config);
|
|
10
|
+
destroy(): void;
|
|
11
|
+
call(method: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
12
|
+
private applyDefaults;
|
|
13
|
+
private callWithRetry;
|
|
14
|
+
private callJson;
|
|
15
|
+
private callMultipart;
|
|
16
|
+
private handleResponse;
|
|
17
|
+
private hasFileParams;
|
|
18
|
+
private isLocalFile;
|
|
19
|
+
private readLocalFile;
|
|
20
|
+
/** Download a file by file_id. Returns the local path. */
|
|
21
|
+
downloadFile(fileId: string, destDir: string): Promise<string>;
|
|
22
|
+
}
|
|
23
|
+
export declare class TelegramApiError extends Error {
|
|
24
|
+
statusCode?: number;
|
|
25
|
+
retryAfter?: number;
|
|
26
|
+
constructor(message: string, statusCode?: number, retryAfter?: number);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=telegram-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram-client.d.ts","sourceRoot":"","sources":["../src/telegram-client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AA2BrC,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAiC;gBAE5C,MAAM,EAAE,MAAM;IAmB1B,OAAO,IAAI,IAAI;IAIT,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWlF,OAAO,CAAC,aAAa;YAWP,aAAa;YAqDb,QAAQ;YAkBR,aAAa;YAgCb,cAAc;IA0B5B,OAAO,CAAC,aAAa;YAeP,WAAW;YAUX,aAAa;IA8B3B,0DAA0D;IACpD,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CA2CrE;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;gBAER,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAMtE"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { readFile, writeFile, stat, mkdir } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, isAbsolute, join, resolve, normalize, sep } from "node:path";
|
|
3
|
+
import { RateLimiter } from "./rate-limiter.js";
|
|
4
|
+
import { CircuitBreaker, CircuitOpenError } from "./circuit-breaker.js";
|
|
5
|
+
/** Default fetch timeout: 60 seconds */
|
|
6
|
+
const FETCH_TIMEOUT_MS = 60_000;
|
|
7
|
+
function log(level, msg) {
|
|
8
|
+
const ts = new Date().toISOString();
|
|
9
|
+
process.stderr.write(`[${ts}] [${level.toUpperCase()}] ${msg}\n`);
|
|
10
|
+
}
|
|
11
|
+
function maskToken(str, token) {
|
|
12
|
+
return str.replaceAll(token, "***");
|
|
13
|
+
}
|
|
14
|
+
export class TelegramClient {
|
|
15
|
+
baseUrl;
|
|
16
|
+
token;
|
|
17
|
+
rateLimiter;
|
|
18
|
+
circuitBreaker;
|
|
19
|
+
config;
|
|
20
|
+
cleanupInterval;
|
|
21
|
+
constructor(config) {
|
|
22
|
+
this.config = config;
|
|
23
|
+
this.token = config.botToken;
|
|
24
|
+
this.baseUrl = `https://api.telegram.org/bot${this.token}`;
|
|
25
|
+
this.rateLimiter = new RateLimiter(config.globalRateLimit, config.perChatRateLimit);
|
|
26
|
+
this.circuitBreaker = new CircuitBreaker(config.circuitBreakerThreshold, config.circuitBreakerCooldown);
|
|
27
|
+
this.cleanupInterval = setInterval(() => this.rateLimiter.cleanup(), 60_000);
|
|
28
|
+
this.cleanupInterval.unref(); // Don't prevent Node.js from exiting
|
|
29
|
+
// Warn if no upload directory restrictions
|
|
30
|
+
if (config.allowedUploadDirs.length === 0) {
|
|
31
|
+
log("warn", "TELEGRAM_ALLOWED_UPLOAD_DIRS not set — file uploads unrestricted. Set it to restrict paths.");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
destroy() {
|
|
35
|
+
clearInterval(this.cleanupInterval);
|
|
36
|
+
}
|
|
37
|
+
async call(method, params = {}) {
|
|
38
|
+
const resolvedParams = this.applyDefaults(params);
|
|
39
|
+
const chatId = resolvedParams.chat_id;
|
|
40
|
+
this.circuitBreaker.check();
|
|
41
|
+
await this.rateLimiter.acquire(chatId);
|
|
42
|
+
const hasFiles = this.hasFileParams(resolvedParams);
|
|
43
|
+
return this.callWithRetry(method, resolvedParams, hasFiles);
|
|
44
|
+
}
|
|
45
|
+
applyDefaults(params) {
|
|
46
|
+
const result = { ...params };
|
|
47
|
+
if (!result.chat_id && this.config.defaultChatId) {
|
|
48
|
+
result.chat_id = this.config.defaultChatId;
|
|
49
|
+
}
|
|
50
|
+
if (!result.message_thread_id && this.config.defaultThreadId) {
|
|
51
|
+
result.message_thread_id = this.config.defaultThreadId;
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
async callWithRetry(method, params, hasFiles, attempt = 1) {
|
|
56
|
+
try {
|
|
57
|
+
const result = hasFiles
|
|
58
|
+
? await this.callMultipart(method, params)
|
|
59
|
+
: await this.callJson(method, params);
|
|
60
|
+
this.circuitBreaker.recordSuccess();
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (error instanceof CircuitOpenError)
|
|
65
|
+
throw error;
|
|
66
|
+
const err = error;
|
|
67
|
+
// Don't retry 4xx (except 429)
|
|
68
|
+
if (err.statusCode && err.statusCode >= 400 && err.statusCode < 500 && err.statusCode !== 429) {
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
// 429: respect retry_after (or default 5s if not provided)
|
|
72
|
+
if (err.statusCode === 429) {
|
|
73
|
+
if (attempt <= this.config.maxRetries) {
|
|
74
|
+
const retryAfter = err.retryAfter ?? 5;
|
|
75
|
+
const waitMs = retryAfter * 1000;
|
|
76
|
+
log("warn", `Rate limited on ${method}, waiting ${retryAfter}s (attempt ${attempt}/${this.config.maxRetries})`);
|
|
77
|
+
await sleep(waitMs);
|
|
78
|
+
return this.callWithRetry(method, params, hasFiles, attempt + 1);
|
|
79
|
+
}
|
|
80
|
+
throw error; // Exhausted retries on 429
|
|
81
|
+
}
|
|
82
|
+
// Record failure for circuit breaker (429 already handled above)
|
|
83
|
+
const justOpened = this.circuitBreaker.recordFailure(err.statusCode);
|
|
84
|
+
if (justOpened) {
|
|
85
|
+
log("error", `Circuit breaker OPENED after ${this.config.circuitBreakerThreshold} failures`);
|
|
86
|
+
}
|
|
87
|
+
// Retry on transient errors (5xx, network)
|
|
88
|
+
if (attempt < this.config.maxRetries) {
|
|
89
|
+
const backoffMs = Math.min(1000 * Math.pow(2, attempt - 1), 10_000);
|
|
90
|
+
log("warn", `Retrying ${method} in ${backoffMs}ms (attempt ${attempt + 1}/${this.config.maxRetries})`);
|
|
91
|
+
await sleep(backoffMs);
|
|
92
|
+
return this.callWithRetry(method, params, hasFiles, attempt + 1);
|
|
93
|
+
}
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async callJson(method, params) {
|
|
98
|
+
const url = `${this.baseUrl}/${method}`;
|
|
99
|
+
const controller = new AbortController();
|
|
100
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
101
|
+
try {
|
|
102
|
+
const response = await fetch(url, {
|
|
103
|
+
method: "POST",
|
|
104
|
+
headers: { "Content-Type": "application/json" },
|
|
105
|
+
body: JSON.stringify(params),
|
|
106
|
+
signal: controller.signal,
|
|
107
|
+
});
|
|
108
|
+
return this.handleResponse(method, response);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
clearTimeout(timeout);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async callMultipart(method, params) {
|
|
115
|
+
const url = `${this.baseUrl}/${method}`;
|
|
116
|
+
const formData = new FormData();
|
|
117
|
+
for (const [key, value] of Object.entries(params)) {
|
|
118
|
+
if (value === undefined || value === null)
|
|
119
|
+
continue;
|
|
120
|
+
if (typeof value === "string" && (await this.isLocalFile(value))) {
|
|
121
|
+
const file = await this.readLocalFile(value);
|
|
122
|
+
formData.append(key, file, basename(value));
|
|
123
|
+
}
|
|
124
|
+
else if (typeof value === "object") {
|
|
125
|
+
formData.append(key, JSON.stringify(value));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
formData.append(key, String(value));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const controller = new AbortController();
|
|
132
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
133
|
+
try {
|
|
134
|
+
const response = await fetch(url, {
|
|
135
|
+
method: "POST",
|
|
136
|
+
body: formData,
|
|
137
|
+
signal: controller.signal,
|
|
138
|
+
});
|
|
139
|
+
return this.handleResponse(method, response);
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
clearTimeout(timeout);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async handleResponse(method, response) {
|
|
146
|
+
let data;
|
|
147
|
+
try {
|
|
148
|
+
data = (await response.json());
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
throw new TelegramApiError(`Failed to parse response from ${method} (HTTP ${response.status})`, response.status);
|
|
152
|
+
}
|
|
153
|
+
if (!data.ok) {
|
|
154
|
+
const description = data.description
|
|
155
|
+
? maskToken(data.description, this.token)
|
|
156
|
+
: "Unknown error";
|
|
157
|
+
throw new TelegramApiError(`${method}: ${description}`, data.error_code || response.status, data.parameters?.retry_after);
|
|
158
|
+
}
|
|
159
|
+
return data.result;
|
|
160
|
+
}
|
|
161
|
+
hasFileParams(params) {
|
|
162
|
+
const fileFields = new Set([
|
|
163
|
+
"photo", "audio", "document", "video", "animation", "voice",
|
|
164
|
+
"video_note", "sticker", "thumbnail", "certificate",
|
|
165
|
+
]);
|
|
166
|
+
for (const [key, value] of Object.entries(params)) {
|
|
167
|
+
if (fileFields.has(key) && typeof value === "string") {
|
|
168
|
+
if (isAbsolute(value))
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
async isLocalFile(value) {
|
|
175
|
+
if (!isAbsolute(value))
|
|
176
|
+
return false;
|
|
177
|
+
try {
|
|
178
|
+
const info = await stat(value);
|
|
179
|
+
return info.isFile();
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async readLocalFile(filePath) {
|
|
186
|
+
const resolved = resolve(normalize(filePath));
|
|
187
|
+
// Path traversal protection: require trailing separator in comparison
|
|
188
|
+
if (this.config.allowedUploadDirs.length > 0) {
|
|
189
|
+
const isAllowed = this.config.allowedUploadDirs.some((dir) => {
|
|
190
|
+
const normalizedDir = resolve(normalize(dir));
|
|
191
|
+
const dirWithSep = normalizedDir.endsWith(sep) ? normalizedDir : normalizedDir + sep;
|
|
192
|
+
return resolved.startsWith(dirWithSep) || resolved === normalizedDir;
|
|
193
|
+
});
|
|
194
|
+
if (!isAllowed) {
|
|
195
|
+
throw new Error(`File upload blocked: ${resolved} is not in allowed directories. ` +
|
|
196
|
+
`Set TELEGRAM_ALLOWED_UPLOAD_DIRS to allow specific paths.`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const info = await stat(resolved);
|
|
200
|
+
if (info.size > this.config.maxFileSize) {
|
|
201
|
+
throw new Error(`File too large: ${(info.size / 1024 / 1024).toFixed(1)}MB exceeds ` +
|
|
202
|
+
`limit of ${(this.config.maxFileSize / 1024 / 1024).toFixed(0)}MB`);
|
|
203
|
+
}
|
|
204
|
+
const buffer = await readFile(resolved);
|
|
205
|
+
return new Blob([buffer]);
|
|
206
|
+
}
|
|
207
|
+
/** Download a file by file_id. Returns the local path. */
|
|
208
|
+
async downloadFile(fileId, destDir) {
|
|
209
|
+
// Step 1: getFile to get file_path
|
|
210
|
+
const fileInfo = (await this.call("getFile", { file_id: fileId }));
|
|
211
|
+
if (!fileInfo.file_path) {
|
|
212
|
+
throw new Error("Telegram returned no file_path — file may be too large (>20MB)");
|
|
213
|
+
}
|
|
214
|
+
// Step 2: download from https://api.telegram.org/file/bot<token>/<file_path>
|
|
215
|
+
const url = `https://api.telegram.org/file/bot${this.token}/${fileInfo.file_path}`;
|
|
216
|
+
const controller = new AbortController();
|
|
217
|
+
const timeout = setTimeout(() => controller.abort(), 120_000);
|
|
218
|
+
try {
|
|
219
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
220
|
+
if (!response.ok) {
|
|
221
|
+
throw new Error(`Download failed: HTTP ${response.status}`);
|
|
222
|
+
}
|
|
223
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
224
|
+
// Determine filename from file_path
|
|
225
|
+
const fileName = fileInfo.file_path.split("/").pop() || `file_${fileId}`;
|
|
226
|
+
const destPath = resolve(normalize(join(destDir, fileName)));
|
|
227
|
+
// Security: ensure dest is inside destDir
|
|
228
|
+
const normalizedDir = resolve(normalize(destDir));
|
|
229
|
+
if (!destPath.startsWith(normalizedDir + sep) && destPath !== normalizedDir) {
|
|
230
|
+
throw new Error(`Path traversal blocked: ${destPath} is not inside ${normalizedDir}`);
|
|
231
|
+
}
|
|
232
|
+
await mkdir(dirname(destPath), { recursive: true });
|
|
233
|
+
await writeFile(destPath, buffer);
|
|
234
|
+
return destPath;
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
clearTimeout(timeout);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
export class TelegramApiError extends Error {
|
|
242
|
+
statusCode;
|
|
243
|
+
retryAfter;
|
|
244
|
+
constructor(message, statusCode, retryAfter) {
|
|
245
|
+
super(message);
|
|
246
|
+
this.name = "TelegramApiError";
|
|
247
|
+
this.statusCode = statusCode;
|
|
248
|
+
this.retryAfter = retryAfter;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function sleep(ms) {
|
|
252
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=telegram-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram-client.js","sourceRoot":"","sources":["../src/telegram-client.ts"],"names":[],"mappings":"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"}
|