SAPCAI quick replies displayed as attachments - botframework

I used SAPCAI (SAP Conversational AI is a French development platform like Azure Bot Service) to build my chatbot, but I'm using the Bot-Framework Webchat on my web app. Therefore, I don't have any C# or JS code. The problem is SAPCAI quick replies are displayed as "attachments". How can I fix that?
Expected
Got

What you want is the Quick replies type.
Quick replies: Same purpose as buttons, but disappear once clicked. Great if you don’t want the user to have to scroll up the conversation and click on a button again.
There is information about how to create click replies on this page. It seems to be in the format of:
{
"type": "quickReplies",
"content": {
"title": "TITLE",
"buttons": [
{
"title": "BUTTON_TITLE",
"value": "BUTTON_VALUE"
}
]
}
}
The important part would be "type": "quickReplies". Since you haven't provided any code I'm not sure if you know how to get to the stage where you enter/edit this JSON. From the documentation on the first page that I linked it would seem that you get to this via:
On the Actions tab of a skill (or on the Requirements tab), you can choose among other things to send messages.
Under the send message button you will be displayed a list of message types to send, quick replies is one of these types. See my screenshots here.
I hope this helps.

I'm not entirely sure how SAPCAI works, but if you are receiving a card in Web Chat, you can use a custom middleware store to convert the card's title to text and its buttons to suggested actions. Then you can add them to the activity in place of the attachment. Note, the store middleware below will convert all card to suggested actions, so you may want to add some additional logic if you do intend to use other cards in your dialog. Also, if the card is an AdaptiveCard, you will need to do some more modifications as well since those tend to more complex than rich cards.
Middleware
const store = createStore(
{},
({ dispatch}) => next => async action => {
if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
const { attachments, from: { role }} = action.payload.activity;
if (role === 'bot' && attachments) {
const text = attachments.map(({ content: { title }}) => title).join(' ');
const actions = attachments.map(({content: { buttons }}) => buttons).flat();
action.payload.activity.text = text;
action.payload.activity.attachments = [];
action.payload.activity.suggestedActions = { actions };
}
}
return next(action)
}
);
renderWebChat({
directLine,
store,
}, document.getElementById('webchat'));
Screenshot
Hope this helps!

Related

Cypress/Testing Dynamic Forms

I just got into Cypress testing and tried to integrate it into a Project but got kind of stumped with testing a form on a site.
What should I be testing on the form ?
My Goal is to make sure that you can't submit the form without entering all fields that are absolutely neccessary and that the information that is provided is "correct".
So my instinct was to write a test like that
it('Submit Form without selecting Gender, Gender shows an Error')
Another test would be then
it('Submit Form without selecting Gender, Gender shows an Error, selecting Gender removes Error')
Would this be a "good" approach ? Is there a "better" one ?
First of all, understand how those frontend validations work. Are they JavaScript validations? Or HTML validations? If HTML, you can check required attribute on elements you need.
How many inputs does the form have? You might be typing a lot of similar test code, therefore I'd go for a data driven approach:
const testData = require('../fixtures/formData.json');
[
{
name: "name",
selector: "#name",
type: "input",
},
{
name: "gender",
selector: "#gender",
type: "checkbox",
},
].forEach(field => {
it(`Cannot send form without ${field}`, () => {
// fill in the form
// e.g. you can prepare a custom command for that
cy
.fillInForm(testData);
// delete one field
if (field.type === 'checkbox') {
cy
.get(field.selector)
.uncheck();
} else {
cy
.get(field.selector)
.clear();
}
// try to submit it
cy
.get('#submitBtn')
.click();
// check it has not been submitted
});
})
Than you can just edit or add data and the test code remains the same, no more typing.

LGTV -WebOS - Is there a way to open an URL on the TV browser?

