Coder Social home page Coder Social logo

bootbot's Introduction

BootBot is a simple but powerful JavaScript Framework to build Facebook Messenger's Chat bots.

Features Usage Video Example Getting Started Documentation Examples Credits License

💬 Questions / Comments? Join our Slack channel!

Features

  • Helper methods to send any type of message supported by Facebook.
  • Subscribe to a particular type of message, or to certain keywords sent by the user.
  • Start conversations, ask questions and save important information in the context of the conversation.
  • Organize your code in modules.
  • Send automatic or manual typing indicators.
  • Set your bot's properties, such as a persistent menu, a greeting text or a get started CTA.
  • Subscribe to received and read events.

Usage

$ npm install bootbot --save
'use strict';
const BootBot = require('bootbot');

const bot = new BootBot({
  accessToken: 'FB_ACCESS_TOKEN',
  verifyToken: 'FB_VERIFY_TOKEN',
  appSecret: 'FB_APP_SECRET'
});

bot.on('message', (payload, chat) => {
  const text = payload.message.text;
  chat.say(`Echo: ${text}`);
});

bot.start();

Video Example

Creating a Giphy Chat Bot in 3 minutes:

IMG

Getting Started

  • Install BootBot via NPM, create a new index.js, require BootBot and create a new bot instance using your Facebook Page's / App's accessToken, verifyToken and appSecret:

Note: If you don't know how to get these tokens, take a look at Facebook's Quick Start Guide or check out this issue.

// index.js
'use strict';
const BootBot = require('bootbot');

const bot = new BootBot({
  accessToken: 'FB_ACCESS_TOKEN',
  verifyToken: 'FB_VERIFY_TOKEN',
  appSecret: 'FB_APP_SECRET'
});
  • Subscribe to messages sent by the user with the bot.on() and bot.hear() methods:
bot.on('message', (payload, chat) => {
	const text = payload.message.text;
	console.log(`The user said: ${text}`);
});

bot.hear(['hello', 'hi', /hey( there)?/i], (payload, chat) => {
	console.log('The user said "hello", "hi", "hey", or "hey there"');
});
  • Reply to user messages using the chat object:
bot.hear(['hello', 'hi', /hey( there)?/i], (payload, chat) => {
	// Send a text message followed by another text message that contains a typing indicator
	chat.say('Hello, human friend!').then(() => {
		chat.say('How are you today?', { typing: true });
	});
});

bot.hear(['food', 'hungry'], (payload, chat) => {
	// Send a text message with quick replies
	chat.say({
		text: 'What do you want to eat today?',
		quickReplies: ['Mexican', 'Italian', 'American', 'Argentine']
	});
});

bot.hear(['help'], (payload, chat) => {
	// Send a text message with buttons
	chat.say({
		text: 'What do you need help with?',
		buttons: [
			{ type: 'postback', title: 'Settings', payload: 'HELP_SETTINGS' },
			{ type: 'postback', title: 'FAQ', payload: 'HELP_FAQ' },
			{ type: 'postback', title: 'Talk to a human', payload: 'HELP_HUMAN' }
		]
	});
});

bot.hear('image', (payload, chat) => {
	// Send an attachment
	chat.say({
		attachment: 'image',
		url: 'http://example.com/image.png'
	});
});
  • Start a conversation and keep the user's answers in context:
bot.hear('ask me something', (payload, chat) => {

	const askName = (convo) => {
		convo.ask(`What's your name?`, (payload, convo) => {
			const text = payload.message.text;
			convo.set('name', text);
			convo.say(`Oh, your name is ${text}`).then(() => askFavoriteFood(convo));
		});
	};

	const askFavoriteFood = (convo) => {
		convo.ask(`What's your favorite food?`, (payload, convo) => {
			const text = payload.message.text;
			convo.set('food', text);
			convo.say(`Got it, your favorite food is ${text}`).then(() => sendSummary(convo));
		});
	};

	const sendSummary = (convo) => {
		convo.say(`Ok, here's what you told me about you:
	      - Name: ${convo.get('name')}
	      - Favorite Food: ${convo.get('food')}`);
      convo.end();
	};

	chat.conversation((convo) => {
		askName(convo);
	});
});
  • Set up webhooks and start the express server:
bot.start();
  • Start up your bot by running node:
$ node index.js
> BootBot running on port 3000
> Facebook Webhook running on localhost:3000/webhook
  • If you want to test your bot locally, install a localhost tunnel like ngrok and run it on your bot's port:
$ ngrok http 3000

Then use the provided HTTPS URL to config your webhook on Facebook's Dashboard. For example if the URL provided by ngrok is https://99b8d4c2.ngrok.io, use https://99b8d4c2.ngrok.io/webhook.

Documentation

BootBot Class

new BootBot(options)

options key Type Default Required
accessToken string Y
verifyToken string Y
appSecret string Y
webhook string "/webhook" N
broadcastEchoes boolean false N
graphApiVersion string v2.12 N

