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

基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 12:02:33