diff --git a/packages/backend/src/apps/twilio/dynamic-data/index.ts b/packages/backend/src/apps/twilio/dynamic-data/index.ts new file mode 100644 index 00000000..d20d4347 --- /dev/null +++ b/packages/backend/src/apps/twilio/dynamic-data/index.ts @@ -0,0 +1,3 @@ +import listIncomingPhoneNumbers from './list-incoming-phone-numbers'; + +export default [listIncomingPhoneNumbers]; diff --git a/packages/backend/src/apps/twilio/dynamic-data/list-incoming-phone-numbers/index.ts b/packages/backend/src/apps/twilio/dynamic-data/list-incoming-phone-numbers/index.ts new file mode 100644 index 00000000..0e36af65 --- /dev/null +++ b/packages/backend/src/apps/twilio/dynamic-data/list-incoming-phone-numbers/index.ts @@ -0,0 +1,56 @@ +import { IGlobalVariable, IJSONObject } from '@automatisch/types'; + +type TResponse = { + data: IJSONObject[]; + error?: IJSONObject; +}; + +type TIncomingPhoneNumber = { + phone_number: string; + friendly_name: string; + sid: string; + capabilities: { + sms: boolean; + }; +}; + +type TResponseData = { + incoming_phone_numbers: TIncomingPhoneNumber[]; + next_page_uri: string; +}; + +export default { + name: 'List incoming phone numbers', + key: 'listIncomingPhoneNumbers', + + async run($: IGlobalVariable) { + const valueType = $.step.parameters.valueType as string; + const isSid = valueType === 'sid'; + + const aggregatedResponse: TResponse = { data: [] }; + let pathname = `/2010-04-01/Accounts/${$.auth.data.accountSid}/IncomingPhoneNumbers.json`; + + do { + const response = await $.http.get(pathname); + + for (const incomingPhoneNumber of response.data.incoming_phone_numbers) { + if (incomingPhoneNumber.capabilities.sms === false) { + continue; + } + + const friendlyName = incomingPhoneNumber.friendly_name; + const phoneNumber = incomingPhoneNumber.phone_number; + const name = [friendlyName, phoneNumber].filter(Boolean).join(' - '); + + aggregatedResponse.data.push({ + value: isSid ? incomingPhoneNumber.sid : phoneNumber, + name, + }); + } + + pathname = response.data.next_page_uri; + } while (pathname); + + return aggregatedResponse; + }, +}; diff --git a/packages/backend/src/apps/twilio/index.ts b/packages/backend/src/apps/twilio/index.ts index b3d959f5..f1684dd5 100644 --- a/packages/backend/src/apps/twilio/index.ts +++ b/packages/backend/src/apps/twilio/index.ts @@ -3,6 +3,7 @@ import addAuthHeader from './common/add-auth-header'; import auth from './auth'; import triggers from './triggers'; import actions from './actions'; +import dynamicData from './dynamic-data'; export default defineApp({ name: 'Twilio', @@ -17,4 +18,5 @@ export default defineApp({ auth, triggers, actions, + dynamicData, }); diff --git a/packages/backend/src/apps/twilio/triggers/receive-sms/index.ts b/packages/backend/src/apps/twilio/triggers/receive-sms/index.ts index f434247b..25785add 100644 --- a/packages/backend/src/apps/twilio/triggers/receive-sms/index.ts +++ b/packages/backend/src/apps/twilio/triggers/receive-sms/index.ts @@ -1,23 +1,73 @@ +import { URLSearchParams } from 'node:url'; +import isEmpty from 'lodash/isEmpty'; import defineTrigger from '../../../../helpers/define-trigger'; import fetchMessages from './fetch-messages'; export default defineTrigger({ name: 'Receive SMS', key: 'receiveSms', - pollInterval: 15, + type: 'webhook', description: 'Triggers when a new SMS is received.', arguments: [ { label: 'To Number', - key: 'toNumber', - type: 'string', + key: 'phoneNumberSid', + type: 'dropdown' as const, required: true, description: 'The number to receive the SMS on. It should be a Twilio number.', + variables: false, + source: { + type: 'query', + name: 'getDynamicData', + arguments: [ + { + name: 'key', + value: 'listIncomingPhoneNumbers', + }, + { + name: 'valueType', + value: 'sid', + } + ], + }, }, ], - async run($) { + async testRun($) { await fetchMessages($); + + if (!isEmpty($.lastExecutionStep?.dataOut)) { + $.pushTriggerItem({ + raw: $.lastExecutionStep.dataOut, + meta: { + internalId: '', + } + }); + } + }, + + async registerHook($) { + const phoneNumberSid = $.step.parameters.phoneNumberSid as string; + const payload = new URLSearchParams({ + SmsUrl: $.webhookUrl, + }).toString(); + + await $.http.post( + `/2010-04-01/Accounts/${$.auth.data.accountSid}/IncomingPhoneNumbers/${phoneNumberSid}.json`, + payload + ); + }, + + async unregisterHook($) { + const toNumber = $.step.parameters.toNumber as string; + const payload = new URLSearchParams({ + SmsUrl: '', + }).toString(); + + await $.http.post( + `/2010-04-01/Accounts/${$.auth.data.accountSid}/IncomingPhoneNumbers/PN${toNumber}.json`, + payload + ); }, });