mirror of
https://github.com/tgbot-collection/ytdlbot.git
synced 2024-09-20 15:05:56 +08:00
let's try pyrogram 2.x again
This commit is contained in:
parent
acacfe58c9
commit
f7243a4a93
|
@ -1,4 +1,5 @@
|
||||||
env
|
env
|
||||||
|
venv
|
||||||
db_data
|
db_data
|
||||||
.ash_history
|
.ash_history
|
||||||
.DS_Store
|
.DS_Store
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -170,3 +170,6 @@ reinforcement/*
|
||||||
/ytdlbot/ytdl-main.session
|
/ytdlbot/ytdl-main.session
|
||||||
/ytdlbot/ytdl-celery.session-journal
|
/ytdlbot/ytdl-celery.session-journal
|
||||||
/ytdlbot/ytdl-celery.session
|
/ytdlbot/ytdl-celery.session
|
||||||
|
/ytdlbot/main.session
|
||||||
|
/ytdlbot/tasks.session
|
||||||
|
/ytdlbot/tasks.session-journal
|
||||||
|
|
0
.gitmodules
vendored
0
.gitmodules
vendored
|
@ -1,9 +0,0 @@
|
||||||
# This configuration file was automatically generated by Gitpod.
|
|
||||||
# Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml)
|
|
||||||
# and commit this file to your remote git repository to share the goodness with others.
|
|
||||||
|
|
||||||
# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- init: pip3 install -r requirements.txt
|
|
||||||
- init: sudo apt update && sudo apt install ffmpeg -y
|
|
11
Dockerfile
11
Dockerfile
|
@ -1,16 +1,13 @@
|
||||||
FROM python:3.11-alpine as builder
|
FROM python:3.11 as builder
|
||||||
|
|
||||||
RUN apk update && apk add --no-cache tzdata alpine-sdk libffi-dev ca-certificates
|
|
||||||
ADD requirements.txt /tmp/
|
ADD requirements.txt /tmp/
|
||||||
RUN pip3 install --user -r /tmp/requirements.txt && rm /tmp/requirements.txt
|
RUN pip3 install --user -r /tmp/requirements.txt && rm /tmp/requirements.txt
|
||||||
|
|
||||||
|
|
||||||
FROM python:3.11-alpine
|
FROM python:3.11-slim
|
||||||
WORKDIR /ytdlbot/ytdlbot
|
WORKDIR /ytdlbot/ytdlbot
|
||||||
ENV TZ=Europe/Stockholm
|
ENV TZ=Europe/London
|
||||||
|
|
||||||
COPY apk.txt /tmp/
|
RUN apt update && apt install -y --no-install-recommends --no-install-suggests ffmpeg vnstat git aria2
|
||||||
RUN apk update && xargs apk add < /tmp/apk.txt
|
|
||||||
COPY --from=builder /root/.local /usr/local
|
COPY --from=builder /root/.local /usr/local
|
||||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
||||||
|
|
13
README.md
13
README.md
|
@ -74,7 +74,7 @@ This bot can be deployed on any platform that supports Python.
|
||||||
To deploy this bot, follow these steps:
|
To deploy this bot, follow these steps:
|
||||||
|
|
||||||
1. Install bot dependencies
|
1. Install bot dependencies
|
||||||
* Install Python 3.8 or a later version, FFmpeg.
|
* Install Python 3.10 or a later version, FFmpeg.
|
||||||
* (optional)Aria2 and add it to the PATH.
|
* (optional)Aria2 and add it to the PATH.
|
||||||
|
|
||||||
2. Clone the code from the repository and cd into it.
|
2. Clone the code from the repository and cd into it.
|
||||||
|
@ -153,7 +153,6 @@ You can configure all the following environment variables:
|
||||||
* AUTHORIZED_USER: only authorized users can use the bot
|
* AUTHORIZED_USER: only authorized users can use the bot
|
||||||
* REQUIRED_MEMBERSHIP: group or channel username, user must join this group to use the bot
|
* REQUIRED_MEMBERSHIP: group or channel username, user must join this group to use the bot
|
||||||
* ENABLE_CELERY: celery mode, default: disable
|
* ENABLE_CELERY: celery mode, default: disable
|
||||||
* ENABLE_QUEUE: celery queue
|
|
||||||
* BROKER: celery broker, should be redis://redis:6379/0
|
* BROKER: celery broker, should be redis://redis:6379/0
|
||||||
* MYSQL_HOST:MySQL host
|
* MYSQL_HOST:MySQL host
|
||||||
* MYSQL_USER: MySQL username
|
* MYSQL_USER: MySQL username
|
||||||
|
@ -291,11 +290,11 @@ https://twitter.com/BennyThinks/status/1475836588542341124
|
||||||
|
|
||||||
## Test instagram
|
## Test instagram
|
||||||
|
|
||||||
https://www.instagram.com/p/ClBSqo3PkJw/
|
* single image: https://www.instagram.com/p/CXpxSyOrWCA/
|
||||||
|
* single video: https://www.instagram.com/p/Cah_7gnDVUW/
|
||||||
https://www.instagram.com/p/CaiAHoWDnrM/
|
* reels: https://www.instagram.com/p/C0ozGsjtY0W/
|
||||||
|
* image carousel: https://www.instagram.com/p/C0ozPQ5o536/
|
||||||
https://www.instagram.com/p/CZtUDyyv1u1/
|
* video and image carousel: https://www.instagram.com/p/C0ozhsVo-m8/
|
||||||
|
|
||||||
# Donation
|
# Donation
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
pyrogram==1.4.16
|
pyrogram==2.0.106
|
||||||
tgcrypto==1.2.5
|
tgcrypto==1.2.5
|
||||||
yt-dlp==2023.11.16
|
yt-dlp==2023.11.16
|
||||||
APScheduler==3.10.4
|
APScheduler==3.10.4
|
||||||
|
@ -11,7 +11,7 @@ flower==2.0.1
|
||||||
psutil==5.9.6
|
psutil==5.9.6
|
||||||
influxdb==5.3.1
|
influxdb==5.3.1
|
||||||
beautifulsoup4==4.12.2
|
beautifulsoup4==4.12.2
|
||||||
fakeredis==2.20.0
|
fakeredis==2.20.1
|
||||||
supervisor==4.2.5
|
supervisor==4.2.5
|
||||||
tgbot-ping==1.0.7
|
tgbot-ping==1.0.7
|
||||||
redis==5.0.1
|
redis==5.0.1
|
||||||
|
|
|
@ -12,14 +12,12 @@ from pyrogram import Client
|
||||||
from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, IPv6
|
from config import APP_HASH, APP_ID, PYRO_WORKERS, TOKEN, IPv6
|
||||||
|
|
||||||
|
|
||||||
def create_app(session: str, workers: int = PYRO_WORKERS) -> Client:
|
def create_app(name: str, workers: int = PYRO_WORKERS) -> Client:
|
||||||
_app = Client(
|
return Client(
|
||||||
session,
|
name,
|
||||||
APP_ID,
|
APP_ID,
|
||||||
APP_HASH,
|
APP_HASH,
|
||||||
bot_token=TOKEN,
|
bot_token=TOKEN,
|
||||||
workers=workers,
|
workers=workers,
|
||||||
ipv6=IPv6,
|
ipv6=IPv6,
|
||||||
)
|
)
|
||||||
|
|
||||||
return _app
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ import os
|
||||||
from blinker import signal
|
from blinker import signal
|
||||||
|
|
||||||
# general settings
|
# general settings
|
||||||
WORKERS: int = int(os.getenv("WORKERS", 100))
|
WORKERS: int = int(os.getenv("WORKERS", 10))
|
||||||
PYRO_WORKERS: int = int(os.getenv("PYRO_WORKERS", min(64, (os.cpu_count() + 4) * 10)))
|
PYRO_WORKERS: int = int(os.getenv("PYRO_WORKERS", min(32, (os.cpu_count() or 0) + 4)))
|
||||||
APP_ID: int = int(os.getenv("APP_ID", 198214))
|
APP_ID: int = int(os.getenv("APP_ID", 198214))
|
||||||
APP_HASH = os.getenv("APP_HASH", "1234b90")
|
APP_HASH = os.getenv("APP_HASH", "1234b90")
|
||||||
TOKEN = os.getenv("TOKEN", "1234")
|
TOKEN = os.getenv("TOKEN", "1234")
|
||||||
|
@ -36,7 +36,6 @@ REQUIRED_MEMBERSHIP: str = os.getenv("REQUIRED_MEMBERSHIP", "")
|
||||||
|
|
||||||
# celery related
|
# celery related
|
||||||
ENABLE_CELERY = os.getenv("ENABLE_CELERY", False)
|
ENABLE_CELERY = os.getenv("ENABLE_CELERY", False)
|
||||||
ENABLE_QUEUE = os.getenv("ENABLE_QUEUE", False)
|
|
||||||
BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/4")
|
BROKER = os.getenv("BROKER", f"redis://{REDIS}:6379/4")
|
||||||
|
|
||||||
MYSQL_HOST = os.getenv("MYSQL_HOST", "mysql")
|
MYSQL_HOST = os.getenv("MYSQL_HOST", "mysql")
|
||||||
|
@ -49,7 +48,6 @@ ARCHIVE_ID = os.getenv("ARCHIVE_ID")
|
||||||
IPv6 = os.getenv("IPv6", False)
|
IPv6 = os.getenv("IPv6", False)
|
||||||
ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False)
|
ENABLE_FFMPEG = os.getenv("ENABLE_FFMPEG", False)
|
||||||
|
|
||||||
|
|
||||||
PLAYLIST_SUPPORT = os.getenv("PLAYLIST_SUPPORT", False)
|
PLAYLIST_SUPPORT = os.getenv("PLAYLIST_SUPPORT", False)
|
||||||
M3U8_SUPPORT = os.getenv("M3U8_SUPPORT", False)
|
M3U8_SUPPORT = os.getenv("M3U8_SUPPORT", False)
|
||||||
ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False)
|
ENABLE_ARIA2 = os.getenv("ENABLE_ARIA2", False)
|
||||||
|
|
|
@ -75,7 +75,7 @@ class Redis:
|
||||||
self.r = redis.StrictRedis(host=REDIS, db=0, decode_responses=True)
|
self.r = redis.StrictRedis(host=REDIS, db=0, decode_responses=True)
|
||||||
self.r.ping()
|
self.r.ping()
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.warning("Redis connection failed, using fake redis instead.")
|
logging.debug("Redis connection failed, using fake redis instead.")
|
||||||
self.r = fakeredis.FakeStrictRedis(host=REDIS, db=0, decode_responses=True)
|
self.r = fakeredis.FakeStrictRedis(host=REDIS, db=0, decode_responses=True)
|
||||||
|
|
||||||
db_banner = "=" * 20 + "DB data" + "=" * 20
|
db_banner = "=" * 20 + "DB data" + "=" * 20
|
||||||
|
@ -256,7 +256,7 @@ class MySQL:
|
||||||
host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", charset="utf8mb4"
|
host=MYSQL_HOST, user=MYSQL_USER, passwd=MYSQL_PASS, db="ytdl", charset="utf8mb4"
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.warning("MySQL connection failed, using fake mysql instead.")
|
logging.debug("MySQL connection failed, using fake mysql instead.")
|
||||||
self.con = FakeMySQL()
|
self.con = FakeMySQL()
|
||||||
|
|
||||||
self.con.ping(reconnect=True)
|
self.con.ping(reconnect=True)
|
||||||
|
|
|
@ -24,15 +24,10 @@ import ffpb
|
||||||
import filetype
|
import filetype
|
||||||
import requests
|
import requests
|
||||||
import yt_dlp as ytdl
|
import yt_dlp as ytdl
|
||||||
|
from pyrogram import types
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from config import (
|
from config import AUDIO_FORMAT, ENABLE_ARIA2, ENABLE_FFMPEG, TG_MAX_SIZE, IPv6
|
||||||
AUDIO_FORMAT,
|
|
||||||
ENABLE_ARIA2,
|
|
||||||
ENABLE_FFMPEG,
|
|
||||||
TG_MAX_SIZE,
|
|
||||||
IPv6,
|
|
||||||
)
|
|
||||||
from limit import Payment
|
from limit import Payment
|
||||||
from utils import adjust_formats, apply_log_formatter, current_time, sizeof_fmt
|
from utils import adjust_formats, apply_log_formatter, current_time, sizeof_fmt
|
||||||
|
|
||||||
|
@ -40,8 +35,8 @@ r = fakeredis.FakeStrictRedis()
|
||||||
apply_log_formatter()
|
apply_log_formatter()
|
||||||
|
|
||||||
|
|
||||||
def edit_text(bot_msg, text: str):
|
def edit_text(bot_msg: types.Message, text: str):
|
||||||
key = f"{bot_msg.chat.id}-{bot_msg.message_id}"
|
key = f"{bot_msg.chat.id}-{bot_msg.id}"
|
||||||
# if the key exists, we shouldn't send edit message
|
# if the key exists, we shouldn't send edit message
|
||||||
if not r.exists(key):
|
if not r.exists(key):
|
||||||
time.sleep(random.random())
|
time.sleep(random.random())
|
||||||
|
@ -87,7 +82,7 @@ def remove_bash_color(text):
|
||||||
|
|
||||||
|
|
||||||
def download_hook(d: dict, bot_msg):
|
def download_hook(d: dict, bot_msg):
|
||||||
# since we're using celery, server location may be located in different continent.
|
# since we're using celery, server location may be located in different region.
|
||||||
# Therefore, we can't trigger the hook very often.
|
# Therefore, we can't trigger the hook very often.
|
||||||
# the key is user_id + download_link
|
# the key is user_id + download_link
|
||||||
original_url = d["info_dict"]["original_url"]
|
original_url = d["info_dict"]["original_url"]
|
||||||
|
|
|
@ -155,8 +155,8 @@ class TronTrx:
|
||||||
|
|
||||||
cur.execute("select user_id, payment_id from payment where payment_id like 'tron,0,T%'")
|
cur.execute("select user_id, payment_id from payment where payment_id like 'tron,0,T%'")
|
||||||
data = cur.fetchall()
|
data = cur.fetchall()
|
||||||
logging.info("Checking unpaid payment...%s", data)
|
|
||||||
for row in data:
|
for row in data:
|
||||||
|
logging.info("Checking user payment %s", row)
|
||||||
user_id = row[0]
|
user_id = row[0]
|
||||||
addr, index = row[1].split(",")[2:]
|
addr, index = row[1].split(",")[2:]
|
||||||
try:
|
try:
|
||||||
|
|
186
ytdlbot/tasks.py
186
ytdlbot/tasks.py
|
@ -7,11 +7,10 @@
|
||||||
|
|
||||||
__author__ = "Benny <benny.think@gmail.com>"
|
__author__ = "Benny <benny.think@gmail.com>"
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import math
|
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import random
|
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -20,7 +19,7 @@ import threading
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import typing
|
import typing
|
||||||
from hashlib import md5
|
from typing import Any
|
||||||
from urllib.parse import quote_plus
|
from urllib.parse import quote_plus
|
||||||
|
|
||||||
import filetype
|
import filetype
|
||||||
|
@ -30,9 +29,7 @@ import requests
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from celery import Celery
|
from celery import Celery
|
||||||
from celery.worker.control import Panel
|
from celery.worker.control import Panel
|
||||||
from pyrogram import Client, idle, types
|
from pyrogram import Client, enums, idle, types
|
||||||
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
|
|
||||||
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
|
|
||||||
|
|
||||||
from channel import Channel
|
from channel import Channel
|
||||||
from client_init import create_app
|
from client_init import create_app
|
||||||
|
@ -40,7 +37,6 @@ from config import (
|
||||||
ARCHIVE_ID,
|
ARCHIVE_ID,
|
||||||
BROKER,
|
BROKER,
|
||||||
ENABLE_CELERY,
|
ENABLE_CELERY,
|
||||||
ENABLE_QUEUE,
|
|
||||||
ENABLE_VIP,
|
ENABLE_VIP,
|
||||||
OWNER,
|
OWNER,
|
||||||
RATE_LIMIT,
|
RATE_LIMIT,
|
||||||
|
@ -66,37 +62,33 @@ apply_log_formatter()
|
||||||
bot_text = BotText()
|
bot_text = BotText()
|
||||||
logging.getLogger("apscheduler.executors.default").propagate = False
|
logging.getLogger("apscheduler.executors.default").propagate = False
|
||||||
|
|
||||||
# celery -A tasks worker --loglevel=info --pool=solo
|
|
||||||
# app = Celery('celery', broker=BROKER, accept_content=['pickle'], task_serializer='pickle')
|
|
||||||
app = Celery("tasks", broker=BROKER)
|
app = Celery("tasks", broker=BROKER)
|
||||||
|
bot = create_app("tasks")
|
||||||
channel = Channel()
|
channel = Channel()
|
||||||
|
|
||||||
session = "ytdl-celery"
|
|
||||||
celery_client = create_app(session)
|
|
||||||
|
|
||||||
|
def get_messages(chat_id: int, message_id: int):
|
||||||
def get_messages(chat_id, message_id):
|
|
||||||
try:
|
try:
|
||||||
return celery_client.get_messages(chat_id, message_id)
|
return bot.get_messages(chat_id, message_id)
|
||||||
except ConnectionError as e:
|
except ConnectionError as e:
|
||||||
logging.critical("WTH!!! %s", e)
|
logging.critical("CRITICAL ERROR: %s", e)
|
||||||
celery_client.start()
|
bot.start()
|
||||||
return celery_client.get_messages(chat_id, message_id)
|
return bot.get_messages(chat_id, message_id)
|
||||||
|
|
||||||
|
|
||||||
@app.task(rate_limit=f"{RATE_LIMIT}/m")
|
@app.task(rate_limit=f"{RATE_LIMIT}/m")
|
||||||
def ytdl_download_task(chat_id, message_id, url: str):
|
def ytdl_download_task(chat_id: int, message_id: int, url: str):
|
||||||
logging.info("YouTube celery tasks started for %s", url)
|
logging.info("YouTube celery tasks started for %s", url)
|
||||||
bot_msg = get_messages(chat_id, message_id)
|
bot_msg = get_messages(chat_id, message_id)
|
||||||
ytdl_normal_download(celery_client, bot_msg, url)
|
ytdl_normal_download(bot_msg, url)
|
||||||
logging.info("YouTube celery tasks ended.")
|
logging.info("YouTube celery tasks ended.")
|
||||||
|
|
||||||
|
|
||||||
@app.task()
|
@app.task()
|
||||||
def audio_task(chat_id, message_id):
|
def audio_task(chat_id: int, message_id: int):
|
||||||
logging.info("Audio celery tasks started for %s-%s", chat_id, message_id)
|
logging.info("Audio celery tasks started for %s-%s", chat_id, message_id)
|
||||||
bot_msg = get_messages(chat_id, message_id)
|
bot_msg = get_messages(chat_id, message_id)
|
||||||
normal_audio(celery_client, bot_msg)
|
normal_audio(bot_msg)
|
||||||
logging.info("Audio celery tasks ended.")
|
logging.info("Audio celery tasks ended.")
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,14 +105,14 @@ def get_unique_clink(original_url: str, user_id: int):
|
||||||
|
|
||||||
|
|
||||||
@app.task()
|
@app.task()
|
||||||
def direct_download_task(chat_id, message_id, url):
|
def direct_download_task(chat_id: int, message_id: int, url: str):
|
||||||
logging.info("Direct download celery tasks started for %s", url)
|
logging.info("Direct download celery tasks started for %s", url)
|
||||||
bot_msg = get_messages(chat_id, message_id)
|
bot_msg = get_messages(chat_id, message_id)
|
||||||
direct_normal_download(celery_client, bot_msg, url)
|
direct_normal_download(bot, bot_msg, url)
|
||||||
logging.info("Direct download celery tasks ended.")
|
logging.info("Direct download celery tasks ended.")
|
||||||
|
|
||||||
|
|
||||||
def forward_video(client, bot_msg, url: str):
|
def forward_video(bot_msg: types.Message, url: str):
|
||||||
redis = Redis()
|
redis = Redis()
|
||||||
chat_id = bot_msg.chat.id
|
chat_id = bot_msg.chat.id
|
||||||
unique = get_unique_clink(url, chat_id)
|
unique = get_unique_clink(url, chat_id)
|
||||||
|
@ -129,28 +121,27 @@ def forward_video(client, bot_msg, url: str):
|
||||||
redis.update_metrics("cache_miss")
|
redis.update_metrics("cache_miss")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
res_msg: "Message" = upload_processor(client, bot_msg, url, cached_fid)
|
res_msg = upload_processor(bot_msg, url, cached_fid)
|
||||||
obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation or res_msg.photo
|
obj = res_msg.document or res_msg.video or res_msg.audio or res_msg.animation or res_msg.photo
|
||||||
|
|
||||||
caption, _ = gen_cap(bot_msg, url, obj)
|
caption, _ = gen_cap(bot_msg, url, obj)
|
||||||
res_msg.edit_text(caption, reply_markup=gen_video_markup())
|
res_msg.edit_text(caption, reply_markup=gen_video_markup())
|
||||||
bot_msg.edit_text(f"Download success!✅✅✅")
|
bot_msg.edit_text(f"Download success!✅")
|
||||||
redis.update_metrics("cache_hit")
|
redis.update_metrics("cache_hit")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mode=None):
|
def ytdl_download_entrance(bot_msg: types.Message, url: str, mode=None):
|
||||||
payment = Payment()
|
payment = Payment()
|
||||||
chat_id = bot_msg.chat.id
|
chat_id = bot_msg.chat.id
|
||||||
try:
|
try:
|
||||||
if forward_video(client, bot_msg, url):
|
if forward_video(bot_msg, url):
|
||||||
return
|
return
|
||||||
mode = mode or payment.get_user_settings(chat_id)[-1]
|
mode = mode or payment.get_user_settings(chat_id)[-1]
|
||||||
if ENABLE_CELERY and mode in [None, "Celery"]:
|
if ENABLE_CELERY and mode in [None, "Celery"]:
|
||||||
async_task(ytdl_download_task, chat_id, bot_msg.message_id, url)
|
ytdl_download_task.delay(chat_id, bot_msg.id, url)
|
||||||
# ytdl_download_task.delay(chat_id, bot_msg.message_id, url)
|
|
||||||
else:
|
else:
|
||||||
ytdl_normal_download(client, bot_msg, url)
|
ytdl_normal_download(bot_msg, url)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Failed to download %s, error: %s", url, e)
|
logging.error("Failed to download %s, error: %s", url, e)
|
||||||
bot_msg.edit_text(f"Download failed!❌\n\n`{traceback.format_exc()[0:4000]}`", disable_web_page_preview=True)
|
bot_msg.edit_text(f"Download failed!❌\n\n`{traceback.format_exc()[0:4000]}`", disable_web_page_preview=True)
|
||||||
|
@ -159,23 +150,22 @@ def ytdl_download_entrance(client: Client, bot_msg: types.Message, url: str, mod
|
||||||
def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str):
|
def direct_download_entrance(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str):
|
||||||
if ENABLE_CELERY:
|
if ENABLE_CELERY:
|
||||||
direct_normal_download(client, bot_msg, url)
|
direct_normal_download(client, bot_msg, url)
|
||||||
# direct_download_task.delay(bot_msg.chat.id, bot_msg.message_id, url)
|
# direct_download_task.delay(bot_msg.chat.id, bot_msg.id, url)
|
||||||
else:
|
else:
|
||||||
direct_normal_download(client, bot_msg, url)
|
direct_normal_download(client, bot_msg, url)
|
||||||
|
|
||||||
|
|
||||||
def audio_entrance(client, bot_msg):
|
def audio_entrance(bot_msg: types.Message):
|
||||||
if ENABLE_CELERY:
|
if ENABLE_CELERY:
|
||||||
async_task(audio_task, bot_msg.chat.id, bot_msg.message_id)
|
audio_task.delay(bot_msg.chat.id, bot_msg.id)
|
||||||
# audio_task.delay(bot_msg.chat.id, bot_msg.message_id)
|
|
||||||
else:
|
else:
|
||||||
normal_audio(client, bot_msg)
|
normal_audio(bot_msg)
|
||||||
|
|
||||||
|
|
||||||
def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str):
|
def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str):
|
||||||
chat_id = bot_msg.chat.id
|
chat_id = bot_msg.chat.id
|
||||||
headers = {
|
headers = {
|
||||||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"
|
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.3987.149 Safari/537.36"
|
||||||
}
|
}
|
||||||
length = 0
|
length = 0
|
||||||
|
|
||||||
|
@ -206,7 +196,7 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message,
|
||||||
logging.info("Downloaded file %s", filename)
|
logging.info("Downloaded file %s", filename)
|
||||||
st_size = os.stat(filepath).st_size
|
st_size = os.stat(filepath).st_size
|
||||||
|
|
||||||
client.send_chat_action(chat_id, "upload_document")
|
client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT)
|
||||||
client.send_document(
|
client.send_document(
|
||||||
bot_msg.chat.id,
|
bot_msg.chat.id,
|
||||||
filepath,
|
filepath,
|
||||||
|
@ -217,7 +207,7 @@ def direct_normal_download(client: Client, bot_msg: typing.Union[types.Message,
|
||||||
bot_msg.edit_text("Download success!✅")
|
bot_msg.edit_text("Download success!✅")
|
||||||
|
|
||||||
|
|
||||||
def normal_audio(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine]):
|
def normal_audio(bot_msg: typing.Union[types.Message, typing.Coroutine]):
|
||||||
chat_id = bot_msg.chat.id
|
chat_id = bot_msg.chat.id
|
||||||
# fn = getattr(bot_msg.video, "file_name", None) or getattr(bot_msg.document, "file_name", None)
|
# fn = getattr(bot_msg.video, "file_name", None) or getattr(bot_msg.document, "file_name", None)
|
||||||
status_msg: typing.Union[types.Message, typing.Coroutine] = bot_msg.reply_text(
|
status_msg: typing.Union[types.Message, typing.Coroutine] = bot_msg.reply_text(
|
||||||
|
@ -225,59 +215,36 @@ def normal_audio(client: Client, bot_msg: typing.Union[types.Message, typing.Cor
|
||||||
)
|
)
|
||||||
orig_url: str = re.findall(r"https?://.*", bot_msg.caption)[0]
|
orig_url: str = re.findall(r"https?://.*", bot_msg.caption)[0]
|
||||||
with tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) as tmp:
|
with tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH) as tmp:
|
||||||
client.send_chat_action(chat_id, "record_audio")
|
bot.send_chat_action(chat_id, enums.ChatAction.RECORD_AUDIO)
|
||||||
# just try to download the audio using yt-dlp
|
# just try to download the audio using yt-dlp
|
||||||
filepath = ytdl_download(orig_url, tmp, status_msg, hijack="bestaudio[ext=m4a]")
|
filepath = ytdl_download(orig_url, tmp, status_msg, hijack="bestaudio[ext=m4a]")
|
||||||
status_msg.edit_text("Sending audio now...")
|
status_msg.edit_text("Sending audio now...")
|
||||||
client.send_chat_action(chat_id, "upload_audio")
|
bot.send_chat_action(chat_id, enums.ChatAction.UPLOAD_AUDIO)
|
||||||
for f in filepath:
|
for f in filepath:
|
||||||
client.send_audio(chat_id, f)
|
bot.send_audio(chat_id, f)
|
||||||
status_msg.edit_text("✅ Conversion complete.")
|
status_msg.edit_text("✅ Conversion complete.")
|
||||||
Redis().update_metrics("audio_success")
|
Redis().update_metrics("audio_success")
|
||||||
|
|
||||||
|
|
||||||
def get_dl_source():
|
def ytdl_normal_download(bot_msg: types.Message | typing.Any, url: str):
|
||||||
worker_name = os.getenv("WORKER_NAME")
|
|
||||||
if worker_name:
|
|
||||||
return f"Downloaded by {worker_name}"
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def upload_transfer_sh(bm, paths: list) -> str:
|
|
||||||
d = {p.name: (md5(p.name.encode("utf8")).hexdigest() + p.suffix, p.open("rb")) for p in paths}
|
|
||||||
monitor = MultipartEncoderMonitor(MultipartEncoder(fields=d), lambda x: upload_hook(x.bytes_read, x.len, bm))
|
|
||||||
headers = {"Content-Type": monitor.content_type}
|
|
||||||
try:
|
|
||||||
req = requests.post("https://transfer.sh", data=monitor, headers=headers)
|
|
||||||
bm.edit_text(f"Download success!✅")
|
|
||||||
return re.sub(r"https://", "\nhttps://", req.text)
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
return f"Upload failed!❌\n\n```{e}```"
|
|
||||||
|
|
||||||
|
|
||||||
def flood_owner_message(client, ex):
|
|
||||||
client.send_message(OWNER, f"CRITICAL INFO: {ex}")
|
|
||||||
|
|
||||||
|
|
||||||
def ytdl_normal_download(client: Client, bot_msg: typing.Union[types.Message, typing.Coroutine], url: str):
|
|
||||||
chat_id = bot_msg.chat.id
|
chat_id = bot_msg.chat.id
|
||||||
temp_dir = tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH)
|
temp_dir = tempfile.TemporaryDirectory(prefix="ytdl-", dir=TMPFILE_PATH)
|
||||||
|
|
||||||
video_paths = ytdl_download(url, temp_dir.name, bot_msg)
|
video_paths = ytdl_download(url, temp_dir.name, bot_msg)
|
||||||
logging.info("Download complete.")
|
logging.info("Download complete.")
|
||||||
client.send_chat_action(chat_id, "upload_document")
|
bot.send_chat_action(chat_id, enums.ChatAction.UPLOAD_DOCUMENT)
|
||||||
bot_msg.edit_text("Download complete. Sending now...")
|
bot_msg.edit_text("Download complete. Sending now...")
|
||||||
try:
|
try:
|
||||||
upload_processor(client, bot_msg, url, video_paths)
|
upload_processor(bot_msg, url, video_paths)
|
||||||
except pyrogram.errors.Flood as e:
|
except pyrogram.errors.Flood as e:
|
||||||
logging.critical("FloodWait from Telegram: %s", e)
|
logging.critical("FloodWait from Telegram: %s", e)
|
||||||
client.send_message(
|
bot.send_message(
|
||||||
chat_id,
|
chat_id,
|
||||||
f"I'm being rate limited by Telegram. Your video will come after {e.x} seconds. Please wait patiently.",
|
f"I'm being rate limited by Telegram. Your video will come after {e} seconds. Please wait patiently.",
|
||||||
)
|
)
|
||||||
flood_owner_message(client, e)
|
bot.send_message(OWNER, f"CRITICAL INFO: {e}")
|
||||||
time.sleep(e.x)
|
time.sleep(e.value)
|
||||||
upload_processor(client, bot_msg, url, video_paths)
|
upload_processor(bot_msg, url, video_paths)
|
||||||
|
|
||||||
bot_msg.edit_text("Download success!✅")
|
bot_msg.edit_text("Download success!✅")
|
||||||
|
|
||||||
|
@ -306,7 +273,7 @@ def generate_input_media(file_paths: list, cap: str) -> list:
|
||||||
return input_media
|
return input_media
|
||||||
|
|
||||||
|
|
||||||
def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]):
|
def upload_processor(bot_msg: types.Message, url: str, vp_or_fid: str | list):
|
||||||
redis = Redis()
|
redis = Redis()
|
||||||
# raise pyrogram.errors.exceptions.FloodWait(13)
|
# raise pyrogram.errors.exceptions.FloodWait(13)
|
||||||
# if is str, it's a file id; else it's a list of paths
|
# if is str, it's a file id; else it's a list of paths
|
||||||
|
@ -316,7 +283,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]):
|
||||||
if isinstance(vp_or_fid, list) and len(vp_or_fid) > 1:
|
if isinstance(vp_or_fid, list) and len(vp_or_fid) > 1:
|
||||||
# just generate the first for simplicity, send as media group(2-20)
|
# just generate the first for simplicity, send as media group(2-20)
|
||||||
cap, meta = gen_cap(bot_msg, url, vp_or_fid[0])
|
cap, meta = gen_cap(bot_msg, url, vp_or_fid[0])
|
||||||
res_msg = client.send_media_group(chat_id, generate_input_media(vp_or_fid, cap))
|
res_msg: list["types.Message"] | Any = bot.send_media_group(chat_id, generate_input_media(vp_or_fid, cap))
|
||||||
# TODO no cache for now
|
# TODO no cache for now
|
||||||
return res_msg[0]
|
return res_msg[0]
|
||||||
elif isinstance(vp_or_fid, list) and len(vp_or_fid) == 1:
|
elif isinstance(vp_or_fid, list) and len(vp_or_fid) == 1:
|
||||||
|
@ -335,7 +302,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]):
|
||||||
logging.info("Sending as document")
|
logging.info("Sending as document")
|
||||||
try:
|
try:
|
||||||
# send as document could be sent as video even if it's a document
|
# send as document could be sent as video even if it's a document
|
||||||
res_msg = client.send_document(
|
res_msg = bot.send_document(
|
||||||
chat_id,
|
chat_id,
|
||||||
vp_or_fid,
|
vp_or_fid,
|
||||||
caption=cap,
|
caption=cap,
|
||||||
|
@ -347,7 +314,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]):
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logging.error("Retry to send as video")
|
logging.error("Retry to send as video")
|
||||||
res_msg = client.send_video(
|
res_msg = bot.send_video(
|
||||||
chat_id,
|
chat_id,
|
||||||
vp_or_fid,
|
vp_or_fid,
|
||||||
supports_streaming=True,
|
supports_streaming=True,
|
||||||
|
@ -359,7 +326,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]):
|
||||||
)
|
)
|
||||||
elif settings[2] == "audio":
|
elif settings[2] == "audio":
|
||||||
logging.info("Sending as audio")
|
logging.info("Sending as audio")
|
||||||
res_msg = client.send_audio(
|
res_msg = bot.send_audio(
|
||||||
chat_id,
|
chat_id,
|
||||||
vp_or_fid,
|
vp_or_fid,
|
||||||
caption=cap,
|
caption=cap,
|
||||||
|
@ -370,7 +337,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]):
|
||||||
# settings==video
|
# settings==video
|
||||||
logging.info("Sending as video")
|
logging.info("Sending as video")
|
||||||
try:
|
try:
|
||||||
res_msg = client.send_video(
|
res_msg = bot.send_video(
|
||||||
chat_id,
|
chat_id,
|
||||||
vp_or_fid,
|
vp_or_fid,
|
||||||
supports_streaming=True,
|
supports_streaming=True,
|
||||||
|
@ -384,7 +351,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]):
|
||||||
# try to send as annimation, photo
|
# try to send as annimation, photo
|
||||||
try:
|
try:
|
||||||
logging.warning("Retry to send as animation")
|
logging.warning("Retry to send as animation")
|
||||||
res_msg = client.send_animation(
|
res_msg = bot.send_animation(
|
||||||
chat_id,
|
chat_id,
|
||||||
vp_or_fid,
|
vp_or_fid,
|
||||||
caption=cap,
|
caption=cap,
|
||||||
|
@ -396,7 +363,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]):
|
||||||
except Exception:
|
except Exception:
|
||||||
# this is likely a photo
|
# this is likely a photo
|
||||||
logging.warning("Retry to send as photo")
|
logging.warning("Retry to send as photo")
|
||||||
res_msg = client.send_photo(
|
res_msg = bot.send_photo(
|
||||||
chat_id,
|
chat_id,
|
||||||
vp_or_fid,
|
vp_or_fid,
|
||||||
caption=cap,
|
caption=cap,
|
||||||
|
@ -409,7 +376,7 @@ def upload_processor(client, bot_msg, url, vp_or_fid: typing.Union[str, list]):
|
||||||
redis.add_send_cache(unique, getattr(obj, "file_id", None))
|
redis.add_send_cache(unique, getattr(obj, "file_id", None))
|
||||||
redis.update_metrics("video_success")
|
redis.update_metrics("video_success")
|
||||||
if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path):
|
if ARCHIVE_ID and isinstance(vp_or_fid, pathlib.Path):
|
||||||
client.forward_messages(bot_msg.chat.id, ARCHIVE_ID, res_msg.message_id)
|
bot.forward_messages(bot_msg.chat.id, ARCHIVE_ID, res_msg.id)
|
||||||
return res_msg
|
return res_msg
|
||||||
|
|
||||||
|
|
||||||
|
@ -441,7 +408,11 @@ def gen_cap(bm, url, video_path):
|
||||||
remain = f"Download token count: free {free}, pay {pay}"
|
remain = f"Download token count: free {free}, pay {pay}"
|
||||||
else:
|
else:
|
||||||
remain = ""
|
remain = ""
|
||||||
worker = get_dl_source()
|
|
||||||
|
if worker_name := os.getenv("WORKER_NAME"):
|
||||||
|
worker = f"Downloaded by {worker_name}"
|
||||||
|
else:
|
||||||
|
worker = ""
|
||||||
cap = (
|
cap = (
|
||||||
f"{user_info}\n{file_name}\n\n{url}\n\nInfo: {meta['width']}x{meta['height']} {file_size}\t"
|
f"{user_info}\n{file_name}\n\n{url}\n\nInfo: {meta['width']}x{meta['height']} {file_size}\t"
|
||||||
f"{meta['duration']}s\n{remain}\n{worker}\n{bot_text.custom_text}"
|
f"{meta['duration']}s\n{remain}\n{worker}\n{bot_text.custom_text}"
|
||||||
|
@ -450,10 +421,10 @@ def gen_cap(bm, url, video_path):
|
||||||
|
|
||||||
|
|
||||||
def gen_video_markup():
|
def gen_video_markup():
|
||||||
markup = InlineKeyboardMarkup(
|
markup = types.InlineKeyboardMarkup(
|
||||||
[
|
[
|
||||||
[ # First row
|
[ # First row
|
||||||
InlineKeyboardButton( # Generates a callback query when pressed
|
types.InlineKeyboardButton( # Generates a callback query when pressed
|
||||||
"convert to audio", callback_data="convert"
|
"convert to audio", callback_data="convert"
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
@ -472,7 +443,6 @@ def hot_patch(*args):
|
||||||
app_path = pathlib.Path().cwd().parent
|
app_path = pathlib.Path().cwd().parent
|
||||||
logging.info("Hot patching on path %s...", app_path)
|
logging.info("Hot patching on path %s...", app_path)
|
||||||
|
|
||||||
apk_install = "xargs apk add < apk.txt"
|
|
||||||
pip_install = "pip install -r requirements.txt"
|
pip_install = "pip install -r requirements.txt"
|
||||||
unset = "git config --unset http.https://github.com/.extraheader"
|
unset = "git config --unset http.https://github.com/.extraheader"
|
||||||
pull_unshallow = "git pull origin --unshallow"
|
pull_unshallow = "git pull origin --unshallow"
|
||||||
|
@ -484,52 +454,32 @@ def hot_patch(*args):
|
||||||
subprocess.call(pull, shell=True, cwd=app_path)
|
subprocess.call(pull, shell=True, cwd=app_path)
|
||||||
|
|
||||||
logging.info("Code is updated, applying hot patch now...")
|
logging.info("Code is updated, applying hot patch now...")
|
||||||
subprocess.call(apk_install, shell=True, cwd=app_path)
|
|
||||||
subprocess.call(pip_install, shell=True, cwd=app_path)
|
subprocess.call(pip_install, shell=True, cwd=app_path)
|
||||||
psutil.Process().kill()
|
psutil.Process().kill()
|
||||||
|
|
||||||
|
|
||||||
def async_task(task_name, *args):
|
|
||||||
if not ENABLE_QUEUE:
|
|
||||||
task_name.delay(*args)
|
|
||||||
return
|
|
||||||
|
|
||||||
t0 = time.time()
|
|
||||||
inspect = app.control.inspect()
|
|
||||||
worker_stats = inspect.stats()
|
|
||||||
route_queues = []
|
|
||||||
padding = math.ceil(sum([i["pool"]["max-concurrency"] for i in worker_stats.values()]) / len(worker_stats))
|
|
||||||
for worker_name, stats in worker_stats.items():
|
|
||||||
route = worker_name.split("@")[1]
|
|
||||||
concurrency = stats["pool"]["max-concurrency"]
|
|
||||||
route_queues.extend([route] * (concurrency + padding))
|
|
||||||
destination = random.choice(route_queues)
|
|
||||||
logging.info("Selecting worker %s from %s in %.2fs", destination, route_queues, time.time() - t0)
|
|
||||||
task_name.apply_async(args=args, queue=destination)
|
|
||||||
|
|
||||||
|
|
||||||
def run_celery():
|
|
||||||
worker_name = os.getenv("WORKER_NAME", "")
|
|
||||||
argv = ["-A", "tasks", "worker", "--loglevel=info", "--pool=threads", f"--concurrency={WORKERS}", "-n", worker_name]
|
|
||||||
if ENABLE_QUEUE:
|
|
||||||
argv.extend(["-Q", worker_name])
|
|
||||||
app.worker_main(argv)
|
|
||||||
|
|
||||||
|
|
||||||
def purge_tasks():
|
def purge_tasks():
|
||||||
count = app.control.purge()
|
count = app.control.purge()
|
||||||
return f"purged {count} tasks."
|
return f"purged {count} tasks."
|
||||||
|
|
||||||
|
|
||||||
|
def run_celery():
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
worker_name = os.getenv("WORKER_NAME", "")
|
||||||
|
argv = ["-A", "tasks", "worker", "--loglevel=info", "--pool=threads", f"--concurrency={WORKERS}", "-n", worker_name]
|
||||||
|
app.worker_main(argv)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# celery_client.start()
|
bot.start()
|
||||||
print("Bootstrapping Celery worker now.....")
|
print("Bootstrapping Celery worker now.....")
|
||||||
time.sleep(5)
|
time.sleep(3)
|
||||||
threading.Thread(target=run_celery, daemon=True).start()
|
threading.Thread(target=run_celery, daemon=True).start()
|
||||||
|
|
||||||
scheduler = BackgroundScheduler(timezone="Asia/Shanghai")
|
scheduler = BackgroundScheduler(timezone="Europe/London")
|
||||||
scheduler.add_job(auto_restart, "interval", seconds=900)
|
scheduler.add_job(auto_restart, "interval", seconds=900)
|
||||||
scheduler.start()
|
scheduler.start()
|
||||||
|
|
||||||
idle()
|
idle()
|
||||||
celery_client.stop()
|
bot.stop()
|
||||||
|
|
|
@ -166,7 +166,6 @@ class Detector:
|
||||||
"types.UpdatesTooLong",
|
"types.UpdatesTooLong",
|
||||||
"Got shutdown from remote",
|
"Got shutdown from remote",
|
||||||
"Code is updated",
|
"Code is updated",
|
||||||
'Retrying "messages.GetMessages"',
|
|
||||||
"OSError: Connection lost",
|
"OSError: Connection lost",
|
||||||
"[Errno -3] Try again",
|
"[Errno -3] Try again",
|
||||||
"MISCONF",
|
"MISCONF",
|
||||||
|
@ -179,7 +178,7 @@ class Detector:
|
||||||
|
|
||||||
def next_salt_detector(self):
|
def next_salt_detector(self):
|
||||||
text = "Next salt in"
|
text = "Next salt in"
|
||||||
if self.logs.count(text) >= 4:
|
if self.logs.count(text) >= 5:
|
||||||
logging.critical("Next salt crash: %s", self.func_name())
|
logging.critical("Next salt crash: %s", self.func_name())
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -187,17 +186,17 @@ class Detector:
|
||||||
text = "The msg_id is too low"
|
text = "The msg_id is too low"
|
||||||
if text in self.logs:
|
if text in self.logs:
|
||||||
logging.critical("msg id crash: %s ", self.func_name())
|
logging.critical("msg id crash: %s ", self.func_name())
|
||||||
for item in pathlib.Path(__file__).parent.glob("ytdl-*"):
|
for item in pathlib.Path(__file__).parent.glob("*.session"):
|
||||||
|
logging.warning("Removing session file: %s", item)
|
||||||
item.unlink(missing_ok=True)
|
item.unlink(missing_ok=True)
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# def idle_detector(self):
|
def sqlite_locked_detector(self):
|
||||||
# mtime = os.stat("/var/log/ytdl.log").st_mtime
|
text = "sqlite3.OperationalError: database is locked"
|
||||||
# cur_ts = time.time()
|
if text in self.logs:
|
||||||
# if cur_ts - mtime > 7200:
|
logging.critical("database is locked: %s ", self.func_name())
|
||||||
# logging.warning("Potential crash detected by %s, it's time to commit suicide...", self.func_name())
|
return True
|
||||||
# return True
|
|
||||||
|
|
||||||
|
|
||||||
def auto_restart():
|
def auto_restart():
|
||||||
|
|
|
@ -15,18 +15,17 @@ import re
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import typing
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import pyrogram.errors
|
import pyrogram.errors
|
||||||
import qrcode
|
import qrcode
|
||||||
import yt_dlp
|
import yt_dlp
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from pyrogram import Client, filters, types
|
from pyrogram import Client, enums, filters, types
|
||||||
from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant
|
from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant
|
||||||
from pyrogram.raw import functions
|
from pyrogram.raw import functions
|
||||||
from pyrogram.raw import types as raw_types
|
from pyrogram.raw import types as raw_types
|
||||||
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
|
||||||
from tgbot_ping import get_runtime
|
from tgbot_ping import get_runtime
|
||||||
from youtubesearchpython import VideosSearch
|
from youtubesearchpython import VideosSearch
|
||||||
|
|
||||||
|
@ -59,12 +58,11 @@ from tasks import (
|
||||||
)
|
)
|
||||||
from utils import auto_restart, clean_tempfile, customize_logger, get_revision
|
from utils import auto_restart, clean_tempfile, customize_logger, get_revision
|
||||||
|
|
||||||
|
logging.info("Authorized users are %s", AUTHORIZED_USER)
|
||||||
customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"])
|
customize_logger(["pyrogram.client", "pyrogram.session.session", "pyrogram.connection.connection"])
|
||||||
logging.getLogger("apscheduler.executors.default").propagate = False
|
logging.getLogger("apscheduler.executors.default").propagate = False
|
||||||
|
|
||||||
app = create_app(":memory:")
|
app = create_app("main")
|
||||||
|
|
||||||
logging.info("Authorized users are %s", AUTHORIZED_USER)
|
|
||||||
channel = Channel()
|
channel = Channel()
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,7 +71,7 @@ def private_use(func):
|
||||||
chat_id = getattr(message.from_user, "id", None)
|
chat_id = getattr(message.from_user, "id", None)
|
||||||
|
|
||||||
# message type check
|
# message type check
|
||||||
if message.chat.type != "private" and not message.text.lower().startswith("/ytdl"):
|
if message.chat.type != enums.ChatType.PRIVATE and not message.text.lower().startswith("/ytdl"):
|
||||||
logging.debug("%s, it's annoying me...🙄️ ", message.text)
|
logging.debug("%s, it's annoying me...🙄️ ", message.text)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -89,14 +87,11 @@ def private_use(func):
|
||||||
|
|
||||||
if REQUIRED_MEMBERSHIP:
|
if REQUIRED_MEMBERSHIP:
|
||||||
try:
|
try:
|
||||||
member: typing.Union[types.ChatMember, typing.Coroutine] = app.get_chat_member(
|
member: types.ChatMember | Any = app.get_chat_member(REQUIRED_MEMBERSHIP, chat_id)
|
||||||
REQUIRED_MEMBERSHIP, chat_id
|
|
||||||
)
|
|
||||||
if member.status not in [
|
if member.status not in [
|
||||||
"creator",
|
enums.ChatMemberStatus.ADMINISTRATOR,
|
||||||
"administrator",
|
enums.ChatMemberStatus.MEMBER,
|
||||||
"member",
|
enums.ChatMemberStatus.OWNER,
|
||||||
"owner",
|
|
||||||
]:
|
]:
|
||||||
raise UserNotParticipant()
|
raise UserNotParticipant()
|
||||||
else:
|
else:
|
||||||
|
@ -116,11 +111,11 @@ def start_handler(client: Client, message: types.Message):
|
||||||
payment = Payment()
|
payment = Payment()
|
||||||
from_id = message.from_user.id
|
from_id = message.from_user.id
|
||||||
logging.info("%s welcome to youtube-dl bot!", message.from_user.id)
|
logging.info("%s welcome to youtube-dl bot!", message.from_user.id)
|
||||||
client.send_chat_action(from_id, "typing")
|
client.send_chat_action(from_id, enums.ChatAction.TYPING)
|
||||||
is_old_user = payment.check_old_user(from_id)
|
is_old_user = payment.check_old_user(from_id)
|
||||||
if is_old_user:
|
if is_old_user:
|
||||||
info = ""
|
info = ""
|
||||||
elif ENABLE_VIP:
|
if ENABLE_VIP:
|
||||||
free_token, pay_token, reset = payment.get_token(from_id)
|
free_token, pay_token, reset = payment.get_token(from_id)
|
||||||
info = f"Free token: {free_token}, Pay token: {pay_token}, Reset: {reset}"
|
info = f"Free token: {free_token}, Pay token: {pay_token}, Reset: {reset}"
|
||||||
else:
|
else:
|
||||||
|
@ -132,21 +127,21 @@ def start_handler(client: Client, message: types.Message):
|
||||||
@app.on_message(filters.command(["help"]))
|
@app.on_message(filters.command(["help"]))
|
||||||
def help_handler(client: Client, message: types.Message):
|
def help_handler(client: Client, message: types.Message):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
client.send_chat_action(chat_id, "typing")
|
client.send_chat_action(chat_id, enums.ChatAction.TYPING)
|
||||||
client.send_message(chat_id, BotText.help, disable_web_page_preview=True)
|
client.send_message(chat_id, BotText.help, disable_web_page_preview=True)
|
||||||
|
|
||||||
|
|
||||||
@app.on_message(filters.command(["about"]))
|
@app.on_message(filters.command(["about"]))
|
||||||
def about_handler(client: Client, message: types.Message):
|
def about_handler(client: Client, message: types.Message):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
client.send_chat_action(chat_id, "typing")
|
client.send_chat_action(chat_id, enums.ChatAction.TYPING)
|
||||||
client.send_message(chat_id, BotText.about)
|
client.send_message(chat_id, BotText.about)
|
||||||
|
|
||||||
|
|
||||||
@app.on_message(filters.command(["sub"]))
|
@app.on_message(filters.command(["sub"]))
|
||||||
def subscribe_handler(client: Client, message: types.Message):
|
def subscribe_handler(client: Client, message: types.Message):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
client.send_chat_action(chat_id, "typing")
|
client.send_chat_action(chat_id, enums.ChatAction.TYPING)
|
||||||
if message.text == "/sub":
|
if message.text == "/sub":
|
||||||
result = channel.get_user_subscription(chat_id)
|
result = channel.get_user_subscription(chat_id)
|
||||||
else:
|
else:
|
||||||
|
@ -161,7 +156,7 @@ def subscribe_handler(client: Client, message: types.Message):
|
||||||
@app.on_message(filters.command(["unsub"]))
|
@app.on_message(filters.command(["unsub"]))
|
||||||
def unsubscribe_handler(client: Client, message: types.Message):
|
def unsubscribe_handler(client: Client, message: types.Message):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
client.send_chat_action(chat_id, "typing")
|
client.send_chat_action(chat_id, enums.ChatAction.TYPING)
|
||||||
text = message.text.split(" ")
|
text = message.text.split(" ")
|
||||||
if len(text) == 1:
|
if len(text) == 1:
|
||||||
client.send_message(chat_id, "/unsub channel_id", disable_web_page_preview=True)
|
client.send_message(chat_id, "/unsub channel_id", disable_web_page_preview=True)
|
||||||
|
@ -181,7 +176,7 @@ def patch_handler(client: Client, message: types.Message):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
if username == OWNER:
|
if username == OWNER:
|
||||||
celery_app.control.broadcast("hot_patch")
|
celery_app.control.broadcast("hot_patch")
|
||||||
client.send_chat_action(chat_id, "typing")
|
client.send_chat_action(chat_id, enums.ChatAction.TYPING)
|
||||||
client.send_message(chat_id, "Oorah!")
|
client.send_message(chat_id, "Oorah!")
|
||||||
hot_patch()
|
hot_patch()
|
||||||
|
|
||||||
|
@ -206,7 +201,7 @@ def purge_handler(client: Client, message: types.Message):
|
||||||
def ping_handler(client: Client, message: types.Message):
|
def ping_handler(client: Client, message: types.Message):
|
||||||
redis = Redis()
|
redis = Redis()
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
client.send_chat_action(chat_id, "typing")
|
client.send_chat_action(chat_id, enums.ChatAction.TYPING)
|
||||||
if os.uname().sysname == "Darwin" or ".heroku" in os.getenv("PYTHONHOME", ""):
|
if os.uname().sysname == "Darwin" or ".heroku" in os.getenv("PYTHONHOME", ""):
|
||||||
bot_info = "ping unavailable."
|
bot_info = "ping unavailable."
|
||||||
else:
|
else:
|
||||||
|
@ -233,7 +228,7 @@ def sub_count_handler(client: Client, message: types.Message):
|
||||||
def direct_handler(client: Client, message: types.Message):
|
def direct_handler(client: Client, message: types.Message):
|
||||||
redis = Redis()
|
redis = Redis()
|
||||||
chat_id = message.from_user.id
|
chat_id = message.from_user.id
|
||||||
client.send_chat_action(chat_id, "typing")
|
client.send_chat_action(chat_id, enums.ChatAction.TYPING)
|
||||||
url = re.sub(r"/direct\s*", "", message.text)
|
url = re.sub(r"/direct\s*", "", message.text)
|
||||||
logging.info("direct start %s", url)
|
logging.info("direct start %s", url)
|
||||||
if not re.findall(r"^https?://", url.lower()):
|
if not re.findall(r"^https?://", url.lower()):
|
||||||
|
@ -250,27 +245,27 @@ def direct_handler(client: Client, message: types.Message):
|
||||||
def settings_handler(client: Client, message: types.Message):
|
def settings_handler(client: Client, message: types.Message):
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
payment = Payment()
|
payment = Payment()
|
||||||
client.send_chat_action(chat_id, "typing")
|
client.send_chat_action(chat_id, enums.ChatAction.TYPING)
|
||||||
data = MySQL().get_user_settings(chat_id)
|
data = MySQL().get_user_settings(chat_id)
|
||||||
set_mode = data[-1]
|
set_mode = data[-1]
|
||||||
text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local")
|
text = {"Local": "Celery", "Celery": "Local"}.get(set_mode, "Local")
|
||||||
mode_text = f"Download mode: **{set_mode}**"
|
mode_text = f"Download mode: **{set_mode}**"
|
||||||
if message.chat.username == OWNER or payment.get_pay_token(chat_id):
|
if message.chat.username == OWNER or payment.get_pay_token(chat_id):
|
||||||
extra = [InlineKeyboardButton(f"Change download mode to {text}", callback_data=text)]
|
extra = [types.InlineKeyboardButton(f"Change download mode to {text}", callback_data=text)]
|
||||||
else:
|
else:
|
||||||
extra = []
|
extra = []
|
||||||
|
|
||||||
markup = InlineKeyboardMarkup(
|
markup = types.InlineKeyboardMarkup(
|
||||||
[
|
[
|
||||||
[ # First row
|
[ # First row
|
||||||
InlineKeyboardButton("send as document", callback_data="document"),
|
types.InlineKeyboardButton("send as document", callback_data="document"),
|
||||||
InlineKeyboardButton("send as video", callback_data="video"),
|
types.InlineKeyboardButton("send as video", callback_data="video"),
|
||||||
InlineKeyboardButton("send as audio", callback_data="audio"),
|
types.InlineKeyboardButton("send as audio", callback_data="audio"),
|
||||||
],
|
],
|
||||||
[ # second row
|
[ # second row
|
||||||
InlineKeyboardButton("High Quality", callback_data="high"),
|
types.InlineKeyboardButton("High Quality", callback_data="high"),
|
||||||
InlineKeyboardButton("Medium Quality", callback_data="medium"),
|
types.InlineKeyboardButton("Medium Quality", callback_data="medium"),
|
||||||
InlineKeyboardButton("Low Quality", callback_data="low"),
|
types.InlineKeyboardButton("Low Quality", callback_data="low"),
|
||||||
],
|
],
|
||||||
extra,
|
extra,
|
||||||
]
|
]
|
||||||
|
@ -288,7 +283,7 @@ def settings_handler(client: Client, message: types.Message):
|
||||||
def buy_handler(client: Client, message: types.Message):
|
def buy_handler(client: Client, message: types.Message):
|
||||||
# process as chat.id, not from_user.id
|
# process as chat.id, not from_user.id
|
||||||
chat_id = message.chat.id
|
chat_id = message.chat.id
|
||||||
client.send_chat_action(chat_id, "typing")
|
client.send_chat_action(chat_id, enums.ChatAction.TYPING)
|
||||||
# currency USD
|
# currency USD
|
||||||
token_count = message.text.replace("/buy", "").strip()
|
token_count = message.text.replace("/buy", "").strip()
|
||||||
if token_count.isdigit():
|
if token_count.isdigit():
|
||||||
|
@ -296,11 +291,11 @@ def buy_handler(client: Client, message: types.Message):
|
||||||
else:
|
else:
|
||||||
price = 100
|
price = 100
|
||||||
|
|
||||||
markup = InlineKeyboardMarkup(
|
markup = types.InlineKeyboardMarkup(
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
InlineKeyboardButton("Bot Payments", callback_data=f"bot-payments-{price}"),
|
types.InlineKeyboardButton("Bot Payments", callback_data=f"bot-payments-{price}"),
|
||||||
InlineKeyboardButton("TRON(TRX)", callback_data="tron-trx"),
|
types.InlineKeyboardButton("TRON(TRX)", callback_data="tron-trx"),
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -311,7 +306,7 @@ def buy_handler(client: Client, message: types.Message):
|
||||||
def tronpayment_btn_calback(client: Client, callback_query: types.CallbackQuery):
|
def tronpayment_btn_calback(client: Client, callback_query: types.CallbackQuery):
|
||||||
callback_query.answer("Generating QR code...")
|
callback_query.answer("Generating QR code...")
|
||||||
chat_id = callback_query.message.chat.id
|
chat_id = callback_query.message.chat.id
|
||||||
client.send_chat_action(chat_id, "typing")
|
client.send_chat_action(chat_id, enums.ChatAction.TYPING)
|
||||||
|
|
||||||
addr = TronTrx().get_payment_address(chat_id)
|
addr = TronTrx().get_payment_address(chat_id)
|
||||||
with BytesIO() as bio:
|
with BytesIO() as bio:
|
||||||
|
@ -324,13 +319,13 @@ def tronpayment_btn_calback(client: Client, callback_query: types.CallbackQuery)
|
||||||
def bot_payment_btn_calback(client: Client, callback_query: types.CallbackQuery):
|
def bot_payment_btn_calback(client: Client, callback_query: types.CallbackQuery):
|
||||||
callback_query.answer("Generating invoice...")
|
callback_query.answer("Generating invoice...")
|
||||||
chat_id = callback_query.message.chat.id
|
chat_id = callback_query.message.chat.id
|
||||||
client.send_chat_action(chat_id, "typing")
|
client.send_chat_action(chat_id, enums.ChatAction.TYPING)
|
||||||
|
|
||||||
data = callback_query.data
|
data = callback_query.data
|
||||||
price = int(data.split("-")[-1])
|
price = int(data.split("-")[-1])
|
||||||
payload = f"{chat_id}-buy"
|
payload = f"{chat_id}-buy"
|
||||||
invoice = generate_invoice(price, f"Buy {TOKEN_PRICE} download tokens", "Pay by card", payload)
|
invoice = generate_invoice(price, f"Buy {TOKEN_PRICE} download tokens", "Pay by card", payload)
|
||||||
app.send(
|
app.invoke(
|
||||||
functions.messages.SendMedia(
|
functions.messages.SendMedia(
|
||||||
peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)),
|
peer=(raw_types.InputPeerUser(user_id=chat_id, access_hash=0)),
|
||||||
media=invoice,
|
media=invoice,
|
||||||
|
@ -384,10 +379,9 @@ def link_checker(url: str) -> str:
|
||||||
|
|
||||||
|
|
||||||
def search_ytb(kw: str):
|
def search_ytb(kw: str):
|
||||||
videosSearch = VideosSearch(kw, limit=10)
|
videos_search = VideosSearch(kw, limit=10)
|
||||||
|
|
||||||
text = ""
|
text = ""
|
||||||
results = videosSearch.result()["result"]
|
results = videos_search.result()["result"]
|
||||||
for item in results:
|
for item in results:
|
||||||
title = item.get("title")
|
title = item.get("title")
|
||||||
link = item.get("link")
|
link = item.get("link")
|
||||||
|
@ -402,7 +396,7 @@ def download_handler(client: Client, message: types.Message):
|
||||||
redis = Redis()
|
redis = Redis()
|
||||||
payment = Payment()
|
payment = Payment()
|
||||||
chat_id = message.from_user.id
|
chat_id = message.from_user.id
|
||||||
client.send_chat_action(chat_id, "typing")
|
client.send_chat_action(chat_id, enums.ChatAction.TYPING)
|
||||||
redis.user_count(chat_id)
|
redis.user_count(chat_id)
|
||||||
if message.document:
|
if message.document:
|
||||||
with tempfile.NamedTemporaryFile(mode="r+") as tf:
|
with tempfile.NamedTemporaryFile(mode="r+") as tf:
|
||||||
|
@ -431,9 +425,7 @@ def download_handler(client: Client, message: types.Message):
|
||||||
if ENABLE_VIP and not payment.check_old_user(chat_id):
|
if ENABLE_VIP and not payment.check_old_user(chat_id):
|
||||||
free, pay, reset = payment.get_token(chat_id)
|
free, pay, reset = payment.get_token(chat_id)
|
||||||
if free + pay <= 0:
|
if free + pay <= 0:
|
||||||
message.reply_text(
|
message.reply_text(f"You don't have enough token. Please wait until {reset} or /buy .", quote=True)
|
||||||
f"You don't have enough token. Please wait until {reset} or /buy more token.", quote=True
|
|
||||||
)
|
|
||||||
redis.update_metrics("reject_token")
|
redis.update_metrics("reject_token")
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
@ -444,22 +436,22 @@ def download_handler(client: Client, message: types.Message):
|
||||||
text = BotText.get_receive_link_text()
|
text = BotText.get_receive_link_text()
|
||||||
try:
|
try:
|
||||||
# raise pyrogram.errors.exceptions.FloodWait(10)
|
# raise pyrogram.errors.exceptions.FloodWait(10)
|
||||||
bot_msg: typing.Union[types.Message, typing.Coroutine] = message.reply_text(text, quote=True)
|
bot_msg: types.Message | Any = message.reply_text(text, quote=True)
|
||||||
except pyrogram.errors.Flood as e:
|
except pyrogram.errors.Flood as e:
|
||||||
f = BytesIO()
|
f = BytesIO()
|
||||||
f.write(str(e).encode())
|
f.write(str(e).encode())
|
||||||
f.write(b"Your job will be done soon. Just wait! Don't rush.")
|
f.write(b"Your job will be done soon. Just wait! Don't rush.")
|
||||||
f.name = "Please don't flood me.txt"
|
f.name = "Please don't flood me.txt"
|
||||||
bot_msg = message.reply_document(
|
bot_msg = message.reply_document(
|
||||||
f, caption=f"Flood wait! Please wait {e.x} seconds...." f"Your job will start automatically", quote=True
|
f, caption=f"Flood wait! Please wait {e} seconds...." f"Your job will start automatically", quote=True
|
||||||
)
|
)
|
||||||
f.close()
|
f.close()
|
||||||
client.send_message(OWNER, f"Flood wait! 🙁 {e.x} seconds....")
|
client.send_message(OWNER, f"Flood wait! 🙁 {e} seconds....")
|
||||||
time.sleep(e.x)
|
time.sleep(e.value)
|
||||||
|
|
||||||
client.send_chat_action(chat_id, "upload_video")
|
client.send_chat_action(chat_id, enums.ChatAction.UPLOAD_VIDEO)
|
||||||
bot_msg.chat = message.chat
|
bot_msg.chat = message.chat
|
||||||
ytdl_download_entrance(client, bot_msg, url)
|
ytdl_download_entrance(bot_msg, url)
|
||||||
|
|
||||||
|
|
||||||
@app.on_callback_query(filters.regex(r"document|video|audio"))
|
@app.on_callback_query(filters.regex(r"document|video|audio"))
|
||||||
|
@ -484,14 +476,13 @@ def download_resolution_callback(client: Client, callback_query: types.CallbackQ
|
||||||
def audio_callback(client: Client, callback_query: types.CallbackQuery):
|
def audio_callback(client: Client, callback_query: types.CallbackQuery):
|
||||||
redis = Redis()
|
redis = Redis()
|
||||||
if not ENABLE_FFMPEG:
|
if not ENABLE_FFMPEG:
|
||||||
callback_query.answer("Audio conversion is disabled now.")
|
callback_query.answer("Request rejected.")
|
||||||
callback_query.message.reply_text("Audio conversion is disabled now.")
|
callback_query.message.reply_text("Audio conversion is disabled now.")
|
||||||
return
|
return
|
||||||
|
|
||||||
callback_query.answer(f"Converting to audio...please wait patiently")
|
callback_query.answer(f"Converting to audio...please wait patiently")
|
||||||
redis.update_metrics("audio_request")
|
redis.update_metrics("audio_request")
|
||||||
vmsg = callback_query.message
|
audio_entrance(callback_query.message)
|
||||||
audio_entrance(client, vmsg)
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_callback_query(filters.regex(r"Local|Celery"))
|
@app.on_callback_query(filters.regex(r"Local|Celery"))
|
||||||
|
@ -509,14 +500,12 @@ def periodic_sub_check():
|
||||||
logging.info(f"periodic update:{video_url} - {uids}")
|
logging.info(f"periodic update:{video_url} - {uids}")
|
||||||
for uid in uids:
|
for uid in uids:
|
||||||
try:
|
try:
|
||||||
bot_msg: typing.Union[types.Message, typing.Coroutine] = app.send_message(
|
bot_msg: types.Message | Any = app.send_message(uid, f"{video_url} is out. Watch it on YouTube")
|
||||||
uid, f"{video_url} is out. Watch it on YouTube"
|
|
||||||
)
|
|
||||||
# ytdl_download_entrance(app, bot_msg, video_url, mode="direct")
|
# ytdl_download_entrance(app, bot_msg, video_url, mode="direct")
|
||||||
except (exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e:
|
except (exceptions.bad_request_400.PeerIdInvalid, exceptions.bad_request_400.UserIsBlocked) as e:
|
||||||
logging.warning("User is blocked or deleted. %s", e)
|
logging.warning("User is blocked or deleted. %s", e)
|
||||||
channel.deactivate_user_subscription(uid)
|
channel.deactivate_user_subscription(uid)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logging.error("Unknown error when sending message to user. %s", traceback.format_exc())
|
logging.error("Unknown error when sending message to user. %s", traceback.format_exc())
|
||||||
finally:
|
finally:
|
||||||
time.sleep(random.random() * 3)
|
time.sleep(random.random() * 3)
|
||||||
|
@ -527,7 +516,7 @@ def raw_update(client: Client, update, users, chats):
|
||||||
payment = Payment()
|
payment = Payment()
|
||||||
action = getattr(getattr(update, "message", None), "action", None)
|
action = getattr(getattr(update, "message", None), "action", None)
|
||||||
if update.QUALNAME == "types.UpdateBotPrecheckoutQuery":
|
if update.QUALNAME == "types.UpdateBotPrecheckoutQuery":
|
||||||
client.send(
|
client.invoke(
|
||||||
functions.messages.SetBotPrecheckoutResults(
|
functions.messages.SetBotPrecheckoutResults(
|
||||||
query_id=update.query_id,
|
query_id=update.query_id,
|
||||||
success=True,
|
success=True,
|
||||||
|
@ -549,14 +538,13 @@ def trx_notify(_, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
redis = Redis()
|
|
||||||
MySQL()
|
MySQL()
|
||||||
TRX_SIGNAL.connect(trx_notify)
|
TRX_SIGNAL.connect(trx_notify)
|
||||||
scheduler = BackgroundScheduler(timezone="Europe/London", job_defaults={"max_instances": 6})
|
scheduler = BackgroundScheduler(timezone="Europe/London", job_defaults={"max_instances": 6})
|
||||||
scheduler.add_job(auto_restart, "interval", seconds=600)
|
scheduler.add_job(auto_restart, "interval", seconds=600)
|
||||||
scheduler.add_job(clean_tempfile, "interval", seconds=120)
|
scheduler.add_job(clean_tempfile, "interval", seconds=120)
|
||||||
if not IS_BACKUP_BOT:
|
if not IS_BACKUP_BOT:
|
||||||
scheduler.add_job(redis.reset_today, "cron", hour=0, minute=0)
|
scheduler.add_job(Redis().reset_today, "cron", hour=0, minute=0)
|
||||||
scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120)
|
scheduler.add_job(InfluxDB().collect_data, "interval", seconds=120)
|
||||||
scheduler.add_job(TronTrx().check_payment, "interval", seconds=60, max_instances=1)
|
scheduler.add_job(TronTrx().check_payment, "interval", seconds=60, max_instances=1)
|
||||||
# default quota allocation of 10,000 units per day
|
# default quota allocation of 10,000 units per day
|
||||||
|
|
Loading…
Reference in a new issue