Repository navigation
Refactor Telegram bot with command handlers and video download - #49
Sasha198-rt wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughThe Telegram bot now uses hard-coded token initialization and implements a pay-to-access system with a global Changes
Sequence DiagramsequenceDiagram
actor User
participant Bot as Telegram Bot
participant Storage as paid_users Set
participant YT as yt_dlp
User->>Bot: /start
Bot->>User: Display start message + buttons
User->>Bot: Click "💰 Купить доступ"
Bot->>User: Prompt /pay command
User->>Bot: /pay
Bot->>Storage: Add user to paid_users
Bot->>User: Access granted
User->>Bot: Click "📥 Скачать видео"
Bot->>User: Prompt for video URL
User->>Bot: Send video URL (text)
Bot->>Storage: Check if user in paid_users
alt User has paid access
Bot->>YT: Execute yt_dlp download
YT->>YT: Download to video.mp4
Bot->>User: Send video.mp4 file
else User lacks access
Bot->>User: Access denied error
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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 Warning |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 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 72-81: The code currently hardcodes 'video.mp4' in ydl_opts and
then opens it for bot.send_video, which can cause cross-request collisions;
modify the handling around ydl_opts/yt_dlp.YoutubeDL/ydl.download and
bot.send_video to create a per-request temporary directory or unique filename
(use tempfile.mkdtemp or tempfile.NamedTemporaryFile to generate a path), set
ydl_opts['outtmpl'] to that unique path, call
yt_dlp.YoutubeDL(ydl_opts).download([url]), then open and send the temp file
with bot.send_video, and ensure you remove the temp file/directory in a finally
block (or use a context manager) to guarantee cleanup.
- Around line 9-10: The /pay flow currently just adds message.chat.id to the
process-local paid_users set (symbol: paid_users) with no verification, which
lets anyone self-grant access, unlocks whole chats in groups, and is lost on
restart; replace this by validating a verified payment callback (e.g.,
handle_payment_callback or on_payment_webhook) that confirms transaction details
and payer identity, persist the entitlement in shared storage (DB or Redis)
keyed to the actual user/account ID (not message.chat.id) and fetch/consult that
persistent store in the payment-check logic (instead of paid_users) so
entitlements survive restarts and are scoped per-user; ensure group chat
payments do not grant access to other users and reject or map group purchases
explicitly if needed.
- Around line 17-21: The "ℹ️ Помощь" keyboard item is being swallowed by the
generic text handler; fix this by adding an explicit per-user awaiting_link
state (e.g., awaiting_link dict keyed by user id) and change the flow so the
download button handler sets awaiting_link[user_id]=True instead of relying on
free-form text, then update the text handler (the existing handle_link path) to
first check for the help label ("ℹ️ Помощь") and route to the help handler,
otherwise only proceed if awaiting_link[user_id] is True; before calling yt_dlp
validate the incoming message.text is a proper URL (simple regex or urlparse)
and clear awaiting_link[user_id] on success or error.
- Around line 6-7: The TOKEN assignment embeds a leaked bot token and
incorrectly uses that literal as the env var name; rotate the leaked token
immediately and update the initialization so TOKEN is read from the proper
environment variable (use os.getenv("TELEGRAM_BOT_TOKEN") or equivalent) and
validate it before constructing telebot.TeleBot(TOKEN) — if TOKEN is missing,
log or raise a clear error and avoid calling telebot.TeleBot with None.
- Line 4: main.py imports yt_dlp but pyproject.toml's dependencies block is
missing that package; add "yt-dlp" to the dependencies list in pyproject.toml
(use the package name exactly as "yt-dlp" with an appropriate version constraint
or "*" if undecided) so a clean install won't raise ImportError, then run your
dependency tooling (poetry/pipenv/pip) to update lockfiles; look for the import
in main.py and the dependencies array in pyproject.toml to update the file.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
| while True: | ||
| time.sleep(3600) No newline at end of file | ||
| from telebot import types | ||
| import yt_dlp |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check main.py line 4 and surrounding context
head -10 main.py | cat -nRepository: aeither/telegram-bot-python
Length of output: 362
🏁 Script executed:
# Check pyproject.toml for dependency declarations
cat -n pyproject.tomlRepository: aeither/telegram-bot-python
Length of output: 623
🏁 Script executed:
# Search for any alternative dependency files
fd -e requirements.txt -e setup.py -e setup.cfg -e Pipfile 2>/dev/nullRepository: aeither/telegram-bot-python
Length of output: 53
Add yt-dlp to the project dependencies in pyproject.toml.
main.py imports yt_dlp on line 4, but it is not declared in the dependencies list in pyproject.toml (lines 12-15). A clean install will fail with an ImportError before the bot starts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@main.py` at line 4, main.py imports yt_dlp but pyproject.toml's dependencies
block is missing that package; add "yt-dlp" to the dependencies list in
pyproject.toml (use the package name exactly as "yt-dlp" with an appropriate
version constraint or "*" if undecided) so a clean install won't raise
ImportError, then run your dependency tooling (poetry/pipenv/pip) to update
lockfiles; look for the import in main.py and the dependencies array in
pyproject.toml to update the file.
| TOKEN = os.getenv("8709005198:AAEIw9YwBKi1iRYn2oQlw_8CR_OWe9XD3ms") | ||
| bot = telebot.TeleBot(TOKEN) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, locate and read main.py to verify the token and os.getenv call
head -20 main.pyRepository: aeither/telegram-bot-python
Length of output: 608
🏁 Script executed:
# Check if .env.example exists and contains TELEGRAM_BOT_TOKEN
cat .env.example 2>/dev/null || echo "File not found"Repository: aeither/telegram-bot-python
Length of output: 231
🏁 Script executed:
# Check README.md for token documentation
cat README.md 2>/dev/null | head -50Repository: aeither/telegram-bot-python
Length of output: 1132
Rotate this token and restore validated env loading.
This exposes a bot credential in source control. Additionally, os.getenv("8709005198:AAEIw9YwBKi1iRYn2oQlw_8CR_OWe9XD3ms") incorrectly passes the token literal as the environment variable name instead of the key. Any deployment that sets TELEGRAM_BOT_TOKEN as documented will receive None here, causing the bot to fail initialization. Rotate the leaked token immediately and change line 6 to read TOKEN = os.getenv("TELEGRAM_BOT_TOKEN").
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@main.py` around lines 6 - 7, The TOKEN assignment embeds a leaked bot token
and incorrectly uses that literal as the env var name; rotate the leaked token
immediately and update the initialization so TOKEN is read from the proper
environment variable (use os.getenv("TELEGRAM_BOT_TOKEN") or equivalent) and
validate it before constructing telebot.TeleBot(TOKEN) — if TOKEN is missing,
log or raise a clear error and avoid calling telebot.TeleBot with None.
| # Простая база "платных" пользователей | ||
| paid_users = set() |
There was a problem hiding this comment.
/pay currently bypasses the paywall entirely.
It adds message.chat.id to a process-local set with no payment verification. Any user can self-activate access, one /pay in a group unlocks the whole chat, and all entitlements disappear on restart. Wire this to a verified payment callback and persist access in shared storage keyed to the actual user/account.
Also applies to: 49-52
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@main.py` around lines 9 - 10, The /pay flow currently just adds
message.chat.id to the process-local paid_users set (symbol: paid_users) with no
verification, which lets anyone self-grant access, unlocks whole chats in
groups, and is lost on restart; replace this by validating a verified payment
callback (e.g., handle_payment_callback or on_payment_webhook) that confirms
transaction details and payer identity, persist the entitlement in shared
storage (DB or Redis) keyed to the actual user/account ID (not message.chat.id)
and fetch/consult that persistent store in the payment-check logic (instead of
paid_users) so entitlements survive restarts and are scoped per-user; ensure
group chat payments do not grant access to other users and reject or map group
purchases explicitly if needed.
| btn1 = types.KeyboardButton("📥 Скачать видео") | ||
| btn2 = types.KeyboardButton("💰 Купить доступ") | ||
| btn3 = types.KeyboardButton("ℹ️ Помощь") | ||
|
|
||
| markup.add(btn1, btn2, btn3) |
There was a problem hiding this comment.
The help button is broken by the catch-all text handler.
The keyboard exposes "ℹ️ Помощь", but only /help is registered. That text falls into handle_link, so unpaid users are told to buy access and paid users try to download the label as a URL. Track an explicit “awaiting link” state and validate URLs before invoking yt_dlp.
Also applies to: 30-31, 60-66
🧰 Tools
🪛 Ruff (0.15.9)
[warning] 19-19: String contains ambiguous ℹ (INFORMATION SOURCE). Did you mean i (LATIN SMALL LETTER I)?
(RUF001)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@main.py` around lines 17 - 21, The "ℹ️ Помощь" keyboard item is being
swallowed by the generic text handler; fix this by adding an explicit per-user
awaiting_link state (e.g., awaiting_link dict keyed by user id) and change the
flow so the download button handler sets awaiting_link[user_id]=True instead of
relying on free-form text, then update the text handler (the existing
handle_link path) to first check for the help label ("ℹ️ Помощь") and route to
the help handler, otherwise only proceed if awaiting_link[user_id] is True;
before calling yt_dlp validate the incoming message.text is a proper URL (simple
regex or urlparse) and clear awaiting_link[user_id] on success or error.
| ydl_opts = { | ||
| 'outtmpl': 'video.mp4', | ||
| 'format': 'mp4' | ||
| } | ||
|
|
||
| with yt_dlp.YoutubeDL(ydl_opts) as ydl: | ||
| ydl.download([url]) | ||
|
|
||
| with open("video.mp4", "rb") as video: | ||
| bot.send_video(message.chat.id, video) |
There was a problem hiding this comment.
Don't reuse video.mp4 for every download.
A single shared output path is unsafe in a multi-user bot: overlapping requests can overwrite each other and send the wrong file to the wrong chat. Write into a per-request temp directory/file and clean it up in finally.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@main.py` around lines 72 - 81, The code currently hardcodes 'video.mp4' in
ydl_opts and then opens it for bot.send_video, which can cause cross-request
collisions; modify the handling around ydl_opts/yt_dlp.YoutubeDL/ydl.download
and bot.send_video to create a per-request temporary directory or unique
filename (use tempfile.mkdtemp or tempfile.NamedTemporaryFile to generate a
path), set ydl_opts['outtmpl'] to that unique path, call
yt_dlp.YoutubeDL(ydl_opts).download([url]), then open and send the temp file
with bot.send_video, and ensure you remove the temp file/directory in a finally
block (or use a context manager) to guarantee cleanup.
Summary by CodeRabbit
/startand/helpcommands for improved user guidance./paycommand to activate premium download functionality.