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.
Files changed (28) hide show
  1. {telegram_opencode_bridge_bot-0.1.4/telegram_opencode_bridge_bot.egg-info → telegram_opencode_bridge_bot-0.1.5}/PKG-INFO +5 -3
  2. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/README.md +4 -2
  3. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/bot.py +27 -6
  4. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/handlers/commands.py +71 -29
  5. telegram_opencode_bridge_bot-0.1.5/handlers/messages.py +818 -0
  6. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/opencode/client.py +48 -0
  7. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/pyproject.toml +1 -1
  8. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5/telegram_opencode_bridge_bot.egg-info}/PKG-INFO +5 -3
  9. telegram_opencode_bridge_bot-0.1.4/handlers/messages.py +0 -482
  10. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/.env.example +0 -0
  11. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/MANIFEST.in +0 -0
  12. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/config.py +0 -0
  13. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/handlers/__init__.py +0 -0
  14. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/list_session_models.py +0 -0
  15. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/opencode/__init__.py +0 -0
  16. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/opencode/server.py +0 -0
  17. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/requirements.txt +0 -0
  18. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/sessions/__init__.py +0 -0
  19. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/sessions/manager.py +0 -0
  20. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/setup.cfg +0 -0
  21. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/telegram_opencode_bridge_bot.egg-info/SOURCES.txt +0 -0
  22. {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
  23. {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
  24. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/telegram_opencode_bridge_bot.egg-info/requires.txt +0 -0
  25. {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
  26. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/utils/__init__.py +0 -0
  27. {telegram_opencode_bridge_bot-0.1.4 → telegram_opencode_bridge_bot-0.1.5}/utils/formatting.py +0 -0
  28. {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.4
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.4
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
- | `/mode <plan\|build>` | Toggle plan/build mode |
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.4
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
- | `/mode <plan\|build>` | Toggle plan/build mode |
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
- mode_command,
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 _mode(update, context):
109
- await mode_command(update, context)
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
- "mode": _mode,
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("mode", handlers["mode"], block=False))
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(
@@ -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
- "/mode <code>&lt;plan|build&gt;</code>Toggle plan/build mode\n"
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
- # Command: /mode <plan|build>
369
+ # Commands: /plan and /build
369
370
  # ──────────────────────────────────────────────
370
- async def mode_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
371
- """Switch between plan and build mode."""
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>&lt;plan|build&gt;</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
- f"{emoji} Mode changed to <b>{mode}</b>",
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("mode", "Toggle plan/build mode"),
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:"):]