From c6805ed7fcab76e09288bcb21abb360a87aa03cd Mon Sep 17 00:00:00 2001 From: nati Date: Thu, 21 Jun 2018 11:05:10 +0300 Subject: [PATCH 1/2] added support for multi page bots --- lib/BootBot.js | 173 +++++++++++++++++++++++++------------------ lib/Chat.js | 30 ++++---- lib/Conversation.js | 7 +- test/BootBot.spec.js | 11 +-- 4 files changed, 127 insertions(+), 94 deletions(-) diff --git a/lib/BootBot.js b/lib/BootBot.js index 62bf2bb..9ce06dc 100644 --- a/lib/BootBot.js +++ b/lib/BootBot.js @@ -82,14 +82,15 @@ class BootBot extends EventEmitter { * @param {String} text * @param {Array.} quickReplies Array of strings or quick_reply objects * @param {SendMessageOptions} [options] + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - sendTextMessage(recipientId, text, quickReplies, options) { + sendTextMessage(recipientId, text, quickReplies, options, pageId) { const message = { text }; const formattedQuickReplies = this._formatQuickReplies(quickReplies); if (formattedQuickReplies && formattedQuickReplies.length > 0) { message.quick_replies = formattedQuickReplies; } - return this.sendMessage(recipientId, message, options); + return this.sendMessage(recipientId, message, options, pageId); } /** @@ -103,15 +104,16 @@ class BootBot extends EventEmitter { * @param {String} text Message to be sent. * @param {Array.} buttons * @param {SendMessageOptions} [options] + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - sendButtonTemplate(recipientId, text, buttons, options) { + sendButtonTemplate(recipientId, text, buttons, options, pageId) { const payload = { template_type: 'button', text }; const formattedButtons = this._formatButtons(buttons); payload.buttons = formattedButtons; - return this.sendTemplate(recipientId, payload, options); + return this.sendTemplate(recipientId, payload, options, pageId); } /** @@ -127,14 +129,15 @@ class BootBot extends EventEmitter { * @param {Recipient|String} recipientId * @param {Array.} elements * @param {SendMessageOptions} [options] + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - sendGenericTemplate(recipientId, elements, options) { + sendGenericTemplate(recipientId, elements, options, pageId) { const payload = { template_type: 'generic', elements }; options && options.imageAspectRatio && (payload.image_aspect_ratio = options.imageAspectRatio) && (delete options.imageAspectRatio); - return this.sendTemplate(recipientId, payload, options); + return this.sendTemplate(recipientId, payload, options, pageId); } /** @@ -143,15 +146,16 @@ class BootBot extends EventEmitter { * @param {Array.} buttons An array with one element: string or button object. * @param {SendMessageOptions} [options] * @param {String} [options.topElementStyle] + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - sendListTemplate(recipientId, elements, buttons, options) { + sendListTemplate(recipientId, elements, buttons, options, pageId) { const payload = { template_type: 'list', elements }; options && options.topElementStyle && (payload.top_element_style = options.topElementStyle) && (delete options.topElementStyle); buttons && buttons.length && (payload.buttons = this._formatButtons([buttons[0]])); - return this.sendTemplate(recipientId, payload, options); + return this.sendTemplate(recipientId, payload, options, pageId); } /** @@ -159,15 +163,16 @@ class BootBot extends EventEmitter { * @param {Recipient|String} recipientId * @param {Object} payload The template payload. * @param {SendMessageOptions} [options] + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - sendTemplate(recipientId, payload, options) { + sendTemplate(recipientId, payload, options, pageId) { const message = { attachment: { type: 'template', payload } }; - return this.sendMessage(recipientId, message, options); + return this.sendMessage(recipientId, message, options, pageId); } /** @@ -176,8 +181,9 @@ class BootBot extends EventEmitter { * @param {String} url URL of the attachment. * @param {Array.} quickReplies * @param {SendMessageOptions} options + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - sendAttachment(recipientId, type, url, quickReplies, options) { + sendAttachment(recipientId, type, url, quickReplies, options, pageId) { const message = { attachment: { type, @@ -188,7 +194,7 @@ class BootBot extends EventEmitter { if (formattedQuickReplies && formattedQuickReplies.length > 0) { message.quick_replies = formattedQuickReplies; } - return this.sendMessage(recipientId, message, options); + return this.sendMessage(recipientId, message, options, pageId); } /** @@ -197,13 +203,14 @@ class BootBot extends EventEmitter { * @param {Recipient|String} recipientId Recipient object or ID. * @param {String} action One of 'mark_seen', 'typing_on' or 'typing_off' * @param {SendMessageOptions} [options] NOT USED. + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - sendAction(recipientId, action, options) { + sendAction(recipientId, action, options, pageId) { const recipient = this._createRecipient(recipientId); return this.sendRequest({ recipient, sender_action: action - }); + }, null, null, pageId); } /** @@ -220,8 +227,9 @@ class BootBot extends EventEmitter { * @param {Recipient|String} recipientId Recipient object or ID. * @param {Message} message The message to send. * @param {SendMessageOptions} [options] + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - sendMessage(recipientId, message, options) { + sendMessage(recipientId, message, options, pageId) { const recipient = this._createRecipient(recipientId); const messagingType = options && options.messagingType; const notificationType = options && options.notificationType; @@ -243,7 +251,7 @@ class BootBot extends EventEmitter { reqBody.tag = tag } const req = () => ( - this.sendRequest(reqBody).then((json) => { + this.sendRequest(reqBody, null, null, pageId).then((json) => { if (typeof onDelivery === 'function') { this.once('delivery', onDelivery); } @@ -256,7 +264,7 @@ class BootBot extends EventEmitter { if (options && options.typing) { const autoTimeout = (message && message.text) ? message.text.length * 10 : 1000; const timeout = (typeof options.typing === 'number') ? options.typing : autoTimeout; - return this.sendTypingIndicator(recipientId, timeout).then(req); + return this.sendTypingIndicator(recipientId, timeout, pageId).then(req); } return req(); } @@ -265,12 +273,13 @@ class BootBot extends EventEmitter { * @param {Object} body The request body object. * @param {String} endpoint Messenger API endpoint * @param {String} method HTTP method. + * @param {Number|String} pageId Facebook page ID for multi page bots. * @returns {Promise} */ - sendRequest(body, endpoint, method) { + sendRequest(body, endpoint, method, pageId) { endpoint = endpoint || 'messages'; method = method || 'POST'; - return fetch(`https://graph.facebook.com/v2.6/me/${endpoint}?access_token=${this.accessToken}`, { + return fetch(`https://graph.facebook.com/v2.6/me/${endpoint}?access_token=${this._getAccessToken(pageId)}`, { method, headers: { 'Content-Type': 'application/json' @@ -293,37 +302,40 @@ class BootBot extends EventEmitter { * Please update your code to use the sendProfileRequest() method instead. * @param {Object} body * @param {String} method + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - sendThreadRequest(body, method) { + sendThreadRequest(body, method, pageId) { console.warning(` sendThreadRequest: Dreprecation warning. Thread API has been replaced by the Messenger Profile API. Please update your code to use the sendProfileRequest() method instead.` ); - return this.sendRequest(body, 'thread_settings', method); + return this.sendRequest(body, 'thread_settings', method, pageId); } /** * @param {Object} body The request body. * @param {String} method HTTP method. + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - sendProfileRequest(body, method) { - return this.sendRequest(body, 'messenger_profile', method); + sendProfileRequest(body, method, pageId) { + return this.sendRequest(body, 'messenger_profile', method, pageId); } /** * Convinient method to send a typing_on action and then a typing_off action after milliseconds to simulate the bot is actually typing. Max value is 20000 (20 seconds). * @param {Recipient|String} recipientId * @param {Number} milliseconds + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - sendTypingIndicator(recipientId, milliseconds) { + sendTypingIndicator(recipientId, milliseconds, pageId) { const timeout = isNaN(milliseconds) ? 0 : milliseconds; if (milliseconds > 20000) { milliseconds = 20000; console.error('sendTypingIndicator: max milliseconds value is 20000 (20 seconds)'); } return new Promise((resolve, reject) => { - return this.sendAction(recipientId, 'typing_on').then(() => { - setTimeout(() => this.sendAction(recipientId, 'typing_off').then((json) => resolve(json)), timeout); + return this.sendAction(recipientId, 'typing_on', null, pageId).then(() => { + setTimeout(() => this.sendAction(recipientId, 'typing_off', null, pageId).then((json) => resolve(json)), timeout); }); }); } @@ -331,10 +343,11 @@ class BootBot extends EventEmitter { /** * Returns a Promise that contains the user's profile information. * @param {String} userId + * @param {Number|String} pageId Facebook page ID for multi page bots. * @returns {Promise} */ - getUserProfile(userId) { - const url = `https://graph.facebook.com/v2.6/${userId}?fields=first_name,last_name,profile_pic,locale,timezone,gender&access_token=${this.accessToken}`; + getUserProfile(userId, pageId) { + const url = `https://graph.facebook.com/v2.6/${userId}?fields=first_name,last_name,profile_pic,locale,timezone,gender&access_token=${this._getAccessToken(pageId)}`; return fetch(url) .then(res => res.json()) .catch(err => console.log(`Error getting user profile: ${err}`)); @@ -344,21 +357,23 @@ class BootBot extends EventEmitter { * Set a greeting text for new conversations. The Greeting Text is only rendered the first time the user interacts with a the Page on Messenger. * Facebook docs: https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/greeting * @param {String|Array.} text Greeting text, or an array of objects to support multiple locales. For more info on the format of these objects, see: https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/greeting + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - setGreetingText(text) { + setGreetingText(text, pageId) { const greeting = (typeof text !== 'string') ? text : [{ locale: 'default', text }]; - return this.sendProfileRequest({ greeting }); + return this.sendProfileRequest({ greeting }, null, pageId); } /** * React to a user starting a conversation with the bot by clicking the Get Started button. * Facebook docs: https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/get-started-button * @param {String|Function} action If action is a string, the Get Started button postback will be set to that string. If it's a function, that callback will be executed when a user clicks the Get Started button. + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - setGetStartedButton(action) { + setGetStartedButton(action, pageId) { const payload = (typeof action === 'string') ? action : 'BOOTBOT_GET_STARTED'; if (typeof action === 'function') { this.on(`postback:${payload}`, action); @@ -367,18 +382,19 @@ class BootBot extends EventEmitter { get_started: { payload } - }); + }, null, pageId); } /** * Removes the Get Started button call to action. + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - deleteGetStartedButton() { + deleteGetStartedButton(pageId) { return this.sendProfileRequest({ fields: [ 'get_started' ] - }, 'DELETE'); + }, 'DELETE', pageId); } /** @@ -386,11 +402,12 @@ class BootBot extends EventEmitter { * Facebook docs: https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/persistent-menu * @param {Array.} buttons If buttons is an array of objects containing a locale attribute, it will be used as-is, expecting it to be an array of localized menues. For more info on the format of these objects, see the documentation. * @param {boolean} [disableInput=false] If disableInput is set to true, it will disable user input in the menu. The user will only be able to interact with the bot via the menu, postbacks, buttons and webviews. + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - setPersistentMenu(buttons, disableInput) { + setPersistentMenu(buttons, disableInput, pageId) { if (buttons && buttons[0] && buttons[0].locale !== undefined) { // Received an array of locales, send it as-is. - return this.sendProfileRequest({ persistent_menu: buttons }); + return this.sendProfileRequest({ persistent_menu: buttons }, null, pageId); } // If it's not an array of locales, we'll assume is an array of buttons. const formattedButtons = this._formatButtons(buttons); @@ -400,18 +417,19 @@ class BootBot extends EventEmitter { composer_input_disabled: disableInput || false, call_to_actions: formattedButtons }] - }); + }, null, pageId); } /** * Removes the Persistent Menu. + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - deletePersistentMenu() { + deletePersistentMenu(pageId) { return this.sendProfileRequest({ fields: [ 'persistent_menu' ] - }, 'DELETE'); + }, 'DELETE', pageId); } /** @@ -421,24 +439,24 @@ class BootBot extends EventEmitter { * @param {SendMessageOptions} options * @returns {Promise} */ - say(recipientId, message, options) { + say(recipientId, message, options, pageId) { if (typeof message === 'string') { - return this.sendTextMessage(recipientId, message, [], options); + return this.sendTextMessage(recipientId, message, [], options, pageId); } else if (message && message.text) { if (message.quickReplies && message.quickReplies.length > 0) { - return this.sendTextMessage(recipientId, message.text, message.quickReplies, options); + return this.sendTextMessage(recipientId, message.text, message.quickReplies, options, pageId); } else if (message.buttons && message.buttons.length > 0) { - return this.sendButtonTemplate(recipientId, message.text, message.buttons, options); + return this.sendButtonTemplate(recipientId, message.text, message.buttons, options, pageId); } } else if (message && message.attachment) { - return this.sendAttachment(recipientId, message.attachment, message.url, message.quickReplies, options); + return this.sendAttachment(recipientId, message.attachment, message.url, message.quickReplies, options, pageId); } else if (message && message.elements && message.buttons) { - return this.sendListTemplate(recipientId, message.elements, message.buttons, options); + return this.sendListTemplate(recipientId, message.elements, message.buttons, options, pageId); } else if (message && message.cards) { - return this.sendGenericTemplate(recipientId, message.cards, options); + return this.sendGenericTemplate(recipientId, message.cards, options, pageId); } else if (Array.isArray(message)) { return message.reduce((promise, msg) => { - return promise.then(() => this.say(recipientId, msg, options)); + return promise.then(() => this.say(recipientId, msg, options, pageId)); }, Promise.resolve()); } console.error('Invalid format for .say() message.'); @@ -467,12 +485,13 @@ class BootBot extends EventEmitter { * Starts a new conversation with the user. * @param {Recipient|String} recipientId * @param {Function} factory Executed immediately receiving the convo instance as it's only param. + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - conversation(recipientId, factory) { + conversation(recipientId, factory, pageId) { if (!recipientId || !factory || typeof factory !== 'function') { return console.error(`You need to specify a recipient and a callback to start a conversation`); } - const convo = new Conversation(this, recipientId); + const convo = new Conversation(this, recipientId, pageId); this._conversations.push(convo); convo.on('end', (endedConvo) => { const removeIndex = this._conversations.indexOf(endedConvo); @@ -527,14 +546,15 @@ class BootBot extends EventEmitter { * @param {String} type * @param {Object} event * @param {Object} data + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - _handleEvent(type, event, data) { + _handleEvent(type, event, data, pageId) { const recipient = (type === 'authentication' && !event.sender) ? { user_ref: event.optin.user_ref } : event.sender.id; - const chat = new Chat(this, recipient); + const chat = new Chat(this, recipient, pageId); this.emit(type, event, chat, data); } - _handleMessageEvent(event) { + _handleMessageEvent(event, pageId) { if (this._handleConversationResponse('message', event)) { return; } const text = event.message.text; const senderId = event.sender.id; @@ -543,14 +563,14 @@ class BootBot extends EventEmitter { this._hearMap.forEach(hear => { if (typeof hear.keyword === 'string' && hear.keyword.toLowerCase() === text.toLowerCase()) { - const res = hear.callback.apply(this, [event, new Chat(this, senderId), { + const res = hear.callback.apply(this, [event, new Chat(this, senderId, pageId), { keyword: hear.keyword, captured }]); captured = true; return res; } else if (hear.keyword instanceof RegExp && hear.keyword.test(text)) { - const res = hear.callback.apply(this, [event, new Chat(this, senderId), { + const res = hear.callback.apply(this, [event, new Chat(this, senderId, pageId), { keyword: hear.keyword, match: text.match(hear.keyword), captured @@ -560,30 +580,30 @@ class BootBot extends EventEmitter { } }); - this._handleEvent('message', event, { captured }); + this._handleEvent('message', event, { captured }, pageId); } - _handleAttachmentEvent(event) { + _handleAttachmentEvent(event, pageId) { if (this._handleConversationResponse('attachment', event)) { return; } - this._handleEvent('attachment', event); + this._handleEvent('attachment', event, null, pageId); } - _handlePostbackEvent(event) { + _handlePostbackEvent(event, pageId) { if (this._handleConversationResponse('postback', event)) { return; } const payload = event.postback.payload; if (payload) { - this._handleEvent(`postback:${payload}`, event); + this._handleEvent(`postback:${payload}`, event, null, pageId); } - this._handleEvent('postback', event); + this._handleEvent('postback', event, null, pageId); } - _handleQuickReplyEvent(event) { + _handleQuickReplyEvent(event, pageId) { if (this._handleConversationResponse('quick_reply', event)) { return; } const payload = event.message.quick_reply && event.message.quick_reply.payload; if (payload) { - this._handleEvent(`quick_reply:${payload}`, event); + this._handleEvent(`quick_reply:${payload}`, event, null, pageId); } - this._handleEvent('quick_reply', event); + this._handleEvent('quick_reply', event, null, pageId); } _handleConversationResponse(type, event) { @@ -613,6 +633,14 @@ class BootBot extends EventEmitter { return (typeof recipient === 'object') ? recipient : { id: recipient }; } + /** + * Returns accessToken if it's a string or uses pageId to get accessToken for multi page bots + * @param {Number|String} pageId Facebook page ID for multi page bots. + * @returns {String} + */ + _getAccessToken(pageId) { + return typeof this.accessToken === 'string' ? this.accessToken : this.accessToken[pageId]; + } _initWebhook() { this.app.get(this.webhook, (req, res) => { @@ -645,30 +673,31 @@ class BootBot extends EventEmitter { handleFacebookData(data) { // Iterate over each entry. There may be multiple if batched. data.entry.forEach((entry) => { + const pageId = entry.id; // Iterate over each messaging event entry.messaging.forEach((event) => { if (event.message && event.message.is_echo && !this.broadcastEchoes) { return; } if (event.optin) { - this._handleEvent('authentication', event); + this._handleEvent('authentication', event, null, pageId); } else if (event.message && event.message.text) { - this._handleMessageEvent(event); + this._handleMessageEvent(event, pageId); if (event.message.quick_reply) { - this._handleQuickReplyEvent(event); + this._handleQuickReplyEvent(event, pageId); } } else if (event.message && event.message.attachments) { - this._handleAttachmentEvent(event); + this._handleAttachmentEvent(event, pageId); } else if (event.postback) { - this._handlePostbackEvent(event); + this._handlePostbackEvent(event, pageId); } else if (event.delivery) { - this._handleEvent('delivery', event); + this._handleEvent('delivery', event, null, pageId); } else if (event.read) { - this._handleEvent('read', event); + this._handleEvent('read', event, null, pageId); } else if (event.account_linking) { - this._handleEvent('account_linking', event); + this._handleEvent('account_linking', event, null, pageId); } else if (event.referral) { - this._handleEvent('referral', event); + this._handleEvent('referral', event, null, pageId); } else { console.log('Webhook received unknown event: ', event); } diff --git a/lib/Chat.js b/lib/Chat.js index 1f6b1aa..ac00c69 100644 --- a/lib/Chat.js +++ b/lib/Chat.js @@ -6,14 +6,16 @@ class Chat extends EventEmitter { /** * @param {BootBot} bot Bot instance. * @param {String} userId + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - constructor(bot, userId) { + constructor(bot, userId, pageId) { super(); if (!bot || !userId) { throw new Error('You need to specify a BootBot instance and a userId'); } this.bot = bot; this.userId = userId; + this.pageId = pageId; } /** @@ -37,7 +39,7 @@ class Chat extends EventEmitter { * @param {SendMessageOptions} options */ say(message, options) { - return this.bot.say(this.userId, message, options); + return this.bot.say(this.userId, message, options, this.pageId); } /** @@ -55,7 +57,7 @@ class Chat extends EventEmitter { * @param {SendMessageOptions} [options] */ sendTextMessage(text, quickReplies, options) { - return this.bot.sendTextMessage(this.userId, text, quickReplies, options); + return this.bot.sendTextMessage(this.userId, text, quickReplies, options, this.pageId); } /** @@ -70,7 +72,7 @@ class Chat extends EventEmitter { * @param {SendMessageOptions} [options] */ sendButtonTemplate(text, buttons, options) { - return this.bot.sendButtonTemplate(this.userId, text, buttons, options); + return this.bot.sendButtonTemplate(this.userId, text, buttons, options, this.pageId); } /** @@ -87,7 +89,7 @@ class Chat extends EventEmitter { * @param {SendMessageOptions} [options] */ sendGenericTemplate(cards, options) { - return this.bot.sendGenericTemplate(this.userId, cards, options); + return this.bot.sendGenericTemplate(this.userId, cards, options, this.pageId); } /** @@ -97,7 +99,7 @@ class Chat extends EventEmitter { * @param {String} [options.topElementStyle] */ sendListTemplate(elements, buttons, options) { - return this.bot.sendListTemplate(this.userId, elements, buttons, options); + return this.bot.sendListTemplate(this.userId, elements, buttons, options, this.pageId); } /** @@ -106,7 +108,7 @@ class Chat extends EventEmitter { * @param {SendMessageOptions} [options] */ sendTemplate(payload, options) { - return this.bot.sendTemplate(this.userId, payload, options); + return this.bot.sendTemplate(this.userId, payload, options, this.pageId); } /** @@ -116,7 +118,7 @@ class Chat extends EventEmitter { * @param {SendMessageOptions} options */ sendAttachment(type, url, quickReplies, options) { - return this.bot.sendAttachment(this.userId, type, url, quickReplies, options); + return this.bot.sendAttachment(this.userId, type, url, quickReplies, options, this.pageId); } /** @@ -126,7 +128,7 @@ class Chat extends EventEmitter { * @param {SendMessageOptions} [options] NOT USED. */ sendAction(action, options) { - return this.bot.sendAction(this.userId, action, options); + return this.bot.sendAction(this.userId, action, options, this.pageId); } /** @@ -135,7 +137,7 @@ class Chat extends EventEmitter { * @param {SendMessageOptions} [options] */ sendMessage(message, options) { - return this.bot.sendMessage(this.userId, message, options); + return this.bot.sendMessage(this.userId, message, options, this.pageId); } /** @@ -145,7 +147,7 @@ class Chat extends EventEmitter { * @returns {Promise} */ sendRequest(body, endpoint, method) { - return this.bot.sendRequest(body, endpoint, method); + return this.bot.sendRequest(body, endpoint, method, this.pageId); } /** @@ -153,14 +155,14 @@ class Chat extends EventEmitter { * @param {Number} milliseconds */ sendTypingIndicator(milliseconds) { - return this.bot.sendTypingIndicator(this.userId, milliseconds); + return this.bot.sendTypingIndicator(this.userId, milliseconds, this.pageId); } /** * Returns a Promise that contains the user's profile information. */ getUserProfile() { - return this.bot.getUserProfile(this.userId); + return this.bot.getUserProfile(this.userId, this.pageId); } /** @@ -168,7 +170,7 @@ class Chat extends EventEmitter { * @param {Function} factory Executed immediately receiving the convo instance as it's only param. */ conversation(factory) { - return this.bot.conversation(this.userId, factory); + return this.bot.conversation(this.userId, factory, this.pageId); } } diff --git a/lib/Conversation.js b/lib/Conversation.js index cd3348c..4c6284c 100644 --- a/lib/Conversation.js +++ b/lib/Conversation.js @@ -7,10 +7,11 @@ class Conversation extends Chat { /** * Create a new conversation with a user. * @param {BootBot} bot Bot instance - * @param {String} userId + * @param {String} userId + * @param {Number|String} pageId Facebook page ID for multi page bots. */ - constructor(bot, userId) { - super(bot, userId); + constructor(bot, userId, pageId) { + super(bot, userId, pageId); this.bot = bot; this.userId = userId; this.context = {}; diff --git a/test/BootBot.spec.js b/test/BootBot.spec.js index 417753f..6d9143b 100644 --- a/test/BootBot.spec.js +++ b/test/BootBot.spec.js @@ -513,15 +513,16 @@ describe('BootBot', () => { '!' ]; const options = { typing: true }; + const pageId = 4321; expect(spy.called).to.equal(false) - bot.say(recipentId, messages, options).then(() => { + bot.say(recipentId, messages, options, pageId).then(() => { expect(spy.callCount).to.equal(4) - expect(spy.getCall(0).args).to.deep.equal([ recipentId, messages, options ]) - expect(spy.getCall(1).args).to.deep.equal([ recipentId, messages[0], options ]) - expect(spy.getCall(2).args).to.deep.equal([ recipentId, messages[1], options ]) - expect(spy.getCall(3).args).to.deep.equal([ recipentId, messages[2], options ]) + expect(spy.getCall(0).args).to.deep.equal([ recipentId, messages, options, pageId ]) + expect(spy.getCall(1).args).to.deep.equal([ recipentId, messages[0], options, pageId ]) + expect(spy.getCall(2).args).to.deep.equal([ recipentId, messages[1], options, pageId ]) + expect(spy.getCall(3).args).to.deep.equal([ recipentId, messages[2], options, pageId ]) done() }) }); From 742ea62f5e5fe10d341e192b42e2735334ad1117 Mon Sep 17 00:00:00 2001 From: nati Date: Thu, 21 Jun 2018 12:16:58 +0300 Subject: [PATCH 2/2] added example for multi page support --- examples/multiple-pages-example.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 examples/multiple-pages-example.js diff --git a/examples/multiple-pages-example.js b/examples/multiple-pages-example.js new file mode 100644 index 0000000..b5af126 --- /dev/null +++ b/examples/multiple-pages-example.js @@ -0,0 +1,28 @@ +'use strict'; +const BootBot = require('../'); +const config = require('config'); + +/** + * For your bot to manage multiple pages + * 1. Subscribe your app to all the pages you want your bot to manage inside your app settings + * 2. Get access tokens for all the pages + * 3. Get your page IDs from facebook.com/pg/{your_page}/about/ + * 4. Initialize bootbot by setting access token with an object like the format below + */ +const bot = new BootBot({ + accessToken: { + 'PAGE_ONE_ID': 'PAGE_ONE_ACCESS_TOKEN', + 'PAGE_TWO_ID': 'PAGE_TWO_ACCESS_TOKEN', + }, + verifyToken: config.get('verify_token'), + appSecret: config.get('app_secret') +}); + +bot.on('message', (payload, chat) => { + const text = payload.message.text; + if(chat.pageId === 'PAGE_ONE_ID') chat.say(`Echo page 1: ${text}`); + else chat.say(`Echo other pages: ${text}`); + // you can also send both pages the same message with out checking chat.pageId +}); + +bot.start();