telegram-opencode-bridge-bot 0.1.4__tar.gz → 0.1.5__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.4/telegram_opencode_bridge_bot.egg-info → telegram_opencode_bridge_bot-0.1.5}/PKG-INFO +5 -3
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/README.md +4 -2
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/bot.py +27 -6
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/handlers/commands.py +71 -29
- telegram_opencode_bridge_bot-0.1.5/handlers/messages.py +818 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/opencode/client.py +48 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/pyproject.toml +1 -1
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5/telegram_opencode_bridge_bot.egg-info}/PKG-INFO +5 -3
- telegram_opencode_bridge_bot-0.1.4/handlers/messages.py +0 -482
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/.env.example +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/MANIFEST.in +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/config.py +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/handlers/__init__.py +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/list_session_models.py +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/opencode/__init__.py +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/opencode/server.py +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/requirements.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/sessions/__init__.py +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/sessions/manager.py +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/setup.cfg +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/telegram_opencode_bridge_bot.egg-info/SOURCES.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/telegram_opencode_bridge_bot.egg-info/dependency_links.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/telegram_opencode_bridge_bot.egg-info/entry_points.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/telegram_opencode_bridge_bot.egg-info/requires.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/telegram_opencode_bridge_bot.egg-info/top_level.txt +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/utils/__init__.py +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/utils/formatting.py +0 -0
- {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/utils/security.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: telegram-opencode-bridge-bot
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: A Telegram bot that bridges messages directly to OpenCode — an AI coding agent running on your machine
|
|
5
5
|
Author-email: MaheshNagabhairava <maheshnagabhirava12345@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -30,6 +30,7 @@ A lightweight Python bot that bridges your Telegram messages directly to [OpenCo
|
|
|
30
30
|
- **Auto message splitting** — handles responses longer than Telegram's 4096 char limit
|
|
31
31
|
- **Security** — whitelist-based access control + rate limiting
|
|
32
32
|
- **Workspace Switching** — Switching from one workspace to another
|
|
33
|
+
- **Uploading from Mobile to WorkSpace** - U can upload the documents/images from your mobile to the opencode agent workspace
|
|
33
34
|
|
|
34
35
|
## 📋 Prerequisites
|
|
35
36
|
|
|
@@ -45,7 +46,7 @@ A lightweight Python bot that bridges your Telegram messages directly to [OpenCo
|
|
|
45
46
|
|
|
46
47
|
### Quick Installation:
|
|
47
48
|
```bash
|
|
48
|
-
pip install telegram-opencode-bridge-bot==0.1.
|
|
49
|
+
pip install telegram-opencode-bridge-bot==0.1.5
|
|
49
50
|
telegram-opencode-bot
|
|
50
51
|
telegram-opencode-bot --env (use --env flag if u want to re-configure later anytime)
|
|
51
52
|
```
|
|
@@ -77,7 +78,8 @@ Open Telegram, find your bot, and start asking! 🎉
|
|
|
77
78
|
| `/help` | Show all commands |
|
|
78
79
|
| `/new` | Start a fresh conversation |
|
|
79
80
|
| `/sessions` | List your recent sessions/conversations and to select a session/conversation |
|
|
80
|
-
| `/
|
|
81
|
+
| `/plan` | plan mode |
|
|
82
|
+
| `/build` | build mode |
|
|
81
83
|
| `/share` | Share current session/conversation (public URL) |
|
|
82
84
|
| `/status` | Check connection & session details |
|
|
83
85
|
| `/id` | Show your Telegram user ID |
|
|
@@ -13,6 +13,7 @@ A lightweight Python bot that bridges your Telegram messages directly to [OpenCo
|
|
|
13
13
|
- **Auto message splitting** — handles responses longer than Telegram's 4096 char limit
|
|
14
14
|
- **Security** — whitelist-based access control + rate limiting
|
|
15
15
|
- **Workspace Switching** — Switching from one workspace to another
|
|
16
|
+
- **Uploading from Mobile to WorkSpace** - U can upload the documents/images from your mobile to the opencode agent workspace
|
|
16
17
|
|
|
17
18
|
## 📋 Prerequisites
|
|
18
19
|
|
|
@@ -28,7 +29,7 @@ A lightweight Python bot that bridges your Telegram messages directly to [OpenCo
|
|
|
28
29
|
|
|
29
30
|
### Quick Installation:
|
|
30
31
|
```bash
|
|
31
|
-
pip install telegram-opencode-bridge-bot==0.1.
|
|
32
|
+
pip install telegram-opencode-bridge-bot==0.1.5
|
|
32
33
|
telegram-opencode-bot
|
|
33
34
|
telegram-opencode-bot --env (use --env flag if u want to re-configure later anytime)
|
|
34
35
|
```
|
|
@@ -60,7 +61,8 @@ Open Telegram, find your bot, and start asking! 🎉
|
|
|
60
61
|
| `/help` | Show all commands |
|
|
61
62
|
| `/new` | Start a fresh conversation |
|
|
62
63
|
| `/sessions` | List your recent sessions/conversations and to select a session/conversation |
|
|
63
|
-
| `/
|
|
64
|
+
| `/plan` | plan mode |
|
|
65
|
+
| `/build` | build mode |
|
|
64
66
|
| `/share` | Share current session/conversation (public URL) |
|
|
65
67
|
| `/status` | Check connection & session details |
|
|
66
68
|
| `/id` | Show your Telegram user ID |
|
|
@@ -41,7 +41,8 @@ from handlers.commands import (
|
|
|
41
41
|
help_command,
|
|
42
42
|
new_command,
|
|
43
43
|
sessions_command,
|
|
44
|
-
|
|
44
|
+
plan_command,
|
|
45
|
+
build_command,
|
|
45
46
|
share_command,
|
|
46
47
|
status_command,
|
|
47
48
|
id_command,
|
|
@@ -53,7 +54,7 @@ from handlers.commands import (
|
|
|
53
54
|
set_bot_commands,
|
|
54
55
|
callback_handler,
|
|
55
56
|
)
|
|
56
|
-
from handlers.messages import handle_message
|
|
57
|
+
from handlers.messages import handle_message, handle_document
|
|
57
58
|
|
|
58
59
|
# ── Logging Setup ──────────────────────────────────────────
|
|
59
60
|
logging.basicConfig(
|
|
@@ -105,8 +106,12 @@ def build_authorized_handlers(authorizer: UserAuthorizer, rate_limiter: RateLimi
|
|
|
105
106
|
await sessions_command(update, context)
|
|
106
107
|
|
|
107
108
|
@authorized(authorizer, rate_limiter)
|
|
108
|
-
async def
|
|
109
|
-
await
|
|
109
|
+
async def _plan(update, context):
|
|
110
|
+
await plan_command(update, context)
|
|
111
|
+
|
|
112
|
+
@authorized(authorizer, rate_limiter)
|
|
113
|
+
async def _build(update, context):
|
|
114
|
+
await build_command(update, context)
|
|
110
115
|
|
|
111
116
|
@authorized(authorizer, rate_limiter)
|
|
112
117
|
async def _share(update, context):
|
|
@@ -148,6 +153,10 @@ def build_authorized_handlers(authorizer: UserAuthorizer, rate_limiter: RateLimi
|
|
|
148
153
|
async def _message(update, context):
|
|
149
154
|
await handle_message(update, context)
|
|
150
155
|
|
|
156
|
+
@authorized(authorizer, rate_limiter)
|
|
157
|
+
async def _document(update, context):
|
|
158
|
+
await handle_document(update, context)
|
|
159
|
+
|
|
151
160
|
return {
|
|
152
161
|
"start": _start,
|
|
153
162
|
"help": _help,
|
|
@@ -158,12 +167,14 @@ def build_authorized_handlers(authorizer: UserAuthorizer, rate_limiter: RateLimi
|
|
|
158
167
|
"project": _project,
|
|
159
168
|
"enable": _enable,
|
|
160
169
|
"disable": _disable,
|
|
161
|
-
"
|
|
170
|
+
"plan": _plan,
|
|
171
|
+
"build": _build,
|
|
162
172
|
"share": _share,
|
|
163
173
|
"status": _status,
|
|
164
174
|
"id": _id,
|
|
165
175
|
"callback": _callback,
|
|
166
176
|
"message": _message,
|
|
177
|
+
"document": _document,
|
|
167
178
|
}
|
|
168
179
|
|
|
169
180
|
|
|
@@ -445,7 +456,8 @@ def main():
|
|
|
445
456
|
application.add_handler(CommandHandler("project", handlers["project"], block=False))
|
|
446
457
|
application.add_handler(CommandHandler("enable", handlers["enable"], block=False))
|
|
447
458
|
application.add_handler(CommandHandler("disable", handlers["disable"], block=False))
|
|
448
|
-
application.add_handler(CommandHandler("
|
|
459
|
+
application.add_handler(CommandHandler("plan", handlers["plan"], block=False))
|
|
460
|
+
application.add_handler(CommandHandler("build", handlers["build"], block=False))
|
|
449
461
|
application.add_handler(CommandHandler("share", handlers["share"], block=False))
|
|
450
462
|
application.add_handler(CommandHandler("status", handlers["status"], block=False))
|
|
451
463
|
application.add_handler(CommandHandler("id", handlers["id"], block=False))
|
|
@@ -462,6 +474,15 @@ def main():
|
|
|
462
474
|
)
|
|
463
475
|
)
|
|
464
476
|
|
|
477
|
+
# ── Register document handler (catches file uploads) ──
|
|
478
|
+
application.add_handler(
|
|
479
|
+
MessageHandler(
|
|
480
|
+
(filters.Document.ALL | filters.PHOTO) & ~filters.COMMAND,
|
|
481
|
+
handlers["document"],
|
|
482
|
+
block=False,
|
|
483
|
+
)
|
|
484
|
+
)
|
|
485
|
+
|
|
465
486
|
# ── Start polling ─────────────────────────────────────
|
|
466
487
|
logger.info("Starting bot with long polling...")
|
|
467
488
|
application.run_polling(
|
{telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/handlers/commands.py
RENAMED
|
@@ -60,7 +60,8 @@ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
|
|
|
60
60
|
"/disable — Disable live progress streaming\n"
|
|
61
61
|
"/sessions — List your recent sessions (tap to switch)\n"
|
|
62
62
|
"/models — List all available models (tap to change)\n"
|
|
63
|
-
"/
|
|
63
|
+
"/plan — Switch to plan mode (read-only)\n"
|
|
64
|
+
"/build — Switch to build mode (read, write, execute)\n"
|
|
64
65
|
"/share — Share current session (get public URL)\n"
|
|
65
66
|
"/status — Show bot & connection status\n"
|
|
66
67
|
"/id — Show your Telegram user ID\n\n"
|
|
@@ -365,39 +366,28 @@ async def switch_command(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
|
|
365
366
|
|
|
366
367
|
|
|
367
368
|
# ──────────────────────────────────────────────
|
|
368
|
-
#
|
|
369
|
+
# Commands: /plan and /build
|
|
369
370
|
# ──────────────────────────────────────────────
|
|
370
|
-
async def
|
|
371
|
-
"""Switch
|
|
371
|
+
async def plan_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|
372
|
+
"""Switch the current session to plan mode (read-only)."""
|
|
372
373
|
user_id = update.effective_user.id
|
|
373
374
|
session_mgr = context.bot_data["session_manager"]
|
|
375
|
+
await session_mgr.set_mode(user_id, "plan")
|
|
376
|
+
await update.message.reply_text(
|
|
377
|
+
"📋 Mode changed to <b>plan</b>\n\n"
|
|
378
|
+
"<i>OpenCode is now in read-only analysis mode (no file modifications).</i>",
|
|
379
|
+
parse_mode="HTML",
|
|
380
|
+
)
|
|
374
381
|
|
|
375
|
-
if not context.args:
|
|
376
|
-
current_info = await session_mgr.get_session_info(user_id)
|
|
377
|
-
current_mode = (current_info or {}).get("mode", "build")
|
|
378
|
-
|
|
379
|
-
await update.message.reply_text(
|
|
380
|
-
f"⚙️ <b>Current mode:</b> {current_mode}\n\n"
|
|
381
|
-
f"<b>Usage:</b> /mode <code><plan|build></code>\n\n"
|
|
382
|
-
f"• <b>build</b> — OpenCode can read, write, and execute\n"
|
|
383
|
-
f"• <b>plan</b> — Read-only analysis, no file modifications",
|
|
384
|
-
parse_mode="HTML",
|
|
385
|
-
)
|
|
386
|
-
return
|
|
387
|
-
|
|
388
|
-
mode = context.args[0].lower()
|
|
389
|
-
if mode not in ("plan", "build"):
|
|
390
|
-
await update.message.reply_text(
|
|
391
|
-
"❌ Invalid mode. Use <code>plan</code> or <code>build</code>.",
|
|
392
|
-
parse_mode="HTML",
|
|
393
|
-
)
|
|
394
|
-
return
|
|
395
|
-
|
|
396
|
-
await session_mgr.set_mode(user_id, mode)
|
|
397
|
-
emoji = "📋" if mode == "plan" else "🔨"
|
|
398
382
|
|
|
383
|
+
async def build_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|
384
|
+
"""Switch the current session to build mode (read, write, execute)."""
|
|
385
|
+
user_id = update.effective_user.id
|
|
386
|
+
session_mgr = context.bot_data["session_manager"]
|
|
387
|
+
await session_mgr.set_mode(user_id, "build")
|
|
399
388
|
await update.message.reply_text(
|
|
400
|
-
|
|
389
|
+
"🔨 Mode changed to <b>build</b>\n\n"
|
|
390
|
+
"<i>OpenCode can now read, write files, and execute commands.</i>",
|
|
401
391
|
parse_mode="HTML",
|
|
402
392
|
)
|
|
403
393
|
|
|
@@ -881,7 +871,8 @@ async def set_bot_commands(app) -> None:
|
|
|
881
871
|
BotCommand("disable", "Disable live progress streaming"),
|
|
882
872
|
BotCommand("sessions", "List your sessions"),
|
|
883
873
|
BotCommand("models", "List all available models"),
|
|
884
|
-
BotCommand("
|
|
874
|
+
BotCommand("plan", "Switch to plan mode (read-only)"),
|
|
875
|
+
BotCommand("build", "Switch to build mode (read, write, execute)"),
|
|
885
876
|
BotCommand("share", "Share current session"),
|
|
886
877
|
BotCommand("status", "Bot & connection status"),
|
|
887
878
|
BotCommand("id", "Show your Telegram user ID"),
|
|
@@ -1013,6 +1004,57 @@ async def callback_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -
|
|
|
1013
1004
|
parse_mode="HTML",
|
|
1014
1005
|
)
|
|
1015
1006
|
|
|
1007
|
+
# 1.5 Handle Sensitive Operations / Tool Permissions
|
|
1008
|
+
elif data.startswith("perm:"):
|
|
1009
|
+
parts = data.split(":")
|
|
1010
|
+
if len(parts) == 3:
|
|
1011
|
+
action = parts[1] # "allow" or "deny"
|
|
1012
|
+
short_key = parts[2] # 8-char lookup key
|
|
1013
|
+
|
|
1014
|
+
pending_perms = bot_data.get("pending_permissions", {})
|
|
1015
|
+
pending = pending_perms.get(short_key)
|
|
1016
|
+
|
|
1017
|
+
if not pending:
|
|
1018
|
+
await query.edit_message_text(
|
|
1019
|
+
text=f"{query.message.text}\n\n⚠️ <b>Request Expired:</b> This permission prompt is no longer valid or the bot was restarted.",
|
|
1020
|
+
parse_mode="HTML"
|
|
1021
|
+
)
|
|
1022
|
+
return
|
|
1023
|
+
|
|
1024
|
+
session_id = pending["session_id"]
|
|
1025
|
+
permission_id = pending["permission_id"]
|
|
1026
|
+
|
|
1027
|
+
response_value = "once" if action == "allow" else "reject"
|
|
1028
|
+
|
|
1029
|
+
try:
|
|
1030
|
+
# Call our client's respond_to_permission method
|
|
1031
|
+
success = await oc_client.respond_to_permission(
|
|
1032
|
+
session_id=session_id,
|
|
1033
|
+
permission_id=permission_id,
|
|
1034
|
+
response=response_value,
|
|
1035
|
+
remember=False
|
|
1036
|
+
)
|
|
1037
|
+
|
|
1038
|
+
# Delete from registry
|
|
1039
|
+
pending_perms.pop(short_key, None)
|
|
1040
|
+
|
|
1041
|
+
# Format final notification status
|
|
1042
|
+
if response_value == "once":
|
|
1043
|
+
status_text = "✅ <b>Approved:</b> The agent was allowed to perform this operation."
|
|
1044
|
+
else:
|
|
1045
|
+
status_text = "❌ <b>Rejected:</b> The agent was denied permission to perform this operation."
|
|
1046
|
+
|
|
1047
|
+
await query.edit_message_text(
|
|
1048
|
+
text=f"{query.message.text}\n\n{status_text}",
|
|
1049
|
+
parse_mode="HTML"
|
|
1050
|
+
)
|
|
1051
|
+
except Exception as e:
|
|
1052
|
+
logger.error(f"Error responding to permission {permission_id} in callback: {e}", exc_info=True)
|
|
1053
|
+
await query.edit_message_text(
|
|
1054
|
+
text=f"{query.message.text}\n\n⚠️ <b>Error:</b> Failed to submit decision to OpenCode server: {e}",
|
|
1055
|
+
parse_mode="HTML"
|
|
1056
|
+
)
|
|
1057
|
+
|
|
1016
1058
|
# 2. Switch Model tap
|
|
1017
1059
|
elif data.startswith("model:"):
|
|
1018
1060
|
new_model = data[len("model:"):]
|