Skip to content

Commit

Permalink
feat: Catch dialog and view allow the bot to gracefully handle errors.
Browse files Browse the repository at this point in the history
  • Loading branch information
yangeorget committed Jul 17, 2018
1 parent c2706c3 commit 792a597
Show file tree
Hide file tree
Showing 23 changed files with 325 additions and 122 deletions.
16 changes: 16 additions & 0 deletions fixtures/api.botfuel.io-443/153183964475760443
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
GET /nlp/entity-extraction?sentence=error&dimensions=city&dimensions=location&timezone=CET
host: api.botfuel.io
accept: application/json

HTTP/1.1 200 OK
server: openresty/1.11.2.5
date: Tue, 17 Jul 2018 15:00:44 GMT
content-type: application/json; charset=utf-8
transfer-encoding: chunked
connection: close
access-control-allow-methods: GET, OPTIONS
access-control-allow-origin: *
access-control-allow-headers: content-type, origin, accept, Botfuel-Bot-Locale
via: 1.1 vegur

[]
16 changes: 16 additions & 0 deletions fixtures/api.botfuel.io-443/153183964514224507
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
GET /nlp/entity-extraction?sentence=error&dimensions=color
host: api.botfuel.io
accept: application/json

HTTP/1.1 200 OK
server: openresty/1.11.2.5
date: Tue, 17 Jul 2018 15:00:45 GMT
content-type: application/json; charset=utf-8
transfer-encoding: chunked
connection: close
access-control-allow-methods: GET, OPTIONS
access-control-allow-origin: *
access-control-allow-headers: content-type, origin, accept, Botfuel-Bot-Locale
via: 1.1 vegur

[]
16 changes: 16 additions & 0 deletions fixtures/api.botfuel.io-443/153183964535921151
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
GET /nlp/entity-extraction?sentence=error&dimensions=forename
host: api.botfuel.io
accept: application/json

HTTP/1.1 200 OK
server: openresty/1.11.2.5
date: Tue, 17 Jul 2018 15:00:45 GMT
content-type: application/json; charset=utf-8
transfer-encoding: chunked
connection: close
access-control-allow-methods: GET, OPTIONS
access-control-allow-origin: *
access-control-allow-headers: content-type, origin, accept, Botfuel-Bot-Locale
via: 1.1 vegur

[]
16 changes: 16 additions & 0 deletions fixtures/api.botfuel.io-443/153183964554089729
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
GET /nlp/entity-extraction?sentence=error&dimensions=number
host: api.botfuel.io
accept: application/json

HTTP/1.1 200 OK
server: openresty/1.11.2.5
date: Tue, 17 Jul 2018 15:00:45 GMT
content-type: application/json; charset=utf-8
transfer-encoding: chunked
connection: close
access-control-allow-methods: GET, OPTIONS
access-control-allow-origin: *
access-control-allow-headers: content-type, origin, accept, Botfuel-Bot-Locale
via: 1.1 vegur

[]
16 changes: 16 additions & 0 deletions fixtures/api.botfuel.io-443/153183964576531181
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
GET /nlp/entity-extraction?sentence=error&dimensions=time&timezone=CET
host: api.botfuel.io
accept: application/json

HTTP/1.1 200 OK
server: openresty/1.11.2.5
date: Tue, 17 Jul 2018 15:00:45 GMT
content-type: application/json; charset=utf-8
transfer-encoding: chunked
connection: close
access-control-allow-methods: GET, OPTIONS
access-control-allow-origin: *
access-control-allow-headers: content-type, origin, accept, Botfuel-Bot-Locale
via: 1.1 vegur

[]
14 changes: 14 additions & 0 deletions fixtures/api.botfuel.io-443/153183964607577078
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
GET /trainer/api/v0/classify?sentence=error
host: api.botfuel.io
accept: application/json

HTTP/1.1 200 OK
server: openresty/1.11.2.5
date: Tue, 17 Jul 2018 15:00:46 GMT
content-type: application/json
content-length: 111
connection: close
access-control-allow-origin: *
via: 1.1 vegur

