1 Commits
Author SHA1 Message Date
JianMiauandClaude Fable 5 34938f0588 0.2.7:回覆別人傳的檔案/圖片再 tag bot,也會讀到那個附件
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 11:50:17 +08:00
3 changed files with 20 additions and 7 deletions
+1 -1
View File
@@ -143,7 +143,7 @@ telegram-bot web 前景執行控制台(除錯用)
- **私訊**:任何訊息都回應;**群組**:只回應 `@bot` 或回覆 bot 訊息的情況;訊息同時 tag 多個 bot 時只有第一個回應
- **引用**:回覆/圈選引用某則訊息再 tag bot,引用內容會一起送給 AI;只引用不打字就直接把引用當輸入
- **附件**:圖片、PDF、文字/程式碼檔都會下載給 AI 讀(Telegram Bot 下載上限 20MB;docx/xlsx/zip 等二進位不支援,bot 會回覆說明)
- **附件**:圖片、PDF、文字/程式碼檔都會下載給 AI 讀(Telegram Bot 下載上限 20MB;docx/xlsx/zip 等二進位不支援,bot 會回覆說明);「回覆別人傳的檔案/圖片再 tag bot」也會讀到那個附件(回覆另一隻 bot 傳的檔案除外,Telegram 不給)
- **bot 接力**:Telegram 不讓 bot 看到其他 bot 的訊息,所以「回覆 A bot 的回覆 + tag B bot(不打字)」時,B 會改拿同聊天室最近一則我們自家 bot 的發言當輸入(6 小時內)
- 對話記憶用到 80% 上下文時自動開新會話(回覆會標示),避免越用越慢
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "telegram-bot",
"version": "0.2.6",
"version": "0.2.7",
"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",
+18 -5
View File
@@ -305,7 +305,7 @@ function startBot(config) {
'/status — 查看會話與設定',
'/help — 顯示這則說明',
'',
'群組中要 @我 或回覆我的訊息才會觸發。可以直接傳圖片(附文字說明)。',
'群組中要 @我 或回覆我的訊息才會觸發。可以直接傳圖片/檔案(附文字說明),或回覆別人傳的檔案再 @我。',
].join('\n');
// 會話 key:獨立模式用 chat id,共用模式所有聊天室用同一把
@@ -320,7 +320,13 @@ function startBot(config) {
const chatId = message.chat.id;
const sessionKey = sessionKeyOf(chatId);
const text = message.text || message.caption || '';
const attachment = pickAttachment(message);
// 附件:自己這則沒有的話,退而看被回覆的那則(「別人傳檔案 → 回覆它並 tag 我」)
let attachment = pickAttachment(message);
let attachmentFromReply = false;
if (!attachment && message.reply_to_message) {
attachment = pickAttachment(message.reply_to_message);
attachmentFromReply = !!attachment;
}
const photo = attachment; // 有任何附件都視同有內容
if (!text && !photo) return;
if (!shouldRespond(message)) return;
@@ -388,24 +394,31 @@ function startBot(config) {
if (attachment) {
const meta = [attachment.name && `原檔名 ${attachment.name}`, attachment.mime, attachment.size && `${Math.round(attachment.size / 1024)}KB`]
.filter(Boolean).join('');
const label = attachmentFromReply ? '被回覆訊息的附件' : '附件';
if (attachment.kind === 'unsupported') {
attachmentNote = `\n\n附件】(${meta})格式不支援(docx/xlsx/zip 等二進位讀不了),回覆時告知使用者,建議貼成文字或轉 PDF`;
attachmentNote = `\n\n${label}】(${meta})格式不支援(docx/xlsx/zip 等二進位讀不了),回覆時告知使用者,建議貼成文字或轉 PDF`;
} else if (attachment.size > TELEGRAM_GETFILE_LIMIT) {
attachmentNote = `\n\n附件】(${meta})超過 Telegram Bot 下載上限 20MB 無法讀取,回覆時告知使用者`;
attachmentNote = `\n\n${label}】(${meta})超過 Telegram Bot 下載上限 20MB 無法讀取,回覆時告知使用者`;
} else {
cleanTmp();
const dest = path.join(config.tmpDir, `${Date.now()}-${attachment.uniqueId}${attachment.ext}`);
await t.downloadFile(attachment.fileId, dest);
if (attachment.kind === 'image') {
images.push(dest);
if (attachmentFromReply) attachmentNote = '\n\n【附圖是使用者回覆的那則訊息裡的圖片】';
} else {
files.push(dest);
attachmentNote = `\n\n附件檔案】${meta ? `${meta}` : ''}請先用讀檔工具讀取再處理:\n${dest}`;
attachmentNote = `\n\n${label}檔案】${meta ? `${meta}` : ''}請先用讀檔工具讀取再處理:\n${dest}`;
}
}
}
let fallback = files.length ? '(使用者只傳了檔案,請讀取後依上下文處理)' : '(使用者只傳了圖片,請描述並依上下文處理)';
if (attachmentFromReply) {
fallback = files.length
? '(使用者沒打字,只回覆了一則帶檔案的訊息並 tag 我:請讀取該檔案後依上下文處理)'
: '(使用者沒打字,只回覆了一則帶圖片的訊息並 tag 我:請描述該圖片並依上下文處理)';
}
if (quotedOnly && (quoted || !sibling)) {
fallback = '(使用者沒有輸入文字,只引用了上面的內容並 tag 我:請直接把引用內容當作這次的輸入來處理)';
} else if (quotedOnly && sibling) {