Skip to content

Instantly share code, notes, and snippets.

@vwart
Created May 23, 2018 14:25
Show Gist options
  • Save vwart/faddaee279aab5127707862ec4994574 to your computer and use it in GitHub Desktop.
Save vwart/faddaee279aab5127707862ec4994574 to your computer and use it in GitHub Desktop.
Optional ChoicePrompts with Bot Framework
import {Library, IPromptChoiceResult, Prompts, ListStyle, IDialogResult, IntentRecognizer, IRecognizeContext, IIntentRecognizerResult} from "botbuilder";
// how to use the extended prompt:
const lib = new Library("ending");
lib
.dialog("example", [
(session, args) => {
session.sendTyping();
const choiceOpt: CustomChoiceOptions = {
listStyle: ListStyle.button,
optional: true // <- this line makes the difference. Per default Choice Prompts will be unchanged
}
Prompts.choice(this, prompt, options, choiceOpt);
},
async (session, result: IPromptChoiceResult) => {
switch (result.response!.index) {
case 0: {
session.sendTyping();
session.endDialog("Ok, bye");
break;
}
case 1: {
session.sendTyping();
session.beginDialog("askForContact");
break;
}
case -1: // <-- this is the new case. User did ignore the choices and entered some text
default: {
const userInput = result.response.entity;
// do something with the userInput
break;
}
}
}
]);
import { Library, Session, IPromptChoiceResult } from "botbuilder";
const lib = new Library("global");
// whenever a choice prompt cannot match the user's input to a choice, it will trigger this "dialog".
// The dialog will then do nothing, but sending fake PromptchoiceResult to the next waterfall step.
// The following step can identify the scenario due to the index being -1. The user input is passed as "entity"
lib.dialog("optional-choice-dispatcher", (session: Session, args: any) => {
}).triggerAction({
matches: "optional-choice-dispatcher",
onSelectAction: (session, args, next) => {
if (args && args.intent && args.intent.entities && args.intent.entities.length > 0
&& args.intent.entities[0] && args.intent.entities[0].entity) {
const choiceResult: IPromptChoiceResult = {
response: {
entity: args.intent.entities[0].entity,
index: -1,
score: 1
}
}
session.endDialogWithResult(choiceResult);
} else {
session.endDialog();
}
}
});
export default lib;
export class OptionalPromptRecognizer extends IntentRecognizer {
onRecognize(context: IRecognizeContext, callback: (err: Error, result: IIntentRecognizerResult) => void): void {
// this recognizer does nothing, until it sees a dialogData with "optional" flag
if (context.dialogData && context.dialogData.options && context.dialogData.options.optional) {
const dialogStack = context.dialogStack();
// be sure to only intervene when a Choice Prompt is at the top of the stack
if (dialogStack && dialogStack.length > 0 && dialogStack[dialogStack.length - 1].id === "BotBuilder:prompt-choice") {
callback(null, {
intent: "optional-choice-dispatcher", // the intent that will proceed waterfall
score: 0.1337, // a low score, so that other recognizers may overried. E.g. LUISRecognizer. But larger than 0.1 is important
entities: [{ type: "string", entity: context.message.text}]
});
return;
}
}
callback(null, { intent: "", score: 0 }); // no intent identified
}
}
export interface CustomChoiceOptions extends IPromptChoiceOptions {
optional: boolean
}
import global from "./dialogs/global";
//[...]
const bot = new UniversalBot(connector, defaultDialog);
// register dispatcher dialog
bot.library(global.clone());
// enable Choice Prompts to be optional
bot.recognizer(new OptionalPromptRecognizer());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment