用 Python 制作 Discord TTS 机器人:在语音频道朗读聊天
请注意:Supertonic 3 目前不支持中文,本文中的机器人可朗读其支持的 31 种语言。Discord 自带的 /tts 会在每位听众的设备上用系统声音朗读消息,而且从不在语音频道里说话。想让没有麦克风的朋友打字、通话中的所有人都能听到,就需要一个加入语音频道并在那里播放消息的机器人。
本指南用 discord.py 在 Python 中制作这样一个机器人。它用任意 SuperClonica 声音朗读指定的文字频道:在自己电脑上用免费引擎离线运行、没有限制,或者如果不想自己运行模型,就通过 SuperClonica 语音合成 API。
准备工作
在 Discord Developer Portal 创建应用并添加机器人,在 Bot 页面开启 Message Content intent,并以“连接”和“说话”权限邀请机器人。安装 FFmpeg 并确保它在 PATH 中,然后安装库:
python -m pip install "discord.py[voice]" superclonica-cpu
discord.py 2.7 的 voice 扩展会带上 Discord 语音所需的加密库。SuperClonica 引擎首次运行时会下载约 400 MB 的 Supertonic 3 模型,之后在普通 CPU 上离线运行。
使用免费引擎的机器人
设置 DISCORD_TOKEN 和 TTS_CHANNEL_ID(要朗读的文字频道 ID),然后运行这个文件。当语音频道中的人在该文字频道发消息时,机器人会加入他所在的语音频道,用选定的声音朗读;台词在队列中排队,不会互相打断。
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 可以是内置声音 M1–M5、F1–F5 之一,也可以是 SuperClonica 目录中的声音文件:免费声音无需账号即可下载,付费声音购买后下载。合成在单独的线程中进行,所以朗读时机器人依然能响应。
通过 API 实现同一个机器人
如果机器人运行在放不下模型的小服务器上,就用 SuperClonica 语音合成 API 朗读:把 voice_line 换成下面的函数,并设置 SUPERCLONICA_KEY。目录中的任何声音都能凭其页面上的 ID 使用,无需购买。
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免费密钥每天 1 分钟音频,足够测试;每月 5 美元的入门套餐在每个 10 分钟窗口内最多 3 分钟语音,对活跃的服务器也绰绰有余。
让体验更好
只朗读一个频道,截断过长的消息,把网址读成“链接”,并让管理员可以移动或踢出机器人。引擎会把几种语言中常见的聊天俚语(例如英语的 idk、pls)读成完整的词。目录中的角色声音由粉丝制作,未经版权方认可:请在服务器里用于娱乐,绝不要用来冒充他人。