Version 1.4.1

This commit is contained in:
Taras Terletskyi 2023-03-29 22:51:49 +03:00
parent af8b65ddf2
commit 26371b2a0a
14 changed files with 81 additions and 35 deletions

View file

@ -2,7 +2,7 @@
Simple and reliable YouTube Download Telegram Bot.
Version: 1.4. [Release details](RELEASES.md).
Version: 1.4.1. [Release details](RELEASES.md).
![frames](.assets/download_success.png)

View file

@ -1,3 +1,21 @@
## Release 1.4.1
Release date: March 29, 2023
## New Features
N/A
## Important
N/A
## Misc
- Maintenance release: added `uvloop`.
---
## Release 1.4
Release date: March 29, 2023

View file

@ -1,6 +1,6 @@
import uvicorn
from api.core.app import app # noqa
from api.core.app import app # noqa: F401
if __name__ == '__main__':
uvicorn.run('main:app', host='0.0.0.0', port=8000)

View file

@ -13,6 +13,8 @@ from bot.core.utils import bold
class VideoBot(Client):
"""Extended Pyrogram's `Client` class."""
_RUN_FOREVER_SLEEP_SECONDS = 86400
def __init__(self) -> None:
self.conf = get_main_config()
super().__init__(
@ -33,7 +35,7 @@ class VideoBot(Client):
if not self.is_initialized:
raise RuntimeError('Bot was not started (initialized).')
while True:
await asyncio.sleep(86400)
await asyncio.sleep(self._RUN_FOREVER_SLEEP_SECONDS)
def get_startup_users(self) -> list[int]:
user_ids = []

View file

@ -3,11 +3,10 @@ import logging
from pyrogram.enums import ParseMode
from pyrogram.types import Message
from yt_shared.emoji import SUCCESS_EMOJI
from yt_shared.utils.user import get_user_id
from bot.core.bot import VideoBot
from bot.core.service import UrlParser, UrlService
from bot.core.utils import bold
from bot.core.utils import bold, get_user_id
class TelegramCallback:
@ -37,7 +36,7 @@ class TelegramCallback:
urls=urls, regexes=client.conf.telegram.url_validation_regexes
)
if not urls:
self._log.info('No urls to download, skipping message')
self._log.debug('No urls to download, skipping message')
return
urls = self._url_parser.parse_urls(urls=urls, message=message, user=user)

View file

@ -5,6 +5,7 @@ import string
from datetime import datetime
from uuid import uuid4
from pyrogram.enums import ChatType
from pyrogram.types import Message
@ -46,6 +47,19 @@ def get_user_info(message: Message) -> str:
)
def get_user_id(message: Message) -> int:
"""Make explicit selection to not forget how this works since we just can return
`message.chat.id` for all cases.
"""
match message.chat.type:
case ChatType.PRIVATE:
return message.from_user.id
case ChatType.GROUP:
return message.chat.id
case _:
return message.chat.id
def build_command_presentation(commands: dict[str, list]) -> str:
groups = []
for desc, cmds in commands.items():

View file

@ -1 +1 @@
__version__ = '1.4'
__version__ = '1.4.1'

View file

@ -2,6 +2,8 @@
"""Bot Launcher Module."""
import asyncio
import uvloop
from bot.core.bot import BotLauncher
from bot.core.log import setup_logging
@ -12,4 +14,5 @@ async def main() -> None:
if __name__ == '__main__':
uvloop.install()
asyncio.run(main())

View file

@ -1,11 +1,16 @@
import asyncio
import uvloop
from worker.core.launcher import WorkerLauncher
from worker.core.log import setup_logging
def main() -> None:
async def main() -> None:
setup_logging()
WorkerLauncher().start()
await WorkerLauncher().start()
if __name__ == '__main__':
main()
uvloop.install()
asyncio.run(main())

View file

@ -7,27 +7,29 @@ from yt_shared.db.session import get_db
from yt_shared.rabbit import get_rabbitmq
from yt_shared.rabbit.rabbit_config import INPUT_QUEUE
from yt_shared.repositories.ytdlp import YtdlpRepository
from yt_shared.utils.common import register_shutdown
from worker.core.callbacks import rmq_callbacks as cb
from worker.core.config import settings
class WorkerLauncher:
_RUN_FOREVER_SLEEP_SECONDS = 86400
def __init__(self) -> None:
self._log = logging.getLogger(self.__class__.__name__)
self._rabbit_mq = get_rabbitmq()
def start(self) -> None:
async def start(self) -> None:
self._log.info('Starting download worker instance')
loop = asyncio.get_event_loop()
loop.run_until_complete(self._start())
try:
loop.run_forever()
finally:
loop.run_until_complete(self.stop())
await self._start()
async def _start(self) -> None:
await self._perform_setup()
await self._run_forever()
async def _run_forever(self) -> None:
await asyncio.sleep(self._RUN_FOREVER_SLEEP_SECONDS)
async def _perform_setup(self) -> None:
await asyncio.gather(
@ -37,6 +39,7 @@ class WorkerLauncher:
self._create_intermediate_directories(),
)
)
self._register_shutdown()
async def _setup_rabbit(self) -> None:
self._log.info('Setting up RabbitMQ connection')
@ -69,6 +72,10 @@ class WorkerLauncher:
os.makedirs(tmp_download_path, exist_ok=True)
os.makedirs(tmp_downloaded_path, exist_ok=True)
async def stop(self) -> None:
def _register_shutdown(self) -> None:
register_shutdown(self.stop)
def stop(self, *args) -> None:
self._log.info('Shutting down %s', self.__class__.__name__)
loop = asyncio.get_running_loop()
loop.run_until_complete(self._rabbit_mq.close())
loop.create_task(self._rabbit_mq.close())

View file

@ -4,3 +4,4 @@ aio-pika==9.0.5
aiohttp==3.8.4
asyncpg==0.27.0
pydantic==1.10.7
uvloop==0.17.0

View file

@ -1,3 +1,4 @@
import logging
from asyncio import get_running_loop
import aio_pika
@ -13,6 +14,7 @@ class RabbitMQ:
RABBITMQ_RECONNECT_INTERVAL = 2
def __init__(self) -> None:
self._log = logging.getLogger(self.__class__.__name__)
self._config = get_rabbit_config()
self.connection: RobustConnection = None
@ -52,8 +54,10 @@ class RabbitMQ:
self.queues[queue_name] = queue
async def close(self) -> None:
self._log.debug('[RabbitMQ] Closing connection')
await self.channel.close()
await self.connection.close()
self._log.debug('[RabbitMQ] Connection closed')
_rabbit_mq = RabbitMQ()

View file

@ -1,8 +1,10 @@
import asyncio
import atexit
import random
import signal
from functools import partial, wraps
from string import ascii_lowercase
from typing import Any
from typing import Any, Callable
ASYNC_LOCK = asyncio.Lock()
@ -53,3 +55,9 @@ def wrap(func):
def random_string(number: int) -> str:
return ''.join(random.choice(ascii_lowercase) for _ in range(number))
def register_shutdown(callback: Callable) -> None:
atexit.register(callback)
signal.signal(signal.SIGTERM, callback)
signal.signal(signal.SIGINT, callback)

View file

@ -1,15 +0,0 @@
from pyrogram.enums import ChatType
from pyrogram.types import Message
def get_user_id(message: Message) -> int:
"""Make explicit selection to not forget how this works since we just can return
`message.chat.id` for all cases.
"""
match message.chat.type:
case ChatType.PRIVATE:
return message.from_user.id
case ChatType.GROUP:
return message.chat.id
case _:
return message.chat.id