Creates a new BootBot instance. Instantiates the new express app and all required webhooks. options param must contain all tokens and app secret of your Facebook app. Optionally, set broadcastEchoes to true if you want the messages your bot send to be echoed back to it (you probably don't need this feature unless you have multiple bots running on the same Facebook page).

If you want to specify a custom endpoint name for your webhook, you can do it with the webhook option.

.start([ port ])

Param Type Default Required
port number 3000 N

Starts the express server on the specified port. Defaults port to 3000.

.close()

Closes the express server (calls .close() on the server instance).


Receive API

Use these methods to subscribe your bot to messages, attachments or anything the user might send.

.on(event, callback)

Param Type Default Required
event string Y
callback function Y

Subscribe to an event emitted by the bot, and execute a callback when those events are emitted. Available events are:

Event Description
message The bot received a text message from the user
quick_reply The bot received a quick reply from the user (quick replies emit both message and quick_reply events)
attachment The bot received an attachment from the user
postback The bot received a postback call from the user (usually means the user clicked a button)
delivery The bot received a confirmation that your message was delivered to the user
read The bot received a confirmation that your message was read by the user
authentication A user has started a conversation with the bot using a "Send to Messenger" button
referral A user that already has a thread with the bot starts a conversation. more

You can also subscribe to specific postbacks and quick replies by using a namespace. For example postback:ADD_TO_CART subscribes only to the postback event containing the ADD_TO_CART payload.

If you want to subscribe to specific keywords on a message event, see the .hear() method below.

When these events ocurr, the specified callback will be invoked with 3 params: (payload, chat, data)

Param Description
payload The data sent by the user (contains the text of the message, the attachment, etc.)
chat A Chat instance that you can use to reply to the user. Contains all the methods defined in the Send API
data Contains extra data provided by the framework, like a captured flag that signals if this message was already captured by a different callback
.on() examples:
bot.on('message', (payload, chat) => {
	console.log('A text message was received!');
});

bot.on('attachment', (payload, chat) => {
	console.log('An attachment was received!');
});

bot.on('postback:HELP_ME', (payload, chat) => {
	console.log('The Help Me button was clicked!');
});

bot.on('message', (payload, chat) => {
	// Reply to the user
	chat.say('Hey, user. I got your message!');
});

.hear(keywords, callback)

Param Type Default Required
keywords string, regex or mixed array Y
callback function Y

A convinient method to subscribe to message events containing specific keywords. The keyword param can be a string, a regex or an array of both strings and regexs that will be tested against the received message. If the bot receives a message that matches any of the keywords, it will execute the specified callback. String keywords are case-insensitive, but regular expressions are not case-insensitive by default, if you want them to be, specify the i flag.

The callback's signature is identical to that of the .on() method above.

.hear() examples:
bot.hear('hello', (payload, chat) => {
	chat.say('Hello, human!');
});

bot.hear(['hello', 'hi', 'hey'], (payload, chat) => {
	chat.say('Hello, human!');
});

bot.hear([/(good)?bye/i, /see (ya|you)/i, 'adios'], (payload, chat) => {
	// Matches: goodbye, bye, see ya, see you, adios
	chat.say('Bye, human!');
});

Note that if a bot is subscribed to both the message event using the .on() method and a specific keyword using the .hear() method, the event will be emitted to both of those subscriptions. If you want to know if a message event was already captured by a different subsciption, you can check for the data.captured flag on the callback.


Send API

BootBot provides helper methods for every type of message supported by Facebook's Messenger API. It also provides a generic sendMessage method that you can use to send a custom payload. All messages from the Send API return a Promise that you can use to apply actions after a message was successfully sent. You can use this to send consecutive messages and ensure that they're sent in the right order.

Important Note:

The Send API methods are shared between the BootBot, Chat and Conversation instances, the only difference is that when you use any of these methods from the Chat or Conversation instances, you don't have to specify the userId.

Example - These two methods are identical:

bot.on('message', (payload, chat) => {
  const text = payload.message.text;
  const userId = payload.sender.id;
  bot.say(userId, 'Hello World');
});

// is the same as...

bot.on('message', (payload, chat) => {
  const text = payload.message.text;
  chat.say('Hello World');
});

You'll likely use the Send API methods from the Chat or Conversation instances (ex: chat.say() or convo.say()), but you can use them from the BootBot instance if you're not in a chat or conversation context (for example, when you want to send a notification to a user).

.say()

Method signature
chat.say(message, [ options ])
convo.say(message, [ options ])
bot.say(userId, message, [ options ])

Send a message to the user. The .say() method can be used to send text messages, button messages, messages with quick replies or attachments. If you want to send a different type of message (like a generic template), see the Send API method for that specific type of message.

The message param can be a string an array, or an object:

  • If message is a string, the bot will send a text message.
  • If message is an array, the .say() method will be called once for each element in the array.
  • If message is an object, the message type will depend on the object's format:
// Send a text message
chat.say('Hello world!');

// Send a text message with quick replies
chat.say({
	text: 'Favorite color?',
	quickReplies: ['Red', 'Blue', 'Green']
});

// Send a button template
chat.say({
	text: 'Favorite color?',
	buttons: [
		{ type: 'postback', title: 'Red', payload: 'FAVORITE_RED' },
		{ type: 'postback', title: 'Blue', payload: 'FAVORITE_BLUE' },
		{ type: 'postback', title: 'Green', payload: 'FAVORITE_GREEN' }
	]
});

// Send a list template
chat.say({
	elements: [
		{ title: 'Artile 1', image_url: '/path/to/image1.png', default_action: {} },
		{ title: 'Artile 2', image_url: '/path/to/image2.png', default_action: {} }
	],
	buttons: [
		{ type: 'postback', title: 'View More', payload: 'VIEW_MORE' }
	]
});

// Send a generic template
chat.say({
	cards: [
		{ title: 'Card 1', image_url: '/path/to/image1.png', default_action: {} },
		{ title: 'Card 2', image_url: '/path/to/image2.png', default_action: {} }
	]
});

// Send an attachment
chat.say({
	attachment: 'video',
	url: 'http://example.com/video.mp4'
});

// Passing an array will make subsequent calls to the .say() method
// For example, calling:

chat.say(['Hello', 'How are you?']);

// is the same as:

chat.say('Hello').then(() => {
  chat.say('How are you?')
});

The options param can contain:

options key Type Default Description
typing boolean or number false Send a typing indicator before sending the message. If set to true, it will automatically calculate how long it lasts based on the message length. If it's a number, it will show the typing indicator for that amount of milliseconds (max. 20000 - 20 seconds)
messagingType string 'RESPONSE' The messaging type of the message being sent.
notificationType string Push notification type: 'REGULAR': sound/vibration - 'SILENT_PUSH': on-screen notification only - 'NO_PUSH': no notification.
tag string The message tag string. Can only be used if messagingType is set to 'MESSAGE_TAG'
onDelivery function Callback that will be executed when the message is received by the user. Receives params: (payload, chat, data)
onRead function Callback that will be executed when the message is read by the user. Receives params: (payload, chat, data)

.sendTextMessage()

Method signature
chat.sendTextMessage(text, [ quickReplies, options ])
convo.sendTextMessage(text, [ quickReplies, options ])
bot.sendTextMessage(userId, text, [ quickReplies, options ])

The text param must be a string containing the message to be sent.

The quickReplies param can be an array of strings or quick_reply objects.

The options param is identical to the options param of the .say() method.

.sendButtonTemplate()

Method signature
chat.sendButtonTemplate(text, buttons, [ options ])
convo.sendButtonTemplate(text, buttons, [ options ])
bot.sendButtonTemplate(userId, text, buttons, [ options ])

The text param must be a string containing the message to be sent.

The buttons param can be an array of strings or button objects.

The options param is identical to the options param of the .say() method.

.sendGenericTemplate()

Method signature
chat.sendGenericTemplate(elements, [ options ])
convo.sendGenericTemplate(elements, [ options ])
bot.sendGenericTemplate(userId, elements, [ options ])

The elements param must be an array of element objects.

The options param extends options param of the .say() method with imageAspectRatio property.

.sendListTemplate()

Method signature
chat.sendListTemplate(elements, buttons, [ options ])
convo.sendListTemplate(elements, buttons, [ options ])
bot.sendListTemplate(userId, elements, buttons, [ options ])

The elements param must be an array of element objects.

The buttons param can be an array with one element: string or button object.

The options param extends options param of the .say() method with topElementStyle property.

.sendTemplate()

Method signature
chat.sendTemplate(payload, [ options ])
convo.sendTemplate(payload, [ options ])
bot.sendTemplate(userId, payload, [ options ])

Use this method if you want to send a custom template payload, like a receipt template or an airline itinerary template.

The options param is identical to the options param of the .say() method.

.sendAttachment()

Method signature
chat.sendAttachment(type, url, [ quickReplies, options ])
convo.sendAttachment(type, url, [ quickReplies, options ])
bot.sendAttachment(userId, type, url, [ quickReplies, options ])

The type param must be 'image', 'audio', 'video' or 'file'.

The url param must be a string with the URL of the attachment.

The quickReplies param can be an array of strings or quick_reply objects.

The options param is identical to the options param of the .say() method.

.sendAction()

Method signature
chat.sendAction(action, [ options ])
convo.sendAction(action, [ options ])
bot.sendAction(userId, action, [ options ])

The action param must be 'mark_seen', 'typing_on' or 'typing_off'. To send a typing indicator in a more convenient way, see the .sendTypingIndicator method.

The options param is identical to the options param of the .say() method.

.sendMessage()

Method signature
chat.sendMessage(message, [ options ])
convo.sendMessage(message, [ options ])
bot.sendMessage(userId, message, [ options ])

Use this method if you want to send a custom message object.

The options param is identical to the options param of the .say() method.

.sendTypingIndicator()

Method signature
chat.sendTypingIndicator(milliseconds)
convo.sendTypingIndicator(milliseconds)
bot.sendTypingIndicator(userId, milliseconds)

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).

