ytdlbot/bot.py

168 lines
5.5 KiB
Python
Raw Normal View History

2021-05-03 11:26:47 +00:00
#!/usr/local/bin/python3
# coding: utf-8
# ytdl-bot - bot.py
# 5/3/21 18:31
#
__author__ = "Benny <benny.think@gmail.com>"
import tempfile
import os
2021-05-04 07:58:01 +00:00
import re
2021-05-03 11:26:47 +00:00
import logging
2021-05-04 03:30:22 +00:00
import threading
import asyncio
2021-05-03 11:26:47 +00:00
import traceback
2021-05-04 07:58:01 +00:00
import functools
2021-05-04 03:30:22 +00:00
2021-05-04 07:27:11 +00:00
import fakeredis
2021-05-04 03:30:22 +00:00
import youtube_dl
2021-05-03 11:26:47 +00:00
from telethon import TelegramClient, events
from tgbot_ping import get_runtime
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(filename)s [%(levelname)s]: %(message)s')
token = os.getenv("TOKEN") or "17Zg"
app_id = int(os.getenv("APP_ID") or "922")
app_hash = os.getenv("APP_HASH") or "490"
2021-05-04 07:27:11 +00:00
2021-05-03 11:26:47 +00:00
bot = TelegramClient('bot', app_id, app_hash).start(bot_token=token)
2021-05-04 07:27:11 +00:00
r = fakeredis.FakeStrictRedis()
EXPIRE = 5
2021-05-03 11:26:47 +00:00
2021-05-04 03:30:22 +00:00
async def upload_callback(current, total, chat_id, message):
2021-05-04 07:27:11 +00:00
key = f"{chat_id}-{message.id}"
# if the key exists, we shouldn't send edit message
if not r.exists(key):
msg = f'Uploading {round(current / total * 100, 2)}%: {current}/{total}'
await bot.edit_message(chat_id, message, msg)
r.set(key, "ok", ex=EXPIRE)
2021-05-04 03:30:22 +00:00
async def sync_edit_message(chat_id, message, msg):
2021-05-04 07:27:11 +00:00
# try to avoid flood
key = f"{chat_id}-{message.id}"
if not r.exists(key):
await bot.edit_message(chat_id, message, msg)
r.set(key, "ok", ex=EXPIRE)
2021-05-03 11:26:47 +00:00
2021-05-04 03:30:22 +00:00
def go(chat_id, message, msg):
asyncio.run(sync_edit_message(chat_id, message, msg))
2021-05-03 11:26:47 +00:00
2021-05-04 03:30:22 +00:00
def progress_hook(d: dict, chat_id, message):
if d['status'] == 'downloading':
downloaded = d.get("downloaded_bytes", 0)
total = d.get("total_bytes") or d.get("total_bytes_estimate", "N/A")
percent = d.get("_percent_str", "N/A")
speed = d.get("_speed_str", "N/A")
2021-05-04 03:30:22 +00:00
msg = f'Downloading {percent}: {downloaded}/{total} @ {speed}'
threading.Thread(target=go, args=(chat_id, message, msg)).start()
2021-05-04 07:58:01 +00:00
def run_in_executor(f):
@functools.wraps(f)
def inner(*args, **kwargs):
loop = asyncio.get_running_loop()
return loop.run_in_executor(None, lambda: f(*args, **kwargs))
return inner
@run_in_executor
def ytdl_download(url, tempdir, chat_id, message) -> dict:
2021-05-03 11:26:47 +00:00
response = dict(status=None, error=None, filepath=None)
logging.info("Downloading for %s", url)
output = os.path.join(tempdir, '%(title)s.%(ext)s')
2021-05-04 03:30:22 +00:00
ydl_opts = {
'progress_hooks': [lambda d: progress_hook(d, chat_id, message)],
'outtmpl': output,
'restrictfilenames': True
2021-05-04 03:30:22 +00:00
}
2021-05-03 11:26:47 +00:00
try:
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
response["status"] = True
response["filepath"] = os.path.join(tempdir, [i for i in os.listdir(tempdir)][0])
2021-05-03 11:26:47 +00:00
except Exception:
err = traceback.format_exc()
logging.error("Download failed for %s ", url)
response["status"] = False
response["error"] = err
return response
@bot.on(events.NewMessage(pattern='/start'))
async def send_welcome(event):
async with bot.action(event.chat_id, 'typing'):
await bot.send_message(event.chat_id, "Wrapper for youtube-dl.")
raise events.StopPropagation
2021-05-04 07:27:11 +00:00
@bot.on(events.NewMessage(pattern='/help'))
async def send_welcome(event):
async with bot.action(event.chat_id, 'typing'):
await bot.send_message(event.chat_id, "Bot is not working? "
"Wait a few seconds, send your link again or report bugs at "
"https://github.com/tgbot-collection/ytdl-bot/issues")
raise events.StopPropagation
2021-05-03 11:26:47 +00:00
@bot.on(events.NewMessage(pattern='/ping'))
async def send_welcome(event):
async with bot.action(event.chat_id, 'typing'):
bot_info = get_runtime("botsrunner_ytdl_1", "YouTube-dl")
await bot.send_message(event.chat_id, f"{bot_info}\n", parse_mode='md')
raise events.StopPropagation
@bot.on(events.NewMessage(pattern='/about'))
async def send_welcome(event):
async with bot.action(event.chat_id, 'typing'):
await bot.send_message(event.chat_id, "YouTube-DL by @BennyThink\n"
"GitHub: https://github.com/tgbot-collection/ytdl-bot")
raise events.StopPropagation
@bot.on(events.NewMessage(incoming=True))
async def echo_all(event):
chat_id = event.message.chat_id
url = event.message.text
2021-05-04 07:58:01 +00:00
logging.info("start %s", url)
if not re.findall(r"^https?://", url.lower()):
await event.reply("I think you should send me a link. Don't you agree with me?")
return
2021-05-04 03:30:22 +00:00
message = await event.reply("Processing...")
2021-05-03 11:26:47 +00:00
temp_dir = tempfile.TemporaryDirectory()
2021-05-04 03:30:22 +00:00
async with bot.action(chat_id, 'video'):
2021-05-04 07:58:01 +00:00
logging.info("downloading start")
2021-05-04 05:24:57 +00:00
result = await ytdl_download(url, temp_dir.name, chat_id, message)
2021-05-04 07:58:01 +00:00
logging.info("downloading complete")
2021-05-04 03:30:22 +00:00
2021-05-03 11:26:47 +00:00
if result["status"]:
2021-05-04 03:30:22 +00:00
async with bot.action(chat_id, 'document'):
2021-05-03 11:26:47 +00:00
video_path = result["filepath"]
2021-05-04 03:30:22 +00:00
await bot.edit_message(chat_id, message, 'Download complete. Sending now...')
await bot.send_file(chat_id, video_path,
progress_callback=lambda x, y: upload_callback(x, y, chat_id, message))
2021-05-03 11:26:47 +00:00
await bot.edit_message(chat_id, message, 'Download success!✅')
else:
2021-05-04 03:30:22 +00:00
async with bot.action(chat_id, 'typing'):
2021-05-04 07:58:01 +00:00
tb = result["error"][0:4000]
2021-05-03 11:26:47 +00:00
await bot.edit_message(chat_id, message, f"{url} download failed❌\n```{tb}```",
parse_mode='markdown')
temp_dir.cleanup()
if __name__ == '__main__':
2021-05-04 07:27:11 +00:00
bot.start()
2021-05-03 11:26:47 +00:00
bot.run_until_disconnected()