with my discord bot I am working on a help command.
My command list file which the help command accesses is:
{
"Help": {
"name":"Help",
"group":"User",
"desc":"Displays a list of commands",
"usage":"help [group OR command]"
},
"Purge": {
"name":"Purge",
"group":"Admin",
"desc":"Deletes a specified number of messages",
"usage":"Purge <amount>"
}
}
These just define group, name, and usage of the commands. The code for the help command so far is:
const Discord = require('discord.js');
const bot = new Discord.Client();
const client = new Discord.Client();
const weather = require('weather-js');
const fs = require('fs');
const commands = JSON.parse(fs.readFileSync('Storage/commands.json', 'utf8'))
const token = "<my token>"
const prefix = 'cb!';
bot.on('message', message => {
// Variables
let msg = message.content.toUpperCase();
let sender = message.author;
let cont = message.content.slice(prefix.length).split(" ");
let args = cont.shift().toLowerCase();
if (message.content.startsWith(prefix+'help')) {
console.log('ok i hate this')
const embed = new Discord.RichEmbed()
.setColor(0x1D82B6)
let commandsFound = 0;
for (var cmd in commands) {
if (commands[cmd].group.toUpperCase() === 'USER') {
commandsFound++
embed.addField(`${commands[cmd].name}`, `**Description:** ${commands[cmd].desc}\n**Usage:** ${prefix + commands[cmd].usage}`);
}
}
embed.setFooter(`Currently showing user commands. To view another group do ${prefix}help [group / command]`)
embed.setDescription(`**${commandsFound} commands found** - <> means required, [] means optional`)
message.author.send({embed})
message.channel.send({embed: {
color: 0x1D82B6,
description: `**Check your DMs ${message.author}!**`
}})
} else {
// Variables
let groupFound = '';
for (var cmd in commands) {
if (args.join(" ").trim().toUpperCase() === commands[cmd].group.toUpperCase()) {
groupFound = commands[cmd].group.toUpperCase();
break;
}
}
if (groupFound != '') {
for (var cmd in commands) {
const embed = new Discord.RichEmbed()
.setColor(0x1D82B6)
let commandsFound = 0;
if (commands[cmd].group.toUpperCase() === groupFound) {
commandsFound++
embed.addField(`${commands[cmd].name}`, `**Description:** ${commands[cmd].desc}\n**Usage:** ${prefix + commands[cmd].usage}`);
}
}
embed.setFooter(`Currently showing ${groupFound} commands. To view another group do ${prefix}help [group / command]`)
embed.setDescription(`**${commandsFound} commands found** - <> means required, [] means optional`)
message.author.send({embed})
message.channel.send({embed: {
color: 0x1D82B6,
description: `**Check your DMs ${message.author}!**`
}})
}
}
});
If I were to type "cb!help admin" I would get this error in the console
if (args.join(" ").trim().toUpperCase() === commands[cmd].group.toUpperCase()) {
^
TypeError: args.join is not a function
What might I do to fix this? I've also tried if (args[0].join... but that doesn't work.
As always, thanks for taking the time to read this. I'm basing this off of out dated code so there are maybe some other errors. I'm trying too fix them all.
args has one definition in your code sample:
let args = cont.shift().toLowerCase();
This makes args a string - string do not have the method join() as join() is part of the Array prototype chain, which strings do not inherit from. shift() will return the first element of the cont array, so you may just want to call args.toUpperCase(), although I would recommend renaming your variables to make the meaning of what args is clearer.
You should try to swap your two variables at the top so that
let args = message.content.slice(prefix.length).split(' ');
let cont = args.shift().toLowerCase();
Then just put args in your if statement
if (args[0].toUpperCase === commands[cmd].group.toUpperCase) { /* Your code... */ }
Your args are already stored in an array so there is no need to trim them and the join function is also therefore not needed.
Hope this helps!
your missing client, insert client, message, args
Related
I have a command file for a discord bot that contains the command and a piece of parsing logic contained within a function that I want to reuse within my index.js
// file: ./commands/scrumPrompt.js
// The function
const extractDeets = function (f, scrum) {
let items = [];
let re = new RegExp("(\n[ -]*" + f + ".*)", "g");
let replace = new RegExp("[ -]*" + f + "[ ]+");
for (const item of scrum.matchAll(re)) {
items.push(item[1].trim().replace(replace, ""));
}
return items;
};
// The actual command itself within the same file
module.exports = {
name: "scrum",
usage: `!scrum < followed by your message > as per Standup format - refer !show for showing the format`,
description: "Reply to standup prompt",
async execute(message, args) {
if (message.channel.type === "text") {
if (!args.length)
return message.reply(
"Please Provide your scrum as per the format in help menu !scrum < your message >"
);
else {
if (message.author.id !== -1) {
const client = new MongoClient(MONGO_URI);
try {
const database = client.db(DB_NAME);
const members = database.collection("members");
const query = { user_id: message.author.id };
const membersdetail = await members.findOne(query);
if (membersdetail !== null) {
// since this method returns the matched document, not a cursor, print it directly
//console.log("Adding Scrum for ", membersdetail.email);
let userscrum = args.splice(0).join(" ");
// Check if multiple !scrum commands are present in developer scrum message
if (userscrum.includes("!scrum") == false) {
// Expects notations of "-" to exist
let [f, e, b, o, bl] = ["f", "e", "b", "o", "x"];
let features = extractDeets(f, userscrum);
let enhancements = extractDeets(e, userscrum);
let bugs = extractDeets(b, userscrum);
let others = extractDeets(o, userscrum);
let blockers = extractDeets(bl, userscrum);
.
.
.
};
I want to keep the name of the function as extractDeets() itself so that it doesn't mess with the usage within the command as well. I'm not completely sure how to export it into the index.js because it's already kind of being imported here:
// Imports the command file + adds the command to the bot commands collection
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
bot.commands.set(command.name, command);
}
I'm unsure of how to add the function as another import, maybe I should export it into another file and then import it from there? I'm not sure if that's possible or doable here. I've tried directly importing from here but then the command doesn't work, which is troublesome.
You can do it like this:
module.exports = { extractDeets };
Later, you can import it like this:
const { extractDeets } = require('../your_file');
I am using the invite tracker from the WOK tutorials, and even though the code works perfectly fine, and im using a command handler that has 0 issues, for some reason, the bot replies with multiple messages instead of 1 as seen in this image:
Here is the code that I used for the invites command:
const Discord = require("discord.js")
module.exports = {
commands: 'invites',
callback: (message) => {
const { guild } = message
guild.fetchInvites().then((invites) => {
const inviteCounter = {}
invites.forEach((invite) => {
const { uses, inviter } = invite
const { username, discriminator } = inviter
const name = `${username}#${discriminator}`
inviteCounter[name] = (inviteCounter[name] || 0) + uses
})
let replyText = ''
const sortedInvites = Object.keys(inviteCounter).sort((a, b) => inviteCounter[b] - inviteCounter[a])
for (const invite of sortedInvites) {
const count = inviteCounter[invite]
replyText += `\n${invite} has invited ${count} member(s)!`
const embed = new Discord.MessageEmbed()
embed.setTitle('Invites: ')
embed.setDescription(replyText)
message.reply(embed)
}
})
}
}
Fyi last time i posted this i forgot to post the code so yeah that was dumb my bad.
This is because you have message.reply in a for...of loop.
for (const invite of sortedInvites) {
const count = inviteCounter[invite]
replyText += `\n${invite} has invited ${count} member(s)!`
const embed = new Discord.MessageEmbed()
embed.setTitle('Invites: ')
embed.setDescription(replyText)
message.reply(embed) //here
}
You probably meant to put it outside of the loop so that it sends the resulting embed after all the iterations.
const { MessageEmbed } = require('discord.js');
const fs = require('fs');
const db = require('quick.db');
const bot = ('Discord.Client')
module.exports = {
name: 'leaderboard',
description: "idk something",
execute(message, args) {
let money = db.all().filter(data => data.ID.startsWith(`money_`)).sort((a, b) => b.data - a.data);
console.log(money)
if (!money.length) {
let noEmbed = new MessageEmbed()
.setAuthor(message.member.displayName, message.author.displayAvatarURL())
.setColor("BLUE")
.setFooter("No leaderboard")
return message.channel.send(noEmbed)
};
money.length = 10;
var finalLb = "";
for (var i in money) {
let currency1;
let fetched = db.fetch(`money_${message.guild.id}`);
if (fetched == null) {
currency1 = '🥪'
} else {
currency1 = fetched
}
if (money[i].data === null) {money[i].data = 0}
finalLb += `**${money.indexOf(money[i]) + 1}.${message.guild.members.fetch(money.ID.split('_')[1]) ? message.guild.members.fetch(money.ID.split('_')[1]).tag : ""}** - ${money.data} ${currency1}\n`;
};
const embed = new MessageEmbed()
.setTitle(message.guild.name)
.setColor("BLUE")
.setDescription(finalLb)
.setTimestamp()
.setFooter('Command: !help for currency commands')
message.channel.send(embed);
}
}
trying to make a leaderboard command and got this error TypeError: Cannot read property 'split' of undefined. ive been looking for answers for a while but so far i havent seen anything specific to my situation. can anyone help?
It seems like you're making a lot of asynchronous calls in the function. What is probably happening is that the string value that you're calling the split function does not exist because the data from the server hasn't reached the client.
To fix this, add the keyword "async" before your function name (execute) and add the keyword "await" before all the function calls to db
async execute(message, args){
...
}
and
let money = await db.all().filter(data => data.ID.startsWith(`money_`)).sort((a, b) => b.data - a.data);
let fetched = await db.fetch(`money_${message.guild.id}`);
I also recommend that you read about asynchronous functions in javascript
The title explains my problem. I am trying to get a string that has quotation marks around it so I can use Node.js to pass into a weather module. Here's my code so far (I have not set the var CityToSearch yet in this code which is what I need help with)
And also yes I'm using Discord.js to send messages.
const Discord = require('discord.js')
const bot = new Discord.Client()
const PREFIX = '/';
const embed = new Discord.MessageEmbed()
const ping = require('minecraft-server-util')
const weather = require('weather-js')
bot.on('message', message => {
if (message.channel.type === 'dm') {return}
let args = message.content.substring(PREFIX.length).split(' ')
if(message.content.startsWith(PREFIX))
switch (args[0]) {
case 'weather':
if (args.includes('"')){
var CityToSearch =
}
weather.find({search: `city, ${CityToSearch}`, degreeType: 'F'}, function(err, result) {
if(err) console.log(err);
var currentw = new Discord.MessageEmbed()
.setColor(0x00ffff)
.setTitle(`Current Weather in ${args[1]} in state ${args[2]}`)
.addField('Temperature', result[0].current.temperature)
.addField('Sky Text', result[0].current.skytext)
.addField('Humidity', result[0].current.humidity)
.addField('Wind Speed & Direction', result[0].current.winddisplay)
.addField('Feels Like', result[0].current.feelslike)
.addField('Location', result[0].current.observationpoint)
.addField('Time', result[0].current.observationtime)
.addField('Date', result[0].current.date)
message.channel.send(currentw)
});
You can split the actual string with ". So that the string will be split and the string at index 1 will be the city you are looking for.
const str = '/weather "San Fransico" California';
console.log(str.split('"'));
console.log(str.split('"')[1]);
I would NOT split the arguments on spaces initially. You can use the Regular Expression below with your arguments to yank out the command, and then parse the inputs as needed:
const args = message.content.substring(PREFIX.length).match(/\/([^\s]+) (.+)/)
if (args) { // valid input?
const command = args[1]
const input = args[2]
switch (command) {
case 'weather':
const cityMatch = input.match(/"([^"]+)"/)
const CityToSearch = (cityMatch) ? cityMatch[1] : input.split(/\s/)[0]
weather.find({search: `city, ${CityToSearch}` ...)
// other commands...
}
}
Basically there is no errors in the output but at the same time it's not doing what I'm trying to achieve.
Ive been tinkering with the script for 5 hours straight mixing up line positioning and now I got it to where it gives me the promise (my initial issue) but I cant parent the channel.
I've tried discord.js server and site, youtube, 2 other sites i forgot the name of but i cant crack it.
function setup(arguments, message){
var server = message.guild;
var name = message.author.username;
let searchquery = arguments.join("")
let cat = server.createChannel("Important", "category");
async function Channelmaker(Sent, obj){
try {
let chan = await server.createChannel(Sent, "Text");
//console.log(obj);
return chan
} catch(prom){
var chan2 = await server.createChannel(Sent, "Text");
return new Promise(resolve => {
var chan2 = server.createChannel(Sent, "Text", parent = obj);
resolve(chan2)
});
}
}
var holding
var chan = Channelmaker("⚖️ rules ⚖️", cat).then(value => {
console.log(value)
holding = value
value.parentID = cat
chan.setParent(cat.Id)
}).catch(error => {
// s
});
console.log("holding")
console.log(holding)
}
The category is not the parent of the "⚖️ rules ⚖️" channel that is created which is the opposite of what I'm trying to achieve
In Guild.createChannel(), use the options parameter including ChannelData, like so:
await server.createChannel(Sent, {
// You can omit the 'type' property; it's 'text' by default.
parent: obj
});