Testing different utterances for a match - alexa

I am new to programming for Amazon Echo. I am using Node.js and am trying to return a different response based on my utterance of a name.
For example if I say the name "David" or "James" or "Benjy" Alexa should just say "Welcome [and the name I said]" but if I say "Jonathan" it should say "Yay! Welcome home Jonathan".
But when I say "Jonathan" it just says "Welcome Jonathan".
I have been modifying the basic alexa-skills-kit-color-expert Lambda and have modified the setColorInSession() function in that code. I have renamed that function setAndWelcomePerson().
I have tried:
to use an if statement to test my utterance and have Alexa reply based on my utterance
to give different examples of utterances to try to teach Alexa to differentiate between one name and the next.
None of this seems to work. Please tell me what I'm doing wrong and suggestions to fix. Some code below:
The setAndWelcomePerson() function from my Lambda code:
/**
* Sets the name of the person(s) and welcomes them.
*/
function setAndWelcomePerson(intent, session, callback) {
var cardTitle = intent.name;
var whoToGreetSlot = intent.slots.Person;
var repromptText = null;
var sessionAttributes = {};
var shouldEndSession = false;
var speechOutput = "";
if (whoToGreetSlot) {
var whoToGreet = whoToGreetSlot.value;
sessionAttributes = createWhoToGreetAttributes(whoToGreet);
if (whoToGreet === "Jonathan") {
speechOutput = "Yay! Welcome home " + whoToGreet + "!";
} else {
speechOutput = "Welcome " + whoToGreet + ".";
}
} else {
speechOutput = "I'm not sure who you want me to welcome. Please try again.";
}
callback(sessionAttributes,
buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}
My intent schema:
{
"intents": [
{
"intent": "WhoShouldBeGreeted",
"slots": [
{
"name": "Person",
"type": "LITERAL"
}
]
},
{
"intent": "AdditionalGreetingRequest",
"slots": []
}
]
}
My sample utterances:
WhoShouldBeGreeted {Sam and Cat|Person}
WhoShouldBeGreeted {Jonathan|Person}
WhoShouldBeGreeted {James|Person}
WhoShouldBeGreeted {Benji|Person}
WhoShouldBeGreeted welcome {Sam and Cat|Person}
WhoShouldBeGreeted welcome {Jonathan|Person}
WhoShouldBeGreeted welcome {James|Person}
WhoShouldBeGreeted welcome {Benji|Person}
Thank you for your help.

You are using a "LITERAL" slot type. (This is discouraged, but it is still supported.) That means you are just recognizing a word. Spoken words have no case. But the === operator in Javascript is case sensitive. If you check your logs, I suspect that when you say "Jonathan" what you get is "jonathan", which your match then fails.
To fix this you can change your compare to lower case, or change the operator to a case insensitive string compare (see here).
Another approach would be to not use the LITERAL slot type, and use the AMAZON.US_FIRST_NAME instead. Since this knows it is a name, it returns it capitalized.

Related

Discord JS | Server: detect re-joined user

I would like that my bot detect when a user is joining again, back, to the server. I have made a JSON file containing the ID of the users that left the server. If the new member's ID is included in the JSON file with IDs, then it (the bot) should give a special welcome message.
The code shown is not working, meaning it is not really taking any ID from the JSON file. I guess that it needs a bucle with the lenght of the JSON's content to compare the Member ID with Badmember.USERS_LEFT_ID[].
JSON file: user_left.json
{
"USERS_LEFT_ID" : [
"1",
"2",
"3",
"4",
]
}
JS File: guildMemberAdd.js
const Badmember = require('users_left.json')
exports.run = async(client, newmember) => {
const channel = newmember.guild.channels.cache.get(Wlc_ch);
if (newmember.user.id == Badmember.USERS_LEFT_ID) {
channel.send('Special message!')
} else {
console.log(newmember);
console.log(newmember.user.id + ' has joined');
}
}
To check if the user is in the ID list, need to somehow iterate through the array. One option is to use the built-in includes() method.
const Badmember = require('users_left.json')
exports.run = async(client, newmember) => {
const channel = newmember.guild.channels.cache.get(Wlc_ch);
if (Badmember.USERS_LEFT_ID.includes(newmember.user.id)) {
channel.send('Special message!')
} else {
console.log(newmember);
console.log(newmember.user.id + ' has joined');
}
}
Check out MDN docs for more details on Array.prototype.includes(): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes

How to create a randomized search query for a discord bot

I want to make a bot command that allows the user to get a random image of their choice. (!image dog)
How do I let the user input the image that they want to be searched? This is what I have so far:
client.on("message", (message) => {
let args = message.content.substring(prefix.length).split(" ");
switch (args[0]) {
case "dog":
image(message);
break;
}
});
function image(message) {
var options = {
url: "http://results.dogpile.com/serp?qc=images&q=" + "dog",
method: "GET",
headers: {
Accept: "text/html",
"User-Agent": "Chrome",
},
};
request(options, function (error, response, responseBody) {
if (error) {
return;
}
$ = cheerio.load(responseBody);
var links = $(".image a.link");
var urls = new Array(links.length).fill(0).map((v, i) => links.eq(i).attr("href"));
console.log(urls);
if (!urls.length) {
return;
}
// Send result
message.channel.send(urls[Math.floor(Math.random() * urls.length)]);
});
}
Your current code is on the right track to doing what you want. You simply need to properly use your args variable.
The Problem(s)
First of all, you need to separate your args from your command. The code you provided in your question currently counts the command itself ("image") as the first arg and "dog" as the second arg, whereas "dog" is supposed to be the first argument of the command. So we need to separate the command from the args, and we can do so easily with Array.shift() which removes the first element of an array and returns it.
In your current code, you have a switch statement that checks the value of the first arg in the message (args[0]), and if the first arg is "dog", you are searching for dog images. But what you want to do is you want to be able to search for more than just dogs, for whatever image the user wants. That means you'll first need to get rid of the argument-checking switch, because as it is now it will only allow users to search for "dog".
Next, you need to realize that you want to search for an image of whatever the user puts in. So what's the value that the user is putting in? It's contained in args[0]. So if you want to be able to search for whatever args[0] is, instead of doing "http://results.dogpile.com/serp?qc=images&q=" + "dog" you need to instead do "http://results.dogpile.com/serp?qc=images&q=" + args[0]. In order to do that, you'll also need to give your image method access to args[0], which you can do by passing it in as an argument of the method.
The Solution
So here's how these simple fixes will look in your code:
client.on("message", (message) => {
let args = message.content.substring(prefix.length).split(" ");
let command = args.shift(); //separates and removes command from args
switch (command) {
case "image":
image(message, args[0]);
break;
}
});
function image(message, query) {
//'query' now represents 'args[0]'
var options = {
url: "http://results.dogpile.com/serp?qc=images&q=" + query,
method: "GET",
headers: {
Accept: "text/html",
"User-Agent": "Chrome",
},
};
request(options, function (error, response, responseBody) {
if (error) {
return;
}
$ = cheerio.load(responseBody);
var links = $(".image a.link");
var urls = new Array(links.length).fill(0).map((v, i) => links.eq(i).attr("href"));
console.log(urls);
if (!urls.length) {
return;
}
// Send result
message.channel.send(urls[Math.floor(Math.random() * urls.length)]);
});
}
However, note that this will only take the first argument and search for it (it will not include spaces). So if you did !image furry cats, it would only look up images of "furry" and not "furry cats". If you want to account for spaces, then use all of the args instead of just args[0]. You could do so by replacing all occurrences of args[0] in this code with args.join(" ").
Let me know if I misunderstood your question. This answer is solely based off of the somewhat limited information you provided in your question and comment.

dataSource.filter filters ID which is not visible in frontend

Basically, this is my code
applySearchFilter(filterValue: string) {
this.values = filterValue.toString().toLowerCase();
this.dataSource.filter = this.values;
}
with "this.dataSource.filter" I can filter any data out of it, but I want to add an exception to keyId.... See the object below.
my object looks something like this.
{
keyId: "00000000-0000-0000-0000-000000010001",
name: "Test 10",
location: "New York"
}
When I input "10", it filters everything in the dataSource that has a 10. So, not only the name has "10" in it, but the keyId too. I took an hour to realize what this kept happening... and it is because of keyId xD
Allthough I get the whole object with keyId included, keyId is not shown in frontend and should not be search/filter-able. Even if it does not show keyId in frontend, he searches for the object that has that keyid
I am not sure how to fix it right now, since something like this.dataSource.data.name or so does not work...
I'd appreciate if you gals and girls would give me some tips.
Oh and here is where I get my data to that get filtered.
public getObject(): void {
this.service.getObjectMethod().then(data=> {
this.dataSource.data = data;
}, (err: Error) => { console.log(err.message); });
}
i solved this issue using.
public filterData(): void {
if (this.inputValues.trim().length !== 0) {
this.filteredValues = [];
this.dataSource.data.filter(data => {
if (data.name.toLowerCase().includes((this.inputValues).toLowerCase()) ||
data.opensCategory.toLowerCase().includes((this.inputValues).toLowerCase())) {
return true && this.filteredValues.push(data);
}
});
this.dataSource.data = this.filteredValues;
}
}
Use the following code to search.
applySearchFilter(filterValue: string) {
this.dataSource.filterPredicate = function (data: any, filterValue: string) {
return data.name.trim().toLocaleLowerCase().indexOf(filterValue.trim().toLocaleLowerCase()) >= 0;
};
}

HTTPS Post URL with an Array of Objects

I've been trying to format a URL POST with objects in my array as they're required to post to my REST API. I'm forced to use an archaic API POST system from a vendor and have been trying to hack together a solution.
Basically the JSON looks similar to:
{"api_key": "12234",
"server_id:"qwp2222",
"recipients": [
{"email":"john#doe.com",
"name": "john doe"}]
}
I am trying to format the Array'd key-value pairs as part of the URL so it would post to the endpoint without much fanfare.
I have the rest of the URL together without issue, it is just the recipients' array that is the problem.
How should I write the POST URL in order to make sure that I can post the array of objects correctly?
I appreciate all the help in advance!
Does not work here, but this code will make linear structure from your JSON, create form from and submit to itself.
If you run it on local or anywhere else and add f.action = 'your get or post page', it will send data properly.
var data = {
api_key: '12234',
server_id: 'qwp2222',
recipients: [{
email: 'john#doe.com',
name: 'john doe'
}]
};
function collectItems(res, json) {
for (var a in json) {
if (json[a].constructor === Array ||
json[a].constructor === Object) {
collectItems(res, json[a]);
} else {
res.push([a, json[a]]);
}
}
}
var all = [];
collectItems(all, data);
var f = document.createElement('FORM');
f.method = 'post';
// default get: ...?api_key=12234&server_id=qwp2222&email=john#doe.com&name=john+doe
for (var e in all) {
var i = document.createElement('INPUT');
i.name = all[e][0];
i.value = all[e][1];
i.type = 'hidden'; // do not show values sent
f.appendChild(i);
}
if (location.search) {
alert("Submit result:" & location.href);
} else {
document.body.appendChild(f);
f.submit();
}

Amazon Alexa Skill

I want to ask alexa different sorts of questions and then at the end I want it should ask "Is there anything else you would like to know?" and when I say yes (where yes is working suggestion) it should suggest me according to the intent I am in. Like if I am in
IncityIntent:
'InCityIntent': function () {
speechOutput = '';
speechOutput = "The atmosphere in the city is beautiful. Is there anything else you would like to know";
this.emit(":ask", speechOutput, speechOutput);
'YesIntent': function () {
speechOutput = '';
/*when the user say yes, he should get this output*/
speechOutput = You can learn more about city by trying, alexa what are the best places in the city";
this.emit(":tell",speechOutput, speechOutput);
FoodIntent:
'FoodIntent': function () {
speechOutput = '';
speechOutput = "Food in the city is delicious. Is there anything else you would like to know";
this.emit(":ask", speechOutput, speechOutput);
'YesIntent': function () {
speechOutput = '';
/*change in response here*/
speechOutput = You can learn more about food by trying, alexa what are the best restaurants in the city";
this.emit(":tell",speechOutput, speechOutput);
First thing, do not create custom YesIntent and NoIntent, instead use AMAZON.YesIntent and AMAZON.NoIntent. You can always add utterences to these predefined intents if you want to.
Your issues can be solved in a couple of ways.
Using sessionAttributes:
Add a previousIntent attribute or something to keep a track of the converstation in the sessionAttributes when you receive the initial request, say InCityIntent. And in your AMAZON.YesIntent or AMAZON.NoIntent handler check for the previous intent and reply accordingly.
'InCityIntent': function () {
const speechOutput = "The atmosphere in the city is beautiful. Is there anything else you would like to know";
const reprompt = "Is there anything else you would like to know";
this.attributes['previousIntent'] = "InCityIntent";
this.emit(":ask", speechOutput, reprompt);
}
'Amazon.YesIntent': function () {
var speechOutput = "";
var reprompt = "";
if (this.attributes
&& this.attributes.previousIntent
&& this.attributes.previousIntent === 'InCityIntent' ) {
speechOutput = "You can learn more about city by trying, Alexa what are the best places in the city";
reprompt = "your reprompt";
} else if ( //check for FoodIntent ) {
// do accordingly
}
this.attributes['previousIntent'] = "Amazon.YesIntent";
this.emit(":ask", speechOutput, reprompt);
}
Using STATE handlers
ask-nodejs-sdk v1 has state handlers to generate response based on states. The idea is similar, the sdk will add the sessionAttribute parameter for you and the when will automatically map the handler with respect to that state.
'InCityIntent': function () {
const speechOutput = "The atmosphere in the city is beautiful. Is there anything else you would like to know";
const reprompt = "Is there anything else you would like to know";
this.handler.state = "ANYTHING_ELSE";
this.emit(":ask", speechOutput, reprompt);
}
const stateHandlers = Alexa.CreateStateHandler("ANYTHING_ELSE", {
"AMAZON.YesIntent": function () {
var speechOutput = "You can learn more about city by trying, Alexa what are the best places in the city";
var reprompt = "your reprompt";
this.emit(":ask", speechOutput, reprompt);
},
Once a state is set, then next time the the intent handlers defined in that particular state handler will be triggered. Change your state accordingly and once done, remove it.

Resources