You can also use this method via the typing option (see .say() method).

.getUserProfile()

Method signature
chat.getUserProfile()
convo.getUserProfile()
bot.getUserProfile(userId)

This method is not technically part of the "Send" API, but it's listed here because it's also shared between the bot, chat and convo instances.

Returns a Promise that contains the user's profile information.

bot.hear('hello', (payload, chat) => {
  chat.getUserProfile().then((user) => {
    chat.say(`Hello, ${user.first_name}!`);
  });
});

Conversations

Conversations provide a convinient method to ask questions and handle the user's answer. They're useful when you want to set a flow of different questions/answers, like an onboarding process or when taking an order for example. Conversations also provide a method to save the information that you need from the user's answers, so the interaction is always in context.

Messages sent by the user won't trigger a global message, postback, attachment or quick_reply event if there's an active conversation with that user. Answers must be managed by the conversation.

bot.conversation()

Method signature
chat.conversation(factory)
bot.conversation(userId, factory)

Starts a new conversation with the user.

The factory param must be a function that is executed immediately receiving the convo instance as it's only param:

bot.on('hello', (payload, chat) => {
	chat.conversation((convo) => {
		// convo is available here...
		convo.ask( ... );
	});
});

convo.ask(question, answer, [ callbacks, options ])

Param Type Default Required
question string, object or function Y
answer function Y
callbacks array N
options object N

