Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import { twilio } from '../../shared/twilio';
- import {
- AGENCY_PHONE_CALL_SETTINGS_QUERY,
- CREATE_INBOUND_PHONE_CALL,
- CREATE_PHONE_CALL_RECORDING,
- FETCH_PHONE_CALL_LIST,
- LEAD_PHONE_NUMBER_QUERY,
- UPDATE_INBOUND_PHONE_CALL,
- UPDATE_PHONE_CALL,
- FETCH_PHONE_CALL,
- FETCH_USER,
- FETCH_INBOUND_PHONE_CALL,
- FETCH_USERS_FOR_CALL,
- CREATE_PHONE_CALL_PARTICIPANT,
- UPDATE_PHONE_CALL_PARTICIPANT,
- FETCH_PHONE_CALL_PARTICIPANT,
- FETCH_PHONE_CALL_PARTICIPANTS,
- ACTIVE_USERS_QUERY,
- CREATE_VOICEMAIL_MUTATION,
- INBOUND_CALL_INFO_QUERY,
- } from './phone-call-queries';
- import { USER_STATUSES } from '../user/user-model';
- import { CallParticipantStatus } from '@cobuildlab/lead-volt-share';
- import { normalize8baseReferenceConnect } from '@cobuildlab/8base-utils';
- import { twiml } from 'twilio';
- import { getBaseUrl } from '../../shared/utils';
- const { LANDLINE_PHONE } = process.env;
- /**
- * Create a new phone call recording record in the database.
- *
- * @param {object} context - 8base's context object.
- * @param {object} data - Phone call recording data to store.
- * @param {object} options - Options object to configure the request.
- *
- * @returns {Promise} The request result.
- */
- export const createPhoneCallRecording = async (context, data, options = {}) => {
- const { phoneCallRecordingCreate } = await context.api.gqlRequest(
- CREATE_PHONE_CALL_RECORDING,
- { data },
- options,
- );
- console.log('call: Created Phone Call Recording:', JSON.stringify(phoneCallRecordingCreate));
- return phoneCallRecordingCreate;
- };
- /**
- * Fetches phone calls.
- *
- * @param {object} context - 8base's context object.
- * @param {object} filter - Filter object to limit the phone calls retrieved.
- * @param {number} first - How many results must retrieve.
- * @param {number} skip - How many results must skip before getting the results.
- * @param {object} options - Options object to configure the request.
- *
- * @returns {Promise} The request result.
- */
- export const fetchPhoneCalls = async (context, filter, first = 1, skip = 0, options = {}) => {
- const { phoneCallsList } = await context.api.gqlRequest(
- FETCH_PHONE_CALL_LIST,
- { filter, first, skip },
- options,
- );
- console.log('call: Fetched Phone Calls:', JSON.stringify(phoneCallsList));
- return phoneCallsList;
- };
- /**
- *
- * @param {string} id - The phoneCall id.
- * @param {object} ctx - The 8base's context object.
- * @param {object} options - Options object passed to the gqlRequest function.
- */
- export const fetchPhoneCall = async (id, ctx, options = {}) => {
- const { phoneCall } = await ctx.api.gqlRequest(FETCH_PHONE_CALL, { id }, options);
- console.log('call: Fetched Phone Call:', JSON.stringify(phoneCall));
- return phoneCall;
- };
- /**
- * Fetches a phone call filtered by its call sid.
- *
- * @param {string} callSid - The twilio call sid.
- * @param {object} ctx - The cloud function's context api.
- * @param {object} options - The options to configure the request.
- *
- * @returns {Promise} The phone call object.
- */
- export async function fetchPhoneCallBySid(callSid, ctx, options = {}) {
- const { phoneCall } = await ctx.api.gqlRequest(FETCH_PHONE_CALL, { callSid }, options);
- console.log('call: Fetched Phone Call by sid:', JSON.stringify(phoneCall));
- return phoneCall;
- }
- /**
- * @param {string }callSid - Call sid.
- */
- export async function fetchCallResorce(callSid) {
- let call;
- try {
- call = await twilio.calls(callSid).fetch();
- } catch (error) {
- console.log('call: fetchCallResourceError: ', callSid);
- console.log(JSON.stringify(error, null, 2));
- }
- return call;
- }
- /**
- * @param {object} data - Phone call data to create.
- * @param {object} context - 8base context.
- * @param {object} options - 8base graphql request options.
- * @returns {Promise<{id:string}>}
- */
- export async function createInboundPhoneCall(data, context, options) {
- normalize8baseReferenceConnect(data, 'to');
- normalize8baseReferenceConnect(data, 'agency');
- if (data.lead) {
- normalize8baseReferenceConnect(data, 'lead');
- }
- const { inboundPhoneCallCreate } = await context.api.gqlRequest(
- CREATE_INBOUND_PHONE_CALL,
- { data },
- options,
- );
- console.log('call: createInboundPhoneCall', JSON.stringify(inboundPhoneCallCreate));
- return inboundPhoneCallCreate;
- }
- /**
- * @param {object} filter - The filter object to find a call.
- * @param {string} filter.id - The id of the call.
- * @param {string} filter.callSid - The id of the call in twilio.
- * @param {object} data - Phone call data to update.
- * @param {object} ctx - 8base context.
- * @param {object} options - 8base graphql request options.
- */
- export async function updatePhoneCall(filter, data, ctx, options = {}) {
- const { phoneCallUpdate } = await ctx.api.gqlRequest(
- UPDATE_PHONE_CALL,
- {
- data,
- filter,
- },
- options,
- );
- console.log('call: Updated Phone Call:', JSON.stringify(phoneCallUpdate));
- return phoneCallUpdate;
- }
- /**
- * @param {object} filter - The filter to find the call to update.
- * @param {string} filter.id - The id of the inbound phone call.
- * @param {string} filter.callSid - The id of the call in twilio.
- * @param {object} data - Phone call data to update.
- * @param {object} ctx - 8base context.
- * @param {object} options - 8base graphql request options.
- *
- * @returns {Promise} The inbound phone call object.
- */
- export async function updateInboundPhoneCall(filter, data, ctx, options = {}) {
- try {
- const { phoneCallUpdate } = await ctx.api.gqlRequest(
- UPDATE_INBOUND_PHONE_CALL,
- {
- filter,
- data,
- },
- options,
- );
- console.log('call: Inbound Phone Call Update:', JSON.stringify(phoneCallUpdate));
- return phoneCallUpdate;
- } catch (error) {
- console.log('call: updateInboundPhoneCall: Error updating inbound phone call:', error);
- throw error; // Rethrow the error to propagate it to the caller
- }
- }
- /**
- * @param {object} context - 8base context.
- * @param {string} id - Phone call data to update.
- * @param {object} options - 8base graphql request options.
- * @returns {Promise<[{recordCalls:boolean,automaticCalls:boolean},Error]>} - Promise tuple of succes and error.
- */
- export async function fetchAgencyPhoneCallSettings(context, id, options = {}) {
- let data = null;
- let error = null;
- try {
- const { agency } = await context.api.gqlRequest(
- AGENCY_PHONE_CALL_SETTINGS_QUERY,
- { id },
- options,
- );
- data = agency.phoneCallSettings;
- } catch (e) {
- error = e;
- }
- console.log('call: Agency Phone Call Settings:', JSON.stringify(data));
- if (error) {
- console.log('call: Error fetching Agency Phone Call Settings:', JSON.stringify(error));
- }
- return [data, error];
- }
- /**
- * @param fromNumber
- * @param toNumber
- * @param agencyId
- * @param ctx
- * @param options
- */
- export async function fetchInboundCallInfo(fromNumber, toNumber, agencyId, ctx, options = {}) {
- const first = 1;
- const leadFilter = {
- parsedPhone: { number: { equals: fromNumber } },
- };
- const agencyFilter = {
- agency: {
- id: {
- equals: agencyId,
- },
- agencyAgencyUserRelation: {
- some: {
- user: {
- currentStatus: {
- status: {
- equals: 'ACTIVE',
- },
- },
- },
- },
- },
- },
- number: {
- number: {
- equals: toNumber,
- },
- },
- };
- const responses = await ctx.api.gqlRequest(
- INBOUND_CALL_INFO_QUERY,
- {
- first,
- leadFilter,
- agencyFilter,
- },
- options,
- );
- console.log('call: RESPONSES FROM PHONE CALL ACTIONS', JSON.stringify(responses));
- const leadPhoneNumbersList = responses.leadPhoneNumbersList.items[0];
- console.log('call: leadPhoneNumbersList', JSON.stringify(leadPhoneNumbersList));
- const agencyInfo = responses.phoneNumbersList;
- console.log('call: agencyInfo', JSON.stringify(agencyInfo));
- const agencyUsers = agencyInfo.items[0].agency.agencyAgencyUserRelation;
- console.log('call: agencyUsers.items', JSON.stringify(agencyUsers.items));
- const activeUsers = [];
- agencyUsers.items.forEach((data) => {
- if (data.user.currentStatus && data.user.currentStatus.status === 'ACTIVE') {
- activeUsers.push({ id: data.user.id });
- }
- });
- const activeUsersWithCount = { count: activeUsers.length, items: activeUsers };
- console.log('call: activeUsersWithCount', JSON.stringify(activeUsersWithCount));
- return [leadPhoneNumbersList, agencyInfo.items[0], activeUsersWithCount];
- }
- /**
- * Fetches a lead phone number registry
- * filtering by the number part.
- *
- * @param {string} number - The number to fetch.
- * @param {string} agency - The agency id.
- * @param {object} ctx - 8base context.
- * @param {object} options - Options to configure the query.
- *
- * @returns {Promise<{id:string,phone:{code: string,number: string},lead: {id: string,firstName: string,lastName: string}}>} - The phone number object.
- */
- export async function fetchLeadPhoneFromNumber(number, agency, ctx, options = {}) {
- const first = 1;
- const filter = {
- OR: [
- { phone: { number: { equals: number } } },
- { parsedPhone: { number: { equals: number } } },
- ],
- lead: {
- campaign: {
- agency: {
- id: {
- equals: agency,
- },
- },
- },
- },
- };
- const { leadPhoneNumbersList } = await ctx.api.gqlRequest(
- LEAD_PHONE_NUMBER_QUERY,
- {
- first,
- filter,
- },
- options,
- );
- const [phone] = leadPhoneNumbersList.items;
- console.log('call: Fetched lead phone number:', JSON.stringify(phone));
- return phone;
- }
- /**
- * @param {string} userId - The user id.
- * @param {object} context - 8base context.
- * @param {object} options - 8base graphql request options.
- */
- export async function fetchUser(userId, context, options = {}) {
- return await context.api.gqlRequest(FETCH_USER, { userId }, options);
- }
- /**
- * Fetches a lead from an inbound call registry.
- *
- * @param {string} id - The id of the call.
- * @param {object} ctx - The cloud function's context api.
- * @param {object} options - The options to configure the request.
- *
- * @returns {Promise<{id:string,callSid:string,agency:{ id:string, phoneCallSettings:{recordCalls:boolean,voicemail:boolean }}}>} The lead object or null.
- */
- export async function fetchInboundCall(id, ctx, options = {}) {
- const { inboundPhoneCall } = await ctx.api.gqlRequest(
- FETCH_INBOUND_PHONE_CALL,
- {
- id,
- },
- options,
- );
- console.log('call: Fetched inbound call:', JSON.stringify(inboundPhoneCall));
- return inboundPhoneCall;
- }
- /**
- * Fetches an inbound call by sid.
- *
- * @param {string} sid - The twilio call sid.
- * @param {object} ctx - The cloud function's context api.
- * @param {object} options - The options to configure the request.
- *
- * @returns {Promise} The inbound call object.
- */
- export async function fetchInboundCallBySid(sid, ctx, options = {}) {
- const { inboundPhoneCall } = await ctx.api.gqlRequest(
- FETCH_INBOUND_PHONE_CALL,
- {
- callSid: sid,
- },
- options,
- );
- console.log('call: Inbound call fetched by SID:', JSON.stringify(inboundPhoneCall));
- return inboundPhoneCall;
- }
- /**
- * Fetch users available for calling.
- *
- * @param {string} agency - The agency id.
- * @param {string} campaign - The campaign.
- * @param {object} ctx - The cloud function's context api.
- * @param {object} options - The options to configure the request.
- *
- * @returns {Promise} The id of the users to call.
- */
- export async function fetchUserIdsToCall(agency, campaign, ctx, options = {}) {
- const first = 5;
- const filter = {
- ...(campaign
- ? {
- OR: [
- {
- userAgencyUserRelation: {
- some: {
- agency: {
- id: { equals: agency },
- },
- },
- },
- currentStatus: {
- status: {
- equals: USER_STATUSES.ACTIVE,
- },
- },
- assigneesCampaignRelation: {
- some: { id: { equals: campaign } },
- },
- },
- {
- userAgencyUserRelation: {
- some: {
- agency: {
- id: { equals: agency },
- },
- },
- },
- currentStatus: {
- status: {
- equals: USER_STATUSES.ACTIVE,
- },
- },
- },
- ],
- }
- : {
- userAgencyUserRelation: {
- some: {
- agency: {
- id: { equals: agency },
- },
- },
- },
- currentStatus: {
- status: {
- equals: USER_STATUSES.ACTIVE,
- },
- },
- }),
- };
- const { usersList } = await ctx.api.gqlRequest(
- FETCH_USERS_FOR_CALL,
- {
- first,
- filter,
- },
- options,
- );
- console.log('call: Users to call:', JSON.stringify(usersList.items));
- return usersList.items.map((item) => item.id);
- }
- /**
- * Creates a new PhoneCallParticipant record.
- *
- * @param {object} data - The data object to create the participant.
- * @param {string} data.user - The user id.
- * @param {string} data.callSid - The participant call sid.
- * @param {string} data.inboundCall - The inbound call id.
- * @param {string} data.outboundCall - The outbound call id.
- * @param {object} ctx - The could function's context api.
- * @param {object} options - The options to configure the request.
- *
- * @returns {Promise} The phone call participant object.
- */
- export async function createParticipant(data, ctx, options = {}) {
- if (data.user) {
- normalize8baseReferenceConnect(data, 'user');
- }
- if (data.inboundCall) {
- normalize8baseReferenceConnect(data, 'inboundCall');
- }
- if (data.outboundCall) {
- normalize8baseReferenceConnect(data, 'outboundCall');
- }
- const { phoneCallParticipantCreate } = await ctx.api.gqlRequest(
- CREATE_PHONE_CALL_PARTICIPANT,
- {
- data,
- },
- options,
- );
- console.log('call: Created phone call participant:', JSON.stringify(phoneCallParticipantCreate));
- return phoneCallParticipantCreate;
- }
- /**
- * Updates a specific call participant status.
- *
- * @param {object} data - The data to update the participant.
- * @param {object} filter - The filter object to find the participant.
- * @param {string} filter.id - The id of the participant in 8base.
- * @param {string} filter.callSid - The id of the participant's call in twilio.
- * @param {object} ctx - The cloud function's context object.
- * @param {object} options - The options to configure the request.
- *
- * @returns {Promise} The participant object.
- */
- export async function updateParticipant(data, filter, ctx, options) {
- const { phoneCallParticipantUpdate } = await ctx.api.gqlRequest(
- UPDATE_PHONE_CALL_PARTICIPANT,
- {
- data,
- filter,
- },
- options,
- );
- console.log('call: phoneCallParticipantUpdate:', JSON.stringify(phoneCallParticipantUpdate));
- return phoneCallParticipantUpdate;
- }
- /**
- * Fetch phone call participant by its call sid.
- *
- * @param {string} callSid - The participant call sid.
- * @param {object} ctx - The cloud function's context.
- * @param {object} options - The options to configure the request.
- *
- * @returns {Promise} The phone call participant object.
- */
- export async function fetchParticipantByCallSid(callSid, ctx, options) {
- const { phoneCallParticipant } = await ctx.api.gqlRequest(
- FETCH_PHONE_CALL_PARTICIPANT,
- {
- callSid,
- },
- options,
- );
- console.log('call: phoneCallParticipant', JSON.stringify(phoneCallParticipant));
- return phoneCallParticipant;
- }
- /**
- * Fetches the participants of the call that
- * didn't pick up the call.
- *
- * @param {string} callSid - The sid of the inbound call.
- * @param {string} participantCallSid - The call sid of the participant that answered the call.
- * @param {object} ctx - The cloud function's context.
- * @param {object} options - The options to configure the request.
- *
- * @returns {Promise} The participants initially called when receiving the call.
- */
- export async function fetchParticipantsToHangup(callSid, participantCallSid, ctx, options) {
- const filter = {
- callSid: {
- not_equals: participantCallSid,
- },
- isInitialParticipant: {
- equals: true,
- },
- status: {
- equals: CallParticipantStatus.ringing,
- },
- OR: [
- {
- inboundCall: {
- callSid: {
- equals: callSid,
- },
- },
- },
- {
- outboundCall: {
- callSid: {
- equals: callSid,
- },
- },
- },
- ],
- };
- const { phoneCallParticipantsList } = await ctx.api.gqlRequest(
- FETCH_PHONE_CALL_PARTICIPANTS,
- {
- filter,
- },
- options,
- );
- // Log the participants to be hung up
- console.log(
- 'call: Participants to be hung up on:',
- JSON.stringify(phoneCallParticipantsList.items),
- );
- return phoneCallParticipantsList.items;
- }
- /**
- * @param {object}ctx - 8base context.
- * @param {{checkPermissions:boolean} | undefined} options - Options.
- * @param {string} agency - Agency Id.
- * @returns {Promise<{items:{id:string},count:number}>} - Result.
- */
- export async function fetchActiveUsers(ctx, options = {}, agency) {
- const { usersList } = await ctx.api.gqlRequest(
- ACTIVE_USERS_QUERY,
- {
- agency,
- },
- options,
- );
- console.log('call: Active Users:', JSON.stringify(usersList));
- return usersList;
- }
- /**
- * @param {object}ctx - 8base context.
- * @param {{checkPermissions:boolean} | undefined} options - Options.
- * @param {string} data - Agency Id.
- * @returns {Promise<{id:string}>} - Result.
- */
- export async function createVoiceMail(ctx, options = {}, data) {
- const { voiceMailCreate } = await ctx.api.gqlRequest(
- CREATE_VOICEMAIL_MUTATION,
- {
- data,
- },
- options,
- );
- return voiceMailCreate;
- }
- /**
- * @param {object} ctx - Comntext.
- * @param {string} callId - Call id.
- * @param allowVoicemail
- * @param voicemailUrl
- * @param {boolean} voicemail - Call id.
- * @returns {string} Response string.
- */
- export async function getCallDropResponse(ctx, callId, allowVoicemail, voicemailUrl = null) {
- const response = new twiml.VoiceResponse();
- if (allowVoicemail || !LANDLINE_PHONE) {
- if (voicemailUrl) {
- response.play(voicemailUrl);
- await ctx.invokeFunction(
- 'send-email-task',
- {
- id: null,
- voicemailNotification: true,
- },
- { waitForResponse: false },
- );
- } else {
- response.say('Sorry there is no agents avialable right now, please leave a message.', {
- voice: 'woman',
- });
- }
- response.record({
- timeout: 5, // after 5 secongs of silence the record ends
- action: `${getBaseUrl(ctx)}/webhook/calls/voicemail/${callId}`,
- });
- } else {
- response.say(
- 'It looks like all of our agents are busy, we will redirect you to a landline please wait',
- {
- voice: 'woman',
- },
- );
- response.dial(LANDLINE_PHONE);
- }
- console.log(
- `call: phone-call-actions voicemail URL: ${getBaseUrl(ctx)}/webhook/calls/voicemail/${callId}`,
- );
- console.log('call: inbound-call-start getCallDropResponse', response.toString());
- return response.toString();
- }
Advertisement
Add Comment
Please, Sign In to add comment