Files
zyrarbot/zyrarbot/application.py
T

93 lines
3.4 KiB
Python

from __future__ import annotations
import asyncio
import logging
import uvicorn
from zyrarbot.audio.media_probe import MediaProbe
from zyrarbot.audio.track_factory import TrackFactory
from zyrarbot.bot.client import MusicBot
from zyrarbot.bot.guild_resolver import GuildResolver
from zyrarbot.music.music_player import MusicPlayer
from zyrarbot.music.notification_channel_resolver import NotificationChannelResolver
from zyrarbot.music.track_embed_factory import TrackEmbedFactory
from zyrarbot.settings import Settings
from zyrarbot.storage.guild_config_repository import GuildConfigRepository
from zyrarbot.storage.history_repository import HistoryRepository
from zyrarbot.storage.json_file_store import JsonFileStore
from zyrarbot.storage.uploads_repository import UploadsRepository
from zyrarbot.web.factory import create_app
logger = logging.getLogger(__name__)
class Application:
"""Korzeń kompozycji: składa wszystkie zależności i prowadzi bota razem z API."""
def __init__(self, settings: Settings | None = None):
self.settings = settings or Settings.from_env()
self.config = GuildConfigRepository(
JsonFileStore(self.settings.config_file),
fallback_plays=self.settings.default_plays,
)
self.history = HistoryRepository(
JsonFileStore(self.settings.history_file), limit=self.settings.history_limit
)
self.uploads = UploadsRepository(self.settings.uploads_dir)
self.tracks = TrackFactory(self.uploads, MediaProbe())
self.bot = MusicBot(self.settings, self.tracks, self.uploads)
self.guilds = GuildResolver(self.bot)
self.player = MusicPlayer(
bot=self.bot,
track_factory=self.tracks,
history=self.history,
config=self.config,
channels=NotificationChannelResolver(self.bot, self.config),
embeds=TrackEmbedFactory(),
)
self.bot.player = self.player
self.api = create_app(
settings=self.settings,
player=self.player,
guilds=self.guilds,
tracks=self.tracks,
uploads=self.uploads,
config=self.config,
)
async def run(self) -> None:
"""Startuje serwer web, a następnie bota; panel działa też bez Discorda."""
config = uvicorn.Config(
self.api,
host=self.settings.web_host,
port=self.settings.web_port,
log_level="info",
)
server = uvicorn.Server(config)
async with self.bot:
server_task = asyncio.create_task(server.serve())
try:
if not self.settings.discord_token:
raise RuntimeError("Brak DISCORD_TOKEN w środowisku")
await self.bot.start(self.settings.discord_token)
except Exception as error: # noqa: BLE001 - panel ma przetrwać awarię bota
logger.error("Nie udało się wystartować bota Discord: %s", error)
logger.info("Serwer webowy działa dalej...")
await self._wait_for_shutdown(server)
finally:
server.should_exit = True
await server_task
@staticmethod
async def _wait_for_shutdown(server: uvicorn.Server) -> None:
while not server.should_exit:
try:
await asyncio.sleep(1)
except asyncio.CancelledError:
break