GUIDE

Un bot TTS discord.js qui diffuse des voix IA dans un salon vocal

Un bot de synthèse vocale est l’un des bots Discord les plus populaires : il rejoint le salon vocal et lit ce que tapent les membres sans micro. Avec le vôtre, vous choisissez la voix : un narrateur, un annonceur, un personnage de jeu ou d’anime, ou la voix propre de votre serveur.

Ce guide en construit un avec discord.js et @discordjs/voice. Le son vient de l’API vocale de SuperClonica, qui diffuse la parole dès environ 300 ms selon nos mesures, si bien que le bot parle presque aussitôt qu’un message arrive.

Ce qu’il vous faut

Node.js 18 ou plus récent, une application bot dans le Discord Developer Portal avec l’intent Message Content activé et une invitation avec les permissions Se connecter et Parler. Installez ensuite :

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

D’après le README de @discordjs/voice, il faut une bibliothèque Opus comme @discordjs/opus et FFmpeg, que fournit ffmpeg-static ; la bibliothèque de chiffrement de bout en bout DAVE est préinstallée. Créez une clé API SuperClonica sur la page de l’API vocale : c’est gratuit et sans carte.

Le bot

Définissez DISCORD_TOKEN, TTS_CHANNEL_ID (le salon textuel à lire), SUPERCLONICA_KEY et VOICE_ID, l’ID affiché sur la page de la voix du catalogue choisie, puis lancez le fichier avec 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);

Chaque message devient une requête avec "stream": true. L’API répond par un flux Ogg que @discordjs/voice convertit avec FFmpeg et joue au fur et à mesure ; les messages arrivés pendant que le bot parle attendent dans une file.

Voix et offres

Chaque voix du catalogue SuperClonica fonctionne par l’API avec son ID, dans toute offre et sans l’acheter ; vous pouvez aussi entraîner une voix à partir de votre propre enregistrement et la donner au bot. La clé gratuite donne une minute d’audio par jour. Débutant, à 5 $ par mois, donne jusqu’à 3 minutes de parole par fenêtre de 10 minutes, et les offres supérieures 8, 20 ou 65 minutes. Quand une fenêtre est épuisée, l’API répond 429 avec Retry-After et rien n’est facturé.

Pour se passer de toute API, utilisez le moteur gratuit SuperClonica sur votre ordinateur ; le guide du bot TTS en Python montre la version hors ligne.