Skip to content

Repository files navigation

my-telegram-bot

CI codecov Python License: MIT

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.

How it works

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 last kind=notify write (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: TelegramPolicy wraps any inner Policy (typically MyGuard's Guard). Non-ASK decisions pass through untouched. An ASK sends a real Allow/Deny prompt over Telegram and blocks (bounded by timeout) for a reply — resolving to the human's answer. Fail-closed is non-negotiable: on timeout, no reply, or any Telegram API error, it resolves DENY, never ALLOW.

  • Run: the fleet's one inbound channel, and the single owner of Telegram's update queue. mytelegrambot run is a long-lived daemon that long-polls getUpdates, resumes from a ledger-tracked update_id cursor, and routes each update: text through a small command router, callback_query into a kind=callback ledger entry that a waiting ask process picks up. Because it is the only getUpdates caller, a concurrent ask and an inbound command can no longer steal each other's updates.

    /idea <title> files a my-idea-labeled issue and then explores it (one Engine call, entirely delegated to MyIdea's own file_idea/explore) so you see the full brief right in Telegram. /note <text> is its twin for freeform notes: files a my-notes issue and MyNotes' one Engine call comes back with a proposed title and tags. /catalog explains 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. /status reports 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, /note and /wish is 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. /idea and /note file 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 /idea reply carries Explore deeper and Close idea buttons, so the thread stays actionable without typing another command. Explore deeper is metered exactly like /idea — a button is not a way around a tester's quota — and Close idea passes the same Policy gate every GitHub write in the fleet does. Because Telegram delivers whatever callback_data a 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. Point run --testers-db at a mythings.testers database to admit registered testers. Each tester gets a hard, fail-closed quota of Engine calls (/idea reserves 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-paste testers add.

  • Halt: /halt stops the fleet from launching any further work; /resume lets it start again. The kill switch used to be a marker file you touch from 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: point run --halt-cmd at fleet_dispatch.py and 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 setup is a one-off admin call that registers the command menu (Telegram autocomplete + the ☰ menu button) and shows a persistent reply keyboard of tappable /command shortcuts. 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.

Usage

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.

Install (development)

python -m venv .venv && source .venv/bin/activate
pip install -e ../my-things-core -e ../my-guard -e ../my-idea -e ".[dev]"
pytest

License

MIT — see LICENSE.

About

Pushes ledger notifications over Telegram and relays Policy ASK to a human.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages