@ebowwa/mcp-telegram 0.1.4 → 0.2.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 +300 -0
- package/package.json +4 -2
- package/src/index.ts +355 -0
- package/auth-signin.js +0 -182
- package/auth-verify.js +0 -234
- package/auth.js +0 -154
- package/bun.lock +0 -302
- package/dist/index.d.ts +0 -18
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/src/index.js +0 -918
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import { TelegramClient } from "telegram";
|
|
|
21
21
|
import { StringSession } from "telegram/sessions";
|
|
22
22
|
import { Api } from "telegram";
|
|
23
23
|
import bigInt from "big-integer";
|
|
24
|
+
import { NewMessage } from "telegram/events";
|
|
24
25
|
import { homedir } from "os";
|
|
25
26
|
import { join } from "path";
|
|
26
27
|
import { mkdirSync, existsSync, writeFileSync, readFileSync } from "fs";
|
|
@@ -37,6 +38,10 @@ if (!existsSync(SESSION_DIR)) {
|
|
|
37
38
|
// Global client instance
|
|
38
39
|
let client = null;
|
|
39
40
|
let sessionString = "";
|
|
41
|
+
// Event storage
|
|
42
|
+
const subscriptions = new Map();
|
|
43
|
+
const eventQueue = [];
|
|
44
|
+
const eventHandlers = new Map(); // Track registered handlers for cleanup
|
|
40
45
|
// ==============
|
|
41
46
|
// Helper Functions
|
|
42
47
|
// ==============
|
|
@@ -115,6 +120,56 @@ function formatMessage(message) {
|
|
|
115
120
|
};
|
|
116
121
|
}
|
|
117
122
|
// ==============
|
|
123
|
+
// Event Subscription Helpers
|
|
124
|
+
// ==============
|
|
125
|
+
function generateId() {
|
|
126
|
+
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
127
|
+
}
|
|
128
|
+
function queueEvent(subscriptionId, type, data) {
|
|
129
|
+
const event = {
|
|
130
|
+
id: generateId(),
|
|
131
|
+
subscriptionId,
|
|
132
|
+
type,
|
|
133
|
+
data,
|
|
134
|
+
timestamp: new Date(),
|
|
135
|
+
};
|
|
136
|
+
eventQueue.push(event);
|
|
137
|
+
// Keep only last 100 events to prevent memory issues
|
|
138
|
+
if (eventQueue.length > 100) {
|
|
139
|
+
eventQueue.shift();
|
|
140
|
+
}
|
|
141
|
+
// Send MCP notification
|
|
142
|
+
server.sendResourceUpdated({
|
|
143
|
+
uri: `telegram://events/${subscriptionId}`,
|
|
144
|
+
}).catch((err) => console.error("Failed to send MCP notification:", err));
|
|
145
|
+
}
|
|
146
|
+
async function setupEventHandlers(c, subscription) {
|
|
147
|
+
const handlerKey = subscription.id;
|
|
148
|
+
// Remove existing handler if any
|
|
149
|
+
if (eventHandlers.has(handlerKey)) {
|
|
150
|
+
// GramJS doesn't have a clean removeEventHandler, so we track and skip
|
|
151
|
+
eventHandlers.delete(handlerKey);
|
|
152
|
+
}
|
|
153
|
+
// Create handler for new messages
|
|
154
|
+
if (subscription.events.includes('new_message')) {
|
|
155
|
+
const handler = async (event) => {
|
|
156
|
+
const message = event.message;
|
|
157
|
+
const chatId = message.peerId?.userId?.toString() ||
|
|
158
|
+
message.peerId?.chatId?.toString() ||
|
|
159
|
+
message.peerId?.channelId?.toString();
|
|
160
|
+
// Only process if this message is from our subscribed chat
|
|
161
|
+
if (chatId === subscription.chatId || message.chatId?.toString() === subscription.chatId) {
|
|
162
|
+
queueEvent(subscription.id, 'new_message', {
|
|
163
|
+
message: formatMessage(message),
|
|
164
|
+
chatId,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
c.addEventHandler(handler, new NewMessage({ incoming: true }));
|
|
169
|
+
eventHandlers.set(handlerKey, handler);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// ==============
|
|
118
173
|
// MCP Server
|
|
119
174
|
// ==============
|
|
120
175
|
const server = new Server({
|
|
@@ -123,6 +178,10 @@ const server = new Server({
|
|
|
123
178
|
}, {
|
|
124
179
|
capabilities: {
|
|
125
180
|
tools: {},
|
|
181
|
+
resources: {
|
|
182
|
+
subscribe: true,
|
|
183
|
+
listChanged: true,
|
|
184
|
+
},
|
|
126
185
|
},
|
|
127
186
|
});
|
|
128
187
|
// List available tools
|
|
@@ -428,6 +487,82 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
428
487
|
required: ["entity"],
|
|
429
488
|
},
|
|
430
489
|
},
|
|
490
|
+
// Real-time Event Subscriptions
|
|
491
|
+
{
|
|
492
|
+
name: "telegram_subscribe",
|
|
493
|
+
description: "Subscribe to real-time events from a Telegram chat. The AI will receive MCP notifications when new messages arrive.",
|
|
494
|
+
inputSchema: {
|
|
495
|
+
type: "object",
|
|
496
|
+
properties: {
|
|
497
|
+
entity: {
|
|
498
|
+
type: "string",
|
|
499
|
+
description: "Username, phone, or entity ID of the chat to subscribe to",
|
|
500
|
+
},
|
|
501
|
+
events: {
|
|
502
|
+
type: "array",
|
|
503
|
+
items: { type: "string", enum: ["new_message", "edited_message", "deleted_message"] },
|
|
504
|
+
description: "Event types to subscribe to (default: ['new_message'])",
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
required: ["entity"],
|
|
508
|
+
},
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
name: "telegram_unsubscribe",
|
|
512
|
+
description: "Unsubscribe from a Telegram chat's events.",
|
|
513
|
+
inputSchema: {
|
|
514
|
+
type: "object",
|
|
515
|
+
properties: {
|
|
516
|
+
subscriptionId: {
|
|
517
|
+
type: "string",
|
|
518
|
+
description: "Subscription ID to remove",
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
required: ["subscriptionId"],
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
name: "telegram_get_subscriptions",
|
|
526
|
+
description: "List all active event subscriptions.",
|
|
527
|
+
inputSchema: {
|
|
528
|
+
type: "object",
|
|
529
|
+
properties: {},
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
name: "telegram_get_events",
|
|
534
|
+
description: "Get queued events from subscriptions. Call this after receiving an MCP resource notification.",
|
|
535
|
+
inputSchema: {
|
|
536
|
+
type: "object",
|
|
537
|
+
properties: {
|
|
538
|
+
subscriptionId: {
|
|
539
|
+
type: "string",
|
|
540
|
+
description: "Filter events by subscription ID (optional)",
|
|
541
|
+
},
|
|
542
|
+
limit: {
|
|
543
|
+
type: "number",
|
|
544
|
+
description: "Maximum number of events to return (default: 50)",
|
|
545
|
+
},
|
|
546
|
+
clear: {
|
|
547
|
+
type: "boolean",
|
|
548
|
+
description: "Clear retrieved events from queue (default: true)",
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
name: "telegram_clear_events",
|
|
555
|
+
description: "Clear all queued events or events for a specific subscription.",
|
|
556
|
+
inputSchema: {
|
|
557
|
+
type: "object",
|
|
558
|
+
properties: {
|
|
559
|
+
subscriptionId: {
|
|
560
|
+
type: "string",
|
|
561
|
+
description: "Clear events only for this subscription (optional, clears all if not provided)",
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
},
|
|
431
566
|
],
|
|
432
567
|
};
|
|
433
568
|
});
|
|
@@ -774,6 +909,171 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
774
909
|
}],
|
|
775
910
|
};
|
|
776
911
|
}
|
|
912
|
+
// ==================
|
|
913
|
+
// Real-time Event Subscriptions
|
|
914
|
+
// ==================
|
|
915
|
+
case "telegram_subscribe": {
|
|
916
|
+
if (!args?.entity) {
|
|
917
|
+
throw new Error("entity is required");
|
|
918
|
+
}
|
|
919
|
+
const c = await getClient();
|
|
920
|
+
const entity = await c.getEntity(args.entity);
|
|
921
|
+
const chatId = entity.id?.toString();
|
|
922
|
+
if (!chatId) {
|
|
923
|
+
throw new Error("Could not resolve chat ID");
|
|
924
|
+
}
|
|
925
|
+
// Check if already subscribed
|
|
926
|
+
const existing = Array.from(subscriptions.values()).find((s) => s.chatId === chatId);
|
|
927
|
+
if (existing) {
|
|
928
|
+
return {
|
|
929
|
+
content: [{
|
|
930
|
+
type: "text",
|
|
931
|
+
text: JSON.stringify({
|
|
932
|
+
success: true,
|
|
933
|
+
message: "Already subscribed to this chat",
|
|
934
|
+
subscription: existing,
|
|
935
|
+
}, null, 2),
|
|
936
|
+
}],
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
const events = args?.events || ["new_message"];
|
|
940
|
+
const subscription = {
|
|
941
|
+
id: generateId(),
|
|
942
|
+
chatId,
|
|
943
|
+
chatName: entity.title || entity.username || entity.firstName,
|
|
944
|
+
events,
|
|
945
|
+
createdAt: new Date(),
|
|
946
|
+
};
|
|
947
|
+
subscriptions.set(subscription.id, subscription);
|
|
948
|
+
// Set up GramJS event handlers
|
|
949
|
+
await setupEventHandlers(c, subscription);
|
|
950
|
+
return {
|
|
951
|
+
content: [{
|
|
952
|
+
type: "text",
|
|
953
|
+
text: JSON.stringify({
|
|
954
|
+
success: true,
|
|
955
|
+
message: `Subscribed to ${subscription.chatName || chatId}`,
|
|
956
|
+
subscription,
|
|
957
|
+
notificationUri: `telegram://events/${subscription.id}`,
|
|
958
|
+
}, null, 2),
|
|
959
|
+
}],
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
case "telegram_unsubscribe": {
|
|
963
|
+
const subscriptionId = args?.subscriptionId;
|
|
964
|
+
if (!subscriptionId) {
|
|
965
|
+
throw new Error("subscriptionId is required");
|
|
966
|
+
}
|
|
967
|
+
const subscription = subscriptions.get(subscriptionId);
|
|
968
|
+
if (!subscription) {
|
|
969
|
+
return {
|
|
970
|
+
content: [{
|
|
971
|
+
type: "text",
|
|
972
|
+
text: JSON.stringify({
|
|
973
|
+
success: false,
|
|
974
|
+
message: "Subscription not found",
|
|
975
|
+
}, null, 2),
|
|
976
|
+
}],
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
subscriptions.delete(subscriptionId);
|
|
980
|
+
eventHandlers.delete(subscriptionId);
|
|
981
|
+
return {
|
|
982
|
+
content: [{
|
|
983
|
+
type: "text",
|
|
984
|
+
text: JSON.stringify({
|
|
985
|
+
success: true,
|
|
986
|
+
message: `Unsubscribed from ${subscription.chatName || subscription.chatId}`,
|
|
987
|
+
}, null, 2),
|
|
988
|
+
}],
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
case "telegram_get_subscriptions": {
|
|
992
|
+
const list = Array.from(subscriptions.values()).map((s) => ({
|
|
993
|
+
id: s.id,
|
|
994
|
+
chatId: s.chatId,
|
|
995
|
+
chatName: s.chatName,
|
|
996
|
+
events: s.events,
|
|
997
|
+
createdAt: s.createdAt,
|
|
998
|
+
}));
|
|
999
|
+
return {
|
|
1000
|
+
content: [{
|
|
1001
|
+
type: "text",
|
|
1002
|
+
text: JSON.stringify({
|
|
1003
|
+
total: list.length,
|
|
1004
|
+
subscriptions: list,
|
|
1005
|
+
}, null, 2),
|
|
1006
|
+
}],
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
case "telegram_get_events": {
|
|
1010
|
+
const subscriptionId = args?.subscriptionId;
|
|
1011
|
+
const limit = args?.limit || 50;
|
|
1012
|
+
const clear = args?.clear !== false;
|
|
1013
|
+
let filteredEvents = eventQueue;
|
|
1014
|
+
if (subscriptionId) {
|
|
1015
|
+
filteredEvents = eventQueue.filter((e) => e.subscriptionId === subscriptionId);
|
|
1016
|
+
}
|
|
1017
|
+
const events = filteredEvents.slice(-limit);
|
|
1018
|
+
if (clear) {
|
|
1019
|
+
// Remove retrieved events from queue
|
|
1020
|
+
const eventIds = new Set(events.map((e) => e.id));
|
|
1021
|
+
const queueIndex = eventQueue.findIndex((e) => eventIds.has(e.id));
|
|
1022
|
+
if (queueIndex !== -1) {
|
|
1023
|
+
eventQueue.splice(queueIndex, events.length);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return {
|
|
1027
|
+
content: [{
|
|
1028
|
+
type: "text",
|
|
1029
|
+
text: JSON.stringify({
|
|
1030
|
+
total: events.length,
|
|
1031
|
+
events: events.map((e) => ({
|
|
1032
|
+
id: e.id,
|
|
1033
|
+
type: e.type,
|
|
1034
|
+
data: e.data,
|
|
1035
|
+
timestamp: e.timestamp,
|
|
1036
|
+
})),
|
|
1037
|
+
}, null, 2),
|
|
1038
|
+
}],
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
case "telegram_clear_events": {
|
|
1042
|
+
const subscriptionId = args?.subscriptionId;
|
|
1043
|
+
if (subscriptionId) {
|
|
1044
|
+
const before = eventQueue.length;
|
|
1045
|
+
const indicesToRemove = [];
|
|
1046
|
+
eventQueue.forEach((e, i) => {
|
|
1047
|
+
if (e.subscriptionId === subscriptionId) {
|
|
1048
|
+
indicesToRemove.push(i);
|
|
1049
|
+
}
|
|
1050
|
+
});
|
|
1051
|
+
// Remove from end to start to preserve indices
|
|
1052
|
+
indicesToRemove.reverse().forEach((i) => eventQueue.splice(i, 1));
|
|
1053
|
+
return {
|
|
1054
|
+
content: [{
|
|
1055
|
+
type: "text",
|
|
1056
|
+
text: JSON.stringify({
|
|
1057
|
+
success: true,
|
|
1058
|
+
message: `Cleared ${indicesToRemove.length} events for subscription`,
|
|
1059
|
+
}, null, 2),
|
|
1060
|
+
}],
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
else {
|
|
1064
|
+
const count = eventQueue.length;
|
|
1065
|
+
eventQueue.length = 0;
|
|
1066
|
+
return {
|
|
1067
|
+
content: [{
|
|
1068
|
+
type: "text",
|
|
1069
|
+
text: JSON.stringify({
|
|
1070
|
+
success: true,
|
|
1071
|
+
message: `Cleared ${count} events`,
|
|
1072
|
+
}, null, 2),
|
|
1073
|
+
}],
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
777
1077
|
default:
|
|
778
1078
|
throw new Error(`Unknown tool: ${name}`);
|
|
779
1079
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ebowwa/mcp-telegram",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Telegram MTProto client MCP server using GramJS - full user account access",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Telegram MTProto client MCP server using GramJS - full user account access with real-time event subscriptions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -21,10 +21,12 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
24
|
+
"big-integer": "^1.6.52",
|
|
24
25
|
"telegram": "^2.26.20",
|
|
25
26
|
"zod": "^4.3.6"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
29
|
+
"@types/big-integer": "^0.0.35",
|
|
28
30
|
"@types/bun": "latest",
|
|
29
31
|
"@types/node": "^22.10.2",
|
|
30
32
|
"typescript": "^5.7.2"
|