If question is a string or an object, the .say() method will be invoked immediately with that string or object, if it's a function it will also be invoked immedately with the convo instance as its only param.

The answer param must be a function that receives the payload, convo and data params (similar to the callback function of the .on() or .hear() methods, except it receives the convo instance instead of the chat instance). The answer function will be called whenever the user replies to the question with a text message or quick reply.

The callbacks array can be used to listen to specific types of answers to the question. You can listen for postback, quick_reply and attachment events, or you can match a specific text pattern. See example bellow:

The options param is identical to the options param of the .say() method.

convo.ask() example:
const question = {
	text: `What's your favorite color?`,
	quickReplies: ['Red', 'Green', 'Blue']
};

const answer = (payload, convo) => {
	const text = payload.message.text;
	convo.say(`Oh, you like ${text}!`);
};

const callbacks = [
	{
		event: 'quick_reply',
		callback: () => { /* User replied using a quick reply */ }
	},
	{
		event: 'attachment',
		callback: () => { /* User replied with an attachment */ }
	},
	{
		pattern: ['black', 'white'],
		callback: () => { /* User said "black" or "white" */ }
	}
];

const options = {
	typing: true // Send a typing indicator before asking the question
};

convo.ask(question, answer, callbacks, options);

convo.set(property, value)

Param Type Default Required
property string Y
value mixed Y

Save a value in the conversation's context. This value will be available in all subsequent questions and answers that are part of this conversation, but the values are lost once the conversation ends.

convo.question(`What's your favorite color?`, (payload, convo) => {
	const text = payload.message.text;

	// Save the user's answer in the conversation's context.
	// You can then call convo.get('favoriteColor') in a future question or answer to retrieve the value.
	convo.set('favoriteColor', text);
	convo.say(`Oh, you like ${text}!`);
});

convo.get(property)

Param Type Default Required
property string Y

Retrieve a value from the conversation's context.

convo.end()

Ends a conversation, giving control back to the bot instance. All .on() and .hear() listeners are now back in action. After you end a conversation the values that you saved using the convo.set() method are now lost.

You must call convo.end() after you no longer wish to interpret user's messages as answers to one of your questions. If you don't, and a message is received with no answer callback listening, the conversation will be ended automatically.


Modules

Modules are simple functions that you can use to organize your code in different files and folders.

.module(factory)

The factory param is a function that gets called immediatly and receives the bot instance as its only parameter. For example:

// help-module.js
module.exports = (bot) => {
	bot.hear('help', (payload, chat) => {
		// Send Help Menu to the user...
	});
};

// index.js
const helpModule = require('./help-module');
bot.module(helpModule);

Take a look at the examples/module-example.js file for a complete example.


Messenger Profile API

.setGreetingText(text)

Facebook Docs

Param Type Default Required
text string or array Y

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.

Localization support: text can be a string containing the greeting text, or an array of objects to support multiple locales. For more info on the format of these objects, see the documentation.

.setGetStartedButton(action)

Facebook Docs

Param Type Default Required
action string or function Y

React to a user starting a conversation with the bot by clicking the Get Started button. 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.

.deleteGetStartedButton()

Removes the Get Started button call to action.

.setPersistentMenu(buttons, [ disableInput ])

Facebook Docs

Param Type Default Required
buttons array of strings or objects Y
disableInput boolean false N

Creates a Persistent Menu that is available at any time during the conversation. The buttons param can be an array of strings, button objects, or locale objects.

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.

Localization support: 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.

.deletePersistentMenu()

Removes the Persistent Menu.


Bypassing Express

You may only want to use bootbot for the Facebook related config and the simple to use Send API features but handle routing from somewhere else. Or there may be times where you want to send a message out of band, like if you get a postback callback and need to end a conversation flow immediately.

Or maybe you don't want to use express but a different HTTP server.

.handleFacebookData(data)

Use this to send a message from a parsed webhook message directly to your bot.

const linuxNewsBot   = new BootBot({argz});
const appleNewsBot   = new BootBot({argz});
const windowsNewsBot = new BootBot({argz});

myNonExpressRouter.post("/mywebhook", (data) => {
	const messages = data.entry[0].messaging;
	messages.forEach(message => {
		switch(data.entry.id) {
			case LINUX_BOT_PAGE_ID:
				linuxNewsBot.handleFacebookData(message);
				break;
			case APPLE_BOT_PAGE_ID:
				appleNewsBot.handleFacebookData(message);
				break;
			// ...
		};
	});
});

