2 Commits
Author SHA1 Message Date
JianMiauandClaude Opus 4.6 6faafabc9d 0.2.6:sendFile 暫時性錯誤自動重試、Claude 模型增加 Opus 4.8/4.6/Haiku 4.5、列表沙盒欄精簡
- telegram.js:sendFile 遇到 Bad Gateway/Gateway Timeout/429 等暫時性錯誤時自動重試最多 3 次(間隔遞增 4s/8s/12s)
- server.js:Claude 模型清單增加 Opus 4.8、Opus 4.6、Haiku 4.5
- index.html:沙盒欄第二行只顯示模型/effort,其餘資訊(會話模式、連網、可寫成員、規範)改為滑鼠停留氣泡顯示

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-08-24 14:57:14 +08:00
JianMiau cfc9671179 更新列表截圖 2026-08-24 12:15:39 +08:00
5 changed files with 35 additions and 8 deletions
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 65 KiB

+1 -1
View File
@@ -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",
+22 -2
View File
@@ -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) {
+8 -4
View File
@@ -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 ? '<br><span style="font-size:11px;color:#ed4245">' + esc(b.error) + '</span>' : '') + '</td>' +
'<td>' + statusBadge(b.status) + '</td>' +
'<td><span class="mono workdir" title="' + esc(b.workDir) + '">' + esc(folderName(b.workDir)) + '</span></td>' +
'<td class="mono">' + esc(b.sandbox || '-') +
((b.model || b.reasoningEffort || b.sessionMode !== 'per-chat' || b.networkAccess)
? '<br><span class="script-name">' + esc([b.model, b.reasoningEffort, b.sessionMode === 'shared' ? '共用會話' : b.sessionMode === 'stateless' ? '無記憶' : '', b.networkAccess ? '可連網' : '', (b.writeUsers && b.writeUsers.length) ? '限 @' + b.writeUsers.join(' @') + ' 可寫' : '', b.promptRules ? '有回覆規範' : ''].filter(Boolean).join(' / ')) + '</span>'
: '') + '</td>' +
(function() {
var line2 = [b.model, b.reasoningEffort].filter(Boolean).join(' / ');
var extra = [b.sessionMode === 'shared' ? '共用會話' : b.sessionMode === 'stateless' ? '無記憶' : '', b.networkAccess ? '可連網' : '', (b.writeUsers && b.writeUsers.length) ? '限 @' + b.writeUsers.join(' @') + ' 可寫' : '', b.promptRules ? '有回覆規範' : ''].filter(Boolean).join(' / ');
var tip = [b.sandbox, line2, extra].filter(Boolean).join('\n');
return '<td class="mono sandbox-cell" title="' + esc(tip) + '">' + esc(b.sandbox || '-') +
(line2 ? '<br><span class="script-name">' + esc(line2) + '</span>' : '') + '</td>';
})() +
'<td class="mono">' + esc(b.tokenMasked || '-') + '</td>' +
'<td>' + fmtBytes(b.memory) + '</td>' +
'<td>' + (b.restarts ?? '-') + '</td>' +
+4 -1
View File
@@ -106,9 +106,12 @@ const CODEX_FALLBACK_MODELS = [
const CLAUDE_EFFORTS = ['low', 'medium', 'high', 'max'];
const CLAUDE_MODELS = [
{ slug: 'claude-fable-5', label: 'Fable 5(最強Mythos 級', efforts: CLAUDE_EFFORTS, defaultEffort: 'high' },
{ slug: 'claude-fable-5', label: 'Fable 5(最強)', efforts: CLAUDE_EFFORTS, defaultEffort: 'high' },
{ slug: 'claude-opus-5', label: 'Opus 5', efforts: CLAUDE_EFFORTS, defaultEffort: 'high' },
{ slug: 'claude-opus-4-8', label: 'Opus 4.8', efforts: CLAUDE_EFFORTS, defaultEffort: 'high' },
{ slug: 'claude-opus-4-6', label: 'Opus 4.6', efforts: CLAUDE_EFFORTS, defaultEffort: 'high' },
{ slug: 'claude-sonnet-5', label: 'Sonnet 5(較快)', efforts: CLAUDE_EFFORTS, defaultEffort: 'high' },
{ slug: 'claude-haiku-4-5-20251001', label: 'Haiku 4.5(最快最便宜)', efforts: CLAUDE_EFFORTS, defaultEffort: 'high' },
];
// codex 全域預設(~/.codex/config.toml),前端用來當下拉選單的預選值