Skip to content
Permalink
Browse files

added multiple facebook access tokens support

refactoring settings module
added pageId param for all Facebook module methods
added customer matching support via facebook reply method
  • Loading branch information...
webus committed Dec 19, 2017
1 parent debf6ce commit d45235ac7cf9a49b99f5aed6c1ee11626bc7d4eb
Showing with 200 additions and 26 deletions.
  1. +3 βˆ’0 .npmignore
  2. +7 βˆ’1 conf/conf.json
  3. +10 βˆ’0 src/common/constants.js
  4. +70 βˆ’0 src/common/multimap.js
  5. +3 βˆ’2 src/environments.js
  6. +2 βˆ’1 src/fb/persistent_menu.js
  7. +16 βˆ’3 src/fb/reply.js
  8. +5 βˆ’3 src/fb/settings.js
  9. +5 βˆ’3 src/fb/typing.js
  10. +5 βˆ’3 src/fb/upload.js
  11. +7 βˆ’0 src/multiconf/index.js
  12. +45 βˆ’0 src/multiconf/mem.js
  13. +7 βˆ’1 src/settings.js
  14. +12 βˆ’0 tests/test_fb_reply.js
  15. +3 βˆ’6 tests/test_fb_settings.js
  16. +0 βˆ’2 tests/test_fb_typing.js
  17. +0 βˆ’1 tests/test_fb_upload.js
@@ -0,0 +1,3 @@
.git
.editorconfig

@@ -3,5 +3,11 @@
"persistentMenu": [
{"type": "postback", "title": "Help", "payload": "Help"}
],
"welcomeText": "Change this welcome text in conf/conf.json file"
"welcomeText": "Change this welcome text in conf/conf.json file",
"env": {
"FB_PAGE_ACCESS_TOKEN": [
{ "PAGE_ID": "12", "FB_PAGE_ACCESS_TOKEN": "567", "is_default": "true" },
{ "PAGE_ID": "15", "FB_PAGE_ACCESS_TOKEN": "890" }
]
}
}
@@ -0,0 +1,10 @@
const FACEBOOK_API_VERSION = '2.9';
const FACEBOOK_GRAPH_URL = `https://graph.facebook.com/v${FACEBOOK_API_VERSION}`;

function getFacebookGraphURL(endpoint) {
return `${FACEBOOK_GRAPH_URL}${endpoint}`;
}

module.exports = {
getFacebookGraphURL
};
@@ -0,0 +1,70 @@
const Promise = require('bluebird');
const _ = require('lodash');

const multiMap = new Map();

const set = (key, value = {}) => {
const oldValue = multiMap.get(key);
if (oldValue) {
const newValue = _.cloneDeep(_.merge(oldValue, value));
multiMap.set(key, newValue);
} else {
multiMap.set(key, _.cloneDeep(value));
}
};

const setAsync = (key, value = {}) => new Promise((resolve, reject) => {
set(key, value);
resolve();
});

const get = (key) => {
const item = multiMap.get(key);
if (!item) {
return null;
} else {
return item;
}
};

const getAsync = key => new Promise((resolve, reject) => {
resolve(get(key));
});

const del = key => {
return multiMap.delete(key);
};

const delAsync = key => new Promise((resolve, reject) => {
resolve(del(key));
});

const delByInternalKey = (key, internalKey) => {
const oldValue = multiMap.get(key);
if (!oldValue) {
reject("Internal key doesn't exists");
throw new Error("Internal key doesn't exists");
}
delete oldValue[internalKey];
multiMap.set(key, oldValue);
return true;
};

const delByInternalKeyAsync = (key, internalKey) => new Promise((resolve, reject) => {
try {
resolve(delByInternalKey(key, internalKey));
} catch (err) {
reject(err);
}
});

module.exports = {
set,
setAsync,
get,
getAsync,
del,
delAsync,
delByInternalKey,
delByInternalKeyAsync
};
@@ -1,5 +1,6 @@
const lodash = require('lodash');
const restify = require('restify');
const env = require('./multiconf')();

