# ŻyrarBOT Bot muzyczny Discord z panelem webowym: kolejka odtwarzania, historia, serwerowa baza plików i sterowanie na żywo przez WebSocket. ## Uruchomienie ```bash pip install -r requirements.txt python main.py # albo: python -m zyrarbot ``` Wymagane zmienne środowiskowe (plik `.env`): | Zmienna | Opis | | --------------- | ----------------------------------------------------------- | | `DISCORD_TOKEN` | Token bota Discord. | | `GUILD_ID` | Opcjonalnie - synchronizuje komendy tylko z jednym serwerem. | Panel działa pod `http://localhost:8000`. W Dockerze: `docker compose up --build`. ## Testy ```bash python -m unittest discover -s tests -t . ``` ## Struktura pakietu Każda klasa mieszka w osobnym pliku, a pliki są pogrupowane w przestrzenie nazw odpowiadające warstwom aplikacji: ``` zyrarbot/ ├── settings.py Settings - konfiguracja ze zmiennych środowiskowych ├── application.py Application - korzeń kompozycji, spina bota i serwer web ├── storage/ Warstwa trwałości (pliki JSON, katalog uploadów) │ ├── json_file_store.py JsonFileStore │ ├── guild_config_repository.py GuildConfigRepository │ ├── history_repository.py HistoryRepository │ ├── uploads_repository.py UploadsRepository │ └── upload_file_info.py UploadFileInfo ├── audio/ Pozyskiwanie dźwięku │ ├── ytdl_options.py YtdlOptions │ ├── ytdl_source.py YTDLSource │ ├── media_probe.py MediaProbe │ └── track_factory.py TrackFactory ├── music/ Domena odtwarzania │ ├── loop_mode.py LoopMode │ ├── history_entry.py HistoryEntry │ ├── guild_music_state.py GuildMusicState │ ├── track_embed_factory.py TrackEmbedFactory │ ├── notification_channel_resolver.py NotificationChannelResolver │ └── music_player.py MusicPlayer ├── bot/ Warstwa Discorda │ ├── client.py ZyrarBot │ ├── guild_resolver.py GuildResolver │ └── cogs/music_cog.py MusicCog └── web/ Panel webowy (FastAPI) ├── factory.py create_app ├── connection_manager.py ConnectionManager ├── status_service.py StatusService ├── status_broadcaster.py StatusBroadcaster ├── dependencies.py wstrzykiwanie zależności z app.state ├── schemas/ modele żądań - jeden na plik └── routers/ trasy pogrupowane tematycznie ``` ### Zasady - **`MusicPlayer` jest jedynym właścicielem stanu odtwarzania.** Zarówno komendy Discorda, jak i endpointy panelu operują wyłącznie przez niego - nie ma już dwóch niezależnych ścieżek modyfikujących kolejkę. - **Kolejka jest nieusuwalna, pozycję śledzi indeks** (`GuildMusicState.index`), dzięki czemu działa przeskakiwanie i przewijanie w tył. - **Zależności wędrują przez konstruktory**, a w warstwie web przez `app.state` i funkcje z `web/dependencies.py`. Nie ma globalnych singletonów.