Skip to content

Refactor Telegram bot with command handlers and video download - #49

Open
Sasha198-rt wants to merge 1 commit into
aeither:mainfrom
Sasha198-rt:patch-1
Open

Sasha198-rt wants to merge 1 commit into
aeither:mainfrom
Sasha198-rt:patch-1

Conversation

@Sasha198-rt

@Sasha198-rt Sasha198-rt commented Apr 8, 2026 •

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features
    • Introduced a user access control system—users must now purchase access to download videos.
    • Added /start and /help commands for improved user guidance.
    • Added /pay command to activate premium download functionality.
    • Implemented interactive button interface ("💰 Купить доступ", "📥 Скачать видео") for streamlined navigation.
    • Video downloads now restricted to users with active paid access.

@coderabbitai

coderabbitai Bot commented Apr 8, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The Telegram bot now uses hard-coded token initialization and implements a pay-to-access system with a global paid_users set. Users can access /start, /help, /pay commands and button handlers for purchase and download flows. Video downloads via yt_dlp are gated behind paid access verification, replacing the previous unconditional echo handler and external command setup.

Changes

Cohort / File(s) Summary
Bot Core & Payment Flow
main.py
Replaced environment-based token loading with hard-coded initialization. Implemented payment system with paid_users set and /pay command granting access by chat ID. Added /start, /help handlers and "💰 Купить доступ" button triggering payment flow. Removed exception-based startup loop in favor of immediate bot.polling().
Gated Download Functionality
main.py
Replaced global echo handler with access-controlled download workflow: handle_link() accepts video URLs only from paid users, invokes yt_dlp to download to video.mp4, and sends file; non-paid users receive access denial. Added "📥 Скачать видео" button handler (ask_link()) and supporting command decorators.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 Whiskers twitching with delight,
A payment gate sees morning light!
Paid users hop to videos they seek,
While yt_dlp downloads all week,
No more free rides—the bot's polite! 🎥💰

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: refactoring the bot with new command handlers (/start, /help, /pay) and implementing a gated video download workflow.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Warning

⚠️ This pull request might be slop. It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c2aa87c-c949-43a6-bf99-a4487786bd44

📥 Commits

Reviewing files that changed from the base of the PR and between 5cfd740 and b2e67a5.

📒 Files selected for processing (1)
  • main.py

Comment thread main.py
while True:
time.sleep(3600) No newline at end of file
from telebot import types
import yt_dlp

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check main.py line 4 and surrounding context
head -10 main.py | cat -n

Repository: aeither/telegram-bot-python

Length of output: 362


🏁 Script executed:

# Check pyproject.toml for dependency declarations
cat -n pyproject.toml

Repository: 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/null

Repository: 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.

Comment thread main.py
Comment on lines +6 to +7
TOKEN = os.getenv("8709005198:AAEIw9YwBKi1iRYn2oQlw_8CR_OWe9XD3ms")
bot = telebot.TeleBot(TOKEN)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, locate and read main.py to verify the token and os.getenv call
head -20 main.py

Repository: 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 -50

Repository: 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.

Comment thread main.py
Comment on lines +9 to +10
# Простая база "платных" пользователей
paid_users = set()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

/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.

Comment thread main.py
Comment on lines +17 to +21
btn1 = types.KeyboardButton("📥 Скачать видео")
btn2 = types.KeyboardButton("💰 Купить доступ")
btn3 = types.KeyboardButton("ℹ️ Помощь")

markup.add(btn1, btn2, btn3)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment thread main.py
Comment on lines +72 to +81
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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant