如何在Discord中连接到VC?

我正在尝试为Discord做一个音乐机器人。我唯一遇到的麻烦就是让机器人真正播放音乐。


我正在使用PyCharm在Macbook上运行我的机器人。我没有做太多尝试,只是因为我不知道该怎么做。您可以看到我使用的代码:

import discord
from discord.ext import commands

import asyncio
import itertools
import sys
import traceback
from async_timeout import timeout
from functools import partial
from youtube_dl import YoutubeDL


ytdlopts = {
    'format': 'bestaudio/best','outtmpl': 'downloads/%(extractor)s-%(id)s-%(title)s.%(ext)s','restrictfilenames': True,'noplaylist': True,'nocheckcertificate': True,'ignoreerrors': False,'logtostderr': False,'quiet': True,'no_warnings': True,'default_search': 'auto','source_address': '0.0.0.0'
}

ffmpegopts = {
    'before_options': '-nostdin','options': '-vn'
}

ytdl = YoutubeDL(ytdlopts)


class VoiceConnectionError(commands.CommandError):
    pass

class InvalidVoiceChannel(VoiceConnectionError):
    pass

class YTDLSource(discord.PCMVolumeTransformer):

    def __init__(self,source,*,data,requester):
        super().__init__(source)
        self.requester = requester

        self.title = data.get('title')
        self.web_url = data.get('webpage_url')

    def __getitem__(self,item: str):
        return self.__getattribute__(item)

    @classmethod
    async def create_source(cls,ctx,search: str,loop,download=False):
        loop = loop or asyncio.get_event_loop()

        to_run = partial(ytdl.extract_info,url=search,download=download)
        data = await loop.run_in_executor(None,to_run)

        if 'entries' in data:
            data = data['entries'][0]

        await ctx.send(f'```ini\n[Added {data["title"]} to the Queue.]\n```',delete_after=15)

        if download:
            source = ytdl.prepare_filename(data)
        else:
            return {'webpage_url': data['webpage_url'],'requester': ctx.author,'title': data['title']}

        return cls(discord.FFmpegPCMAudio(source),data=data,requester=ctx.author)

    @classmethod
    async def regather_stream(cls,loop):
        loop = loop or asyncio.get_event_loop()
        requester = data['requester']

        to_run = partial(ytdl.extract_info,url=data['webpage_url'],download=False)
        data = await loop.run_in_executor(None,to_run)

        return cls(discord.FFmpegPCMAudio(data['url']),requester=requester)


class MusicPlayer:
    __slots__ = ('bot','_guild','_channel','_cog','queue','next','current','np','volume')

    def __init__(self,ctx):
        self.bot = ctx.bot
        self._guild = ctx.guild
        self._channel = ctx.channel
        self._cog = ctx.cog

        self.queue = asyncio.Queue()
        self.next = asyncio.Event()

        self.np = None
        self.volume = .5
        self.current = None

        ctx.bot.loop.create_task(self.player_loop())

    async def player_loop(self):
        await self.bot.wait_until_ready()

        while not self.bot.is_closed():
            self.next.clear()

            try:
                async with timeout(300):
                    source = await self.queue.get()
            except asyncio.TimeoutError:
                return self.destroy(self._guild)

            if not isinstance(source,YTDLSource):
                try:
                    source = await YTDLSource.regather_stream(source,loop=self.bot.loop)
                except Exception as e:
                    await self._channel.send(f'There was an error processing your song.\n'
                                             f'```css\n[{e}]\n```')
                    continue

            source.volume = self.volume
            self.current = source

            self._guild.voice_client.play(source,after=lambda _: self.bot.loop.call_soon_threadsafe(self.next.set))
            self.np = await self._channel.send(f'**Now Playing:** `{source.title}` requested by '
                                               f'`{source.requester}`')
            await self.next.wait()

            source.cleanup()
            self.current = None

            try:
                await self.np.delete()
            except discord.HTTPException:
                pass

    def destroy(self,guild):
        return self.bot.loop.create_task(self._cog.cleanup(guild))


