Skip to content
This repository has been archived by the owner on Jan 19, 2020. It is now read-only.

Commit

Permalink
Tidying it all up.
Browse files Browse the repository at this point in the history
  • Loading branch information
James Campbell committed Feb 22, 2017
1 parent 7c836d9 commit 6446004
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 244 deletions.
81 changes: 27 additions & 54 deletions lib/clients/base-client.js
Expand Up @@ -19,6 +19,27 @@ class BaseClient {
return client.bind(this);
}

/**
* This is called when the client is about to start
*/
__clientWillStart(bot, config) {

var defaults = {}
var config = Object.assign({}, defaults, config);

assert(config.namespace, "Namespace not provided")

this.bot = bot
this.router = bot.createEndpointNamespace(config.namespace)

// Move into Message broker
this.queue = queue();
this.queue.concurrency = 1;

this.__registerWebhook()
this.__registerEventEndpoint()
}

/**
* sendMessage() falls back to the sendMessage implementation of whatever
* subclass inherits form BaseBot. The expected format is normally any type of
Expand All @@ -40,48 +61,20 @@ class BaseClient {
*
*/
sendMessage(message, cb) {

// TODO: Implement conversation management
// this.queue.push((cb) => {
// session.startTyping();
//
// let typingTime = 0;
//
// if (text) {
// const averageWordsPerMinute = 600;
// const averageWordsPerSecond = averageWordsPerMinute /
// 60;
// const averageWordsPerMillisecond =
// averageWordsPerSecond / 1000;
// const totalWords = text.split(' ').length;
// typingTime = totalWords /
// averageWordsPerMillisecond;
// }
//
// setTimeout(() => {
// session.client.send(session, text,
// attachment);
// cb();
// }, typingTime);
// });
//
// this.queue.start();

return this.__sendMessage.bind(this)
.then((body) => {
if (cb) {
return cb(null, body);
}
return body;
})
.catch((err) => {
if (cb) {
return cb(err);
}

.catch((err) => {
if (cb) {
return cb(err);
}

throw err;
});
throw err;
});
}

/**
Expand Down Expand Up @@ -223,26 +216,6 @@ class BaseClient {
this.bot.memory.users[this.user] = Object.assign(context, newValues);
}

/**
*
*/
__clientWillStart(bot, config) {

var defaults = {}
var config = Object.assign({}, defaults, config);

assert(config.namespace, "Namespace not provided")

this.bot = bot
this.router = bot.createEndpointNamespace(config.namespace)

this.queue = queue();
this.queue.concurrency = 1;

this.__registerWebhook()
this.__registerEventEndpoint()
}

