Reputation: 3
I am making a discord.js
bot for a server I am in, but after attempting to send a DM to a user (who was just kicked, to notify them of it,) the bot won't send it at all and it will spit out an error,
"UnhandledPromiseRejectionWarning: DiscordAPIError: Cannot send messages to this user"
its out of control now, because it will work, I'll add a small something, it will STOP working (more or less expected) and then when I revert that addition it will spit out the error again.
Here is my code for the kick command
const Discord = require("discord.js");
const Client = new Discord.Client
const prefix = "m!"
module.exports.run = async(bot, message, args) => {
params = message.content.slice(prefix.length).trim().split(/ +/g);
command = args.shift().toLowerCase();
let memberKick = message.mentions.members.first();
let reasonKick = args.slice(1).join(" ");
if(message.member.roles.find("name", "MedaFrost Controller")) {
var userReslv = message.guild.members.find("id", memberKick.id);
let usericon = memberKick.displayAvatarURL;
let botembed = new Discord.RichEmbed
botembed.setDescription("❌ You Were Kicked From Roscord!");
botembed.setColor("FF0000");
botembed.setThumbnail(usericon);
botembed.addField("Reason:", `${reasonKick}`);
userReslv.send(botembed);
memberKick.kick(reasonKick);
message.channel.send("✅ " + `${memberKick}` + " was kicked! ✅")
} else {
const userId = message.member.id
message.channel.send("Sorry, " + `<@${userId}>` + ", but you do not have sufficient permissions!")
}
}
module.exports.help = {
name: "kickplayer",
description: "Kicks mentioned user."
}
(Proof that the emoji is not the problem) Link to image:
Upvotes: 0
Views: 14361
Reputation: 126
You should not be using delays or timers to solve this issue. Asynchronous calls in Discord.js return Promises, you should use those promises.
Promises are something that can basically turn your asynchronous workflow synchronous: when something happens -> do this.
This is the workflow:
Don't worry, they won't have time to react to the message as these actions will most likely happen instantaneously, thanks to Promises. There are no additional delays.
So basically:
GuildMember
.createDM()
.then((DMChannel) => {
// We have now a channel ready.
// Send the message.
DMChannel
.send(reason)
.then(() => {
// Message sent, time to kick.
GuildMember.kick(reason);
});
});
Here's the referenced GuildMember
You can also catch, and you should. Catching happens when a Promise fails to execute the task it was assigned.
GuildMember
.createDM()
.then((DMChannel) => {
// We have now a channel ready.
// Send the message.
DMChannel
.send(reason)
.then(() => {
// Message sent, time to kick.
GuildMember
.kick(reason)
.catch((e) => {
console.log('Failed to kick!', e);
// Do something else instead...
});
});
});
(You can catch the message sending too, or creating the channel.)
Here you can learn more about Promises, which are an essential part of JavaScript: https://javascript.info/promise-basics
Upvotes: 10
Reputation: 26920
It looks like a lot of the functions you are using are asynchronous.
Specifically:
Additionally, you are trying to send
to a GuildMember
. I'm not super familiar with this API, but I think they may be considered not a GuildMember
anymore after you kick in which case the reference may be stale. I think you should instead send directly to the User
, after you kick
.
Additionally, I don't think you are waiting enough in general. kick
and send
both return Promise
s which you should probably await
to resolve, especially kick
.
After you have that reference then use the send
with the constructed message:
async function kicker(bot, message, args) {
/** @type GuildMember */
const guildMemberToKick = message.mentions.members.first();
/** @type User */
const guildMemberUser = guildMemberToKick.user;
/** @type RichEmbed */
const kickRichMessage = new Discord.RichEmbed;
/** @type String */
const kickReason = "You've been a baaaad person";
// Build the rich kick message
kickRichMessage.setDescription("❌ You Were Kicked From Roscord!");
kickRichMessage.setColor("FF0000");
kickRichMessage.setThumbnail(guildMemberUser.displayAvatarURL);
kickRichMessage.addField("Reason:", kickReason);
// Kick and wait
await guildMemberToKick.kick(kickReason);
// Tell the user we kicked them and wait
await guildMemberUser.send(kickRichMessage);
// Tell the channel we kicked and wait
await message.channel.send(`✅ ${guildMemberToKick.displayName} was kicked! ✅`);
}
Upvotes: 2
Reputation: 169368
Expanding from the comments: Maybe add a delay between the message and the actual kick?
Since your function is async
, it's a good idea to write a small async
delay helper:
const delay = (msec) => new Promise((resolve) => setTimeout(resolve, msec));
Now you can simply
userReslv.send(botembed);
await delay(100); // 100 msec = 0.1 seconds
memberKick.kick(reasonKick);
// ...
Upvotes: 1