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

Discord Bot执行!play命令报错voiceChannel.join is not afunction求助

问题原因
  1. 你当前使用的discord.js为v14及以上版本,v13版本后官方就将语音相关功能从主库拆分到了独立的@discordjs/voice包,旧版本的voiceChannel.join()方法已经被完全移除,调用不存在的方法自然会抛出类型错误。
  2. 客户端初始化时缺少GUILD_VOICE_STATES语音状态意图,无法正常获取语音频道状态、执行加入操作。
  3. v14版本后读取消息内容需要额外开启MessageContent意图,你当前的配置也缺少该配置,且未在Discord开发者后台开启对应权限开关的话也会影响命令识别。
解决方案

步骤1:安装必要依赖

执行以下命令安装语音相关依赖包:

npm install @discordjs/voice opusscript ffmpeg-static

步骤2:修正主文件配置

修改主文件的客户端初始化逻辑,补全所需意图:

const { Client, IntentsBitField } = require('discord.js');
const prefix = '!';
const fs = require('fs');
const { join } = require('path');

const client = new Client({ 
  intents: [
    IntentsBitField.Flags.Guilds,
    IntentsBitField.Flags.GuildMessages,
    IntentsBitField.Flags.GuildVoiceStates,
    IntentsBitField.Flags.MessageContent
  ] 
});

client.commands = new Discord.Collection();

const commandFiles = fs.readdirSync('./commands/').filter(file => file.endsWith('.js'));
for(const file of commandFiles){
    const command = require(`./commands/${file}`);
    client.commands.set(command.name, command);
}

client.once('ready', () => {
    console.log('Mein Mussig Bot ist nun online!');
});

client.on('messageCreate', message =>{
    if(!message.content.startsWith(prefix) || message.author.bot)  return;

    const args = message.content.slice(prefix.length).split(/ +/);
    const command = args.shift().toLowerCase();

    if(command === 'ping'){
        client.commands.get('ping').execute(message,args);
    } else if (command === 'play'){
        client.commands.get('play').execute(message,args);
    } else if (command === 'stop'){
        client.commands.get('stop').execute(message,args);
    }
});

client.login('token')

同时需要前往Discord开发者平台的Bot管理页面,开启Message Content Intent权限开关。

步骤3:重写play.js语音逻辑

替换原有play.js的代码为适配v14版本的写法:

const { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus } = require('@discordjs/voice');
const ytdl = require('ytdl-core');
const ytSearch = require('yt-search');

module.exports = {
    name: 'play',
    description: 'Joins and plays a video from youtube',
    async execute(message, args) {
        const voiceChannel = message.member.voice.channel;
 
        if (!voiceChannel) return message.channel.send('You need to be in a channel to execute this command!');
        const permissions = voiceChannel.permissionsFor(message.client.user);
        if (!permissions.has('CONNECT')) return message.channel.send('You dont have the correct permissions');
        if (!permissions.has('SPEAK')) return message.channel.send('You dont have the correct permissions');
        if (!args.length) return message.channel.send('You need to send the second argument!');
 
        const validURL = (str) =>{
            var regex = /(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/;
            return regex.test(str);
        }

        // 初始化音频播放器
        const player = createAudioPlayer();
        // 加入语音频道
        const connection = joinVoiceChannel({
            channelId: voiceChannel.id,
            guildId: voiceChannel.guild.id,
            adapterCreator: voiceChannel.guild.voiceAdapterCreator,
        });
        connection.subscribe(player);

        // 播放结束自动退出
        player.on(AudioPlayerStatus.Idle, () => {
            connection.destroy();
            message.channel.send('leaving channel');
        });

        // 捕获播放错误
        player.on('error', error => {
            console.error(error);
            message.channel.send('播放出错,请重试');
            connection.destroy();
        });
 
        if(validURL(args[0])){
            const stream  = ytdl(args[0], {filter: 'audioonly', highWaterMark: 1<<25});
            const resource = createAudioResource(stream);
            player.play(resource);
            await message.reply(`:thumbsup: Now Playing ***Your Link!***`)
            return
        }
        
        const videoFinder = async (query) => {
            const videoResult = await ytSearch(query);
            return (videoResult.videos.length > 1) ? videoResult.videos[0] : null;
        }
 
        const video = await videoFinder(args.join(' '));
        if(video){
            const stream  = ytdl(video.url, {filter: 'audioonly', highWaterMark: 1<<25});
            const resource = createAudioResource(stream);
            player.play(resource);
            await message.reply(`:thumbsup: Now Playing ***${video.title}***`)
        } else {
            message.channel.send('No video results found');
            connection.destroy();
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 04:45:02