Bot won't react after seeing a word contained in a word list - node.js

I am trying to make my bot react to every message containing words like hey or hi, but when I send a message containing one of those, it does nothing, here is my code:
const words = [
'hey',
'Hey'
]
client.on('message', () => {
if (message.content.includes(words)) return message.react("👋")
})
There are no errors in the console either.

Issue
message.content.includes(words)
is equivalent to
message.content.includes(words.toString())
which actually checks if the string joining the words array by a comma exists in message.content (not what you want). In your case, it does the following:
message.content.includes('hey,Hey') // words = ['hey', 'Hey']
Array.prototype.toString returns a string joining the array elements by a comma.
Solution
You can use Array.prototype.some to check if any of the words in the words array is present in the message content. And you don't have to add the capitalized word forms to words. It's enough to only add lowercase words and use String.prototype.toLowerCase to lower case the message content before checking if any of the entries in words exists in the content.
const words = [
'hey',
// ...
]
const content = message.content.toLowerCase()
const shouldReact = words.some((word) => content.includes(word))
if (shouldReact) {
// ...
}

Related

How do you get the the string between two markers even if there is multiple for the string inside it in nodes

I was trying to make a node's program that takes a string, and gets all of the content inside it:
var str = "Hello {world}!";
console.log(getBracketSubstrings(str)); // => ['world']
It works, but when I do:
var str = "Hello {world{!}}";
console.log(getBracketSubstrings(str)); // => ['world{!']
It returns ['world{!}'], when I want it to return:
['world{!}']
Is there anyway to do this to a string in nodes?
You could use a pattern with a capture group, matching from { followed by any char except a closing curly using [^}]* until you encounter a }
{([^}]*)}
See a regex demo
const getBracketSubstrings = s => Array.from(s.matchAll(/{([^}]*)}/g), x => x[1]);
console.log(getBracketSubstrings("Hello {world}!"));
console.log(getBracketSubstrings("Hello {world{!}}"));

Regex to replace hashtags from array in text

I try to replace text with keywords by hashtags and remove words used from array.
My array:
['Test', 'NodeJS', 'regex']
Here, test with NodeJS to try regex !
Become:
Here, #Test with #NodeJS to try #Regex !
Could you guide me on how to do this with NodeJS and a regex?
You could do it with reduce, new RegExp and replace with a callback function (for marking a word as used):
var tags = ['Test', 'NodeJS', 'notfound', 'regex']
var s = "Here, test with NodeJS to try regex !";
s = tags.reduce((acc, tag, i) =>
acc.replace(new RegExp("\\b" + tag + "\\b", "gi"), () => {
tags[i] = null;
return "#" + tag;
}), s);
tags = tags.filter(Boolean);
console.log(s);
console.log(tags);
If your array contains strings with special characters that need to be escaped, then first transform them like explained in this Q&A.
If you only want to replace the first occurrence of a specific tag, then remove the "g" modifier.

How to split text depending on word count

