telegram-opencode-bridge-bot 0.1.5__tar.gz → 0.1.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {telegram_opencode_bridge_bot-0.1.5/telegram_opencode_bridge_bot.egg-info → telegram_opencode_bridge_bot-0.1.7}/PKG-INFO +7 -3
  2. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/README.md +6 -2
  3. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/bot.py +86 -5
  4. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/handlers/commands.py +443 -1
  5. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/handlers/messages.py +222 -42
  6. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/opencode/client.py +26 -0
  7. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/opencode/server.py +19 -16
  8. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/pyproject.toml +1 -1
  9. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/sessions/manager.py +31 -1
  10. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7/telegram_opencode_bridge_bot.egg-info}/PKG-INFO +7 -3
  11. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/utils/formatting.py +151 -1
  12. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/.env.example +0 -0
  13. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/MANIFEST.in +0 -0
  14. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/config.py +0 -0
  15. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/handlers/__init__.py +0 -0
  16. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/list_session_models.py +0 -0
  17. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/opencode/__init__.py +0 -0
  18. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/requirements.txt +0 -0
  19. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/sessions/__init__.py +0 -0
  20. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/setup.cfg +0 -0
  21. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/telegram_opencode_bridge_bot.egg-info/SOURCES.txt +0 -0
  22. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/telegram_opencode_bridge_bot.egg-info/dependency_links.txt +0 -0
  23. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/telegram_opencode_bridge_bot.egg-info/entry_points.txt +0 -0
  24. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/telegram_opencode_bridge_bot.egg-info/requires.txt +0 -0
  25. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/telegram_opencode_bridge_bot.egg-info/top_level.txt +0 -0
  26. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/utils/__init__.py +0 -0
  27. {telegram_opencode_bridge_bot-0.1.5 → telegram_opencode_bridge_bot-0.1.7}/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.5
