Pokemon Evolutions package doesn't get chains right - bots

I am working on a pokemon bot and I am stuck in somewhere.
I want to make a command to check if a pokemon contains the second pokemon in its evolution line or not. I am using a node.js package called Evolutions.
Here is code:
if (command === 'does') {
const b = evolution.getEvolutionChain(args[0]);
const c = args[1]
if (b.includes(c)) {
msg.channel.send("True!")
} else {
msg.channel.send("False!")
};
}
The code doesn't give an error but it always sends False!.
I used in my server (where the bot is) a! is the prefix.
a!does charmander charmeleon
Everything is correct but it always sends False!, even if it needs to be True!.

Since the evolution.getEvolutionChain(args[0]); returns the values with capitalized letters it's easiest if you turn all the values (both the array of evolutions as the user given evolution) to lowercase. After that you can check if the array contains the given evolution.

Related

How to check contents of message without including User's name in contents

Ive got a command im testing that enables the Bot to send me a message when a specific string is passed into a Channel.
My command works fine, but the bot seems to also include the user's name into the contents as well.
I found this out by testing with my name as the string the bot is running an if statement for, and found out that even typing just Hi, or any other message triggers the if statement as my name 'Jerseyetr" is included in the contents of the message.
if (message.content.includes('jersey') || ('jerseyetr')) {
try{
console.log('it worked!');
//jersey.send('testing!');
//client.users.get("xxxxxx").send("test");
return;
//guild.member
}
catch (err){
console.log(err);
console.log('it did NOT work. Sad face');
}
}
What would be the proper way to filter out the User's name?
The problem here is that you always enter in the if, and never in the else (if there's one).
Why? Because of your condition. The OR condition || will test first the message.content.includes('jersey'). If it's true it will enter in the if, if it's false it will test the second condition. But your second condition is always true, because the string 'jerseyetr' is not empty and is considered true by Javascritps.
This page list all the things that are considered falsy. All the other value are considered true.
I guess you wanted to do this:
if (message.content.includes('jersey') || message.content.includes('jerseyetr')) {
...
}
However, the second expression isn't needed because jersey is contained in jerseyetr, so if the message contains jerseyetr, it will also contains jersey.
You'll see that Discord doesn't includes the username in the message.content but only the text of the message
Side note: if you want to test something like this in your message, you can maybe use a regexp:
/jersey/.test(message.content)

Loop with break or continue based on user input

I loosely know how to use a loop. I've used simple ones and understand how it works. However, I have a situation where I think a loop would be useful, but I'm not quite certain how to get the desired result. I am self-taught in javascript and I've tried poking around some other posts, but I don't recall seeing anything that helped in my case. This may very well be one of my gaps in understanding.
What I'd like this to do: This code is going to be taking the top card off a deck of cards for a TCG. Each card has a "type". I want the person to input a command into Discord like "!flipcard 10". I would like the bot to make a loop to flip cards off the top of the deck until 10 is reached OR a certain card type is flipped over. The card "types" I am working with are "action", "evo", "flip" and "hero". I only want the bot to react differently to the "Flip" card type. I have gotten working code to where I can do this if the user inputs !flipcard but does it one at a time. The maximum amount of cards that can ever be flipped is 34 cards or so, but this is less likely to happen than lower numbers.
I just wrote the code up from what I think I need to do, but I get stuck with not knowing exactly where to go next. So it isn't exactly functioning code yet. I do get an "illegal break statement" error currently, so I can't progress much more.
for (i = 0; i < damageToTake; i++) {
cardRemoved = deckAarray.shift()
//Bunch of stuff edited out for Embed mentioned below
if (cardType == "Flip"){
message.channel.send(flipEmbed)
const filter = m => m.author.id === message.author.id
author.send(`You revealed a Flip card! Do you wish to use it? Please reply with 'yes' or 'no'.`).then((newmsg) => {
newmsg.channel.awaitMessages(filter, {
max: 1,
}).then((collected) => {
reply = collected.first();
if (reply.content == 'yes'){
console.log("Yes reply.")
break;
} else if (reply.content == 'no'){
console.log("No reply")
}
})
})
} else if (cardType != "Flip"){
message.channel.send(nonflipEmbed)
continue;
}
}
})
}
Like I mentioned, the code isn't working to a point I can try it and get a problem because I am getting an "illegal break statement" error.
I think I am on the right track, but I'm uncertain how to make it work exactly how I'm hoping.
So, to conclude...
I would like this code to to the following:
1)User inputs command and specifies how many cards to flip over.
2)Loop should flip cards (displaying the correct embed based on card type)
3)When a "flip" card type is revealed, I would like the code to pause, essentially, to ask the author whether or not they would like to use the Flip card that was revealed. If yes, stop the loop. If no, continue the loop from where it left off until the desired number are flipped OR if another Flip card type is revealed.
Thanks for the help in advance!
What you are trying to do is impossible without code modification. The reason for this is that for loops are synchronous, while promises are not. This means that .then() will only be reached after the loop completes.
One way around this is to wrap everything inside an async function and run your loop in there. Then, use try-catch clauses to break out of the loop when needed. Consider the toy example below:
( async () => {
for (i = 0; i < 100; i++) {
console.log('i:', i);
try {
const val = await randomPromise();
console.log('should resolve and break!');
console.log(val);
break;
} catch (e) {
console.log('moving on...');
}
}
})();
function randomPromise() {
return new Promise( (resolve, reject) => {
const val = Math.random();
if (val > 0.9) {
resolve(val);
} else {
reject(0);
}
})
}
You've changed contexts -- as in you have two functions that know nothing about each other's control flow (e.g.: if/then, for loops, while loops, etc). They cannot influence each other in the way you're wanting. What you're doing is the equivalent of this pseudo code:
function printAllNumbers() {
for(currentNumber : allNumbers) {
printNumber(currentNumber);
}
}
function printNumber(num) {
print(num);
break; // This won't work because it has no clue about for/loop in other func
}
printNumber is scoped so that it only knows what its own function (those 2 lines) are doing. It has no clue about the for/each in the printAllNumbers call, hence the error you're seeing.
And what I said above is only partially true -- you're actually maybe 2 functions deep at this point because you've called .then() and then after your .newMessage() call another .then(). You're reading the code sequentially, but you should try and look at it as branching off paths in this way:
for {
call async functions and (.then) call these other functions
}
...some time passes
<branched off path> The other functions within .then(functions) get called
I recommend reading up on how asynchronous functions work, and also how promises work to better understand in what order your code will execute.
To do what you want, you'll need to write separate code to handle these scenarios:
1) Receiving the number of cards from user and just flat out looping through and printing those cards to chat
2) Receiving further input based on the user (will need to keep track of user) and the current state of their cards that have been flipped -- probably want to number the cards and have them send a command like USE 1 or DISCARD ALL
The code won't pause in the middle of your loop to wait for input, these each have to be different pieces of code.

