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/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
- // Try with reminders
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
- for (let attempt = 0; attempt <= maxReminders; attempt++) {
217
- try {
218
- const decision = await this.waitForPermissionWithTimeout(sent.message_id, toolName, reminderIntervalMs);
219
- return decision;
220
- } catch (err) {
221
- if (attempt < maxReminders) {
222
- // Send reminder
223
- await this.bot.sendMessage(
224
- this.config.chatId,
225
- `ā° [${this.config.sessionName}] Reminder: Still waiting for permission\n\nTool: ${toolName}\n\nšŸ‘† Please respond to the message above`,
226
- { reply_to_message_id: sent.message_id }
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
- return await this.waitForPermissionWithTimeout(sent.message_id, toolName, this.config.permissionTimeoutMs!);
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.slice(0, 500);
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
- // Try with reminders
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
- for (let attempt = 0; attempt <= maxReminders; attempt++) {
384
- try {
385
- const response = await this.waitForResponseWithTimeout(sent.message_id, reminderIntervalMs);
386
-
387
- // Check if user wants to stop
388
- const lowerResponse = response.toLowerCase().trim();
389
- if (lowerResponse === 'done' || lowerResponse === 'stop' || lowerResponse === 'finish' || lowerResponse === 'ok') {
390
- return {};
391
- }
392
-
393
- // User provided instructions - continue
394
- return {
395
- decision: 'block',
396
- reason: response,
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
- await this.bot.sendMessage(
413
- this.config.chatId,
414
- `āš ļø [${this.config.sessionName}] Last chance! Claude will stop in ${Math.round(this.config.responseTimeoutMs! / 60000)} minutes if no response.\n\nšŸ’¬ Reply now to continue working.`,
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
  */