-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Twitter integration #1
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,228 @@ | ||
# custom-channel-middleware | ||
Middleware for custom channels to feed into Twilio Flex | ||
# Customer Channel Middleware | ||
|
||
Middleware for custom channels to feed into [Twilio Flex](https://www.twilio.com/flex) | ||
|
||
|
||
The following guide demonstrates how you can enable Twilio Flex to communicate via Twitter DMs. | ||
|
||
### Setup a Twilio Flex instance and middleware | ||
|
||
Follow [this guide](https://www.twilio.com/blog/add-custom-chat-channel-twilio-flex) to setup your Flex instance, including the optional portions. | ||
|
||
### Obtain a Twitter developer account | ||
|
||
You will need access to the Twitter API, which you can apply for [here](https://developer.twitter.com/en/apply-for-access). | ||
|
||
Setup an App and update the permissions to `Read, Write, and Direct Messages`. Note that you will need to regenerate your credentials when you modify this setting. | ||
|
||
Create a developer environment [here](https://developer.twitter.com/en/account/environments) for the `Account Activity API` and name it `dev`. | ||
|
||
### Install autohook | ||
|
||
Once you have your Twitter developer account, you can set up the environment variables as described [here](https://github.com/twitterdev/autohook#dotenv-envtwitter). Installation instructions can be found [here](https://github.com/twitterdev/autohook#install). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An important note on the env variables is that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catch! |
||
|
||
### Setup Twilio serverless function | ||
|
||
The Twilio serverless function will be responsible for responding to Twitter DMs via Twilio Flex. | ||
|
||
After you create the function, replace the code with the following: | ||
|
||
```javascript | ||
exports.handler = function(context, event, callback) { | ||
if (event['Source'] == 'SDK') { | ||
let Twit = require('twit') | ||
let T = new Twit({ | ||
consumer_key: process.env.TWITTER_CONSUMER_KEY, | ||
consumer_secret: process.env.TWITTER_CONSUMER_SECRET, | ||
access_token: process.env.TWITTER_ACCESS_TOKEN, | ||
access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET, | ||
timeout_ms: 60*1000, // optional HTTP request timeout to apply to all requests. | ||
strictSSL: true, // optional - requires SSL certificates to be valid. | ||
}) | ||
let sendTo = { | ||
event: { | ||
type: 'message_create', | ||
message_create: { | ||
target: { | ||
recipient_id: event['recipient_id'], | ||
}, | ||
message_data: { | ||
text: event['Body'], | ||
}, | ||
}, | ||
} | ||
} | ||
T.post("direct_messages/events/new", sendTo, function(err, data, response){ | ||
console.info(data); | ||
}); | ||
}; | ||
} | ||
``` | ||
|
||
In the Settings section, add the following dependency: `twit` with version `2.2.11`. | ||
|
||
In the Settings section, add the following environment variables: `TWITTER_ACCESS_TOKEN_SECRET`, `TWITTER_ACCESS_TOKEN`, `TWITTER_CONSUMER_SECRET`, `TWITTER_CONSUMER_KEY` | ||
|
||
Now, save your function and click `Deploy All` and copy the URL for your function, you will need it to update the middleware. | ||
|
||
### Update middleware | ||
|
||
In `./twilio-flex-custom-webchat/middleware/server.js`, replace all the code there with: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: this code can be downloaded from https://github.com/vernig/twilio-flex-custom-webchat/ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a link to that repo in the references section. |
||
|
||
```javascript | ||
require('dotenv').load(); | ||
|
||
const flex = require('./flex-custom-webchat'); | ||
|
||
const { Autohook } = require('twitter-autohook'); | ||
(async ƛ => { | ||
const webhook = new Autohook(); | ||
// Removes existing webhooks | ||
await webhook.removeWebhooks(); | ||
// Listen for incoming direct messages | ||
webhook.on('event', async event => { | ||
if (event.direct_message_events) { | ||
let active_user = event.for_user_id; | ||
let sender_id = event.direct_message_events[0].message_create.sender_id | ||
if(sender_id != active_user){ | ||
console.log("New message from: " + event.users[sender_id].name) | ||
console.log(event.direct_message_events[0].message_create.message_data.text) | ||
await flex.sendMessageToFlex(event.direct_message_events[0].message_create.message_data.text, event.users[sender_id].name, event.users[sender_id].screen_name, event['for_user_id']); | ||
} | ||
} | ||
}); | ||
// Starts a server and adds a new webhook | ||
await webhook.start(); | ||
// Subscribes to a user's activity | ||
await webhook.subscribe({oauth_token: process.env.TWITTER_ACCESS_TOKEN, oauth_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET}); | ||
})(); | ||
``` | ||
|
||
In `./twilio-flex-custom-webchat/middleware/flex-custom-webchat.js`, replace the code with: | ||
|
||
```javascript | ||
require('dotenv').config(); | ||
const fetch = require('node-fetch'); | ||
const { URLSearchParams } = require('url'); | ||
var base64 = require('base-64'); | ||
|
||
const client = require('twilio')( | ||
process.env.TWILIO_ACCOUNT_SID, | ||
process.env.TWILIO_AUTH_TOKEN | ||
); | ||
|
||
var flexChannelCreated; | ||
|
||
function sendChatMessage(serviceSid, channelSid, chatUserName, recipientId, body) { | ||
console.log('Sending new chat message'); | ||
const params = new URLSearchParams(); | ||
params.append('RecipientId', recipientId); | ||
params.append('Body', body); | ||
params.append('From', chatUserName); | ||
console.log('Params:' + params) | ||
return fetch( | ||
`https://chat.twilio.com/v2/Services/${serviceSid}/Channels/${channelSid}/Messages`, | ||
{ | ||
method: 'post', | ||
body: params, | ||
headers: { | ||
'X-Twilio-Webhook-Enabled': 'true', | ||
Authorization: `Basic ${base64.encode( | ||
`${process.env.TWILIO_ACCOUNT_SID}:${process.env.TWILIO_AUTH_TOKEN}` | ||
)}` | ||
} | ||
} | ||
); | ||
} | ||
|
||
function createNewChannel(flexFlowSid, flexChatService, recipientId, chatUserName) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like this function is being called on every new message, and these webhooks are being created on every new message. After three or four messages in the same conversation, this results in:
so we probably need to have a different mode that just creates the webhooks once. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. Nice catch! |
||
return client.flexApi.channel | ||
.create({ | ||
flexFlowSid: flexFlowSid, | ||
identity: recipientId, | ||
chatUserFriendlyName: chatUserName, | ||
chatFriendlyName: 'Flex Custom Chat', | ||
target: chatUserName | ||
}) | ||
.then(channel => { | ||
console.log(`Created new channel ${channel.sid}`); | ||
return client.chat | ||
.services(flexChatService) | ||
.channels(channel.sid) | ||
.webhooks.create({ | ||
type: 'webhook', | ||
'configuration.method': 'POST', | ||
'configuration.url': `<Twilo Flex function URL>?channel=${channel.sid}&recipient_id=${recipientId}`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's say we have two Twitter handles... a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't remember that happening in the demo, it is definitely super important to test this piece of code rigorously. |
||
'configuration.filters': ['onMessageSent'] | ||
}) | ||
.then(() => client.chat | ||
.services(flexChatService) | ||
.channels(channel.sid) | ||
.webhooks.create({ | ||
type: 'webhook', | ||
'configuration.method': 'POST', | ||
'configuration.url': `<Twilo Flex function URL>/channel-update`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't work out if this webhook is actually needed. As it is, I don't think this does anything, since there's no function for this. Seems like this may be a hold-over from the original There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps this is related to the issue above where we are creating multiple webhooks. Instead, we should be updating the channel. |
||
'configuration.filters': ['onChannelUpdated'] | ||
})) | ||
}) | ||
.then(webhook => webhook.channelSid) | ||
.catch(error => { | ||
console.log(error); | ||
}); | ||
} | ||
|
||
async function resetChannel(status) { | ||
if (status == 'INACTIVE') { | ||
flexChannelCreated = false; | ||
} | ||
} | ||
|
||
async function sendMessageToFlex(msg, user, screen_name, sender_id) { | ||
flexChannelCreated = await createNewChannel( | ||
process.env.FLEX_FLOW_SID, | ||
process.env.FLEX_CHAT_SERVICE, | ||
sender_id, | ||
user | ||
); | ||
console.log(flexChannelCreated) | ||
console.log('DM from: ' + screen_name) | ||
sendChatMessage( | ||
process.env.FLEX_CHAT_SERVICE, | ||
flexChannelCreated, | ||
user, | ||
sender_id, | ||
msg | ||
); | ||
} | ||
|
||
exports.sendMessageToFlex = sendMessageToFlex; | ||
exports.resetChannel = resetChannel; | ||
``` | ||
|
||
Replace `<Twilo Flex function URL>` with the URL obtained from your Twilio Function. | ||
|
||
Launch the middleware in the directory `<path-to>/twilio-flex-custom-webchat/middleware` with the command `node server.js`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This actually needs several env variables to be passed for it to work: And if you don't have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another nice catch! We should probably provide sample env files. |
||
|
||
### Try it out! | ||
|
||
[Login](https://flex.twilio.com) to your Flex instance and set your agent's status to be `Available`. | ||
|
||
DM the Twitter account associated with the Twitter developer account and [navigate to your agent desktop](https://flex.twilio.com/agent-desktop). | ||
|
||
Accept the message and respond. | ||
|
||
Celebrate! | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Things yet to be figured out:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure about point one, but I believe point two is true. I think that would make for a nice second iteration :) |
||
#### References | ||
* https://www.twilio.com/blog/add-custom-chat-channel-twilio-flex | ||
* https://github.com/vernig/twilio-flex-custom-webchat | ||
* https://github.com/twitterdev/autohook | ||
* https://github.com/ttezel/twit | ||
|
||
#### Contributors | ||
* Nick Hurlburt | ||
* Ashkon Honardoost | ||
* Deepak Srikanth | ||
* Elmer Thomas | ||
* Toby Allen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: what's really needed later is to add
twitter-autohook
to yourpackage.json
in themiddleware
project further down.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we simply specify that you will need to run
npm install twitter-autohook
?