Table-Scraper Not Returning Data Before Function Returns

I am currently working on a Discord bot, which uses node.js, to scrape info from Dododex.com, a database of creatures from the game ARK. I found a very useful library called table-scraper which scrapes tables from web pages and returns the data as an object (or array of objects for multiple tables). In the background, table-scraper uses another library called x-ray, along with the common web request-maker request. Keep in mind that I don't know that much about node or how request works.
What I have working is asking the user for the name of a creature to get the data for. I then check that name against a list of creatures (that I have in a file/JSON object) to see if that name is valid.
Now, what I want to get working is to scrape the relevant data from the dododex page belonging to that creature using table-scraper. Table-scraper is definitely working with dododex, but when I call it to gather the data and put it into an object, it seems to start scraping, but then lets the function return with an empty object. If the program didn't crash right after the return (caused by the attempt to access an empty object), I know from testing that it would finish scraping, even though the function the scraping was called in has already finished.
Here's where the relevant code starts:
const creature = getDinoFromName(args[0].toLowerCase(), arkdata); //Function explained below
if(creature.health.base == 404) //If the creature's name is invalid, getDinoFromName() will return a creature with health.base = 404
{
console.log("unknown");
unknownCreature(args, msg); //Tells the user that the creature is unknown
return;
}
else
{
//Outputs the data
}
And here's where the data gets gathered:
function getDinoFromName(name, arkdata)
{
for(let i = 0; i < arkdata.creatures.length; i++) //Loops through arkdata, a json object with the possible names of all the creatures
{
if(arkdata.creatures[i].possible_names.includes(name)) //If the name the user provided matches one of the possible names of a creature (creatures can have multiple possible names, i.e. rex, t-rex, tyrannosaurus rex)
{
var creature;
console.log("found");
tableScraper.get("http://www.dododex.com/taming/" + arkdata.creatures[i].possible_names[0]).then(function(tableData)
{
console.log("scraped");
var stats = tableData[1]; //tableData is an array of 3 tables matching the 3 html tables found on the site, tableData[1] is the one with the creature's stats. It looks like this: http://ronsoros.github.io/?31db928bc662538a80bd25dd1207ac080e5ebca7
creature = //Now the actual creature object, parsed from tableData[1]
{
"health":
{
"base": stats[0]["Base (Lvl 1)"], //
"perwild": stats[0]["Increase Per Lvl (Wild)"],
"pertamed": stats[0]["Increase Per Lvl (Tamed)"]
}
//There is more data to parse but I only included hp as an example
}
});
return creature;
}
}
//If the creature name is not found to be one of the possible names, returns this to signify the creature was not found
return {"health":{"base":404}};
}
Running the code result in node crashing and the error
/app/server.js:320
if(creature.health.base == 404)
TypeError: Cannot read property 'health' of undefined
at stats (/app/server.js:320:15)
at Client.client.on.msg (/app/server.js:105:7)
Something wonky is going on here, and I really can't figure it out. Any help is appreciated.

In Microsoft Bot Framework session.conversationData.array of regexes changes to array of Object type after executing once

I am working with the below piece of code in Microsoft Bot Framework to access the list of regexes for global commands. This code is a part of botbuilder module:
if (typeof session.conversationData.globalCommands === "undefined") {
// An array which contains the list of all global commands
session.conversationData.globalCommands = [];
// Accessing the list of global commands
globalActions = session.library.actions.actions;
lenGlobalActions = Object.keys(globalActions).length;
// Assigning values to the above list
for (var i=0; i<lenGlobalActions; i++){
session.conversationData.globalCommands.push(globalActions[Object.keys(globalActions)[i]].options.matches);
}
}
// Checking if the incoming message from the user is a global command
var isGlobalCommand = session.conversationData.globalCommands.some(regex => regex.test(session.message.text));
The issue here is, the code runs fine for the first time and the values assigned to the variable session.conversationData.globalCommands are in the form given below:
However, after the first execution, the array converts to the below without any changes made in the code anywhere else:
Due to this, the line:
var isGlobalCommand = session.conversationData.globalCommands.some(regex => regex.test(session.message.text));
throws an exception "regex.test is not a function".
I am unable to understand why this should be happening and how do I solve this as I need this list separately for some processing.
I believe you cannot store complex types in the bot store (conversationData, etc). The object needs to be serializable to JSON and I don't believe a RegExp it is.
The workaround would be to store the regex as an string and then recreate the regex object using the constructor and the stored string expression.
Check the core-State sample to know more about the store capabilities.

Different variable name case convention in one application

This is a really trivial problem. I am just curious on how to deal with this in a "professional" manner.
I am trying to stick to variable naming convention. For NodeJs I am doing camelCasing. For database, I am using PostgreSQL and using underscore_casing.
Now the problem arises when I query data from PostgreSQL. I'll get a user object with following format,
{user_id: 1, account_type : "Admin"}
I can pass this object directly to server side-render and will have to use underscore casing to access account_type. Of course, I can manually create a new user JSON object with property userId and accountType but that is unnecessary work.
Is it possible to follow variable naming convention for both language and avoid having mixed variable names casing in some files? What is a good way to stay organized?
The are two good ways to approach this issue. The simplest one - do no conversion, use the exact database names. And the second one is to camel-case columns automatically.
Either way, you should always follow the underscore notation for all PostgreSQL declarations, as it will give you the option to activate camel-casing in your app at a later time, if it becomes necessary. Never use camel-case inside the database, or you will end up in a lot of pain later.
If you want the best of both worlds, follow the underscore notation for all PostgreSQL declarations, and convert to camel-case as you read data.
Below is an example of how to do it properly with pg-promise, copied from event receive example:
// Example below shows the fastest way to camelize column names:
const options = {
receive(e) {
camelizeColumns(e.data);
}
};
function camelizeColumns(data) {
const template = data[0];
for (var prop in template) {
const camel = pgp.utils.camelize(prop);
if (!(camel in template)) {
for (var i = 0; i < data.length; i++) {
const d = data[i];
d[camel] = d[prop];
delete d[prop];
}
}
}
}
Also see the following article: Pg-promise and case sensitivity in column names.
UPDATE
The code above has been updated for use of pg-promise v11 or later.
I've struggled with this too, and I've concluded that there's really no way to avoid this kind of ugliness unless you rewrite the objects that come from the database. Fortunately, that's not too difficult in Javascript:
const fromDBtoJS = (obj) => {
// declare a variable to hold the result
const result = {};
// iterate over the keys on the object
Object.keys(obj).forEach((key) => {
// adjust the key
const newKey = key.replace(/_[a-z]/g, (x) => x[1].toUpperCase());
// add the value from the old object with the new key
result[newKey] = obj[key];
});
// return the result
return result;
};
Here's a JSFiddle. The "replace" code above was found here
If you wanted to use classes for models in your application, you could incorporate this code into the constructor or database load method so it's all handled more-or-less automatically.

Resources