I am trying to make a lyric project using discord.js, cheerio and the website called genius.com.
I have successfully found a way to scrape the lyrics from the website, I am onto the part where I need to split it because discord has a max word limit of 2000.
I can check how many characters/words are in the overall lyrics by doing lyrics.length, I just need to find a way to split the string and send both, in the future I might implement richEmbeds to make it more stylish but for now I'm focusing on the basics.
var request = require('request');
var cheerio = require('cheerio');
/*
This is a project for my discord bot, the reason for the 2000 word limit is because
discords character limit is currently set to 2000, this means that i will have to add
a function to split the lyrics and send each part
*/
//Define the URL that we are going to be scraping the data from
var UR_L = "https://genius.com/Josh-a-and-jake-hill-not-afraid-of-dying-lyrics";
//send a request to the website and return the contents of the website
request(UR_L, function(err, resp, body) {
//load the website using cheerio
$ = cheerio.load(body);
//define lyrics as the selector to text form
var lyrics = $('p').text();
if (lyrics.length > "2000" && lyrics.length < "4000") {
} else if (lyrics.length > "4000" && lyrics.length < "6000") {
} else {
//send the lyrics as one message
}
})
You can find a live version running here on repl.it.
You don't need to use any fancy function, that function is already built in discord.js: you can attach some options to a message, and MessageOptions.split is what you're searching for. When you want to send the text, do it like this:
channel.send(lyrics, { split: true });
If lyrics.length is greater that the limit, discord.js will cut your messages and send them one after the other, making it seem like it's only one.
channel is the TextChannel you want to send the messages to.
Discord has a 2000 characters limit not a 2000 words limit.
One solution to your problem could be this:
// This will result in an array with strings of max 2000 length
const lyricsArr = lyrics.match(/.{1,2000}/g);
lyricsArr.forEach(chunk => sendMessage(chunk))
Given the async nature of sending messages, you might want to look into modules like p-iteration to ensure the chunks arrive in the correct order.
That being said, there exists APIs for getting lyrics of songs, which I would recommend instead of scraping. See apiseeds lyrics API as an example.
UPDATE
const lyrics = 'These are my lyrics';
const lyricsArr = lyrics.match(/.{1,8}/g);
console.log(lyricsArr); // [ 'These ar', 'e my lyr', 'ics' ]
lyricsArr.forEach((chunk, i) => {
// Break if this is the last chunk.
if (i == lyricsArr.length -1) {
return;
}
// If last character is not a space, we split a word in two.
// Add additional non-wordbreaking symbols between the slashes (in the regex) if needed.
if (!chunk[chunk.length - 1].match(/[ ,.!]/)) {
const lastWord = chunk.match(/\s([^ .]+)$/)
lyricsArr[i + 1] = lastWord[1] + lyricsArr[i + 1];
lyricsArr[i] = lyricsArr[i].split(/\s[^ .]*$/)[0];
}
})
console.log(lyricsArr) // [ 'These', 'are my', 'lyrics' ]
Updated as per the comments.
This is some crude code that i did not spend much time on, but it does the job.
Some info when using this approach:
You need to add any symbols that should not be considered wordbreaking to the regex in the second if
This has not been tested thoroughly, so use at your own risk.
It will definitely break if you have a word in the lyrics longer than the chunk size. Since this is around 2000, I imagine it will not be problem.
This will no longer ensure that the chunk length is below the limit, so change the limit to around 1900 to be safe
You can use .split( ) Javascript function.
word_list = lyrics.split(" ")
And word_list.length to access the number of words in your message and word_list[0] to select the first word for instance.

Attempting to print object key pair in nodejs javascript array

I am looking to iterate and print to the console the text from the text key.
For example, if this matches a string "foo bar" I am looking to print "foo bar" to the console.
var stringSearcher = require('string-search');
stringSearcher.find('This is the string to search text in', 'string' .then(function(resultArr) {
//resultArr => [ {line: 1, text: 'This is the string to search text in'} ]
});
`
In plain nodejs I would do something like this:
var source = "Hello world";
var target = "Hello";
source_arr = source.split(" ");
source_arr.forEach(function(word){
if(word.trim() === target){
console.log("target");
}
})
If all you're trying to get to is the text property of the resultArr you show, then you would do this:
console.log(resultArr[0].text)
And, in real code, you probably want to verify that the array has a .length > 0 and if there is more than one result, you might want to show all the matching results.
To iterate through all matching results:
const stringSearcher = require('string-search');
stringSearcher.find('This is the string to search text in', 'string'.then(function(resultArr) {
for (let obj of resultArr) {
console.log(obj.text);
}
});
To explain. resultArr is an array of objects. So, when you iterate the array, you get an object at each point in the array. Then, to get the text property from each object, you use obj.text.

Which algorithm to find the only one duplicate word in a string?

This is very common interview question:
There's a all-english sentence which contains only a duplicate word, for example:
input string: today is a good day is true
output: is
I have an idea:
Read every character from the string, using some hash function to compute the hash value until get a space(' '), then put that hash value in a hash-table.
Repeat Step 1 until the end of the string, if there's duplicate hash-value, then return that word, else return null.
Is that practical?
Your approach is reasonable(actually the best I can think of). Still take into account the fact that a collision may appear. Even if the hashes are the same, compare the words.
It would work, but you can make your life a lot easier.
Are you bound to a specific programming language?
If you code in c# for example, i would suggest you use the
String.Split function (and split by " ") to transform your sentence into a list of words. Then you can easily find duplicates by using LINQ (see How to get duplicate items from a list using LINQ?) or by iterating through your list.
You can use the Map() function, and also return how many times the duplicate word is found in the string.
var a = 'sometimes I feel clever and sometimes not';
var findDuplicateWord = a => {
var map = new Map();
a = a.split(' ');
a.forEach(e => {
if (map.has(e)) {
let count = map.get(e);
map.set(e, count + 1);
} else {
map.set(e, 1);
}
});
let dupe = [];
let hasDupe = false;
map.forEach((value, key) => {
if (value > 1) {
hasDupe = true;
dupe.push(key, value);
}
});
console.log(dupe);
return hasDupe;
};
findDuplicateWord(a);
//output
/* Native Browser JavaScript
[ 'sometimes', 2 ]
=> true */

Resources