I am developing a web hosting app for the LG/TV WebOS.
I know that the web hosting app is basically running inside a browser engine (webkit?).
When the user is about to make the payment (I am using Paypal because I don't like PaymentWall), the app directs the user to the paypal confirmation page where there is no mean for the user to click on the CORFIRM PURCHASE button. I don't see the "mouse" cursor and there is no documentation I could find about the theme.
So, I was thinking if I could launch that payment page on the tv's browser.
That browser has a cursor that moves when I click the arrows.
Any way to do that? To launch an URL from the app into the television browser? or to make the cursor appear inside the app?
I used this approach.
openLink(url: string): void {
webOS.service.request("luna://com.webos.applicationManager", {
method: "launch",
parameters: {
id: "com.webos.app.browser",
params: {
target: url,
},
},
onSuccess: (res: any): void => {
console.log("Browser open success. ", res);
},
onFailure: (res: any): void => {
console.log("Browser open fail. ", res);
},
});
}
It requires webOS.js or webOSTV.js library.
Personally, I use the following library to enhance navigation for web based applications.
https://github.com/luke-chang/js-spatial-navigation
It creates fluid navigation between UI elements using just the arrow buttons.
The user experience is basically identical to that of native applications like Netflix.

I need to add style to action button in adaptive card

I am using Adaptive Card 1.2 to be displayed on MS Teams. I want to style the action button but not able to do do.
subCard.Actions = new List<AdaptiveAction>() {
new AdaptiveSubmitAction {
Title = "Ok",
DataJson = "{ \"Type\": \"response_feedback\" }",
Style="positive",
}
};
From my testing, it seems like Style is not implemented in Teams, at least not at the moment. By the way, you can use App Studio to test this - there's a tab there called "card editor" where you can create card JSON, see a preview, and even send it to yourself.

How to pass a value to the bot using the ImBack type but witout showing the text on the chat window

I have defined a ThumbnailCard using this structure:
private static Attachment GetThumbnailCard()
{
var thumbnailCard= new ThumbnailCard
{
Title = "title",
Subtitle = "subtitle",
Text = "text",
Images = new List<CardImage>() { new CardImage(url: "http://example.com/image.jpg")},
Buttons = new List<CardAction>() { new CardAction(type: ActionTypes.ImBack, title: "Product 1", value: "Product 01") },
};
return thumbnailCard.ToAttachment();
}
And everything is working fine, the button is showing "Product 1" and when the user uses the button you see on the chat window the content of value in this case "Product 01".
But i need more functionality, so i review the documentation and found other parameters of the cardAction class like text and displayText.
So that when the user uses the button on the chat you will see "I want to buy Product 01", and the bot will get the Id of that product so it can check it on the database.
I have done this according to the documentation:
new CardAction(type: ActionTypes.ImBack, title: "Product 1", value: "IdOfProduct01", text: "Product 01", displayText: "I Want to buy Product 01")
And i am receiving the value on this method:
private async Task ReceivingButtonClick(IDialogContext context, IAwaitable<object> result)
{
var rpta = await result as Activity;
}
But when i try to access rpta.value it is always null, and the chat windows is still showing the content of value instead of displayText or text
Does anyone knows if maybe this functionality is not available yet, i'm using Microsoft.Bot.Builder 3.13.1 and i am testing this on the emulator, or maybe i'm receiving the result as an Activity and i should be receiving it as something else.
Thanks in advance for the answers.
This is the current behavior in BOT Framework. ImBack is similar to just typing a message and it sends back whatever was set in the 'value'. I guess you don't want to show id to the user when he/she clicks the product button. You can instead use ActionTypes.PostBack as type and that will send back the message to BOT but not show it on chat.
Try this:
new CardAction(type: ActionTypes.PostBack, title: "Product 1", value: "IdOfProduct01", text: "Product 01", displayText: "I Want to buy Product 01")
The problem with using PostBack is that it is not supported in all channels. So, make sure to check it for the channel you are implementing your bot for.
Does anyone knows if maybe this functionality is not available yet, i'm using Microsoft.Bot.Builder 3.13.1 and i am testing this on the emulator, or maybe i'm receiving the result as an Activity and i should be receiving it as something else.
The displayText and Text properties of CardAction were new added in v3 SDK, problem is that DirectLineJS hasn’t been modified to support these properties yet. You may refer to the source code of directLine.ts to see that problem.
And our emulator uses WebChat and WebChat uses DirectLine, so for ActionTypes.ImBack, that cause the problem.
You may submit an issue on Github BotFramework-WebChat, it will be convenient for us to monitor this issue. Thank you.

Firefox WebExtension notifications API: How to call a function when the notification is clicked

I'm trying to develop a Firefox add on using WebExtensions. What I'm trying to do is open a new Firefox tab or window when the user clicks the notification. But it doesn't work.
When I click the notification, nothing happens.
I'm creating notifications like:
var q = chrome.notifications.create({
"type": "basic",
"iconUrl": chrome.extension.getURL("128-128q.png"),
"title": 'title',
"message": 'content'
});
chrome.notifications.onClicked.addListener(function(notificationId) {
window.open('http://www.google.com');
});
browser.notifications.onClicked.addListener(function(notificationId) {
window.open('http://www.google.com');
});
q.onClicked.addListener(function(notificationId) {
window.open('http://www.google.com');
});
var audio = new Audio('message.mp3');
audio.play();
How can I make this work?
It appears that the problem is that you are attempting to open a new URL in a way that does not work.
The following should work, using chrome.tabs.create():
var q = chrome.notifications.create("MyExtensionNotificationId", {
"type": "basic",
"iconUrl": chrome.extension.getURL("128-128q.png"),
"title": 'title',
"message": 'content'
});
chrome.notifications.onClicked.addListener(function(notificationId) {
chrome.tabs.create({url:"http://www.google.com"});
});
However, you need to be testing this in Firefox 47.0+ as support for chrome.notifications.onClicked() was only added recently. My statement of Firefox 47.0+ is based on the compatibility table. However, the compatibility table has at least one definite error. Thus, a higher version of Firefox may be required. The code I tested worked in Firefox Nightly, version 50.0a1, but did not work properly in Firefox Developer Edition, version 48.0a2. In general, given that the WebExtensions API is in active development, you should be testing against Firefox Nightly if you have questions/issues which are not functioning as you expect. You can also check the source code for the API to see what really is implemented and when it was added.
Note: this works with either chrome.notifications.onClicked or browser.notifications.onClicked. However, don't use both to add two separate anonymous functions which do the same thing as doing so will result in whatever you are doing happening twice.
I did not actually test it with your code, but I did test it with a modified version of the notify-link-clicks-i18n WebExtensions example. I modified the background-script.js file from that example to be:
/*
Log that we received the message.
Then display a notification. The notification contains the URL,
which we read from the message.
*/
function notify(message) {
console.log("notify-link-clicks-i18n: background script received message");
var title = chrome.i18n.getMessage("notificationTitle");
var content = chrome.i18n.getMessage("notificationContent", message.url);
let id = "notify-link-clicks-i18n::" + title + "::" + message.url;
chrome.notifications.create(id,{
"type": "basic",
"iconUrl": chrome.extension.getURL("icons/link-48.png"),
"title": title,
"message": content
});
}
/*
Assign `notify()` as a listener to messages from the content script.
*/
chrome.runtime.onMessage.addListener(notify);
//Add the listener for clicks on a notification:
chrome.notifications.onClicked.addListener(function(notificationId) {
console.log("Caught notification onClicked with ID:" + notificationId);
//Open a new tab with the desired URL:
browser.tabs.create({url:"http://www.google.com"});
});
My preference for something like this where a unique ID is possible is to provide a unique ID, that both identifies that the notification was displayed by my add-on and what the notification was. This allows the listener function to choose to only act on notifications which were displayed by my add-on and/or only a subset of those I display, or have different actions based on why I displayed the notification.
The reason you had trouble here in figuring out what was not working is probably because you did not reduce the issue to the minimum necessary to demonstrate the issue. In other words, it would have been a good idea to just try opening a new URL separately from the notifications onClicked listener and just try a console.log() within the listener.

Resources