<p align="center">
  <img src="assets/banner.png" alt="Aoi banner" width="40%">
</p>

# Aoi

**Aoi** (Аой-тян) — домашний Matrix-бот для музыкальной библиотеки. Она следит за Navidrome и discovery-playlist, аккуратно докладывает о новых альбомах, свежих Discovery-плейлистах и предстоящих релизах любимых артистов, обогащая карточки данными Last.fm и ссылками на внешние источники.

## Что умеет

| Блок | Что происходит |
| --- | --- |
| Navidrome albums | Поллинг библиотек `main` и `anime` — новые альбомы превращаются в карточки с обложкой, описанием, тегами и рейтингом. |
| Discovery playlists | Свежесгенерированные плейлисты discovery-playlist (имя содержит «Discovery») приходят в Matrix отдельным постом. |
| Release watch | Аналитика по любимым/высоко оцененным артистам: анонсы и фактические релизы из дискографии, отслеживаемой discovery-playlist. |
| Rich-описания | Last.fm-биография, теги, обложка из `album.getInfo`, ссылки на Last.fm и поиск Bandcamp. |
| Baseline | На первом запуске можно зафиксировать всё уже существующее как «уже видела», чтобы не получить спам с историей. |
| State & dedupe | SQLite хранит увиденные альбомы, плейлисты и релизы — повторов не будет. |

## Как устроена логика

Aoi не делает запросов к LLM и не пытается «угадывать» — она опирается на конкретные источники и кеширует увиденное:

1. По расписанию опрашивает Navidrome и discovery-playlist (раздельные интервалы для альбомов, плейлистов и релизов).
2. Сравнивает с локальным state и забирает только новые сущности.
3. Обогащает карточки Last.fm-данными при наличии ключа.
4. Шлёт карточку в Matrix-комнату с обложкой, описанием и ссылками.
5. Помечает сущность как обработанную в SQLite.

Каждый источник работает независимо, так что падение Navidrome не валит discovery, и наоборот.

## Сообщения в Matrix

Карточка нового альбома выглядит примерно так:

```text
🎧 Новый альбом в Navidrome
Artist — Album Title (2026)

━━ ОПИСАНИЕ ━━
Краткая биография/описание альбома из Last.fm…

━━ ТЕГИ ━━
shoegaze · indie · dream-pop

🔗 Last.fm: https://www.last.fm/music/Artist/Album
🔗 Bandcamp: https://bandcamp.com/search?q=Artist+Album
```

Discovery-плейлисты приходят отдельной карточкой со списком треков, release watch — отдельной с пометкой типа релиза (`announced`, `released`).

## Файлы проекта

| Файл | Назначение |
| --- | --- |
| `aoi.py` | Основной сервис. |
| `config.example.json` | Безопасный пример конфигурации. |
| `config.json` | Локальная конфигурация с секретами, не коммитится. |
| `requirements.txt` | Python-зависимости. |
| `deploy/aoi.service` | systemd unit для production. |
| `assets/banner.png` | Баннер README. |
| `aoi.db` | SQLite-состояние, не коммитится. |
| `aoi.log` | Лог сервиса, не коммитится. |

## Конфигурация

Создать конфиг из примера:

```bash
cp config.example.json config.json
```

Заполнить:

- `matrix.homeserver`, `matrix.room_id`, `matrix.access_token`, `matrix.user_id`;
- `bot.name` (по умолчанию `Аой-тян`) и `bot.avatar_path` для Matrix-профиля;
- `navidrome.url`, `navidrome.username`, `navidrome.password`, идентификаторы библиотек;
- `discovery.db_path` — путь к SQLite базе discovery-playlist;
- `metadata.lastfm_api_key` — ключ Last.fm для био и тегов;
- `polling.*` — интервалы опроса источников;
- `polling.baseline_existing_on_first_run` — `true` на первом запуске, чтобы не спамить историей.

Production-путь:

```text
/storage/scripts/aoi
```

## Установка

В production Aoi использует виртуальное окружение Watcher (общие зависимости):

```bash
cd /storage/scripts/watcher
python3 -m venv .venv
. .venv/bin/activate
pip install -r /storage/scripts/aoi/requirements.txt
```

Можно завести отдельный venv — тогда обновить `deploy/aoi.service`.

## Запуск

```bash
/storage/scripts/watcher/.venv/bin/python /storage/scripts/aoi/aoi.py
```

Healthcheck:

```bash
curl -fsS http://127.0.0.1:18323/healthz
```

Ожидаемый ответ:

```json
{"ok":true}
```

## systemd

Unit хранится в репозитории:

```text
deploy/aoi.service
```

Установка или обновление:

```bash
sudo cp /storage/scripts/aoi/deploy/aoi.service /etc/systemd/system/aoi.service
sudo systemctl daemon-reload
sudo systemctl enable aoi.service
sudo systemctl restart aoi.service
```

Операции:

```bash
sudo systemctl restart aoi.service
systemctl --no-pager -l status aoi.service
journalctl -u aoi.service -f
tail -f /storage/scripts/aoi/aoi.log
```

## Ручные команды

```bash
curl -X POST http://127.0.0.1:18323/run/navidrome-albums
curl -X POST http://127.0.0.1:18323/run/discovery-playlists
curl -X POST http://127.0.0.1:18323/run/release-watch
```

## Что не коммитить

- `config.json`
- `*.db`
- `*.log`
- `.venv/`
- `__pycache__/`
