-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add Twilio Trigger Node (#8859)
Co-authored-by: Michael Kret <[email protected]> Co-authored-by: Giulio Andreini <[email protected]>
- Loading branch information
1 parent
2aab78b
commit c204995
Showing
4 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"node": "n8n-nodes-base.twilioTrigger", | ||
"nodeVersion": "1.0", | ||
"codexVersion": "1.0", | ||
"categories": ["Communication", "Development"], | ||
"resources": { | ||
"credentialDocumentation": [ | ||
{ | ||
"url": "https://docs.n8n.io/credentials/twilio" | ||
} | ||
], | ||
"primaryDocumentation": [ | ||
{ | ||
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.twilio-trigger/" | ||
} | ||
] | ||
}, | ||
"alias": ["SMS", "Phone", "Voice"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
import type { | ||
IHookFunctions, | ||
IWebhookFunctions, | ||
INodeType, | ||
INodeTypeDescription, | ||
IWebhookResponseData, | ||
} from 'n8n-workflow'; | ||
|
||
import { twilioTriggerApiRequest } from './GenericFunctions'; | ||
|
||
export class TwilioTrigger implements INodeType { | ||
description: INodeTypeDescription = { | ||
displayName: 'Twilio Trigger', | ||
name: 'twilioTrigger', | ||
icon: 'file:twilio.svg', | ||
group: ['trigger'], | ||
version: [1], | ||
defaultVersion: 1, | ||
subtitle: '=Updates: {{$parameter["updates"].join(", ")}}', | ||
description: 'Starts the workflow on a Twilio update', | ||
defaults: { | ||
name: 'Twilio Trigger', | ||
}, | ||
inputs: [], | ||
outputs: ['main'], | ||
credentials: [ | ||
{ | ||
name: 'twilioApi', | ||
required: true, | ||
}, | ||
], | ||
webhooks: [ | ||
{ | ||
name: 'default', | ||
httpMethod: 'POST', | ||
responseMode: 'onReceived', | ||
path: 'webhook', | ||
}, | ||
], | ||
properties: [ | ||
{ | ||
displayName: 'Trigger On', | ||
name: 'updates', | ||
type: 'multiOptions', | ||
options: [ | ||
{ | ||
name: 'New SMS', | ||
value: 'com.twilio.messaging.inbound-message.received', | ||
description: 'When an SMS message is received', | ||
}, | ||
{ | ||
name: 'New Call', | ||
value: 'com.twilio.voice.insights.call-summary.complete', | ||
description: 'When a call is received', | ||
}, | ||
], | ||
required: true, | ||
default: [], | ||
}, | ||
{ | ||
displayName: "The 'New Call' event may take up to thirty minutes to be triggered", | ||
name: 'callTriggerNotice', | ||
type: 'notice', | ||
default: '', | ||
displayOptions: { | ||
show: { | ||
updates: ['com.twilio.voice.insights.call-summary.complete'], | ||
}, | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
webhookMethods = { | ||
default: { | ||
async checkExists(this: IHookFunctions): Promise<boolean> { | ||
const webhookUrl = this.getNodeWebhookUrl('default'); | ||
|
||
const { sinks } = (await twilioTriggerApiRequest.call(this, 'GET', 'Sinks')) || {}; | ||
|
||
const sink = sinks.find( | ||
(entry: { sink_configuration: { destination: string | undefined } }) => | ||
entry.sink_configuration.destination === webhookUrl, | ||
); | ||
|
||
if (sink) { | ||
const { subscriptions } = | ||
(await twilioTriggerApiRequest.call(this, 'GET', 'Subscriptions')) || {}; | ||
|
||
const subscription = subscriptions.find( | ||
(entry: { sink_sid: any }) => entry.sink_sid === sink.sid, | ||
); | ||
|
||
if (subscription) { | ||
const { types } = | ||
(await twilioTriggerApiRequest.call( | ||
this, | ||
'GET', | ||
`Subscriptions/${subscription.sid}/SubscribedEvents`, | ||
)) || {}; | ||
|
||
const typesFound = types.map((type: { type: any }) => type.type); | ||
|
||
const allowedUpdates = this.getNodeParameter('updates') as string[]; | ||
|
||
if (typesFound.sort().join(',') === allowedUpdates.sort().join(',')) { | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
} | ||
return false; | ||
}, | ||
|
||
async create(this: IHookFunctions): Promise<boolean> { | ||
const workflowData = this.getWorkflowStaticData('node'); | ||
const webhookUrl = this.getNodeWebhookUrl('default'); | ||
|
||
const allowedUpdates = this.getNodeParameter('updates') as string[]; | ||
|
||
const bodySink = { | ||
Description: 'Sink created by n8n Twilio Trigger Node.', | ||
SinkConfiguration: `{ "destination": "${webhookUrl}", "method": "POST" }`, | ||
SinkType: 'webhook', | ||
}; | ||
|
||
const sink = await twilioTriggerApiRequest.call(this, 'POST', 'Sinks', bodySink); | ||
|
||
workflowData.sinkId = sink.sid; | ||
|
||
const body = { | ||
Description: 'Subscription created by n8n Twilio Trigger Node.', | ||
Types: `{ "type": "${allowedUpdates[0]}" }`, | ||
SinkSid: sink.sid, | ||
}; | ||
|
||
const subscription = await twilioTriggerApiRequest.call( | ||
this, | ||
'POST', | ||
'Subscriptions', | ||
body, | ||
); | ||
workflowData.subscriptionId = subscription.sid; | ||
// if there is more than one event type add the others on the existing subscription | ||
if (allowedUpdates.length > 1) { | ||
for (let index = 1; index < allowedUpdates.length; index++) { | ||
await twilioTriggerApiRequest.call( | ||
this, | ||
'POST', | ||
`Subscriptions/${workflowData.subscriptionId}/SubscribedEvents`, | ||
{ | ||
Type: allowedUpdates[index], | ||
}, | ||
); | ||
} | ||
} | ||
|
||
return true; | ||
}, | ||
|
||
async delete(this: IHookFunctions): Promise<boolean> { | ||
const workflowData = this.getWorkflowStaticData('node'); | ||
const sinkId = workflowData.sinkId; | ||
const subscriptionId = workflowData.subscriptionId; | ||
|
||
try { | ||
if (sinkId) { | ||
await twilioTriggerApiRequest.call(this, 'DELETE', `Sinks/${sinkId}`, {}); | ||
workflowData.sinkId = ''; | ||
} | ||
if (subscriptionId) { | ||
await twilioTriggerApiRequest.call( | ||
this, | ||
'DELETE', | ||
`Subscriptions/${subscriptionId}`, | ||
{}, | ||
); | ||
workflowData.subscriptionId = ''; | ||
} | ||
} catch (error) { | ||
return false; | ||
} | ||
return true; | ||
}, | ||
}, | ||
}; | ||
|
||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> { | ||
const bodyData = this.getBodyData(); | ||
|
||
return { | ||
workflowData: [this.helpers.returnJsonArray(bodyData)], | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters