基于Discord.js实现语音频道用户随机角色私信分配功能
实现Discord.js狼人杀风格角色分配命令
我帮你梳理一下实现这个功能的完整步骤,代码基于Discord.js v14(目前最稳定的版本),全程用前缀命令!role来处理你的需求:
一、核心需求拆解
我们要落地的功能点很明确:
- 解析
!role 1wolf 2villagers这类命令,精准提取角色类型和对应数量 - 获取目标语音频道的所有非机器人成员
- 随机分配指定数量的角色,通过私信发送给用户(注意:这里的角色只是消息内容,并非服务器的Guild角色)
- 覆盖各种异常场景(比如用户数量不足、参数格式错误、用户关闭私信权限等)
二、准备工作
首先确保你的机器人拥有以下必要权限:
SendMessages(发送公屏消息)ReadMessageHistory(读取消息历史)ViewChannel(查看频道)SendPrivateMessages(发送私信,这是核心权限)- 必须开启
GuildVoiceStates意图(否则无法读取语音频道成员信息)
三、完整代码实现
const { Client, GatewayIntentBits } = require('discord.js'); const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildVoiceStates // 读取语音频道成员必须开启这个 ] }); // 机器人登录成功提示 client.on('ready', () => { console.log(`Logged in as ${client.user.tag}! 准备好分配狼人杀角色啦~`); }); // 监听消息,处理前缀命令 client.on('messageCreate', async (message) => { // 忽略机器人消息和非!开头的命令 if (message.author.bot || !message.content.startsWith('!')) return; const args = message.content.slice(1).trim().split(' '); const command = args.shift().toLowerCase(); // 处理!role命令 if (command === 'role') { try { // 1. 解析命令参数,提取角色和数量 const roleAssignments = {}; let totalRoles = 0; for (const arg of args) { // 匹配「数字+角色名」的格式,比如1wolf、2villagers const match = arg.match(/^(\d+)(\w+)$/); if (!match) { return message.reply('参数格式不对哦!请用`!role 1wolf 2villagers`这种格式~'); } const count = parseInt(match[1]); const role = match[2].toLowerCase(); roleAssignments[role] = (roleAssignments[role] || 0) + count; totalRoles += count; } // 2. 获取目标语音频道的成员 // 这里默认取发送命令用户所在的语音频道,也可以扩展成指定频道ID的参数 let voiceChannel = message.member?.voice.channel; if (!voiceChannel) { return message.reply('你得先加入一个语音频道呀,或者告诉我要分配角色的频道ID!'); } // 筛选出频道里的非机器人用户 const members = voiceChannel.members.filter(member => !member.user.bot); const memberList = Array.from(members.values()); // 检查用户数量是否足够分配角色 if (memberList.length < totalRoles) { return message.reply(`语音频道只有${memberList.length}个玩家,但需要分配${totalRoles}个角色,人数不够哦!`); } // 3. 生成随机角色池并洗牌 const rolePool = []; for (const [role, count] of Object.entries(roleAssignments)) { for (let i = 0; i < count; i++) { rolePool.push(role); } } // 用Fisher-Yates洗牌算法保证随机性 for (let i = rolePool.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [rolePool[i], rolePool[j]] = [rolePool[j], rolePool[i]]; } // 4. 分配角色并发送私信 message.reply('正在分配角色,大家注意查收私信哦!'); for (let i = 0; i < totalRoles; i++) { const member = memberList[i]; const role = rolePool[i]; try { // 美化角色名:首字母大写 await member.send(`你的狼人杀角色是:**${role.charAt(0).toUpperCase() + role.slice(1)}**`); } catch (err) { // 处理用户关闭私信的情况 message.channel.send(`抱歉,没法给${member.user.tag}发私信,请让他开启私信权限哦!`); } } } catch (err) { console.error('处理命令时出错:', err); message.reply('哎呀,处理命令时出了点小问题,再试一次吧!'); } } }); // 替换成你的机器人令牌 client.login('YOUR_BOT_TOKEN');
四、关键细节说明
- 参数解析:用正则表达式严格匹配「数字+角色名」格式,避免无效参数干扰
- 语音成员获取:必须开启
GuildVoiceStates意图,否则无法读取语音频道的成员数据 - 随机分配:采用Fisher-Yates洗牌算法,比简单的
sort(() => Math.random() - 0.5)更公平 - 错误处理:覆盖了参数错误、无语音频道、用户数量不足、私信发送失败等常见异常
- 体验优化:角色名首字母大写,私信内容更美观,公屏提示清晰
五、扩展建议
- 可以添加频道参数,让用户指定目标语音频道(比如
!role 1wolf 2villagers #狼人杀房间1) - 支持更多角色类型,比如预言家、女巫等,只需要在命令里添加对应的参数即可
- 可以添加角色分配完成后的统计提示,或者给未分配到特殊角色的用户默认分配「村民」角色
内容的提问来源于stack exchange,提问作者lost
相关产品推荐
相关产品推荐

