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.

Old Zhang's AI Learning
Old Zhang's AI Learning
Old Zhang's AI Learning
Run OpenCode AI Assistant on Your Phone via Telegram and Discord Bots (with Core Code)

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 --version

Configure 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/health

A 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 script

Core 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.py

In 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 script

Core 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.py

In 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-bot
PythonAI assistantBot DevelopmentTelegramDiscordOpenCode
Old Zhang's AI Learning
Written by

Old 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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.