/**
* Internal method for registering webhook endpoint
*/
Expand Down
66 changes: 32 additions & 34 deletions lib/clients/facebook-messenger-client.js
Expand Up @@ -11,6 +11,38 @@ Array.prototype.flatMap = function(lambda) {
//TODO: Extract into modules ?
class FacebookMessengerClient extends BaseClient {

__clientWillStart(bot, config) {

const defaults = {
namespace: "facebook",
verify_token: process.env.MESSENGER_VERIFY_TOKEN,
access_token: process.env.MESSENGER_ACCESS_TOKEN,
graph_uri: 'https://graph.facebook.com/v2.7',
};

var config = Object.assign({}, defaults, config);

super.__clientWillStart(bot, config)

assert(config.verify_token, "Messenger verify token not provided")
assert(config.access_token, "Messenger access token not provided")

this.access_token = config.access_token
this.verify_token = config.verify_token
}

__handleWebhookRequest(req, res, buf) {
this.__verifyRequestSignature(req, res, buf)

const query = req.query;

if (query['hub.mode'] === 'subscribe') {
this.__handleSubscription(req, res);
} else {
this.__handleEvent(req, res);
}
}

send(session, text, attachment) {
console.log(`Sending "${text}"`);

Expand Down Expand Up @@ -70,38 +102,6 @@ class FacebookMessengerClient extends BaseClient {
});
}

__clientWillStart(bot, config) {

const defaults = {
namespace: "facebook",
verify_token: process.env.MESSENGER_VERIFY_TOKEN,
access_token: process.env.MESSENGER_ACCESS_TOKEN,
graph_uri: 'https://graph.facebook.com/v2.7',
};

var config = Object.assign({}, defaults, config);

super.__clientWillStart(bot, config)

assert(config.verify_token, "Messenger verify token not provided")
assert(config.access_token, "Messenger access token not provided")

this.access_token = config.access_token
this.verify_token = config.verify_token
}

__handleWebhookRequest(req, res, buf) {
this.__verifyRequestSignature(req, res, buf)

const query = req.query;

if (query['hub.mode'] === 'subscribe') {
this.__handleSubscription(req, res);
} else {
this.__handleEvent(req, res);
}
}

/*
* Verify that the callback came from Facebook. Using the App Secret from
* the App Dashboard, we can verify the signature that is sent with each
Expand Down Expand Up @@ -178,8 +178,6 @@ class FacebookMessengerClient extends BaseClient {
}

__sendMessage(message) {
// TODO add request spliting when text is over 320 characters long.
// log warning too.
const options = {
uri: `${this.config.graph_uri}/me/messages`,
qs: {
Expand Down
152 changes: 76 additions & 76 deletions lib/clients/slack_bot.js → lib/clients/slack-client.js
@@ -1,105 +1,105 @@
'use strict';

const express = require('express');
const bodyParser = require('body-parser');
const BaseClient = require('./base-client');
const request = require('request-promise');
const JsonFileStore = require('jfs');
const BaseBot = require('./base_bot');

const webAPIURL = 'https://slack.com/api/';
class SlackClient extends BaseClient {

class SlackBot extends BaseBot {
__clientWillStart(bot, config) {

constructor(settings) {
super(settings);
this.requiresWebhook = true;
this.requiredCredentials = ['clientId',
'clientSecret',
'verificationToken'
];
const defaults = {
namespace: "slack",
client_id: process.env.SLACK_CLIENT_ID,
client_secret: process.env.SLACK_CLIENT_SECRET,
verification_token: process.env.SLACK_CLIENT_VERIFICATION_TOKEN,
api_endpoint: 'https://slack.com/api/'
};

this.__applySettings(settings);
var config = Object.assign({}, defaults, config);

this.__createMountPoints();
// this is the id that will be set after the first message is sent by
// this bot. It will be this bot's user value as defined by Slack,
// and not its bot_id value which is a different thing.
// See the difference between app/bots and bot_users in Slack.
this.id = null;

super.__clientWillStart(bot, config)

assert(config.client_id, "Slack client id not provided")
assert(config.client_secret, "Messenger access token not provided")
assert(config.verification_token, "Messenger access token not provided")

this.client_id = config.client_id
this.client_secret = config.client_secret
this.verification_token = config.verification_token
this.api_endpoint = config.api_endpoint
}

__handleWebhookRequest(req, res, buf) {

// just verify origin of any post requests using verificationToken
this.__verifyRequestOrigin(req, res, buf)

// make sure Event's webhookEndpoint (Request URL) is valid
this.__respondToVerificationHandshake(req, res, buf)

// get code from Slack Button request and make oauth request
this.__authorizeApplicationForTeam(req, res, buf)

// finally, if it's a valid POST Events request, deal with it
this.__emitUpdateFromEvent(req.body);

// just letting Slack know we got the update
res.sendStatus(200);
}

__applySettings(settings) {
super.__applySettings(settings);
super.__applySettings(settings);

if (settings.landingPageURL) {
this.landingPageURL = settings.landingPageURL;
}
if (settings.landingPageURL) {
this.landingPageURL = settings.landingPageURL;
}

if ((!settings.storeTeamInfoInFile && !settings.storeTeamInfoHooks) ||
(settings.storeTeamInfoInFile && settings.storeTeamInfoHooks)) {
throw new Error(`ERROR: bots of type '${this.type}' must be defined with exactly one of storeTeamInfoInFile set to true or storeTeamInfoHooks defined`);
}
if ((!settings.storeTeamInfoInFile && !settings.storeTeamInfoHooks) ||
(settings.storeTeamInfoInFile && settings.storeTeamInfoHooks)) {
throw new Error(`ERROR: bots of type '${this.type}' must be defined with exactly one of storeTeamInfoInFile set to true or storeTeamInfoHooks defined`);
}

if (settings.storeTeamInfoInFile) {
this.storeTeamInfoInFile = settings.storeTeamInfoInFile;
} else {
this.storeTeamInfoHooks = settings.storeTeamInfoHooks;
}
if (settings.storeTeamInfoInFile) {
this.storeTeamInfoInFile = settings.storeTeamInfoInFile;
} else {
this.storeTeamInfoHooks = settings.storeTeamInfoHooks;
}

if (this.storeTeamInfoInFile) {
this.jsonFileStoreLocation = 'slack_teams_info';
const jsonFileStoreDB = new JsonFileStore(this.jsonFileStoreLocation);

this.storeTeamInfoHooks = {};
this.storeTeamInfoHooks.storeTeamInfo = function storeTeamInfo(bot, teamInfo) {
return new Promise((resolve, reject) => {
jsonFileStoreDB.save(teamInfo.team_id, teamInfo, (err) => {
if (err) {
return reject(err);
}
return resolve(teamInfo);
});
if (this.storeTeamInfoInFile) {
this.jsonFileStoreLocation = 'slack_teams_info';
const jsonFileStoreDB = new JsonFileStore(this.jsonFileStoreLocation);

this.storeTeamInfoHooks = {};
this.storeTeamInfoHooks.storeTeamInfo = function storeTeamInfo(bot, teamInfo) {
return new Promise((resolve, reject) => {
jsonFileStoreDB.save(teamInfo.team_id, teamInfo, (err) => {
if (err) {
return reject(err);
}
return resolve(teamInfo);
});
};
});
};

this.storeTeamInfoHooks.getTeamInfo = function getTeamInfo(bot, teamId) {
return new Promise((resolve, reject) => {
jsonFileStoreDB.get(teamId, (err, teamInfo) => {
if (err) {
return reject(`An error occurred trying to get info for: ${teamId}`);
}
this.storeTeamInfoHooks.getTeamInfo = function getTeamInfo(bot, teamId) {
return new Promise((resolve, reject) => {
jsonFileStoreDB.get(teamId, (err, teamInfo) => {
if (err) {
return reject(`An error occurred trying to get info for: ${teamId}`);
}

return resolve(teamInfo);
});
return resolve(teamInfo);
});
};
}
});
};
}
/**
* sets up the app.
* Adds an express Router to the mount point "/slack".
* sub Router contains code for posting to wehook.
*/
__createMountPoints() {
this.app = express();
// for parsing application/json
this.app.use(bodyParser.json());
// for parsing application/x-www-form-urlencoded
this.app.use(bodyParser.urlencoded({ extended: true }));
// just verify origin of any post requests using verificationToken
this.app.post(this.webhookEndpoint, this.__verifyRequestOrigin.bind(this));
// make sure Event's webhookEndpoint (Request URL) is valid
this.app.post(this.webhookEndpoint,
this.__respondToVerificationHandshake.bind(this));
// get code from Slack Button request and make oauth request
this.app.get(this.webhookEndpoint,
this.__authorizeApplicationForTeam.bind(this));
// finally, if it's a valid POST Events request, deal with it
this.app.post(this.webhookEndpoint, (req, res) => {
this.__emitUpdateFromEvent(req.body);
// just letting Slack know we got the update
res.sendStatus(200);
});
}

__verifyRequestOrigin(req, res, next) {
Expand Down

0 comments on commit 6446004

Please sign in to comment.