Skip to content

CLI contract

The CLI uses pydantic-settings nested CLI parsing with CliSubCommand, CliPositionalArg, cli_parse_args, cli_kebab_case, and cli_implicit_flags.

Global flags

--database-url <path|sqlite:///path>
--bot-token <token>
--api-base <url>
--output-format short|json|yaml|table
--media-storage-mode skip|file|blob
--media-base-dir <dir>
--poll-timeout-seconds <int>
--poll-limit <int>
--poll-allowed-updates message,edited_message,callback_query,...

Browse commands

tg status [--live]

# webhook inspection / mutation

tg webhook show
tg webhook set --url <https-url> [--allowed-updates message,callback_query,...]
tg webhook delete [--drop-pending-updates]

# chats / messages / updates

tg chats list [--search TEXT] [--limit N] [--type private|group|supergroup|channel]
tg chats show <chat-ref>

tg messages list [--chat-ref <chat-ref>] [--search TEXT] [--media-kind KIND] [--limit N]
tg message show <msg-ref>

tg updates list [--limit N] [--kind KIND] [--source webhook|poll|inject] [--status normalized|failed|ignored]
tg updates show <upd-ref>

# callback queries / media / actions

tg callbacks list [--message-ref <msg-ref>] [--answered|--no-answered] [--limit N]
tg callbacks show <cbq-ref>

tg media list [--message-ref <msg-ref>] [--chat-ref <chat-ref>] [--media-kind document|photo|video|...] [--download-status pending|downloaded|failed|skipped] [--limit N]
tg media show <media-ref>

tg actions list [--subject-ref <ref>] [--action-kind KIND] [--limit N]
tg actions show <act-ref>

Ingest commands

tg ingest update --file <json-file> [--bot-ref bot:main] [--source inject]

Semantics:

  • ingest update replays a raw Telegram update into the normalized SQLite store.
  • If --bot-token is available and media policy is not skip, the ingester attempts to resolve and persist file payloads.
  • Without a live API client, media rows are still normalized and marked pending.

Action commands

tg send text --chat-ref <chat-ref> --text <text>
tg send photo --chat-ref <chat-ref> --file <path> [--caption TEXT]
tg send document --chat-ref <chat-ref> --file <path> [--caption TEXT]

tg reply <msg-ref> --text <text>
tg react <msg-ref> --emoji <emoji> [--big]
tg ack <cbq-ref> [--text TEXT] [--alert]
tg delete <msg-ref>
tg pin <msg-ref> [--disable-notification]
tg unpin <msg-ref>

Semantics:

  • All outbound actions require --bot-token or TGCLI_BOT_TOKEN.
  • Every action is recorded in tg_actions, including failures.
  • reply, react, delete, pin, and unpin operate on local stable refs.

Polling commands

tg poll once [--timeout-seconds N] [--limit N] [--allowed-updates CSV] [--source NAME] [--hook-names CSV]

tg poll schedules list
tg poll schedules add --name <name> --interval-seconds <n> [--timeout-seconds N] [--limit N] [--allowed-updates CSV] [--hook-names CSV] [--source NAME] [--disabled]
tg poll schedules run-due

tg poll runs list [--schedule-name NAME] [--limit N]

Semantics:

  • poll once calls getUpdates, normalizes each returned update, optionally downloads media, records a tg_poll_runs row, and emits named hooks.
  • poll schedules add only persists scheduling metadata; it does not talk to Telegram and therefore works without a live token.
  • poll schedules run-due is the primitive scheduler loop: it finds enabled schedules with next_run_at <= now, executes them, advances offsets, and updates next_run_at.
  • Hooks are registered in code via HookRegistry. The scaffold ships with a noop hook and leaves room for daemon-local service integrations.

Output modes

--output-format short|json|yaml|table
  • short is meant for operator use and shell pipelines.
  • json / yaml preserve the structured contract.
  • table is best-effort and follows the same normalized data models.