mcp-telegram-claudecode 1.0.0 → 1.3.1
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/CHANGELOG.md +65 -0
- package/README.md +91 -4
- package/README_CN.md +90 -3
- package/index.js +198 -12
- package/package.json +1 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [1.3.1] - 2026-02-04
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Added `pollingInProgress` lock to prevent race condition when multiple polls overlap
|
|
9
|
+
- Fixed message duplication issue caused by concurrent polling
|
|
10
|
+
- Added request timeouts (10s for messages, 30s for photos) to prevent hanging
|
|
11
|
+
- Moved FormData import to top level for better performance
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Updated `telegram_send_message` tool description to emphasize always responding via Telegram
|
|
15
|
+
- Updated `telegram_start_polling` description to note auto-start behavior
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Auto-start polling when MCP server starts (if BOT_TOKEN and CHAT_ID are configured)
|
|
19
|
+
|
|
20
|
+
## [1.3.0] - 2026-02-04
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- `telegram_send_photo` tool for sending images
|
|
24
|
+
- Proxy support via HTTP_PROXY/HTTPS_PROXY environment variables
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Improved error handling in polling
|
|
28
|
+
|
|
29
|
+
## [1.2.0] - 2026-02-03
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- `telegram_start_polling` and `telegram_stop_polling` tools
|
|
33
|
+
- Auto-injection of Telegram messages to terminal via SendKeys (Windows)
|
|
34
|
+
|
|
35
|
+
## [1.1.0] - 2026-02-02
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
- `telegram_check_new` tool for quick message check
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- Improved message filtering by chat ID
|
|
42
|
+
|
|
43
|
+
## [1.0.0] - 2026-02-01
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
- Initial release
|
|
47
|
+
- `telegram_send_message` tool
|
|
48
|
+
- `telegram_get_messages` tool
|
|
49
|
+
- Basic Telegram Bot API integration
|
|
50
|
+
- MCP server implementation using @modelcontextprotocol/sdk
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Known Issues
|
|
55
|
+
|
|
56
|
+
- **Multiple Claude Code instances**: Running multiple Claude Code windows will cause message duplication as each instance runs its own MCP server
|
|
57
|
+
- **SendKeys reliability**: Terminal injection depends on window focus and may fail occasionally
|
|
58
|
+
- **No message persistence**: Failed injections result in lost messages
|
|
59
|
+
|
|
60
|
+
## Planned Improvements
|
|
61
|
+
|
|
62
|
+
- Lock file mechanism to prevent multiple instance conflicts
|
|
63
|
+
- Retry logic for SendKeys injection
|
|
64
|
+
- Failure notifications via Telegram
|
|
65
|
+
- Message queue for failed injections
|
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
|
{
|
|
@@ -182,6 +182,93 @@ If you're in a region where Telegram is blocked:
|
|
|
182
182
|
|
|
183
183
|
---
|
|
184
184
|
|
|
185
|
+
## How It Works
|
|
186
|
+
|
|
187
|
+
### Architecture
|
|
188
|
+
|
|
189
|
+
This MCP server acts as a bridge between Claude Code and Telegram:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
┌─────────────┐ MCP Protocol ┌─────────────────┐ Telegram API ┌──────────┐
|
|
193
|
+
│ Claude Code │ ◄──────────────────► │ MCP Server │ ◄─────────────────► │ Telegram │
|
|
194
|
+
│ │ │ (this project) │ │ │
|
|
195
|
+
└─────────────┘ └─────────────────┘ └──────────┘
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Auto-Polling & Terminal Injection (Experimental)
|
|
199
|
+
|
|
200
|
+
When the MCP server starts, it automatically begins polling for new Telegram messages. When a message is received, it attempts to inject the text into the active terminal window using:
|
|
201
|
+
|
|
202
|
+
1. **Clipboard**: Message is copied to system clipboard
|
|
203
|
+
2. **SendKeys (Windows)**: PowerShell script simulates Ctrl+V and Enter keystrokes
|
|
204
|
+
3. **Window Activation**: Attempts to find and activate terminal windows (Windows Terminal, cmd, PowerShell, VS Code)
|
|
205
|
+
|
|
206
|
+
**This is an experimental feature** - it enables "remote control" of Claude Code via Telegram, but has reliability limitations.
|
|
207
|
+
|
|
208
|
+
### Tools Available
|
|
209
|
+
|
|
210
|
+
| Tool | Description |
|
|
211
|
+
|------|-------------|
|
|
212
|
+
| `telegram_send_message` | Send text to Telegram |
|
|
213
|
+
| `telegram_get_messages` | Retrieve recent messages |
|
|
214
|
+
| `telegram_check_new` | Quick check for new messages |
|
|
215
|
+
| `telegram_send_photo` | Send images to Telegram |
|
|
216
|
+
| `telegram_start_polling` | Manually start auto-polling |
|
|
217
|
+
| `telegram_stop_polling` | Stop auto-polling |
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Known Issues & Limitations
|
|
222
|
+
|
|
223
|
+
### ⚠️ Multiple Claude Code Instances
|
|
224
|
+
|
|
225
|
+
**Problem**: If you run multiple Claude Code windows, each will start its own MCP server instance. All instances will poll the same Telegram bot, causing:
|
|
226
|
+
- Duplicate message processing
|
|
227
|
+
- Multiple injection attempts
|
|
228
|
+
- Duplicate responses
|
|
229
|
+
|
|
230
|
+
**Workaround**: Only run one Claude Code instance when using Telegram integration, or disable the Telegram MCP in additional instances.
|
|
231
|
+
|
|
232
|
+
### ⚠️ SendKeys Reliability (Windows)
|
|
233
|
+
|
|
234
|
+
The terminal injection feature depends on:
|
|
235
|
+
- **Window focus**: Target terminal must be activatable
|
|
236
|
+
- **Clipboard access**: System clipboard must be available
|
|
237
|
+
- **Timing**: SendKeys requires precise timing
|
|
238
|
+
|
|
239
|
+
**When it may fail**:
|
|
240
|
+
- Another application has focus and won't release it
|
|
241
|
+
- System is under heavy load
|
|
242
|
+
- Remote desktop or virtual machine environments
|
|
243
|
+
- Screen is locked
|
|
244
|
+
|
|
245
|
+
**Workaround**: If injection fails, manually check Telegram messages using the `telegram_get_messages` tool.
|
|
246
|
+
|
|
247
|
+
### ⚠️ Platform Support
|
|
248
|
+
|
|
249
|
+
- **Windows**: Full support (auto-polling + SendKeys injection)
|
|
250
|
+
- **macOS/Linux**: Partial support (tools work, but auto-injection not implemented)
|
|
251
|
+
|
|
252
|
+
### ⚠️ Not Fully Unattended
|
|
253
|
+
|
|
254
|
+
This MCP cannot wake up Claude Code on its own. The auto-injection only works when:
|
|
255
|
+
- Claude Code is running and waiting for input
|
|
256
|
+
- A terminal window is accessible
|
|
257
|
+
|
|
258
|
+
For true unattended operation, consider using Claude Code's hook system instead.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Planned Improvements
|
|
263
|
+
|
|
264
|
+
- [ ] Lock file mechanism to prevent multiple instance conflicts
|
|
265
|
+
- [ ] Retry logic for failed injections
|
|
266
|
+
- [ ] Failure notifications via Telegram
|
|
267
|
+
- [ ] Message queue for failed injections
|
|
268
|
+
- [ ] macOS/Linux injection support
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
185
272
|
## License
|
|
186
273
|
|
|
187
274
|
MIT License - see [LICENSE](LICENSE) file for details.
|
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
|
{
|
|
@@ -182,6 +182,93 @@ claude /settings
|
|
|
182
182
|
|
|
183
183
|
---
|
|
184
184
|
|
|
185
|
+
## 工作原理
|
|
186
|
+
|
|
187
|
+
### 架构
|
|
188
|
+
|
|
189
|
+
这个 MCP 服务器充当 Claude Code 和 Telegram 之间的桥梁:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
┌─────────────┐ MCP 协议 ┌─────────────────┐ Telegram API ┌──────────┐
|
|
193
|
+
│ Claude Code │ ◄──────────────────► │ MCP 服务器 │ ◄─────────────────► │ Telegram │
|
|
194
|
+
│ │ │ (本项目) │ │ │
|
|
195
|
+
└─────────────┘ └─────────────────┘ └──────────┘
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 自动轮询与终端注入(实验性功能)
|
|
199
|
+
|
|
200
|
+
MCP 服务器启动时会自动开始轮询 Telegram 新消息。收到消息后,会尝试将文本注入到活动的终端窗口:
|
|
201
|
+
|
|
202
|
+
1. **剪贴板**:消息被复制到系统剪贴板
|
|
203
|
+
2. **SendKeys (Windows)**:PowerShell 脚本模拟 Ctrl+V 和 Enter 按键
|
|
204
|
+
3. **窗口激活**:尝试查找并激活终端窗口(Windows Terminal、cmd、PowerShell、VS Code)
|
|
205
|
+
|
|
206
|
+
**这是一个实验性功能** - 它可以实现通过 Telegram "远程控制" Claude Code,但存在可靠性限制。
|
|
207
|
+
|
|
208
|
+
### 可用工具
|
|
209
|
+
|
|
210
|
+
| 工具 | 说明 |
|
|
211
|
+
|------|------|
|
|
212
|
+
| `telegram_send_message` | 发送文字到 Telegram |
|
|
213
|
+
| `telegram_get_messages` | 获取最近消息 |
|
|
214
|
+
| `telegram_check_new` | 快速检查新消息 |
|
|
215
|
+
| `telegram_send_photo` | 发送图片到 Telegram |
|
|
216
|
+
| `telegram_start_polling` | 手动启动自动轮询 |
|
|
217
|
+
| `telegram_stop_polling` | 停止自动轮询 |
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 已知问题与限制
|
|
222
|
+
|
|
223
|
+
### ⚠️ 多个 Claude Code 实例
|
|
224
|
+
|
|
225
|
+
**问题**:如果运行多个 Claude Code 窗口,每个都会启动自己的 MCP 服务器实例。所有实例都会轮询同一个 Telegram 机器人,导致:
|
|
226
|
+
- 消息重复处理
|
|
227
|
+
- 多次注入尝试
|
|
228
|
+
- 重复回复
|
|
229
|
+
|
|
230
|
+
**解决方法**:使用 Telegram 集成时只运行一个 Claude Code 实例,或在其他实例中禁用 Telegram MCP。
|
|
231
|
+
|
|
232
|
+
### ⚠️ SendKeys 可靠性 (Windows)
|
|
233
|
+
|
|
234
|
+
终端注入功能依赖于:
|
|
235
|
+
- **窗口焦点**:目标终端必须可以被激活
|
|
236
|
+
- **剪贴板访问**:系统剪贴板必须可用
|
|
237
|
+
- **时序控制**:SendKeys 需要精确的时序
|
|
238
|
+
|
|
239
|
+
**可能失败的情况**:
|
|
240
|
+
- 其他应用程序占用焦点且不释放
|
|
241
|
+
- 系统负载过高
|
|
242
|
+
- 远程桌面或虚拟机环境
|
|
243
|
+
- 屏幕被锁定
|
|
244
|
+
|
|
245
|
+
**解决方法**:如果注入失败,可以手动使用 `telegram_get_messages` 工具检查消息。
|
|
246
|
+
|
|
247
|
+
### ⚠️ 平台支持
|
|
248
|
+
|
|
249
|
+
- **Windows**:完整支持(自动轮询 + SendKeys 注入)
|
|
250
|
+
- **macOS/Linux**:部分支持(工具可用,但未实现自动注入)
|
|
251
|
+
|
|
252
|
+
### ⚠️ 非完全无人值守
|
|
253
|
+
|
|
254
|
+
这个 MCP 无法主动唤醒 Claude Code。自动注入只在以下情况下有效:
|
|
255
|
+
- Claude Code 正在运行并等待输入
|
|
256
|
+
- 终端窗口可以被访问
|
|
257
|
+
|
|
258
|
+
如需真正的无人值守操作,请考虑使用 Claude Code 的 Hook 系统。
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## 计划改进
|
|
263
|
+
|
|
264
|
+
- [ ] 锁文件机制防止多实例冲突
|
|
265
|
+
- [ ] 注入失败重试逻辑
|
|
266
|
+
- [ ] 通过 Telegram 发送失败通知
|
|
267
|
+
- [ ] 失败消息队列
|
|
268
|
+
- [ ] macOS/Linux 注入支持
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
185
272
|
## 许可证
|
|
186
273
|
|
|
187
274
|
MIT 许可证 - 详见 [LICENSE](LICENSE) 文件。
|
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,10 @@ 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');
|
|
20
|
+
const FormData = require('form-data');
|
|
16
21
|
|
|
17
22
|
// Configuration from environment variables
|
|
18
23
|
const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
|
|
@@ -34,6 +39,14 @@ const API_BASE = `https://api.telegram.org/bot${BOT_TOKEN}`;
|
|
|
34
39
|
// Track last update ID for polling
|
|
35
40
|
let lastUpdateId = 0;
|
|
36
41
|
|
|
42
|
+
// Polling state
|
|
43
|
+
let pollingActive = false;
|
|
44
|
+
let pollingInterval = null;
|
|
45
|
+
let pollingInProgress = false;
|
|
46
|
+
|
|
47
|
+
// Temp directory for injection scripts
|
|
48
|
+
const tempDir = process.env.TEMP || process.env.TMP || '/tmp';
|
|
49
|
+
|
|
37
50
|
/**
|
|
38
51
|
* Send a text message to Telegram
|
|
39
52
|
*/
|
|
@@ -44,9 +57,8 @@ async function sendMessage(text) {
|
|
|
44
57
|
|
|
45
58
|
const response = await axiosInstance.post(`${API_BASE}/sendMessage`, {
|
|
46
59
|
chat_id: CHAT_ID,
|
|
47
|
-
text: text
|
|
48
|
-
|
|
49
|
-
});
|
|
60
|
+
text: text
|
|
61
|
+
}, { timeout: 10000 });
|
|
50
62
|
|
|
51
63
|
return response.data;
|
|
52
64
|
}
|
|
@@ -59,9 +71,6 @@ async function sendPhoto(photoPath, caption = '') {
|
|
|
59
71
|
throw new Error('TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID must be configured');
|
|
60
72
|
}
|
|
61
73
|
|
|
62
|
-
const fs = require('fs');
|
|
63
|
-
const FormData = require('form-data');
|
|
64
|
-
|
|
65
74
|
const form = new FormData();
|
|
66
75
|
form.append('chat_id', CHAT_ID);
|
|
67
76
|
form.append('photo', fs.createReadStream(photoPath));
|
|
@@ -70,7 +79,8 @@ async function sendPhoto(photoPath, caption = '') {
|
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
const response = await axiosInstance.post(`${API_BASE}/sendPhoto`, form, {
|
|
73
|
-
headers: form.getHeaders()
|
|
82
|
+
headers: form.getHeaders(),
|
|
83
|
+
timeout: 30000
|
|
74
84
|
});
|
|
75
85
|
|
|
76
86
|
return response.data;
|
|
@@ -89,7 +99,8 @@ async function getMessages(limit = 10) {
|
|
|
89
99
|
offset: lastUpdateId + 1,
|
|
90
100
|
limit: limit,
|
|
91
101
|
timeout: 0
|
|
92
|
-
}
|
|
102
|
+
},
|
|
103
|
+
timeout: 10000
|
|
93
104
|
});
|
|
94
105
|
|
|
95
106
|
const messages = [];
|
|
@@ -97,7 +108,6 @@ async function getMessages(limit = 10) {
|
|
|
97
108
|
for (const update of response.data.result) {
|
|
98
109
|
lastUpdateId = update.update_id;
|
|
99
110
|
if (update.message && update.message.text) {
|
|
100
|
-
// Filter by chat ID if configured
|
|
101
111
|
if (!CHAT_ID || update.message.chat.id.toString() === CHAT_ID) {
|
|
102
112
|
messages.push({
|
|
103
113
|
id: update.message.message_id,
|
|
@@ -150,11 +160,130 @@ async function checkNewMessages() {
|
|
|
150
160
|
return { hasNew, latestMessage, updateId: lastUpdateId };
|
|
151
161
|
}
|
|
152
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Inject text into terminal via clipboard and SendKeys (Windows)
|
|
165
|
+
*/
|
|
166
|
+
function injectToTerminal(text) {
|
|
167
|
+
try {
|
|
168
|
+
// Replace newlines with spaces for single-line injection
|
|
169
|
+
const singleLine = text.replace(/[\r\n]+/g, ' ').trim();
|
|
170
|
+
|
|
171
|
+
// Write to temp file with UTF-8 BOM
|
|
172
|
+
const tempFile = path.join(tempDir, 'telegram-mcp-cmd.txt');
|
|
173
|
+
const BOM = '\uFEFF';
|
|
174
|
+
fs.writeFileSync(tempFile, BOM + singleLine, 'utf8');
|
|
175
|
+
|
|
176
|
+
// Copy to clipboard with UTF-8 encoding
|
|
177
|
+
execSync(`powershell -command "$text = Get-Content -Path '${tempFile}' -Raw -Encoding UTF8; Set-Clipboard -Value $text"`, { stdio: 'ignore' });
|
|
178
|
+
|
|
179
|
+
// Create PowerShell script for SendKeys
|
|
180
|
+
const scriptPath = path.join(tempDir, 'telegram-mcp-inject.ps1');
|
|
181
|
+
const script = `
|
|
182
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
183
|
+
$wshell = New-Object -ComObject wscript.shell
|
|
184
|
+
$windows = @('WindowsTerminal', 'cmd', 'powershell', 'Code')
|
|
185
|
+
foreach ($proc in $windows) {
|
|
186
|
+
$p = Get-Process -Name $proc -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
187
|
+
if ($p) {
|
|
188
|
+
$wshell.AppActivate($p.Id)
|
|
189
|
+
Start-Sleep -Milliseconds 500
|
|
190
|
+
[System.Windows.Forms.SendKeys]::SendWait('^v')
|
|
191
|
+
Start-Sleep -Milliseconds 200
|
|
192
|
+
[System.Windows.Forms.SendKeys]::SendWait('{ENTER}')
|
|
193
|
+
break
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
`;
|
|
197
|
+
fs.writeFileSync(scriptPath, script);
|
|
198
|
+
execSync(`powershell -ExecutionPolicy Bypass -File "${scriptPath}"`, { stdio: 'ignore' });
|
|
199
|
+
|
|
200
|
+
return true;
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error('Injection failed:', error.message);
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Poll for messages and inject to terminal
|
|
209
|
+
*/
|
|
210
|
+
async function pollAndInject() {
|
|
211
|
+
if (!pollingActive || pollingInProgress) return;
|
|
212
|
+
|
|
213
|
+
pollingInProgress = true;
|
|
214
|
+
try {
|
|
215
|
+
const response = await axiosInstance.get(`${API_BASE}/getUpdates`, {
|
|
216
|
+
params: {
|
|
217
|
+
offset: lastUpdateId + 1,
|
|
218
|
+
limit: 10,
|
|
219
|
+
timeout: 0
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (response.data.ok && response.data.result.length > 0) {
|
|
224
|
+
for (const update of response.data.result) {
|
|
225
|
+
lastUpdateId = update.update_id;
|
|
226
|
+
|
|
227
|
+
if (update.message && update.message.text) {
|
|
228
|
+
const text = update.message.text;
|
|
229
|
+
const chatId = update.message.chat.id.toString();
|
|
230
|
+
|
|
231
|
+
// Only process messages from authorized chat
|
|
232
|
+
if (CHAT_ID && chatId !== CHAT_ID) continue;
|
|
233
|
+
|
|
234
|
+
// Skip commands starting with /
|
|
235
|
+
if (text.startsWith('/')) continue;
|
|
236
|
+
|
|
237
|
+
// Inject message to terminal
|
|
238
|
+
injectToTerminal(text);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} catch (error) {
|
|
243
|
+
if (!error.message.includes('timeout') && !error.message.includes('ECONNRESET')) {
|
|
244
|
+
console.error('Poll error:', error.message);
|
|
245
|
+
}
|
|
246
|
+
} finally {
|
|
247
|
+
pollingInProgress = false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Start polling service
|
|
253
|
+
*/
|
|
254
|
+
function startPolling(intervalMs = 2000) {
|
|
255
|
+
if (pollingActive) {
|
|
256
|
+
return { success: false, message: 'Polling already active' };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
pollingActive = true;
|
|
260
|
+
pollingInterval = setInterval(pollAndInject, intervalMs);
|
|
261
|
+
|
|
262
|
+
return { success: true, message: 'Polling started' };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Stop polling service
|
|
267
|
+
*/
|
|
268
|
+
function stopPolling() {
|
|
269
|
+
if (!pollingActive) {
|
|
270
|
+
return { success: false, message: 'Polling not active' };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
pollingActive = false;
|
|
274
|
+
if (pollingInterval) {
|
|
275
|
+
clearInterval(pollingInterval);
|
|
276
|
+
pollingInterval = null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return { success: true, message: 'Polling stopped' };
|
|
280
|
+
}
|
|
281
|
+
|
|
153
282
|
// Create MCP server
|
|
154
283
|
const server = new Server(
|
|
155
284
|
{
|
|
156
285
|
name: 'telegram-claude-mcp',
|
|
157
|
-
version: '1.
|
|
286
|
+
version: '1.3.1',
|
|
158
287
|
},
|
|
159
288
|
{
|
|
160
289
|
capabilities: {
|
|
@@ -169,7 +298,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
169
298
|
tools: [
|
|
170
299
|
{
|
|
171
300
|
name: 'telegram_send_message',
|
|
172
|
-
description: 'Send a text message to the configured Telegram chat.
|
|
301
|
+
description: 'Send a text message to the configured Telegram chat. IMPORTANT: You MUST use this tool to reply to the user regardless of whether they sent their message via terminal or Telegram. Always respond through Telegram so the user can see your replies on their phone.',
|
|
173
302
|
inputSchema: {
|
|
174
303
|
type: 'object',
|
|
175
304
|
properties: {
|
|
@@ -219,6 +348,27 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
219
348
|
},
|
|
220
349
|
required: ['photo_path']
|
|
221
350
|
}
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
name: 'telegram_start_polling',
|
|
354
|
+
description: 'Manually start auto-polling for Telegram messages (polling starts automatically on MCP load, so this is usually not needed). When enabled, new messages will be automatically injected into the terminal as user input.',
|
|
355
|
+
inputSchema: {
|
|
356
|
+
type: 'object',
|
|
357
|
+
properties: {
|
|
358
|
+
interval: {
|
|
359
|
+
type: 'number',
|
|
360
|
+
description: 'Polling interval in milliseconds (default: 2000)'
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
name: 'telegram_stop_polling',
|
|
367
|
+
description: 'Stop auto-polling for Telegram messages.',
|
|
368
|
+
inputSchema: {
|
|
369
|
+
type: 'object',
|
|
370
|
+
properties: {}
|
|
371
|
+
}
|
|
222
372
|
}
|
|
223
373
|
]
|
|
224
374
|
};
|
|
@@ -282,6 +432,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
282
432
|
};
|
|
283
433
|
}
|
|
284
434
|
|
|
435
|
+
case 'telegram_start_polling': {
|
|
436
|
+
const result = startPolling(args.interval || 2000);
|
|
437
|
+
if (result.success) {
|
|
438
|
+
await sendMessage('Telegram远程控制已启动!发送消息将自动注入到Claude Code终端。');
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
content: [
|
|
442
|
+
{
|
|
443
|
+
type: 'text',
|
|
444
|
+
text: result.message
|
|
445
|
+
}
|
|
446
|
+
]
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
case 'telegram_stop_polling': {
|
|
451
|
+
const result = stopPolling();
|
|
452
|
+
if (result.success) {
|
|
453
|
+
await sendMessage('Telegram远程控制已停止。');
|
|
454
|
+
}
|
|
455
|
+
return {
|
|
456
|
+
content: [
|
|
457
|
+
{
|
|
458
|
+
type: 'text',
|
|
459
|
+
text: result.message
|
|
460
|
+
}
|
|
461
|
+
]
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
285
465
|
default:
|
|
286
466
|
throw new Error(`Unknown tool: ${name}`);
|
|
287
467
|
}
|
|
@@ -302,7 +482,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
302
482
|
async function main() {
|
|
303
483
|
const transport = new StdioServerTransport();
|
|
304
484
|
await server.connect(transport);
|
|
305
|
-
console.error('Telegram Claude MCP server running');
|
|
485
|
+
console.error('Telegram Claude MCP server running (v1.3.1)');
|
|
486
|
+
|
|
487
|
+
// Auto-start polling when server starts
|
|
488
|
+
if (BOT_TOKEN && CHAT_ID) {
|
|
489
|
+
startPolling(2000);
|
|
490
|
+
console.error('Auto-polling started');
|
|
491
|
+
}
|
|
306
492
|
}
|
|
307
493
|
|
|
308
494
|
main().catch(console.error);
|