I'm trying to make a irc bot that takes input like this user: +1 I want to have a end result where I can have a main number being added to, from the # someone types with +#.
expected output: #x added: 1 rep: 1 executed[+] second execution
#x added: 1 rep: 2 executed[+]
actual output #x added: +1 rep: +1undefined executed[+] second is identical.
I've tried using Number(commandName), along with toString().replace(/\D\s/g,'') I got some promising results but they seemed to have some problems so I scrapped that code...
so in conclusion how can I add the numbers together and avoid the +?
const tmi = require('tmi.js');
// Define configuration options
const opts = {
identity: {
username: "x",
password: "x"
},
channels: [
"#x"
]
};
// Create a client with our options
const client = new tmi.client(opts);
// Register our event handlers (defined below)
client.on('message', onMessageHandler);
client.on('connected', onConnectedHandler);
// Connect to Twitch:
client.connect();
const totalnum = 0;
// Called every time a message comes in
function onMessageHandler(target, context, msg, self) {
if (self) {
return;
} // Ignore messages from the bot
// Remove whitespace from chat message
let commandName = msg.trim();
var regexadd = /([+]\d*)[^+\s]/;
// If the command is known, let's execute it
if (regexadd.exec(commandName)) {
var totalnum = addem(commandName, totalnum);
console.log(target, `added:`, commandName, `rep:`, totalnum, `executed[+]`)
} else {
console.log(`* Unknown command ${commandName}`);
}
function addem(x, y) {
return (x + y);
}
}
// Called every time the bot connects to Twitch chat
function onConnectedHandler(addr, port) {
console.log(`* Connected to ${addr}:${port}`);
}
I found a few things that appear to be wrong with your code:
You're not adding numbers. addem()'s first parameter is the name of the command, it should be the number captured in your regex capture group.
Your regex includes the + sign in the capture group, you probably wanted to exclude it
You should parse the result of exec to a hint either with ParseInt() or implicitly with +
You use RegExp.prototype.exec() instead of RegExp.prototype.match() to retrieve a capture group.
Here's what this could look like
var regexadd = /\+(\d*)[^+\s]/;
if (regexadd.exec(commandName)) {
var totalnum = addem(+commandName.match(regexadd)[1], totalnum);
console.log(target, `added:`, commandName, `rep:`, totalnum, `executed[+]`)
}
I also think it would be best to use RegExp.prototype.test() instead of RegExp.prototype.exec() for your if statement - you will limit results to true or false.
Related
Brief history
I have a JavaScript object which stores query templates with $nameParam so that it can be replaced with string.replace(). Issue is that until now I needed $nameParam to be in quotation marks and now I need to get rid of them. Depending on when they are added, the query either works or not. I have observed some interesting behaviour:
Method which replaces string
This method basically calls database and gets records. You can see the longest line - it's responsible for replacing $nameParam with the provided parameter.
function setupEndpoint(app) {
app.get('/neo4jdb/:queryType',
async (req, res) => {
const session = driver.session();
res.on('finish', () => session.close());
try {
const response = await session.run(queryTemplates[req.params.queryType],
{ nameParam: req.query.name });
let statement = response.summary.query.text.replace('$nameParam', `"${response.summary.query.parameters.nameParam}"`);
statement = statement.replace('RETURN', '\nRETURN');
console.info(`Neo4j statement: ${statement}`);
res.send({ rawItems: response.records, statement });
} catch (err) {
console.error('Error connecting Neo4j', configs);
console.error(err);
res.send({ error: 'Error connecting Neo4j' });
}
});
}
Working example
Query template
const queryTemplates = {
getShipsForCompany: `MATCH (A:Company) -[RELATIONSHIP]-> (B:Ship) MATCH (B:Ship)-[RD]-()
WHERE A.name=$nameParam RETURN B as Ship, A as Company, type(RELATIONSHIP) as R, count(RD) as degree limit 50`
};
Replacement
response.summary.query.text.replace('$nameParam', `"${response.summary.query.parameters.nameParam}"`);
Produced output
Neo4j statement: MATCH (A:Company) -[RELATIONSHIP]-> (B:Ship) MATCH (B:Ship)-[RD]-() WHERE A.name="TRINITY HOUSE"
RETURN B as Ship, A as Company, type(RELATIONSHIP) as R, count(RD) as degree limit 50
Not-working example
Query template
const queryTemplates = {
getShipsForCompany: `MATCH (A:Company) -[RELATIONSHIP]-> (B:Ship) MATCH (B:Ship)-[RD]-()
WHERE A.name="$nameParam" RETURN B as Ship, A as Company, type(RELATIONSHIP) as R, count(RD) as degree limit 50`
};
Replacement
response.summary.query.text.replace('$nameParam', `${response.summary.query.parameters.nameParam}`);
Produced output
Neo4j statement: MATCH (A:Company) -[RELATIONSHIP]-> (B:Ship) MATCH (B:Ship)-[RD]-() WHERE A.name="TRINITY HOUSE"
RETURN B as Ship, A as Company, type(RELATIONSHIP) as R, count(RD) as degree limit 50
Summary
Both produced output queries are the same. Why first one returns results and second one is empty? (2nd one copied to DB engine works fine)
Neo4j cypher syntax is below.
WHERE A.name=$nameParam
It should NOT have double quotes on the parameter name. Thus, the 2nd example is syntax error during runtime.
I need a code showing user's server join position. For example:
User: !userinfo
Bot: You're 23rd member of server.
Here is a very basic example. I've tried to explain it with comments as much as I can.
// Import the Client class from Discord.js
const {Client} = require('discord.js')
// Initialise a new client
const client = new Client()
// This will be used later on to determine the ordinal suffix to use
// for 1st, 2nd, and 3rd
const digitToOrdinalSuffix = {
1: 'st',
2: 'nd',
3: 'rd'
}
// Add a listener for the 'message' event (which is emitted when a message is
// sent)
// The {author, channel, content, guild} destructures the message object.
// author is the message's author, channel is the message's channel etc.
client.on('message', async ({author, channel, content, guild}) => {
try {
// If the message content is '!userinfo'...
if (content === '!userinfo') {
// Send a message if it's a DM
if (!guild) return await channel.send('This command can’t be used in DMs!')
// This is the actual part that gets the user's 'join position'
// Get the message server's members...
const result = guild.members.cache
// sort them in ascending order by when they joined...
.sorted((a, b) => a.joinedAt - b.joinedAt)
// convert the Collection to an array so we can use findIndex...
.array()
// and find the position of the member in the array.
// For example, the first member to join the server will be the first
// member of the array (index 0), so they will be the
// 0 + 1 = 1st member to join.
.findIndex(member => member.id === author.id) + 1
// The last digit of the result (0-9)
const lastDigit = result % 10
// The last 2 digits of the result (0-99)
const last2Digits = result % 100
// Send the message
await channel.send(
`You’re the ${result}${
// 4th, 5th, 6th, 7th, 8th, 9th, 10th, 14th, 15th...
lastDigit === 0 || lastDigit > 3 ||
// 11th, 12th, 13th
last2Digits >= 11 && last2Digits <= 13
? 'th'
// 1st, 2nd, 3rd, 21st, 22nd, 23rd...
: digitToOrdinalSuffix[lastDigit]
} member of the server.`
)
}
} catch (error) {
// Log any errors
console.error(error)
}
})
// Login to Discord using your bot token. It is highly recommended that you don't
// literally put your token here as a string; store it somewhere else like a
// config or .env file.
client.login('your token')
If you are planning on adding a lot more commands, I recommend using a better command handler that trims the user input and handles arguments, instead of using if/else if to check if the input matches a string. A good example of this is on the Discord.js guide, which is a very good resource for learning how to code a bot using Discord.js.
I'm working with a discord bot and I'm doing a help command with pages.
I have this set up:
if(message.content.startWith(`${prefix}help`))
and
if(message.content.startWith(`${prefix}help 2`))
If i do >help 2 I get both. Is there any way to stop that from happening?
When using startsWith(">help") (not startWith) it will match ">help 2" because that string does indeed start with ">help". If you want to match these cases more precisely you should put the most specific cases first and use else if for subsequent comparisons.
if (message.content.startsWith(`${prefix}help 2`)) { /* do 2 */ }
else if (message.content.startsWith(`${prefix}help 3`)) { /* do 3 */ }
else if (message.content.startsWith(`${prefix}help 4`)) { /* do 4 */ }
else if (message.content.startsWith(`${prefix}help`)) { /* do other */ }
else if (message.content.startsWith(`${prefix}faq`)) { /* do faq */ }
The code above will only match the second case if the first didn't match.
Further explanation:
When comparing partial strings you need to be careful that you're matching the correct thing. Look at this example:
let names = [ "Roberta", "Robert" ]
// case #1 - fails (wrong order)
names.forEach(name => {
let salutation;
if (name.startsWith("Robert"))
salutation = "Mr.";
else if (name.startsWith("Roberta"))
salutation = "Miss";
console.log("1) "+salutation+" "+name);
})
// case #2 - fails (missing else)
names.forEach(name => {
let salutation;
if (name.startsWith("Roberta"))
salutation = "Miss";
if (name.startsWith("Robert"))
salutation = "Mr.";
console.log("2) "+salutation+" "+name);
})
// case #3 - works
names.forEach(name => {
let salutation;
if (name.startsWith("Roberta"))
salutation = "Miss";
else if (name.startsWith("Robert"))
salutation = "Mr.";
console.log("3) "+salutation+" "+name);
})
Case #1 fails because both names match on the first if-statement because both names actually do start with "Robert". But we didn't want it to match "Roberta" because we have a second check for that start of the string. However the code never went into the second if-statement because it already matched on the first one. By reversing the order of checking and putting the check for "Roberta" first we get the right behaviour because it's more specific ('Robert' doesn't start with 'Roberta' but 'Roberta' does start with 'Robert'). So the important part is to order your if-statements to match with the most specific before the more general values.
Case #2 fails because even if the first if-statement matches, the second one can also match. We want to use else if to make sure that if something earlier in the code has already matched that we don't keep checking other cases.
A better way to do this would be to check the page number for your help command:
if(msg.startsWith(prefix)){
let args = msg.slice(1).split(" ");
let command = args.shift(); //shift removes the first element of the array and returns it
if(command === 'help'){
let helpPage = args[0] || "1"; //take the page from the msg if supplied, otherwise default to page 1;
if(helpPage === "1"){ /* send page 1 */ }
else if(helpPage === "2"){ /* send page 2 */ }
// and so on
}
}
Before everything, I have been making a word filter program for my discord.js bot so excuse me for the bad words!
Since you can't add extra parameters in includes() I decided to make a var line:
var filteredwords = ['asshole', 'fuck']
But now I want to place these words (further more will be added) in the following code line:
if (message.content.includes('asshole'));
So instead of 'asshole' I want to place the array? How can I do that? Since I'm a beginner in JS I could not understand the other topics with a similar question. It would be fine if you explain it in noob language. :)
If useful, this is my full code:
const Discord = require('discord.js');
const client = new Discord.Client();
var filteredwords = ['asshole', 'fuck']
function commandIs(str, msg) {
return msg.content.toLowerCase().startsWith('--' + str);
}
client.on('ready', () => {
console.log('The bot is started succesfully')
});
client.on('message', message => {
if (commandIs('trump', message)) {
message.reply('He is the president of the United States of America!');
}
if (commandIs('putin', message)) {
message.reply('He is the president of Russia!');
}
if (commandIs('spacetaco', message)) {
message.reply('He is the first user who joined Arcanews!');
}
if (message.content.includes('asshole')); {
message.reply('Do not swear please');
message.delete();
var colors = require('colors/safe');
console.log(colors.red(`The following message got deleted:
${message.content}`));
}
});
if(filterwords.some(badword=>message.content.includes(badword))){
alert("BAD!");
}
Array.prototype.some iterates over the array and returns true if one of the given function called with the array elem as argument is true, therefore if it contains at least one bad word...
Because of the "includes" method you used, I think the type of "message.content" is an array.
Therefore, the problem can be regard as compare two arrays. You can simply apply two loops for checking, or you can use reduce method for checking.
var messageA = {
content: "Here is a asshole in it.".split(" "),
};
var messageB = {
content: "Here is a hole in it.".split(" "),
};
var filteredwords = ['asshole', 'fuck'];
var isValidA = messageA.content.reduce(function(pre, cur) {
if (pre) return !!filteredwords.indexOf(cur);
return pre;
}, true);
var isValidB = messageB.content.reduce(function(pre, cur) {
if (pre) return !!filteredwords.indexOf(cur);
return pre;
}, true);
console.log(isValidA); // false
console.log(isValidB); // true
You can use regex matching here. A much faster implementation
var filteredwords = ['bad-word', 'another-bad-word']
var message = 'Hello this string has a bad-word in it'; // Use your messsage.content here
// In a regex - '\b' start or end of a word
// " .join('\\b|\\b') " - gives me a regex string with each of the values from filteredwords array concatinated to for a 'OR' expression
// Here is th regex exp taht is generated `\basshole\b|\bfuck\b`
if( (new RegExp( '\\b' + filteredwords.join('\\b|\\b') + '\\b') ).test(message) ){
alert('match'); // Here the word were matched
}else{
alert('no match'); // Wooho, a safe message
}
Pro tip :
RegEx solution stands out in a way that you do the match that are case-insensitive and for bad-words that appear as a part of another word eg 'dambad-Word' would give a match for bad-word
EDIT: Updating answer to the full-code posted by const Discord = require('discord.js');
const client = new Discord.Client();
var filteredwords = ['asshole', 'fuck']
function commandIs(str, msg) {
return msg.content.toLowerCase().startsWith('--' + str);
}
client.on('ready', () => {
console.log('The bot is started succesfully')
});
client.on('message', message => {
if (commandIs('trump', message)) {
message.reply('He is the president of the United States of America!');
}
if (commandIs('putin', message)) {
message.reply('He is the president of Russia!');
}
if (commandIs('spacetaco', message)) {
message.reply('He is the first user who joined Arcanews!');
}
if ( (new RegExp('\\b' + filteredwords.join('\\b|\\b') + '\\b')).test(message.content ) ) {
message.reply('Do not swear please');
message.delete();
var colors = require('colors/safe');
console.log(colors.red(`The following message got deleted:${message.content}`));
}
});
If you want to do the matching for not whole words like say curseWord should be detected in the sentence hello there youcurseword (case insensitive as well), you can replace the last IF condition with :
// We can get rid of '\b' to make the search not limited to whole-word matching
if ((new RegExp(filteredwords.join('|'))).test(message.content))
// We can use 'i' flag to make the matching case insensitive
if ((new RegExp(filteredwords.join('|') , 'i')).test(message.content))
I'm working on my final project of the Winter 2017 quarter to demonstrate how to use Regular Expressions in both C# and JavaScript code behind pages. I've got the C# version of my demonstration program done, but the JavaScript version is making me pull what little hair I have left on my head out (no small achievement since I got a fresh buzz cut this morning!). The problem involves not getting any output after applying a Regular Expression in a While loop to get each instance of the expression and printing it out.
On my HTML page I have an input textarea, seven radio buttons, an output textarea, and two buttons underneath (one button is to move the output text to the input area to perform multiple iterations of applying expressions, and the other button to clear all textareas for starting from scratch). Each radio button links to a function that applies a regular expression to the text in the input area. Five of my seven functions work; the sixth is the one I can't figure out, and the seventh is essentially the same but with a slightly different RegEx pattern, so if I fix the sixth function, the seventh function will be a snap.
(I tried to insert/upload a JPG of the front end, but the photo upload doesn't seem to be working. Hopefully you get the drift of what I've set up.)
Here are my problem children from my JS code behind:
// RegEx_Demo_JS.js - code behind for RegEx_Demo_JS
var inputString; // Global variable for the input from the input text box.
var pattern; // Global variable for the regular expression.
var result; // Global variable for the result of applying the regular expression to the user input.
// Initializes a new instance of the StringBuilder class
// and appends the given value if supplied
function StringBuilder()
{
var strings = [];
this.append = function (string)
{
string = verify(string);
if (string.length > 0) strings[strings.length] = string;
}
this.appendLine = function (string)
{
string = verify(string);
if (this.isEmpty())
{
if (string.length > 0) strings[strings.length] = string;
else return;
}
else strings[strings.length] = string.length > 0 ? "\r\n" + string : "\r\n";
}
this.clear = function () { strings = []; };
this.isEmpty = function () { return strings.length == 0; };
this.toString = function () { return strings.join(""); };
var verify = function (string)
{
if (!defined(string)) return "";
if (getType(string) != getType(new String())) return String(string);
return string;
}
var defined = function (el)
{
// Changed per Ryan O'Hara's comment:
return el != null && typeof(el) != "undefined";
}
var getType = function (instance)
{
if (!defined(instance.constructor)) throw Error("Unexpected object type");
var type = String(instance.constructor).match(/function\s+(\w+)/);
return defined(type) ? type[1] : "undefined";
}
}
Within the code of the second radio button (which will be the seventh and last function to complete), I tested the ScriptBuilder with data in a local variable, and it ran successfully and produced output into the output textarea. But I get no output from this next function that invokes a While loop:
function RegEx_Match_TheOnly_AllInstances()
{
inputString = document.getElementById("txtUserInput").value;
pattern = /(\s+the\s+)/ig; // Using an Flag (/i) to select either lowercase or uppercase version. Finds first occurrence either as a standalone word or inside a word.
//result = pattern.exec(inputString); // Finds the first index location
var arrResult; // Array for the results of the search.
var sb = getStringBuilder(); // Variable to hold iterations of the result and the text
while ((arrResult = pattern.exec(inputString)) !==null)
{
sb.appendLine = "Match: " + arrResult[0] ;
}
document.getElementById("txtRegExOutput").value = sb.toString();
/* Original code from C# version:
// string pattern = #"\s+(?i)the\s+"; // Same as above, but using Option construct for case insensitive search.
string pattern = #"(^|\s+)(?i)the(\W|\s+)";
MatchCollection matches = Regex.Matches(userTextInput, pattern);
StringBuilder outputString = new StringBuilder();
foreach (Match match in matches)
{
string outputRegExs = "Match: " + "\"" + match.Value + "\"" + " at index [" + match.Index + ","
+ (match.Index + match.Length) + "]" + "\n";
outputString.Append(outputRegExs);
}
txtRegExOutput.Text = outputString.ToString();
*/
} // End RegEx_Match_The_AllInstances
I left the commented code in to show what I had used in the C# code behind version to illustrate what I'm trying to accomplish.
The test input/string I used for this function is:
Don’t go there. If you want to be the Man, you have to beat The Man.
That should return two hits. Ideally, I want it to show the word that it found and the index where it found the word, but at this point I'd be happy to just get some output showing every instance it found, and then build on that with the index and possibly the lastIndex.
So, is my problem in my While loop, the way I'm applying the StringBuilder, or a combination of the two? I know the StringBuilder code works, at least when not being used in a loop and using some test data from the site I found that code. And the code for simply finding the first instance of "the" as a standalone or inside another word does work and returns output, but that doesn't use a loop.
I've looked through Stack Overflow and several other JavaScript websites for inspiration, but nothing I've tried so far has worked. I appreciate any help anyone can provide! (If you need me to post any other code, please advise and I'll be happy to oblige.)