GUIA

Um bot TTS com discord.js que transmite vozes de IA para um canal de voz

Um bot de texto para fala é um dos bots de Discord mais populares: ele entra no canal de voz e lê o que os membros sem microfone escrevem. Com o seu, você escolhe a voz: um narrador, um locutor, um personagem de jogo ou anime ou a voz própria do seu servidor.

Este guia monta um com discord.js e @discordjs/voice. O áudio vem da API de voz da SuperClonica, que pelas nossas medições transmite fala a partir de uns 300 ms, então o bot fala quase assim que a mensagem chega.

O que você precisa

Node.js 18 ou mais novo, uma aplicação de bot no Discord Developer Portal com o intent Message Content ativado e um convite com as permissões Conectar e Falar. Depois instale:

npm install discord.js @discordjs/voice @discordjs/opus ffmpeg-static

Segundo o README do @discordjs/voice, ele precisa de uma biblioteca Opus como @discordjs/opus e do FFmpeg, que o ffmpeg-static fornece; a biblioteca de criptografia de ponta a ponta DAVE já vem instalada. Crie uma chave de API da SuperClonica na página da API de voz; é grátis e não pede cartão.

O bot

Defina DISCORD_TOKEN, TTS_CHANNEL_ID (o canal de texto que será lido), SUPERCLONICA_KEY e VOICE_ID, o ID que aparece na página da voz do catálogo escolhida, e rode o arquivo com 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);

Cada mensagem vira uma requisição com "stream": true. A API responde com um fluxo Ogg que o @discordjs/voice converte pelo FFmpeg e toca conforme chega; mensagens que chegam enquanto o bot fala esperam numa fila.

Vozes e planos

Toda voz do catálogo da SuperClonica funciona pela API com seu ID, em qualquer plano e sem comprar; você também pode treinar uma voz com a sua gravação e entregá-la ao bot. A chave gratuita dá um minuto de áudio por dia. O Inicial, US$ 5 por mês, dá até 3 minutos de fala em cada janela de 10 minutos, e os planos maiores 8, 20 ou 65 minutos. Quando a janela acaba, a API responde 429 com Retry-After e nada é cobrado.

Para não usar API nenhuma, use o motor gratuito da SuperClonica no seu computador; o guia do bot TTS em Python mostra a versão offline.