Бот для Яндекс Музыки в Telegram. Инлайн-поиск, карточка «сейчас играет», выгрузка альбомов, лирика, свой кэш-канал вместо повторных закачек. На GramIO 0.12.
- 🔎 инлайн-поиск треков —
@bot название, ссылка на трек/альбом, «недавнее» (история прослушиваний, с пагинацией и бесконечным скроллом), или вообще пусто (тогда отдаёт текущий трек). Опечатки подправляет Yandex suggest; - 🖼 карточка «сейчас играет» — ambient-рендер на
@napi-rs/canvas, рендер уходит в пул воркеров, чтобы не блокировать event loop; - ⚡ аудио-эффекты прямо в инлайне —
@bot ncore название/@bot speed название/@bot slow название(nightcore / ускорение / замедление+реверб), реальный DSP через ffmpeg, результат кэшируется отдельно от оригинала; - ❤️ лайк/анлайк трека кнопкой под треком;
- 🎚 выбор качества звука —
best/economy/lossless(для lossless — расшифровка CDN-ссылок на лету); - 📦 выгрузка альбома пачками (
sendMediaGroup) — с прогрессом, отменой, повтором упавших треков, возобновлением после рестарта бота, без OOM даже на здоровенных аудиокнигах; - 💾 свой кэш-канал в Telegram вместо повторных закачек с Яндекса — пустышка → реальная заливка, stale-refresh по расписанию;
- 📝 лирика строкой-цитатой — Genius (по фразе, потом по автору/треку), фолбэк на Yandex, если не нашлось;
- 🔐 device-flow логин, токены шифруются at-rest (Fernet), ws-наблюдатель за плеером владельца (Ynison);
/start,/help,/np,/settings,/login,/logout, админские/health,/broadcast(с подтверждением и остановкой на лету).
Порядок важен: два шага в BotFather — самая частая причина «бот молчит», без них бот заведётся и будет выглядеть рабочим, но инлайн не заработает.
- Бот в @BotFather:
/newbot→ токен;/setinline→ любая заглушка подсказки (напримерищу трек...) — обязательно, без этого инлайн (@bot запрос) не работает вообще;/setinlinefeedback→ выбери бота →100%— обязательно, иначе Telegram присылаетchosen_inline_result(сигнал «юзер выбрал трек, качай») только для доли запросов, и большинство выборов зависают на плейсхолдере навсегда. - Канал-кэш: создай приватный канал, добавь бота админом (права на
публикацию и удаление сообщений). Узнать numeric id: перешли любое
сообщение ИЗ канала боту @userinfobot —
формат
-100xxxxxxxxxx. Свой telegram id (дляADMIN_USER_ID) там же. - Клонируй и поставь:
Спросит токен/id канала/твой id (Enter — пропустить и дозаполнить в
git clone <url> nowym && cd nowym ./install.sh
.envвручную), сам сгенерит секреты, при постгресе под рукой — накатит роли и пропишет DSN сам. - Подключение к Telegram — выбери один способ:
- self-hosted Bot API сервер (рекомендуется — без домена и TLS,
поднимается локально за пару минут):
В
# api_id/api_hash — на my.telegram.org → API development tools (создать приложение) docker run -d --name telegram-bot-api --restart always \ -e TELEGRAM_API_ID=<api_id> -e TELEGRAM_API_HASH=<api_hash> \ -p 127.0.0.1:8081:8081 aiogram/telegram-bot-api:latest
.env:BOT_API_BASE_URL=http://127.0.0.1:8081/bot(именно с/botна конце — токен и метод бот доклеивает сам). - облачный webhook — если уже есть домен с TLS-сертификатом (certbot
и т.п.): заполни
WEBHOOK_HOST/WEBHOOK_SSL_CERT/WEBHOOK_SSL_KEYв.env,BOT_API_BASE_URLоставь пустым.
- self-hosted Bot API сервер (рекомендуется — без домена и TLS,
поднимается локально за пару минут):
- Запуск:
npm run start(илиpm2 start ecosystem.config.cjs—install.shуже подогнал в нём пути под эту машину).
Если что-то не так — бот скажет прямо на старте (например явно укажет,
какая переменная в .env не задана), падать молча со временем не должен.
Ambient-рендер «сейчас играет» — обложка, размытая в фон, прогресс-бар,
мета-строка (альбом/тип/год/лейбл/номер трека). Два формата: 16:9 под
фото-сообщение, 9:16 под сторис/шортсы.
Есть и лирик-режим — вместо прогресс-бара лента строк текста вокруг активной (Genius → Yandex fallback):
Не настройка, а префикс инлайн-запроса — результат летит через ffmpeg и кэшируется отдельно от оригинального трека:
@bot ncore название трека — nightcore (ускорение + питч вверх)
@bot speed название трека — ускорение без питч-шифта
@bot slow название трека — замедление + реверб
Качество звука (best/economy/lossless), формат ответа (кнопка/текст/
всё/ничего — отдельно для трека и для карточки), какие поля показывать на
карточке (альбом/тип/год/лейбл/номер трека/аватар), стиль прогресс-бара
(wavy/bar), соотношение сторон карточки (16:9/9:16).
GramIO 0.12 + @gramio/dialogs (меню/настройки) +
@gramio/format + @gramio/storage-redis + @napi-rs/canvas (рендер
карточки) + undici/node-wreq.
Кроме Node.js нужны на машине (не npm-пакеты):
- PostgreSQL — две базы (users/cache), см. ниже;
- Redis — для
@gramio/dialogs; - ffmpeg — только для аудио-эффектов (
ncore/speed/slow, см. ниже); без него всё остальное работает, просто эта фича упадёт с понятной ошибкой; - Docker — опционально, для рекомендуемого self-hosted Bot API сервера (см. «Быстрый старт»). Без домена/TLS через него проще всего, но можно и без Docker (собрать сервер из исходников или пойти путём webhook).
Debian/Ubuntu:
sudo apt install postgresql redis-server ffmpeg./install.sh из «Быстрого старта» выше делает всё это за один проход:
проверит node, поставит зависимости, подготовит .env (секреты сгенерит
сам; спросит токен/id канала/твой id и впишет их же), при доступном
постгресе — накатит роли и пропишет POSTGRES_*_DSN сам (пароли
сгенерены, руками синхронизировать не с чем), проверит redis/ffmpeg,
подгонит ecosystem.config.cjs под текущую машину, прогонит typecheck
как smoke-тест.
Руками — то же самое по шагам:
npm install
cp .env.example .envОбязательные (без них бот не стартует — упадёт с явной ошибкой, какой переменной не хватает):
| Переменная | Назначение |
|---|---|
TELEGRAM_BOT_TOKEN |
токен бота от @BotFather |
TELEGRAM_CHANNEL_ID |
id служебного канала-кэша (бот — админ канала) |
TOKEN_ENCRYPTION_KEY |
ключ шифрования токенов в БД (install.sh генерит сам) |
POSTGRES_USERS_DSN |
DSN базы пользователей |
POSTGRES_CACHE_DSN |
DSN базы кэша |
BOT_API_BASE_URL или WEBHOOK_HOST |
один из двух обязателен — способ получать апдейты от Telegram, см. «Быстрый старт» |
Функционально обязательна, хоть и не проверяется на старте:
ADMIN_USER_ID — без него /health//broadcast недоступны никому
(гейт fail-closed) и алерты об ошибках в личку админу не шлются.
Опционально: GENIUS_TOKEN (лирика точнее — ключ на genius.com/api-clients),
OWNER_ID (кто в Ynison-наблюдателе, по умолчанию = ADMIN_USER_ID),
WEBHOOK_PATH/WEBHOOK_SECRET/WEBHOOK_SSL_CERT/WEBHOOK_SSL_KEY
(детали облачного webhook-режима), NOW_PLAYING_TOKEN (без него GET /now-playing всегда 401), ICON_SET_NAME (Telegram custom-emoji пак для
иконок кнопок — дефолт tgiosicons публичный, работает без настройки;
свой пак — через @Stickers), лимиты и размеры кэшей — дефолты и полный
список в src/settings.ts.
Две изолированные Postgres-базы (users/cache). install.sh накатывает
роли автоматически (пароли генерит сам, тут же прописывает DSN в .env).
Руками — один раз:
sudo -u postgres psql -f docs/sql/init.sql # свои пароли вместо CHANGE_ME_*и те же пароли — в POSTGRES_*_DSN в .env. Схему таблиц внутри баз бот
создаёт сам при первом старте, руками накатывать не нужно.
Нужен для стека диалогов (@gramio/dialogs). Дефолт — localhost:6379, без
пароля. sudo apt install redis-server && sudo systemctl enable --now redis-server.
npm run start # tsx src/main.ts — webhook (облако) или long-polling (self-hosted API)
npm run dev # то же самое, но tsx watch — авто-рестарт на изменениях
npm run typecheck # tsc --noEmit
npm test # node --import tsx --test, ~123 теста, секунд 12Режим выбирается сам: задан BOT_API_BASE_URL → self-hosted Bot API сервер +
long-polling; пусто → облако + webhook. В webhook-режиме поднимается один
http(s)-сокет с тремя роутами: WEBHOOK_PATH (апдейты Telegram, опционально
проверяется X-Telegram-Bot-Api-Secret-Token), GET /now-playing (JSON о
текущем треке владельца + недавние (recent, до 20 последних, из живых
трекчейнджей вотчера, без похода в Yandex API), Authorization: Bearer NOW_PLAYING_TOKEN) и GET /health. TLS — если заданы
WEBHOOK_SSL_CERT/WEBHOOK_SSL_KEY.
В репе есть готовый ecosystem.config.cjs (единственный fork-инстанс,
autorestart, --expose-gc). Полностью портативный — cwd/interpreter
вычисляются на лету (__dirname/process.execPath), патчить или
адаптировать под сервер не нужно, работает сразу после клонирования куда
угодно.
pm2 start ecosystem.config.cjs
pm2 save # чтобы пережило ребут (после pm2 startup)
pm2 logs nowym-ts| Путь | Назначение |
|---|---|
src/bot/ |
хендлеры, диалоги-меню (@gramio/dialogs), сборка карточки, DI-контейнер |
src/bot/iconSet.ts |
резолвер премиум-эмодзи (icon_custom_emoji_id) под кнопки |
src/services/ |
рендер карточки (+ пул воркеров cardRenderPool.ts), поиск, кэш, альбомы, рассылка. lyricsVideo.ts — закомментированная заготовка, не живой код |
src/yandex/ |
клиент Yandex Music API, метаданные, лирика, Ynison |
src/infra/ |
http-клиент, шифрование, логирование, rate-limit, ретраи 429/5xx (floodRetry.ts), алертер об ошибках админу (adminAlerter.ts) |
src/storage/ |
пулы Postgres, схемы users/cache/settings |
src/tagging/ |
тегирование аудио (TagLib/ffmpeg) |
test/ |
node:test, ~123 штуки |
install.sh |
установка в одну команду (см. выше) |
docs/sql/init.sql |
создание Postgres-ролей и баз |
- Бот запускается, но
@bot запросничего не отдаёт — почти всегда забытый/setinlineв BotFather. Проверить:/setinlineбез аргумента в диалоге с BotFather покажет текущий статус. - Инлайн-выдача показывается, но выбор трека зависает на плейсхолдере
(«гружу...») навсегда — забытый
/setinlinefeedback→100%в BotFather. Без него Telegram шлётchosen_inline_resultне на каждый выбор, а бот именно по этому сигналу качает и подменяет плейсхолдер на реальное аудио. - Бот не стартует вовсе —
.envнеполный, ошибка при старте называет переменную явно (X не задан в .env). Если ругается на «ни BOT_API_BASE_URL, ни WEBHOOK_HOST» — см. «Быстрый старт», шаг 4, нужен ровно один из двух способов получать апдейты. npm run typecheckиnpm test— если не про конфиг, а про сам код.pm2 logs nowym-tsиGET /health— если это прод. Свежий рестарт подхватывает.envи код с диска, а не то, что было закешировано node-процессом в памяти.
MIT — используй как хочешь, в коммерции и не только, но сохраняй указание оригинального авторства.




