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

Discord.js presenceUpdate事件报错:无法读取undefined的remove属性

Discord 支持系统 presenceUpdate 事件报错排查

报错定位

抛出的错误为TypeError: Cannot read properties of undefined (reading 'remove'),触发位置是代码第24行的角色移除逻辑,核心原因是Discord.js API 调用方式错误,同时代码存在多处逻辑偏差,具体问题点如下:

  • 事件参数顺序错误:presenceUpdate 事件的标准回调参数为 (oldPresence, newPresence),你定义的(client, oldMember, newMember, newPresence)参数顺序和结构完全不匹配,导致后续取到的对象全部错位
  • 角色操作接口调用错误:roles 属性属于单个服务器成员(GuildMember)实例,你写的members.roles.add()、guild.members.roles.remove()是在成员集合、服务器成员管理器上找roles属性,这两个对象本身没有roles字段,取值自然为undefined,调用remove方法直接抛错
  • 状态检测逻辑错误:你过滤完在线成员后取status[0]做判断,实际拿到的是成员缓存里的第一个用户,根本不是触发本次状态更新的目标用户;且你调用includes的对象是成员实例本身,不是用户设置的自定义状态文本,永远匹配不到你配置的关键词
  • 守卫判断顺序错误:你先访问oldMember.status做判断,后面才判断oldMember/newMember是否为undefined,只要其中一个值为空,前面取属性的步骤就会提前抛错
  • 冗余遍历问题:presenceUpdate事件触发时,传入的newPresence对象已经携带了对应服务器ID,不需要遍历客户端缓存的所有服务器,既浪费性能,还可能出现跨服误加角色的问题

修正后可运行代码

// 注意:事件监听器注册时client通过闭包获取,不要放在事件回调参数中
module.exports = (oldPresence, newPresence) => {
    // 提前做空值拦截
    if (!newPresence?.guild || !newPresence.member) return;
    const guild = newPresence.guild;
    const targetMember = newPresence.member;

    // 读取当前服务器配置
    const { soutien_role_id: soutienRoleId, soutien_text: soutienText } = client.db.settings.selectSoutien.get(guild.id);
    if (!soutienRoleId || !soutienText) return;

    // 检查角色是否存在
    const soutienRole = guild.roles.cache.get(soutienRoleId);
    if (!soutienRole) return;
    // 检查bot是否有权限操作该角色
    if (guild.members.me.roles.highest.position <= soutienRole.position) return;
    // 跳过无法操作的成员(例如服务器所有者、角色层级高于bot的成员)
    if (!targetMember.manageable) return;

    // 提取用户的自定义状态文本
    const customActivity = newPresence.activities.find(act => act.type === 'CUSTOM_STATUS');
    const statusText = customActivity?.state?.toLowerCase() || '';
    const hasSoutienText = statusText.includes(soutienText.toLowerCase());

    // 执行角色增删
    if (hasSoutienText) {
        if (!targetMember.roles.cache.has(soutienRoleId)) {
            targetMember.roles.add(soutienRole).catch(console.error);
        }
    } else {
        if (targetMember.roles.cache.has(soutienRoleId)) {
            targetMember.roles.remove(soutienRole).catch(console.error);
        }
    }
}

前置配置注意事项

  • 需要在Discord开发者后台对应Bot的设置页,开启Presence Intent和Server Members Intent两个特权意图,否则无法接收到完整的presence更新事件,也拿不到完整的成员缓存
  • 确保Bot在服务器内的角色位置,高于你要自动分配的soutien_role_id对应角色,否则会抛出权限不足的错误
  • 自定义状态文本存在activities数组中类型为CUSTOM_STATUS的项里,state字段才是用户输入的状态内容,不要和presence.status字段混淆——后者存的是online/idle/dnd/offline的在线状态枚举值,不可能包含你配置的关键词

内容的提问来源于stack exchange,提问作者InFuzz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 04:06:06