ANLEITUNG

Ein discord.js-TTS-Bot, der KI-Stimmen in einen Sprachkanal streamt

Ein Text-to-Speech-Bot gehört zu den beliebtesten Discord-Bots: Er tritt dem Sprachkanal bei und liest vor, was stumme Mitglieder tippen. Mit einem eigenen Bot wählen Sie die Stimme: einen Erzähler, einen Ansager, eine Spiel- oder Anime-Figur oder die eigene Stimme Ihres Servers.

Diese Anleitung baut einen mit discord.js und @discordjs/voice. Den Ton liefert die Sprach-API von SuperClonica, die nach unseren Messungen ab etwa 300 ms Sprache streamt, sodass der Bot fast sofort nach einer Nachricht spricht.

Was Sie brauchen

Node.js 18 oder neuer, eine Bot-Anwendung im Discord Developer Portal mit aktiviertem Message-Content-Intent und eine Einladung mit den Rechten Verbinden und Sprechen. Dann installieren Sie:

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

Laut README von @discordjs/voice braucht es eine Opus-Bibliothek wie @discordjs/opus und FFmpeg, das ffmpeg-static liefert; die DAVE-Bibliothek für Ende-zu-Ende-Verschlüsselung ist vorinstalliert. Erstellen Sie auf der Seite der Sprach-API einen SuperClonica-API-Schlüssel; er ist kostenlos und braucht keine Karte.

Der Bot

Setzen Sie DISCORD_TOKEN, TTS_CHANNEL_ID (der vorzulesende Textkanal), SUPERCLONICA_KEY und VOICE_ID, die ID von der Seite der gewählten Katalogstimme, und starten Sie die Datei mit 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);

Jede Nachricht wird zu einer Anfrage mit "stream": true. Die API antwortet mit einem Ogg-Stream, den @discordjs/voice über FFmpeg umwandelt und schon beim Eintreffen abspielt; Nachrichten, die kommen, während der Bot spricht, warten in einer Warteschlange.

Stimmen und Tarife

Jede Stimme des SuperClonica-Katalogs funktioniert über die API mit ihrer ID, in jedem Tarif und ohne Kauf; Sie können auch eine Stimme aus Ihrer eigenen Aufnahme trainieren und dem Bot geben. Der kostenlose Schlüssel gibt eine Minute Audio pro Tag. Start für 5 $ im Monat gibt bis zu 3 Minuten Sprache in jedem 10-Minuten-Fenster, die größeren Tarife 8, 20 oder 65 Minuten. Ist ein Fenster aufgebraucht, antwortet die API mit 429 und Retry-After, berechnet wird nichts.

Ganz ohne API geht es mit der kostenlosen SuperClonica-Engine auf Ihrem Rechner; die Anleitung zum Python-TTS-Bot zeigt die Offline-Version.