Bot Framework Rendering Choices Changes Format - botframework

I've just tested a dialog with a list of choices that seem to render differently and I can't find any information on why this would be happening.
Given this list of choices:
Choices = ChoiceFactory.ToChoices(new List<string>
{
"No",
"Yes - xxxxxxxxxxxxxxxxx",
"Yes - xxxxxxxxxxxxxxxxx",
"Yes - xxxxxxxxxxxxxxxxx",
"Yes - xxxxxxxxxxxxxxxxx"
})
It renders like this:
Whereas this list:
Choices = ChoiceFactory.ToChoices(new List<string>
{
"No",
"Yes 2",
"Yes - 3",
"Yes - 4",
"Yes - 5"
})
It renders how I want it too:
I've got other instances where I have a long list of buttons that scroll so I'm very confused why the first list above rendered like a list.
How can I force it to render like the second example?

This behavior has to do with the size of each choice and the amount of choices. When the choices themselves are small (ex: "Yes 2"), then they are able to show as the button type (how you want it to look). When they are displayed in this fashion, and there are many choices; then it will scroll off the screen (as you have seen).
When the choices (any one choice) becomes longer (ex: "Yes - xxxxxxxxxxxxxxxxx") then they get put into the list format. I don't believe there is a way to override this, but I will take a look. If not; the only option is to try and keep your choices small in size.
Additionally; each channel handles rendering/displaying in it's own fashion. For example; if i create many (~20) choices of the small variety; then they will show as the scroll-able "buttons" in web chat, where in Skype they will show as a list.

Because of the strange relationship between Choice and his CardAction, You can shorten the value, but leave the title long, so you'll see cards with long text.
var values = new Dictionary<string, string>
{
{"1", "No"},
{"2", "Yes - xxxxxxxxxxxxxxxxx"},
{"3", "Yes - xxxxxxxxxxxxxxxxx"},
{"4", "Yes - xxxxxxxxxxxxxxxxx"},
{"5", "Yes - xxxxxxxxxxxxxxxxx"}
};
Choices = values.Select(v =>
new Choice(v.Key)
{
Action = new CardAction(title: v.Value, value: v.Key)
})
.ToList()
The key point is to put a short value in Value and a desired value in Title.
The rest of the CardAction settings can be found in this answer.

Related

Selecting an element not equal to a certain string

