Files
oh-my-pi-telegram/README.md
T
2026-10-13 21:47:35 -03:00

206 lines
12 KiB
Markdown

<div align="center">
# oh-my-pi-telegram
**A Telegram companion hub for live [Oh My Pi](https://github.com/can1357/oh-my-pi) (`omp`) sessions —
queue prompts, stream replies, send files and voice, and supervise your agent from your phone.**
![Version](https://img.shields.io/badge/version-0.22.0--evandro.1-informational)
![Node](https://img.shields.io/badge/node-%3E%3D22.19.0-informational)
![License](https://img.shields.io/badge/license-MIT-informational)
[Install](#installation) · [Features](#what-it-does) · [How it works](#how-it-works) · [Limitations](#limitations) · [Development](#development) · [CI/CD](#cicd)
<img src="screenshot.png" width="520" alt="pi-telegram running in Telegram">
</div>
> [!NOTE]
> **Project status:** fork of [`@llblab/[email protected]`](https://github.com/llblab/pi-telegram/tree/v0.22.0)
> (SHA `afe09c5`), repackaged as `@evandrodevbr/oh-my-pi-telegram` and declared as an
> `omp` extension. Per [`CHANGELOG.md`](CHANGELOG.md), the fork passes the upstream
> gates (typecheck, 1229/1230 tests with one platform-only skip, `npm audit` with 0
> vulnerabilities, clean `npm pack --dry-run`). The latest commit adds a design spec
> for an OMP 17 status-bar segment ([`docs/superpowers/specs/`](docs/superpowers/specs/2026-07-15-telegram-omp-status-design.md));
> that spec needs a new OMP core API and is **not implemented in this repository yet**.
> [!IMPORTANT]
> **This is a runtime adapter, not a remote terminal.** Telegram prompts are normal
> model turns in the instance's *currently active* session and inherit its
> post-compaction context, so token cost is not proportional only to the new mobile
> message. Telegram can compact the current session, but cannot create, resume, fork,
> browse or switch sessions, and arbitrary slash commands are never forwarded to the TUI.
## Installation
Requires `omp` ≥ 16.5.2 (`@oh-my-pi/pi-coding-agent`) and Node.js ≥ 22.19.0.
```bash
# From git (this repository)
omp plugin install https://git.evandro.dev.br/evandro/oh-my-pi-telegram.git
# From npm (once the package is published)
omp plugin install npm:@evandrodevbr/oh-my-pi-telegram
# From a local checkout (development)
omp plugin link /path/to/oh-my-pi-telegram
```
Restart (or reload) `omp` so the extension loads, then:
1. Create a bot with [@BotFather](https://t.me/BotFather) (`/newbot`) and copy the token.
2. In `omp`, run `/telegram-setup` and paste the token.
3. Run `/telegram-connect` to activate polling for this instance.
4. In the bot DM, send `/start`.
> [!WARNING]
> **The first Telegram user to message the bot becomes the allowed owner**; everyone
> else is ignored. Pair right after `/telegram-connect`, and never put the bot token in
> chat, source, committed config or logs. Setup can prefill the token from
> `TELEGRAM_BOT_TOKEN`, `TELEGRAM_BOT_KEY`, `TELEGRAM_TOKEN` or `TELEGRAM_KEY`; the saved
> config lives in `~/.pi/agent/telegram.json`.
## Why this fork and not upstream
`llblab/[email protected]` targets the Pi extension platform (`@earendil-works/pi-*`).
`omp` consumes the same platform republished as `@oh-my-pi/pi-coding-agent`. This fork:
1. Renames the package to `@evandrodevbr/oh-my-pi-telegram` so it is unambiguous in the `omp` extension registry.
2. Keeps the upstream `peerDependencies` on `@earendil-works/pi-{ai,agent-core,coding-agent}` `>=0.80.6`, the runtime surface `omp` consumes — no code changes or shims.
3. Declares the extension through an `omp` block in `package.json` (`extensions`, display name and `compatibleWith: >=16.5.2`); the old `pi` block was replaced by it.
4. Re-runs every upstream gate (`typecheck` → `test` → `audit` → `pack:check`) on the `v0.22.0` source.
Lineage: `badlogic/pi-telegram` → `llblab/pi-telegram` (0.22.0) → **this fork**.
## What it does
| Surface | What you can do |
|---|---|
| **Prompt intake** | Send text, replies, edits, images, files, albums and voice notes into the session |
| **Queue control** | Messages sent while the agent is busy become queued turns; inspect, delete, promote, continue, abort, stop or force the next one |
| **Operator menu** | `/start` opens status, prompt templates, model, thinking level, settings, queue and extension sections |
| **Prompt templates** | Run Pi prompt templates as Telegram-safe commands such as `/fix_tests` |
| **Model and thinking** | Switch model or reasoning level through safe continuation flows; confirm `/compact` |
| **Rich replies** | Native Rich Markdown (default) or legacy Markdown-to-HTML for final answers; optional live draft previews; native `…typing` status while the agent works |
| **Files** | Inbound files land in `<agent-dir>/tmp/telegram` (50 MiB default limit); `telegram_attach` returns artifacts as real files |
| **Voice** | Inbound voice through command-template, programmatic or STT handlers; outbound via `telegram_voice` and TTS providers |
| **Buttons** | Hidden `telegram_button` comments become inline buttons whose callbacks re-enter the session |
| **Threaded Mode** | One leader plus visible follower `omp` instances, each with its own Telegram thread, through one bot |
| **Named profiles** | Run independent bots from one agent directory with isolated polling, diagnostics and bus transport |
| **Extension platform** | Companion extensions add commands, menu sections, status rows, update/callback handlers, inbound/outbound handlers and voice providers |
| **Diagnostics** | `/telegram-status` shows connection, mode, queue, transport and recent redacted events |
<details>
<summary>Commands</summary>
In the bot DM:
| Command | Purpose |
|---|---|
| `/start` | Pair when needed and open the operator menu |
| `/compact` | Confirm and run session compaction when safe |
| `/next` | Dispatch the next queued turn, aborting first if needed |
| `/continue` | Enqueue a priority continuation prompt |
| `/abort` | Abort the active run, keeping the queue |
| `/stop` | Abort the active run and clear waiting Telegram turns |
`/help`, `/status`, `/model`, `/thinking`, `/queue` and `/settings` are hidden shortcuts into the same menus.
Inside `omp`:
| Command | Purpose |
|---|---|
| `/telegram-setup [profile]` | Save or update the default (or a named-profile) bot token |
| `/telegram-connect [profile]` | Activate a profile and acquire its transport ownership |
| `/telegram-disconnect` | Stop polling and release ownership |
| `/telegram-status` | Inspect connection, mode, queue, transport and diagnostics |
Profile names use lowercase ASCII letters and digits (max 32 characters); `default`, `main` and `active` are reserved.
</details>
<details>
<summary>Environment variables</summary>
| Area | Variables |
|---|---|
| Bot token bootstrap | `TELEGRAM_BOT_TOKEN`, `TELEGRAM_BOT_KEY`, `TELEGRAM_TOKEN`, `TELEGRAM_KEY` |
| HTTP proxy | `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, plus `NODE_USE_ENV_PROXY=1` or Node `--use-env-proxy` |
| Network family | `PI_TELEGRAM_NETWORK_FAMILY` = `auto`, `ipv4`, `ipv6` or `ipv4-fallback` |
| Agent data root | `PI_CODING_AGENT_DIR` |
| Inbound file limit | `PI_TELEGRAM_INBOUND_FILE_MAX_BYTES`, `TELEGRAM_MAX_FILE_SIZE_BYTES` |
| Outbound attachment limit | `PI_TELEGRAM_OUTBOUND_ATTACHMENT_MAX_BYTES`, `TELEGRAM_MAX_ATTACHMENT_SIZE_BYTES` |
Defaults: config in `~/.pi/agent`, inbound temp files in `~/.pi/agent/tmp/telegram`, and `assistant: { rendering: "rich", draftPreviews: false }`.
</details>
## How it works
```mermaid
flowchart LR
TG["Telegram<br/>private DM · Threaded Mode"] -->|"getUpdates"| POLL
subgraph OMP["omp instance (extension)"]
POLL["polling + updates<br/>pairing · routing"] --> Q["turns + queue<br/>control · priority · default"]
Q -->|"sendUserMessage"| AG["agent session<br/>(currently active)"]
AG -->|"lifecycle events"| OUT["previews · rich replies<br/>files · voice · buttons"]
end
OUT -->|"Bot API"| TG
POLL <-->|"local bus<br/>Unix socket / named pipe"| F["follower omp instances<br/>(Threaded Mode)"]
```
The core loop is: Telegram message → Telegram turn → queue or active dispatch →
agent lifecycle → streaming preview / native active status → final Rich Markdown
reply → optional files, voice, buttons or callback actions. Exactly one instance
owns `getUpdates` per bot; in Threaded Mode followers are visible processes the
operator started and ran `/telegram-connect` in — Telegram never launches hidden
`omp` processes.
<details>
<summary>Layout and public entrypoints</summary>
| Path | Role |
|---|---|
| `index.ts` | Composition root: wires live ports, session state, transport adapters and lifecycle hooks |
| `lib/*.ts` | Flat domain modules (queue, polling, locks, bus, menus, rendering, delivery…) forming an acyclic import graph |
| `api/*.ts` | Public companion-extension entrypoints: `inbound`, `outbound`, `delivery`, `activity`, `updates`, `commands`, `sections`, `status`, `voice`, `keyboard` |
| `docs/` | Architecture and contract documentation |
| `tests/*.test.ts` | Domain regressions run with `node --test` |
State: `~/.pi/agent/telegram.json` (config), `~/.pi/agent/locks.json` (polling ownership) and profile-scoped redacted `logs*.jsonl` under `tmp/telegram` (never model context).
Documentation: [Architecture](docs/architecture.md) · [Public API](docs/public-api.md) · [Delivery](docs/delivery.md) · [Activity](docs/activity.md) · [Inbound](docs/inbound.md) · [Outbound](docs/outbound.md) · [Voice](docs/voice.md) · [Sections](docs/sections.md) · [Updates](docs/updates.md) · [Multi-Instance Bus](docs/multi-instance-bus.md) · [Locks](docs/locks.md) · [UI Style](docs/ui-style.md) · [Callback Namespaces](docs/callback-namespaces.md) · [Command Templates](docs/command-templates.md) — index in [`docs/README.md`](docs/README.md).
</details>
## Limitations
- Telegram is a companion surface around a live runtime, not a second runtime: no hidden `omp` processes, no PTY or fake terminal, no raw TTY input, no session replacement without an official extension API, and non-owner users cannot control the bridge.
- In Threaded Mode, threadless messages sent from the `All` tab are not routed as prompts once bound threads exist; use a bound thread tab.
- Package metadata declares the extension only under `omp.extensions` (the `pi` block was removed in `572967c`); install this fork through `omp`, and use upstream `@llblab/pi-telegram` for plain Pi.
- Open work is tracked in [`BACKLOG.md`](BACKLOG.md) (promoted-follower reload evidence, native Windows Threaded Mode follow-ups, evidence-backed Telegram client and Rich Markdown quirks).
## Development
```bash
npm ci
npm run typecheck # tsc --noEmit
npm test # node --experimental-strip-types --test tests/*.test.ts
npm run audit # npm audit
npm run pack:check # npm pack --dry-run
npm run validate # all of the above
```
Engineering conventions: [`AGENTS.md`](AGENTS.md). Completed delivery history: [`CHANGELOG.md`](CHANGELOG.md).
## CI/CD
- [`.github/workflows/validate.yml`](.github/workflows/validate.yml) — on every push and pull request: Node 24, `npm ci`, `npm run validate`.
- [`.github/workflows/release.yml`](.github/workflows/release.yml) — on tags `v*.*.*`: checks that the tag matches `package.json`'s version, extracts that version's section from `CHANGELOG.md` into the release notes, and creates the release with `gh release create` (a GitHub CLI call). The extraction expects a `## <version>` heading; the fork's entry currently sits under `## Unreleased` as a `###` heading, so a tag would fail that check until the section is promoted.
## Credits and notices
- MIT licensed ([`LICENSE`](LICENSE)): copyright Evandro Fonseca Junior (evandrodevbr) and llblab.
- Built on [`llblab/pi-telegram`](https://github.com/llblab/pi-telegram), itself a fork of [`badlogic/pi-telegram`](https://github.com/badlogic/pi-telegram); runs inside [Oh My Pi](https://github.com/can1357/oh-my-pi) and talks to the Telegram Bot API.
- Unofficial project, not affiliated with Telegram or the Oh My Pi maintainers.