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.
- {telegram_opencode_bridge_bot-0.1.0/telegram_opencode_bridge_bot.egg-info → telegram_opencode_bridge_bot-0.1.2}/PKG-INFO +5 -15
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/README.md +129 -139
- telegram_opencode_bridge_bot-0.1.2/bot.py +430 -0
- telegram_opencode_bridge_bot-0.1.2/config.py +92 -0
- telegram_opencode_bridge_bot-0.1.2/list_session_models.py +44 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/pyproject.toml +6 -3
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2/telegram_opencode_bridge_bot.egg-info}/PKG-INFO +5 -15
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/telegram_opencode_bridge_bot.egg-info/SOURCES.txt +3 -0
- {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
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/.env.example +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/MANIFEST.in +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/handlers/__init__.py +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/handlers/commands.py +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/handlers/messages.py +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/opencode/__init__.py +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/opencode/client.py +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/opencode/server.py +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/requirements.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/sessions/__init__.py +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/sessions/manager.py +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/setup.cfg +0 -0
- {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
- {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
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/telegram_opencode_bridge_bot.egg-info/requires.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/utils/__init__.py +0 -0
- {telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/utils/formatting.py +0 -0
- {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.
|
|
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 <
|
|
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.
|
|
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
|
|
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
|
-
###
|
|
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.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
|
63
|
-
|
|
64
|
-
| `/
|
|
65
|
-
| `/
|
|
66
|
-
| `/
|
|
67
|
-
| `/
|
|
68
|
-
| `/
|
|
69
|
-
| `/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
├──
|
|
123
|
-
├──
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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.
|
|
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 = "
|
|
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.
|
|
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 <
|
|
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.
|
|
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
|
|
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
|
-
###
|
|
63
|
+
### 3. Chat!
|
|
74
64
|
|
|
75
65
|
Open Telegram, find your bot, and start asking! 🎉
|
|
76
66
|
|
|
File without changes
|
|
File without changes
|
{telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/handlers/__init__.py
RENAMED
|
File without changes
|
{telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/handlers/commands.py
RENAMED
|
File without changes
|
{telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/handlers/messages.py
RENAMED
|
File without changes
|
{telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/opencode/__init__.py
RENAMED
|
File without changes
|
{telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/opencode/client.py
RENAMED
|
File without changes
|
{telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/opencode/server.py
RENAMED
|
File without changes
|
|
File without changes
|
{telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/sessions/__init__.py
RENAMED
|
File without changes
|
{telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/sessions/manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{telegram_opencode_bridge_bot-0.1.0 → telegram_opencode_bridge_bot-0.1.2}/utils/formatting.py
RENAMED
|
File without changes
|
|
File without changes
|