Repository: Yoctol/bottender
Pull request #29 (2017-11-21 07:47:20)

Joi validate


Patch:
diff --git a/README.md b/README.md
index 8a1377fb..4dfc4f9e 100644
--- a/README.md
+++ b/README.md
@@ -7,34 +7,28 @@
 
 ![](https://user-images.githubusercontent.com/3382565/32216617-85cb703a-bdf3-11e7-9a0b-0e084b47c39e.png)
 
-* **Flexible** - Declare handlers as any JavaScript function.
+- **Flexible** - Declare handlers as any JavaScript function.
 
-* **Modern** - Source written with ES6/ES7 syntax and great async await
-  supports.
+- **Modern** - Source written with ES6/ES7 syntax and great async await supports.
 
-* **Modular** - Use session stores, server framework adapters and platform
-  connectors with same interface.
+- **Modular** - Use session stores, server framework adapters and platform connectors with same interface.
 
-* **Learn Once, Write Anywhere** - Handle multiple platforms with consistent
-  development experience.
+- **Learn Once, Write Anywhere** - Handle multiple platforms with consistent development experience.
 
-Bottender is built on top of
-[Messaging APIs](https://github.com/Yoctol/messaging-apis).
+Bottender is built on top of [Messaging APIs](https://github.com/Yoctol/messaging-apis).
 
 ## Documentation
 
 You can find the Bottender documentation on the website.
 
-* [Getting Started](https://bottender.js.org/docs/GettingStarted)
-* [Platforms](https://bottender.js.org/docs/Platforms-Messenger)
-* [Guides](https://bottender.js.org/docs/Guides-Commands)
-* [API Reference](https://bottender.js.org/docs/APIReference-Context)
+- [Getting Started](https://bottender.js.org/docs/GettingStarted)
+- [Platforms](https://bottender.js.org/docs/Platforms-Messenger)
+- [Guides](https://bottender.js.org/docs/Guides-Commands)
+- [API Reference](https://bottender.js.org/docs/APIReference-Context)
 
 ## Examples
 
-We have a bunch of examples in the
-[examples](https://github.com/Yoctol/bottender/tree/master/examples) folder.
-Here is the first one to get you started:
+We have a bunch of examples in the [examples](https://github.com/Yoctol/bottender/tree/master/examples) folder. Here is the first one to get you started:
 
 ```js
 const { ConsoleBot } = require('bottender');
@@ -50,8 +44,7 @@ bot.createRuntime();
 
 This will create and run a bot which always reply "Hello World" in the console.
 
-You'll notice that there is an async function to be passed to the `onEvent`
-method, we call it handler. You can put your bot logic in there.
+You'll notice that there is an async function to be passed to the `onEvent` method, we call it handler. You can put your bot logic in there.
 
 ## Installation
 
@@ -69,8 +62,7 @@ npm install bottender
 
 ## Contributing
 
-Pull Requests and issue reports are welcome. You can follow steps below to
-submit your pull requests:
+Pull Requests and issue reports are welcome. You can follow steps below to submit your pull requests:  
 
 Fork, then clone the repo:
 
diff --git a/examples/console-hello-world/README.md b/examples/console-hello-world/README.md
index 4a3a1d66..dfa8d5e3 100644
--- a/examples/console-hello-world/README.md
+++ b/examples/console-hello-world/README.md
@@ -13,12 +13,12 @@ npm run dev
 
 ## Idea of this example
 
-This example is a simple bot running on your terminal.\
+This example is a simple bot running on your terminal.  
 It shows how to build a most basic console bot.
 
 ## Related examples
 
-* [messenger-hello-world](../messenger-hello-world)
-* [line-hello-world](../line-hello-world)
-* [slack-hello-world](../slack-hello-world)
-* [telegram-hello-world](../telegram-hello-world)
+- [messenger-hello-world](../messenger-hello-world)
+- [line-hello-world](../line-hello-world)
+- [slack-hello-world](../slack-hello-world)
+- [telegram-hello-world](../telegram-hello-world)
diff --git a/examples/cross-platform/README.md b/examples/cross-platform/README.md
index 69d9ffac..a194bdb2 100644
--- a/examples/cross-platform/README.md
+++ b/examples/cross-platform/README.md
@@ -13,15 +13,12 @@ npm run dev
 
 ## Idea of this example
 
-This example shows the basic idea of building a cross-platform bot. You have
-better understand the concept of
-[handler](https://bottender.js.org/docs/APIReference-Handler) at first. The best
-feature of this example is that you can only develop one handler for four
-different bots, instead of developing one handler for each platform.
+This example shows the basic idea of building a cross-platform bot. You have better understand the concept of [handler](https://bottender.js.org/docs/APIReference-Handler) at first.
+The best feature of this example is that you can only develop one handler for four different bots, instead of developing one handler for each platform.
 
 ## Related examples
 
-* [messenger-builder](../messenger-builder)
-* [line-builder](../line-builder)
-* [slack-builder](../slack-builder)
-* [telegram-builder](../telegram-builder)
+- [messenger-builder](../messenger-builder)
+- [line-builder](../line-builder)
+- [slack-builder](../slack-builder)
+- [telegram-builder](../telegram-builder)
diff --git a/examples/echo-bot/README.md b/examples/echo-bot/README.md
index c41f56ed..e90aa97a 100644
--- a/examples/echo-bot/README.md
+++ b/examples/echo-bot/README.md
@@ -13,12 +13,9 @@ npm run dev
 
 ## Idea of this example
 
-This is a simple console bot which will echo what you said and shows how to
-distinguish what kind of message the bot has received. The easiest way is to use
-`context.event.isXXXXX`. It helps your bot recognize incoming requests.\
-Check our [event guide](https://bottender.js.org/docs/APIReference-Event/) for more
-information.
+This is a simple console bot which will echo what you said and shows how to distinguish what kind of message the bot has received. The easiest way is to use `context.event.isXXXXX`. It helps your bot recognize incoming requests.   
+Check our [event guide](https://bottender.js.org/docs/APIReference-Event/) for more information.
 
 ## Related examples
 
-* [console-hello-world](../console-hello-world)
+- [console-hello-world](../console-hello-world)
diff --git a/examples/line-builder/README.md b/examples/line-builder/README.md
index 037a807c..3edb1707 100644
--- a/examples/line-builder/README.md
+++ b/examples/line-builder/README.md
@@ -13,14 +13,12 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to combine
-[handler](https://bottender.js.org/docs/APIReference-Handler) with
-[LINE](https://line.me/) bot. For more information, check our
-[LINE guides](https://bottender.js.org/docs/Platforms-LINE).
+This example shows how to combine [handler](https://bottender.js.org/docs/APIReference-Handler) with [LINE](https://line.me/) bot.
+For more information, check our [LINE guides](https://bottender.js.org/docs/Platforms-LINE).  
 
 ## Related examples
 
-* [line-hello-world](../line-hello-world)
-* [messenger-builder](../messenger-builder)
-* [slack-builder](../slack-builder)
-* [telegram-builder](../telegram-builder)
+- [line-hello-world](../line-hello-world)
+- [messenger-builder](../messenger-builder)
+- [slack-builder](../slack-builder)
+- [telegram-builder](../telegram-builder)
diff --git a/examples/line-hello-world/README.md b/examples/line-hello-world/README.md
index 00554432..cd6d7f35 100644
--- a/examples/line-hello-world/README.md
+++ b/examples/line-hello-world/README.md
@@ -13,13 +13,12 @@ npm run dev
 
 ## Idea of this example
 
-This example is a simple bot running on [LINE](https://line.me/). You have to
-get `channelSecret` and `accessToken` before running this bot.\
-For more information, check our [LINE guides](https://bottender.js.org/docs/Platforms-LINE).
+This example is a simple bot running on [LINE](https://line.me/). You have to get `channelSecret` and `accessToken` before running this bot.  
+For more information, check our [LINE guides](https://bottender.js.org/docs/Platforms-LINE).  
 
 ## Related examples
 
-* [line-builder](../line-builder)
-* [console-hello-world](../console-hello-world)
-* [slack-hello-world](../slack-hello-world)
-* [telegram-hello-world](../telegram-hello-world)
+- [line-builder](../line-builder)
+- [console-hello-world](../console-hello-world)
+- [slack-hello-world](../slack-hello-world)
+- [telegram-hello-world](../telegram-hello-world)
diff --git a/examples/messenger-builder/README.md b/examples/messenger-builder/README.md
index 02fdb8d3..a3610ab1 100644
--- a/examples/messenger-builder/README.md
+++ b/examples/messenger-builder/README.md
@@ -13,15 +13,12 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to combine
-[handler](https://bottender.js.org/docs/APIReference-Handler) with
-[Messenger](https://www.messenger.com/) bot. You have to get `accessToken` and
-`appSecret` before running this bot.\
-For more information, check our [Messenger guides](https://bottender.js.org/docs/Platforms-Messenger).
+This example shows how to combine [handler](https://bottender.js.org/docs/APIReference-Handler) with [Messenger](https://www.messenger.com/) bot. You have to get `accessToken` and `appSecret` before running this bot.  
+For more information, check our [Messenger guides](https://bottender.js.org/docs/Platforms-Messenger).  
 
 ## Related examples
 
-* [messenger-hello-world](../messenger-hello-world)
-* [line-builder](../line-builder)
-* [slack-builder](../slack-builder)
-* [telegram-builder](../telegram-builder)
+- [messenger-hello-world](../messenger-hello-world)
+- [line-builder](../line-builder)
+- [slack-builder](../slack-builder)
+- [telegram-builder](../telegram-builder)
diff --git a/examples/messenger-hello-world/README.md b/examples/messenger-hello-world/README.md
index 7bbbc504..01c65305 100644
--- a/examples/messenger-hello-world/README.md
+++ b/examples/messenger-hello-world/README.md
@@ -13,14 +13,13 @@ npm run dev
 
 ## Idea of this example
 
-This example is a simple bot running on [Messenger](https://www.messenger.com/).
-You have to get `accessToken` and `appSecret` before running this bot.\
-For more information, check our [Messenger guides](https://bottender.js.org/docs/Platforms-Messenger).
+This example is a simple bot running on [Messenger](https://www.messenger.com/). You have to get `accessToken` and `appSecret` before running this bot.  
+For more information, check our [Messenger guides](https://bottender.js.org/docs/Platforms-Messenger).  
 
 ## Related examples
 
-* [messenger-builder](../messenger-builder)
-* [console-hello-world](../console-hello-world)
-* [line-hello-world](../line-hello-world)
-* [slack-hello-world](../slack-hello-world)
-* [telegram-hello-world](../telegram-hello-world)
+- [messenger-builder](../messenger-builder)
+- [console-hello-world](../console-hello-world)
+- [line-hello-world](../line-hello-world)
+- [slack-hello-world](../slack-hello-world)
+- [telegram-hello-world](../telegram-hello-world)
diff --git a/examples/middleware/README.md b/examples/middleware/README.md
index a9e30171..e493d66d 100644
--- a/examples/middleware/README.md
+++ b/examples/middleware/README.md
@@ -13,14 +13,13 @@ npm run dev
 
 ## Idea of this example
 
-The feature of this example is using
-[handler](https://bottender.js.org/docs/APIReference-Handler) as middleware, so
-you are able to combine two handlers into one.\
-For more information, check our [Middleware guides](https://bottender.js.org/docs/Guides-Middleware).
+The feature of this example is using [handler](https://bottender.js.org/docs/APIReference-Handler) as middleware, so you are able to combine two handlers into one.  
+For more information, check our [Middleware guides](https://bottender.js.org/docs/Guides-Middleware).  
+
 
 ## Related examples
 
-* [messenger-builder](../messenger-builder)
-* [line-builder](../line-builder)
-* [slack-builder](../slack-builder)
-* [telegram-builder](../telegram-builder)
+- [messenger-builder](../messenger-builder)
+- [line-builder](../line-builder)
+- [slack-builder](../slack-builder)
+- [telegram-builder](../telegram-builder)
diff --git a/examples/slack-builder/README.md b/examples/slack-builder/README.md
index f3a345c8..a26b8f38 100644
--- a/examples/slack-builder/README.md
+++ b/examples/slack-builder/README.md
@@ -13,15 +13,12 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to combine
-[handler](https://bottender.js.org/docs/APIReference-Handler) with
-[Slack](https://slack.com/) bot. You have to get `accessToken` before running
-this bot.\
-For more information, check our [Slack guides](https://bottender.js.org/docs/Platforms-Slack).
+This example shows how to combine [handler](https://bottender.js.org/docs/APIReference-Handler) with [Slack](https://slack.com/) bot. You have to get `accessToken` before running this bot.  
+For more information, check our [Slack guides](https://bottender.js.org/docs/Platforms-Slack).  
 
 ## Related examples
 
-* [slack-hello-world](../slack-hello-world)
-* [messenger-builder](../messenger-builder)
-* [line-builder](../line-builder)
-* [telegram-builder](../telegram-builder)
+- [slack-hello-world](../slack-hello-world)
+- [messenger-builder](../messenger-builder)
+- [line-builder](../line-builder)
+- [telegram-builder](../telegram-builder)
diff --git a/examples/slack-hello-world/README.md b/examples/slack-hello-world/README.md
index 1ed7c678..ccee78da 100644
--- a/examples/slack-hello-world/README.md
+++ b/examples/slack-hello-world/README.md
@@ -13,14 +13,13 @@ npm run dev
 
 ## Idea of this example
 
-This example is a simple bot running on [Slack](https://slack.com/). You have to
-get `accessToken` before running this bot.\
-For more information, check our [Slack guides](https://bottender.js.org/docs/Platforms-Slack).
+This example is a simple bot running on [Slack](https://slack.com/). You have to get `accessToken` before running this bot.  
+For more information, check our [Slack guides](https://bottender.js.org/docs/Platforms-Slack).  
 
 ## Related examples
 
-* [slack-builder](../slack-builder)
-* [console-hello-world](../console-hello-world)
-* [messenger-hello-world](../messenger-hello-world)
-* [slack-hello-world](../slack-hello-world)
-* [telegram-hello-world](../telegram-hello-world)
+- [slack-builder](../slack-builder)
+- [console-hello-world](../console-hello-world)
+- [messenger-hello-world](../messenger-hello-world)
+- [slack-hello-world](../slack-hello-world)
+- [telegram-hello-world](../telegram-hello-world)
diff --git a/examples/telegram-builder/README.md b/examples/telegram-builder/README.md
index 5840fcff..d090e12e 100644
--- a/examples/telegram-builder/README.md
+++ b/examples/telegram-builder/README.md
@@ -13,15 +13,12 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to combine
-[handler](https://bottender.js.org/docs/APIReference-Handler) with
-[Telegram](https://telegram.org/) bot. You have to get `accessToken` and a
-webhook `url` before running this bot.\
-For more information, check our [Telegram guides](https://bottender.js.org/docs/Platforms-Telegram).
+This example shows how to combine [handler](https://bottender.js.org/docs/APIReference-Handler) with [Telegram](https://telegram.org/) bot. You have to get `accessToken` and a webhook `url` before running this bot.  
+For more information, check our [Telegram guides](https://bottender.js.org/docs/Platforms-Telegram).  
 
 ## Related examples
 
-* [telegram-hello-world](../telegram-hello-world)
-* [messenger-builder](../messenger-builder)
-* [line-builder](../line-builder)
-* [slack-builder](../slack-builder)
+- [telegram-hello-world](../telegram-hello-world)
+- [messenger-builder](../messenger-builder)
+- [line-builder](../line-builder)
+- [slack-builder](../slack-builder)
diff --git a/examples/telegram-hello-world/README.md b/examples/telegram-hello-world/README.md
index d5e5f35a..e0b145da 100644
--- a/examples/telegram-hello-world/README.md
+++ b/examples/telegram-hello-world/README.md
@@ -13,14 +13,13 @@ npm run dev
 
 ## Idea of this example
 
-This example is a simple bot running on [Telegram](https://telegram.org/). You
-have to get `accessToken` and a webhook `url` before running this bot.\
-For more information, check our [Telegram guides](https://bottender.js.org/docs/Platforms-Telegram).
+This example is a simple bot running on [Telegram](https://telegram.org/). You have to get `accessToken` and a webhook `url` before running this bot.  
+For more information, check our [Telegram guides](https://bottender.js.org/docs/Platforms-Telegram).  
 
 ## Related examples
 
-* [telegram-builder](../telegram-builder)
-* [console-hello-world](../console-hello-world)
-* [messenger-hello-world](../messenger-hello-world)
-* [line-hello-world](../line-hello-world)
-* [slack-hello-world](../slack-hello-world)
+- [telegram-builder](../telegram-builder)
+- [console-hello-world](../console-hello-world)
+- [messenger-hello-world](../messenger-hello-world)
+- [line-hello-world](../line-hello-world)
+- [slack-hello-world](../slack-hello-world)
diff --git a/examples/todos/README.md b/examples/todos/README.md
index b9fa6444..55ea753b 100644
--- a/examples/todos/README.md
+++ b/examples/todos/README.md
@@ -13,13 +13,9 @@ npm run dev
 
 ## Idea of this example
 
-Building a todo app is the best way to understand new tools. This example shows
-what a To-Do console bot looks like. We suggest that you read our
-[session guide](https://bottender.js.org/docs/Guides-Session) first. That will
-help you come to a better understanding.
+Building a todo app is the best way to understand new tools. This example shows what a To-Do console bot looks like. We suggest that you read our [session guide](https://bottender.js.org/docs/Guides-Session) first. That will help you come to a better understanding.  
 
-To begin with, the bot sets a initial state like following code. Noticed that
-state must be an **`Object`**.
+To begin with, the bot sets a initial state like following code.  Noticed that state must be an **`Object`**.  
 
 ```js
 bot.setInitialState({
@@ -27,11 +23,9 @@ bot.setInitialState({
 });
 ```
 
-There are three features in this example: **Add todo**, **list all todos** and
-**clear all todos**.
+There are three features in this example: **Add todo**, **list all todos** and **clear all todos**.  
 
-First of all, just talk anything to the bot, and it will call `context.setState`
-to store your todo in session. This shows how to add a todo.
+First of all, just talk anything to the bot, and it will call `context.setState` to store your todo in session. This shows how to add a todo.  
 
 ```js
 const newTodos = context.event.text;
@@ -41,9 +35,7 @@ context.setState({
 await context.sendText(`Todo: ${newTodos} added!`);
 ```
 
-To show all todos, enter **/list**, then the bot will check whether
-`context.session` is empty. If it has any todos, the bot will send all of them
-to you.
+To show all todos, enter **/list**, then the bot will check whether `context.session` is empty. If it has any todos, the bot will send all of them to you.  
 
 ```js
 if (context.state.todos.length > 0) {
@@ -53,18 +45,16 @@ if (context.state.todos.length > 0) {
 }
 ```
 
-Finally, enter **/clear**, the bot will clear all todos by calling
-`context.resetState`. That is a very useful method to reset your state.
+Finally, enter **/clear**, the bot will clear all todos by calling `context.resetState`. That is a very useful method to reset your state.  
 
 ```js
 context.resetState();
 await context.sendText('Successfully clear all todos!');
 ```
 
-For more information about Bottender, please visit our
-[Docs Website](https://bottender.js.org/).
+For more information about Bottender, please visit our [Docs Website](https://bottender.js.org/).
 
 ## Related examples
 
-* [with-state](../with-state)
-* [console-hello-world](../console-hello-world)
+- [with-state](../with-state)
+- [console-hello-world](../console-hello-world)
diff --git a/examples/with-botimize/README.md b/examples/with-botimize/README.md
index b3231148..c39dbcf4 100644
--- a/examples/with-botimize/README.md
+++ b/examples/with-botimize/README.md
@@ -13,12 +13,9 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to combine your bot with **bot analytics tool**. In this
-case, we take [Botimize](https://www.getbotimize.com/) as an example.
+This example shows how to combine your bot with **bot analytics tool**. In this case, we take [Botimize](https://www.getbotimize.com/) as an example.  
 
-The following code is the main part of this example which demonstrates the way
-to connect your bot with [Botimize](https://www.getbotimize.com/). Furthermore,
-it will be better to check [with-express example](../with-express) first.
+The following code is the main part of this example which demonstrates the way to connect your bot with [Botimize](https://www.getbotimize.com/). Furthermore, You have better check [middleware guide](https://bottender.js.org/docs/Guides-Middleware) if you don't understand what is middleware.
 
 ```js
 const server = createServer(bot, {
@@ -30,8 +27,9 @@ const server = createServer(bot, {
 });
 ```
 
+
 ## Related examples
 
-* [with-chatbase](../with-chatbase)
-* [with-express](../with-express)
-* [messenger-hello-world](../messenger-hello-world)
+- [middleware](../middleware)
+- [with-express](../with-express)
+- [messenger-hello-world](../messenger-hello-world)
diff --git a/examples/with-botimize/botimizeMiddleware.js b/examples/with-botimize/botimizeMiddleware.js
index af05397b..1435dcad 100644
--- a/examples/with-botimize/botimizeMiddleware.js
+++ b/examples/with-botimize/botimizeMiddleware.js
@@ -5,7 +5,7 @@ module.exports = function botimizeMiddleware(bot, { apiKey, platform }) {
 
   const { client } = bot.connector;
   const accessToken = client._accessToken;
-  const axios = client.axios;
+  const axios = client.getHTTPClient();
 
   // Add a response interceptor
   axios.interceptors.response.use(response => {
diff --git a/examples/with-chatbase/README.md b/examples/with-chatbase/README.md
deleted file mode 100644
index 829c6b93..00000000
--- a/examples/with-chatbase/README.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# With Chatbase
-
-## Install and Run
-
-Download this example or clone [Bottender](https://github.com/Yoctol/bottender).
-
-```
-curl https://codeload.github.com/Yoctol/bottender/tar.gz/master | tar -xz --strip=2 bottender-master/examples/with-chatbase
-cd with-chatbase
-npm install
-npm run dev
-```
-
-## Idea of this example
-
-This example shows how to combine your bot with **bot analytics tool**. In this
-case, we take [Chatbase](https://chatbase.com/welcome) which is built by Google
-as an example.
-
-The following code is the main part of this example which demonstrates the way
-to connect your bot with [Chatbase](https://chatbase.com/welcome). Furthermore,
-it will be better to check [with-express example](../with-express) first.
-
-```js
-const server = createServer(bot, {
-  verifyToken: '__FILL_YOUR_VERIFY_TOKEN_HERE__',
-  webhookMiddleware: chatbaseMiddleware(bot, {
-    apiKey: '__FILL_YOUR_CHATBASE_KEY_HERE__',
-    platform: 'Facebook',
-  }),
-});
-```
-
-## Related examples
-
-* [with-botimize](../with-botimize)
-* [with-express](../with-express)
-* [messenger-hello-world](../messenger-hello-world)
diff --git a/examples/with-chatbase/chatbaseMiddleware.js b/examples/with-chatbase/chatbaseMiddleware.js
deleted file mode 100644
index 96d2f114..00000000
--- a/examples/with-chatbase/chatbaseMiddleware.js
+++ /dev/null
@@ -1,44 +0,0 @@
-const _chatbase = require('@google/chatbase');
-
-module.exports = function chatbaseMiddleware(bot, { apiKey, platform }) {
-  const chatbase = _chatbase.setApiKey(apiKey).setPlatform(platform);
-
-  const { client } = bot.connector;
-  const axios = client.axios;
-
-  // Add a response interceptor
-  axios.interceptors.response.use(response => {
-    const { config } = response;
-    if (/graph\.facebook\.com.*\/me\/messages/.test(config.url)) {
-      const { recipient: { id }, message: { text } } = JSON.parse(config.data);
-
-      chatbase
-        .newMessage()
-        .setAsTypeAgent()
-        .setUserId(id)
-        .setTimestamp(Date.now().toString())
-        .setMessage(text)
-        .send()
-        .catch(e => console.error(e));
-    }
-    return response;
-  });
-
-  return (req, res, next) => {
-    const {
-      sender: { id },
-      message: { text },
-    } = req.body.entry[0].messaging[0];
-
-    chatbase
-      .newMessage()
-      .setAsTypeUser()
-      .setUserId(id)
-      .setTimestamp(Date.now().toString())
-      .setMessage(text)
-      .send()
-      .catch(e => console.error(e));
-
-    next();
-  };
-};
diff --git a/examples/with-chatbase/index.js b/examples/with-chatbase/index.js
deleted file mode 100644
index d38e9529..00000000
--- a/examples/with-chatbase/index.js
+++ /dev/null
@@ -1,25 +0,0 @@
-const { MessengerBot } = require('bottender');
-const { createServer } = require('bottender/express');
-
-const chatbaseMiddleware = require('./chatbaseMiddleware');
-
-const bot = new MessengerBot({
-  accessToken: '__FILL_YOUR_TOKEN_HERE__',
-  appSecret: '__FILL_YOUR_SECRET_HERE__',
-});
-
-bot.onEvent(async context => {
-  await context.sendText('Hello World');
-});
-
-const server = createServer(bot, {
-  verifyToken: '__FILL_YOUR_VERIFY_TOKEN_HERE__',
-  webhookMiddleware: chatbaseMiddleware(bot, {
-    apiKey: '__FILL_YOUR_CHATBASE_KEY_HERE__',
-    platform: 'Facebook',
-  }),
-});
-
-server.listen(5000, () => {
-  console.log('server is running on 5000 port...');
-});
diff --git a/examples/with-chatbase/package.json b/examples/with-chatbase/package.json
deleted file mode 100644
index a442d55d..00000000
--- a/examples/with-chatbase/package.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "dependencies": {
-    "@google/chatbase": "^1.0.0",
-    "bottender": "latest"
-  },
-  "devDependencies": {
-    "nodemon": "^1.11.0"
-  },
-  "scripts": {
-    "dev": "nodemon index.js",
-    "start": "node index.js"
-  }
-}
diff --git a/examples/with-config/README.md b/examples/with-config/README.md
index 670898bb..33a14d64 100644
--- a/examples/with-config/README.md
+++ b/examples/with-config/README.md
@@ -13,14 +13,11 @@ npm run dev
 
 ## Idea of this example
 
-It is just way too much trouble when you handle lots of platform configs
-separately. Therefore, we recommend you putting them all together into
-[bottender.confg.js](./bottender.config.js) and use it as a parameter of
-`createServer`.
+It is just way too much trouble when you handle lots of platform configs separately. Therefore, we recommend you putting them all together into [bottender.confg.js](./bottender.config.js) and use it as a parameter of `createServer`.
 
 ## Related examples
 
-* [cross-platform](../cross-platform)
-* [with-express](../with-express)
-* [messenger-hello-world](../messenger-hello-world)
-* [messenger-builder](../messenger-builder)
+- [cross-platform](../cross-platform)
+- [with-express](../with-express)
+- [messenger-hello-world](../messenger-hello-world)
+- [messenger-builder](../messenger-builder)
diff --git a/examples/with-dotenv/README.md b/examples/with-dotenv/README.md
index 4ba5a6de..5215f1e0 100644
--- a/examples/with-dotenv/README.md
+++ b/examples/with-dotenv/README.md
@@ -14,11 +14,9 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to use [dotenv](https://github.com/motdotla/dotenv)
-package to prevent from committing sensitive information into source control
-system. You have to get `accessToken` and `appSecret` into `.env` file before
-running this bot.
+This example shows how to use [dotenv](https://github.com/motdotla/dotenv) package to prevent from committing sensitive information into source control system.
+You have to get `accessToken` and `appSecret` into `.env` file before running this bot.  
 
 ## Related examples
 
-* [messenger-hello-world](../messenger-hello-world)
+- [messenger-hello-world](../messenger-hello-world)
diff --git a/examples/with-express/README.md b/examples/with-express/README.md
index a7bba599..8230642f 100644
--- a/examples/with-express/README.md
+++ b/examples/with-express/README.md
@@ -13,13 +13,12 @@ npm run dev
 
 ## Idea of this example
 
-This is a simple example of creating an
-[express](https://github.com/expressjs/express) server as your bot's server.\
+This is a simple example of creating an [express](https://github.com/expressjs/express) server as your bot's server.  
 Check our [server guide](https://bottender.js.org/docs/Guides-Server) for more information.
 
 ## Related examples
 
-* [with-koa](../with-koa)
-* [with-micro](../with-micro)
-* [with-restify](../with-restify)
-* [messenger-hello-world](../messenger-hello-world)
+- [with-koa](../with-koa)
+- [with-micro](../with-micro)
+- [with-restify](../with-restify)
+- [messenger-hello-world](../messenger-hello-world)
diff --git a/examples/with-file-session/README.md b/examples/with-file-session/README.md
index 4a46d5b7..e8db7fc0 100644
--- a/examples/with-file-session/README.md
+++ b/examples/with-file-session/README.md
@@ -13,12 +13,11 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to use Session to store some information from users and
-store sessions in your local file. For more information, check our
-[session guide](https://bottender.js.org/docs/Guides-Session).
+This example shows how to use Session to store some information from users and store sessions in your local file.
+For more information, check our [session guide](https://bottender.js.org/docs/Guides-Session).
 
 ## Related examples
 
-* [with-memory-session](../with-memory-session)
-* [with-mongo-session](../with-mongo-session)
-* [with-redis-session](../with-redis-session)
+- [with-memory-session](../with-memory-session)
+- [with-mongo-session](../with-mongo-session)
+- [with-redis-session](../with-redis-session)
diff --git a/examples/with-koa/README.md b/examples/with-koa/README.md
index aedc8411..29cd53ea 100644
--- a/examples/with-koa/README.md
+++ b/examples/with-koa/README.md
@@ -13,13 +13,12 @@ npm run dev
 
 ## Idea of this example
 
-This is a simple example of creating an [koa](https://github.com/koajs/koa)
-server as your bot's server.\
+This is a simple example of creating an [koa](https://github.com/koajs/koa) server as your bot's server.  
 Check our [server guide](https://bottender.js.org/docs/Guides-Server) for more information.
 
 ## Related examples
 
-* [with-express](../with-express)
-* [with-micro](../with-micro)
-* [with-restify](../with-restify)
-* [messenger-hello-world](../messenger-hello-world)
+- [with-express](../with-express)
+- [with-micro](../with-micro)
+- [with-restify](../with-restify)
+- [messenger-hello-world](../messenger-hello-world)
diff --git a/examples/with-luis.ai/README.md b/examples/with-luis.ai/README.md
index 50b5665f..e7723904 100644
--- a/examples/with-luis.ai/README.md
+++ b/examples/with-luis.ai/README.md
@@ -13,11 +13,9 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to combine your bot with **semantic analysis tool**. In
-this case, we take [LUIS.ai](https://www.luis.ai/) as an example. Before you run
-this example, make sure you have got `appId` and `appKey` from
-[LUIS.ai](https://www.luis.ai/).
+This example shows how to combine your bot with **semantic analysis tool**. In this case, we take [LUIS.ai](https://www.luis.ai/) as an example. Before you run this example, make sure you have got `appId` and `appKey` from [LUIS.ai](https://www.luis.ai/).  
+
 
 ## Related examples
 
-* [console-hello-world](../console-hello-world)
+- [console-hello-world](../console-hello-world)
diff --git a/examples/with-memory-session/README.md b/examples/with-memory-session/README.md
index 8f960966..a860ca22 100644
--- a/examples/with-memory-session/README.md
+++ b/examples/with-memory-session/README.md
@@ -13,12 +13,11 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to use Session to store some information from users. For
-more information, check our
-[session guide](https://bottender.js.org/docs/Guides-Session).
+This example shows how to use Session to store some information from users.
+For more information, check our [session guide](https://bottender.js.org/docs/Guides-Session).
 
 ## Related examples
 
-* [with-file-session](../with-file-session)
-* [with-mongo-session](../with-mongo-session)
-* [with-redis-session](../with-redis-session)
+- [with-file-session](../with-file-session)
+- [with-mongo-session](../with-mongo-session)
+- [with-redis-session](../with-redis-session)
diff --git a/examples/with-micro/README.md b/examples/with-micro/README.md
index 64252f9c..b92a96b3 100644
--- a/examples/with-micro/README.md
+++ b/examples/with-micro/README.md
@@ -13,13 +13,12 @@ npm run dev
 
 ## Idea of this example
 
-This is a simple example of creating an [micro](https://github.com/zeit/micro)
-server as your bot's server.\
+This is a simple example of creating an [micro](https://github.com/zeit/micro) server as your bot's server.  
 Check our [server guide](https://bottender.js.org/docs/Guides-Server) for more information.
 
 ## Related examples
 
-* [with-express](../with-express)
-* [with-koa](../with-koa)
-* [with-restify](../with-restify)
-* [messenger-hello-world](../messenger-hello-world)
+- [with-express](../with-express)
+- [with-koa](../with-koa)
+- [with-restify](../with-restify)
+- [messenger-hello-world](../messenger-hello-world)
diff --git a/examples/with-mongo-session/README.md b/examples/with-mongo-session/README.md
index db0a7022..f6fd4b1b 100644
--- a/examples/with-mongo-session/README.md
+++ b/examples/with-mongo-session/README.md
@@ -13,12 +13,11 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to use Session to store some information from users and
-store sessions in your MongoDB. For more information, check our
-[session guide](https://bottender.js.org/docs/Guides-Session).
+This example shows how to use Session to store some information from users and store sessions in your MongoDB.
+For more information, check our [session guide](https://bottender.js.org/docs/Guides-Session).
 
 ## Related examples
 
-* [with-memory-session](../with-memory-session)
-* [with-file-session](../with-file-session)
-* [with-redis-session](../with-redis-session)
+- [with-memory-session](../with-memory-session)
+- [with-file-session](../with-file-session)
+- [with-redis-session](../with-redis-session)
diff --git a/examples/with-nextjs-webview/README.md b/examples/with-nextjs-webview/README.md
index cc6741cd..80d11173 100644
--- a/examples/with-nextjs-webview/README.md
+++ b/examples/with-nextjs-webview/README.md
@@ -13,13 +13,13 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to use next.js to serve static webview in your bot. Due
-to the next.js file system routing, you can access static page `/hello`. For
-more information, check [next.js official docs](https://zeit.co/blog/next4).
+This example shows how to use next.js to serve static webview in your bot.
+Due to the next.js file system routing, you can access static page `/hello`.
+For more information, check [next.js official docs](https://zeit.co/blog/next4).
 
 ## Related examples
 
-* [with-express](../with-express)
-* [with-koa](../with-koa)
-* [with-micro](../with-micro)
-* [with-restify](../with-restify)
+- [with-express](../with-express)
+- [with-koa](../with-koa)
+- [with-micro](../with-micro)
+- [with-restify](../with-restify)
diff --git a/examples/with-ngrok/README.md b/examples/with-ngrok/README.md
index 08ec5f0c..7fc9f0ec 100644
--- a/examples/with-ngrok/README.md
+++ b/examples/with-ngrok/README.md
@@ -13,17 +13,11 @@ npm run dev
 
 ## Idea of this example
 
-When you are developing bot, it is very convenient to expose your local server
-and get a webhook url by using
-[ngrok](https://github.com/inconshreveable/ngrok). This example shows that you
-just add an optional object `{ ngrok: true }` to `createServer`. Then Bottender
-will start using [ngrok](https://github.com/inconshreveable/ngrok) to serve your
-server and log webhook url on terminal automatically. By this way, you no longer
-to worry about finding a public url for your server.
+When you are developing bot, it is very convenient to expose your local server and get a webhook url by using [ngrok](https://github.com/inconshreveable/ngrok). This example shows that you just add an optional object ```{ ngrok: true }``` to `createServer`. Then Bottender will start using [ngrok](https://github.com/inconshreveable/ngrok) to serve your server and log webhook url on terminal automatically. By this way, you no longer to worry about finding a public url for your server.
 
 ## Related examples
 
-* [with-express](../with-express)
-* [with-koa](../with-koa)
-* [with-restify](../with-restify)
-* [messenger-hello-world](../messenger-hello-world)
+- [with-express](../with-express)
+- [with-koa](../with-koa)
+- [with-restify](../with-restify)
+- [messenger-hello-world](../messenger-hello-world)
diff --git a/examples/with-now-deployment/README.md b/examples/with-now-deployment/README.md
index f43a8642..79a0a0d0 100644
--- a/examples/with-now-deployment/README.md
+++ b/examples/with-now-deployment/README.md
@@ -37,9 +37,8 @@ now -e ACCESS_TOKEN=@access-token -e APP_SECRET=@app-secret -e VERIFY_TOKEN=@ver
 
 ## Idea of this example
 
-This example is a simple bot show how to deploy with
-[zeit/now](https://zeit.co/now).
+This example is a simple bot show how to deploy with [zeit/now](https://zeit.co/now).
 
 ## Related examples
 
-* [messenger-hello-world](../messenger-hello-world)
+- [messenger-hello-world](../messenger-hello-world)
diff --git a/examples/with-ramda/README.md b/examples/with-ramda/README.md
index a6734fe1..64c05aca 100644
--- a/examples/with-ramda/README.md
+++ b/examples/with-ramda/README.md
@@ -13,9 +13,8 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to write a simple bot without imperative conditional
-statements using Ramda.js.
+This example shows how to write a simple bot without imperative conditional statements using Ramda.js.
 
 ## Related examples
 
-* [console-hello-world](../console-hello-world)
+- [console-hello-world](../console-hello-world)
diff --git a/examples/with-redis-session/README.md b/examples/with-redis-session/README.md
index 767f04c1..86d55b3b 100644
--- a/examples/with-redis-session/README.md
+++ b/examples/with-redis-session/README.md
@@ -13,12 +13,11 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to use Session to store some information from users and
-store sessions in your Redis server. For more information, check our
-[session guide](https://bottender.js.org/docs/Guides-Session).
+This example shows how to use Session to store some information from users and store sessions in your Redis server.
+For more information, check our [session guide](https://bottender.js.org/docs/Guides-Session).
 
 ## Related examples
 
-* [with-memory-session](../with-memory-session)
-* [with-file-session](../with-file-session)
-* [with-mongo-session](../with-mongo-session)
+- [with-memory-session](../with-memory-session)
+- [with-file-session](../with-file-session)
+- [with-mongo-session](../with-mongo-session)
diff --git a/examples/with-restify/README.md b/examples/with-restify/README.md
index 39048355..01e9af21 100644
--- a/examples/with-restify/README.md
+++ b/examples/with-restify/README.md
@@ -13,13 +13,12 @@ npm run dev
 
 ## Idea of this example
 
-This is a simple example of creating an
-[restify](https://github.com/restify/node-restify) server as your bot's server.\
+This is a simple example of creating an [restify](https://github.com/restify/node-restify) server as your bot's server.  
 Check our [server guide](https://bottender.js.org/docs/Guides-Server) for more information.
 
 ## Related examples
 
-* [with-express](../with-express)
-* [with-koa](../with-koa)
-* [with-micro](../with-micro)
-* [messenger-hello-world](../messenger-hello-world)
+- [with-express](../with-express)
+- [with-koa](../with-koa)
+- [with-micro](../with-micro)
+- [messenger-hello-world](../messenger-hello-world)
diff --git a/examples/with-state/README.md b/examples/with-state/README.md
index d4beedf2..03098626 100644
--- a/examples/with-state/README.md
+++ b/examples/with-state/README.md
@@ -13,13 +13,12 @@ npm run dev
 
 ## Idea of this example
 
-This example shows how to use Session State to store some information from
-users. For more information, check our
-[session guide](https://bottender.js.org/docs/Guides-Session).
+This example shows how to use Session State to store some information from users.
+For more information, check our [session guide](https://bottender.js.org/docs/Guides-Session).
 
 ## Related examples
 
-* [with-memory-session](../with-memory-session)
-* [with-file-session](../with-file-session)
-* [with-mongo-session](../with-mongo-session)
-* [with-redis-session](../with-redis-session)
+- [with-memory-session](../with-memory-session)
+- [with-file-session](../with-file-session)
+- [with-mongo-session](../with-mongo-session)
+- [with-redis-session](../with-redis-session)
diff --git a/examples/with-typing/README.md b/examples/with-typing/README.md
index bfeb523e..edb757c4 100644
--- a/examples/with-typing/README.md
+++ b/examples/with-typing/README.md
@@ -13,13 +13,10 @@ npm run dev
 
 ## Idea of this example
 
-This example shows using `withTyping` to make your bot user experience better.
-It will show typing indicator and delay all your send request for seconds. For
-example, this is
-[Messenger sender action](https://developers.facebook.com/docs/messenger-platform/send-messages/sender-actions/).
+This example shows using `withTyping` to make your bot user experience better. It will show typing indicator and delay all your send request for seconds. For example, this is [Messenger sender action](https://developers.facebook.com/docs/messenger-platform/send-messages/sender-actions/).
 Notice that the unit of optional number of delay seconds is **millisecond**.
 
 ## Related examples
 
-* [with-express](../with-express)
-* [messenger-hello-world](../messenger-hello-world)
+- [with-express](../with-express)
+- [messenger-hello-world](../messenger-hello-world)
diff --git a/package.json b/package.json
index ba3a7940..32105018 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,5 @@
 {
   "name": "bottender",
-  "description": "Make Bots in Your Way, Fast and Flexibly",
   "version": "0.14.4",
   "license": "MIT",
   "main": "lib/index.js",
diff --git a/src/cli/shared/getConfig.js b/src/cli/shared/getConfig.js
index 2bee1041..d4b70f8a 100644
--- a/src/cli/shared/getConfig.js
+++ b/src/cli/shared/getConfig.js
@@ -9,7 +9,7 @@ import schema from './schema';
 
 const getConfig = (configPath, platform) => {
   const config = importFresh(path.resolve(configPath));
-  const validateResult = Joi.validate(config, schema);
+  const validateResult = Joi.validate(config, schema, { allowUnknown: true });
   invariant(
     !validateResult.error,
     validateResult.error &&
diff --git a/src/cli/shared/schema.js b/src/cli/shared/schema.js
index d457eff3..4dcc6723 100644
--- a/src/cli/shared/schema.js
+++ b/src/cli/shared/schema.js
@@ -1,5 +1,33 @@
 import Joi from 'joi';
 
+const subMenuItemSchema = Joi.object().keys({
+  type: Joi.string(),
+  title: Joi.string(),
+  url: Joi.string().when('type', {
+    is: 'web_url',
+    then: Joi.string().required(),
+  }),
+  payload: Joi.string().when('type', {
+    is: 'postback',
+    then: Joi.string().required(),
+  }),
+  webview_height_ratio: Joi.string(),
+  messenger_extensions: Joi.boolean(),
+  fallback_url: Joi.string(),
+  webview_share_button: Joi.string(),
+});
+
+const menuItemSchema = Joi.object()
+  .keys({
+    call_to_actions: Joi.array()
+      .items()
+      .when('type', {
+        is: 'nested',
+        then: Joi.array().items(subMenuItemSchema),
+      }),
+  })
+  .concat(subMenuItemSchema);
+
 const schema = Joi.object().keys({
   messenger: Joi.object().keys({
     accessToken: Joi.string().required(),
@@ -15,18 +43,14 @@ const schema = Joi.object().keys({
         Joi.object().keys({
           locale: Joi.string(),
           composer_input_disabled: Joi.boolean(),
-          call_to_actions: Joi.array().items(
-            Joi.object().keys({
-              type: Joi.string(),
-              title: Joi.string(),
-              payload: Joi.string(),
+          call_to_actions: Joi.array()
+            .items(menuItemSchema)
+            .when('composer_input_disabled', {
+              is: true,
+              then: Joi.array()
+                .items(menuItemSchema)
+                .required(),
             }),
-            Joi.object().keys({
-              type: Joi.string(),
-              title: Joi.string(),
-              url: Joi.string(),
-            })
-          ),
         })
       ),
       greeting: Joi.array().items(


Test patch:
diff --git a/examples/with-unit-tests/README.md b/examples/with-unit-tests/README.md
index abd5588c..109347c4 100644
--- a/examples/with-unit-tests/README.md
+++ b/examples/with-unit-tests/README.md
@@ -1,5 +1,4 @@
 # With Unit Tests
-
 ## Install and Run
 
 Download this example or clone [Bottender](https://github.com/Yoctol/bottender).
@@ -13,11 +12,9 @@ npm run dev
 
 ## Idea of this example
 
-Unit test is very important for every matured products, and so does your bot.
-This example shows how to use Bottender testing tools in an easy way.\
-Check our [testing guide](https://bottender.js.org/docs/Guides-Testing) for more
-information.
+Unit test is very important for every matured products, and so does your bot. This example shows how to use Bottender testing tools in an easy way.  
+Check our [testing guide](https://bottender.js.org/docs/Guides-Testing) for more information.
 
 ## Related examples
 
-* [console-hello-world](../console-hello-world)
+- [console-hello-world](../console-hello-world)
diff --git a/src/cli/shared/__tests__/schema.spec.js b/src/cli/shared/__tests__/schema.spec.js
index 75d6fdd6..46642233 100644
--- a/src/cli/shared/__tests__/schema.spec.js
+++ b/src/cli/shared/__tests__/schema.spec.js
@@ -26,6 +26,85 @@ const config = {
               type: 'web_url',
               title: '__TITLE_HERE__',
               url: 'http://example.com',
+              webview_height_ratio: 'tall',
+            },
+          ],
+        },
+      ],
+      greeting: [
+        {
+          locale: 'default',
+          text: 'Hello!',
+        },
+      ],
+      whitelisted_domains: ['http://example.com'],
+    },
+  },
+  line: {
+    channelSecret: '__PUT_YOUR_CHANNEL_SECRET_HERE__',
+    accessToken: '__PUT_YOUR_ACCESS_TOKEN_HERE__',
+  },
+  telegram: {
+    accessToken: '__PUT_YOUR_ACCESS_TOKEN_HERE__',
+  },
+  slack: {
+    accessToken: '__PUT_YOUR_ACCESS_TOKEN_HERE__',
+  },
+  ngrok: true,
+};
+
+const nestedConfig = {
+  messenger: {
+    accessToken: '__PUT_YOUR_ACCESS_TOKEN_HERE__',
+    verifyToken: '__PUT_YOUR_VERITY_TOKEN_HERE__',
+    appId: '__PUT_YOUR_APP_ID_HERE__',
+    appSecret: '__PUT_YOUR_APP_SECRET_HERE__',
+    profile: {
+      get_started: {
+        payload: 'GET_STARTED',
+      },
+      persistent_menu: [
+        {
+          locale: 'default',
+          composer_input_disabled: true,
+          call_to_actions: [
+            {
+              title: 'My Account',
+              type: 'nested',
+              call_to_actions: [
+                {
+                  title: 'Pay Bill',
+                  type: 'postback',
+                  payload: 'PAYBILL_PAYLOAD',
+                },
+                {
+                  title: 'History',
+                  type: 'postback',
+                  payload: 'HISTORY_PAYLOAD',
+                },
+                {
+                  title: 'Contact Info',
+                  type: 'postback',
+                  payload: 'CONTACT_INFO_PAYLOAD',
+                },
+              ],
+            },
+            {
+              type: 'web_url',
+              title: 'Latest News',
+              url: 'http://petershats.parseapp.com/hat-news',
+              webview_height_ratio: 'full',
+            },
+          ],
+        },
+        {
+          locale: 'zh_CN',
+          composer_input_disabled: false,
+          call_to_actions: [
+            {
+              title: 'Pay Bill',
+              type: 'postback',
+              payload: 'PAYBILL_PAYLOAD',
             },
           ],
         },
@@ -56,3 +135,8 @@ it('should valid', () => {
   const validateResult = Joi.validate(config, schema);
   expect(validateResult.error).toBeNull();
 });
+
+it('should valid nested config', () => {
+  const validateResult = Joi.validate(nestedConfig, schema);
+  expect(validateResult.error).toBeNull();
+});