Skip to content

Commit c43137d

Browse files
feat: Update README, tests, dependencies (#179)
feat: Update README, tests, dependencies
2 parents 9ac00a3 + a0a8563 commit c43137d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+6333
-9849
lines changed

.eslintignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
lib/middleware/*.js
2-
lib/middleware/*.d.ts
1+
lib
2+
examples

.eslintrc.yml

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,25 @@
11
env:
22
es6: true
33
node: true
4-
mocha: true
5-
extends: 'plugin:@typescript-eslint/recommended'
4+
jest: true
5+
extends:
6+
- 'plugin:@typescript-eslint/recommended'
7+
- 'prettier/@typescript-eslint'
8+
- 'plugin:prettier/recommended'
9+
610
parser: '@typescript-eslint/parser'
711
parserOptions:
812
project: './tsconfig.json'
913
plugins:
1014
- '@typescript-eslint'
1115
rules:
12-
indent:
13-
- error
14-
- 2
15-
linebreak-style:
16-
- error
17-
- unix
18-
quotes:
19-
- error
20-
- single
21-
semi:
22-
- error
23-
- always
2416
no-console:
17+
- warn
18+
' @typescript-eslint/no-explicit-any':
19+
- warning
20+
'@typescript-eslint/camelcase':
2521
- off
26-
'@typescript-eslint/indent':
22+
prettier/prettier:
2723
- error
28-
- 2
29-
' @typescript-eslint/no-explicit-any':
30-
- warning
24+
- singleQuote: true
25+

.npmignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
examples
2-
test
2+
test
3+
src

.travis.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ cache:
55
- node_modules
66
script:
77
- npm run lint
8-
- npm test
8+
- npm test -- --coverage && codecov
9+
10+
before_deploy:
11+
- npm run build
912
deploy:
1013
- provider: script
1114
skip_cleanup: true

README.md

Lines changed: 81 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,31 @@
22

33
This middleware plugin for [Botkit](http://howdy.ai/botkit) allows developers to easily integrate a [Watson Assistant](https://www.ibm.com/watson/ai-assistant/) workspace with multiple social channels like Slack, Facebook, and Twilio. Customers can have simultaneous, independent conversations with a single workspace through different channels.
44

5+
<details>
6+
<summary>Table of Contents</summary>
7+
8+
* [Middleware Overview](#middleware-overview)
9+
* [Function Overview](#function-overview)
10+
* [Installation](#installation)
11+
* [Prerequisites](#prerequisites)
12+
+ [Acquire channel credentials](#acquire-channel-credentials)
13+
+ [Bot setup](#bot-setup)
14+
* [Features](#features)
15+
+ [Message filtering](#message-filtering)
16+
- [Using interpret function instead of registering middleware](#using-interpret-function-instead-of-registering-middleware)
17+
- [Using middleware wrapper](#using-middleware-wrapper)
18+
+ [Minimum Confidence](#minimum-confidence)
19+
- [Use it manually in your self-defined controller.hears() function(s)](#use-it-manually-in-your-self-defined-controllerhears-functions)
20+
- [Use the middleware's hear() function](#use-the-middlewares-hear-function)
21+
+ [Implementing app actions](#implementing-app-actions)
22+
+ [Using sendToWatson to update context](#using-sendtowatson-to-update-context)
23+
* [Implementing event handlers](#implementing-event-handlers)
24+
+ [Intent matching](#intent-matching)
25+
- [`before` and `after`](#before-and-after)
26+
- [Dynamic workspace](#dynamic-workspace)
27+
28+
</details>
29+
530
## Middleware Overview
631

732
* Automatically manages context in multi-turn conversations to keep track of where the user left off in the conversation.
@@ -25,20 +50,20 @@ This middleware plugin for [Botkit](http://howdy.ai/botkit) allows developers to
2550
## Installation
2651

2752
```sh
28-
$ npm install botkit-middleware-watson --save
53+
$ npm install botkit-middleware-watson
2954
```
3055

3156
## Prerequisites
3257

33-
1. Sign up for an [IBM Cloud account](https://console.bluemix.net/registration/).
58+
1. Sign up for an [IBM Cloud account](https://cloud.ibm.com/registration/).
3459
1. Create an instance of the Watson Assistant service and get your credentials:
35-
- Go to the [Watson Assistant](https://console.bluemix.net/catalog/services/conversation) page in the IBM Cloud Catalog.
60+
- Go to the [Watson Assistant](https://cloud.ibm.com/catalog/services/conversation) page in the IBM Cloud Catalog.
3661
- Log in to your IBM Cloud account.
3762
- Click **Create**.
3863
- Copy the `apikey` value, or copy the `username` and `password` values if your service instance doesn't provide an `apikey`.
3964
- Copy the `url` value.
4065

41-
1. Create a workspace using the Watson Assistant service and copy the `workspace_id`. If you don't know how to create a workspace follow the [Getting Started tutorial](https://console.bluemix.net/docs/services/conversation/getting-started.html).
66+
1. Create a workspace using the Watson Assistant service and copy the `workspace_id`. If you don't know how to create a workspace follow the [Getting Started tutorial](https://cloud.ibm.com/docs/services/conversation/getting-started.html).
4267

4368

4469
### Acquire channel credentials
@@ -56,7 +81,7 @@ Otherwise, follow [Botkit's instructions](https://botkit.ai/docs/provisioning/sl
5681
This section walks you through code snippets to set up your Slack bot. If you want, you can jump straight to the [full example](/examples/simple-bot).
5782

5883
In your app, add the following lines to create your Slack controller using Botkit:
59-
```typescript
84+
```js
6085
import { WatsonMiddleware } from 'botkit-middleware-watson';
6186
import Botkit = require('botkit');
6287
const { SlackAdapter } = require('botbuilder-adapter-slack');
@@ -78,7 +103,7 @@ Create the middleware object which you'll use to connect to the Watson Assistant
78103

79104
If your credentials are `username` and `password` use:
80105

81-
```typescript
106+
```js
82107
const watsonMiddleware = new WatsonMiddleware({
83108
username: YOUR_ASSISTANT_USERNAME,
84109
password: YOUR_ASSISTANT_PASSWORD,
@@ -91,7 +116,7 @@ const watsonMiddleware = new WatsonMiddleware({
91116

92117
If your credentials is `apikey` use:
93118

94-
```typescript
119+
```js
95120
const watsonMiddleware = new WatsonMiddleware({
96121
iam_apikey: YOUR_API_KEY,
97122
url: YOUR_ASSISTANT_URL,
@@ -102,12 +127,12 @@ const watsonMiddleware = new WatsonMiddleware({
102127
```
103128

104129
Tell your Slackbot to use the _watsonMiddleware_ for incoming messages:
105-
```typescript
130+
```js
106131
controller.middleware.receive.use(watsonMiddleware.receive.bind(watsonMiddleware));
107132
```
108133

109134
Finally, make your bot _listen_ to incoming messages and respond with Watson Assistant:
110-
```typescript
135+
```js
111136
controller.hears(['.*'], ['direct_message', 'direct_mention', 'mention'], async function(bot, message) {
112137
if (message.watsonError) {
113138
await bot.reply(message, "I'm sorry, but for technical reasons I can't respond to your message");
@@ -131,21 +156,20 @@ If you would like to make your bot to only respond to _direct messages_ using As
131156
#### Using interpret function instead of registering middleware
132157

133158
```js
134-
slackController.hears(['.*'], ['direct_message'], async function(bot, message) {
135-
middleware.interpret(bot, message, function() {
136-
if (message.watsonError) {
137-
bot.reply(message, "I'm sorry, but for technical reasons I can't respond to your message");
138-
} else {
139-
bot.reply(message, message.watsonData.output.text.join('\n'));
140-
}
141-
});
159+
slackController.hears(['.*'], ['direct_message'], async (bot, message) => {
160+
await middleware.interpret(bot, message)
161+
if (message.watsonError) {
162+
bot.reply(message, "I'm sorry, but for technical reasons I can't respond to your message");
163+
} else {
164+
bot.reply(message, message.watsonData.output.text.join('\n'));
165+
}
142166
});
143167
```
144168

145169
#### Using middleware wrapper
146170

147171
```js
148-
const receiveMiddleware = function (bot, message, next) {
172+
const receiveMiddleware = (bot, message, next) => {
149173
if (message.type === 'direct_message') {
150174
watsonMiddleware.receive(bot, message, next);
151175
} else {
@@ -164,7 +188,7 @@ To use the setup parameter `minimum_confidence`, you have multiple options:
164188

165189
For example:
166190
```js
167-
controller.hears(['.*'], ['direct_message', 'direct_mention', 'mention', 'message_received'], async function(bot, message) {
191+
controller.hears(['.*'], ['direct_message', 'direct_mention', 'mention', 'message_received'], async (bot, message) => {
168192
if (message.watsonError) {
169193
await bot.reply(message, "Sorry, there are technical problems."); // deal with watson error
170194
} else {
@@ -180,6 +204,7 @@ controller.hears(['.*'], ['direct_message', 'direct_mention', 'mention', 'messag
180204
```
181205

182206
#### Use the middleware's hear() function
207+
183208
You can find the default implementation of this function [here](https://github.com/watson-developer-cloud/botkit-middleware/blob/e29b002f2a004f6df57ddf240a3fdf8cb28f95d0/lib/middleware/index.js#L40). If you want, you can redefine this function in the same way that watsonMiddleware.before and watsonMiddleware.after can be redefined. Refer to the [Botkit Middleware documentation](https://botkit.ai/docs/core.html#controllerhears) for an example. Then, to use this function instead of Botkit's default pattern matcher (that does not use minimum_confidence), plug it in using:
184209
```js
185210
controller.changeEars(watsonMiddleware.hear)
@@ -189,7 +214,7 @@ Note: if you want your own `hear()` function to implement pattern matching like
189214

190215
### Implementing app actions
191216

192-
Watson Assistant side of app action is documented in [Developer Cloud](https://console.bluemix.net/docs/services/assistant/deploy-custom-app.html#deploy-custom-app)
217+
Watson Assistant side of app action is documented in [Developer Cloud](https://cloud.ibm.com/docs/services/assistant/deploy-custom-app.html#deploy-custom-app)
193218
A common scenario of processing actions is:
194219

195220
* Send message to user "Please wait while I ..."
@@ -198,21 +223,19 @@ A common scenario of processing actions is:
198223
* Send message to Watson with updated context
199224
* Send result message(s) to user.
200225

201-
### using sendToWatson to update context (possible since v1.5.0)
226+
### Using sendToWatson to update context
202227

203228
Using sendToWatson to update context simplifies the bot code compared to solution using updateContext below.
204229

205-
```typescript
206-
function checkBalance(context, callback) {
230+
```js
231+
const checkBalance = async (context) => {
207232
//do something real here
208233
const contextDelta = {
209234
validAccount: true,
210235
accountBalance: 95.33
211236
};
212-
callback(null, context);
213-
}
214-
215-
const checkBalanceAsync = Promise.promisify(checkBalance);
237+
return context;
238+
});
216239

217240
const processWatsonResponse = async (bot, message) => {
218241
if (message.watsonError) {
@@ -227,12 +250,11 @@ const processWatsonResponse = async (bot, message) => {
227250
newMessage.text = 'balance result';
228251

229252
try {
230-
const contextDelta = await checkBalanceAsync(message.watsonData.context);
231-
await watsonMiddleware.sendToWatsonAsync(bot, newMessage, contextDelta);
232-
}catch(error) {
253+
const contextDelta = await checkBalance(message.watsonData.context);
254+
await watsonMiddleware.sendToWatson(bot, newMessage, contextDelta);
255+
} catch(error) {
233256
newMessage.watsonError = error;
234257
}
235-
236258
return await processWatsonResponse(bot, newMessage);
237259
}
238260
}
@@ -245,34 +267,25 @@ controller.on('message_received', processWatsonResponse);
245267

246268
Events are messages having type different than `message`.
247269

248-
[Example](https://github.com/howdyai/botkit/blob/master/examples/facebook_bot.js) of handler:
270+
[Example](https://github.com/howdyai/botkit/blob/master/packages/docs/reference/facebook.md#facebookeventtypemiddleware) of handler:
271+
249272
```js
250-
controller.on('facebook_postback', function(bot, message) {
251-
bot.reply(message, 'Great Choice!!!! (' + message.payload + ')');
273+
controller.on('facebook_postback', async (bot, message) => {
274+
await bot.reply(message, `Great Choice. (${message.payload})`);
252275
});
253276
```
277+
254278
Since they usually have no text, events aren't processed by middleware and have no watsonData attribute.
255279
If event handler wants to make use of some data from context, it has to read it first.
256280
Example:
281+
257282
```js
258-
controller.on('facebook_postback', function(bot, message) {
259-
watsonMiddleware.readContext(message.user, function(err, context) {
260-
if (!context) {
261-
context = {};
262-
}
283+
controller.on('facebook_postback', async (bot, message) => {
284+
const context = watsonMiddleware.readContext(message.user);
263285
//do something useful here
264-
myFunction(context.field1, context.field2, function(err, result) {
265-
const newMessage = clone(message);
266-
newMessage.text = 'postback result';
267-
268-
watsonMiddleware.sendToWatson(bot, newMessage, {postbackResult: 'success'}, function(err) {
269-
if (err) {
270-
newMessage.watsonError = error;
271-
}
272-
processWatsonResponse(bot, newMessage);
273-
});
274-
});
275-
});
286+
const result = await myFunction(context.field1, context.field2);
287+
const newMessage = {...message, text: 'postback result' };
288+
await watsonMiddleware.sendToWatson(bot, newMessage, { postbackResult: 'success' });
276289
});
277290
```
278291

@@ -288,7 +301,7 @@ The `hear()` function can be used on individual handler functions, or can be use
288301

289302
Used on an individual handler:
290303

291-
```typescript
304+
```js
292305
slackController.hears(['hello'], ['direct_message', 'direct_mention', 'mention'], watsonMiddleware.hear, async function(bot, message) {
293306
await bot.reply(message, message.watsonData.output.text.join('\n'));
294307
// now do something special related to the hello intent
@@ -297,42 +310,42 @@ slackController.hears(['hello'], ['direct_message', 'direct_mention', 'mention']
297310

298311
Used globally:
299312

300-
```typescript
313+
```js
301314
slackController.changeEars(watsonMiddleware.hear.bind(watsonMiddleware));
302315

303-
slackController.hears(['hello'], ['direct_message', 'direct_mention', 'mention'], function(bot, message) {
304-
bot.reply(message, message.watsonData.output.text.join('\n'));
316+
slackController.hears(['hello'], ['direct_message', 'direct_mention', 'mention'], async (bot, message) => {
317+
await bot.reply(message, message.watsonData.output.text.join('\n'));
305318
// now do something special related to the hello intent
306319
});
307320
```
308321

309322
#### `before` and `after`
310323

311-
The _before_ and _after_ callbacks can be used to perform some tasks _before_ and _after_ Assistant is called. One may use it to modify the request/response payloads, execute business logic like accessing a database or making calls to external services.
324+
The _before_ and _after_ async calls can be used to perform some tasks _before_ and _after_ Assistant is called. One may use it to modify the request/response payloads, execute business logic like accessing a database or making calls to external services.
312325

313326
They can be customized as follows:
314327

315328
```js
316-
middleware.before = function(message, assistantPayload, callback) {
317-
// Code here gets executed before making the call to Assistant.
318-
callback(null, customizedPayload);
329+
middleware.before = (message, assistantPayload) => async () => {
330+
// Code here gets executed before making the call to Assistant.
331+
return assistantPayload;
319332
}
320333
```
321334

322335
```js
323-
middleware.after = function(message, assistantResponse, callback) {
324-
// Code here gets executed after the call to Assistant.
325-
callback(null, assistantResponse);
326-
}
336+
middleware.after = (message, assistantResponse) => async () => {
337+
// Code here gets executed after the call to Assistant.
338+
return assistantResponse;
339+
});
327340
```
328341

329342
#### Dynamic workspace
330343

331-
If you need to make use of multiple workspaces in a single bot, workspace_id can be changed dynamically by setting workspace_id property in context.
344+
If you need to make use of multiple workspaces in a single bot, `workspace_id` can be changed dynamically by setting `workspace_id` property in context.
332345

333-
Example of setting workspace_id to id provided as a property of hello message:
334-
```typescript
335-
async function handleHelloEvent(bot, message) {
346+
Example of setting `workspace_id` to id provided as a property of hello message:
347+
```js
348+
async handleHelloEvent = (bot, message) => {
336349
message.type = 'welcome';
337350
const contextDelta = {};
338351

@@ -341,7 +354,7 @@ async function handleHelloEvent(bot, message) {
341354
}
342355

343356
try {
344-
watsonMiddleware.sendToWatsonAsync(bot, message, contextDelta);
357+
await watsonMiddleware.sendToWatson(bot, message, contextDelta);
345358
} catch(error) {
346359
message.watsonError = error;
347360
}

0 commit comments

Comments
 (0)