diff --git a/packages/nodes-base/nodes/Twilio/GenericFunctions.ts b/packages/nodes-base/nodes/Twilio/GenericFunctions.ts
index a2d9cab967..a29dcd2fb8 100644
--- a/packages/nodes-base/nodes/Twilio/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/Twilio/GenericFunctions.ts
@@ -59,3 +59,17 @@ export async function twilioApiRequest(this: IHookFunctions | IExecuteFunctions,
throw new NodeApiError(this.getNode(), error);
}
}
+
+const XML_CHAR_MAP: { [key: string]: string } = {
+ '<': '<',
+ '>': '>',
+ '&': '&',
+ '"': '"',
+ "'": '''
+};
+
+export function escapeXml(str: string) {
+ return str.replace(/[<>&"']/g, function (ch: string) {
+ return XML_CHAR_MAP[ch];
+ });
+}
diff --git a/packages/nodes-base/nodes/Twilio/Twilio.node.json b/packages/nodes-base/nodes/Twilio/Twilio.node.json
index 9dfbe17374..9874070754 100644
--- a/packages/nodes-base/nodes/Twilio/Twilio.node.json
+++ b/packages/nodes-base/nodes/Twilio/Twilio.node.json
@@ -61,6 +61,8 @@
]
},
"alias": [
- "SMS"
+ "SMS",
+ "Phone",
+ "Voice"
]
-}
\ No newline at end of file
+}
diff --git a/packages/nodes-base/nodes/Twilio/Twilio.node.ts b/packages/nodes-base/nodes/Twilio/Twilio.node.ts
index 1aff7cc067..5d0ad13854 100644
--- a/packages/nodes-base/nodes/Twilio/Twilio.node.ts
+++ b/packages/nodes-base/nodes/Twilio/Twilio.node.ts
@@ -11,6 +11,7 @@ import {
import {
twilioApiRequest,
+ escapeXml,
} from './GenericFunctions';
export class Twilio implements INodeType {
@@ -44,6 +45,11 @@ export class Twilio implements INodeType {
name: 'SMS',
value: 'sms',
},
+ {
+ // eslint-disable-next-line n8n-nodes-base/node-param-resource-with-plural-option
+ name: 'Call',
+ value: 'call',
+ },
],
default: 'sms',
},
@@ -70,13 +76,34 @@ export class Twilio implements INodeType {
default: 'send',
},
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ noDataExpression: true,
+ displayOptions: {
+ show: {
+ resource: [
+ 'call',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Make',
+ value: 'make',
+ },
+ ],
+ default: 'make',
+ },
+
// ----------------------------------
- // sms
+ // sms / call
// ----------------------------------
// ----------------------------------
- // sms:send
+ // sms:send / call:make
// ----------------------------------
{
displayName: 'From',
@@ -89,9 +116,11 @@ export class Twilio implements INodeType {
show: {
operation: [
'send',
+ 'make',
],
resource: [
'sms',
+ 'call',
],
},
},
@@ -108,9 +137,11 @@ export class Twilio implements INodeType {
show: {
operation: [
'send',
+ 'make',
],
resource: [
'sms',
+ 'call',
],
},
},
@@ -151,6 +182,40 @@ export class Twilio implements INodeType {
},
description: 'The message to send',
},
+ {
+ displayName: 'Use TwiML',
+ name: 'twiml',
+ type: 'boolean',
+ default: false,
+ displayOptions: {
+ show: {
+ operation: [
+ 'make',
+ ],
+ resource: [
+ 'call',
+ ],
+ },
+ },
+ description: 'Whether to use the Twilio Markup Language in the message',
+ },
+ {
+ displayName: 'Message',
+ name: 'message',
+ type: 'string',
+ default: '',
+ required: true,
+ displayOptions: {
+ show: {
+ operation: [
+ 'make',
+ ],
+ resource: [
+ 'call',
+ ],
+ },
+ },
+ },
{
displayName: 'Options',
name: 'options',
@@ -218,6 +283,30 @@ export class Twilio implements INodeType {
} else {
throw new NodeOperationError(this.getNode(), `The operation "${operation}" is not known!`);
}
+ } else if (resource === 'call') {
+ if (operation === 'make') {
+ // ----------------------------------
+ // call:make
+ // ----------------------------------
+
+ requestMethod = 'POST';
+ endpoint = '/Calls.json';
+
+ const message = this.getNodeParameter('message', i) as string;
+ const useTwiml = this.getNodeParameter('twiml', i) as boolean;
+ body.From = this.getNodeParameter('from', i) as string;
+ body.To = this.getNodeParameter('to', i) as string;
+
+ if (useTwiml) {
+ body.Twiml = message;
+ } else {
+ body.Twiml = `${escapeXml(message)}`;
+ }
+
+ body.StatusCallback = this.getNodeParameter('options.statusCallback', i, '') as string;
+ } else {
+ throw new NodeOperationError(this.getNode(), `The operation "${operation}" is not known!`);
+ }
} else {
throw new NodeOperationError(this.getNode(), `The resource "${resource}" is not known!`);
}