0
votes

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:

4
Maybe add a delay between the message and the actual kick? – AKX
I'm a bit of a dumbass, how would I go about doing that? πŸ˜… – Frostbite
Posted as an answer. – AKX
What is that await(100)? Where is that coming from? – zero298

4 Answers

1
votes

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); 
// ...
10
votes

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:

  1. Send a message to a GuildMember
  2. Kick after the message is properly sent

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

1
votes

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 Promises 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! βœ…`);
}
0
votes

continuing AKX's response...

For anyone else struggling with this!

I put the const delay right above my var userReslv and I put the await delay(100) between my kick and message send. (There are probably many better ways to phrase that..)