telegram-opencode-bridge-bot 0.1.6__tar.gz → 0.1.8__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.
- telegram_opencode_bridge_bot-0.1.8/PKG-INFO +187 -0
- telegram_opencode_bridge_bot-0.1.8/README.md +170 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/bot.py +79 -5
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/handlers/commands.py +238 -1
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/handlers/messages.py +242 -45
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/opencode/client.py +41 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/opencode/server.py +19 -16
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/pyproject.toml +1 -1
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/sessions/manager.py +76 -6
- telegram_opencode_bridge_bot-0.1.8/telegram_opencode_bridge_bot.egg-info/PKG-INFO +187 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/utils/formatting.py +151 -1
- telegram_opencode_bridge_bot-0.1.6/PKG-INFO +0 -154
- telegram_opencode_bridge_bot-0.1.6/README.md +0 -137
- telegram_opencode_bridge_bot-0.1.6/telegram_opencode_bridge_bot.egg-info/PKG-INFO +0 -154
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/.env.example +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/MANIFEST.in +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/config.py +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/handlers/__init__.py +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/list_session_models.py +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/opencode/__init__.py +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/requirements.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/sessions/__init__.py +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/setup.cfg +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/telegram_opencode_bridge_bot.egg-info/SOURCES.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/telegram_opencode_bridge_bot.egg-info/dependency_links.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/telegram_opencode_bridge_bot.egg-info/entry_points.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/telegram_opencode_bridge_bot.egg-info/requires.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/telegram_opencode_bridge_bot.egg-info/top_level.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/utils/__init__.py +0 -0
- {telegram_opencode_bridge_bot-0.1.6 → telegram_opencode_bridge_bot-0.1.8}/utils/security.py +0 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: telegram-opencode-bridge-bot
|
|
3
|
+
Version: 0.1.8
|
|
4
|
+
Summary: A Telegram bot that bridges messages directly to OpenCode — an AI coding agent running on your machine
|
|
5
|
+
Author-email: MaheshNagabhairava <maheshnagabhirava12345@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/MaheshNagabhairava/telegram-opencode-bridge-bot
|
|
8
|
+
Keywords: telegram,bot,opencode,ai,coding,bridge
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: python-telegram-bot[ext]>=21.0
|
|
14
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
15
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
16
|
+
Requires-Dist: aiosqlite>=0.19.0
|
|
17
|
+
|
|
18
|
+
# 🤖 Telegram ➔ OpenCode Bridge Bot
|
|
19
|
+
|
|
20
|
+
[](https://opensource.org/licenses/MIT)
|
|
21
|
+
[](https://www.python.org/)
|
|
22
|
+
[](https://opencode.ai)
|
|
23
|
+
|
|
24
|
+
A premium, feature-rich Python bot that bridges your Telegram client directly to [OpenCode](https://opencode.ai) — a powerful AI coding agent running on your local machine. Authorize your personal user account and get professional, agentic software engineering assistance right in your pocket.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## ✨ Features
|
|
29
|
+
|
|
30
|
+
- **Direct OpenCode Integration** — Routes your chat prompts directly to the OpenCode HTTP API or local server processes.
|
|
31
|
+
- **Dynamic Project Isolation** — Effortlessly view, create, delete, and switch workspace folders on your local machine using an interactive Telegram file explorer interface.
|
|
32
|
+
- **Mobile File-Upload Uploads** — Send files, code logs, or images directly from Telegram to upload them into your active workspace folder.
|
|
33
|
+
- **Robust Session Registry** — Seamlessly list, switch, delete, and share conversations, automatically synced with the backend SQLite database.
|
|
34
|
+
- **Conversational History View (`/history`)** — Chronologically fetches the active conversation, filtered of long execution logs, reasoning traces, or step metadata. Messages are sent sequentially with rate-limit respect and support pagination controls.
|
|
35
|
+
- **Live Tool Execution Streaming** — Watch the agent interact with your filesystem, write code, run bash commands, and search the web in real-time (`/enable` / `/disable`).
|
|
36
|
+
- **Flexible Model & Agent Mode Selectors** — Swap underlying LLM models on the fly (`/models`) and switch agent personas (e.g. Build, Plan, or Webtester) using clean interactive menus (`/mode`).
|
|
37
|
+
- **Secure by Default** — Enforces a strict user whitelist check on every message and callback action to prevent unauthorized access.
|
|
38
|
+
- **Robustness & Windows Stability** — Configured with automated API retries, suppressed verbose network noise, and Selector Event Loops for clean subprocess termination on Windows systems.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 📋 Prerequisites
|
|
43
|
+
|
|
44
|
+
1. **Python 3.10+** installed on your system.
|
|
45
|
+
2. **OpenCode CLI** installed and initialized:
|
|
46
|
+
```bash
|
|
47
|
+
npm install -g opencode-ai
|
|
48
|
+
# or
|
|
49
|
+
curl -fsSL https://opencode.ai/install | bash
|
|
50
|
+
```
|
|
51
|
+
3. **Telegram Bot Token** — Obtain one instantly via [@BotFather](https://t.me/BotFather).
|
|
52
|
+
4. **Your Telegram User ID** — Retrieve it using [@userinfobot](https://t.me/userinfobot), or start the bot and send `/id`.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 🚀 Installation & Setup
|
|
57
|
+
|
|
58
|
+
### Option A: Standard PyPI Installation
|
|
59
|
+
Install and run the bot wrapper directly from the terminal:
|
|
60
|
+
```bash
|
|
61
|
+
pip install telegram-opencode-bridge-bot==0.1.8
|
|
62
|
+
telegram-opencode-bot
|
|
63
|
+
```
|
|
64
|
+
> **💡 Tip:** Use the `--env` flag anytime to reconfigure your variables:
|
|
65
|
+
> `telegram-opencode-bot --env`
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### Option B: Manual Git Installation
|
|
70
|
+
|
|
71
|
+
1. **Clone the repository** and install dependencies:
|
|
72
|
+
```bash
|
|
73
|
+
git clone https://github.com/MaheshNagabhairava/telegram-opencode-bridge-bot.git
|
|
74
|
+
cd telegram-opencode-bot
|
|
75
|
+
pip install -r requirements.txt
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
2. **Start the bot**:
|
|
79
|
+
```bash
|
|
80
|
+
python bot.py
|
|
81
|
+
```
|
|
82
|
+
> **💡 Tip:** Use the `--env` flag anytime to reconfigure your variables:
|
|
83
|
+
> `python bot.py --env`
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 📱 Command Reference
|
|
87
|
+
|
|
88
|
+
| Command | Parameter | Description |
|
|
89
|
+
| :--- | :--- | :--- |
|
|
90
|
+
| **`/start`** | — | Prints the welcome card and verifies connectivity to the OpenCode server. |
|
|
91
|
+
| **`/help`** | — | Displays a complete guide of all available commands and tips. |
|
|
92
|
+
| **`/new`** | — | Clears the current active session cache to start a fresh topic. |
|
|
93
|
+
| **`/stop`** | — | Aborts any active running task or model generation process. |
|
|
94
|
+
| **`/status`** | — | Prints detailed statistics about the server connection and active session settings. |
|
|
95
|
+
| **`/id`** | — | Displays your Telegram User ID (useful for whitelisting). |
|
|
96
|
+
| **`/project`** | `[name]` | Opens the directory explorer. Optionally switches directly to the specified folder. |
|
|
97
|
+
| **`/create_project`** | `<name>` | Creates a new isolated folder workspace inside your primary workspace path. |
|
|
98
|
+
| **`/delete_project`** | `[name]` | Erases a workspace directory from disk (requires inline button verification). |
|
|
99
|
+
| **`/sessions`** | — | Lists your recent conversations in the current workspace to allow switching. |
|
|
100
|
+
| **`/history`** | `[count]` | Displays the chronological history of the active session, split into text batches. |
|
|
101
|
+
| **`/delete`** | — | Prompts an inline keyboard to permanently delete a session. |
|
|
102
|
+
| **`/models`** | — | Lists all models available on the OpenCode server to easily select a default. |
|
|
103
|
+
| **`/mode`** | — | Opens a menu to select agent personas (e.g. Build, Plan, custom testers). |
|
|
104
|
+
| **`/plan`** | — | Quick switch shortcut to Plan Mode (read-only analysis). |
|
|
105
|
+
| **`/build`** | — | Quick switch shortcut to Build Mode (read, write, execute permissions). |
|
|
106
|
+
| **`/enable`** | — | Enables real-time streaming of tool calls, shell executions, and file edits. |
|
|
107
|
+
| **`/disable`** | — | Disables streaming; the bot will only report the final LLM response. |
|
|
108
|
+
| **`/share`** | — | Fetches a public, shareable web preview URL of the active conversation. |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## ⚙️ Configuration Variables
|
|
113
|
+
|
|
114
|
+
The following parameters can be defined in your `.env` configuration file:
|
|
115
|
+
|
|
116
|
+
| Variable | Type | Description | Default |
|
|
117
|
+
| :--- | :--- | :--- | :--- |
|
|
118
|
+
| `TELEGRAM_BOT_TOKEN` | *String* | Secret token issued by @BotFather. | **Required** |
|
|
119
|
+
| `AUTHORIZED_USERS` | *List* | Comma-separated list of whitelisted Telegram User IDs. | **Required** |
|
|
120
|
+
| `OPENCODE_SERVER_URL` | *String* | The target URL hosting the OpenCode endpoint. | `http://localhost:4096` |
|
|
121
|
+
| `OPENCODE_SERVER_USERNAME`| *String* | Optional Basic Auth username for server login. | *(empty)* |
|
|
122
|
+
| `OPENCODE_SERVER_PASSWORD`| *String* | Optional Basic Auth password for server login. | *(empty)* |
|
|
123
|
+
| `OPENCODE_MODEL` | *String* | The default LLM provider/model key configured. | `opencode/deepseek-v4-flash-free` |
|
|
124
|
+
| `OPENCODE_WORK_DIR` | *String* | Local parent path containing your projects. | `.` |
|
|
125
|
+
| `PROJECT_SCAN_DEPTH` | *Integer*| Max recursion depth used to search project directories. | `2` |
|
|
126
|
+
| `MAX_MESSAGE_LENGTH` | *Integer*| Maximum character limit before chunking Telegram texts. | `4000` |
|
|
127
|
+
| `RESPONSE_TIMEOUT` | *Integer*| Request timeout in seconds (set `0` to disable timeouts). | `300` |
|
|
128
|
+
| `DB_PATH` | *String* | Storage path for the SQLite session manager. | `sessions.db` |
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 🏗️ Architecture Layout
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
👤 Telegram Client (Mobile / Desktop)
|
|
136
|
+
│
|
|
137
|
+
▼ (HTTPS Long Polling / Webhook)
|
|
138
|
+
🤖 Telegram Bridge Bot (Python)
|
|
139
|
+
│
|
|
140
|
+
▼ (HTTP Local API / SSE Events)
|
|
141
|
+
💻 OpenCode Server (localhost:4096)
|
|
142
|
+
│
|
|
143
|
+
┌────────┴────────┐
|
|
144
|
+
▼ ▼
|
|
145
|
+
🤖 LLM Provider 📁 Local Workspace
|
|
146
|
+
(Claude/DeepSeek/etc.) (Shell / Filesystem)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 🔒 Security Policy
|
|
152
|
+
|
|
153
|
+
> [!WARNING]
|
|
154
|
+
> This bot allows remote execution of shell commands, filesystem operations, and internet fetching on the host machine.
|
|
155
|
+
>
|
|
156
|
+
> 1. Keep `AUTHORIZED_USERS` configured strictly with **your own** Telegram User ID.
|
|
157
|
+
> 2. Avoid exposing your Telegram Bot Token or sharing public URLs if your host machine handles sensitive information.
|
|
158
|
+
> 3. Verify tool permissions in the console log if executing arbitrary files.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 📁 Project Structure
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
telegram-opencode-bot/
|
|
166
|
+
├── bot.py # Main entry point, loops, and setup
|
|
167
|
+
├── config.py # validated configuration loader
|
|
168
|
+
├── sessions.db # Persistent SQL database (generated)
|
|
169
|
+
├── requirements.txt # python dependencies
|
|
170
|
+
├── handlers/
|
|
171
|
+
│ ├── commands.py # Slash command handlers (/start, /history, etc.)
|
|
172
|
+
│ └── messages.py # Text & file message routing logic
|
|
173
|
+
├── opencode/
|
|
174
|
+
│ ├── client.py # Async HTTP client wrapper for OpenCode API
|
|
175
|
+
│ └── server.py # Background subprocess lifecycle management
|
|
176
|
+
├── sessions/
|
|
177
|
+
│ └── manager.py # SQL persistence and state registry
|
|
178
|
+
└── utils/
|
|
179
|
+
├── formatting.py # Conversions, splits, and custom table layouts
|
|
180
|
+
└── security.py # Rate-limiting, whitelist guards, and checks
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 📄 License
|
|
186
|
+
|
|
187
|
+
Distributed under the MIT License. See [LICENSE](LICENSE) for more details.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# 🤖 Telegram ➔ OpenCode Bridge Bot
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://www.python.org/)
|
|
5
|
+
[](https://opencode.ai)
|
|
6
|
+
|
|
7
|
+
A premium, feature-rich Python bot that bridges your Telegram client directly to [OpenCode](https://opencode.ai) — a powerful AI coding agent running on your local machine. Authorize your personal user account and get professional, agentic software engineering assistance right in your pocket.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ✨ Features
|
|
12
|
+
|
|
13
|
+
- **Direct OpenCode Integration** — Routes your chat prompts directly to the OpenCode HTTP API or local server processes.
|
|
14
|
+
- **Dynamic Project Isolation** — Effortlessly view, create, delete, and switch workspace folders on your local machine using an interactive Telegram file explorer interface.
|
|
15
|
+
- **Mobile File-Upload Uploads** — Send files, code logs, or images directly from Telegram to upload them into your active workspace folder.
|
|
16
|
+
- **Robust Session Registry** — Seamlessly list, switch, delete, and share conversations, automatically synced with the backend SQLite database.
|
|
17
|
+
- **Conversational History View (`/history`)** — Chronologically fetches the active conversation, filtered of long execution logs, reasoning traces, or step metadata. Messages are sent sequentially with rate-limit respect and support pagination controls.
|
|
18
|
+
- **Live Tool Execution Streaming** — Watch the agent interact with your filesystem, write code, run bash commands, and search the web in real-time (`/enable` / `/disable`).
|
|
19
|
+
- **Flexible Model & Agent Mode Selectors** — Swap underlying LLM models on the fly (`/models`) and switch agent personas (e.g. Build, Plan, or Webtester) using clean interactive menus (`/mode`).
|
|
20
|
+
- **Secure by Default** — Enforces a strict user whitelist check on every message and callback action to prevent unauthorized access.
|
|
21
|
+
- **Robustness & Windows Stability** — Configured with automated API retries, suppressed verbose network noise, and Selector Event Loops for clean subprocess termination on Windows systems.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 📋 Prerequisites
|
|
26
|
+
|
|
27
|
+
1. **Python 3.10+** installed on your system.
|
|
28
|
+
2. **OpenCode CLI** installed and initialized:
|
|
29
|
+
```bash
|
|
30
|
+
npm install -g opencode-ai
|
|
31
|
+
# or
|
|
32
|
+
curl -fsSL https://opencode.ai/install | bash
|
|
33
|
+
```
|
|
34
|
+
3. **Telegram Bot Token** — Obtain one instantly via [@BotFather](https://t.me/BotFather).
|
|
35
|
+
4. **Your Telegram User ID** — Retrieve it using [@userinfobot](https://t.me/userinfobot), or start the bot and send `/id`.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 🚀 Installation & Setup
|
|
40
|
+
|
|
41
|
+
### Option A: Standard PyPI Installation
|
|
42
|
+
Install and run the bot wrapper directly from the terminal:
|
|
43
|
+
```bash
|
|
44
|
+
pip install telegram-opencode-bridge-bot==0.1.8
|
|
45
|
+
telegram-opencode-bot
|
|
46
|
+
```
|
|
47
|
+
> **💡 Tip:** Use the `--env` flag anytime to reconfigure your variables:
|
|
48
|
+
> `telegram-opencode-bot --env`
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### Option B: Manual Git Installation
|
|
53
|
+
|
|
54
|
+
1. **Clone the repository** and install dependencies:
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/MaheshNagabhairava/telegram-opencode-bridge-bot.git
|
|
57
|
+
cd telegram-opencode-bot
|
|
58
|
+
pip install -r requirements.txt
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
2. **Start the bot**:
|
|
62
|
+
```bash
|
|
63
|
+
python bot.py
|
|
64
|
+
```
|
|
65
|
+
> **💡 Tip:** Use the `--env` flag anytime to reconfigure your variables:
|
|
66
|
+
> `python bot.py --env`
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 📱 Command Reference
|
|
70
|
+
|
|
71
|
+
| Command | Parameter | Description |
|
|
72
|
+
| :--- | :--- | :--- |
|
|
73
|
+
| **`/start`** | — | Prints the welcome card and verifies connectivity to the OpenCode server. |
|
|
74
|
+
| **`/help`** | — | Displays a complete guide of all available commands and tips. |
|
|
75
|
+
| **`/new`** | — | Clears the current active session cache to start a fresh topic. |
|
|
76
|
+
| **`/stop`** | — | Aborts any active running task or model generation process. |
|
|
77
|
+
| **`/status`** | — | Prints detailed statistics about the server connection and active session settings. |
|
|
78
|
+
| **`/id`** | — | Displays your Telegram User ID (useful for whitelisting). |
|
|
79
|
+
| **`/project`** | `[name]` | Opens the directory explorer. Optionally switches directly to the specified folder. |
|
|
80
|
+
| **`/create_project`** | `<name>` | Creates a new isolated folder workspace inside your primary workspace path. |
|
|
81
|
+
| **`/delete_project`** | `[name]` | Erases a workspace directory from disk (requires inline button verification). |
|
|
82
|
+
| **`/sessions`** | — | Lists your recent conversations in the current workspace to allow switching. |
|
|
83
|
+
| **`/history`** | `[count]` | Displays the chronological history of the active session, split into text batches. |
|
|
84
|
+
| **`/delete`** | — | Prompts an inline keyboard to permanently delete a session. |
|
|
85
|
+
| **`/models`** | — | Lists all models available on the OpenCode server to easily select a default. |
|
|
86
|
+
| **`/mode`** | — | Opens a menu to select agent personas (e.g. Build, Plan, custom testers). |
|
|
87
|
+
| **`/plan`** | — | Quick switch shortcut to Plan Mode (read-only analysis). |
|
|
88
|
+
| **`/build`** | — | Quick switch shortcut to Build Mode (read, write, execute permissions). |
|
|
89
|
+
| **`/enable`** | — | Enables real-time streaming of tool calls, shell executions, and file edits. |
|
|
90
|
+
| **`/disable`** | — | Disables streaming; the bot will only report the final LLM response. |
|
|
91
|
+
| **`/share`** | — | Fetches a public, shareable web preview URL of the active conversation. |
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## ⚙️ Configuration Variables
|
|
96
|
+
|
|
97
|
+
The following parameters can be defined in your `.env` configuration file:
|
|
98
|
+
|
|
99
|
+
| Variable | Type | Description | Default |
|
|
100
|
+
| :--- | :--- | :--- | :--- |
|
|
101
|
+
| `TELEGRAM_BOT_TOKEN` | *String* | Secret token issued by @BotFather. | **Required** |
|
|
102
|
+
| `AUTHORIZED_USERS` | *List* | Comma-separated list of whitelisted Telegram User IDs. | **Required** |
|
|
103
|
+
| `OPENCODE_SERVER_URL` | *String* | The target URL hosting the OpenCode endpoint. | `http://localhost:4096` |
|
|
104
|
+
| `OPENCODE_SERVER_USERNAME`| *String* | Optional Basic Auth username for server login. | *(empty)* |
|
|
105
|
+
| `OPENCODE_SERVER_PASSWORD`| *String* | Optional Basic Auth password for server login. | *(empty)* |
|
|
106
|
+
| `OPENCODE_MODEL` | *String* | The default LLM provider/model key configured. | `opencode/deepseek-v4-flash-free` |
|
|
107
|
+
| `OPENCODE_WORK_DIR` | *String* | Local parent path containing your projects. | `.` |
|
|
108
|
+
| `PROJECT_SCAN_DEPTH` | *Integer*| Max recursion depth used to search project directories. | `2` |
|
|
109
|
+
| `MAX_MESSAGE_LENGTH` | *Integer*| Maximum character limit before chunking Telegram texts. | `4000` |
|
|
110
|
+
| `RESPONSE_TIMEOUT` | *Integer*| Request timeout in seconds (set `0` to disable timeouts). | `300` |
|
|
111
|
+
| `DB_PATH` | *String* | Storage path for the SQLite session manager. | `sessions.db` |
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 🏗️ Architecture Layout
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
👤 Telegram Client (Mobile / Desktop)
|
|
119
|
+
│
|
|
120
|
+
▼ (HTTPS Long Polling / Webhook)
|
|
121
|
+
🤖 Telegram Bridge Bot (Python)
|
|
122
|
+
│
|
|
123
|
+
▼ (HTTP Local API / SSE Events)
|
|
124
|
+
💻 OpenCode Server (localhost:4096)
|
|
125
|
+
│
|
|
126
|
+
┌────────┴────────┐
|
|
127
|
+
▼ ▼
|
|
128
|
+
🤖 LLM Provider 📁 Local Workspace
|
|
129
|
+
(Claude/DeepSeek/etc.) (Shell / Filesystem)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 🔒 Security Policy
|
|
135
|
+
|
|
136
|
+
> [!WARNING]
|
|
137
|
+
> This bot allows remote execution of shell commands, filesystem operations, and internet fetching on the host machine.
|
|
138
|
+
>
|
|
139
|
+
> 1. Keep `AUTHORIZED_USERS` configured strictly with **your own** Telegram User ID.
|
|
140
|
+
> 2. Avoid exposing your Telegram Bot Token or sharing public URLs if your host machine handles sensitive information.
|
|
141
|
+
> 3. Verify tool permissions in the console log if executing arbitrary files.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 📁 Project Structure
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
telegram-opencode-bot/
|
|
149
|
+
├── bot.py # Main entry point, loops, and setup
|
|
150
|
+
├── config.py # validated configuration loader
|
|
151
|
+
├── sessions.db # Persistent SQL database (generated)
|
|
152
|
+
├── requirements.txt # python dependencies
|
|
153
|
+
├── handlers/
|
|
154
|
+
│ ├── commands.py # Slash command handlers (/start, /history, etc.)
|
|
155
|
+
│ └── messages.py # Text & file message routing logic
|
|
156
|
+
├── opencode/
|
|
157
|
+
│ ├── client.py # Async HTTP client wrapper for OpenCode API
|
|
158
|
+
│ └── server.py # Background subprocess lifecycle management
|
|
159
|
+
├── sessions/
|
|
160
|
+
│ └── manager.py # SQL persistence and state registry
|
|
161
|
+
└── utils/
|
|
162
|
+
├── formatting.py # Conversions, splits, and custom table layouts
|
|
163
|
+
└── security.py # Rate-limiting, whitelist guards, and checks
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 📄 License
|
|
169
|
+
|
|
170
|
+
Distributed under the MIT License. See [LICENSE](LICENSE) for more details.
|
|
@@ -19,6 +19,10 @@ import logging
|
|
|
19
19
|
import sys
|
|
20
20
|
import os
|
|
21
21
|
|
|
22
|
+
# Switch to Selector Event Loop on Windows for robust signal handling and clean shutdowns
|
|
23
|
+
if sys.platform == 'win32':
|
|
24
|
+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
25
|
+
|
|
22
26
|
from telegram import Update
|
|
23
27
|
from telegram.ext import (
|
|
24
28
|
ApplicationBuilder,
|
|
@@ -44,6 +48,7 @@ from handlers.commands import (
|
|
|
44
48
|
delete_command,
|
|
45
49
|
plan_command,
|
|
46
50
|
build_command,
|
|
51
|
+
mode_command,
|
|
47
52
|
share_command,
|
|
48
53
|
status_command,
|
|
49
54
|
id_command,
|
|
@@ -54,6 +59,7 @@ from handlers.commands import (
|
|
|
54
59
|
delete_project_command,
|
|
55
60
|
enable_command,
|
|
56
61
|
disable_command,
|
|
62
|
+
history_command,
|
|
57
63
|
set_bot_commands,
|
|
58
64
|
callback_handler,
|
|
59
65
|
)
|
|
@@ -67,6 +73,43 @@ logging.basicConfig(
|
|
|
67
73
|
)
|
|
68
74
|
logger = logging.getLogger("opencode-telegram-bot")
|
|
69
75
|
|
|
76
|
+
_lock_file = None
|
|
77
|
+
|
|
78
|
+
def acquire_bot_lock():
|
|
79
|
+
"""Acquire an exclusive lock file to prevent multiple instances from running concurrently."""
|
|
80
|
+
global _lock_file
|
|
81
|
+
lock_path = os.path.join(os.path.abspath("."), "bot.lock")
|
|
82
|
+
try:
|
|
83
|
+
_lock_file = open(lock_path, "w")
|
|
84
|
+
if os.name == 'nt':
|
|
85
|
+
import msvcrt
|
|
86
|
+
try:
|
|
87
|
+
_lock_file.seek(0)
|
|
88
|
+
msvcrt.locking(_lock_file.fileno(), msvcrt.LK_NBLCK, 1)
|
|
89
|
+
_lock_file.write(str(os.getpid()))
|
|
90
|
+
_lock_file.flush()
|
|
91
|
+
except (OSError, IOError):
|
|
92
|
+
print("\n" + "="*65)
|
|
93
|
+
print("❌ ERROR: Another instance of the Telegram bot is already running!")
|
|
94
|
+
print("Please close the other terminal or kill the stray Python process.")
|
|
95
|
+
print("="*65 + "\n")
|
|
96
|
+
sys.exit(1)
|
|
97
|
+
else:
|
|
98
|
+
import fcntl
|
|
99
|
+
try:
|
|
100
|
+
fcntl.flock(_lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
101
|
+
_lock_file.write(str(os.getpid()))
|
|
102
|
+
_lock_file.flush()
|
|
103
|
+
except (OSError, IOError):
|
|
104
|
+
print("\n" + "="*65)
|
|
105
|
+
print("❌ ERROR: Another instance of the Telegram bot is already running!")
|
|
106
|
+
print("Please close the other terminal or kill the stray Python process.")
|
|
107
|
+
print("="*65 + "\n")
|
|
108
|
+
sys.exit(1)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
logger.warning(f"Could not acquire bot lock: {e}")
|
|
111
|
+
|
|
112
|
+
|
|
70
113
|
|
|
71
114
|
class RetryingHTTPXRequest(HTTPXRequest):
|
|
72
115
|
"""Custom HTTPXRequest that automatically retries failed requests on connection timeouts/errors."""
|
|
@@ -120,6 +163,10 @@ def build_authorized_handlers(authorizer: UserAuthorizer, rate_limiter: RateLimi
|
|
|
120
163
|
async def _build(update, context):
|
|
121
164
|
await build_command(update, context)
|
|
122
165
|
|
|
166
|
+
@authorized(authorizer, rate_limiter)
|
|
167
|
+
async def _mode(update, context):
|
|
168
|
+
await mode_command(update, context)
|
|
169
|
+
|
|
123
170
|
@authorized(authorizer, rate_limiter)
|
|
124
171
|
async def _share(update, context):
|
|
125
172
|
await share_command(update, context)
|
|
@@ -160,6 +207,10 @@ def build_authorized_handlers(authorizer: UserAuthorizer, rate_limiter: RateLimi
|
|
|
160
207
|
async def _disable(update, context):
|
|
161
208
|
await disable_command(update, context)
|
|
162
209
|
|
|
210
|
+
@authorized(authorizer, rate_limiter)
|
|
211
|
+
async def _history(update, context):
|
|
212
|
+
await history_command(update, context)
|
|
213
|
+
|
|
163
214
|
@authorized(authorizer)
|
|
164
215
|
async def _callback(update, context):
|
|
165
216
|
await callback_handler(update, context)
|
|
@@ -185,8 +236,10 @@ def build_authorized_handlers(authorizer: UserAuthorizer, rate_limiter: RateLimi
|
|
|
185
236
|
"delete_project": _delete_project,
|
|
186
237
|
"enable": _enable,
|
|
187
238
|
"disable": _disable,
|
|
239
|
+
"history": _history,
|
|
188
240
|
"plan": _plan,
|
|
189
241
|
"build": _build,
|
|
242
|
+
"mode": _mode,
|
|
190
243
|
"share": _share,
|
|
191
244
|
"status": _status,
|
|
192
245
|
"id": _id,
|
|
@@ -198,8 +251,17 @@ def build_authorized_handlers(authorizer: UserAuthorizer, rate_limiter: RateLimi
|
|
|
198
251
|
|
|
199
252
|
async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|
200
253
|
"""Log the error and send a Telegram message to notify the user."""
|
|
201
|
-
#
|
|
202
|
-
|
|
254
|
+
# Suppress full traceback for common transient network / timeout errors to keep logs clean
|
|
255
|
+
from telegram.error import NetworkError, TimedOut
|
|
256
|
+
|
|
257
|
+
err = context.error
|
|
258
|
+
err_str = str(err).lower()
|
|
259
|
+
if isinstance(err, (NetworkError, TimedOut)) or "httpx" in err_str or "httpcore" in err_str or "read error" in err_str:
|
|
260
|
+
logger.warning(f"📡 Transient Telegram network/timeout error: {err}")
|
|
261
|
+
return
|
|
262
|
+
|
|
263
|
+
# Log unexpected errors with traceback
|
|
264
|
+
logger.error("Exception while handling an update:", exc_info=err)
|
|
203
265
|
|
|
204
266
|
# Notify the user if the update is a Telegram Update with a message
|
|
205
267
|
if isinstance(update, Update) and update.effective_message:
|
|
@@ -233,19 +295,25 @@ async def post_shutdown(application) -> None:
|
|
|
233
295
|
# Stop the background opencode server process if running
|
|
234
296
|
try:
|
|
235
297
|
from opencode.server import stop_server
|
|
236
|
-
await stop_server()
|
|
298
|
+
await asyncio.wait_for(stop_server(), timeout=8.0)
|
|
237
299
|
except Exception as e:
|
|
238
300
|
logger.warning(f"Failed to stop background OpenCode server: {e}")
|
|
239
301
|
|
|
240
302
|
# Close session manager DB
|
|
241
303
|
session_mgr = application.bot_data.get("session_manager")
|
|
242
304
|
if session_mgr:
|
|
243
|
-
|
|
305
|
+
try:
|
|
306
|
+
await asyncio.wait_for(session_mgr.close(), timeout=3.0)
|
|
307
|
+
except Exception as e:
|
|
308
|
+
logger.warning(f"Failed to close session manager: {e}")
|
|
244
309
|
|
|
245
310
|
# Close HTTP client
|
|
246
311
|
oc_client = application.bot_data.get("opencode_client")
|
|
247
312
|
if oc_client:
|
|
248
|
-
|
|
313
|
+
try:
|
|
314
|
+
await asyncio.wait_for(oc_client.close(), timeout=3.0)
|
|
315
|
+
except Exception as e:
|
|
316
|
+
logger.warning(f"Failed to close HTTP client: {e}")
|
|
249
317
|
|
|
250
318
|
logger.info("Goodbye!")
|
|
251
319
|
|
|
@@ -412,6 +480,9 @@ def main():
|
|
|
412
480
|
logger.error("Copy .env.example → .env and fill in your values.")
|
|
413
481
|
sys.exit(1)
|
|
414
482
|
|
|
483
|
+
# ── Acquire exclusive bot lock to prevent concurrent instances ───────
|
|
484
|
+
acquire_bot_lock()
|
|
485
|
+
|
|
415
486
|
logger.info("=" * 50)
|
|
416
487
|
logger.info(" OpenCode Telegram Bot")
|
|
417
488
|
logger.info("=" * 50)
|
|
@@ -447,6 +518,7 @@ def main():
|
|
|
447
518
|
ApplicationBuilder()
|
|
448
519
|
.token(config.telegram_bot_token)
|
|
449
520
|
.request(request)
|
|
521
|
+
.get_updates_request(request)
|
|
450
522
|
.post_init(post_init)
|
|
451
523
|
.post_shutdown(post_shutdown)
|
|
452
524
|
.build()
|
|
@@ -477,6 +549,8 @@ def main():
|
|
|
477
549
|
application.add_handler(CommandHandler("delete_project", handlers["delete_project"], block=False))
|
|
478
550
|
application.add_handler(CommandHandler("enable", handlers["enable"], block=False))
|
|
479
551
|
application.add_handler(CommandHandler("disable", handlers["disable"], block=False))
|
|
552
|
+
application.add_handler(CommandHandler("history", handlers["history"], block=False))
|
|
553
|
+
application.add_handler(CommandHandler("mode", handlers["mode"], block=False))
|
|
480
554
|
application.add_handler(CommandHandler("plan", handlers["plan"], block=False))
|
|
481
555
|
application.add_handler(CommandHandler("build", handlers["build"], block=False))
|
|
482
556
|
application.add_handler(CommandHandler("share", handlers["share"], block=False))
|