How to keep track of what question user is responding to? - facebook-messenger

Some questions my messenger chatbot is asking require textual input. In the button template I can set the payload so I know what exactly user chosen and handle it based on that. But with textual responses I need to filter if user answer is somehow related to "Change address, Billing, Returns, etc." For example:
bot-question: What is your new phone number?
user-answer: +123 123 12345
And now in the back-end I'm doing this:
if(user-answer in_array('billing keywords') {
// code
} elseif (user-answer in_array('delivery keywords')) {
// code
} elseif (user-answer in_array('payments keywords')) {
// code
} elseif (user-answer in_array('change-phone-number keywords')) {
// Finally got where I wanted 🎉😒
}
Isn't it possible to somehow add some tag to bot-question, so I would already know that the answer will be related to change-phone-number keywords? For example like this:
"template_type" => "text",
"text" => "What is your new phone number?",
"payload" => "changing_phone_number"

In the end it was impossible to get some postback from a normal text message, so what I did, I created additional columns:
expecting_billing_info
expecting_phone_number_update
etc.
And I set it to 0 by default, but when bot asks a question like:
Would you like to change your phone number?
I set it to 1 and when user types it in I do:
if (expecting_phone_number_update == 1) {
// 1. get the message
// 2. Check if it's correct phone number format
// 3. Reply based on 2. and update phone number
} elseif (expecting_billing_info == 1) {
// ...
}
This is not perfectly clean solution, but it does the work. Let's hope messenger will release this postback feature one day for normal messages as well! :)

Related

Jovo FrameWork: this.followUpState doesn't do its job like it's supposed to

I have this intent
'NameIntent': function(name) {
let speech = 'Hello ' + name.value + ', nice to meet you! which Radio do you want me to play? ;
this.followUpState('MakeSureEnterRadioIntentState').ask(speech);
},
which gets triggered with the utterance {name}. Example: User says: SAM
The stateIntent code is as follows:
The PlayRadioIntent get triggered with the utterance {radioName}. Example: User says: Mosaique
'MakeSureEnterRadioIntentState': { //TO solve this problem: IN CASE USER SAYS MOSAIQUE AFTER NAMEINTENT ALEXA WILL INTERPRET MOSAIQUE AS A NAME AND WILL REENTER NAMEINTENT
'PlayRadioIntent': function(channel) {
this.tell("The radioName is" + channel.value);}
The problem I tried to resolve with this state:
ALEXA: Hello Sam nice to meet you! which Radio do you want me to play? ;
USER: Mosaique
//Mosaique is a radio name
ALEXA: Hello Mosaique nice to meet you! which Radio do you want me to play? ;
//ALEXA interprets mosaique as a name instead of a radioName and reenters the NameIntent. I thought using states would be perfect for resolving this confusion. and they SHOULD. But, they aren't and I do not really understand why.
HELP?
The Jovo Routing works as follows if it is in a state: (1) Look if the intent is found in the state, if not (2) look if "Unhandled" is defined in the state, if not (3) look if the intent can be found outside the state, if not (4) look if "Unhandled" is defined outside the state, if not (5) throw an error.
In your case, "NameIntent" can't be found in the state, so it goes to the global (stateless) "NameIntent". To stay in the state, you can add an "Unhandled" intent that acts as a "catch all" for any intent that can't be found in there.
Take a look at this section to learn more about states and Unhandled: https://www.jovo.tech/blog/p2s5-introduction-to-states/#unhandled-intent

ExpectedConditions OR AND AND in DefaultWait<T>

In using C#, Selenium webdriver, I navigate to a page, which might redirect to a login screen or eventually redirect or not to the actual app page, depending on whether I have cached credentials or not. (case in point azure active dir auth).
What I am after is to find if a known element of my app page appears or the login screen of azure auth with an element id "use_another_account_link" appears. SO I want to OR the condition of ExpectedConditions without having to wait N seconds on each.
You could use a CSS selector with two expessions separated by a , to implement the OR :
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
var element = wait.Until(ExpectedConditions.ElementExists(By.CssSelector("#id1, #id2")));
if (element.GetAttribute("id") == "id1") {
// handle element with id="id1"
} else {
// handle element with id="id2"
}
Or an XPath with two expessions separated by a |:
var element = wait.Until(ExpectedConditions.ElementExists(By.XPath("id('id1') | id('id2')")));
if (element.GetAttribute("id") == "id1") {
// handle element with id="id1"
} else {
// handle element with id="id2"
}
This is a case where you have to provide your own Func<IWebElement, IWebElement> to IWait<IWebElement>.Until(). You can write something specific like this:
(Func<IWebElement, IWebElement>)
(return ExpectedConditions.ElementExists(By.Id("use_another_account_link"))
|| ExpectedConditions.ElementExists(By.Id("your_login_field_id")))
Or something generic like this:
public Func<IWebElement, IWebElement> Or(Func<IWebElement, IWebElement> either,
Func<IWebElement, IWebElement> or) {
return (Func<IWebElement, IWebElement>) either || or;
}
Wait until at least anyone condition returns true
wait.until(
ExpectedConditions.or(
ExpectedConditions.visibilityOfAllElementsLocatedBy(By.name("Services")),
ExpectedConditions.visibilityOfAllElementsLocatedBy(By.name("Products"))
ExpectedConditions.visibilityOfAllElementsLocatedBy(By.name("Contact Us"))
)
);
The accepted answer is undoubtedly correct from a CSS/XPath perspective, however it may not always do what is expected when used with certain Selenium ExpectedConditions.
In the case where you want to wait for a login page to display, but also need to cater for the fact that this page will display either:
a userid entry field with the password field hidden, or;
a password entry field with the userid field hidden (userid read from cookies)
as is the case for eBay, then the following code will timeout in both cases.
WebElement login = (new WebDriverWait(driver, Duration.ofSeconds(5))).until(
ExpectedConditions.visibilityOfElementLocated(
By.cssSelector("input#userid, input#pass")
));
Instead it is necessary to use ExpectedConditions.or to determine whether either field is both present and visible:
boolean bDisplayed = (new WebDriverWait(driver, Duration.ofSeconds(5))).until(ExpectedConditions.or(
ExpectedConditions.visibilityOfElementLocated(By.cssSelector("#userid")), ExpectedConditions.visibilityOfElementLocated(By.cssSelector("#pass"))
));
and then proceed to work out which field is visible.

React/Flux app data structure

i'm building an Gmail-like email browser client app prototype and i need a little help/advice structuring my React/Flux app. I decided to use pure Flux to get a better idea of how it works.
It's a simple email client with a list of letters, grouped by folders and tags and an ability to add letters to favorites.
So, i have a LettersStore containing an array of letters. The single letter data object looks something like this
{
id: 0,
new: true, //unread
checked: false,
starred: false,
folder: "inbox", //could be 'sent', 'spam', 'drafts'
sender: "Sender Name",
subject: "Re:",
snippet: "Hello there, how are you...",
body: "Hello there, how are you doing, Mike?",
date: "02.19.2016 16:30",
tags:["personal", "urgent"]
}
So what i'm trying to achieve is to let users navigate through folders (inbox, sent, drafts, spam) and filters (starred, tag, etc.)
In both folders and filters there has to be a way to select (check) some/all letters. The view state depends on how many letters are selected (the Select-all checkbox update, just like on Gmail). When the user selects a letter, the Flux action is being triggered and the state of the app updates.
The controller-view on top of the app does all the calls to the LettersStore public methods and passes the data down as props, but i'm not sure, what public methods the LettersStore should have. Currently it has:
emitChange()
addChangeListener()
removeChangeListener()
getAll() //returns array
areSomeLettersInFolderChecked(folderName) //returns bool
areAllLettersInFolderChecked(folderName) //returns bool
countNewLettersInAllFolders() //returns object
This works ok with folders, but when it comes to filters, it doesn't make sense anymore, since a starred letter is in some folder, and i feel like it's not the right thing to add specific methods like areSomeLettersInFilterChecked(filterType) etc.
Also, just like in Gmail, there has to be a way to select letter in the "Starred" filter, which belongs to the "Inbox" folder, then navigate to "Inbox" folder and keep that letter selected.
Maybe i should move the areSomeLettersInFolderChecked-like stuff to the component level?
I'm sure here has to be a proper way of doing it. Thanks in advance!
Rather than trying to encapsulate all the possible states and filters into your letter objects, keep it dumb. Normalize it and use supporting data structures to represent the other characteristics.
I'd strip it down to just the following properties:
{
id: 0,
sender: "Sender Name",
subject: "Re:",
snippet: "Hello there, how are you...",
body: "Hello there, how are you doing, Mike?",
date: "02.19.2016 16:30",
tags:["personal", "urgent"]
}
Your LetterStore can stay the same, or alternatively you could use an object or map to store letters against their id's for quick lookups later.
Now we need to represent the properties we removed from the message.
We can use individual sets to determine whether a message belongs to the new, checked and starred categories.
For instance, to star a message, just add it's id to the starred set.
var starred = new Set();
starred.add(message.id);
You can easily check whether a message is starred later on.
function isStarred(message) {
return starred.has(message.id);
}
The pattern would be the same for checked and unread.
To represent folders you probably want to use a combination of objects and sets.
var folders = {
inbox: new Set(),
sent: new Set(),
spam: new Set(),
drafts: new Set()
}
Simplifying your structures into these sets makes designing queries quite easy. Here are some examples of the methods you talked about implemented with sets.
function checkAll() {
messages.forEach(function(message) {
checked.add(message.id);
});
return checked;
}
function isChecked(message) {
return checked.has(message.id);
}
function inFolder(name, message) {
return folders[name].has(message.id);
}
// is message checked and in inbox
if(isChecked(message) && inFolder('inbox', message)) {
// do something
}
It becomes easy to construct complex queries, simply by checking whether messages belong to multiple sets.

Symfony / Sonata Custom Query to prefill choices

I want to Create a Custom search Field in the FormMapper:
What it is supposed to do: As soon, as the user starts to enter data (with a minimum of 3 characters), I want to run a query against a backend to get the choices available for the given set (that is: artists with the letters "foo" for example).
I have to admit, that I have no experience with Symfony / Sonata, and I have been struggling for 2 Days now. So what I have - and what would make sense is, to create a
// in Bundle/Admin/ArtistAdmin.php
$formMapper
->add('artist', 'sonata_type_immutable_array', array(
'keys' => array(
array('type', 'choice',
array('choices' => array('callback' => array($this, 'getArtists')))))))
So I setup a callback, that pings me back the input, the user did so far.
// in Bundle/Admin/ArtistAdmin.php
public static function getArtists($value)
{
if (!(strlen($value) > 2)) return null;
// get data from url for given input
// create array
// return array
But I get either a SimpleXML Error, or errors I hardly understand and I would really appreciate any help - even if only a vague pointing in a different / better direction.

ExtJS findExact() and custom validator bug

I'm using a custom validator on my combobox's:
function(v) {
console.log(v === 'some value I know for SURE is in the store'); // (1)
var index = this.getStore().findExact(this.displayField, v);
return (index!==-1) ? true : 'Invalid selection';
}
Basically admits the same set as forceSelection but allows the user to type arbitrary text to attempt to auto-complete.
However; I'm having really odd results with findExact(). For example, if the combobox's value is currently valid, and a user does a space + backspace, the validator will fail, even though the output of (1) is true.
Any ideas what is causing the problem? The end-experience is currently very buggy-feeling..
When you type additional space, store is filtered. After you press backspace, and validator is fired, store is still empty.
If you have local store, then you could validate combo with some delay after each change. Example:
listeners: {
change: function() {
this.validate();
},
delay: 100
}
That should be enough.
On the other hand if you have remote store, try something like this:
validator: function(v) {
var store = this.getStore(),
index = store.findExact(this.displayField, v);
if (index === -1 && store.isLoading()) {
store.on('load', function() {
this.validate();
}, this, { single: true, delay: 100 });
}
return (index !== -1) ? true : 'Invalid selection';
}
I had a similar issue and found this entry. My problem was, that I reused the same store instance across multiple ComboBoxes. After giving each ComboBox an own store with cloned data, everything was fine.
See also:
https://www.sencha.com/forum/showthread.php?305182-ComboBox-forceSelection-clears-input&p=1127218&viewfull=1#post1127218
I just spent a few days on this issue and found a really great solution (by accident, really). You can - as the accepted answer suggests - utilize the provided validator function; however, from my understanding, there is a much simpler solution than the accepted answer provides: evaluating whether or not the user-provided input equates to a value in the store (which is the underlying question in the original post).
The advantage of thinking about the input in this way is that it enables us to handle the use case of an invalid value entered by the user (validated; no value) and - after the field loses focus - Ext JS sets the field back to its previous value (which remembers its store value).
This is an entirely different route than your thinking, but it should work, especially as .validate() runs regardless of whether you provide an implementation of the validator procedure:
validator : function(someParam) {
if(this.value === null) {
return "error message"; //falsy
} else {
return true;
}
}
If you enable forceSelection, the above works, very well, and gets rid of the buggy feeling. This allows you to rely on the .validate to do its magic elsewhere (notice I don't even call it; read the doc. to figure out when its called in relationship to validator) and not have to worry about what the user correctly explains in the accepted answer.
We were having trouble with forceSelection clearing user text before they were finished typing. We seemed to get what we needed by setting forceSelection false and just checking that they selected something.
validator: function(v) {
if (this.getSelection() === null) {
return 'invalid text';
}else{
return true;
}
}

Resources