В этом руководстве создается Discord-бот, который по слеш-команде присоединяется к голосовому каналу пользователя, захватывает несколько секунд аудио, распознает песню с помощью AudD и отвечает, указывая исполнителя, название и song_link, которую ваши пользователи могут открыть. Это решение подойдет для сообществ, где в голосовых каналах играет музыка (вечеринки прослушивания, диджей-сеты, совместный просмотр), и люди постоянно спрашивают: «Что это за трек?».
AudD поддерживает эталонную реализацию на Go, AudDMusic/DiscordBot, которая является базовой версией и ее стоит изучить, если вы планируете развертывание в продакшене. В этом руководстве аналогичное решение создается на Node.js с использованием @audd/sdk и discord.js, поскольку инструментарий Node является наиболее простым для старта. В репозитории на Go используется github.com/AudDMusic/audd-go, если вы предпочитаете создавать бота на нем.
Что вы создадите
Единую слеш-команду — /whatsong — которая выполняет пять действий последовательно:
- Определяет, в каком голосовом канале находится пользователь, и подключается к нему.
- Подписывается на аудиопоток канала и записывает короткий фрагмент (около 6 секунд вполне достаточно — AudD создает цифровой отпечаток по короткому клипу).
- Декодирует пользовательские пакеты Opus из Discord в формат PCM, а затем кодирует этот буфер в небольшой WAV/MP3-клип в памяти.
- Отправляет клип на стандартную точку входа (endpoint) распознавания AudD.
- Отвечает в текстовом канале в формате «исполнитель — название» и указывает song_link, либо сообщает «нет совпадений», если ничего не было распознано.
Работа с аудио — это единственная сложная часть, и она относится к discord.js / @discordjs/voice, а не к AudD. Сам вызов AudD представляет собой простую строчку audd.recognize(buffer) — SDK принимает «сырые» байты напрямую, поэтому, как только у вас появился клип в памяти, вы сразу передаете его туда.
Используйте стандартную точку входа, а не enterprise. Вы отправляете один короткий клип и хотите получить один ответ. Стандартная точка входа (POST https://api.audd.io/) отвечает менее чем за 2 секунды и возвращает result: null (SDK возвращает null) в случае отсутствия совпадений. Enterprise-версия предназначена для длинных файлов, где вам нужно найти каждый трек.
Используйте стандартную точку входа, а не enterprise. Вы отправляете один короткий клип и хотите получить один ответ. Стандартная точка входа (POST https://api.audd.io/) отвечает менее чем за 2 секунды и возвращает result: null (SDK возвращает null) в случае отсутствия совпадений. Enterprise-версия предназначена для длинных файлов, где вам нужно найти каждый трек.
Предварительные требования
- API-токен с сайта dashboard.audd.io. Первые 300 запросов бесплатны; тестовый токен подходит для запуска «hello-world», но ограничен 10 запросами в день.
- Приложение Discord + токен бота из Discord Developer Portal, с включенными привилегированными интентами Server Members и voice, а также приглашенный на ваш сервер бот с разрешениями на подключение (Connect) и выступление (Speak).
- Node.js версии 20 или новее.
- ffmpeg, доступный в системе (@discordjs/voice и этап кодирования используют его через командную строку).
- Пакеты:
Пошаговое руководство
Шаг 1: Проверка вызова AudD изолированно
Прежде чем работать со звуком из Discord, проверьте работу распознавания на известном файле. Это именно тот вызов, который бот будет делать в конце — меняется только источник: вместо URL используется буфер в памяти.
При запуске этого кода выводятся исполнитель, название и ссылка lis.tn для тестового трека. Успешный вызов, который ничего не распознал, возвращает null — это не ошибка, а вердикт «нет совпадений», который вы передадите в чат.
Шаг 2: Регистрация слеш-команды и подключение к голосовому каналу
Настройте клиент discord.js и команду /whatsong. Когда она вызывается, найдите голосовой канал пользователя по голосовому состоянию (voice state) гильдии и подключитесь к нему с помощью @discordjs/voice.
Параметр selfDeaf: false имеет значение: оглушенный (deafened) бот не получает аудио. Когда вы запустите это и введете /whatsong из голосового канала, бот присоединится к нему.
Шаг 3: Захват короткого аудиофрагмента
Discord передает голос в виде индивидуальных Opus-пакетов для каждого пользователя через приемник соединения (receiver). Подпишитесь на аудио, декодируйте его в PCM с помощью prism-media и соберите несколько секунд в буфер. Аудио PCM в Discord имеет параметры 48 кГц, 16 бит, стерео — закодируйте его в клип, для которого AudD сможет создать отпечаток.
Несколько реальных особенностей приема голоса в Discord, которые полезно знать:
- Аудио обрабатывается отдельно для каждого пользователя. receiver.subscribe(userId) записывает один источник. Для музыкального бота или человека, транслирующего аудио с рабочего стола в канал, этот идентификатор пользователя принадлежит тому, кто воспроизводит музыку. Простой подход заключается в подписке на самого громкого участника; эталонный бот на Go отслеживает говорящих пользователей и записывает активного.
- EndBehaviorType.AfterSilence останавливает поток после паузы, поэтому явный setTimeout используется здесь в качестве верхнего предела для непрерывного аудио.
- На выходе получается «сырой» PCM, а не медиаконтейнер. AudD требует настоящий аудиофайл, поэтому следующий шаг упаковывает/кодирует его.
Шаг 4: Кодирование PCM в клип и распознавание
Преобразуйте буфер «сырого» PCM в небольшой файл WAV или MP3 с помощью ffmpeg, а затем передайте закодированные байты напрямую в audd.recognize. SDK автоматически определяет, что вы передали Uint8Array/Buffer, и загружает его.
6-секундный клип со скоростью 128 кбит/с весит примерно 100 КБ, что значительно ниже лимита в 10 МБ для стандартной точки входа. Нет необходимости задействовать enterprise-версию.
Шаг 5: Ответ в чате и очистка ресурсов
Соберите все вместе в обработнике взаимодействий (interaction handler). Сначала подтвердите выполнение команды (запись занимает несколько секунд, что дольше стандартного 3-секундного окна для первичного ответа в Discord), а затем отредактируйте ответ, добавив результат.
Запустив бота и введя /whatsong во время воспроизведения музыки, через несколько секунд вы получите ответ вроде Imagine Dragons — Warriors со ссылкой, которой можно поделиться.
Что вы получаете в ответ
audd.recognize возвращает единственный объект результата (лучшее совпадение) или null. Поля, которые использует бот:
Чтобы прикрепить ссылки на стриминговые сервисы вместо простой переадресации на lis.tn, запросите метаданные провайдеров. Каждый провайдер добавляет задержку, поэтому запрашивайте только то, что необходимо для ответа:
Обработка ошибок
У голосового бота есть несколько характерных сценариев сбоев; разделите их, чтобы пользователи получали понятное сообщение:
- Нет совпадений — результат распознавания равен null. Это не ошибка. Ответьте «нет совпадений»; это частый случай, когда в клипе преобладает речь или тишина.
- Ошибки аутентификации — неверный или отсутствующий токен AudD. Вызывайте сбой при запуске, а не при выполнении команды; SDK выдает исключение при создании, если токен не найден.
- Ошибки квот — на аккаунте не осталось запросов (тестовый токен ограничен 10 запросами в день). Выведите сообщение «попробуйте позже» и оповестите администраторов.
- Недопустимое аудио — закодированный клип не удалось декодировать (пустая запись, буфер тишины). Обрабатывайте это как понятную пользователю ошибку «не удалось захватить аудио», а не как сбой программы.
- Ошибки подключения — временный сбой сети при обращении к AudD. SDK повторяет попытки при сетевых ошибках до этапа загрузки; выведите общее сообщение о повторной попытке в случае неудачи.
Процесс записи завершается с ошибкой независимо от AudD: оглушенный бот, пользователь без звука или отсутствие бинарного файла ffmpeg — все это обнаруживается до вызова распознавания. Убедитесь, что буфер PCM не пуст перед кодированием, чтобы не отправлять пустой клип и не тратить запрос на гарантированное отсутствие совпадений.
Дальнейшее развитие
- Записывайте активного спикера, а не того, кто вызвал команду. Отслеживайте события connection.receiver.speaking и записывайте тот источник, который действительно издает звук — именно так поступает эталонный бот на Go.
- Распознавайте более длинные фрагменты. Если пользователи хотят идентифицировать целый диджей-сет, а не один трек, сохраняйте запись в файл и отправляйте его на enterprise-точку входа (audd.recognizeEnterprise(buffer, { limit })), чтобы получить список всех песен, а не только одну. Всегда задавайте параметр limit — enterprise-версия тарифицирует каждые 12 секунд.
- Кэширование по окну аудиоотпечатка. Если один и тот же трек продолжает воспроизводиться, используйте дебаунс для повторных вызовов /whatsong в течение короткого промежутка времени, чтобы не тратить запрос на ответ, который вы только что получили.
- Чтение полей вне типизированной поверхности. Любое серверное поле, которое SDK не предоставляет в виде типизированного свойства, доступно в song.extras.
- Создание сканера авторских прав для пользовательского контента
- Сканирование архива аудиофайлов и запись метаданных обратно
- Документация Node.js SDK
- Справочник API

