如何使Discord Bot等待特定的用户使用JDA发送消息?

问题描述:

我目前正在介绍Java编程课程,并且最近开始尝试使用JDA工具为我的服务器制作一个基本的Discord机器人.理想情况下,我希望我的机器人在有人说你好苹果!"时做出响应.询问他们的名字,然后回答嗨!"如果此消息是由说你好苹果!"的同一个人发送的现在,我的机器人无法等待最初的"Hello Apples!"之后的所有用户输入.消息,并立即溢出所有文本.我相信我当前的代码已正确设置,以确保漫游器仅以"Hi!"响应.如果它从最初发送"Hi Apples!"的那个人那里收到一条消息,但是我不能完全确定,因为它不等待其他消息,因此从同一条消息中读取两次并打印出来:
你好!告诉我你的名字,或说停"!
苹果!
轮到您

I'm currently taking an intro to programming class in Java, and have recently started experimenting with the JDA tools to make a basic Discord bot for my server. Ideally, I want my bot to respond when someone says "Hello Apples!" by asking them for their name, then responding with "Hi !" if this message was sent by the same person who said "Hello Apples!" Right now my bot fails to await any user input past the initial "Hello Apples!" message, and spills out all of its text at once. I believe my current code is set up properly to ensure that the bot will only respond with "Hi !" if it receives a message from the same person who originally sent "Hi Apples!", but I can't be completely sure because it doesn't wait for an additional message, and as a result reads from the same message twice and prints out:
Hi! Tell me your name, or say "Stop"!
Hi Hi Apples!!
Wait your turn

我真的很想知道如何创建某种形式的停止",或者一种方法,该方法可以使机器人等待来自最初问候机器人的用户的其他用户输入,以及在可能的情况下,设置时间限制,以使漫游器在不回复的情况下也不会无法运行.

I would really like to know how to create a "stop" of some sort, or a method that would cause the bot to wait for addition user input from the user who originally greeted the bot, and if possible, a way to set a time limit so the bot doesn't remain inoperable if they don't reply.

import net.dv8tion.jda.core.AccountType;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.JDABuilder;

public class Main {
  public static void main(String[] args) throws Exception {
    try {
      JDA api = new     JDABuilder(AccountType.BOT).setToken("NTQxMTMxMDc4MTY1ODU2MjYw.DzbGoQ.oFIM_py    pLMOc60qU1PgeeUXU8Qo").build();
      api.addEventListener(new MyEventListener());
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.entities.MessageChannel;
import net.dv8tion.jda.core.entities.Role;
import net.dv8tion.jda.core.entities.User;
import net.dv8tion.jda.core.entities.*;
import net.dv8tion.jda.core.events.message.MessageReceivedEvent;
import net.dv8tion.jda.core.hooks.ListenerAdapter;

public class MyEventListener extends ListenerAdapter {
  public void onMessageReceived(MessageReceivedEvent event) {
    if (event.getAuthor().isBot()) return;

    Message message = event.getMessage();
    String content = message.getContentRaw();
    MessageChannel channel = event.getChannel();

    if (content.startsWith("Hi Apples!")) {
      Member member = message.getMember();
      channel.sendMessage("Hi! Tell me your name, or say \"Stop\"!").queue();
      int n = 0;    
      while (n == 0) {
        Message message2 = event.getMessage(); 
        String content2 = message.getContentRaw();
        Member member2 = message2.getMember();
        String nick = member2.getNickname();
        if (member == member2) {
          channel.sendMessage("Hi " + content2 + "!").queue();
          n = 1;
        }
        else {
        }
          channel.sendMessage("Wait your turn " + nick + "!").queue();
        if (content2 == "Stop") {
          channel.sendMessage("Understood!").queue();
          n = 1;
        }
      }   
    }        
  }
}

我的预期结果是:
使用者:苹果!
BOT:嗨!告诉我你的名字,或者说停止!
USER2:您好!
BOT:请轮到USER2!
使用者:Jimmy
BOT:吉米!

My expected results are:
USER: Hi Apples!
BOT: Hi! Tell me your name, or say stop!
USER2: Hi!
BOT: Wait your turn USER2!
USER: Jimmy
BOT: Hi Jimmy!

实际结果:(一次发送)
你好!告诉我你的名字,或说停"!
苹果!
轮到您了(我不和谐的昵称)!

Actual results: (Sent all at once)
Hi! Tell me your name, or say "Stop"!
Hi Hi Apples!!
Wait your turn (my discord nickname)!

由于您使用的是基于事件的框架,因此可以使用状态机来实现此行为.每当您得到初始触发器时,在这种情况下,如果"Hi Apple!" ,您都将为该文本通道启动一个新的状态机.

Since you are using an event-based framework you could implement this behavior with the use of a state-machine. Whenever you get your initial trigger, in this case "Hi Apple!" you would initiate a new state-machine for that text channel.

在此状态机中,您将处理消息事件,直到到达终止信号为止,在本例中为"Stop!" .

In this state-machine you handle message events until your termination signal arrives, in this case "Stop!".

状态机将使用事件方法中的切换案例以及私有的 state 字段来实现.在这种情况下,您整个对话中只有一次互动,因此只有一种状态使这一点变得毫无意义.

The state-machine would be implemented using a switch-case in the event method together with a private state field. In this case you only have one interaction in the entire conversation so there is only one state which makes this pointless.

但是例如,在进行对话的情况下(我认为这将在以后发生),您将需要使用状态机概念.

But for example in the case of having a conversation which is what I assume this will be later on you would need to go with the state-machine concept.

public class AppleStateMachine extends ListenerAdapter {
    private final long channelId, authorId; // id because keeping the entity would risk cache to become outdated

    public AppleStateMachine(MessageChannel channel, User author) {
        this.channelId = channel.getIdLong();
        this.authorId = author.getIdLong();
    }

    @Override
    public void onMessageReceived(MessageReceivedEvent event) {
        if (event.getAuthor().isBot()) return; // don't respond to other bots
        if (event.getChannel().getIdLong() != channelId) return; // ignore other channels
        MessageChannel channel = event.getChannel();
        String content = event.getMessage().getContentRaw();
        // since only one state is present you don't need a switch but that would be the concept if you had more than 1 interaction point in this protocol
        if (content.equals("Stop")) {
            channel.sendMessage("Understood!").queue();
            event.getJDA().removeEventListener(this); // stop listening
        }
        else if (event.getAuthor().getIdLong() == authorId) {
            channel.sendMessage("Hi " + content + "!").queue();
            event.getJDA().removeEventListener(this); // stop listening
        }
        else {
            channel.sendMessage("Wait your turn " + event.getMember().getEffectiveName() + "!").queue();
        }
    }
}

然后,您只需要在初始事件处理程序中注册此实例

Then you only need to register an instance of this in your initial event handler

if (content.startsWith("Hi Apples!")) {
    channel.sendMessage("Hi! Tell me your name, or say \"Stop\"!").queue();
    event.getJDA().addEventListener(new AppleStateMachine(channel, member.getUser());
}

请确保此处不要混用 GuildMessageReceivedEvent MessageReceivedEvent ,因为它们是按顺序触发的,因此您可能会收到两次初始消息.状态机和消息侦听器都应侦听相同类型的事件.

Make sure that you don't mix GuildMessageReceivedEvent and MessageReceivedEvent here, since they are fired in sequence and you might receive your initial message twice. Both your state machine and the message listener should listen to the same kind of event.

另一种替代方法是使用 JDA-Utilities EventWaiter.

Another alternative to this is using the JDA-Utilities class EventWaiter.