@pipedream/twilio 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,102 @@
1
+ import twilio from "../twilio.app.mjs";
2
+ import twilioClient from "twilio";
3
+
4
+ export default {
5
+ props: {
6
+ twilio,
7
+ incomingPhoneNumber: {
8
+ propDefinition: [
9
+ twilio,
10
+ "incomingPhoneNumber",
11
+ ],
12
+ },
13
+ authToken: {
14
+ propDefinition: [
15
+ twilio,
16
+ "authToken",
17
+ ],
18
+ },
19
+ http: {
20
+ label: "HTTP Responder",
21
+ description: "Exposes a `respond()` method that lets the source issue HTTP responses",
22
+ type: "$.interface.http",
23
+ customResponse: true,
24
+ },
25
+ },
26
+ hooks: {
27
+ async activate() {
28
+ const createWebhookResp = await this.setWebhook(
29
+ this.incomingPhoneNumber,
30
+ this.http.endpoint,
31
+ );
32
+ console.log(createWebhookResp);
33
+ },
34
+ async deactivate() {
35
+ const deleteWebhookResp = await this.setWebhook(
36
+ this.incomingPhoneNumber,
37
+ "", // remove the webhook URL
38
+ );
39
+ console.log(deleteWebhookResp);
40
+ },
41
+ },
42
+ methods: {
43
+ getResponseBody() {
44
+ return null;
45
+ },
46
+ isRelevant() {
47
+ return true;
48
+ },
49
+ validateRequest(body, headers) {
50
+ const twilioSignature = headers["x-twilio-signature"];
51
+ if (!twilioSignature) {
52
+ console.log("No x-twilio-signature header in request. Exiting.");
53
+ return false;
54
+ }
55
+
56
+ /** See https://www.twilio.com/docs/usage/webhooks/webhooks-security */
57
+ return twilioClient.validateRequest(
58
+ this.authToken,
59
+ twilioSignature,
60
+ /** This must match the incoming URL exactly, which contains a / */
61
+ `${this.http.endpoint}/`,
62
+ body,
63
+ );
64
+ },
65
+ emitEvent(body, headers) {
66
+ const meta = this.generateMeta(body, headers);
67
+ this.$emit(body, meta);
68
+ },
69
+ },
70
+ async run(event) {
71
+ let {
72
+ body,
73
+ headers,
74
+ } = event;
75
+
76
+ const responseBody = this.getResponseBody();
77
+ if (responseBody) {
78
+ this.http.respond({
79
+ status: 200,
80
+ headers: {
81
+ "Content-Type": "text/xml",
82
+ },
83
+ body: responseBody,
84
+ });
85
+ }
86
+
87
+ if (typeof body !== "object")
88
+ body = Object.fromEntries(new URLSearchParams(body));
89
+
90
+ if (!this.isRelevant(body)) {
91
+ console.log("Event not relevant. Skipping...");
92
+ return;
93
+ }
94
+
95
+ if (!this.validateRequest(body, headers)) {
96
+ console.log("Event could not be validated. Skipping...");
97
+ return;
98
+ }
99
+
100
+ this.emitEvent(body, headers);
101
+ },
102
+ };
@@ -0,0 +1,33 @@
1
+ import common from "../common-webhook.mjs";
2
+
3
+ export default {
4
+ ...common,
5
+ key: "twilio-new-call",
6
+ name: "New Call (Instant)",
7
+ description:
8
+ "Configures a webhook in Twilio, tied to a phone number, and emits an event each time a call to that number is completed",
9
+ version: "0.0.3",
10
+ type: "source",
11
+ dedupe: "unique",
12
+ methods: {
13
+ ...common.methods,
14
+ async setWebhook(...args) {
15
+ return await this.twilio.setIncomingCallWebhookURL(...args);
16
+ },
17
+ generateMeta(body, headers) {
18
+ return {
19
+ /** if Twilio retries a message, but we've already emitted, dedupe */
20
+ id: headers["i-twilio-idempotency-token"],
21
+ summary: `New call from ${this.getMaskedNumber(body.From)}`,
22
+ ts: Date.now(),
23
+ };
24
+ },
25
+ isRelevant(body) {
26
+ return body.CallStatus == "completed";
27
+ },
28
+ getMaskedNumber(number) {
29
+ const { length: numberLength } = number;
30
+ return number.slice(numberLength - 4).padStart(numberLength, "#");
31
+ },
32
+ },
33
+ };
@@ -0,0 +1,46 @@
1
+ import common from "../common-webhook.mjs";
2
+ import twilio from "twilio";
3
+ const MessagingResponse = twilio.twiml.MessagingResponse;
4
+
5
+ export default {
6
+ ...common,
7
+ key: "twilio-new-incoming-sms",
8
+ name: "New Incoming SMS (Instant)",
9
+ description:
10
+ "Configures a webhook in Twilio, tied to an incoming phone number, and emits an event each time an SMS is sent to that number",
11
+ version: "0.0.7",
12
+ type: "source",
13
+ dedupe: "unique",
14
+ props: {
15
+ ...common.props,
16
+ responseMessage: {
17
+ propDefinition: [
18
+ common.props.twilio,
19
+ "responseMessage",
20
+ ],
21
+ },
22
+ },
23
+ methods: {
24
+ ...common.methods,
25
+ async setWebhook(...args) {
26
+ return await this.twilio.setIncomingSMSWebhookURL(...args);
27
+ },
28
+ getResponseBody() {
29
+ const twiml = new MessagingResponse();
30
+ let responseBody = "<Response></Response>";
31
+ if (this.responseMessage) {
32
+ twiml.message(this.responseMessage);
33
+ responseBody = twiml.toString();
34
+ }
35
+ return responseBody;
36
+ },
37
+ generateMeta(body, headers) {
38
+ return {
39
+ /** if Twilio retries a message, but we've already emitted, dedupe */
40
+ id: headers["i-twilio-idempotency-token"],
41
+ summary: body.Body,
42
+ ts: Date.now(),
43
+ };
44
+ },
45
+ },
46
+ };
@@ -0,0 +1,29 @@
1
+ import common from "../common-polling.mjs";
2
+
3
+ export default {
4
+ ...common,
5
+ key: "twilio-new-phone-number",
6
+ name: "New Phone Number",
7
+ description: "Emits an event when you add a new phone number to your account",
8
+ version: "0.0.3",
9
+ type: "source",
10
+ dedupe: "unique",
11
+ methods: {
12
+ ...common.methods,
13
+ async listResults(...args) {
14
+ return await this.twilio.listIncomingPhoneNumbers(...args);
15
+ },
16
+ generateMeta(number) {
17
+ const {
18
+ sid: id,
19
+ friendlyName: summary,
20
+ dateCreated,
21
+ } = number;
22
+ return {
23
+ id,
24
+ summary,
25
+ ts: Date.parse(dateCreated),
26
+ };
27
+ },
28
+ },
29
+ };
@@ -0,0 +1,28 @@
1
+ import common from "../common-polling.mjs";
2
+
3
+ export default {
4
+ ...common,
5
+ key: "twilio-new-phone-recording",
6
+ name: "New Recording",
7
+ description: "Emits an event when a new call recording is created",
8
+ version: "0.0.3",
9
+ type: "source",
10
+ dedupe: "unique",
11
+ methods: {
12
+ ...common.methods,
13
+ async listResults(...args) {
14
+ return await this.twilio.listRecordings(...args);
15
+ },
16
+ generateMeta(recording) {
17
+ const {
18
+ sid: id,
19
+ dateCreated,
20
+ } = recording;
21
+ return {
22
+ id,
23
+ summary: `New recording ${id}`,
24
+ ts: Date.parse(dateCreated),
25
+ };
26
+ },
27
+ },
28
+ };
@@ -0,0 +1,28 @@
1
+ import common from "../common-polling.mjs";
2
+
3
+ export default {
4
+ ...common,
5
+ key: "twilio-new-transcription",
6
+ name: "New Transcription",
7
+ description: "Emits an event when a new call transcription is created",
8
+ version: "0.0.3",
9
+ type: "source",
10
+ dedupe: "unique",
11
+ methods: {
12
+ ...common.methods,
13
+ async listResults(...args) {
14
+ return await this.twilio.listTranscriptions(...args);
15
+ },
16
+ generateMeta(transcription) {
17
+ const {
18
+ sid: id,
19
+ dateCreated,
20
+ } = transcription;
21
+ return {
22
+ id,
23
+ summary: `New transcription ${id}`,
24
+ ts: Date.parse(dateCreated),
25
+ };
26
+ },
27
+ },
28
+ };
package/twilio.app.mjs ADDED
@@ -0,0 +1,343 @@
1
+ import twilioClient from "twilio";
2
+ import {
3
+ callToString,
4
+ messageToString,
5
+ recordingToString,
6
+ } from "./utils.mjs";
7
+
8
+ export default {
9
+ type: "app",
10
+ app: "twilio",
11
+ propDefinitions: {
12
+ authToken: {
13
+ type: "string",
14
+ secret: true,
15
+ label: "Twilio Auth Token",
16
+ description:
17
+ "Your Twilio auth token, found [in your Twilio console](https://www.twilio.com/console). Required for validating Twilio events.",
18
+ },
19
+ body: {
20
+ type: "string",
21
+ label: "Message Body",
22
+ description: "The text of the message you want to send, limited to 1600 characters.",
23
+ },
24
+ from: {
25
+ type: "string",
26
+ label: "From",
27
+ description: "The phone number or alphanumeric sender ID the message is from",
28
+ async options() {
29
+ const numbers = await this.listIncomingPhoneNumbers();
30
+ return numbers.map((number) => {
31
+ return {
32
+ label: number.friendlyName,
33
+ value: number.phoneNumber,
34
+ };
35
+ });
36
+ },
37
+ },
38
+ incomingPhoneNumber: {
39
+ type: "string",
40
+ label: "Incoming Phone Number",
41
+ description:
42
+ "The Twilio phone number where you'll receive messages. This source creates a webhook tied to this incoming phone number, **overwriting any existing webhook URL**.",
43
+ async options() {
44
+ const numbers = await this.listIncomingPhoneNumbers();
45
+ return numbers.map((number) => {
46
+ return {
47
+ label: number.friendlyName,
48
+ value: number.sid,
49
+ };
50
+ });
51
+ },
52
+ },
53
+ mediaUrl: {
54
+ type: "string[]",
55
+ label: "Media URL",
56
+ description: "The URL of the media you wish to send out with the message. The media size limit is `5MB`. You may provide up to 10 media URLs per message.",
57
+ optional: true,
58
+ },
59
+ responseMessage: {
60
+ type: "string",
61
+ optional: true,
62
+ label: "Response Message",
63
+ description:
64
+ "The message you want to send in response to incoming messages. Leave this blank if you don't need to issue a response.",
65
+ },
66
+ to: {
67
+ type: "string",
68
+ label: "To",
69
+ description: "The destination phone number in E.164 format. Format with a `+` and country code (e.g., `+16175551212`).",
70
+ },
71
+ limit: {
72
+ type: "integer",
73
+ label: "Limit",
74
+ description: "The maximum number of results to be worked with during one execution cycle.",
75
+ optional: true,
76
+ default: 50,
77
+ },
78
+ messageId: {
79
+ type: "string",
80
+ label: "Message ID",
81
+ description: "The SID of the Message",
82
+ optional: true,
83
+ async options() {
84
+ const messages = await this.listMessages();
85
+ return messages.map((message) => {
86
+ return {
87
+ label: this.messageToString(message),
88
+ value: message.sid,
89
+ };
90
+ });
91
+ },
92
+ },
93
+ parentCallSid: {
94
+ type: "string",
95
+ label: "Parent Call SID",
96
+ description: "Only include calls spawned by calls with this SID.",
97
+ optional: true,
98
+ async options() {
99
+ return this.listCallsOptions();
100
+ },
101
+ },
102
+ sid: {
103
+ type: "string",
104
+ label: "Call ID",
105
+ description: "The SID of the Call",
106
+ optional: true,
107
+ async options() {
108
+ return this.listCallsOptions();
109
+ },
110
+ },
111
+ recordingID: {
112
+ type: "string",
113
+ label: "Recording ID",
114
+ description: "The SID of the Recording",
115
+ async options() {
116
+ const recordings = await this.listRecordings();
117
+ return recordings.map((recording) => {
118
+ return {
119
+ label: this.recordingToString(recording),
120
+ value: recording.sid,
121
+ };});
122
+ },
123
+ },
124
+ format: {
125
+ type: "string",
126
+ label: "Format",
127
+ description: "The format of the recording audio file",
128
+ options: [
129
+ {
130
+ label: "MP3",
131
+ value: ".mp3",
132
+ },
133
+ {
134
+ label: "WAV",
135
+ value: ".wav",
136
+ },
137
+ ],
138
+ default: ".wav",
139
+ },
140
+ status: {
141
+ type: "string",
142
+ label: "Status",
143
+ description: "The status of the call",
144
+ optional: true,
145
+ options: [
146
+ "queued",
147
+ "ringing",
148
+ "in-progress",
149
+ "canceled",
150
+ "completed",
151
+ "failed",
152
+ "busy",
153
+ "no-answer",
154
+ ],
155
+ },
156
+ },
157
+ methods: {
158
+ // Static methods
159
+ callToString,
160
+ messageToString,
161
+ recordingToString,
162
+
163
+ getClient() {
164
+ return twilioClient(this.$auth.Sid, this.$auth.Secret, {
165
+ accountSid: this.$auth.AccountSid,
166
+ });
167
+ },
168
+ async setWebhookURL(phoneNumberSid, params) {
169
+ const client = this.getClient();
170
+ return await client.incomingPhoneNumbers(phoneNumberSid).update(params);
171
+ },
172
+ async setIncomingSMSWebhookURL(phoneNumberSid, url) {
173
+ const params = {
174
+ smsMethod: "POST",
175
+ smsUrl: url,
176
+ };
177
+ return await this.setWebhookURL(phoneNumberSid, params);
178
+ },
179
+ async setIncomingCallWebhookURL(phoneNumberSid, url) {
180
+ const params = {
181
+ statusCallbackMethod: "POST",
182
+ statusCallback: url,
183
+ };
184
+ return await this.setWebhookURL(phoneNumberSid, params);
185
+ },
186
+ /**
187
+ * Returns a IncomingPhoneNumber list resource representing an account's
188
+ * Twilio phone numbers
189
+ *
190
+ * @param {object} [params] - an object containing parameters to be fed to
191
+ * the Twilio API call, as defined in [the API docs](https://bit.ly/3F8y6KY)
192
+ *
193
+ * @returns the IncomingPhoneNumber list
194
+ */
195
+ async listIncomingPhoneNumbers(params) {
196
+ const client = this.getClient();
197
+ return await client.incomingPhoneNumbers.list(params);
198
+ },
199
+ /**
200
+ * Returns a list of Recordings, each representing a recording generated
201
+ * during a call or conference for the given account
202
+ *
203
+ * @param {object} [params] - an object containing parameters to be fed to
204
+ * the Twilio API call, as defined in [the API docs](https://bit.ly/3mg9ebJ)
205
+ *
206
+ * @returns the list of recordings
207
+ */
208
+ async listRecordings(params) {
209
+ const client = this.getClient();
210
+ return await client.recordings.list(params);
211
+ },
212
+ /**
213
+ * Returns a list of Transcriptions generated from all recordings in an
214
+ * account
215
+ *
216
+ * @param {object} [params] - an object containing parameters to be fed to
217
+ * the Twilio API call, as defined in [the API docs](https://bit.ly/3uwx5HB)
218
+ *
219
+ * @returns the list of transcriptions
220
+ */
221
+ async listTranscriptions(params) {
222
+ const client = this.getClient();
223
+ return await client.transcriptions.list(params);
224
+ },
225
+ /**
226
+ * Returns a list of messages associated with your account. When getting the
227
+ * list of all messages, results will be sorted on the DateSent field with
228
+ * the most recent messages appearing first.
229
+ *
230
+ * @param {object} [params] - an object containing parameters to be fed to the
231
+ * Twilio API call, as defined in [the API docs](https://bit.ly/3mh26eY)
232
+ *
233
+ * @returns the list of messages
234
+ */
235
+ async listMessages(params) {
236
+ const client = this.getClient();
237
+ return await client.messages.list(params);
238
+ },
239
+ /**
240
+ * Returns a single message specified by the provided Message `sid`
241
+ *
242
+ * @param {String} sid - the Twilio-provided string that uniquely identifies
243
+ * the Message resource to fetch
244
+ *
245
+ * @returns the message
246
+ */
247
+ async getMessage(sid) {
248
+ const client = this.getClient();
249
+ return await client.messages(sid).fetch();
250
+ },
251
+ /**
252
+ * Deletes a message record from your account. Once the record is deleted,
253
+ * it will no longer appear in the API and Account Portal logs.
254
+ *
255
+ * @param {String} sid - the Twilio-provided string that uniquely identifies
256
+ * the Message resource to delete
257
+ * @returns `true` on success
258
+ */
259
+ async deleteMessage(sid) {
260
+ const client = this.getClient();
261
+ return await client.messages(sid).remove();
262
+ },
263
+ /**
264
+ * Returns a list of media associated with your message
265
+ *
266
+ * @param {String} messageId - The SID of the Message resource that this
267
+ * Media resource belongs to
268
+ * @param {Object} [params] - an object containing parameters to be fed to the
269
+ * Twilio API call, as defined in [the API docs](https://bit.ly/3mc8apg)
270
+ */
271
+ async listMessageMedia(messageId, params) {
272
+ const client = this.getClient();
273
+ return await client.messages(messageId).media.list(params);
274
+ },
275
+ /**
276
+ * Return a list of phone calls made to and from an account
277
+ *
278
+ * @param {Object} params - an object containing parameters to be fed to the
279
+ * Twilio API call, as defined in [the API docs](https://bit.ly/3iiHIJ9)
280
+ * @returns the list of calls
281
+ */
282
+ async listCalls(params) {
283
+ const client = this.getClient();
284
+ return await client.calls.list(params);
285
+ },
286
+ async listCallsOptions(params = {}) {
287
+ const calls = await this.listCalls(params);
288
+ return calls.map((call) => {
289
+ return {
290
+ label: this.callToString(call),
291
+ value: call.sid,
292
+ };
293
+ });
294
+ },
295
+ /**
296
+ * Returns the Call resource of an individual call, identified by its Call `sid`
297
+ *
298
+ * @param {String} sid - The SID of the Call resource to fetch
299
+ * @returns the Call
300
+ */
301
+ async getCall(sid) {
302
+ const client = this.getClient();
303
+ return await client.calls(sid).fetch();
304
+ },
305
+ /**
306
+ * This will delete a call record from the account. Once the record is
307
+ * deleted, it will no longer appear in the API and Account Portal logs.
308
+ *
309
+ * @param {String} sid - the Twilio-provided Call SID that uniquely identifies
310
+ * the Call resource to delete
311
+ * @returns `true` on success
312
+ */
313
+ async deleteCall(sid) {
314
+ const client = this.getClient();
315
+ return await client.calls(sid).remove();
316
+ },
317
+ /**
318
+ * Returns a recording specified by the provided Recording `sid`
319
+ *
320
+ * @param {String} sid - the Twilio-provided string that uniquely identifies
321
+ * the Recording resource to fetch
322
+ *
323
+ * @returns the recording
324
+ */
325
+ async getRecording(sid) {
326
+ const client = this.getClient();
327
+ return await client.recordings(sid).fetch();
328
+ },
329
+ /**
330
+ * Returns a list of transcriptions available for the recording identified
331
+ * by the Recording `sid`
332
+ *
333
+ * @param {String} sid - the Twilio-provided string that uniquely identifies
334
+ * the Recording resource
335
+ *
336
+ * @returns the list of transcriptions
337
+ */
338
+ async listRecordingTranscriptions(sid, params) {
339
+ const client = this.getClient();
340
+ return await client.recordings(sid).transcriptions.list(params);
341
+ },
342
+ },
343
+ };