Discord.js v14等级系统迁移报错:无法读取guild属性
Discord.js v14 等级系统迁移报错问题
问题描述
正在将Discord Bot升级为使用Slash Commands,迁移原有等级系统后代码无法正常运行,出现如下报错:
报错信息
D:\Development Projects\04 - Archived Bot Projects\SystemdBot-Beta\src\functions\database\level.js:6 const guildEnable = getEnable(message.guild.id); ^ TypeError: Cannot read properties of undefined (reading 'guild') at module.exports (D:\Development Projects\04 - Archived Bot Projects\SystemdBot-Beta\src\functions\database\level.js:6:41) at Object.<anonymous> (D:\Development Projects\04 - Archived Bot Projects\SystemdBot-Beta\src\bot.js:18:46) at Module._compile (node:internal/modules/cjs/loader:1275:14) at Module._extensions..js (node:internal/modules/cjs/loader:1329:10) at Module.load (node:internal/modules/cjs/loader:1133:32) at Module._load (node:internal/modules/cjs/loader:972:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12) at node:internal/main/run_main_module:23:47 Node.js v19.9.0
相关代码
const config = require('../../../config/config'); const db = require('./cfgdb'); const lvldb = require('./lvldb'); module.exports = async (client, message) => { const guildEnable = getEnable(message.guild.id); if (guildEnable.leveling !== 1) return; const guildSettings = getGuildSettings(message.guild.id); if (!guildSettings) return; if (message.author.bot) return; const userId = message.author.id; const guildId = message.guild.id; const user = await client.users.fetch(userId); const username = user.username; const member = message.member; const booster = guildSettings.boosterrole; let xp; if(member.roles.cache.has(booster)) { xp = Math.floor(Math.random() * (50 - 35 + 1) + 35); } else { xp = Math.floor(Math.random() * (20 - 15 + 1) + 15); } const row = lvldb.prepare('SELECT * FROM level WHERE userid = ? AND guildid = ?').get(userId, guildId); if (!row || !('level' in row)) { lvldb.prepare('INSERT INTO level (userid, guildid, username, xp, totalxp) VALUES (?, ?, ?, ?, ?)').run(userId, guildId, username, xp, xp); } else { lvldb.prepare('UPDATE level SET xp = ?, totalxp = ? WHERE userid = ? AND guildid = ?').run(row.xp + xp, row.totalxp + xp, userId, guildId); } const updatedRow = lvldb.prepare('SELECT * FROM level WHERE userid = ? AND guildid = ?').get(userId, guildId); const xpNeededForCurrentLevel = 5 * (updatedRow.level ** 2 + 50 * updatedRow.level) + 100; const xpForPreviousLevels = updatedRow.level > 1 ? (5 * ((updatedRow.level - 1) ** 2 + 50 * (updatedRow.level - 1)) + 100) : 0; const currentLevelProgress = updatedRow.xp - xpForPreviousLevels; const totalXP = xpForPreviousLevels + currentLevelProgress; if ('level' in updatedRow && updatedRow.xp >= xpNeededForCurrentLevel) { lvldb.prepare('UPDATE level SET level = ?, xp = ?, totalxp = ? WHERE userid = ? AND guildid = ?').run(updatedRow.level + 1, updatedRow.xp - xpNeededForCurrentLevel, totalXP, userId, guildId); const level = updatedRow.level + 1; const channel = client.channels.cache.get(guildSettings.levelup); if(!channel) return; const message = await channel.send(`Congrats ${user}, you just reached level ${level}!`); function getRoleForLevel(guildId, levels) { const stmt = lvldb.prepare(` SELECT role_id FROM lvlroles WHERE guild_id = ? AND levels = ?; `); const result = stmt.get(guildId, levels); return result ? result.role_id : null; } if (level) { const roleId = getRoleForLevel(message.guild.id, level); if (roleId) { const role = message.guild.roles.cache.get(roleId); if (role) { const member = message.guild.members.cache.get(userId); member.roles.add(role); } } } } }; function getEnable(guildId) { const stmt = db.prepare('SELECT * FROM cfg_enable WHERE guild_id = ?'); const row = stmt.get(guildId); return row; } function getGuildSettings(guildId) { const stmt = db.prepare('SELECT * FROM cfg_chid WHERE guild_id = ?'); const row = stmt.get(guildId); return row; }
报错原因
message对象未正确传入:报错显示message是undefined,说明在bot.js第18行调用该模块时,没有传递有效的message参数。- 逻辑未适配Slash Commands:原代码基于传统
messageCreate事件设计,而Slash Commands使用的是interactionCreate事件,交互对象为Interaction而非Message,不存在message.guild属性。
解决方案
方案1:保留传统消息事件(兼容旧逻辑)
如果仍需支持普通消息触发等级系统,修改模块调用逻辑,先过滤非服务器消息:
// 在bot.js中修改调用代码 client.on('messageCreate', async (message) => { // 跳过私信消息,避免message.guild为undefined if (!message.guild) return; // 调用等级模块 require('./src/functions/database/level.js')(client, message); });
方案2:重构代码适配Slash Commands
如果已完全迁移到Slash Commands,修改等级模块以支持Interaction对象:
const config = require('../../../config/config'); const db = require('./cfgdb'); const lvldb = require('./lvldb'); // 兼容Message和Interaction对象,优先处理Interaction module.exports = async (client, context) => { // 获取服务器对象,非服务器环境直接返回 const guild = context.guild || (context.message?.guild); if (!guild) return; const guildEnable = getEnable(guild.id); // 增加可选链避免guildEnable为undefined时报错 if (guildEnable?.leveling !== 1) return; const guildSettings = getGuildSettings(guild.id); if (!guildSettings) return; // 获取用户对象,兼容Message和Interaction const user = context.user || context.author; if (user.bot) return; const userId = user.id; const guildId = guild.id; const username = user.username; // 获取成员对象,兼容Message和Interaction const member = context.member; const booster = guildSettings.boosterrole; let xp; // 增加可选链避免member为undefined时报错 if(member?.roles.cache.has(booster)) { xp = Math.floor(Math.random() * (50 - 35 + 1) + 35); } else { xp = Math.floor(Math.random() * (20 - 15 + 1) + 15); } const row = lvldb.prepare('SELECT * FROM level WHERE userid = ? AND guildid = ?').get(userId, guildId); if (!row || !('level' in row)) { lvldb.prepare('INSERT INTO level (userid, guildid, username, xp, totalxp) VALUES (?, ?, ?, ?, ?)').run(userId, guildId, username, xp, xp); } else { lvldb.prepare('UPDATE level SET xp = ?, totalxp = ? WHERE userid = ? AND guildid = ?').run(row.xp + xp, row.totalxp + xp, userId, guildId); } const updatedRow = lvldb.prepare('SELECT * FROM level WHERE userid = ? AND guildid = ?').get(userId, guildId); if (!updatedRow) return; // 防止数据查询失败 const xpNeededForCurrentLevel = 5 * (updatedRow.level ** 2 + 50 * updatedRow.level) + 100; const xpForPreviousLevels = updatedRow.level > 1 ? (5 * ((updatedRow.level - 1) ** 2 + 50 * (updatedRow.level - 1)) + 100) : 0; const currentLevelProgress = updatedRow.xp - xpForPreviousLevels; const totalXP = xpForPreviousLevels + currentLevelProgress; if ('level' in updatedRow && updatedRow.xp >= xpNeededForCurrentLevel) { lvldb.prepare('UPDATE level SET level = ?, xp = ?, totalxp = ? WHERE userid = ? AND guildid = ?').run(updatedRow.level + 1, updatedRow.xp - xpNeededForCurrentLevel, totalXP, userId, guildId); const level = updatedRow.level + 1; const channel = client.channels.cache.get(guildSettings.levelup); if(!channel) return; // 发送升级提示 const sendMessage = await channel.send(`恭喜 ${user},你升级到${level}级啦!`); function getRoleForLevel(guildId, levels) { const stmt = lvldb.prepare(` SELECT role_id FROM lvlroles WHERE guild_id = ? AND levels = ?; `); const result = stmt.get(guildId, levels); return result ? result.role_id : null; } if (level) { const roleId = getRoleForLevel(guild.id, level); if (roleId) { const role = guild.roles.cache.get(roleId); // 确保角色和成员都存在再添加 if (role && member) { member.roles.add(role); } } } } }; function getEnable(guildId) { const stmt = db.prepare('SELECT * FROM cfg_enable WHERE guild_id = ?'); const row = stmt.get(guildId); return row; } function getGuildSettings(guildId) { const stmt = db.prepare('SELECT * FROM cfg_chid WHERE guild_id = ?'); const row = stmt.get(guildId); return row; }
方案3:修改模块调用逻辑(针对Slash Commands)
在bot.js中修改interactionCreate事件的调用代码:
client.on('interactionCreate', async (interaction) => { // 处理其他交互逻辑(如命令处理)... // 调用等级模块,传入interaction对象 require('./src/functions/database/level.js')(client, interaction); });
内容的提问来源于stack exchange,提问作者TechXplore
相关产品推荐
相关产品推荐

