You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Discord.js实现机器人!play命令的文件匹配搜索功能

没问题,我来帮你搞定Discord.js里这个!play命令的实现。咱们一步步拆解,从文件读取到智能匹配逻辑都给你讲明白,直接就能用!

实现!play命令的完整步骤

首先,我们需要用到Node.js自带的fs(文件系统)和path(路径处理)模块,再结合Discord.js的消息监听功能来完成需求。

1. 基础初始化与依赖导入

先导入必要的模块,初始化Discord客户端(记得开启所需的Intents,不然无法接收消息):

const { Client, IntentsBitField } = require('discord.js');
const fs = require('fs').promises; // 用异步版本避免阻塞事件循环
const path = require('path');

// 初始化客户端,开启必要的权限
const client = new Client({
  intents: [
    IntentsBitField.Flags.Guilds,
    IntentsBitField.Flags.GuildMessages,
    IntentsBitField.Flags.MessageContent,
    IntentsBitField.Flags.GuildVoiceStates, // 如果要做语音播放,必须加这个
  ],
});

2. 核心命令逻辑实现

接下来监听messageCreate事件,处理!play命令的核心逻辑:

client.on('messageCreate', async (message) => {
  // 过滤机器人自身消息和非!play开头的消息
  if (message.author.bot || !message.content.startsWith('!play')) return;

  // 提取用户输入的搜索关键词(比如!play faded → 关键词是'faded')
  const searchQuery = message.content.slice('!play '.length).trim().toLowerCase();
  if (!searchQuery) {
    return message.reply('请输入要搜索的歌曲名称哦!');
  }

  // 定义音乐目录的绝对路径(用path.join避免跨平台路径问题)
  const musicDirectory = path.join(__dirname, 'MusicFiles');

  try {
    // 异步读取音乐目录下的所有文件
    const allFiles = await fs.readdir(musicDirectory);
    if (allFiles.length === 0) {
      return message.reply('音乐目录里还没有任何文件呢!');
    }

    // 🔍 核心:找到最匹配的文件
    let bestMatchFile = null;
    let highestMatchScore = 0;

    allFiles.forEach(file => {
      const fileNameLower = file.toLowerCase();
      let matchScore = 0;

      // 规则1:完全匹配 → 最高分10
      if (fileNameLower === searchQuery) {
        matchScore = 10;
      }
      // 规则2:文件名以关键词开头 → 得分7
      else if (fileNameLower.startsWith(searchQuery)) {
        matchScore = 7;
      }
      // 规则3:文件名包含关键词 → 得分5
      else if (fileNameLower.includes(searchQuery)) {
        matchScore = 5;
      }

      // 进阶:用编辑距离(Levenshtein Distance)计算相似度,让匹配更智能
      function calculateLevenshtein(a, b) {
        const matrix = Array.from({ length: a.length + 1 }, () => Array(b.length + 1).fill(0));
        for (let i = 0; i <= a.length; i++) matrix[i][0] = i;
        for (let j = 0; j <= b.length; j++) matrix[0][j] = j;
        
        for (let i = 1; i <= a.length; i++) {
          for (let j = 1; j <= b.length; j++) {
            const cost = a[i-1] === b[j-1] ? 0 : 1;
            matrix[i][j] = Math.min(
              matrix[i-1][j] + 1, // 删除
              matrix[i][j-1] + 1, // 插入
              matrix[i-1][j-1] + cost // 替换
            );
          }
        }
        return matrix[a.length][b.length];
      }

      // 编辑距离越小,相似度越高,转换为得分(这里设最大基础分10)
      const distance = calculateLevenshtein(fileNameLower, searchQuery);
      const distanceBasedScore = Math.max(0, 10 - distance);

      // 取两种匹配规则的最高分
      matchScore = Math.max(matchScore, distanceBasedScore);

      // 更新最佳匹配
      if (matchScore > highestMatchScore) {
        highestMatchScore = matchScore;
        bestMatchFile = file;
      }
    });

    // 处理没有匹配结果的情况
    if (!bestMatchFile) {
      return message.reply(`找不到和「${searchQuery}」匹配的音乐文件哦!`);
    }

    // ✅ 成功拿到最匹配的文件全名,接下来就可以用它做播放了
    const fullFilePath = path.join(musicDirectory, bestMatchFile);
    message.reply(`找到最匹配的音乐:**${bestMatchFile}**,完整路径:\`${fullFilePath}\``);

    // 这里可以继续添加音频播放逻辑,比如用@discordjs/voice模块来播放本地文件
    // 示例(需要先安装@discordjs/voice和相关依赖):
    // const { joinVoiceChannel, createAudioPlayer, createAudioResource } = require('@discordjs/voice');
    // const voiceChannel = message.member.voice.channel;
    // if (!voiceChannel) return message.reply('请先加入一个语音频道!');
    // const player = createAudioPlayer();
    // const resource = createAudioResource(fullFilePath);
    // player.play(resource);
    // joinVoiceChannel({
    //   channelId: voiceChannel.id,
    //   guildId: message.guild.id,
    //   adapterCreator: message.guild.voiceAdapterCreator,
    // }).subscribe(player);

  } catch (error) {
    console.error('读取音乐目录出错:', error);
    message.reply('读取音乐目录时发生错误,请检查目录是否存在!');
  }
});

// 最后记得登录你的机器人
client.login('你的机器人令牌');

3. 关键细节说明

  • 路径处理:用path.join来拼接目录路径,确保在Windows、Linux、Mac等系统下都能正常工作,避免手动写斜杠导致的错误。
  • 不区分大小写匹配:把关键词和文件名都转成小写,这样用户输入Faded或者faded都能匹配到文件。
  • 多维度匹配规则:结合精确匹配、前缀匹配、包含匹配和编辑距离,既保证精准性,又能处理用户输入拼写错误的情况(比如输入fade也能匹配到Faded.mp3)。
  • 异步读取文件:用fs.promises.readdir而不是同步的fs.readdirSync,避免阻塞Discord机器人的事件循环,保证机器人响应流畅。

4. 后续播放提示

如果要实现真正的音频播放,你需要安装@discordjs/voice模块和它的依赖:

npm install @discordjs/voice @discordjs/opus ffmpeg-static

然后参考代码里的注释部分,添加语音频道连接和音频播放逻辑即可。

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

火山引擎 最新活动