diff --git a/package.json b/package.json
index a165ded..407151b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "telegram-bot",
- "version": "0.2.1",
+ "version": "0.2.6",
"description": "Telegram bot powered by your local AI CLI (OpenAI Codex or Claude Code) — zero-dependency, pm2-managed, with a web console to manage multiple bots",
"license": "MIT",
"type": "commonjs",
diff --git a/src/telegram.js b/src/telegram.js
index 65dab99..c8f4463 100644
--- a/src/telegram.js
+++ b/src/telegram.js
@@ -86,6 +86,10 @@ function createTelegram(token) {
}).catch(() => {});
}
+ const TRANSIENT_RE = /Bad Gateway|Gateway Timeout|Too Many Requests|Service Unavailable|Internal Server Error|ETIMEDOUT|ECONNRESET|fetch failed/i;
+ const SEND_FILE_RETRIES = 3;
+ const SEND_FILE_RETRY_DELAY = 4000;
+
// 上傳檔案到 Telegram:圖片走 sendPhoto,其餘(或 sendPhoto 失敗時)走 sendDocument
async function sendFile(chatId, filePath, { caption, replyTo } = {}) {
const fs = require('fs');
@@ -106,13 +110,29 @@ function createTelegram(token) {
return data.result;
};
+ // 帶重試的上傳:Telegram 暫時性故障(502/504/429 等)自動等一下重來
+ const uploadWithRetry = async (method, field) => {
+ for (let i = 1; ; i++) {
+ try {
+ return await upload(method, field);
+ } catch (err) {
+ if (i < SEND_FILE_RETRIES && TRANSIENT_RE.test(err.message)) {
+ console.log(`sendFile 重試 ${i}/${SEND_FILE_RETRIES}(${err.message})`);
+ await new Promise((r) => setTimeout(r, SEND_FILE_RETRY_DELAY * i));
+ continue;
+ }
+ throw err;
+ }
+ }
+ };
+
// sendPhoto 上限 10MB,且某些格式會被拒,失敗就退回用檔案傳
if (isImage && size <= 9.5 * 1024 * 1024) {
try {
- return await upload('sendPhoto', 'photo');
+ return await uploadWithRetry('sendPhoto', 'photo');
} catch { /* fall through */ }
}
- return upload('sendDocument', 'document');
+ return uploadWithRetry('sendDocument', 'document');
}
async function downloadFile(fileId, destPath) {
diff --git a/src/web/public/index.html b/src/web/public/index.html
index 5a6c1af..2ef065b 100644
--- a/src/web/public/index.html
+++ b/src/web/public/index.html
@@ -110,6 +110,7 @@
.notice-steps b { color: #e2e8f0; background: rgba(88,101,242,0.18); padding: 1px 7px;
border-radius: 5px; font-weight: 600; }
.workdir { cursor: help; border-bottom: 1px dotted #475569; }
+ .sandbox-cell { cursor: help; }
.tag { display: inline-block; padding: 1px 7px; border-radius: 5px; font-size: 10px; font-weight: 700;
margin-left: 6px; vertical-align: middle; letter-spacing: .3px; }
.tag-codex { background: rgba(16,163,127,0.18); color: #34d399; }
@@ -486,10 +487,13 @@ function renderBots(bots) {
(b.error ? '
' + esc(b.error) + '' : '') + '' +
'