I am trying to select an incorrect answer (radio button) to get an error message to appear, but the answers are random (except the correct answer).
How can I say get the radio buttons, and then click one that does not equal "correct answer" using cypress assertions?
cy.get('[data-cy="multipleChoiceQuestionAnswer"]')
.should('not.contain', 'correct answer')//.find('label').not('corect answer')//.not.includes('correct answer')
.click()
I would like to be able to select one of the two radio buttons for the incorrect answers, right now I can only select the correct answer.
well:
be aware that .should('not.contain', 'correct answer') is an assertion, is not a way to filter/get some elements.
It's, essentially, just a way to check (aka "assert") that something is like you expect it to be.
An assertion like yours is useful just to get the Cypress log print something like this
Read it like if you are telling
"Ehy Cypress, I selected an element, could you check that it doesn't contain the correct answer, please?"
What are assertions useful for? They aren't useful when everything goes right but when the test goes wrong.
Because without assertions, you can find yourself behind a broken test with Cypress telling you that "there isn't the element" but you can't know which element Cypress isn't finding.
Placing some "key point" assertions allows you to understand why a test failed in short time.
Anyway: if your HTML is something like this
<div data-cy="multipleChoiceQuestionAnswer"><label>correct answer<input type="checkbox"/></label></div>
<div data-cy="multipleChoiceQuestionAnswer"><label>no<input type="checkbox"/></label></div>
<div data-cy="multipleChoiceQuestionAnswer"><label>nope<input type="checkbox"/></label></div>
you can accomplish your goal making:
cy.get('[data-cy="multipleChoiceQuestionAnswer"]').then(els => {
// `els` is a jQuery instance, let's parse the various elements
let $el;
for(let i = 0, n = els.length; i < n; i++) {
// it transforms every element in a jQuery instance
$el = Cypress.$(els[i]);
// it uses jQuery to get the label text
if($el.find("label").text() !== "correct answer") {
// it stops as soon as the answer isn't the correct one
break;
}
}
// returns the element to be clicked
return $el.find("input");
})
// it assert about it (to have a useful hint in the Cypress command log)
.should("not.contain", "correct answer")
// clicks it
.click();
I hope the code is self-explanatory (in case it isn't, ask me some more clarifications) 😊

RASA FormAction ActionExecutionRejection doesn’t re-prompt for missing slot

I am trying to implement a FormAction here, and I’ve overridden validate method.
Here is the code for the same:
def validate(self, dispatcher, tracker, domain):
logger.info("Validate of single entity called")
document_number = tracker.get_slot("document_number")
# Run regex on latest_message
extracted = re.findall(regexp, tracker.latest_message['text'])
document_array = []
for e in extracted:
document_array.append(e[0])
# generate set for needed things and
document_set = set(document_array)
document_array = list(document_set)
logger.info(document_set)
if len(document_set) > 0:
if document_number and len(document_number):
document_array = list(set(document_array + document_number))
return [SlotSet("document_number", document_array)]
else:
if document_number and len(document_number):
document_array = list(set(document_array + document_number))
return [SlotSet("document_number", document_array)]
else:
# Here it doesn't have previously set slot
# So Raise an error
raise ActionExecutionRejection(self.name(),
"Please provide document number")
So, ideally as per the docs, when ActionExecutionRejection occurs, it should utter a template with name utter_ask_{slotname} but it doesn’t trigger that action.
Here is my domain.yml templates
templates:
utter_greet:
- text: "Hi, hope you are having a good day! How can I help?"
utter_ask_document_number:
- text: "Please provide document number"
utter_help:
- text: "To find the document, please say the ID of a single document or multiple documents"
utter_goodbye:
- text: "Talk to you later!"
utter_thanks:
- text: "My pleasure."
The ActionExecutionRejection doesn't by default utter a template with the name utter_ask_{slotname}, but rather leaves the form logic to allow other policies (e.g. FallbackPolicy) to take action. The utter_ask_{slotname} is the default for the happy path in which it's trying to get a required slot for the first time. This default implementation of the action rejection is there in order to handle certain unhappy paths such as if a user decides they want to exit the flow by denying, or take a detour by chatting, etc.
If you want to implement the template to re-ask for the required slot using the utterance, you could replace the ActionExecutionRejection with dispatcher.utter_template(<desired template name>, tracker). However, this will leave you with no way to exit the form action without validation -- I don't know what your intents are, but perhaps you want to also incorporate some logic based on the intent (i.e. if it's something like "deny", let the ActionExecutionRejection happen so it can exit, it it's an "enter data" type of intent make sure it asks again).

New Teams Who Bot Actionable Messages Syntax? Undocumented O365ConnectorCard functionality?

I'm working with the O365ConnectorCard capabilities in Teams for bots and I'm trying to recreate the scrolling list of people the new Who bot can produce when you say something like who works with jim#contoso.com?.
You can see what it looks like here.
If it is using the connector card functionality, I'm assuming that is a Section but maybe using undocumented syntax? Additionally, the sections are clickable from the Who bot, but no matter what combination of PotentialAction added, I cannot get the row to have a hover and click of type imBack.
The MessageCard Playground as well doesn't have any examples that match what that Who bot can produce.
Anyone know how this was done? Any MS folks want to post some sample JSON of what's possible but not available yet from the Microsoft.Bot.Connector.Teams NuGet package :)? I'm currently up to v0.8.0.
Thanks!
I believe the Who bot is using the List Layout. The list layout is used to display a collection of cards in a stacked list.
We haven't documented these new card formats yet - we were waiting for Who bot to ship, but there are actually two new card formats, PersonCard and ListCard. You can see some commented-out examples of how to use them here: https://github.com/OfficeDev/BotBuilder-MicrosoftTeams/blob/3c6f07b7600bb20713626cbf79acf5e114e57d0d/CSharp/Tests/Microsoft.Bot.Connector.Teams.Tests.Shared/CardTests.cs.
ListCard is different from List, and there are supports for three kinds of objects in a list: Person, File, and a generic one called "resultitem" - that latter one may not render properly on Android. There's also a way to add a separator line.
This may or may not be enough to get you going, but in case you find it useful:
{
"content":{
"title":"Test List Card",
"items":[
{
"type":"section",
"title":"List Card section"
},
{
"type":"person",
"id":"shmayura#microsoft.com",
"title":"Shanmathi Mayuram Krithivasan",
"subtitle":"SOFTWARE ENGINEER",
"tap":{
"type":"invoke",
"title":"Details?",
"value":"{\"intentName\":\"WhoIs\",\"employeeName\":null,\"employeeEmail\":\"shmayura#microsoft.com\",\"topic\":null}"
}
},
{
"type":"file",
"id":"https://microsoft.sharepoint.com/teams/skypespacesteamnew/Shared%20Documents/Design/FinancialReport.xlsx",
"title":"FinancialReport",
"subtitle":"teams > skypespacesteamnew > design",
"tap":{
"type":"openUrl",
"title":"Open url",
"value":"https://microsoft.sharepoint.com/teams/skypespacesteamnew/Shared%20Documents/Design/FinancialReport.xlsx"
}
},
{
"type":"resultItem",
"title":"Seattle to Chicago",
"subtitle":"$500 July 4 - July 8",
"icon":"https://skypeteamsbotstorage.blob.core.windows.net/bottestartifacts/sandwich_thumbnail.png",
"tap":{
"type":"imBack",
"title":"Reply",
"value":"flightto Chicago"
}
}
],
"buttons":[
{
"type":"imBack",
"title":"Open Online",
"value":"editOnline"
}
]
},
"contentType":"application/vnd.microsoft.teams.card.list"
}

How can I parse this plaintext RP-style string into a more generic XML-style one?

I'm making an app that will translate roleplaying-style messages into something much more generic. The user has the ability to specify their preferences, like:
Moves
- /me <move>
- *<move>*
Speech
- <speech>
- "<speech>"
Out-of-Character
- [<ooc>]
- ((ooc))
- //ooc
I need to parse a message like this:
/me eats food "This is *munch* good!" [You're good at this]
or like this:
*eats food* This is *munch* good! ((You're good at this))
into a more generic, XML-like string like this:
<move>eats food <speech>This is <move>munch</move> good!</speech> <ooc>You're good at this</ooc></move>
but with regard to which is inside which. For example:
*eats food "This is munch* good" // You're good at this
should be parsed as:
<move>eats food "This is munch</move><speech> good" </speech><ooc> You're good at this</ooc>
even if that's not what the user intended. Note that the quotes in this last example weren't parsed because they didn't wrap a complete segment, and the current move segment had not finished by the time the first was encountered, and speech had already started when the second one was, and the second one didn't have another after it to surround a separate speech segment.
I've tried doing this iteratively, recursively, with trees, and even with regexes, but I haven't found a solution that works like I want it to. How do I parse the above RP-style messages into the above generic XML-style messages?
Also important is that the spacing is preserved.
Here are some other examples using the above-listed preferences:
I like roller coasters.
[what are you like?]
/me eats a hamburger // wanna grab lunch after this?
*jumps up and down* This ((the party)) is great!
/me performs *an action* within an action "And that's just fine [As is *an action* in ooc in speech]"
And messages /me can change contexts // at any point
[But ill-formatted ones *must be parsed] according "to* the rules"
-And text formatted in <non-specified ways> is &not treated; specially-
become:
<speech>I like roller coasters.</speech>
<ooc>what are you like?</ooc>
<move>eats a hamburger <ooc> wanna grab lunch after this?</ooc></move>
<move>jumps up and down</move><speech> This <ooc>the party</ooc> is great!</speech>
<move>performs <move>an action</move> within an action <speech>And that's just fine <ooc>As is <move>an action</move> in ooc in speech</ooc></speech></move>
<speech>And messages <move>can change contexts <ooc> at any point</ooc></move></speech>
<ooc>But ill-formatted ones *must be parsed</ooc><speech> according <speech>to* the rules</speech></speech>
<speech>-And text formatted in <non-specified ways> is &not treated; specially-</speech>
What you have is a bunch of tokens that should trigger an xml tag. It is fairly straightforward to implement this using a function for each tag.
void move(){
xmlPrintWriter.println("<move>");
parse();
xmlPrintWriter.println(content);
xmlPrintWriter.println("</move>");
}
Where the parse() consumes and classifies the input text.
void parse(){
if (text.startsWith("*")) action = MOVE;
... other cases
if ( action == MOVE){
move();
}
... other actions.
The parse method has to check for all possible state-changers "*" -> move, "((" -> ooc, """ -> speech and so on.
Here MOVE is a class constant, action a state variable along with text and xmlPrintWriter. move and parse are both methods
This approach will not work though if you allow your last example. Then the situation becomes extremely hairy and would need to be decided on a case by case basis.
Something to this affect might do:
public static RPMessageSegment split(RPMessageSegment text)
{
ArrayList<RPMessageSegment> majorSegments = new ArrayPP<>();
scan: for(int i = 0, l = text.length() - 1; i < l; i++)
{
dels: for(Delimiter d : delimiters)
{
if (d.startsWith(text, i))
{
RPMessageSegment newSegment = d.extractSegment(text, i);
i += newSegment.lengthWithOriginalDelimiters();
majorSegments.add(newSegment);
continue scan;
}
}
}
if (majorSegments.length() == 1)
return majorSegments.get(0);
for(int i = 0, l = majorSegments.length(); i < l; i++)
{
majorSegments.set(i, split(majorSegments.get(i)));
}
return new RPMessageSegment(majorSegments);
}
Of course, this presumes that the referenced classes have these methods that respond as one might expect. They shouldn't be terribly hard to imagine, not to mention write.
After it's parsed into RPMessageSegments, those can easily be echoed out into strings surrounded by XML-style tags

List values in AppleScript?

I have the following AppleScript so far:
# List of possible options to control the development environment.
set WhatDoUWantToDoList to {"1", "2", "3", "4"}
set MySites to {"test1", "test2"}
# task selected
set selectedTask to {choose from list WhatDoUWantToDoList with prompt "Pick your task now!!!" without multiple selections allowed}
if selectedTask is equal to {"1"} then
display dialog selectedTask
else
# site selected
set selectedSite to {choose from list MySites with prompt "Pick the site your working on!!!"}
if (selectedTask is not equal to false and selectedSite is not equal to false) then
display dialog selectedTask
display dialog selectedSite
else
display dialog "you messed up!"
end if
end if
I am trying to say if option 1 is selected in list 1 display only the selected task, however, if any other option is selected in list 1 you have to move to the new code block, and must select an option in list 2, if you cancel on list 1 and list 2 you screwed up.
Any ideas on what I am missing here?
{ } in AppleScript creates a list, so when you set selectedTask, you're putting the results from choose from list into another list. When you try to compare the result to {"1"}, it's actually {{"1"}}, so it isn't equal.
Use parentheses ( ) for grouping instead.
using this code worked: if selectedTask contains "1" then
Choose from list will always return an array because multiple selection is possible. The basic idea is:
set selectedValues to (choose from list {"Value 1", "Value 2"} with prompt "Choose")
if (selectedValues is not false) then
set selectedValue to item 1 of selectedValues
display dialog ("You chose " & selectedValue as text)
else
display dialog ("You did not chose!")
end if

Resources