Lonster_Monster
Lonster_Monster

Reputation: 73

How to make Discord.js reply to discord DM messages

I am trying to get discord.js to read DM messages on Discord to have a bot that will be a server/faction application bot made from scratch but I Have it to send the first part when you type %apply to the bot the problem comes when trying to get passed question 2 it keeps getting question 2 instead of going to question 3

I am trying to filter out the DM messages when they are not the same as the passed ones so I have several if commands

bot.on("message", function(message) {
if (message.author.equals(bot.user)) return;
    if (message.content === "%apply") {
        apply = "TRUE";
        a0 = message.author.lastMessageID
        message.author.sendMessage("```We need to ask some questions so  we can know a litte bit about yourself```");
        message.author.sendMessage("```Application Started - Type '#Cancel' to cancel the application```");
        message.author.sendMessage("```Question 1: In-Game Name?```");
    }
    if ((message.guild === null) && (message.author.lastMessageID != a0) && (message.content != "%apply") && (apply === "TRUE")) {
        a1 = message.author.lastMessageID;
        message.author.sendMessage("```Question 2: Age?```");
    }
    if ((message.guild === null) && (message.author.lastMessageID != a1) && (message.author.lastMessageID != a0) && (apply === "TRUE")) {
        a2 = message.author.lastMessageID;
        message.author.sendMessage("```Question 3: Timezone? NA, AU, EU, NZ, or Other? (If other, describe your timezone)```");
    }
    if ((message.guild === null) && (message.author.lastMessageID != a2) && (message.author.lastMessageID != a1) && (message.author.lastMessageID != a0) && (apply === "TRUE")) {
        a3 = message.author.lastMessageID;
        message.author.sendMessage("```Question 4: Do you have schematica?```");
    }

I expected it to go from question 1 to question 2 the question 3

Upvotes: 0

Views: 11064

Answers (2)

slothiful
slothiful

Reputation: 5623

Preface

Although @Gruntzy's answer is not wrong, there's another solution that's built into Discord.js and meant for these situations - TextChannel.awaitMessages(). You can use it in a system like shown below.

Sample Code

const questions = [                    // ------------------------------------
  "What's your IGN?",                  //
  "How old are you?",                  // Define the questions you'd like the
  "What time zone do you reside in?",  // application to have in this array.
  "Do you have Schematica?"            //
];                                     // ------------------------------------

const applying = [];

bot.on("message", async message => {
  if (message.author.bot) return;

  if (message.content.toLowerCase() === "%apply") {
    if (applying.includes(message.author.id)) return;

    try {
      console.log(`${message.author.tag} began applying.`);

      applying.push(message.author.id);
      await message.channel.send(":pencil: **Application started!** Type `#cancel` to exit.");

      for (let i = 0, cancel = false; i < questions.length && cancel === false; i++) {
        await message.channel.send(questions[i]);
        await message.channel.awaitMessages(m => m.author.id === message.author.id, { max: 1, time: 300000, errors: ["time"] })
          .then(collected => {
            if (collected.first().content.toLowerCase() === "#cancel") {
              await message.channel.send(":x: **Application cancelled.**");
              applying.splice(applying.indexOf(message.author.id), 1);
              cancel = true;

              console.log(`${message.author.tag} cancelled their application.`);
            }
          }).catch(() => {
            await message.channel.send(":hourglass: **Application timed out.**");
            applying.splice(applying.indexOf(message.author.id), 1);
            cancel = true;

            console.log(`${message.author.tag} let their application time out.`);
          });
      }

      await message.channel.send(":thumbsup: **You're all done!**");

      console.log(`${message.author.tag} finished applying.`);
    } catch(err) {
      console.error(err);
    }
  }
});

Explanation

To make the code easier to understand, let's go through it step by step...

1. Preceding lines.

  • The questions array contains all the questions you'd like to be asked in the application. I've implemented an array for the sake of efficiency; a question can be added or removed by changing only one line, and the same code won't be copied and pasted over and over again.
  • The applying array will help us keep track of the users in the application process.

2. Message event.

  • I've implemented an arrow function (ES6) as the callback and declared it async so we can use await inside of it.
  • We prevent bots from triggering any commands.
  • We check case iNsEnSiTiVeLy whether or not we should run the command.
  • You'll notice that the proceeding code is wrapped in a try...catch statement. This allows us to catch any unhandled promise rejections (i.e. if TextChannel.send() throws an error) easily.

3. %apply command.

  • We add the user to the applying array.
  • We send the start message.
    • Notice the keyword await: it waits for the promise to be fulfilled before moving on.
  • We use a for loop to iterate through the questions array.
    • let i = 0, cancel = false declares i (the "counter" variable) and a cancel variable so we can stop the loop if the user wants to cancel the application or if it times out. We can't use break within our then() callback, so I've reverted to this method.
    • i < questions.length && cancel === false are our conditions to match before continuing with the next iteration - the counter must be within the range of the array, and cancel must still be false.
    • i++ increments the counter by 1.
  • Inside the loop, we send the question and then invoke TextChannel.awaitMessages(). Our first parameter is a filter that the message must pass through, and the second is the options.
    • In our then() callback, we check if the message was #cancel. If so, we send the cancellation message, remove the user from the array, and set cancel's value to true.
    • Our catch() method would be called if no message was provided in 5 minutes. So in our callback, we send the time out message, remove the user from the array, and set cancel's value to true.
  • Once the loop is complete, we send the completion message. At this point, you'd handle the application however you wish.

Upvotes: 4

Gruntzy
Gruntzy

Reputation: 443

Your variables a0, ... , a3 are inside the "onMessage" scope, and are undefined everytime in your callback. So if you message is not %apply, you are stuck into the "Question 2" step

You should keep track of your users registration steps in a global variable, and read it to know what step you are into. Here is a short example of how to do this. Note that this is a really basic approach, and it might be better to use some in-memory database if you need to add more complex features to your apply procedure. It also requires more controls, and I guess some other data storage to keep track of the user answers.

let userApplications = {}

bot.on("message", function(message) {
  if (message.author.equals(bot.user)) return;

  let authorId = message.author.id;

  if (message.content === "%apply") {
      console.log(`Apply begin for authorId ${authorId}`);
      // User is not already in a registration process    
      if (!(authorId in userApplications)) {
          userApplications[authorId] = { "step" : 1}

          message.author.send("```We need to ask some questions so  we can know a litte bit about yourself```");
          message.author.send("```Application Started - Type '#Cancel' to cancel the application```");
          message.author.send("```Question 1: In-Game Name?```");
      }

  } else {

      if (message.channel.type === "dm" && authorId in userApplications) {
          let authorApplication = userApplications[authorId];

          if (authorApplication.step == 1 ) {
              message.author.send("```Question 2: Age?```");
              authorApplication.step ++;
          }
          else if (authorApplication.step == 2) {
              message.author.send("```Question 3: Timezone? NA, AU, EU, NZ, or Other? (If other, describe your timezone)```");
              authorApplication.step ++;
          }
          else if (authorApplication.step == 3) {
              message.author.send("```Question 4: Do you have schematica?```");
              authorApplication.step ++;
          }

          else if (authorApplication.step == 4) {
              message.author.send("```Thanks for your registration. Type %apply to register again```");
              delete userApplications[authorId];
          }

      }
  }


});

some other quick notes :

  • sendMessage(msg) is deprecated in the discord.js api, send(msg) should be used now
  • to test if the message received is a dm, i think it's better to check message.channel.type rather than looking for a empty message.guildId

Upvotes: 0

Related Questions