Bridges the MyThingsLab harness to the user over Telegram:
pushes ledger notifications, and turns a Policy ASK decision into a real
synchronous human confirmation instead of collapsing to DENY under an
unattended runner.
notify/ask are deterministic, no Engine call — pure comms/plumbing that
only relays existing Action/Ledger data, never composes prose. /idea is
the one exception (see below).
-
Notify: reads the shared
Ledger's entries since this tool's own lastkind=notifywrite (incremental window, same pattern as MyReporter/ MyChangelogger) and pushes them to Telegram, split across messages if the backlog runs past Telegram's 4096-character limit. (It used to send one message: a large enough backlog was rejected outright, the cursor held, and the same undeliverable digest was retried forever.) -
Ask:
TelegramPolicywraps any innerPolicy(typically MyGuard'sGuard). Non-ASKdecisions pass through untouched. AnASKsends a real Allow/Deny prompt over Telegram and blocks (bounded bytimeout) for a reply — resolving to the human's answer. Fail-closed is non-negotiable: on timeout, no reply, or any Telegram API error, it resolvesDENY, neverALLOW. -
Run: the fleet's one inbound channel, and the single owner of Telegram's update queue.
mytelegrambot runis a long-lived daemon that long-pollsgetUpdates, resumes from a ledger-trackedupdate_idcursor, and routes each update: text through a small command router,callback_queryinto akind=callbackledger entry that a waitingaskprocess picks up. Because it is the onlygetUpdatescaller, a concurrentaskand an inbound command can no longer steal each other's updates./idea <title>files amy-idea-labeled issue and then explores it (one Engine call, entirely delegated to MyIdea's ownfile_idea/explore) so you see the full brief right in Telegram./note <text>is its twin for freeform notes: files amy-notesissue and MyNotes' one Engine call comes back with a proposed title and tags./catalogexplains every shipped tool in plain language (MyGuide), with a Try button under each that narrates a dry run./wish <text>takes what you want in your own words and points you at the tools that serve it — MyGuide's one Engine call — or tells you the fleet can't./statusreports what the bot has done so far, read straight from the ledger./help(and/start, which Telegram auto-sends on first open) reply with a static command list. Everything except/idea,/noteand/wishis deterministic — no Engine call, no side effects. Mistype a command and you get a nudge toward/help; ordinary chat (no leading/) is still ignored. -
Talking back while it thinks: an Engine call takes tens of seconds, so the bot answers before it has finished.
/ideaand/notefile their issue first, so they send you the issue number and link straight away and the brief as a follow-up — if the Engine call then fails, you still know the capture landed. Throughout, you see a live "typing…" indicator rather than dead air. Replies past Telegram's 4096-character limit are split across messages instead of truncated: the tail of a brief is the part you waited for. -
Buttons: every
/ideareply carries Explore deeper and Close idea buttons, so the thread stays actionable without typing another command.Explore deeperis metered exactly like/idea— a button is not a way around a tester's quota — andClose ideapasses the samePolicygate every GitHub write in the fleet does. Because Telegram delivers whatevercallback_dataa client sends, the subject is authorized too: a tester may only act on an idea they filed. -
Testers: by default only the operator's chat (
TELEGRAM_CHAT_ID) is heard; every other chat is dropped silently. Pointrun --testers-dbat amythings.testersdatabase to admit registered testers. Each tester gets a hard, fail-closed quota of Engine calls (/ideareserves before it spends, and the refusal is the default), replies go back to their own chat, and their activity lands in their own ledger — never the operator's digest. Revoking access is one flag:mytelegrambot testers disable <id>.To find a prospective tester's chat id, have them message the bot once, then run
mytelegrambot testers pending— an unrecognized chat still gets no reply and no ack, but the knock is recorded locally (deduplicated, and capped so it cannot become a spam sink) and printed with a ready-to-pastetesters add. -
Halt:
/haltstops the fleet from launching any further work;/resumelets it start again. The kill switch used to be a marker file youtouchfrom a terminal — so the fleet's most safety-critical control was unreachable exactly when the unattended, billed loop was running and you were away from the machine. Operator only (a tester who could stop every worker would be a denial-of-service with a chat account),Policy-gated like every other write, and a pure CLI hand-off: pointrun --halt-cmdatfleet_dispatch.pyand the bot appends--abort/--clear-halt. It never imports the fleet and never learns where its marker file lives. The reply is the command's own output, verbatim — never a claim about what happened that the command didn't make. -
Setup:
mytelegrambot setupis a one-off admin call that registers the command menu (Telegram autocomplete + the ☰ menu button) and shows a persistent reply keyboard of tappable/commandshortcuts. Taps arrive as ordinary text, so they route through the normal parser — no callback plumbing.
Calls the Telegram Bot API over stdlib urllib.request + json only — no
python-telegram-bot SDK. TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID are read
from the environment, never logged, never written to the ledger.
mytelegrambot setup
mytelegrambot notify [--since ISO8601]
mytelegrambot ask --action-kind <kind> --payload-json <json> [--timeout 300]
mytelegrambot run [--repo owner/name] [--note-repo owner/name] [--engine claude-cli|noop] [--testers-db PATH] [--halt-cmd CMD]
mytelegrambot testers pending
mytelegrambot testers add <handle> --chat-id <id> --quota <n>
mytelegrambot testers disable <id>run is a long-lived systemd service on the Pi — see
deploy/systemd/ for the unit file and install steps.
testers add prints the tester's token exactly once; only its sha256 is stored.
Primarily consumed as a library (TelegramPolicy wrapping another Policy)
inside another tool's runtime — the CLI above is for manual/CI-script use.
python -m venv .venv && source .venv/bin/activate
pip install -e ../my-things-core -e ../my-guard -e ../my-idea -e ".[dev]"
pytestMIT — see LICENSE.