Repository: Yoctol/bottender
Pull request #627 (2020-01-14 06:40:31)

update docs: advanced-guides-nlu
update NLU guides due to the latest bottender NLU related oackages

codecov-io: # [Codecov](https://codecov.io/gh/Yoctol/bottender/pull/627?src=pr&el=h1) Report
> Merging [#627](https://codecov.io/gh/Yoctol/bottender/pull/627?src=pr&el=desc) into [master](https://codecov.io/gh/Yoctol/bottender/commit/c71ff60ddaa288b75b376692d8cf7166208e617e?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/Yoctol/bottender/pull/627/graphs/tree.svg?width=650&token=JX3EGtxYH3&height=150&src=pr)](https://codecov.io/gh/Yoctol/bottender/pull/627?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master     #627   +/-   ##
=======================================
  Coverage   80.81%   80.81%           
=======================================
  Files          95       95           
  Lines        4284     4284           
  Branches     1041     1041           
=======================================
  Hits         3462     3462           
  Misses        783      783           
  Partials       39       39
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/Yoctol/bottender/pull/627?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Yoctol/bottender/pull/627?src=pr&el=footer). Last update [c71ff60...952cd29](https://codecov.io/gh/Yoctol/bottender/pull/627?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/docs/advanced-guides-deployment.md b/docs/advanced-guides-deployment.md
new file mode 100644
index 00000000..d4dc703a
--- /dev/null
+++ b/docs/advanced-guides-deployment.md
@@ -0,0 +1,344 @@
+---
+id: advanced-guides-deployment
+title: Deployment
+---
+
+ <p><img width="500" src="https://user-images.githubusercontent.com/662387/72043275-b7c3fe80-32eb-11ea-9a49-f2d5c073f397.jpg"></p>
+
+Once you finished your bot in development, the next step is to deploy your bot to a hosting service!
+
+> **Note:** We aim to offer deployment guides for every popular modern hosting services. [Tweet us](https://twitter.com/bottenderjs) if you haven't seen your favorite one.
+
+### Before Going Further
+
+We assumed that you already built at least one basic Bottender app in development. If you haven't, you may check [Getting Started](https://bottender.js.org/docs/getting-started) to create your first Bottender app in a few minutes, then jump to the setup doc of your favorite chat channel:
+
+- [Setup Messenger](https://bottender.js.org/docs/channel-messenger-setup)
+- [Setup LINE](https://bottender.js.org/docs/channel-line-setup)
+- [Setup Slack](https://bottender.js.org/docs/channel-slack-setup)
+- [Setup Telegram](https://bottender.js.org/docs/channel-telegram-setup)
+- [Setup Viber](https://bottender.js.org/docs/channel-viber-setup)
+
+## Heroku
+
+<p><img width="300" src="https://user-images.githubusercontent.com/662387/72130857-a8a98300-33b5-11ea-9ec6-10c8aac37230.jpg"></p>
+
+Heroku is one of the most popular hosting services. Not only the clear document, ease of scalability, using Git for deployment, but also the friendly [free pricing plan](https://www.heroku.com/pricing) for experiment purpose.
+
+In the following, you can see the necessary steps of Heroku Deployment:
+
+### Step 1: Create a Heroku Account & Download Heroku CLI
+
+First, [sign up](https://www.heroku.com/) a Heroku account if you haven't, then download and install [Heroku CLI](https://devcenter.heroku.com/articles/getting-started-with-nodejs#set-up).
+
+> **Note:** For the full command list, please refer to Heroku's doc, [Heroku CLI Commands](https://devcenter.heroku.com/articles/heroku-cli).
+
+### Step 2: Heroku Login & Create a Heroku App
+
+Before going further, make sure you have login your Heroku account by:
+
+```sh
+heroku login
+```
+
+Then, you can create a Heroku app by the below command.
+
+```sh
+heroku create <your-heroku-app-name>
+```
+
+> **Note:** You may see some app name regulation if you don't meet it. For example: `Name must start with a letter, end with a letter or digit and can only contain lowercase letters, digits, and dashes`
+
+Once you created your Heroku app successfully, you could see a deployment address for your app like `https://<your-heroku-app-name>.herokuapp.com/`. You can note it down for the coming webhook setting.
+
+### Step 3: Fill in Environment Variables to Heroku
+
+Config the environment variables of your Heroku app with the following commands: `heroku config:set -a <your-heroku-app-name> <ENV_KEY_01>=<ENV_VALUE_01>`.
+
+For chat channels require multiple environment variables, you may use commands like `heroku config:set -a <your-heroku-app-name> <ENV_KEY_01>=<ENV_VALUE_01> <ENV_KEY_02>=<ENV_VALUE_02>`.
+
+For example:
+
+```sh
+heroku config:set -a <your-heroku-app-name> MESSENGER_PAGE_ID=xxxxxx MESSENGER_ACCESS_TOKEN=xxxxxx MESSENGER_APP_ID=xxxxxx MESSENGER_APP_SECRET=xxxxxx MESSENGER_VERIFY_TOKEN=xxxxxx
+```
+
+### Step 4: Using Git in Your Bottender App
+
+Deployment of Heroku depends on Git. Make sure you have run `git init` and make the first commit in your Bottender app.
+
+For example:
+
+```sh
+git init
+git add .
+git commit -am "first commit"
+```
+
+### Step 5: Deploy Your Bot to Heroku and Set Up Webhook
+
+When you try to set up the webhook, some chat channels (e.g., Messenger) might ask for an immediate bot server verification. So, we recommend you to set up the webhook after your Bottender app server running.
+
+There are two basic types of webhook setup:
+
+1. Set up webhook by Developer Console UI, e.g., Messenger, LINE, Slack
+2. Set up webhook by CLI, e.g., Messenger, Telegram, Viber
+
+#### Step 5a: Set up Webhook by UI of Developer Console
+
+Use Heroku CLI by Git push to complete the deployment.
+
+```sh
+heroku git:remote -a <your-heroku-app-name>
+git push heroku master
+```
+
+Then fill in your webhook URL on the developer console of the chat channel.
+
+> **Note:** If you are not familiar with webhook setup, you may refer to Bottender docs, [Setup Messenger](https://bottender.js.org/docs/channel-messenger-setup), [Setup LINE](https://bottender.js.org/docs/channel-line-setup), and [Setup Slack](https://bottender.js.org/docs/channel-slack-setup).
+
+> **Note:** If you haven't changed your webhook path in `bottender.config.js`, by default, your Messenger Bot webhook is `https://<your-heroku-app-name>.herokuapp.com/webhooks/messenger`; your LINE Bot webhook is `https://<your-heroku-app-name>.herokuapp.com/webhooks/line`, and so on.
+
+#### Step 5b: Set up Webhook by CLI
+
+You can benefit from the `Procfile` feature of Heroku, which specifies the commands executed by the app on startup. We are going to use two process types of `Procfile`:
+
+- `web` process type: tell Heroku to run your bot server for every `dyno`
+- `release` process type: set up webhook before a new release is deployed to production
+
+> **Note:** For more info about `Procfile`, you may refer to Heroku's guide, [The Procfile](https://devcenter.heroku.com/articles/procfile).
+
+Using a Messenger Bot as an example, the `Procfile` looks like the below with default webhook path settings:
+
+```js
+// Procfile
+
+web: npm start
+release: echo "Y" | npx bottender messenger webhook set -w https://<your-heroku-app-name>.com/webhooks/messenger
+```
+
+> **Note:**
+>
+> - The `echo "Y"` aims to answer the first interactive CLI prompt
+> - If you haven't changed your webhook path in `bottender.config.js`, by default, your Messenger Bot webhook is `https://<your-heroku-app-name>.herokuapp.com/webhooks/messenger`; your LINE Bot webhook is `https://<your-heroku-app-name>.herokuapp.com/webhooks/line`, and so on.
+
+Finally, You can use Heroku CLI by Git push to complete the deployment and let Heroku runs the `Procfile` to help you finish the webhook setup.
+
+```sh
+heroku git:remote -a <your-heroku-app-name>
+git push heroku master
+```
+
+### Step 6: Completed!
+
+Congratulations! You have made your Bottender bot production-ready. Share your fantastic bot with your friends!
+
+> **Note:** If you would like to share your Bottender Bot with the community, please feel free to add your project to [Bottender Users](https://bottender.js.org/users).
+
+## ZEIT Now 2.0
+
+<p><img width="1000" src="https://user-images.githubusercontent.com/662387/72130872-acd5a080-33b5-11ea-8c6c-ae06dd250be6.png"></p>
+
+ZEIT Now 2.0 is a super developer-friendly hosting service. It is famous for its one-word magic command `now` in the deployment of static web hosting.
+
+However, it needs a bit configuration to deploy a Bottender app and make it works serverless. In the following sections, you can see the necessary steps of ZEIT Now 2.0 deployment:
+
+### Step 1: Create a ZEIT Account
+
+First, [create](https://ZEIT.co/signup) a ZEIT Account if you haven't.
+
+### Step 2: Install ZEIT Now CLI
+
+We love to deploy with CLI! Install ZEIT Now CLI with npm by:
+
+```sh
+npm install -g now
+```
+
+### Step 3: Create a `server.js`
+
+ZEIT Now 2.0 doesn't support `npm` scripts, so we need a `server.js` as the entry point.
+
+```js
+// server.js
+
+const bodyParser = require('body-parser');
+const express = require('express');
+const { bottender } = require('bottender');
+
+const app = bottender({
+  dev: process.env.NODE_ENV !== 'production',
+});
+
+const port = Number(process.env.PORT) || 5000;
+
+const handle = app.getRequestHandler();
+
+app.prepare().then(() => {
+  const server = express();
+
+  server.use(
+    bodyParser.json({
+      verify: (req, _, buf) => {
+        req.rawBody = buf.toString();
+      },
+    })
+  );
+
+  server.all('*', (req, res) => {
+    return handle(req, res);
+  });
+
+  server.listen(port, err => {
+    if (err) throw err;
+    console.log(`> Ready on http://localhost:${port}`);
+  });
+});
+```
+
+### Step 4: Update Your `now.json`
+
+In the following configuration, you will make a few settings:
+
+- Set ZEIT Now to version 2.0
+- Use `@now/node` to bundle `server.js`
+- Put `bottender.config.js` and `index.js` into `includeFiles`. ZEIT Now uses `ncc` and `webpack bundler` under the hood, so we need to tell them to put those two files into the bundle)
+- Route all (/.\*) to `server.js`
+- Add your chat channel specific environment variables from `.env` to env. The number of environment variables various from the chat channel you use.
+- (optional) If you are debugging your app, you may set `DEBUG` env to `bottender*,messaging-api*`
+
+Using a Messenger Bot as an example. your `now.json` is like:
+
+```js
+// now.json
+
+{
+  "version": 2,
+  "builds": [
+    {
+      "src": "server.js",
+      "use": "@now/node",
+      "config": {
+        "includeFiles": [
+          "bottender.config.js",
+          "index.js"
+        ],
+        "bundle": true
+      }
+    }
+  ],
+  "routes": [
+    {
+      "src": "/.*",
+      "dest": "/server.js"
+    }
+  ],
+  "env": {
+    "MESSENGER_PAGE_ID": "xxxxxx",
+    "MESSENGER_ACCESS_TOKEN": "xxxxxx",
+    "MESSENGER_APP_ID": "xxxxxx",
+    "MESSENGER_APP_SECRET": "xxxxxx",
+    "MESSENGER_VERIFY_TOKEN": "xxxxxx",
+    "DEBUG": "bottender*,messaging-api*"
+  }
+}
+```
+
+### Step 5: Update Your `bottender.config.js`
+
+ZEIT Now 2.0 is a serverless hosting service, and serverless functions terminated right after HTTP response. So you have to make Bottender work synchronously, i.e., respond to chat channel right after received an event.
+
+to make the Bottender app executes in the synchronous mode, you must configure chat channels with `sync: true` in `bottender.config.js`.
+
+```js
+// bottender.config.js
+
+module.exports = {
+  // ... skip
+
+  channels: {
+    messenger: {
+      enabled: true,
+      sync: true,
+      // ...skip
+    },
+    line: {
+      enabled: true,
+      sync: true,
+      // ...skip
+    },
+    telegram: {
+      enabled: false,
+      sync: true,
+      // ...skip
+    },
+    slack: {
+      enabled: false,
+      sync: true,
+      // ...skip
+    },
+    viber: {
+      enabled: false,
+      sync: true,
+      // ...skip
+    },
+  },
+};
+```
+
+### Step 6: Update Your `package.json`
+
+Set up Node.js version to `10.x` or `12.x` in the `engines` env to avoid warnings for deprecated node 8.
+
+```js
+// package.json
+{
+ // ...skip
+ "engines": { "node": "12.x" }
+}
+```
+
+### Step 7: Deploy with `now`
+
+After all the settings, you are ready to use the magic word, `now`, for ZEIT Now 2.0 deployment.
+
+```sh
+now
+```
+
+Then you will see something like the screenshot below.
+
+![](https://user-images.githubusercontent.com/662387/72136431-c382f400-33c3-11ea-9745-839c212c1b2e.png)
+
+> **Note:** Your deployed URL would be like `https://<your-app-name>.<your-user-name>.now.sh`
+
+### Step 8: Webhook Setup
+
+If you haven't changed your webhook path in `bottender.config.js`, by default, your Messenger Bot webhook is `https://<your-app-name>.<your-user-name>.now.sh/webhooks/messenger`; your LINE Bot webhook is `https://<your-app-name>.<your-user-name>.now.sh/webhooks/line`, and so on.
+
+There are two basic types of webhook setup:
+
+1. Set up webhook by Developer Console UI, e.g., Messenger, LINE, Slack
+2. Set up webhook by CLI, e.g., Messenger, Telegram, Viber
+
+#### Step 8a: Set up Webhook by Developer Console UI
+
+Fill in your webhook URL on the developer console of the chat channel.
+
+> **Note:** If you are not familiar with webhook setup, you may refer to Bottender docs, [Setup Messenger](https://bottender.js.org/docs/channel-messenger-setup), [Setup LINE](https://bottender.js.org/docs/channel-line-setup), [Setup Slack](https://bottender.js.org/docs/channel-slack-setup).
+
+#### Step 8b: Set up Webhook by CLI
+
+Make sure you have the same environment variable settings in your local `.env` file and `now.js`. Then you can use the following command to set up the webhook.
+
+Using a Messenger Bot as an example, your command is like:
+
+```sh
+npx bottender messenger webhook set -w https://<your-app-name>.<your-user-name>.now.sh/webhooks/messenger
+```
+
+### Step 9: Completed!
+
+Congratulations! You have made your Bottender bot production-ready. Share your fantastic bot with your friends!
+
+> **Note:** If you would like to share your Bottender Bot with the community, please feel free to add your project to [Bottender Users](https://bottender.js.org/users).
diff --git a/docs/advanced-guides-multi-channel.md b/docs/advanced-guides-multi-channel.md
index d4d9b86d..19911a2e 100644
--- a/docs/advanced-guides-multi-channel.md
+++ b/docs/advanced-guides-multi-channel.md
@@ -1,6 +1,6 @@
 ---
 id: advanced-guides-multi-channel
-title: Multi-Messaging Channel Support
+title: Multi-Channel Support
 ---
 
 Bottender intends to meet enterprise project needs. So, Bottender supports multiple chat channels in the very early stage. For example, you can put the environment variables of each chat channel in one config file; you can use [`Platform Specific Routes`](the-basics-routing#platform-specific-routes) to organize user events from various platforms.
diff --git a/docs/advanced-guides-nlu.md b/docs/advanced-guides-nlu.md
index 99e0fcca..858e7ab4 100644
--- a/docs/advanced-guides-nlu.md
+++ b/docs/advanced-guides-nlu.md
@@ -5,167 +5,235 @@ title: Natural Language Understanding
 
 ## Leverage NLU Technologies
 
-If you want to have more general intent recognition, you can leverage modern NLU (Natural Language Understanding) technologies. They can help you recognize the intent of user input sentences. There are several online services you can choose from, for example:
+When it comes to building a bot for open questions, e.g., FAQ of customer support, or an in-bot search engine, you may think of making a bot with the NLU engine to analyze the user's input. The primary mission of NLU is to analyze the user's intent and entities to respond with the right answer.
+
+A well-trained NLU engine usually has a 90%+ accurate rate in terms of telling the right intent or find the user input is beyond the current knowledge base. Sometimes, it takes time and a few iterations to find the best intent structure and train the NLU engine smart.
+
+In the following sections, you can see how to integrate Bottender with various modern NLU services:
 
-- [Dialogflow](advanced-guides-nlu.md#building-with-dialogflow)
 - [QnA Maker](advanced-guides-nlu.md#building-with-qna-maker)
+- [Dialogflow](advanced-guides-nlu.md#building-with-dialogflow)
 - [LUIS](advanced-guides-nlu.md#building-with-luis)
 - [Rasa](advanced-guides-nlu.md#building-with-rasa-nlu)
 
-## Building with Dialogflow
+## Building with QnA Maker
+
+The reason that we choose QnA Maker in the first place is because of the friendly building process. Unlike other NLU service requires a certain amount of time to build the intent and write the training phrases. In QnA Maker, you just need to copy & paster your FAQ, and the NLU engine is ready to use.
+
+> **Note:** [Tweet us](https://twitter.com/bottenderjs) if you would like to share your QnA Maker engine performance with us :)
 
-To build a bot integrated with [Dialogflow](https://dialogflow.com/), you have to setup Dialogflow following the [Official Setup Guide](https://cloud.google.com/dialogflow/docs/quick/setup) and fill the two values: `GOOGLE_APPLICATION_CREDENTIALS` (the file path of the JSON file that contains your service account key) and `GOOGLE_APPLICATION_PROJECT_ID` (the GCP project ID) into the `.env` file.
+### Step 1: QnA Maker Setup
+
+To build a bot integrated with [QnA Maker](https://www.qnamaker.ai/), you have to create the QnA Maker knowledge base and publish it following the [Official Guide](https://docs.microsoft.com/en-us/azure/cognitive-services/qnamaker/quickstarts/create-publish-knowledge-base).
+
+After you publish your knowledge base, you will get `RESOURCE_NAME`, `KNOWLEDGE_BASE_ID`, and `ENDPOINT_KEY` (See [Here](https://docs.microsoft.com/en-us/azure/cognitive-services/qnamaker/quickstarts/get-answer-from-knowledge-base-using-url-tool?pivots=url-test-tool-postman) for detailed guide). Make sure you copy them into the `.env` file.
 
 ```
-# .env
-GOOGLE_APPLICATION_CREDENTIALS=
-GOOGLE_APPLICATION_PROJECT_ID=
+// .env
+
+RESOURCE_NAME=
+KNOWLEDGE_BASE_ID=
+ENDPOINT_KEY=
+```
+
+### Step 2: Connect Bottender with QnA Maker by `bottender/qna-maker`
+
+To make the bot development enjoyable, we made a [`bottender/qna-maker`](https://github.com/Yoctol/bottender/tree/master/packages/bottender-qna-maker) package. You can install the package with `npm` or `yarn`.
+
+With `npm`:
+
+```sh
+npm install @bottender/qna-maker
 ```
 
-Next, you can build an agent following the [official document](https://cloud.google.com/dialogflow/docs/quick/build-agent). In this example, we build an agent and create a simple intent with display name `greeting`. You can set your own training phrases on the Dialogflow console for this intent.
+Or with `yarn`:
 
-After you finish the settings of the agent, you can then call Dialogflow's API to detect the intention of the message the bot receives. We use [Dialogflow Node SDK](https://github.com/googleapis/nodejs-dialogflow) to integrate our bot with Dialogflow.
+```sh
+yarn add @bottender/qna-maker
+```
+
+In the following sample code, you can see how elegant it is to integrate Bottender with QnA Maker. All you need to do is to fill in your environment variables, and score thread, then Bottender uses answers from QnA Maker as the response.
 
 ```js
-const dialogflow = require('dialogflow');
-
-const PROJECT_ID = process.env.GOOGLE_APPLICATION_PROJECT_ID;
-
-const sessionClient = new dialogflow.SessionsClient();
-
-module.exports = async function App(context) {
-  if (context.event.isText) {
-    const sessionPath = sessionClient.sessionPath(
-      PROJECT_ID,
-      context.session.id
-    );
-
-    const request = {
-      session: sessionPath,
-      queryInput: {
-        text: {
-          text: context.event.text,
-          languageCode: 'en',
-        },
-      },
-      queryParams: {
-        timeZone: '',
-      },
-    };
-
-    const responses = await sessionClient.detectIntent(request);
-    const { intent } = responses[0].queryResult;
-
-    if (intent.displayName === 'greeting') {
-      await context.sendText('Hello!');
-    }
-  }
+const qnaMaker = require('@bottender/qna-maker');
+
+async function Unknown(context) {
+  await context.sendText('Sorry, I don’t know what you say.');
+}
+
+const QnaMaker = qnaMaker({
+  resourceName: process.env.RESOURCE_NAME,
+  knowledgeBaseId: process.env.KNOWLEDGE_BASE_ID,
+  endpointKey: process.env.ENDPOINT_KEY,
+  scoreThreshold: 70,
+});
+
+module.exports = async function App() {
+  return chain([
+    QnaMaker, //
+    Unknown,
+  ]);
 };
 ```
 
-You can check out the full example [Here](https://github.com/Yoctol/bottender/tree/master/examples/with-dialogflow).
+> **Note:**
+>
+> - Fore the full example code, please refer to Bottender example, [With QnA Maker](https://github.com/Yoctol/bottender/tree/master/examples/with-qna-maker).
 
-For more information about how to use Dialogflow with Node.js, you can check [Dialogflow: Node.js Client Document](https://googleapis.dev/nodejs/dialogflow/latest/index.html).
+## Building with Dialogflow
 
-## Building with QnA Maker
+Google creates Dialogflow. Since 2019 Google announced its NLU pre-training [BERT](https://www.blog.google/products/search/search-language-understanding-bert), we are confident in Google's NLU solution; at least we can think it might be the state-of-the-art.
 
-To build a bot integrated with [QnA Maker](https://www.qnamaker.ai/), you have to create the QnA Maker knowledge base and publish it following the [Official Guide](https://docs.microsoft.com/en-us/azure/cognitive-services/qnamaker/quickstarts/create-publish-knowledge-base).
+### Step 1: Dialogflow Setup
 
-After you publish your knowledge base, you will get `RESOURCE_NAME`, `KNOWLEDGE_BASE_ID`, and `ENDPOINT_KEY` (See [Here](https://docs.microsoft.com/en-us/azure/cognitive-services/qnamaker/quickstarts/get-answer-from-knowledge-base-using-url-tool?pivots=url-test-tool-postman) for detailed guide). Make sure you copy them into the `.env` file.
+To build a bot integrated with [Dialogflow](https://dialogflow.com/), you have to set up Dialogflow following the Dialogflow doc, [Quickstart: Setup](https://cloud.google.com/dialogflow/docs/quick/setup) and fill in the two values into the `.env` file:
+
+- `GOOGLE_APPLICATION_CREDENTIALS`, which is the file path of the JSON file that contains your service account key
+- `GOOGLE_APPLICATION_PROJECT_ID`, which stands for the GCP project ID
 
 ```
-# .env
-RESOURCE_NAME=
-KNOWLEDGE_BASE_ID=
-ENDPOINT_KEY=
+// .env
+
+GOOGLE_APPLICATION_CREDENTIALS=
+GOOGLE_APPLICATION_PROJECT_ID=
 ```
 
-Next, we can get the answer by calling the API endpoint. In the example, we use `axios` as our HTTP client. For other Node HTTP clients like `request-promise`, you can see this [document](https://docs.microsoft.com/en-us/azure/cognitive-services/qnamaker/quickstarts/get-answer-from-knowledge-base-nodejs). The returned response will contain an array with possible QA pairs and corresponding scores. You can define your own score threshold in the code.
+### Step 2: Create a Dialogflow Agent
+
+Next, you can build a Dialogflow agent following the Dialogflow doc, [Quickstart: Build an Agent](https://cloud.google.com/dialogflow/docs/quick/build-agent). In this example, we make an agent and create an intent with the display name `greeting.` You can set your training phrases on the Dialogflow console for this intent.
+
+After you finish the settings of the agent, you can call Dialogflow's API to analyze the intent of the message the bot receives.
+
+### Step 3: Connect Bottender with Dialogflow by `bottender/dialogflow`
+
+To make the bot development enjoyable, we made a [`bottender/dialogflow`](https://github.com/Yoctol/bottender/tree/master/packages/bottender-dialogflow) package. You can install the package with `npm` or `yarn`.
+
+With `npm`:
+
+```sh
+npm install @bottender/dialogflow
+```
+
+or with `yarn`:
+
+```sh
+yarn add @bottender/dialogflow
+```
+
+In the following sample code, you can see how elegant it is to integrate Bottender with Dialogflow. All you need to do is to fill in your environment variables, write a map between `intents` (e.g., `greeting`) and corresponding `functions` (e.g., `SayHello`).
 
 ```js
-const axios = require('axios');
-
-const { RESOURCE_NAME, KNOWLEDGE_BASE_ID, ENDPOINT_KEY } = process.env;
-
-module.exports = async function App(context) {
-  if (context.event.isText) {
-    const { data } = await axios.post(
-      `https://${RESOURCE_NAME}.azurewebsites.net/qnamaker/knowledgebases/${KNOWLEDGE_BASE_ID}/generateAnswer`,
-      { question: context.event.text },
-      {
-        headers: {
-          Authorization: `EndpointKey ${ENDPOINT_KEY}`,
-        },
-      }
-    );
-
-    const topAnswer = data.answers[0];
-
-    // You can define your own score threshold here.
-    if (topAnswer.score > 70) {
-      await context.sendText(topAnswer.answer);
-    }
-  }
+const dialogflow = require('@bottender/dialogflow');
+
+async function SayHello(context) {
+  await context.sendText('Hello!');
+}
+
+async function Unknown(context) {
+  await context.sendText('Sorry, I don’t know what you say.');
+}
+
+const Dialogflow = dialogflow({
+  projectId: process.env.GOOGLE_APPLICATION_PROJECT_ID,
+  actions: {
+    greeting: SayHello,
+  },
+});
+
+module.exports = async function App() {
+  return chain([
+    Dialogflow, //
+    Unknown,
+  ]);
 };
 ```
 
-You can check out the full example [Here](https://github.com/Yoctol/bottender/tree/master/examples/with-qna-maker).
+> **Note:**
+>
+> - Dialogflow offers `intent name` and `intent display name`. The value of the former one is fixed once it is created; the value of the latter one can be change at any time. While writing the map between `intents` and `functions` at `bottender/dialogflow`, you can use any of the two to represent a single `intent.`
+> - Fore the full example code, please refer to Bottender example, [With Dialogflow](https://github.com/Yoctol/bottender/tree/master/examples/with-dialogflow)
 
 ## Building with LUIS
 
+### Step 1: LUIS Setup
+
 To build a bot integrated with [LUIS (Language Understanding Intelligent Service)](https://luis.ai/), you have to create a new app in the LUIS portal following the [Official Setup Guide](https://docs.microsoft.com/en-us/azure/cognitive-services/LUIS/get-started-portal-build-app) and fill the three values: `LUIS_APP_ID`, `LUIS_APP_KEY`, and `LUIS_APP_ENDPOINT` into the `.env` file.
 
 ```
-# .env
+// .env
 LUIS_APP_ID=
 LUIS_APP_KEY=
 LUIS_APP_ENDPOINT=
 ```
 
-Next, you can add a simple intent. In this example, we create a simple intent with the intent name `greeting`. You can set your own training phrases on the LUIS console for this intent. And then you have to train the model and publish it.
+### Step 2: Train and Publish Your LUIS Project
+
+In this example, we create an intent with the intent name `greeting`. You can set your training phrases on the LUIS console for this intent. And then you have to train the model and publish it.
 
-After publishing successfully, you can call LUIS's API to detect the intention of the message the bot receives. In this example, we use `axios` as our HTTP client to call the API.
+### Step 3: Connect Bottender with LUIS by `bottender/luis`
+
+To make the bot development enjoyable, we made a [`bottender/luis`](https://github.com/Yoctol/bottender/tree/master/packages/bottender-luis) package. You can install the package with `npm` or `yarn`.
+
+With `npm`:
+
+```sh
+npm install @bottender/luis
+```
+
+Or with `yarn`:
+
+```sh
+yarn add @bottender/luis
+```
+
+In the following sample code, you can see how elegant it is to integrate Bottender with LUIS. All you need to do is to fill in your environment variables, and score threshold, then write a map between `intents` (e.g., `greeting`) and corresponding `functions` (e.g., `SayHello`).
 
 ```js
-const axios = require('axios');
-
-const { LUIS_APP_KEY, LUIS_APP_ENDPOINT, LUIS_APP_ID } = process.env;
-
-module.exports = async function App(context) {
-  if (context.event.isText) {
-    const queryParams = {
-      verbose: true,
-      q: context.event.text,
-      'subscription-key': LUIS_APP_KEY,
-    };
-
-    const { data } = await axios.get(
-      `${LUIS_APP_ENDPOINT}luis/v2.0/apps/${LUIS_APP_ID}`,
-      {
-        params: queryParams,
-      }
-    );
-
-    const { topScoringIntent } = data;
-
-    if (topScoringIntent.intent === 'greeting') {
-      await context.sendText('Hello!');
-    }
-  }
+const luis = require('@bottender/luis');
+
+async function SayHello(context) {
+  await context.sendText('Hello!');
+}
+
+async function Unknown(context) {
+  await context.sendText('Sorry, I don’t know what you say.');
+}
+
+const Luis = luis({
+  appId: process.env.LUIS_APP_ID,
+  appKey: process.env.LUIS_APP_KEY,
+  endpoint: 'https://westus.api.cognitive.microsoft.com',
+  actions: {
+    greeting: SayHello,
+  },
+  scoreThreshold: 0.7,
+});
+
+module.exports = async function App() {
+  return chain([
+    Luis, //
+    Unknown,
+  ]);
 };
 ```
 
-You can check out the full example [Here](https://github.com/Yoctol/bottender/tree/master/examples/with-luis.ai).
+> **Note:**
+>
+> - Fore the full example code, please refer to Bottender example, [With LUIS.ai](https://github.com/Yoctol/bottender/tree/master/examples/with-luis.ai).
 
 ## Building with Rasa NLU
 
+You may choose Rasa NLU if you're finding an on-premises NLU solution.
+
+### Step 1: Rasa NLU Setup
+
 To build a bot integrated with [Rasa NLU](https://rasa.com/docs/rasa/nlu/about/), you have to install Rasa first following the [Official Installation Guide](https://rasa.com/docs/rasa/user-guide/installation/). Next, you can train your NLU model by running:
 
 ```sh
 rasa train nlu
 ```
 
-This command will look for the training data files in the data/ directory and saves the model in the models/ directory. For information about how to generate training data, you can see [here](https://rasa.com/docs/rasa/nlu/training-data-format/).
+This command will look for the training data files in the data/ directory and saves the model in the models/ directory. For information about how to generate training data, you can see Rasa's document, [Training Data Format](https://rasa.com/docs/rasa/nlu/training-data-format/).
 
 After you get your NLU model ready, you can run the following command:
 
@@ -173,29 +241,53 @@ After you get your NLU model ready, you can run the following command:
 rasa run --enable-api -m models/nlu-your-model-id.tar.gz
 ```
 
-This will start a server with your NLU model locally on port 5005. Next, you can request predictions from your model by calling the `/model/parse` endpoint. You can see [here](https://rasa.com/docs/rasa/api/http-api/#operation/parseModelMessage) for the document of this API.
+This command starts a server with your NLU model locally on port 5005. Next, you can request predictions from your model by calling the `/model/parse` endpoint. You can see [here](https://rasa.com/docs/rasa/api/http-api/#operation/parseModelMessage) for the document of this API.
+
+### Step 2: Connect Bottender with Rasa by `bottender/rasa`
+
+To make the bot development enjoyable, we made a [`bottender/rasa`](https://github.com/Yoctol/bottender/tree/master/packages/bottender-rasa) package. You can install the package with `npm` or `yarn`.
+
+With `npm`:
+
+```sh
+npm install @bottender/rasa
+```
+
+Or with `yarn`:
+
+```sh
+yarn add @bottender/rasa
+```
 
-In this example, we use `axios` as our HTTP client. The returned response will contain an intent with its corresponding confidence. You can define your own confidence threshold in the code.
+In the following sample code, you can see how elegant it is to integrate Bottender with Rasa. All you need to do is to set up the origin URL, and confidence threshold, then write a map between `intents` (e.g., `greeting`) and corresponding `functions` (e.g., `SayHello`).
 
 ```js
-const axios = require('axios');
-
-module.exports = async function App(context) {
-  if (context.event.isText) {
-    const { data } = await axios.post(`http://localhost:5005/model/parse`, {
-      text: context.event.text,
-    });
-
-    const { intent } = data;
-
-    // You can define your own confidence threshold here.
-    if (intent.confidence > 0.7) {
-      if (intent.name === 'greeting') {
-        await context.sendText('Hello!');
-      }
-    }
-  }
+const rasa = require('@bottender/rasa');
+
+async function SayHello(context) {
+  await context.sendText('Hello!');
+}
+
+async function Unknown(context) {
+  await context.sendText('Sorry, I don’t know what you say.');
+}
+
+const Rasa = rasa({
+  origin: 'http://localhost:5005',
+  actions: {
+    greeting: SayHello,
+  },
+  confidenceThreshold: 0.7,
+});
+
+module.exports = async function App() {
+  return chain([
+    Rasa, //
+    Unknown,
+  ]);
 };
 ```
 
-You can check out the full example [Here](https://github.com/Yoctol/bottender/tree/master/examples/with-rasa).
+> **Note:**
+>
+> - Fore the full example code, please refer to Bottender example, [With Rasa](https://github.com/Yoctol/bottender/tree/master/examples/with-rasa).
diff --git a/docs/channel-line-flex.md b/docs/channel-line-flex.md
index a6066baa..86548388 100644
--- a/docs/channel-line-flex.md
+++ b/docs/channel-line-flex.md
@@ -1,6 +1,6 @@
 ---
 id: channel-line-flex
-title: Sending Flex Message
+title: Sending LINE Flex Message
 ---
 
 ## Flex Message, the User Experience Bridge Between Web and Bot
diff --git a/docs/channel-line-rich-menu.md b/docs/channel-line-rich-menu.md
index 2bfe0e91..bef566da 100644
--- a/docs/channel-line-rich-menu.md
+++ b/docs/channel-line-rich-menu.md
@@ -1,6 +1,6 @@
 ---
 id: channel-line-rich-menu
-title: Rich Menu
+title: LINE Rich Menu
 ---
 
 ## What is Rich Menu
diff --git a/docs/channel-messenger-persona.md b/docs/channel-messenger-persona.md
index 2b4cc38b..186d3818 100644
--- a/docs/channel-messenger-persona.md
+++ b/docs/channel-messenger-persona.md
@@ -1,6 +1,6 @@
 ---
 id: channel-messenger-persona
-title: Using Persona
+title: Using Messenger Persona
 ---
 
 ## Messenger Persona Practical Guide
diff --git a/docs/channel-viber-sending-messages.md b/docs/channel-viber-sending-messages.md
new file mode 100644
index 00000000..78f1ef5e
--- /dev/null
+++ b/docs/channel-viber-sending-messages.md
@@ -0,0 +1,273 @@
+---
+id: channel-viber-sending-messages
+title: Sending Viber Messages
+---
+
+## Sending Text Messages
+
+Use this method to send text messages.
+
+```js
+async function SendHi(context) {
+  await context.sendText('Hi!');
+}
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481925-61e46008-aeeb-11e7-842f-79fee8066c6a.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Text message](https://developers.viber.com/docs/api/rest-bot-api/#text-message).
+
+## Sending Picture Messages
+
+Use this method to send picture.
+
+```js
+await context.sendPicture({
+  text: 'Photo description',
+  media: 'http://www.images.com/img.jpg',
+  thumbnail: 'http://www.images.com/thumb.jpg',
+});
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481916-5ec6cdac-aeeb-11e7-878b-6c8c4211a760.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Picture message](https://developers.viber.com/docs/api/rest-bot-api/#picture-message).
+
+## Sending Video Messages
+
+Use this method to send videos.
+
+```js
+await context.sendVideo({
+  media: 'http://www.images.com/video.mp4',
+  size: 10000,
+  thumbnail: 'http://www.images.com/thumb.jpg',
+  duration: 10,
+});
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481918-5fa12074-aeeb-11e7-8287-830197d93b5b.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Video message](https://developers.viber.com/docs/api/rest-bot-api/#video-message).
+
+## Sending File Messages
+
+Use this method to send files.
+
+```js
+await context.sendFile({
+  media: 'http://www.images.com/file.doc',
+  size: 10000,
+  fileName: 'name_of_file.doc',
+});
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481919-600f437e-aeeb-11e7-9f13-7269a055cb86.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [File message](https://developers.viber.com/docs/api/rest-bot-api/#file-message).
+
+## Sending Contact Messages
+
+Use this method to send contact messages.
+
+```js
+await context.sendContact({
+  name: 'Itamar',
+  phoneNumber: '+972511123123',
+});
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481924-615ce8b2-aeeb-11e7-8425-2d3bfa115fc1.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Contact message](https://developers.viber.com/docs/api/rest-bot-api/#contact-message).
+
+## Sending Location Messages
+
+Use this method to send a location point on the map.
+
+```js
+await context.sendLocation({
+  lat: '37.7898',
+  lon: '-122.3942',
+});
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481923-61199a9e-aeeb-11e7-8a25-e3813eceb25b.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Location message](https://developers.viber.com/docs/api/rest-bot-api/#location-message).
+
+## Sending URL Messages
+
+Use this method to send URLs.
+
+```js
+await client.sendURL('http://developers.viber.com');
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481921-6069f346-aeeb-11e7-97bf-83a17da0bc7a.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [URL message](https://developers.viber.com/docs/api/rest-bot-api/#url-message).
+
+## Sending Sticker Messages
+
+Use this method to send stickers.
+
+```js
+await context.sendSticker(46105);
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481922-60c2c444-aeeb-11e7-8fc9-bce2e5d06c42.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Sticker message](https://developers.viber.com/docs/api/rest-bot-api/#sticker-message).
+
+## Sending Carousel Content Messages
+
+Use this method to send carousel messages.
+
+```js
+await context.sendCarouselContent({
+  type: 'rich_media',
+  buttonsGroupColumns: 6,
+  buttonsGroupRows: 7,
+  bgColor: '#FFFFFF',
+  buttons: [
+    {
+      columns: 6,
+      rows: 3,
+      actionType: 'open-url',
+      actionBody: 'https://www.google.com',
+      image: 'http://html-test:8080/myweb/guy/assets/imageRMsmall2.png',
+    },
+    {
+      columns: 6,
+      rows: 2,
+      text:
+        '<font color=#323232><b>Headphones with Microphone, On-ear Wired earphones</b></font><font color=#777777><br>Sound Intone </font><font color=#6fc133>$17.99</font>',
+      actionType: 'open-url',
+      actionBody: 'https://www.google.com',
+      textSize: 'medium',
+      textVAlign: 'middle',
+      textHAlign: 'left',
+    },
+    {
+      columns: 6,
+      rows: 1,
+      actionType: 'reply',
+      actionBody: 'https://www.google.com',
+      text: '<font color=#ffffff>Buy</font>',
+      textSize: 'large',
+      textVAlign: 'middle',
+      textHAlign: 'middle',
+      image: 'https://s14.postimg.org/4mmt4rw1t/Button.png',
+    },
+    {
+      columns: 6,
+      rows: 1,
+      actionType: 'reply',
+      actionBody: 'https://www.google.com',
+      text: '<font color=#8367db>MORE DETAILS</font>',
+      textSize: 'small',
+      textVAlign: 'middle',
+      textHAlign: 'middle',
+    },
+    {
+      columns: 6,
+      Rows: 3,
+      ActionType: 'open-url',
+      ActionBody: 'https://www.google.com',
+      Image: 'https://s16.postimg.org/wi8jx20wl/image_RMsmall2.png',
+    },
+    {
+      columns: 6,
+      rows: 2,
+      text:
+        "<font color=#323232><b>Hanes Men's Humor Graphic T-Shirt</b></font><font color=#777777><br>Hanes</font><font color=#6fc133>$10.99</font>",
+      actionType: 'open-url',
+      actionBody: 'https://www.google.com',
+      textSize: 'medium',
+      textVAlign: 'middle',
+      textHAlign: 'left',
+    },
+    {
+      columns: 6,
+      rows: 1,
+      actionType: 'reply',
+      actionBody: 'https://www.google.com',
+      text: '<font color=#ffffff>Buy</font>',
+      textSize: 'large',
+      textVAlign: 'middle',
+      textHAlign: 'middle',
+      image: 'https://s14.postimg.org/4mmt4rw1t/Button.png',
+    },
+    {
+      columns: 6,
+      rows: 1,
+      actionType: 'reply',
+      actionBody: 'https://www.google.com',
+      text: '<font color=#8367db>MORE DETAILS</font>',
+      textSize: 'small',
+      textVAlign: 'middle',
+      textHAlign: 'middle',
+    },
+  ],
+});
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481917-5f1b43b4-aeeb-11e7-8557-e25951d69b53.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Rich Media message / Carousel content message](https://developers.viber.com/docs/api/rest-bot-api/#rich-media-message--carousel-content-message).
+
+## Sending with Keyboard
+
+Use `keyboard` option to send keyboard with the message.
+
+```js
+await context.sendText('Hello', {
+  keyboard: {
+    defaultHeight: true,
+    bgColor: '#FFFFFF',
+    buttons: [
+      {
+        columns: 6,
+        rows: 1,
+        bgColor: '#2db9b9',
+        bgMediaType: 'gif',
+        bgMedia: 'http://www.url.by/test.gif',
+        bgLoop: true,
+        actionType: 'open-url',
+        actionBody: 'www.tut.by',
+        image: 'www.tut.by/img.jpg',
+        text: 'Key text',
+        textVAlign: 'middle',
+        textHAlign: 'center',
+        textOpacity: 60,
+        textSize: 'regular',
+      },
+    ],
+  },
+});
+```
+
+Which in turn will look like this:
+
+![](https://camo.githubusercontent.com/fe2141c452f4cf0ed0f7854f51c589b15873a317/68747470733a2f2f646576656c6f706572732e76696265722e636f6d2f646f63732f696d672f6578616d706c655f6b6579626f6172642e706e67)
+
+> **Note:** For more info, please refer to Viber's official doc, [Keyboards](https://developers.viber.com/docs/api/rest-bot-api/#keyboards).
diff --git a/lerna.json b/lerna.json
index be0fe01e..d7f67c12 100644
--- a/lerna.json
+++ b/lerna.json
@@ -2,7 +2,7 @@
   "lerna": "3.4.0",
   "npmClient": "yarn",
   "useWorkspaces": true,
-  "version": "1.2.0-alpha.7",
+  "version": "1.2.0-alpha.8",
   "command": {
     "publish": {
       "ignoreChanges": [
diff --git a/packages/bottender-dialogflow/package.json b/packages/bottender-dialogflow/package.json
index 51010724..88f7ca8c 100644
--- a/packages/bottender-dialogflow/package.json
+++ b/packages/bottender-dialogflow/package.json
@@ -7,7 +7,7 @@
     "type": "git",
     "url": "https://github.com/Yoctol/bottender.git"
   },
-  "version": "1.2.0-alpha.7",
+  "version": "1.2.0-alpha.8",
   "main": "dist/index.js",
   "files": [
     "dist"
diff --git a/packages/bottender-luis/package.json b/packages/bottender-luis/package.json
index 3e2c750d..fd2bb6d4 100644
--- a/packages/bottender-luis/package.json
+++ b/packages/bottender-luis/package.json
@@ -7,7 +7,7 @@
     "type": "git",
     "url": "https://github.com/Yoctol/bottender.git"
   },
-  "version": "1.2.0-alpha.7",
+  "version": "1.2.0-alpha.8",
   "main": "dist/index.js",
   "files": [
     "dist"
diff --git a/packages/bottender-qna-maker/package.json b/packages/bottender-qna-maker/package.json
index 5d7a3061..9b54f94c 100644
--- a/packages/bottender-qna-maker/package.json
+++ b/packages/bottender-qna-maker/package.json
@@ -7,7 +7,7 @@
     "type": "git",
     "url": "https://github.com/Yoctol/bottender.git"
   },
-  "version": "1.2.0-alpha.7",
+  "version": "1.2.0-alpha.8",
   "main": "dist/index.js",
   "files": [
     "dist"
diff --git a/packages/bottender-rasa/package.json b/packages/bottender-rasa/package.json
index 96f7e7dc..40e9ca37 100644
--- a/packages/bottender-rasa/package.json
+++ b/packages/bottender-rasa/package.json
@@ -7,7 +7,7 @@
     "type": "git",
     "url": "https://github.com/Yoctol/bottender.git"
   },
-  "version": "1.2.0-alpha.7",
+  "version": "1.2.0-alpha.8",
   "main": "dist/index.js",
   "files": [
     "dist"
diff --git a/packages/bottender/package.json b/packages/bottender/package.json
index d559eae6..6b81d06e 100644
--- a/packages/bottender/package.json
+++ b/packages/bottender/package.json
@@ -7,7 +7,7 @@
     "type": "git",
     "url": "https://github.com/Yoctol/bottender.git"
   },
-  "version": "1.2.0-alpha.7",
+  "version": "1.2.0-alpha.8",
   "main": "dist/index.js",
   "browser": "dist/browser.js",
   "bin": {
diff --git a/packages/bottender/src/getSessionStore.ts b/packages/bottender/src/getSessionStore.ts
new file mode 100644
index 00000000..8a9bd481
--- /dev/null
+++ b/packages/bottender/src/getSessionStore.ts
@@ -0,0 +1,64 @@
+import warning from 'warning';
+
+import getBottenderConfig from './shared/getBottenderConfig';
+
+function getSessionStore() {
+  const bottenderConfig = getBottenderConfig();
+
+  const { session } = bottenderConfig;
+
+  const sessionDriver = (session && session.driver) || 'memory';
+
+  const storesConfig = (session && session.stores) || {};
+
+  switch (sessionDriver) {
+    case 'memory': {
+      const MemorySessionStore = require('./session/MemorySessionStore')
+        .default;
+
+      return new MemorySessionStore(
+        storesConfig.memory || {},
+        session && session.expiresIn
+      );
+    }
+    case 'file': {
+      const FileSessionStore = require('./session/FileSessionStore').default;
+
+      return new FileSessionStore(
+        storesConfig.file || {},
+        session && session.expiresIn
+      );
+    }
+    case 'mongo': {
+      const MongoSessionStore = require('./session/MongoSessionStore').default;
+
+      return new MongoSessionStore(
+        storesConfig.mongo || {},
+        session && session.expiresIn
+      );
+    }
+    case 'redis': {
+      const RedisSessionStore = require('./session/RedisSessionStore').default;
+
+      return new RedisSessionStore(
+        storesConfig.redis || {},
+        session && session.expiresIn
+      );
+    }
+    default: {
+      warning(
+        false,
+        `Received unknown driver: ${sessionDriver}, so fallback it to \`memory\` driver.`
+      );
+      const MemorySessionStore = require('./session/MemorySessionStore')
+        .default;
+
+      return new MemorySessionStore(
+        storesConfig.memory || {},
+        session && session.expiresIn
+      );
+    }
+  }
+}
+
+export default getSessionStore;
diff --git a/packages/bottender/src/index.ts b/packages/bottender/src/index.ts
index 7a3a1bc7..d01cfea4 100644
--- a/packages/bottender/src/index.ts
+++ b/packages/bottender/src/index.ts
@@ -5,6 +5,7 @@ export { bottender };
 /* Core */
 export { default as Bot } from './bot/Bot';
 export { default as Context } from './context/Context';
+export { default as getSessionStore } from './getSessionStore';
 
 /* Action */
 export { default as chain } from './chain';
diff --git a/packages/bottender/src/initializeServer.ts b/packages/bottender/src/initializeServer.ts
index 234464df..8e72da37 100644
--- a/packages/bottender/src/initializeServer.ts
+++ b/packages/bottender/src/initializeServer.ts
@@ -11,6 +11,7 @@ import SlackBot from './slack/SlackBot';
 import TelegramBot from './telegram/TelegramBot';
 import ViberBot from './viber/ViberBot';
 import getBottenderConfig from './shared/getBottenderConfig';
+import getSessionStore from './getSessionStore';
 import { Action, BottenderConfig, Channel, Plugin } from './types';
 
 const BOT_MAP = {
@@ -30,35 +31,9 @@ function initializeServer({
 } = {}): express.Application | void {
   const bottenderConfig = getBottenderConfig();
 
-  const { initialState, plugins, session, channels } = merge(
-    bottenderConfig,
-    config
-  );
-
-  // session
-  const sessionDriver = (session && session.driver) || 'memory';
-  let SessionStore;
-  switch (sessionDriver) {
-    case 'file':
-      SessionStore = require('./session/FileSessionStore').default;
-      break;
-    case 'mongo':
-      SessionStore = require('./session/MongoSessionStore').default;
-      break;
-    case 'redis':
-      SessionStore = require('./session/RedisSessionStore').default;
-      break;
-    default:
-      SessionStore = require('./session/MemorySessionStore').default;
-  }
-
-  const sessionDriverConfig =
-    ((session && session.stores) || {})[sessionDriver] || {};
+  const { initialState, plugins, channels } = merge(bottenderConfig, config);
 
-  const sessionStore = new SessionStore(
-    sessionDriverConfig,
-    session && session.expiresIn
-  );
+  const sessionStore = getSessionStore();
 
   // TODO: refine handler entry, improve error message and hint
   // eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-var-requires
diff --git a/packages/bottender/src/server/Server.ts b/packages/bottender/src/server/Server.ts
index 60731a5a..ec665b02 100644
--- a/packages/bottender/src/server/Server.ts
+++ b/packages/bottender/src/server/Server.ts
@@ -8,47 +8,14 @@ import { pascalcase } from 'messaging-api-common';
 
 import Bot from '../bot/Bot';
 import getBottenderConfig from '../shared/getBottenderConfig';
-import {
-  Action,
-  BottenderConfig,
-  Plugin,
-  SessionConfig,
-  SessionDriver,
-} from '../types';
+import getSessionStore from '../getSessionStore';
+import { Action, BottenderConfig, Plugin } from '../types';
 
 export type ServerOptions = {
   useConsole?: boolean;
   dev?: boolean;
 };
 
-function createSessionStore(
-  sessionConfig: SessionConfig = { driver: SessionDriver.Memory, stores: {} }
-) {
-  const sessionDriver = (sessionConfig && sessionConfig.driver) || 'memory';
-  let SessionStore;
-  switch (sessionDriver) {
-    case 'file':
-      SessionStore = require('../session/FileSessionStore').default;
-      break;
-    case 'mongo':
-      SessionStore = require('../session/MongoSessionStore').default;
-      break;
-    case 'redis':
-      SessionStore = require('../session/RedisSessionStore').default;
-      break;
-    default:
-      SessionStore = require('../session/MemorySessionStore').default;
-  }
-
-  const sessionDriverConfig =
-    ((sessionConfig && sessionConfig.stores) || {})[sessionDriver] || {};
-
-  return new SessionStore(
-    sessionDriverConfig,
-    sessionConfig && sessionConfig.expiresIn
-  );
-}
-
 class Server {
   _channelBots: { webhookPath: string; bot: Bot<any, any, any> }[] = [];
 
@@ -73,11 +40,11 @@ class Server {
   public async prepare(): Promise<void> {
     const bottenderConfig = getBottenderConfig();
 
-    const { initialState, plugins, session, channels } = merge(
+    const { initialState, plugins, channels } = merge(
       bottenderConfig /* , config */
     ) as BottenderConfig;
 
-    const sessionStore = createSessionStore(session);
+    const sessionStore = getSessionStore();
 
     // TODO: refine handler entry, improve error message and hint
     // eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-var-requires
diff --git a/packages/bottender/src/session/MongoSessionStore.ts b/packages/bottender/src/session/MongoSessionStore.ts
index f12bd6be..939637e6 100644
--- a/packages/bottender/src/session/MongoSessionStore.ts
+++ b/packages/bottender/src/session/MongoSessionStore.ts
@@ -8,7 +8,7 @@ import SessionStore from './SessionStore';
 type MongoOption =
   | string
   | {
-      url: string;
+      url?: string;
       collectionName?: string;
     };
 
@@ -27,7 +27,7 @@ export default class MongoSessionStore implements SessionStore {
       this._url = options;
       this._collectionName = 'sessions';
     } else {
-      this._url = options.url;
+      this._url = options.url || 'mongodb://localhost:27017';
       this._collectionName = options.collectionName || 'sessions';
     }
     this._expiresIn = expiresIn || 0;
diff --git a/packages/bottender/src/types.ts b/packages/bottender/src/types.ts
index 2f8852a7..3f4cc2b0 100644
--- a/packages/bottender/src/types.ts
+++ b/packages/bottender/src/types.ts
@@ -86,10 +86,10 @@ export type SessionConfig = {
       port?: number;
       host?: string;
       password?: string;
-      db: number;
+      db?: number;
     };
     mongo?: {
-      url: string;
+      url?: string;
       collectionName?: string;
     };
   };
diff --git a/website/i18n/en.json b/website/i18n/en.json
index 422509db..5f3e29b6 100644
--- a/website/i18n/en.json
+++ b/website/i18n/en.json
@@ -5,8 +5,11 @@
     "previous": "Previous",
     "tagline": "A framework for building conversational user interfaces.",
     "docs": {
+      "advanced-guides-deployment": {
+        "title": "Deployment"
+      },
       "advanced-guides-multi-channel": {
-        "title": "Multi-Messaging Channel Support"
+        "title": "Multi-Channel Support"
       },
       "advanced-guides-nlu": {
         "title": "Natural Language Understanding"
@@ -57,7 +60,7 @@
         "title": "Error Handling in LINE"
       },
       "channel-line-flex": {
-        "title": "Sending Flex Message"
+        "title": "Sending LINE Flex Message"
       },
       "channel-line-handling-events": {
         "title": "Handling LINE Events"
@@ -66,7 +69,7 @@
         "title": "Migrating from LINE SDK for Node.js"
       },
       "channel-line-rich-menu": {
-        "title": "Rich Menu"
+        "title": "LINE Rich Menu"
       },
       "channel-line-routing": {
         "title": "LINE Routing"
@@ -87,7 +90,7 @@
         "title": "Messenger Multi-Page Support"
       },
       "channel-messenger-persona": {
-        "title": "Using Persona"
+        "title": "Using Messenger Persona"
       },
       "channel-messenger-profile": {
         "title": "Messenger Profile"
@@ -134,6 +137,9 @@
       "channel-viber-routing": {
         "title": "Viber Routing"
       },
+      "channel-viber-sending-messages": {
+        "title": "Sending Viber Messages"
+      },
       "channel-viber-setup": {
         "title": "Setup Viber"
       },
@@ -392,8 +398,11 @@
       "version-1.0.5/version-1.0.5-the-basics-session": {
         "title": "Session"
       },
+      "version-1.1.0/version-1.1.0-advanced-guides-deployment": {
+        "title": "Deployment"
+      },
       "version-1.1.0/version-1.1.0-advanced-guides-multi-channel": {
-        "title": "Multi-Messaging Channel Support"
+        "title": "Multi-Channel Support"
       },
       "version-1.1.0/version-1.1.0-channel-line-handling-events": {
         "title": "Handling LINE Events"
@@ -437,6 +446,9 @@
       "version-1.1.0/version-1.1.0-channel-viber-routing": {
         "title": "Viber Routing"
       },
+      "version-1.1.0/version-1.1.0-channel-viber-sending-messages": {
+        "title": "Sending Viber Messages"
+      },
       "version-1.1.0/version-1.1.0-the-basics-routing": {
         "title": "Routing"
       }
diff --git a/website/sidebars.json b/website/sidebars.json
index 53467425..a2dd6380 100755
--- a/website/sidebars.json
+++ b/website/sidebars.json
@@ -13,6 +13,7 @@
       "the-basics-errors"
     ],
     "Advanced Guides": [
+      "advanced-guides-deployment",
       "advanced-guides-nlu",
       "advanced-guides-multi-channel"
     ],
@@ -52,17 +53,39 @@
     "Guides (Viber)": [
       "channel-viber-setup",
       "channel-viber-handling-events",
+      "channel-viber-sending-messages",
       "channel-viber-routing"
     ],
     "FAQ": []
   },
   "api": {
-    "Core": ["api-context", "api-event"],
-    "Messenger": ["api-messenger-context", "api-messenger-event"],
-    "LINE": ["api-line-context", "api-line-event"],
-    "Slack": ["api-slack-context", "api-slack-event"],
-    "Telegram": ["api-telegram-context", "api-telegram-event"],
-    "Viber": ["api-viber-context", "api-viber-event"],
-    "Console": ["api-console-context", "api-console-event"]
+    "Core": [
+      "api-context",
+      "api-event"
+    ],
+    "Messenger": [
+      "api-messenger-context",
+      "api-messenger-event"
+    ],
+    "LINE": [
+      "api-line-context",
+      "api-line-event"
+    ],
+    "Slack": [
+      "api-slack-context",
+      "api-slack-event"
+    ],
+    "Telegram": [
+      "api-telegram-context",
+      "api-telegram-event"
+    ],
+    "Viber": [
+      "api-viber-context",
+      "api-viber-event"
+    ],
+    "Console": [
+      "api-console-context",
+      "api-console-event"
+    ]
   }
-}
+}
\ No newline at end of file
diff --git a/website/versioned_docs/version-1.1.0/advanced-guides-deployment.md b/website/versioned_docs/version-1.1.0/advanced-guides-deployment.md
new file mode 100644
index 00000000..7e033b07
--- /dev/null
+++ b/website/versioned_docs/version-1.1.0/advanced-guides-deployment.md
@@ -0,0 +1,345 @@
+---
+id: version-1.1.0-advanced-guides-deployment
+title: Deployment
+original_id: advanced-guides-deployment
+---
+
+ <p><img width="500" src="https://user-images.githubusercontent.com/662387/72043275-b7c3fe80-32eb-11ea-9a49-f2d5c073f397.jpg"></p>
+
+Once you finished your bot in development, the next step is to deploy your bot to a hosting service!
+
+> **Note:** We aim to offer deployment guides for every popular modern hosting services. [Tweet us](https://twitter.com/bottenderjs) if you haven't seen your favorite one.
+
+### Before Going Further
+
+We assumed that you already built at least one basic Bottender app in development. If you haven't, you may check [Getting Started](https://bottender.js.org/docs/getting-started) to create your first Bottender app in a few minutes, then jump to the setup doc of your favorite chat channel:
+
+- [Setup Messenger](https://bottender.js.org/docs/channel-messenger-setup)
+- [Setup LINE](https://bottender.js.org/docs/channel-line-setup)
+- [Setup Slack](https://bottender.js.org/docs/channel-slack-setup)
+- [Setup Telegram](https://bottender.js.org/docs/channel-telegram-setup)
+- [Setup Viber](https://bottender.js.org/docs/channel-viber-setup)
+
+## Heroku
+
+<p><img width="300" src="https://user-images.githubusercontent.com/662387/72130857-a8a98300-33b5-11ea-9ec6-10c8aac37230.jpg"></p>
+
+Heroku is one of the most popular hosting services. Not only the clear document, ease of scalability, using Git for deployment, but also the friendly [free pricing plan](https://www.heroku.com/pricing) for experiment purpose.
+
+In the following, you can see the necessary steps of Heroku Deployment:
+
+### Step 1: Create a Heroku Account & Download Heroku CLI
+
+First, [sign up](https://www.heroku.com/) a Heroku account if you haven't, then download and install [Heroku CLI](https://devcenter.heroku.com/articles/getting-started-with-nodejs#set-up).
+
+> **Note:** For the full command list, please refer to Heroku's doc, [Heroku CLI Commands](https://devcenter.heroku.com/articles/heroku-cli).
+
+### Step 2: Heroku Login & Create a Heroku App
+
+Before going further, make sure you have login your Heroku account by:
+
+```sh
+heroku login
+```
+
+Then, you can create a Heroku app by the below command.
+
+```sh
+heroku create <your-heroku-app-name>
+```
+
+> **Note:** You may see some app name regulation if you don't meet it. For example: `Name must start with a letter, end with a letter or digit and can only contain lowercase letters, digits, and dashes`
+
+Once you created your Heroku app successfully, you could see a deployment address for your app like `https://<your-heroku-app-name>.herokuapp.com/`. You can note it down for the coming webhook setting.
+
+### Step 3: Fill in Environment Variables to Heroku
+
+Config the environment variables of your Heroku app with the following commands: `heroku config:set -a <your-heroku-app-name> <ENV_KEY_01>=<ENV_VALUE_01>`.
+
+For chat channels require multiple environment variables, you may use commands like `heroku config:set -a <your-heroku-app-name> <ENV_KEY_01>=<ENV_VALUE_01> <ENV_KEY_02>=<ENV_VALUE_02>`.
+
+For example:
+
+```sh
+heroku config:set -a <your-heroku-app-name> MESSENGER_PAGE_ID=xxxxxx MESSENGER_ACCESS_TOKEN=xxxxxx MESSENGER_APP_ID=xxxxxx MESSENGER_APP_SECRET=xxxxxx MESSENGER_VERIFY_TOKEN=xxxxxx
+```
+
+### Step 4: Using Git in Your Bottender App
+
+Deployment of Heroku depends on Git. Make sure you have run `git init` and make the first commit in your Bottender app.
+
+For example:
+
+```sh
+git init
+git add .
+git commit -am "first commit"
+```
+
+### Step 5: Deploy Your Bot to Heroku and Set Up Webhook
+
+When you try to set up the webhook, some chat channels (e.g., Messenger) might ask for an immediate bot server verification. So, we recommend you to set up the webhook after your Bottender app server running.
+
+There are two basic types of webhook setup:
+
+1. Set up webhook by Developer Console UI, e.g., Messenger, LINE, Slack
+2. Set up webhook by CLI, e.g., Messenger, Telegram, Viber
+
+#### Step 5a: Set up Webhook by UI of Developer Console
+
+Use Heroku CLI by Git push to complete the deployment.
+
+```sh
+heroku git:remote -a <your-heroku-app-name>
+git push heroku master
+```
+
+Then fill in your webhook URL on the developer console of the chat channel.
+
+> **Note:** If you are not familiar with webhook setup, you may refer to Bottender docs, [Setup Messenger](https://bottender.js.org/docs/channel-messenger-setup), [Setup LINE](https://bottender.js.org/docs/channel-line-setup), and [Setup Slack](https://bottender.js.org/docs/channel-slack-setup).
+
+> **Note:** If you haven't changed your webhook path in `bottender.config.js`, by default, your Messenger Bot webhook is `https://<your-heroku-app-name>.herokuapp.com/webhooks/messenger`; your LINE Bot webhook is `https://<your-heroku-app-name>.herokuapp.com/webhooks/line`, and so on.
+
+#### Step 5b: Set up Webhook by CLI
+
+You can benefit from the `Procfile` feature of Heroku, which specifies the commands executed by the app on startup. We are going to use two process types of `Procfile`:
+
+- `web` process type: tell Heroku to run your bot server for every `dyno`
+- `release` process type: set up webhook before a new release is deployed to production
+
+> **Note:** For more info about `Procfile`, you may refer to Heroku's guide, [The Procfile](https://devcenter.heroku.com/articles/procfile).
+
+Using a Messenger Bot as an example, the `Procfile` looks like the below with default webhook path settings:
+
+```js
+// Procfile
+
+web: npm start
+release: echo "Y" | npx bottender messenger webhook set -w https://<your-heroku-app-name>.com/webhooks/messenger
+```
+
+> **Note:**
+>
+> - The `echo "Y"` aims to answer the first interactive CLI prompt
+> - If you haven't changed your webhook path in `bottender.config.js`, by default, your Messenger Bot webhook is `https://<your-heroku-app-name>.herokuapp.com/webhooks/messenger`; your LINE Bot webhook is `https://<your-heroku-app-name>.herokuapp.com/webhooks/line`, and so on.
+
+Finally, You can use Heroku CLI by Git push to complete the deployment and let Heroku runs the `Procfile` to help you finish the webhook setup.
+
+```sh
+heroku git:remote -a <your-heroku-app-name>
+git push heroku master
+```
+
+### Step 6: Completed!
+
+Congratulations! You have made your Bottender bot production-ready. Share your fantastic bot with your friends!
+
+> **Note:** If you would like to share your Bottender Bot with the community, please feel free to add your project to [Bottender Users](https://bottender.js.org/users).
+
+## ZEIT Now 2.0
+
+<p><img width="1000" src="https://user-images.githubusercontent.com/662387/72130872-acd5a080-33b5-11ea-8c6c-ae06dd250be6.png"></p>
+
+ZEIT Now 2.0 is a super developer-friendly hosting service. It is famous for its one-word magic command `now` in the deployment of static web hosting.
+
+However, it needs a bit configuration to deploy a Bottender app and make it works serverless. In the following sections, you can see the necessary steps of ZEIT Now 2.0 deployment:
+
+### Step 1: Create a ZEIT Account
+
+First, [create](https://ZEIT.co/signup) a ZEIT Account if you haven't.
+
+### Step 2: Install ZEIT Now CLI
+
+We love to deploy with CLI! Install ZEIT Now CLI with npm by:
+
+```sh
+npm install -g now
+```
+
+### Step 3: Create a `server.js`
+
+ZEIT Now 2.0 doesn't support `npm` scripts, so we need a `server.js` as the entry point.
+
+```js
+// server.js
+
+const bodyParser = require('body-parser');
+const express = require('express');
+const { bottender } = require('bottender');
+
+const app = bottender({
+  dev: process.env.NODE_ENV !== 'production',
+});
+
+const port = Number(process.env.PORT) || 5000;
+
+const handle = app.getRequestHandler();
+
+app.prepare().then(() => {
+  const server = express();
+
+  server.use(
+    bodyParser.json({
+      verify: (req, _, buf) => {
+        req.rawBody = buf.toString();
+      },
+    })
+  );
+
+  server.all('*', (req, res) => {
+    return handle(req, res);
+  });
+
+  server.listen(port, err => {
+    if (err) throw err;
+    console.log(`> Ready on http://localhost:${port}`);
+  });
+});
+```
+
+### Step 4: Update Your `now.json`
+
+In the following configuration, you will make a few settings:
+
+- Set ZEIT Now to version 2.0
+- Use `@now/node` to bundle `server.js`
+- Put `bottender.config.js` and `index.js` into `includeFiles`. ZEIT Now uses `ncc` and `webpack bundler` under the hood, so we need to tell them to put those two files into the bundle)
+- Route all (/.\*) to `server.js`
+- Add your chat channel specific environment variables from `.env` to env. The number of environment variables various from the chat channel you use.
+- (optional) If you are debugging your app, you may set `DEBUG` env to `bottender*,messaging-api*`
+
+Using a Messenger Bot as an example. your `now.json` is like:
+
+```js
+// now.json
+
+{
+  "version": 2,
+  "builds": [
+    {
+      "src": "server.js",
+      "use": "@now/node",
+      "config": {
+        "includeFiles": [
+          "bottender.config.js",
+          "index.js"
+        ],
+        "bundle": true
+      }
+    }
+  ],
+  "routes": [
+    {
+      "src": "/.*",
+      "dest": "/server.js"
+    }
+  ],
+  "env": {
+    "MESSENGER_PAGE_ID": "xxxxxx",
+    "MESSENGER_ACCESS_TOKEN": "xxxxxx",
+    "MESSENGER_APP_ID": "xxxxxx",
+    "MESSENGER_APP_SECRET": "xxxxxx",
+    "MESSENGER_VERIFY_TOKEN": "xxxxxx",
+    "DEBUG": "bottender*,messaging-api*"
+  }
+}
+```
+
+### Step 5: Update Your `bottender.config.js`
+
+ZEIT Now 2.0 is a serverless hosting service, and serverless functions terminated right after HTTP response. So you have to make Bottender work synchronously, i.e., respond to chat channel right after received an event.
+
+to make the Bottender app executes in the synchronous mode, you must configure chat channels with `sync: true` in `bottender.config.js`.
+
+```js
+// bottender.config.js
+
+module.exports = {
+  // ... skip
+
+  channels: {
+    messenger: {
+      enabled: true,
+      sync: true,
+      // ...skip
+    },
+    line: {
+      enabled: true,
+      sync: true,
+      // ...skip
+    },
+    telegram: {
+      enabled: false,
+      sync: true,
+      // ...skip
+    },
+    slack: {
+      enabled: false,
+      sync: true,
+      // ...skip
+    },
+    viber: {
+      enabled: false,
+      sync: true,
+      // ...skip
+    },
+  },
+};
+```
+
+### Step 6: Update Your `package.json`
+
+Set up Node.js version to `10.x` or `12.x` in the `engines` env to avoid warnings for deprecated node 8.
+
+```js
+// package.json
+{
+ // ...skip
+ "engines": { "node": "12.x" }
+}
+```
+
+### Step 7: Deploy with `now`
+
+After all the settings, you are ready to use the magic word, `now`, for ZEIT Now 2.0 deployment.
+
+```sh
+now
+```
+
+Then you will see something like the screenshot below.
+
+![](https://user-images.githubusercontent.com/662387/72136431-c382f400-33c3-11ea-9745-839c212c1b2e.png)
+
+> **Note:** Your deployed URL would be like `https://<your-app-name>.<your-user-name>.now.sh`
+
+### Step 8: Webhook Setup
+
+If you haven't changed your webhook path in `bottender.config.js`, by default, your Messenger Bot webhook is `https://<your-app-name>.<your-user-name>.now.sh/webhooks/messenger`; your LINE Bot webhook is `https://<your-app-name>.<your-user-name>.now.sh/webhooks/line`, and so on.
+
+There are two basic types of webhook setup:
+
+1. Set up webhook by Developer Console UI, e.g., Messenger, LINE, Slack
+2. Set up webhook by CLI, e.g., Messenger, Telegram, Viber
+
+#### Step 8a: Set up Webhook by Developer Console UI
+
+Fill in your webhook URL on the developer console of the chat channel.
+
+> **Note:** If you are not familiar with webhook setup, you may refer to Bottender docs, [Setup Messenger](https://bottender.js.org/docs/channel-messenger-setup), [Setup LINE](https://bottender.js.org/docs/channel-line-setup), [Setup Slack](https://bottender.js.org/docs/channel-slack-setup).
+
+#### Step 8b: Set up Webhook by CLI
+
+Make sure you have the same environment variable settings in your local `.env` file and `now.js`. Then you can use the following command to set up the webhook.
+
+Using a Messenger Bot as an example, your command is like:
+
+```sh
+npx bottender messenger webhook set -w https://<your-app-name>.<your-user-name>.now.sh/webhooks/messenger
+```
+
+### Step 9: Completed!
+
+Congratulations! You have made your Bottender bot production-ready. Share your fantastic bot with your friends!
+
+> **Note:** If you would like to share your Bottender Bot with the community, please feel free to add your project to [Bottender Users](https://bottender.js.org/users).
diff --git a/website/versioned_docs/version-1.1.0/advanced-guides-multi-channel.md b/website/versioned_docs/version-1.1.0/advanced-guides-multi-channel.md
index 2d63c76a..2e77f6eb 100644
--- a/website/versioned_docs/version-1.1.0/advanced-guides-multi-channel.md
+++ b/website/versioned_docs/version-1.1.0/advanced-guides-multi-channel.md
@@ -1,6 +1,6 @@
 ---
 id: version-1.1.0-advanced-guides-multi-channel
-title: Multi-Messaging Channel Support
+title: Multi-Channel Support
 original_id: advanced-guides-multi-channel
 ---
 
diff --git a/website/versioned_docs/version-1.1.0/channel-viber-sending-messages.md b/website/versioned_docs/version-1.1.0/channel-viber-sending-messages.md
new file mode 100644
index 00000000..fc67cdf6
--- /dev/null
+++ b/website/versioned_docs/version-1.1.0/channel-viber-sending-messages.md
@@ -0,0 +1,274 @@
+---
+id: version-1.1.0-channel-viber-sending-messages
+title: Sending Viber Messages
+original_id: channel-viber-sending-messages
+---
+
+## Sending Text Messages
+
+Use this method to send text messages.
+
+```js
+async function SendHi(context) {
+  await context.sendText('Hi!');
+}
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481925-61e46008-aeeb-11e7-842f-79fee8066c6a.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Text message](https://developers.viber.com/docs/api/rest-bot-api/#text-message).
+
+## Sending Picture Messages
+
+Use this method to send picture.
+
+```js
+await context.sendPicture({
+  text: 'Photo description',
+  media: 'http://www.images.com/img.jpg',
+  thumbnail: 'http://www.images.com/thumb.jpg',
+});
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481916-5ec6cdac-aeeb-11e7-878b-6c8c4211a760.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Picture message](https://developers.viber.com/docs/api/rest-bot-api/#picture-message).
+
+## Sending Video Messages
+
+Use this method to send videos.
+
+```js
+await context.sendVideo({
+  media: 'http://www.images.com/video.mp4',
+  size: 10000,
+  thumbnail: 'http://www.images.com/thumb.jpg',
+  duration: 10,
+});
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481918-5fa12074-aeeb-11e7-8287-830197d93b5b.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Video message](https://developers.viber.com/docs/api/rest-bot-api/#video-message).
+
+## Sending File Messages
+
+Use this method to send files.
+
+```js
+await context.sendFile({
+  media: 'http://www.images.com/file.doc',
+  size: 10000,
+  fileName: 'name_of_file.doc',
+});
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481919-600f437e-aeeb-11e7-9f13-7269a055cb86.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [File message](https://developers.viber.com/docs/api/rest-bot-api/#file-message).
+
+## Sending Contact Messages
+
+Use this method to send contact messages.
+
+```js
+await context.sendContact({
+  name: 'Itamar',
+  phoneNumber: '+972511123123',
+});
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481924-615ce8b2-aeeb-11e7-8425-2d3bfa115fc1.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Contact message](https://developers.viber.com/docs/api/rest-bot-api/#contact-message).
+
+## Sending Location Messages
+
+Use this method to send a location point on the map.
+
+```js
+await context.sendLocation({
+  lat: '37.7898',
+  lon: '-122.3942',
+});
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481923-61199a9e-aeeb-11e7-8a25-e3813eceb25b.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Location message](https://developers.viber.com/docs/api/rest-bot-api/#location-message).
+
+## Sending URL Messages
+
+Use this method to send URLs.
+
+```js
+await client.sendURL('http://developers.viber.com');
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481921-6069f346-aeeb-11e7-97bf-83a17da0bc7a.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [URL message](https://developers.viber.com/docs/api/rest-bot-api/#url-message).
+
+## Sending Sticker Messages
+
+Use this method to send stickers.
+
+```js
+await context.sendSticker(46105);
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481922-60c2c444-aeeb-11e7-8fc9-bce2e5d06c42.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Sticker message](https://developers.viber.com/docs/api/rest-bot-api/#sticker-message).
+
+## Sending Carousel Content Messages
+
+Use this method to send carousel messages.
+
+```js
+await context.sendCarouselContent({
+  type: 'rich_media',
+  buttonsGroupColumns: 6,
+  buttonsGroupRows: 7,
+  bgColor: '#FFFFFF',
+  buttons: [
+    {
+      columns: 6,
+      rows: 3,
+      actionType: 'open-url',
+      actionBody: 'https://www.google.com',
+      image: 'http://html-test:8080/myweb/guy/assets/imageRMsmall2.png',
+    },
+    {
+      columns: 6,
+      rows: 2,
+      text:
+        '<font color=#323232><b>Headphones with Microphone, On-ear Wired earphones</b></font><font color=#777777><br>Sound Intone </font><font color=#6fc133>$17.99</font>',
+      actionType: 'open-url',
+      actionBody: 'https://www.google.com',
+      textSize: 'medium',
+      textVAlign: 'middle',
+      textHAlign: 'left',
+    },
+    {
+      columns: 6,
+      rows: 1,
+      actionType: 'reply',
+      actionBody: 'https://www.google.com',
+      text: '<font color=#ffffff>Buy</font>',
+      textSize: 'large',
+      textVAlign: 'middle',
+      textHAlign: 'middle',
+      image: 'https://s14.postimg.org/4mmt4rw1t/Button.png',
+    },
+    {
+      columns: 6,
+      rows: 1,
+      actionType: 'reply',
+      actionBody: 'https://www.google.com',
+      text: '<font color=#8367db>MORE DETAILS</font>',
+      textSize: 'small',
+      textVAlign: 'middle',
+      textHAlign: 'middle',
+    },
+    {
+      columns: 6,
+      Rows: 3,
+      ActionType: 'open-url',
+      ActionBody: 'https://www.google.com',
+      Image: 'https://s16.postimg.org/wi8jx20wl/image_RMsmall2.png',
+    },
+    {
+      columns: 6,
+      rows: 2,
+      text:
+        "<font color=#323232><b>Hanes Men's Humor Graphic T-Shirt</b></font><font color=#777777><br>Hanes</font><font color=#6fc133>$10.99</font>",
+      actionType: 'open-url',
+      actionBody: 'https://www.google.com',
+      textSize: 'medium',
+      textVAlign: 'middle',
+      textHAlign: 'left',
+    },
+    {
+      columns: 6,
+      rows: 1,
+      actionType: 'reply',
+      actionBody: 'https://www.google.com',
+      text: '<font color=#ffffff>Buy</font>',
+      textSize: 'large',
+      textVAlign: 'middle',
+      textHAlign: 'middle',
+      image: 'https://s14.postimg.org/4mmt4rw1t/Button.png',
+    },
+    {
+      columns: 6,
+      rows: 1,
+      actionType: 'reply',
+      actionBody: 'https://www.google.com',
+      text: '<font color=#8367db>MORE DETAILS</font>',
+      textSize: 'small',
+      textVAlign: 'middle',
+      textHAlign: 'middle',
+    },
+  ],
+});
+```
+
+Which in turn will look like this:
+
+![](https://user-images.githubusercontent.com/3382565/31481917-5f1b43b4-aeeb-11e7-8557-e25951d69b53.jpg)
+
+> **Note:** For more info, please refer to Viber's official doc, [Rich Media message / Carousel content message](https://developers.viber.com/docs/api/rest-bot-api/#rich-media-message--carousel-content-message).
+
+## Sending with Keyboard
+
+Use `keyboard` option to send keyboard with the message.
+
+```js
+await context.sendText('Hello', {
+  keyboard: {
+    defaultHeight: true,
+    bgColor: '#FFFFFF',
+    buttons: [
+      {
+        columns: 6,
+        rows: 1,
+        bgColor: '#2db9b9',
+        bgMediaType: 'gif',
+        bgMedia: 'http://www.url.by/test.gif',
+        bgLoop: true,
+        actionType: 'open-url',
+        actionBody: 'www.tut.by',
+        image: 'www.tut.by/img.jpg',
+        text: 'Key text',
+        textVAlign: 'middle',
+        textHAlign: 'center',
+        textOpacity: 60,
+        textSize: 'regular',
+      },
+    ],
+  },
+});
+```
+
+Which in turn will look like this:
+
+![](https://camo.githubusercontent.com/fe2141c452f4cf0ed0f7854f51c589b15873a317/68747470733a2f2f646576656c6f706572732e76696265722e636f6d2f646f63732f696d672f6578616d706c655f6b6579626f6172642e706e67)
+
+> **Note:** For more info, please refer to Viber's official doc, [Keyboards](https://developers.viber.com/docs/api/rest-bot-api/#keyboards).
diff --git a/website/versioned_sidebars/version-1.1.0-sidebars.json b/website/versioned_sidebars/version-1.1.0-sidebars.json
index 013012d4..e0c4e9be 100644
--- a/website/versioned_sidebars/version-1.1.0-sidebars.json
+++ b/website/versioned_sidebars/version-1.1.0-sidebars.json
@@ -13,6 +13,7 @@
       "version-1.1.0-the-basics-errors"
     ],
     "Advanced Guides": [
+      "version-1.1.0-advanced-guides-deployment",
       "version-1.1.0-advanced-guides-nlu",
       "version-1.1.0-advanced-guides-multi-channel"
     ],
@@ -52,6 +53,7 @@
     "Guides (Viber)": [
       "version-1.1.0-channel-viber-setup",
       "version-1.1.0-channel-viber-handling-events",
+      "version-1.1.0-channel-viber-sending-messages",
       "version-1.1.0-channel-viber-routing"
     ],
     "FAQ": []


Test patch:
diff --git a/packages/bottender/src/__tests__/getSessionStore.spec.ts b/packages/bottender/src/__tests__/getSessionStore.spec.ts
new file mode 100644
index 00000000..3d28b3e3
--- /dev/null
+++ b/packages/bottender/src/__tests__/getSessionStore.spec.ts
@@ -0,0 +1,74 @@
+import getBottenderConfig from '../shared/getBottenderConfig';
+import getSessionStore from '../getSessionStore';
+import { SessionDriver } from '../types';
+
+jest.mock('../shared/getBottenderConfig');
+
+const getBottenderConfigMocked = getBottenderConfig as jest.MockedFunction<
+  typeof getBottenderConfig
+>;
+
+it('should get MemorySessionStore by default', () => {
+  getBottenderConfigMocked.mockReturnValueOnce({});
+  expect(getSessionStore().constructor.name).toEqual('MemorySessionStore');
+});
+
+it('should get MemorySessionStore when assigning memory as driver', () => {
+  getBottenderConfigMocked.mockReturnValueOnce({
+    session: {
+      driver: SessionDriver.Memory,
+      stores: {
+        memory: {
+          maxSize: 500,
+        },
+      },
+    },
+  });
+  expect(getSessionStore().constructor.name).toEqual('MemorySessionStore');
+});
+
+it('should get FileSessionStore when assigning file as driver', () => {
+  getBottenderConfigMocked.mockReturnValueOnce({
+    session: {
+      driver: SessionDriver.File,
+      stores: {
+        file: {
+          dirname: '.sessions',
+        },
+      },
+    },
+  });
+  expect(getSessionStore().constructor.name).toEqual('FileSessionStore');
+});
+
+it('should get MongoSessionStore when assigning mongo as driver', () => {
+  getBottenderConfigMocked.mockReturnValueOnce({
+    session: {
+      driver: SessionDriver.Mongo,
+      stores: {
+        mongo: {
+          url: 'mongodb://localhost:27017',
+          collectionName: 'sessions',
+        },
+      },
+    },
+  });
+  expect(getSessionStore().constructor.name).toEqual('MongoSessionStore');
+});
+
+it('should get RedisSessionStore when assigning redis as driver', () => {
+  getBottenderConfigMocked.mockReturnValueOnce({
+    session: {
+      driver: SessionDriver.Redis,
+      stores: {
+        redis: {
+          port: 6379,
+          host: '127.0.0.1',
+          password: 'auth',
+          db: 0,
+        },
+      },
+    },
+  });
+  expect(getSessionStore().constructor.name).toEqual('RedisSessionStore');
+});
diff --git a/packages/bottender/src/__tests__/index.spec.ts b/packages/bottender/src/__tests__/index.spec.ts
index 9b726597..d46461d5 100644
--- a/packages/bottender/src/__tests__/index.spec.ts
+++ b/packages/bottender/src/__tests__/index.spec.ts
@@ -51,6 +51,10 @@ describe('core', () => {
     expect(core.createServer).toBeDefined();
   });
 
+  it('export getSessionStore', () => {
+    expect(core.getSessionStore).toBeDefined();
+  });
+
   it('export chain', () => {
     expect(core.chain).toBeDefined();
   });