Spotify Daily Vibe Bot (Telegram + Spotify + Docker)
Готовый backend-сервис, который:
- привязывается к вашему Spotify-аккаунту
- читает лайкнутые треки (
Liked Songs) - учитывает недавние прослушивания (последние дни)
- генерирует Spotify-плейлист с похожим вайбом по команде
/generate - опционально может запускаться по расписанию через
cron - минимизирует повторы и старается держать
>=80%новых треков (не лайкнутых и не рекомендованных ранее ботом) - управляется через Telegram
- запускается в Docker (
app, опциональноcron)
Что внутри
FastAPIbackend (OAuth callback + internal job endpoint)python-telegram-bot(polling)SQLite(история рекомендаций, кэш лайкнутых, run log)supercronicв отдельном контейнере для nightly cron trigger (опционально)
Важный момент по Spotify API
Spotify endpoint /recommendations может быть ограничен/недоступен для некоторых приложений. В сервисе реализован fallback:
- Spotify recommendations (если доступен)
- top tracks по артистам из вашего recent listening / liked library
- Spotify search по seed-артистам (если recommendations/top-tracks недоступны)
- optional Last.fm similarity (очень желательно для лучшего "вайба")
Для лучшего качества рекомендаций рекомендуется добавить LASTFM_API_KEY.
Быстрый старт
- Создайте Telegram-бота через
@BotFatherи получите токен. - Создайте Spotify App: https://developer.spotify.com/dashboard
- В Spotify App добавьте Redirect URI (должен совпасть 1 в 1), например:
https://your-domain.com/auth/spotify/callback- или для локальной разработки через tunnel:
https://xxxx.ngrok-free.app/auth/spotify/callback
- Скопируйте
.env.exampleв.envи заполните значения. - Запустите:
docker compose up -d --build
По умолчанию это поднимет только app (ручной режим через Telegram /generate).
Если захотите включить ночной cron, запустите отдельно:
docker compose --profile cron up -d cron
- Откройте Telegram и напишите боту:
/start/connect(получите ссылку на Spotify auth)- после подключения:
/generate
Настройка .env
Минимально обязательные поля:
TELEGRAM_BOT_TOKENSPOTIFY_CLIENT_IDSPOTIFY_CLIENT_SECRETSPOTIFY_REDIRECT_URIINTERNAL_JOB_TOKEN
Рекомендуемые:
LASTFM_API_KEY(улучшает похожесть треков)APP_TIMEZONE/TZSPOTIFY_DEFAULT_MARKET(двухбуквенный код страны, напримерNL,DE,US)CRON_SCHEDULE(например15 2 * * *, только если включаетеcron)
Telegram команды
/connect— привязать Spotify/status— статус подключения и последний плейлист/generate— сгенерировать плейлист сейчас/latest— ссылка на последний плейлист/setsize 30— размер плейлиста (5..100)/setratio 0.8— целевая доля новых треков (0.5..1.0)/sync— принудительно обновить лайкнутые треки
Алгоритм подбора рекомендаций
Ниже описан фактический пайплайн генерации плейлиста (как он сейчас работает в коде).
1. Подготовка входных данных
Перед генерацией бот:
- обновляет Spotify access token по refresh token (если нужно)
- синхронизирует лайкнутые треки из
Liked Songsв локальный кэш (saved_tracks) - загружает recent listening за окно
RECENT_DAYS_WINDOW(по умолчанию5дней) - загружает историю ранее рекомендованных треков (
recommendation_history)
2. Построение seed-профиля
Бот собирает seed'ы из двух источников: recent plays и liked library.
- Recent plays:
- каждый recent track получает вес с убыванием по позиции (более свежие прослушивания важнее)
- накапливаются веса по трекам и артистам
- Liked tracks:
- берется срез последних лайков (
~120) - плюс случайная выборка из более старых лайков (для разнообразия)
- из них также накапливаются веса по артистам
- берется срез последних лайков (
На выходе seed-профиля формируются:
seed_track_ids(до ~10 треков)seed_artists(до ~20 артистов)seed_artist_names(для Last.fm и Spotify Search fallback)recent_track_meta(нужно для Last.fm track-similar)
3. Сбор кандидатов (candidate pool)
Бот собирает общий пул кандидатов из нескольких источников и дедуплицирует их.
Источники (по порядку):
Spotify recommendations- вызывается батчами
- соблюдается лимит Spotify: максимум
5seed'ов на запрос (суммарно track + artist)
Spotify artist top tracks- по seed-артистам
Spotify searchпо seed-артистам (fallback)- используется, если recommendations / top-tracks ограничены или дали мало результатов
Last.fm track similar->Spotify search- для recent seed-треков
Last.fm artist similar->Spotify search- для seed-артистов
Если Spotify/Last.fm отдают ошибки на отдельных вызовах, бот старается деградировать мягко (использовать другие источники), а не валить весь run сразу.
4. Дедупликация кандидатов
Кандидаты дедуплицируются:
- по
spotify_track_id - по нормализованной сигнатуре
track_name + artist_names(на случай дублей / разных версий)
Если один и тот же трек найден из нескольких источников:
- сохраняется лучший score
- источник объединяется (например,
source1+source2)
5. Фильтрация и ранжирование
Базовая логика:
- сначала исключаются треки, которые уже есть в ваших лайках (
liked_ids) - если после этого пул пустой, включается fallback:
- разрешается использовать already-liked треки (с penalty), чтобы не падать с пустым результатом
Дополнительные коррекции score:
- penalty за уже рекомендованные раньше ботом (
history_ids) - penalty за лайкнутые (если включился liked fallback)
- небольшой boost за коллаборации / нескольких артистов
- небольшой boost за накопленные причины/источники
- popularity scoring слегка тяготеет к mid-popularity (не только мейнстрим и не только deep cuts)
6. Отбор финального списка (selection)
После ранжирования кандидаты делятся на:
novel— не были рекомендованы ранее и не в лайкахreused— уже были рекомендованы или (fallback) уже лайкнуты
Далее бот:
- сначала пытается набрать минимум по
min_new_ratio - соблюдает artist caps (ограничение количества треков одного артиста)
- если новых треков недостаточно, ослабляет ограничения
- затем дозаполняет повторными кандидатами
Результат:
tracks— финальный порядок трековnew_count/reused_countnotes— пояснение, если не удалось выдержать target по новым трекам
7. Создание плейлиста и запись истории
После сборки списка бот:
- создает Spotify playlist
- добавляет треки
- записывает run в
playlist_runsиplaylist_run_tracks - обновляет
recommendation_history - сохраняет
latest_playlist_urlу пользователя
Как работает анти-повтор
Бот хранит:
- все треки, которые уже рекомендовал раньше
- все ваши лайкнутые треки (кэш обновляется)
При сборке нового плейлиста:
- сначала исключает лайкнутые треки (если это возможно)
- отдает приоритет трекам, которых не было в рекомендациях ранее
- если новых треков не хватает, дозаполняет повторами из истории
- если кандидаты есть только среди лайкнутых, может использовать liked fallback вместо полного фейла run
- пишет статистику в БД (
new / reused)
Если в доступном пуле не хватает новых треков для 80%, бот сообщит об этом в статусе run.
Cron (ночной запуск)
По умолчанию cron отключен (manual-first режим: запускаете /generate вручную в Telegram).
В docker-compose.yml сервис cron помечен профилем cron, поэтому он не стартует при обычном:
docker compose up -d --build
Если хотите включить ночной запуск, поднимите его отдельно:
docker compose --profile cron up -d cron
cron по CRON_SCHEDULE вызывает внутренний endpoint:
POST /internal/jobs/nightly
Измените время через .env:
CRON_SCHEDULE=15 2 * * *
TZ=Europe/Moscow
Отключить обратно:
docker compose stop cron
Хранилище данных
- SQLite БД:
./data/app.db
Эта папка примонтирована как volume, поэтому данные переживают перезапуск контейнеров.
Проверка работы
GET /healthдолжен вернуть{"ok": true}- после
/generateв Telegram появится ссылка на Spotify playlist
Типичный деплой
- VPS + Docker Compose
APP_BASE_URL= публичный URL сервисаSPOTIFY_REDIRECT_URI=${APP_BASE_URL}/auth/spotify/callback- Telegram работает через polling (webhook не нужен)
cronможно не включать совсем, если генерация только вручную
Архитектура
Подробное описание архитектуры приложения, потоков данных и таблиц БД вынесено в DESIGN.md.
Feature Plans
Ниже план ближайших улучшений (roadmap), которые хорошо ложатся на текущую архитектуру.
- Явный feedback loop:
- команды вроде
/ban,/unban,/prefer - отдельная blacklist-таблица, чтобы "не понравилось" != "просто не лайкнул"
- команды вроде
- Настройки anti-repeat:
- жесткий запрет повторов на N дней/недель
- отдельные правила для liked / previously recommended
- Explainability / debug:
- why-this-track (показать источник, score, причины попадания)
- dry-run endpoint/команда без создания плейлиста
- Тонкая настройка алгоритма:
- веса источников (Spotify / Last.fm / search fallback)
- режимы генерации (explore / familiar / mixed)
- Улучшение источников кандидатов:
- дополнительные музыкальные источники / метаданные
- более умная работа с жанрами/артист-кластерами
- Персональный scheduler:
- per-user timezone и per-user cron schedule
- выбор дней недели / времени генерации
- Наблюдаемость:
- структурированные логи по source coverage и причинам фильтрации
- простые метрики по ошибкам Spotify/Last.fm и latency
- Хранилище / масштабирование:
- миграции (Alembic)
- Postgres вместо SQLite для multi-user сценариев
Ограничения / улучшения (если захотите дальше)
- Персонификация по timezone на пользователя (сейчас cron общий, но user-specific generation поддерживается вручную)
- Больше источников похожих треков (например, MusicBrainz/Discogs mapping)
- Выделенный Postgres вместо SQLite для multi-user нагрузки