TTS-бот на discord.js, который стримит AI-голоса в голосовой канал
Бот озвучки текста — один из самых популярных ботов Discord: он заходит в голосовой канал и читает то, что пишут участники без микрофона. Свой бот позволяет выбрать голос: диктора, анонсера, персонажа игры или аниме или собственный голос сервера.
В этом руководстве мы соберём такого бота на discord.js и @discordjs/voice. Звук даёт API озвучки SuperClonica, который по нашим замерам начинает стримить речь примерно через 300 мс, так что бот заговаривает почти сразу после сообщения.
Что понадобится
Node.js 18 или новее, приложение-бот в Discord Developer Portal с включённым Message Content intent и приглашение с правами Connect и Speak. Затем установите:
npm install discord.js @discordjs/voice @discordjs/opus ffmpeg-static
Согласно README @discordjs/voice, ему нужна библиотека Opus, например @discordjs/opus, и FFmpeg, который даёт ffmpeg-static; библиотека сквозного шифрования DAVE ставится сама. Создайте API-ключ SuperClonica на странице API озвучки — это бесплатно и без карты.
Бот
Задайте DISCORD_TOKEN, TTS_CHANNEL_ID (текстовый канал для чтения), SUPERCLONICA_KEY и VOICE_ID — ID со страницы выбранного голоса каталога — и запустите файл через node:
import { Client, GatewayIntentBits } from "discord.js";
import { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, getVoiceConnection } from "@discordjs/voice";
import { Readable } from "node:stream";
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent, GatewayIntentBits.GuildVoiceStates] });
const player = createAudioPlayer();
const queue = [];
async function speak(text) {
const answer = await fetch("https://superclonica.com/api/v1/speech", {
method: "POST",
headers: { Authorization: "Bearer " + process.env.SUPERCLONICA_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ text, voice: process.env.VOICE_ID, language: "en", stream: true }),
});
if (!answer.ok) throw new Error(await answer.text());
return createAudioResource(Readable.fromWeb(answer.body));
}
player.on(AudioPlayerStatus.Idle, async () => {
const next = queue.shift();
if (next) player.play(await next());
});
client.on("messageCreate", async (message) => {
if (message.author.bot || message.channelId !== process.env.TTS_CHANNEL_ID) return;
const channel = message.member?.voice.channel;
if (!channel) return;
const connection = getVoiceConnection(message.guildId) ?? joinVoiceChannel({
channelId: channel.id, guildId: message.guildId, adapterCreator: message.guild.voiceAdapterCreator });
connection.subscribe(player);
const job = () => speak(message.member.displayName + " says: " + message.cleanContent.slice(0, 300));
if (player.state.status === AudioPlayerStatus.Idle && queue.length === 0) player.play(await job());
else queue.push(job);
});
client.login(process.env.DISCORD_TOKEN);Каждое сообщение — это один запрос с "stream": true. API отвечает потоком Ogg, который @discordjs/voice перекодирует через FFmpeg и играет по мере получения; сообщения, пришедшие, пока бот говорит, ждут в очереди.
Голоса и тарифы
Любой голос каталога SuperClonica работает через API по своему ID, на любом тарифе и без покупки; можно и обучить голос по своей записи и отдать боту его. Бесплатный ключ даёт минуту звука в день. «Старт» за $5 в месяц даёт до 3 минут речи в каждом 10-минутном окне, тарифы старше — 8, 20 или 65 минут. Когда окно исчерпано, API отвечает 429 с Retry-After и ничего не списывает.
Чтобы обойтись совсем без API, используйте бесплатный движок SuperClonica на своём компьютере; офлайн-версию показывает руководство по TTS-боту на Python.