如何在JDA中实现带回复交互的Discord机器人离开反馈DM功能
解决Discord机器人离开反馈的私聊监听问题
你的思路方向是对的,但JDA的监听器机制是全局注册生效的,没办法直接给单个私聊频道绑定专属的监听器实例。我们需要换一种更合理的方式:用一个全局监听器,配合用户跟踪集合来识别哪些用户正在提交离开反馈。下面是具体的实现方案:
1. 全局跟踪需要反馈的用户
首先在你的Main类里添加一个线程安全的集合,用来记录当前需要提交反馈的用户ID(因为JDA的事件是多线程处理的,必须用线程安全的容器避免并发问题):
import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class Main { public static JDA jda; // 用ConcurrentHashMap的keySet实现线程安全的Set public static Set<Long> feedbackPendingUsers = ConcurrentHashMap.newKeySet(); // ... 你的其他初始化代码 }
2. 修改离开事件代码,标记需要反馈的用户
在发送离开DM的同时,把用户ID加入到刚才的跟踪集合里:
// 成员离开事件的处理代码 event.getUser().openPrivateChannel() .flatMap(channel -> { // 将用户标记为需要提交反馈 Main.feedbackPendingUsers.add(event.getUser().getIdLong()); return channel.sendMessage("Hello, we are sorry you're leaving "+event.getGuild().getName()+", if you don't mind, please tell us why you left or leave any other feedback here, it'll help us improve the server and improve experience for you if you re-join again in the future.\n\nThank you ❤."); }) .queue( success -> {}, error -> { // 如果DM发送失败,记得移除用户标记,避免无效跟踪 Main.feedbackPendingUsers.remove(event.getUser().getIdLong()); } );
3. 重构全局反馈监听器
把你原来的Feedback类改成全局监听器,不需要绑定特定频道和用户,而是通过集合判断是否需要处理当前消息:
import bot.Main; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; public class FeedbackListener extends ListenerAdapter { @Override public void onMessageReceived(MessageReceivedEvent event) { // 跳过机器人发送的消息 if (event.getAuthor().isBot()) return; // 只处理:私聊频道 + 用户在反馈跟踪集合里 if (!event.isFromPrivateChannel() || !Main.feedbackPendingUsers.contains(event.getAuthor().getIdLong())) { return; } User user = event.getAuthor(); String feedbackContent = event.getMessage().getContentRaw(); // 回复用户感谢消息 event.getChannel().sendMessage("Thanks for your feedback!").queue(); // 构建反馈Embed EmbedBuilder feedbackEmbed = new EmbedBuilder(); feedbackEmbed.setTitle("Automated System Operations - Leaving Feedback"); feedbackEmbed.addField("Feedback", feedbackContent, false); feedbackEmbed.setColor(0xC90004); feedbackEmbed.setAuthor(user.getAsTag() + " - " + user.getId(), null, user.getAvatarUrl()); feedbackEmbed.setImage("https://media.discordapp.net/attachments/894913784823566428/896323821336948736/unknown.png?width=384&height=192"); // 发送到服务器的指定反馈频道 TextChannel feedbackLogChannel = Main.jda.getGuildById("894913620868202506") .getTextChannelById("896322509874540545"); if (feedbackLogChannel != null) { feedbackLogChannel.sendMessage(feedbackEmbed.build()).queue( success -> { // 处理成功后,移除用户的跟踪标记 Main.feedbackPendingUsers.remove(user.getIdLong()); }, error -> { // 即使发送失败,也要移除标记,避免用户重复提交 Main.feedbackPendingUsers.remove(user.getIdLong()); } ); } else { // 如果找不到日志频道,也要移除标记并告知用户 Main.feedbackPendingUsers.remove(user.getIdLong()); event.getChannel().sendMessage("Oops, we ran into an issue saving your feedback. Sorry about that!").queue(); } } }
4. 注册全局监听器
最后在Main类初始化JDA的时候,别忘了注册这个监听器:
public static void main(String[] args) throws Exception { jda = JDABuilder.createDefault("YOUR_BOT_TOKEN") .addEventListeners(new FeedbackListener()) // 注册反馈监听器 // ... 其他监听器或配置 .build(); }
为什么你的原方法行不通?
JDA的ListenerAdapter实例是全局生效的,一旦通过addEventListeners注册,就会接收所有匹配的事件。你原来的思路是给每个私聊频道创建一个监听器实例,但这样会导致监听器实例越来越多,而且JDA不会区分哪个监听器对应哪个频道——所有监听器都会收到所有消息事件,既浪费资源又容易出问题。用全局监听器+用户跟踪的方式,既简洁又高效。
内容的提问来源于stack exchange,提问作者Abdullah Ahmed
相关产品推荐
相关产品推荐

