125 lines
3.6 KiB
TypeScript
125 lines
3.6 KiB
TypeScript
|
|
import * as Hapi from "@hapi/hapi";
|
||
|
|
import * as Joi from "joi";
|
||
|
|
import * as Boom from "@hapi/boom";
|
||
|
|
import * as R from "remeda";
|
||
|
|
import * as Helpers from "../helpers";
|
||
|
|
import Twilio from "twilio";
|
||
|
|
import { crudRoutesFor, CrudControllerBase } from "common";
|
||
|
|
import { VoiceLineRecord, SavedVoiceLine } from "db";
|
||
|
|
|
||
|
|
const TwilioHandlers = {
|
||
|
|
freeNumbers: async (provider, request: Hapi.Request) => {
|
||
|
|
const { accountSid, apiKeySid, apiKeySecret } = provider.credentials;
|
||
|
|
const client = Twilio(apiKeySid, apiKeySecret, {
|
||
|
|
accountSid,
|
||
|
|
});
|
||
|
|
const numbers = R.pipe(
|
||
|
|
await client.incomingPhoneNumbers.list({ limit: 100 }),
|
||
|
|
R.filter((n) => n.capabilities.voice),
|
||
|
|
R.map(R.pick(["sid", "phoneNumber"]))
|
||
|
|
);
|
||
|
|
const numberSids = R.map(numbers, R.prop("sid"));
|
||
|
|
const voiceLineRepo = request.db().voiceLines;
|
||
|
|
const voiceLines: SavedVoiceLine[] =
|
||
|
|
await voiceLineRepo.findAllByProviderLineSids(numberSids);
|
||
|
|
const voiceLineSids = new Set(R.map(voiceLines, R.prop("providerLineSid")));
|
||
|
|
|
||
|
|
return R.pipe(
|
||
|
|
numbers,
|
||
|
|
R.reject((n) => voiceLineSids.has(n.sid)),
|
||
|
|
R.map((n) => ({ id: n.sid, name: n.phoneNumber }))
|
||
|
|
);
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
export const VoiceProviderRoutes = Helpers.withDefaults([
|
||
|
|
{
|
||
|
|
method: "GET",
|
||
|
|
path: "/api/voice/providers/{providerId}/freeNumbers",
|
||
|
|
options: {
|
||
|
|
description:
|
||
|
|
"get a list of the incoming numbers for a provider account that aren't assigned to a voice line",
|
||
|
|
validate: {
|
||
|
|
params: {
|
||
|
|
providerId: Joi.string().uuid().required(),
|
||
|
|
},
|
||
|
|
},
|
||
|
|
handler: async (request: Hapi.Request, _h: Hapi.ResponseToolkit) => {
|
||
|
|
const { providerId } = request.params;
|
||
|
|
const voiceProvidersRepo = request.db().voiceProviders;
|
||
|
|
const provider = await voiceProvidersRepo.findById(providerId);
|
||
|
|
if (!provider) return Boom.notFound();
|
||
|
|
switch (provider.kind) {
|
||
|
|
case "TWILIO":
|
||
|
|
return TwilioHandlers.freeNumbers(provider, request);
|
||
|
|
default:
|
||
|
|
return Boom.badImplementation();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
]);
|
||
|
|
|
||
|
|
class VoiceLineRecordController extends CrudControllerBase(VoiceLineRecord) { }
|
||
|
|
|
||
|
|
const validator = (): Record<string, Hapi.RouteOptionsValidate> => ({
|
||
|
|
create: {
|
||
|
|
payload: Joi.object({
|
||
|
|
providerType: Joi.string().required(),
|
||
|
|
providerId: Joi.string().required(),
|
||
|
|
number: Joi.string().required(),
|
||
|
|
language: Joi.string().required(),
|
||
|
|
voice: Joi.string().required(),
|
||
|
|
promptText: Joi.string().optional(),
|
||
|
|
promptRecording: Joi.binary()
|
||
|
|
.encoding("base64")
|
||
|
|
.max(50 * 1000 * 1000)
|
||
|
|
.optional(),
|
||
|
|
}).label("VoiceLineCreate"),
|
||
|
|
},
|
||
|
|
updateById: {
|
||
|
|
params: {
|
||
|
|
id: Joi.string().uuid().required(),
|
||
|
|
},
|
||
|
|
payload: Joi.object({
|
||
|
|
providerType: Joi.string().optional(),
|
||
|
|
providerId: Joi.string().optional(),
|
||
|
|
number: Joi.string().optional(),
|
||
|
|
language: Joi.string().optional(),
|
||
|
|
voice: Joi.string().optional(),
|
||
|
|
promptText: Joi.string().optional(),
|
||
|
|
promptRecording: Joi.binary()
|
||
|
|
.encoding("base64")
|
||
|
|
.max(50 * 1000 * 1000)
|
||
|
|
.optional(),
|
||
|
|
}).label("VoiceLineUpdate"),
|
||
|
|
},
|
||
|
|
deleteById: {
|
||
|
|
params: {
|
||
|
|
id: Joi.string().uuid().required(),
|
||
|
|
},
|
||
|
|
},
|
||
|
|
getById: {
|
||
|
|
params: {
|
||
|
|
id: Joi.string().uuid().required(),
|
||
|
|
},
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
export const VoiceLineRoutes = async (
|
||
|
|
_server: Hapi.Server
|
||
|
|
): Promise<Hapi.ServerRoute[]> => {
|
||
|
|
const controller = new VoiceLineRecordController("voiceLines", "id");
|
||
|
|
return Helpers.withDefaults(
|
||
|
|
crudRoutesFor(
|
||
|
|
"voice-line",
|
||
|
|
"/api/voice/voice-line",
|
||
|
|
controller,
|
||
|
|
"id",
|
||
|
|
validator()
|
||
|
|
)
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export * from "./twilio";
|