使用JDA开发Discord机器人:无法动态更新在线用户数至活动状态
问题:JDA机器人活动状态无法更新显示在线用户数
我正在使用JDA开发Discord机器人,尝试在机器人的活动区域显示服务器在线用户数,但启动机器人后,活动状态从未更新。以下是相关代码:
主机器人类
public DiscordBot() throws LoginException { config = Dotenv.configure().load(); String token = config.get("TOKENDISCORD"); eventListener = new BotEventListener(); eventListener.addOnlineStatusChangeListener(event -> { int onlineMembers = eventListener.getQuantityOnlineMembers(); }); bot = JDABuilder.createDefault(token, GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_MESSAGES, GatewayIntent.GUILD_PRESENCES, GatewayIntent.GUILD_VOICE_STATES ) .setMemberCachePolicy(MemberCachePolicy.ALL) .disableCache(EnumSet.of( CacheFlag.CLIENT_STATUS, CacheFlag.ACTIVITY, CacheFlag.EMOTE )) .setChunkingFilter(ChunkingFilter.ALL) .enableCache(EnumSet.of( CacheFlag.ONLINE_STATUS )) .setActivity(Activity.listening(new CustomActivity(eventListener).getName())) .setMemberCachePolicy(MemberCachePolicy.VOICE) .addEventListeners(eventListener) .addEventListeners(new BotCommands()) .enableCache(CacheFlag.VOICE_STATE) .build(); }
事件监听器类
// 用户上线/下线时触发观察者 @Override public void onUserUpdateOnlineStatus(UserUpdateOnlineStatusEvent event) { System.out.println("test 1"); onlineStatusEvent = event; int onlineMembers = getQuantityOnlineMembers(); for (Consumer<Integer> listener : onlineStatusChangeListeners) { listener.accept(onlineMembers); } } // 统计在线用户数量 public int getQuantityOnlineMembers() { System.out.println("test 2"); if (onlineStatusEvent == null) { return 0; } System.out.println("test 3"); List<Member> members = onlineStatusEvent.getGuild().getMembers(); int onlineMembers = 0; for (Member member : members) { if (member.getOnlineStatus() == OnlineStatus.ONLINE) { onlineMembers++; System.out.println(onlineMembers); } } return onlineMembers; }
CustomActivity类
public class CustomActivity extends ActivityImpl { private BotEventListener eventListener; public CustomActivity(BotEventListener eventListener) { super(null); this.eventListener = eventListener; } @Override public String getName() { int onlineMembers = eventListener.getQuantityOnlineMembers(); return "Users online: " + String.valueOf(onlineMembers); } }
我猜想可能需要创建用户数映射,在onUserUpdateOnlineStatus的观察者触发时强制更新活动状态。
问题分析
- 活动状态不会自动刷新:调用
setActivity时,只会读取一次CustomActivity.getName()的返回值,之后JDA不会监听这个方法的变化,必须主动调用getPresence().setActivity()才能更新状态。 - 在线用户统计逻辑依赖不稳定:
getQuantityOnlineMembers方法依赖onlineStatusEvent,机器人启动时该事件为null,会直接返回0;且只能统计触发状态变化的单个服务器用户,无法适配多服务器场景。 - 缓存策略冲突:代码中先后设置
MemberCachePolicy.ALL和MemberCachePolicy.VOICE,最终生效的是后者,会导致非语音频道成员无法被缓存,统计在线用户时出现遗漏。
解决方案
1. 重构事件监听器,添加主动更新活动逻辑
修改监听器,在用户状态变化时主动调用JDA API更新活动状态:
public class BotEventListener extends ListenerAdapter { private JDA bot; private List<Consumer<Integer>> onlineStatusChangeListeners = new ArrayList<>(); public BotEventListener(JDA bot) { this.bot = bot; // 启动时初始化活动状态 updateActivity(); } public void addOnlineStatusChangeListener(Consumer<Integer> listener) { onlineStatusChangeListeners.add(listener); } @Override public void onUserUpdateOnlineStatus(UserUpdateOnlineStatusEvent event) { int onlineMembers = getQuantityOnlineMembers(event.getGuild()); // 更新活动状态 updateActivity(onlineMembers); // 通知其他监听器 for (Consumer<Integer> listener : onlineStatusChangeListeners) { listener.accept(onlineMembers); } } // 统计指定服务器的在线用户数 public int getQuantityOnlineMembers(Guild guild) { if (guild == null) return 0; return (int) guild.getMembers().stream() .filter(member -> member.getOnlineStatus() == OnlineStatus.ONLINE) .count(); } // 更新活动状态的通用方法 private void updateActivity() { // 这里以第一个服务器为例,可根据需求指定目标服务器或统计所有服务器用户 if (bot.getGuilds().isEmpty()) return; Guild targetGuild = bot.getGuilds().get(0); int online = getQuantityOnlineMembers(targetGuild); updateActivity(online); } private void updateActivity(int onlineMembers) { Activity activity = Activity.listening("Users online: " + onlineMembers); bot.getPresence().setActivity(activity); } }
2. 调整主机器人类初始化逻辑
移除CustomActivity类,修改JDA初始化流程,确保监听器能获取JDA实例:
public DiscordBot() throws LoginException { config = Dotenv.configure().load(); String token = config.get("TOKENDISCORD"); // 先构建JDA实例 bot = JDABuilder.createDefault(token, GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_MESSAGES, GatewayIntent.GUILD_PRESENCES, GatewayIntent.GUILD_VOICE_STATES ) .setMemberCachePolicy(MemberCachePolicy.ALL) // 确保缓存所有成员,避免统计遗漏 .disableCache(EnumSet.of( CacheFlag.CLIENT_STATUS, CacheFlag.ACTIVITY, CacheFlag.EMOTE )) .setChunkingFilter(ChunkingFilter.ALL) .enableCache(EnumSet.of( CacheFlag.ONLINE_STATUS )) .addEventListeners(new BotCommands()) .enableCache(CacheFlag.VOICE_STATE) .build(); // 等待JDA就绪后初始化监听器 bot.awaitReady(); BotEventListener eventListener = new BotEventListener(bot); bot.addEventListener(eventListener); }
3. 移除无用的CustomActivity类
无需继承ActivityImpl动态生成名称,直接在更新活动时创建新的Activity实例即可。
内容的提问来源于stack exchange,提问作者Yago Andrade
相关产品推荐
相关产品推荐

