claude-telegram-bot 0.3.19 β 0.3.24
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/bot.mjs +92 -2
- package/ctb.mjs +2 -2
- package/package.json +1 -1
package/bot.mjs
CHANGED
|
@@ -149,6 +149,8 @@ const STR = {
|
|
|
149
149
|
compactOk: "ποΈ Context compacted. The conversation continues with a summary.",
|
|
150
150
|
compactFail: (m) => `β οΈ Compact failed: ${m}`,
|
|
151
151
|
compactNoSession: "No active session to compact. Just send a message to start one.",
|
|
152
|
+
testFallbackDisabled: "β οΈ Ollama fallback is not enabled. Set `\"ollamaFallback\": true` in config.json.",
|
|
153
|
+
testFallbackFail: (m) => `β οΈ Ollama test failed: ${m}`,
|
|
152
154
|
busy: "β³ A previous task is still running. Please try again when it finishes.",
|
|
153
155
|
queued: (n) => `β³ Queued (#${n}). Will run when the current task finishes.`,
|
|
154
156
|
stopOk: "π Task stopped.",
|
|
@@ -287,6 +289,8 @@ const STR = {
|
|
|
287
289
|
compactFail: (m) => `β οΈ compact μ€ν¨: ${m}`,
|
|
288
290
|
compactNoSession: "μμΆν νμ± μΈμ
μ΄ μμ΅λλ€. λ©μμ§λ₯Ό λ³΄λ΄ μΈμ
μ μμνμΈμ.",
|
|
289
291
|
contextTooLong: "β οΈ ν둬ννΈκ° λ무 κΉλλ€. `/compact` λ‘ μ»¨ν
μ€νΈλ₯Ό μμΆνκ±°λ `/new` λ‘ μ μΈμ
μ μμνμΈμ.",
|
|
292
|
+
testFallbackDisabled: "β οΈ Ollama ν΄λ°±μ΄ λΉνμ±ν μνμ
λλ€. config.jsonμ `\"ollamaFallback\": true` λ₯Ό μΆκ°νμΈμ.",
|
|
293
|
+
testFallbackFail: (m) => `β οΈ Ollama ν
μ€νΈ μ€ν¨: ${m}`,
|
|
290
294
|
},
|
|
291
295
|
};
|
|
292
296
|
const t = (l, key, ...a) => {
|
|
@@ -536,6 +540,14 @@ function parseResetTime(raw) {
|
|
|
536
540
|
return null;
|
|
537
541
|
}
|
|
538
542
|
|
|
543
|
+
function isFallbackError(raw, code) {
|
|
544
|
+
const t = (raw || "").toLowerCase();
|
|
545
|
+
return t.includes("credit") || t.includes("balance") || t.includes("billing") || t.includes("payment")
|
|
546
|
+
|| t.includes("rate_limit") || t.includes("rate limit") || t.includes("too many requests") || code === 429
|
|
547
|
+
|| t.includes("usage limit") || t.includes("monthly limit")
|
|
548
|
+
|| t.includes("overloaded") || code === 529;
|
|
549
|
+
}
|
|
550
|
+
|
|
539
551
|
function classifyClaudeError(raw, code) {
|
|
540
552
|
const t = raw.toLowerCase();
|
|
541
553
|
if (t.includes("credit") || t.includes("balance") || t.includes("billing") || t.includes("payment"))
|
|
@@ -603,15 +615,34 @@ function runClaude(prompt, sessionId, opts = {}) {
|
|
|
603
615
|
const rawErr = j.result ?? "";
|
|
604
616
|
const text = j.is_error ? classifyClaudeError(rawErr, code) : (rawErr || "(empty response)");
|
|
605
617
|
const resetAt = j.is_error ? parseResetTime(rawErr) : null;
|
|
606
|
-
|
|
618
|
+
const canFallback = j.is_error && isFallbackError(rawErr, code);
|
|
619
|
+
resolve({ ok: !j.is_error, text, sessionId: j.session_id, cost: j.total_cost_usd, resetAt, canFallback });
|
|
607
620
|
} catch {
|
|
608
621
|
const raw = (err || out || "no output").slice(0, 3500);
|
|
609
|
-
resolve({ ok: false, text: classifyClaudeError(raw, code), resetAt: parseResetTime(raw) });
|
|
622
|
+
resolve({ ok: false, text: classifyClaudeError(raw, code), resetAt: parseResetTime(raw), canFallback: isFallbackError(raw, code) });
|
|
610
623
|
}
|
|
611
624
|
});
|
|
612
625
|
});
|
|
613
626
|
}
|
|
614
627
|
|
|
628
|
+
// ββ Ollama ν΄λ°± μ€ν ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
629
|
+
async function runOllama(prompt, lang = "en") {
|
|
630
|
+
const header = lang === "ko"
|
|
631
|
+
? "π Claudeκ° μ μ μ¬κ³ μμ΄μ. μ κ° λμ λμλ릴κ²μ. (μΈμ
μ μ΄μ΄μ§μ§ μμμ)\n\n"
|
|
632
|
+
: "π Claude is resting right now. I'll help in the meantime. (Session won't continue)\n\n";
|
|
633
|
+
const model = cfg.ollamaModel || "phi3:mini";
|
|
634
|
+
const r = await fetch("http://localhost:11434/api/generate", {
|
|
635
|
+
method: "POST",
|
|
636
|
+
headers: { "Content-Type": "application/json" },
|
|
637
|
+
body: JSON.stringify({ model, prompt, stream: false }),
|
|
638
|
+
signal: AbortSignal.timeout(60_000),
|
|
639
|
+
});
|
|
640
|
+
if (!r.ok) return { ok: false, text: `Ollama HTTP ${r.status}` };
|
|
641
|
+
const j = await r.json();
|
|
642
|
+
const text = (j.response || "").trim();
|
|
643
|
+
return text ? { ok: true, text: header + text } : { ok: false, text: "no response" };
|
|
644
|
+
}
|
|
645
|
+
|
|
615
646
|
// ββ ν¬λ‘ μ€μΌμ€λ¬ βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
616
647
|
// νμ€ cron 5νλ "λΆ μ μΌ μ μμΌ"μ μμ‘΄μ± 0 μ μ§λ₯Ό μν΄ μ΅μ νμλ‘ μ§μ ꡬν.
|
|
617
648
|
// μ§μ: * Β· λͺ©λ‘(1,3,5) Β· λ²μ(1-5) Β· μ€ν
(*/15, 9-17/2). μμΌ 0Β·7 = μΌμμΌ.
|
|
@@ -846,6 +877,44 @@ async function downloadAttachment(att) {
|
|
|
846
877
|
return { dest, name };
|
|
847
878
|
}
|
|
848
879
|
|
|
880
|
+
// ββ ν
λ κ·Έλ¨ λ©μμ§ λ©νλ°μ΄ν° μΆμΆ ββββββββββββββββββββββββββββββββββββββ
|
|
881
|
+
function buildMsgMeta(msg) {
|
|
882
|
+
const parts = [];
|
|
883
|
+
|
|
884
|
+
// ν¬μλ μΆμ² (μ κ· API: forward_origin, ꡬλ²μ ν΄λ°±: forward_from / forward_from_chat)
|
|
885
|
+
const fo = msg.forward_origin;
|
|
886
|
+
if (fo) {
|
|
887
|
+
if (fo.type === "user" && fo.sender_user)
|
|
888
|
+
parts.push(`[Forwarded from: ${fo.sender_user.first_name}${fo.sender_user.username ? ` (@${fo.sender_user.username})` : ""}]`);
|
|
889
|
+
else if (fo.type === "channel" && fo.chat)
|
|
890
|
+
parts.push(`[Forwarded from channel: ${fo.chat.title}${fo.chat.username ? ` (@${fo.chat.username})` : ""}]`);
|
|
891
|
+
else if (fo.type === "chat" && fo.sender_chat)
|
|
892
|
+
parts.push(`[Forwarded from: ${fo.sender_chat.title}]`);
|
|
893
|
+
else if (fo.type === "hidden_user" && fo.sender_user_name)
|
|
894
|
+
parts.push(`[Forwarded from: ${fo.sender_user_name} (hidden)]`);
|
|
895
|
+
else
|
|
896
|
+
parts.push(`[Forwarded message]`);
|
|
897
|
+
} else if (msg.forward_from) {
|
|
898
|
+
const u = msg.forward_from;
|
|
899
|
+
parts.push(`[Forwarded from: ${u.first_name}${u.username ? ` (@${u.username})` : ""}]`);
|
|
900
|
+
} else if (msg.forward_from_chat) {
|
|
901
|
+
const c = msg.forward_from_chat;
|
|
902
|
+
parts.push(`[Forwarded from: ${c.title}${c.username ? ` (@${c.username})` : ""}]`);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// 리νλΌμ΄ 컨ν
μ€νΈ
|
|
906
|
+
const reply = msg.reply_to_message;
|
|
907
|
+
if (reply) {
|
|
908
|
+
const replyText = (reply.text || reply.caption || "").trim().slice(0, 300);
|
|
909
|
+
const replyFrom = reply.from?.first_name || "unknown";
|
|
910
|
+
parts.push(replyText
|
|
911
|
+
? `[Replying to ${replyFrom}: "${replyText}${replyText.length >= 300 ? "β¦" : ""}"]`
|
|
912
|
+
: `[Replying to ${replyFrom}'s message]`);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
return parts.join("\n");
|
|
916
|
+
}
|
|
917
|
+
|
|
849
918
|
// ββ λ©μμ§ μ²λ¦¬ βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
850
919
|
let busy = false;
|
|
851
920
|
const msgQueue = []; // { msg, receivedAt } β busy μ€ μμ λ©μμ§ λκΈ°μ΄
|
|
@@ -958,6 +1027,18 @@ async function handle(msg) {
|
|
|
958
1027
|
}
|
|
959
1028
|
return;
|
|
960
1029
|
}
|
|
1030
|
+
if (text === "/testfallback") {
|
|
1031
|
+
if (!cfg.ollamaFallback) { await send(chatId, t(l, "testFallbackDisabled")); return; }
|
|
1032
|
+
await send(chatId, "π§ͺ Ollama μ°κ²° ν
μ€νΈ μ€β¦");
|
|
1033
|
+
try {
|
|
1034
|
+
const res = await runOllama("Reply with exactly one sentence: Ollama fallback is working.", l);
|
|
1035
|
+
if (res.ok) await send(chatId, res.text);
|
|
1036
|
+
else await send(chatId, t(l, "testFallbackFail", res.text));
|
|
1037
|
+
} catch (e) {
|
|
1038
|
+
await send(chatId, t(l, "testFallbackFail", e.message));
|
|
1039
|
+
}
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
961
1042
|
if (text === "/new") {
|
|
962
1043
|
state.sessionId = undefined;
|
|
963
1044
|
saveState(state);
|
|
@@ -1069,6 +1150,8 @@ async function handle(msg) {
|
|
|
1069
1150
|
await send(chatId, t(l, "attachFail", e.message));
|
|
1070
1151
|
}
|
|
1071
1152
|
}
|
|
1153
|
+
const meta = buildMsgMeta(msg);
|
|
1154
|
+
if (meta) prompt = prompt ? `${meta}\n\n${prompt}` : meta;
|
|
1072
1155
|
prevSessionId = state.sessionId; // /stop --reset 볡μ λμ μ μ₯
|
|
1073
1156
|
const res = await runClaude(prompt, state.sessionId, { modelHint: true, trackChild: true, injectMemory: true });
|
|
1074
1157
|
if (res.sessionId) {
|
|
@@ -1080,6 +1163,13 @@ async function handle(msg) {
|
|
|
1080
1163
|
// λ μ΄νΈ 리λ°μ΄κ³ 리μ
μκ°μ μλ©΄ /reserve ννΈ μΆκ°
|
|
1081
1164
|
const hint = res.resetAt ? t(l, "reserveHint") : "";
|
|
1082
1165
|
rateLimitState = res.resetAt ? { prompt, resetAt: res.resetAt } : null;
|
|
1166
|
+
// Ollama ν΄λ°±: λ μ΄νΈλ¦¬λ°Β·ν¬λ λ§ μλ¬μ΄κ³ ollamaFallback μΌμ Έ μμΌλ©΄ Ollamaλ‘ μ¬μλ
|
|
1167
|
+
if (cfg.ollamaFallback && res.canFallback && !stopping) {
|
|
1168
|
+
try {
|
|
1169
|
+
const oRes = await runOllama(prompt, l);
|
|
1170
|
+
if (oRes.ok) { await send(chatId, oRes.text); return; }
|
|
1171
|
+
} catch {}
|
|
1172
|
+
}
|
|
1083
1173
|
const errMsg = res.text === "contextTooLong" ? t(l, "contextTooLong") : `β οΈ ${res.text}${hint}`;
|
|
1084
1174
|
if (!stopping) await send(chatId, errMsg);
|
|
1085
1175
|
} else {
|
package/ctb.mjs
CHANGED
|
@@ -151,8 +151,8 @@ async function main() {
|
|
|
151
151
|
|
|
152
152
|
async function summarizeSession(sid, lang) {
|
|
153
153
|
const langInstruction = lang && lang.startsWith("ko")
|
|
154
|
-
? "
|
|
155
|
-
: "
|
|
154
|
+
? "λ°©κΈ λ‘컬 ν°λ―Έλ μ½λ© μΈμ
μ΄ λλ¬μ΄. μ΄ λνμμ κ°μ₯ μ΅κ·Όμ λλ λ΄μ©(μ΄ λ©μμ§ μ§μ κΉμ§)μ λ°νμΌλ‘, κ·Έ μΈμ
μμ 무μμ νλμ§ νκ΅μ΄λ‘ μ§§μ ꡬ문(10λ¨μ΄ μ΄λ΄)μΌλ‘ μμ½ν΄μ€. λ§ν¬λ€μ΄ μμ΄ ν
μ€νΈλ§. μ€μν μμ
μ΄ μμμΌλ©΄ μ νν μ΄λ κ²λ§ λ΅ν΄: SKIP"
|
|
155
|
+
: "A local terminal coding session just ended. Based on the most recent exchanges in this conversation (just before this message), summarize in one short phrase (10 words max) what was accomplished. Plain text only. If nothing significant was done, reply exactly: SKIP";
|
|
156
156
|
return new Promise((resolve) => {
|
|
157
157
|
const child = spawn("claude", [
|
|
158
158
|
"--resume", sid,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-telegram-bot",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.24",
|
|
4
4
|
"description": "Drive Claude Code from Telegram β messages run headless `claude -p` in a project dir and replies come back to the chat. Zero dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|