imtoagent 0.3.4 → 0.3.6

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.
Files changed (39) hide show
  1. package/README.md +97 -97
  2. package/bin/imtoagent-real +197 -153
  3. package/bin/imtoagent.cjs +13 -5
  4. package/index.ts +106 -106
  5. package/modules/agent/claude-adapter.ts +6 -6
  6. package/modules/agent/claude.ts +6 -6
  7. package/modules/agent/codex-adapter.ts +13 -13
  8. package/modules/agent/codex-exec-server.ts +11 -11
  9. package/modules/agent/codex.ts +29 -29
  10. package/modules/agent/opencode-adapter.ts +17 -17
  11. package/modules/agent/opencode.ts +10 -10
  12. package/modules/capabilities.ts +33 -33
  13. package/modules/cli/setup.ts +164 -164
  14. package/modules/core/config.ts +5 -5
  15. package/modules/core/error.ts +8 -8
  16. package/modules/core/runtime.ts +10 -10
  17. package/modules/core/session.ts +4 -4
  18. package/modules/core/stats.ts +14 -14
  19. package/modules/core/types.ts +7 -7
  20. package/modules/im/feishu.ts +56 -56
  21. package/modules/im/telegram.ts +23 -23
  22. package/modules/im/wechat.ts +54 -54
  23. package/modules/im/wecom.ts +50 -50
  24. package/modules/media/feishu-inbound-adapter.ts +4 -4
  25. package/modules/media/resolver.ts +11 -11
  26. package/modules/media/telegram-inbound-adapter.ts +8 -8
  27. package/modules/prompt-builder.ts +12 -12
  28. package/modules/proxy/anthropic-proxy.ts +31 -31
  29. package/modules/proxy/codex-proxy.ts +18 -18
  30. package/modules/utils/backend-check.ts +12 -12
  31. package/modules/utils/paths.ts +8 -8
  32. package/package.json +1 -1
  33. package/scripts/postinstall.cjs +10 -10
  34. package/scripts/postinstall.ts +13 -13
  35. package/templates/soul.template/identity.md +5 -5
  36. package/templates/soul.template/profile.md +7 -7
  37. package/templates/soul.template/rules.md +5 -5
  38. package/templates/soul.template/skills.md +2 -2
  39. package/templates/soul.template/workspace.md +3 -3
@@ -89,9 +89,9 @@ export class FeishuIMModule implements IMModule {
89
89
  };
90
90
  return this._tenantAccessToken.token;
91
91
  }
92
- throw new Error(`获取 token 失败: ${res.code} ${res.msg}`);
92
+ throw new Error(`Failed to get token: ${res.code} ${res.msg}`);
93
93
  } catch (e: any) {
94
- throw new Error(`获取 token 失败: ${e.message}`);
94
+ throw new Error(`Failed to get token: ${e.message}`);
95
95
  }
96
96
  }
97
97
 
@@ -108,16 +108,16 @@ export class FeishuIMModule implements IMModule {
108
108
  data: { app_id: this.appId, app_secret: this.appSecret },
109
109
  });
110
110
  if (res.code === 0 && res.app_access_token) {
111
- // 飞书 app_token 有效期约 2 小时
111
+ // Feishu app_token valid for ~2 hours
112
112
  this._appAccessToken = {
113
113
  token: res.app_access_token,
114
114
  expiresAt: now + 2 * 60 * 60 * 1000,
115
115
  };
116
116
  return this._appAccessToken.token;
117
117
  }
118
- throw new Error(`获取 app token 失败: ${res.code} ${res.msg}`);
118
+ throw new Error(`Failed to get app token: ${res.code} ${res.msg}`);
119
119
  } catch (e: any) {
120
- throw new Error(`获取 app token 失败: ${e.message}`);
120
+ throw new Error(`Failed to get app token: ${e.message}`);
121
121
  }
122
122
  }
123
123
 
