@overpod/mcp-telegram 1.26.0 → 1.27.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.
Files changed (80) hide show
  1. package/dist/client.d.ts +12 -0
  2. package/dist/client.js +136 -0
  3. package/dist/index.js +15 -24
  4. package/dist/ipc-protocol.d.ts +19 -0
  5. package/dist/ipc-protocol.js +22 -0
  6. package/dist/lock.d.ts +12 -0
  7. package/dist/lock.js +83 -0
  8. package/dist/master.d.ts +1 -0
  9. package/dist/master.js +105 -0
  10. package/dist/rate-limiter.d.ts +1 -1
  11. package/dist/rate-limiter.js +8 -8
  12. package/dist/telegram-client.d.ts +6 -470
  13. package/dist/telegram-client.js +17 -874
  14. package/dist/telegram-helpers.d.ts +470 -0
  15. package/dist/telegram-helpers.js +870 -0
  16. package/dist/tools/account.js +7 -7
  17. package/dist/tools/boosts.js +4 -4
  18. package/dist/tools/chats.js +7 -7
  19. package/dist/tools/contacts.js +3 -3
  20. package/dist/tools/extras.js +3 -3
  21. package/dist/tools/group-calls.js +3 -3
  22. package/dist/tools/messages.js +17 -17
  23. package/dist/tools/quick-replies.js +3 -3
  24. package/dist/tools/reactions.js +2 -2
  25. package/dist/tools/shared.d.ts +3 -3
  26. package/dist/tools/shared.js +8 -7
  27. package/dist/tools/stars.js +3 -3
  28. package/dist/tools/stickers.js +5 -5
  29. package/dist/tools/stories.js +5 -5
  30. package/package.json +1 -1
  31. package/dist/__tests__/admin-log.test.d.ts +0 -1
  32. package/dist/__tests__/admin-log.test.js +0 -41
  33. package/dist/__tests__/approve-join-request.test.d.ts +0 -1
  34. package/dist/__tests__/approve-join-request.test.js +0 -107
  35. package/dist/__tests__/boosts.test.d.ts +0 -1
  36. package/dist/__tests__/boosts.test.js +0 -310
  37. package/dist/__tests__/broadcast-stats.test.d.ts +0 -1
  38. package/dist/__tests__/broadcast-stats.test.js +0 -172
  39. package/dist/__tests__/business-chat-links.test.d.ts +0 -1
  40. package/dist/__tests__/business-chat-links.test.js +0 -102
  41. package/dist/__tests__/get-message-buttons.test.d.ts +0 -1
  42. package/dist/__tests__/get-message-buttons.test.js +0 -122
  43. package/dist/__tests__/group-calls.test.d.ts +0 -1
  44. package/dist/__tests__/group-calls.test.js +0 -503
  45. package/dist/__tests__/inline-query-send.test.d.ts +0 -1
  46. package/dist/__tests__/inline-query-send.test.js +0 -94
  47. package/dist/__tests__/inline-query.test.d.ts +0 -1
  48. package/dist/__tests__/inline-query.test.js +0 -115
  49. package/dist/__tests__/megagroup-stats.test.d.ts +0 -1
  50. package/dist/__tests__/megagroup-stats.test.js +0 -166
  51. package/dist/__tests__/press-button.test.d.ts +0 -1
  52. package/dist/__tests__/press-button.test.js +0 -123
  53. package/dist/__tests__/quick-replies.test.d.ts +0 -1
  54. package/dist/__tests__/quick-replies.test.js +0 -245
  55. package/dist/__tests__/rate-limiter.test.d.ts +0 -1
  56. package/dist/__tests__/rate-limiter.test.js +0 -81
  57. package/dist/__tests__/reactions.test.d.ts +0 -1
  58. package/dist/__tests__/reactions.test.js +0 -23
  59. package/dist/__tests__/set-chat-permissions-merge.test.d.ts +0 -1
  60. package/dist/__tests__/set-chat-permissions-merge.test.js +0 -107
  61. package/dist/__tests__/set-chat-reactions.test.d.ts +0 -1
  62. package/dist/__tests__/set-chat-reactions.test.js +0 -129
  63. package/dist/__tests__/stars-status.test.d.ts +0 -1
  64. package/dist/__tests__/stars-status.test.js +0 -205
  65. package/dist/__tests__/stars-transactions.test.d.ts +0 -1
  66. package/dist/__tests__/stars-transactions.test.js +0 -82
  67. package/dist/__tests__/stories.test.d.ts +0 -1
  68. package/dist/__tests__/stories.test.js +0 -361
  69. package/dist/__tests__/toggle-anti-spam.test.d.ts +0 -1
  70. package/dist/__tests__/toggle-anti-spam.test.js +0 -80
  71. package/dist/__tests__/toggle-channel-signatures.test.d.ts +0 -1
  72. package/dist/__tests__/toggle-channel-signatures.test.js +0 -80
  73. package/dist/__tests__/toggle-forum-mode.test.d.ts +0 -1
  74. package/dist/__tests__/toggle-forum-mode.test.js +0 -80
  75. package/dist/__tests__/toggle-prehistory-hidden.test.d.ts +0 -1
  76. package/dist/__tests__/toggle-prehistory-hidden.test.js +0 -80
  77. package/dist/__tests__/tools/shared.test.d.ts +0 -1
  78. package/dist/__tests__/tools/shared.test.js +0 -110
  79. package/dist/__tests__/updates.test.d.ts +0 -1
  80. package/dist/__tests__/updates.test.js +0 -221
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { DESTRUCTIVE, fail, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
2
+ import { DESTRUCTIVE, fail, ok, READ_ONLY, requireConnection, WRITE } from "./shared.js";
3
3
  const MUTE_FOREVER_UNTIL = 2147483647; // max 32-bit signed int
4
4
  export function registerAccountTools(server, telegram) {
5
5
  server.registerTool("telegram-mute-chat", {
@@ -58,7 +58,7 @@ export function registerAccountTools(server, telegram) {
58
58
  const text = folders
59
59
  .map((f) => `[${f.id}] ${f.emoticon ? `${f.emoticon} ` : ""}${f.title} (${f.includeCount} chats, ${f.pinnedCount} pinned)`)
60
60
  .join("\n");
61
- return ok(sanitize(text));
61
+ return ok(text);
62
62
  }
63
63
  catch (e) {
64
64
  return fail(e);
@@ -103,7 +103,7 @@ export function registerAccountTools(server, telegram) {
103
103
  const text = sessions
104
104
  .map((s) => `${s.current ? "→ " : " "}${s.device} (${s.platform}) — ${s.appName} ${s.appVersion}\n IP: ${s.ip} (${s.country}) | Last active: ${s.dateActive}${s.current ? " [CURRENT]" : ""}\n Hash: ${s.hash}`)
105
105
  .join("\n\n");
106
- return ok(sanitize(text));
106
+ return ok(text);
107
107
  }
108
108
  catch (e) {
109
109
  return fail(e);
@@ -252,7 +252,7 @@ export function registerAccountTools(server, telegram) {
252
252
  const text = links
253
253
  .map((l) => `${l.link}${l.title ? ` (${l.title})` : ""} — ${l.usageCount} uses${l.expired ? " [EXPIRED]" : ""}${l.revoked ? " [REVOKED]" : ""}`)
254
254
  .join("\n");
255
- return ok(sanitize(text));
255
+ return ok(text);
256
256
  }
257
257
  catch (e) {
258
258
  return fail(e);
@@ -291,7 +291,7 @@ export function registerAccountTools(server, telegram) {
291
291
  if (drafts.length === 0)
292
292
  return ok("No drafts");
293
293
  const text = drafts.map((d) => `[${d.chatId}] ${d.chatTitle} (${d.date})\n ${d.text}`).join("\n\n");
294
- return ok(sanitize(text));
294
+ return ok(text);
295
295
  }
296
296
  catch (e) {
297
297
  return fail(e);
@@ -344,7 +344,7 @@ export function registerAccountTools(server, telegram) {
344
344
  if (dialogs.length === 0)
345
345
  return ok("No saved dialogs");
346
346
  const text = dialogs.map((d) => `[${d.peerId}] ${d.peerTitle} — last msg #${d.lastMsgId}`).join("\n");
347
- return ok(sanitize(text));
347
+ return ok(text);
348
348
  }
349
349
  catch (e) {
350
350
  return fail(e);
@@ -379,7 +379,7 @@ export function registerAccountTools(server, telegram) {
379
379
  return fail(new Error(err));
380
380
  try {
381
381
  const result = await telegram.getBusinessChatLinks();
382
- return ok(sanitize(JSON.stringify(result)));
382
+ return ok(JSON.stringify(result));
383
383
  }
384
384
  catch (e) {
385
385
  return fail(e);
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { fail, ok, READ_ONLY, requireConnection, sanitize } from "./shared.js";
2
+ import { fail, ok, READ_ONLY, requireConnection } from "./shared.js";
3
3
  export function registerBoostTools(server, telegram) {
4
4
  server.registerTool("telegram-get-my-boosts", {
5
5
  description: "List the user's premium boost slots (premium.GetMyBoosts). Each entry includes slot index, the peer it currently boosts (if any), the date the boost was applied, expiration timestamp, and cooldownUntilDate (when a slot can be reassigned). Premium users have multiple slots; non-Premium users typically have a single slot. Read-only.",
@@ -11,7 +11,7 @@ export function registerBoostTools(server, telegram) {
11
11
  return fail(new Error(err));
12
12
  try {
13
13
  const result = await telegram.getMyBoosts();
14
- return ok(sanitize(JSON.stringify(result)));
14
+ return ok(JSON.stringify(result));
15
15
  }
16
16
  catch (e) {
17
17
  return fail(e);
@@ -29,7 +29,7 @@ export function registerBoostTools(server, telegram) {
29
29
  return fail(new Error(err));
30
30
  try {
31
31
  const result = await telegram.getBoostsStatus(chat);
32
- return ok(sanitize(JSON.stringify(result)));
32
+ return ok(JSON.stringify(result));
33
33
  }
34
34
  catch (e) {
35
35
  return fail(e);
@@ -56,7 +56,7 @@ export function registerBoostTools(server, telegram) {
56
56
  return fail(new Error(err));
57
57
  try {
58
58
  const result = await telegram.getBoostsList(chat, { gifts, offset, limit });
59
- return ok(sanitize(JSON.stringify(result)));
59
+ return ok(JSON.stringify(result));
60
60
  }
61
61
  catch (e) {
62
62
  return fail(e);
@@ -27,7 +27,7 @@ export function registerChatTools(server, telegram) {
27
27
  return `${prefix} ${d.name} (${d.id})${botTag}${contactTag}${unread}`;
28
28
  })
29
29
  .join("\n");
30
- return ok(sanitize(text) || "No chats");
30
+ return ok(text || "No chats");
31
31
  }
32
32
  catch (e) {
33
33
  return fail(e);
@@ -49,7 +49,7 @@ export function registerChatTools(server, telegram) {
49
49
  const text = results
50
50
  .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)}` : ""}`)
51
51
  .join("\n");
52
- return ok(sanitize(text) || "No results");
52
+ return ok(text || "No results");
53
53
  }
54
54
  catch (e) {
55
55
  return fail(e);
@@ -99,7 +99,7 @@ export function registerChatTools(server, telegram) {
99
99
  return `${m.name}${m.username ? ` (@${m.username})` : ""} (${m.id})${role}`;
100
100
  })
101
101
  .join("\n");
102
- return ok(sanitize(text) || "No members found");
102
+ return ok(text || "No members found");
103
103
  }
104
104
  catch (e) {
105
105
  return fail(e);
@@ -386,7 +386,7 @@ export function registerChatTools(server, telegram) {
386
386
  return `[#${e.id}] [${e.date}] ${e.userName}: ${e.action}${details}`;
387
387
  })
388
388
  .join("\n");
389
- return ok(sanitize(text) || "No admin log events");
389
+ return ok(text || "No admin log events");
390
390
  }
391
391
  catch (e) {
392
392
  return fail(e);
@@ -606,7 +606,7 @@ export function registerChatTools(server, telegram) {
606
606
  return fail(new Error(err));
607
607
  try {
608
608
  const topic = await telegram.createForumTopic(chatId, title, iconColor, iconEmojiId);
609
- return ok(sanitize(`Created topic "${topic.title}" (id=${topic.id}) in ${chatId}`));
609
+ return ok(`Created topic "${topic.title}" (id=${topic.id}) in ${chatId}`);
610
610
  }
611
611
  catch (e) {
612
612
  return fail(e);
@@ -667,7 +667,7 @@ export function registerChatTools(server, telegram) {
667
667
  return fail(new Error(err));
668
668
  try {
669
669
  const stats = await telegram.getBroadcastStats(chatId, { dark, includeGraphs });
670
- return ok(sanitize(JSON.stringify(stats)));
670
+ return ok(JSON.stringify(stats));
671
671
  }
672
672
  catch (e) {
673
673
  return fail(e);
@@ -690,7 +690,7 @@ export function registerChatTools(server, telegram) {
690
690
  return fail(new Error(err));
691
691
  try {
692
692
  const stats = await telegram.getMegagroupStats(chatId, { dark, includeGraphs });
693
- return ok(sanitize(JSON.stringify(stats)));
693
+ return ok(JSON.stringify(stats));
694
694
  }
695
695
  catch (e) {
696
696
  return fail(e);
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { fail, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
2
+ import { fail, ok, READ_ONLY, requireConnection, WRITE } from "./shared.js";
3
3
  export function registerContactTools(server, telegram) {
4
4
  server.registerTool("telegram-get-contacts", {
5
5
  description: "Get your Telegram contacts list with phone numbers",
@@ -14,7 +14,7 @@ export function registerContactTools(server, telegram) {
14
14
  const text = contacts
15
15
  .map((c) => `P ${c.name}${c.username ? ` (@${c.username})` : ""} (${c.id})${c.phone ? ` +${c.phone}` : ""}`)
16
16
  .join("\n");
17
- return ok(sanitize(text) || "No contacts");
17
+ return ok(text || "No contacts");
18
18
  }
19
19
  catch (e) {
20
20
  return fail(e);
@@ -73,7 +73,7 @@ export function registerContactTools(server, telegram) {
73
73
  return `${tag} ${r.name}${username} (${r.id})${unread}${preview}`;
74
74
  })
75
75
  .join("\n");
76
- return ok(sanitize(text));
76
+ return ok(text);
77
77
  }
78
78
  catch (e) {
79
79
  return fail(e);
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { fail, formatReactions, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
2
+ import { fail, formatReactions, ok, READ_ONLY, requireConnection, WRITE } from "./shared.js";
3
3
  export function registerExtraTools(server, telegram) {
4
4
  server.registerTool("telegram-pin-message", {
5
5
  description: "Pin a message in a Telegram chat",
@@ -122,7 +122,7 @@ export function registerExtraTools(server, telegram) {
122
122
  return `# ${t.title} (id: ${t.id})${flagStr}${unread}`;
123
123
  })
124
124
  .join("\n");
125
- return ok(sanitize(text) || "No topics found");
125
+ return ok(text || "No topics found");
126
126
  }
127
127
  catch (e) {
128
128
  return fail(e);
@@ -146,7 +146,7 @@ export function registerExtraTools(server, telegram) {
146
146
  const text = messages
147
147
  .map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
148
148
  .join("\n\n");
149
- return ok(sanitize(text) || "No messages in this topic");
149
+ return ok(text || "No messages in this topic");
150
150
  }
151
151
  catch (e) {
152
152
  return fail(e);
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { fail, ok, READ_ONLY, requireConnection, sanitize } from "./shared.js";
2
+ import { fail, ok, READ_ONLY, requireConnection } from "./shared.js";
3
3
  export function isGroupCallsEnabled() {
4
4
  return process.env.MCP_TELEGRAM_ENABLE_GROUP_CALLS === "1";
5
5
  }
@@ -27,7 +27,7 @@ export function registerGroupCallTools(server, telegram) {
27
27
  return fail(new Error(err));
28
28
  try {
29
29
  const result = await telegram.getGroupCall(chat, { limit });
30
- return ok(sanitize(JSON.stringify(result)));
30
+ return ok(JSON.stringify(result));
31
31
  }
32
32
  catch (e) {
33
33
  return fail(e);
@@ -68,7 +68,7 @@ export function registerGroupCallTools(server, telegram) {
68
68
  return fail(new Error(err));
69
69
  try {
70
70
  const result = await telegram.getGroupCallParticipants(chat, { ids, sources, offset, limit });
71
- return ok(sanitize(JSON.stringify(result)));
71
+ return ok(JSON.stringify(result));
72
72
  }
73
73
  catch (e) {
74
74
  return fail(e);
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { DESTRUCTIVE, fail, formatReactions, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
2
+ import { DESTRUCTIVE, fail, formatReactions, ok, READ_ONLY, requireConnection, 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",
@@ -45,7 +45,7 @@ export function registerMessageTools(server, telegram) {
45
45
  const text = messages
46
46
  .map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
47
47
  .join("\n\n");
48
- return ok(sanitize(text) || "No messages");
48
+ return ok(text || "No messages");
49
49
  }
50
50
  catch (e) {
51
51
  return fail(e);
@@ -70,7 +70,7 @@ export function registerMessageTools(server, telegram) {
70
70
  const text = messages
71
71
  .map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
72
72
  .join("\n\n");
73
- return ok(sanitize(text) || "No messages found");
73
+ return ok(text || "No messages found");
74
74
  }
75
75
  catch (e) {
76
76
  return fail(e);
@@ -94,7 +94,7 @@ export function registerMessageTools(server, telegram) {
94
94
  const text = messages
95
95
  .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)}`)
96
96
  .join("\n\n");
97
- return ok(sanitize(text) || "No messages found");
97
+ return ok(text || "No messages found");
98
98
  }
99
99
  catch (e) {
100
100
  return fail(e);
@@ -185,7 +185,7 @@ export function registerMessageTools(server, telegram) {
185
185
  return line;
186
186
  })
187
187
  .join("\n");
188
- return ok(sanitize(text) || "No unread chats");
188
+ return ok(text || "No unread chats");
189
189
  }
190
190
  catch (e) {
191
191
  return fail(e);
@@ -206,7 +206,7 @@ export function registerMessageTools(server, telegram) {
206
206
  const text = messages
207
207
  .map((m) => `[#${m.id}] [${m.date}] ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}`)
208
208
  .join("\n\n");
209
- return ok(sanitize(text) || "No scheduled messages");
209
+ return ok(text || "No scheduled messages");
210
210
  }
211
211
  catch (e) {
212
212
  return fail(e);
@@ -252,7 +252,7 @@ export function registerMessageTools(server, telegram) {
252
252
  const text = messages
253
253
  .map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
254
254
  .join("\n\n");
255
- return ok(sanitize(text) || "No replies");
255
+ return ok(text || "No replies");
256
256
  }
257
257
  catch (e) {
258
258
  return fail(e);
@@ -272,7 +272,7 @@ export function registerMessageTools(server, telegram) {
272
272
  return fail(new Error(err));
273
273
  try {
274
274
  const link = await telegram.getMessageLink(chatId, messageId, thread);
275
- return ok(sanitize(link));
275
+ return ok(link);
276
276
  }
277
277
  catch (e) {
278
278
  return fail(e);
@@ -294,7 +294,7 @@ export function registerMessageTools(server, telegram) {
294
294
  const text = messages
295
295
  .map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
296
296
  .join("\n\n");
297
- return ok(sanitize(text) || "No unread mentions");
297
+ return ok(text || "No unread mentions");
298
298
  }
299
299
  catch (e) {
300
300
  return fail(e);
@@ -316,7 +316,7 @@ export function registerMessageTools(server, telegram) {
316
316
  const text = messages
317
317
  .map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
318
318
  .join("\n\n");
319
- return ok(sanitize(text) || "No unread reactions");
319
+ return ok(text || "No unread reactions");
320
320
  }
321
321
  catch (e) {
322
322
  return fail(e);
@@ -346,7 +346,7 @@ export function registerMessageTools(server, telegram) {
346
346
  const text = translations.length === messageIds.length
347
347
  ? translations.map((t, i) => `[#${messageIds[i]}] ${t}`).join("\n\n")
348
348
  : translations.join("\n\n");
349
- return ok(sanitize(text) || "No translations");
349
+ return ok(text || "No translations");
350
350
  }
351
351
  catch (e) {
352
352
  const msg = e.message ?? "";
@@ -412,7 +412,7 @@ export function registerMessageTools(server, telegram) {
412
412
  return fail(new Error(err));
413
413
  try {
414
414
  const res = await telegram.getInlineBotResults(bot, chatId, query, offset);
415
- return ok(sanitize(JSON.stringify(res)));
415
+ return ok(JSON.stringify(res));
416
416
  }
417
417
  catch (e) {
418
418
  return fail(e);
@@ -464,7 +464,7 @@ export function registerMessageTools(server, telegram) {
464
464
  return fail(new Error(err));
465
465
  try {
466
466
  const result = await telegram.getMessageButtons(chatId, messageId);
467
- return ok(sanitize(JSON.stringify(result)));
467
+ return ok(JSON.stringify(result));
468
468
  }
469
469
  catch (e) {
470
470
  return fail(e);
@@ -506,7 +506,7 @@ export function registerMessageTools(server, telegram) {
506
506
  buttonIndex: hasIndex ? { row: row, column: column } : undefined,
507
507
  data,
508
508
  });
509
- return ok(sanitize(JSON.stringify(answer)));
509
+ return ok(JSON.stringify(answer));
510
510
  }
511
511
  catch (e) {
512
512
  return fail(e);
@@ -522,7 +522,7 @@ export function registerMessageTools(server, telegram) {
522
522
  return fail(new Error(err));
523
523
  try {
524
524
  const state = await telegram.getUpdatesState();
525
- return ok(sanitize(JSON.stringify(state)));
525
+ return ok(JSON.stringify(state));
526
526
  }
527
527
  catch (e) {
528
528
  return fail(e);
@@ -556,7 +556,7 @@ export function registerMessageTools(server, telegram) {
556
556
  return fail(new Error(err));
557
557
  try {
558
558
  const diff = await telegram.getUpdates({ pts, qts, date, ptsLimit, ptsTotalLimit });
559
- return ok(sanitize(JSON.stringify(diff)));
559
+ return ok(JSON.stringify(diff));
560
560
  }
561
561
  catch (e) {
562
562
  return fail(e);
@@ -580,7 +580,7 @@ export function registerMessageTools(server, telegram) {
580
580
  return fail(new Error(err));
581
581
  try {
582
582
  const diff = await telegram.getChannelUpdates(chatId, { pts, limit, force });
583
- return ok(sanitize(JSON.stringify(diff)));
583
+ return ok(JSON.stringify(diff));
584
584
  }
585
585
  catch (e) {
586
586
  return fail(e);
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { fail, ok, READ_ONLY, requireConnection, sanitize } from "./shared.js";
2
+ import { fail, ok, READ_ONLY, requireConnection } from "./shared.js";
3
3
  export function isQuickRepliesEnabled() {
4
4
  return process.env.MCP_TELEGRAM_ENABLE_QUICK_REPLIES === "1";
5
5
  }
@@ -22,7 +22,7 @@ export function registerQuickRepliesTools(server, telegram) {
22
22
  return fail(new Error(err));
23
23
  try {
24
24
  const result = await telegram.getQuickReplies(hash);
25
- return ok(sanitize(JSON.stringify(result)));
25
+ return ok(JSON.stringify(result));
26
26
  }
27
27
  catch (e) {
28
28
  return fail(e);
@@ -49,7 +49,7 @@ export function registerQuickRepliesTools(server, telegram) {
49
49
  return fail(new Error(err));
50
50
  try {
51
51
  const result = await telegram.getQuickReplyMessages(shortcutId, { ids, hash });
52
- return ok(sanitize(JSON.stringify(result)));
52
+ return ok(JSON.stringify(result));
53
53
  }
54
54
  catch (e) {
55
55
  return fail(e);
@@ -92,7 +92,7 @@ export function registerReactionTools(server, telegram) {
92
92
  const reactions = await telegram.getTopReactions(limit);
93
93
  if (reactions.length === 0)
94
94
  return ok("No top reactions available");
95
- return ok(sanitize(reactions.map((r) => r.emoji).join(" ")));
95
+ return ok(reactions.map((r) => r.emoji).join(" "));
96
96
  }
97
97
  catch (e) {
98
98
  return fail(e);
@@ -155,7 +155,7 @@ export function registerReactionTools(server, telegram) {
155
155
  const reactions = await telegram.getRecentReactions(limit);
156
156
  if (reactions.length === 0)
157
157
  return ok("No recent reactions");
158
- return ok(sanitize(reactions.map((r) => r.emoji).join(" ")));
158
+ return ok(reactions.map((r) => r.emoji).join(" "));
159
159
  }
160
160
  catch (e) {
161
161
  return fail(e);
@@ -13,7 +13,9 @@ export declare const DESTRUCTIVE: {
13
13
  readonly destructiveHint: true;
14
14
  readonly openWorldHint: true;
15
15
  };
16
- /** Helper: success response */
16
+ /** Remove unpaired UTF-16 surrogates that break JSON serialization */
17
+ export declare function sanitize(text: string): string;
18
+ /** Helper: success response — always sanitizes to prevent surrogate crashes */
17
19
  export declare function ok(text: string): {
18
20
  content: {
19
21
  type: "text";
@@ -28,8 +30,6 @@ export declare function fail(e: unknown): {
28
30
  }[];
29
31
  isError: true;
30
32
  };
31
- /** Remove unpaired UTF-16 surrogates that break JSON serialization */
32
- export declare function sanitize(text: string): string;
33
33
  /** Format reactions array into compact text like: [👍×5 ❤️×3(me) 🔥×1] */
34
34
  export declare function formatReactions(reactions?: {
35
35
  emoji: string;
@@ -2,17 +2,18 @@
2
2
  export const READ_ONLY = { readOnlyHint: true, openWorldHint: true };
3
3
  export const WRITE = { readOnlyHint: false, openWorldHint: true };
4
4
  export const DESTRUCTIVE = { readOnlyHint: false, destructiveHint: true, openWorldHint: true };
5
- /** Helper: success response */
5
+ /** Remove unpaired UTF-16 surrogates that break JSON serialization */
6
+ export function sanitize(text) {
7
+ return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
8
+ }
9
+ /** Helper: success response — always sanitizes to prevent surrogate crashes */
6
10
  export function ok(text) {
7
- return { content: [{ type: "text", text }] };
11
+ return { content: [{ type: "text", text: sanitize(text) }] };
8
12
  }
9
13
  /** Helper: error response with isError flag */
10
14
  export function fail(e) {
11
- return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
12
- }
13
- /** Remove unpaired UTF-16 surrogates that break JSON serialization */
14
- export function sanitize(text) {
15
- return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
15
+ const msg = e instanceof Error ? e.message : String(e);
16
+ return { content: [{ type: "text", text: `Error: ${sanitize(msg)}` }], isError: true };
16
17
  }
17
18
  /** Format reactions array into compact text like: [👍×5 ❤️×3(me) 🔥×1] */
18
19
  export function formatReactions(reactions) {
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { fail, ok, READ_ONLY, requireConnection, sanitize } from "./shared.js";
2
+ import { fail, ok, READ_ONLY, requireConnection } from "./shared.js";
3
3
  export function isStarsEnabled() {
4
4
  return process.env.MCP_TELEGRAM_ENABLE_STARS === "1";
5
5
  }
@@ -20,7 +20,7 @@ export function registerStarsTools(server, telegram) {
20
20
  return fail(new Error(err));
21
21
  try {
22
22
  const result = await telegram.getStarsStatus(peer);
23
- return ok(sanitize(JSON.stringify(result)));
23
+ return ok(JSON.stringify(result));
24
24
  }
25
25
  catch (e) {
26
26
  return fail(e);
@@ -62,7 +62,7 @@ export function registerStarsTools(server, telegram) {
62
62
  offset,
63
63
  limit,
64
64
  });
65
- return ok(sanitize(JSON.stringify(result)));
65
+ return ok(JSON.stringify(result));
66
66
  }
67
67
  catch (e) {
68
68
  return fail(e);
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { fail, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
2
+ import { fail, ok, READ_ONLY, requireConnection, WRITE } from "./shared.js";
3
3
  export function registerStickerTools(server, telegram) {
4
4
  server.registerTool("telegram-get-sticker-set", {
5
5
  description: "Get all stickers from a sticker set by its short name. Returns each sticker with index and emoji. Use the index with telegram-send-sticker to send a specific sticker",
@@ -25,7 +25,7 @@ export function registerStickerTools(server, telegram) {
25
25
  }
26
26
  lines.push("");
27
27
  lines.push(`Send a sticker: telegram-send-sticker(chatId, stickerSet="${set.shortName}", index=N)`);
28
- return ok(sanitize(lines.join("\n")));
28
+ return ok(lines.join("\n"));
29
29
  }
30
30
  catch (e) {
31
31
  return fail(e);
@@ -54,7 +54,7 @@ export function registerStickerTools(server, telegram) {
54
54
  }
55
55
  lines.push("");
56
56
  lines.push("Use telegram-get-sticker-set(shortName) to see individual stickers.");
57
- return ok(sanitize(lines.join("\n")));
57
+ return ok(lines.join("\n"));
58
58
  }
59
59
  catch (e) {
60
60
  return fail(e);
@@ -79,7 +79,7 @@ export function registerStickerTools(server, telegram) {
79
79
  lines.push(`• ${set.title} — ${set.count} stickers`);
80
80
  lines.push(` Short name: ${set.shortName}`);
81
81
  }
82
- return ok(sanitize(lines.join("\n")));
82
+ return ok(lines.join("\n"));
83
83
  }
84
84
  catch (e) {
85
85
  return fail(e);
@@ -128,7 +128,7 @@ export function registerStickerTools(server, telegram) {
128
128
  for (let i = 0; i < stickers.length; i++) {
129
129
  lines.push(`[${i}] ${stickers[i].emoji}`);
130
130
  }
131
- return ok(sanitize(lines.join("\n")));
131
+ return ok(lines.join("\n"));
132
132
  }
133
133
  catch (e) {
134
134
  return fail(e);
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { fail, ok, READ_ONLY, requireConnection, sanitize } from "./shared.js";
2
+ import { fail, ok, READ_ONLY, requireConnection } from "./shared.js";
3
3
  export function registerStoryTools(server, telegram) {
4
4
  server.registerTool("telegram-get-all-stories", {
5
5
  description: "Fetch active stories from contacts/channels the user follows. Pagination via 'next' + 'state' — pass the returned state back on the next call with next:true to load more. Use hidden:true to read stories from muted/archived peers. Returns compact story metadata (id, date, expireDate, caption, mediaType, counters) without raw media blobs.",
@@ -18,7 +18,7 @@ export function registerStoryTools(server, telegram) {
18
18
  }
19
19
  try {
20
20
  const result = await telegram.getAllStories({ next, hidden, state });
21
- return ok(sanitize(JSON.stringify(result)));
21
+ return ok(JSON.stringify(result));
22
22
  }
23
23
  catch (e) {
24
24
  return fail(e);
@@ -40,7 +40,7 @@ export function registerStoryTools(server, telegram) {
40
40
  const result = await telegram.getPeerStories(chat);
41
41
  if (result === null)
42
42
  return ok("No active stories found for the specified peer");
43
- return ok(sanitize(JSON.stringify(result)));
43
+ return ok(JSON.stringify(result));
44
44
  }
45
45
  catch (e) {
46
46
  return fail(e);
@@ -61,7 +61,7 @@ export function registerStoryTools(server, telegram) {
61
61
  return fail(new Error(err));
62
62
  try {
63
63
  const result = await telegram.getStoriesById(chat, ids);
64
- return ok(sanitize(JSON.stringify(result)));
64
+ return ok(JSON.stringify(result));
65
65
  }
66
66
  catch (e) {
67
67
  return fail(e);
@@ -94,7 +94,7 @@ export function registerStoryTools(server, telegram) {
94
94
  offset,
95
95
  limit,
96
96
  });
97
- return ok(sanitize(JSON.stringify(result)));
97
+ return ok(JSON.stringify(result));
98
98
  }
99
99
  catch (e) {
100
100
  const msg = e.message ?? "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overpod/mcp-telegram",
3
- "version": "1.26.0",
3
+ "version": "1.27.0",
4
4
  "description": "MCP server for Telegram userbot — messages, media, reactions, polls & more. Built on GramJS/MTProto.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1 +0,0 @@
1
- export {};
@@ -1,41 +0,0 @@
1
- import assert from "node:assert";
2
- import { describe, it } from "node:test";
3
- import { Api } from "telegram/tl/index.js";
4
- import { describeAdminLogAction, describeAdminLogDetails } from "../telegram-client.js";
5
- describe("describeAdminLogAction", () => {
6
- it("converts ChangeTitle to snake_case", () => {
7
- const action = new Api.ChannelAdminLogEventActionChangeTitle({ prevValue: "a", newValue: "b" });
8
- assert.strictEqual(describeAdminLogAction(action), "change_title");
9
- });
10
- it("converts ParticipantJoin to snake_case", () => {
11
- const action = new Api.ChannelAdminLogEventActionParticipantJoin();
12
- assert.strictEqual(describeAdminLogAction(action), "participant_join");
13
- });
14
- it("handles ToggleSlowMode", () => {
15
- const action = new Api.ChannelAdminLogEventActionToggleSlowMode({ prevValue: 0, newValue: 30 });
16
- assert.strictEqual(describeAdminLogAction(action), "toggle_slow_mode");
17
- });
18
- it("handles ChangeHistoryTTL without splitting acronym", () => {
19
- const action = new Api.ChannelAdminLogEventActionChangeHistoryTTL({ prevValue: 0, newValue: 86400 });
20
- assert.strictEqual(describeAdminLogAction(action), "change_history_ttl");
21
- });
22
- });
23
- describe("describeAdminLogDetails", () => {
24
- const describeUser = (id) => `user_${id.toString()}`;
25
- it("formats title change", () => {
26
- const action = new Api.ChannelAdminLogEventActionChangeTitle({ prevValue: "old", newValue: "new" });
27
- assert.strictEqual(describeAdminLogDetails(action, describeUser), '"old" → "new"');
28
- });
29
- it("formats username change", () => {
30
- const action = new Api.ChannelAdminLogEventActionChangeUsername({ prevValue: "old", newValue: "new" });
31
- assert.strictEqual(describeAdminLogDetails(action, describeUser), "@old → @new");
32
- });
33
- it("formats slow mode change", () => {
34
- const action = new Api.ChannelAdminLogEventActionToggleSlowMode({ prevValue: 0, newValue: 30 });
35
- assert.strictEqual(describeAdminLogDetails(action, describeUser), "0s → 30s");
36
- });
37
- it("returns empty string for unknown actions", () => {
38
- const action = new Api.ChannelAdminLogEventActionParticipantJoin();
39
- assert.strictEqual(describeAdminLogDetails(action, describeUser), "");
40
- });
41
- });
@@ -1 +0,0 @@
1
- export {};