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




