4.1 KiB
AGENTS.md
Instructions for AI agents working on this repository.
Project overview
opencode-dispatch is a Telegram bot that acts as a secure remote bridge to a locally-running opencode instance. It forwards messages from a single authorized Telegram chat to the opencode REST API and relays the responses back.
Single entry point: bot.py. Python only.
Architecture
Telegram (user) ──► bot.py ──► opencode REST API (opencode serve)
◄──────────────────────────────
Concurrency model:
python-telegram-botruns an asyncio event loopsend_to_opencode()is synchronous (blocking HTTP, up to 1200s timeout) and is always called vialoop.run_in_executor()to avoid blocking the event loop- A
queue.Queueserializes requests when a message arrives while one is already processing - A
threading.Lock(processing_lock) guardsis_processingandcurrent_task
opencode API endpoints used:
| Method | Path | Purpose |
|---|---|---|
GET |
/global/health |
Health check (/status command) |
GET |
/session |
List existing sessions |
POST |
/session |
Create a new session |
POST |
/session/{id}/message |
Send a message and get the response |
Environment variables
Defined in .env (copy from .env.example).
| Variable | Required | Description |
|---|---|---|
TELEGRAM_BOT_TOKEN |
Yes | Bot token from @BotFather |
TELEGRAM_ALLOWED_CHAT_ID |
Yes | Telegram chat ID to allow. Bot refuses to start if unset. |
OPENCODE_API_URL |
No | Default: http://127.0.0.1:5050 |
OPENCODE_SERVER_PASSWORD |
No | If set on the opencode server, must match |
Note:
OPENCODE_SERVER_PASSWORDis read from the env but not yet forwarded in HTTP requests. If you implement password support, add it as a header or query param toget_session()andsend_to_opencode().
Security invariants — do not break
ALLOWED_CHAT_IDis enforced.main()exits ifTELEGRAM_ALLOWED_CHAT_IDis not set.is_authorized()is called at the top of every handler — including all command handlers (/start,/help,/status,/working,/clear) and all message handlers. Unauthorized requests are silently dropped (no reply).- No internal info is leaked to Telegram. Error messages sent to the user are generic strings. Detailed errors go to
loggeronly. - No user-visible stack traces. Use
logger.exception()server-side, return a static string to the user.
Adding a new bot command
- Write an
async def my_command(update, context)function. - Call
if not is_authorized(update): returnas the first line. - Register it in
main()withapp.add_handler(CommandHandler("my_command", my_command)). - Add it to the help text in
help_command().
Adding a new message type handler (e.g. video)
Same pattern as handle_voice, handle_document, handle_photo:
- Implement the handler with an
is_authorizedguard. - Register with
app.add_handler(MessageHandler(filters.VIDEO, handle_video))inmain().
Running locally
# Install dependencies
pip install -r requirements.txt
# Configure
cp .env.example .env
# Edit .env: fill TELEGRAM_BOT_TOKEN and TELEGRAM_ALLOWED_CHAT_ID
# Start opencode in your target project folder
cd ~/your-project
opencode serve --port 5050
# Run the bot
python bot.py
Dependencies
python-telegram-bot==21.6 # async Telegram bot framework
requests==2.32.3 # synchronous HTTP client for opencode API
python-dotenv==1.0.1 # .env file loading
No linting or test runner is currently configured. The .gitignore includes .ruff_cache/ and .pytest_cache/ anticipating future setup.
What is not implemented yet
OPENCODE_SERVER_PASSWORDforwarding in HTTP requests- Voice, photo, and document handling (handlers exist but reply "not supported")
- Queued message processing: messages added to the queue while the bot is busy are stored but never drained — the queue worker (
process_queue) was removed. Queued items are currently orphaned.