Discord.js:实现支持提及用户/账号ID的封禁命令并解决非服务器成员权限检查报错
Let's break down what's going wrong and fix your ban command step by step.
The Root Cause of Your Error
The guild.members.fetch(args[0]) method returns a Promise, not a direct GuildMember object. When you try to call target.hasPermission() on a Promise, you get that TypeError because Promises don't have that method. Additionally, your permission check logic runs regardless of whether you're using a mention or an ID, which doesn't align with your requirement to only check permissions when mentioning a user.
Step-by-Step Fixes
Here's the revised code with key improvements explained:
const Discord = require('discord.js') module.exports = { name: "ban", description: "ban someone", execute: async(client, message, args) => { // Check if the user has permission to ban if(!message.member.hasPermission("BAN_MEMBERS")) { return message.reply("you don't have enough permissions to use this command.").then(m => m.delete({ timeout: 5000 })); } const guild = message.guild; let target; const mentionedMember = message.mentions.members.first(); // First try to get the mentioned member if (mentionedMember) { target = mentionedMember; } else if (args[0]) { // Fetch member by ID (resolve the Promise with await) try { target = await guild.members.fetch(args[0]); } catch (err) { // Handle invalid ID or user not in server return message.reply("Could not find a user with that ID.").then(m => m.delete({ timeout: 5000 })); } } if(!target) { return message.reply("please mention someone or provide a valid user ID to ban.").then(m => m.delete({ timeout: 5000 })); } // Only check permissions if the user was mentioned (your requirement) if (mentionedMember) { if(target.hasPermission("MANAGE_MESSAGES")) { return message.reply("you cannot ban a staff member.").then(m => m.delete({ timeout: 5000 })); } } // Prevent self-ban and bot-ban if(target.id === message.author.id) { return message.reply("you cannot ban yourself.").then(m => m.delete({ timeout: 5000 })); } if(target.id === client.user.id) { return message.reply("you cannot ban the bot.").then(m => m.delete({ timeout: 5000 })); } // Handle ban reason let reason = args.slice(1).join(' '); if(!reason) reason = "No reason given."; // Send DM to banned user (handle cases where DMs are disabled) const dmembed = new Discord.MessageEmbed() .setTitle("You have been banned from Rice Farm #11") .addField("Moderator", message.author) .addField("Reason", `${reason}`) .setColor("RED") .setTimestamp(); try { await target.send(dmembed); } catch (err) { console.log(`Could not send DM to ${target.user.tag}: ${err}`); } // Execute ban and send confirmation await target.ban({reason: reason}); await message.channel.send(`**${message.author.tag}** banned **${target.user.tag}** indefinitely.\nReason: *${reason}*.`); // Send log embed to logs channel const reportChannel = message.guild.channels.cache.find(x => x.name === 'logs'); if (reportChannel) { const logchat = new Discord.MessageEmbed() .setTitle("Member Banned") .addField("Target", `${target.user} (${target.user.id})`) .addField("Moderator", `${message.author} (${message.author.id})`) .addField("Reason", `${reason}`) .setColor("GREEN") .setFooter(`ID: ${Math.floor(Math.random() * 100000000000000)}`) .setTimestamp() .setThumbnail(target.user.displayAvatarURL()); await reportChannel.send(logchat); } else { console.log("Logs channel not found!"); } } }
Key Changes Explained
Proper Async Member Fetch:
- We use
awaitwithguild.members.fetch(args[0])to resolve the Promise and get a validGuildMemberobject. - Added a
try/catchblock to handle invalid IDs or users not present in the server.
- We use
Permission Check Restricted to Mentions:
- We store the mentioned member in a separate variable, then only run the
hasPermissioncheck if this variable exists. This strictly follows your requirement to skip permission checks when using an ID.
- We store the mentioned member in a separate variable, then only run the
Robust Error Handling:
- Wrapped the DM send attempt in a
try/catchblock to avoid crashes if the banned user has DMs disabled. - Added a check to confirm the logs channel exists before attempting to send the log embed.
- Wrapped the DM send attempt in a
Clean Reason Parsing:
- The reason is always parsed from the second argument onwards, whether you're using a mention or an ID, which keeps the command behavior consistent.
Additional Note
If you want to ban users who aren't currently in the server (using their ID), you can skip the member fetch step entirely and call guild.members.ban(args[0], { reason: reason }) directly. Let me know if you need help adjusting the code for that use case!
内容的提问来源于stack exchange,提问作者user16540688