Examples

Check the examples directory to see more demos of:

  • An echo bot
  • A bot that searches for random gifs
  • An example conversation with questions and answers
  • How to organize your code using modules
  • How to use the Messenger Profile API to set a Persistent Menu or a Get Started CTA
  • How to get the user's profile information

To run the examples, make sure to complete the examples/config/default.json file with your bot's tokens, and then cd into the examples folder and run the desired example with node. For example:

$ cd examples
$ node echo-example.js

Credits

Made with 🍺 by Maxi Ferreira - @Charca

License

MIT

bootbot's People

Contributors

charca avatar chr0m1ng avatar datafile4 avatar devalnor avatar hkalexling avatar joshes avatar learts avatar lehainam-dev avatar mraaroncruz avatar rmattos avatar tom-sherman avatar udokah avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bootbot's Issues

Passing arguments to events subscriptions

Hi!!

I have a question about how to pass information from a button event to the the button subscription..

I modified the bootbot source code to work something out, but I would to confirm if there is another way before I make a pull request. It is something like:

chat.say({
    text: 'Some product here',
    buttons: [
        { type: 'postback', title: 'More info', payload: 'INFO:product_id' }
    ]
});

bot.on('postback:INFO', (payload, chat, data, args) => {
    console.log(args); // should print "product_id"
});

Initialize Bootbot without needing app secret [Feature Request]

It would be good to find a way to do initialize Bootbot without needing app secret. I'm finding a way where I can put my code that uses Bootbot in github.

In Messenger bot setup it says nodejs needs a GET method for Facebook verification. I'm not sure if inside Bootbot constructor we can just setup an app.get('/webhook/', function (req, res)... method. Is this possible? Some samples use something like this:

app.get('/webhook/', function (req, res) {
    if (req.query['hub.verify_token'] === 'some_token') {
        res.send(req.query['hub.challenge'])
    }
    res.send('Error, wrong token')
})

Add `sendListTemplate` method

Support for List Template (docs)

Could be a copy of sendGenericTemplate that in addition to elements, also support top_element_style and buttons props.

Closing webview and resuming the conversation

Hey,

I have a simple webview that lets the user to pick date and time, but once the user selects it I want to close the webview, pass the data back to the conversation and continue from where we left.

Is it possible?

Cannot read property 'filter' of undefined

Sometimes when I send message in messenger, I receive this error

TypeError: Cannot read property 'filter' of undefined
    at Conversation.respond (/Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/bootbot/lib/Conversation.js:61:53)
    at _conversations.forEach.convo (/Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/bootbot/lib/BootBot.js:344:22)
    at Array.forEach (native)
    at BootBot._handleConversationResponse (/Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/bootbot/lib/BootBot.js:341:25)
    at BootBot._handleMessageEvent (/Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/bootbot/lib/BootBot.js:291:14)
    at entry.messaging.forEach (/Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/bootbot/lib/BootBot.js:377:20)
    at Array.forEach (native)
    at data.entry.forEach (/Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/bootbot/lib/BootBot.js:370:27)
    at Array.forEach (native)
    at app.post (/Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/bootbot/lib/BootBot.js:368:18)
    at Layer.handle [as handle_request] (/Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/express/lib/router/route.js:131:13)
    at Route.dispatch (/Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/express/lib/router/layer.js:95:5)
    at /Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/express/lib/router/index.js:277:22
    at Function.process_params (/Users/iurisouz4/Documents/Julien/Projects/facebook_bootbot/node_modules/express/lib/router/index.js:330:12)

After that, I receive this error :

Error sending message: FetchError: request to https://graph.facebook.com/v2.6/me/messages?access_token=EAAPBAL6ZBQEkBAMIblP8n6wISEjVJVaea4B54zghnwvy87ZAiCE1uexYSt8KipGqBQjzTeCG71hyWoB3wBZBHo33wX9ozH8x1cmgM7VxntkNy01ZABlpKp8Xp0zZC1vgxjIgFBzDQ58EzakeW0IMC58ZAnhGLbI0YKMfuRUKa4g2GUrzAOmlZAL failed, reason: connect ETIMEDOUT 31.13.73.1:443

But this second error don't appear everytime.

Actually I'm using ngrok for tunneling it. Do you think that is my problem? Or is it a bug? Because sometimes I have delay too, both in my project and in your examples.

Middleware Support [Feature Request]

Hey @Charca , I saw your post about BootBot in the Bots Facebook group and after looking at the framework, I really want to try BootBot for my next Messenger bot. I've been using NLP services in most of my bots (specifically Wit.ai) however and would love it if BootBot had support for middleware to do additional processing when a message is received. I could use bot.on('message') to process messages with Wit.ai, but there wouldn't be a way to dispatch on these results using bot.hear(). My solution to this is allowing the BootBot to accept middleware, which are executed when a message is received and add a context which hear() can use to dispatch on. Here is some pseudocode of how it could be used:

'use strict';
const BootBot = require('bootbot');

const bot = new BootBot({
  accessToken: 'FB_ACCESS_TOKEN',
  verifyToken: 'FB_VERIFY_TOKEN',
  appSecret: 'FB_APP_SECRET'
});

