Commit Graph
90 Commits
Author SHA1 Message Date
mmacierzanka b19f3e1923 fix: preserve plays when skipping tracks 2026-08-21 17:26:02 +02:00
mmacierzanka 03611e3226 fix: preserve plays on stop and disconnect 2026-08-21 17:19:36 +02:00
mmacierzanka 6736329603 fix: decrement plays after playback 2026-08-21 17:18:21 +02:00
mmacierzanka d6bd302165 feat: restore the last queue per server
Backend / Lint i formatowanie (pull_request) Successful in 3s
Backend / Testy i kontrakt API (pull_request) Successful in 10s
Panel / Lint, typy i build (pull_request) Successful in 18s
Backend / Testowy build obrazu (pull_request) Successful in 15s
2026-08-21 17:10:40 +02:00
mmacierzanka 3624c152f5 fix: sync volume after external changes 2026-08-21 16:59:11 +02:00
mmacierzanka 21f3a1060c fix: validate uploaded media files
Backend / Lint i formatowanie (pull_request) Successful in 6s
Backend / Testy i kontrakt API (pull_request) Successful in 10s
Backend / Testowy build obrazu (pull_request) Successful in 10s
Panel / Lint, typy i build (pull_request) Successful in 34s
2026-08-21 16:37:41 +02:00
mmacierzanka 004641c9b8 feat: expose per-server default plays setting 2026-08-21 16:32:41 +02:00
mmacierzanka 36621ce261 feat: support direct links to guild views 2026-08-21 16:27:45 +02:00
mmacierzanka 8b340c61f0 feat: add notification channel selector 2026-08-21 16:26:30 +02:00
mmacierzanka cfe9a2fc31 fix: store playback history per Discord server 2026-08-21 16:21:57 +02:00
mmacierzanka 32fd296d9e fix: migrate legacy state to the first Discord server 2026-08-21 16:18:57 +02:00
mmacierzanka 64b428555c refactor: isolate application state per Discord server 2026-08-21 16:14:22 +02:00
mmacierzanka 94be2c7c79 Remove CI cache (has issues)
Backend / Lint i formatowanie (pull_request) Successful in 4s
Backend / Testy i kontrakt API (pull_request) Successful in 4s
Panel / Lint, typy i build (pull_request) Successful in 22s
Backend / Testowy build obrazu (pull_request) Successful in 1m48s
2026-08-21 15:49:55 +02:00
mmacierzanka d946e6b0af Panel na Vue 3 + TypeScript (#2)
Backend / Lint i formatowanie (pull_request) Successful in 4s
Backend / Testowy build obrazu (pull_request) Canceled after 0s
Backend / Testy i kontrakt API (pull_request) Canceled after 28s
Panel / Lint, typy i build (pull_request) Successful in 4m51s
2026-08-21 15:47:31 +02:00
mmacierzanka 6978b23055 Fix frontend CI
Backend / Lint i formatowanie (pull_request) Successful in 4s
Panel / Lint, typy i build (pull_request) Successful in 4m48s
Backend / Testowy build obrazu (pull_request) Canceled after 0s
Backend / Testy i kontrakt API (pull_request) Canceled after 7m8s
2026-08-21 15:38:33 +02:00
mmacierzanka 384770565a refactor: consolidate frontend serving and add health check
Backend / Lint i formatowanie (pull_request) Successful in 4s
Panel / Lint, typy i build (pull_request) Successful in 4m50s
Panel / Testowy build obrazu (pull_request) Failing after 2s
Backend / Testy i kontrakt API (pull_request) Successful in 9m24s
Backend / Testowy build obrazu (pull_request) Successful in 1m38s
2026-08-21 15:29:45 +02:00
mmacierzanka 240120db92 Merge branch 'release/4.0' into refactor/frontend-vue
# Conflicts:
#	.gitignore
#	web.py
2026-08-21 15:15:11 +02:00
mmacierzanka b917041808 Restrukturyzacja backendu w pakiet zyrarbot (#1) 2026-08-21 15:13:09 +02:00
mmacierzanka 2bd19b9703 Fix formatting
Backend / Lint i formatowanie (pull_request) Successful in 5s
Backend / Testy i kontrakt API (pull_request) Successful in 9m24s
Backend / Testowy build obrazu (pull_request) Successful in 4s
2026-08-21 15:01:27 +02:00
mmacierzanka 7e25db9038 Increase playback history capacity
Backend / Lint i formatowanie (pull_request) Failing after 3s
Backend / Testowy build obrazu (pull_request) Canceled after 0s
Backend / Testy i kontrakt API (pull_request) Canceled after 2m16s
2026-08-21 14:59:27 +02:00
mmacierzanka fff320d3f2 Fix song counter and inconsistent queue state 2026-08-21 14:56:29 +02:00
mmacierzanka 4468f96c3b Use pip cache (already configured) when installing packages
Backend / Lint i formatowanie (pull_request) Successful in 3s
Backend / Testy i kontrakt API (pull_request) Successful in 9m25s
Backend / Testowy build obrazu (pull_request) Successful in 6s
2026-08-21 13:50:40 +02:00
mmacierzanka d3a5058fde Formatting, README and other friends
Backend / Lint i formatowanie (pull_request) Successful in 3s
Backend / Testy i kontrakt API (pull_request) Failing after 2m19s
Backend / Testowy build obrazu (pull_request) Skipped
2026-08-21 13:41:38 +02:00
mmacierzanka b9152c41be Rename schemas to requests
Backend / Lint i formatowanie (pull_request) Failing after 4s
Backend / Testowy build obrazu (pull_request) Canceled after 0s
Backend / Testy i kontrakt API (pull_request) Canceled after 3m28s
2026-08-21 13:33:04 +02:00
mmacierzanka e4776d1348 Rename web to api to reflect, that it's not serving the frontend, split FastAPI creation 2026-08-21 13:31:11 +02:00
mmacierzanka d31747e80b refactor: split music cog by responsibility 2026-08-21 13:16:19 +02:00
mmacierzanka d706317dc8 Move web and bot packages to a consolidated ui package 2026-08-21 13:04:23 +02:00
mmacierzanka e21ccb4bb8 Adjust version and main bot class name 2026-08-21 12:55:39 +02:00
lgooseandClaude Bot 1f0a92b6f6 fix(ci): stabilne sprawdzenie proxy w jobie image panelu
Panel / Lint, typy i build (pull_request) Successful in 4m56s
Panel / Testowy build obrazu (pull_request) Successful in 18s
Krok padał na runnerze bez żadnego komunikatu. Powód był podwójny:

- asercja zakładała, że bez backendu nginx odpowie 502, a to zależy od
  tego, jak szybko zawiedzie DNS. Na runnerze zapytanie wisiało 30 sekund,
  aż do domyślnego resolver_timeout.
- grep bez dopasowania kończy się kodem 1, a Gitea uruchamia kroki w
  bash -e -o pipefail, więc podstawienie zmiennej przerywało cały krok,
  zanim zdążył wypisać powód. Lokalnie tego nie było widać, bo zsh nie
  ustawia tych flag.

Zamiast opierać się na tym, że proxy zawiedzie, job stawia teraz atrapę
backendu pod nazwą discord-bot - dokładnie tą, którą wskazuje nginx.conf
i compose.yaml - i sprawdza, że przez proxy wraca treść z atrapy. Test
przechodzi więc wtedy, gdy przekazywanie faktycznie działa, a nie wtedy,
gdy zawodzi w oczekiwany sposób. Sprawdzane jest też /ws.

Pozostałe usztywnienia: limit czasu na każde zapytanie, || true tam, gdzie
brak dopasowania jest poprawnym wynikiem, jawne komunikaty błędów z logiem
kontenera oraz sprzątanie kontenerów i sieci przez trap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:44:07 +02:00
lgooseandClaude Bot c5e016605b ci: pipeline panelu dla Gitea Actions
Panel / Lint, typy i build (pull_request) Successful in 4m55s
Panel / Testowy build obrazu (pull_request) Failing after 49s
Workflow .gitea/workflows/frontend.yaml z dwoma jobami:

- verify  npm ci, ESLint, kontrola typów, build produkcyjny i test dymny
- image   walidacja compose.yaml, build obrazu panelu i sprawdzenie
          działającego kontenera; job czeka na verify

Test dymny działa na zbudowanym artefakcie, a nie na źródłach, więc
sprawdza dokładnie to, co trafia do obrazu.

Job image weryfikuje trzy rzeczy w uruchomionym kontenerze: czy nginx
oddaje panel, czy nieznana ścieżka wraca jako index.html (fallback SPA)
oraz czy /api jest przekazywane do backendu. Backendu w tym kroku nie ma,
więc oczekiwaną odpowiedzią jest błąd bramy - 502 dowodzi, że reguła proxy
istnieje i celuje w usługę discord-bot, podczas gdy 404 oznaczałoby, że
nginx potraktował /api jak ścieżkę aplikacji.

Filtry paths ograniczają uruchomienia do zmian w katalogu frontend,
compose.yaml i samym workflow.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:34:55 +02:00
lgooseandClaude Bot 1b424eaa7d fix(panel): healthcheck obrazu odpytuje 127.0.0.1 zamiast localhost
nginx nasłuchuje wyłącznie na IPv4, a w obrazie alpine localhost rozwiązuje
się najpierw na ::1. Healthcheck kończył się więc odmową połączenia i
kontener nigdy nie osiągał stanu healthy - wyszło to przy pisaniu CI,
gdzie ten sam sposób odpytania nie działał.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:34:55 +02:00
lgooseandClaude Bot 105f3e6c6a chore(frontend): ESLint dla Vue i TypeScriptu
Konfiguracja we flat configu: zalecane zestawy js, typescript-eslint oraz
eslint-plugin-vue. Bloki <script setup lang="ts"> obsługuje vue-eslint-parser
przekazujący zawartość do parsera TypeScriptu.

Odstępstwa od domyślnych reguł:

- vue/multi-word-component-names wyłączone - kafelki panelu mają nazwy
  własne i nie każda jest dwuczłonowa
- no-unused-vars przepuszcza nazwy z prefiksem podkreślenia
- no-console wyłączone dla testu dymnego, który raportuje wynik na stdout

Szablony przepuszczone przez eslint --fix, co ujednoliciło łamanie
atrybutów i domykanie znaczników. Po zmianach kontrola typów, build
i test dymny nadal przechodzą.

Dochodzą skrypty npm: lint (z --max-warnings 0) oraz lint:fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:34:55 +02:00
lgooseandClaude Bot fd8d71a80d ci: pipeline backendu dla Gitea Actions
Backend / Lint i formatowanie (pull_request) Successful in 20s
Backend / Testy i kontrakt API (pull_request) Successful in 4m58s
Backend / Testowy build obrazu (pull_request) Successful in 1m34s
Workflow .gitea/workflows/backend.yaml z trzema jobami:

- lint   ruff check oraz ruff format --check --diff
- test   testy jednostkowe i kontrakt API, plus compileall całego pakietu
- image  testowy build obrazu; job czeka na lint i test

Job image nie poprzestaje na zbudowaniu obrazu - uruchamia kontener bez
prawdziwego tokenu Discorda i czeka, aż /api/status odpowie. Panel łączy
się z backendem po sieci, więc sam start procesu niczego nie dowodzi.
Zapytanie leci z wnętrza kontenera, dzięki czemu krok nie zależy od tego,
czy runner ma zainstalowanego curla albo wgeta.

Filtry paths ograniczają uruchomienia do zmian w kodzie Pythona,
zależnościach, konfiguracji lintera, Dockerfile i samym workflow.

Zależności instalują się z gotowych kółek, więc obraz CI nie potrzebuje
kompilatora. ffmpeg i libopus są wymagane dopiero przy odtwarzaniu,
a testy nie dotykają warstwy audio.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:25:40 +02:00
lgooseandClaude Bot 9ae22704ff test: przypięcie powierzchni API
Panel jest osobnym klientem HTTP, więc zmiana ścieżki albo metody psuje go
po cichu - w kodzie Pythona nic nie przestaje się kompilować. Test buduje
aplikację, generuje specyfikację OpenAPI i porównuje ją z listą 22
endpointów w obie strony:

- brak oczekiwanego endpointu to błąd
- nowy endpoint spoza listy również, żeby rozszerzenie API było świadomą
  decyzją, a nie skutkiem ubocznym

Sprawdzany jest też sam dokument OpenAPI, co wyłapuje błędy w deklaracjach
tras i modelach żądań.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:25:40 +02:00
lgooseandClaude Bot 2a961e1a8a chore: konfiguracja ruff i poprawki wskazane przez lintera
Ruff jako linter i formatter, konfiguracja w pyproject.toml:

- reguły E, W, F, I, N, UP, B, C4, SIM, TID, BLE, RUF
- extend-immutable-calls dla fastapi.Depends, File i Query - wstrzykiwanie
  zależności opiera się na wywołaniach w wartościach domyślnych argumentów,
  więc bugbear B008 zgłaszał tam 66 fałszywych trafień
- zakaz importów względnych, aby ścieżki w pakiecie czytało się jednoznacznie
- katalog frontend wyłączony z analizy

Poprawki, które wskazał linter:

- Awaitable, Callable i AsyncIterator importowane z collections.abc zamiast
  z typing
- zbędny argument trybu w open(path, "r")
- contextlib.suppress zamiast try/except/pass przy anulowaniu zadania
- zdjęte dwie niepotrzebne dyrektywy noqa
- pięć linii przekraczających 100 znaków zawiniętych

Całość przepuszczona przez ruff format. Do .gitignore dochodzi .ruff_cache/.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:25:40 +02:00
lgooseandClaude Bot ccfb0d2e34 docs: README z opisem struktury pakietu
Opisane uruchamianie, zmienne środowiskowe, testy oraz mapa pakietu
z klasą przypisaną do pliku. Spisane też trzy zasady, które trzymają
strukturę w ryzach: MusicPlayer jest jedynym właścicielem stanu, kolejka
jest nieusuwalna i śledzona indeksem, a zależności idą przez konstruktory
zamiast globalnych singletonów.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:11:37 +02:00
lgooseandClaude Bot 0879120c85 test: regresja arytmetyki kolejki wskaźnikowej
Kolejka jest nieusuwalna, a pozycję śledzi indeks, więc przeskoki,
usuwanie i zmiana kolejności muszą ten indeks poprawnie przesuwać. To
najbardziej podatna na błędy część przeniesionego kodu, więc dostaje
testy przypinające zachowanie:

- start odtwarzania przy dodaniu do pustej kolejki
- limit odtworzeń usuwający utwór i cofający indeks
- przechodzenie dalej oraz oba tryby zapętlania
- koniec kolejki czyszczący aktualny utwór
- przeskok, usunięcie utworu przed indeksem i usunięcie odtwarzanego
- zmiana kolejności zachowująca indeks na odtwarzanym utworze
- stosowanie głośności serwera i zatrzymanie przy niedziałających utworach

16 przypadków na atrapach Discorda, bez sieci i ffmpega. Uruchamiane
przez python -m unittest discover -s tests -t . - bez nowych zależności.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:11:37 +02:00
lgooseandClaude Bot c8a188647b fix(music): zatrzymanie pomijania, gdy nie da się odtworzyć żadnego utworu
Utwór, dla którego nie udało się zbudować świeżego źródła, był pomijany
przez rekurencyjne wywołanie play_next(advance=True). Przy włączonym
zapętlaniu kolejki - a przy zapętlaniu jednego utworu tym bardziej -
kolejny indeks wskazywał znowu na utwór niedziałający, więc pomijanie
kręciło się w kółko, zasypując kanał powiadomieniami.

Licznik nieudanych prób wędruje teraz przez play_next. Po liczbie
niepowodzeń równej długości kolejki odtwarzanie zatrzymuje się z jednym
komunikatem zamiast pętli.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:11:22 +02:00
lgooseandClaude Bot ab1d0d4a91 fix(music): stosowanie głośności serwera do nowo uruchamianych utworów
YTDLSource powstaje z domyślną głośnością 0.5, a ustawienie głośności
zmieniało tylko źródło aktualnie odtwarzane. Nowy utwór startował więc
na 50%, podczas gdy panel i /api/status raportowały stan serwera - domyślnie
100%. Zmiana głośności "znikała" po przejściu do kolejnego utworu.

Teraz play_next ustawia na źródle głośność ze stanu serwera, zanim
przekaże je do odtworzenia.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:11:10 +02:00
lgooseandClaude Bot 526e9ab644 refactor: korzeń kompozycji i usunięcie starych modułów
- Application składa wszystkie zależności w jednym miejscu i prowadzi bota
  równolegle z serwerem web. Panel przeżywa awarię logowania do Discorda,
  tak jak dotąd, ale komunikat idzie przez logging zamiast print.
- zyrarbot/__main__.py pozwala uruchomić pakiet przez python -m zyrarbot
  i konfiguruje logowanie; main.py zostaje cienkim punktem wejścia.
- Usunięte bot.py, web.py i ytdl.py - ich zawartość rozeszła się po
  pakiecie. Zniknął przy tym globalny obiekt bota tworzony przy imporcie
  modułu, który wymuszał kolejność importów między bot.py a web.py.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:59 +02:00
lgooseandClaude Bot 3fb1e26fb4 refactor(web): routery tematyczne i wstrzykiwanie zależności
22 endpointy z jednego pliku rozdzielone na routery według obszaru:
status, playback, queue, voice, uploads, settings i websocket.

- Powtarzany w każdym endpoincie blok "pobierz cog, sprawdź czy istnieje,
  ustal aktywny serwer, w razie czego rzuć 400" zastąpiony zależnościami
  z dependencies.py. Zniknęło 22-krotne bot.get_cog("MusicBot").
- Zależności wędrują przez app.state zamiast importu globalnego obiektu
  bota, więc warstwa web nie ma już zależności cyklicznej z modułem bota.
- create_app składa aplikację i montuje routery.
- Wycofane @app.on_event("startup") zastąpione lifespanem, który dodatkowo
  sprząta po sobie: odpina hook i zatrzymuje pętlę rozgłaszania.

Kontrakt HTTP pozostaje bez zmian - te same ścieżki, metody, kody błędów
i kształt odpowiedzi.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:59 +02:00
lgooseandClaude Bot c2f6d265c1 refactor(web): rozgłaszanie stanu po WebSockecie
- ConnectionManager - rejestr aktywnych połączeń (bez zmian poza typami).
- StatusService - budowanie obrazu stanu bota. Endpoint get_status miał
  126 linii i składał odpowiedź w dwóch wariantach; teraz jest to zestaw
  metod budujących poszczególne fragmenty payloadu.
- StatusBroadcaster - rozgłaszanie na żądanie oraz pętla cykliczna, wraz
  z zatrzymywaniem zadania przy wyłączaniu aplikacji. Wcześniej zadanie
  było tworzone w on_event("startup") i nigdy nie anulowane.

Wariant odpowiedzi dla bota bez serwerów zwraca teraz komplet kluczy
(dochodzi queue_index: -1), więc panel dostaje zawsze ten sam kształt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:42 +02:00
lgooseandClaude Bot 6e61892c04 refactor(web): modele żądań w osobnych plikach
Jedenaście klas pydantic zadeklarowanych jednym blokiem na górze web.py
rozdzielone na pliki zgodnie z zasadą jedna klasa = jeden plik. Każdy
model dostaje docstring mówiący, do czego służy - z samych nazw pól
(np. channel_id akceptujące pusty string jako reset) nie wynikało to
w oczywisty sposób.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:42 +02:00
lgooseandClaude Bot b2651bb957 refactor(bot): klient Discorda i cienki cog komend
- ZyrarBot buduje intencje, rejestruje cog i synchronizuje drzewo komend.
- MusicCog zawiera już wyłącznie obsługę komend ukośnikowych: walidację
  argumentów, komunikaty dla użytkownika i wywołania MusicPlayer. Cała
  praca ze stanem zniknęła z tego pliku.
- GuildResolver ustala kontekst serwera dla panelu webowego, który nie ma
  interakcji Discorda - wcześniej robiła to funkcja get_active_guild
  w web.py, sięgająca po globalny obiekt bota.
- Powtarzana w czterech komendach odpowiedź "dodano do kolejki" albo
  "teraz odtwarzane" zebrana w jednym helperze.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:28 +02:00
lgooseandClaude Bot a143cb21d3 refactor(music): MusicPlayer jako jedyny właściciel stanu odtwarzania
Logika odtwarzania wyprowadzona z coga Discorda do osobnej klasy. Dotąd
kolejkę modyfikowały dwie niezależne ścieżki: metody MusicBot oraz
endpointy w web.py sięgające po music_cog.queues[guild_id] i ustawiające
music_cog.jump_flag wprost. Teraz obie warstwy wołają MusicPlayer.

Przeniesione bez zmiany zachowania: enqueue, play_next wraz z arytmetyką
indeksu i limitem odtworzeń, remove, reorder, jump, sterowanie głośnością
oraz hook on_state_change rozgłaszający stan po WebSockecie.

Wybór kolejnego indeksu wydzielony do _next_index, dzięki czemu reguły
trybów zapętlania i przeskoku dają się czytać bez śledzenia całej metody.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:28 +02:00
lgooseandClaude Bot 4cc00618d1 refactor(music): stan serwera, tryb zapętlania i budowanie powiadomień
- GuildMusicState zbiera stan jednego serwera w jedną strukturę. Dotąd
  było to dziewięć równoległych słowników indeksowanych guild_id
  (queues, currents, volumes, loop_currents, loop_playlists, last_channels,
  history, queue_indices, jump_flag), co wymagało pamiętania, że przy
  każdej zmianie trzeba ruszyć kilka z nich naraz.
- LoopMode zastępuje parę booleanów loop_current/loop_playlist, których
  kombinacja "oba włączone" nie miała sensu, a mimo to była osiągalna.
  Wartości enuma (off/playlist/single) odpowiadają kontraktowi API panelu.
- TrackEmbedFactory zbiera budowanie osadzeń Discorda, powielone dotąd
  w enqueue_track i play_next.
- NotificationChannelResolver opisuje kolejność szukania kanału logów:
  zapamiętany -> z konfiguracji -> pierwszy zapisywalny na serwerze.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:11 +02:00
lgooseandClaude Bot 53ffae1663 refactor(audio): wydzielenie warstwy pozyskiwania dźwięku
ytdl.py rozbity na cztery pliki o rozłącznych rolach:

- YtdlOptions   opcje yt-dlp i FFmpeg, wcześniej moduły-globalne
- MediaProbe    odczyt długości pliku przez ffprobe
- YTDLSource    samo źródło audio - dane utworu i zliczanie odtworzonego
                czasu; dochodzą właściwości is_local, filepath, display_url
                i has_web_link, które zastępują powtarzane w kilku miejscach
                data.get("webpage_url") or url
- TrackFactory  budowanie źródeł z zapytania, adresu lub pliku

Rozdzielenie źródła od sposobu jego tworzenia usuwa globalną instancję
YoutubeDL i pozwala podmienić fabrykę w testach. Metoda recreate zbiera
w jednym miejscu odtwarzanie utworu od nowa z zachowaniem jego tożsamości
i pozostałej liczby odtworzeń.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:11 +02:00
lgooseandClaude Bot e59b4ad9f1 refactor(storage): repozytoria konfiguracji, historii i plików
Dostęp do danych trwałych schodzi za trzy repozytoria, zamiast być
rozsmarowany po metodach coga i endpointach:

- GuildConfigRepository - kanały powiadomień per serwer i domyślna liczba
  odtworzeń.
- HistoryRepository - historia per serwer z buforem w pamięci i limitem
  wpisów. Zasada odsiewania duplikatów (zgodny tytuł albo adres) siedzi
  teraz w HistoryEntry.is_same_track_as zamiast w warunku wbudowanym
  w listę składaną.
- UploadsRepository - katalog uploadów. Każda nazwa przechodzi przez
  os.path.basename w jednym miejscu, co zamyka temat ścieżek względnych
  przychodzących z API.

Wpisy historii i pliki opisują struktury HistoryEntry oraz UploadFileInfo
zamiast luźnych słowników z kluczami sprawdzanymi przez .get().

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:09:52 +02:00
lgooseandClaude Bot e194a8611c refactor(config): Settings i JsonFileStore jako podstawa pakietu
Początek pakietu zyrarbot, budowanego wedle zasady jedna klasa = jeden plik.

- Settings zbiera konfigurację ze zmiennych środowiskowych oraz ścieżki
  i limity, które dotąd były literałami rozsianymi po bot.py i web.py
  ("config/config.json", "uploads", zakres 1-999, domyślne 5 odtworzeń).
- JsonFileStore zastępuje sześć niemal identycznych bloków open/json.load
  z ręcznym try/except. Brakujący lub uszkodzony plik daje pusty słownik
  zamiast wyjątku, a zapis sam tworzy katalog.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:09:52 +02:00
lgooseandClaude Bot 1b5ce2109b docs(frontend): opis panelu, struktury i uruchamiania
README opisuje pracę lokalną (npm run dev z proxy na backend), budowanie,
uruchomienie kontenera oraz strukturę katalogu src z rolą każdego pliku.
Spisane też zasady, które utrzymują panel spójnym: stan płynie z serwera,
zero innerHTML, ikony jako komponenty zamiast skryptu z CDN.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:09:02 +02:00