Skip to content

feat: implement /kill and auto-clean dead topic state#81

Open
DefaultPerson wants to merge 1 commit into
six-ddc:mainfrom
DefaultPerson:feat/kill-command-and-topic-cleanup
Open

feat: implement /kill and auto-clean dead topic state#81
DefaultPerson wants to merge 1 commit into
six-ddc:mainfrom
DefaultPerson:feat/kill-command-and-topic-cleanup

Conversation

@DefaultPerson
Copy link
Copy Markdown

Summary

/kill was advertised as a BotCommand menu entry but had no handler — the
catch-all MessageHandler(filters.COMMAND, ...) forwarded the literal text
/kill into the Claude Code TUI. This PR adds the handler and, along the way,
fixes the orphan-state buildup that affects every path where a topic dies
(close, delete, /kill, dead window).

Background

When a window goes away, three state files can retain stale entries:

  • state.jsonwindow_states, window_display_names, user_window_offsets
  • session_map.json — Claude Code's SessionStart hook writes it but emits no
    "session ended" event
  • monitor_state.json

The 60s probe in status_polling only matched Topic_id_invalid. In private
chats Telegram returns ok=true from unpinAllForumTopicMessages regardless of
message_thread_id, so DM-mode topic deletions are never detected via the probe
and the worker logs Failed to send message: Message thread not found once per
second indefinitely.

Changes

  • bot.kill_command — kills the tmux window, drops the binding, purges
    window state, removes the session_map.json entry, and calls
    delete_forum_topic. Registered before the catch-all MessageHandler.
  • session.SessionManager — adds purge_window,
    remove_session_map_entry, and cleanup_dead_topic. Reused by
    kill_command, topic_closed_handler, and the status_polling cleanup
    paths.
  • status_polling — probe matches both Topic_id_invalid and
    Message thread not found; interval lowered from 60s to 5s.
  • message_sender.send_with_fallback / safe_send — on a final
    BadRequest containing either marker, call
    session_manager.cleanup_dead_topic(chat_id, thread_id). This is the only
    path that detects DM-mode topic deletion.
  • scripts/restart.sh — sweeps any .venv/bin/ccbot processes outside the
    target tmux pane. Multiple instances polling the same bot token produce
    Conflict: terminated by other getUpdates request and silently break command
    routing.
  • Style — one-line ruff format fix in bot.py for a pre-existing
    violation that broke ruff format --check on main.

Test plan

  • uv run ruff check src/ tests/ and ruff format --check src/ tests/
  • uv run pyright src/ccbot/ — no new errors (two pre-existing
    resolved_chat F821 errors at lines 1144/1522 are untouched)
  • uv run pytest -q — 240 passed (test_kill_command.py adds 5 cases);
    one pre-existing unrelated failure on test_format_tool_result_text[Write]
  • Manual: /kill in a bound topic kills the window, deletes the topic,
    purges all three state files
  • Manual: deleting a topic via Telegram UI triggers cleanup within ~1s
    via the send-error path
  • Manual: restart.sh kills a stray ccbot instance running in another
    tmux server

/kill was registered only as a BotCommand menu hint with no handler, so
it fell through to forward_command_handler and was sent into Claude Code
as a slash command. Wire up an actual kill_command that kills the tmux
window, drops the binding, purges window state, removes the session_map
entry, and deletes the Telegram topic.

Also fix accumulating orphan state when topics die outside /kill:

- session.purge_window / remove_session_map_entry / cleanup_dead_topic
- topic_closed_handler now invokes the same purge path
- status_polling probe accepts both Topic_id_invalid and Message thread
  not found (interval lowered 60s -> 5s)
- send_with_fallback / safe_send react to those markers immediately —
  needed because unpinAllForumTopicMessages returns ok=true for private
  chats with arbitrary thread_ids, so the probe alone cannot detect a
  deleted topic in DM mode

restart.sh now sweeps any \$VENV/bin/ccbot processes outside the target
pane to prevent multiple instances racing the same bot token.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant