GUIA

Um bot TTS do Discord em Python que lê o chat em voz alta num canal de voz

O /tts nativo do Discord lê uma mensagem no dispositivo de cada ouvinte com a voz do sistema dele e nunca fala num canal de voz. Para que amigos sem microfone escrevam e todos na chamada ouçam, você precisa de um bot que entre no canal de voz e toque as mensagens lá.

Este guia monta um em Python com discord.py. Ele lê um canal de texto escolhido com qualquer voz da SuperClonica: com o motor gratuito no seu computador, offline e sem limites, ou pela API de voz da SuperClonica se preferir não rodar o modelo.

O que você precisa

Crie uma aplicação no Discord Developer Portal, adicione um bot, ative o intent Message Content na página Bot e convide o bot com as permissões Conectar e Falar. Instale o FFmpeg para que esteja no PATH e depois as bibliotecas:

python -m pip install "discord.py[voice]" superclonica-cpu

O extra voice do discord.py 2.7 traz as bibliotecas de criptografia de que a voz do Discord precisa. O motor SuperClonica baixa o modelo Supertonic 3, cerca de 400 MB, na primeira execução e depois roda offline num processador comum.

O bot com o motor gratuito

Defina DISCORD_TOKEN e TTS_CHANNEL_ID, o ID do canal de texto que será lido, e rode o arquivo. Quando alguém que está num canal de voz escreve nesse canal de texto, o bot entra no canal de voz da pessoa e lê a mensagem com a voz escolhida; as falas esperam numa fila e nunca se sobrepõem.

import asyncio, os, re, tempfile
import discord
from superclonica import Superclonica

TOKEN = os.environ["DISCORD_TOKEN"]
TEXT_CHANNEL = int(os.environ["TTS_CHANNEL_ID"])  # the text channel the bot reads
VOICE = "announcer.json"  # a voice file from the catalog, or M1..M5, F1..F5
LINK = re.compile(r"https?://\S+")

intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
tts = Superclonica()
queue = asyncio.Queue()

def voice_line(text):
    audio, _ = tts.synthesize(text, voice=VOICE, lang="en")
    path = tempfile.NamedTemporaryFile(suffix=".wav", delete=False).name
    tts.save(path, audio)
    return path

async def speaker():
    while True:
        voice_client, text = await queue.get()
        path = await asyncio.to_thread(voice_line, text)
        done = asyncio.Event()
        voice_client.play(discord.FFmpegPCMAudio(path), after=lambda error: client.loop.call_soon_threadsafe(done.set))
        await done.wait()
        os.remove(path)

@client.event
async def on_ready():
    client.loop.create_task(speaker())

@client.event
async def on_message(message):
    if message.author.bot or message.channel.id != TEXT_CHANNEL or not message.author.voice:
        return
    voice_client = message.guild.voice_client or await message.author.voice.channel.connect()
    text = LINK.sub("link", message.clean_content)[:300]
    await queue.put((voice_client, f"{message.author.display_name} says: {text}"))

client.run(TOKEN)

VOICE pode ser uma das vozes embutidas M1 a M5 e F1 a F5 ou um arquivo de voz do catálogo da SuperClonica: as gratuitas são baixadas sem conta, as pagas depois da compra. A síntese roda numa thread separada, então o bot continua respondendo enquanto uma fala é gerada.

O mesmo bot pela API

Se o bot roda num servidor pequeno sem espaço para o modelo, gere as falas pela API de voz da SuperClonica: troque voice_line por esta função e defina SUPERCLONICA_KEY. Qualquer voz do catálogo funciona pelo ID da página dela, sem comprar.

import os, tempfile
import requests

API = "https://superclonica.com/api/v1/speech"
KEY = os.environ["SUPERCLONICA_KEY"]
VOICE_ID = "49cd4d119695528694fac0a1b9fb58a8"  # any catalog voice: the ID on its page

def voice_line(text):
    answer = requests.post(API, headers={"Authorization": "Bearer " + KEY}, timeout=120,
                           json={"text": text, "voice": VOICE_ID, "language": "en", "format": "mp3"})
    answer.raise_for_status()
    path = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False).name
    with open(path, "wb") as file:
        file.write(answer.content)
    return path

Uma chave gratuita dá um minuto de áudio por dia, o bastante para testar; o Inicial, US$ 5 por mês, dá até 3 minutos de fala em cada janela de 10 minutos, de sobra para um servidor movimentado.

Para ficar agradável

Leia um canal só, corte mensagens longas, diga «link» em vez do endereço e deixe os moderadores moverem ou expulsarem o bot. O motor lê como palavras gírias de chat comuns em português, como vc e pq, e em outros idiomas. As vozes de personagens do catálogo são feitas por fãs e não são endossadas pelos detentores dos direitos: use-as para se divertir no seu servidor, nunca para se passar por alguém.