Discord机器人开发:如何跨模块调用@discordjs/voice的AudioPlayer方法
问题原因
你当前的player是play.js中execute函数的局部变量,仅在该函数执行过程中有效,外部无法直接访问,即使尝试导出该变量也只能拿到undefined,同时全局单实例的导出方式也无法适配多服务器同时使用的场景。
解决方案
我们可以通过在Discord客户端实例上挂载全局缓存的方式,按服务器ID存储对应的播放器实例,实现跨文件访问:
步骤1:在机器人入口文件初始化缓存
在你的机器人启动文件(一般为index.js/bot.js)中,给客户端实例新增一个Map类型的属性,用来存储各服务器的播放器:
const { Client, GatewayIntentBits } = require('discord.js'); const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMessages ] }); // 新增全局服务器播放器缓存,key为服务器ID,value为对应AudioPlayer实例 client.guildPlayers = new Map();
步骤2:修改play.js的播放器逻辑
创建播放器后存入全局缓存,避免重复创建实例:
const { SlashCommandBuilder } = require('@discordjs/builders'); const { StreamType, createAudioPlayer, createAudioResource, joinVoiceChannel, getVoiceConnection } = require('@discordjs/voice'); const ytdl = require('ytdl-core'); module.exports = { data: new SlashCommandBuilder() .setName('play') .setDescription('An example of the play command.'), async execute(interaction) { const guildId = interaction.guild.id; const connection = joinVoiceChannel({ channelId: interaction.member.voice.channel.id, guildId: guildId, adapterCreator: interaction.guild.voiceAdapterCreator }); const stream = ytdl('https://www.youtube.com/watch?v=jNQXAC9IVRw', { filter: 'audioonly' }); const resource = createAudioResource(stream, { inputType: StreamType.Arbitrary }); // 优先从缓存取播放器,不存在则新建 let player = interaction.client.guildPlayers.get(guildId); if (!player) { player = createAudioPlayer(); interaction.client.guildPlayers.set(guildId, player); // 可选:播放器闲置时自动清理缓存,避免内存泄漏 player.on('stateChange', (oldState, newState) => { if (newState.status === 'idle') { interaction.client.guildPlayers.delete(guildId); getVoiceConnection(guildId)?.destroy(); } }) } player.play(resource); connection.subscribe(player); await interaction.reply('Playing.'); }, };
步骤3:修改pause.js的调用逻辑
从全局缓存中取出对应服务器的播放器再调用暂停方法,增加边界判断避免报错:
const { SlashCommandBuilder } = require('@discordjs/builders'); module.exports = { data: new SlashCommandBuilder() .setName('pause') .setDescription('An example of the pause command.'), async execute(interaction) { const guildId = interaction.guild.id; const player = interaction.client.guildPlayers.get(guildId); // 边界校验:无播放器/非播放状态下直接提示 if (!player || player.state.status !== 'playing') { return interaction.reply('当前没有正在播放的内容'); } player.pause(); await interaction.reply('Paused.'); }, };
补充说明
该方案下各服务器的播放器实例相互独立,不会出现串流问题,后续如果要开发恢复播放、停止播放、切歌等命令,都可以用相同的逻辑从client.guildPlayers中取出对应服务器的播放器操作即可。
内容的提问来源于stack exchange,提问作者BelowMediocrity
相关产品推荐
相关产品推荐