@@ -126,14 +126,14 @@ export class FeishuIMModule implements IMModule {
126
126
  // ================================================================
127
127
 
128
128
  async reply(chatId: string, text: string, maxLen = 140000) {
129
- const safe = text.length > maxLen ? text.slice(0, maxLen) + '\n\n...(截断)' : text;
129
+ const safe = text.length > maxLen ? text.slice(0, maxLen) + '\n\n...(truncated)' : text;
130
130
  try {
131
131
  await this.client.im.message.create({
132
132
  params: { receive_id_type: 'chat_id' },
133
133
  data: { receive_id: chatId, msg_type: 'text', content: JSON.stringify({ text: safe }) },
134
134
  });
135
135
  } catch (e: any) {
136
- console.error(`[Feishu] 回复失败: ${e.message}`);
136
+ console.error(`[Feishu] Reply failed: ${e.message}`);
137
137
  }
138
138
  }
139
139
 
@@ -144,7 +144,7 @@ export class FeishuIMModule implements IMModule {
144
144
  data: { receive_id: chatId, msg_type: 'text', content: JSON.stringify({ text }) },
145
145
  });
146
146
  } catch (e: any) {
147
- console.error(`[Feishu] 进度推送失败: ${e.message}`);
147
+ console.error(`[Feishu] Progress notification failed: ${e.message}`);
148
148
  }
149
149
  }
150
150
 
@@ -169,14 +169,14 @@ export class FeishuIMModule implements IMModule {
169
169
  }
170
170
  if (fileKey) {
171
171
  await this.sendFile(chatId, fileKey, fb.filename);
172
- console.log(`[Feishu] 文件已发送: ${fb.filename}`);
172
+ console.log(`[Feishu] File sent: ${fb.filename}`);
173
173
  }
174
174
  } catch (e: any) {
175
- console.error(`[Feishu] 文件发送失败: ${fb.filename} - ${e.message}`);
175
+ console.error(`[Feishu] File send failed: ${fb.filename} - ${e.message}`);
176
176
  }
177
177
  }
178
178
 
179
- // 如果只剩下一个文本块,直接发文本
179
+ // If only one text block remains, send as plain text
180
180
  if (cardBlocks.length === 1 && cardBlocks[0].type === 'text') {
181
181
  if (fileBlocks.length === 0) return this.reply(chatId, cardBlocks[0].content);
182
182
  // 有文件在前,文本块附后
@@ -216,8 +216,8 @@ ${this.escapeCodeBlock(block.code)}
216
216
  cardElements.push({ tag: 'img', img_key: imageKey, alt: { tag: 'plain_text', content: block.alt || '' } });
217
217
  }
218
218
  } catch (e: any) {
219
- console.error(`[Feishu] 图片上传失败: ${e.message}`);
220
- cardElements.push({ tag: 'markdown', content: `⚠️ 图片加载失败` });
219
+ console.error(`[Feishu] Image upload failed: ${e.message}`);
220
+ cardElements.push({ tag: 'markdown', content: `⚠️ Image load failed` });
221
221
  }
222
222
  }
223
223
  break;
@@ -268,9 +268,9 @@ ${this.escapeCardMarkdown(block.content || '')}`,
268
268
  content: JSON.stringify(card),
269
269
  },
270
270
  });
271
- console.log(`[Feishu] 卡片消息已发送 (${cardBlocks.length} blocks)`);
271
+ console.log(`[Feishu] Card message sent (${cardBlocks.length} blocks)`);
272
272
  } catch (e: any) {
273
- console.error(`[Feishu] 卡片发送失败: ${e.message}`);
273
+ console.error(`[Feishu] Card send failed: ${e.message}`);
274
274
  // 降级:拼接为纯文本发送
275
275
  const fallback = cardBlocks.map(b => {
276
276
  switch (b.type) {
@@ -305,11 +305,11 @@ ${b.content || ''}`;
305
305
  },
306
306
  });
307
307
  } catch (e: any) {
308
- console.error(`[Feishu] 图片发送失败: ${e.message}`);
308
+ console.error(`[Feishu] Image send failed: ${e.message}`);
309
309
  }
310
310
  }
311
311
 
