link-stack/apps/bridge-worker/tasks/voice/twilio-recording.ts
2024-04-21 16:59:50 +02:00

101 lines
2.6 KiB
TypeScript

import Wreck from "@hapi/wreck";
import { withDb, AppDatabase } from "../../lib/db";
import { twilioClientFor } from "../../lib/common";
import { CallInstance } from "twilio/lib/rest/api/v2010/account/call";
import workerUtils from "../../lib/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<void> =>
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;