Vai alla navigazione

Inviare audio generato tramite Twilio

Scopri come integrare il parlato generato nelle chiamate telefoniche con Twilio.

Guida pratica · Presuppone che tu abbia completato il quickstart di ElevenAPI e disponga di un account Twilio.

In questa guida scoprirai come inviare un messaggio generato dall’IA tramite una chiamata telefonica usando Twilio ed ElevenLabs. Questo processo ti permette di inviare messaggi vocali di alta qualità direttamente ai tuoi chiamanti.

Crea gli account Twilio e ngrok

Per questa guida useremo Twilio e ngrok, quindi crea i relativi account.

Crea il server con Express

Inizializza il progetto

Crea una nuova cartella per il progetto

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

Installa le dipendenze

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

Installa le dipendenze di sviluppo

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

Crea i file

// 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=

Comprendere il codice

Gestire la chiamata in arrivo

Quando chiami il tuo numero, Twilio effettua una richiesta POST al tuo endpoint in /call/incoming. Poi usiamo twiml.connect per indicare a Twilio che vogliamo gestire la chiamata tramite il nostro websocket, impostando l’URL sul nostro endpoint /call/connection.

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());
});

Creare il Text to Speech

Qui ascoltiamo i messaggi che Twilio invia al nostro endpoint websocket. Quando riceviamo un evento del messaggio start, generiamo l’audio usando l’SDK TypeScript di ElevenLabs.

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,
});

Inviare il messaggio

Dopo aver ricevuto l’audio da ElevenLabs, lo convertiamo in un array buffer e lo inviamo a Twilio tramite il websocket.

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"),
},
})
);

Indirizza ngrok alla tua applicazione

Twilio richiede un URL accessibile pubblicamente. Useremo ngrok per inoltrare la porta locale della nostra applicazione ed esporla come URL pubblico.

Esegui il seguente comando nel terminale:

ngrok http 5000

Copia il dominio ngrok (senza https://) da usare nelle variabili d’ambiente.

Aggiorna le variabili d’ambiente

Aggiorna il file .env con il dominio ngrok e la chiave API di ElevenLabs.

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

Avvia l’applicazione

Esegui il seguente comando per avviare l’app:

npm run dev

Configura Twilio

Segui le guide di Twilio per creare un nuovo numero. Dopo aver creato il numero, vai alla scheda “Configure” in Phone Numbers -> Manage -> Active numbers.

Nella sezione “A call comes in”, inserisci l’URL completo della tua applicazione (assicurati di aggiungere il path /call/incoming):

Ad es. https://***ngrok.app/call/incoming

Effettua una chiamata telefonica

Chiama il tuo numero. Dovresti sentire un messaggio con una voce di ElevenLabs.

Suggerimenti per il deployment in produzione

Quando esegui l’applicazione in produzione, assicurati di impostare la variabile d’ambiente SERVER_DOMAIN su quella del tuo server. Ricordati anche di aggiornare l’URL in Twilio affinché punti al server di produzione.

Conclusione

Ora dovresti avere una conoscenza di base dell’integrazione di Twilio con le voci di ElevenLabs. Per ulteriori domande o suggerimenti su come migliorare questo articolo del blog, seleziona pure il pulsante “Suggest edits” o “Raise issue” qui sotto.