Skip to content

Commit 7b5a0f7

Browse files
committed
add token bucket
1 parent db1605e commit 7b5a0f7

File tree

6 files changed

+35
-11
lines changed

6 files changed

+35
-11
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ Websites [supported by youtube-dl](https://ytdl-org.github.io/youtube-dl/support
2929

3030
I don't have unlimited servers and bandwidth, so I have to make some restrictions.
3131

32-
* 10 GiB one-way traffic per 24 hours for each user
32+
* 5 GiB one-way traffic per 24 hours for each user
3333
* maximum 5 minutes streaming conversion support
3434
* maximum 3 subscriptions
35+
* limited request in certain time range
3536

3637
You can choose to become 'VIP' if you really need large traffic. And also, you could always deploy your own bot.
3738

@@ -128,6 +129,7 @@ you can configure all the following environment variables:
128129
above `AUTHORIZED_USER`
129130

130131
* ENABLE_CELERY: Distribution mode, default: disable. You'll can setup workers in different locations.
132+
* ENABLE_FFMPEG: enable ffmpeg so Telegram can stream
131133
* MYSQL_HOST: you'll have to setup MySQL if you enable VIP mode
132134
* MYSQL_USER
133135
* MYSQL_PASS

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ flower==1.2.0
1111
psutil==5.9.4
1212
influxdb==5.3.1
1313
beautifulsoup4==4.11.1
14-
fakeredis==1.10.0
14+
fakeredis==1.10.1
1515
supervisor==4.2.4
1616
tgbot-ping==1.0.4
1717
redis==4.3.3
@@ -20,3 +20,4 @@ tqdm==4.64.1
2020
requests-toolbelt==0.10.1
2121
ffpb==0.4.1
2222
youtube-search-python==1.6.6
23+
token-bucket==0.3.0

ytdlbot/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
REDIS = os.getenv("REDIS")
2020

2121
# quota settings
22-
QUOTA = int(os.getenv("QUOTA", 10 * 1024 * 1024 * 1024)) # 10G
22+
QUOTA = int(os.getenv("QUOTA", 5 * 1024 * 1024 * 1024)) # 5G
2323
if os.uname().sysname == "Darwin":
2424
QUOTA = 10 * 1024 * 1024 # 10M
2525

@@ -58,3 +58,5 @@
5858

5959
IPv6 = os.getenv("IPv6", False)
6060
ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False)
61+
RATE = float(os.getenv("RATE", 60 * 5))
62+
BURST = int(os.getenv("BURST", 3))

ytdlbot/constant.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
import os
1111
import time
1212

