mirror of
https://github.com/tgbot-collection/ytdlbot.git
synced 2025-02-24 23:34:44 +08:00
telegram payment
This commit is contained in:
parent
3cbfe3f1f1
commit
7a7b7f3172
6 changed files with 148 additions and 77 deletions
|
@ -441,13 +441,15 @@ ping - Bot running status
|
|||
help - Help
|
||||
ytdl - Download video in group
|
||||
vip - Join VIP
|
||||
terms - View Terms of Service
|
||||
settings - Set your preference
|
||||
direct - Download file directly
|
||||
sub - Subscribe to YouTube Channel
|
||||
unsub - Unsubscribe from YouTube Channel
|
||||
sub_count - Check subscription status, owner only.
|
||||
uncache - Delete cache for this link
|
||||
uncache - Delete cache for this link, owner only.
|
||||
purge - Delete all tasks , owner only.
|
||||
topup - Top up quota
|
||||
tgvip - Using Telegram payment to pay for VIP
|
||||
```
|
||||
|
||||
# Test data
|
||||
|
|
|
@ -27,7 +27,7 @@ TG_MAX_SIZE = 2 * 1024 * 1024 * 1024 * 0.99
|
|||
# TG_MAX_SIZE = 10 * 1024 * 1024
|
||||
|
||||
EX = os.getenv("EX", 24 * 3600)
|
||||
MULTIPLY = os.getenv("MULTIPLY", 5) # VIP1 is 5*5-25G, VIP2 is 50G
|
||||
MULTIPLY = os.getenv("MULTIPLY", 10) # VIP1 is 5*5-25G, VIP2 is 50G
|
||||
USD2CNY = os.getenv("USD2CNY", 6) # $5 --> ¥30
|
||||
|
||||
ENABLE_VIP = os.getenv("VIP", False)
|
||||
|
@ -58,5 +58,7 @@ ARCHIVE_ID = os.getenv("ARCHIVE_ID")
|
|||
|
||||
IPv6 = os.getenv("IPv6", False)
|
||||
ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False)
|
||||
RATE = float(os.getenv("RATE", 60 * 5))
|
||||
# 0.01 means basically no limit
|
||||
RATE = float(os.getenv("RATE", 0.01))
|
||||
BURST = int(os.getenv("BURST", 3))
|
||||
PROVIDER_TOKEN = os.getenv("PROVIDER_TOKEN") or "1234"
|
||||
|
|
|
@ -22,62 +22,53 @@ class BotText:
|
|||
start = "Welcome to YouTube Download bot. Type /help for more information."
|
||||
|
||||
help = f"""
|
||||
1. This bot should works at all times. If it doesn't, try to send the link again or DM @BennyThink
|
||||
1. This bot should works at all times. If it doesn't, wait for a few minutes, try to send the link again.
|
||||
|
||||
2. At this time of writing, this bot consumes hundreds of GigaBytes of network traffic per day.
|
||||
2. At this time of writing, this bot consumes more than 100GB of network traffic per day.
|
||||
In order to avoid being abused,
|
||||
every one can use this bot within **{sizeof_fmt(QUOTA)} of quota for every {int(EX / 3600)} hours.**
|
||||
|
||||
3. Free users can't receive streaming formats of one video whose duration is longer than 300 seconds.
|
||||
3. You can optionally choose to become 'VIP' user if you need more traffic. Type /vip for more information.
|
||||
|
||||
4. You can optionally choose to become 'VIP' user if you need more traffic. Type /vip for more information.
|
||||
4. Source code for this bot will always stay open, here-> https://github.com/tgbot-collection/ytdlbot
|
||||
|
||||
5. Source code for this bot will always stay open, here-> https://github.com/tgbot-collection/ytdlbot
|
||||
|
||||
6. Request limit is applied for everyone, excluding VIP users.
|
||||
5. Request limit is applied for everyone, excluding VIP users.
|
||||
""" if ENABLE_VIP else "Help text"
|
||||
|
||||
about = "YouTube-DL by @BennyThink. Open source on GitHub: https://github.com/tgbot-collection/ytdlbot"
|
||||
|
||||
terms = f"""
|
||||
1. You can use this service, free of charge, {sizeof_fmt(QUOTA)} per {int(EX / 3600)} hours.
|
||||
|
||||
2. The above traffic, is counted for one-way.
|
||||
For example, if you download a video of 1GB, your current quota will be 9GB instead of 8GB.
|
||||
|
||||
3. Streaming support is limited due to high costs of conversion.
|
||||
|
||||
4. I won't gather any personal information, which means I don't know how many and what videos did you download.
|
||||
|
||||
5. Please try not to abuse this service.
|
||||
|
||||
6. It's a open source project, you can always deploy your own bot.
|
||||
|
||||
7. For VIPs, please refer to /vip command
|
||||
""" if ENABLE_VIP else "Please contact the actual owner of this bot"
|
||||
|
||||
vip = f"""
|
||||
**Terms:**
|
||||
1. No refund, I'll keep it running as long as I can.
|
||||
2. I'll record your unique ID after a successful payment, usually it's payment ID or email address.
|
||||
3. VIPs identity won't expire.
|
||||
1. You can use this service, free of charge, {sizeof_fmt(QUOTA)} per {int(EX / 3600)} hours.
|
||||
2. The above traffic, is counted one-way. For example, if you download a video of 1GB, your will use 1GB instead of 2GB.
|
||||
3. Streaming support is limited due to high costs of conversion.
|
||||
4. I won't gather any personal information, which means I don't know how many and what videos did you download.
|
||||
5. No rate limit for VIP users.
|
||||
6. Possible to refund, but you'll have to bear with process fee.
|
||||
7. I'll record your unique ID after a successful payment, usually it's payment ID or email address.
|
||||
8. VIP identity won't expire.
|
||||
9. Please try not to abuse this service. It's a open source project, you can always deploy your own bot.
|
||||
|
||||
**Pay Tier:**
|
||||
1. Everyone: {sizeof_fmt(QUOTA)} per {int(EX / 3600)} hours
|
||||
2. VIP1: ${MULTIPLY} or ¥{MULTIPLY * USD2CNY}, {sizeof_fmt(QUOTA * 5)} per {int(EX / 3600)} hours
|
||||
3. VIP2: ${MULTIPLY * 2} or ¥{MULTIPLY * USD2CNY * 2}, {sizeof_fmt(QUOTA * 5 * 2)} per {int(EX / 3600)} hours
|
||||
2. VIP1: ${MULTIPLY} or ¥{MULTIPLY * USD2CNY}, {sizeof_fmt(QUOTA * 2)} per {int(EX / 3600)} hours
|
||||
3. VIP2: ${MULTIPLY * 2} or ¥{MULTIPLY * USD2CNY * 2}, {sizeof_fmt(QUOTA * 2 * 2)} per {int(EX / 3600)} hours
|
||||
4. VIP4....VIPn.
|
||||
5. Unlimited streaming conversion support.
|
||||
Note: If you pay $9, you'll become VIP1 instead of VIP2.
|
||||
|
||||
**Temporary top up**
|
||||
Just want more traffic for a short period of time? Don't worry, you can use /topup command to top up your quota.
|
||||
It's valid permanently, until you use it up.
|
||||
|
||||
**Payment method:**
|
||||
1. (afdian) Mainland China: {AFD_LINK}
|
||||
2. (buy me a coffee) Other countries or regions: {COFFEE_LINK}
|
||||
__I live in a place where I don't have access to Telegram Payments. So...__
|
||||
3. Telegram Payment(stripe), please directly using /tgvip command.
|
||||
|
||||
**After payment:**
|
||||
|
||||
1. afdian: with your order number `/vip 123456`
|
||||
2. buy me a coffee: with your email `/vip someone@else.com`
|
||||
3. Telegram Payment: automatically activated
|
||||
""" if ENABLE_VIP else "VIP is not enabled."
|
||||
vip_pay = "Processing your payments...If it's not responding after one minute, please contact @BennyThink."
|
||||
|
||||
|
@ -95,6 +86,8 @@ Video quality: **{0}**
|
|||
Sending format: **{1}**
|
||||
"""
|
||||
custom_text = os.getenv("CUSTOM_TEXT", "")
|
||||
topup_description = f"US$1 will give you {sizeof_fmt(QUOTA)} traffic permanently"
|
||||
topup_title = "Pay US$1 for more traffic!"
|
||||
|
||||
def remaining_quota_caption(self, chat_id):
|
||||
if not ENABLE_VIP:
|
||||
|
|
|
@ -34,26 +34,42 @@ class VIP(Redis, MySQL):
|
|||
data = self.cur.fetchone()
|
||||
return data
|
||||
|
||||
def add_vip(self, user_data: "dict") -> ("bool", "str"):
|
||||
def __add_vip(self, user_data: "dict"):
|
||||
sql = "INSERT INTO vip VALUES (%s,%s,%s,%s,%s,%s);"
|
||||
self.cur.execute(sql, list(user_data.values()))
|
||||
self.con.commit()
|
||||
# also remove redis cache
|
||||
self.r.delete(user_data["user_id"])
|
||||
|
||||
def add_vip(self, user_data: "dict") -> "str":
|
||||
# first select
|
||||
self.cur.execute("SELECT * FROM vip WHERE payment_id=%s", (user_data["payment_id"],))
|
||||
is_exist = self.cur.fetchone()
|
||||
if is_exist:
|
||||
return "Failed. {} is being used by user {}".format(user_data["payment_id"], is_exist[0])
|
||||
self.cur.execute(sql, list(user_data.values()))
|
||||
self.con.commit()
|
||||
# also remove redis cache
|
||||
self.r.delete(user_data["user_id"])
|
||||
self.__add_vip(user_data)
|
||||
return "Success! You are VIP{} now!".format(user_data["level"])
|
||||
|
||||
def direct_add_vip(self, user_data: "dict") -> ("bool", "str"):
|
||||
self.__add_vip(user_data)
|
||||
return "Success payment from Telegram! You are VIP{} now!".format(user_data["level"])
|
||||
|
||||
def remove_vip(self, user_id: "int"):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_user_quota(self, user_id: "int") -> int:
|
||||
# even VIP have certain quota
|
||||
q = self.check_vip(user_id)
|
||||
return q[-1] if q else QUOTA
|
||||
topup = self.r.hget("topup", user_id)
|
||||
if q:
|
||||
return q[-1]
|
||||
elif topup:
|
||||
return int(topup) + QUOTA
|
||||
else:
|
||||
return QUOTA
|
||||
|
||||
def set_topup(self, user_id: "int"):
|
||||
self.r.hset("topup", user_id, QUOTA)
|
||||
|
||||
def check_remaining_quota(self, user_id: "int"):
|
||||
user_quota = self.get_user_quota(user_id)
|
||||
|
|
|
@ -32,12 +32,12 @@ from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
|
|||
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
|
||||
|
||||
from client_init import create_app
|
||||
from config import (ARCHIVE_ID, BROKER, ENABLE_CELERY,
|
||||
ENABLE_QUEUE, ENABLE_VIP, TG_MAX_SIZE, WORKERS)
|
||||
from config import (ARCHIVE_ID, BROKER, ENABLE_CELERY, ENABLE_QUEUE,
|
||||
ENABLE_VIP, TG_MAX_SIZE, WORKERS)
|
||||
from constant import BotText
|
||||
from db import Redis
|
||||
from downloader import (edit_text, sizeof_fmt, tqdm_progress,
|
||||
upload_hook, ytdl_download)
|
||||
from downloader import (edit_text, sizeof_fmt, tqdm_progress, upload_hook,
|
||||
ytdl_download)
|
||||
from limit import VIP
|
||||
from utils import (apply_log_formatter, auto_restart, customize_logger,
|
||||
get_metadata, get_revision, get_user_settings)
|
||||
|
@ -433,10 +433,12 @@ def run_celery():
|
|||
argv.extend(["-Q", worker_name])
|
||||
app.worker_main(argv)
|
||||
|
||||
|
||||
def purge_tasks():
|
||||
count = app.control.purge()
|
||||
return f"purged {count} tasks."
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
celery_client.start()
|
||||
print("Bootstrapping Celery worker now.....")
|
||||
|
|
|
@ -20,21 +20,24 @@ import pyrogram.errors
|
|||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from pyrogram import Client, filters, types
|
||||
from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant
|
||||
from pyrogram.raw import functions
|
||||
from pyrogram.raw import types as raw_types
|
||||
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from tgbot_ping import get_runtime
|
||||
from token_bucket import Limiter, MemoryStorage
|
||||
|
||||
from client_init import create_app
|
||||
from config import (AUTHORIZED_USER, BURST, ENABLE_CELERY, ENABLE_FFMPEG,
|
||||
ENABLE_VIP, OWNER, RATE, REQUIRED_MEMBERSHIP)
|
||||
ENABLE_VIP, MULTIPLY, OWNER, PROVIDER_TOKEN, QUOTA, RATE,
|
||||
REQUIRED_MEMBERSHIP)
|
||||
from constant import BotText
|
||||
from db import InfluxDB, MySQL, Redis
|
||||
from limit import VIP, verify_payment
|
||||
from tasks import app as celery_app
|
||||
from tasks import (audio_entrance, direct_download_entrance, hot_patch, purge_tasks,
|
||||
ytdl_download_entrance)
|
||||
from utils import (auto_restart, customize_logger, get_revision, clean_tempfile,
|
||||
get_user_settings, set_user_settings)
|
||||
from tasks import (audio_entrance, direct_download_entrance, hot_patch,
|
||||
purge_tasks, ytdl_download_entrance)
|
||||
from utils import (auto_restart, clean_tempfile, customize_logger,
|
||||
get_revision, get_user_settings, set_user_settings)
|
||||
|
||||
customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"])
|
||||
logging.getLogger('apscheduler.executors.default').propagate = False
|
||||
|
@ -187,13 +190,6 @@ def about_handler(client: "Client", message: "types.Message"):
|
|||
client.send_message(chat_id, bot_text.about)
|
||||
|
||||
|
||||
@app.on_message(filters.command(["terms"]))
|
||||
def terms_handler(client: "Client", message: "types.Message"):
|
||||
chat_id = message.chat.id
|
||||
client.send_chat_action(chat_id, "typing")
|
||||
client.send_message(chat_id, bot_text.terms)
|
||||
|
||||
|
||||
@app.on_message(filters.command(["sub_count"]))
|
||||
def sub_count_handler(client: "Client", message: "types.Message"):
|
||||
username = message.from_user.username
|
||||
|
@ -268,6 +264,53 @@ def vip_handler(client: "Client", message: "types.Message"):
|
|||
bm.edit_text(msg)
|
||||
|
||||
|
||||
def generate_invoice(amount: "int", title: "str", description: "str", payload: "bytes"):
|
||||
invoice = raw_types.input_media_invoice.InputMediaInvoice(
|
||||
invoice=(
|
||||
raw_types.invoice.Invoice(currency="USD", prices=[raw_types.LabeledPrice(label="price", amount=amount)])),
|
||||
title=title,
|
||||
description=description,
|
||||
provider=PROVIDER_TOKEN,
|
||||
provider_data=raw_types.DataJSON(data="{}"),
|
||||
payload=payload,
|
||||
)
|
||||
return invoice
|
||||
|
||||
|
||||
# payment related
|
||||
@app.on_message(filters.command(["topup"]))
|
||||
def topup_handler(client: "Client", message: "types.Message"):
|
||||
chat_id = message.chat.id
|
||||
client.send_chat_action(chat_id, "typing")
|
||||
invoice = generate_invoice(100, bot_text.topup_title, bot_text.topup_description,
|
||||
f"{message.chat.id}-topup".encode())
|
||||
|
||||
app.send(
|
||||
functions.messages.SendMedia(
|
||||
peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)),
|
||||
media=invoice,
|
||||
random_id=app.rnd_id(),
|
||||
message="Please use your card to pay for more traffic"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@app.on_message(filters.command(["tgvip"]))
|
||||
def tgvip_handler(client: "Client", message: "types.Message"):
|
||||
chat_id = message.chat.id
|
||||
client.send_chat_action(chat_id, "typing")
|
||||
invoice = generate_invoice(1000, f"VIP1", f"pay USD${MULTIPLY} for VIP1", f"{message.chat.id}-vip1".encode())
|
||||
|
||||
app.send(
|
||||
functions.messages.SendMedia(
|
||||
peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)),
|
||||
media=invoice,
|
||||
random_id=app.rnd_id(),
|
||||
message="Please use your card to pay for more traffic"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@app.on_message(filters.incoming & filters.text)
|
||||
@private_use
|
||||
def download_handler(client: "Client", message: "types.Message"):
|
||||
|
@ -284,25 +327,6 @@ def download_handler(client: "Client", message: "types.Message"):
|
|||
red.update_metrics("bad_request")
|
||||
message.reply_text("I think you should send me a link.", quote=True)
|
||||
return
|
||||
# TODO
|
||||
# red.update_metrics("search_request")
|
||||
# result = VideosSearch(url, limit=5).result().get("result", [])
|
||||
# text = ""
|
||||
# count = 1
|
||||
# buttons = []
|
||||
# for item in result:
|
||||
# text += f"{count}. {item['title']} - {item['link']}\n\n"
|
||||
# buttons.append(
|
||||
# InlineKeyboardButton(
|
||||
# f"{count}",
|
||||
# callback_data=f"search_{item['id']}"
|
||||
# )
|
||||
# )
|
||||
# count += 1
|
||||
#
|
||||
# markup = InlineKeyboardMarkup([buttons])
|
||||
# client.send_message(chat_id, text, disable_web_page_preview=True, reply_markup=markup)
|
||||
# return
|
||||
|
||||
if re.findall(r"^https://www\.youtube\.com/channel/", VIP.extract_canonical_link(url)) or "list" in url:
|
||||
message.reply_text("Channel/list download is disabled now. Please send me individual video link.", quote=True)
|
||||
|
@ -393,6 +417,38 @@ def periodic_sub_check():
|
|||
time.sleep(random.random() * 3)
|
||||
|
||||
|
||||
@app.on_raw_update()
|
||||
def raw_update(client: "Client", update, users, chats):
|
||||
action = getattr(getattr(update, "message", None), "action", None)
|
||||
if update.QUALNAME == 'types.UpdateBotPrecheckoutQuery':
|
||||
client.send(
|
||||
functions.messages.SetBotPrecheckoutResults(
|
||||
query_id=update.query_id,
|
||||
success=True,
|
||||
)
|
||||
)
|
||||
elif action and action.QUALNAME == 'types.MessageActionPaymentSentMe':
|
||||
logging.info("Payment received. %s", action)
|
||||
uid = update.message.peer_id.user_id
|
||||
vip = VIP()
|
||||
amount = f"{action.total_amount / 100} {action.currency}"
|
||||
if "vip" in action.payload.decode():
|
||||
ud = {
|
||||
"user_id": uid,
|
||||
"username": users.get(uid).username,
|
||||
"payment_amount": 10,
|
||||
"payment_id": 0,
|
||||
"level": 1,
|
||||
"quota": QUOTA * 2
|
||||
}
|
||||
vip.add_vip(ud)
|
||||
client.send_message(uid, f"Thank you {uid}. VIP payment received: {amount}")
|
||||
|
||||
else:
|
||||
vip.set_topup(uid)
|
||||
client.send_message(uid, f"Thank you {uid}. Top up payment received: {amount}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
MySQL()
|
||||
scheduler = BackgroundScheduler(timezone="Europe/Stockholm", job_defaults={'max_instances': 5})
|
||||
|
|
Loading…
Reference in a new issue