const envVars = [
{ name: 'FB_PAGE_ACCESS_TOKEN', required: true, module: ['fb'] },
@@ -33,7 +34,7 @@ function processEnvironmentVariables(self) {
function checkEnvironmentVariables() {
let enabledModules = [];
for (const envVar of envVars) {
if (envVar.name in process.env) {
if (env.getEnv(envVar.name)) {
enabledModules = lodash.union(enabledModules, envVar.module);
}
}
@@ -52,7 +53,7 @@ function checkEnvironmentVariables() {

const notFoundEnvs = [];
for (const e of checkVars) {
if (!(e in process.env)) {
if (!env.getEnv(e)) {
notFoundEnvs.push(e);
}
}
@@ -1,12 +1,13 @@
const rp = require('request-promise');
const lodash = require('lodash');
const log = require('../log');
const env = require('../multiconf')();

async function setMessengerProfileData(data) {
const reqData = {
url: 'https://graph.facebook.com/v2.10/me/messenger_profile',
qs: {
access_token: process.env.FB_PAGE_ACCESS_TOKEN
access_token: env.getEnvDefault('FB_PAGE_ACCESS_TOKEN')
},
resolveWithFullResponse: true,
method: 'POST',
@@ -1,8 +1,13 @@
const _ = require('lodash');
const rp = require('request-promise');
const log = require('../log');
const env = require('../multiconf')();
const constants = require('../common/constants');

async function reply(message, senderId, {messagingType='RESPONSE', params=null}={}) {
async function reply(message, senderId, {
messagingType='RESPONSE',
params=null,
pageId=null }={}) {
const logData = {};
const sendData = {
messaging_type: messagingType,
@@ -17,6 +22,14 @@ async function reply(message, senderId, {messagingType='RESPONSE', params=null}=
sendData['recipient'] = {
user_ref: senderId
};
} else if (_.get(params, 'is_customer_matching') === true) {
sendData['recipient'] = {
phone_number: _.get(params, 'customer_matching.phone_number'),
name: {
first_name: _.get(params, 'customer_matching.first_name'),
last_name: _.get(params, 'customer_matching.last_name')
}
};
} else {
logData['senderId'] = senderId;
sendData['recipient'] = {
@@ -32,9 +45,9 @@ async function reply(message, senderId, {messagingType='RESPONSE', params=null}=
}

const reqData = {
url: 'https://graph.facebook.com/v2.9/me/messages',
url: constants.getFacebookGraphURL('/me/messages'),
qs: {
access_token: process.env.FB_PAGE_ACCESS_TOKEN
access_token: env.getFacebookPageTokenByPageID(pageId)
},
method: 'POST',
json: sendData,
@@ -1,11 +1,13 @@
const rp = require('request-promise');
const log = require('../log');
const constants = require('../common/constants');
const env = require('../multiconf')();

async function setThreadSettings(data, method = 'POST') {
async function setThreadSettings(data, method = 'POST', { pageId=null}={}) {
const reqData = {
url: 'https://graph.facebook.com/v2.9/me/thread_settings',
url: constants.getFacebookGraphURL('/me/thread_settings'),
qs: {
access_token: process.env.FB_PAGE_ACCESS_TOKEN
access_token: env.getFacebookPageTokenByPageID(pageId)
},
resolveWithFullResponse: true,
method,
@@ -1,7 +1,9 @@
const log = require('../log');
const rp = require('request-promise');
const constants = require('../common/constants');
const env = require('../multiconf')();

async function typing(userID, isOn = false) {
async function typing(userID, isOn = false, { pageId=null }={}) {
// mark_seen - Mark last message as read
// typing_on - turn typing indicators on
// typing_off - turn typing indicators off
@@ -15,9 +17,9 @@ async function typing(userID, isOn = false) {
sender_action: action
};
const reqData = {
url: 'https://graph.facebook.com/v2.9/me/messages',
url: constants.getFacebookGraphURL('/me/messages'),
qs: {
access_token: process.env.FB_PAGE_ACCESS_TOKEN
access_token: env.getFacebookPageTokenByPageID(pageId)
},
resolveWithFullResponse: true,
method: 'POST',
@@ -1,7 +1,9 @@
const rp = require('request-promise');
const log = require('../log');
const env = require('../multiconf')();
const constants = require('../common/constants');

async function attachmentUpload(attachmentURL, attachmentType = 'video') {
async function attachmentUpload(attachmentURL, attachmentType = 'video', { pageId=null }={}) {
const msg = {
message: {
attachment: {
@@ -15,9 +17,9 @@ async function attachmentUpload(attachmentURL, attachmentType = 'video') {
};

const reqData = {
url: 'https://graph.facebook.com/v2.9/me/message_attachments',
url: constants.getFacebookGraphURL('/me/message_attachments'),
qs: {
access_token: process.env.FB_PAGE_ACCESS_TOKEN
access_token: env.getFacebookPageTokenByPageID(pageId)
},
resolveWithFullResponse: true,
method: 'POST',
@@ -0,0 +1,7 @@
module.exports = () => {
if (process.env.REDIS_URL) {
throw new Error('Not implemented');
} else {
return require('./mem');
}
};
@@ -0,0 +1,45 @@
const _ = require('lodash');
const multimap = require('../common/multimap');
const settings = require('../settings');

function getConfigEnv(envName) {
const config = settings.checkAndLoadConfig();
return _.get(config, `env.${envName}`);
}

function getEnv(envName) {
return getConfigEnv(envName) || _.get(process.env, envName, null);
}

function getEnvDefault(envName) {
const envVal = getEnv(envName);
if (_.isArray(envVal)) {
const searchResult = _.find(envVal, (obj) => { return _.get(obj, 'is_default') === 'true'; });
return _.get(searchResult, envName);
} else {
return envVal;
}
}

function getFacebookPageTokenByPageID(pageID) {
const envVal = getEnv('FB_PAGE_ACCESS_TOKEN');
if (_.isArray(envVal)) {
let searchResult = null;
if (pageID) {
searchResult = _.find(envVal, (obj) => { return _.get(obj, 'PAGE_ID') === pageID; });
}
let result = _.get(searchResult, 'FB_PAGE_ACCESS_TOKEN', null);
if (!result) {
result = getEnvDefault('FB_PAGE_ACCESS_TOKEN');
}
return result;
} else {
return envVal;
}
}

module.exports = {
getEnv,
getEnvDefault,
getFacebookPageTokenByPageID
};
@@ -3,7 +3,12 @@ const path = require('path');
const lodash = require('lodash');

function checkAndLoadConfig() {
const basePath = path.dirname(require.main.filename);
let basePath = null;
if (lodash.has(require.main, 'filename')) {
basePath = path.dirname(require.main.filename);
} else {
basePath = process.cwd();
}
const configPath = path.join(basePath, 'conf/conf.json');
if (fs.existsSync(configPath)) {
return require(configPath); // eslint-disable-line global-require,import/no-dynamic-require
@@ -65,5 +70,6 @@ async function parseConfig(self) {
}

module.exports = {
checkAndLoadConfig,
parseConfig
};
@@ -21,6 +21,17 @@ describe('Testing FB reply', () => {
return null;
});

rewiremock('../multiconf').with(() => {
return {
getEnv: (envName) => {
return "123";
},
getFacebookPageTokenByPageID: (envName) => {
return "123";
}
};
});

rewiremock.enable();
rewiremock.isolation();

@@ -37,5 +48,6 @@ describe('Testing FB reply', () => {
assert.equal(lodash.get(rpData, 'json.message.text'), 'hello');
assert.isOk(lodash.has(rpData, 'qs.access_token'));
assert.isOk(lodash.has(rpData, 'url'));
assert.equal(lodash.get(rpData, 'qs.access_token'), '123');
});
});
@@ -22,15 +22,16 @@ describe('Testing FB settings', () => {
});

rewiremock.enable();
rewiremock.isolation();

const fbSet = require(rewiremock.resolve('../src/fb/settings'));
await fbSet.setThreadSettings({});

rewiremock.disable();
rewiremock.clear();

assert.equal(lodash.get(rpData, 'url'), 'https://graph.facebook.com/v2.9/me/thread_settings');
const constants = require('../src/common/constants');

assert.equal(lodash.get(rpData, 'url'), constants.getFacebookGraphURL('/me/thread_settings'));
assert.equal(lodash.get(rpData, 'method'), 'POST');
});

@@ -43,7 +44,6 @@ describe('Testing FB settings', () => {
});

rewiremock.enable();
rewiremock.isolation();

const fbSet = require(rewiremock.resolve('../src/fb/settings'));
await fbSet.greetingText('hello');
@@ -66,7 +66,6 @@ describe('Testing FB settings', () => {
});

rewiremock.enable();
rewiremock.isolation();

const fbSet = require(rewiremock.resolve('../src/fb/settings'));
await fbSet.getStartedButton('hello');
@@ -90,7 +89,6 @@ describe('Testing FB settings', () => {
});

rewiremock.enable();
rewiremock.isolation();

const fbSet = require(rewiremock.resolve('../src/fb/settings'));
await fbSet.persistentMenu([
@@ -120,7 +118,6 @@ describe('Testing FB settings', () => {
});

rewiremock.enable();
rewiremock.isolation();

const fbSet = require(rewiremock.resolve('../src/fb/settings'));
await fbSet.deletePersistentMenu();
Oops, something went wrong.

0 comments on commit d45235a

Please sign in to comment.
You can’t perform that action at this time.