@pipedream/twilio 0.3.11 → 0.5.0
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.
- package/README.md +9 -12
- package/actions/check-verification-token/check-verification-token.mjs +19 -9
- package/actions/create-verification-service/create-verification-service.mjs +24 -0
- package/actions/delete-call/delete-call.mjs +3 -2
- package/actions/delete-message/delete-message.mjs +3 -2
- package/actions/download-recording-media/download-recording-media.mjs +26 -6
- package/actions/get-call/get-call.mjs +3 -2
- package/actions/get-message/get-message.mjs +3 -2
- package/actions/list-calls/list-calls.mjs +2 -2
- package/actions/list-message-media/list-message-media.mjs +3 -2
- package/actions/list-messages/list-messages.mjs +2 -2
- package/actions/list-transcripts/list-transcripts.mjs +51 -0
- package/actions/make-phone-call/make-phone-call.mjs +93 -3
- package/actions/phone-number-lookup/phone-number-lookup.mjs +41 -0
- package/actions/{send-mms/send-mms.mjs → send-message/send-message.mjs} +5 -5
- package/actions/send-sms-verification/send-sms-verification.mjs +2 -2
- package/package.json +5 -3
- package/sources/{common-polling.mjs → common/common-polling.mjs} +17 -12
- package/sources/{common-webhook.mjs → common/common-webhook.mjs} +1 -8
- package/sources/new-call/new-call.mjs +2 -2
- package/sources/new-incoming-sms/new-incoming-sms.mjs +2 -2
- package/sources/new-phone-number/new-phone-number.mjs +2 -2
- package/sources/new-recording/new-recording.mjs +2 -2
- package/sources/new-transcript-created/new-transcript-created.mjs +52 -0
- package/twilio.app.mjs +46 -9
- package/actions/list-recording-transcriptions/list-recording-transcriptions.mjs +0 -37
- package/actions/send-sms/send-sms.mjs +0 -57
- package/sources/new-transcription/new-transcription.mjs +0 -28
package/README.md
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# Overview
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
messages over the internet
|
|
13
|
-
- A voicemail application that allows you to leave and receive voicemails over
|
|
14
|
-
the internet
|
|
3
|
+
Twilio's API on Pipedream opens up a multitude of communication capabilities, allowing you to build robust, scalable, and automated workflows. With Twilio, you can send and receive SMS and MMS messages, make voice calls, and perform other communication functions programmatically. Leveraging Pipedream's seamless integration, you can connect these communications features with hundreds of other services to automate notifications, streamline customer interactions, and enhance operational efficiency.
|
|
4
|
+
|
|
5
|
+
# Example Use Cases
|
|
6
|
+
|
|
7
|
+
- **SMS Alert System for eCommerce Order Updates**: Connect Twilio with Shopify to send SMS updates to customers when their order status changes. Apply conditions to trigger messages for order confirmation, shipment tracking, and delivery notifications, adding a personal touch to the online shopping experience.
|
|
8
|
+
|
|
9
|
+
- **Automated Customer Support Ticketing**: Use Twilio with Zendesk to create a support ticket when a customer sends a text message to your Twilio number. This workflow automates the ticket generation process, ensuring that customer issues are promptly recorded and queued for support staff attention without manual entry.
|
|
10
|
+
|
|
11
|
+
- **Two-Factor Authentication (2FA) for App Security**: Integrate Twilio with your custom app's authentication system to send a one-time passcode via SMS for 2FA. Enhance security by incorporating an extra verification step, making it tougher for unauthorized users to gain access to sensitive information or accounts.
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import app from "../../twilio.app.mjs";
|
|
2
|
+
import { ConfigurationError } from "@pipedream/platform";
|
|
2
3
|
|
|
3
4
|
export default {
|
|
4
5
|
key: "twilio-check-verification-token",
|
|
5
6
|
name: "Check Verification Token",
|
|
6
|
-
description: "Check if user-provided token is correct. [See the documentation](https://www.twilio.com/docs/verify/api)
|
|
7
|
+
description: "Check if user-provided token is correct. [See the documentation](https://www.twilio.com/docs/verify/api)",
|
|
7
8
|
type: "action",
|
|
8
|
-
version: "0.0.
|
|
9
|
+
version: "0.0.2",
|
|
9
10
|
props: {
|
|
10
11
|
app,
|
|
11
12
|
serviceSid: {
|
|
@@ -27,12 +28,21 @@ export default {
|
|
|
27
28
|
},
|
|
28
29
|
},
|
|
29
30
|
async run({ $ }) {
|
|
30
|
-
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
try {
|
|
32
|
+
const res = await this.app.checkVerificationToken(
|
|
33
|
+
this.serviceSid,
|
|
34
|
+
this.to,
|
|
35
|
+
this.code,
|
|
36
|
+
);
|
|
37
|
+
$.export("$summary", `Verification code is ${res.valid
|
|
38
|
+
? ""
|
|
39
|
+
: "not"} valid`);
|
|
40
|
+
return res;
|
|
41
|
+
} catch (e) {
|
|
42
|
+
if (e.status === 404) {
|
|
43
|
+
throw new ConfigurationError("Verification ID not found. Twilio deletes the verification SID once it's expired (10 minutes), approved, or the max attempts to check a code have been reached.");
|
|
44
|
+
}
|
|
45
|
+
throw new ConfigurationError(JSON.stringify(e));
|
|
46
|
+
}
|
|
37
47
|
},
|
|
38
48
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import twilio from "../../twilio.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "twilio-create-verification-service",
|
|
5
|
+
name: "Create Verification Service",
|
|
6
|
+
description: "Create a verification service for sending SMS verifications. [See the documentation](https://www.twilio.com/docs/verify/api/service#create-a-verification-service)",
|
|
7
|
+
type: "action",
|
|
8
|
+
version: "0.0.1",
|
|
9
|
+
props: {
|
|
10
|
+
twilio,
|
|
11
|
+
friendlyName: {
|
|
12
|
+
type: "string",
|
|
13
|
+
label: "Friendly Name",
|
|
14
|
+
description: "The name of the new verification service",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
async run({ $ }) {
|
|
18
|
+
const response = await this.twilio.createVerificationService({
|
|
19
|
+
friendlyName: this.friendlyName,
|
|
20
|
+
});
|
|
21
|
+
$.export("$summary", `Successfully created verification service with SID: ${response.sid}"`);
|
|
22
|
+
return response;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -3,8 +3,8 @@ import twilio from "../../twilio.app.mjs";
|
|
|
3
3
|
export default {
|
|
4
4
|
key: "twilio-delete-call",
|
|
5
5
|
name: "Delete Call",
|
|
6
|
-
description: "Remove a call record from your account. [See the
|
|
7
|
-
version: "0.1.
|
|
6
|
+
description: "Remove a call record from your account. [See the documentation](https://www.twilio.com/docs/voice/api/call-resource#delete-a-call-resource)",
|
|
7
|
+
version: "0.1.3",
|
|
8
8
|
type: "action",
|
|
9
9
|
props: {
|
|
10
10
|
twilio,
|
|
@@ -13,6 +13,7 @@ export default {
|
|
|
13
13
|
twilio,
|
|
14
14
|
"sid",
|
|
15
15
|
],
|
|
16
|
+
optional: false,
|
|
16
17
|
},
|
|
17
18
|
},
|
|
18
19
|
async run({ $ }) {
|
|
@@ -3,8 +3,8 @@ import twilio from "../../twilio.app.mjs";
|
|
|
3
3
|
export default {
|
|
4
4
|
key: "twilio-delete-message",
|
|
5
5
|
name: "Delete Message",
|
|
6
|
-
description: "Delete a message record from your account. [See the
|
|
7
|
-
version: "0.1.
|
|
6
|
+
description: "Delete a message record from your account. [See the documentation](https://www.twilio.com/docs/sms/api/message-resource#delete-a-message-resource)",
|
|
7
|
+
version: "0.1.3",
|
|
8
8
|
type: "action",
|
|
9
9
|
props: {
|
|
10
10
|
twilio,
|
|
@@ -13,6 +13,7 @@ export default {
|
|
|
13
13
|
twilio,
|
|
14
14
|
"messageId",
|
|
15
15
|
],
|
|
16
|
+
optional: false,
|
|
16
17
|
},
|
|
17
18
|
},
|
|
18
19
|
async run({ $ }) {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import twilio from "../../twilio.app.mjs";
|
|
2
|
-
import got from "got@12.4.1";
|
|
3
2
|
import stream from "stream";
|
|
4
3
|
import { promisify } from "util";
|
|
5
4
|
import fs from "fs";
|
|
5
|
+
import { axios } from "@pipedream/platform";
|
|
6
6
|
|
|
7
7
|
export default {
|
|
8
8
|
key: "twilio-download-recording-media",
|
|
9
9
|
name: "Download Recording Media",
|
|
10
|
-
description: "Download a recording media file. [See the
|
|
11
|
-
version: "0.1.
|
|
10
|
+
description: "Download a recording media file. [See the documentation](https://www.twilio.com/docs/voice/api/recording#fetch-a-recording-media-file)",
|
|
11
|
+
version: "0.1.5",
|
|
12
12
|
type: "action",
|
|
13
13
|
props: {
|
|
14
14
|
twilio,
|
|
@@ -27,7 +27,21 @@ export default {
|
|
|
27
27
|
filePath: {
|
|
28
28
|
type: "string",
|
|
29
29
|
label: "File Path",
|
|
30
|
-
description: "The destination path in [`/tmp`](https://pipedream.com/docs/workflows/steps/code/nodejs/working-with-files/#the-tmp-directory) for the downloaded the file (e.g., `/tmp/myFile.mp3`)",
|
|
30
|
+
description: "The destination path in [`/tmp`](https://pipedream.com/docs/workflows/steps/code/nodejs/working-with-files/#the-tmp-directory) for the downloaded the file (e.g., `/tmp/myFile.mp3`). Make sure to include the file extension.",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
methods: {
|
|
34
|
+
getFileStream({
|
|
35
|
+
$, downloadUrl,
|
|
36
|
+
}) {
|
|
37
|
+
return axios($, {
|
|
38
|
+
url: downloadUrl,
|
|
39
|
+
auth: {
|
|
40
|
+
username: `${this.twilio.$auth.AccountSid}`,
|
|
41
|
+
password: `${this.twilio.$auth.AuthToken}`,
|
|
42
|
+
},
|
|
43
|
+
responseType: "stream",
|
|
44
|
+
});
|
|
31
45
|
},
|
|
32
46
|
},
|
|
33
47
|
async run({ $ }) {
|
|
@@ -39,10 +53,16 @@ export default {
|
|
|
39
53
|
// Add chosen download format extension (e.g. ".mp3"), as specified in the Twilio API docs:
|
|
40
54
|
// https://www.twilio.com/docs/voice/api/recording#fetch-a-recording-media-file
|
|
41
55
|
const downloadUrl = uri + this.format;
|
|
56
|
+
const fileStream = await this.getFileStream({
|
|
57
|
+
$,
|
|
58
|
+
downloadUrl,
|
|
59
|
+
});
|
|
42
60
|
const pipeline = promisify(stream.pipeline);
|
|
43
61
|
const resp = await pipeline(
|
|
44
|
-
|
|
45
|
-
fs.createWriteStream(this.filePath)
|
|
62
|
+
fileStream,
|
|
63
|
+
fs.createWriteStream(this.filePath.includes("/tmp")
|
|
64
|
+
? this.filePath
|
|
65
|
+
: `/tmp/${this.filePath}`),
|
|
46
66
|
);
|
|
47
67
|
$.export("$summary", `Successfully downloaded the recording media file to "${this.filePath}"`);
|
|
48
68
|
return resp;
|
|
@@ -4,8 +4,8 @@ import { callToString } from "../../common/utils.mjs";
|
|
|
4
4
|
export default {
|
|
5
5
|
key: "twilio-get-call",
|
|
6
6
|
name: "Get Call",
|
|
7
|
-
description: "Return call resource of an individual call. [See the
|
|
8
|
-
version: "0.1.
|
|
7
|
+
description: "Return call resource of an individual call. [See the documentation](https://www.twilio.com/docs/voice/api/call-resource#fetch-a-call-resource)",
|
|
8
|
+
version: "0.1.3",
|
|
9
9
|
type: "action",
|
|
10
10
|
props: {
|
|
11
11
|
twilio,
|
|
@@ -14,6 +14,7 @@ export default {
|
|
|
14
14
|
twilio,
|
|
15
15
|
"sid",
|
|
16
16
|
],
|
|
17
|
+
optional: false,
|
|
17
18
|
},
|
|
18
19
|
},
|
|
19
20
|
async run({ $ }) {
|
|
@@ -4,8 +4,8 @@ import { messageToString } from "../../common/utils.mjs";
|
|
|
4
4
|
export default {
|
|
5
5
|
key: "twilio-get-message",
|
|
6
6
|
name: "Get Message",
|
|
7
|
-
description: "Return details of a message. [See the
|
|
8
|
-
version: "0.1.
|
|
7
|
+
description: "Return details of a message. [See the documentation](https://www.twilio.com/docs/sms/api/message-resource#fetch-a-message-resource)",
|
|
8
|
+
version: "0.1.3",
|
|
9
9
|
type: "action",
|
|
10
10
|
props: {
|
|
11
11
|
twilio,
|
|
@@ -14,6 +14,7 @@ export default {
|
|
|
14
14
|
twilio,
|
|
15
15
|
"messageId",
|
|
16
16
|
],
|
|
17
|
+
optional: false,
|
|
17
18
|
},
|
|
18
19
|
},
|
|
19
20
|
async run({ $ }) {
|
|
@@ -4,8 +4,8 @@ import { omitEmptyStringValues } from "../../common/utils.mjs";
|
|
|
4
4
|
export default {
|
|
5
5
|
key: "twilio-list-calls",
|
|
6
6
|
name: "List Calls",
|
|
7
|
-
description: "Return a list of calls associated with your account. [See the
|
|
8
|
-
version: "0.1.
|
|
7
|
+
description: "Return a list of calls associated with your account. [See the documentation](https://www.twilio.com/docs/voice/api/call-resource#read-multiple-call-resources)",
|
|
8
|
+
version: "0.1.3",
|
|
9
9
|
type: "action",
|
|
10
10
|
props: {
|
|
11
11
|
twilio,
|
|
@@ -4,8 +4,8 @@ import { omitEmptyStringValues } from "../../common/utils.mjs";
|
|
|
4
4
|
export default {
|
|
5
5
|
key: "twilio-list-message-media",
|
|
6
6
|
name: "List Message Media",
|
|
7
|
-
description: "Return a list of media associated with your message. [See the
|
|
8
|
-
version: "0.1.
|
|
7
|
+
description: "Return a list of media associated with your message. [See the documentation](https://www.twilio.com/docs/sms/api/media-resource#read-multiple-media-resources)",
|
|
8
|
+
version: "0.1.3",
|
|
9
9
|
type: "action",
|
|
10
10
|
props: {
|
|
11
11
|
twilio,
|
|
@@ -14,6 +14,7 @@ export default {
|
|
|
14
14
|
twilio,
|
|
15
15
|
"messageId",
|
|
16
16
|
],
|
|
17
|
+
optional: false,
|
|
17
18
|
},
|
|
18
19
|
limit: {
|
|
19
20
|
propDefinition: [
|
|
@@ -5,8 +5,8 @@ import { omitEmptyStringValues } from "../../common/utils.mjs";
|
|
|
5
5
|
export default {
|
|
6
6
|
key: "twilio-list-messages",
|
|
7
7
|
name: "List Messages",
|
|
8
|
-
description: "Return a list of messages associated with your account. [See the
|
|
9
|
-
version: "0.1.
|
|
8
|
+
description: "Return a list of messages associated with your account. [See the documentation](https://www.twilio.com/docs/sms/api/message-resource#read-multiple-message-resources)",
|
|
9
|
+
version: "0.1.4",
|
|
10
10
|
type: "action",
|
|
11
11
|
props: {
|
|
12
12
|
twilio,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import twilio from "../../twilio.app.mjs";
|
|
2
|
+
import { omitEmptyStringValues } from "../../common/utils.mjs";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
key: "twilio-list-transcripts",
|
|
6
|
+
name: "List Transcripts",
|
|
7
|
+
description: "Return a list of transcripts. [See the documentation](https://www.twilio.com/docs/voice/intelligence/api/transcript-resource#fetch-multiple-transcripts)",
|
|
8
|
+
version: "0.0.1",
|
|
9
|
+
type: "action",
|
|
10
|
+
props: {
|
|
11
|
+
twilio,
|
|
12
|
+
includeTranscriptText: {
|
|
13
|
+
propDefinition: [
|
|
14
|
+
twilio,
|
|
15
|
+
"includeTranscriptText",
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
limit: {
|
|
19
|
+
propDefinition: [
|
|
20
|
+
twilio,
|
|
21
|
+
"limit",
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
async run({ $ }) {
|
|
26
|
+
let results = await this.twilio.listTranscripts(
|
|
27
|
+
omitEmptyStringValues({
|
|
28
|
+
limit: this.limit,
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
if (this.includeTranscriptText) {
|
|
32
|
+
const transcripts = [];
|
|
33
|
+
for (const result of results) {
|
|
34
|
+
const {
|
|
35
|
+
sentences, transcript,
|
|
36
|
+
} = await this.twilio.getSentences(result.sid);
|
|
37
|
+
transcripts.push({
|
|
38
|
+
...result,
|
|
39
|
+
_version: undefined,
|
|
40
|
+
sentences,
|
|
41
|
+
transcript,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
results = transcripts;
|
|
45
|
+
}
|
|
46
|
+
$.export("$summary", `Successfully fetched ${results.length} transcript${results.length === 1
|
|
47
|
+
? ""
|
|
48
|
+
: "s"}`);
|
|
49
|
+
return results;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
@@ -5,8 +5,8 @@ import { callToString } from "../../common/utils.mjs";
|
|
|
5
5
|
export default {
|
|
6
6
|
key: "twilio-make-phone-call",
|
|
7
7
|
name: "Make a Phone Call",
|
|
8
|
-
description: "Make a phone call
|
|
9
|
-
version: "0.1.
|
|
8
|
+
description: "Make a phone call passing text, a URL, or an application that Twilio will use to handle the call. [See the documentation](https://www.twilio.com/docs/voice/api/call-resource#create-a-call-resource)",
|
|
9
|
+
version: "0.1.4",
|
|
10
10
|
type: "action",
|
|
11
11
|
props: {
|
|
12
12
|
twilio,
|
|
@@ -22,11 +22,94 @@ export default {
|
|
|
22
22
|
"to",
|
|
23
23
|
],
|
|
24
24
|
},
|
|
25
|
+
callType: {
|
|
26
|
+
type: "string",
|
|
27
|
+
label: "Call Type",
|
|
28
|
+
description: "Whether to use `text`, a `URL`, or an `application` to handle the call",
|
|
29
|
+
options: [
|
|
30
|
+
{
|
|
31
|
+
label: "Enter text for Twilio to speak when the user picks up the phone",
|
|
32
|
+
value: "text",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
label: "Enter a URL that returns the TwiML instructions for the call",
|
|
36
|
+
value: "url",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: "Enter the SID of an Application resource that will handle the call",
|
|
40
|
+
value: "application",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
reloadProps: true,
|
|
44
|
+
},
|
|
25
45
|
text: {
|
|
26
46
|
label: "Text",
|
|
27
47
|
type: "string",
|
|
28
48
|
description: "The text you'd like Twilio to speak to the user when they pick up the phone.",
|
|
49
|
+
hidden: true,
|
|
29
50
|
},
|
|
51
|
+
url: {
|
|
52
|
+
type: "string",
|
|
53
|
+
label: "URL",
|
|
54
|
+
description: "The absolute URL that returns the TwiML instructions for the call",
|
|
55
|
+
hidden: true,
|
|
56
|
+
},
|
|
57
|
+
applicationSid: {
|
|
58
|
+
propDefinition: [
|
|
59
|
+
twilio,
|
|
60
|
+
"applicationSid",
|
|
61
|
+
],
|
|
62
|
+
hidden: true,
|
|
63
|
+
},
|
|
64
|
+
timeout: {
|
|
65
|
+
type: "integer",
|
|
66
|
+
label: "Timeout",
|
|
67
|
+
description: "The integer number of seconds that we should allow the phone to ring before assuming there is no answer. The default is `60` seconds and the maximum is `600` seconds.",
|
|
68
|
+
optional: true,
|
|
69
|
+
},
|
|
70
|
+
record: {
|
|
71
|
+
type: "boolean",
|
|
72
|
+
label: "Record",
|
|
73
|
+
description: "Whether to record the call",
|
|
74
|
+
optional: true,
|
|
75
|
+
reloadProps: true,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
async additionalProps(existingProps) {
|
|
79
|
+
const props = {};
|
|
80
|
+
existingProps.text.hidden = !(this.callType === "text");
|
|
81
|
+
existingProps.url.hidden = !(this.callType === "url");
|
|
82
|
+
existingProps.applicationSid.hidden = !(this.callType === "application");
|
|
83
|
+
if (this.record) {
|
|
84
|
+
props.trim = {
|
|
85
|
+
type: "string",
|
|
86
|
+
label: "Trim",
|
|
87
|
+
description: "Whether to trim any leading and trailing silence from the recording",
|
|
88
|
+
options: [
|
|
89
|
+
"trim-silence",
|
|
90
|
+
"do-not-trim",
|
|
91
|
+
],
|
|
92
|
+
optional: true,
|
|
93
|
+
};
|
|
94
|
+
props.recordingTrack = {
|
|
95
|
+
type: "string",
|
|
96
|
+
label: "Recording Track",
|
|
97
|
+
description: "The audio track to record for the call. Default is `both`.",
|
|
98
|
+
options: [
|
|
99
|
+
"inbound",
|
|
100
|
+
"outbound",
|
|
101
|
+
"both",
|
|
102
|
+
],
|
|
103
|
+
optional: true,
|
|
104
|
+
};
|
|
105
|
+
props.recordingCallbackUrl = {
|
|
106
|
+
type: "string",
|
|
107
|
+
label: "Recording Callback URL",
|
|
108
|
+
description: "The URL that we call when the recording is available to be accessed",
|
|
109
|
+
optional: true,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return props;
|
|
30
113
|
},
|
|
31
114
|
async run({ $ }) {
|
|
32
115
|
// Parse the given number into its E.164 equivalent
|
|
@@ -47,7 +130,14 @@ export default {
|
|
|
47
130
|
const data = {
|
|
48
131
|
to: toParsed.phoneNumber,
|
|
49
132
|
from: fromParsed.phoneNumber,
|
|
50
|
-
twiml: `<Response><Say>${this.text}</Say></Response>`,
|
|
133
|
+
twiml: this.text && `<Response><Say>${this.text}</Say></Response>`,
|
|
134
|
+
url: this.url,
|
|
135
|
+
applicationSid: this.applicationSid,
|
|
136
|
+
timeout: this.timeout,
|
|
137
|
+
record: this.record,
|
|
138
|
+
trim: this.trim,
|
|
139
|
+
recordingTrack: this.recordingTrack,
|
|
140
|
+
recordingStatusCallback: this.recordingCallbackUrl,
|
|
51
141
|
};
|
|
52
142
|
|
|
53
143
|
const resp = await this.twilio.getClient().calls.create(data);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import app from "../../twilio.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "twilio-phone-number-lookup",
|
|
5
|
+
name: "Phone Number Lookup",
|
|
6
|
+
description: "Lookup information about a phone number. [See the documentation](https://www.twilio.com/docs/lookup/v2-api/line-type-intelligence) for more information",
|
|
7
|
+
type: "action",
|
|
8
|
+
version: "0.0.1",
|
|
9
|
+
props: {
|
|
10
|
+
app,
|
|
11
|
+
sid: {
|
|
12
|
+
type: "string",
|
|
13
|
+
label: "Phone Number",
|
|
14
|
+
description: "The phone number to lookup",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
methods: {
|
|
18
|
+
lineTypeLookup(sid) {
|
|
19
|
+
const client = this.app.getClient();
|
|
20
|
+
return client.lookups.v2.phoneNumbers(sid).fetch({
|
|
21
|
+
fields: "line_type_intelligence",
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
async run({ $ }) {
|
|
26
|
+
const {
|
|
27
|
+
lineTypeLookup,
|
|
28
|
+
sid,
|
|
29
|
+
} = this;
|
|
30
|
+
|
|
31
|
+
const response = await lineTypeLookup(sid);
|
|
32
|
+
|
|
33
|
+
if (response.validationErrors?.length) {
|
|
34
|
+
$.export("$summary", `Failed to fetch phone number lookup: \`${JSON.stringify(response.validationErrors, null, 2)}\`.`);
|
|
35
|
+
} else {
|
|
36
|
+
$.export("$summary", `Successfully fetched phone number lookup of \`${sid}\``);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return response;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -3,11 +3,11 @@ import twilio from "../../twilio.app.mjs";
|
|
|
3
3
|
import { messageToString } from "../../common/utils.mjs";
|
|
4
4
|
|
|
5
5
|
export default {
|
|
6
|
-
key: "twilio-send-
|
|
7
|
-
name: "Send
|
|
8
|
-
description: "Send an SMS with
|
|
6
|
+
key: "twilio-send-message",
|
|
7
|
+
name: "Send Message",
|
|
8
|
+
description: "Send an SMS text with optional media files. [See the documentation](https://www.twilio.com/docs/sms/api/message-resource#create-a-message-resource)",
|
|
9
9
|
type: "action",
|
|
10
|
-
version: "0.1
|
|
10
|
+
version: "0.0.1",
|
|
11
11
|
props: {
|
|
12
12
|
twilio,
|
|
13
13
|
from: {
|
|
@@ -58,7 +58,7 @@ export default {
|
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
const resp = await this.twilio.getClient().messages.create(data);
|
|
61
|
-
$.export("$summary", `Successfully sent a new
|
|
61
|
+
$.export("$summary", `Successfully sent a new message, "${messageToString(resp)}"`);
|
|
62
62
|
return resp;
|
|
63
63
|
},
|
|
64
64
|
};
|
|
@@ -3,9 +3,9 @@ import app from "../../twilio.app.mjs";
|
|
|
3
3
|
export default {
|
|
4
4
|
key: "twilio-send-sms-verification",
|
|
5
5
|
name: "Send SMS Verification",
|
|
6
|
-
description: "Send an SMS verification to a phone number. [See the documentation](https://www.twilio.com/docs/verify/api)
|
|
6
|
+
description: "Send an SMS verification to a phone number. [See the documentation](https://www.twilio.com/docs/verify/api)",
|
|
7
7
|
type: "action",
|
|
8
|
-
version: "0.0.
|
|
8
|
+
version: "0.0.2",
|
|
9
9
|
props: {
|
|
10
10
|
app,
|
|
11
11
|
serviceSid: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipedream/twilio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Pipedream Twilio Components",
|
|
5
5
|
"main": "twilio.app.mjs",
|
|
6
6
|
"keywords": [
|
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
"homepage": "https://pipedream.com/apps/twilio",
|
|
11
11
|
"author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@pipedream/platform": "^
|
|
14
|
-
"
|
|
13
|
+
"@pipedream/platform": "^3.0.0",
|
|
14
|
+
"got": "^13.0.0",
|
|
15
|
+
"phone": "^3.1.49",
|
|
16
|
+
"stream": "^0.0.3",
|
|
15
17
|
"twilio": "^3.54.2"
|
|
16
18
|
},
|
|
17
19
|
"gitHead": "e12480b94cc03bed4808ebc6b13e7fdb3a1ba535",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import twilio from "
|
|
1
|
+
import twilio from "../../twilio.app.mjs";
|
|
2
2
|
import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
|
|
3
3
|
|
|
4
4
|
export default {
|
|
@@ -23,23 +23,28 @@ export default {
|
|
|
23
23
|
},
|
|
24
24
|
emitEvent(result) {
|
|
25
25
|
const meta = this.generateMeta(result);
|
|
26
|
+
delete result._version;
|
|
26
27
|
this.$emit(result, meta);
|
|
27
28
|
},
|
|
28
29
|
},
|
|
29
30
|
async run() {
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
const dateCreatedAfter = this._getCreatedAfter();
|
|
32
|
+
let newDateCreatedAfter = dateCreatedAfter;
|
|
33
|
+
|
|
34
|
+
const results = await this.listResults({
|
|
32
35
|
dateCreatedAfter,
|
|
33
|
-
};
|
|
34
|
-
|
|
36
|
+
});
|
|
37
|
+
|
|
35
38
|
for (const result of results) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const dateCreated = result.dateCreated;
|
|
40
|
+
const ts = Date.parse(dateCreated);
|
|
41
|
+
if ( !dateCreatedAfter || ts > Date.parse(dateCreatedAfter) ) {
|
|
42
|
+
this.emitEvent(result);
|
|
43
|
+
if (!dateCreatedAfter || ts > Date.parse(newDateCreatedAfter)) {
|
|
44
|
+
newDateCreatedAfter = dateCreated;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
42
47
|
}
|
|
43
|
-
this._setCreatedAfter(
|
|
48
|
+
this._setCreatedAfter(newDateCreatedAfter);
|
|
44
49
|
},
|
|
45
50
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import twilio from "
|
|
1
|
+
import twilio from "../../twilio.app.mjs";
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
4
|
props: {
|
|
@@ -9,12 +9,6 @@ export default {
|
|
|
9
9
|
"incomingPhoneNumber",
|
|
10
10
|
],
|
|
11
11
|
},
|
|
12
|
-
authToken: {
|
|
13
|
-
propDefinition: [
|
|
14
|
-
twilio,
|
|
15
|
-
"authToken",
|
|
16
|
-
],
|
|
17
|
-
},
|
|
18
12
|
http: {
|
|
19
13
|
label: "HTTP Responder",
|
|
20
14
|
description: "Exposes a `respond()` method that lets the source issue HTTP responses",
|
|
@@ -93,7 +87,6 @@ export default {
|
|
|
93
87
|
signature,
|
|
94
88
|
url: `${this.http.endpoint}/`,
|
|
95
89
|
params: body,
|
|
96
|
-
authToken: this.authToken,
|
|
97
90
|
});
|
|
98
91
|
|
|
99
92
|
if (!isRequestValid) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import common from "../common-webhook.mjs";
|
|
1
|
+
import common from "../common/common-webhook.mjs";
|
|
2
2
|
import constants from "../../common/constants.mjs";
|
|
3
3
|
|
|
4
4
|
export default {
|
|
@@ -6,7 +6,7 @@ export default {
|
|
|
6
6
|
key: "twilio-new-call",
|
|
7
7
|
name: "New Call (Instant)",
|
|
8
8
|
description: "Emit new event each time a call to the phone number is completed. Configures a webhook in Twilio, tied to a phone number.",
|
|
9
|
-
version: "0.1.
|
|
9
|
+
version: "0.1.4",
|
|
10
10
|
type: "source",
|
|
11
11
|
dedupe: "unique",
|
|
12
12
|
methods: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import twilio from "twilio";
|
|
2
|
-
import common from "../common-webhook.mjs";
|
|
2
|
+
import common from "../common/common-webhook.mjs";
|
|
3
3
|
import constants from "../../common/constants.mjs";
|
|
4
4
|
|
|
5
5
|
const MessagingResponse = twilio.twiml.MessagingResponse;
|
|
@@ -9,7 +9,7 @@ export default {
|
|
|
9
9
|
key: "twilio-new-incoming-sms",
|
|
10
10
|
name: "New Incoming SMS (Instant)",
|
|
11
11
|
description: "Emit new event every time an SMS is sent to the phone number set. Configures a webhook in Twilio, tied to an incoming phone number.",
|
|
12
|
-
version: "0.1.
|
|
12
|
+
version: "0.1.4",
|
|
13
13
|
type: "source",
|
|
14
14
|
dedupe: "unique",
|
|
15
15
|
props: {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import common from "../common-polling.mjs";
|
|
1
|
+
import common from "../common/common-polling.mjs";
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
4
|
...common,
|
|
5
5
|
key: "twilio-new-phone-number",
|
|
6
6
|
name: "New Phone Number",
|
|
7
7
|
description: "Emit new event when you add a new phone number to your account",
|
|
8
|
-
version: "0.1.
|
|
8
|
+
version: "0.1.5",
|
|
9
9
|
type: "source",
|
|
10
10
|
dedupe: "unique",
|
|
11
11
|
methods: {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import common from "../common-polling.mjs";
|
|
1
|
+
import common from "../common/common-polling.mjs";
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
4
|
...common,
|
|
5
5
|
key: "twilio-new-recording",
|
|
6
6
|
name: "New Recording",
|
|
7
7
|
description: "Emit new event when a new call recording is created",
|
|
8
|
-
version: "0.1.
|
|
8
|
+
version: "0.1.5",
|
|
9
9
|
type: "source",
|
|
10
10
|
dedupe: "unique",
|
|
11
11
|
methods: {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import common from "../common/common-polling.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
...common,
|
|
5
|
+
key: "twilio-new-transcript-created",
|
|
6
|
+
name: "New Transcript Created",
|
|
7
|
+
description: "Emit new event when a new call transcript is created",
|
|
8
|
+
version: "0.0.1",
|
|
9
|
+
type: "source",
|
|
10
|
+
dedupe: "unique",
|
|
11
|
+
props: {
|
|
12
|
+
...common.props,
|
|
13
|
+
includeTranscriptText: {
|
|
14
|
+
propDefinition: [
|
|
15
|
+
common.props.twilio,
|
|
16
|
+
"includeTranscriptText",
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
methods: {
|
|
21
|
+
...common.methods,
|
|
22
|
+
async listResults(...args) {
|
|
23
|
+
const results = await this.twilio.listTranscripts(...args);
|
|
24
|
+
if (!this.includeTranscriptText) {
|
|
25
|
+
return results;
|
|
26
|
+
}
|
|
27
|
+
const transcripts = [];
|
|
28
|
+
for (const result of results) {
|
|
29
|
+
const {
|
|
30
|
+
sentences, transcript,
|
|
31
|
+
} = await this.twilio.getSentences(result.sid);
|
|
32
|
+
transcripts.push({
|
|
33
|
+
...result,
|
|
34
|
+
sentences,
|
|
35
|
+
transcript,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return transcripts;
|
|
39
|
+
},
|
|
40
|
+
generateMeta(transcript) {
|
|
41
|
+
const {
|
|
42
|
+
sid: id,
|
|
43
|
+
dateCreated,
|
|
44
|
+
} = transcript;
|
|
45
|
+
return {
|
|
46
|
+
id,
|
|
47
|
+
summary: `New transcript ${id}`,
|
|
48
|
+
ts: Date.parse(dateCreated),
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
};
|
package/twilio.app.mjs
CHANGED
|
@@ -10,13 +10,6 @@ export default {
|
|
|
10
10
|
type: "app",
|
|
11
11
|
app: "twilio",
|
|
12
12
|
propDefinitions: {
|
|
13
|
-
authToken: {
|
|
14
|
-
type: "string",
|
|
15
|
-
secret: true,
|
|
16
|
-
label: "Twilio Auth Token",
|
|
17
|
-
description:
|
|
18
|
-
"Your Twilio auth token, found [in your Twilio console](https://www.twilio.com/console). Required for validating Twilio events.",
|
|
19
|
-
},
|
|
20
13
|
body: {
|
|
21
14
|
type: "string",
|
|
22
15
|
label: "Message Body",
|
|
@@ -72,7 +65,7 @@ export default {
|
|
|
72
65
|
limit: {
|
|
73
66
|
type: "integer",
|
|
74
67
|
label: "Limit",
|
|
75
|
-
description: "The maximum number of results to
|
|
68
|
+
description: "The maximum number of results to retrieve",
|
|
76
69
|
optional: true,
|
|
77
70
|
default: 50,
|
|
78
71
|
},
|
|
@@ -166,10 +159,28 @@ export default {
|
|
|
166
159
|
}));
|
|
167
160
|
},
|
|
168
161
|
},
|
|
162
|
+
includeTranscriptText: {
|
|
163
|
+
type: "boolean",
|
|
164
|
+
label: "Include Transcript Text?",
|
|
165
|
+
description: "Set to `true` to include the transcript sentences in the response",
|
|
166
|
+
optional: true,
|
|
167
|
+
},
|
|
168
|
+
applicationSid: {
|
|
169
|
+
type: "string",
|
|
170
|
+
label: "Application SID",
|
|
171
|
+
description: "The SID of the Application resource",
|
|
172
|
+
async options() {
|
|
173
|
+
const applications = await this.listApplications();
|
|
174
|
+
return applications?.map((application) => ({
|
|
175
|
+
label: application.friendly_name,
|
|
176
|
+
value: application.sid,
|
|
177
|
+
})) || [];
|
|
178
|
+
},
|
|
179
|
+
},
|
|
169
180
|
},
|
|
170
181
|
methods: {
|
|
171
182
|
validateRequest({
|
|
172
|
-
signature, url, params, authToken = this.$auth.
|
|
183
|
+
signature, url, params, authToken = this.$auth.AuthToken,
|
|
173
184
|
} = {}) {
|
|
174
185
|
// See https://www.twilio.com/docs/usage/webhooks/webhooks-security
|
|
175
186
|
return twilio.validateRequest(
|
|
@@ -240,6 +251,32 @@ export default {
|
|
|
240
251
|
const client = this.getClient();
|
|
241
252
|
return client.transcriptions.list(params);
|
|
242
253
|
},
|
|
254
|
+
listTranscripts(params) {
|
|
255
|
+
const client = this.getClient();
|
|
256
|
+
return client.intelligence.v2.transcripts.list(params);
|
|
257
|
+
},
|
|
258
|
+
listTranscriptSentences(transcriptId, params = {}) {
|
|
259
|
+
const client = this.getClient();
|
|
260
|
+
return client.intelligence.v2.transcripts(transcriptId).sentences.list(params);
|
|
261
|
+
},
|
|
262
|
+
async getSentences(transcriptId) {
|
|
263
|
+
const sentences = await this.listTranscriptSentences(transcriptId, {
|
|
264
|
+
limit: 1000,
|
|
265
|
+
});
|
|
266
|
+
const transcriptParts = sentences.map((sentence) => `Speaker ${sentence.mediaChannel} (Sentence ${sentence.sentenceIndex}):\n${sentence.transcript}\nConfidence: ${sentence.confidence}\n`);
|
|
267
|
+
return {
|
|
268
|
+
sentences,
|
|
269
|
+
transcript: transcriptParts.join("\n"),
|
|
270
|
+
};
|
|
271
|
+
},
|
|
272
|
+
listApplications(params) {
|
|
273
|
+
const client = this.getClient();
|
|
274
|
+
return client.applications.list(params);
|
|
275
|
+
},
|
|
276
|
+
createVerificationService(params) {
|
|
277
|
+
const client = this.getClient();
|
|
278
|
+
return client.verify.v2.services.create(params);
|
|
279
|
+
},
|
|
243
280
|
/**
|
|
244
281
|
* Returns a list of messages associated with your account. When getting the
|
|
245
282
|
* list of all messages, results will be sorted on the DateSent field with
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import twilio from "../../twilio.app.mjs";
|
|
2
|
-
import { omitEmptyStringValues } from "../../common/utils.mjs";
|
|
3
|
-
|
|
4
|
-
export default {
|
|
5
|
-
key: "twilio-list-recording-transcriptions",
|
|
6
|
-
name: "List Recording Transcriptions",
|
|
7
|
-
description: "Return a set of transcriptions available for a recording. [See the docs](https://www.twilio.com/docs/voice/api/recording#fetch-a-recordings-transcriptions) for more information",
|
|
8
|
-
version: "0.1.2",
|
|
9
|
-
type: "action",
|
|
10
|
-
props: {
|
|
11
|
-
twilio,
|
|
12
|
-
recordingID: {
|
|
13
|
-
propDefinition: [
|
|
14
|
-
twilio,
|
|
15
|
-
"recordingID",
|
|
16
|
-
],
|
|
17
|
-
},
|
|
18
|
-
limit: {
|
|
19
|
-
propDefinition: [
|
|
20
|
-
twilio,
|
|
21
|
-
"limit",
|
|
22
|
-
],
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
async run({ $ }) {
|
|
26
|
-
const resp = await this.twilio.listRecordingTranscriptions(
|
|
27
|
-
this.recordingID,
|
|
28
|
-
omitEmptyStringValues({
|
|
29
|
-
limit: this.limit,
|
|
30
|
-
}),
|
|
31
|
-
);
|
|
32
|
-
$.export("$summary", `Successfully fetched ${resp.length} recording transcription${resp.length === 1
|
|
33
|
-
? ""
|
|
34
|
-
: "s"} for the recording, "${this.recordingID}"`);
|
|
35
|
-
return resp;
|
|
36
|
-
},
|
|
37
|
-
};
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { phone } from "phone";
|
|
2
|
-
import twilio from "../../twilio.app.mjs";
|
|
3
|
-
import { messageToString } from "../../common/utils.mjs";
|
|
4
|
-
|
|
5
|
-
export default {
|
|
6
|
-
key: "twilio-send-sms",
|
|
7
|
-
name: "Send SMS",
|
|
8
|
-
description: "Send a simple text-only SMS. [See the docs](https://www.twilio.com/docs/sms/api/message-resource#create-a-message-resource) for more information",
|
|
9
|
-
type: "action",
|
|
10
|
-
version: "0.1.3",
|
|
11
|
-
props: {
|
|
12
|
-
twilio,
|
|
13
|
-
from: {
|
|
14
|
-
propDefinition: [
|
|
15
|
-
twilio,
|
|
16
|
-
"from",
|
|
17
|
-
],
|
|
18
|
-
},
|
|
19
|
-
to: {
|
|
20
|
-
propDefinition: [
|
|
21
|
-
twilio,
|
|
22
|
-
"to",
|
|
23
|
-
],
|
|
24
|
-
},
|
|
25
|
-
body: {
|
|
26
|
-
propDefinition: [
|
|
27
|
-
twilio,
|
|
28
|
-
"body",
|
|
29
|
-
],
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
async run({ $ }) {
|
|
33
|
-
// Parse the given number into its E.164 equivalent
|
|
34
|
-
// The E.164 phone number will be included in the first element
|
|
35
|
-
// of the array, but the array will be empty if parsing fails.
|
|
36
|
-
// See https://www.npmjs.com/package/phone
|
|
37
|
-
const toParsed = phone(this.to);
|
|
38
|
-
if (!toParsed || !toParsed.phoneNumber) {
|
|
39
|
-
throw new Error(`Phone number ${this.to} could not be parsed as a valid number.`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const fromParsed = phone(this.from);
|
|
43
|
-
if (!fromParsed || !fromParsed.phoneNumber) {
|
|
44
|
-
throw new Error(`Phone number ${this.from} could not be parsed as a valid number.`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const data = {
|
|
48
|
-
to: toParsed.phoneNumber,
|
|
49
|
-
from: fromParsed.phoneNumber,
|
|
50
|
-
body: this.body,
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const resp = await this.twilio.getClient().messages.create(data);
|
|
54
|
-
$.export("$summary", `Successfully sent a new SMS, "${messageToString(resp)}"`);
|
|
55
|
-
return resp;
|
|
56
|
-
},
|
|
57
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import common from "../common-polling.mjs";
|
|
2
|
-
|
|
3
|
-
export default {
|
|
4
|
-
...common,
|
|
5
|
-
key: "twilio-new-transcription",
|
|
6
|
-
name: "New Transcription",
|
|
7
|
-
description: "Emit new event when a new call transcription is created",
|
|
8
|
-
version: "0.1.4",
|
|
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
|
-
};
|