[
  {
    "path": ".gitignore",
    "content": "# Folder view configuration files\n.DS_Store\nDesktop.ini\n\n# Thumbnail cache files\n._*\nThumbs.db\n\n# Files that might appear on external disks\n.Spotlight-V100\n.Trashes\n\nexample_jd.js\nslack_button.html\ntesting_creds.md\ntests/config.js\ntests/_config.js\n\n# Application specific files\nnode_modules\nnpm-debug.log\n.sass-cache\n\n/logs\njsconfig.json\n.vscode\ncoverage\n.nyc_output\njsdoc"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n- '9'\n- '8'\n- '7'\n- '6'\n- '4'\n\nafter_success: npm run coveralls\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016, 2017 IBM\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE."
  },
  {
    "path": "Readme.md",
    "content": "# Botmaster\n\n[![Build Status](https://travis-ci.org/botmasterai/botmaster.svg?branch=master)](https://travis-ci.org/botmasterai/botmaster)\n[![Coverage Status](https://coveralls.io/repos/github/botmasterai/botmaster/badge.svg?branch=master)](https://coveralls.io/github/botmasterai/botmaster?branch=master)\n[![Dependency Status](https://gemnasium.com/badges/github.com/botmasterai/botmaster.svg)](https://gemnasium.com/github.com/botmasterai/botmaster)\n[![npm-version](https://img.shields.io/npm/v/botmaster.svg)](https://www.npmjs.com/package/botmaster)\n[![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)](LICENSE)\n\nBotmaster v3 out.\n---\n\nBotmaster v3 is virtually a complete rewrite of the framework. A lot of the syntax remains the same,\nbut there are quite a few breaking changes that were necessary in order to get the framework\nto where we wanted it to be. It is now truly a micro-framework. With only 2 dependencies and without\nrelying on express anymore, Botmaster v3 is the only JS bot framework that gives as much control\nas possible to the developer without losing its ease of use.\n\nA migration documentation can be found at: https://github.com/botmasterai/botmasterai.github.io/blob/master/docs/changelog.md#major-308\n\nBotmaster is a lightweight chatbot framework. Its purpose is to integrate your existing chatbot into a variety of messaging channels - currently Facebook Messenger, Twitter DM and Telegram.\n\n## Documentation\n\nFind the whole documentation for the framework here: https://github.com/botmasterai/botmasterai.github.io/tree/master/docs\n\n## License\n\nThis library is licensed under the MIT [license](LICENSE)\n"
  },
  {
    "path": "api-reference/base-bot.md",
    "content": "<a name=\"BaseBot\"></a>\n\n## BaseBot\nThe class from which all Bot classes mus inherit. It contains all the base\nmethods that are accessible via all bot classes. Classes that inherit from\nBaseBot and want to make implementation specific methods available have to\nprepend the method name with an underscore; e.g. in botmaster-messenger:\n_getGetStartedButton\n\n**Kind**: global class  \n\n* [BaseBot](#BaseBot)\n    * [new BaseBot(settings)](#new_BaseBot_new)\n    * _instance_\n        * [.createOutgoingMessage(message)](#BaseBot+createOutgoingMessage) ⇒ <code>OutgoingMessage</code>\n        * [.createOutgoingMessageFor(recipientId)](#BaseBot+createOutgoingMessageFor) ⇒ <code>OutgoingMessage</code>\n        * [.sendMessage(message, [sendOptions])](#BaseBot+sendMessage) ⇒ <code>Promise</code>\n        * [.sendMessageTo(message, recipientId, [sendOptions])](#BaseBot+sendMessageTo) ⇒ <code>Promise</code>\n        * [.sendTextMessageTo(text, recipientId, [sendOptions])](#BaseBot+sendTextMessageTo) ⇒ <code>Promise</code>\n        * [.reply(incomingUpdate, text, [sendOptions])](#BaseBot+reply) ⇒ <code>Promise</code>\n        * [.sendAttachmentTo(attachment, recipientId, [sendOptions])](#BaseBot+sendAttachmentTo) ⇒ <code>Promise</code>\n        * [.sendAttachmentFromUrlTo(type, url, recipientId, [sendOptions])](#BaseBot+sendAttachmentFromUrlTo) ⇒ <code>Promise</code>\n        * [.sendDefaultButtonMessageTo(buttonTitles, textOrAttachment, recipientId, [sendOptions])](#BaseBot+sendDefaultButtonMessageTo) ⇒ <code>Promise</code>\n        * [.sendIsTypingMessageTo(recipientId, [sendOptions])](#BaseBot+sendIsTypingMessageTo) ⇒ <code>Promise</code>\n        * [.sendCascade(messageArray, [sendOptions])](#BaseBot+sendCascade) ⇒ <code>Promise</code>\n        * [.sendTextCascadeTo(textArray, recipientId, [sendOptions])](#BaseBot+sendTextCascadeTo) ⇒ <code>Promise</code>\n        * [.sendRawMessage(rawMessage)](#BaseBot+sendRawMessage) ⇒ <code>Promise</code>\n        * [.getUserInfo(userId)](#BaseBot+getUserInfo) ⇒ <code>Promise</code>\n    * _static_\n        * [.createOutgoingMessage(message)](#BaseBot.createOutgoingMessage) ⇒ <code>OutgoingMessage</code>\n        * [.createOutgoingMessageFor(recipientId)](#BaseBot.createOutgoingMessageFor) ⇒ <code>OutgoingMessage</code>\n\n<a name=\"new_BaseBot_new\"></a>\n\n### new BaseBot(settings)\nConstructor to the BaseBot class from which all the bot classes inherit.\nA set a basic functionalities are defined here that have to be implemented\nin the subclasses in order for them to work.\n\n\n| Param | Type | Description |\n| --- | --- | --- |\n| settings | <code>object</code> | inheritors of BaseBot take a settings object as first param. |\n\n<a name=\"BaseBot+createOutgoingMessage\"></a>\n\n### baseBot.createOutgoingMessage(message) ⇒ <code>OutgoingMessage</code>\ncreateOutgoingMessage exposes the OutgoingMessage constructor\nvia BaseBot. This simply means one can create their own\nOutgoingMessage object using any bot object. They can then compose\nit with all its helper functions\n\nThis is the instance version of this method\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>OutgoingMessage</code> - outgoingMessage. The same object passed in with\nall the helper functions from OutgoingMessage  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| message | <code>object</code> | base object that the outgoing Message should be based on |\n\n<a name=\"BaseBot+createOutgoingMessageFor\"></a>\n\n### baseBot.createOutgoingMessageFor(recipientId) ⇒ <code>OutgoingMessage</code>\nsame as #createOutgoingMessage, creates empty outgoingMessage with\nid of the recipient set. Again, this is jut sugar syntax for creating a\nnew outgoingMessage object\n\nThis is the instance version of this method\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>OutgoingMessage</code> - outgoingMessage. A valid OutgoingMessage object with recipient set.  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| recipientId | <code>string</code> | id of the recipient the message is for |\n\n<a name=\"BaseBot+sendMessage\"></a>\n\n### baseBot.sendMessage(message, [sendOptions]) ⇒ <code>Promise</code>\nsendMessage() falls back to the sendMessage implementation of whatever\nsubclass inherits form BaseBot. The expected format is normally any type of\nmessage object that could be sent on to messenger\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>Promise</code> - promise that resolves with a body object (see example)  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| message | <code>object</code> |  |\n| [sendOptions] | <code>boolean</code> | an object containing options regarding the sending of the message. Currently the only valid options is: `ignoreMiddleware`. |\n\n**Example**  \n```js\nconst outgoingMessage = bot.createOutgoingMessageFor(update.sender.id);\noutgoingMessage.addText('Hello world');\n\nbot.sendMessage(outgoingMessage);\n```\n**Example**  \n```js\n// The returned promise for all sendMessage type events resolves with\n// a body that looks something like this:\n {\n  sentOutgoingMessage: // the OutgoingMessage instance before being formatted\n  sentRawMessage: // the OutgoingMessage object after being formatted for the platforms\n  raw: rawBody, // the raw response from the platforms received from sending the message\n  recipient_id: <id_of_user>,\n  message_id: <message_id_of_what_was_just_sent>\n }\n\n// Some platforms may not have either of these parameters. If that's the case,\n// the value assigned will be a falsy value\n```\n<a name=\"BaseBot+sendMessageTo\"></a>\n\n### baseBot.sendMessageTo(message, recipientId, [sendOptions]) ⇒ <code>Promise</code>\nsendMessageTo() Just makes it easier to send a message without as much\nstructure.\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>Promise</code> - promise that resolves with a body object\n(see `sendMessage` example)  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| message | <code>object</code> | NOT an instance of OutgoingMessage. Use #sendMessage if you want to send instances of OutgoingMessage |\n| recipientId | <code>string</code> | a string representing the id of the user to whom you want to send the message. |\n| [sendOptions] | <code>object</code> | see `sendOptions` for `sendMessage` |\n\n**Example**  \n```js\n// message object can look something like this:\n// as you can see, this is not an OutgoingMessage instance\nconst message = {\n text: 'Some random text'\n}\n\nbot.sendMessageTo(message, update.sender.id);\n```\n<a name=\"BaseBot+sendTextMessageTo\"></a>\n\n### baseBot.sendTextMessageTo(text, recipientId, [sendOptions]) ⇒ <code>Promise</code>\nsendTextMessageTo() Just makes it easier to send a text message with\nminimal structure.\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>Promise</code> - promise that resolves with a body object\n(see `sendMessage` example)  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| text | <code>string</code> |  |\n| recipientId | <code>string</code> | a string representing the id of the user to whom you want to send the message. |\n| [sendOptions] | <code>object</code> | see `sendOptions` for `sendMessage` |\n\n**Example**  \n```js\nbot.sendTextMessageTo('something super important', update.sender.id);\n```\n<a name=\"BaseBot+reply\"></a>\n\n### baseBot.reply(incomingUpdate, text, [sendOptions]) ⇒ <code>Promise</code>\nreply() Another way to easily send a text message. In this case,\nwe just send the update that came in as is and then the text we\nwant to send as a reply.\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>Promise</code> - promise that resolves with a body object\n(see `sendMessage` example)  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| incomingUpdate | <code>object</code> |  |\n| text | <code>string</code> | text to send to the user associated with the received update |\n| [sendOptions] | <code>object</code> | see `sendOptions` for `sendMessage` |\n\n**Example**  \n```js\nbot.reply(update, 'something super important!');\n```\n<a name=\"BaseBot+sendAttachmentTo\"></a>\n\n### baseBot.sendAttachmentTo(attachment, recipientId, [sendOptions]) ⇒ <code>Promise</code>\nsendAttachmentTo() makes it easier to send an attachment message with\nless structure.\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>Promise</code> - promise that resolves with a body object\n(see `sendMessage` example)  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| attachment | <code>object</code> | a valid Messenger style attachment. See [here](https://developers.facebook.com/docs/messenger-platform/send-api-reference) for more on that. |\n| recipientId | <code>string</code> | a string representing the id of the user to whom you want to send the message. |\n| [sendOptions] | <code>object</code> | see `sendOptions` for `sendMessage` |\n\n**Example**  \n```js\n// attachment object typically looks something like this:\nconst attachment = {\n  type: 'image',\n  payload: {\n    url: \"some_valid_url_of_some_image\"\n  },\n};\n\nbot.sendAttachmentTo(attachment, update.sender.id);\n```\n<a name=\"BaseBot+sendAttachmentFromUrlTo\"></a>\n\n### baseBot.sendAttachmentFromUrlTo(type, url, recipientId, [sendOptions]) ⇒ <code>Promise</code>\nsendAttachmentFromUrlTo() makes it easier to send an attachment message with\nminimal structure.\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>Promise</code> - promise that resolves with a body object\n(see `sendMessage` example)  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| type | <code>string</code> | string representing the type of attachment (audio, video, image or file) |\n| url | <code>string</code> | the url to your file |\n| recipientId | <code>string</code> | a string representing the id of the user to whom you want to send the message. |\n| [sendOptions] | <code>object</code> | see `sendOptions` for `sendMessage` |\n\n**Example**  \n```js\nbot.sendAttachmentFromUrlTo('image', \"some image url you've got\", update.sender.id);\n```\n<a name=\"BaseBot+sendDefaultButtonMessageTo\"></a>\n\n### baseBot.sendDefaultButtonMessageTo(buttonTitles, textOrAttachment, recipientId, [sendOptions]) ⇒ <code>Promise</code>\nsendDefaultButtonMessageTo() makes it easier to send a default set of\nbuttons. The default button type is the Messenger quick_replies, where\nthe payload is the same as the button title and the content_type is text.\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>Promise</code> - promise that resolves with a body object\n(see `sendMessage` example)  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| buttonTitles | <code>Array</code> | array of button titles (no longer than 10 in size). |\n| textOrAttachment | <code>string_OR_object</code> | a string or an attachment object similar to the ones required in `bot.sendAttachmentTo`. This is meant to provide context to the buttons. I.e. why are there buttons here. A piece of text or an attachment could detail that. If falsy, text will be added that reads: 'Please select one of:'. |\n| recipientId | <code>string</code> | a string representing the id of the user to whom you want to send the message. |\n| [sendOptions] | <code>object</code> | see `sendOptions` for `sendMessage` |\n\n**Example**  \n```js\nconst buttonArray = ['button1', 'button2'];\nbot.sendDefaultButtonMessageTo(buttonArray,\n  'Please select \"button1\" or \"button2\"', update.sender.id,);\n```\n<a name=\"BaseBot+sendIsTypingMessageTo\"></a>\n\n### baseBot.sendIsTypingMessageTo(recipientId, [sendOptions]) ⇒ <code>Promise</code>\nsendIsTypingMessageTo() just sets the is typing status to the platform\nif available.\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>Promise</code> - promise that resolves with a body object\n(see `sendMessage` example)  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| recipientId | <code>string</code> | a string representing the id of the user to whom you want to send the message. |\n| [sendOptions] | <code>object</code> | see `sendOptions` for `sendMessage` |\n\n**Example**  \n```js\nbot.sendIsTypingMessageTo(update.sender.id);\n// the returned value is different from the standard one. it won't have a message_id\n```\n<a name=\"BaseBot+sendCascade\"></a>\n\n### baseBot.sendCascade(messageArray, [sendOptions]) ⇒ <code>Promise</code>\nsendCascade() allows developers to send a cascade of messages\nin a sequence. All types of messages can be sent (including raw messages).\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>Promise</code> - promise that resolves with an array of body objects\n(see `sendMessage` example for one said object)  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| messageArray | <code>Array</code> | of messages in a format as such: [{raw: someRawObject}, {message: some valid outgoingMessage}] |\n| [sendOptions] | <code>object</code> | see `sendOptions` for `sendMessage`. will only apply to non rawMessages. (remember that for rawMessages, outgoing middleware is bypassed anyways). |\n\n**Example**  \n```js\nconst rawMessage1 = {\n  nonStandard: 'message1',\n  recipient: {\n    id: 'user_id',\n  },\n};\nconst message2 = bot.createOutgoingMessageFor(update.sender.id);\nmessage2.addText('some text');\n\nconst messageArray = [{ raw: rawMessage1 }, { message: message2 }];\n\nbot.sendCascade(messageArray);\n```\n<a name=\"BaseBot+sendTextCascadeTo\"></a>\n\n### baseBot.sendTextCascadeTo(textArray, recipientId, [sendOptions]) ⇒ <code>Promise</code>\nsendTextCascadeTo() is simply a helper function around sendCascadeTo.\nIt allows developers to send a cascade of text messages more easily.\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>Promise</code> - promise that resolves with an array of body objects\n(see `sendMessage` example for one said object)  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| textArray | <code>Array</code> | of messages. |\n| recipientId | <code>string</code> | a string representing the id of the user to whom you want to send the message. |\n| [sendOptions] | <code>object</code> | see `sendOptions` for `sendMessage` |\n\n**Example**  \n```js\nbot.sendTextCascadeTo(['message1', 'message2'], user.sender.id);\n```\n<a name=\"BaseBot+sendRawMessage\"></a>\n\n### baseBot.sendRawMessage(rawMessage) ⇒ <code>Promise</code>\nsendRawMessage() simply sends a raw platform dependent message. This method\ncalls __sendMessage in each botClass without calling formatOutgoingMessage\nbefore. It's really just sugar around __sendMessage which shouldn't be used\ndirectly.\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>Promise</code> - promise  \n\n| Param | Type |\n| --- | --- |\n| rawMessage | <code>Object</code> | \n\n<a name=\"BaseBot+getUserInfo\"></a>\n\n### baseBot.getUserInfo(userId) ⇒ <code>Promise</code>\nRetrieves the basic user info from a user if platform supports it\n\n**Kind**: instance method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>Promise</code> - promise that resolves into the user info or an empty\nobject by default  \n\n| Param | Type |\n| --- | --- |\n| userId | <code>string</code> | \n\n<a name=\"BaseBot.createOutgoingMessage\"></a>\n\n### BaseBot.createOutgoingMessage(message) ⇒ <code>OutgoingMessage</code>\ncreateOutgoingMessage exposes the OutgoingMessage constructor\nvia BaseBot. This simply means one can create their own\nOutgoingMessage object using any bot object. They can then compose\nit with all its helper functions\n\nThis is the static version of this method\n\n**Kind**: static method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>OutgoingMessage</code> - outgoingMessage. The same object passed in with\nall the helper functions from OutgoingMessage  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| message | <code>object</code> | base object that the outgoing Message should be based on |\n\n<a name=\"BaseBot.createOutgoingMessageFor\"></a>\n\n### BaseBot.createOutgoingMessageFor(recipientId) ⇒ <code>OutgoingMessage</code>\nsame as #createOutgoingMessage, creates empty outgoingMessage with\nid of the recipient set. Again, this is jut sugar syntax for creating a\nnew outgoingMessage object\n\nThis is the static version of this method\n\n**Kind**: static method of [<code>BaseBot</code>](#BaseBot)  \n**Returns**: <code>OutgoingMessage</code> - outgoingMessage. A valid OutgoingMessage object with recipient set.  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| recipientId | <code>string</code> | id of the recipient the message is for |\n\n"
  },
  {
    "path": "api-reference/botmaster.md",
    "content": "<a name=\"Botmaster\"></a>\n\n## Botmaster\nThe Botmaster class to rule them all\n\n**Kind**: global class  \n\n* [Botmaster](#Botmaster)\n    * [new Botmaster(settings)](#new_Botmaster_new)\n    * [.addBot(bot)](#Botmaster+addBot) ⇒ [<code>Botmaster</code>](#Botmaster)\n    * [.getBot(options)](#Botmaster+getBot) ⇒ <code>BaseBot</code>\n    * [.getBots(botType)](#Botmaster+getBots) ⇒ <code>Array</code>\n    * [.removeBot(bot)](#Botmaster+removeBot) ⇒ [<code>Botmaster</code>](#Botmaster)\n    * [.use(middleware)](#Botmaster+use) ⇒ [<code>Botmaster</code>](#Botmaster)\n    * [.useWrapped(incomingMiddleware, outgoingMiddleware)](#Botmaster+useWrapped) ⇒ [<code>Botmaster</code>](#Botmaster)\n\n<a name=\"new_Botmaster_new\"></a>\n\n### new Botmaster(settings)\nsets up a botmaster object attached to the correct server if one is set\nas a parameter. If not, it creates its own http server\n\n\n| Param | Type |\n| --- | --- |\n| settings | <code>object</code> | \n\n<a name=\"Botmaster+addBot\"></a>\n\n### botmaster.addBot(bot) ⇒ [<code>Botmaster</code>](#Botmaster)\nAdd an existing bot to this instance of Botmaster\n\n**Kind**: instance method of [<code>Botmaster</code>](#Botmaster)  \n**Returns**: [<code>Botmaster</code>](#Botmaster) - returns the botmaster object for chaining  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| bot | <code>BaseBot</code> | the bot object to add to botmaster. Must be from a subclass of BaseBot |\n\n<a name=\"Botmaster+getBot\"></a>\n\n### botmaster.getBot(options) ⇒ <code>BaseBot</code>\nExtract First bot of given type or provided id.\n\n**Kind**: instance method of [<code>Botmaster</code>](#Botmaster)  \n**Returns**: <code>BaseBot</code> - The bot found of a class that inherits of BaseBot  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| options | <code>object</code> | must be { type: 'someBotType} or { id: someBotId }. |\n\n<a name=\"Botmaster+getBots\"></a>\n\n### botmaster.getBots(botType) ⇒ <code>Array</code>\nExtract all bots of given type.\n\n**Kind**: instance method of [<code>Botmaster</code>](#Botmaster)  \n**Returns**: <code>Array</code> - Array of bots found  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| botType | <code>string</code> | (there can be multiple bots of a same type) |\n\n<a name=\"Botmaster+removeBot\"></a>\n\n### botmaster.removeBot(bot) ⇒ [<code>Botmaster</code>](#Botmaster)\nRemove an existing bot from this instance of Botmaster\n\n**Kind**: instance method of [<code>Botmaster</code>](#Botmaster)  \n**Returns**: [<code>Botmaster</code>](#Botmaster) - returns the botmaster object for chaining  \n\n| Param | Type |\n| --- | --- |\n| bot | <code>Object</code> | \n\n<a name=\"Botmaster+use\"></a>\n\n### botmaster.use(middleware) ⇒ [<code>Botmaster</code>](#Botmaster)\nAdd middleware to this botmaster object\nThis function is just sugar for `middleware.__use` in them\n\n**Kind**: instance method of [<code>Botmaster</code>](#Botmaster)  \n**Returns**: [<code>Botmaster</code>](#Botmaster) - returns the botmaster object so you can chain middleware  \n\n| Param | Type |\n| --- | --- |\n| middleware | <code>object</code> | \n\n**Example**  \n```js\n// The middleware param object is something that looks like this for incoming:\n{\n type: 'incoming',\n name: 'my-incoming-middleware',\n controller: (bot, update, next) => {\n   // do stuff with update,\n   // call next (or return a promise)\n },\n // includeEcho: true (defaults to false), opt-in to get echo updates\n // includeDelivery: true (defaults to false), opt-in to get delivery updates\n // includeRead: true (defaults to false), opt-in to get user read updates\n}\n\n// and like this for outgoing middleware\n\n{\n type: 'outgoing',\n name: 'my-outgoing-middleware',\n controller: (bot, update, message, next) => {\n   // do stuff with message,\n   // call next (or return a promise)\n }\n}\n```\n<a name=\"Botmaster+useWrapped\"></a>\n\n### botmaster.useWrapped(incomingMiddleware, outgoingMiddleware) ⇒ [<code>Botmaster</code>](#Botmaster)\nAdd wrapped middleware to this botmaster instance. Wrapped middleware\nplaces the incoming middleware at beginning of incoming stack and\nthe outgoing middleware at end of outgoing stack.\nThis function is just sugar `middleware.useWrapped`.\n\n**Kind**: instance method of [<code>Botmaster</code>](#Botmaster)  \n**Returns**: [<code>Botmaster</code>](#Botmaster) - returns the botmaster object so you can chain middleware  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| incomingMiddleware | <code>object</code> |  |\n| outgoingMiddleware | <code>object</code> | The middleware objects are as you'd expect them to be (see use) |\n\n"
  },
  {
    "path": "api-reference/outgoing-message.md",
    "content": "<a name=\"OutgoingMessage\"></a>\n\n## OutgoingMessage\nThis class will help you compose sendable message objects.\n\n**Kind**: global class  \n\n* [OutgoingMessage](#OutgoingMessage)\n    * [new OutgoingMessage([message])](#new_OutgoingMessage_new)\n    * [.addRecipientById(id)](#OutgoingMessage+addRecipientById) ⇒ <code>OutgoinMessage</code>\n    * [.addRecipientByPhoneNumber(phoneNumber)](#OutgoingMessage+addRecipientByPhoneNumber) ⇒ <code>OutgoinMessage</code>\n    * [.removeRecipient()](#OutgoingMessage+removeRecipient) ⇒ <code>OutgoinMessage</code>\n    * [.addText(text)](#OutgoingMessage+addText) ⇒ <code>OutgoinMessage</code>\n    * [.removeText()](#OutgoingMessage+removeText) ⇒ <code>OutgoinMessage</code>\n    * [.addAttachment(attachment)](#OutgoingMessage+addAttachment) ⇒ <code>OutgoinMessage</code>\n    * [.addAttachmentFromUrl(type, url)](#OutgoingMessage+addAttachmentFromUrl) ⇒ <code>OutgoinMessage</code>\n    * [.removeAttachment()](#OutgoingMessage+removeAttachment) ⇒ <code>OutgoinMessage</code>\n    * [.addQuickReplies(quickReplies)](#OutgoingMessage+addQuickReplies) ⇒ <code>OutgoinMessage</code>\n    * [.addPayloadLessQuickReplies(quickRepliesTitles)](#OutgoingMessage+addPayloadLessQuickReplies) ⇒ <code>OutgoinMessage</code>\n    * [.addLocationQuickReply()](#OutgoingMessage+addLocationQuickReply) ⇒ <code>OutgoinMessage</code>\n    * [.removeQuickReplies()](#OutgoingMessage+removeQuickReplies) ⇒ <code>OutgoinMessage</code>\n    * [.addSenderAction(senderAction)](#OutgoingMessage+addSenderAction) ⇒ <code>OutgoinMessage</code>\n    * [.addTypingOnSenderAction()](#OutgoingMessage+addTypingOnSenderAction) ⇒ <code>OutgoinMessage</code>\n    * [.addTypingOffSenderAction()](#OutgoingMessage+addTypingOffSenderAction) ⇒ <code>OutgoinMessage</code>\n    * [.addMarkSeenSenderAction()](#OutgoingMessage+addMarkSeenSenderAction) ⇒ <code>OutgoinMessage</code>\n    * [.removeSenderAction()](#OutgoingMessage+removeSenderAction) ⇒ <code>OutgoinMessage</code>\n\n<a name=\"new_OutgoingMessage_new\"></a>\n\n### new OutgoingMessage([message])\nConstructor to the OutgoingMessage class. Takes in an optional\nmessage object that it will use as its base to add the OutgoingMessage\nmethods to. This constructor is not actually exposed in the public API.\nIn order to instantiate an OutgoingMessage object, you'll need to use the\ncreateOutgoingMessage and createOutgoingMessageFor methods provided with\nall classes that inherit from BaseBot. There are static and non-static\nversions of both methods to make sure you can do so wherever as you wish\n\n\n| Param | Type | Description |\n| --- | --- | --- |\n| [message] | <code>object</code> | the base object to convert into an OutgoingMessage object |\n\n<a name=\"OutgoingMessage+addRecipientById\"></a>\n\n### outgoingMessage.addRecipientById(id) ⇒ <code>OutgoinMessage</code>\nAdds `recipient.id` param to the OutgoingMessage object. This is most\nlikely what you will want to do to add a recipient. Alternatively, you Can\nuse addRecipientByPhoneNumber if the platform you are sending the message to\nsupports that.\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| id | <code>string</code> | the id to add to the OutgoingMessage object |\n\n<a name=\"OutgoingMessage+addRecipientByPhoneNumber\"></a>\n\n### outgoingMessage.addRecipientByPhoneNumber(phoneNumber) ⇒ <code>OutgoinMessage</code>\nAdds `recipient.phone_number` param to the OutgoingMessage object.\nYou might prefer to add a recipient by id rather. This is achieved via\naddRecipientById\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| phoneNumber | <code>string</code> | the phone number to add to the OutgoingMessage object |\n\n<a name=\"OutgoingMessage+removeRecipient\"></a>\n\n### outgoingMessage.removeRecipient() ⇒ <code>OutgoinMessage</code>\nremoves the `recipient` param from the OutgoingMessage object.\nThis will remove the object wether it was set with a phone number or an id\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n<a name=\"OutgoingMessage+addText\"></a>\n\n### outgoingMessage.addText(text) ⇒ <code>OutgoinMessage</code>\nAdds `message.text` to the OutgoingMessage\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| text | <code>string</code> | the text to add to the OutgoingMessage object |\n\n<a name=\"OutgoingMessage+removeText\"></a>\n\n### outgoingMessage.removeText() ⇒ <code>OutgoinMessage</code>\nRemoves the `message.text` param from the OutgoingMessage object.\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n<a name=\"OutgoingMessage+addAttachment\"></a>\n\n### outgoingMessage.addAttachment(attachment) ⇒ <code>OutgoinMessage</code>\nAdds `message.attachment` to the OutgoingMessage. If you want to add\nan attachment simply from a type and a url, have a look at:\naddAttachmentFromUrl\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| attachment | <code>object</code> | valid messenger type attachment that can be formatted by the platforms your bot uses |\n\n<a name=\"OutgoingMessage+addAttachmentFromUrl\"></a>\n\n### outgoingMessage.addAttachmentFromUrl(type, url) ⇒ <code>OutgoinMessage</code>\nAdds `message.attachment` from a type and url without requiring you to\nprovide the whole attachment object. If you want to add an attachment using\na full object, use addAttachment.\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| type | <code>string</code> | the attachment type (audio, video, image, file) |\n| url | <code>string</code> | the url of the attachment. |\n\n<a name=\"OutgoingMessage+removeAttachment\"></a>\n\n### outgoingMessage.removeAttachment() ⇒ <code>OutgoinMessage</code>\nRemoves `message.attachment` param from the OutgoingMessage object.\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n<a name=\"OutgoingMessage+addQuickReplies\"></a>\n\n### outgoingMessage.addQuickReplies(quickReplies) ⇒ <code>OutgoinMessage</code>\nAdds `message.quick_replies` to the OutgoinMessage object. Use\naddPayloadLessQuickReplies if you just want to add quick replies from an\narray of titles\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| quickReplies | <code>Array</code> | The quick replies objects to add to the OutgoingMessage |\n\n<a name=\"OutgoingMessage+addPayloadLessQuickReplies\"></a>\n\n### outgoingMessage.addPayloadLessQuickReplies(quickRepliesTitles) ⇒ <code>OutgoinMessage</code>\nAdds `message.quick_replies` to the OutgoinMessage object from a simple array\nof quick replies titles.Use addQuickReplies if want to add quick replies\nfrom an quick reply objects\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| quickRepliesTitles | <code>Array</code> | The titles of the quick replies objects to add to the OutgoingMessage |\n\n<a name=\"OutgoingMessage+addLocationQuickReply\"></a>\n\n### outgoingMessage.addLocationQuickReply() ⇒ <code>OutgoinMessage</code>\nAdds a `content_type: location` message.quick_replies to the OutgoingMessage.\nUse this if the platform the bot class you are using is based on supports\nasking for the location to its users.\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n<a name=\"OutgoingMessage+removeQuickReplies\"></a>\n\n### outgoingMessage.removeQuickReplies() ⇒ <code>OutgoinMessage</code>\nRemoves `message.quick_replies` param from the OutgoingMessage object.\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n<a name=\"OutgoingMessage+addSenderAction\"></a>\n\n### outgoingMessage.addSenderAction(senderAction) ⇒ <code>OutgoinMessage</code>\nAdds an arbitrary `sender_action` to the OutgoinMessage\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| senderAction | <code>string</code> | Arbitrary sender action (typing_on, typing_off or mark_seens) |\n\n<a name=\"OutgoingMessage+addTypingOnSenderAction\"></a>\n\n### outgoingMessage.addTypingOnSenderAction() ⇒ <code>OutgoinMessage</code>\nAdds `sender_action: typing_on` to the OutgoinMessage\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n<a name=\"OutgoingMessage+addTypingOffSenderAction\"></a>\n\n### outgoingMessage.addTypingOffSenderAction() ⇒ <code>OutgoinMessage</code>\nAdds `sender_action: typing_off`  to the OutgoinMessage\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n<a name=\"OutgoingMessage+addMarkSeenSenderAction\"></a>\n\n### outgoingMessage.addMarkSeenSenderAction() ⇒ <code>OutgoinMessage</code>\nAdds `sender_action: mark_seen`  to the OutgoinMessage\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n<a name=\"OutgoingMessage+removeSenderAction\"></a>\n\n### outgoingMessage.removeSenderAction() ⇒ <code>OutgoinMessage</code>\nRemoves `sender_action` param from the OutgoingMessage object.\n\n**Kind**: instance method of [<code>OutgoingMessage</code>](#OutgoingMessage)  \n**Returns**: <code>OutgoinMessage</code> - returns this object to allow for chaining of methods.  \n"
  },
  {
    "path": "lib/.eslintrc.js",
    "content": "module.exports = {\n    \"extends\": \"airbnb\",\n    \"rules\": {\n      \"no-underscore-dangle\": \"off\",\n      \"prefer-rest-params\": \"off\",\n      \"no-restricted-syntax\": \"off\",\n      \"no-param-reassign\": \"off\",\n      \"class-methods-use-this\": \"off\",\n      \"strict\": \"off\",\n    },\n};\n"
  },
  {
    "path": "lib/base_bot.js",
    "content": "'use strict';\n\nconst EventEmitter = require('events');\nconst OutgoingMessage = require('./outgoing_message');\nconst get = require('lodash').get;\nconst TwoDotXError = require('./errors').TwoDotXError;\nconst SendMessageTypeError = require('./errors').SendMessageTypeError;\n\n/**\n * The class from which all Bot classes mus inherit. It contains all the base\n * methods that are accessible via all bot classes. Classes that inherit from\n * BaseBot and want to make implementation specific methods available have to\n * prepend the method name with an underscore; e.g. in botmaster-messenger:\n * _getGetStartedButton\n */\n\nclass BaseBot extends EventEmitter {\n  /**\n   * Constructor to the BaseBot class from which all the bot classes inherit.\n   * A set a basic functionalities are defined here that have to be implemented\n   * in the subclasses in order for them to work.\n   *\n   * @param {object} settings - inheritors of BaseBot take a settings\n   * object as first param.\n   * @example\n   * // In general however, one can instantiate a bot object like this:\n   * const bot = new BaseBotSubClass({ // e.g. MessengerBot\n   *   credentials: <my_base_bot_sub_class_credentials>,\n   *   webhookEnpoint: 'someEndpoint' // only if class requires them\n   * })\n   */\n  constructor() {\n    super();\n    this.type = 'baseBot';\n\n    // just being explicit about what subclasses can send and receive.\n    // anything else they want to implement has to be done in raw mode.\n    // I.e. using bot class events and upon receiving and sendRawMessage for sending.\n\n    this.receives = {\n      text: false,\n      attachment: {\n        audio: false,\n        file: false,\n        image: false,\n        video: false,\n        location: false,\n        // can occur in FB messenger when user sends a message which only contains a URL\n        // most platforms won't support that\n        fallback: false,\n      },\n      echo: false,\n      read: false,\n      delivery: false,\n      postback: false,\n      // in FB Messenger, this will exist whenever a user clicks on\n      // a quick_reply button. It will contain the payload set by the developer\n      // when sending the outgoing message. Bot classes should only set this\n      // value to true if the platform they are building for has an equivalent\n      // to this.\n      quickReply: false,\n    };\n\n    this.sends = {\n      text: false,\n      quickReply: false,\n      locationQuickReply: false,\n      senderAction: {\n        typingOn: false,\n        typingOff: false,\n        markSeen: false,\n      },\n      attachment: {\n        audio: false,\n        file: false,\n        image: false,\n        video: false,\n      },\n    };\n\n    this.retrievesUserInfo = false;\n\n    this.requiresWebhook = false;\n    this.requiredCredentials = [];\n  }\n\n  /**\n  * Just validating the settings and throwing errors or warnings\n  * where appropriate.\n  * @ignore\n  * @param {object} settings\n  */\n  __applySettings(settings) {\n    if (typeof settings !== 'object') {\n      throw new TypeError(`settings must be object, got ${typeof settings}`);\n    }\n\n    if (this.requiredCredentials.length > 0) {\n      if (!settings.credentials) {\n        throw new Error(`no credentials specified for bot of type '${this.type}'`);\n      } else {\n        this.credentials = settings.credentials;\n      }\n\n      for (const credentialName of this.requiredCredentials) {\n        if (!this.credentials[credentialName]) {\n          throw new Error(`bots of type '${this.type}' are expected to have '${credentialName}' credentials`);\n        }\n      }\n    }\n\n    if (this.requiresWebhook) {\n      if (!settings.webhookEndpoint) {\n        throw new Error(`bots of type '${this.type}' must be defined with webhookEndpoint in their settings`);\n      } else {\n        this.webhookEndpoint = settings.webhookEndpoint;\n      }\n    } else if (settings.webhookEndpoint) {\n      throw new Error(`bots of type '${this.type}' do not require webhookEndpoint in their settings`);\n    }\n  }\n\n  /**\n   * sets up the app if needed.\n   * As in sets up the endpoints that the bot can get called onto\n   * see code in bot classes packages to see examples of this in action\n   * Should not return anything\n   *\n   * __createMountPoints() {}\n   */\n\n  /**\n   * Format the update gotten from the bot source (telegram, messenger etc..).\n   * Returns an update in a standard format\n   *\n   * @param {object} rawUpdate\n   * @return {object} update\n   *\n   * __formatUpdate(rawUpdate) {}\n   */\n\n  /**\n   * createOutgoingMessage exposes the OutgoingMessage constructor\n   * via BaseBot. This simply means one can create their own\n   * OutgoingMessage object using any bot object. They can then compose\n   * it with all its helper functions\n   *\n   * This is the static version of this method\n   *\n   * @param {object} message base object that the outgoing Message should be based on\n   *\n   * @return {OutgoingMessage} outgoingMessage. The same object passed in with\n   * all the helper functions from OutgoingMessage\n   */\n  static createOutgoingMessage(message) {\n    return new OutgoingMessage(message);\n  }\n\n  /**\n   * createOutgoingMessage exposes the OutgoingMessage constructor\n   * via BaseBot. This simply means one can create their own\n   * OutgoingMessage object using any bot object. They can then compose\n   * it with all its helper functions\n   *\n   * This is the instance version of this method\n   *\n   * @param {object} message base object that the outgoing Message should be based on\n   *\n   * @return {OutgoingMessage} outgoingMessage. The same object passed in with\n   * all the helper functions from OutgoingMessage\n   */\n  createOutgoingMessage(message) {\n    return BaseBot.createOutgoingMessage(message);\n  }\n\n  /**\n   * same as #createOutgoingMessage, creates empty outgoingMessage with\n   * id of the recipient set. Again, this is jut sugar syntax for creating a\n   * new outgoingMessage object\n   *\n   * This is the static version of this method\n   *\n   * @param {string} recipientId id of the recipient the message is for\n   *\n   * @return {OutgoingMessage} outgoingMessage. A valid OutgoingMessage object with recipient set.\n   */\n\n  static createOutgoingMessageFor(recipientId) {\n    return new OutgoingMessage().addRecipientById(recipientId);\n  }\n\n  /**\n   * same as #createOutgoingMessage, creates empty outgoingMessage with\n   * id of the recipient set. Again, this is jut sugar syntax for creating a\n   * new outgoingMessage object\n   *\n   * This is the instance version of this method\n   *\n   * @param {string} recipientId id of the recipient the message is for\n   *\n   * @return {OutgoingMessage} outgoingMessage. A valid OutgoingMessage object with recipient set.\n   */\n\n  createOutgoingMessageFor(recipientId) {\n    return BaseBot.createOutgoingMessageFor(recipientId);\n  }\n\n  /**\n   * sendMessage() falls back to the sendMessage implementation of whatever\n   * subclass inherits form BaseBot. The expected format is normally any type of\n   * message object that could be sent on to messenger\n   * @param {object} message\n   * @param {boolean} [sendOptions] an object containing options regarding the\n   * sending of the message. Currently the only valid options is: `ignoreMiddleware`.\n   *\n   * @return {Promise} promise that resolves with a body object (see example)\n   *\n   * @example\n   * const outgoingMessage = bot.createOutgoingMessageFor(update.sender.id);\n   * outgoingMessage.addText('Hello world');\n   *\n   * bot.sendMessage(outgoingMessage);\n   *\n   * @example\n   * // The returned promise for all sendMessage type events resolves with\n   * // a body that looks something like this:\n   *  {\n   *   sentOutgoingMessage: // the OutgoingMessage instance before being formatted\n   *   sentRawMessage: // the OutgoingMessage object after being formatted for the platforms\n   *   raw: rawBody, // the raw response from the platforms received from sending the message\n   *   recipient_id: <id_of_user>,\n   *   message_id: <message_id_of_what_was_just_sent>\n   *  }\n   *\n   * // Some platforms may not have either of these parameters. If that's the case,\n   * // the value assigned will be a falsy value\n   *\n   */\n  sendMessage(message, sendOptions) {\n    sendOptions = sendOptions || {}; // empty object if undefined\n\n    const outgoingMessage = !(message instanceof OutgoingMessage)\n      ? new OutgoingMessage(message)\n      : message;\n\n    const responseBody = {};\n    return this.__validateSendOptions(sendOptions)\n\n    .then(() => {\n      let outgoingMiddlewarePromise;\n      if (this.master && !sendOptions.ignoreMiddleware) {\n        outgoingMiddlewarePromise = this.master.middleware.__runOutgoingMiddleware(\n          this, this.__associatedUpdate, outgoingMessage);\n      } else {\n        // don't actually go through middleware\n        outgoingMiddlewarePromise = Promise.resolve(outgoingMessage);\n      }\n      return outgoingMiddlewarePromise;\n    })\n    .then(() => {\n      responseBody.sentOutgoingMessage = outgoingMessage;\n      return this.__formatOutgoingMessage(outgoingMessage, sendOptions);\n    })\n    .then((rawMessage) => {\n      responseBody.sentRawMessage = rawMessage;\n      return this.__sendMessage(rawMessage, sendOptions);\n    })\n    .then((rawBody) => {\n      responseBody.raw = rawBody;\n      return this.__createStandardBodyResponseComponents(\n        responseBody.sentOutgoingMessage,\n        responseBody.sentRawMessage,\n        responseBody.raw);\n    })\n    .then((StandardBodyResponseComponents) => {\n      responseBody.recipient_id = StandardBodyResponseComponents.recipient_id;\n      responseBody.message_id = StandardBodyResponseComponents.message_id;\n      return responseBody;\n    })\n    .catch((err) => {\n      if (err === 'cancel') {\n        return 'cancelled';\n      }\n\n      throw err;\n    });\n  }\n\n  /**\n   * Bot class implementation of the __formatOutgoingMessage function. Each Bot class\n   * has to implement this in order to be able to send outgoing messages that start\n   * off as valid Messenger message objects (i.e. OutgoingMessage objects).\n   *\n   * @param {OutgoingMessage} outgoingMessage The outgoingMessage object that\n   * needs to be formatted to the platform standard (formatted out).\n   * @return {Promise} promise that resolves in the body of the response from the platform\n   *\n   * __formatOutgoingMessage(outgoingMessage) {}\n   */\n\n  /**\n   * Bot class implementation of the __sendMessage function. Each Bot class\n   * has to implement this in order to be able to send outgoing messages.\n   *\n   * @param {object} message\n   * @return {Promise} promise that resolves in the body of the response from the platform\n   *\n   * __sendMessage(rawUpdate) {}\n   */\n\n  /**\n   * Bot class implementation of the __createStandardBodyResponseComponents\n   * function. Each Bot class has to implement this in order to be able to\n   * send outgoing messages using sendMessage. This function returns the standard\n   * recipient_id and message_id we can expect from using sendMessage\n   *\n   * @param {OutgoingMessage} sentOutgoingMessage The OutgoingMessage object\n   * before formatting\n   * @param {object} sentRawMessage The raw message that was actually sent to\n   * the platform after __formatOutgoingMessage was called\n   * @param {object} rawPlatformBody the raw body response from the platform\n   *\n   * @return {Promise} promise that resolves in an object that contains\n   * both the recipient_id and message_id fields\n   *\n   * __createStandardBodyResponseComponents(\n   *   sentOutgoingMessage, sentRawMessage, rawPlatformBody) {}\n   */\n\n  /**\n   * sendMessageTo() Just makes it easier to send a message without as much\n   * structure.\n   * @param {object} message NOT an instance of OutgoingMessage. Use\n   * #sendMessage if you want to send instances of OutgoingMessage\n   * @param {string} recipientId a string representing the id of the user to\n   * whom you want to send the message.\n   * @param {object} [sendOptions] see `sendOptions` for `sendMessage`\n   *\n   * @return {Promise} promise that resolves with a body object\n   * (see `sendMessage` example)\n   *\n   * @example\n   *\n   * // message object can look something like this:\n   * // as you can see, this is not an OutgoingMessage instance\n   * const message = {\n   *  text: 'Some random text'\n   * }\n   *\n   * bot.sendMessageTo(message, update.sender.id);\n   *\n   */\n  sendMessageTo(message, recipientId, sendOptions) {\n    const outgoingMessage = this.createOutgoingMessage({\n      message,\n    });\n    outgoingMessage.addRecipientById(recipientId);\n\n    return this.sendMessage(outgoingMessage, sendOptions);\n  }\n\n  /**\n   * sendTextMessageTo() Just makes it easier to send a text message with\n   * minimal structure.\n   * @param {string} text\n   * @param {string} recipientId a string representing the id of the user to\n   * whom you want to send the message.\n   * @param {object} [sendOptions] see `sendOptions` for `sendMessage`\n   *\n   * @return {Promise} promise that resolves with a body object\n   * (see `sendMessage` example)\n   *\n   * @example\n   * bot.sendTextMessageTo('something super important', update.sender.id);\n   */\n  sendTextMessageTo(text, recipientId, sendOptions) {\n    if (!get(this, 'sends.text')) {\n      return Promise.reject(new SendMessageTypeError(this.type, 'text'));\n    }\n    const outgoingMessage = this.createOutgoingMessage()\n      .addRecipientById(recipientId)\n      .addText(text);\n\n    return this.sendMessage(outgoingMessage, sendOptions);\n  }\n\n  /**\n   * reply() Another way to easily send a text message. In this case,\n   * we just send the update that came in as is and then the text we\n   * want to send as a reply.\n   * @param {object} incomingUpdate\n   * @param {string} text text to send to the user associated with the received update\n   * @param {object} [sendOptions] see `sendOptions` for `sendMessage`\n   * @return {Promise} promise that resolves with a body object\n   * (see `sendMessage` example)\n   *\n   * @example\n   * bot.reply(update, 'something super important!');\n   */\n  reply(incomingUpdate, text, sendOptions) {\n    return this.sendTextMessageTo(text, incomingUpdate.sender.id, sendOptions);\n  }\n\n  /**\n   * sendAttachmentTo() makes it easier to send an attachment message with\n   * less structure.\n   * @param {object} attachment a valid Messenger style attachment.\n   * See [here](https://developers.facebook.com/docs/messenger-platform/send-api-reference)\n   * for more on that.\n   *\n   * @param {string} recipientId a string representing the id of the user to\n   * whom you want to send the message.\n   * @param {object} [sendOptions] see `sendOptions` for `sendMessage`\n   *\n   * @return {Promise} promise that resolves with a body object\n   * (see `sendMessage` example)\n   * @example\n   * // attachment object typically looks something like this:\n   * const attachment = {\n   *   type: 'image',\n   *   payload: {\n   *     url: \"some_valid_url_of_some_image\"\n   *   },\n   * };\n   *\n   * bot.sendAttachmentTo(attachment, update.sender.id);\n   */\n  sendAttachmentTo(attachment, recipientId, sendOptions) {\n    if (!get(this, 'sends.attachment')) {\n      return Promise.reject(new SendMessageTypeError(this.type, 'attachment'));\n    }\n    const outgoingMessage = this.createOutgoingMessage()\n      .addRecipientById(recipientId)\n      .addAttachment(attachment);\n\n    return this.sendMessage(outgoingMessage, sendOptions);\n  }\n\n  /**\n   * sendAttachmentFromUrlTo() makes it easier to send an attachment message with\n   * minimal structure.\n   * @param {string} type string representing the type of attachment\n   * (audio, video, image or file)\n   * @param {string} url the url to your file\n   * @param {string} recipientId a string representing the id of the user to\n   * whom you want to send the message.\n   * @param {object} [sendOptions] see `sendOptions` for `sendMessage`\n   *\n   * @return {Promise} promise that resolves with a body object\n   * (see `sendMessage` example)\n   *\n   * @example\n   * bot.sendAttachmentFromUrlTo('image', \"some image url you've got\", update.sender.id);\n   */\n  sendAttachmentFromUrlTo(type, url, recipientId, sendOptions) {\n    if (!get(this, `sends.attachment.${type}`)) {\n      let cantThrowErrorMessageType = `${type} attachment`;\n      if (!get(this, 'sends.attachment')) {\n        cantThrowErrorMessageType = 'attachment';\n      }\n      return Promise.reject(new SendMessageTypeError(this.type,\n        cantThrowErrorMessageType));\n    }\n    const attachment = {\n      type,\n      payload: {\n        url,\n      },\n    };\n\n    return this.sendAttachmentTo(attachment, recipientId, sendOptions);\n  }\n\n  /**\n   * sendDefaultButtonMessageTo() makes it easier to send a default set of\n   * buttons. The default button type is the Messenger quick_replies, where\n   * the payload is the same as the button title and the content_type is text.\n   *\n   * @param {Array} buttonTitles array of button titles (no longer than 10 in size).\n   * @param {string_OR_object} textOrAttachment a string or an attachment object\n   * similar to the ones required in `bot.sendAttachmentTo`.\n   * This is meant to provide context to the buttons.\n   * I.e. why are there buttons here. A piece of text or an attachment\n   * could detail that. If falsy, text will be added that reads:\n   * 'Please select one of:'.\n   * @param {string} recipientId a string representing the id of the user to\n   * whom you want to send the message.\n   * @param {object} [sendOptions] see `sendOptions` for `sendMessage`\n   *\n   * @return {Promise} promise that resolves with a body object\n   * (see `sendMessage` example)\n   *\n   * @example\n   * const buttonArray = ['button1', 'button2'];\n   * bot.sendDefaultButtonMessageTo(buttonArray,\n   *   'Please select \"button1\" or \"button2\"', update.sender.id,);\n   */\n  sendDefaultButtonMessageTo(buttonTitles, textOrAttachment, recipientId) {\n    const validateSendDefaultButtonMessageToArguments = () => {\n      let err = null;\n      if (!this.sends.quickReply) {\n        err = new SendMessageTypeError(this.type, 'quick replies');\n      } else if (buttonTitles.length > 10) {\n        err = new RangeError('buttonTitles must be of length 10 or less');\n      }\n\n      if (textOrAttachment) {\n        if (textOrAttachment.constructor === String) {\n          if (!this.sends.text) {\n            err = new SendMessageTypeError(this.type, 'text');\n          }\n        } else if (textOrAttachment.constructor === Object && textOrAttachment.type) {\n          if (!this.sends.attachment) {\n            err = new SendMessageTypeError(this.type, 'attachment');\n          } else if (!this.sends.attachment[textOrAttachment.type]) {\n            err = new SendMessageTypeError(this.type,\n              `${textOrAttachment.type} attachment`);\n          }\n        } else {\n          err = new TypeError('third argument must be a \"String\", an ' +\n                              'attachment \"Object\" or absent');\n        }\n      }\n\n      return err;\n    };\n\n    const potentialError = validateSendDefaultButtonMessageToArguments();\n    if (potentialError) {\n      return Promise.reject(potentialError);\n    }\n\n    // //////////////////////////////////////////////////////\n    // actual code after validating with\n    // validateSendDefaultButtonMessageToArguments function\n    // //////////////////////////////////////////////////////\n\n    const outgoingMessage = this.createOutgoingMessage();\n    outgoingMessage.addRecipientById(recipientId);\n    // deal with textOrAttachment\n    if (!textOrAttachment && this.sends.text) {\n      outgoingMessage.addText('Please select one of:');\n    } else if (textOrAttachment.constructor === String) {\n      outgoingMessage.addText(textOrAttachment);\n    } else {\n      // it must be an attachment or an error would have been thrown\n      outgoingMessage.addAttachment(textOrAttachment);\n    }\n\n    const quickReplies = [];\n    for (const buttonTitle of buttonTitles) {\n      quickReplies.push({\n        content_type: 'text',\n        title: buttonTitle,\n        payload: buttonTitle, // indeed, in default mode payload is buttonTitle\n      });\n    }\n    outgoingMessage.addQuickReplies(quickReplies);\n    return this.sendMessage(outgoingMessage, arguments[3]);\n  }\n\n  /**\n   * sendIsTypingMessageTo() just sets the is typing status to the platform\n   * if available.\n   *\n   * @param {string} recipientId a string representing the id of the user to\n   * whom you want to send the message.\n   * @param {object} [sendOptions] see `sendOptions` for `sendMessage`\n   *\n   * @return {Promise} promise that resolves with a body object\n   * (see `sendMessage` example)\n   *\n   * @example\n   * bot.sendIsTypingMessageTo(update.sender.id);\n   * // the returned value is different from the standard one. it won't have a message_id\n   */\n  sendIsTypingMessageTo(recipientId, sendOptions) {\n    if (!get(this, 'sends.senderAction.typingOn')) {\n      return Promise.reject(new SendMessageTypeError(this.type,\n        'typing_on sender action'));\n    }\n    const isTypingMessage = {\n      recipient: {\n        id: recipientId,\n      },\n      sender_action: 'typing_on',\n    };\n    return this.sendMessage(isTypingMessage, sendOptions);\n  }\n\n  /**\n   * sendCascade() allows developers to send a cascade of messages\n   * in a sequence. All types of messages can be sent (including raw messages).\n   *\n   * @param {Array} messageArray of messages in a format as such:\n   * [{raw: someRawObject}, {message: some valid outgoingMessage}]\n   * @param {object} [sendOptions] see `sendOptions` for `sendMessage`. will\n   * only apply to non rawMessages. (remember that for rawMessages, outgoing\n   * middleware is bypassed anyways).\n   *\n   * @return {Promise} promise that resolves with an array of body objects\n   * (see `sendMessage` example for one said object)\n   *\n   * @example\n   * const rawMessage1 = {\n   *   nonStandard: 'message1',\n   *   recipient: {\n   *     id: 'user_id',\n   *   },\n   * };\n   * const message2 = bot.createOutgoingMessageFor(update.sender.id);\n   * message2.addText('some text');\n   *\n   * const messageArray = [{ raw: rawMessage1 }, { message: message2 }];\n   *\n   * bot.sendCascade(messageArray);\n   */\n  sendCascade(messageArray, sendOptions) {\n    const returnedBodies = [];\n\n    let promiseCascade = Promise.resolve();\n\n    for (const messageObject of messageArray) {\n      promiseCascade = promiseCascade.then((body) => {\n        if (body) {\n          returnedBodies.push(body);\n        }\n        if (messageObject.raw) {\n          return this.sendRawMessage(messageObject.raw);\n        } else if (messageObject.message) {\n          return this.sendMessage(messageObject.message, sendOptions);\n        }\n        throw new Error('No valid message options specified');\n      });\n    }\n\n    return promiseCascade\n\n    .then((body) => {\n      // add last body and deal with potential callback\n      returnedBodies.push(body);\n      return returnedBodies;\n    });\n  }\n\n  /**\n   * sendTextCascadeTo() is simply a helper function around sendCascadeTo.\n   * It allows developers to send a cascade of text messages more easily.\n   *\n   * @param {Array} textArray of messages.\n   * @param {string} recipientId a string representing the id of the user to\n   * whom you want to send the message.\n   * @param {object} [sendOptions] see `sendOptions` for `sendMessage`\n   *\n   * @return {Promise} promise that resolves with an array of body objects\n   * (see `sendMessage` example for one said object)\n   *\n   * @example\n   * bot.sendTextCascadeTo(['message1', 'message2'], user.sender.id);\n   */\n\n  sendTextCascadeTo(textArray, recipientId, sendOptions) {\n    const cascadeArray = textArray.map((text) => {\n      const outgoingMessage = this.createOutgoingMessageFor(recipientId)\n        .addText(text);\n\n      return { message: outgoingMessage };\n    });\n\n    return this.sendCascade(cascadeArray, sendOptions);\n  }\n\n  /**\n   * sendRawMessage() simply sends a raw platform dependent message. This method\n   * calls __sendMessage in each botClass without calling formatOutgoingMessage\n   * before. It's really just sugar around __sendMessage which shouldn't be used\n   * directly.\n   *\n   * @param {Object} rawMessage\n   *\n   * @return {Promise} promise\n   *\n   */\n  sendRawMessage(rawMessage) {\n    return this.__sendMessage(rawMessage);\n  }\n\n  /**\n   * __validateSendOptions() is simply an internal helper function to validate\n   * wether sendOptions is valid\n   * @ignore\n   * @param {function} [sendOptions]\n   *\n   * @return {object} with cb and sendOptions as parameters\n   *\n   */\n\n  __validateSendOptions(sendOptions) {\n    return new Promise((resolve, reject) => {\n      let err = null;\n\n      if (typeof sendOptions === 'function') {\n        err = new TwoDotXError('Using botmaster sendMessage type methods ' +\n          'with callback functions is no longer supported in botmaster 3. ');\n      } else if (typeof sendOptions !== 'object') {\n        err = new TypeError('sendOptions must be of type ' +\n        `object. Got ${typeof sendOptions} instead`);\n      }\n\n      if (err) {\n        return reject(err);\n      }\n\n      return resolve();\n    });\n  }\n\n  /**\n   * __emitUpdate() emits an update after going through the\n   * incoming middleware based on the passed in update. Note that we patched\n   * the bot object with the update, so that it is available in the outgoing\n   * middleware too.\n   * @ignore\n   * @param {object} update\n   */\n  __emitUpdate(update) {\n    if (!this.master) {\n      return Promise.reject(new Error('bot needs to be added to a botmaster ' +\n                            'instance in order to emit received updates'));\n    }\n\n    return this.master.middleware.__runIncomingMiddleware(this, update)\n    .catch((err) => {\n      // doing this, to make sure all errors (even ones rejected from\n      // promises within incoming middleware) can be retrieved somewhere;\n      if (err === 'cancel') {\n        return 'cancelled';\n      }\n      if (err && err.message) {\n        err.message = `\"${err.message}\". This is most probably on your end.`;\n      }\n\n      this.emit('error', err || 'empty error object', update);\n      return err;\n    });\n  }\n\n  /**\n   * Retrieves the basic user info from a user if platform supports it\n   *\n   * @param {string} userId\n   *\n   * @return {Promise} promise that resolves into the user info or an empty\n   * object by default\n   */\n  getUserInfo(userId, options) {\n    if (!this.retrievesUserInfo) {\n      return Promise.reject(TypeError(\n        `Bots of type ${this.type} don't provide access to user info.`));\n    }\n    return this.__getUserInfo(userId, options);\n  }\n\n  /**\n   * __createBotPatchedWithUpdate is used to create a new bot\n   * instance that on sendMessage sends the update as a sendOption.\n   * This is important, because we want to have access to the update object\n   * even within outgoing middleware. This allows us to always have access\n   * to it.\n   * @ignore\n   * @param {object} update - update to be patched to sendMessage\n   * @returns {object} bot\n   */\n  __createBotPatchedWithUpdate(update) {\n    const newBot = Object.create(this);\n    newBot.__associatedUpdate = update;\n    return newBot;\n  }\n}\n\nmodule.exports = BaseBot;\n"
  },
  {
    "path": "lib/botmaster.js",
    "content": "'use strict';\n\nconst http = require('http');\nconst EventEmitter = require('events');\nconst find = require('lodash').find;\nconst remove = require('lodash').remove;\nconst has = require('lodash').has;\nconst debug = require('debug')('botmaster:botmaster');\nconst TwoDotXError = require('./errors').TwoDotXError;\nconst Middleware = require('./middleware');\n\n/**\n * The Botmaster class to rule them all\n */\n\nclass Botmaster extends EventEmitter {\n  /**\n   * sets up a botmaster object attached to the correct server if one is set\n   * as a parameter. If not, it creates its own http server\n   *\n   * @param {object} settings\n   *\n   * @example\n   * // attach the botmaster generated server to port 5000 rather than the default 3000\n   * const botmaster = new Botmaster({\n   *   port: 5000,\n   * });\n   *\n   * @example\n   * const http = require('http');\n   *\n   * const myServer = http.createServer()\n   * // use my own server rather than letting botmaster creat its own.\n   * const botmaster = new Botmaster({\n   *   server: myServer,\n   * });\n   */\n\n  constructor(settings) {\n    super();\n    this.settings = settings || {};\n    this.__throwPotentialUnsupportedSettingsErrors();\n    this.__setupServer();\n    this.middleware = new Middleware(this);\n\n    // this is used for mounting routes onto bot classes \"mini-apps\"\"\n    this.__serverRequestListeners = {};\n    // default useDefaultMountPathPrepend to true\n    if (this.settings.useDefaultMountPathPrepend === undefined) {\n      this.settings.useDefaultMountPathPrepend = true;\n    }\n    this.bots = [];\n  }\n\n  __throwPotentialUnsupportedSettingsErrors() {\n    const unsupportedSettings = ['botsSettings', 'app'];\n\n    for (const settingName of unsupportedSettings) {\n      if (this.settings[settingName]) {\n        throw new TwoDotXError(\n          `Starting botmaster with ${settingName} ` +\n          'is no longer supported.');\n      }\n    }\n  }\n\n  __setupServer() {\n    if (this.settings.server && this.settings.port) {\n      throw new Error(\n        'IncompatibleArgumentsError: Please specify only ' +\n        'one of port and server');\n    }\n    if (this.settings.server) {\n      this.server = this.settings.server;\n    } else {\n      const port = has(this, 'settings.port')\n        ? this.settings.port\n        : 3000;\n      this.server = this.__listen(port);\n    }\n    this.__setupServersRequestListeners();\n  }\n\n  __setupServersRequestListeners() {\n    const nonBotmasterListeners = this.server.listeners('request').slice(0);\n    this.server.removeAllListeners('request');\n\n    this.server.on('request', (req, res) => {\n      // run botmaster requestListeners first\n      for (const path in this.__serverRequestListeners) {\n        if (req.url.indexOf(path) === 0) {\n          const requestListener = this.__serverRequestListeners[path];\n          return requestListener.call(this.server, req, res);\n        }\n      }\n      // then run the non-botmaster ones\n      if (nonBotmasterListeners.length > 0) {\n        for (const requestListener of nonBotmasterListeners) {\n          requestListener.call(this.server, req, res);\n        }\n      } else {\n        // just return a 404\n        res.writeHead(404, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ message: `Couldn't ${req.method} ${req.url}` }));\n      }\n    });\n  }\n\n  __listen(port) {\n    const server = http.createServer();\n    server.listen(port, '0.0.0.0', () => { // running it for the public\n      const serverMsg = `server parameter not specified. Running new server on port: ${port}`;\n      debug(serverMsg);\n      this.emit('listening', serverMsg);\n    });\n\n    return server;\n  }\n\n  /**\n   * Add an existing bot to this instance of Botmaster\n   *\n   * @param {BaseBot} bot the bot object to add to botmaster. Must be from\n   * a subclass of BaseBot\n   *\n   * @return {Botmaster} returns the botmaster object for chaining\n   */\n  addBot(bot) {\n    if (bot.requiresWebhook) {\n      const path = this.__getBotWebhookPath(bot);\n      this.__serverRequestListeners[path] = bot.requestListener;\n    }\n    bot.master = this;\n    this.bots.push(bot);\n    bot.on('error', (err, update) => {\n      debug(err.message);\n      this.emit('error', bot, err, update);\n    });\n\n    debug(`added bot of type: ${bot.type} with id: ${bot.id}`);\n\n    return this;\n  }\n\n  __getBotWebhookPath(bot) {\n    const webhookEndpoint = bot.webhookEndpoint.replace(/^\\/|\\/$/g, '');\n\n    const path = this.settings.useDefaultMountPathPrepend\n      ? `/${bot.type}/${webhookEndpoint}`\n      : `/${webhookEndpoint}`;\n\n    return path;\n  }\n\n  /**\n   * Extract First bot of given type or provided id.\n   *\n   * @param {object} options must be { type: 'someBotType} or { id: someBotId }.\n   *\n   * @return {BaseBot} The bot found of a class that inherits of BaseBot\n   */\n  getBot(options) {\n    if (!options ||\n       (!options.type && !options.id) ||\n       (options.type && options.id)) {\n      throw new Error('\\'getBot\\' needs exactly one of type or id');\n    }\n\n    if (options.id) {\n      return find(this.bots, { id: options.id });\n    }\n\n    return find(this.bots, { type: options.type });\n  }\n\n   /**\n    * Extract all bots of given type.\n    *\n    * @param {string} botType (there can be multiple bots of a same type)\n    *\n    * @return {Array} Array of bots found\n    */\n  getBots(botType) {\n    if (typeof botType !== 'string' && !(botType instanceof String)) {\n      throw new Error('\\'getBots\\' takes in a string as only parameter');\n    }\n\n    const foundBots = [];\n    for (const bot of this.bots) {\n      if (bot.type === botType) {\n        foundBots.push(bot);\n      }\n    }\n\n    return foundBots;\n  }\n\n  /**\n   * Remove an existing bot from this instance of Botmaster\n   *\n   * @param {Object} bot\n   *\n   * @return {Botmaster} returns the botmaster object for chaining\n   */\n  removeBot(bot) {\n    if (bot.requiresWebhook) {\n      const path = this.__getBotWebhookPath(bot);\n      delete this.__serverRequestListeners[path];\n    }\n    remove(this.bots, bot);\n    bot.removeAllListeners();\n\n    debug(`removed bot of type: ${bot.type} with id: ${bot.id}`);\n\n    return this;\n  }\n\n  /**\n   * Add middleware to this botmaster object\n   * This function is just sugar for `middleware.__use` in them\n   *\n   * @param {object} middleware\n   *\n   * @example\n   *\n   * // The middleware param object is something that looks like this for incoming:\n   * {\n   *  type: 'incoming',\n   *  name: 'my-incoming-middleware',\n   *  controller: (bot, update, next) => {\n   *    // do stuff with update,\n   *    // call next (or return a promise)\n   *  },\n   *  // includeEcho: true (defaults to false), opt-in to get echo updates\n   *  // includeDelivery: true (defaults to false), opt-in to get delivery updates\n   *  // includeRead: true (defaults to false), opt-in to get user read updates\n   * }\n   *\n   * // and like this for outgoing middleware\n   *\n   * {\n   *  type: 'outgoing',\n   *  name: 'my-outgoing-middleware',\n   *  controller: (bot, update, message, next) => {\n   *    // do stuff with message,\n   *    // call next (or return a promise)\n   *  }\n   * }\n   *\n   * @return {Botmaster} returns the botmaster object so you can chain middleware\n   *\n   */\n  use(middleware) {\n    this.middleware.__use(middleware);\n\n    return this;\n  }\n\n  /**\n   * Add wrapped middleware to this botmaster instance. Wrapped middleware\n   * places the incoming middleware at beginning of incoming stack and\n   * the outgoing middleware at end of outgoing stack.\n   * This function is just sugar `middleware.useWrapped`.\n   *\n   * @param {object} incomingMiddleware\n   * @param {object} outgoingMiddleware\n   *\n   * The middleware objects are as you'd expect them to be (see use)\n   *\n   * @return {Botmaster} returns the botmaster object so you can chain middleware\n   */\n  useWrapped(incomingMiddleware, outgoingMiddleware) {\n    this.middleware.__useWrapped(incomingMiddleware, outgoingMiddleware);\n\n    return this;\n  }\n}\n\nmodule.exports = Botmaster;\n"
  },
  {
    "path": "lib/errors.js",
    "content": "'use strict';\n\nconst debugBase = require('debug');\n\nclass TwoDotXError extends Error {\n  constructor(message) {\n    super(message);\n\n    this.message += 'See the latest documentation ' +\n      'at http://botmasterai.com to see the preferred syntax. ' +\n      'Alternatively, you can downgrade botmaster to 2.x.x by doing: ' +\n      '\"npm install --save botmaster@2.x.x\" or \"yarn add botmaster@2.x.x\"';\n  }\n\n}\n\nclass SendMessageTypeError extends Error {\n  constructor(botType, messageType) {\n    super(`Bots of type ${botType} can't send` +\n          ` messages with ${messageType}`);\n\n    const debug = debugBase(`botmaster:${botType}`);\n    debug(`Tried sending message of type ${messageType} to bot of ` +\n          `type ${botType} that do not support this message type`);\n  }\n}\n\nmodule.exports = {\n  TwoDotXError,\n  SendMessageTypeError,\n};\n"
  },
  {
    "path": "lib/index.js",
    "content": "'use strict';\n\nconst Botmaster = require('./botmaster');\nBotmaster.BaseBot = require('./base_bot');\n\nmodule.exports = Botmaster;\n"
  },
  {
    "path": "lib/middleware.js",
    "content": "'use strict';\n\nconst get = require('lodash').get;\nconst debug = require('debug')('botmaster:middleware');\n\nclass Middleware {\n  /**\n   * Singleton Middleware class every botmaster instance should own one of\n   * incomingMiddleware and\n   * outgoingMiddleware variables;\n   *\n   * This class is not part of the exposed API. Use botmaster.use instead\n   * @ignore\n   */\n  constructor() {\n    this.incomingMiddlewareStack = [];\n    this.outgoingMiddlewareStack = [];\n  }\n\n  /**\n   * Add middleware.\n   * See botmaster #use for more info.\n   * @ignore\n   */\n  __use(middleware) {\n    this.__validateMiddleware(middleware);\n\n    if (middleware.type === 'incoming') {\n      this.incomingMiddlewareStack.push(middleware);\n      debug(`added ${middleware.name || 'nameless'} incoming middleware`);\n    } else {\n      this.outgoingMiddlewareStack.push(middleware);\n      debug(`added ${middleware.name || 'nameless'} outgoing middleware`);\n    }\n\n    return this;\n  }\n\n  /**\n   * Add Wrapped middleware\n   * See botmaster #useWrapped for more info.\n   * @ignore\n   * @param {object} params\n   */\n  __useWrapped(incomingMiddleware, outgoingMiddleware) {\n    if (!incomingMiddleware || !outgoingMiddleware) {\n      throw new Error('useWrapped should be called with both an' +\n                      ' incoming and an outgoing middleware');\n    }\n    this.__validateMiddleware(incomingMiddleware);\n    this.__validateMiddleware(outgoingMiddleware);\n\n    if (incomingMiddleware.type === 'outgoing') {\n      throw new TypeError('first argument of \"useWrapped\" should be an' +\n      ' incoming middleware');\n    } else if (outgoingMiddleware.type === 'incoming') {\n      throw new TypeError('second argument of \"useWrapped\" should be an' +\n      ' outgoing middleware');\n    }\n\n    this.incomingMiddlewareStack.unshift(incomingMiddleware);\n    this.outgoingMiddlewareStack.push(outgoingMiddleware);\n    debug(`added wrapped ${incomingMiddleware.name || 'nameless'} incoming middleware`);\n    debug(`added wrapped ${outgoingMiddleware.name || 'nameless'} outgoing middleware`);\n\n    return this;\n  }\n\n  __validateMiddleware(middleware) {\n    if (typeof middleware !== 'object') {\n      throw new Error(`middleware should be an object. Not ${typeof middleware}`);\n    }\n\n    const middlewareController = middleware.controller;\n\n    if (middleware.type !== 'incoming' && middleware.type !== 'outgoing') {\n      throw new TypeError('invalid middleware type. Type should be either ' +\n      '\\'incoming\\' or \\'outgoing\\'');\n    }\n    if (typeof middlewareController !== 'function') {\n      throw new TypeError('middleware controller can\\'t be of type ' +\n      `${typeof middlewareController}. It needs to be a function`);\n    }\n  }\n\n  __runIncomingMiddleware(bot, update) {\n    return this.__runMiddlewareStack({\n      bot,\n      update,\n      middlewareStack: this.incomingMiddlewareStack,\n    });\n  }\n\n  __runOutgoingMiddleware(bot, associatedUpdate, message) {\n    return this.__runMiddlewareStack({\n      bot,\n      update: associatedUpdate,\n      message,\n      middlewareStack: this.outgoingMiddlewareStack,\n    });\n  }\n\n  __runMiddlewareStack(context) {\n    const bot = context.bot;\n    const update = context.update;\n    const patchedBot = bot.__createBotPatchedWithUpdate(update);\n    const message = context.message;\n    const middlewareStack = context.middlewareStack;\n\n    let middlewarePromiseStack = Promise.resolve();\n\n    const throwThrowableResolvedValue = (resolvedValue) => {\n      if (resolvedValue === 'cancel' || resolvedValue === 'skip') {\n        throw resolvedValue;\n      }\n    };\n\n    for (const middleware of middlewareStack) {\n      middlewarePromiseStack = middlewarePromiseStack\n\n      .then((resolvedValue) => {\n        throwThrowableResolvedValue(resolvedValue);\n        // otherwise, do nothing with resolvedValue\n        if (this.__shouldRun(middleware, context)) {\n          let innerPromise;\n          return new Promise((resolve, reject) => {\n            // next is a patched reject so that we can determine if\n            // next was called within a returned promise, which is not allowed\n            const next = err => reject({\n              err,\n              nextRejection: true,\n            });\n            if (middlewareStack === this.incomingMiddlewareStack) {\n              innerPromise = middleware.controller(\n                patchedBot, update, next);\n            } else {\n              innerPromise = middleware.controller(\n                patchedBot, update, message, next);\n            }\n\n            if (innerPromise && innerPromise.constructor === Promise) {\n              innerPromise.then(resolve).catch(reject);\n            }\n          }).catch((err) => {\n            if (err && err.nextRejection) {\n              if (innerPromise && innerPromise.constructor === Promise) {\n                throw new Error('next can\\'t be called if middleware ' +\n                                'returns a promise/is an async function');\n              } else if (err.err) {\n                throw err.err;\n              } else {\n                return;\n              }\n            }\n\n            throw err;\n          });\n        }\n        //  otherwise, return nothing\n        return Promise.resolve();\n      });\n    }\n\n    return middlewarePromiseStack\n\n    .then((resolvedValue) => {\n      throwThrowableResolvedValue(resolvedValue);\n    })\n    .catch((err) => {\n      if (err === 'skip') {\n        return Promise.resolve();\n      }\n\n      throw err;\n    });\n  }\n\n  /**\n   * Simply returns true or false based on whether this middleware function\n   * should be run for this object.\n   * @ignore\n   * @param {object} options\n   *\n   * @example\n   * // options is an object that can contain any of:\n   * {\n   *   includeEcho, // opt-in to get echo updates\n   *   includeDelivery, // opt-in to get delivery updates\n   *   includeRead, // opt-in to get read updates\n   * }\n   */\n  __shouldRun(middleware, context) {\n    if (middleware.type === 'outgoing') {\n      // for now, no condition to not run outgoing middleware\n      return true;\n    }\n    // we are de facto dealing with incoming middleware\n    const ignoreReceivedEchoUpdate = !middleware.includeEcho &&\n      get(context.update, 'message.is_echo');\n    const ignoreReceivedDeliveryUpdate = !middleware.includeDelivery &&\n      get(context.update, 'delivery');\n    const ignoreReceivedReadUpdate = !middleware.includeRead &&\n      get(context.update, 'read');\n\n    if (ignoreReceivedEchoUpdate ||\n        ignoreReceivedDeliveryUpdate ||\n        ignoreReceivedReadUpdate) {\n      return false;\n    }\n\n    return true;\n  }\n}\n\nmodule.exports = Middleware;\n"
  },
  {
    "path": "lib/outgoing_message.js",
    "content": "'use strict';\n\nconst assign = require('lodash').assign;\nconst has = require('lodash').has;\nconst set = require('lodash').set;\nconst unset = require('lodash').unset;\n\n/**\n * This class will help you compose sendable message objects.\n */\n\nclass OutgoingMessage {\n\n  /**\n   * Constructor to the OutgoingMessage class. Takes in an optional\n   * message object that it will use as its base to add the OutgoingMessage\n   * methods to. This constructor is not actually exposed in the public API.\n   * In order to instantiate an OutgoingMessage object, you'll need to use the\n   * createOutgoingMessage and createOutgoingMessageFor methods provided with\n   * all classes that inherit from BaseBot. There are static and non-static\n   * versions of both methods to make sure you can do so wherever as you wish\n   *\n   * @private\n   * @param {object} [message] the base object to convert into an OutgoingMessage object\n   */\n  constructor(message) {\n    if (!message) {\n      message = {};\n    }\n    if (typeof message !== 'object') {\n      throw new TypeError('OutgoingMessage constructor takes in an object as param');\n    }\n    assign(this, message);\n\n    return this;\n  }\n\n  __addProperty(path, nameForError, value) {\n    if (!value) {\n      throw new Error(`${nameForError} must have a value. Can't be ${value}`);\n    } else if (has(this, path)) {\n      throw new Error(`Can't add ${nameForError} to outgoingMessage that already has ${nameForError}`);\n    }\n    set(this, path, value);\n\n    return this;\n  }\n\n  __removeProperty(path, nameForError) {\n    if (!has(this, path)) {\n      throw new Error(`Can't remove ${nameForError} from outgoingMessage that doesn't have any ${nameForError}`);\n    }\n    unset(this, path);\n\n    return this;\n  }\n\n  /**\n   * Adds `recipient.id` param to the OutgoingMessage object. This is most\n   * likely what you will want to do to add a recipient. Alternatively, you Can\n   * use addRecipientByPhoneNumber if the platform you are sending the message to\n   * supports that.\n   *\n   * @param {string} id the id to add to the OutgoingMessage object\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  addRecipientById(id) {\n    const recipient = {\n      id,\n    };\n    return this.__addProperty('recipient', 'recipient', recipient);\n  }\n\n  /**\n   * Adds `recipient.phone_number` param to the OutgoingMessage object.\n   * You might prefer to add a recipient by id rather. This is achieved via\n   * addRecipientById\n   *\n   * @param {string} phoneNumber the phone number to add to the OutgoingMessage object\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  addRecipientByPhoneNumber(phoneNumber) {\n    const recipient = {\n      phone_number: phoneNumber,\n    };\n    return this.__addProperty('recipient', 'recipient', recipient);\n  }\n\n  /**\n   * removes the `recipient` param from the OutgoingMessage object.\n   * This will remove the object wether it was set with a phone number or an id\n   *\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  removeRecipient() {\n    return this.__removeProperty('recipient', 'recipient');\n  }\n\n  /**\n   * Adds `message.text` to the OutgoingMessage\n   *\n   * @param {string} text the text to add to the OutgoingMessage object\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  addText(text) {\n    return this.__addProperty('message.text', 'text', text);\n  }\n\n  /**\n   * Removes the `message.text` param from the OutgoingMessage object.\n   *\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  removeText() {\n    return this.__removeProperty('message.text', 'text');\n  }\n\n  /**\n   * Adds `message.attachment` to the OutgoingMessage. If you want to add\n   * an attachment simply from a type and a url, have a look at:\n   * addAttachmentFromUrl\n   *\n   * @param {object} attachment valid messenger type attachment that can be\n   * formatted by the platforms your bot uses\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  addAttachment(attachment) {\n    return this.__addProperty('message.attachment', 'attachment', attachment);\n  }\n\n  /**\n   * Adds `message.attachment` from a type and url without requiring you to\n   * provide the whole attachment object. If you want to add an attachment using\n   * a full object, use addAttachment.\n   *\n   * @param {string} type the attachment type (audio, video, image, file)\n   * @param {string} url the url of the attachment.\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  addAttachmentFromUrl(type, url) {\n    if (!type || !url) {\n      throw new Error('addAttachmentFromUrl must be called with truthy \"type\" and \"url\" arguments');\n    }\n    if (typeof type !== 'string' || typeof url !== 'string') {\n      throw new TypeError('addAttachmentFromUrl must be called with \"type\" and \"url\" arguments of type string');\n    }\n    const attachment = {\n      type,\n      payload: {\n        url,\n      },\n    };\n\n    return this.addAttachment(attachment);\n  }\n\n  /**\n   * Removes `message.attachment` param from the OutgoingMessage object.\n   *\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  removeAttachment() {\n    return this.__removeProperty('message.attachment', 'attachment');\n  }\n\n  /**\n   * Adds `message.quick_replies` to the OutgoinMessage object. Use\n   * addPayloadLessQuickReplies if you just want to add quick replies from an\n   * array of titles\n   *\n   * @param {Array} quickReplies The quick replies objects to add to the\n   * OutgoingMessage\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  addQuickReplies(quickReplies) {\n    return this.__addProperty('message.quick_replies', 'quick_replies', quickReplies);\n  }\n\n  /**\n   * Adds `message.quick_replies` to the OutgoinMessage object from a simple array\n   * of quick replies titles.Use addQuickReplies if want to add quick replies\n   * from an quick reply objects\n   *\n   * @param {Array} quickRepliesTitles The titles of the quick replies objects to add to the\n   * OutgoingMessage\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  addPayloadLessQuickReplies(quickRepliesTitles) {\n    const errorText = 'addPayloadLessQuickReplies needs to be passed in an array of strings as first argument';\n    if (!(quickRepliesTitles instanceof Array)) {\n      throw new TypeError(errorText);\n    }\n    const quickReplies = [];\n    for (const title of quickRepliesTitles) {\n      if (typeof title !== 'string') {\n        throw new TypeError(errorText);\n      }\n      const quickReply = {\n        title,\n        payload: title,\n        content_type: 'text',\n      };\n      quickReplies.push(quickReply);\n    }\n\n    return this.addQuickReplies(quickReplies);\n  }\n\n  /**\n   * Adds a `content_type: location` message.quick_replies to the OutgoingMessage.\n   * Use this if the platform the bot class you are using is based on supports\n   * asking for the location to its users.\n   *\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  addLocationQuickReply() {\n    const locationQuickReply = [\n      {\n        content_type: 'location',\n      },\n    ];\n\n    return this.addQuickReplies(locationQuickReply);\n  }\n\n  /**\n   * Removes `message.quick_replies` param from the OutgoingMessage object.\n   *\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  removeQuickReplies() {\n    return this.__removeProperty('message.quick_replies', 'quick_replies');\n  }\n\n  /**\n   * Adds an arbitrary `sender_action` to the OutgoinMessage\n   * @param {string} senderAction Arbitrary sender action\n   * (typing_on, typing_off or mark_seens)\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  addSenderAction(senderAction) {\n    return this.__addProperty('sender_action', 'sender_action', senderAction);\n  }\n\n  /**\n   * Adds `sender_action: typing_on` to the OutgoinMessage\n   *\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  addTypingOnSenderAction() {\n    return this.__addProperty('sender_action', 'sender_action', 'typing_on');\n  }\n\n  /**\n   * Adds `sender_action: typing_off`  to the OutgoinMessage\n   *\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  addTypingOffSenderAction() {\n    return this.__addProperty('sender_action', 'sender_action', 'typing_off');\n  }\n\n  /**\n   * Adds `sender_action: mark_seen`  to the OutgoinMessage\n   *\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  addMarkSeenSenderAction() {\n    return this.__addProperty('sender_action', 'sender_action', 'mark_seen');\n  }\n\n  /**\n   * Removes `sender_action` param from the OutgoingMessage object.\n   *\n   * @return {OutgoinMessage} returns this object to allow for chaining of methods.\n   */\n  removeSenderAction() {\n    return this.__removeProperty('sender_action', 'sender_action');\n  }\n}\n\nmodule.exports = OutgoingMessage;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"botmaster\",\n  \"version\": \"3.2.0\",\n  \"description\": \"Framework allowing developers to write bots that are agnostic with respect to the channel used by their users (messenger, telegram etc...)\",\n  \"main\": \"./lib/index.js\",\n  \"scripts\": {\n    \"test\": \"export NODE_ENV=test; nyc --reporter=lcov --reporter=html ava; nyc report\",\n    \"test-debug\": \"export NODE_ENV=test DEBUG=botmaster:*; nyc --reporter=lcov --reporter=html ava\",\n    \"test-watch\": \"export NODE_ENV=test; ava --watch\",\n    \"coveralls\": \"cat ./coverage/lcov.info | coveralls\",\n    \"postversion\": \"git push && git push --tags\",\n    \"report\": \"nyc report\",\n    \"botmaster-docs\": \"jsdoc2md lib/botmaster.js > api-reference/botmaster.md\",\n    \"base-bot-docs\": \"jsdoc2md lib/base_bot.js > api-reference/base-bot.md\",\n    \"outgoing-message-docs\": \"jsdoc2md lib/outgoing_message.js > api-reference/outgoing-message.md\",\n    \"docs\": \"mkdir -p api-reference; yarn botmaster-docs; yarn base-bot-docs; yarn outgoing-message-docs\",\n    \"docs-deploy\": \"yarn docs && cp -r api-reference ../botmasterai.github.io/docs\"\n  },\n  \"ava\": {\n    \"files\": [\n      \"tests/**/*.js\",\n      \"!**/index.js\"\n    ],\n    \"source\": [],\n    \"match\": [],\n    \"serial\": true,\n    \"verbose\": true,\n    \"failFast\": true,\n    \"tap\": false,\n    \"powerAssert\": false\n  },\n  \"nyc\": {\n    \"check-coverage\": true,\n    \"lines\": 100,\n    \"statements\": 100,\n    \"functions\": 100,\n    \"branches\": 100,\n    \"exclude\": [\n      \"tests\"\n    ]\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/jdwuarin/botmaster\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/jdwuarin/botmaster/issues\"\n  },\n  \"keywords\": [\n    \"bot\",\n    \"framework\",\n    \"toolkit\",\n    \"botmaster\",\n    \"slack\",\n    \"messenger\",\n    \"telegram\",\n    \"twitter\",\n    \"bot-library\"\n  ],\n  \"dependencies\": {\n    \"debug\": \"^3.1.0\",\n    \"lodash\": \"^4.17.4\"\n  },\n  \"engines\": {\n    \"node\": \"4.x.x || >=6.x.x\"\n  },\n  \"devDependencies\": {\n    \"ava\": \"^0.19.1\",\n    \"body-parser\": \"^1.18.2\",\n    \"botmaster-test-fixtures\": \"^2.1.0\",\n    \"coveralls\": \"^3.0.0\",\n    \"eslint\": \"^4.16.0\",\n    \"eslint-config-airbnb\": \"^16.1.0\",\n    \"eslint-plugin-ava\": \"^4.5.0\",\n    \"eslint-plugin-import\": \"^2.8.0\",\n    \"eslint-plugin-jsx-a11y\": \"^6.0.3\",\n    \"eslint-plugin-react\": \"^7.6.0\",\n    \"express\": \"^4.16.2\",\n    \"jsdoc-to-markdown\": \"^4.0.1\",\n    \"koa\": \"^2.4.1\",\n    \"nyc\": \"^11.4.1\",\n    \"request-promise\": \"^4.2.2\"\n  },\n  \"author\": \"JD Wuarin <jwuarin@uk.ibm.com>\",\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "tests/.eslintrc.js",
    "content": "module.exports = {\n  extends: 'airbnb',\n  plugins: [\n    'ava',\n  ],\n  rules: {\n    'import/no-extraneous-dependencies': 'off',\n    'no-underscore-dangle': 'off',\n    semi: [2, 'always'],\n    'no-param-reassign': 'off',\n    'no-restricted-syntax': 'off',\n    'strict': 'off',\n  },\n};\n"
  },
  {
    "path": "tests/_mock_bot.js",
    "content": "'use strict';\n\nconst BaseBot = require('../lib/base_bot');\nconst express = require('express');\nconst expressBodyParser = require('body-parser');\nconst Koa = require('koa');\nconst assign = require('lodash').assign;\nconst get = require('lodash').get;\nconst merge = require('lodash').merge;\n\nclass MockBot extends BaseBot {\n\n  /**\n   * Bot class that allows testers to create instances of a large number\n   * of different bot instances with various different settings.\n   *\n   * @param {object} settings\n   */\n  constructor(settings) {\n    super(settings);\n    if (!settings) {\n      settings = {};\n    }\n    this.type = settings.type || 'mock';\n    // the following settings would be hard coded in a standard\n    // bot class implementation.\n    this.requiresWebhook = settings.requiresWebhook || false;\n    this.requiredCredentials = settings.requiredCredentials || [];\n\n    this.receives = settings.receives || {\n      text: true,\n      attachment: {\n        audio: true,\n        file: true,\n        image: true,\n        video: true,\n        location: true,\n        // can occur in FB messenger when user sends a message which only contains a URL\n        // most platforms won't support that\n        fallback: true,\n      },\n      echo: true,\n      read: true,\n      delivery: true,\n      postback: true,\n      quickReply: true,\n    };\n\n    this.sends = settings.sends || {\n      text: true,\n      quickReply: true,\n      locationQuickReply: true,\n      senderAction: {\n        typingOn: true,\n        typingOff: true,\n        markSeen: true,\n      },\n      attachment: {\n        audio: true,\n        file: true,\n        image: true,\n        video: true,\n      },\n    };\n\n    this.retrievesUserInfo = settings.retrievesUserInfo || false;\n    this.id = settings.id || 'mockId';\n\n    this.__applySettings(settings);\n    if (this.webhookEndpoint) {\n      if (this.webhookEndpoint.indexOf('koa') > -1) {\n        this.__createKoaMountPoints();\n      } else {\n        // default to express\n        this.__createExpressMountPoints();\n      }\n    }\n  }\n\n  // Note how neither of those classes uses webhookEndpoint.\n  // This is because I can now count on botmaster to make sure that requests\n  // meant to go to this bot are indeed routed to this bot.\n  __createExpressMountPoints() {\n    const app = express();\n    this.requestListener = app;\n\n    // for parsing application/json\n    app.use(expressBodyParser.json());\n\n    app.post('*', (req, res) => {\n      const update = this.__formatRawUpdate(req.body);\n      this.__emitUpdate(update);\n\n      res.sendStatus(200);\n    });\n  }\n\n  __createKoaMountPoints() {\n    const app = new Koa();\n    this.requestListener = app.callback();\n\n    app.use((ctx) => {\n      let bodyString = '';\n      ctx.req.on('data', (chunk) => {\n        bodyString += chunk;\n      });\n\n      ctx.req.on('end', () => {\n        const body = JSON.parse(bodyString);\n        const update = this.__formatRawUpdate(body);\n        this.__emitUpdate(update);\n      });\n\n      ctx.status = 200;\n    });\n  }\n\n\n  __formatRawUpdate(rawUpdate) {\n    const timestamp = Math.floor(Date.now());\n    const recipientId = get('recipient.id', rawUpdate, 'update_id');\n\n    const update = {\n      raw: rawUpdate,\n      sender: {\n        id: recipientId,\n      },\n      recipient: {\n        id: this.id,\n      },\n      timestamp,\n      message: {\n        mid: `${this.id}.${recipientId}.${String(timestamp)}.`,\n        seq: null,\n      },\n    };\n\n    merge(update, rawUpdate);\n\n    return update;\n  }\n\n  // doesn't actually do anything in mock_bot\n  __formatOutgoingMessage(outgoingMessage) {\n    const rawMessage = assign({}, outgoingMessage);\n    return Promise.resolve(rawMessage);\n  }\n\n  __sendMessage(rawMessage) {\n    const responseBody = {\n      nonStandard: 'responseBody',\n    };\n\n    return Promise.resolve(responseBody);\n  }\n\n  __createStandardBodyResponseComponents(sentOutgoingMessage, sentRawMessage, raw) {\n    const timestamp = Math.floor(Date.now());\n\n    return Promise.resolve({\n      recipient_id: sentRawMessage.recipient.id,\n      message_id: `${this.id}.${sentRawMessage.recipient.id}.${String(timestamp)}`,\n    });\n  }\n\n  __getUserInfo(userId) {\n    return Promise.resolve({\n      first_name: 'Peter',\n      last_name: 'Chang',\n      profile_pic: 'https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xpf1/v/t1.0-1/p200x200/13055603_10105219398495383_8237637584159975445_n.jpg?oh=1d241d4b6d4dac50eaf9bb73288ea192&oe=57AF5C03&__gda__=1470213755_ab17c8c8e3a0a447fed3f272fa2179ce',\n      locale: 'en_US',\n      timezone: -7,\n      gender: 'male',\n    });\n  }\n\n}\n\nmodule.exports = MockBot;\n"
  },
  {
    "path": "tests/base_bot/applySettings.js",
    "content": "import test from 'ava';\n\nimport MockBot from '../_mock_bot';\n\nconst errorTestTitleBase = 'should throw an error when controller is called';\nconst successTestTitleBase = 'should not throw an error when controller is called';\n\ntest(`${errorTestTitleBase} with a string`, (t) => {\n  t.plan(1);\n\n  const botSettings = 'invalid';\n\n  try {\n    const bot = new MockBot(botSettings);\n  } catch (err) {\n    t.is(err.message.indexOf('settings must be object') > -1, true);\n  }\n});\n\ntest(`${errorTestTitleBase} with no credentials, although class requires some`, (t) => {\n  t.plan(1);\n\n  const botSettings = {\n    requiredCredentials: ['token', 'password'],\n  };\n\n  try {\n    const bot = new MockBot(botSettings);\n  } catch (err) {\n    t.is(err.message.indexOf('no credentials specified') > -1, true);\n  }\n});\n\ntest(`${errorTestTitleBase} with misnamed credentials`, (t) => {\n  t.plan(1);\n\n  const botSettings = {\n    requiredCredentials: ['token', 'password'],\n    credentials: {\n      token: 'something',\n      pass: 'something else',\n    },\n  };\n\n  try {\n    const bot = new MockBot(botSettings);\n  } catch (err) {\n    console.log(err.message);\n    t.is(err.message.indexOf('are expected to have \\'password\\' credentials') > -1, true);\n  }\n});\n\ntest(`${successTestTitleBase} with correctly named credentials`, (t) => {\n  t.plan(1);\n\n  const botSettings = {\n    requiredCredentials: ['token', 'password'],\n    credentials: {\n      token: 'something',\n      password: 'something else',\n    },\n  };\n  const bot = new MockBot(botSettings);\n  t.pass();\n});\n\ntest(`${errorTestTitleBase} with no webhookEndpoint although it requires one`, (t) => {\n  t.plan(1);\n\n  const botSettings = {\n    requiresWebhook: true,\n  };\n\n  try {\n    const bot = new MockBot(botSettings);\n  } catch (err) {\n    t.is(err.message.indexOf('must be defined with webhookEndpoint') > -1, true);\n  }\n});\n\ntest(`${successTestTitleBase} with webhookEndpoint and it needs one`, (t) => {\n  t.plan(1);\n\n  const botSettings = {\n    requiresWebhook: true,\n    webhookEndpoint: 'webhook',\n  };\n\n  const bot = new MockBot(botSettings);\n  t.pass();\n});\n\ntest(`${errorTestTitleBase} with a webhookEndpoint although it does not requires one`, (t) => {\n  t.plan(1);\n\n  const botSettings = {\n    webhookEndpoint: 'webhook',\n  };\n\n  try {\n    const bot = new MockBot(botSettings);\n  } catch (err) {\n    t.is(err.message.indexOf('do not require webhookEndpoint in') > -1, true);\n  }\n});\n"
  },
  {
    "path": "tests/base_bot/emitUpdate.js",
    "content": "import test from 'ava';\n\nimport MockBot from '../_mock_bot';\n\ntest('Emits error when called from non owned bot', (t) => {\n  t.plan(1);\n\n  const bot = new MockBot();\n\n  return bot.__emitUpdate({})\n  .catch((err) => {\n    t.is(err.message, 'bot needs to be added to a botmaster instance ' +\n                      'in order to emit received updates');\n  });\n});\n"
  },
  {
    "path": "tests/base_bot/get_user_info.js",
    "content": "import test from 'ava';\n\nimport MockBot from '../_mock_bot';\n\ntest('throws error when bot type does not support retrieving user is', async (t) => {\n  t.plan(1);\n\n  const bot = new MockBot();\n  try {\n    await bot.getUserInfo('user_id');\n    t.fail('Error not returned');\n  } catch (err) {\n    t.is(err.message, 'Bots of type mock don\\'t provide access to user info.',\n        'Error message is not same as expected');\n  }\n});\n\ntest('works when bot type supports retrieving the info', async (t) => {\n  t.plan(1);\n\n  const bot = new MockBot({\n    retrievesUserInfo: true,\n  });\n\n  const userInfo = await bot.getUserInfo('user_id');\n  t.is(userInfo.first_name, 'Peter', 'userInfo is not same as expected');\n});\n"
  },
  {
    "path": "tests/base_bot/send_message.js",
    "content": "import test from 'ava';\nimport { outgoingMessageFixtures,\n         incomingUpdateFixtures,\n         attachmentFixtures } from 'botmaster-test-fixtures';\nimport { assign } from 'lodash';\n\nimport MockBot from '../_mock_bot';\n\nconst sendMessageMacro = async (t, params) => {\n  t.plan(5);\n  // test using promises\n  const body = await params.sendMessageMethod();\n\n  t.deepEqual(assign({}, body.sentOutgoingMessage), params.expectedSentMessage,\n    'sentOutgoingMessage is not same as message');\n  t.deepEqual(body.sentRawMessage, params.expectedSentMessage,\n    'sentRawMessage is not same as expected');\n  t.deepEqual(body.raw, { nonStandard: 'responseBody' },\n    'raw is not same as expected raw body response');\n  t.truthy(body.recipient_id, 'recipient_id not present');\n  t.truthy(body.message_id, 'message_id not present');\n};\n\nconst sendRawMessageMacro = async (t, params) => {\n  t.plan(1);\n\n  const body = await params.sendMessageMethod();\n\n  t.deepEqual(body, { nonStandard: 'responseBody' },\n    'body is not same as expected raw body response');\n};\n\nconst sendCascadeMessageMacro = async (t, params) => {\n  t.plan(params.planFor);\n\n  const bodies = await params.sendMessageMethod();\n\n  for (let i = 0; i < bodies.length; i += 1) {\n    const body = bodies[i];\n    if (body.raw) {\n      const expectedSentMessage = params.expectedSentMessages[i];\n      t.deepEqual(assign({}, body.sentOutgoingMessage), expectedSentMessage,\n        'sentOutgoingMessage is not same as message');\n      t.deepEqual(body.sentRawMessage, expectedSentMessage,\n        'sentRawMessage is not same as expected');\n      t.deepEqual(body.raw, { nonStandard: 'responseBody' },\n        'raw is not same as expected raw body response');\n      t.truthy(body.recipient_id, 'recipient_id not present');\n      t.truthy(body.message_id, 'message_id not present');\n    } else {\n      t.deepEqual(body, { nonStandard: 'responseBody' },\n        'body is not same as expected raw body response');\n    }\n  }\n};\n\nconst sendMessageErrorMacro = async (t, params) => {\n  t.plan(1);\n\n  try {\n    await params.sendMessageMethod();\n    t.false(true, 'Error should have been returned, but didn\\'t get any');\n  } catch (err) {\n    t.deepEqual(err.message, params.expectedErrorMessage,\n      'Error message is not same as expected');\n  }\n};\n\n\n// All tests are isolated in own scopes in order to be properly setup\n{\n  const bot = new MockBot();\n  const messageToSend = outgoingMessageFixtures.audioMessage();\n\n  test('#sendMessage works', sendMessageMacro, {\n    sendMessageMethod: bot.sendMessage.bind(bot, messageToSend),\n    expectedSentMessage: outgoingMessageFixtures.audioMessage(),\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  // patching bot just to see if that works too with callbacks\n  const patchedBot = bot.__createBotPatchedWithUpdate({});\n  const messageToSend = outgoingMessageFixtures.audioMessage();\n\n  test('#sendMessage throws error when sendOptions is not of valid type on a patched bot', sendMessageErrorMacro, {\n    sendMessageMethod: patchedBot.sendMessage.bind(patchedBot, messageToSend, 'Should not be valid'),\n    expectedErrorMessage: 'sendOptions must be of type object. Got string instead',\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  const messageToSend = outgoingMessageFixtures.audioMessage();\n\n  test('#sendMessage throws error when sendOptions is not of valid type on a non patched bot', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendMessage.bind(bot, messageToSend, 'Should not be valid'),\n    expectedErrorMessage: 'sendOptions must be of type object. Got string instead',\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  const patchedBot = bot.__createBotPatchedWithUpdate({});\n  const messageToSend = outgoingMessageFixtures.audioMessage();\n\n  test('#sendMessage throws error when tried to use with callback', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendMessage.bind(bot, messageToSend, () => {}),\n    expectedErrorMessage: 'Using botmaster sendMessage type methods ' +\n      'with callback functions is no longer supported in botmaster 3. ' +\n      'See the latest documentation ' +\n      'at http://botmasterai.com to see the preferred syntax. ' +\n      'Alternatively, you can downgrade botmaster to 2.x.x by doing: ' +\n      '\"npm install --save botmaster@2.x.x\" or \"yarn add botmaster@2.x.x\"',\n  });\n\n  test('#sendMessage throws error when cb is not of valid type on a patched bot', sendMessageErrorMacro, {\n    sendMessageMethod: patchedBot.sendMessage.bind(patchedBot, messageToSend, () => {}),\n    expectedErrorMessage: 'Using botmaster sendMessage type methods ' +\n      'with callback functions is no longer supported in botmaster 3. ' +\n      'See the latest documentation ' +\n      'at http://botmasterai.com to see the preferred syntax. ' +\n      'Alternatively, you can downgrade botmaster to 2.x.x by doing: ' +\n      '\"npm install --save botmaster@2.x.x\" or \"yarn add botmaster@2.x.x\"',\n  });\n}\n\n{\n  const bot = new MockBot();\n  const messageToSend = outgoingMessageFixtures.audioMessage();\n\n  test('#sendRawMessage works', sendRawMessageMacro, {\n    sendMessageMethod: bot.sendRawMessage.bind(bot, messageToSend),\n    expectedSentMessage: outgoingMessageFixtures.audioMessage(),\n  });\n}\n\n{\n  const bot = new MockBot();\n  const messageToSend = outgoingMessageFixtures.audioMessage();\n\n  test('#sendMessage works with sendOptions', sendMessageMacro, {\n    sendMessageMethod: bot.sendMessage.bind(bot, messageToSend, { ignoreMiddleware: true }),\n    expectedSentMessage: outgoingMessageFixtures.audioMessage(),\n  });\n}\n\n{\n  const bot = new MockBot();\n  const subMessagePart = {\n    text: 'Hello World!',\n  };\n\n  test('#sendMessageTo works', sendMessageMacro, {\n    sendMessageMethod: bot.sendMessageTo.bind(bot, subMessagePart, 'user_id'),\n    expectedSentMessage: outgoingMessageFixtures.textMessage(),\n  });\n}\n\n{\n  const bot = new MockBot({\n    sends: {\n      text: false,\n    },\n  });\n\n  test('#sendTextMessageTo throws error if bot class does not support text', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendTextMessageTo.bind(bot, 'Hello World!', 'user_id'),\n    expectedErrorMessage: 'Bots of type mock can\\'t send messages with text',\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  test('#sendTextMessageTo works', sendMessageMacro, {\n    sendMessageMethod: bot.sendTextMessageTo.bind(bot, 'Hello World!', 'user_id'),\n    expectedSentMessage: outgoingMessageFixtures.textMessage(),\n  });\n}\n\n{\n  const bot = new MockBot({\n    sends: {\n      text: false,\n    },\n  });\n\n  const updateToReplyTo = incomingUpdateFixtures.textUpdate();\n\n  test('#reply throws error if bot class does not support text', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendTextMessageTo.bind(bot, updateToReplyTo, 'Hello World!'),\n    expectedErrorMessage: 'Bots of type mock can\\'t send messages with text',\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  const updateToReplyTo = incomingUpdateFixtures.textUpdate();\n  // patching bot just to see if that works too with callbacks\n  const patchedBot = bot.__createBotPatchedWithUpdate(updateToReplyTo);\n\n  test('#reply works', sendMessageMacro, {\n    sendMessageMethod: patchedBot.reply.bind(patchedBot, updateToReplyTo, 'Hello World!'),\n    expectedSentMessage: outgoingMessageFixtures.textMessage(),\n  });\n}\n\n{\n  const bot = new MockBot({\n    sends: {\n      attachment: false,\n    },\n  });\n\n  const attachment = attachmentFixtures.audioAttachment();\n\n  test('#sendAttachmentTo throws error if bot class does not support attachment', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendAttachmentTo.bind(bot, attachment, 'user_id'),\n    expectedErrorMessage: 'Bots of type mock can\\'t send messages with attachment',\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  const attachment = attachmentFixtures.audioAttachment();\n\n  test('#sendAttachmentTo works', sendMessageMacro, {\n    sendMessageMethod: bot.sendAttachmentTo.bind(bot, attachment, 'user_id'),\n    expectedSentMessage: outgoingMessageFixtures.audioMessage(),\n  });\n}\n\n{\n  const bot = new MockBot({\n    sends: {\n      attachment: false,\n    },\n  });\n\n  test('#sendAttachmentFromUrlTo throws error if bot class does not support attachment', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendAttachmentFromUrlTo.bind(\n      bot, 'audio', 'SOME_AUDIO_URL', 'user_id'),\n    expectedErrorMessage: 'Bots of type mock can\\'t send messages with attachment',\n  });\n}\n\n{\n  const bot = new MockBot({\n    sends: {\n      attachment: {\n        audio: false,\n        image: true,\n      },\n    },\n  });\n\n  test('#sendAttachmentFromUrlTo throws error if bot class does not support attachment of specific type', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendAttachmentFromUrlTo.bind(\n      bot, 'audio', 'SOME_AUDIO_URL', 'user_id'),\n    expectedErrorMessage: 'Bots of type mock can\\'t send messages with audio attachment',\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  test('#sendAttachmentFromUrlTo works', sendMessageMacro, {\n    sendMessageMethod: bot.sendAttachmentFromUrlTo.bind(\n      bot, 'audio', 'SOME_AUDIO_URL', 'user_id'),\n    expectedSentMessage: outgoingMessageFixtures.audioMessage(),\n  });\n}\n\n{\n  const bot = new MockBot({\n    sends: {\n      quickReply: false,\n    },\n  });\n\n  test('#sendDefaultButtonMessageTo throws error if bot class does not support quickReply', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendDefaultButtonMessageTo.bind(\n      bot, [], undefined, 'user_id'),\n    expectedErrorMessage: 'Bots of type mock can\\'t send messages with quick replies',\n  });\n}\n\n{\n  const bot = new MockBot();\n  const buttonTitles = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'];\n\n  test('#sendDefaultButtonMessageTo throws error if button count is larger than 10', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendDefaultButtonMessageTo.bind(\n      bot, buttonTitles, undefined, 'user_id'),\n    expectedErrorMessage: 'buttonTitles must be of length 10 or less',\n  });\n}\n\n{\n  const bot = new MockBot({\n    sends: {\n      text: false,\n      quickReply: true,\n    },\n  });\n\n  test('#sendDefaultButtonMessageTo throws error if bot class does not support text and text is set', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendDefaultButtonMessageTo.bind(\n      bot, [], 'Click on one of', 'user_id'),\n    expectedErrorMessage: 'Bots of type mock can\\'t send messages with text',\n  });\n}\n\n{\n  const bot = new MockBot({\n    sends: {\n      attachment: false,\n      quickReply: true,\n    },\n  });\n\n  test('#sendDefaultButtonMessageTo throws error if bot class does not support attachment and attachment is set', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendDefaultButtonMessageTo.bind(\n      bot, [], attachmentFixtures.imageAttachment(), 'user_id'),\n    expectedErrorMessage: 'Bots of type mock can\\'t send messages with attachment',\n  });\n}\n\n{\n  const bot = new MockBot({\n    sends: {\n      attachment: {\n        image: false,\n      },\n      quickReply: true,\n    },\n  });\n\n  test('#sendDefaultButtonMessageTo throws error if bot class does not support image attachment and image attachment is set', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendDefaultButtonMessageTo.bind(\n      bot, [], attachmentFixtures.imageAttachment(), 'user_id'),\n    expectedErrorMessage: 'Bots of type mock can\\'t send messages with image attachment',\n  });\n}\n\n{\n  const bot = new MockBot();\n  const buttonTitles = ['B1', 'B2'];\n\n  test('#sendDefaultButtonMessageTo throws error if textOrAttachment is not valid', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendDefaultButtonMessageTo.bind(\n      bot, buttonTitles, new MockBot(), 'user_id'),\n    expectedErrorMessage: 'third argument must be a \"String\", an attachment \"Object\" or absent',\n  });\n}\n\n{\n  const bot = new MockBot();\n  const buttonTitles = ['B1', 'B2'];\n\n  const quickReplies = [\n    {\n      content_type: 'text',\n      title: 'B1',\n      payload: 'B1',\n    },\n    {\n      content_type: 'text',\n      title: 'B2',\n      payload: 'B2',\n    },\n  ];\n\n  const expectedSentMessage = outgoingMessageFixtures.textOnlyQuickReplyMessage();\n  expectedSentMessage.message.quick_replies = quickReplies;\n\n  test('#sendDefaultButtonMessageTo works with falsy textOrAttachment', sendMessageMacro, {\n    expectedSentMessage,\n    sendMessageMethod: bot.sendDefaultButtonMessageTo.bind(\n      bot, buttonTitles, undefined, 'user_id'),\n  });\n}\n\n{\n  const bot = new MockBot();\n  const buttonTitles = ['B1', 'B2'];\n\n  const quickReplies = [\n    {\n      content_type: 'text',\n      title: 'B1',\n      payload: 'B1',\n    },\n    {\n      content_type: 'text',\n      title: 'B2',\n      payload: 'B2',\n    },\n  ];\n\n  const expectedSentMessage = outgoingMessageFixtures.textOnlyQuickReplyMessage();\n  expectedSentMessage.message.quick_replies = quickReplies;\n  expectedSentMessage.message.text = 'Click one of:';\n\n  test('#sendDefaultButtonMessageTo works with text type textOrAttachment', sendMessageMacro, {\n    expectedSentMessage,\n    sendMessageMethod: bot.sendDefaultButtonMessageTo.bind(\n      bot, buttonTitles, 'Click one of:', 'user_id'),\n  });\n}\n\n{\n  const bot = new MockBot();\n  const buttonTitles = ['B1', 'B2'];\n\n  const quickReplies = [\n    {\n      content_type: 'text',\n      title: 'B1',\n      payload: 'B1',\n    },\n    {\n      content_type: 'text',\n      title: 'B2',\n      payload: 'B2',\n    },\n  ];\n\n  const expectedSentMessage = outgoingMessageFixtures.textOnlyQuickReplyMessage();\n  expectedSentMessage.message.quick_replies = quickReplies;\n  delete expectedSentMessage.message.text;\n  expectedSentMessage.message.attachment = attachmentFixtures.imageAttachment();\n\n  test('#sendDefaultButtonMessageTo works with object type textOrAttachment', sendMessageMacro, {\n    expectedSentMessage,\n    sendMessageMethod: bot.sendDefaultButtonMessageTo.bind(\n      bot, buttonTitles, attachmentFixtures.imageAttachment(), 'user_id'),\n  });\n}\n\n{\n  const bot = new MockBot({\n    sends: {\n      text: false,\n    },\n  });\n\n  test('#sendIsTypingMessageTo throws error if bot class does not support typing_on sender action', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendIsTypingMessageTo.bind(bot, 'user_id'),\n    expectedErrorMessage: 'Bots of type mock can\\'t send messages with typing_on sender action',\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  test('#sendIsTypingMessageTo works', sendMessageMacro, {\n    sendMessageMethod: bot.sendIsTypingMessageTo.bind(bot, 'user_id'),\n    expectedSentMessage: outgoingMessageFixtures.typingOnMessage(),\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  test('#sendCascade throws error when used with no valid params', sendMessageErrorMacro, {\n    sendMessageMethod: bot.sendCascade.bind(bot, [{}]),\n    expectedErrorMessage: 'No valid message options specified',\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  const rawMessage1 = {\n    nonStandard: 'message1',\n    recipient: {\n      id: 'user_id',\n    },\n  };\n  const rawMessage2 = {\n    nonStandard: 'message2',\n    recipient: {\n      id: 'user_id',\n    },\n  };\n\n  const messageArray = [{ raw: rawMessage1 }, { raw: rawMessage2 }];\n\n  test('#sendCascade works with raw messages', sendCascadeMessageMacro, {\n    sendMessageMethod: bot.sendCascade.bind(bot, messageArray),\n    planFor: 2, // num assertions to plan for\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  const message1 = outgoingMessageFixtures.textOnlyQuickReplyMessage();\n  const message2 = outgoingMessageFixtures.imageMessage();\n\n  const messageArray = [{ message: message1 }, { message: message2 }];\n  const expectedSentMessages = [message1, message2];\n\n  test('#sendCascade works with valid messages', sendCascadeMessageMacro, {\n    sendMessageMethod: bot.sendCascade.bind(bot, messageArray),\n    planFor: 10,\n    expectedSentMessages,\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  const message1 = outgoingMessageFixtures.textOnlyQuickReplyMessage();\n\n  const messageArray = [{ message: message1 }];\n  const expectedSentMessages = [message1];\n\n  test('#sendCascade works with single valid messages', sendCascadeMessageMacro, {\n    sendMessageMethod: bot.sendCascade.bind(bot, messageArray),\n    planFor: 5,\n    expectedSentMessages,\n  });\n}\n\n{\n  const bot = new MockBot();\n\n  const rawMessage1 = {\n    nonStandard: 'message1',\n    recipient: {\n      id: 'user_id',\n    },\n  };\n  const message2 = outgoingMessageFixtures.imageMessage();\n\n  const messageArray = [{ raw: rawMessage1 }, { message: message2 }];\n  const expectedSentMessages = [rawMessage1, message2];\n\n  test('#sendCascade works with mixed raw and botmaster messages', sendCascadeMessageMacro, {\n    sendMessageMethod: bot.sendCascade.bind(bot, messageArray),\n    planFor: 6,\n    expectedSentMessages,\n  });\n}\n\n\n{\n  const bot = new MockBot();\n\n  const textArray = ['Hello World!', 'Goodbye World!'];\n  const secondExpectedMessage = outgoingMessageFixtures.textMessage();\n  secondExpectedMessage.message.text = 'Goodbye World!';\n  const expectedSentMessages = [\n    outgoingMessageFixtures.textMessage(),\n    secondExpectedMessage,\n  ];\n\n  test('#sendTextCascadeTo works', sendCascadeMessageMacro, {\n    sendMessageMethod: bot.sendTextCascadeTo.bind(bot, textArray, 'user_id'),\n    planFor: 10,\n    expectedSentMessages,\n  });\n}\n"
  },
  {
    "path": "tests/botmaster/add_bot.js",
    "content": "import test from 'ava';\nimport http from 'http';\nimport express from 'express';\nimport request from 'request-promise';\n\nimport Botmaster from '../../lib';\nimport MockBot from '../_mock_bot';\n\ntest('works with a bot that doesn\\'t require webhhooks', (t) => {\n  t.plan(3);\n\n  return new Promise((resolve) => {\n    const botmaster = new Botmaster();\n\n    botmaster.on('listening', () => {\n      const bot = new MockBot();\n\n      botmaster.addBot(bot);\n      t.is(Object.keys(botmaster.__serverRequestListeners).length, 0);\n      t.is(botmaster.bots.length, 1);\n      t.is(botmaster.bots[0], bot);\n      botmaster.server.close(resolve);\n    });\n  });\n});\n\nconst arbitraryBotMacro = (t, { botmasterSettings, botSettings }) => {\n  t.plan(3);\n  return new Promise((resolve) => {\n    const botmaster = new Botmaster(botmasterSettings);\n\n    botmaster.on('listening', () => {\n      const bot = new MockBot(botSettings);\n\n      botmaster.addBot(bot);\n      t.is(Object.keys(botmaster.__serverRequestListeners).length, 1);\n      t.is(botmaster.bots.length, 1);\n\n      const uri = botmaster.settings.useDefaultMountPathPrepend\n        ? `http://localhost:3000/${botSettings.type}/webhook/endpoint`\n        : 'http://localhost:3000/webhook/endpoint';\n\n      const updateToSend = { text: 'Hello world' };\n      const requestOptions = {\n        method: 'POST',\n        uri,\n        json: updateToSend,\n      };\n\n      request(requestOptions);\n\n      botmaster.use({\n        type: 'incoming',\n        controller: (onUpdateBot, update) => {\n          t.deepEqual(update.raw, updateToSend);\n          botmaster.server.close(resolve);\n        },\n      });\n\n      botmaster.on('error', () => {\n        botmaster.server.close(resolve);\n      });\n    });\n  });\n};\n\ntest('works with an express bot', arbitraryBotMacro, {\n  botSettings: {\n    requiresWebhook: true,\n    webhookEndpoint: 'webhook/endpoint',\n    type: 'express',\n  },\n});\n\ntest('works with a koa bot', arbitraryBotMacro, {\n  botSettings: {\n    requiresWebhook: true,\n    webhookEndpoint: 'webhook/endpoint',\n    type: 'koa',\n  },\n});\n\ntest('works with a webhook that has slash bot', arbitraryBotMacro, {\n  botSettings: {\n    requiresWebhook: true,\n    webhookEndpoint: '/webhook/endpoint/',\n    type: 'express',\n  },\n});\n\n// this test could also have been in constructor. As it spans over both constructor and bot adding\ntest('should accept requests where expected when useDefaultMountPathPrepend is truthy', arbitraryBotMacro, {\n  botmasterSettings: {\n    useDefaultMountPathPrepend: false,\n  },\n  botSettings: {\n    requiresWebhook: true,\n    webhookEndpoint: 'webhook/endpoint',\n    type: 'express',\n  },\n});\n\ntest('works with an express server AND both an express and a koa bot', (t) => {\n  t.plan(6);\n\n  return new Promise((resolve) => {\n    // just dev's personal app stuff\n    const app = express();\n\n    const appResponse = {\n      text: 'wadup?',\n    };\n\n    app.use('/someRoute', (req, res) => {\n      res.json(appResponse);\n    });\n    // ///////////////////////////////\n\n    const myServer = http.createServer(app);\n\n    const botmaster = new Botmaster({\n      server: myServer,\n    });\n\n    myServer.listen(3000, () => {\n      // creating and adding bots\n      const koaBotSettings = {\n        requiresWebhook: true,\n        webhookEndpoint: 'webhook',\n        type: 'koa',\n      };\n\n      const expressBotSettings = {\n        requiresWebhook: true,\n        webhookEndpoint: 'webhook',\n        type: 'express',\n      };\n      const koaBot = new MockBot(koaBotSettings);\n      const expressBot = new MockBot(expressBotSettings);\n\n      botmaster.addBot(koaBot);\n      botmaster.addBot(expressBot);\n      t.is(Object.keys(botmaster.__serverRequestListeners).length, 2);\n      t.is(botmaster.bots.length, 2);\n      // ///////////////////////////////\n\n      // send requests to bots\n      const updateToSendToKoaBot = { text: 'Hello Koa Bot' };\n      const updateToSendToExpressBot = { text: 'Hello express Bot' };\n\n      const koaBotRequestOptions = {\n        method: 'POST',\n        uri: 'http://localhost:3000/koa/webhook',\n        json: updateToSendToKoaBot,\n      };\n\n      const expressBotRequestOptions = {\n        method: 'POST',\n        uri: 'http://localhost:3000/express/webhook',\n        json: updateToSendToExpressBot,\n      };\n\n      request(koaBotRequestOptions)\n      .then(() => request(expressBotRequestOptions));\n      // ////////////////////////////\n\n      // catch update events\n      let receivedUpdatesCount = 0;\n      botmaster.use({\n        type: 'incoming',\n        controller: ('update', (onUpdateBot, update) => {\n          receivedUpdatesCount += 1;\n          if (update.raw.text.indexOf('Koa') > -1) {\n            t.deepEqual(update.raw, updateToSendToKoaBot);\n          } else if (update.raw.text.indexOf('express') > -1) {\n            t.deepEqual(update.raw, updateToSendToExpressBot);\n          }\n          if (receivedUpdatesCount === 2) {\n            const appRequestOptions = {\n              uri: 'http://localhost:3000/someRoute',\n              json: true,\n            };\n            request.get(appRequestOptions)\n\n            .then((body) => {\n              t.deepEqual(appResponse, body);\n              t.is(botmaster.server, myServer);\n              botmaster.server.close(resolve);\n            });\n          }\n        }),\n      });\n      // ////////////////////////////\n    });\n  });\n});\n"
  },
  {
    "path": "tests/botmaster/constructor.js",
    "content": "import test from 'ava';\nimport http from 'http';\nimport express from 'express';\nimport Koa from 'koa';\nimport request from 'request-promise';\n\nimport MockBot from '../_mock_bot';\nimport Botmaster from '../../lib';\n\n// just this code to make sure unhandled exceptions are printed to\n// the console when developing.\nprocess.on('unhandledRejection', (err) => {\n  console.error('UNHANDLED REJECTION', err.stack);\n});\n\n// const request = require('request-promise');\n// const JsonFileStore = require('jfs');\n\ntest('shouldn\\'t throw any error if settings aren\\'t specified', (t) => {\n  t.plan(2);\n\n  return new Promise((resolve) => {\n    const botmaster = new Botmaster();\n\n    botmaster.on('listening', () => {\n      t.is(botmaster.server.address().port, 3000);\n      botmaster.server.close(() => {\n        t.pass();\n        resolve();\n      });\n    });\n  });\n});\n\ntest('should throw any error if settings.botsSetting are specified', (t) => {\n  t.plan(1);\n\n  const settings = {\n    botsSettings: 'something',\n  };\n  try {\n    const botmaster = new Botmaster(settings);\n  } catch (e) {\n    t.is(e.message.indexOf(\n      'Starting botmaster with botsSettings is no longer supported') > -1,\n      true, e.message);\n  }\n});\n\ntest('should throw any error if settings.app are specified', (t) => {\n  t.plan(1);\n\n  const settings = {\n    app: 'something',\n  };\n  try {\n    const botmaster = new Botmaster(settings);\n  } catch (e) {\n    t.is(e.message.indexOf(\n      'Starting botmaster with app is no longer') > -1, true,\n      e.message);\n  }\n});\n\ntest('should use my server when passed in settings', (t) => {\n  t.plan(2);\n  const myServer = http.createServer();\n\n  const settings = {\n    server: myServer,\n  };\n  const botmaster = new Botmaster(settings);\n  t.is(botmaster.server === myServer, true);\n  t.is(botmaster.server, myServer);\n});\n\ntest('should correctly set port when passed in settings', (t) => {\n  t.plan(1);\n\n  return new Promise((resolve) => {\n    const settings = {\n      port: 5000,\n    };\n    const botmaster = new Botmaster(settings);\n\n    botmaster.on('listening', () => {\n      t.is(botmaster.server.address().port, 5000);\n      botmaster.server.close(resolve);\n    });\n  });\n});\n\ntest('should throw and error when server and port passed in settings', (t) => {\n  t.plan(1);\n\n  const myServer = http.createServer();\n\n  try {\n    const settings = {\n      server: myServer,\n      port: 4000,\n    };\n    const botmaster = new Botmaster(settings);\n  } catch (e) {\n    t.is(e.message.indexOf('IncompatibleArgumentsError') > -1, true);\n  }\n});\n\ntest('when used with default botmaster server,' +\n     'requestListener should return 404s to unfound routes', (t) => {\n  t.plan(1);\n\n  return new Promise((resolve) => {\n    const botmaster = new Botmaster();\n\n    botmaster.on('listening', () => {\n      const options = {\n        uri: 'http://localhost:3000/someRoute',\n        json: true,\n      };\n      request.get(options)\n\n      .catch((err) => {\n        t.is(err.error.message, 'Couldn\\'t GET /someRoute');\n        botmaster.server.close(resolve);\n      });\n    });\n  });\n});\n\ntest('when used with a server created with an express app' +\n     'requestListener should route non botmaster requests to express app', (t) => {\n  t.plan(2);\n  const app = express();\n  const appResponse = {\n    text: 'wadup?',\n  };\n\n  app.use('/someRoute', (req, res) => {\n    res.json(appResponse);\n  });\n\n  return new Promise((resolve) => {\n    const myServer = app.listen(3000);\n    const botmaster = new Botmaster({ server: myServer });\n\n    myServer.on('listening', () => {\n      const options = {\n        uri: 'http://localhost:3000/someRoute',\n        json: true,\n      };\n      request.get(options)\n\n      .then((body) => {\n        t.deepEqual(appResponse, body);\n        t.is(botmaster.server, myServer);\n        botmaster.server.close(resolve);\n      });\n    });\n  });\n});\n\ntest('when used with a server created with a koa app' +\n     'requestListener should route non botmaster requests to koa app', (t) => {\n  t.plan(2);\n  const app = new Koa();\n  const appResponse = {\n    text: 'wadup?',\n  };\n\n  app.use((ctx) => {\n    if (ctx.request.url === '/someRoute') {\n      ctx.body = appResponse;\n    }\n  });\n\n  return new Promise((resolve) => {\n    const myServer = app.listen(3000);\n    const botmaster = new Botmaster({ server: myServer });\n\n    myServer.on('listening', () => {\n      const options = {\n        uri: 'http://localhost:3000/someRoute',\n        json: true,\n      };\n      request.get(options)\n\n      .then((body) => {\n        t.deepEqual(body, appResponse);\n        t.is(botmaster.server, myServer);\n        botmaster.server.close(() => {\n          resolve();\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "tests/botmaster/get_bot.js",
    "content": "import test from 'ava';\n\nimport Botmaster from '../../lib';\nimport MockBot from '../_mock_bot';\n\nconst testTitleBase = 'Botmaster #getBot';\n\nlet botmaster;\nlet botOne;\nlet botTwo;\nlet botThree;\n\ntest.before(() => {\n  const botOneOptions = {\n    type: 'platformOne',\n    id: 'botOne',\n  };\n  botOne = new MockBot(botOneOptions);\n\n  const botTwoOptions = {\n    type: 'platformOne', // same type as botOne (but added after)\n    id: 'botTwo',\n  };\n  botTwo = new MockBot(botTwoOptions);\n\n  const botThreeOptions = {\n    type: 'platformThree',\n    id: 'botThree',\n  };\n  botThree = new MockBot(botThreeOptions);\n\n  // just using createServer here so I don't have to close it after.\n  // i.e. no need for before and after hooks\n  botmaster = new Botmaster();\n\n  botmaster.addBot(botOne);\n  botmaster.addBot(botTwo);\n  botmaster.addBot(botThree);\n});\n\ntest(`${testTitleBase} should throw an error when getting called without any options `, (t) => {\n  t.plan(1);\n\n  try {\n    botmaster.getBot();\n  } catch (err) {\n    t.is(err.message.indexOf('needs exactly one of') > -1, true);\n  }\n});\n\ntest(`${testTitleBase} should throw an error when getting called without two options`, (t) => {\n  t.plan(1);\n\n  try {\n    botmaster.getBot({\n      type: 'platformOne',\n      id: 'botOne',\n    });\n  } catch (err) {\n    t.is(err.message.indexOf('needs exactly one of') > -1, true);\n  }\n});\n\ntest(`${testTitleBase} should work when getting called with only id option`, (t) => {\n  t.plan(1);\n\n  const bot = botmaster.getBot({\n    id: 'botOne',\n  });\n  t.is(bot, botOne);\n});\n\ntest(`${testTitleBase} should work when getting called with only type option`, (t) => {\n  t.plan(1);\n\n  const bot = botmaster.getBot({\n    type: 'platformOne',\n  });\n  t.is(bot, botOne);\n});\n\ntest(`${testTitleBase}s should work when getting called with only type option`, (t) => {\n  t.plan(1);\n\n  try {\n    botmaster.getBots({\n      type: 'platformOne',\n    });\n  } catch (err) {\n    t.is(err.message.indexOf('takes in a string as') > -1, true);\n  }\n});\n\ntest(`${testTitleBase}s should return bots of a certain type when requested`, (t) => {\n  t.plan(6);\n\n  const platformOneBots = botmaster.getBots('platformOne');\n  t.is(platformOneBots.length, 2);\n  t.is(platformOneBots[0].type, 'platformOne');\n  t.is(platformOneBots[1].type, 'platformOne');\n\n  const platformTwoBots = botmaster.getBots('platformTwo');\n  t.is(platformTwoBots.length, 0);\n\n  const platformThreeBots = botmaster.getBots('platformThree');\n  t.is(platformThreeBots.length, 1);\n  t.is(platformThreeBots[0].type, 'platformThree');\n});\n\ntest.after(() => {\n  return new Promise((resolve) => {\n    botmaster.server.close(resolve);\n  });\n});\n"
  },
  {
    "path": "tests/botmaster/remove_bot.js",
    "content": "import test from 'ava';\nimport request from 'request-promise';\n\nimport Botmaster from '../../lib';\nimport MockBot from '../_mock_bot';\n\ntest('works with a bot that doesn\\'t require webhhooks', (t) => {\n  t.plan(2);\n\n  return new Promise((resolve) => {\n    const botmaster = new Botmaster();\n\n    botmaster.on('listening', () => {\n      const bot = new MockBot();\n\n      botmaster.addBot(bot);\n      botmaster.removeBot(bot);\n      t.is(Object.keys(botmaster.__serverRequestListeners).length, 0);\n      t.is(botmaster.bots.length, 0);\n      botmaster.server.close(resolve);\n    });\n  });\n});\n\nconst arbitraryBotMacro = (t, { botmasterSettings, botSettings }) => {\n  t.plan(3);\n  console.log(botSettings);\n  return new Promise((resolve) => {\n    const botmaster = new Botmaster(botmasterSettings);\n\n    botmaster.on('listening', () => {\n      const bot = new MockBot(botSettings);\n\n      botmaster.addBot(bot);\n      botmaster.removeBot(bot);\n      t.is(Object.keys(botmaster.__serverRequestListeners).length, 0);\n      t.is(botmaster.bots.length, 0);\n\n      const updateToSend = { text: 'Hello world' };\n      const requestOptions = {\n        method: 'POST',\n        uri: `http://localhost:3000/mock/${botSettings.webhookEndpoint}`,\n        json: updateToSend,\n      };\n\n      request(requestOptions)\n\n      .catch((err) => {\n        t.is(err.error.message,\n          `Couldn't POST /mock/${botSettings.webhookEndpoint}`);\n        botmaster.server.close(resolve);\n      });\n    });\n  });\n};\n\ntest('works with an express bot', arbitraryBotMacro, {\n  botSettings: {\n    requiresWebhook: true,\n    webhookEndpoint: 'express',\n  },\n});\n\ntest('works with a koa bot', arbitraryBotMacro, {\n  botSettings: {\n    requiresWebhook: true,\n    webhookEndpoint: 'koa',\n  },\n});\n\ntest('Removes path if useDefaultMountPathPrepend is false', arbitraryBotMacro, {\n  botmasterSettings: {\n    useDefaultMountPathPrepend: false,\n  },\n  botSettings: {\n    requiresWebhook: true,\n    webhookEndpoint: '/express/',\n  },\n});\n"
  },
  {
    "path": "tests/index.js",
    "content": "'use strict';\n\nconst MockBot = require('./_mock_bot');\n\n// if using MockBot in your library, just do a require('botmaster/tests').MockBot\n// and make sure the following packages are either in your dependencies exports\n// dev-dependencies:\n\n/**\n * express\n * koa\n * body-parser\n */\n\nmodule.exports = {\n  MockBot,\n};\n"
  },
  {
    "path": "tests/middleware/use.js",
    "content": "import test from 'ava';\nimport request from 'request-promise';\nimport { assign } from 'lodash';\nimport { outgoingMessageFixtures,\n         incomingUpdateFixtures } from 'botmaster-test-fixtures';\n\nimport Botmaster from '../../lib';\nimport MockBot from '../_mock_bot';\n\ntest.beforeEach((t) => {\n  return new Promise((resolve) => {\n    t.context.botmaster = new Botmaster();\n    t.context.bot = new MockBot({\n      requiresWebhook: true,\n      webhookEndpoint: 'webhook',\n      type: 'express',\n    });\n    t.context.botmaster.addBot(t.context.bot);\n    t.context.baseRequestOptions = {\n      method: 'POST',\n      uri: 'http://localhost:3000/express/webhook',\n      body: {},\n      json: true,\n      resolveWithFullResponse: true,\n    };\n    t.context.botmaster.on('listening', resolve);\n  });\n});\n\ntest.afterEach((t) => {\n  return new Promise((resolve) => {\n    t.context.botmaster.server.close(resolve);\n  });\n});\n\ntest('throws an error middleware is not an object', (t) => {\n  t.plan(1);\n\n  try {\n    t.context.botmaster.use('something');\n  } catch (err) {\n    t.is(err.message,\n      'middleware should be an object. Not string',\n      'Error message is not the same as expected');\n  }\n});\n\ntest('throws an error if type is not incoming or outgoing', (t) => {\n  t.plan(1);\n\n  try {\n    t.context.botmaster.use({\n      type: 'something',\n    });\n  } catch (err) {\n    t.is(err.message,\n      'invalid middleware type. Type should be either \\'incoming\\' or \\'outgoing\\'',\n      'Error message is not the same as expected');\n  }\n});\n\ntest('throws an error if controller is not defined', (t) => {\n  t.plan(1);\n\n  try {\n    t.context.botmaster.use({\n      type: 'incoming',\n    });\n  } catch (err) {\n    t.is(err.message,\n      'middleware controller can\\'t be of type undefined. It needs to be a function',\n      'Error message is not the same as expected');\n  }\n});\n\ntest('throws an error if middlewareCallback is not a function', (t) => {\n  t.plan(1);\n\n  try {\n    t.context.botmaster.use({\n      type: 'incoming',\n      controller: 'not valid',\n    });\n  } catch (err) {\n    t.is(err.message,\n      'middleware controller can\\'t be of type string. It needs to be a function',\n      'Error message is not the same as expected');\n  }\n});\n\nconst incomingMiddlewareErrorMacro = (t, controller) => {\n  t.plan(1);\n\n  return new Promise((resolve) => {\n    const botmaster = t.context.botmaster;\n\n    botmaster.use({\n      controller,\n      type: 'incoming',\n    });\n\n    botmaster.use({\n      type: 'incoming',\n      controller: () => {\n        t.fail('this middleware should not get hit');\n        resolve();\n      },\n    });\n\n    botmaster.on('error', (bot, err) => {\n      t.is(err.message,\n        '\"update.blop is not a function\". This is most probably on your end.',\n        'Error message did not match');\n      resolve();\n    });\n\n    request(t.context.baseRequestOptions);\n  });\n};\n\nincomingMiddlewareErrorMacro.title = customTitlePart =>\n  `Errors in incoming middleware are emitted correctly ${customTitlePart}`;\n\ntest('in synchronous middleware', incomingMiddlewareErrorMacro,\n  (bot, update) => {\n    update.blop();\n  });\n\ntest('using next', incomingMiddlewareErrorMacro,\n  (bot, update, next) => {\n    process.nextTick(() => {\n      try {\n        update.blop();\n      } catch (err) {\n        next(err);\n      }\n    });\n  });\n\ntest('using promises', incomingMiddlewareErrorMacro,\n  (bot, update) => {\n    return new Promise((resolve, reject) => {\n      process.nextTick(() => {\n        try {\n          update.blop();\n        } catch (err) {\n          reject(err);\n        }\n      });\n    });\n  });\n\ntest('using async function', incomingMiddlewareErrorMacro,\n  async (bot, update) => {\n    // just a function that returns a promise\n    const somePromise = () => new Promise((resolve) => {\n      process.nextTick(() => {\n        resolve();\n      });\n    });\n\n    await somePromise();\n    update.blop();\n  });\n\ntest('Error is emitted if error is thrown by user and does not inherit from Error', (t) => {\n  t.plan(1);\n\n  return new Promise((resolve) => {\n    const botmaster = t.context.botmaster;\n\n    botmaster.use({\n      controller: async () => {\n        const err = 'not expected';\n        throw err;\n      },\n      type: 'incoming',\n    });\n\n    botmaster.use({\n      type: 'incoming',\n      controller: () => {\n        t.fail('this middleware should not get hit');\n        resolve();\n      },\n    });\n\n    botmaster.on('error', (bot, err) => {\n      t.is(err,\n        'not expected',\n        'Error message did not match');\n      resolve();\n    });\n\n    request(t.context.baseRequestOptions);\n  });\n});\n\ntest('Error is emitted if error is thrown by user and is falsy', (t) => {\n  t.plan(1);\n\n  return new Promise((resolve) => {\n    const botmaster = t.context.botmaster;\n\n    botmaster.use({\n      controller: () => Promise.reject(),\n      type: 'incoming',\n    });\n\n    botmaster.use({\n      type: 'incoming',\n      controller: () => {\n        t.fail('this middleware should not get hit');\n        resolve();\n      },\n    });\n\n    botmaster.on('error', (bot, err) => {\n      t.is(err,\n        'empty error object',\n        'Error message did not match');\n      resolve();\n    });\n\n    request(t.context.baseRequestOptions);\n  });\n});\n\ntest('Emits error if next is used within returned promise', (t) => {\n  t.plan(1);\n\n  return new Promise((resolve) => {\n    const botmaster = t.context.botmaster;\n\n    botmaster.use({\n      type: 'incoming',\n      controller: async (bot, update, next) => {\n        next('skip');\n      },\n    });\n\n    botmaster.use({\n      type: 'incoming',\n      controller: () => {\n        t.fail('this middleware should not get hit');\n        resolve();\n      },\n    });\n\n    botmaster.on('error', (bot, err) => {\n      t.is(err.message,\n        '\"next can\\'t be called if middleware returns a promise/is an async ' +\n        'function\". This is most probably on your end.',\n        'Error message did not match');\n      resolve();\n    });\n\n    request(t.context.baseRequestOptions);\n  });\n});\n\ntest('sets up the incoming middleware function specified if good params' +\n' passed. Does not call any outgoing middleware when going through', (t) => {\n  t.plan(1);\n\n  return new Promise((resolve) => {\n    const botmaster = t.context.botmaster;\n\n    botmaster.use({\n      type: 'incoming',\n      controller: async (bot, update) => {\n        update.message.text = 'Hello World!';\n      },\n    });\n\n    botmaster.use({\n      type: 'outgoing',\n      controller: () => {\n        t.fail('outgoing middleware should not be called');\n      },\n    });\n\n    botmaster.use({\n      type: 'incoming',\n      controller: (bot, update) => {\n        t.is(update.message.text, 'Hello World!', 'update object did not match');\n        resolve();\n      },\n    });\n\n    request(t.context.baseRequestOptions);\n  });\n});\n\n\ntest('sets up the incoming middleware and calls them using __emitUpdate', (t) => {\n  t.plan(1);\n\n  return new Promise((resolve) => {\n    t.context.botmaster.use({\n      type: 'incoming',\n      controller: (bot, update, next) => {\n        update.text = 'Hello World!';\n        next();\n      },\n    });\n\n    t.context.botmaster.use({\n      type: 'incoming',\n      controller: (bot, update) => {\n        t.is(update.text, 'Hello World!', 'update object did not match');\n        resolve();\n      },\n    });\n\n    t.context.bot.__emitUpdate({});\n  });\n});\n\n\ntest('sets up the incoming middleware in order of declaration', (t) => {\n  t.plan(1);\n\n  return new Promise((resolve) => {\n    t.context.botmaster.use({\n      type: 'incoming',\n      controller: (bot, update, next) => {\n        update.text = 'Hello ';\n        next();\n      },\n    });\n\n    t.context.botmaster.use({\n      type: 'incoming',\n      controller: (bot, update) => {\n        update.text += 'World!';\n        return Promise.resolve();\n      },\n    });\n\n    t.context.botmaster.use({\n      type: 'incoming',\n      controller: async (bot, update) => {\n        update.text += ' And others';\n      },\n    });\n\n    t.context.botmaster.use({\n      type: 'incoming',\n      controller: (bot, update) => {\n        t.is(update.text, 'Hello World! And others', 'update object did not match');\n        resolve();\n      },\n    });\n\n    t.context.bot.__emitUpdate({});\n  });\n});\n\nconst incomingMiddlewareChainBreakerMacro = (t, controller) => {\n  t.plan(1);\n\n  return new Promise(async (resolve) => {\n    t.context.botmaster.use({\n      type: 'incoming',\n      controller,\n    });\n\n    t.context.botmaster.use({\n      type: 'incoming',\n      controller: () => {\n        t.fail('this middleware should not get hit');\n        resolve();\n      },\n    });\n\n    const val = await t.context.bot.__emitUpdate({});\n    if (val) {\n      t.is(val, 'cancelled');\n    } else {\n      t.pass();\n    }\n    resolve();\n  });\n};\n\nincomingMiddlewareChainBreakerMacro.title = customTitlePart =>\n  `using middleware chain breakers in incoming middleware works as expected ${customTitlePart}`;\n\ntest('using next skip', incomingMiddlewareChainBreakerMacro, (bot, update, next) => {\n  next('skip');\n});\n\ntest('using promise skip', incomingMiddlewareChainBreakerMacro,\n  () => Promise.resolve('skip'));\n\ntest('using async skip', incomingMiddlewareChainBreakerMacro,\n  async () => 'skip');\n\ntest('using next cancel', incomingMiddlewareChainBreakerMacro, (bot, update, next) => {\n  next('cancel');\n});\n\ntest('using promise cancel', incomingMiddlewareChainBreakerMacro,\n  () => Promise.resolve('cancel'));\n\ntest('using async cancel', incomingMiddlewareChainBreakerMacro,\n  async () => 'cancel');\n\n\ntest('echo, read and delivery are not included by default', (t) => {\n  t.plan(3);\n\n  return new Promise((resolve) => {\n    t.context.botmaster.use({\n      type: 'incoming',\n      controller: () => {\n        t.fail('this middleware should never get hit in this test');\n        resolve();\n      },\n    });\n\n    let hitMiddlewareCount = 0;\n    const resolveWhenNeeded = () => {\n      hitMiddlewareCount += 1;\n      if (hitMiddlewareCount === 3) {\n        resolve();\n      }\n    };\n\n    t.context.botmaster.use({\n      type: 'incoming',\n      includeEcho: true,\n      controller: (bot, update, next) => {\n        t.truthy(update.message.is_echo, 'message is not an echo');\n        resolveWhenNeeded();\n        next();\n      },\n    });\n\n    t.context.botmaster.use({\n      type: 'incoming',\n      includeDelivery: true,\n      controller: (bot, update) => {\n        t.truthy(update.delivery, 'message is not a delivery confirmation');\n        resolveWhenNeeded();\n        return Promise.resolve();\n      },\n    });\n\n    t.context.botmaster.use({\n      type: 'incoming',\n      includeRead: true,\n      controller: async (bot, update) => {\n        t.truthy(update.read, 'message is not a read confirmation');\n        resolveWhenNeeded();\n      },\n    });\n\n    t.context.bot.__emitUpdate(incomingUpdateFixtures.echoUpdate());\n    t.context.bot.__emitUpdate(incomingUpdateFixtures.messageReadUpdate());\n    t.context.bot.__emitUpdate(incomingUpdateFixtures.messageDeliveredUpdate());\n  });\n});\n\nconst outgoingMiddlewareErrorMacro = (t, controller) => {\n  t.plan(1);\n\n  return new Promise((resolve) => {\n    const botmaster = t.context.botmaster;\n\n    botmaster.use({\n      controller,\n      type: 'outgoing',\n    });\n\n    botmaster.use({\n      type: 'outgoing',\n      controller: () => {\n        t.fail('this middleware should not get hit');\n        resolve();\n      },\n    });\n\n    botmaster.bots[0].sendMessage({})\n    .catch((err) => {\n      t.is(err.message,\n          'message.blop is not a function',\n          'Error message did not match');\n      resolve();\n    });\n  });\n};\n\noutgoingMiddlewareErrorMacro.title = customTitlePart =>\n  `Errors in outgoing middleware are thrown correctly ${customTitlePart}`;\n\ntest('in synchronous middleware', outgoingMiddlewareErrorMacro,\n  (bot, update, message) => {\n    message.blop();\n  });\n\ntest('using next', outgoingMiddlewareErrorMacro,\n  (bot, update, message, next) => {\n    process.nextTick(() => {\n      try {\n        message.blop();\n      } catch (err) {\n        next(err);\n      }\n    });\n  });\n\ntest('using promises', outgoingMiddlewareErrorMacro,\n  (bot, update, message) => {\n    return new Promise((resolve, reject) => {\n      process.nextTick(() => {\n        try {\n          message.blop();\n        } catch (err) {\n          reject(err);\n        }\n      });\n    });\n  });\n\ntest('using async function', outgoingMiddlewareErrorMacro,\n  async (bot, update, message) => {\n    // just a function that returns a promise\n    const somePromise = () => new Promise((resolve) => {\n      process.nextTick(() => {\n        resolve();\n      });\n    });\n\n    await somePromise();\n    message.blop();\n  });\n\ntest('sets up the outgoing middleware in order of declaration. ' +\n  'Then calls them when prompted without calling incoming middleware', (t) => {\n  t.plan(1);\n\n  return new Promise((resolve) => {\n    const botmaster = t.context.botmaster;\n\n    botmaster.use({\n      type: 'incoming',\n      controller: (bot, update, next) => {\n        t.fail('Called incoming middleware, although should not');\n        next();\n      },\n    });\n\n    botmaster.use({\n      type: 'outgoing',\n      controller: async (bot, update, message) => {\n        message.removeText();\n      },\n    });\n\n    botmaster.use({\n      type: 'outgoing',\n      controller: (bot, update, message, next) => {\n        message.addText('Goodbye Worlds!');\n        next();\n      },\n    });\n\n    botmaster.bots[0].sendMessage(outgoingMessageFixtures.textMessage())\n    .then((body) => {\n      t.is(body.sentOutgoingMessage.message.text, 'Goodbye Worlds!', 'sent message did not match');\n      resolve();\n    })\n    .catch((err) => {\n      t.fail(err.message);\n      resolve();\n    });\n  });\n});\n\nconst skipOutgoingMiddlewareMacro = (t, controller) => {\n  t.plan(2);\n\n  return new Promise(async (resolve) => {\n    t.context.botmaster.use({\n      type: 'outgoing',\n      controller: (bot, update, message, next) => {\n        t.pass();\n        return controller(bot, update, message, next);\n      },\n    });\n\n    t.context.botmaster.use({\n      type: 'outgoing',\n      controller: () => {\n        t.fail('this middleware should not get hit');\n        resolve();\n      },\n    });\n\n    const body = await t.context.bot.sendMessage(\n      outgoingMessageFixtures.textMessage());\n    t.deepEqual(body.sentRawMessage, outgoingMessageFixtures.textMessage());\n    resolve();\n  });\n};\n\nskipOutgoingMiddlewareMacro.title = customTitlePart =>\n  `using middleware skip in outgoing middleware works as expected ${customTitlePart}`;\n\ntest('using next skip', skipOutgoingMiddlewareMacro, (bot, update, message, next) => {\n  next('skip');\n});\n\ntest('using promise skip', skipOutgoingMiddlewareMacro,\n  () => Promise.resolve('skip'));\n\ntest('using async skip', skipOutgoingMiddlewareMacro,\n  async () => 'skip');\n\n\nconst cancelOutgoingMiddlewareMacro = async (t, controller) => {\n  t.plan(2);\n\n  return new Promise(async (resolve) => {\n    t.context.botmaster.use({\n      type: 'outgoing',\n      controller: (bot, update, message, next) => {\n        t.pass();\n        return controller(bot, update, message, next);\n      },\n    });\n\n    t.context.botmaster.use({\n      type: 'outgoing',\n      controller: () => {\n        t.fail('this middleware should not get hit');\n        resolve();\n      },\n    });\n\n    const body = await t.context.bot.sendMessage(\n      outgoingMessageFixtures.textMessage());\n    t.is(body, 'cancelled');\n    resolve();\n  });\n};\n\ncancelOutgoingMiddlewareMacro.title = customTitlePart =>\n  `using middleware cancel in outgoing middleware works as expected ${customTitlePart}`;\n\ntest('using next cancel', cancelOutgoingMiddlewareMacro, (bot, update, message, next) => {\n  next('cancel');\n});\n\ntest('using promise cancel', cancelOutgoingMiddlewareMacro,\n  () => Promise.resolve('cancel'));\n\ntest('using async cancel', cancelOutgoingMiddlewareMacro,\n  async () => 'cancel');\n\ntest('sets up the outgoing middleware which is ignored if specified so in sendOptions.', (t) => {\n  t.plan(2);\n\n  return new Promise(async (resolve) => {\n    t.context.botmaster.use({\n      type: 'outgoing',\n      controller: () => {\n        t.fail('this middleware should not get hit');\n        resolve();\n      },\n    });\n\n    const bot = t.context.bot;\n    try {\n      await bot.sendMessage(\n        outgoingMessageFixtures.textMessage(), { ignoreMiddleware: true });\n      await bot.reply(\n        incomingUpdateFixtures.textUpdate(), 'wadup?', { ignoreMiddleware: true });\n      await bot.sendAttachmentFromUrlTo(\n        'image', 'some_link', 'user_id', { ignoreMiddleware: true });\n      await bot.sendDefaultButtonMessageTo(\n        ['b1', 'b2'], undefined, 'user_id', { ignoreMiddleware: true });\n      await bot.sendIsTypingMessageTo(\n        'user_id', { ignoreMiddleware: true });\n      const bodies = await bot.sendTextCascadeTo(\n        ['message1', 'message2'], 'user_id', { ignoreMiddleware: true });\n\n      t.is(bodies[0].sentOutgoingMessage.message.text, 'message1',\n        'sentOutgoingMessage was not as expected');\n      t.is(bodies[1].sentOutgoingMessage.message.text, 'message2',\n        'sentOutgoingMessage was not as expected');\n\n      resolve();\n    } catch (err) {\n      t.fail(err.message);\n      resolve();\n    }\n  });\n});\n\ntest('sets up the outgoing middleware which is aware of update when manually set using __createBotPatchedWithUpdate', (t) => {\n  t.plan(2);\n\n  return new Promise(async (resolve) => {\n    const botmaster = t.context.botmaster;\n\n    const mockUpdate = { id: 1 };\n    botmaster.use({\n      type: 'outgoing',\n      controller: (bot, update, message, next) => {\n        t.is(update, mockUpdate, 'associated update is not the same');\n        t.deepEqual(assign({}, message), outgoingMessageFixtures.textMessage(),\n          'Message is not the same');\n        next();\n      },\n    });\n\n    const bot = botmaster.bots[0];\n    try {\n      // with a patchedBot\n      const patchedBot = bot.__createBotPatchedWithUpdate(mockUpdate);\n      await patchedBot.sendMessage(outgoingMessageFixtures.textMessage());\n\n      botmaster.server.close(resolve);\n    } catch (err) {\n      t.fail(err.message);\n      botmaster.server.close(resolve);\n    }\n  });\n});\n\ntest('sets up the outgoing middleware which is aware of update when sending message from incoming middleware', (t) => {\n  t.plan(3);\n\n  return new Promise((resolve) => {\n    const botmaster = t.context.botmaster;\n\n    botmaster.use({\n      type: 'incoming',\n      controller: async (bot, update) => {\n        const body = await bot.reply(update, 'Hello World!');\n        t.is(body.sentOutgoingMessage.message.text, 'Hello World!');\n        resolve();\n      },\n    });\n\n    botmaster.use({\n      type: 'outgoing',\n      controller: (bot, update, message, next) => {\n        t.deepEqual(assign({}, update), incomingUpdateFixtures.textUpdate(), 'associated update is not the same');\n        t.deepEqual(assign({}, message), outgoingMessageFixtures.textMessage(), 'Message is not the same');\n        next();\n      },\n    });\n\n    const bot = botmaster.bots[0];\n    bot.__emitUpdate(incomingUpdateFixtures.textUpdate());\n  });\n});\n\ntest('sets up the outgoing middleware which is aware of update on the second pass when sending a message in outgoing middleware', (t) => {\n  t.plan(8);\n\n  return new Promise((resolve) => {\n    const botmaster = t.context.botmaster;\n\n    const receivedUpdate = incomingUpdateFixtures.textUpdate();\n\n    let pass = 1;\n    botmaster.use({\n      type: 'outgoing',\n      controller: async (bot, update, message) => {\n        if (pass === 1) {\n          t.is(message.message.text, 'Hello World!', 'message text is not as expected on first pass');\n          t.is(update.newProp, 1, 'newProp is not the expected value on first pass');\n          t.is(update, receivedUpdate, 'Reference to update is not the same');\n          update.newProp = 2;\n          pass += 1;\n\n          const body = await bot.reply(update, 'Goodbye World!');\n          t.is(body.sentRawMessage.message.text, 'Goodbye World!');\n        } else if (pass === 2) {\n          t.is(message.message.text, 'Goodbye World!', 'message text is not as expected on second pass');\n          t.is(update.newProp, 2, 'newProp is not the expected value on second pass');\n          t.is(update, receivedUpdate, 'Reference to update is not the same');\n        }\n      },\n    });\n\n    botmaster.use({\n      type: 'incoming',\n      controller: async (bot, update) => {\n        update.newProp = 1;\n        const body = await bot.reply(update, 'Hello World!');\n        t.is(body.sentRawMessage.message.text, 'Hello World!');\n        resolve();\n      },\n    });\n\n    t.context.bot.__emitUpdate(receivedUpdate);\n  });\n});\n"
  },
  {
    "path": "tests/middleware/use_wrapped.js",
    "content": "import test from 'ava';\n\nimport Botmaster from '../../lib';\n\ntest.beforeEach((t) => {\n  return new Promise((resolve) => {\n    t.context.botmaster = new Botmaster();\n    t.context.botmaster.on('listening', resolve);\n  });\n});\n\ntest.afterEach((t) => {\n  return new Promise((resolve) => {\n    t.context.botmaster.server.close(resolve);\n  });\n});\n\nconst errorThrowingMacro = (t, params) => {\n  t.plan(1);\n\n  const botmaster = t.context.botmaster;\n  try {\n    botmaster.useWrapped(params.incomingMiddleware, params.outgoingMiddleware);\n  } catch (err) {\n    t.is(err.message,\n      params.errorMessage,\n      'Error message is not the same as expected');\n  }\n};\n\nerrorThrowingMacro.title = customTitlePart =>\n  `throws an error if ${customTitlePart}`;\n\ntest('called with no params', errorThrowingMacro, {\n  errorMessage: 'useWrapped should be called with both an' +\n                ' incoming and an outgoing middleware',\n});\n\ntest('called with no outgoing middleware', errorThrowingMacro, {\n  incomingMiddleware: {\n    type: 'incoming',\n    controller: __ => __,\n  },\n  errorMessage: 'useWrapped should be called with both an' +\n                ' incoming and an outgoing middleware',\n});\n\ntest('called with no incoming middleware', errorThrowingMacro, {\n  outgoingMiddleware: {\n    type: 'outgoing',\n    controller: __ => __,\n  },\n  errorMessage: 'useWrapped should be called with both an' +\n                ' incoming and an outgoing middleware',\n});\n\ntest('called with two incoming middlewares', errorThrowingMacro, {\n  incomingMiddleware: {\n    type: 'outgoing',\n    controller: __ => __,\n  },\n  outgoingMiddleware: {\n    type: 'outgoing',\n    controller: __ => __,\n  },\n  errorMessage: 'first argument of \"useWrapped\" should be an' +\n                ' incoming middleware',\n});\n\ntest('called with two incoming middlewares', errorThrowingMacro, {\n  incomingMiddleware: {\n    type: 'incoming',\n    controller: __ => __,\n  },\n  outgoingMiddleware: {\n    type: 'incoming',\n    controller: __ => __,\n  },\n  errorMessage: 'second argument of \"useWrapped\" should be an' +\n                ' outgoing middleware',\n});\n\ntest('middleware gets added where expected', (t) => {\n  t.plan(4);\n\n  const botmaster = t.context.botmaster;\n\n  const useIncomingController = __ => __;\n  botmaster.use({\n    type: 'incoming',\n    controller: useIncomingController,\n  });\n\n  const useOutgoingController = __ => __;\n  botmaster.use({\n    type: 'outgoing',\n    controller: useOutgoingController,\n  });\n\n  const useWrappedIncomingController = __ => __;\n  const useWrappedOutgoingController = __ => __;\n\n  botmaster.useWrapped({\n    type: 'incoming',\n    controller: useWrappedIncomingController,\n  }, {\n    type: 'outgoing',\n    controller: useWrappedOutgoingController,\n  });\n\n  t.is(botmaster.middleware.incomingMiddlewareStack.length, 2);\n  t.is(botmaster.middleware.outgoingMiddlewareStack.length, 2);\n  t.is(botmaster.middleware.incomingMiddlewareStack[0].controller,\n    useWrappedIncomingController);\n  t.is(botmaster.middleware.outgoingMiddlewareStack[1].controller,\n    useWrappedOutgoingController);\n});\n"
  },
  {
    "path": "tests/outgoing_message.js",
    "content": "import test from 'ava';\nimport { outgoingMessageFixtures, attachmentFixtures } from 'botmaster-test-fixtures';\nimport { assign } from 'lodash';\nimport MockBot from './_mock_bot';\n\nimport OutgoingMessage from '../lib/outgoing_message';\n\nconst createBaseOutgoingMessage = () => {\n  const outgoingMessage = {\n    recipient: {\n      id: 'user_id',\n    },\n  };\n\n  return new OutgoingMessage(outgoingMessage);\n};\n\ntest('Instantiating an OutgoingMessage object via a bot\\'s createOutgoingMessage works', (t) => {\n  t.plan(1);\n\n  const bot = new MockBot();\n  const botOutgoingMessage = bot.createOutgoingMessage({});\n\n  t.deepEqual(botOutgoingMessage, new OutgoingMessage());\n});\n\ntest('Instantiating an OutgoingMessage object via a bot class\\'s createOutgoingMessage works', (t) => {\n  t.plan(1);\n\n  const botOutgoingMessage = MockBot.createOutgoingMessage({});\n\n  t.deepEqual(botOutgoingMessage, new OutgoingMessage());\n});\n\ntest('Instantiating an OutgoingMessage starter object via a bot\\'s createOutgoingMessageFor works', (t) => {\n  t.plan(1);\n\n  const bot = new MockBot();\n  const botOutgoingMessage = bot.createOutgoingMessageFor('user_id');\n\n  t.deepEqual(botOutgoingMessage, createBaseOutgoingMessage());\n});\n\ntest('Instantiating an OutgoingMessage starter object via a bot class\\'s createOutgoingMessageFor works', (t) => {\n  t.plan(1);\n\n  const botOutgoingMessage = MockBot.createOutgoingMessageFor('user_id');\n\n  t.deepEqual(botOutgoingMessage, createBaseOutgoingMessage());\n});\n\ntest('#constructor does not throw an error when initialized without argument', (t) => {\n  t.plan(1);\n\n  const m = new OutgoingMessage();\n  t.pass();\n});\n\ntest('throws an error when argument passed is not an object', (t) => {\n  t.plan(1);\n\n  try {\n    const m = new OutgoingMessage('not an object');\n  } catch (err) {\n    t.is(err.message,\n      'OutgoingMessage constructor takes in an object as param');\n  }\n});\n\ntest('#constructor properly assigns passed in object', (t) => {\n  t.plan(1);\n\n  const message = outgoingMessageFixtures.textMessage();\n  const outgoingMessage = new OutgoingMessage(message);\n\n  // assign is used here and in all the subsequent tests, in order\n  // to make sure that the deepEqual passes. Otherwise, it is comparing an\n  // instance of OutgoingMessage with Object, which won't work!\n  t.deepEqual(assign({}, outgoingMessage), message);\n});\n\ntest('#__addPropery throws error when trying to add property with falsy value', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n\n  try {\n    outgoingMessage.__addProperty('arbitrary', 'arbitrary', undefined);\n  } catch (err) {\n    t.is(err.message,\n      'arbitrary must have a value. Can\\'t be undefined');\n  }\n});\n\ntest('#__addPropery throws error when trying to add property that already has a value', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.arbitrary = 'some value';\n\n  try {\n    outgoingMessage.__addProperty('arbitrary', 'arbitrary', 'some other value');\n  } catch (err) {\n    t.is(err.message,\n      'Can\\'t add arbitrary to outgoingMessage that already has arbitrary');\n  }\n});\n\ntest('#__removePropery throws error when trying to remove property that doesn\\'t have a value', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n\n  try {\n    outgoingMessage.__removeProperty('arbitrary.arb', 'arbitrary');\n  } catch (err) {\n    t.is(err.message,\n      'Can\\'t remove arbitrary from outgoingMessage that doesn\\'t have any arbitrary');\n  }\n});\n\ntest('#addRecipientId properly works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = new OutgoingMessage().addRecipientById('user_id');\n\n  t.deepEqual(outgoingMessage, createBaseOutgoingMessage());\n});\n\ntest('#addRecipientPhone properly works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = new OutgoingMessage().addRecipientByPhoneNumber('phoneNumber');\n\n  const expectedMessage = {\n    recipient: {\n      phone_number: 'phoneNumber',\n    },\n  };\n\n  t.deepEqual(assign({}, outgoingMessage), expectedMessage);\n});\n\ntest('#removeRecipient properly works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.removeRecipient();\n\n  t.deepEqual(outgoingMessage, new OutgoingMessage());\n});\n\ntest('#addText properly works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.addText('Hello World!');\n\n  t.deepEqual(assign({}, outgoingMessage), outgoingMessageFixtures.textMessage());\n});\n\ntest('#removeText properly works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.addText('Hello World!').removeText();\n\n  const expectedOutgoingMessage = createBaseOutgoingMessage();\n  expectedOutgoingMessage.message = {};\n\n  t.deepEqual(outgoingMessage, expectedOutgoingMessage);\n});\n\ntest('#addAttachment works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.addAttachment(attachmentFixtures.audioAttachment());\n\n  t.deepEqual(assign({}, outgoingMessage), outgoingMessageFixtures.audioMessage());\n});\n\ntest('#addAttachmentFromUrl throws error if not passed in strings', (t) => {\n  t.plan(2);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  try {\n    outgoingMessage.addAttachmentFromUrl({ type: 'audio' }, 'SOME_AUDIO_URL');\n  } catch (err) {\n    t.is(err.message,\n      'addAttachmentFromUrl must be called with \"type\" and \"url\" arguments of type string');\n  }\n\n  try {\n    outgoingMessage.addAttachmentFromUrl('audio', { url: 'SOME_AUDIO_URL' });\n  } catch (err) {\n    t.is(err.message,\n      'addAttachmentFromUrl must be called with \"type\" and \"url\" arguments of type string');\n  }\n});\n\ntest('#addAttachmentFromUrl throws error if not passed in both type and url', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  try {\n    outgoingMessage.addAttachmentFromUrl('audio');\n  } catch (err) {\n    t.is(err.message,\n      'addAttachmentFromUrl must be called with truthy \"type\" and \"url\" arguments');\n  }\n});\n\ntest('#addAttachmentFromUrl works when using the right arguments', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.addAttachmentFromUrl('audio', 'SOME_AUDIO_URL');\n\n  t.deepEqual(assign({}, outgoingMessage), outgoingMessageFixtures.audioMessage());\n});\n\ntest('#removeAttachment works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage\n    .addAttachment(attachmentFixtures.audioAttachment())\n    .removeAttachment();\n\n  const expectedOutgoingMessage = createBaseOutgoingMessage();\n  expectedOutgoingMessage.message = {};\n\n  t.deepEqual(outgoingMessage, expectedOutgoingMessage);\n});\n\ntest('#addQuickReplies works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  const quickReplies = outgoingMessageFixtures.textOnlyQuickReplyMessage().message.quick_replies;\n  outgoingMessage.addQuickReplies(quickReplies);\n  outgoingMessage.addText('Please select one of:');\n\n  t.deepEqual(assign({}, outgoingMessage), outgoingMessageFixtures.textOnlyQuickReplyMessage());\n});\n\ntest('#addPayloadLessQuickReplies throws error if not passed in array of strings', (t) => {\n  t.plan(2);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  try {\n    outgoingMessage.addPayloadLessQuickReplies('not an array');\n  } catch (err) {\n    t.is(err.message,\n      'addPayloadLessQuickReplies needs to be passed in an array of strings as first argument');\n  }\n\n  try {\n    outgoingMessage.addPayloadLessQuickReplies(['not an array of strings', {}]);\n  } catch (err) {\n    t.is(err.message,\n      'addPayloadLessQuickReplies needs to be passed in an array of strings as first argument');\n  }\n});\n\ntest('#addPayloadLessQuickReplies properly works when passed array of strings', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.addPayloadLessQuickReplies(['B1', 'B2']);\n  outgoingMessage.addText('Please select one of:');\n\n  const quickReplies = [\n    {\n      content_type: 'text',\n      title: 'B1',\n      payload: 'B1',\n    },\n    {\n      content_type: 'text',\n      title: 'B2',\n      payload: 'B2',\n    },\n  ];\n\n  const expectedOutgoingMessage = outgoingMessageFixtures.textOnlyQuickReplyMessage();\n  expectedOutgoingMessage.message.quick_replies = quickReplies;\n\n  t.deepEqual(assign({}, outgoingMessage), expectedOutgoingMessage);\n});\n\ntest('#addLocationQuickReply properly works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.addLocationQuickReply();\n  outgoingMessage.addText('Please share your location:');\n\n  t.deepEqual(assign({}, outgoingMessage), outgoingMessageFixtures.locationQuickReplyMessage());\n});\n\ntest('#removeQuickReplies works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage\n    .addLocationQuickReply()\n    .removeQuickReplies();\n\n  const expectedOutgoingMessage = createBaseOutgoingMessage();\n  expectedOutgoingMessage.message = {};\n\n  t.deepEqual(outgoingMessage, expectedOutgoingMessage);\n});\n\ntest('#addSenderAction properly works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.addSenderAction('typing_on');\n\n  t.deepEqual(assign({}, outgoingMessage), outgoingMessageFixtures.typingOnMessage());\n});\n\ntest('#removeSenderAction properly works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.addSenderAction('some_action').removeSenderAction();\n\n  const expectedOutgoingMessage = createBaseOutgoingMessage();\n\n  t.deepEqual(outgoingMessage, expectedOutgoingMessage);\n});\n\ntest('#addTypingOnSenderAction properly works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.addTypingOnSenderAction();\n\n  t.deepEqual(assign({}, outgoingMessage), outgoingMessageFixtures.typingOnMessage());\n});\n\ntest('#addTypingOffSenderAction properly works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.addTypingOffSenderAction();\n\n  t.deepEqual(assign({}, outgoingMessage), outgoingMessageFixtures.typingOffMessage());\n});\n\ntest('#addMarkSeenSenderAction properly works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage();\n  outgoingMessage.addMarkSeenSenderAction();\n\n  t.deepEqual(assign({}, outgoingMessage), outgoingMessageFixtures.markSeenMessage());\n});\n\ntest('chaining of all methods works', (t) => {\n  t.plan(1);\n\n  const outgoingMessage = createBaseOutgoingMessage()\n    .removeRecipient()\n    .addRecipientByPhoneNumber('phoneNumber')\n    .removeRecipient()\n    .addRecipientById('user_id')\n    .addText('Hello')\n    .removeText()\n    .addAttachment({})\n    .removeAttachment()\n    .addAttachmentFromUrl('image', 'someUrl')\n    .removeAttachment()\n    .addQuickReplies([])\n    .removeQuickReplies()\n    .addPayloadLessQuickReplies(['B1', 'B2'], 'select one of')\n    .removeQuickReplies()\n    .addLocationQuickReply()\n    .removeQuickReplies()\n    .addSenderAction('some_abstract_action')\n    .removeSenderAction()\n    .addTypingOnSenderAction()\n    .removeSenderAction()\n    .addTypingOffSenderAction()\n    .removeSenderAction()\n    .addMarkSeenSenderAction()\n    .removeSenderAction();\n\n  const expectedOutgoingMessage = createBaseOutgoingMessage();\n  expectedOutgoingMessage.message = {};\n\n  t.deepEqual(outgoingMessage, expectedOutgoingMessage);\n});\n"
  }
]