mcp-telegram-claudecode 1.0.0 → 1.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/README.md +4 -4
- package/README_CN.md +3 -3
- package/index.js +181 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,9 +56,9 @@ The configuration file is located at:
|
|
|
56
56
|
|
|
57
57
|
## Configuration Examples
|
|
58
58
|
|
|
59
|
-
### Without Proxy
|
|
59
|
+
### Without Proxy
|
|
60
60
|
|
|
61
|
-
If you can access Telegram directly
|
|
61
|
+
If you can access Telegram directly:
|
|
62
62
|
|
|
63
63
|
```json
|
|
64
64
|
{
|
|
@@ -75,9 +75,9 @@ If you can access Telegram directly without a proxy:
|
|
|
75
75
|
}
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
### With Proxy
|
|
78
|
+
### With Proxy
|
|
79
79
|
|
|
80
|
-
If
|
|
80
|
+
If you need a proxy to access Telegram:
|
|
81
81
|
|
|
82
82
|
```json
|
|
83
83
|
{
|
package/README_CN.md
CHANGED
|
@@ -56,7 +56,7 @@ claude /settings
|
|
|
56
56
|
|
|
57
57
|
## 配置示例
|
|
58
58
|
|
|
59
|
-
###
|
|
59
|
+
### 不使用代理
|
|
60
60
|
|
|
61
61
|
如果你可以直接访问 Telegram:
|
|
62
62
|
|
|
@@ -75,9 +75,9 @@ claude /settings
|
|
|
75
75
|
}
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
###
|
|
78
|
+
### 使用代理
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
如果你需要代理才能访问 Telegram:
|
|
81
81
|
|
|
82
82
|
```json
|
|
83
83
|
{
|
package/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Telegram Claude MCP Server
|
|
5
5
|
* MCP server for Telegram integration with Claude Code
|
|
6
|
+
* With auto-polling and terminal injection support
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
|
|
@@ -13,6 +14,9 @@ const {
|
|
|
13
14
|
} = require('@modelcontextprotocol/sdk/types.js');
|
|
14
15
|
const axios = require('axios');
|
|
15
16
|
const { HttpsProxyAgent } = require('https-proxy-agent');
|
|
17
|
+
const { execSync } = require('child_process');
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
16
20
|
|
|
17
21
|
// Configuration from environment variables
|
|
18
22
|
const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
|
|
@@ -34,6 +38,13 @@ const API_BASE = `https://api.telegram.org/bot${BOT_TOKEN}`;
|
|
|
34
38
|
// Track last update ID for polling
|
|
35
39
|
let lastUpdateId = 0;
|
|
36
40
|
|
|
41
|
+
// Polling state
|
|
42
|
+
let pollingActive = false;
|
|
43
|
+
let pollingInterval = null;
|
|
44
|
+
|
|
45
|
+
// Temp directory for injection scripts
|
|
46
|
+
const tempDir = process.env.TEMP || process.env.TMP || '/tmp';
|
|
47
|
+
|
|
37
48
|
/**
|
|
38
49
|
* Send a text message to Telegram
|
|
39
50
|
*/
|
|
@@ -44,8 +55,7 @@ async function sendMessage(text) {
|
|
|
44
55
|
|
|
45
56
|
const response = await axiosInstance.post(`${API_BASE}/sendMessage`, {
|
|
46
57
|
chat_id: CHAT_ID,
|
|
47
|
-
text: text
|
|
48
|
-
parse_mode: 'Markdown'
|
|
58
|
+
text: text
|
|
49
59
|
});
|
|
50
60
|
|
|
51
61
|
return response.data;
|
|
@@ -59,7 +69,6 @@ async function sendPhoto(photoPath, caption = '') {
|
|
|
59
69
|
throw new Error('TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID must be configured');
|
|
60
70
|
}
|
|
61
71
|
|
|
62
|
-
const fs = require('fs');
|
|
63
72
|
const FormData = require('form-data');
|
|
64
73
|
|
|
65
74
|
const form = new FormData();
|
|
@@ -97,7 +106,6 @@ async function getMessages(limit = 10) {
|
|
|
97
106
|
for (const update of response.data.result) {
|
|
98
107
|
lastUpdateId = update.update_id;
|
|
99
108
|
if (update.message && update.message.text) {
|
|
100
|
-
// Filter by chat ID if configured
|
|
101
109
|
if (!CHAT_ID || update.message.chat.id.toString() === CHAT_ID) {
|
|
102
110
|
messages.push({
|
|
103
111
|
id: update.message.message_id,
|
|
@@ -150,11 +158,127 @@ async function checkNewMessages() {
|
|
|
150
158
|
return { hasNew, latestMessage, updateId: lastUpdateId };
|
|
151
159
|
}
|
|
152
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Inject text into terminal via clipboard and SendKeys (Windows)
|
|
163
|
+
*/
|
|
164
|
+
function injectToTerminal(text) {
|
|
165
|
+
try {
|
|
166
|
+
// Replace newlines with spaces for single-line injection
|
|
167
|
+
const singleLine = text.replace(/[\r\n]+/g, ' ').trim();
|
|
168
|
+
|
|
169
|
+
// Write to temp file with UTF-8 BOM
|
|
170
|
+
const tempFile = path.join(tempDir, 'telegram-mcp-cmd.txt');
|
|
171
|
+
const BOM = '\uFEFF';
|
|
172
|
+
fs.writeFileSync(tempFile, BOM + singleLine, 'utf8');
|
|
173
|
+
|
|
174
|
+
// Copy to clipboard with UTF-8 encoding
|
|
175
|
+
execSync(`powershell -command "$text = Get-Content -Path '${tempFile}' -Raw -Encoding UTF8; Set-Clipboard -Value $text"`, { stdio: 'ignore' });
|
|
176
|
+
|
|
177
|
+
// Create PowerShell script for SendKeys
|
|
178
|
+
const scriptPath = path.join(tempDir, 'telegram-mcp-inject.ps1');
|
|
179
|
+
const script = `
|
|
180
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
181
|
+
$wshell = New-Object -ComObject wscript.shell
|
|
182
|
+
$windows = @('WindowsTerminal', 'cmd', 'powershell', 'Code')
|
|
183
|
+
foreach ($proc in $windows) {
|
|
184
|
+
$p = Get-Process -Name $proc -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
185
|
+
if ($p) {
|
|
186
|
+
$wshell.AppActivate($p.Id)
|
|
187
|
+
Start-Sleep -Milliseconds 500
|
|
188
|
+
[System.Windows.Forms.SendKeys]::SendWait('^v')
|
|
189
|
+
Start-Sleep -Milliseconds 200
|
|
190
|
+
[System.Windows.Forms.SendKeys]::SendWait('{ENTER}')
|
|
191
|
+
break
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
`;
|
|
195
|
+
fs.writeFileSync(scriptPath, script);
|
|
196
|
+
execSync(`powershell -ExecutionPolicy Bypass -File "${scriptPath}"`, { stdio: 'ignore' });
|
|
197
|
+
|
|
198
|
+
return true;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error('Injection failed:', error.message);
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Poll for messages and inject to terminal
|
|
207
|
+
*/
|
|
208
|
+
async function pollAndInject() {
|
|
209
|
+
if (!pollingActive) return;
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const response = await axiosInstance.get(`${API_BASE}/getUpdates`, {
|
|
213
|
+
params: {
|
|
214
|
+
offset: lastUpdateId + 1,
|
|
215
|
+
limit: 10,
|
|
216
|
+
timeout: 0
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if (response.data.ok && response.data.result.length > 0) {
|
|
221
|
+
for (const update of response.data.result) {
|
|
222
|
+
lastUpdateId = update.update_id;
|
|
223
|
+
|
|
224
|
+
if (update.message && update.message.text) {
|
|
225
|
+
const text = update.message.text;
|
|
226
|
+
const chatId = update.message.chat.id.toString();
|
|
227
|
+
|
|
228
|
+
// Only process messages from authorized chat
|
|
229
|
+
if (CHAT_ID && chatId !== CHAT_ID) continue;
|
|
230
|
+
|
|
231
|
+
// Skip commands starting with /
|
|
232
|
+
if (text.startsWith('/')) continue;
|
|
233
|
+
|
|
234
|
+
// Inject message to terminal
|
|
235
|
+
injectToTerminal(text);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
if (!error.message.includes('timeout') && !error.message.includes('ECONNRESET')) {
|
|
241
|
+
console.error('Poll error:', error.message);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Start polling service
|
|
248
|
+
*/
|
|
249
|
+
function startPolling(intervalMs = 2000) {
|
|
250
|
+
if (pollingActive) {
|
|
251
|
+
return { success: false, message: 'Polling already active' };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
pollingActive = true;
|
|
255
|
+
pollingInterval = setInterval(pollAndInject, intervalMs);
|
|
256
|
+
|
|
257
|
+
return { success: true, message: 'Polling started' };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Stop polling service
|
|
262
|
+
*/
|
|
263
|
+
function stopPolling() {
|
|
264
|
+
if (!pollingActive) {
|
|
265
|
+
return { success: false, message: 'Polling not active' };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
pollingActive = false;
|
|
269
|
+
if (pollingInterval) {
|
|
270
|
+
clearInterval(pollingInterval);
|
|
271
|
+
pollingInterval = null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return { success: true, message: 'Polling stopped' };
|
|
275
|
+
}
|
|
276
|
+
|
|
153
277
|
// Create MCP server
|
|
154
278
|
const server = new Server(
|
|
155
279
|
{
|
|
156
280
|
name: 'telegram-claude-mcp',
|
|
157
|
-
version: '1.
|
|
281
|
+
version: '1.1.0',
|
|
158
282
|
},
|
|
159
283
|
{
|
|
160
284
|
capabilities: {
|
|
@@ -219,6 +343,27 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
219
343
|
},
|
|
220
344
|
required: ['photo_path']
|
|
221
345
|
}
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
name: 'telegram_start_polling',
|
|
349
|
+
description: 'Start auto-polling for Telegram messages. When enabled, new messages will be automatically injected into the terminal as user input. Call this at the start of a session to enable remote communication.',
|
|
350
|
+
inputSchema: {
|
|
351
|
+
type: 'object',
|
|
352
|
+
properties: {
|
|
353
|
+
interval: {
|
|
354
|
+
type: 'number',
|
|
355
|
+
description: 'Polling interval in milliseconds (default: 2000)'
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
name: 'telegram_stop_polling',
|
|
362
|
+
description: 'Stop auto-polling for Telegram messages.',
|
|
363
|
+
inputSchema: {
|
|
364
|
+
type: 'object',
|
|
365
|
+
properties: {}
|
|
366
|
+
}
|
|
222
367
|
}
|
|
223
368
|
]
|
|
224
369
|
};
|
|
@@ -282,6 +427,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
282
427
|
};
|
|
283
428
|
}
|
|
284
429
|
|
|
430
|
+
case 'telegram_start_polling': {
|
|
431
|
+
const result = startPolling(args.interval || 2000);
|
|
432
|
+
if (result.success) {
|
|
433
|
+
await sendMessage('Telegram远程控制已启动!发送消息将自动注入到Claude Code终端。');
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
content: [
|
|
437
|
+
{
|
|
438
|
+
type: 'text',
|
|
439
|
+
text: result.message
|
|
440
|
+
}
|
|
441
|
+
]
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
case 'telegram_stop_polling': {
|
|
446
|
+
const result = stopPolling();
|
|
447
|
+
if (result.success) {
|
|
448
|
+
await sendMessage('Telegram远程控制已停止。');
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
content: [
|
|
452
|
+
{
|
|
453
|
+
type: 'text',
|
|
454
|
+
text: result.message
|
|
455
|
+
}
|
|
456
|
+
]
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
285
460
|
default:
|
|
286
461
|
throw new Error(`Unknown tool: ${name}`);
|
|
287
462
|
}
|
|
@@ -302,7 +477,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
302
477
|
async function main() {
|
|
303
478
|
const transport = new StdioServerTransport();
|
|
304
479
|
await server.connect(transport);
|
|
305
|
-
console.error('Telegram Claude MCP server running');
|
|
480
|
+
console.error('Telegram Claude MCP server running (v1.1.0 with polling support)');
|
|
306
481
|
}
|
|
307
482
|
|
|
308
483
|
main().catch(console.error);
|