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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 05:06:04