feat(connectors): slash commands and no more silent replies on a bound chat - #650
Open
djpentz wants to merge 2 commits into
Open
feat(connectors): slash commands and no more silent replies on a bound chat#650djpentz wants to merge 2 commits into
djpentz wants to merge 2 commits into
Conversation
An inbound chat message had three fates: it matched a prompt's `[ow:id]` tag, it went to a designated DM session, or it was filed as unrouted and the sender told nothing. The third is the common one, and it is what a person meets when they reply "approve" without the tag, ask for "help", or want to know what is waiting. Silence there is indistinguishable from a dead bot — the owner of the deployment this came from typed "approve", then "Help", then "Say something!", and got nothing back three times. Slash commands, answered in the chat they came from, before any routing — "what is waiting on me?" is a question about the bot, not something to hand an agent: /pending what is waiting, whose it is, and the exact lines to answer it with /status workers, routines and their last outcome, plus the queue /runs how recent scheduled runs went /help the list A leading slash is optional and an @botName suffix is stripped, because that is what people type. Anything unmatched now gets a reply naming what IS waiting and how to answer, instead of vanishing. Answers acknowledge themselves too. Resolving a prompt from a chat said nothing back, so "always" — whose whole point is to stop the asking — gave no sign it had worked. It now reports what happened, including the case where a standing grant is refused for a tool that cannot hold one, which the API otherwise reports as success. The commands read through a narrow injected view (ChatContext), so they are testable without a server and cannot reach anything they were not handed.
The adapter registered `MessageHandler(filters.TEXT & ~filters.COMMAND, ...)`, so every message beginning with "/" was dropped before anything saw it — and no CommandHandler was registered either. A bot with no command surface at all, and no way to add one without this line changing. The failure mode is what makes it worth a test: plain text arrives normally, so the bot looks alive, while "/help" and "/status" vanish with no reply and no record. Indistinguishable, from the phone, from a dead bot. The distinction is Telegram's `bot_command` entity rather than the leading slash, which is why the regression test builds a Message carrying that entity and asserts both directions: the filter we now use accepts it, the one we replaced does not.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
An inbound message on a bound chat has three possible fates today:
[ow:<id>]tag and resolves an Inbox item,unrouted.jsonand the sender is told nothing.Three is the common case, and it is what a person meets when they reply
approvewithout the tag, type
help, or want to know what is waiting. From the phone, silenceis indistinguishable from a dead bot.
Verbatim from the
unrouted.jsonof the deployment this comes from — one owner, onemorning, no reply to any of it:
{"text": "approve", "reason": "no DM session designated"} {"text": "Help", "reason": "no DM session designated"} {"text": "Say something!", "reason": "no DM session designated"}Resolutions are silent too. Answering a mirrored prompt from a chat sends nothing back,
so
always— whose entire purpose is to stop a routine asking again — gives no sign itworked, and the person is left checking the app to find out.
The change
Slash commands, answered in the chat they came from, dispatched before routing —
"what is waiting on me?" is a question about the bot, not something to hand an agent.
A leading slash is optional, an
@botnamesuffix is stripped, and each has synonyms(
waiting,queue,history) — that is what people type.Nothing is met with silence. An unmatched message replies with what is waiting
and the exact line to send.
/pendingprints the three answers ready to copy, sonobody has to assemble
always [ow:5f00907543c0…]by hand.Answers acknowledge themselves, including the awkward truth: when
always_taskis refused for a tool that cannot carry a standing grant, the reply says "approved
once, it will ask again" rather than letting the API's
{"ok": true}imply otherwise.Design
coworker/connectors/commands.pyis pure: it reads through aChatContextof fourcallables handed in by the manager. No server needed to test it, and it cannot reach
anything it was not given.
Tests
tests/test_chat_commands.py— ten cases covering each command, the tolerant parsing,the empty queue, questions versus approvals, and both unmatched-text paths.
Full suite: 1962 passed, 1 skipped. (Three deselected locally — they bind the fixed
OAuth callback port, which a container on this machine holds; unrelated.)
🤖 Generated with Claude Code
https://claude.ai/code/session_01PgGyV9GDu4oLhonom37BU5