quickreplies coding for node-wit example messenger.js - node.js

I cannot get quickreplies to work in node-wit messenger.js example.
I tried may things including:
1) Updated this line of code Our bot actions (messenger.js):
const actions = { send({sessionId}, {text,quickreplies})
2) And Updated this line of code Our bot actions (messenger.js):
return fbMessage(recipientId, text,quickreplies)
3) Updated my custom action in messenger.js :
getWelcome({context, entities})
{
context.welcome = "how are you. Please select from the options below";
context.welcome.quickreplies = [
{
title: 'Choice A',
content_type: 'text',
payload: 'empty'
},
{
title: 'Choice B',
content_type: 'text',
payload: 'empty'
},
]
return context;
},
4) I tried so many permutations. I cant get it to work with node-wit messenger.js example. The quick repliess are not displayed I searched and read all documentation
5) Can you help with this and also exactly how to retrieve the quick reply selected in messenger.js
Thanks

Sorry if it is late, but I guess you were looking for this:
send(request, response) {
const {sessionId, context, entities} = request;
let {text, quickreplies} = response;
if(text.substring(0,6) === IDENTIFY_PAYLOAD){
text = text.substring(6); // It is a payload, not raw text
} else {
text = {"text": text};
}
if(typeof quickreplies !== "undefined"){
text.quick_replies = response.quickreplies
.map(x => { return {
"title": x, "content_type": "text", "payload": "empty"}
});
}
}
What I did was sending a payload with a string that identifies it is not raw text (That's why I use an identifier for a payload), then in the send action I receive the two parameters request and response, and I get their attributes. Finally, I convert the payload params using a routine which I find on internet and that in fact works well to map the quick replies.

Related

PayPal. Some required information is missing or incorrect. Please correct the fields below and try again

I have a form with data, which I send to 'https://pilot-payflowpro.paypal.com' API by HTTP post query. I send 'requestString' as string with different values, joined by '&'. If I have value with quotes like - ' or " or - (example - Les Clefs dOr Fund - see on the picture) - then I got such an error.
public async sendPayPalRequest(
donationInfo: DonationInfoModel,
// urlBase: string,
): Promise<string> {
const templateString = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const transactionDetails: IPayPalTransactionInfo = {
....
currency: 'USD',
comment1: StringHelper.replaceQuotationWithBacktick(donationInfo.fundName),
....
};
....
const requestParameters = [];
for (const key in transactionDetails) {
if (transactionDetails.hasOwnProperty(key)) {
requestParameters.push(
`${key.toUpperCase()}[${transactionDetails[key].length}]=${transactionDetails[key]}`,
);
}
}
const response = await axios.post(process.env.PAYPAL_TRANSACTION_URL, requestString, {
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
});
return response.data;
}
...
If I replace quotes with "", or "^" - the payment is successful.
Does anybody overcome such an issue?
The *payflowpro.paypal.com endpoint recognizes [#] length tags so the parser knows to ignore special characters in values like & or =.
COMPANYNAME[14]=Ruff & Johnson
COMMENT1[7]=Level=5
Quotes, however, are not supported.
See the documentation: https://developer.paypal.com/api/nvp-soap/payflow/integration-guide/simple-transaction/#link-usespecialcharactersinvalues

Axios GET request is returned a wrong response (checked with Postman)

I'm a very newbie in JS (I started to learn 3 days ago) and can't find what happened wrong in my code.
I'm trying to get the API response from the following URL: https://api.dictionaryapi.dev/api/v2/entries/en/dog
If the request is made with Postman the response is right. That's means, the dict returned in JSON has the field "meaning" and the sub-fields "partOfSpeech", "definitions", ...
"meanings": [
{
"partOfSpeech": "noun",
"definitions": [
{
"definition": "A mammal, Canis familiaris or Canis lupus familiaris, that has been domesticated for thousands of years, of highly variable appearance due to human breeding.",
"synonyms": [],
"antonyms": [],
"example": "The dog barked all night long."
But if I try to get the response in my code with Axios, the field "meaning" has the wrong structure, and seems that some parts of the response are lost.
The structure returned by axios:
meaning: { noun: [Array], verb: [Array] }
In "meaning", sub-fields like "partOfSpeech" doesn't exist!
noun: [
{
definition: 'Meat from a dog eaten as food.',
synonyms: [],
antonyms: [],
example: 'We visited South Korea this time around, where we ate dog meat for the first time.'
},
{
definition: 'Meat prepared to be given to a dog as food.',
synonyms: [],
antonyms: []
},
Below is a part of my code
const axios = require('axios').default;
async function getInfo(URL, params) {
try {
const response = await axios.get(URL, params);
return response.data;
} catch (error) {
return error;
}
}
function searchWordDefinition(word) {
return getInfo(dict_URL + word);
}
var dict_URL = 'https://api.dictionaryapi.dev/api/v1/entries/en/';
var words = ["dog"];
Your Axios script uses the V1 of the API, your POSTMAN example uses V2.
Change your dict_URL to https://api.dictionaryapi.dev/api/v2/entries/en/ and you should be good.

postback button in dialogflow messenger cx

Hello I'm trying to create a flow in dialogflow cx, where in case of multiple options I want my user to select 1 option where all the options are buttons.
I have used the default payload but not sure how can I send back which button got clicked to my webhook and return respective info, currently if I click on button it simply open example.com, if I exclude the link it opens same page in new tab.
{
"type": "button",
"icon": {
"type": "chevron_right",
"color": "#FF9800"
},
"text": "Button text 1",
"link" : "www.example.com",
"event": {
"name": "some name",
"languageCode": "en",
"parameters": {}
}
}
For your use case, since the button response type always redirects to a page when clicked, you can consider using suggestion chips instead.
{
"richContent": [
[
{
"options": [
{
"text": "Chip 1"
},
{
"text": "Chip 2"
}
],
"type": "chips"
}
]
]
}
Suggestion chips act like a user text query when the user clicks on it, therefore, you can just create a route that can be triggered by text of the chip and get the text query from the webhook request sent to your webhook to return the respective information. For example:
Intent:
Route:
Then in your webhook, you can get the parameter value in the text field of the webhook request which you will refer to in order to create a webhook response with the respective information.
Here’s an example in Node.js using Express:
app.post("/webhook", (req, res) => {
let option = req.body.text;
let jsonResponse = {
fulfillment_response: {
messages: [
{
text: {
//fulfillment text response to be sent to the agent
text: [`You've chosen the ${option} option`]
}
}
]
}
};
res.json(jsonResponse);
});
Alternatively, you can also use entity types and assign the selected chip into a parameter that will be also sent to your webhook.
To assign the text of the chip to a parameter, the intent of the route should contain training phrases that are annotated to an entity type containing all of the options. For example:
Intent:
Entity Type:
Then in your webhook, you can get the parameter value in the intentInfo.parameters.parameter_id.resolvedValue field of the webhook request which you will refer to in order to create a webhook response with the respective information.
Here’s an example in Node.js using Express:
app.post("/webhook", (req, res) => {
let option = req.body.intentInfo.parameters.options.resolvedValue;
let jsonResponse = {
fulfillment_response: {
messages: [
{
text: {
//fulfillment text response to be sent to the agent
text: [`You've chosen the ${option} option`]
}
}
]
}
};
res.json(jsonResponse);
});
Results:
There is a simple albeit hacky way I have discover possible (tested in es). Which is to make a chip and get its element then force clicking it
We can listen to button click and I detect that it was empty button with just text. Then I use renderCustomCard to make a chip. Everything inside dialogflow messenger are hidden deep inside nested shadowRoot. But as of now its structure allow us to get the chip out to call click() on it. In effect it make it seem very the same as user manually click the chip
const dfMessenger = document.querySelector('df-messenger');
dfMessenger.addEventListener('df-button-clicked',function(event) {
if(event.detail.element.event || event.detail.element.link)
return;
dfMessenger.renderCustomCard([
{
"type": "chips",
"options": [
{
"text": event.detail.element.text
}
]
}
]);
var messageList = dfMessenger.shadowRoot.querySelector("df-messenger-chat").shadowRoot.querySelector("df-message-list").shadowRoot;
var chips = [...messageList.querySelectorAll("df-chips")].flatMap((chips) => [...chips.shadowRoot.querySelectorAll(".df-chips-wrapper>a")]).filter((a) => a.innerHTML.indexOf(event.detail.element.text) > -1);
if(chips.length > 0)
chips.slice(-1)[0].click();
});
Working for today. No guarantee they will block this method in the future. But I actually guess they would implement actual postback button in similar manner later after beta version

How to create Adaptive card and continue dialog on specific response in azure bot node v4

I am currently trying to create a waterfall that starts with an adaptive card.
Originally I had the waterfall working with ChoicePrompt on every step, but on step 1 I wanted 2 choices to openUrl - so changed to an adaptive card to start (is this required or is there a way to openUrl from a specific response in ChoicePrompt the user gives?)
The issue here is that every response (that isn't the openUrl buttons) leads to the adaptive card to repeat itself rather than pass the non-openUrl choice to the next step of the dialog.
I am also storing each dialog response (currently in an array, which I clear at the end of the dialog) to perform a certain action based on all responses combined (is there a better way to save user responses than pushing into an array?)
var answers = [];
async firstStep(stepContext) {
var send = {
text: 'question',
attachments: [
{
"contentType": "application/vnd.microsoft.card.hero",
"content": {
"text": null,
"buttons": [
{
"type": "imBack",
"title": "one",
"value": "one"
},
{
"type": "openUrl",
"title": "two",
"value": "https://example.com"
},
{
"type": "openUrl",
"title": "three",
"value": "https://example.com"
},
]
}
}
]
}
return await stepContext.context.sendActivity(send);
}
async secondStep(stepContext) {
const resp = stepContext.result.value;
answers.push(resp);
return await stepContext.prompt('ChoicePrompt', {
prompt: questions[1],
choices: ChoiceFactory.toChoices(options[1]),
style: ListStyle.suggestedAction
});
}
async thirdStep(stepContext) {
const resp = stepContext.result.value;
answers.push(resp);
return await stepContext.prompt('ChoicePrompt', {
prompt: questions[2],
choices: ChoiceFactory.toChoices(options[2]),
style: ListStyle.suggestedAction
});
}
async finalStep(stepContext) {
const resp = stepContext.result.value;
answers.push(resp);
// get func
var fun = await this.func(answers);
//do stuff with what function returns
// reset quiz
answers = [];
return await stepContext.endDialog();
}
So to summarise, the initial adaptive card, I would like it to continue to repeat itself if anything other than one is returned, but if one is returned I would like it to move onto the next part of the dialog with that value, and save that value (maybe better than I do above).
Lastly, if there is an easy way to openUrl in the current tab and not a new one that would be great.
Any insight here on how to work with hero cards would be really helpful.
Thanks in advance.

Creating an SMS group forwarder in Twilio with NodeJS

Here's what I'm trying to accomplish:
Set a list of names and numbers (my "group")
When a text message is sent to the Twilio number, forward it on to every member in the group
At a high-level, the idea seems straight forward enough. My programming / syntax skills are rusty, though, and I'd love some help.
I'm using Twilio Functions, and I've been able to send and receive messages successfully. Now I am stuck on how to carry this idea of iterating through a group.
Here's what I've written so far:
var groupmembers = {
jonathan:{
name: 'Jonathan',
number: '+0000000000'
},
joshua:{
name: 'Joshua',
number: '+1110000000'
}
}
exports.handler = function(context, event, callback) {
// Set some values for later use
this.fromNumber = event.From
this.body = event.Body || ''
let twiml = new Twilio.twiml.MessagingResponse();
groupmembers.forEach(function(member) {
// Skip sending if it's the same number
if (member.number === this.fromNumber ) {
return;
}
// Otherwise, let's send a mesage!
twiml.message("Hello World").to( member.number );
callback(null, twiml);
});
};
The issues I believe I have:
Not being sure how to properly set my array or "dictionary"
Not knowing the proper syntax for passing the "to" variable to the message
Not knowing the proper syntax for doing a loop in NodeJS (the Functions console is telling me that 'groupmembers.forEach is not a function')
Thank you for any and all feedback and for pointing me in the right direction!
The mistake you have is pretty simple. groupmembers is an object, you want an array.
You may want something akin to this instead:
var groupmembers = [{
name: 'Jonathan',
number: '+0000000000'
},
{
name: 'Joshua',
number: '+1110000000'
}]
Apart from that, it looks to me as okay.
With more searching and the point in the right direction from Slava I was able to figure it out! Here's the complete code:
/**
* Represents a search trough an array.
* #function search
* #param {Array} array - The array you wanna search trough
* #param {string} key - The key to search for
* #param {string} [prop] - The property name to find it in
* Props: https://stackoverflow.com/a/33097318/315818
*/
function search(array, key, prop){
// Optional, but fallback to key['name'] if not selected
prop = (typeof prop === 'undefined') ? 'name' : prop;
for (var i=0; i < array.length; i++) {
if (array[i][prop] === key) {
return array[i];
}
}
}
var groupmembers = [
{
name: 'Jonathan',
number: '+000000000'
},
{
name: 'Joshua',
number: '+111111111'
}
];
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.MessagingResponse();
// Search for the group member that matches the sender number
let sender = search(groupmembers, event.From, 'number');
// Now, loop through each of the group members
groupmembers.forEach(function(member) {
// Skip sending if it's the same number
if (member.number === event.From ) {
return;
}
// Now, forward on the message to the group member, using the sender's name
twiml.message(`${sender.name}: ${event.Body}`, {
to: member.number
});
});
// Loop ended
callback(null, twiml);
};

Resources