class Music(commands.Cog):
    __slots__ = ('bot','players')

    def __init__(self,bot):
        self.bot = bot
        self.players = {}
        if not discord.opus.is_loaded():
            discord.opus.load_opus('opus')

    async def cleanup(self,guild):
        try:
            await guild.voice_client.disconnect()
        except AttributeError:
            pass

        try:
            del self.players[guild.id]
        except KeyError:
            pass

    async def __local_check(self,ctx):
        if not ctx.guild:
            raise commands.NoPrivateMessage
        return True

    async def __error(self,error):
        if isinstance(error,commands.NoPrivateMessage):
            try:
                return await ctx.send('This command can not be used in Private Messages.')
            except discord.HTTPException:
                pass
        elif isinstance(error,InvalidVoiceChannel):
            await ctx.send('Error connecting to Voice Channel. '
                           'Please make sure you are in a valid channel or provide me with one')

        print('Ignoring exception in command {}:'.format(ctx.command),file=sys.stderr)
        traceback.print_exception(type(error),error,error.__traceback__,file=sys.stderr)

    def get_player(self,ctx):
        try:
            player = self.players[ctx.guild.id]
        except KeyError:
            player = MusicPlayer(ctx)
            self.players[ctx.guild.id] = player

        return player

    @commands.command(name='connect',aliases=['join'])
    async def connect_(self,channel: discord.VoiceChannel=None):
        if not channel:
            try:
                channel = ctx.author.voice.channel
            except AttributeError:
                raise InvalidVoiceChannel('No channel to join. Please either specify a valid channel or join one.')

        vc = ctx.voice_client

        if vc:
            if vc.channel.id == channel.id:
                return
            try:
                await vc.move_to(channel)
            except asyncio.TimeoutError:
                raise VoiceConnectionError(f'Moving to channel: <{channel}> timed out.')
        else:
            try:
                await channel.connect()
            except asyncio.TimeoutError:
                raise VoiceConnectionError(f'Connecting to channel: <{channel}> timed out.')

        await ctx.send(f'Connected to: **{channel}**',delete_after=20)

    @commands.command(name='play',aliases=['sing'])
    async def play_(self,search: str):
        await ctx.trigger_typing()

        vc = ctx.voice_client

        if not vc:
            await ctx.invoke(self.connect_)

        player = self.get_player(ctx)

        source = await YTDLSource.create_source(ctx,search,loop=self.bot.loop,download=False)

        await player.queue.put(source)

    @commands.command(name='pause')
    async def pause_(self,ctx):
        vc = ctx.voice_client

        if not vc or not vc.is_playing():
            return await ctx.send('I am not currently playing anything!',delete_after=20)
        elif vc.is_paused():
            return

        vc.pause()
        await ctx.send(f'**`{ctx.author}`**: Paused the song!')

    @commands.command(name='resume')
    async def resume_(self,ctx):
        vc = ctx.voice_client

        if not vc or not vc.is_connected():
            return await ctx.send('I am not currently playing anything!',delete_after=20)
        elif not vc.is_paused():
            return

        vc.resume()
        await ctx.send(f'**`{ctx.author}`**: Resumed the song!')

    @commands.command(name='skip')
    async def skip_(self,delete_after=20)

        if vc.is_paused():
            pass
        elif not vc.is_playing():
            return

        vc.stop()
        await ctx.send(f'**`{ctx.author}`**: Skipped the song!')

    @commands.command(name='queue',aliases=['q','playlist'])
    async def queue_info(self,ctx):
        vc = ctx.voice_client

        if not vc or not vc.is_connected():
            return await ctx.send('I am not currently connected to voice!',delete_after=20)

        player = self.get_player(ctx)
        if player.queue.empty():
            return await ctx.send('There are currently no more queued songs.')

        upcoming = list(itertools.islice(player.queue._queue,5))

        fmt = '\n'.join(f'**`{_["title"]}`**' for _ in upcoming)
        embed = discord.Embed(title=f'Upcoming - Next {len(upcoming)}',description=fmt)

        await ctx.send(embed=embed)

    @commands.command(name='now_playing',aliases=['np','currentsong','playing'])
    async def now_playing_(self,delete_after=20)

        player = self.get_player(ctx)
        if not player.current:
            return await ctx.send('I am not currently playing anything!')

        try:
            await player.np.delete()
        except discord.HTTPException:
            pass

        player.np = await ctx.send(f'**Now Playing:** `{vc.source.title}` '
                                   f'requested by `{vc.source.requester}`')

    @commands.command(name='volume',aliases=['vol'])
    async def change_volume(self,vol: float):
        vc = ctx.voice_client

        if not vc or not vc.is_connected():
            return await ctx.send('I am not currently connected to voice!',delete_after=20)

        if not 0 < vol < 101:
            return await ctx.send('Please enter a value between 1 and 100.')

        player = self.get_player(ctx)

        if vc.source:
            vc.source.volume = vol / 100

        player.volume = vol / 100
        await ctx.send(f'**`{ctx.author}`**: Set the volume to **{vol}%**')

    @commands.command(name='stop')
    async def stop_(self,delete_after=20)

        await self.cleanup(ctx.guild)


def setup(bot):
    bot.add_cog(Music(bot))
  

主要来自here


这是我的原始代码:

if vc:
  if vc.channel.id == channel.id:
    return
  await vc.move_to(channel)
else:
  await channel.connect()

await ctx.send(f'Connected to: **{channel}**')

我使用以下代码尝试调试问题:

vc = ctx.voice_client

if vc:
  if vc.channel.id == channel.id:
    return
  await vc.move_to(channel)
else:
  await channel.connect()
  while not ctx.voice_client:
    await channel.connect()
await ctx.send(f'Connected to: **{channel}**')


我希望机器人可以加入频道,然后“连接”到该频道,从而使其能够通过它播放音乐。但是,使用我的初始代码,当我尝试通过它播放音乐时,它给出了一个错误discord.errors.ClientException: Not connected to voice.。但是,它通过聊天发送了消息。在代码的第二次迭代中,该消息从未发送过,这使我相信该漫游器从未真正连接到该通道。

Image since I don't have enough reputation