3
+ Version: 0.1.7
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
@@ -23,7 +23,7 @@ A lightweight Python bot that bridges your Telegram messages directly to [OpenCo
23
23
 
24
24
  - **Direct OpenCode integration** — routes your messages to OpenCode's HTTP API
25
25
  - **Persistent sessions** — conversations maintain context across messages
26
- - **Session management** — create, switch, list, and share sessions
26
+ - **Session management** — create, delete, switch, list, and share sessions
27
27
  - **Model switching** — change AI models on the fly (`/model`)
28
28
  - **Plan/Build modes** — toggle between read-only analysis and full execution
29
29
  - **Smart formatting** — code blocks with syntax highlighting in Telegram
@@ -31,6 +31,7 @@ A lightweight Python bot that bridges your Telegram messages directly to [OpenCo
31
31
  - **Security** — whitelist-based access control + rate limiting
32
32
  - **Workspace Switching** — Switching from one workspace to another
33
33
  - **Uploading from Mobile to WorkSpace** - U can upload the documents/images from your mobile to the opencode agent workspace
34
+ - **Workspace Management** — Creation and deletion of the workspace/folder
34
35
 
35
36
  ## 📋 Prerequisites
36
37
 
@@ -46,7 +47,7 @@ A lightweight Python bot that bridges your Telegram messages directly to [OpenCo
46
47
 
47
48
  ### Quick Installation:
48
49
  ```bash
49
- pip install telegram-opencode-bridge-bot==0.1.5
50
+ pip install telegram-opencode-bridge-bot==0.1.7
50
51
  telegram-opencode-bot
51
52
  telegram-opencode-bot --env (use --env flag if u want to re-configure later anytime)
52
53
  ```
@@ -88,6 +89,9 @@ Open Telegram, find your bot, and start asking! 🎉
88
89
  | `/project` | To view the current workspace, sub folder workspaces and to change the workspace |
89
90
  | `/enable` | To enable the streaming |
90
91
  | `/disable` | To disable the streaming |
92
+ | `/create_project` | To create new project/folder/workspace |
93
+ | `/delete_project` | To create new project/folder/workspace |
94
+ | `/delete` | To delete a conversation |
91
95
 
92
96
  ## 🏗️ Architecture
93
97
 
@@ -6,7 +6,7 @@ A lightweight Python bot that bridges your Telegram messages directly to [OpenCo
6
6
 
7
7
  - **Direct OpenCode integration** — routes your messages to OpenCode's HTTP API
8
8
  - **Persistent sessions** — conversations maintain context across messages
9
- - **Session management** — create, switch, list, and share sessions
9
+ - **Session management** — create, delete, switch, list, and share sessions
10
10
  - **Model switching** — change AI models on the fly (`/model`)
11
11
  - **Plan/Build modes** — toggle between read-only analysis and full execution
12
12
  - **Smart formatting** — code blocks with syntax highlighting in Telegram
@@ -14,6 +14,7 @@ A lightweight Python bot that bridges your Telegram messages directly to [OpenCo
14
14
  - **Security** — whitelist-based access control + rate limiting
15
15
  - **Workspace Switching** — Switching from one workspace to another
16
16
  - **Uploading from Mobile to WorkSpace** - U can upload the documents/images from your mobile to the opencode agent workspace
17
+ - **Workspace Management** — Creation and deletion of the workspace/folder
17
18
 
18
19
  ## 📋 Prerequisites
19
20
 
@@ -29,7 +30,7 @@ A lightweight Python bot that bridges your Telegram messages directly to [OpenCo
29
30
 
30
31
  ### Quick Installation:
31
32
  ```bash
32
- pip install telegram-opencode-bridge-bot==0.1.5
33
+ pip install telegram-opencode-bridge-bot==0.1.7
33
34
  telegram-opencode-bot
34
35
  telegram-opencode-bot --env (use --env flag if u want to re-configure later anytime)
35
36
  ```
@@ -71,6 +72,9 @@ Open Telegram, find your bot, and start asking! 🎉
71
72
  | `/project` | To view the current workspace, sub folder workspaces and to change the workspace |
72
73
  | `/enable` | To enable the streaming |
73
74
  | `/disable` | To disable the streaming |
75
+ | `/create_project` | To create new project/folder/workspace |
76
+ | `/delete_project` | To create new project/folder/workspace |
77
+ | `/delete` | To delete a conversation |
74
78
 
75
79
  ## 🏗️ Architecture
76
80
 
@@ -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,
@@ -41,6 +45,7 @@ from handlers.commands import (
41
45
  help_command,
42
46
  new_command,
43
47
  sessions_command,
48
+ delete_command,
44
49
  plan_command,
45
50
  build_command,
46
51
  share_command,
@@ -49,6 +54,8 @@ from handlers.commands import (
49
54
  models_command,
50
55
  stop_command,
51
56
  project_command,
57
+ create_project_command,
58
+ delete_project_command,
52
59
  enable_command,
53
60
  disable_command,
54
61
  set_bot_commands,
@@ -64,6 +71,43 @@ logging.basicConfig(
64
71
  )
65
72
  logger = logging.getLogger("opencode-telegram-bot")
66
73
 
74
+ _lock_file = None
75
+
76
+ def acquire_bot_lock():
77
+ """Acquire an exclusive lock file to prevent multiple instances from running concurrently."""
78
+ global _lock_file
79
+ lock_path = os.path.join(os.path.abspath("."), "bot.lock")
80
+ try:
81
+ _lock_file = open(lock_path, "w")
82
+ if os.name == 'nt':
83
+ import msvcrt
84
+ try:
85
+ _lock_file.seek(0)
86
+ msvcrt.locking(_lock_file.fileno(), msvcrt.LK_NBLCK, 1)
87
+ _lock_file.write(str(os.getpid()))
88
+ _lock_file.flush()
89
+ except (OSError, IOError):
90
+ print("\n" + "="*65)
91
+ print("❌ ERROR: Another instance of the Telegram bot is already running!")
92
+ print("Please close the other terminal or kill the stray Python process.")
93
+ print("="*65 + "\n")
94
+ sys.exit(1)
95
+ else:
96
+ import fcntl
97
+ try:
98
+ fcntl.flock(_lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
99
+ _lock_file.write(str(os.getpid()))
100
+ _lock_file.flush()
101
+ except (OSError, IOError):
102
+ print("\n" + "="*65)
103
+ print("❌ ERROR: Another instance of the Telegram bot is already running!")
104
+ print("Please close the other terminal or kill the stray Python process.")
105
+ print("="*65 + "\n")
106
+ sys.exit(1)
107
+ except Exception as e:
108
+ logger.warning(f"Could not acquire bot lock: {e}")
109
+
110
+
67
111
 
68
112
  class RetryingHTTPXRequest(HTTPXRequest):
69
113
  """Custom HTTPXRequest that automatically retries failed requests on connection timeouts/errors."""
@@ -105,6 +149,10 @@ def build_authorized_handlers(authorizer: UserAuthorizer, rate_limiter: RateLimi
105
149
  async def _sessions(update, context):
106
150
  await sessions_command(update, context)
107
151
 
152
+ @authorized(authorizer, rate_limiter)
153
+ async def _delete(update, context):
154
+ await delete_command(update, context)
155
+
108
156
  @authorized(authorizer, rate_limiter)
109
157
  async def _plan(update, context):
110
158
  await plan_command(update, context)
@@ -137,6 +185,14 @@ def build_authorized_handlers(authorizer: UserAuthorizer, rate_limiter: RateLimi
137
185
  async def _project(update, context):
138
186
  await project_command(update, context)
139
187
 
188
+ @authorized(authorizer, rate_limiter)
189
+ async def _create_project(update, context):
190
+ await create_project_command(update, context)
191
+
192
+ @authorized(authorizer, rate_limiter)
193
+ async def _delete_project(update, context):
194
+ await delete_project_command(update, context)
195
+
140
196
  @authorized(authorizer, rate_limiter)
141
197
  async def _enable(update, context):
142
198
  await enable_command(update, context)
@@ -162,9 +218,12 @@ def build_authorized_handlers(authorizer: UserAuthorizer, rate_limiter: RateLimi
162
218
  "help": _help,
163
219
  "new": _new,
164
220
  "sessions": _sessions,
221
+ "delete": _delete,
165
222
  "models": _models,
166
223
  "stop": _stop,
167
224
  "project": _project,
225
+ "create_project": _create_project,
226
+ "delete_project": _delete_project,
168
227
  "enable": _enable,
169
228
  "disable": _disable,
170
229
  "plan": _plan,
@@ -180,8 +239,17 @@ def build_authorized_handlers(authorizer: UserAuthorizer, rate_limiter: RateLimi
180
239
 
181
240
  async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
182
241
  """Log the error and send a Telegram message to notify the user."""
183
- # Log the error with traceback
184
- logger.error("Exception while handling an update:", exc_info=context.error)
242
+ # Suppress full traceback for common transient network / timeout errors to keep logs clean
243
+ from telegram.error import NetworkError, TimedOut
244
+
245
+ err = context.error
246
+ err_str = str(err).lower()
247
+ if isinstance(err, (NetworkError, TimedOut)) or "httpx" in err_str or "httpcore" in err_str or "read error" in err_str:
248
+ logger.warning(f"📡 Transient Telegram network/timeout error: {err}")
249
+ return
250
+
251
+ # Log unexpected errors with traceback
252
+ logger.error("Exception while handling an update:", exc_info=err)
185
253
 
186
254
  # Notify the user if the update is a Telegram Update with a message
187
255
  if isinstance(update, Update) and update.effective_message:
@@ -215,19 +283,25 @@ async def post_shutdown(application) -> None:
215
283
  # Stop the background opencode server process if running
216
284
  try:
217
285
  from opencode.server import stop_server
218
- await stop_server()
286
+ await asyncio.wait_for(stop_server(), timeout=8.0)
219
287
  except Exception as e:
220
288
  logger.warning(f"Failed to stop background OpenCode server: {e}")
221
289
 
222
290
  # Close session manager DB
223
291
  session_mgr = application.bot_data.get("session_manager")
224
292
  if session_mgr:
225
- await session_mgr.close()
293
+ try:
294
+ await asyncio.wait_for(session_mgr.close(), timeout=3.0)
295
+ except Exception as e:
296
+ logger.warning(f"Failed to close session manager: {e}")
226
297
 
227
298
  # Close HTTP client
228
299
  oc_client = application.bot_data.get("opencode_client")
229
300
  if oc_client:
230
- await oc_client.close()
301
+ try:
302
+ await asyncio.wait_for(oc_client.close(), timeout=3.0)
303
+ except Exception as e:
304
+ logger.warning(f"Failed to close HTTP client: {e}")
231
305
 
232
306
  logger.info("Goodbye!")
233
307
 
@@ -394,6 +468,9 @@ def main():
394
468
  logger.error("Copy .env.example → .env and fill in your values.")
395
469
  sys.exit(1)
396
470
 
471
+ # ── Acquire exclusive bot lock to prevent concurrent instances ───────
472
+ acquire_bot_lock()
473
+
397
474
  logger.info("=" * 50)
398
475
  logger.info(" OpenCode Telegram Bot")
399
476
  logger.info("=" * 50)
@@ -429,6 +506,7 @@ def main():
429
506
  ApplicationBuilder()
430
507
  .token(config.telegram_bot_token)
431
508
  .request(request)
509
+ .get_updates_request(request)
432
510
  .post_init(post_init)
433
511
  .post_shutdown(post_shutdown)
434
512
  .build()
@@ -451,9 +529,12 @@ def main():
451
529
  application.add_handler(CommandHandler("help", handlers["help"], block=False))
452
530
  application.add_handler(CommandHandler("new", handlers["new"], block=False))
453
531
  application.add_handler(CommandHandler("sessions", handlers["sessions"], block=False))
532
+ application.add_handler(CommandHandler("delete", handlers["delete"], block=False))
454
533
  application.add_handler(CommandHandler("models", handlers["models"], block=False))
455
534
  application.add_handler(CommandHandler("stop", handlers["stop"], block=False))
456
535
  application.add_handler(CommandHandler("project", handlers["project"], block=False))
536
+ application.add_handler(CommandHandler("create_project", handlers["create_project"], block=False))
537
+ application.add_handler(CommandHandler("delete_project", handlers["delete_project"], block=False))
457
538
  application.add_handler(CommandHandler("enable", handlers["enable"], block=False))
458
539
  application.add_handler(CommandHandler("disable", handlers["disable"], block=False))
459
540
  application.add_handler(CommandHandler("plan", handlers["plan"], block=False))