通过 Twilio 发送生成的音频

了解如何通过 Twilio 将生成的语音集成到电话通话中。

操作指南 · 假设你已完成 ElevenAPI 快速入门,并拥有 Twilio 账户。

本指南将介绍如何使用 Twilio 和 ElevenLabs 通过电话发送 AI 生成的消息。此流程可将高质量语音消息直接发送给来电者。

创建 Twilio 和 ngrok 账户

本指南将使用 Twilio 和 ngrok,请先创建相应账户。

使用 Express 创建服务器

初始化项目

为项目创建新文件夹。

mkdir elevenlabs-twilio
cd elevenlabs-twilio
npm init -y

安装依赖项

npm install @elevenlabs/elevenlabs-js express express-ws twilio

安装开发依赖项

npm i @types/node @types/express @types/express-ws @types/ws dotenv tsx typescript

创建文件

// src/app.ts
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";
import express, { Response } from "express";
import ExpressWs from "express-ws";
import { Readable } from "stream";
import VoiceResponse from "twilio/lib/twiml/VoiceResponse";
import { type WebSocket } from "ws";
const app = ExpressWs(express()).app;
const PORT: number = parseInt(process.env.PORT || "5000");
const elevenlabs = new ElevenLabsClient();
const voiceId = "aMSt68OGf4xUZAnLpTU8";
const outputFormat = "ulaw_8000";
const text = "This is a test. You can now hang up. Thank you.";
function startApp() {
app.post("/call/incoming", (_, res: Response) => {
const twiml = new VoiceResponse();
twiml.connect().stream({
url: `wss://${process.env.SERVER_DOMAIN}/call/connection`,
});
res.writeHead(200, { "Content-Type": "text/xml" });
res.end(twiml.toString());
});
app.ws("/call/connection", (ws: WebSocket) => {
ws.on("message", async (data: string) => {
const message: {
event: string;
start?: { streamSid: string; callSid: string };
} = JSON.parse(data);
if (message.event === "start" && message.start) {
const streamSid = message.start.streamSid;
const response = await elevenlabs.textToSpeech.convert(voiceId, {
modelId: "eleven_flash_v2_5",
outputFormat: outputFormat,
text,
});
const readableStream = Readable.from(response);
const audioArrayBuffer = await streamToArrayBuffer(readableStream);
ws.send(
JSON.stringify({
streamSid,
event: "media",
media: {
payload: Buffer.from(audioArrayBuffer as any).toString("base64"),
},
})
);
}
});
ws.on("error", console.error);
});
app.listen(PORT, () => {
console.log(`Local: http://localhost:${PORT}`);
console.log(`Remote: https://${process.env.SERVER_DOMAIN}`);
});
}
function streamToArrayBuffer(readableStream: Readable) {
return new Promise((resolve, reject) => {
const chunks: Buffer[] = [];
readableStream.on("data", (chunk) => {
chunks.push(chunk);
});
readableStream.on("end", () => {
resolve(Buffer.concat(chunks).buffer);
});
readableStream.on("error", reject);
});
}
startApp();
# .env
SERVER_DOMAIN=
ELEVENLABS_API_KEY=

了解代码

处理来电

当有人拨打号码时,Twilio 会向 /call/incoming 端点发送 POST 请求。 随后,我们使用 twiml.connect,通过将 url 设置为 /call/connection 端点,告知 Twilio 要通过 WebSocket 处理通话。

function startApp() {
app.post('/call/incoming', (_, res: Response) => {
const twiml = new VoiceResponse();
twiml.connect().stream({
url: `wss://${process.env.SERVER_DOMAIN}/call/connection`,
});
res.writeHead(200, { 'Content-Type': 'text/xml' });
res.end(twiml.toString());
});

创建文本转语音

此处会监听 Twilio 发送到 WebSocket 端点的消息。收到 start 消息事件时,会使用 ElevenLabs TypeScript SDK 生成音频。

app.ws('/call/connection', (ws: WebSocket) => {
ws.on('message', async (data: string) => {
const message: {
event: string;
start?: { streamSid: string; callSid: string };
} = JSON.parse(data);
if (message.event === 'start' && message.start) {
const streamSid = message.start.streamSid;
const response = await elevenlabs.textToSpeech.convert(voiceId, {
modelId: 'eleven_flash_v2_5',
outputFormat: outputFormat,
text,
});

发送消息

从 ElevenLabs 收到音频后,将其转换为数组缓冲区,并通过 WebSocket 将音频发送给 Twilio。

const readableStream = Readable.from(response);
const audioArrayBuffer = await streamToArrayBuffer(readableStream);
ws.send(
JSON.stringify({
streamSid,
event: "media",
media: {
payload: Buffer.from(audioArrayBuffer as any).toString("base64"),
},
})
);

将 ngrok 指向应用

Twilio 需要可公开访问的 URL。我们将使用 ngrok 转发应用的本地端口,并将其公开为公共 URL。

在终端中运行以下命令:

ngrok http 5000

复制 ngrok 域名(不含 https://),用于环境变量。

更新环境变量

使用 ngrok 域名和 ElevenLabs API 密钥更新 .env 文件。

# .env
SERVER_DOMAIN=*******.ngrok.app
ELEVENLABS_API_KEY=*************************

启动应用

运行以下命令启动应用:

npm run dev

设置 Twilio

按照 Twilio 指南创建新号码。创建号码后,前往 Phone Numbers -> Manage -> Active numbers 中的“Configure”标签页。

在“A call comes in”部分,输入应用的完整 URL(务必添加 /call/incoming 路径):

例如:https://***ngrok.app/call/incoming

拨打电话

拨打该号码。你应该会听到使用 ElevenLabs 音色的消息。

部署到生产环境的提示

在生产环境中运行应用时,请确保将 SERVER_DOMAIN 环境变量设置为服务器域名。同时务必更新 Twilio 中的 URL,使其指向生产服务器。

总结

现在你应该已基本了解如何将 Twilio 与 ElevenLabs 音色集成。如有其他问题,或对如何改进这篇博客文章有建议,欢迎点击下方“Suggest edits”或“Raise issue”按钮。