如您所见,该机器人出现在语音通道中,但实际上并未“连接”。

xlongfei 回答:如何在Discord中连接到VC?

您使用的是“播放”命令还是“连接”命令?

为此,“ connect”命令将仅连接到语音通道,但如果未连接,则play命令将连接到语音通道,然后尝试播放音乐。

我看到它也在使用 discord.FFmpegPCMAudio 这直接来自discord.py文档。

  

警告:您的路径中必须具有ffmpeg或avconv可执行文件   环境变量以使其正常工作。

https://discordpy.readthedocs.io/en/latest/api.html?highlight=ffmpeg#discord.FFmpegPCMAudio

您的漫游器最有可能连接到该频道。我只是测试了该代码,并使其正常工作。当您使用第二段代码时,消息从未显示过,这一事实告诉我,该机器人由于无法连接而陷入了while循环。

您应该尝试建立一个公共语音通道并尝试使用它,或者确保您的漫游器对您正在使用的私人语音通道具有足够的权限。

您也可以尝试使用此方法来帮助您找出为什么它不起作用:

        try:
            voice_client = await channel.connect()
            # Code below this e.g.
            if not voice_client.is_connected():
                print(f"Not connected to {channel.name}")
        except asyncio.TimeoutError:
            raise VoiceConnectionError(f'Connecting to channel: <{channel}> timed out.')
        except discord.ClientException:
            print("You are already connected to a voice channel.")
        except discord.opus.OpusNotLoaded:
            print("The opus library has not been loaded.")
        except discord.DiscordException as ex:
            print(ex)

connect() 引发3个异常并返回一个voiceclient,我还包括基本的discord Exception,以帮助找出问题所在。请参阅下面的链接以获取文档。

这也是discord.VoiceClient文档中的警告。

  

警告::要使用基于PCM的AudioSource,必须在系统上安装opus库并通过opus.load_opus()进行加载。否则,您的AudioSource必须经过opus编码(例如,使用FFmpegOpusAudio),否则该库将无法传输音频。

https://discordpy.readthedocs.io/en/latest/api.html?highlight=connect#discord.VoiceChannel.connect https://discordpy.readthedocs.io/en/latest/api.html?highlight=connect#discord.VoiceClient

,

您是从文件的顶部读取了信息,还是只是将其复制并粘贴了?

  

”“请理解,音乐机器人很复杂,即使是这个基本示例,对于初学者来说也是令人生畏的。   因此,强烈建议您先熟悉discord.py,python和asyncio   您尝试编写音乐机器人。   本示例使用:Python 3.6   有关更基本的语音示例,请阅读:       https://github.com/Rapptz/discord.py/blob/rewrite/examples/basic_voice.py   这是一个非常基本的播放列表示例,它允许按行业协会播放唯一队列。   这些命令为基本用法实现了非常基本的逻辑。但是允许扩展。最好实施   您自己的命令权限和用法逻辑。   例如,您可能想在跳过歌曲之前进行投票,或者只允许管理员停止播放器。   音乐机器人需要大量的工作和调音。祝好运。   如果您发现任何错误,请随时与我联系。 @ Eviee#0666“

https://github.com/Rapptz/discord.py#installing

还是discord.py的安装信息?

  

需要安装Python 3.5.3或更高版本

     

要在没有完整语音支持的情况下安装库,只需运行   以下命令:

     

Linux / OS X python3 -m pip install -U discord.py

     

Windows

     

py -3 -m pip install -U discord.py

     

否则,要获得语音支持,您应该运行以下命令:

     

Linux / OS X python3 -m pip install -U discord.py [语音]

     

Windows py -3 -m pip install -U discord.py [语音]

,

代码工作正常。安装的库不是语音版本。可以与库的语音版本配合使用。

,
@client.command(pass_context=True,aliases=['j','joi'])
async def join(ctx):
    channel = ctx.message.author.voice.channel
    voice = get(client.voice_clients,guild=ctx.guild)

    if voice and voice.is_connected():
        await voice.move_to(channel)
    else:
        voice = await channel.connect()

    await voice.disconnect()

    if voice and voice.is_connected():
        await voice.move_to(channel)
    else:
        voice = await channel.connect()
        print(f"The bot has connected to {channel}\n")

    await ctx.send(f"Joined {channel}")
,

这些是离开和加入:


@client.command()
async def join(ctx):

    if not ctx.message.author.voice:
        await ctx.send("You are not connected to a voice channel!")
        return
    else:
        channel = ctx.message.author.voice.channel
        user = ctx.message.author.mention
        await ctx.send(f'{user},Connected to {channel}')
        await ctx.message.add_reaction('✅')
    await channel.connect()

@client.command()
async def leave(ctx):
    voice_client = ctx.message.guild.voice_client
    user = ctx.message.author.mention
    channel = ctx.message.author.voice.channel
    if not voice_client:
        await ctx.send(f"{user} I am not connected to a voice channel")
    else:
        await voice_client.disconnect()
        await ctx.send(f'{user},Disconnected from {channel}')
        await ctx.message.add_reaction('✅')
本文链接:https://www.f2er.com/3142844.html

大家都在问