You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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的观察者触发时强制更新活动状态。


问题分析

  1. 活动状态不会自动刷新:调用setActivity时,只会读取一次CustomActivity.getName()的返回值,之后JDA不会监听这个方法的变化,必须主动调用getPresence().setActivity()才能更新状态。
  2. 在线用户统计逻辑依赖不稳定:getQuantityOnlineMembers方法依赖onlineStatusEvent,机器人启动时该事件为null,会直接返回0;且只能统计触发状态变化的单个服务器用户,无法适配多服务器场景。
  3. 缓存策略冲突:代码中先后设置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 19:10:13