@botbuildercommunity/adapter-twilio-whatsapp
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -7,2 +7,13 @@ # Changelog | ||
## [1.1.0] - 2020-02 | ||
### Added | ||
- Support location messages (inbound and outbound) | ||
### Fixed | ||
- Fixed the Twilio validation of incoming requests | ||
## [1.0.0] - 2019-09 | ||
### Changed | ||
- Bumped for v1 release | ||
## [0.1.0] - 2019-07 | ||
@@ -14,6 +25,2 @@ ### Added | ||
- Added support for: Send proactive notifications | ||
- Added support for: Track message deliveries (`sent`, `delivered`, `read` receipts) | ||
## [1.0.0] - 2019-09 | ||
### Changed | ||
- Bumped for v1 release | ||
- Added support for: Track message deliveries (`sent`, `delivered`, `read` receipts) |
@@ -1,6 +0,6 @@ | ||
import { Activity, BotAdapter, TurnContext, ConversationReference, ResourceResponse, WebRequest, WebResponse } from 'botbuilder'; | ||
import * as Twilio from 'twilio'; | ||
/** | ||
* @module botbuildercommunity/adapter-twilio-whatsapp | ||
*/ | ||
import { Activity, BotAdapter, TurnContext, ConversationReference, ResourceResponse, WebRequest, WebResponse } from 'botbuilder'; | ||
import * as Twilio from 'twilio'; | ||
/** | ||
@@ -7,0 +7,0 @@ * Settings used to configure a `TwilioWhatsAppAdapter` instance. |
"use strict"; | ||
/** | ||
* @module botbuildercommunity/adapter-twilio-whatsapp | ||
*/ | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
@@ -138,2 +142,3 @@ }); | ||
res.end(); | ||
return; | ||
} | ||
@@ -145,2 +150,3 @@ const isTwilioRequest = Twilio.validateRequest(authToken, signature, requestUrl, message); | ||
res.end(); | ||
return; | ||
} | ||
@@ -211,2 +217,3 @@ // Handle events | ||
} | ||
activity.attachments = []; | ||
// Message Received | ||
@@ -216,3 +223,2 @@ if (activity.type === botbuilder_1.ActivityTypes.Message) { | ||
if (message.NumMedia && parseInt(message.NumMedia) > 0) { | ||
activity.attachments = []; | ||
const amount = parseInt(message.NumMedia); | ||
@@ -227,2 +233,19 @@ for (let i = 0; i < amount; i++) { | ||
} | ||
// Has location? | ||
// Latitude=37.7879277&Longitude=-122.3937508&Address=375+Beale+St%2C+San+Francisco%2C+CA+94105 | ||
if (message.Latitude && message.Longitude) { | ||
const geo = { | ||
elevation: null, | ||
type: 'GeoCoordinates', | ||
latitude: parseFloat(message.Latitude), | ||
longitude: parseFloat(message.Longitude), | ||
name: message.Address | ||
}; | ||
const attachment = { | ||
contentType: 'application/json', | ||
content: geo, | ||
name: message.Address | ||
}; | ||
activity.attachments.push(attachment); | ||
} | ||
} | ||
@@ -264,2 +287,3 @@ // Create a Conversation Reference | ||
parseActivity(activity) { | ||
var _a, _b, _c, _d, _e; | ||
// Change formatting to WhatsApp formatting | ||
@@ -278,4 +302,2 @@ if (activity.text) { | ||
// Not supported by current Twilio WhatsApp API yet | ||
// Handle locations | ||
// Not supported by current Twilio WhatsApp API yet | ||
// Create new Message for Twilio | ||
@@ -286,9 +308,18 @@ // @ts-ignore Using any since MessageInstance interface doesn't include `mediaUrl` | ||
from: this.settings.phoneNumber, | ||
to: activity.conversation.id, | ||
mediaUrl: undefined | ||
to: activity.conversation.id | ||
}; | ||
// Handle Persistant Actions (like locations) | ||
// https://www.twilio.com/docs/sms/whatsapp/api#location-messages-with-whatsapp | ||
if ((_b = (_a = activity) === null || _a === void 0 ? void 0 : _a.channelData) === null || _b === void 0 ? void 0 : _b.persistentAction) { | ||
if (Array.isArray(activity.channelData.persistentAction)) { | ||
message.persistentAction = activity.channelData.persistentAction; | ||
} | ||
else { | ||
message.persistentAction = [activity.channelData.persistentAction]; | ||
} | ||
} | ||
// Handle attachments | ||
// One media attachment is supported per message, with a size limit of 5MB. | ||
// https://www.twilio.com/docs/sms/whatsapp/api#sending-a-freeform-whatsapp-message-with-media-attachment | ||
if (activity.attachments && activity.attachments.length > 0) { | ||
if (((_d = (_c = activity) === null || _c === void 0 ? void 0 : _c.attachments) === null || _d === void 0 ? void 0 : _d.length) > 0) { | ||
const attachment = activity.attachments[0]; | ||
@@ -302,2 +333,9 @@ // Check if contentUrl is provided | ||
} | ||
// Check if attachment is location | ||
if (attachment.contentType === 'application/json') { | ||
if (((_e = attachment.content) === null || _e === void 0 ? void 0 : _e.type) === 'GeoCoordinates') { | ||
const geo = attachment.content; | ||
message.persistentAction = [`geo:${geo.latitude},${geo.longitude}${(geo.name ? `|${geo.name}` : '')}`]; | ||
} | ||
} | ||
} | ||
@@ -304,0 +342,0 @@ // Messages without text or mediaUrl are not valid |
{ | ||
"name": "@botbuildercommunity/adapter-twilio-whatsapp", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Twilio WhatsApp adapter for use with the Bot Framework.", | ||
@@ -10,6 +10,25 @@ "main": "./lib/index.js", | ||
}, | ||
"nyc": { | ||
"extends": "@istanbuljs/nyc-config-typescript", | ||
"all": true, | ||
"check-coverage": true, | ||
"lines": 0, | ||
"statements": 0, | ||
"functions": 0, | ||
"branches": 0, | ||
"exclude": [ | ||
"**/*.d.ts", | ||
"**/*.test.js" | ||
], | ||
"reporter": [ | ||
"text", | ||
"cobertura", | ||
"html" | ||
], | ||
"report-dir": "./.nyc_output/coverage" | ||
}, | ||
"scripts": { | ||
"build": "tsc", | ||
"test": "tsc && mocha", | ||
"test-ci": "tsc && mocha --reporter xunit --reporter-options output=./mocha-test-results.xml --reporter-options suiteName=$npm_package_name" | ||
"test": "tsc && nyc mocha", | ||
"test-ci": "tsc && nyc mocha --reporter xunit --reporter-options output=./mocha-test-results.xml --reporter-options suiteName=$npm_package_name" | ||
}, | ||
@@ -29,3 +48,5 @@ "engines": { | ||
"adapters", | ||
"whatsapp" | ||
"whatsapp", | ||
"twilio", | ||
"twilio-whatsapp" | ||
], | ||
@@ -42,9 +63,11 @@ "contributors": [ | ||
"dependencies": { | ||
"botbuilder": "^4.5.3", | ||
"twilio": "^3.33.2" | ||
"botbuilder": "^4.7.2", | ||
"twilio": "^3.39.4" | ||
}, | ||
"devDependencies": { | ||
"mocha": "^6.2.0", | ||
"nock": "^10.0.6", | ||
"typescript": "^3.5.3" | ||
"@istanbuljs/nyc-config-typescript": "^1.0.1", | ||
"mocha": "^7.0.1", | ||
"nock": "^11.8.2", | ||
"nyc": "^15.0.0", | ||
"typescript": "^3.7.5" | ||
}, | ||
@@ -51,0 +74,0 @@ "publishConfig": { |
@@ -9,4 +9,5 @@ # Twilio WhatsApp Adapter (beta) | ||
This adapter supports the limited capabilities of Twilio WhatsApp, including; | ||
* Send and receive text messages | ||
* Send and receive text messages with attachment (`image`, `audio`, `video`, `document`) | ||
* Send and receive text messages with attachment (`image`, `audio`, `video`, `document`, `location`) | ||
* Send proactive notifications | ||
@@ -56,4 +57,13 @@ * Track message deliveries (`sent`, `delivered`, `read` receipts) | ||
### Send attachments | ||
* [Send and receive attachments](#send-and-receive-attachments) | ||
* [Send and receive location messages](#send-and-receive-location-messages) | ||
* [Send proactive notifications](#send-proactive-notifications) | ||
* [Implement channel-specific functionality](#implement-channel-specific-functionality) | ||
* [Monitor the status of your WhatsApp outbound message](#monitor-the-status-of-your-whatsapp-outbound-message) | ||
### Send and receive attachments | ||
The Bot Framework SDK supports the task of sending rich messages to the user. The Twilio WhatsApp adapter is using the same principles as the Bot Framework SDK. ([official documentation](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-add-media-attachments?view=azure-bot-service-4.0&tabs=javascript)). | ||
Attachments to WhatsApp messages can be of many different file types, including JPG, MP3, and PDF. [See more about the supported file types in the Twilio FAQs](https://support.twilio.com/hc/en-us/articles/360017961894-Sending-and-Receiving-Media-with-WhatsApp-Messaging-on-Twilio-Beta-). The file type can be found by looking at the `contentType` of the attachment. | ||
**Example** | ||
@@ -75,4 +85,60 @@ ```javascript | ||
>_You can send media messages up to 5 MB in size. At this time, Twilio will not transcode media for outgoing WhatsApp messages, so if you need to send a media object that is larger than 5 MB, please reduce the file size before sending it to Twilio._ | ||
### Send and receive location messages | ||
Twilio WhatsApp offers the ability to send and receive [location messages](https://www.twilio.com/docs/sms/whatsapp/api#location-messages-with-whatsapp). | ||
#### Sending | ||
Location messages can be sent in two ways. By using a JSON attachment or by sending the location directly via channelData. | ||
**Attachment** | ||
```javascript | ||
const replyWithLocation = { | ||
type: 'message', | ||
text: `Microsoft Nederland`, | ||
attachments: [ | ||
{ | ||
contentType: 'application/json', | ||
content: { | ||
elevation: null, | ||
type: 'GeoCoordinates', | ||
latitude: 52.3037702, | ||
longitude: 4.7501761, | ||
name: 'Schiphol' | ||
} | ||
} | ||
] | ||
}; | ||
await context.sendActivity(replyWithLocation); | ||
``` | ||
**ChannelData** | ||
```javascript | ||
const replyWithLocation = { | ||
type: 'message', | ||
text: 'name', // The name of the location being sent (Location must exist in Google maps for the hyperlink to work on Mac/Windows WhatsApp client) | ||
channelData: { | ||
persistentAction: 'geo:{latitude},{longitude}|{label}' | ||
} | ||
}; | ||
await context.sendActivity(replyWithLocation); | ||
``` | ||
#### Receiving | ||
```javascript | ||
if (context.activity.attachments && context.activity.attachments.length > 0) { | ||
for (attachment of context.activity.attachments) { | ||
if (attachment.contentType === 'application/json' && attachment.content.type === 'GeoCoordinates') { | ||
console.log('Received location!'); | ||
await context.sendActivity('Received a location' + | ||
`${attachment.name} (${attachment.content.name}) (${attachment.content.latitude},${attachment.content.longitude})`); | ||
} | ||
} | ||
} | ||
``` | ||
### Send proactive notifications | ||
Proactive notifications are supported using the default SDK functions. Read more about how to [send proactive notifications to users](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-proactive-message?view=azure-bot-service-4.0&tabs=csharp). | ||
Proactive notifications are supported using the same principles as the Bot Framework SDK. Read more about how to [send proactive notifications to users](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-proactive-message?view=azure-bot-service-4.0&tabs=csharp). | ||
@@ -94,10 +160,10 @@ A WhatsApp session begins with a user initiated message to your app. Sessions are valid for 24 hours after the most recently received message, during which time you can communicate with them using free form messages. In order to send a message outside the 24 hour Session window, you must use a pre-approved template (see [Sending Notifications](https://www.twilio.com/docs/sms/whatsapp/api#sending-notifications) section). | ||
The Twilio WhatsApp channel is using `whatsapp` as the channel id. Within the TurnContext, you can use the following snippet to detect if the request is coming from the Twilio WhatsApp channel and implement your custom logic. | ||
`if (context.activity.channelId === 'whatsapp)'` | ||
`if (context.activity.channelId === 'whatsapp')` | ||
Using the `channelData` object on new message activities is not supported, since there is no WhatsApp specific functionality that isn't supported by the Bot Framework SDK. | ||
Using the `channelData` object on new message activities is currently only supported for passing `persistentAction`, which can be used to send location messages. | ||
### Monitor the status of your WhatsApp outbound message | ||
If you configure the `status callback url` in Twilio Configuration, multiple status events will be broadcasted to your bot. You can use this functionality to [monitor the status of your WhatsApp outbound message](https://www.twilio.com/docs/sms/whatsapp/api#monitor-the-status-of-your-whatsapp-outbound-message). Possible values include: 'messageRead', 'messageDelivered', 'messageSent', 'messageQueued', 'messageFailed' | ||
If you configure the `status callback url` in Twilio Configuration, multiple status events will be broadcasted to your bot. You can use this functionality to [monitor the status of your WhatsApp outbound message](https://www.twilio.com/docs/sms/whatsapp/api#monitor-the-status-of-your-whatsapp-outbound-message). Possible values include: 'messageRead', 'messageDelivered', 'messageSent', 'messageQueued', 'messageFailed'. | ||
Within the TurnContext you are able to differentiate between the events by reading the value of `context.activity.type`. | ||
Within the TurnContext you are able to differentiate between the events by reading the value of `context.activity.type`. If you are using an `ActivityHandler`, you should use the `onUnrecognizedActivityType` method. | ||
@@ -104,0 +170,0 @@ **Example (JavaScript)** |
@@ -1,6 +0,1 @@ | ||
import { Activity, ActivityTypes, BotAdapter, TurnContext, ConversationReference, ResourceResponse, WebRequest, WebResponse } from 'botbuilder'; | ||
import * as Twilio from 'twilio'; | ||
import { MessageInstance } from 'twilio/lib/rest/api/v2010/account/message'; | ||
import { parse } from 'qs'; | ||
/** | ||
@@ -10,2 +5,6 @@ * @module botbuildercommunity/adapter-twilio-whatsapp | ||
import { Activity, ActivityTypes, BotAdapter, TurnContext, ConversationReference, ResourceResponse, WebRequest, WebResponse, Attachment, GeoCoordinates } from 'botbuilder'; | ||
import * as Twilio from 'twilio'; | ||
import { MessageInstance } from 'twilio/lib/rest/api/v2010/account/message'; | ||
import { parse } from 'qs'; | ||
@@ -177,2 +176,3 @@ /** | ||
res.end(); | ||
return; | ||
} | ||
@@ -187,2 +187,3 @@ | ||
res.end(); | ||
return; | ||
} | ||
@@ -257,2 +258,4 @@ | ||
activity.attachments = []; | ||
// Message Received | ||
@@ -264,7 +267,6 @@ if (activity.type === ActivityTypes.Message) { | ||
activity.attachments = []; | ||
const amount = parseInt(message.NumMedia); | ||
for (let i = 0; i < amount; i++) { | ||
const attachment = { | ||
const attachment: Attachment = { | ||
contentType: message['MediaContentType' + i], | ||
@@ -278,2 +280,22 @@ contentUrl: message['MediaUrl' + i] | ||
// Has location? | ||
// Latitude=37.7879277&Longitude=-122.3937508&Address=375+Beale+St%2C+San+Francisco%2C+CA+94105 | ||
if (message.Latitude && message.Longitude) { | ||
const geo: GeoCoordinates = { | ||
elevation: null, | ||
type: 'GeoCoordinates', | ||
latitude: parseFloat(message.Latitude), | ||
longitude: parseFloat(message.Longitude), | ||
name: message.Address | ||
}; | ||
const attachment: Attachment = { | ||
contentType: 'application/json', | ||
content: geo, | ||
name: message.Address | ||
}; | ||
activity.attachments.push(attachment); | ||
} | ||
} | ||
@@ -338,5 +360,2 @@ | ||
// Handle locations | ||
// Not supported by current Twilio WhatsApp API yet | ||
// Create new Message for Twilio | ||
@@ -347,10 +366,19 @@ // @ts-ignore Using any since MessageInstance interface doesn't include `mediaUrl` | ||
from: this.settings.phoneNumber, | ||
to: activity.conversation.id, | ||
mediaUrl: undefined | ||
to: activity.conversation.id | ||
}; | ||
// Handle Persistant Actions (like locations) | ||
// https://www.twilio.com/docs/sms/whatsapp/api#location-messages-with-whatsapp | ||
if (activity?.channelData?.persistentAction) { | ||
if (Array.isArray(activity.channelData.persistentAction)) { | ||
message.persistentAction = activity.channelData.persistentAction; | ||
} else { | ||
message.persistentAction = [activity.channelData.persistentAction]; | ||
} | ||
} | ||
// Handle attachments | ||
// One media attachment is supported per message, with a size limit of 5MB. | ||
// https://www.twilio.com/docs/sms/whatsapp/api#sending-a-freeform-whatsapp-message-with-media-attachment | ||
if (activity.attachments && activity.attachments.length > 0) { | ||
if (activity?.attachments?.length > 0) { | ||
const attachment = activity.attachments[0]; | ||
@@ -364,2 +392,10 @@ | ||
} | ||
// Check if attachment is location | ||
if (attachment.contentType === 'application/json') { | ||
if (attachment.content?.type === 'GeoCoordinates') { | ||
const geo = attachment.content; | ||
message.persistentAction = [`geo:${ geo.latitude },${ geo.longitude }${ (geo.name ? `|${ geo.name }` : '') }`]; | ||
} | ||
} | ||
} | ||
@@ -366,0 +402,0 @@ |
Sorry, the diff of this file is not supported yet
269249
28
1400
174
5
Updatedbotbuilder@^4.7.2
Updatedtwilio@^3.39.4