import Wreck from "@hapi/wreck"; import { withDb, AppDatabase } from "../db"; import { twilioClientFor } from "../common"; import { CallInstance } from "twilio/lib/rest/api/v2010/account/call"; import workerUtils from "../utils"; interface WebhookPayload { startTime: string; endTime: string; to: string; from: string; duration: string; callSid: string; recording: string; mimeType: string; } const getTwilioRecording = async (url: string) => { try { const { payload } = await Wreck.get(url); return { recording: payload as Buffer }; } catch (error: any) { console.error(error.output); return { error: error.output }; } }; const formatPayload = ( call: CallInstance, recording: Buffer ): WebhookPayload => { return { startTime: call.startTime.toISOString(), endTime: call.endTime.toISOString(), to: call.toFormatted, from: call.fromFormatted, duration: call.duration, callSid: call.sid, recording: recording.toString("base64"), mimeType: "audio/mpeg", }; }; const notifyWebhooks = async ( db: AppDatabase, voiceLineId: string, call: CallInstance, recording: Buffer ) => { const webhooks = await db.webhooks.findAllByBackendId("voice", voiceLineId); if (webhooks && webhooks.length === 0) return; webhooks.forEach(({ id }) => { const payload = formatPayload(call, recording); workerUtils.addJob( "notify-webhook", { payload, webhookId: id, }, { // this de-depuplicates the job jobKey: `webhook-${id}-call-${call.sid}`, } ); }); }; interface TwilioRecordingTaskOptions { accountSid: string; callSid: string; recordingSid: string; voiceLineId: string; } const twilioRecordingTask = async ( options: TwilioRecordingTaskOptions ): Promise => withDb(async (db: AppDatabase) => { const { voiceLineId, accountSid, callSid, recordingSid } = options; const voiceLine = await db.voiceLines.findById({ id: voiceLineId }); if (!voiceLine) return; const provider = await db.voiceProviders.findByTwilioAccountSid(accountSid); if (!provider) return; const client = twilioClientFor(provider); const meta = await client.recordings(recordingSid).fetch(); const mp3Url = "https://api.twilio.com/" + meta.uri.slice(0, -4) + "mp3"; const { recording, error } = await getTwilioRecording(mp3Url); if (error) { throw new Error(`failed to get recording for call ${callSid}`); } const call = await client.calls(callSid).fetch(); await notifyWebhooks(db, voiceLineId, call, recording!); }); export default twilioRecordingTask;