312
- // URL 上传图片到飞书,返回 image_key
312
+ // Upload image from URL to Feishu, returns image_key
313
313
  async uploadImageFromUrl(url: string): Promise<string | null> {
314
314
  try {
315
315
  let buffer: Buffer | null = null;
@@ -335,15 +335,15 @@ ${b.content || ''}`;
335
335
  });
336
336
  const key = r?.image_key || r?.data?.image_key;
337
337
  if (key) return key;
338
- console.error(`[Feishu] 图片上传失败: image_key missing`);
338
+ console.error(`[Feishu] Image upload failed: image_key missing`);
339
339
  return null;
340
340
  } catch (e: any) {
341
- console.error(`[Feishu] 图片上传异常: ${e.message}`);
341
+ console.error(`[Feishu] Image upload error: ${e.message}`);
342
342
  return null;
343
343
  }
344
344
  }
345
345
 
346
- // 从本地文件上传到飞书,返回 image_key
346
+ // Upload image from local file to Feishu, returns image_key
347
347
  async uploadImageFromFile(filePath: string): Promise<string | null> {
348
348
  try {
349
349
  const buffer = fs.readFileSync(filePath);
@@ -353,42 +353,42 @@ ${b.content || ''}`;
353
353
  });
354
354
  const key = r?.image_key || r?.data?.image_key;
355
355
  if (key) return key;
356
- console.error(`[Feishu] 图片上传失败: image_key missing`);
356
+ console.error(`[Feishu] Image upload failed: image_key missing`);
357
357
  return null;
358
358
  } catch (e: any) {
359
- console.error(`[Feishu] 图片上传失败: ${e.message}`);
359
+ console.error(`[Feishu] Image upload failed: ${e.message}`);
360
360
  return null;
361
361
  }
362
362
  }
363
363
 
364
364
  // ================================================================
365
- // 文件上传
365
+ // File Upload
366
366
  // ================================================================
367
367
 
368
- // URL 下载并上传到飞书,返回 file_key
368
+ // Download from URL and upload to Feishu, returns file_key
369
369
  async uploadFileFromUrl(url: string, filename: string): Promise<string | null> {
370
370
  try {
371
371
  const buffer = await this.downloadFile(url);
372
372
  if (!buffer) return null;
373
373
  return this.uploadFileFromBuffer(buffer, filename || path.basename(new URL(url).pathname) || 'file');
374
374
  } catch (e: any) {
375
- console.error(`[Feishu] 文件上传异常: ${e.message}`);
375
+ console.error(`[Feishu] File upload error: ${e.message}`);
376
376
  return null;
377
377
  }
378
378
  }
379
379
 
380
- // 从本地路径上传文件到飞书,返回 file_key
380
+ // Upload file from local path to Feishu, returns file_key
381
381
  async uploadFileFromPath(filePath: string): Promise<string | null> {
382
382
  try {
383
383
  const buffer = fs.readFileSync(filePath);
384
384
  return this.uploadFileFromBuffer(buffer, path.basename(filePath));
385
385
  } catch (e: any) {
386
- console.error(`[Feishu] 文件上传失败: ${e.message}`);
386
+ console.error(`[Feishu] File upload failed: ${e.message}`);
387
387
  return null;
388
388
  }
389
389
  }
390
390
 
391
- // Buffer 上传文件到飞书,返回 file_key
391
+ // Upload file from Buffer to Feishu, returns file_key
392
392
  private async uploadFileFromBuffer(buffer: Buffer, filename: string): Promise<string | null> {
393
393
  try {
394
394
  const token = await this.getAppToken();
@@ -406,10 +406,10 @@ ${b.content || ''}`;
406
406
  if (data.code === 0 && data.data?.file_key) {
407
407
  return data.data.file_key;
408
408
  }
409
- console.error(`[Feishu] 文件上传失败: ${data.code} ${data.msg}`);
409
+ console.error(`[Feishu] File upload failed: ${data.code} ${data.msg}`);
410
410
  return null;
411
411
  } catch (e: any) {
412
- console.error(`[Feishu] 文件上传异常: ${e.message}`);
412
+ console.error(`[Feishu] File upload error: ${e.message}`);
413
413
  return null;
414
414
  }
415
415
  }
@@ -426,18 +426,18 @@ ${b.content || ''}`;
426
426
  },
427
427
  });
428
428
  } catch (e: any) {
429
- console.error(`[Feishu] 文件发送失败: ${e.message}`);
429
+ console.error(`[Feishu] File send failed: ${e.message}`);
430
430
  }
431
431
  }
432
432
 
433
433
  // ================================================================
