ガイド

AI音声をボイスチャンネルにストリーミングするdiscord.jsのTTSボット

読み上げボットはDiscordで特に人気のあるボットです。ボイスチャンネルに参加し、マイクを使わないメンバーが書いた内容を読み上げます。自分でつくれば声を選べます。ナレーター、アナウンサー、ゲームやアニメのキャラクター、サーバー独自の声などです。

このガイドでは、discord.jsと@discordjs/voiceでボットをつくります。音声はSuperClonicaの音声合成APIから届き、私たちの計測では約300 msでストリーミングが始まるので、メッセージが届くとほぼすぐに話し始めます。

必要なもの

Node.js 18以降、Message Content intentを有効にしたDiscord Developer Portalのボットアプリ、「接続」と「発言」の権限付きの招待です。次にインストールします:

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

@discordjs/voiceのREADMEによると、@discordjs/opusなどのOpusライブラリと、ffmpeg-staticが提供するFFmpegが必要です。エンドツーエンド暗号化の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の1リクエストになります。APIはOggストリームを返し、@discordjs/voiceがFFmpegで変換して届いた順に再生します。ボットが話している間に来たメッセージはキューで待ちます。

声とプラン

SuperClonicaカタログのすべての声は、どのプランでも購入なしにIDでAPIから使えます。自分の録音から声を学習させてボットに使わせることもできます。無料キーは1日1分です。月5ドルのスタートは10分ごとの枠で最大3分、上位プランは8分、20分、65分です。枠を使い切るとAPIは429とRetry-Afterを返し、料金はかかりません。

APIをまったく使わないなら、自分のパソコンで無料のSuperClonicaエンジンを動かしてください。オフライン版はPythonのTTSボットのガイドで紹介しています。