Well, events messageUpdate and messageDelete doesn't "work" after restarting the bot
I mean, they don't respond to messages that were sent before the restart
I added MESSAGE to partials
Now the events works, but there is a problem with the if (newMessage.author.bot) return and if (message.author.bot) return and generally with message|newMessage.author|member etc.
Here are the error:
TypeError: Cannot read properties of null (reading 'bot')
at module.exports.run (C:\Users\ALL\Desktop\Slodziak\Slodziak13vCanary\events\messageDelete.js:14:28)
at Slodziak.<anonymous> (C:\Users\ALL\Desktop\Slodziak\Slodziak13vCanary\aslodziak.js:40:43)
at Slodziak.emit (node:events:527:28)
at Slodziak.emit (node:domain:475:12)
at MessageDeleteAction.handle (C:\Users\ALL\Desktop\Slodziak\Slodziak13vCanary\node_modules\discord.js\src\client\actions\MessageDelete.js:24:16)
at Object.module.exports [as MESSAGE_DELETE] (C:\Users\ALL\Desktop\Slodziak\Slodziak13vCanary\node_modules\discord.js\src\client\websocket\handlers\MESSAGE_DELETE.js:4:32)
at WebSocketManager.handlePacket (C:\Users\ALL\Desktop\Slodziak\Slodziak13vCanary\node_modules\discord.js\src\client\websocket\WebSocketManager.js:346:31) at WebSocketShard.onPacket (C:\Users\ALL\Desktop\Slodziak\Slodziak13vCanary\node_modules\discord.js\src\client\websocket\WebSocketShard.js:478:22)
at WebSocketShard.onMessage (C:\Users\ALL\Desktop\Slodziak\Slodziak13vCanary\node_modules\discord.js\src\client\websocket\WebSocketShard.js:317:10)
at WebSocket.onMessage (C:\Users\ALL\Desktop\Slodziak\Slodziak13vCanary\node_modules\ws\lib\event-target.js:199:18)
Can someone help? I need these partials MESSAGE to messageUpdate because the bot did not respond to messages that were sent after restart. More specifically, to the automod, i.e. that it deletes invitations if someone has edited the message and has no permissions
messageUpdate
module.exports = class {
constructor(client) {
this.client = client;
}
async run(oldMessage, newMessage) {
const data = {};
const client = this.client;
data.config = client.config;
if (newMessage.author.bot) return;
if (!newMessage.editedAt) return;
code that deleted invites if member has not permissions
}
};
messageDelete
module.exports = class {
constructor(client) {
this.client = client;
}
async run(message) {
const data = {};
const client = this.client;
data.config = client.config;
if (message.author.bot) return;
Code that snipes deleted message
}
}
The error means that newMessage.author is null. You already knew that you would need to use the MESSAGE partials.
The problem is that you don't check if the received message is a partial only.
A partial message will not contain all of the original Message parameters. The id, the channelId, and the guildId will be present, but others, like author, can be null.
To solve this, you'll need to check if your message is partial only, and if it is, you can fetch it and use it as usual:
module.exports = class {
constructor(client) {
this.client = client;
}
async run(oldMessage, newMessage) {
const data = {};
const client = this.client;
data.config = client.config;
if (oldMessage.partial) oldMessage = await oldMessage.fetch();
if (newMessage.partial) newMessage = await newMessage.fetch();
if (newMessage.author.bot) return;
if (!newMessage.editedAt) return;
// code that deleted invites if the member has no permissions
}
};
Related
I'm trying to make my bot say a simple message whenever someone tells it 'hello'. Currently, the code of the command itself looks like this:
const { SlashCommandBuilder } = require('#discordjs/builders');
const greeter = "default";
const greetOptions = [
`Hello, ${greeter}. It's very nice to hear from you today.`
]
module.exports = {
data: new SlashCommandBuilder()
.setName('hello')
.setDescription('say hi to Hal!'),
execute(message, args) {
let greeter = message.user.username;
msg.channel.send(greetOptions[Math.floor(Math.random() * greetOptions.length)]);
},
};
The code I am using to manage when commands are typed looks as follows:
let prefix = "hal, ";
client.on('messageCreate', message => {
if (!message.content.startsWith(prefix)) return;
const args = message.content.slice(prefix.length);
const command = args.toLowerCase();
console.log(`${message.author.tag} called ${command}`);
if (!client.commands.has(command)) return;
try {
client.commands.get(command).execute(message, args);
} catch (error) {
console.error(error);
message.channel.send({ content: 'There was an error while executing this command!', ephemeral: true });
}
});
When I run it, it throws the error, "Cannot read properties of undefined (reading 'username').
If You want to mention a user you can do ${message.author}. But if you want to say for ex. Hello, Brandon then you need to do ${message.author.username}. The message ${message.author.tag} does not always function and also I recommend you const user = message.mentions.users.first() || message.author or just const user = message.author for short so then you can do ${user.username}. Maybe this might fix the bot failing to respond otherwise if it doesn't tell me.
Fix the first code and change msg.channel.send to message.channel.send.
I created a small command handler with a simple "ping" command. However, trying to access message.channel shows up undefined. Why is this?
./index.js
//Discord, fs, prefix, client etc. declarations
client.commands = new Collection()
const files = fs.readdirSync("./commands").filter(file => file.endsWith(".js")
for (const file of files) {
const command = require(`./commands/${file}`)
client.commands.set(command.name, command)
}
client.on("messageCreate", message => {
const [command, ...args] = message.content.slice(prefix.length).split(/ +/g)
if (client.commands.has(command) {
client.commands.get(command).execute(message, client, args)
}
})
client.login(/*token*/)
./commands/ping.js
module.exports = {
name: "ping",
description: "Make me say pong",
async execute(client, message, args) {
message.channel.send("Pong!")
}
}
While Message.channel may be valid, message is not an instance of Message. It is actually an instance of Client. The order of the arguments always matter, and putting them in the wrong order can throw TypeErrors. There is 1 simple solution here
Make the arguments in the right order! You can either change the execution, or the declaration
client.commands.get(command).execute(client, message, args)
This is really common and happens for more than sending messages to channels
I'm trying to code a discord bot that send a message to all users in a list. I am having problems using the client.users.fetch(); method on discord.js. The error message says something about DiscordAPIError: Unknown user, Unhandled promise rejection, and DiscordAPIError: Cannot send messages to this user, even though I am in the same guild as the bot.
Here is the code I have so far:
const Discord = require('discord.js');
const client = new Discord.Client();
const ownerId = 'YOUR-ID'
const users = ['YOUR-ID']
client.on('ready', () => {
console.log('Bot is online!');
});
client.on('message', async message => {
if (message.content.includes("test")) {
if (message.author.id == ownerId) {
message.channel.send("ok!")
var userID
var user
for (let i = 0; i < users.length; i++) {
userID = users[i];
user = client.users.fetch(userID.toString(), true);
client.user.send('works');
}
}
}
});
client.login('YOUR-TOKEN');
There are several issues in your code.
Firstly, client.users.fetch(...) is an asynchronous function therefore it requires an await.
Secondly, client.user.send(...) will actually send a message to the bot which is impossible. So you'll want to replace it with either message.channel.send(...) which will send the message in the same channel the message was received in or message.author.send(...) which will send a message to the author of the message.
Below is an example fix:
const Discord = require('discord.js'); // Define Discord
const client = new Discord.Client(); // Define client
const ownerId = 'your-discord-user-id';
const users = [] // Array of user ID's
client.on('ready', () => { // Ready event listener
console.log('Bot is online!'); // Log that the bot is online
});
client.on('message', async message => { // Message event listener
if (message.content.includes("test")) { // If the message includes "test"
if (message.author.id == ownerId) { // If the author of the message is the bot owner
message.channel.send("ok!"); // Send a message
// Define variables
let userID;
let user;
for (let i = 0; i < users.length; i++) { // Loop through the users array
userID = users[i]; // Get the user ID from the array
user = await client.users.fetch(userID.toString()); // Await for the user to be fetched
message.channel.send('works'); // Send a message to tell the message author the command worked
}
}
}
});
client.login('YOUR-TOKEN'); // Login your bot
I'm using discord.js as well as javascript to code. I'm just trying to work out this test bot I have made and despite all the step-by-step guides that I have pretty much followed to a T, I am only able to get the bot to work with basic "ping pong" like commands. Every time I try to add anything more or less, the bot completely stops working.
Here is an example of the code I have so far. It's a little 'gaggy' some friends and I, as I said, are just trying to work out how to make the test bot first. Maybe it's just completely going over my head and I'm just not doing it the correct way. Either way, some assistance would be helpful. Thanks.
/*
A ping pong bot, whenever you send "ping", it replies "pong".
*/
// Import the discord.js module
const Discord = require('discord.js');
// Create an instance of a Discord client
// This is sometimes called 'bot', but 'client' is prefered
const client = new Discord.Client();
const token = 'MY_BOT_TOKEN';
client.on('ready', () => {
console.log('I am ready!');
});
// Create an event listener for messages
client.on('message', message => {
// If the message is "ping"
if (message.content === 'silas!ping') {
// Send "pong" in the same channel
message.channel.send('pong');
}
});
// Log our bot in
client.login(token);
What I am also trying to do is add a "SAY" command, this is what I have been using, and once I add it, it completely breaks the bot. As well as adding in a prefix.
client.on('message', message => {
if (message.channel.type != 'text' || message.author.bot || !message.startsWith('*'))
return;
if (message.content === '*ping') {
message.channel.send('pong');
}
});
client.on('message', message => {
if (message.channel.type != 'text' || message.author.bot || !message.startsWith('*'))
return;
if (message.content === '*ping') {
message.channel.send('pong');
}
else if (message.content === '*say') {
message.channel.send(message.content);
}
});
Im not sure if this means anything to get help, but this is the error i recieve when trying to add the following code given to me for help
if (!message.content.startsWith(prefix)) return;
^
ReferenceError: message is not defined
at Object. (C:\Users\my location files\index.js:23:1)
[90m at Module._compile (internal/modules/cjs/loader.js:1137:30)[39m
[90m at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)[39m
[90m at Module.load (internal/modules/cjs/loader.js:985:32)[39m
[90m at Function.Module._load (internal/modules/cjs/loader.js:878:14)[39m
[90m at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)[39m
[90m at internal/main/run_main_module.js:17:47[39m
Another, little more advanced, way would be to seperate the prefix and the command.
This allows you to define a prefix without changing all .startsWith() conditions.
With Array.split() you can seperate each following word if they have whitespaces between each other.
This would end up like this:
let prefix = "*";
// Prevents any unnecessary condition checks if the message does not contain the prefix
if (!message.content.startsWith(prefix)) return;
const args = message.content.toLowerCase().split(' ');
let command = args[0];
command = command.slice(prefix.length);
if(command === "say"){
// do whatevery you want :)
}
After it was checked that the message starts with the prefix, you split each word and then take the first one. With slicing the prefix out, you end up with only having the plain command. This allows an even better approach for checking commands since the condition doesn't include the prefix anymore.
Everything after args[0] would be additional arguments you can use for any case you want.
you could make a config.json.
inside your config.json
{
"TOKEN": "",
"PREFIX": "",
}
and then require on you bot main file and also define the client.commands and client.aliases
const config = require("./config.json");
client.commands = new Discord.Collection();
client.aliases = new Discord.Collection();
on your message event you wanna add a command handler
client.on("message", async(message) => {
if (message.author.bot) return;
if (!message.guild) return;
if (!message.content.startsWith(config.PREFIX)) return;
if (!message.member) message.member = await message.guild.fetchMember(message);
const args = message.content.slice(config.PREFIX.length).trim().split(/ +/g);
const cmd = args.shift().toLowerCase();
if (cmd.length === 0) return;
let command = client.commands.get(cmd);
if (!command) command = client.commands.get(client.aliases.get(cmd));
try {
if (command)
command.run(client, message, args);
} catch(error) {
console.log(error)
}
})
after that, add a new folder call handlers and inside the handlers folder make a new file call command.js
const { readdirSync } = require("fs");
const ascii = require("ascii-table");
let table = new ascii("Command");
table.setHeading("Command", "Load Status");
module.exports = (client) => {
readdirSync("./commands/").forEach(dir => {
const commands = readdirSync(`./commands/${dir}/`).filter(file => file.endsWith(".js"));
for (let file of commands) {
let pull = require(`../commands/${dir}/${file}`);
if (pull.name) {
client.commands.set(pull.name, pull);
table.addRow(file, '✅');
} else {
table.addRow(file, `❌ -> missing a help.name, or help.name is not a string.`);
continue;
}
if (pull.aliases && Array.isArray(pull.aliases)) pull.aliases.forEach(alias => client.aliases.set(alias, pull.name));
}
});
console.log(table.toString());
}
and then require on the bot main file
["command"].forEach(handler => {
require(`./handlers/${handler}`)(client);
});
make a new folder call commands inside add new folder command category such as info
simple ping command
module.exports = {
name: "ping",
aliases: ["pong"],
description: "Check Bot Ping",
usage: "",
run: async(client, message, args) => {
messsage.channel.send("PONG")
},
};
hope this guide is helpful :)
I'm currently trying to reorganize my Bot for my Discord Server by upgrading my Command Handler.
Most of the command I have also upgraded but one command stands out because it is not working.
When I try to run that command the following happens:
(node:7020) UnhandledPromiseRejectionWarning: TypeError: CommandFile.run is not a constructor
at module.exports.run (C:\Users\jason\OneDrive\Bureaublad\KoalaBot\KoalaBotV2.1\src\commands\Music\Play.js:43:20)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:7020) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:7020) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
And this is the code I have for the part that is currently not working, full code is somewhere down below:
if (!Validate) {
let CommandFile = require(`./Search.js`);
return new CommandFile.run(Client, message, args, ops);
}
The Command Handler I'm using is from MenuDocs v12 Tutorial.
I've tried placing this part into a function outside the class and called the function after the if statement but to no avail.
let CommandFile = require(`./Search.js`);
return CommandFile.run(Client, message, args, ops);
I have also checked if it was the correct file location of the Search.js.
Search.js File:
const Command = require('../../Structures/Command.js');
const Discord = require("discord.js");
const Search= require('yt-search');
module.exports = class extends Command {
// eslint-disable-next-line no-unused-vars
async run(message, args, ops) {
const Client = this.client
//Search for videos based on arguments
Search(args.join(' '), function(err, res) {
//Error Handling
let errembed = new Discord.MessageEmbed()
.setColor("#218559")
.setAuthor(Client.user.username, Client.user.displayAvatarURL({ dynamic:true }))
.setDescription("Sorry, something went wrong")
if (err) return message.channel.send(errembed);
//Show first 10 results
let Videos = res.videos.slice(0, 10);
//Loop for output string
let Resp = '';
for (var i in Videos) {
Resp += `**[${parseInt(i)+1}]:** \`${Videos[i].title}\`\n`;
}
//Text Info Instructions
Resp += `\n**Choose a number between** \`1-${Videos.length}\``;
//Embed
let Embed = new Discord.MessageEmbed()
.setColor("#218559")
.setTitle("Search Results")
.setAuthor(Client.user.username, Client.user.displayAvatarURL({ dynamic:true }))
.setDescription(Resp)
.setThumbnail(Client.user.displayAvatarURL({ dynamic:true }))
//Send output
message.channel.send(Embed);
//Message collector
const Filter = m => !isNaN(m.content) && m.content < Videos.length+1 && m.content > 0;
//Filter accepts only numbers
const Collector = message.channel.createMessageCollector(Filter);
//Update collector variables
Collector.videos = Videos;
//Create Listener Event
Collector.once('collect', function(m) {
//Run play command, passing in the url as args[0]
let CommandFile = require(`./Play.js`);
CommandFile.run(Client, message, [this.videos[parseInt(m.content)-1].url], ops);
});
});
}
};
This all worked with the previous command handler I was using.
The complete Play.js:
const Command = require('../../Structures/Command.js');
const Discord = require("discord.js");
const ytdl = require("ytdl-core");
module.exports = class extends Command {
constructor(...args) {
super(...args, {
aliases: ['p']
});
}
// eslint-disable-next-line no-unused-vars
async run(message, args, ops) {
const Client = this.client
//Check if author is connected to a voice channel
let AC = new Discord.MessageEmbed()
.setColor("#218559")
.setAuthor(Client.user.username, Client.user.displayAvatarURL({
dynamic: true
}))
.setDescription("You have to be connected to a voice channel, you dummy!")
if (!message.member.voice.channel) return message.channel.send(AC);
//Check if author input an URL
let VU = new Discord.MessageEmbed()
.setColor("#218559")
.setAuthor(Client.user.username, Client.user.displayAvatarURL({
dynamic: true
}))
.setDescription("Hey Dumdum, you need a valid URL")
if (!args[0]) return message.channel.send(VU);
//Validate Info
let Validate = await ytdl.validateURL(args[0]);
//Check Validation
if (!Validate) {
let CommandFile = require(`./Search.js`);
return new CommandFile.run(Client, message, args, ops);
}
//Fetch video information
let Info = await ytdl.getInfo(args[0]);
//Fetch Active
let Data = ops.active.get(message.guild.id) || {};
//Update the Data
if (!Data.Connection) Data.Connection = await message.member.voice.channel.join();
if (!Data.Queue) Data.Queue = [];
Data.guildID = message.guild.id;
//Add song to queue
Data.Queue.push({
SongTitle: Info.videoDetails.title,
Requester: message.author.tag,
url: args[0],
AnnounceChannel: message.channel.id
})
//If there is no dispatcher, run the play function
let QA = new Discord.MessageEmbed()
.setColor("#218559")
.setAuthor(Client.user.username, Client.user.displayAvatarURL({
dynamic: true
}))
.setDescription(`Added to Queue: ${Info.videoDetails.title} | Requested by my favourite dummy ${Data.Queue[0].Requester}`)
if (!Data.Dispatcher) Play(Client, ops, Data);
else {
message.channel.send(QA);
}
//Update the Map
ops.active.set(message.guild.id, Data);
}
};
//Play Function
async function Play(Client, ops, Data) {
//Send Now Playing Message
let NP = new Discord.MessageEmbed()
.setColor("#218559")
.setAuthor(Client.user.username, Client.user.displayAvatarURL({
dynamic: true
}))
.setDescription(`Now playing: ${Data.Queue[0].SongTitle} | Requested by my favourite dummy ${Data.Queue[0].Requester}`)
Client.channels.cache.get(Data.Queue[0].AnnounceChannel).send(NP);
//Update Dispatcher Data
Data.Dispatcher = await Data.Connection.play(ytdl(Data.Queue[0].url, {
filter: "audioonly"
}));
Data.Dispatcher.guildID = Data.guildID;
//Listener event
Data.Dispatcher.once("finish", function() {
//Finish Function
finish(Client, ops, this);
});
}
function finish(Client, ops, Dispatcher) {
//Fetch Guild Object from Map
let Fetched = ops.active.get(Dispatcher.guildID);
//Remove the previous SongTitle
Fetched.Queue.shift();
//Check if Queue is empty
if (Fetched.Queue.length > 0) {
//Update the map with the new queue
ops.active.set(Dispatcher.guildID, Fetched);
//Play the next song
Play(Client, ops, Fetched);
} else {
//Delete the guild object from the Map
ops.active.delete(Dispatcher.guildID);
//Leave the voice channel
let vc = Client.guilds.cache.get(Dispatcher.guildID).me.voice.channel;
if (vc) vc.leave();
}
}
Someone suggested I do this in the Search.js but the error there is that it isn't a class.
class Search extends Command {
async run(message, args, ops) {
/* things here */
}
};
module.exports = { Search };
run is not a static method, but you're calling it like one. add a parentheses when you initialize the exported class inside the required commandfile.
if (!Validate) {
let CommandFile = require(`./Search.js`);
return new CommandFile.run(Client, message, args, ops);
}
Node expects an empty constructor when you initialize a new class, so it views the run method you're calling as a constructor. The problem is that run isn't called by the actual class.
This
return new CommandFile.run(Client, message, args, ops);
should become this:
return new CommandFile().run(Client, message, args, ops);
Use this same logic wherever you initialize a new class without calling an empty constructor.
Unrelated to your issue, you do const Client = this.client which will turn up undefined since this.client isn't anything. there is no point in setting it as a class field from what i'm seeing of the file. Just set Client to a new Discord.Client() instead.
Hope this helped and good luck!