Cherry-picks the still-relevant upstream/main fixes, adapted to the iTerm2
backend and covered by tests:
- six-ddc#88 (7c2e15c): wrap the cosmetic pre-injection Telegram calls — send_action,
enqueue_status_update, and the interactive-UI precheck — in try/except across
text/photo/voice/forward handlers, so a transient TimedOut on a flaky link can
no longer abort the handler before send_to_window and silently drop the user's
message (the update offset has already advanced, so Telegram won't redeliver).
- six-ddc#67 (865ab89): on a hard interactive-UI edit failure, keep the old message and
delete it only AFTER the replacement send succeeds, so a failed replacement
never strands the user without controls. ("Message is not modified" was already
handled on this branch.)
- Write line count (f5ddd7f): compute from the tool_use input content, not the
result confirmation string, so it stops always showing "Wrote 1 lines".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Symptom
On a flaky network (e.g. reaching
api.telegram.orgfrom mainland China), messages sent from Telegram intermittently never reach the tmux session. The user has to resend 2–3 times before one gets through, with no error shown.Root cause
In
text_handler(andphoto_handler/voice_handler/forward_command_handler), a cosmetic outbound-Telegram call —chat.send_action(ChatAction.TYPING), the "typing…" indicator — runs before the message is injected into tmux viasend_to_window, with no error isolation. The bot also registers no global error handler (No error handlers are registeredin logs).Sequence in
text_handler(pre-fix):send_action(TYPING)enqueue_status_update(...)capture_pane+ interactive-UI prechecksend_to_window(wid, text)When
send_actionraisestelegram.error.TimedOut, the exception propagates out of the handler (no error handler), so execution never reaches line 970 — the message is never injected. Because the update offset has already advanced, Telegram does not redeliver, so the message is lost and the user must resend.Evidence (from a ~7-day production log)
telegram.error.TimedOutraised atbot.py:950 in text_handler, aborting before injection: 129 occurrences.telegram.error.TimedOut: Timed outinsidesend_chat_action.get_updatestimeouts (82) are harmless — they self-recover because the offset isn't advanced, so Telegram redelivers.Fix
Wrap every pre-injection outbound-Telegram call (
send_action,enqueue_status_update) and the interactive-UI precheck intry/exceptso a transient network failure can never prevent the tmux injection.send_to_windowis the one call that must always run. Applied to all four handlers that sendTYPINGbefore injecting. Ordering/semantics are unchanged (the UI precheck still runs first when it can); only the cosmetic calls' ability to abort the critical path is removed.A complementary hardening (not in this PR) would be registering a global
application.add_error_handler(...)so any future handler exception is at least logged and optionally surfaced to the user.Note: while diagnosing this,
ccbot --version/--helpwere observed to boot a second daemon (no early-exit), which then fights the running instance forgetUpdatesand raisestelegram.error.Conflict: terminated by other getUpdates request. Might be worth a real--version/--helpthat exits without starting the bot.