// A middleware returns a promise that when resolved adds context
// that can be used to dispatch on hear messages
// hear callbacks are not executed until all middleware have been resolved
bot.addMiddleware((message) => fetchWitAiIntent(message).then(
  (witAiResult) => {witai: {intent: witAiResult.userIntent}}
));

// a predicate can be used to decide what response should be triggered by the bot
function userHungry(payload, middlewareContext) {
  return middlewareContext.witAi.intent === 'hungry';
}

// predicate used to determine if case should be executed
bot.hear(['food', userHungry], (payload, chat, middlewareContext) => {
  // Do something here.
});

I don't know if middleware is in the roadmap or not. Let me know what you think, and if you are fine with it I would love to implement it.

Postback doesn't work for example in your example (threads-example.js) it should give you saying response if you click on help or sittings but it didn't

`bot.setPersistentMenu([
{
type: 'postback',
title: 'Help',
payload: 'PERSISTENT_MENU_HELP'
},
{
type: 'postback',
title: 'Settings',
payload: 'PERSISTENT_MENU_SETTINGS'
},
{
type: 'web_url',
title: 'Go to Website',
url: 'http://yostik.io'
}
]);

bot.on('postback:PERSISTENT_MENU_HELP', (payload, chat) => {
chat.say(I'm here to help!);
});

bot.on('postback:PERSISTENT_MENU_SETTINGS', (payload, chat) => {
chat.say(Here are your settings: ...);
});`

chat.sendGenericTemplate() is not completing

hello, i am trying to send a generic template but it starts sending and never completes! what is wrong with what i am doing?

  `bot.on('postback:yes-order', (payload, chat) => {
chat.sendGenericTemplate({

    elements:[
      {
        title:"Cold Subs",
        image_url:"https://www.jerseymikes.com/media/static/category-photos/cold-subs.jpg",
        subtitle:"Great cold fast and ready with a smile",
            buttons:[
          {
            type:"postback",
            title:"SELECT",
            payload:"cold-subs"
          }              
        ]
      },
      {
        title:"Hot Subs",
        image_url:"https://www.jerseymikes.com/media/static/category-photos/hot-subs.jpg",
        subtitle:"Fresh grilled and mouth watering goodness",
        buttons:[

          {
            type:"postback",
            title:"SELECT",
            payload:"hot-subs"
          }              
        ]
      },
      {
       title:"Wraps",
        image_url:"https://www.jerseymikes.com/media/static/category-photos/wraps.jpg",
        subtitle:"Wraps wrapped to perfect prefection",
        buttons:[

          {
            type:"postback",
            title:"SELECT",
            payload:"cold-subs"
          }              
        ]
      }
    ]

});`

can @Charca or anyone else show me the right way? thanks!

postback with regex or wildcard or parameter

Hi,
First, thanks for this Awesome library.
I'm using it and enjoying every moment.

I brought up a pretty nice project of my own.
I now ran into an issue where I have a list of items with buttons.
I want to define a postback so that when the user presses one of the buttons I will arrive at the same function and handle same just with different parameter.
Can you help to recommend how to easily solve this?

Should I use postback with regex? If so, how?

Thanks.

How to trigger an event programmatically ?

Let's say I have an event like this

bot.on('postback:HELP', (payload, chat) => {
  chat.say('need help');
});

how to trigger this event when for example the user says "help"

bot.hear('help', (payload, chat) => {
  // trigger "postback:HELP"
});

Are you using BootBot in production?

Hey folks,

I'm planning on adding a section to the repo's documentation featuring bots built using BootBot that are already in production for people to try out. I just found out that the amazing Game of Thrones chatbot GoTBot uses this framework, and I thought it'd be pretty cool to showcase other users.

So if you have a chatbot built with BootBot that you'd like to showcase on the repo, please drop a link in this issue. Thanks!

messages not getting delivered

for some reason my bot is not working with this framework. i have no reason why its not receiving messages? screen shot 2016-08-13 at 6 15 03 am
the code i am using

`const BootBot = require('bootbot');

const bot = new BootBot({
accessToken: process.env.access_token,
verifyToken: process.env.verify_token,
appSecret: process.env.app_secret
});

bot.hear('ask me something', (payload, chat) => {
chat.conversation((convo) => {
    askName(convo);
});

const askName = (convo) => {
    convo.ask(`What's your name?`, (payload, convo) => {
        const text = payload.message.text;
        convo.set('name', text);
        convo.say(`Oh, your name is ${text}`).then(() => askFavoriteFood(convo));
    });
};

const askFavoriteFood = (convo) => {
    convo.ask(`What's your favorite food?`, (payload, convo) => {
        const text = payload.message.text;
        convo.set('food', text);
        convo.say(`Got it, your favorite food is ${text}`).then(() => sendSummary(convo));
    });
};