13-
from config import (AFD_LINK, COFFEE_LINK, ENABLE_CELERY, ENABLE_VIP, EX,
14-
MULTIPLY, REQUIRED_MEMBERSHIP, USD2CNY)
13+
from config import (AFD_LINK, BURST, COFFEE_LINK, ENABLE_CELERY, ENABLE_VIP,
14+
EX, MULTIPLY, RATE, REQUIRED_MEMBERSHIP, USD2CNY)
1515
from db import InfluxDB
1616
from downloader import sizeof_fmt
1717
from limit import QUOTA, VIP
@@ -33,6 +33,8 @@ class BotText:
3333
4. You can optionally choose to become 'VIP' user if you need more traffic. Type /vip for more information.
3434
3535
5. Source code for this bot will always stay open, here-> https://github.com/tgbot-collection/ytdlbot
36+
37+
6. Request limit is applied for everyone, excluding VIP users.
3638
""" if ENABLE_VIP else "Help text"
3739

3840
about = "YouTube-DL by @BennyThink. Open source on GitHub: https://github.com/tgbot-collection/ytdlbot"
@@ -149,3 +151,5 @@ def ping_worker():
149151
text += f"{status}{hostname} **{active}** {load} {rev}\n"
150152

151153
return text
154+
155+
too_fast = f"You have reached rate limit. Current rate limit is 1 request per {RATE} seconds, {BURST - 1} bursts."

ytdlbot/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def next_salt_detector(self):
205205
def idle_detector(self):
206206
mtime = os.stat("/var/log/ytdl.log").st_mtime
207207
cur_ts = time.time()
208-
if cur_ts - mtime > 300:
208+
if cur_ts - mtime > 1800:
209209
logging.warning("Potential crash detected by %s, it's time to commit suicide...", self.func_name())
210210
return True
211211

ytdlbot/ytdl_bot.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@
2222
from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant
2323
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
2424
from tgbot_ping import get_runtime
25+
from token_bucket import Limiter, MemoryStorage
2526
from youtubesearchpython import VideosSearch
2627

2728
from client_init import create_app
28-
from config import (AUTHORIZED_USER, ENABLE_CELERY, ENABLE_VIP, OWNER,
29-
REQUIRED_MEMBERSHIP)
29+
from config import (AUTHORIZED_USER, BURST, ENABLE_CELERY, ENABLE_FFMPEG,
30+
ENABLE_VIP, OWNER, RATE, REQUIRED_MEMBERSHIP)
3031
from constant import BotText
3132
from db import InfluxDB, MySQL, Redis
3233
from limit import VIP, verify_payment
@@ -44,6 +45,11 @@
4445

4546
logging.info("Authorized users are %s", AUTHORIZED_USER)
4647

48+
# rate, capacity
49+
mem = MemoryStorage()
50+
# 5 minutes, 2 bursts
51+
lim = Limiter(1 / RATE, BURST, mem)
52+
4753

4854
def private_use(func):
4955
def wrapper(client: "Client", message: "types.Message"):
@@ -145,7 +151,7 @@ def patch_handler(client: "Client", message: "types.Message"):
145151

146152

147153
@app.on_message(filters.command(["uncache"]))
148-
def patch_handler(client: "Client", message: "types.Message"):
154+
def uncache_handler(client: "Client", message: "types.Message"):
149155
username = message.from_user.username
150156
link = message.text.split()[1]
151157
if username == OWNER:
@@ -169,7 +175,7 @@ def ping_handler(client: "Client", message: "types.Message"):
169175

170176

171177
@app.on_message(filters.command(["about"]))
172-
def help_handler(client: "Client", message: "types.Message"):
178+
def about_handler(client: "Client", message: "types.Message"):
173179
chat_id = message.chat.id
174180
client.send_chat_action(chat_id, "typing")
175181
client.send_message(chat_id, bot_text.about)
@@ -296,6 +302,11 @@ def download_handler(client: "Client", message: "types.Message"):
296302
message.reply_text("Channel download is disabled now. Please send me individual video link.", quote=True)
297303
red.update_metrics("reject_channel")
298304
return
305+
# non vip user, consume too many token
306+
if (not VIP().check_vip(chat_id)) and (not lim.consume(str(chat_id).encode(), 1)):
307+
red.update_metrics("rate_limit")
308+
message.reply_text(bot_text.too_fast, quote=True)
309+
return
299310

300311
red.update_metrics("video_request")
301312
text = bot_text.get_receive_link_text()
@@ -338,9 +349,13 @@ def download_resolution_callback(client: "Client", callback_query: types.Callbac
338349

339350
@app.on_callback_query(filters.regex(r"convert"))
340351
def audio_callback(client: "Client", callback_query: types.CallbackQuery):
352+
if not ENABLE_FFMPEG:
353+
callback_query.answer("Audio conversion is disabled now.")
354+
callback_query.message.reply_text("Audio conversion is disabled now.")
355+
return
356+
341357
callback_query.answer(f"Converting to audio...please wait patiently")
342358
Redis().update_metrics("audio_request")
343-
344359
vmsg = callback_query.message
345360
audio_entrance(vmsg, client)
346361

0 commit comments

Comments
 (0)