434
- // 能力声明
434
+ // Capabilities
435
435
  // ================================================================
436
436
 
437
437
  getCapabilities(): IMCapabilities {
438
438
  return {
439
439
  text: true,
440
- codeBlock: true, // 卡片 markdown 支持 ``` 语法
440
+ codeBlock: true, // Card markdown supports ``` syntax
441
441
  cardMessage: true,
442
442
  fileSend: true,
443
443
  imageSend: true,
@@ -461,7 +461,7 @@ ${b.content || ''}`;
461
461
  this.running = false;
462
462
  if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; }
463
463
  try { this.wsClient?.stop?.(); } catch {}
464
- console.log(`[Feishu] WS 已停止 (appId=${this.appId.slice(-8)})`);
464
+ console.log(`[Feishu] WS stopped (appId=${this.appId.slice(-8)})`);
465
465
  }
466
466
 
467
467
  private _connect() {
@@ -495,7 +495,7 @@ ${b.content || ''}`;
495
495
  });
496
496
  if (resolved) {
497
497
  attachments.push(resolved.attachment);
498
- text = '[用户发送了一张图片]';
498
+ text = '[User sent an image]';
499
499
  }
500
500
  break;
501
501
  }
@@ -510,7 +510,7 @@ ${b.content || ''}`;
510
510
  });
511
511
  if (resolved) {
512
512
  attachments.push(resolved.attachment);
513
- text = `[用户发送了文件: ${content.file_name || 'unknown'}]`;
513
+ text = `[User sent a file: ${content.file_name || 'unknown'}]`;
514
514
  }
515
515
  break;
516
516
  }
@@ -520,13 +520,13 @@ ${b.content || ''}`;
520
520
  const resolved = await this._mediaResolver.resolveOne({
521
521
  messageId: message.message_id,
522
522
  resourceKey: content.file_key,
523
- type: 'file', // 飞书音频也用 file 类型下载
523
+ type: 'file', // Feishu audio also uses file type for download
524
524
  });
525
525
  if (resolved) {
526
526
  // 补充音频时长(resolver 无法从飞书 API 获取 duration)
527
527
  resolved.attachment.durationMs = content.duration;
528
528
  attachments.push(resolved.attachment);
529
- text = `[用户发送了语音消息 (${(content.duration || 0) / 1000})]`;
529
+ text = `[User sent a voice message (${(content.duration || 0) / 1000}s)]`;
530
530
  }
531
531
  break;
532
532
  }
@@ -536,11 +536,11 @@ ${b.content || ''}`;
536
536
  break;
537
537
 
538
538
  case 'media':
539
- text = '[用户发送了视频消息]';
539
+ text = '[User sent a video message]';
540
540
  break;
541
541
 
542
542
  case 'sticker':
543
- text = '[用户发送了表情]';
543
+ text = '[User sent a sticker]';
544
544
  break;
545
545
 
546
546
  case 'system':
@@ -551,11 +551,11 @@ ${b.content || ''}`;
551
551
  break;
552
552
 
553
553
  case 'merge_forward':
554
- text = '[用户转发了合并消息]';
554
+ text = '[User forwarded a merged message]';
555
555
  break;
556
556
 
557
557
  default:
558
- console.log(`[Feishu] 未处理的消息类型: ${msgType}`);
558
+ console.log(`[Feishu] Unhandled message type: ${msgType}`);
559
559
  return;
560
560
  }
561
561
 
@@ -563,23 +563,23 @@ ${b.content || ''}`;
563
563
 
564
564
  await this.messageHandler!(chatId, text, userId, attachments.length > 0 ? attachments : undefined);
565
565
  } catch (e: any) {
566
- console.error(`[Feishu] 消息处理异常: ${e.message}`);
567
- // 不抛异常,防止 SDK dispatcher 未处理 rejection 导致进程退出
566
+ console.error(`[Feishu] Message processing error: ${e.message}`);
567
+ // Don't throw to prevent SDK dispatcher unhandled rejection from crashing the process
568
568
  }
569
569
  },
570
570
  });
571
571
 
572
572
  // 自定义 Logger — 过滤 SDK 内部 WS 重连噪音
573
573
  const quietLogger = {
574
- info: (...args: any[]) => { /* 静默 info */ },
575
- warn: (...args: any[]) => { /* 静默 warn */ },
574
+ info: (...args: any[]) => { /* silent info */ },
575
+ warn: (...args: any[]) => { /* silent warn */ },
576
576
  error: (...args: any[]) => {
577
577
  const msg = args.join(' ');
578
578
  // 过滤已知的 SDK 内部 WS 重连噪音(不影响功能,SDK 自带自动重连)
579
579
  if (msg.includes('[ws]') && (msg.includes('ECONNREFUSED') || msg.includes('connect failed') || msg.includes('system busy') || msg.includes('repeat connection'))) return;
580
580
  console.error(`[Feishu-SDK] ${msg}`);
581
581
  },
582
- debug: (...args: any[]) => { /* 静默 debug */ },
582
+ debug: (...args: any[]) => { /* silent debug */ },
583
583
  };
584
584
 
585
585
  this.wsClient = new Lark.WSClient({
@@ -592,19 +592,19 @@ ${b.content || ''}`;
592
592
  this.wsClient.start({ eventDispatcher: dispatcher })
593
593
  .then(() => {
594
594
  this.reconnectAttempts = 0;
595
- console.log(`[Feishu] WS 已连接`);
595
+ console.log(`[Feishu] WS connected`);
596
596
  })
597
597
  .catch((e: any) => {
598
- console.error(`[Feishu] WS 连接失败: ${e.message}`);
598
+ console.error(`[Feishu] WS connection failed: ${e.message}`);
599
599
  this._scheduleReconnect();
600
600
  });
601
601
 
602
602
  this.wsClient.on?.('close', () => {
603
- console.log('[Feishu] WS 断开');
603
+ console.log('[Feishu] WS disconnected');
604
604
  this._scheduleReconnect();
605
605
  });
606
606
  this.wsClient.on?.('error', (e: any) => {
607
- console.error(`[Feishu] WS 错误: ${e.message || e}`);
607
+ console.error(`[Feishu] WS error: ${e.message || e}`);
608
608
  });
609
609
  }
610
610
 
@@ -614,7 +614,7 @@ ${b.content || ''}`;
614
614
 
615
615
  const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
616
616
  this.reconnectAttempts++;
617
- console.log(`[Feishu] ${delay/1000}s 后重连 (第${this.reconnectAttempts})`);
617
+ console.log(`[Feishu] Reconnecting in ${delay/1000}s (attempt ${this.reconnectAttempts})`);
618
618
 
619
619
  this.reconnectTimer = setTimeout(() => {
620
620
  this.reconnectTimer = null;
@@ -662,13 +662,13 @@ ${b.content || ''}`;
662
662
  try {
663
663
  const resp = await fetch(url, { signal: AbortSignal.timeout(30000) });
664
664
  if (!resp.ok) {
665
- console.error(`[Feishu] 下载失败: HTTP ${resp.status}, url=${url.slice(0, 80)}`);
665
+ console.error(`[Feishu] Download failed: HTTP ${resp.status}, url=${url.slice(0, 80)}`);
666
666
  return null;
667
667
  }
668
668
  const buf = await resp.arrayBuffer();
669
669
  return Buffer.from(buf);
670
670
  } catch (e) {
671
- console.error(`[Feishu] 下载异常: ${(e as Error).message}, url=${url.slice(0, 80)}`);
671
+ console.error(`[Feishu] Download error: ${(e as Error).message}, url=${url.slice(0, 80)}`);
672
672
  return null;
673
673
  }
674
674
  }
@@ -691,8 +691,8 @@ ${b.content || ''}`;
691
691
  case 'text': return elem.text || '';
692
692
  case 'a': return `[${elem.text}](${elem.href})`;
693
693
  case 'at': return `@${elem.user_name || elem.user_id || 'unknown'}`;
694
- case 'img': return `[图片]`;
695
- case 'emotion': return `[表情]`;
694
+ case 'img': return `[Image]`;
695
+ case 'emotion': return `[Sticker]`;
696
696
  default: return `[${elem.tag}]`;
697
697
  }
698
698
  }).join('');
@@ -120,7 +120,7 @@ export class TelegramAdapter implements IMModule {
120
120
  this.apiUrl = `https://api.telegram.org/bot${this.token}`;
