diff --git a/package.json b/package.json index bfcc72c9f..db301135d 100644 --- a/package.json +++ b/package.json @@ -121,4 +121,4 @@ "built": false } } -} \ No newline at end of file +} diff --git a/packages/channels/src/twilio/api.ts b/packages/channels/src/twilio/api.ts index fff0d9567..e4bb4f054 100644 --- a/packages/channels/src/twilio/api.ts +++ b/packages/channels/src/twilio/api.ts @@ -1,5 +1,9 @@ import express, { Response } from 'express' +// @ts-ignore +import extName from 'ext-name' +import path from 'path' import { validateRequest } from 'twilio' +import urlUtil from 'url' import yn from 'yn' import { ChannelApi, ChannelApiManager, ChannelApiRequest } from '../base/api' import { TwilioService } from './service' @@ -25,15 +29,31 @@ export class TwilioApi extends ChannelApi { } private async receive(scope: string, body: any) { - const botPhoneNumber = body.To - const userPhoneNumber = body.From + const { NumMedia, To: botPhoneNumber, From: userPhoneNumber } = body + const endpoint = { identity: botPhoneNumber, sender: userPhoneNumber, thread: '*' } - const index = Number(body.Body) - const content = this.service.handleIndexResponse(scope, index, botPhoneNumber, userPhoneNumber) || { - type: 'text', - text: body.Body + for (let i = 0; i < NumMedia; i++) { + const contentUrl = body[`MediaUrl${i}`] + const contentType = body[`MediaContentType${i}`] + const extension = extName.mime(contentType)[0].ext + const mediaSid = path.basename(urlUtil.parse(contentUrl).pathname as string) + const name = `${mediaSid}.${extension}` + + await this.service.receive(scope, endpoint, { + type: this.mapMimeTypeToStandardType(contentType), + url: contentUrl, + title: name + }) } - await this.service.receive(scope, { identity: botPhoneNumber, sender: userPhoneNumber, thread: '*' }, content) + if (body.Body.length > 0) { + const index = Number(body.Body) + const content = this.service.handleIndexResponse(scope, index, botPhoneNumber, userPhoneNumber) || { + type: 'text', + text: body.Body + } + + await this.service.receive(scope, endpoint, content) + } } } diff --git a/packages/channels/src/typings/ext-name.d.ts b/packages/channels/src/typings/ext-name.d.ts new file mode 100644 index 000000000..fe2fb4e6f --- /dev/null +++ b/packages/channels/src/typings/ext-name.d.ts @@ -0,0 +1 @@ +declare module 'ext-name' diff --git a/packages/server/package.json b/packages/server/package.json index f0ba022bd..30ba0f492 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -42,6 +42,7 @@ "dotenv": "^15.0.0", "exponential-backoff": "^3.1.0", "express": "^4.17.2", + "ext-name": "^5.0.0", "http-terminator": "^3.0.4", "joi": "^17.6.0", "knex": "^0.95.15", @@ -50,8 +51,9 @@ "ms": "^2.1.3", "portfinder": "^1.0.28", "socket.io": "^4.4.1", + "url": "^0.11.3", "uuid": "^8.3.2", "yargs": "^17.3.1", "yn": "^4.0.0" } -} \ No newline at end of file +} diff --git a/test/jest.unit.config.ts b/test/jest.unit.config.ts index d180442f5..e0add1507 100644 --- a/test/jest.unit.config.ts +++ b/test/jest.unit.config.ts @@ -38,7 +38,10 @@ const config: Config.InitialOptions = { } }, clearMocks: true, - moduleNameMapper: pathsToModuleNameMapper(ServerConfig.compilerOptions.paths, { prefix: '/test/' }) + moduleNameMapper: { + '^ext-name$': '/../channels/src/typings/ext-name.d.ts', + ...pathsToModuleNameMapper(ServerConfig.compilerOptions.paths, { prefix: '/test/' }) + } }, { rootDir: 'packages/components', diff --git a/yarn.lock b/yarn.lock index 0d8cfdd1e..32d4ad78d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2451,6 +2451,7 @@ __metadata: dotenv: ^15.0.0 exponential-backoff: ^3.1.0 express: ^4.17.2 + ext-name: ^5.0.0 http-terminator: ^3.0.4 joi: ^17.6.0 knex: ^0.95.15 @@ -2460,6 +2461,7 @@ __metadata: ms: ^2.1.3 portfinder: ^1.0.28 socket.io: ^4.4.1 + url: ^0.11.3 uuid: ^8.3.2 yargs: ^17.3.1 yn: ^4.0.0 @@ -13195,6 +13197,25 @@ __metadata: languageName: node linkType: hard +"ext-list@npm:^2.0.0": + version: 2.2.2 + resolution: "ext-list@npm:2.2.2" + dependencies: + mime-db: ^1.28.0 + checksum: 9b2426bea312e674eeced62c5f18407ab9a8653bbdfbde36492331c7973dab7fbf9e11d6c38605786168b42da333910314988097ca06eee61f1b9b57efae3f18 + languageName: node + linkType: hard + +"ext-name@npm:^5.0.0": + version: 5.0.0 + resolution: "ext-name@npm:5.0.0" + dependencies: + ext-list: ^2.0.0 + sort-keys-length: ^1.0.0 + checksum: f598269bd5de4295540ea7d6f8f6a01d82a7508f148b7700a05628ef6121648d26e6e5e942049e953b3051863df6b54bd8fe951e7877f185e34ace5d44370b33 + languageName: node + linkType: hard + "ext@npm:^1.1.2": version: 1.6.0 resolution: "ext@npm:1.6.0" @@ -15920,7 +15941,7 @@ __metadata: languageName: node linkType: hard -"is-plain-obj@npm:^1.1.0": +"is-plain-obj@npm:^1.0.0, is-plain-obj@npm:^1.1.0": version: 1.1.0 resolution: "is-plain-obj@npm:1.1.0" checksum: 0ee04807797aad50859652a7467481816cbb57e5cc97d813a7dcd8915da8195dc68c436010bf39d195226cde6a2d352f4b815f16f26b7bf486a5754290629931 @@ -18231,7 +18252,7 @@ __metadata: languageName: node linkType: hard -"mime-db@npm:1.52.0": +"mime-db@npm:1.52.0, mime-db@npm:^1.28.0": version: 1.52.0 resolution: "mime-db@npm:1.52.0" checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f @@ -21263,6 +21284,15 @@ __metadata: languageName: node linkType: hard +"qs@npm:^6.11.2": + version: 6.11.2 + resolution: "qs@npm:6.11.2" + dependencies: + side-channel: ^1.0.4 + checksum: e812f3c590b2262548647d62f1637b6989cc56656dc960b893fe2098d96e1bd633f36576f4cd7564dfbff9db42e17775884db96d846bebe4f37420d073ecdc0b + languageName: node + linkType: hard + "qs@npm:~6.5.2": version: 6.5.2 resolution: "qs@npm:6.5.2" @@ -23313,6 +23343,24 @@ __metadata: languageName: node linkType: hard +"sort-keys-length@npm:^1.0.0": + version: 1.0.1 + resolution: "sort-keys-length@npm:1.0.1" + dependencies: + sort-keys: ^1.0.0 + checksum: f9acac5fb31580a9e3d43b419dc86a1b75e85b79036a084d95dd4d1062b621c9589906588ac31e370a0dd381be46d8dbe900efa306d087ca9c912d7a59b5a590 + languageName: node + linkType: hard + +"sort-keys@npm:^1.0.0": + version: 1.1.2 + resolution: "sort-keys@npm:1.1.2" + dependencies: + is-plain-obj: ^1.0.0 + checksum: 5963fd191a2a185a5ec86f06e47721e8e04713eda43bb04ae60d2a8afb21241553dd5bc9d863ed2bd7c3d541b609b0c8d0e58836b1a3eb6764c09c094bcc8b00 + languageName: node + linkType: hard + "source-list-map@npm:^2.0.0": version: 2.0.1 resolution: "source-list-map@npm:2.0.1" @@ -25451,6 +25499,16 @@ __metadata: languageName: node linkType: hard +"url@npm:^0.11.3": + version: 0.11.3 + resolution: "url@npm:0.11.3" + dependencies: + punycode: ^1.4.1 + qs: ^6.11.2 + checksum: f9e7886f46a16f96d2e42fbcc5d682c231c55ef5442c1ff66150c0f6556f6e3a97d094a84f51be15ec2432711d212eb60426659ce418f5fcadeaa3f601532c4e + languageName: node + linkType: hard + "use-composed-ref@npm:^1.0.0": version: 1.1.0 resolution: "use-composed-ref@npm:1.1.0"