telegram-claude-mcp 1.6.0 ā 2.0.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/ARCHITECTURE.md +234 -0
- package/README.md +249 -58
- package/bin/daemon-ctl.js +207 -0
- package/bin/daemon.js +20 -0
- package/bin/proxy.js +22 -0
- package/bin/setup.js +90 -63
- package/hooks-v2/notify-hook.sh +32 -0
- package/hooks-v2/permission-hook.sh +43 -0
- package/hooks-v2/stop-hook.sh +45 -0
- package/package.json +16 -5
- package/src/daemon/index.ts +415 -0
- package/src/daemon/session-manager.ts +173 -0
- package/src/daemon/telegram-multi.ts +611 -0
- package/src/proxy/index.ts +429 -0
- package/src/shared/protocol.ts +146 -0
- package/src/telegram.ts +85 -71
package/src/telegram.ts
CHANGED
|
@@ -209,48 +209,55 @@ export class TelegramManager {
|
|
|
209
209
|
{ chat_id: this.config.chatId, message_id: sent.message_id }
|
|
210
210
|
);
|
|
211
211
|
|
|
212
|
-
//
|
|
212
|
+
// Use a single long-lived pending permission with periodic reminders
|
|
213
|
+
const totalTimeoutMs = this.config.permissionTimeoutMs! + (120000 * 5); // Base timeout + 5 reminder intervals
|
|
213
214
|
const reminderIntervalMs = 120000; // 2 minutes between reminders
|
|
214
|
-
const maxReminders = 4; // Up to 4 reminders (total ~10 min with initial wait)
|
|
215
215
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
216
|
+
// Start reminder loop in background
|
|
217
|
+
let reminderCount = 0;
|
|
218
|
+
const maxReminders = 5;
|
|
219
|
+
const reminderInterval = setInterval(async () => {
|
|
220
|
+
reminderCount++;
|
|
221
|
+
if (reminderCount <= maxReminders) {
|
|
222
|
+
try {
|
|
223
|
+
const isLastChance = reminderCount === maxReminders;
|
|
224
|
+
if (isLastChance) {
|
|
225
|
+
// Send final reminder with new buttons
|
|
226
|
+
const retryMsg = await this.bot.sendMessage(
|
|
227
|
+
this.config.chatId,
|
|
228
|
+
`ā ļø [${this.config.sessionName}] Last chance! Permission will be denied soon.\n\nTool: ${toolName}\n\nClick below to respond now:`,
|
|
229
|
+
);
|
|
230
|
+
await this.bot.editMessageReplyMarkup(
|
|
231
|
+
{
|
|
232
|
+
inline_keyboard: [
|
|
233
|
+
[
|
|
234
|
+
{ text: 'ā
Allow Now', callback_data: `allow:${sent.message_id}` },
|
|
235
|
+
{ text: 'ā Deny', callback_data: `deny:${sent.message_id}` },
|
|
236
|
+
],
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
{ chat_id: this.config.chatId, message_id: retryMsg.message_id }
|
|
240
|
+
);
|
|
241
|
+
} else {
|
|
242
|
+
await this.bot.sendMessage(
|
|
243
|
+
this.config.chatId,
|
|
244
|
+
`ā° [${this.config.sessionName}] Reminder ${reminderCount}/${maxReminders}: Permission needed\n\nTool: ${toolName}\n\nš Please respond to the message above`,
|
|
245
|
+
{ reply_to_message_id: sent.message_id }
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
} catch (e) {
|
|
249
|
+
console.error('[Telegram] Error sending reminder:', e);
|
|
228
250
|
}
|
|
229
251
|
}
|
|
230
|
-
}
|
|
252
|
+
}, reminderIntervalMs);
|
|
231
253
|
|
|
232
|
-
// Final attempt: ask if they want to retry
|
|
233
|
-
const retryMsg = await this.bot.sendMessage(
|
|
234
|
-
this.config.chatId,
|
|
235
|
-
`ā ļø [${this.config.sessionName}] Permission request timed out\n\nTool: ${toolName}\n\nClick below to respond now:`,
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
await this.bot.editMessageReplyMarkup(
|
|
239
|
-
{
|
|
240
|
-
inline_keyboard: [
|
|
241
|
-
[
|
|
242
|
-
{ text: 'ā
Allow Now', callback_data: `allow:${sent.message_id}` },
|
|
243
|
-
{ text: 'ā Deny', callback_data: `deny:${sent.message_id}` },
|
|
244
|
-
],
|
|
245
|
-
],
|
|
246
|
-
},
|
|
247
|
-
{ chat_id: this.config.chatId, message_id: retryMsg.message_id }
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
// One more wait with the original timeout
|
|
251
254
|
try {
|
|
252
|
-
|
|
255
|
+
// Wait for permission with full timeout
|
|
256
|
+
const decision = await this.waitForPermissionWithTimeout(sent.message_id, toolName, totalTimeoutMs);
|
|
257
|
+
clearInterval(reminderInterval);
|
|
258
|
+
return decision;
|
|
253
259
|
} catch {
|
|
260
|
+
clearInterval(reminderInterval);
|
|
254
261
|
// Truly timed out
|
|
255
262
|
return { behavior: 'deny', message: 'Permission request timed out after multiple reminders' };
|
|
256
263
|
}
|
|
@@ -351,8 +358,7 @@ export class TelegramManager {
|
|
|
351
358
|
if (entry.type === 'assistant' && entry.message?.content) {
|
|
352
359
|
const textContent = entry.message.content.find((c: any) => c.type === 'text');
|
|
353
360
|
if (textContent?.text) {
|
|
354
|
-
lastMessage = textContent.text
|
|
355
|
-
if (textContent.text.length > 500) lastMessage += '...';
|
|
361
|
+
lastMessage = this.truncateMiddle(textContent.text, 600);
|
|
356
362
|
break;
|
|
357
363
|
}
|
|
358
364
|
}
|
|
@@ -376,57 +382,47 @@ export class TelegramManager {
|
|
|
376
382
|
messageIds: [...this.getSessionState().messageIds, sent.message_id],
|
|
377
383
|
});
|
|
378
384
|
|
|
379
|
-
//
|
|
385
|
+
// Use a single long-lived pending response with periodic reminders
|
|
386
|
+
const totalTimeoutMs = this.config.responseTimeoutMs! + (120000 * 5); // Base timeout + 5 reminder intervals
|
|
380
387
|
const reminderIntervalMs = 120000; // 2 minutes between reminders
|
|
381
|
-
const maxReminders = 4; // Up to 4 reminders
|
|
382
388
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
} catch (err) {
|
|
399
|
-
if (attempt < maxReminders) {
|
|
400
|
-
// Send reminder
|
|
401
|
-
await this.bot.sendMessage(
|
|
402
|
-
this.config.chatId,
|
|
403
|
-
`ā° [${this.config.sessionName}] Reminder: Claude is waiting for your response\n\nš¬ Reply with instructions to continue, or "done" to let Claude stop`,
|
|
404
|
-
{ reply_to_message_id: sent.message_id }
|
|
405
|
-
);
|
|
389
|
+
// Start reminder loop in background
|
|
390
|
+
let reminderCount = 0;
|
|
391
|
+
const maxReminders = 5;
|
|
392
|
+
const reminderInterval = setInterval(async () => {
|
|
393
|
+
reminderCount++;
|
|
394
|
+
if (reminderCount <= maxReminders) {
|
|
395
|
+
try {
|
|
396
|
+
const isLastChance = reminderCount === maxReminders;
|
|
397
|
+
const message = isLastChance
|
|
398
|
+
? `ā ļø [${this.config.sessionName}] Last chance! Claude will stop soon if no response.\n\nš¬ Reply now to continue working.`
|
|
399
|
+
: `ā° [${this.config.sessionName}] Reminder ${reminderCount}/${maxReminders}: Claude is waiting\n\nš¬ Reply with instructions to continue, or "done" to stop`;
|
|
400
|
+
|
|
401
|
+
await this.bot.sendMessage(this.config.chatId, message, { reply_to_message_id: sent.message_id });
|
|
402
|
+
} catch (e) {
|
|
403
|
+
console.error('[Telegram] Error sending reminder:', e);
|
|
406
404
|
}
|
|
407
405
|
}
|
|
408
|
-
}
|
|
406
|
+
}, reminderIntervalMs);
|
|
409
407
|
|
|
410
|
-
// Final attempt with longer timeout
|
|
411
408
|
try {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
{ reply_to_message_id: sent.message_id }
|
|
416
|
-
);
|
|
417
|
-
|
|
418
|
-
const response = await this.waitForResponseWithTimeout(sent.message_id, this.config.responseTimeoutMs!);
|
|
409
|
+
// Wait for response with full timeout
|
|
410
|
+
const response = await this.waitForResponseWithTimeout(sent.message_id, totalTimeoutMs);
|
|
411
|
+
clearInterval(reminderInterval);
|
|
419
412
|
|
|
413
|
+
// Check if user wants to stop
|
|
420
414
|
const lowerResponse = response.toLowerCase().trim();
|
|
421
415
|
if (lowerResponse === 'done' || lowerResponse === 'stop' || lowerResponse === 'finish' || lowerResponse === 'ok') {
|
|
422
416
|
return {};
|
|
423
417
|
}
|
|
424
418
|
|
|
419
|
+
// User provided instructions - continue
|
|
425
420
|
return {
|
|
426
421
|
decision: 'block',
|
|
427
422
|
reason: response,
|
|
428
423
|
};
|
|
429
424
|
} catch (err) {
|
|
425
|
+
clearInterval(reminderInterval);
|
|
430
426
|
// Truly timed out - notify and allow stop
|
|
431
427
|
await this.bot.sendMessage(
|
|
432
428
|
this.config.chatId,
|
|
@@ -476,6 +472,24 @@ export class TelegramManager {
|
|
|
476
472
|
});
|
|
477
473
|
}
|
|
478
474
|
|
|
475
|
+
/**
|
|
476
|
+
* Truncate text by removing the middle, keeping beginning and end
|
|
477
|
+
* This is useful because Claude's questions are usually at the end
|
|
478
|
+
*/
|
|
479
|
+
private truncateMiddle(text: string, maxLength: number): string {
|
|
480
|
+
if (text.length <= maxLength) return text;
|
|
481
|
+
|
|
482
|
+
// Keep more at the end (where questions usually are)
|
|
483
|
+
const startLength = Math.floor(maxLength * 0.3); // 30% from start
|
|
484
|
+
const endLength = Math.floor(maxLength * 0.6); // 60% from end
|
|
485
|
+
// ~10% for the ellipsis marker
|
|
486
|
+
|
|
487
|
+
const start = text.slice(0, startLength);
|
|
488
|
+
const end = text.slice(-endLength);
|
|
489
|
+
|
|
490
|
+
return `${start}\n\n[...truncated...]\n\n${end}`;
|
|
491
|
+
}
|
|
492
|
+
|
|
479
493
|
/**
|
|
480
494
|
* Format tool input for display
|
|
481
495
|
*/
|