Refactor: Modular Telegram Bot v4.0 — async DB, proxy service, speed test, analytics - #3
Conversation
… separated concerns - Extract database layer to bot/database.py (async via aiosqlite) - Split monolithic script into 12 focused modules - Add proxy service: HTTP/HTTPS/SOCKS4/SOCKS5 aggregation from 7+ sources - Add proxy REST API endpoint at /api/proxies - Persistent DB connection with WAL mode and 8MB cache - Guard decorator for centralized auth, rate limiting, ban checking - Queue-based media download system with progress tracking - Periodic proxy refresh and daily report jobs - Admin proxy management panel - .gitignore, requirements.txt, .env.example, README.md Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| f"*Referral success!*\n\n" | ||
| f"*{user.full_name or user.username or 'New user'}* joined via your link!\n" | ||
| f"*{REFERRAL_BONUS:,}* added to your wallet\n" | ||
| f"Balance: *{(referrer['balance'] + REFERRAL_BONUS):,}*", |
There was a problem hiding this comment.
🔴 Referral balance display double-counts the bonus
In cmd_start, after create_user returns, the referrer's balance is fetched via db.get_user(referred_by) at line 30. The create_user method (bot/database.py:277-279) already incremented the referrer's balance in the DB before committing at line 293. So referrer['balance'] already includes the REFERRAL_BONUS. However, line 37 displays referrer['balance'] + REFERRAL_BONUS, effectively double-counting the bonus and showing the referrer a balance that is REFERRAL_BONUS higher than their actual wallet balance.
| f"Balance: *{(referrer['balance'] + REFERRAL_BONUS):,}*", | |
| f"Balance: *{referrer['balance']:,}*", |
Was this helpful? React with 👍 or 👎 to provide feedback.
| new_price = int(original * (100 - disc["percent"]) / 100) | ||
| context.user_data["vip_price"] = new_price | ||
| context.user_data["discount_code"] = code | ||
| await db.use_discount(code) |
There was a problem hiding this comment.
🔴 Discount code usage count incremented before payment is made
In handle_discount_code, db.use_discount(code) is called at line 268, which increments the discount code's used_count in the database. However, at this point the user has only applied the discount — they haven't submitted a payment receipt yet (the next state is STATE_WAITING_RECEIPT). If the user cancels the conversation, the discount code's usage count was already permanently incremented. This can exhaust a code's max_uses limit without any actual purchases, effectively making the discount code unavailable to legitimate users.
Prompt for agents
The discount code's used_count is incremented in handle_discount_code (bot/handlers/main_menu.py:268) when the user applies the code, but the payment hasn't been submitted yet. If the user cancels, the code is wasted.
The fix should move the db.use_discount(code) call to handle_receipt (bot/handlers/main_menu.py:222) after the payment is successfully created. The discount code is already stored in context.user_data['discount_code'] at line 267, so it can be consumed in handle_receipt. Additionally, consider calling use_discount in confirm_payment (database.py) when the payment is actually confirmed by an admin, to be truly safe against receipt rejections.
Was this helpful? React with 👍 or 👎 to provide feedback.
| """REST API endpoint for proxy list.""" | ||
| db = request.app["db"] | ||
| protocol = request.query.get("protocol") | ||
| limit = min(int(request.query.get("limit", "50")), 200) |
There was a problem hiding this comment.
🟡 Unhandled ValueError in proxy API when limit parameter is non-numeric
The proxy_api_handler at line 61 calls int(request.query.get("limit", "50")) without any exception handling. If a client sends a request like /api/proxies?limit=abc, this raises a ValueError which propagates as an unhandled 500 error. This is a public-facing REST API endpoint.
| limit = min(int(request.query.get("limit", "50")), 200) | |
| try: | |
| limit = min(int(request.query.get("limit", "50")), 200) | |
| except (ValueError, TypeError): | |
| limit = 50 |
Was this helpful? React with 👍 or 👎 to provide feedback.
| app = web.Application() | ||
| app["bot"] = bot | ||
| app["db"] = db | ||
| app["http_client"] = aiohttp.ClientSession() |
There was a problem hiding this comment.
🟡 aiohttp ClientSession created in web server is never closed
In start_web_server at line 71, an aiohttp.ClientSession() is created and stored in app["http_client"], but no cleanup handler is registered to close it on shutdown. This causes resource leaks (unclosed connector/session) and will emit ResourceWarning messages. The session should be closed via app.on_cleanup or app.on_shutdown signal.
| app["http_client"] = aiohttp.ClientSession() | |
| app["http_client"] = aiohttp.ClientSession() | |
| async def close_session(app): | |
| await app["http_client"].close() | |
| app.on_cleanup.append(close_session) |
Was this helpful? React with 👍 or 👎 to provide feedback.
| if row: | ||
| await self._execute( | ||
| "UPDATE file_links SET download_count=download_count+1 WHERE file_unique_id=?", | ||
| (unique_id,), | ||
| ) |
There was a problem hiding this comment.
🟡 File download_count incremented even when file is expired
The db.get_file() method (bot/database.py:527-536) unconditionally increments download_count whenever a file record is found. Both stream_handler (bot/server.py:23) and cmd_start (bot/handlers/start.py:45) call db.get_file() and only check expiry afterwards. This means every access to an expired file link still inflates the download counter, producing inaccurate download statistics.
Prompt for agents
The get_file method in bot/database.py:527-536 increments download_count as a side effect before the caller can verify the file hasn't expired. The callers (bot/server.py stream_handler and bot/handlers/start.py cmd_start) check expiry after calling get_file.
Two approaches to fix:
1. Split get_file into a read-only lookup method and a separate increment_download_count method. Let callers call the increment only after verifying the file is valid and not expired.
2. Move the expiry check into get_file itself, returning None for expired files without incrementing the count.
Was this helpful? React with 👍 or 👎 to provide feedback.
…tics Bug fixes (Devin Review): - Fix referral balance double-count in start handler - Fix discount code usage before payment (moved to receipt submit) - Fix unhandled ValueError in proxy API limit param - Fix aiohttp ClientSession leak in web server - Fix file download_count increment on expired files New features: - Proxy checker: user sends ip:port, bot validates live - Speed test: download speed test via Cloudflare/Hetzner - Admin wallet charge: manually add balance to any user - Enhanced analytics: weekly breakdown, top actions, chart in full stats - New conversation states for proxy check and wallet charge Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Test Results — Integration TestingRan shell-based integration tests against the refactored bot (no BOT_TOKEN available, so Telegram-specific flows untested). Result: 44/44 assertions passed across 8 test areas. Bug Fix Verification (5/5 verified)
New Feature Verification (4/4 verified)
Infrastructure Tests
Untested (requires BOT_TOKEN)
|
Summary
Rewrites 2775-line monolithic bot (
unzjxjxj.py) into 12 focused modules with async database, new features, and bug fixes.Architecture:
bot/database.py— async SQLite viaaiosqlite, persistent connection, WAL mode, 8MB cachebot/handlers/— 6 handler modules separated by domain (start, menu, youtube, file, proxy, admin)bot/server.py— aiohttp web server for file streaming + proxy REST APIbot/decorators.py—@guard(perm=...)for centralized auth/rate-limit/ban checksNew features:
/api/proxiesip:port, bot validates liveness + measures latencyuser_id amount reasonto manually credit any userBug fixes (Devin Review):
REFERRAL_BONUSused_countwas incremented before payment (now on receipt submit)ValueErrorcrash in proxy API on non-numericlimitparamaiohttp.ClientSessionleak — addedon_cleanuphandlerdownload_countwas incremented even for expired files — split intoget_file()+increment_file_downloads()All original features preserved: VPN configs, YouTube/Instagram/TikTok downloads, file-to-link, VIP tiers, wallet, referral, admin panel.
Link to Devin session: https://app.devin.ai/sessions/451ad0922c2a4d229212dc5e8d49eea4
Requested by: @mohammad1390555