From 6485fa791aca4547829c07e30de323b1f453ae91 Mon Sep 17 00:00:00 2001 From: rabidgremlin Date: Mon, 28 Aug 2017 13:07:58 +1200 Subject: [PATCH] Less goldfish (#5) * Added long term session attributes and functions. * Upgraded blade-ink to v0.4.3. * Added tests for long term session attributes. * Updated version. --- README.md | 16 ++-- gradle.properties | 2 +- .../mutters/core/session/Session.java | 55 ++++++++++- .../mutters/core/session/TestSession.java | 64 +++++++++++++ mutters-ink-bot/build.gradle | 2 +- .../rabidgremlin/mutters/bot/ink/InkBot.java | 6 ++ .../GetLongTermAttributeFunction.java | 96 +++++++++++++++++++ .../RemoveLongTermAttributeFunction.java | 73 ++++++++++++++ .../SetLongTermAttributeFunction.java | 69 +++++++++++++ mutters-ink-bot/src/test/ink/order/main.ink | 52 ++++++++++ .../src/test/ink/{ => taxi}/cancel_taxi.ink | 0 .../src/test/ink/{ => taxi}/confused.ink | 0 .../src/test/ink/{ => taxi}/globals.ink | 0 .../src/test/ink/{ => taxi}/intents.ink | 0 .../src/test/ink/{ => taxi}/main.ink | 0 .../src/test/ink/{ => taxi}/order_taxi.ink | 0 .../src/test/ink/{ => taxi}/where_taxi.ink | 0 .../ink/functions/TestLongTermFunctions.java | 80 ++++++++++++++++ .../ink/functions/orderbot/OrderInkBot.java | 12 +++ .../orderbot/OrderInkBotConfiguration.java | 72 ++++++++++++++ .../src/test/resources/orderbot.ink.json | 1 + .../src/test/resources/taxibot.ink.json | 2 +- 22 files changed, 588 insertions(+), 14 deletions(-) create mode 100644 mutters-core/src/test/java/com/rabidgremlin/mutters/core/session/TestSession.java create mode 100644 mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/functions/GetLongTermAttributeFunction.java create mode 100644 mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/functions/RemoveLongTermAttributeFunction.java create mode 100644 mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/functions/SetLongTermAttributeFunction.java create mode 100644 mutters-ink-bot/src/test/ink/order/main.ink rename mutters-ink-bot/src/test/ink/{ => taxi}/cancel_taxi.ink (100%) rename mutters-ink-bot/src/test/ink/{ => taxi}/confused.ink (100%) rename mutters-ink-bot/src/test/ink/{ => taxi}/globals.ink (100%) rename mutters-ink-bot/src/test/ink/{ => taxi}/intents.ink (100%) rename mutters-ink-bot/src/test/ink/{ => taxi}/main.ink (100%) rename mutters-ink-bot/src/test/ink/{ => taxi}/order_taxi.ink (100%) rename mutters-ink-bot/src/test/ink/{ => taxi}/where_taxi.ink (100%) create mode 100644 mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/functions/TestLongTermFunctions.java create mode 100644 mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/functions/orderbot/OrderInkBot.java create mode 100644 mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/functions/orderbot/OrderInkBotConfiguration.java create mode 100644 mutters-ink-bot/src/test/resources/orderbot.ink.json diff --git a/README.md b/README.md index 41aa21c..2dc0556 100644 --- a/README.md +++ b/README.md @@ -223,10 +223,10 @@ repositories { } dependencies { - compile 'com.rabidgremlin:mutters-ink-bot:4.0.1' - compile 'com.rabidgremlin:mutters-opennlp-intent:4.0.1' - compile 'com.rabidgremlin:mutters-opennlp-ner:4.0.1' - compile 'com.rabidgremlin:mutters-slots:4.0.1' + compile 'com.rabidgremlin:mutters-ink-bot:4.1.0' + compile 'com.rabidgremlin:mutters-opennlp-intent:4.1.0' + compile 'com.rabidgremlin:mutters-opennlp-ner:4.1.0' + compile 'com.rabidgremlin:mutters-slots:4.1.0' } ``` @@ -244,10 +244,10 @@ repositories { } dependencies { - compile 'com.rabidgremlin:mutters-ink-bot:4.0.1-SNAPSHOT' - compile 'com.rabidgremlin:mutters-opennlp-intent:4.0.1-SNAPSHOT' - compile 'com.rabidgremlin:mutters-opennlp-ner:4.0.1-SNAPSHOT' - compile 'com.rabidgremlin:mutters-slots:4.0.1-SNAPSHOT' + compile 'com.rabidgremlin:mutters-ink-bot:4.1.0-SNAPSHOT' + compile 'com.rabidgremlin:mutters-opennlp-intent:4.1.0-SNAPSHOT' + compile 'com.rabidgremlin:mutters-opennlp-ner:4.1.0-SNAPSHOT' + compile 'com.rabidgremlin:mutters-slots:4.1.0-SNAPSHOT' } ``` diff --git a/gradle.properties b/gradle.properties index 014bb7e..e05025f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=4.0.1 +version=4.1.0 # These are place holder values. Real values should be set in user's home gradle.properties # and are only needed when signing and uploading to central maven repo diff --git a/mutters-core/src/main/java/com/rabidgremlin/mutters/core/session/Session.java b/mutters-core/src/main/java/com/rabidgremlin/mutters/core/session/Session.java index feed9ea..31ccf75 100644 --- a/mutters-core/src/main/java/com/rabidgremlin/mutters/core/session/Session.java +++ b/mutters-core/src/main/java/com/rabidgremlin/mutters/core/session/Session.java @@ -4,7 +4,12 @@ import java.util.HashMap; /** - * This class represents a user's session with the bot. It maintains the any state needed by the bot. + * This class represents a user's session with the bot. It maintains the any state needed by the bot. The session holds + * two types of attributes. Normal attributes are typically stored whilst a conversation takes place. They are removed + * from the session when the reset() method is called which normally takes place when a conversation ends. + * + * Long term attributes are designed to hang around as long as the session. They are typically used to store data across + * conversations in the same session. * * @author rabidgremlin * @@ -15,6 +20,8 @@ public class Session /** Map of attributes for the session. */ private HashMap attributes = new HashMap(); + private HashMap longTermAttributes = new HashMap(); + /** * Get the specified attribute. * @@ -48,7 +55,7 @@ public void removeAttribute(String attributeName) } /** - * Resets the session. Removing all attributes. + * Resets the session. Removing all attributes. Note: Long term attributes are not removed from the session. * */ public void reset() @@ -56,6 +63,48 @@ public void reset() attributes = new HashMap(); } + /** + * Get the specified long term attribute. + * + * @param attributeName The name of the attribute. + * @return The attribute or null if it isn't in the map. + */ + public Object getLongTermAttribute(String attributeName) + { + return longTermAttributes.get(attributeName.toLowerCase()); + } + + /** + * Sets the value of the specified long term attribute. + * + * @param attributeName The name of the attribute. + * @param value The value of the attribute. + */ + public void setLongTermAttribute(String attributeName, Object value) + { + longTermAttributes.put(attributeName.toLowerCase(), value); + } + + /** + * Removes the specified long term attribute from the session. + * + * @param attributeName The name of the attribute. + */ + public void removeLongTermAttribute(String attributeName) + { + longTermAttributes.remove(attributeName.toLowerCase()); + } + + /** + * Resets the session. Removing all attributes (both normal and long term). + * + */ + public void resetAll() + { + attributes = new HashMap(); + longTermAttributes = new HashMap(); + } + /* * (non-Javadoc) * @@ -65,7 +114,7 @@ public void reset() @Override public String toString() { - return "Session [attributes=" + attributes + "]"; + return "Session [attributes=" + attributes + ", longTermAttributes=" + longTermAttributes + "]"; } } diff --git a/mutters-core/src/test/java/com/rabidgremlin/mutters/core/session/TestSession.java b/mutters-core/src/test/java/com/rabidgremlin/mutters/core/session/TestSession.java new file mode 100644 index 0000000..2ed4f0d --- /dev/null +++ b/mutters-core/src/test/java/com/rabidgremlin/mutters/core/session/TestSession.java @@ -0,0 +1,64 @@ +package com.rabidgremlin.mutters.core.session; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + + +public class TestSession +{ + @Test + public void testAttributeLifeCycle() + { + Session session = new Session(); + + session.setAttribute("bob", "alice"); + assertThat(session.getAttribute("bob"), is("alice")); + + session.removeAttribute("bob"); + assertThat(session.getAttribute("bob"), is(nullValue())); + } + + @Test + public void testLongTermAttributeLifeCycle() + { + Session session = new Session(); + + session.setLongTermAttribute("bobLT", "aliceLT"); + assertThat(session.getLongTermAttribute("bobLT"), is("aliceLT")); + + session.removeLongTermAttribute("bobLT"); + assertThat(session.getLongTermAttribute("bobLT"), is(nullValue())); + } + + @Test + public void testReset() + { + Session session = new Session(); + + session.setAttribute("bob", "alice"); + session.setLongTermAttribute("bobLT", "aliceLT"); + + session.reset(); + + assertThat(session.getLongTermAttribute("bob"), is(nullValue())); + assertThat(session.getLongTermAttribute("bobLT"), is("aliceLT")); + } + + @Test + public void testResetAll() + { + Session session = new Session(); + + session.setAttribute("bob", "alice"); + session.setLongTermAttribute("bobLT", "aliceLT"); + + session.resetAll(); + + assertThat(session.getLongTermAttribute("bob"), is(nullValue())); + assertThat(session.getLongTermAttribute("bobLT"), is(nullValue())); + } +} diff --git a/mutters-ink-bot/build.gradle b/mutters-ink-bot/build.gradle index b86e4e5..3008296 100644 --- a/mutters-ink-bot/build.gradle +++ b/mutters-ink-bot/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { compile project(':mutters-core') - compile 'com.bladecoder.ink:blade-ink:0.4.2' + compile 'com.bladecoder.ink:blade-ink:0.4.3' compile 'commons-io:commons-io:2.5' compile 'org.slf4j:slf4j-api:1.7.21' compile 'org.apache.commons:commons-lang3:3.4' diff --git a/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/InkBot.java b/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/InkBot.java index 8a32211..4d5f5e6 100644 --- a/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/InkBot.java +++ b/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/InkBot.java @@ -16,7 +16,10 @@ import com.rabidgremlin.mutters.bot.ink.InkBotConfiguration.GlobalIntent; import com.rabidgremlin.mutters.bot.ink.functions.AddAttachmentFunction; import com.rabidgremlin.mutters.bot.ink.functions.AddQuickReplyFunction; +import com.rabidgremlin.mutters.bot.ink.functions.GetLongTermAttributeFunction; +import com.rabidgremlin.mutters.bot.ink.functions.RemoveLongTermAttributeFunction; import com.rabidgremlin.mutters.bot.ink.functions.SetHintFunction; +import com.rabidgremlin.mutters.bot.ink.functions.SetLongTermAttributeFunction; import com.rabidgremlin.mutters.bot.ink.functions.SetRepromptFunction; import com.rabidgremlin.mutters.core.Context; import com.rabidgremlin.mutters.core.IntentMatch; @@ -99,6 +102,9 @@ public InkBot(T configuration) addFunction(new SetRepromptFunction()); addFunction(new AddAttachmentFunction()); addFunction(new AddQuickReplyFunction()); + addFunction(new SetLongTermAttributeFunction()); + addFunction(new GetLongTermAttributeFunction()); + addFunction(new RemoveLongTermAttributeFunction()); // add any other functions for the bot List functions = configuration.getInkFunctions(); diff --git a/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/functions/GetLongTermAttributeFunction.java b/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/functions/GetLongTermAttributeFunction.java new file mode 100644 index 0000000..a52f35a --- /dev/null +++ b/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/functions/GetLongTermAttributeFunction.java @@ -0,0 +1,96 @@ +package com.rabidgremlin.mutters.bot.ink.functions; + +import com.bladecoder.ink.runtime.Story; +import com.rabidgremlin.mutters.bot.ink.CurrentResponse; +import com.rabidgremlin.mutters.bot.ink.InkBotFunction; +import com.rabidgremlin.mutters.core.IntentMatch; +import com.rabidgremlin.mutters.core.session.Session; + +/** + * This function gets the value of a long term session attribute. These attributes are not removed at the end of a + * conversation so they can be used to share context between conversations in the same session. + * + * For example in your Ink script you could have: + * ``` + * VAR current_order = "" + * + * === check_order_status === + * ::GET_LONG_TERM_ATTR name::currentorder var::current_order + * { + * - current_order == "": + * -> get_order_number_for_status_check // this would prompt for order number and store in current_order then jump to check_order_status + * - else: + * -> check_order_status // retrieves and displays status for order number in current_order + * } + * -> END + * ``` + * + * Note: If there is no long term attribute with the specified name in the session then the specified Ink variable + * will be set to "". + * + * + * @author rabidgremlin + * + */ +public class GetLongTermAttributeFunction + implements InkBotFunction +{ + + /* + * (non-Javadoc) + * + * @see com.rabidgremlin.mutters.bot.ink.InkBotFunction#getFunctionName() + */ + @Override + public String getFunctionName() + { + return "GET_LONG_TERM_ATTR"; + } + + /* + * (non-Javadoc) + * + * @see com.rabidgremlin.mutters.bot.ink.InkBotFunction#respondexecute(CurrentResponse currentResponse, Session + * session, IntentMatch intentMatch, Story story, String param) + */ + @Override + public void execute(CurrentResponse currentResponse, Session session, IntentMatch intentMatch, Story story, String param) + { + FunctionDetails details = FunctionHelper.parseFunctionString(param); + + if (details.getFunctionParams() == null) + { + throw new IllegalArgumentException("Missing name and variable value for GET_LONG_TERM_ATTR"); + } + + String name = details.getFunctionParams().get("name"); + if (name == null) + { + throw new IllegalArgumentException("Missing name value for GET_LONG_TERM_ATTR"); + } + + String var = details.getFunctionParams().get("var"); + if (var == null) + { + throw new IllegalArgumentException("Missing var value for GET_LONG_TERM_ATTR"); + } + + try + { + Object value = session.getLongTermAttribute(name); + if (value == null) + { + story.getVariablesState().set(var, ""); + } + else + { + story.getVariablesState().set(var, value); + } + } + catch(Exception e) + { + throw new RuntimeException("Failed to get long term attribute",e); + } + } + +} diff --git a/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/functions/RemoveLongTermAttributeFunction.java b/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/functions/RemoveLongTermAttributeFunction.java new file mode 100644 index 0000000..223ec55 --- /dev/null +++ b/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/functions/RemoveLongTermAttributeFunction.java @@ -0,0 +1,73 @@ +package com.rabidgremlin.mutters.bot.ink.functions; + +import com.bladecoder.ink.runtime.Story; +import com.rabidgremlin.mutters.bot.ink.CurrentResponse; +import com.rabidgremlin.mutters.bot.ink.InkBotFunction; +import com.rabidgremlin.mutters.core.IntentMatch; +import com.rabidgremlin.mutters.core.session.Session; + +/** + * This function removes the specified long term session attribute from the session. + * These attributes are not removed at the end of a conversation so they can be used + * to share context between conversations in the same session. + * + * For example in your Ink script you could have: + * ``` + * VAR current_order = "" + * + * === check_order_status === + * ::GET_LONG_TERM_ATTR name::currentorder var::current_order + * For order {current_order} ? + * + YesIntent + * -> display_order_details + * + NoIntent + * ::REMOVE_LONG_TERM_ATTR name::currentorder + * -> get_order_number + * -> END + * ``` + * + * + * @author rabidgremlin + * + */ +public class RemoveLongTermAttributeFunction + implements InkBotFunction +{ + + /* + * (non-Javadoc) + * + * @see com.rabidgremlin.mutters.bot.ink.InkBotFunction#getFunctionName() + */ + @Override + public String getFunctionName() + { + return "REMOVE_LONG_TERM_ATTR"; + } + + /* + * (non-Javadoc) + * + * @see com.rabidgremlin.mutters.bot.ink.InkBotFunction#respondexecute(CurrentResponse currentResponse, Session + * session, IntentMatch intentMatch, Story story, String param) + */ + @Override + public void execute(CurrentResponse currentResponse, Session session, IntentMatch intentMatch, Story story, String param) + { + FunctionDetails details = FunctionHelper.parseFunctionString(param); + + if (details.getFunctionParams() == null) + { + throw new IllegalArgumentException("Missing name and value values for REMOVE_LONG_TERM_ATTR"); + } + + String name = details.getFunctionParams().get("name"); + if (name == null) + { + throw new IllegalArgumentException("Missing name value for REMOVE_LONG_TERM_ATTR"); + } + + session.removeLongTermAttribute(name); + } + +} diff --git a/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/functions/SetLongTermAttributeFunction.java b/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/functions/SetLongTermAttributeFunction.java new file mode 100644 index 0000000..6a307f4 --- /dev/null +++ b/mutters-ink-bot/src/main/java/com/rabidgremlin/mutters/bot/ink/functions/SetLongTermAttributeFunction.java @@ -0,0 +1,69 @@ +package com.rabidgremlin.mutters.bot.ink.functions; + +import com.bladecoder.ink.runtime.Story; +import com.rabidgremlin.mutters.bot.ink.CurrentResponse; +import com.rabidgremlin.mutters.bot.ink.InkBotFunction; +import com.rabidgremlin.mutters.core.IntentMatch; +import com.rabidgremlin.mutters.core.session.Session; + +/** + * This function sets the value of a long term session attribute. These attributes are not removed at the end of a + * conversation so they can be used to share context between conversations in the same session. + * + * For example in your Ink script you could have: + * ``` + * Your order {order_number} has been placed! + * ::SET_LONG_TERM_ATTR name::currentorder value::{order_number} + * ``` + * + * + * @author rabidgremlin + * + */ +public class SetLongTermAttributeFunction + implements InkBotFunction +{ + + /* + * (non-Javadoc) + * + * @see com.rabidgremlin.mutters.bot.ink.InkBotFunction#getFunctionName() + */ + @Override + public String getFunctionName() + { + return "SET_LONG_TERM_ATTR"; + } + + /* + * (non-Javadoc) + * + * @see com.rabidgremlin.mutters.bot.ink.InkBotFunction#respondexecute(CurrentResponse currentResponse, Session + * session, IntentMatch intentMatch, Story story, String param) + */ + @Override + public void execute(CurrentResponse currentResponse, Session session, IntentMatch intentMatch, Story story, String param) + { + FunctionDetails details = FunctionHelper.parseFunctionString(param); + + if (details.getFunctionParams() == null) + { + throw new IllegalArgumentException("Missing name and value values for SET_LONG_TERM_ATTR"); + } + + String name = details.getFunctionParams().get("name"); + if (name == null) + { + throw new IllegalArgumentException("Missing name value for SET_LONG_TERM_ATTR"); + } + + String value = details.getFunctionParams().get("value"); + if (value == null) + { + throw new IllegalArgumentException("Missing value value for SET_LONG_TERM_ATTR"); + } + + session.setLongTermAttribute(name, value); + } + +} diff --git a/mutters-ink-bot/src/test/ink/order/main.ink b/mutters-ink-bot/src/test/ink/order/main.ink new file mode 100644 index 0000000..91e88fd --- /dev/null +++ b/mutters-ink-bot/src/test/ink/order/main.ink @@ -0,0 +1,52 @@ +// INK script to test long term session attribute functions + +-> start + +// holds the order number for the current conversation +VAR order_number = "" + +=== start === ++ CreateOrderIntent -> create_order ++ CheckStatusIntent -> check_status +-> END + +=== create_order === +~ order_number = 123456 // fake order creation +Your order {order_number} has been created! +::SET_LONG_TERM_ATTR name::currentorder value::{order_number} +-> END + +=== check_status === +::GET_LONG_TERM_ATTR name::currentorder var::order_number +{ + - order_number == "": + -> get_order_number_for_status_check + - else: + -> check_order_status +} +-> END + += get_order_number_for_status_check +What is the order number of the order you want to check the status of ? +// the rest of this divert would: +// * capture the order number via a number intent +// * set the order_number variable +// * jump to display_order_status +-> END + += check_order_status +For order {order_number} ? ++ YesIntent + -> display_order_details ++ NoIntent + // customer wants to talk about another order so unset current order long term attribute + ::REMOVE_LONG_TERM_ATTR name::currentorder + -> get_order_number_for_status_check +-> END + += display_order_details +// return dummy status +Order {order_number} is currently being packed. +// store current order for next conversation in session +::SET_LONG_TERM_ATTR name::currentorder value::{order_number} +-> END \ No newline at end of file diff --git a/mutters-ink-bot/src/test/ink/cancel_taxi.ink b/mutters-ink-bot/src/test/ink/taxi/cancel_taxi.ink similarity index 100% rename from mutters-ink-bot/src/test/ink/cancel_taxi.ink rename to mutters-ink-bot/src/test/ink/taxi/cancel_taxi.ink diff --git a/mutters-ink-bot/src/test/ink/confused.ink b/mutters-ink-bot/src/test/ink/taxi/confused.ink similarity index 100% rename from mutters-ink-bot/src/test/ink/confused.ink rename to mutters-ink-bot/src/test/ink/taxi/confused.ink diff --git a/mutters-ink-bot/src/test/ink/globals.ink b/mutters-ink-bot/src/test/ink/taxi/globals.ink similarity index 100% rename from mutters-ink-bot/src/test/ink/globals.ink rename to mutters-ink-bot/src/test/ink/taxi/globals.ink diff --git a/mutters-ink-bot/src/test/ink/intents.ink b/mutters-ink-bot/src/test/ink/taxi/intents.ink similarity index 100% rename from mutters-ink-bot/src/test/ink/intents.ink rename to mutters-ink-bot/src/test/ink/taxi/intents.ink diff --git a/mutters-ink-bot/src/test/ink/main.ink b/mutters-ink-bot/src/test/ink/taxi/main.ink similarity index 100% rename from mutters-ink-bot/src/test/ink/main.ink rename to mutters-ink-bot/src/test/ink/taxi/main.ink diff --git a/mutters-ink-bot/src/test/ink/order_taxi.ink b/mutters-ink-bot/src/test/ink/taxi/order_taxi.ink similarity index 100% rename from mutters-ink-bot/src/test/ink/order_taxi.ink rename to mutters-ink-bot/src/test/ink/taxi/order_taxi.ink diff --git a/mutters-ink-bot/src/test/ink/where_taxi.ink b/mutters-ink-bot/src/test/ink/taxi/where_taxi.ink similarity index 100% rename from mutters-ink-bot/src/test/ink/where_taxi.ink rename to mutters-ink-bot/src/test/ink/taxi/where_taxi.ink diff --git a/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/functions/TestLongTermFunctions.java b/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/functions/TestLongTermFunctions.java new file mode 100644 index 0000000..d72e6be --- /dev/null +++ b/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/functions/TestLongTermFunctions.java @@ -0,0 +1,80 @@ +package com.rabidgremlin.mutters.bot.ink.functions; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +import com.rabidgremlin.mutters.bot.ink.functions.orderbot.OrderInkBot; +import com.rabidgremlin.mutters.bot.ink.functions.orderbot.OrderInkBotConfiguration; +import com.rabidgremlin.mutters.core.Context; +import com.rabidgremlin.mutters.core.bot.BotResponse; +import com.rabidgremlin.mutters.core.session.Session; + +public class TestLongTermFunctions +{ + private static OrderInkBot orderBot = new OrderInkBot(new OrderInkBotConfiguration()); + + @Test + public void testSetLongTermFunctionInScript() throws Exception + { + Session session = new Session(); + Context context = new Context(); + + BotResponse response = orderBot.respond(session, context, "Order a widget"); + + assertThat(response, is(notNullValue())); + assertThat(response.getResponse(), is("Your order 123456 has been created!")); + assertThat(session.getLongTermAttribute("currentorder"), is("123456")); + } + + @Test + public void testGetLongTermFunctionInScript() throws Exception + { + Session session = new Session(); + Context context = new Context(); + + // do the order so value is set + BotResponse response = orderBot.respond(session, context, "Order a widget"); + + // ask for status which should use long term current order as context + response = orderBot.respond(session, context, "What is the status of my order"); + assertThat(response, is(notNullValue())); + assertThat(response.getResponse(), is("For order 123456 ?")); + assertThat(session.getLongTermAttribute("currentorder"), is("123456")); + } + + @Test + public void testDeleteLongTermFunctionInScript() throws Exception + { + Session session = new Session(); + Context context = new Context(); + + // order, then answer no when bot confirms on current order + BotResponse response = orderBot.respond(session, context, "Order a widget"); + response = orderBot.respond(session, context, "What is the status of my order"); + response = orderBot.respond(session, context, "No"); + + // check that we are prompted for order number and that long term current order has been unset + assertThat(response, is(notNullValue())); + assertThat(response.getResponse(), is("What is the order number of the order you want to check the status of ?")); + assertThat(session.getLongTermAttribute("currentorder"), is(nullValue())); + } + + @Test + public void testGetLongTermFunctionInScriptNoValue() throws Exception + { + Session session = new Session(); + Context context = new Context(); + + // ask for status of order with out any prior conversation + BotResponse response = orderBot.respond(session, context, "What is the status of my order"); + + // should go down route of prompting for order number + assertThat(response, is(notNullValue())); + assertThat(response.getResponse(), is("What is the order number of the order you want to check the status of ?")); + assertThat(session.getLongTermAttribute("currentorder"), is(nullValue())); + } +} diff --git a/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/functions/orderbot/OrderInkBot.java b/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/functions/orderbot/OrderInkBot.java new file mode 100644 index 0000000..e3d0ce9 --- /dev/null +++ b/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/functions/orderbot/OrderInkBot.java @@ -0,0 +1,12 @@ +package com.rabidgremlin.mutters.bot.ink.functions.orderbot; + +import com.rabidgremlin.mutters.bot.ink.InkBot; + +public class OrderInkBot + extends InkBot +{ + public OrderInkBot(OrderInkBotConfiguration configuration) + { + super(configuration); + } +} diff --git a/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/functions/orderbot/OrderInkBotConfiguration.java b/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/functions/orderbot/OrderInkBotConfiguration.java new file mode 100644 index 0000000..cecd44b --- /dev/null +++ b/mutters-ink-bot/src/test/java/com/rabidgremlin/mutters/bot/ink/functions/orderbot/OrderInkBotConfiguration.java @@ -0,0 +1,72 @@ +package com.rabidgremlin.mutters.bot.ink.functions.orderbot; + +import java.util.List; + +import org.junit.Test; + +import com.rabidgremlin.mutters.bot.ink.InkBotConfiguration; +import com.rabidgremlin.mutters.bot.ink.InkBotFunction; +import com.rabidgremlin.mutters.bot.ink.StoryUtils; +import com.rabidgremlin.mutters.core.IntentMatcher; +import com.rabidgremlin.mutters.templated.SimpleTokenizer; +import com.rabidgremlin.mutters.templated.TemplatedIntent; +import com.rabidgremlin.mutters.templated.TemplatedIntentMatcher; + +public class OrderInkBotConfiguration + implements InkBotConfiguration +{ + + @Override + public IntentMatcher getIntentMatcher() + { + SimpleTokenizer tokenizer = new SimpleTokenizer(); + + TemplatedIntentMatcher matcher = new TemplatedIntentMatcher(tokenizer); + + TemplatedIntent createOrderIntent = matcher.addIntent("CreateOrderIntent"); + createOrderIntent.addUtterance("Order a widget"); + + TemplatedIntent checkStatusIntent = matcher.addIntent("CheckStatusIntent"); + checkStatusIntent.addUtterance("What is the status of my order"); + + TemplatedIntent yesIntent = matcher.addIntent("YesIntent"); + yesIntent.addUtterance("Yes"); + + TemplatedIntent noIntent = matcher.addIntent("NoIntent"); + noIntent.addUtterance("No"); + + + return matcher; + } + + @Override + public String getStoryJson() + { + return StoryUtils.loadStoryJsonFromClassPath("orderbot.ink.json"); + } + + @Override + public List getInkFunctions() + { + return null; + } + + @Override + public List getGlobalIntents() + { + return null; + } + + @Override + public ConfusedKnot getConfusedKnot() + { + return null; + } + + @Override + public List getDefaultResponses() + { + return null; + } + +} diff --git a/mutters-ink-bot/src/test/resources/orderbot.ink.json b/mutters-ink-bot/src/test/resources/orderbot.ink.json new file mode 100644 index 0000000..f7fbcb9 --- /dev/null +++ b/mutters-ink-bot/src/test/resources/orderbot.ink.json @@ -0,0 +1 @@ +{"inkVersion":17,"root":[{"->":"start"},"done",{"start":[["ev",{"^->":"start.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^CreateOrderIntent",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.0.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"create_order"},"\n",{"#f":7}]}],["ev",{"^->":"start.1.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^CheckStatusIntent",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.1.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"check_status"},"\n","end",{"#f":7}]}],{"#f":3}],"create_order":["ev",123456,"/ev",{"temp=":"order_number","re":true},"^Your order ",["G>","ev",{"VAR?":"order_number"},"out","/ev","G<",null],"^ has been created!","\n","^::SET_LONG_TERM_ATTR name::currentorder value::",["G>","ev",{"VAR?":"order_number"},"out","/ev","G<",null],"\n","end",{"#f":3}],"check_status":["^::GET_LONG_TERM_ATTR name::currentorder var::order_number","\n",["G>",["ev",{"VAR?":"order_number"},"str","^","/str","==","/ev",{"->":".^.b","c":true},{"b":[{"->":".^.^.^.^.get_order_number_for_status_check"},{"->":".^.^.^.3"},null]}],[{"->":".^.b"},{"b":[{"->":".^.^.^.^.check_order_status"},{"->":".^.^.^.3"},null]}],"nop","G<",null],"\n","end",{"get_order_number_for_status_check":["^What is the order number of the order you want to check the status of ?","\n","end",{"#f":3}],"check_order_status":["^For order ",["G>","ev",{"VAR?":"order_number"},"out","/ev","G<",null],"^ ?","\n",["ev",{"^->":"check_status.check_order_status.4.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^YesIntent",{"->":"$r","var":true},null],"c":["ev",{"^->":"check_status.check_order_status.4.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n","\n",{"->":".^.^.^.^.display_order_details"},{"#f":7}]}],["ev",{"^->":"check_status.check_order_status.5.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^NoIntent",{"->":"$r","var":true},null],"c":["ev",{"^->":"check_status.check_order_status.5.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n","\n","^::REMOVE_LONG_TERM_ATTR name::currentorder","\n",{"->":".^.^.^.^.get_order_number_for_status_check"},"end",{"#f":7}]}],{"#f":3}],"display_order_details":["^Order ",["G>","ev",{"VAR?":"order_number"},"out","/ev","G<",null],"^ is currently being packed.","\n","^::SET_LONG_TERM_ATTR name::currentorder value::",["G>","ev",{"VAR?":"order_number"},"out","/ev","G<",null],"\n","end",{"#f":3}],"#f":3}],"global decl":["ev","str","^","/str",{"VAR=":"order_number"},"/ev","end",null],"#f":3}],"listDefs":{}} \ No newline at end of file diff --git a/mutters-ink-bot/src/test/resources/taxibot.ink.json b/mutters-ink-bot/src/test/resources/taxibot.ink.json index 22ecd29..240bc02 100644 --- a/mutters-ink-bot/src/test/resources/taxibot.ink.json +++ b/mutters-ink-bot/src/test/resources/taxibot.ink.json @@ -1 +1 @@ -{"inkVersion":16,"root":["\n","\n","\n","\n","\n","\n",{"->":"start"},"done",{"start":[["ev",{"^->":"start.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^OrderTaxi",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.0.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"order_taxi"},{"#f":7}]}],["ev",{"^->":"start.1.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^CancelTaxi",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.1.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"cancel_taxi"},{"#f":7}]}],["ev",{"^->":"start.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^WhereTaxi",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.2.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"where_taxi"},{"#f":7}]}],{"#f":3}],"order_taxi":[[[["G>",["ev",{"VAR?":"address"},"str","^","/str","==","/ev",{"->":".^.b","c":true},{"b":[{"->":"order_taxi.request_address"},{"->":".^.^.^.3"},null]}],[{"->":".^.b"},{"b":[{"->":"order_taxi.order_the_taxi"},{"->":".^.^.^.3"},null]}],"nop","G<",null],"\n","end",{"#f":7,"#n":"order_taxi_loop"}],null],{"request_address":[["^What is the pick up address ?","\n","^::SET_REPROMPT Where would you like to be picked up ?","\n","^::SET_HINT 123 Someplace Rd","\n",["ev",{"^->":"order_taxi.request_address.0.6.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^GaveAddress",{"->":"$r","var":true},null],"c":["ev",{"^->":"order_taxi.request_address.0.6.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.^.g-0"},{"#f":7}]}],{"g-0":[{"->":".^.^.^.^.0.order_taxi_loop"},{"#f":7}]}],{"#f":3}],"order_the_taxi":["^::ORDER_TAXI","\n","^Taxi ",["G>","ev",{"VAR?":"taxiNo"},"out","/ev","G<",null],"^ is on its way","\n","^::ADD_ATTACHMENT type::link url::http://trackcab.example.com/t/",["G>","ev",{"VAR?":"taxiNo"},"out","/ev","G<",null],"^ title::Track your taxi here","\n","^::ADD_QUICK_REPLY Where is my taxi?","\n","^::ADD_QUICK_REPLY Cancel my taxi","\n","end",{"#f":3}],"#f":3}],"cancel_taxi":["^Your taxi has been cancelled","\n","end",{"#f":3}],"where_taxi":["^Your taxi is about 7 minutes away","\n","end",{"#f":3}],"stop":["^Ok","\n","end",{"#f":3}],"help":["^I can help you order a taxi or find out the location of your current taxi.","\n","^Try say \"Order a cab\" or \"Where is my cab\"","\n","end",{"#f":3}],"confused_bot":["^I'm sorry I'm not understanding you at all :(","\n","^If you are in a hurry, please call 555-12345 to order your taxi.","\n","end",{"#f":3}],"global decl":["ev","str","^","/str",{"VAR=":"address"},"str","^","/str",{"VAR=":"taxiNo"},"/ev","end",null],"#f":3}],"listDefs":{}} \ No newline at end of file +{"inkVersion":17,"root":["\n","\n","\n","\n","\n","\n",{"->":"start"},"done",{"start":[["ev",{"^->":"start.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^OrderTaxi",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.0.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"order_taxi"},"\n",{"#f":7}]}],["ev",{"^->":"start.1.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^CancelTaxi",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.1.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"cancel_taxi"},"\n",{"#f":7}]}],["ev",{"^->":"start.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^WhereTaxi",{"->":"$r","var":true},null],"c":["ev",{"^->":"start.2.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n",{"->":"where_taxi"},"\n",{"#f":7}]}],{"#f":3}],"order_taxi":[[[["G>",["ev",{"VAR?":"address"},"str","^","/str","==","/ev",{"->":".^.b","c":true},{"b":[{"->":"order_taxi.request_address"},{"->":".^.^.^.3"},null]}],[{"->":".^.b"},{"b":[{"->":"order_taxi.order_the_taxi"},{"->":".^.^.^.3"},null]}],"nop","G<",null],"\n","end",{"#f":7,"#n":"order_taxi_loop"}],null],{"request_address":[["^What is the pick up address ?","\n","^::SET_REPROMPT Where would you like to be picked up ?","\n","^::SET_HINT 123 Someplace Rd","\n",["ev",{"^->":"order_taxi.request_address.0.6.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.c","flg":2},{"s":["^GaveAddress",{"->":"$r","var":true},null],"c":["ev",{"^->":"order_taxi.request_address.0.6.c.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.s"},[{"#n":"$r2"}],"\n","\n",{"->":".^.^.^.g-0"},{"#f":7}]}],{"g-0":[{"->":".^.^.^.^.0.order_taxi_loop"},{"#f":7}]}],{"#f":3}],"order_the_taxi":["^::ORDER_TAXI","\n","^Taxi ",["G>","ev",{"VAR?":"taxiNo"},"out","/ev","G<",null],"^ is on its way","\n","^::ADD_ATTACHMENT type::link url::http://trackcab.example.com/t/",["G>","ev",{"VAR?":"taxiNo"},"out","/ev","G<",null],"^ title::Track your taxi here","\n","^::ADD_QUICK_REPLY Where is my taxi?","\n","^::ADD_QUICK_REPLY Cancel my taxi","\n","end",{"#f":3}],"#f":3}],"cancel_taxi":["^Your taxi has been cancelled","\n","end",{"#f":3}],"where_taxi":["^Your taxi is about 7 minutes away","\n","end",{"#f":3}],"stop":["^Ok","\n","end",{"#f":3}],"help":["^I can help you order a taxi or find out the location of your current taxi.","\n","^Try say \"Order a cab\" or \"Where is my cab\"","\n","end",{"#f":3}],"confused_bot":["^I'm sorry I'm not understanding you at all :(","\n","^If you are in a hurry, please call 555-12345 to order your taxi.","\n","end",{"#f":3}],"global decl":["ev","str","^","/str",{"VAR=":"address"},"str","^","/str",{"VAR=":"taxiNo"},"/ev","end",null],"#f":3}],"listDefs":{}} \ No newline at end of file