// ========================= // WebSocket Twilio <-> OpenAI Realtime // ========================= import WebSocket, { WebSocketServer } from 'ws'; import fs from 'fs-extra'; import path from 'path'; import { OpenAI } from 'openai'; const PORT = 8089; const PROMPT_DIR = path.resolve('./prompts'); const LOG_DIR = path.resolve('./logs'); fs.ensureDirSync(PROMPT_DIR); fs.ensureDirSync(LOG_DIR); const wss = new WebSocketServer({ port: PORT }); console.log(`WebSocket server running on ws://localhost:${PORT}`); // OpenAI Realtime const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); if (!process.env.OPENAI_API_KEY) { console.error("⚠️ Définir OPENAI_API_KEY dans les variables d'environnement !"); process.exit(1); } wss.on('connection', async (ws, req) => { const callSid = new URLSearchParams(req.url.split('?')[1]).get('callSid') || 'TEST'; console.log(`Nouvelle connexion pour callSid=${callSid}`); // Charger le prompt initial let prompt = {}; const promptFile = path.join(PROMPT_DIR, `${callSid}.json`); if (fs.existsSync(promptFile)) prompt = fs.readJsonSync(promptFile); // Créer session Realtime OpenAI (ChatGPT + TTS) const session = await openai.realtime.sessions.create({ model: "gpt-5o-realtime-preview", voice: "alloy" // voix ChatGPT }); // Gestion audio entrant Twilio ws.on('message', async (msg) => { const data = JSON.parse(msg); if (data.event === 'media') { const audio = Buffer.from(data.media.payload, 'base64'); // Envoyer audio à OpenAI try { await session.sendAudio(audio); // stream vers ChatGPT } catch (err) { console.error(`Erreur sendAudio: ${err.message}`); } } }); // Réception texte/TTS d’OpenAI session.on('transcript', (text) => { console.log(`[${callSid}] Transcript: ${text}`); logEvent(callSid, { type: 'transcript', text }); }); session.on('audio', (audioBuffer) => { ws.send(JSON.stringify({ event: 'media', media: { payload: audioBuffer.toString('base64') } })); }); ws.on('close', () => { console.log(`Connexion fermée pour callSid=${callSid}`); }); }); // Fonction utilitaire pour logs function logEvent(callSid, event) { const logFile = path.join(LOG_DIR, `${callSid}.json`); let logs = []; if (fs.existsSync(logFile)) logs = fs.readJsonSync(logFile); logs.push({ timestamp: Date.now(), ...event }); fs.writeJsonSync(logFile, logs, { spaces: 2 }); }