Run OpenCode AI Assistant on Your Phone via Telegram and Discord Bots (with Core Code)
This guide shows how to deploy OpenCode Server as a backend and connect it to Telegram and Discord bots, enabling on‑the‑go AI‑powered code assistance with multi‑model support, detailed setup steps, code examples, and a comparison of the two platforms.
Architecture
┌─────────────┐ HTTP API ┌─────────────────┐ Local ┌──────────────┐
│ Telegram │ ◄────────────────► │ OpenCode │ ◄──────────────►│ AI Models │
│ / Discord │ (python-xxx-bot) │ Server │ (HTTP/ │ (Gemini, Kimi, etc) │
│ Bot │ │ (opencode) │ WebSocket) │
└─────────────┘ └─────────────────┘ └──────────────┘The bot forwards user messages to OpenCode Server, which calls the selected AI model and returns the result to the chat.
Supported Features
Create, switch, and delete sessions
Switch between multiple models (Gemini, Claude, Kimi, local Ollama)
Project initialization (/init)
Undo operations (/undo)
Direct conversational Q&A
Technology Stack
Backend Service: OpenCode Server – provides HTTP API for session and model management.
Telegram Bot: python-telegram-bot 20.7 – mature async bot framework.
Discord Bot: discord.py – official Python SDK.
HTTP Client: aiohttp – async requests, high performance.
AI Models: Gemini 3 Pro, Kimi K2.5, Ollama (local) – multi‑model support, switchable at runtime.
Step 1 – Prepare OpenCode Server
Install OpenCode
# macOS recommended
curl -fsSL https://opencode.ai/install | bash
# or via Homebrew
brew install anomalyco/tap/opencode
# Verify installation
opencode --versionConfigure Provider
Edit ~/.config/opencode/opencode.json and add model providers. Example configuration:
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"quotio": {
"name": "Quotio",
"models": {
"gemini-3-pro-preview": {
"name": "Gemini 3 Pro Preview",
"limit": {"context": 1048576, "output": 65536}
},
"gemini-3-flash-preview": {
"name": "Gemini 3 Flash Preview",
"limit": {"context": 1048576, "output": 65536}
}
},
"options": {"apiKey": "your-api-key", "baseURL": "http://127.0.0.1:8317/v1"}
},
"zen": {"name": "OpenCode Zen", "models": {"kimi-k2.5": {"name": "kimi-k2.5", "_launch": true}}},
"ollama": {
"name": "Ollama (local)",
"npm": "@ai-sdk/openai-compatible",
"options": {"baseURL": "http://localhost:11434/v1"},
"models": {"qwen:0.5b": {"name": "qwen:0.5b"}}
}
}
}Start the Server
# Set a password and launch
OPENCODE_SERVER_PASSWORD=zhangai opencode serve --port 4096
# Verify health endpoint
curl -u opencode:zhangai http://127.0.0.1:4096/global/healthA JSON response indicates the server is running correctly.
Step 2 – Telegram Bot Implementation
Create Bot
Search @BotFather in Telegram.
Send /newbot and follow the prompts.
Record the token (format 123456789:ABCdefGHIjklMNOpqrSTUvwxyz).
Project Structure
opencode-telegram-bot/
├── bot.py # Main program
├── opencode_client.py # API client (shared)
├── requirements.txt # Dependencies
├── .env # Configuration
└── start.sh # Startup scriptCore Code – opencode_client.py
import aiohttp
from typing import Optional, Dict, Any, List
class OpenCodeClient:
def __init__(self, base_url: str, username: str = None, password: str = None):
self.base_url = base_url.rstrip('/')
self.auth = aiohttp.BasicAuth(username, password) if username else None
self.session = None
async def __aenter__(self):
self.session = aiohttp.ClientSession()
return self
async def __aexit__(self, *args):
if self.session:
await self.session.close()
async def _request(self, method: str, endpoint: str, **kwargs):
url = f"{self.base_url}{endpoint}"
async with self.session.request(method, url, auth=self.auth,
timeout=aiohttp.ClientTimeout(total=300), **kwargs) as resp:
if resp.status >= 400:
raise Exception(f"HTTP {resp.status}: {await resp.text()}")
return await resp.json() if 'json' in resp.content_type else await resp.text()
async def create_session(self, title: str = None):
return await self._request('POST', '/session', json={'title': title} if title else {})
async def send_message(self, session_id: str, content: str, model: str = None):
body = {'parts': [{'type': 'text', 'text': content}]}
if model:
provider, model_id = model.split('/') if '/' in model else (model, model)
body['model'] = {'providerID': provider, 'modelID': model_id}
return await _request('POST', f'/session/{session_id}/message', json=body)Core Code – bot.py
from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters
user_sessions = {}
async def new_command(update, context):
"""Create a new session"""
async with get_opencode_client() as client:
session = await client.create_session()
user_sessions[update.effective_user.id] = session['id']
await update.message.reply_text(f"✅ Session created! ID: {session['id']}")
async def handle_message(update, context):
user_id = update.effective_user.id
session_id = user_sessions.get(user_id)
if not session_id:
await update.message.reply_text("Please create a session with /new first.")
return
msg = await update.message.reply_text("🤔 Thinking…")
async with get_opencode_client() as client:
result = await client.send_message(session_id, update.message.text)
response = ''.join(p.get('text', '') for p in result.get('parts', []))
await msg.edit_text(response[:4000]) # Telegram limit
def main():
app = Application.builder().token(TELEGRAM_TOKEN).build()
app.add_handler(CommandHandler("new", new_command))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
app.run_polling()Run Telegram Bot
# Install dependencies
pip install python-telegram-bot aiohttp python-dotenv
# Configure .env (example)
echo "TELEGRAM_BOT_TOKEN=yourToken" > .env
echo "OPENCODE_SERVER_URL=http://127.0.0.1:4096" >> .env
echo "OPENCODE_USERNAME=opencode" >> .env
echo "OPENCODE_PASSWORD=zhangai" >> .env
# Start the bot
python bot.pyIn Telegram, send /new to create a session and then type code questions directly.
Step 3 – Discord Bot Implementation
Create Bot
Open the Discord Developer Portal.
Click New Application and give it a name.
Go to the Bot page and click Reset Token to obtain the token.
Enable MESSAGE CONTENT INTENT (required to read messages).
Invite the bot to your server via the OAuth2 URL Generator.
Project Structure
opencode-discord-bot/
├── bot.py # Main program
├── opencode_client.py # Shared API client
├── requirements.txt # Dependencies
├── .env # Configuration
└── start.sh # Startup scriptCore Code – bot.py
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.message_content = True # Must enable!
bot = commands.Bot(command_prefix='!', intents=intents)
@bot.command(name='new')
async def new_command(ctx, *, title: str = None):
"""Create a new session"""
async with get_opencode_client() as client:
session = await client.create_session(title)
user_sessions[ctx.author.id] = session['id']
await ctx.send(f"✅ Session created!
📝 Title: {title or 'Untitled'}
🆔 ID: {session['id']}")
@bot.event
async def on_message(message):
if message.author == bot.user:
return
if not (isinstance(message.channel, discord.DMChannel) or bot.user.mentioned_in(message)):
return
session_id = user_sessions.get(message.author.id)
if not session_id:
return
async with message.channel.typing():
async with get_opencode_client() as client:
result = await client.send_message(session_id, message.content)
response = ''.join(p.get('text', '') for p in result.get('parts', []))
await message.reply(response[:1900]) # Discord limit 2000
bot.run(DISCORD_TOKEN)Run Discord Bot
# Install dependencies
pip install discord.py aiohttp python-dotenv
# Configure .env (example)
echo "DISCORD_BOT_TOKEN=yourToken" > .env
echo "OPENCODE_SERVER_URL=http://127.0.0.1:4096" >> .env
# Start the bot
python bot.pyIn Discord, send !new to create a session, then either @mention the bot or DM it to converse.
Comparison of Telegram vs. Discord
Message length limit: Telegram 4096 characters, Discord 2000 characters.
Trigger method: Telegram – send message directly; Discord – DM or @bot mention.
Command prefix: Telegram uses /; Discord uses ! (configurable).
Global availability: Both platforms are globally accessible.
Typical use case: Telegram – personal quick queries; Discord – team collaboration.
Common Pitfalls
Discord MESSAGE CONTENT INTENT
If the bot does not respond to commands or messages, the most likely cause is that the MESSAGE CONTENT INTENT is disabled. Enable it under the Bot settings in the Discord Developer Portal.
OpenCode model parameter format
Passing model: "gemini-3-pro-preview" as a string triggers an error Invalid input: expected object, received string. The correct payload is:
body['model'] = {
'providerID': 'quotio',
'modelID': 'gemini-3-pro-preview'
}Models that do not support tools
Models such as qwen:0.5b will fail with does not support tools. Switch to a model that supports function calling, e.g., the Gemini series.
Multiple Telegram bot instances
Running two instances simultaneously results in Conflict: terminated by other getUpdates request. Kill the old process before restarting, e.g., pkill -f "python.*bot.py".
Conclusion
The core idea is to use OpenCode Server as a unified backend and connect any instant‑messaging platform as a thin front‑end, providing portable AI‑assisted coding.
✅ Access the assistant anywhere via mobile chat apps.
✅ Switch freely among Gemini, Kimi, Ollama, etc.
✅ Full session management.
✅ Small codebase, easy to maintain.
Drawbacks:
❌ You must host OpenCode Server yourself.
❌ Complex code snippets may be hard to view in a chat UI.
Project repositories:
Telegram version: ~/opencode-telegram-bot Discord version:
~/opencode-discord-botOld Zhang's AI Learning
AI practitioner specializing in large-model evaluation and on-premise deployment, agents, AI programming, Vibe Coding, general AI, and broader tech trends, with daily original technical articles.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