121
121
 
122
122
  if (cfg.proxy) {
123
- console.log(`[Telegram] 已配置代理: ${cfg.proxy}(局部使用,不影响其他模块)`);
123
+ console.log(`[Telegram] Proxy configured: ${cfg.proxy} (local only, does not affect other modules)`);
124
124
  }
125
125
  }
126
126
 
@@ -164,7 +164,7 @@ export class TelegramAdapter implements IMModule {
164
164
  // 降级:设置环境变量(影响全局,但总比没有好)
165
165
  if (!process.env.HTTPS_PROXY && !process.env.https_proxy) {
166
166
  process.env.HTTPS_PROXY = this.proxy;
167
- console.log(`[Telegram] 已设置 HTTPS_PROXY=${this.proxy}`);
167
+ console.log(`[Telegram] Set HTTPS_PROXY=${this.proxy}`);
168
168
  }
169
169
  }
170
170
  }
@@ -178,12 +178,12 @@ export class TelegramAdapter implements IMModule {
178
178
  getCapabilities(): IMCapabilities {
179
179
  return {
180
180
  text: true,
181
- codeBlock: true, // MarkdownV2 支持 ``` 代码块
182
- cardMessage: false, // Telegram 无原生卡片,sendBlocks 降级为文本
181
+ codeBlock: true, // MarkdownV2 supports ``` code blocks
182
+ cardMessage: false, // Telegram has no native cards, sendBlocks falls back to text
183
183
  fileSend: true,
184
184
  imageSend: true,
185
185
  audioSend: true,
186
- buttonAction: true, // 内联键盘
186
+ buttonAction: true, // Inline keyboard
187
187
  maxTextLength: 4096,
188
188
  };
189
189
  }
@@ -196,13 +196,13 @@ export class TelegramAdapter implements IMModule {
196
196
  this.handler = handler;
197
197
  this.running = true;
198
198
  this._poll();
199
- console.log('[Telegram] 长轮询已启动');
199
+ console.log('[Telegram] Long polling started');
200
200
  }
201
201
 
202
202
  stop(): void {
203
203
  this.running = false;
204
204
  if (this.pollTimer) { clearTimeout(this.pollTimer); this.pollTimer = null; }
205
- console.log('[Telegram] 已停止');
205
+ console.log('[Telegram] Stopped');
206
206
  }
207
207
 
208
208
  // ================================================================
@@ -234,7 +234,7 @@ export class TelegramAdapter implements IMModule {
234
234
 
235
235
  if (this.handler) {
236
236
  this.handler(chatId, text, userId, attachments.length > 0 ? attachments : undefined).catch(e =>
237
- console.error('[Telegram] 消息处理异常:', e.message)
237
+ console.error('[Telegram] Message processing error:', e.message)
238
238
  );
239
239
  }
240
240
  }
@@ -242,7 +242,7 @@ export class TelegramAdapter implements IMModule {
242
242
  }
243
243
  } catch (e: any) {
244
244
  if (!this.warnedPollError) {
245
- console.error('[Telegram] 长轮询错误:', e.message);
245
+ console.error('[Telegram] Long poll error:', e.message);
246
246
  this.warnedPollError = true;
247
247
  }
248
248
  }
@@ -342,7 +342,7 @@ export class TelegramAdapter implements IMModule {
342
342
  if (voiceAtt) voiceAtt.durationMs = msg.voice.duration * 1000;
343
343
  }
344
344
  } catch (e: any) {
345
- console.error('[Telegram] 媒体解析失败:', e.message);
345
+ console.error('[Telegram] Media resolution error:', e.message);
346
346
  }
347
347
  }
348
348
 
