가이드

AI 목소리를 음성 채널에 스트리밍하는 discord.js TTS 봇

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 요청을 하나씩 보냅니다. API는 Ogg 스트림으로 답하고, @discordjs/voice가 FFmpeg로 변환해 도착하는 대로 재생합니다. 봇이 말하는 동안 온 메시지는 대기열에서 기다립니다.

목소리와 요금제

SuperClonica 카탈로그의 모든 목소리는 어떤 요금제에서든 구매 없이 ID로 API에서 쓸 수 있습니다. 내 녹음으로 목소리를 학습시켜 봇에 줄 수도 있습니다. 무료 키는 하루 1분입니다. 월 5달러 스타트는 10분 창마다 최대 3분, 상위 요금제는 8분, 20분, 65분입니다. 창을 다 쓰면 API는 429와 Retry-After를 돌려주고 요금은 청구되지 않습니다.

API 없이 쓰려면 내 컴퓨터에서 무료 SuperClonica 엔진을 돌리세요. 오프라인 버전은 Python TTS 봇 가이드에 있습니다.