- aoi.py: poller for Navidrome (main + anime libraries), discovery playlists, release watch for liked/rated artists - Last.fm enrichment (bio, tags); Bandcamp search links - config.example.json: safe template; config.json gitignored - deploy/aoi.service: systemd unit (production) - assets/banner.png + aoi-avatar.png: persona banner + bot avatar - Russian README in line with sibling bots (rada/watcher)
160 lines
6.3 KiB
Markdown
160 lines
6.3 KiB
Markdown
<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__/`
|