telegram-opencode-bridge-bot 0.1.0__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {telegram_opencode_bridge_bot-0.1.0/telegram_opencode_bridge_bot.egg-info → telegram_opencode_bridge_bot-0.1.2}/PKG-INFO +5 -15
  2. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/README.md +129 -139
  3. telegram_opencode_bridge_bot-0.1.2/bot.py +430 -0
  4. telegram_opencode_bridge_bot-0.1.2/config.py +92 -0
  5. telegram_opencode_bridge_bot-0.1.2/list_session_models.py +44 -0
  6. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/pyproject.toml +6 -3
  7. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2/telegram_opencode_bridge_bot.egg-info}/PKG-INFO +5 -15
  8. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/telegram_opencode_bridge_bot.egg-info/SOURCES.txt +3 -0
  9. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/telegram_opencode_bridge_bot.egg-info/top_level.txt +3 -0
  10. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/.env.example +0 -0
  11. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/MANIFEST.in +0 -0
  12. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/handlers/__init__.py +0 -0
  13. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/handlers/commands.py +0 -0
  14. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/handlers/messages.py +0 -0
  15. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/opencode/__init__.py +0 -0
  16. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/opencode/client.py +0 -0
  17. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/opencode/server.py +0 -0
  18. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/requirements.txt +0 -0
  19. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/sessions/__init__.py +0 -0
  20. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/sessions/manager.py +0 -0
  21. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/setup.cfg +0 -0
  22. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/telegram_opencode_bridge_bot.egg-info/dependency_links.txt +0 -0
  23. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/telegram_opencode_bridge_bot.egg-info/entry_points.txt +0 -0
  24. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/telegram_opencode_bridge_bot.egg-info/requires.txt +0 -0
  25. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/utils/__init__.py +0 -0
  26. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/utils/formatting.py +0 -0
  27. {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/utils/security.py +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: telegram-opencode-bridge-bot
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: A Telegram bot that bridges messages directly to OpenCode — an AI coding agent running on your machine
5
- Author-email: MaheshNagabhairava <your@email.com>
5
+ Author-email: MaheshNagabhairava <maheshnagabhirava12345@gmail.com>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/MaheshNagabhairava/telegram-opencode-bridge-bot
8
8
  Keywords: telegram,bot,opencode,ai,coding,bridge
@@ -52,25 +52,15 @@ cd telegram-opencode-bot
52
52
  pip install -r requirements.txt
53
53
  ```
54
54
 
55
- ### 2. Start OpenCode Server
56
-
57
- In a separate terminal:
58
-
59
- ```bash
60
- opencode serve --port 4096 --hostname 127.0.0.1
61
- ```
62
-
63
- This starts the OpenCode HTTP API on `localhost:4096`.
64
-
65
- ### 3. Run the Bot
55
+ ### 2. Run the Bot
66
56
 
67
57
  ```bash
68
58
  python bot.py
69
- python bot.py --env (if u want to setup ur configuration later)
59
+ python bot.py --env (use --env flag if u want to re-configure later anytime)
70
60
  ```
71
61
  > **💡 Tip:** Don't know your Telegram user ID? Start the bot with `AUTHORIZED_USERS=0` temporarily, send `/id` to the bot, then update the `.env` with your real ID.
72
62
 
73
- ### 4. Chat!
63
+ ### 3. Chat!
74
64
 
75
65
  Open Telegram, find your bot, and start asking! 🎉
76
66
 
@@ -1,139 +1,129 @@
1
- # 🤖 Telegram → OpenCode Bridge Bot
2
-
3
- A lightweight Python bot that bridges your Telegram messages directly to [OpenCode](https://opencode.ai) — an AI coding agent running on your machine. Think of it as having Claude/GPT-powered coding assistance right in your pocket via Telegram.
4
-
5
- ## ✨ Features
6
-
7
- - **Direct OpenCode integration** — routes your messages to OpenCode's HTTP API
8
- - **Persistent sessions** — conversations maintain context across messages
9
- - **Session management** — create, switch, list, and share sessions
10
- - **Model switching** — change AI models on the fly (`/model`)
11
- - **Plan/Build modes** — toggle between read-only analysis and full execution
12
- - **Smart formatting** — code blocks with syntax highlighting in Telegram
13
- - **Auto message splitting** — handles responses longer than Telegram's 4096 char limit
14
- - **Security** — whitelist-based access control + rate limiting
15
- - **Workspace Switching** — Switching from one workspace to another
16
-
17
- ## 📋 Prerequisites
18
-
19
- 1. **Python 3.10+**
20
- 2. **OpenCode CLI** — install via:
21
- ```bash
22
- npm install -g opencode-ai
23
- # or
24
- curl -fsSL https://opencode.ai/install | bash
25
- ```
26
- 3. **Telegram Bot Token** — get one from [@BotFather](https://t.me/BotFather)
27
- 4. **Your Telegram User ID** — send `/id` to the bot after setup, or use [@userinfobot](https://t.me/userinfobot)
28
-
29
- ## 🚀 Quick Start
30
-
31
- ### 1. Clone & Install
32
-
33
- ```bash
34
- cd telegram-opencode-bot
35
- pip install -r requirements.txt
36
- ```
37
-
38
- ### 2. Start OpenCode Server
39
-
40
- In a separate terminal:
41
-
42
- ```bash
43
- opencode serve --port 4096 --hostname 127.0.0.1
44
- ```
45
-
46
- This starts the OpenCode HTTP API on `localhost:4096`.
47
-
48
- ### 3. Run the Bot
49
-
50
- ```bash
51
- python bot.py
52
- python bot.py --env (if u want to setup ur configuration later)
53
- ```
54
- > **💡 Tip:** Don't know your Telegram user ID? Start the bot with `AUTHORIZED_USERS=0` temporarily, send `/id` to the bot, then update the `.env` with your real ID.
55
-
56
- ### 4. Chat!
57
-
58
- Open Telegram, find your bot, and start asking! 🎉
59
-
60
- ## 📱 Commands
61
-
62
- | Command | Description |
63
- |---------|-------------|
64
- | `/start` | Welcome message & connection check |
65
- | `/help` | Show all commands |
66
- | `/new` | Start a fresh conversation |
67
- | `/sessions` | List your recent sessions |
68
- | `/switch <id>` | Switch to a different session |
69
- | `/model <name>` | Change AI model(ex: /model opencode/deepseek-v4-flash-free) |
70
- | `/mode <plan\|build>` | Toggle plan/build mode |
71
- | `/share` | Share current session (public URL) |
72
- | `/status` | Check connection & session details |
73
- | `/id` | Show your Telegram user ID |
74
- | `/stop` | Abort active model processing |
75
- | `/models` | List all available models |
76
- | `/project` | To view the current workspace, sub folder workspaces and to change the workspace (ex: /project 2)|
77
- | `/project depth <1to5>` | Depth level recursive check for subfolders (ex: if u give depth 2, only 2 sub folders it will show from root) |
78
- | `/enable` | To enable the streaming |
79
- | `/disable` | To disable the streaming |
80
-
81
- ## 🏗️ Architecture
82
-
83
- ```
84
- Telegram User
85
-
86
-
87
- Telegram Bot (Python)
88
-
89
- ├──► OpenCode HTTP API (localhost:4096) ← primary
90
-
91
- ├──► LLM Provider (Claude/GPT/Gemini)
92
- └──► Local Filesystem & Shell
93
- ```
94
-
95
- ## ⚙️ Configuration
96
-
97
- | Variable | Description | Default |
98
- |----------|-------------|---------|
99
- | `TELEGRAM_BOT_TOKEN` | Bot token from @BotFather | **Required** |
100
- | `AUTHORIZED_USERS` | Comma-separated Telegram user IDs | **Required** |
101
- | `OPENCODE_SERVER_URL` | OpenCode HTTP API URL | `http://localhost:4096` |
102
- | `OPENCODE_SERVER_USERNAME` | OpenCode auth username | *(empty)* |
103
- | `OPENCODE_SERVER_PASSWORD` | OpenCode auth password | *(empty)* |
104
- | `OPENCODE_MODEL` | Default AI model | `anthropic/claude-sonnet-4` |
105
- | `OPENCODE_WORK_DIR` | Working directory for OpenCode | `.` |
106
- | `MAX_MESSAGE_LENGTH` | Max Telegram message length | `4000` |
107
- | `RESPONSE_TIMEOUT` | Max wait for response (seconds) | `0` |
108
- | `DB_PATH` | SQLite database path | `sessions.db` |
109
-
110
- ## 🔒 Security
111
-
112
- - **User whitelist** only Telegram user IDs in `AUTHORIZED_USERS` can use the bot
113
- - **Rate limiting** — 20 requests per minute per user (configurable)
114
- - **No public exposure** — designed to run on your local machine
115
-
116
- > ⚠️ **Warning:** This bot executes AI-driven code on your machine. Only authorize trusted users.
117
-
118
- ## 📁 Project Structure
119
-
120
- ```
121
- telegram-opencode-bot/
122
- ├── bot.py # Main entry point
123
- ├── config.py # Environment configuration
124
- ├── handlers/
125
- ├── commands.py # Slash command handlers
126
- │ └── messages.py # Text message → OpenCode bridge
127
- ├── opencode/
128
- │ ├── client.py # OpenCode HTTP API client
129
-
130
- ├── sessions/
131
- │ └── manager.py # Per-user session tracking (SQLite)
132
- ├── utils/
133
- │ ├── formatting.py # Telegram message formatting
134
- │ └── security.py # Auth, rate limiting, sanitization
135
- ├── .env.example # Environment template
136
- ├── requirements.txt # Python dependencies
137
- └── README.md # This file
138
- ```
139
-
1
+ # 🤖 Telegram → OpenCode Bridge Bot
2
+
3
+ A lightweight Python bot that bridges your Telegram messages directly to [OpenCode](https://opencode.ai) — an AI coding agent running on your machine. Think of it as having Claude/GPT-powered coding assistance right in your pocket via Telegram.
4
+
5
+ ## ✨ Features
6
+
7
+ - **Direct OpenCode integration** — routes your messages to OpenCode's HTTP API
8
+ - **Persistent sessions** — conversations maintain context across messages
9
+ - **Session management** — create, switch, list, and share sessions
10
+ - **Model switching** — change AI models on the fly (`/model`)
11
+ - **Plan/Build modes** — toggle between read-only analysis and full execution
12
+ - **Smart formatting** — code blocks with syntax highlighting in Telegram
13
+ - **Auto message splitting** — handles responses longer than Telegram's 4096 char limit
14
+ - **Security** — whitelist-based access control + rate limiting
15
+ - **Workspace Switching** — Switching from one workspace to another
16
+
17
+ ## 📋 Prerequisites
18
+
19
+ 1. **Python 3.10+**
20
+ 2. **OpenCode CLI** — install via:
21
+ ```bash
22
+ npm install -g opencode-ai
23
+ # or
24
+ curl -fsSL https://opencode.ai/install | bash
25
+ ```
26
+ 3. **Telegram Bot Token** — get one from [@BotFather](https://t.me/BotFather)
27
+ 4. **Your Telegram User ID** — send `/id` to the bot after setup, or use [@userinfobot](https://t.me/userinfobot)
28
+
29
+ ## 🚀 Quick Start
30
+
31
+ ### 1. Clone & Install
32
+
33
+ ```bash
34
+ cd telegram-opencode-bot
35
+ pip install -r requirements.txt
36
+ ```
37
+
38
+ ### 2. Run the Bot
39
+
40
+ ```bash
41
+ python bot.py
42
+ python bot.py --env (use --env flag if u want to re-configure later anytime)
43
+ ```
44
+ > **💡 Tip:** Don't know your Telegram user ID? Start the bot with `AUTHORIZED_USERS=0` temporarily, send `/id` to the bot, then update the `.env` with your real ID.
45
+
46
+ ### 3. Chat!
47
+
48
+ Open Telegram, find your bot, and start asking! 🎉
49
+
50
+ ## 📱 Commands
51
+
52
+ | Command | Description |
53
+ |---------|-------------|
54
+ | `/start` | Welcome message & connection check |
55
+ | `/help` | Show all commands |
56
+ | `/new` | Start a fresh conversation |
57
+ | `/sessions` | List your recent sessions |
58
+ | `/switch <id>` | Switch to a different session |
59
+ | `/model <name>` | Change AI model(ex: /model opencode/deepseek-v4-flash-free) |
60
+ | `/mode <plan\|build>` | Toggle plan/build mode |
61
+ | `/share` | Share current session (public URL) |
62
+ | `/status` | Check connection & session details |
63
+ | `/id` | Show your Telegram user ID |
64
+ | `/stop` | Abort active model processing |
65
+ | `/models` | List all available models |
66
+ | `/project` | To view the current workspace, sub folder workspaces and to change the workspace (ex: /project 2)|
67
+ | `/project depth <1to5>` | Depth level recursive check for subfolders (ex: if u give depth 2, only 2 sub folders it will show from root) |
68
+ | `/enable` | To enable the streaming |
69
+ | `/disable` | To disable the streaming |
70
+
71
+ ## 🏗️ Architecture
72
+
73
+ ```
74
+ Telegram User
75
+
76
+
77
+ Telegram Bot (Python)
78
+
79
+ ├──► OpenCode HTTP API (localhost:4096) ← primary
80
+
81
+ ├──► LLM Provider (Claude/GPT/Gemini)
82
+ └──► Local Filesystem & Shell
83
+ ```
84
+
85
+ ## ⚙️ Configuration
86
+
87
+ | Variable | Description | Default |
88
+ |----------|-------------|---------|
89
+ | `TELEGRAM_BOT_TOKEN` | Bot token from @BotFather | **Required** |
90
+ | `AUTHORIZED_USERS` | Comma-separated Telegram user IDs | **Required** |
91
+ | `OPENCODE_SERVER_URL` | OpenCode HTTP API URL | `http://localhost:4096` |
92
+ | `OPENCODE_SERVER_USERNAME` | OpenCode auth username | *(empty)* |
93
+ | `OPENCODE_SERVER_PASSWORD` | OpenCode auth password | *(empty)* |
94
+ | `OPENCODE_MODEL` | Default AI model | `anthropic/claude-sonnet-4` |
95
+ | `OPENCODE_WORK_DIR` | Working directory for OpenCode | `.` |
96
+ | `MAX_MESSAGE_LENGTH` | Max Telegram message length | `4000` |
97
+ | `RESPONSE_TIMEOUT` | Max wait for response (seconds) | `0` |
98
+ | `DB_PATH` | SQLite database path | `sessions.db` |
99
+
100
+ ## 🔒 Security
101
+
102
+ - **User whitelist** only Telegram user IDs in `AUTHORIZED_USERS` can use the bot
103
+ - **Rate limiting** 20 requests per minute per user (configurable)
104
+ - **No public exposure** designed to run on your local machine
105
+
106
+ > ⚠️ **Warning:** This bot executes AI-driven code on your machine. Only authorize trusted users.
107
+
108
+ ## 📁 Project Structure
109
+
110
+ ```
111
+ telegram-opencode-bot/
112
+ ├── bot.py # Main entry point
113
+ ├── config.py # Environment configuration
114
+ ├── handlers/
115
+ │ ├── commands.py # Slash command handlers
116
+ │ └── messages.py # Text message OpenCode bridge
117
+ ├── opencode/
118
+ │ ├── client.py # OpenCode HTTP API client
119
+
120
+ ├── sessions/
121
+ │ └── manager.py # Per-user session tracking (SQLite)
122
+ ├── utils/
123
+ ├── formatting.py # Telegram message formatting
124
+ │ └── security.py # Auth, rate limiting, sanitization
125
+ ├── .env.example # Environment template
126
+ ├── requirements.txt # Python dependencies
127
+ └── README.md # This file
128
+ ```
129
+
@@ -0,0 +1,430 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Telegram → OpenCode Bridge Bot
4
+
5
+ Main entry point. Wires together all components:
6
+ - Telegram bot (python-telegram-bot)
7
+ - OpenCode HTTP client + subprocess fallback
8
+ - Session manager (SQLite-backed)
9
+ - Security (auth whitelist + rate limiting)
10
+
11
+ Usage:
12
+ 1. Copy .env.example → .env and fill in your values
13
+ 2. Start OpenCode server: opencode serve
14
+ 3. Run the bot: python bot.py
15
+ """
16
+
17
+ import asyncio
18
+ import logging
19
+ import sys
20
+ import os
21
+
22
+ from telegram import Update
23
+ from telegram.ext import (
24
+ ApplicationBuilder,
25
+ CommandHandler,
26
+ MessageHandler,
27
+ filters,
28
+ ContextTypes,
29
+ )
30
+ from utils.formatting import format_error
31
+
32
+ from config import config
33
+ from opencode.client import OpenCodeClient
34
+ from sessions.manager import SessionManager
35
+ from utils.security import UserAuthorizer, RateLimiter, authorized
36
+ from handlers.commands import (
37
+ start_command,
38
+ help_command,
39
+ new_command,
40
+ sessions_command,
41
+ switch_command,
42
+ model_command,
43
+ mode_command,
44
+ share_command,
45
+ status_command,
46
+ id_command,
47
+ models_command,
48
+ stop_command,
49
+ project_command,
50
+ enable_command,
51
+ disable_command,
52
+ set_bot_commands,
53
+ )
54
+ from handlers.messages import handle_message
55
+
56
+ # ── Logging Setup ──────────────────────────────────────────
57
+ logging.basicConfig(
58
+ level=logging.INFO,
59
+ format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
60
+ handlers=[logging.StreamHandler(sys.stderr)],
61
+ )
62
+ logger = logging.getLogger("opencode-telegram-bot")
63
+
64
+
65
+ # ── Wrap handlers with auth decorator ──────────────────────
66
+
67
+ def build_authorized_handlers(authorizer: UserAuthorizer, rate_limiter: RateLimiter):
68
+ """Wrap all handlers with authorization and rate limiting."""
69
+
70
+ @authorized(authorizer, rate_limiter)
71
+ async def _start(update, context):
72
+ await start_command(update, context)
73
+
74
+ @authorized(authorizer, rate_limiter)
75
+ async def _help(update, context):
76
+ await help_command(update, context)
77
+
78
+ @authorized(authorizer, rate_limiter)
79
+ async def _new(update, context):
80
+ await new_command(update, context)
81
+
82
+ @authorized(authorizer, rate_limiter)
83
+ async def _sessions(update, context):
84
+ await sessions_command(update, context)
85
+
86
+ @authorized(authorizer, rate_limiter)
87
+ async def _switch(update, context):
88
+ await switch_command(update, context)
89
+
90
+ @authorized(authorizer, rate_limiter)
91
+ async def _model(update, context):
92
+ await model_command(update, context)
93
+
94
+ @authorized(authorizer, rate_limiter)
95
+ async def _mode(update, context):
96
+ await mode_command(update, context)
97
+
98
+ @authorized(authorizer, rate_limiter)
99
+ async def _share(update, context):
100
+ await share_command(update, context)
101
+
102
+ @authorized(authorizer, rate_limiter)
103
+ async def _status(update, context):
104
+ await status_command(update, context)
105
+
106
+ @authorized(authorizer)
107
+ async def _id(update, context):
108
+ await id_command(update, context)
109
+
110
+ @authorized(authorizer, rate_limiter)
111
+ async def _models(update, context):
112
+ await models_command(update, context)
113
+
114
+ @authorized(authorizer, rate_limiter)
115
+ async def _stop(update, context):
116
+ await stop_command(update, context)
117
+
118
+ @authorized(authorizer, rate_limiter)
119
+ async def _project(update, context):
120
+ await project_command(update, context)
121
+
122
+ @authorized(authorizer, rate_limiter)
123
+ async def _enable(update, context):
124
+ await enable_command(update, context)
125
+
126
+ @authorized(authorizer, rate_limiter)
127
+ async def _disable(update, context):
128
+ await disable_command(update, context)
129
+
130
+ @authorized(authorizer, rate_limiter)
131
+ async def _message(update, context):
132
+ await handle_message(update, context)
133
+
134
+ return {
135
+ "start": _start,
136
+ "help": _help,
137
+ "new": _new,
138
+ "sessions": _sessions,
139
+ "switch": _switch,
140
+ "model": _model,
141
+ "models": _models,
142
+ "stop": _stop,
143
+ "project": _project,
144
+ "enable": _enable,
145
+ "disable": _disable,
146
+ "mode": _mode,
147
+ "share": _share,
148
+ "status": _status,
149
+ "id": _id,
150
+ "message": _message,
151
+ }
152
+
153
+
154
+ async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
155
+ """Log the error and send a Telegram message to notify the user."""
156
+ # Log the error with traceback
157
+ logger.error("Exception while handling an update:", exc_info=context.error)
158
+
159
+ # Notify the user if the update is a Telegram Update with a message
160
+ if isinstance(update, Update) and update.effective_message:
161
+ try:
162
+ error_message = f"An unexpected error occurred: {context.error}"
163
+ # Reply to the user with the formatted error
164
+ await update.effective_message.reply_text(
165
+ format_error(error_message),
166
+ parse_mode="HTML"
167
+ )
168
+ except Exception as e:
169
+ logger.error(f"Failed to send error notification message: {e}")
170
+
171
+
172
+ async def post_init(application) -> None:
173
+ """Run after the bot application is initialized."""
174
+ # Register bot commands in Telegram's menu
175
+ await set_bot_commands(application)
176
+
177
+ # Initialize session manager
178
+ session_mgr = application.bot_data["session_manager"]
179
+ await session_mgr.initialize()
180
+
181
+ logger.info("🤖 Bot is ready! (OpenCode serve will start lazily on the first message)")
182
+
183
+
184
+ async def post_shutdown(application) -> None:
185
+ """Clean up resources on shutdown."""
186
+ logger.info("Shutting down...")
187
+
188
+ # Stop the background opencode server process if running
189
+ try:
190
+ from opencode.server import stop_server
191
+ await stop_server()
192
+ except Exception as e:
193
+ logger.warning(f"Failed to stop background OpenCode server: {e}")
194
+
195
+ # Close session manager DB
196
+ session_mgr = application.bot_data.get("session_manager")
197
+ if session_mgr:
198
+ await session_mgr.close()
199
+
200
+ # Close HTTP client
201
+ oc_client = application.bot_data.get("opencode_client")
202
+ if oc_client:
203
+ await oc_client.close()
204
+
205
+ logger.info("Goodbye!")
206
+
207
+
208
+ def run_firstrun_setup():
209
+ """Interactively prompts the user for configuration values and writes them to .env."""
210
+ print("=" * 60)
211
+ print("🚀 OpenCode Telegram Bot — Interactive First-Run Setup")
212
+ print("=" * 60)
213
+ print("This utility will help you configure your .env file.\n")
214
+
215
+ # 1. Read existing .env values if they exist, to provide defaults
216
+ current_values = {}
217
+ if os.path.exists(".env"):
218
+ try:
219
+ with open(".env", "r", encoding="utf-8") as f:
220
+ for line in f:
221
+ line = line.strip()
222
+ if line and not line.startswith("#") and "=" in line:
223
+ k, v = line.split("=", 1)
224
+ # Strip quotes if present
225
+ v = v.strip().strip("'\"")
226
+ current_values[k.strip()] = v
227
+ except Exception:
228
+ pass
229
+
230
+ # Helper function to ask with default
231
+ def ask(key, prompt_text, default_val=""):
232
+ default = current_values.get(key, default_val)
233
+ default_display = f" [{default}]" if default else ""
234
+
235
+ try:
236
+ val = input(f"{prompt_text}{default_display}: ").strip()
237
+ except (KeyboardInterrupt, EOFError):
238
+ print("\nSetup aborted.")
239
+ sys.exit(1)
240
+
241
+ if not val:
242
+ return default
243
+ return val
244
+
245
+ # 2. Gather inputs
246
+ token = ask(
247
+ "TELEGRAM_BOT_TOKEN",
248
+ "1. Enter your TELEGRAM_BOT_TOKEN (from @BotFather)"
249
+ )
250
+ while not token:
251
+ print("❌ TELEGRAM_BOT_TOKEN is required to run the bot!")
252
+ token = ask("TELEGRAM_BOT_TOKEN", "1. Enter your TELEGRAM_BOT_TOKEN")
253
+
254
+ users = ask(
255
+ "AUTHORIZED_USERS",
256
+ "2. Enter AUTHORIZED_USERS (comma-separated Telegram User IDs, e.g. 846469353)"
257
+ )
258
+ while not users:
259
+ print("❌ AUTHORIZED_USERS is required to restrict access to the bot!")
260
+ users = ask("AUTHORIZED_USERS", "2. Enter AUTHORIZED_USERS")
261
+
262
+ server_url = ask(
263
+ "OPENCODE_SERVER_URL",
264
+ "3. Enter OPENCODE_SERVER_URL",
265
+ "http://localhost:4096"
266
+ )
267
+
268
+ username = ask(
269
+ "OPENCODE_SERVER_USERNAME",
270
+ "4. Enter OPENCODE_SERVER_USERNAME (leave blank if no authentication)"
271
+ )
272
+
273
+ password = ask(
274
+ "OPENCODE_SERVER_PASSWORD",
275
+ "5. Enter OPENCODE_SERVER_PASSWORD (leave blank if no authentication)"
276
+ )
277
+
278
+ model = ask(
279
+ "OPENCODE_MODEL",
280
+ "6. Enter OPENCODE_MODEL",
281
+ "opencode/deepseek-v4-flash-free"
282
+ )
283
+
284
+ work_dir = ask(
285
+ "OPENCODE_WORK_DIR",
286
+ "7. Enter OPENCODE_WORK_DIR (full path to your parent workspace)",
287
+ os.path.abspath(".")
288
+ )
289
+
290
+ # 3. Format and write .env
291
+ env_content = f"""# Telegram Bot Configuration
292
+ TELEGRAM_BOT_TOKEN={token}
293
+ AUTHORIZED_USERS={users}
294
+
295
+ # OpenCode Configuration
296
+ OPENCODE_SERVER_URL={server_url}
297
+ OPENCODE_SERVER_USERNAME={username}
298
+ OPENCODE_SERVER_PASSWORD={password}
299
+ OPENCODE_MODEL={model}
300
+ OPENCODE_WORK_DIR="{work_dir}"
301
+
302
+ # Limits
303
+ MAX_MESSAGE_LENGTH=4000
304
+ RESPONSE_TIMEOUT=0 # Set to 0 to disable request timeouts entirely
305
+
306
+ # Database
307
+ DB_PATH=sessions.db
308
+ """
309
+
310
+ try:
311
+ with open(".env", "w", encoding="utf-8") as f:
312
+ f.write(env_content)
313
+ print("\n✅ Success! Configuration written to .env file.")
314
+ print("=" * 60)
315
+ print("You can now start the bot using:")
316
+ print(" python bot.py")
317
+ print("=" * 60 + "\n")
318
+ except Exception as e:
319
+ print(f"\n❌ Error writing to .env file: {e}")
320
+
321
+
322
+ def main():
323
+ """Build and run the Telegram bot."""
324
+
325
+ # If --env CLI flag is passed, or if .env does not exist, run setup
326
+ if "--env" in sys.argv:
327
+ run_firstrun_setup()
328
+ sys.exit(0)
329
+
330
+ if not os.path.exists(".env"):
331
+ print("⚠️ .env file not found!")
332
+ try:
333
+ choice = input("Would you like to run the interactive setup now? (y/n): ").strip().lower()
334
+ if choice in ("y", "yes"):
335
+ run_firstrun_setup()
336
+ print("\nSetup complete! Starting the bot...")
337
+ else:
338
+ print("Please copy .env.example to .env and configure it before starting.")
339
+ sys.exit(1)
340
+ except (KeyboardInterrupt, EOFError):
341
+ print("\nSetup cancelled.")
342
+ sys.exit(1)
343
+
344
+ # ── Validate config ───────────────────────────────────
345
+ try:
346
+ config.validate()
347
+ except ValueError as e:
348
+ logger.error(f"Configuration error: {e}")
349
+ logger.error("Copy .env.example → .env and fill in your values.")
350
+ sys.exit(1)
351
+
352
+ logger.info("=" * 50)
353
+ logger.info(" OpenCode Telegram Bot")
354
+ logger.info("=" * 50)
355
+ logger.info(f" OpenCode Server: {config.opencode_server_url}")
356
+ logger.info(f" Default Model: {config.opencode_model}")
357
+ logger.info(f" Work Directory: {config.opencode_work_dir}")
358
+ logger.info(f" Authorized Users: {len(config.authorized_users)}")
359
+ logger.info("=" * 50)
360
+
361
+ # ── Initialize components ─────────────────────────────
362
+ authorizer = UserAuthorizer(config.authorized_users)
363
+ rate_limiter = RateLimiter(max_requests=20, window_seconds=60)
364
+
365
+ oc_client = OpenCodeClient(
366
+ server_url=config.opencode_server_url,
367
+ username=config.opencode_server_username,
368
+ password=config.opencode_server_password,
369
+ timeout=config.response_timeout,
370
+ )
371
+
372
+ session_mgr = SessionManager(db_path=config.db_path)
373
+
374
+ # ── Build Telegram application ────────────────────────
375
+ application = (
376
+ ApplicationBuilder()
377
+ .token(config.telegram_bot_token)
378
+ .post_init(post_init)
379
+ .post_shutdown(post_shutdown)
380
+ .build()
381
+ )
382
+
383
+ # ── Register global error handler ─────────────────────
384
+ application.add_error_handler(error_handler)
385
+
386
+ # Store components in bot_data for access in handlers
387
+ application.bot_data["config"] = config
388
+ application.bot_data["opencode_client"] = oc_client
389
+ application.bot_data["session_manager"] = session_mgr
390
+ application.bot_data["server_started"] = False
391
+
392
+ # ── Build auth-wrapped handlers ───────────────────────
393
+ handlers = build_authorized_handlers(authorizer, rate_limiter)
394
+
395
+ # ── Register command handlers ─────────────────────────
396
+ application.add_handler(CommandHandler("start", handlers["start"], block=False))
397
+ application.add_handler(CommandHandler("help", handlers["help"], block=False))
398
+ application.add_handler(CommandHandler("new", handlers["new"], block=False))
399
+ application.add_handler(CommandHandler("sessions", handlers["sessions"], block=False))
400
+ application.add_handler(CommandHandler("switch", handlers["switch"], block=False))
401
+ application.add_handler(CommandHandler("model", handlers["model"], block=False))
402
+ application.add_handler(CommandHandler("models", handlers["models"], block=False))
403
+ application.add_handler(CommandHandler("stop", handlers["stop"], block=False))
404
+ application.add_handler(CommandHandler("project", handlers["project"], block=False))
405
+ application.add_handler(CommandHandler("enable", handlers["enable"], block=False))
406
+ application.add_handler(CommandHandler("disable", handlers["disable"], block=False))
407
+ application.add_handler(CommandHandler("mode", handlers["mode"], block=False))
408
+ application.add_handler(CommandHandler("share", handlers["share"], block=False))
409
+ application.add_handler(CommandHandler("status", handlers["status"], block=False))
410
+ application.add_handler(CommandHandler("id", handlers["id"], block=False))
411
+
412
+ # ── Register message handler (catches all text) ───────
413
+ application.add_handler(
414
+ MessageHandler(
415
+ filters.TEXT & ~filters.COMMAND,
416
+ handlers["message"],
417
+ block=False,
418
+ )
419
+ )
420
+
421
+ # ── Start polling ─────────────────────────────────────
422
+ logger.info("Starting bot with long polling...")
423
+ application.run_polling(
424
+ drop_pending_updates=True,
425
+ allowed_updates=["message"],
426
+ )
427
+
428
+
429
+ if __name__ == "__main__":
430
+ main()
@@ -0,0 +1,92 @@
1
+ """
2
+ Configuration module for the Telegram-OpenCode bot.
3
+
4
+ Loads settings from environment variables (with .env file support)
5
+ and exposes them via a validated dataclass singleton.
6
+ """
7
+
8
+ import os
9
+ from dataclasses import dataclass, field
10
+ from typing import List
11
+ from dotenv import load_dotenv
12
+
13
+ load_dotenv()
14
+
15
+
16
+ @dataclass
17
+ class Config:
18
+ """Application configuration loaded from environment variables.
19
+
20
+ Attributes:
21
+ telegram_bot_token: Bot token obtained from @BotFather.
22
+ authorized_users: List of Telegram user IDs permitted to interact with the bot.
23
+ opencode_server_url: Base URL of the OpenCode HTTP server.
24
+ opencode_server_username: Optional username for OpenCode server authentication.
25
+ opencode_server_password: Optional password for OpenCode server authentication.
26
+ opencode_model: LLM model identifier used by OpenCode.
27
+ opencode_work_dir: Working directory OpenCode operates in.
28
+ max_message_length: Maximum characters per Telegram message chunk.
29
+ response_timeout: Seconds to wait for an OpenCode response before timing out.
30
+ db_path: File path for the SQLite session database.
31
+ """
32
+
33
+ # Telegram
34
+ telegram_bot_token: str = field(
35
+ default_factory=lambda: os.getenv('TELEGRAM_BOT_TOKEN', '')
36
+ )
37
+ authorized_users: List[int] = field(
38
+ default_factory=lambda: [
39
+ int(uid.strip())
40
+ for uid in os.getenv('AUTHORIZED_USERS', '').split(',')
41
+ if uid.strip().isdigit()
42
+ ]
43
+ )
44
+
45
+ # OpenCode
46
+ opencode_server_url: str = field(
47
+ default_factory=lambda: os.getenv('OPENCODE_SERVER_URL', 'http://localhost:4444')
48
+ )
49
+ opencode_server_username: str = field(
50
+ default_factory=lambda: os.getenv('OPENCODE_SERVER_USERNAME', '')
51
+ )
52
+ opencode_server_password: str = field(
53
+ default_factory=lambda: os.getenv('OPENCODE_SERVER_PASSWORD', '')
54
+ )
55
+ opencode_model: str = field(
56
+ default_factory=lambda: os.getenv('OPENCODE_MODEL', 'OpenCode Zen/DeepSeek V4 Flash Free')
57
+ )
58
+ opencode_work_dir: str = field(
59
+ default_factory=lambda: os.getenv('OPENCODE_WORK_DIR', '.')
60
+ )
61
+ project_scan_depth: int = field(
62
+ default_factory=lambda: int(os.getenv('PROJECT_SCAN_DEPTH', '2'))
63
+ )
64
+
65
+ # Limits
66
+ max_message_length: int = field(
67
+ default_factory=lambda: int(os.getenv('MAX_MESSAGE_LENGTH', '4000'))
68
+ )
69
+ response_timeout: int = field(
70
+ default_factory=lambda: int(os.getenv('RESPONSE_TIMEOUT', '300'))
71
+ )
72
+
73
+ # Database
74
+ db_path: str = field(
75
+ default_factory=lambda: os.getenv('DB_PATH', 'sessions.db')
76
+ )
77
+
78
+ def validate(self) -> None:
79
+ """Validate that all required configuration values are present.
80
+
81
+ Raises:
82
+ ValueError: If a required configuration value is missing.
83
+ """
84
+ if not self.telegram_bot_token:
85
+ raise ValueError('TELEGRAM_BOT_TOKEN is required')
86
+ if not self.authorized_users:
87
+ raise ValueError(
88
+ 'AUTHORIZED_USERS is required (comma-separated Telegram user IDs)'
89
+ )
90
+
91
+
92
+ config = Config()
@@ -0,0 +1,44 @@
1
+ import asyncio
2
+ import aiohttp
3
+ import json
4
+
5
+ async def main():
6
+ url = "http://localhost:4444"
7
+ async with aiohttp.ClientSession() as session:
8
+ # Create a session
9
+ async with session.post(f"{url}/session") as resp:
10
+ create_json = await resp.json()
11
+ session_id = create_json.get("id") or create_json.get("session_id")
12
+
13
+ print(f"Created session ID: {session_id}")
14
+
15
+ # Query GET /session/:id/models
16
+ models_url = f"{url}/session/{session_id}/models"
17
+ print(f"Querying {models_url}...")
18
+ async with session.get(models_url) as resp:
19
+ print(f"Status: {resp.status}")
20
+ data = await resp.json()
21
+
22
+ # Print complete JSON to see all configured models
23
+ print("\n--- All Configured Models on OpenCode Serve ---")
24
+ print(json.dumps(data, indent=2))
25
+
26
+ # Search for DeepSeek or Zen
27
+ print("\n--- Searching for 'deepseek' or 'zen' ---")
28
+ found = False
29
+ data_str = json.dumps(data)
30
+ if "deepseek" in data_str.lower() or "zen" in data_str.lower():
31
+ print("MATCH FOUND!")
32
+ # Let's inspect the exact provider and model IDs
33
+ if isinstance(data, dict):
34
+ # Usually returned as { "models": { ... }, "providers": [ ... ] } or similar
35
+ for k, v in data.items():
36
+ if "deepseek" in k.lower() or "zen" in k.lower() or "deepseek" in json.dumps(v).lower() or "zen" in json.dumps(v).lower():
37
+ print(f"\nKey: {k}")
38
+ print(json.dumps(v, indent=2)[:1000])
39
+ found = True
40
+ if not found:
41
+ print("No matches found for DeepSeek or Zen.")
42
+
43
+ if __name__ == "__main__":
44
+ asyncio.run(main())
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "telegram-opencode-bridge-bot"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "A Telegram bot that bridges messages directly to OpenCode — an AI coding agent running on your machine"
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  requires-python = ">=3.10"
12
12
  authors = [
13
- { name = "MaheshNagabhairava", email = "your@email.com" }
13
+ { name = "MaheshNagabhairava", email = "maheshnagabhirava12345@gmail.com" }
14
14
  ]
15
15
  keywords = ["telegram", "bot", "opencode", "ai", "coding", "bridge"]
16
16
  classifiers = [
@@ -32,4 +32,7 @@ Homepage = "https://github.com/MaheshNagabhairava/telegram-opencode-bridge-bot"
32
32
 
33
33
  [tool.setuptools.packages.find]
34
34
  where = ["."]
35
- include = ["handlers*", "opencode*", "sessions*", "utils*"]
35
+ include = ["handlers*", "opencode*", "sessions*", "utils*"]
36
+
37
+ [tool.setuptools]
38
+ py-modules = ["bot", "config", "list_session_models"]
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: telegram-opencode-bridge-bot
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: A Telegram bot that bridges messages directly to OpenCode — an AI coding agent running on your machine
5
- Author-email: MaheshNagabhairava <your@email.com>
5
+ Author-email: MaheshNagabhairava <maheshnagabhirava12345@gmail.com>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/MaheshNagabhairava/telegram-opencode-bridge-bot
8
8
  Keywords: telegram,bot,opencode,ai,coding,bridge
@@ -52,25 +52,15 @@ cd telegram-opencode-bot
52
52
  pip install -r requirements.txt
53
53
  ```
54
54
 
55
- ### 2. Start OpenCode Server
56
-
57
- In a separate terminal:
58
-
59
- ```bash
60
- opencode serve --port 4096 --hostname 127.0.0.1
61
- ```
62
-
63
- This starts the OpenCode HTTP API on `localhost:4096`.
64
-
65
- ### 3. Run the Bot
55
+ ### 2. Run the Bot
66
56
 
67
57
  ```bash
68
58
  python bot.py
69
- python bot.py --env (if u want to setup ur configuration later)
59
+ python bot.py --env (use --env flag if u want to re-configure later anytime)
70
60
  ```
71
61
  > **💡 Tip:** Don't know your Telegram user ID? Start the bot with `AUTHORIZED_USERS=0` temporarily, send `/id` to the bot, then update the `.env` with your real ID.
72
62
 
73
- ### 4. Chat!
63
+ ### 3. Chat!
74
64
 
75
65
  Open Telegram, find your bot, and start asking! 🎉
76
66
 
@@ -1,6 +1,9 @@
1
1
  .env.example
2
2
  MANIFEST.in
3
3
  README.md
4
+ bot.py
5
+ config.py
6
+ list_session_models.py
4
7
  pyproject.toml
5
8
  requirements.txt
6
9
  handlers/__init__.py
@@ -1,4 +1,7 @@
1
+ bot
2
+ config
1
3
  handlers
4
+ list_session_models
2
5
  opencode
3
6
  sessions
4
7
  utils