/** * WebVoice.ts * * Example usage: * * import WebVoice from './WebVoice'; * * const voice = new WebVoice(); * * // Register your event handlers * voice.on('call-start', () => console.log('Call started')); * voice.on('call-end', () => console.log('Call ended')); * voice.on('message', (msg) => console.log('Message:', msg)); * voice.on('volume-level', (vol) => console.log('Volume:', vol)); * voice.on('error', (err) => console.error('Error:', err)); * * // Initialize with your agent ID * voice.init('YOUR_AGENT_ID'); * * // Start a WebRTC call with server-provided info: * await voice.startCall({ * callServiceUrl: 'wss://YOUR_WEBSOCKET_SERVER/webrtc-call', * agentData: { ID: 'MyAgent123', ... }, * region: 'us-east-1', // or whichever region * convoId: 'abc123', * }); * * // Optionally mute/unmute local audio: * voice.toggleMute(); * * // End the call when done: * voice.endCall(); */ import { z } from "zod"; import { AppIceServers, GET_CALL_SERVICE_API_URL, initWebRtcCallSchema, } from "../../../../src/app/Types/firebase"; // import { // generateRandomId, // GET_CALL_SERVICE_API_URL, // } from "../../../../src/app/utils/functions"; import { TypedWebCall } from "./voice-only-events"; const initEvent = initWebRtcCallSchema.omit({ type: true }); // @ts-ignore export type InitCallEvent = z.infer; function generateRandomId(length: number) { return Math.random().toString(36).substring(2, length + 2); } export default class WebCall extends TypedWebCall { private agentId: string | null = null; private region: string | null = null; private convoId: string | null = null; private sessionId: string | null = null; private mediaStream: MediaStream | null = null; // Local (microphone) stream private remoteStream: MediaStream | null = null; // Remote (assistant) stream private audioElement: HTMLAudioElement | null = null; // Hidden audio element for remote playback private peerConnection: RTCPeerConnection | null = null; private webSocket: WebSocket | null = null; // To keep track of whether local mic is muted private isMuted: boolean = false; // For volume-level detection private audioContext: AudioContext | null = null; private analyser: AnalyserNode | null = null; private volumeAnimationFrame: number | null = null; /** * Initialize the API with the given agent ID. * @param agentId - Your unique agent ID (or any string) */ public async init(input: InitCallEvent): Promise { this.agentId = input.agentId || null; this.region = input.region || null; this.convoId = input.convoId || generateRandomId(15); this.sessionId = input.sessionId || generateRandomId(15); } /** * Start the WebRTC call flow: * 1) Open a WebSocket to your signaling server * 2) Create a RTCPeerConnection * 3) Get local microphone and attach it * 4) Handle remote track (attach to hidden