cx/twitter/server.js (229 lines of code) (raw):
/**
* TODO(developer):
* Add your service key to the current folder.
* Uncomment and fill in these variables.
*/
// const projectId = 'my-project';
// const locationId = 'global';
// const agentId = 'my-agent';
// const languageCode = 'en'
// const twitterAPIKey = "Place your consumer key here";
// const twitterSecretAPIKey = "Place you secret consumer key here";
// const twitterAccessToken = "Place your access token here";
// const twitterSecretAccessToken = "Place your secret access token here";
// const environmentName = "Place your twitter environment name here";
// const targetUrl = "Place your server's url here";
const express = require('express');
const request = require('request');
const app = express();
const {SessionsClient} = require('@google-cloud/dialogflow-cx');
const crypto = require('crypto');
const twitterText = require('twitter-text');
app.use(express.json());
const twitterOAuth = {
consumer_key: twitterAPIKey,
consumer_secret: twitterSecretAPIKey,
token: twitterAccessToken,
token_secret: twitterSecretAccessToken
};
const twitterId =
twitterAccessToken.substring(0, twitterAccessToken.indexOf('-'));
const client = new SessionsClient(
{apiEndpoint: locationId + '-dialogflow.googleapis.com'});
const listener = app.listen(process.env.PORT, function() {
console.log(
'Your Twitter integration server is listening on port ' +
listener.address().port);
init();
});
process.on('SIGTERM', () => {
listener.close(async () => {
deleteSubscription();
await deleteWebhooksByUrl(targetUrl);
console.log('Closing http server.');
process.exit(0);
});
});
// Converts Twitter request to a detectIntent request.
function twitterToDetectIntent(message, sessionPath) {
const request = {
session: sessionPath,
queryInput: {
text: {
text: message,
},
languageCode,
}
};
return request;
}
/**
* Takes as input a request from twitter and converts the request to
* detectIntent request which is used to call the detectIntent() function
* and finally output the response given by detectIntent().
*/
async function detectIntentResponse(message, userId) {
const sessionId = userId;
const sessionPath = client.projectLocationAgentSessionPath(
projectId, locationId, agentId, sessionId);
console.info(sessionPath);
var request = twitterToDetectIntent(message, sessionPath);
var [response] = await client.detectIntent(request);
console.log(JSON.stringify(response))
return response;
};
function sendMessage(text, recipientId) {
request.post(
'https://api.twitter.com/1.1/direct_messages/events/new.json', {
oauth: twitterOAuth,
json: {
event: {
type: 'message_create',
message_create: {
target: {recipient_id: recipientId},
message_data: {
text: text,
}
}
}
}
},
function(err, resp, body) {
if (err) {
console.error('Failed to send message: ' + err);
}
});
}
function sendStatus(text, tweetId) {
// Tweets must be less than 280 characters. See documentation below.
// https://developer.twitter.com/en/docs/developer-utilities/twitter-text.html
const parsedTweet = twitterText.parseTweet(text);
if (parsedTweet.valid) {
request.post(
'https://api.twitter.com/1.1/statuses/update.json', {
oauth: twitterOAuth,
form: {status: text, in_reply_to_status_id: tweetId},
},
function(err, resp, body) {
if (err) {
console.error(err);
}
});
} else {
console.error(
'Failed to send status because it is longer than 280 characters: ' +
err);
}
}
async function init() {
deleteSubscription();
await deleteWebhooksByUrl(targetUrl);
await registerWebhook();
registerSubscription();
}
function registerWebhook() {
return new Promise((resolve, reject) => {
request.post(
'https://api.twitter.com/1.1/account_activity/all/' + environmentName +
'/webhooks.json',
{oauth: twitterOAuth, form: {url: targetUrl}},
function(err, resp, body) {
if (err) {
console.error('Failed to register webhook: ' + err);
reject();
} else {
resolve();
}
});
});
}
function registerSubscription() {
request.post(
'https://api.twitter.com/1.1/account_activity/all/' + environmentName +
'/subscriptions.json',
{
oauth: twitterOAuth,
},
function(err, resp, body) {
if (err) {
console.error('Failed to register subscription: ' + err);
}
});
}
function listWebhooks(url) {
return new Promise((resolve, reject) => {
request.get(
'https://api.twitter.com/1.1/account_activity/all/webhooks.json',
{oauth: twitterOAuth}, function(err, resp, body) {
if (err) {
console.error('Failed to check webhooks: ' + err);
reject(err);
}
const environments = JSON.parse(body).environments;
if (Array.isArray(environments)) {
const environment = environments.find((element) => {
return element.environment_name === environmentName;
});
if (environment) {
const webhooks =
environment.webhooks.filter((value, index, arr) => {
return value.url === url;
});
resolve(webhooks);
}
resolve([]);
}
});
});
}
function deleteWebhookById(webhookId) {
return new Promise((resolve, reject) =>{
request.delete(
'https://api.twitter.com/1.1/account_activity/all/' + environmentName +
'/webhooks/' + webhookId + '.json',
{oauth: twitterOAuth}, function(err, resp, body) {
if (err) {
console.error('Failed to delete webhook: ' + err);
reject(err);
}
resolve();
});
});
}
async function deleteWebhooksByUrl(targetUrl) {
const webhooks = await listWebhooks(targetUrl);
for (webhook of webhooks) {
if (webhook.id) {
await deleteWebhookById(webhook.id);
}
}
}
function deleteSubscription() {
request.delete(
'https://api.twitter.com/1.1/account_activity/all/' + environmentName +
'/subscriptions.json',
{oauth: twitterOAuth}, function(err, resp, body) {
if (err) {
console.error('Failed to delete subscriptions: ' + err);
}
});
}
app.post('/', async function(req, res) {
if (Array.isArray(req.body.direct_message_events) &&
req.body.direct_message_events.length > 0) {
const message = req.body.direct_message_events[0];
const text = message.message_create.message_data.text;
const senderId = message.message_create.sender_id;
if (senderId !== twitterId) {
var responses = await detectIntentResponse(text, senderId);
for (let response of responses.queryResult.responseMessages) {
if (response.hasOwnProperty('text')) {
sendMessage(response.text.text.join(), senderId);
};
};
}
} else if (
Array.isArray(req.body.tweet_create_events) &&
req.body.tweet_create_events.length > 0) {
const message = req.body.tweet_create_events[0];
const text = message.text;
const senderId = message.user.id_str;
if (senderId !== twitterId && message.in_reply_to_user_id_str) {
var responses = await detectIntentResponse(
text.replace('@' + message.in_reply_to_screen_name, ''), senderId);
const screenName = message.user.screen_name;
for (let response of responses.queryResult.responseMessages) {
if (response.hasOwnProperty('text')) {
sendStatus(
'@' + screenName + ' ' + response.text.text.join(), senderId);
};
};
}
}
res.sendStatus(200);
});
// responds to challenge response check verification requests
app.get('/', function(req, res) {
const crc = req.query.crc_token;
const hmac = 'sha256=' +
crypto.createHmac('sha256', twitterSecretAPIKey)
.update(crc)
.digest('base64');
res.json({'response_token': hmac});
});