Repository navigation
Update main.py - #47
check6559-wq wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughThe Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (5)
main.py (5)
386-392: Repeated iteration over user_data is inefficient.The stats calculation iterates
user_data.values()four times (lines 386-388, 391-392). For a bot with many users, this is wasteful. Consider computing stats in a single pass.Proposed optimization
# Single-pass stats calculation plan_counts = {'free': 0, 'silver': 0, 'gold': 0} total_referrals = 0 top_referrer = None max_refs = 0 for uid, data in user_data.items(): plan_counts[data['plan']] = plan_counts.get(data['plan'], 0) + 1 total_referrals += data['referrals'] if data['referrals'] > max_refs: max_refs = data['referrals'] top_referrer = uid🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@main.py` around lines 386 - 392, Iterate user_data once to compute plan_counts, total_referrals, top_referrer and max_refs in a single-pass (e.g., initialize plan_counts = {'free':0,'silver':0,'gold':0}, total_referrals = 0, top_referrer = 'None', max_refs = -1 and update inside a for uid, data in user_data.items() loop), then replace the repeated list comprehensions in the output block with references to plan_counts['free'], plan_counts['silver'], plan_counts['gold'], total_referrals and top_referrer; ensure you handle the empty-user case so top_referrer remains 'None' when user_data is empty.
5-5: Remove unused import.
jsonis imported but never used in this file.-import json🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@main.py` at line 5, Remove the unused top-level import "json" from the file (the import statement "import json") so there are no unused imports in main.py; simply delete that import line and run linters/tests to confirm nothing else depends on the removed symbol.
15-18: In-memory storage will lose all data on restart.The comment acknowledges this limitation, but consider logging a warning at startup or documenting the expected behavior. Users will lose their plan upgrades, referrals, and API keys whenever the bot restarts.
For production use, consider persisting to SQLite (minimal dependency) or Redis. At minimum, add a startup warning:
print("⚠️ WARNING: Using in-memory storage - all data will be lost on restart!")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@main.py` around lines 15 - 18, Add a startup warning and short persistence guidance to make the risks of the in-memory stores explicit: log/print a clear warning at app startup indicating that user_data, otp_storage, and api_keys are in-memory and will be lost on restart, and update any startup/docs/comment block to recommend using SQLite or Redis for production. Locate the initialization of the dictionaries (user_data, otp_storage, api_keys) and insert a single startup warning (e.g., via print or logger.warning) plus a brief comment pointing to persistence options.
407-412: Slash-command menu is not registered with Telegram.The previous implementation called
register_commands()fromcommands.pyto register commands viabot.set_my_commands(). This is now missing, so users won't see command suggestions when typing/in the chat.Proposed fix - add command registration before polling
if __name__ == "__main__": print("🤖 HIT PRO Bot started!") print(f"🌐 Web App: {WEBAPP_URL}") print(f"👑 Admins: {ADMIN_IDS}") + + # Register commands with Telegram + from telebot.types import BotCommand + bot.set_my_commands([ + BotCommand("start", "Start the bot"), + BotCommand("profile", "Your account info"), + BotCommand("otp", "Get login OTP"), + BotCommand("help", "Show all commands"), + ]) + bot.infinity_polling()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@main.py` around lines 407 - 412, The slash-command menu isn't registered because register_commands() from commands.py is not being called before starting the bot; call the register_commands() function (and ensure it's imported at top, e.g., from commands import register_commands) immediately before bot.infinity_polling(), so that register_commands() can call bot.set_my_commands(...) to register the commands with Telegram; add error handling/logging around register_commands() to surface registration failures if needed.
146-150: OTP storage grows unbounded without cleanup.Expired OTPs are never removed from
otp_storage. For a long-running bot, this leads to unbounded memory growth. Consider adding cleanup logic when generating new OTPs or using a periodic cleanup task.Proposed fix - add cleanup when generating OTP
def send_otp(chat_id): user_id = chat_id + # Clean up expired OTPs + current_time = time.time() + expired_keys = [k for k, v in otp_storage.items() if v['expires'] < current_time] + for k in expired_keys: + del otp_storage[k] + otp = generate_otp() otp_storage[user_id] = {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@main.py` around lines 146 - 150, otp_storage is being populated with entries that never get removed, causing unbounded growth; update the OTP generation path that assigns otp_storage[user_id] (the block using 'code', 'timestamp', 'expires' with time.time()) to first purge expired entries by iterating over otp_storage and deleting any where entry['expires'] <= time.time() before inserting the new OTP, or alternatively add a lightweight background cleanup task that periodically scans otp_storage and deletes expired entries; ensure you reference and update the same otp_storage structure and the fields 'expires'/'timestamp' so lookups and removals remain consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@main.py`:
- Around line 53-57: Parse and validate the referral parameter before converting
to int: when extracting param from message.text (the code around param =
message.text.split()[1]) check that param.startswith('ref_') and that the
substring param[4:] consists only of digits (or use a try/except around
int(param[4:])); on invalid input catch ValueError (or skip/ log the bad param)
and only call handle_referral(user_id, referrer_id) when conversion succeeds so
handle_referral and referrer_id are never called with malformed data.
- Around line 24-28: The functions generate_otp and generate_api_key use
random.randint (Mersenne Twister) which is not cryptographically secure; import
the secrets module and replace random usage: for generate_otp produce a 6-digit
OTP with secrets (e.g., secrets.randbelow(900000) + 100000 or building from
secrets.choice on digits) and for generate_api_key use a secure token from
secrets (e.g., secrets.token_urlsafe or secrets.token_hex) combined with your
hitchk_ prefix and timestamp if needed; update imports to include secrets and
remove reliance on random for these two functions.
- Line 9: Remove the hardcoded Telegram token fallback by changing the BOT_TOKEN
constant to read only from the environment (os.getenv/os.environ) without a
default, and add a fail-fast check where BOT_TOKEN is used (e.g., at startup)
that raises an explicit error or exits if the env var is missing; update any
tests or deployment configs to provide the new token via environment or a
secrets manager, and ensure the exposed token is revoked and replaced outside
the codebase as part of the remediation.
- Around line 307-313: Replace the bare except and broad Exception handling
around the bot.send_message calls: catch the specific Telegram exception (e.g.,
telegram.error.Unauthorized or the library-specific exception thrown when a user
blocks the bot) for the inner try around bot.send_message(target_user, ...) and
handle it by logging a warning instead of pass; for the outer except around the
whole upgrade flow catch more specific exceptions where possible or catch
Exception as e but send a generic user-facing message via
bot.send_message(message.chat.id, "❌ An internal error occurred while upgrading
the plan.") and log the detailed error (using logging.exception or similar)
rather than exposing str(e) to the user; update code references: the inner
try/except protecting bot.send_message(target_user, ...) and the outer except
handling the overall upgrade flow.
- Around line 346-359: The broadcast handler currently slices message.text with
[11:] (fragile), spams bot.send_message to all user_data keys without rate
limiting, and uses a bare except that swallows errors; fix by parsing the
payload with message.text.split(maxsplit=1) to safely get broadcast_text (handle
missing second element with a usage reply), iterate user_data keys and throttle
sends to respect Telegram limits (e.g., sleep between sends or batch at
~30/sec), replace the bare except with a specific exception catch (e.g.,
telegram.error.TelegramError or Exception as e) and log the error before
incrementing failed, and keep updating sent/failed counters after each send
attempt so bot.send_message, broadcast_text, user_data, sent, and failed are
used consistently.
---
Nitpick comments:
In `@main.py`:
- Around line 386-392: Iterate user_data once to compute plan_counts,
total_referrals, top_referrer and max_refs in a single-pass (e.g., initialize
plan_counts = {'free':0,'silver':0,'gold':0}, total_referrals = 0, top_referrer
= 'None', max_refs = -1 and update inside a for uid, data in user_data.items()
loop), then replace the repeated list comprehensions in the output block with
references to plan_counts['free'], plan_counts['silver'], plan_counts['gold'],
total_referrals and top_referrer; ensure you handle the empty-user case so
top_referrer remains 'None' when user_data is empty.
- Line 5: Remove the unused top-level import "json" from the file (the import
statement "import json") so there are no unused imports in main.py; simply
delete that import line and run linters/tests to confirm nothing else depends on
the removed symbol.
- Around line 15-18: Add a startup warning and short persistence guidance to
make the risks of the in-memory stores explicit: log/print a clear warning at
app startup indicating that user_data, otp_storage, and api_keys are in-memory
and will be lost on restart, and update any startup/docs/comment block to
recommend using SQLite or Redis for production. Locate the initialization of the
dictionaries (user_data, otp_storage, api_keys) and insert a single startup
warning (e.g., via print or logger.warning) plus a brief comment pointing to
persistence options.
- Around line 407-412: The slash-command menu isn't registered because
register_commands() from commands.py is not being called before starting the
bot; call the register_commands() function (and ensure it's imported at top,
e.g., from commands import register_commands) immediately before
bot.infinity_polling(), so that register_commands() can call
bot.set_my_commands(...) to register the commands with Telegram; add error
handling/logging around register_commands() to surface registration failures if
needed.
- Around line 146-150: otp_storage is being populated with entries that never
get removed, causing unbounded growth; update the OTP generation path that
assigns otp_storage[user_id] (the block using 'code', 'timestamp', 'expires'
with time.time()) to first purge expired entries by iterating over otp_storage
and deleting any where entry['expires'] <= time.time() before inserting the new
OTP, or alternatively add a lightweight background cleanup task that
periodically scans otp_storage and deletes expired entries; ensure you reference
and update the same otp_storage structure and the fields 'expires'/'timestamp'
so lookups and removals remain consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
| from telebot import types | ||
|
|
||
| # Configuration | ||
| BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN', '8742056784:AAHzOGPUiuhqyi7pIWrPSiBWwtF0LTgAd3M') |
There was a problem hiding this comment.
Critical: Hardcoded bot token must be removed immediately.
This line contains what appears to be a real Telegram Bot API token as a fallback default. Once committed, this secret is exposed in version control history and can be used by anyone to hijack the bot.
- Revoke this token immediately via
@BotFather - Generate a new token
- Remove the hardcoded fallback entirely
Proposed fix
-BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN', '8742056784:AAHzOGPUiuhqyi7pIWrPSiBWwtF0LTgAd3M')
+BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
+if not BOT_TOKEN:
+ raise RuntimeError("TELEGRAM_BOT_TOKEN environment variable is required")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN', '8742056784:AAHzOGPUiuhqyi7pIWrPSiBWwtF0LTgAd3M') | |
| BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN') | |
| if not BOT_TOKEN: | |
| raise RuntimeError("TELEGRAM_BOT_TOKEN environment variable is required") |
🧰 Tools
🪛 Betterleaks (1.1.1)
[high] 9-9: Detected a Telegram Bot API Token, risking unauthorized bot operations and message interception on Telegram.
(telegram-bot-api-token)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@main.py` at line 9, Remove the hardcoded Telegram token fallback by changing
the BOT_TOKEN constant to read only from the environment (os.getenv/os.environ)
without a default, and add a fail-fast check where BOT_TOKEN is used (e.g., at
startup) that raises an explicit error or exits if the env var is missing;
update any tests or deployment configs to provide the new token via environment
or a secrets manager, and ensure the exposed token is revoked and replaced
outside the codebase as part of the remediation.
| def generate_otp(): | ||
| return str(random.randint(100000, 999999)) | ||
|
|
||
| def generate_api_key(): | ||
| return f"hitchk_{random.randint(100000, 999999)}_{int(time.time())}" |
There was a problem hiding this comment.
Use cryptographically secure random generation for OTPs and API keys.
random.randint() uses a predictable PRNG (Mersenne Twister). OTPs and API keys are security-sensitive and should use the secrets module instead.
Proposed fix
Add import at top of file:
import secretsThen update the functions:
def generate_otp():
- return str(random.randint(100000, 999999))
+ return f"{secrets.randbelow(900000) + 100000}"
def generate_api_key():
- return f"hitchk_{random.randint(100000, 999999)}_{int(time.time())}"
+ return f"hitchk_{secrets.token_hex(8)}_{int(time.time())}"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def generate_otp(): | |
| return str(random.randint(100000, 999999)) | |
| def generate_api_key(): | |
| return f"hitchk_{random.randint(100000, 999999)}_{int(time.time())}" | |
| def generate_otp(): | |
| return f"{secrets.randbelow(900000) + 100000}" | |
| def generate_api_key(): | |
| return f"hitchk_{secrets.token_hex(8)}_{int(time.time())}" |
🧰 Tools
🪛 Ruff (0.15.7)
[error] 25-25: Standard pseudo-random generators are not suitable for cryptographic purposes
(S311)
[error] 28-28: Standard pseudo-random generators are not suitable for cryptographic purposes
(S311)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@main.py` around lines 24 - 28, The functions generate_otp and
generate_api_key use random.randint (Mersenne Twister) which is not
cryptographically secure; import the secrets module and replace random usage:
for generate_otp produce a 6-digit OTP with secrets (e.g.,
secrets.randbelow(900000) + 100000 or building from secrets.choice on digits)
and for generate_api_key use a secure token from secrets (e.g.,
secrets.token_urlsafe or secrets.token_hex) combined with your hitchk_ prefix
and timestamp if needed; update imports to include secrets and remove reliance
on random for these two functions.
| if len(message.text.split()) > 1: | ||
| param = message.text.split()[1] | ||
| if param.startswith('ref_'): | ||
| referrer_id = int(param[4:]) | ||
| handle_referral(user_id, referrer_id) |
There was a problem hiding this comment.
Add error handling for referral parameter parsing.
int(param[4:]) will raise ValueError if the referral parameter contains non-numeric characters (e.g., /start ref_abc), causing the handler to fail silently or raise an unhandled exception.
Proposed fix
if len(message.text.split()) > 1:
param = message.text.split()[1]
if param.startswith('ref_'):
- referrer_id = int(param[4:])
- handle_referral(user_id, referrer_id)
+ try:
+ referrer_id = int(param[4:])
+ handle_referral(user_id, referrer_id)
+ except ValueError:
+ pass # Invalid referral link format, ignore🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@main.py` around lines 53 - 57, Parse and validate the referral parameter
before converting to int: when extracting param from message.text (the code
around param = message.text.split()[1]) check that param.startswith('ref_') and
that the substring param[4:] consists only of digits (or use a try/except around
int(param[4:])); on invalid input catch ValueError (or skip/ log the bad param)
and only call handle_referral(user_id, referrer_id) when conversion succeeds so
handle_referral and referrer_id are never called with malformed data.
| try: | ||
| bot.send_message(target_user, f"🎉 **Plan Upgraded!**\n\nYour plan: {plan_emoji[new_plan]} **{new_plan.upper()}**\n\nEnjoy your new features!") | ||
| except: | ||
| pass # User might have blocked the bot | ||
|
|
||
| except Exception as e: | ||
| bot.send_message(message.chat.id, f"❌ Error: {str(e)}") |
There was a problem hiding this comment.
Improve exception handling - avoid bare except and overly broad catches.
Lines 309-310 use bare except: pass which silently swallows all errors including programming errors. Lines 312-313 catch Exception broadly and expose internal error messages to users.
Proposed fix
try:
bot.send_message(target_user, f"🎉 **Plan Upgraded!**\n\nYour plan: {plan_emoji[new_plan]} **{new_plan.upper()}**\n\nEnjoy your new features!")
- except:
- pass # User might have blocked the bot
+ except telebot.apihelper.ApiException:
+ pass # User blocked bot or chat not found
- except Exception as e:
- bot.send_message(message.chat.id, f"❌ Error: {str(e)}")
+ except ValueError:
+ bot.send_message(message.chat.id, "❌ Invalid user ID format")🧰 Tools
🪛 Ruff (0.15.7)
[error] 309-309: Do not use bare except
(E722)
[error] 309-310: try-except-pass detected, consider logging the exception
(S110)
[warning] 312-312: Do not catch blind exception: Exception
(BLE001)
[warning] 313-313: Use explicit conversion flag
Replace with conversion flag
(RUF010)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@main.py` around lines 307 - 313, Replace the bare except and broad Exception
handling around the bot.send_message calls: catch the specific Telegram
exception (e.g., telegram.error.Unauthorized or the library-specific exception
thrown when a user blocks the bot) for the inner try around
bot.send_message(target_user, ...) and handle it by logging a warning instead of
pass; for the outer except around the whole upgrade flow catch more specific
exceptions where possible or catch Exception as e but send a generic user-facing
message via bot.send_message(message.chat.id, "❌ An internal error occurred
while upgrading the plan.") and log the detailed error (using logging.exception
or similar) rather than exposing str(e) to the user; update code references: the
inner try/except protecting bot.send_message(target_user, ...) and the outer
except handling the overall upgrade flow.
| broadcast_text = message.text[11:] # Remove '/broadcast ' | ||
| if not broadcast_text: | ||
| bot.send_message(message.chat.id, "Usage: `/broadcast [your message]`", parse_mode='Markdown') | ||
| return | ||
|
|
||
| sent = 0 | ||
| failed = 0 | ||
|
|
||
| for user_id in user_data.keys(): | ||
| try: | ||
| bot.send_message(user_id, f"📢 **HIT PRO Announcement**\n\n{broadcast_text}", parse_mode='Markdown') | ||
| sent += 1 | ||
| except: | ||
| failed += 1 |
There was a problem hiding this comment.
Broadcast implementation has fragile parsing and missing rate limiting.
- Line 346 uses hardcoded offset
[11:]- fragile if command format changes. Usemessage.text.split(maxsplit=1)instead. - No rate limiting between messages - Telegram enforces ~30 messages/second limit, which will cause failures for larger user bases.
- Bare
excepton line 358 swallows all errors silently.
Proposed fix
try:
- broadcast_text = message.text[11:] # Remove '/broadcast '
+ parts = message.text.split(maxsplit=1)
+ broadcast_text = parts[1] if len(parts) > 1 else ""
if not broadcast_text:
bot.send_message(message.chat.id, "Usage: `/broadcast [your message]`", parse_mode='Markdown')
return
sent = 0
failed = 0
for user_id in user_data.keys():
try:
bot.send_message(user_id, f"📢 **HIT PRO Announcement**\n\n{broadcast_text}", parse_mode='Markdown')
sent += 1
- except:
+ time.sleep(0.05) # Rate limit: ~20 msgs/sec
+ except telebot.apihelper.ApiException:
failed += 1🧰 Tools
🪛 Ruff (0.15.7)
[error] 358-358: Do not use bare except
(E722)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@main.py` around lines 346 - 359, The broadcast handler currently slices
message.text with [11:] (fragile), spams bot.send_message to all user_data keys
without rate limiting, and uses a bare except that swallows errors; fix by
parsing the payload with message.text.split(maxsplit=1) to safely get
broadcast_text (handle missing second element with a usage reply), iterate
user_data keys and throttle sends to respect Telegram limits (e.g., sleep
between sends or batch at ~30/sec), replace the bare except with a specific
exception catch (e.g., telegram.error.TelegramError or Exception as e) and log
the error before incrementing failed, and keep updating sent/failed counters
after each send attempt so bot.send_message, broadcast_text, user_data, sent,
and failed are used consistently.
Summary by CodeRabbit
/otpfor time-limited one-time passwords,/profilefor viewing user information and plans, and/helpfor accessing documentation