const sendSummary = (convo) => {
    convo.say(`Ok, here's what you told me about you:
      - Name: ${convo.get('name')}
      - Favorite Food: ${convo.get('food')}`);
  convo.end();
};
});
bot.start();`

any clues as to why it is not sending? any help would be appreciated.

Account Linking

Is it possible to use account linking feature with bootbot?

wit.ai

Hi,
I think it would be great if there was an example for integrating wit.ai with bootbot, among other nlp service providers out there. Since ultimately most users would want to do more that give straight hard-coded responses from their bot. This would totally speed up things when you working with bootbot.
This is not entirely not limiting to wit, but luis and others out there

Nested conversation.ask() don't fire

Hello,

When i execute this code , the program get stucked on the second recursion of getResponse().
Both someFunctionWhoGenerateResponse and generateGreeting function return valid string and work perfectly.

`
const bot = new BootBot({
accessToken: config.get('accessToken'),
verifyToken: config.get('verifyToken'),
appSecret: config.get('appSecret')
});

function getResponse(txt, convo) {
var rps = someFunctionWhoGenerateResponse(txt);
convo.sendTypingIndicator();
convo.ask(rps, (payload, convo, data) => {
convo.say("hum");
getResponse(payload.message.text, convo);
});
}

function sendGreeting(response, convo) {
var init = generateGreeting();
convo.ask(init, (payload, convo, data) => {
getResponse(payload.message.text, convo);
});
}

