{"id":"17e329ea-cbd4-4e94-ba4b-18d4c91541b5","slug":"opencode-telegram-gateway","kind":"skill","name":"opencode-telegram-gateway","summary":"Multi-provider OpenAI-compatible Telegram gateway with sessions, admin channel gate, and SQLite state.","owner":"TheyanzXD","repoUrl":"https://github.com/TheyanzXD/opencode-telegram-gateway","license":"MIT","licenseTier":"permissive","trust":"community","stars":9,"match":null,"fewStars":false,"format":"skill","createdAt":"2026-09-18T11:37:56.000Z","addedAt":"2026-10-21T18:07:55.428Z","logoUrl":"https://avatars.githubusercontent.com/u/236061827?u=33d03e8349c75c69a16b04d05b06ca309b967d11&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["databases","communication"],"tags":["ai","ai-agent","ai-agents","ai-coding","ai-model","ai-tools","database","opencode"],"runtime":"node","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-25T17:20:16.680Z","sample":false,"url":"https://conduits.dev/tools/opencode-telegram-gateway","language":"JavaScript","transports":["stdio"],"install":{"npm":"opencode-gateway-telegram"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# OpenCode Telegram Gateway\n\nA multi-provider OpenAI-compatible **Telegram gateway** with streaming, vision, conversation sessions, a 10k+ free-proxy pool rotated per chat, admin channel-gating, a full **agent harness** (file + bash + GitHub tools), and full SQLite-backed state. Built with Node.js — **no root required**, runs from a plain user account using only Node's built-in tooling (no native package manager steps; no system services; no `sudo`).\n\n## Features\n\n- 🛰 **Multi-provider** — any OpenAI-compatible endpoint. Five auth modes (`header` / `xheader` / `query` / `body` / `none`) cover OpenAI, OpenRouter, Groq, DeepInfra, OpenAI-style clones, local Ollama, and custom gateways.\n- 🛡 **Zod-validated** config — malformed `providers.yaml` is rejected at boot, never silently accepted.\n- 💬 **Conversation sessions** — `/sessions new|list|resume|delete|rename|export|active`. Each session scopes its own history in SQLite.\n- 🖼 **Vision** — image attachments forwarded to vision-capable models automatically; override via `VISION_PROVIDER`/`VISION_MODEL`.\n- ⚡ **Streaming** — Telegram edit-in-place as the model types.\n- 🤖 **Agent harness** — tool-calling loop with `read_file`, `write_file`, `edit_file`, `bash_exec`, plus GitHub/git/memory/web-search tools. Live-status updates and inline-keyboard approvals. Toggle with `AGENT_ENABLED`. Details in [`AGENTS.md`](AGENTS.md).\n- 📎 **Document upload** — text/code files dropped in chat are read into the agent (PDF: ponytail).\n- 🧠 **Long-term memory** — `memory_recall`/`memory_remember` tools, FTS5 BM25 ranking.\n- 🔌 **MCP client** — connects to any Model Context Protocol server (`stdio` or `http`) and unions its tools with the hardcoded set. See `mcp.json.example`.\n- 🐙 **GitHub account manager** — `/gitconfig <token>` stores a PAT per user; manage via inline keyboard (profile, repos, gists, revoke). Agent tools `github_list_repos`, `github_view_repo`, `github_create_gist` use the same token.\n- 📊 **Per-user stats** — `/stats [7d|30d|all]` aggregates messages, token usage, tool counts, memory chunks.\n- 🪝 **Webhook mode** — set `WEBHOOK_ENABLED=true` + domain to switch from polling.\n- 🌐 **Proxy pool (10k+)** — auto-fetches public proxies from ~20 sources at startup, rotates per-chat (stable hash), tracks per-proxy health, auto-refreshes every N hours. Up to 39k observed in practice (HTTP/SOCKS4/SOCKS5/HTTPS).\n- 👮 **Admin channel gate** — `/admin` and `/sessions export` only work inside the configured `TELEGRAM_HOME_CHANNEL` (toggle with `ADMIN_REQUIRE_CHANNEL=false`).\n- 📦 **Export to home channel** — `/sessions export` and `/admin export` zip users/sessions/messages/usage/proxies/config and post the zip to your home channel.\n- 💾 **Single-file SQLite** — `better-sqlite3` WAL, zero ops. Schema: `users`, `messages`, `usage`, `sessions`, `proxies`.\n- 📜 **Pino logging** — pretty in dev, JSON in prod, redact `apiKey` automatically.\n- 🧙 **CLI setup wizard** — `npm run setup` walks you through bot token, providers, defaults. No config files to handcraft.\n- 🩺 **Doctor** — `npm run doctor` validates everything and pings each provider with a real prompt.\n- 💰 **Cost & quota** — `/usage` shows per-day and per-model spend; `QUOTA_DAILY_TOKENS` caps a user's daily tokens and `/quota <userId> <n>` overrides one user without a restart.\n- ⏹ **Stop & regenerate** — every streaming reply has a ⏹ button (keeps the partial text); ↻ regenerates from the stored prompt. Editing your own message regenerates it too.\n- 🧵 **Reply chains** — reply to an old message and the turns around it come back into context. Forum topics are separate conversations.\n- 🧠 **Personality** — `/soul set <markdown>` writes a personality file that becomes the base system prompt. Per-user, versionable, see [docs/personality.md](docs/personality.md).\n- 📌 **Pin & search** — `/pin` keeps a turn in every prompt; `/search <text>` is FTS5 across your whole history and pins.\n- 🔑 **Bring-your-own-key** — `/key set <provider> <key>` bills a user's turns to their own account, with their own fallback chain (`/key chain`).\n- 🌐 **Inline mode** — `@botname <prompt>` answers from any chat. Multi-language UI: `/lang en|id|es|ru|ja`.\n- 📤 **Outbound webhooks** — `WEBHOOK_SUBSCRIBERS` fires signed events on quota hits, agent turns, and more.\n- 🔁 **Fallback chain** — `FALLBACK_CHAIN=provider:model,...` steps down on hard failure, retries once on transient. A stream that already produced text never switches models.\n- 🩺 **Webhook + health + watchdog** — `WEBHOOK_URL` switches off polling; `GET /health` returns 200/503; the watchdog flags a silent process. `SIGHUP` reloads config without dropping connections.\n- 🔒 **Secret redaction + DLQ** — credential-shaped strings are masked before any log, reply, or webhook; a turn that fails every fallback lands in the dead-letter queue for replay.\n- 🧩 **MCP client** — `MCP_SERVERS` turns every MCP server on npm into agent tools. Stdio and HTTP transports.\n- 👶 **Subagent delegation** — `delegate_task` runs an independent subtask in its own context window and returns only the summary (max depth 2, read-only by default).\n- 💾 **Long-term memory** — `remember`/`recall` durable facts, `record_lesson` a failed approach + the fix, `record_decision` the why-not log, `scratchpad_*` working state out of the context window.\n- 🔐 **RBAC** — three tiers: chat access (`TELEGRAM_ALLOWED_USERS`), tool access (`TELEGRAM_TOOL_USERS`), operators (`TELEGRAM_ADMIN_USERS`). Dangerous tools need a one-tap approval unless the user is trusted.\n- ↩️ **`/undo`** — every mutating tool snapshots first; one command restores the last change in this user's workspace. Overwritten files go back, created files are removed.\n- 📎 **Document ingestion** — drop a `.zip` or a code file into the chat and it lands in the workspace. Zip-slip entries are refused before extraction; over-8 MB is refused with a reason.\n- 🔎 **Code intelligence** — `semantic_code_search` (RAG over the workspace), `code_symbols` (real AST), `dependency_graph` (blast radius of a change), `dead_code_scan`. `sqlite-vec` when installable, pure-JS cosine when not, and the index says which.\n- 🎨 **Image generation** — `image_generate` when a provider offers it; says plainly when none does. — drop a `.zip` or a code file into the chat and it lands in the workspace. Zip-slip entries are refused before extraction; over-8 MB is refused with a reason.\n- 🔍 **Observability** — `trace_export` a full turn trace, `cost_report` the spend breakdown. Per-user workspace isolation for every tool that touches disk.\n\nSee [docs/features.md](docs/features.md) for the full reference.\n\n## Quick start (no root)\n\n```bash\ngit clone https://github.com/TheyanzXD/opencode-telegram-gateway.git\ncd opencode-telegram-gateway\nnpm ci                # ~80 packages, no sudo, no global state\ncp .env.example .env  # then put your bot token in .env\nnpm run doctor        # validate config + ping each provider\nnpm start             # launch the bot\n```\n\nYou only need two things: a **Telegram bot token** (from [@BotFather](https://t.me/BotFather)) and **one API key** for the provider you picked as `DEFAULT_PROVIDER`. `providers.yaml` ships with public providers — OpenRouter and Groq both have free tiers that need only an email to sign up.\n\n### Optional: headless browser (agent tools)\n\nThe agent's browser tools (`browser_navigate`, `browser_snapshot`, …) need\n**Camoufox** — anti-detect Firefox. On a datacenter IP, plain headless Chromium\nis hard-blocked (Google → `/sorry`, Brave → captcha); Camoufox spoofs a real\nWindows/Firefox fingerprint (`navigator.webdriver` false, matching UA/plugins/\nWebGL), which is what gets past the browser-side checks.\n\n```bash\nnpx camou install\n```\n\nThat is the whole setup. `src/bootstrap.js` resolves the install from the `camou`\nCLI's own registry at startup — no manual env, no patched library.\n\nThe browser is then driven by the model: `/agent \"find …\"` calls\n`browser_navigate`, `browser_snapshot` (`@eN` refs), `browser_click`, etc.\n\n### Optional: authenticated proxies\n\n```bash\necho 'user:pass@1.2.3.4:1081' >> premium-proxy-list.txt   # socks5, one per line\n# .env\nPROXY_PREMIUM_FILE=/absolute/path/to/premium-proxy-list.txt\n```\n\nPremium entries are seeded ahead of the public lists, so per-chat rotation prefers them. The file is gitignored — never commit credentials.\n\n`better-sqlite3` builds natively but `npm ci` handles it via prebuilt binaries for the common Node versions — no `apt`, no `sudo`, no system packages.\n\n## Skills\n\n`skills/` holds markdown knowledge the bot injects only when a turn matches it.\nEach is a directory with a `SKILL.md` frontmatter (`name`, `description` ≤ 60\nchars, `when` patterns). Four ship with the repo:\n\n- **agent-tools** — the 58-tool catalog with blast radius\n- **camoufox-antidetect** — browser blocks, known walls, session rules\n- **context-budget** — where the tokens go and how the cache stays warm\n- **telegram-markdown** — MarkdownV2 escaping rules\n\nAdd your own: `mkdir skills/<name>`, write a `SKILL.md`, restart. A skill that\nfails validation is skipped with a warning, never fatal.\n\n## CLI\n\n```\nopencode-gateway setup            Interactive wizard\nopencode-gateway start            Run the bot (default)\nopencode-gateway doctor           Validate config + ping providers\nopencode-gateway models [name]    List all providers × models\nopencode-gateway tui              Live config editor\nopencode-gateway proxy            Show pool stats\nopencode-gateway proxy refresh    Fetch up to PROXY_TARGET (default 10000)\nopencode-gateway proxy sweep      Liveness-check 200 random proxies\nopencode-gateway proxy prune      Remove long-dead entries (fails >= 20)\nopencode-gateway proxy check <host>:<port> [scheme]   Single-proxy liveness\n```\n\n## Bot commands\n\n### Anyone\n```\n/start               Current model + active session\n/help                Full command reference\n/model [p/m]         Switch model  (e.g. /model openai/gpt-4o)\n/model list <p>      Live model list from the provider itself\n/model add <p/m>     Register a new model (admin) — e.g. /model add groq/new-model 128000\n/models              List every registered model\n/agent <task>         Tool-calling agent — shell, files, browser, web search\n                       code tools: execute_python, execute_node, multi_edit,\n                       ast_edit, grep, glob\n                       browser tools: browser_navigate, browser_snapshot\n                       (@eN refs), browser_click, browser_type, browser_read,\n                       browser_search — see docs/agent.md\n                       tunneling: tunnel_open <port> — your local server gets\n                       a clickable URL\n/abort                Cancel the running /agent in this chat\n/todo                 The agent's task list\n/tools                List every tool the agent can call\n/yolo on|off          Auto-approve dangerous tools (skip the keyboard)\n/estop                Emergency stop — cancel everything now\n/temperature <0-2>   Set temperature\n/system <prompt>     Set system prompt\n/reset               Clear history of active session\n/history             Show last 10 messages\n/cwd [path]          Show or set agent working directory\n/tools               List agent tools\n/gitconfig [token]   Link GitHub account (token stored per-user)\n/stats [days]        Usage, tool counts, memory chunks\n/about               Bot info + stats\n/usage               Your token spend (24h / 7d / all time, by model)\n/soul [set|append|clear]  Personality file — becomes the base prompt\n/key set <p> <k> [url] [models]  Use your own API key\n/key chain <p:m,...>      Your own fallback chain\n/lang <code>         Bot UI language: en id es ru ja\n/pin [note]          Pin the replied message into every prompt\n/search <text>       Full-text search across your history\n/export              Download the conversation (.md + .json)\n```\n\n### Sessions (`/sessions …`)\n```\n/sessions list                   Show your saved sessions\n/sessions new <name>             Create + activate\n/sessions resume <name>          Activate (history switches scope)\n/sessions rename <old> <new>     Rename\n/sessions delete <name>          Remove session (messages kept as orphan history)\n/sessions export [name]          ZIP history → home channel\n/sessions active                 Show current\n```\n\n### Admin (`/admin …`) — channel-only\n```\n/admin panel                     Stats overview\n/admin providers                 List providers + models\n/admin users                     Recent users\n/admin ban <user_id>             Ban\n/admin unban <user_id>           Unban\n/admin broadcast <text>          Send to all non-banned users\n/admin default <provider>/<model>  Set default model\n/admin export [user_id]         ZIP everything → home channel (filtered if user_id given)\n```\n\nAdmin commands only execute inside `TELEGRAM_HOME_CHANNEL`. Outside, the bot politely refuses.\n\n## Config\n\n### `.env`\n\n```env\nTELEGRAM_BOT_TOKEN=1234:abcdef...\nTELEGRAM_ALLOWED_USERS=111,222\nTELEGRAM_ADMIN_USERS=111\nTELEGRAM_HOME_CHANNEL=-1001234567890\n\n# Ship defaults point at openrouter + a free model.\n# Any provider in providers.yaml works here.\nDEFAULT_PROVIDER=openrouter\nDEFAULT_MODEL=deepseek/deepseek-chat-v3-0324:free\nDEFAULT_TEMPERATURE=0.7\nDEFAULT_MAX_TOKENS=4096\nSYSTEM_PROMPT=You are a helpful assistant.\n\nDB_PATH=./data/gateway.db\nLOG_LEVEL=info\nLOG_FORMAT=pretty\nREQUEST_TIMEOUT_MS=120000\n\nVISION_PROVIDER=\nVISION_MODEL=\nHISTORY_LIMIT=20\nMAX_INPUT_CHARS=8000\nSTREAMING=true\n\nPROXY_ENABLED=true\nPROXY_TARGET=10000\nPROXY_PER_CHAT=true\nPROXY_REFRESH_HOURS=6\n\nADMIN_REQUIRE_CHANNEL=true\n```\n\n### `providers.yaml`\n\n```yaml\nproviders:\n  - name: openrouter\n    base_url: https://openrouter.ai/api/v1\n    auth_mode: header              # Bearer\n    key_env: OPENROUTER_API_KEY\n    models:\n      deepseek/deepseek-chat-v3-0324:free: { context: 64000 }\n      anthropic/claude-3.5-sonnet: { context: 200000, vision: true }\n\n  - name: groq\n    base_url: https://api.groq.com/openai/v1\n    auth_mode: header\n    key_env: GROQ_API_KEY\n    models:\n      llama-3.3-70b-versatile: { context: 128000 }\n\n  - name: ollama\n    base_url: http://localhost:11434/v1\n    auth_mode: none\n    models:\n      llama3.2: { context: 128000 }\n```\n\n#### Auth modes\n\n| mode        | Header / param sent                            |\n|-------------|------------------------------------------------|\n| `header`    | `Authorization: Bearer <KEY>`                  |\n| `xheader`   | `x-api-key: <KEY>`                             |\n| `query`     | `?key=<KEY>` appended to base URL              |\n| `body`      | `api_key: <KEY>` merged into JSON body         |\n| `none`      | no auth (local servers, public endpoints)      |\n\n## Architecture\n\n```\n┌──────────────────┐    ┌────────────────┐    ┌─────────────────┐\n│ Telegram (grammy)│───▶│  bot/*         │───▶│ providers/client│\n│                  │    │  middleware,   │    │ + proxy/pool    │\n│  /sessions,      │    │  commands,     │    │ (rotates per    │\n│  /admin, /model, │    │  handlers      │    │  chat hash)     │\n└──────────────────┘    └────────┬───────┘    └────────┬────────┘\n                                │                     │\n                          ┌─────▼──────┐      ┌───────▼────────┐\n                          │ db.js      │      │ LLM provider   │\n                          │ (sqlite    │      │ (OpenAI-compat)│\n                          │  users,    │      └────────────────┘\n                          │  messages, │\n                          │  sessions, │      ┌─────────────────┐\n                          │  proxies,  │◀─────│ proxy/fetcher   │\n                          │  usage)    │      │ 20+ public      │\n                          └────────────┘      │ list sources    │\n                                             └─────────────────┘\n```\n\n**Per-chat rotation:** `hash(chatId) % pool_size` → same proxy per chat. Failed proxies are demoted (`fails++`) and dropped from the rotation at `fails >= 5`. Periodic `sweep` checks 200 random entries against `httpbin.org/ip`.\n\n**Sessions:** Each `messages` row stores a nullable `session_id`. `/sessions new` creates a session and sets it active (deactivating any previous one). `/sessions resume` flips the active flag. `/sessions delete` orphans its messages (kept in DB) and removes the session row. `/sessions export` zips messages + Markdown render of every conversation and posts the zip to the home channel.\n\n**Admin gate:** `authMiddleware` sets `ctx.session.isAdminChannel = (chat.id == TELEGRAM_HOME_CHANNEL)` (configurable via `ADMIN_REQUIRE_CHANNEL`). `/admin` and `/sessions export` both check this flag.\n\n## Security & privacy\n\n- API keys live in `.env`, never in chat. The setup wizard masks passwords at input.\n- Pino log redacts any field named `apiKey` automatically.\n- Admin commands require being physically inside the configured channel (DM refused).\n- Proxies are public, free, and used only for outbound LLM API calls. The bot does NOT route user traffic through them.\n- GitHub PATs from `/gitconfig` are stored **plain text** in the bot's per-user tenant SQLite file (`data/tenants/<user_id>.db`) for v1 convenience. Anyone with read access to the tenant directory can read every user's token. Do not expose `data/tenants/` beyond the bot's user account. (Encryption with `BOT_SECRET` is on the roadmap; see ponytails in [`AGENTS.md`](AGENTS.md).)\n- MCP servers are subprocess children (stdio) or remote (HTTP). Subprocess servers can do anything the bot's user account can do on the host. Only configure MCP servers you trust.\n\n## MCP (Model Context Protocol)\n\n`mcp.json.example` ships with two sample servers. The bot launches them, asks each for `tools/list`, and unions their tools with the hardcoded set when sending the `tools[]` payload to the model. Tool names get a `mcp__<server>__<tool>` prefix to avoid collisions.\n\n```json\n{\n  \"servers\": {\n    \"filesystem\": { \"command\": \"npx\", \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"] },\n    \"github-mcp\":  { \"url\": \"http://localhost:3001/mcp\" }\n  }\n}\n```\n\nSee [`AGENTS.md`](AGENTS.md) for the full explanation, transport details, and 100+ community MCP servers.\n\n## Webhook mode\n\nSet `WEBHOOK_ENABLED=true` plus `WEBHOOK_DOMAIN=https://your.host` and the bot switches to webhook delivery (default). Leave `WEBHOOK_DOMAIN` empty for polling. Set `WEBHOOK_SECRET` to enforce a header token.\n\n## Per-user storage\n\nEach Telegram user gets their own SQLite file at `data/tenants/<user_id>.db` containing messages, sessions, tool audit, long-term memory, and prefs. The global DB keeps user identity, proxies, and usage rollups. On first run after upgrade, legacy `users` config columns are auto-copied into per-user `prefs`.\n\n## Docs\n- [`AGENTS.md`](AGENTS.md) — operator handbook: deploy, config locations, proxy troubleshooting\n- [`SOUL.md`](SOUL.md) — what the bot is, and its boundaries\n- [`prompts/default.md`](prompts/default.md) — the default system prompt, annotated. Live version: `SYSTEM_PROMPT` in `.env`\n- [`docs/telegram-markdown.md`](docs/telegram-markdown.md) — the formatting gotchas that eat replies\n- [`docs/memory-skills-context.md`](docs/memory-skills-context.md) — how memory works here, and how to extend it\n- [`docs/browser.md`](docs/browser.md) — the browser as agent tools: Camoufox, `@eN` refs, engine fallback\n- [`docs/searching-and-execution.md`](docs/searching-and-execution.md) — finding anything, and what can run shell commands\n\n## Test\n\n```\nnode --test tests/*.test.js\n```\n10 tests cover config loading, Zod provider validation, Zod rejection of malformed input, zod → OpenAI JSON Schema conversion, sandbox path-escape rejection, bash_exec denial, read_file line capping, per-user DB isolation, memory FTS5 roundtrip, MCP stdio JSON-RPC roundtrip.\n\n## See also\n\n- [`AGENTS.md`](AGENTS.md) — full agent-harness reference: tools, rich-Telegram UX, `/gitconfig`, env vars, ponytails.\n\n## License\n\nMIT.\n\n- Code intelligence: RAG search, AST symbols, dependency graph, dead code, image generation\n- `/gitpull` — one-command self-update + auto-restart\n- `/memory` — durable memory surface\n- oh-my-pi tooling, recoded native: read_pro (files/archives/SQLite/PDF), read_summary, ast_grep structural search, stateful REPL, checkpoint/rewind, context_notes, think, security_scan, GitHub ops, TTS"}