[{"id":"5b4e00b3e4f4c8001203ac3a","label":"error","probability":1.0,"resolvePrompt":"error?","type":"Intent"}]
1 change: 0 additions & 1 deletion packages/botfuel-dialog/src/adapters/web-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ class WebAdapter extends Adapter {
try {
const res = await rp.post(requestOptions);
if (res.statusCode && res.statusCode !== 200) {
// not handled on messenger
logger.error('postResponse: KO', res);
} else {
logger.debug('postResponse: OK');
Expand Down
130 changes: 60 additions & 70 deletions packages/botfuel-dialog/src/bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class Bot {
nlu: Nlu;

constructor(config: RawConfig) {
logger.debug('constructor', config);
this.config = getConfiguration(config);
logger.debug('constructor', this.config);
checkCredentials(this.config);
Expand All @@ -76,113 +77,85 @@ class Bot {
* @private
*/
async init(): Promise<void> {
// Brain
logger.debug('init');
await this.brain.init();
// NLU
await this.nlu.init();
}

/**
* Handles errors. Adds a user friendly message to common errors.
*/
handleError(error: Error): void {
if (error instanceof AuthenticationError) {
logger.error('Botfuel API authentication failed!');
logger.error(
'Please check your app’s credentials and that its plan limits haven’t been reached on https://api.botfuel.io',
);
} else if (error instanceof ResolutionError) {
logger.error(`Could not resolve '${error.name}'`);
} else if (error instanceof DialogError) {
logger.error(`Could not execute dialog '${error.name}'`);
}
throw error;
}

/**
* Runs the bot.
*/
async run(): Promise<void> {
logger.debug('run');
try {
await this.init();
await this.adapter.run();
} catch (error) {
this.handleError(error);
}
await this.init();
await this.adapter.run();
}

/**
* Plays user messages (only available with the TestAdapter).
*/
async play(userMessages: UserMessage[]): Promise<void> {
logger.debug('play', userMessages);
try {
await this.init();
await this.adapter.play(userMessages);
} catch (error) {
this.handleError(error);
}
await this.init();
await this.adapter.play(userMessages);
}

/**
* Cleans the bot brain.
*/
async clean(): Promise<void> {
logger.debug('clean');
try {
await this.brain.init();
await this.brain.clean();
} catch (error) {
this.handleError(error);
}
await this.brain.init();
await this.brain.clean();
}

/**
* Handles a user message.
*/
async handleMessage(userMessage: UserMessage): Promise<BotMessageJson[]> {
let botMessages: BotMessageJson[] = [];

logger.debug('handleMessage', userMessage);

const contextIn = {
user: userMessage.user,
brain: this.brain,
userMessage,
config: this.config,
};

await this.middlewareManager.in(contextIn, async () => {
botMessages = await this.respond(userMessage);
});

const contextOut = {
user: userMessage.user,
brain: this.brain,
botMessages,
config: this.config,
userMessage,
};
await this.middlewareManager.out(contextOut, async () => {});

return botMessages;
try {
const contextIn = {
user: userMessage.user,
brain: this.brain,
userMessage,
config: this.config,
};
let botMessages: BotMessageJson[] = [];
await this.middlewareManager.in(contextIn, async () => {
logger.debug('handleMessage: responding');
botMessages = await this.respond(userMessage);
});
const contextOut = {
user: userMessage.user,
brain: this.brain,
botMessages,
config: this.config,
userMessage,
};
await this.middlewareManager.out(contextOut, async () => {});
return botMessages;
} catch (error) {
logger.debug('handleMessage: catching error', error);
return this.respondWhenError(userMessage, error);
}
}

/**
* Responds to the user.
*/
async respond(userMessage: UserMessage): Promise<BotMessageJson[]> {
logger.debug('respond', userMessage);

// TODO Replace Conditional with Polymorphism (Fowler)
switch (userMessage.type) {
case 'postback':
logger.debug('respond: postback', userMessage);
return this.respondWhenPostback(userMessage);
case 'image':
logger.debug('respond: image', userMessage);
return this.respondWhenImage(userMessage);
case 'text':
default:
logger.debug('respond: text', userMessage);
return this.respondWhenText(userMessage);
}
}
Expand All @@ -200,14 +173,12 @@ class Bot {
userMessage,
},
);

logger.debug('respondWhenText: classificationResults', classificationResults, messageEntities);
const botMessages = await this.dm.executeClassificationResults(
return this.dm.executeClassificationResults(
userMessage,
classificationResults,
messageEntities,
);
return botMessages;
}

/**
Expand All @@ -222,8 +193,7 @@ class Bot {
messageEntities: userMessage.payload.value.entities,
},
};
const botMessages = await this.dm.executeDialog(userMessage, dialog);
return botMessages;
return this.dm.executeDialog(userMessage, dialog);
}

/**
Expand All @@ -238,8 +208,28 @@ class Bot {
url: userMessage.payload.value,
},
};
const botMessages = await this.dm.executeDialog(userMessage, dialog);
return botMessages;
return this.dm.executeDialog(userMessage, dialog);
}

async respondWhenError(userMessage: UserMessage, error): Promise<BotMessageJson[]> {
logger.debug('respondWhenError', userMessage, error);
if (error instanceof AuthenticationError) {
logger.error('Botfuel API authentication failed!');
logger.error(
'Please check your app’s credentials and that its plan limits haven’t been reached on https://api.botfuel.io',
);
} else if (error instanceof ResolutionError) {
logger.error(`Could not resolve '${error.name}'`);
} else if (error instanceof DialogError) {
logger.error(`Could not execute dialog '${error.name}'`);
}
const catchDialog = {
name: 'catch',
data: {
error,
},
};
return this.dm.executeDialog(userMessage, catchDialog);
}
}

Expand Down
Loading

0 comments on commit 792a597

Please sign in to comment.