@@ -354,11 +354,11 @@ export class TelegramAdapter implements IMModule {
354
354
  // 纯媒体消息(无文本无caption),生成占位文本
355
355
  if (!text && attachments.length > 0) {
356
356
  const types = attachments.map(a => {
357
- if (a.type === 'image') return '图片';
358
- if (a.type === 'audio') return '语音';
359
- return '文件';
357
+ if (a.type === 'image') return 'image';
358
+ if (a.type === 'audio') return 'voice';
359
+ return 'file';
360
360
  });
361
- text = `[用户发送了${types.join('')}]`;
361
+ text = `[User sent ${types.join(', ')}]`;
362
362
  }
363
363
 
364
364
  return { text: text.trim(), attachments };
@@ -371,7 +371,7 @@ export class TelegramAdapter implements IMModule {
371
371
  /** 成功后重置所有状态 */
372
372
  private _onSuccess(): void {
373
373
  if (this.circuitOpen) {
374
- console.log('[Telegram] 网络恢复,长轮询恢复正常');
374
+ console.log('[Telegram] Network recovered, long polling restored');
375
375
  }
376
376
  this.circuitOpen = false;
377
377
  this.consecutiveFailures = 0;
@@ -388,7 +388,7 @@ export class TelegramAdapter implements IMModule {
388
388
  this.circuitOpen = true;
389
389
  this.backoffMs = this.recoveryInterval;
390
390
  if (!this.warnedCircuitOpen) {
391
- console.error('[Telegram] ⚠️ 连续失败,进入熔断(每 30s 试探恢复)');
391
+ console.error('[Telegram] ⚠️ Consecutive failures, circuit breaker activated (probing recovery every 30s)');
392
392
  this.warnedCircuitOpen = true;
393
393
  }
394
394
  } else if (!this.circuitOpen) {
@@ -403,7 +403,7 @@ export class TelegramAdapter implements IMModule {
403
403
  // ================================================================
404
404
 
405
405
  async reply(chatId: string, text: string, maxLen = 4096): Promise<void> {
406
- const safe = text.length > maxLen ? text.slice(0, maxLen) + '\n\n…(截断)' : text;
406
+ const safe = text.length > maxLen ? text.slice(0, maxLen) + '\n\n…(truncated)' : text;
407
407
  await this._api('sendMessage', {
408
408
  chat_id: chatId,
409
409
  text: safe,
@@ -449,7 +449,7 @@ export class TelegramAdapter implements IMModule {
449
449
  try {
450
450
  await this.sendImageByUrl(chatId, block.url, block.alt);
451
451
  } catch (e: any) {
452
- lines.push(`⚠️ 图片加载失败`);
452
+ lines.push(`⚠️ Image load failed`);
453
453
  }
454
454
  }
455
455
  break;
@@ -459,7 +459,7 @@ export class TelegramAdapter implements IMModule {
459
459
  try {
460
460
  await this.sendFileByUrl(chatId, block.url, block.filename);
461
461
  } catch (e: any) {
462
- lines.push(`⚠️ 文件发送失败: ${block.filename}`);
462
+ lines.push(`⚠️ File send failed: ${block.filename}`);
463
463
  }
464
464
  }
465
465
  break;
@@ -469,7 +469,7 @@ export class TelegramAdapter implements IMModule {
469
469
  if (block.content) lines.push(block.content);
470
470
  if (block.buttons?.length) {
471
471
  await this._sendInlineButtons(chatId, lines.join('\n'), block.buttons);
472
- return; // 按钮消息已发送,不继续拼接
472
+ return; // Button message already sent, don't continue concatenating
473
473
  }
474
474
  break;
475
475
 
@@ -490,7 +490,7 @@ export class TelegramAdapter implements IMModule {
490
490
  try {
491
491
  await this._sendAudio(chatId, block.url, block.filename);
492
492
  } catch (e: any) {
493
- lines.push(`⚠️ 音频发送失败`);
493
+ lines.push(`⚠️ Audio send failed`);
494
494
  }
495
495
  }
496
496
  break;
@@ -516,7 +516,7 @@ export class TelegramAdapter implements IMModule {
516
516
  photo: imageKey,
517
517
  caption: alt || '',
518
518
  }).catch(async () => {
519
- console.error(`[Telegram] 图片发送失败`);
519
+ console.error(`[Telegram] Image send failed`);
520
520
  });
521
521
  }
522
522
 
@@ -550,7 +550,7 @@ export class TelegramAdapter implements IMModule {
550
550
  document: fileKey,
551
551
  caption: fileName,
552
552
  }).catch(() => {
553
- console.error(`[Telegram] 文件发送失败: ${fileName}`);
553
+ console.error(`[Telegram] File send failed: ${fileName}`);
554
554
  });
555
555
  }
556
556