bot.hear('hello', (payload, chat) => {
chat.conversation((convo)=> {
sendGreeting(payload.message.text, convo);
});
});
`

How to communicate with different pages with the same app

I want to make generic bot the can communicate with different pages knowing from which page the message received and response to the user in that specific page.

For knowing which page I think It can be done easily from the recipient attribute like this

bot.on('message', (payload, chat) => {
switch(payload.recipient) {
  case '12334784': //page id
    // do something
    break;
  case '9877654': //page id
    // do something
    break;
}
});

But in the first place BootBot accept only one page access token

const bot = new BootBot({
  accessToken: 'PAGE_ACCESS_TOKEN',
  verifyToken: 'FB_VERIFY_TOKEN',
  appSecret: 'FB_APP_SECRET'
});

What is the best way to accomplish such thing? How to accept messages from different pages?

Persistent menu action during a conversation

Hi,

From what I understand, bot.on and bot.hear are bypassed during a conversation.
Since callback from persistent menu are handled in bot.hear, how can we handle those events during a conversation ?

Error: Couldn't validate the request signature.

Hi,
Since today, I receive the error in console: Error: Couldn't validate the request signature.

The bot continue to work, but I think that's not good and in my research, I didn't find a solution for this.
Thanks!

postback while in conversation do nothing

Currently, when a user clicks on a button the bot doesn't respond on the first time, the user should click twice to execute the postback action

bot: what is your name?
user: Charca
bot: and your age?
user: <click on About button>
bot: (do nothing)
user: <click on About button>
bot: I am a bot that helps you order food

How to close the conversation and execute the postback function when a user clicks on a button?

bot: what is your name?
user: Charca
bot: and your age?
user: <click on About button>
bot: I am a bot that helps you order food

Signature validation error.

I want my bot to accept HTTP requests as well so for now what I'm doing something like this

bot.app.get('/', (req, res) => { //Some other stuff });

Now when I'm trying to do POST request with JSON data I was getting "Couldn't validate the request signature." error so for this I had to remove the middleware.

bot.app._router.stack = bot.app._router.stack.slice(0,2)

What I propose is in _verifyRequestSignature check for webhook if it is not let it go as a normal web request.

Doubt about workflow using the bootbot-cli.

Hello, I am a beginner in chatbots development and a friend recommended me the bootbot framework, I really liked it! I also found this CLI, but I am in doubt about how I will develop using it.

I need to run bootbot start every time I change a file?
I need to run bootbot start together with nodemon index.js?
I need to run set up my webhook URLs every time I start developing?

I'm asking these questions because I ran the project using both ways, but sometimes I felt a delay between the commands on my machine and noticed the bot working in Messenger, so I could not identify where the errors were and even know If there was any mistake or the project was not online.

And congratulations for the brilliant work! @Charca @pferdefleisch

Add support for content_type: "location" and "image_url" on Quick Replies

We need to refactor the _formatQuickReplies method to support an object with an open format. New types of quick replies format added since we implemented this methods are:

Location:

    {
        "content_type":"location",
    }

Text with Image:

    {
        "content_type":"text",
        "title":"Green",
        "payload":"DEVELOPER_DEFINED_PAYLOAD_FOR_PICKING_GREEN",
        "image_url":"http://petersfantastichats.com/img/green.png"
    }

But more formats will likely be added in the future so we should support whatever object the user sends (or auto-format like we do now if it's a string).

Facebook Docs: https://developers.facebook.com/docs/messenger-platform/send-api-reference/quick-replies

Error: The parameter user_id is required

Hi,
I'm using bot with conversation as bellow

  bot.on('postback:QUIZ_TEST', function(payload, chat) {
    chat.say("Let' do quiz test", {typing: true}).then(function() {
      chat.conversation(askQuiz)
    })
  })

Then I faced this kind of error.

Error sending message: FetchError: request to https://graph.facebook.com/v2.6/me/messages?access_token=xxx failed, reason: getaddrinfo ENOTFOUND graph.facebook.com graph.facebook.com:443

When I query by this url via browser I got a message from response of FB

{
error: {
message: "(#100) For field 'messages': The parameter user_id is required",
type: "OAuthException",
code: 100,
fbtrace_id: "BQLNcgIP4cd"
}
}

It's seem like chat or convo which I used was missing recipient_id (user_id).
I can explicit pass it by using only bot.say(userId, ...) but it's inconvenience.

Thanks in advance.

Persistent menu problem

Hi there! I wrote a comment on the issue #24 thinking that my problem had some sort of connection with that bug but I was wrong.
Randomly whenever it wants, my app won't respond to any persistent menu button even for hours sometime, but will work on bot.on('message') callback.

My app is hosted on heroku and it seems that the problem doesn't respond to any logic, it happens really in a random way, there are no constants to consider (like the problem may appear at the resume of the heroku dyno from sleeping) and the bad news is that when this happen, my bot doesn't log anything, it's like Facebook is not calling the webhook in that situation.
So I don't even know how to debug this problem (probably it is caused by the library because other bots don't act like this).

I'll post the code ASAP and I must try to run it locally and tunnel it in order to consider or not heroku as a problem. Currently I'm running it with nodemon.
Sometimes refreshing the Facebook page on the browser and deleting the conversation on the android app solve the problems, sometimes not!

Ps: is it normal that the bot logs four or five POST requests for only one message sent?

composer_input_disabled not working

Hi, I'm trying to disable user input in my bot, but it doesn't seams to work. Here is my persistent menu setup code:

bot.setPersistentMenu(
[
    {
      "locale":"default",
      "call_to_actions":[
        {
          "type":"postback",
          "title":"Español",
          "payload":"ES"
        },
		{
          "type":"postback",
          "title":"English",
          "payload":"EN"
        }
      ]
    },
    {
      "locale":"zh_CN",
      "composer_input_disabled":false
    }
  ]
, true);

All the bot interaction will be made with quickreplies and the persistent menu to set the language.

Regards

Align on configuration variable names

I'm using the bootbot-cli tool and the config names are using underscores, whereas bootbot is using camelCase. I was going to put a PR together, but wasn't sure which one should change.

LMKWYT.

Trouble uploading code to heroku

Whenever I try to upload this code to heroku I get this error

Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch

I solved this problem by changing this line of code from BootBot.js (line 25)
this.app.set('port', port || 5000)

to this
this.app.set('port', process.env.PORT || 5000)

then it work like magic, because now heroku will be able to bind to it

Allow account_linking type event to propagate from webhook to allow handling

First of all thanks for the awesome library!! Nicely written and architected!

Facebook's latest Webhook reference (https://developers.facebook.com/docs/messenger-platform/webhook-reference/account-linking) supports account linking process to Link Messenger account to 3rd party accounts. It will be nice If Bootbot will allow account_linking type event to propagate.

This is a one line change, that I will be happy to submit, or feel free to add it or let me know if it does not make any sense.

In Bootbot.js in _initWebhook function:

if (event.optin) {
......
} else { console.log('Webhook received unknown event: ', event); }

before the final else add a new line

if (event.optin) {
....
} else if (event.read) {
this._handleEvent('read', event);
}else if (event.account_linking){ // Add this to support account linking
this._handleEvent('account_linking', event); // Add this to support account linking
} else {
console.log('Webhook received unknown event: ', event);
}

Thanks in advance

How to see debug messages or logs?

When I send templates sometimes it didn't show in chat and I don't know what is the reason.

But after a long time trying I discover a syntax error in my template or my elements has more than the maximum allowed elements. This errors are easy to solve but sometimes hard to find.

So is there any logs that I can see? or any debug event?

webhooks failing on heroku app

hello, my webhooks seem to be failing. facebook is sending me alerts saying my webhooks are failing when using this framework. i am hosting node js on heroku. could someone point me in the right direction as far as getting it working? thanks!

Conversation: multiple answers

I faced with problem that users sometimes answers to same question in few messages.
Any ideas how to workaround this?

Add other webhooks

I am trying to use the api.ai webhook but i am getting an error.

Error: Couldn't validate the request signature.
at BootBot._verifyRequestSignature (F:\Bot\Plan_Arvana\node_modules\bootbot\lib\BootBot.js:445:13)
at F:\Bot\Plan_Arvana\node_modules\body-parser\lib\read.js:100:9
at invokeCallback (F:\Bot\Plan_Arvana\node_modules\raw-body\index.js:262:16)
at done (F:\Bot\Plan_Arvana\node_modules\raw-body\index.js:251:7)
at IncomingMessage.onEnd (F:\Bot\Plan_Arvana\node_modules\raw-body\index.js:307:7)
at emitNone (events.js:86:13)
at IncomingMessage.emit (events.js:185:7)
at endReadableNT (_stream_readable.js:974:12)
at _combinedTickCallback (internal/process/next_tick.js:74:11)
at process._tickCallback (internal/process/next_tick.js:98:9)

anyhelp??

clarification on where to get app developer keys

I have made an app project on Facebook and am only seemingly able to find an App Secret and an App ID.

I still do not know where to find the following

  1. access_token
  2. verify_token

I know this is not a product feature of Bootbot but rather of Facebook... but I was not able to find this information in their documentation.

It would be really helpful to include pointers/directions in the Getting Started section!

Thanks for the help and the cool project man 👍

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.