A Telegram bot that orchestrates multiple OpenCode instances through forum topics. Each forum topic in a Telegram supergroup gets its own dedicated OpenCode instance, enabling multi-user/multi-project AI assistance.
Developer? Architecture, configuration reference, API details, and contributing internals live in DEV.md.
- Forum Topic to OpenCode Instance: Each topic gets a dedicated OpenCode session
- Real-time Streaming: Responses stream into Telegram as editable messages
- Session Discovery: Connect to any running OpenCode instance on your machine
- Instance Lifecycle Management: Auto-start, health checks, crash recovery, idle timeout
- Persistent State: Topics and instances survive bot restarts
- Permission Handling: Approve/deny dangerous operations via inline buttons
- Telegram-Aware Answers: Replies stay short, use Telegram-safe formatting, and lead with the conclusion
- Connection Resilience: Dropped event streams reconnect automatically; if live updates can't be restored, the bot says so in chat instead of going silent
- Quick Start
- Install as a User Service
- Running with Docker
- Usage
- Troubleshooting
- Contributing
- License
- Open Telegram and message @BotFather
- Send
/newbotand follow the prompts - Copy the bot token (looks like
123456789:ABCdefGHIjklMNOpqrsTUVwxyz)
- Create a new Telegram group
- Convert it to a supergroup (Settings → Group Type → Supergroup)
- Enable Topics (Settings → Topics → Enable)
- Add your bot as an admin with permissions to manage topics
The chat ID for supergroups starts with -100. You can find it by:
- Adding @RawDataBot to your group temporarily
- It will show the chat ID in its message
- Remove the bot after getting the ID
# Clone the repository
git clone https://github.com/huynle/opencode-telegram.git
cd opencode-telegram
# Guided setup: walks through each setting, validates input, writes .env
bash scripts/setup-env.shRe-running the setup script is safe: it backs up the existing .env first
and keeps current values as defaults.
Alternatively, configure manually:
cp .env.example .env
# Edit .env with your bot token and chat ID# Development with hot reload
bun run dev
# Production
bun run startFor a bot that survives logouts and restarts, install it as a per-user
systemd service. The installer is user-space-only: no root, no sudo, no
system-wide unit, nothing under /opt. It copies the repo to
~/.local/share/opencode-telegram by default and runs the service as your
own account via systemctl --user.
# Preview what the installer will do (changes nothing)
bash scripts/install.sh --dry-run
# Install
bash scripts/install.shRun bash scripts/install.sh --help for all flags (--prefix,
--env-from, --non-interactive, --uninstall/--yes, --dry-run,
--allow-root). The old --system and --user NAME flags were removed
and now abort with an error — just re-run without them.
If the installer reports that lingering is off, enable it so the service
starts on boot without a login (the installer offers this step, or prints
the command under --non-interactive/--dry-run):
sudo loginctl enable-linger $USERCheck status, logs, and health:
systemctl --user is-active opencode-telegram.service
journalctl --user -u opencode-telegram.service -f
curl -s http://localhost:4200/api/healthUninstall:
bash scripts/install.sh --uninstall # removes the unit, keeps installed files (incl. data/)
bash scripts/install.sh --uninstall --yes # also deletes the install prefix after typed DELETE confirmationOld system-mode installs leave root-owned leftovers the new installer will
not touch. Keep exactly ONE service per bot token — two bot copies polling
Telegram cause 409 Conflict errors and flapping. Migrate with a fresh
user-space install above, then remove the old pieces:
sudo systemctl disable --now opencode-telegram.service
sudo rm -f /etc/systemd/system/opencode-telegram.service && sudo systemctl daemon-reload
sudo rm -rf /opt/opencode-telegram # deletes its data/ (chat/session DBs) — back up first if history matters--uninstall prints these exact remediation lines automatically when it
detects the leftovers.
Recommendation: running natively with Bun gives you everything, including session discovery. Docker is fine if you only need instances created via
/new, or if you register external instances via the API.
Short version:
docker build -t opencode-telegram .
docker run -d --name opencode-telegram \
--network=host \
-v $(pwd)/data:/app/data \
-v ~/oc-bot:/root/oc-bot \
--env-file .env \
opencode-telegramThings to know:
- Discovery doesn't work in plain Docker. The bot finds host OpenCode
sessions via
ps/lsof, which can't see host processes from inside a container. On Linux you can add--pid=hostto enable it. - On macOS/Windows, Docker Desktop runs in a VM, so discovery won't work there at all — run natively or register instances via the API instead.
- Useful commands:
docker logs -f opencode-telegram,docker stop opencode-telegram,docker compose up -d.
Full Docker details (compose file, all three run modes, volume table) are in DEV.md.
These commands work in the General topic of your supergroup:
| Command | Description |
|---|---|
/new <name> |
Create folder + topic + start OpenCode instance |
/sessions |
List all OpenCode sessions (managed + discovered) |
/connect <name> |
Connect to an existing session by name or ID |
/clear |
Clean up stale topic mappings |
/status |
Show orchestrator status |
/help |
Show context-aware help |
These commands work inside individual topic threads:
| Command | Description |
|---|---|
/session |
Show current topic's OpenCode session info |
/cancel |
Stop the in-flight reply (session stays active, just send again) |
/link <path> |
Link topic to existing project directory |
/stream |
Toggle real-time streaming on/off |
/disconnect |
Disconnect session and delete topic |
/help |
Show context-aware help |
Replies carry inline buttons: Approve / Deny on permission cards,
Cancel on in-progress replies (same as /cancel), plus Retry and
Full output where relevant. Long answers collapse with a button for the
rest, and tool activity shows as a compact threaded summary.
Read-only requests (file reads, search, listing) are approved automatically —
only writes, commands, and network access ask. If you send several messages
quickly, they merge into a single reply.
Topics are never auto-deleted: use /disconnect inside a topic to remove it,
or /clear in General to drop mappings whose sessions are gone.
The bot can discover any running OpenCode instance on your machine:
/sessions # Lists all sessions including discovered ones
/connect myproject # Connect to a discovered session by name
/connect ses_abc123 # Connect by session ID prefix
Discovered sessions show with a magnifying glass icon in /sessions output.
Note: Discovery requires the bot to run natively (not in Docker) or with
--pid=hoston Linux.
Topics follow the <project>-<session title> naming convention:
- On
/new <project>: Topic is created with just<project>name initially - After first message: Once OpenCode generates a session title, the topic is automatically renamed to
<project>-<session title> - On
/connect: If the session already has a title, the topic is created with<project>-<session title>immediately
Symptom: Instance crashes with "Failed to start server on port 4100"
Solution: The bot cleans up stale ports automatically on start. For manual cleanup:
lsof -ti:4100 | xargs killSymptom: SSE events received but nothing arrives in Telegram
Solution: Check that the topic is properly linked with the /session command.
If the bot recently restarted, send your message again — in-flight prompts
from before a restart are not replayed.
Symptom: Multiple "Thinking..." or response messages
Solution: Fixed in the current version (subscriptions are cleaned up on restart, and harmless "message is not modified" edits are ignored). If you still see duplicates, restart the bot and report it.
Symptom: /sessions only shows managed instances, not host OpenCode sessions
Cause: Docker containers have isolated process namespaces.
Solutions (in order of recommendation):
- Run natively:
bun run start(recommended) - Linux with
--pid=host: Shares host process namespace - External API: Manually register instances via
/api/register(see DEV.md)
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
This project is licensed under the MIT License - see the LICENSE file for details.