I cannot get my await to work in the new py update - python

I am having some trouble with my discord bot that we use in a server for a group of friends to listen to youtube music. I will include the error screenshot and sources below. Thanks for looking.
Error from console(replit)
main.py:
import discord
from discord.ext import commands
import music
from webserver import keep_alive
import os
cogs = [music]
client = commands.Bot(command_prefix='!', intents = discord.Intents.all())
for i in range(len(cogs)):
cogs[i].setup(client)
keep_alive()
TOKEN = os.environ.get("DISCORD_BOT_SECRET");
client.run(TOKEN)
music.py:
import discord
from discord.ext import commands
import random
import asyncio
import itertools
import sys
import traceback
from async_timeout import timeout
from functools import partial
import youtube_dl
from youtube_dl import YoutubeDL
# Suppress noise about console usage from errors
youtube_dl.utils.bug_reports_message = lambda: ''
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' # ipv6 addresses cause issues sometimes
}
ffmpegopts = {
'before_options': '-nostdin',
'options': '-vn'
}
ytdl = YoutubeDL(ytdlopts)
class VoiceConnectionError(commands.CommandError):
"""Custom Exception class for connection errors."""
class InvalidVoiceChannel(VoiceConnectionError):
"""Exception for cases of invalid Voice Channels."""
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')
self.duration = data.get('duration')
# YTDL info dicts (data) have other useful information you might want
# https://github.com/rg3/youtube-dl/blob/master/README.md
def __getitem__(self, item: str):
"""Allows us to access attributes similar to a dict.
This is only useful when you are NOT downloading.
"""
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:
# take first item from a playlist
data = data['entries'][0]
embed = discord.Embed(title="", description=f"Queued [{data['title']}]({data['webpage_url']}) [{ctx.author.mention}]", color=discord.Color.green())
await ctx.send(embed=embed)
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, data, *, loop):
"""Used for preparing a stream, instead of downloading.
Since Youtube Streaming links expire."""
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']), data=data, requester=requester)
class MusicPlayer:
"""A class which is assigned to each guild using the bot for Music.
This class implements a queue and loop, which allows for different guilds to listen to different playlists
simultaneously.
When the bot disconnects from the Voice it's instance will be destroyed.
"""
__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 # Now playing message
self.volume = .5
self.current = None
ctx.bot.loop.create_task(self.player_loop())
async def player_loop(self):
"""Our main player loop."""
await self.bot.wait_until_ready()
while not self.bot.is_closed():
self.next.clear()
try:
# Wait for the next song. If we timeout cancel the player and disconnect...
async with timeout(300): # 5 minutes...
source = await self.queue.get()
except asyncio.TimeoutError:
return self.destroy(self._guild)
if not isinstance(source, YTDLSource):
# Source was probably a stream (not downloaded)
# So we should regather to prevent stream expiration
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))
embed = discord.Embed(title="Now playing", description=f"[{source.title}]({source.web_url}) [{source.requester.mention}]", color=discord.Color.green())
self.np = await self._channel.send(embed=embed)
await self.next.wait()
# Make sure the FFmpeg process is cleaned up.
source.cleanup()
self.current = None
def destroy(self, guild):
"""Disconnect and cleanup the player."""
return self.bot.loop.create_task(self._cog.cleanup(guild))
class Music(commands.Cog):
"""Music related commands."""
__slots__ = ('bot', 'players')
def __init__(self, bot):
self.bot = bot
self.players = {}
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):
"""A local check which applies to all commands in this cog."""
if not ctx.guild:
raise commands.NoPrivateMessage
return True
async def __error(self, ctx, error):
"""A local error handler for all errors arising from commands in this cog."""
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):
"""Retrieve the guild player, or generate one."""
try:
player = self.players[ctx.guild.id]
except KeyError:
player = MusicPlayer(ctx)
self.players[ctx.guild.id] = player
return player
#commands.command(name='join', aliases=['connect', 'j'], description="connects to voice")
async def connect_(self, ctx, *, channel: discord.VoiceChannel=None):
"""Connect to voice.
Parameters
------------
channel: discord.VoiceChannel [Optional]
The channel to connect to. If a channel is not specified, an attempt to join the voice channel you are in
will be made.
This command also handles moving the bot to different channels.
"""
if not channel:
try:
channel = ctx.author.voice.channel
except AttributeError:
embed = discord.Embed(title="", description="No channel to join. Please call `,join` from a voice channel.", color=discord.Color.green())
await ctx.send(embed=embed)
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.')
if (random.randint(0, 1) == 0):
await ctx.message.add_reaction('πŸ‘')
await ctx.send(f'**Joined `{channel}`**')
#commands.command(name='play', aliases=['sing','p','P','Play'], description="streams music")
async def play_(self, ctx, *, search: str):
"""Request a song and add it to the queue.
This command attempts to join a valid voice channel if the bot is not already in one.
Uses YTDL to automatically search and retrieve a song.
Parameters
------------
search: str [Required]
The song to search and retrieve using YTDL. This could be a simple search, an ID or URL.
"""
await ctx.trigger_typing()
vc = ctx.voice_client
if not vc:
await ctx.invoke(self.connect_)
player = self.get_player(ctx)
# If download is False, source will be a dict which will be used later to regather the stream.
# If download is True, source will be a discord.FFmpegPCMAudio with a VolumeTransformer.
source = await YTDLSource.create_source(ctx, search, loop=self.bot.loop, download=False)
await player.queue.put(source)
#commands.command(name='pause', aliases=['Pause'], description="pauses music")
async def pause_(self, ctx):
"""Pause the currently playing song."""
vc = ctx.voice_client
if not vc or not vc.is_playing():
embed = discord.Embed(title="", description="I am currently not playing anything", color=discord.Color.green())
return await ctx.send(embed=embed)
elif vc.is_paused():
return
vc.pause()
await ctx.send("Paused ⏸️")
#commands.command(name='resume', aliases=['Resume'], description="resumes music")
async def resume_(self, ctx):
"""Resume the currently paused song."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
embed = discord.Embed(title="", description="I'm not connected to a voice channel", color=discord.Color.green())
return await ctx.send(embed=embed)
elif not vc.is_paused():
return
vc.resume()
await ctx.send("Resuming ⏯️")
#commands.command(name='skip', aliases=['Skip','s','S'], description="skips to next song in queue")
async def skip_(self, ctx):
"""Skip the song."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
embed = discord.Embed(title="", description="I'm not connected to a voice channel", color=discord.Color.green())
return await ctx.send(embed=embed)
if vc.is_paused():
pass
elif not vc.is_playing():
return
vc.stop()
#commands.command(name='remove', aliases=['rm', 'rem'], description="removes specified song from queue")
async def remove_(self, ctx, pos : int=None):
"""Removes specified song from queue"""
vc = ctx.voice_client
if not vc or not vc.is_connected():
embed = discord.Embed(title="", description="I'm not connected to a voice channel", color=discord.Color.green())
return await ctx.send(embed=embed)
player = self.get_player(ctx)
if pos == None:
player.queue._queue.pop()
else:
try:
s = player.queue._queue[pos-1]
del player.queue._queue[pos-1]
embed = discord.Embed(title="", description=f"Removed [{s['title']}]({s['webpage_url']}) [{s['requester'].mention}]", color=discord.Color.green())
await ctx.send(embed=embed)
except:
embed = discord.Embed(title="", description=f'Could not find a track for "{pos}"', color=discord.Color.green())
await ctx.send(embed=embed)
#commands.command(name='clear', aliases=['clr', 'cl', 'cr'], description="clears entire queue")
async def clear_(self, ctx):
"""Deletes entire queue of upcoming songs."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
embed = discord.Embed(title="", description="I'm not connected to a voice channel", color=discord.Color.green())
return await ctx.send(embed=embed)
player = self.get_player(ctx)
player.queue._queue.clear()
await ctx.send('πŸ’£ **Cleared**')
#commands.command(name='queue', aliases=['q', 'playlist', 'que'], description="shows the queue")
async def queue_info(self, ctx):
"""Retrieve a basic queue of upcoming songs."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
embed = discord.Embed(title="", description="I'm not connected to a voice channel", color=discord.Color.green())
return await ctx.send(embed=embed)
player = self.get_player(ctx)
if player.queue.empty():
embed = discord.Embed(title="", description="queue is empty", color=discord.Color.green())
return await ctx.send(embed=embed)
seconds = vc.source.duration % (24 * 3600)
hour = seconds // 3600
seconds %= 3600
minutes = seconds // 60
seconds %= 60
if hour > 0:
duration = "%dh %02dm %02ds" % (hour, minutes, seconds)
else:
duration = "%02dm %02ds" % (minutes, seconds)
# Grabs the songs in the queue...
upcoming = list(itertools.islice(player.queue._queue, 0, int(len(player.queue._queue))))
fmt = '\n'.join(f"`{(upcoming.index(_)) + 1}.` [{_['title']}]({_['webpage_url']}) | ` {duration} Requested by: {_['requester']}`\n" for _ in upcoming)
fmt = f"\n__Now Playing__:\n[{vc.source.title}]({vc.source.web_url}) | ` {duration} Requested by: {vc.source.requester}`\n\n__Up Next:__\n" + fmt + f"\n**{len(upcoming)} songs in queue**"
embed = discord.Embed(title=f'Queue for {ctx.guild.name}', description=fmt, color=discord.Color.green())
embed.set_footer(text=f"{ctx.author.display_name}", icon_url=ctx.author.avatar_url)
await ctx.send(embed=embed)
#commands.command(name='np', aliases=['song', 'current', 'currentsong', 'playing'], description="shows the current playing song")
async def now_playing_(self, ctx):
"""Display information about the currently playing song."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
embed = discord.Embed(title="", description="I'm not connected to a voice channel", color=discord.Color.green())
return await ctx.send(embed=embed)
player = self.get_player(ctx)
if not player.current:
embed = discord.Embed(title="", description="I am currently not playing anything", color=discord.Color.green())
return await ctx.send(embed=embed)
seconds = vc.source.duration % (24 * 3600)
hour = seconds // 3600
seconds %= 3600
minutes = seconds // 60
seconds %= 60
if hour > 0:
duration = "%dh %02dm %02ds" % (hour, minutes, seconds)
else:
duration = "%02dm %02ds" % (minutes, seconds)
embed = discord.Embed(title="", description=f"[{vc.source.title}]({vc.source.web_url}) [{vc.source.requester.mention}] | `{duration}`", color=discord.Color.green())
embed.set_author(icon_url=self.bot.user.avatar_url, name=f"Now Playing 🎢")
await ctx.send(embed=embed)
#commands.command(name='volume', aliases=['vol', 'v'], description="changes Kermit's volume")
async def change_volume(self, ctx, *, vol: float=None):
"""Change the player volume.
Parameters
------------
volume: float or int [Required]
The volume to set the player to in percentage. This must be between 1 and 100.
"""
vc = ctx.voice_client
if not vc or not vc.is_connected():
embed = discord.Embed(title="", description="I am not currently connected to voice", color=discord.Color.green())
return await ctx.send(embed=embed)
if not vol:
embed = discord.Embed(title="", description=f"πŸ”Š **{(vc.source.volume)*100}%**", color=discord.Color.green())
return await ctx.send(embed=embed)
if not 0 < vol < 101:
embed = discord.Embed(title="", description="Please enter a value between 1 and 100", color=discord.Color.green())
return await ctx.send(embed=embed)
player = self.get_player(ctx)
if vc.source:
vc.source.volume = vol / 100
player.volume = vol / 100
embed = discord.Embed(title="", description=f'**`{ctx.author}`** set the volume to **{vol}%**', color=discord.Color.green())
await ctx.send(embed=embed)
#commands.command(name='leave', aliases=["stop", "dc", "disconnect", "bye"], description="stops music and disconnects from voice")
async def leave_(self, ctx):
"""Stop the currently playing song and destroy the player.
!Warning!
This will destroy the player assigned to your guild, also deleting any queued songs and settings.
"""
vc = ctx.voice_client
if not vc or not vc.is_connected():
embed = discord.Embed(title="", description="I'm not connected to a voice channel", color=discord.Color.green())
return await ctx.send(embed=embed)
if (random.randint(0, 1) == 0):
await ctx.message.add_reaction('πŸ‘‹')
await ctx.send('**Successfully disconnected**')
await self.cleanup(ctx.guild)
def setup(bot):
await bot.add_cog(Music(bot))
We tried awaiting the last line in the music.py file per the code doc on discord dev, but it appears something else is going on?

You can not use await statement outside async def ....
You need to do like so:
async def setup(bot):
await bot.add_cog(Music(bot))
Then at the entry point of your program you should call something like asyncio.run(setup())
Please refer to official documentation, it has some handy examples: https://docs.python.org/3/library/asyncio-task.html

Related

Don't understand commands.command() in my discord.py bot code - what prefix/commands will I have to use?

I have this code from somewhere else and now I need help. It's a code for a music bot (youtube_dl, discord.py) and I have two problems. My first problem is in line 25, where I don't know, whether I am supposed to type in a ipv4 and if so what ipv4 or if I can just ignore this.
My second problem lies from line 216 to 381, actually just the commands.command(...)...
I don't know what command I am supposed to use here. For example in line 216; am I supposed to type "connect", "connect_" or what? Is there a prefix I missed or is there a principal thing I don't know about? Please help I'm really struggling.
import itertools
import discord
from discord.ext import commands
import traceback
from async_timeout import timeout
from functools import partial
from youtube_dl import YoutubeDL
import asyncio
import sys
client = discord.Client()
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' # ipv6 addresses cause issues sometimes
}
ffmpegopts = {
'before_options': '-nostdin',
'options': '-vn'
}
ytdl = YoutubeDL(ytdlopts)
class VoiceConnectionError(commands.CommandError):
"""Custom Exception class for connection errors."""
class InvalidVoiceChannel(VoiceConnectionError):
"""Exception for cases of invalid Voice Channels."""
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')
# YTDL info dicts (data) have other useful information you might want
# https://github.com/rg3/youtube-dl/blob/master/README.md
def __getitem__(self, item: str):
"""Allows us to access attributes similar to a dict.
This is only useful when you are NOT downloading.
"""
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:
# take first item from a playlist
data = data['entries'][0]
await ctx.send(f'```ini\n[Added {data["title"]} to the Queue.]\n```')
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, data, *, loop):
"""Used for preparing a stream, instead of downloading.
Since Youtube Streaming links expire."""
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']), data=data, requester=requester)
class MusicPlayer(commands.Cog):
"""A class which is assigned to each guild using the bot for Music.
This class implements a queue and loop, which allows for different guilds to listen to different playlists
simultaneously.
When the bot disconnects from the Voice it's instance will be destroyed.
"""
__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 # Now playing message
self.volume = .5
self.current = None
ctx.bot.loop.create_task(self.player_loop())
async def player_loop(self):
"""Our main player loop."""
await self.bot.wait_until_ready()
while not self.bot.is_closed():
self.next.clear()
try:
# Wait for the next song. If we timeout cancel the player and disconnect...
async with timeout(300): # 5 minutes...
source = await self.queue.get()
except asyncio.TimeoutError:
return self.destroy(self._guild)
if not isinstance(source, YTDLSource):
# Source was probably a stream (not downloaded)
# So we should regather to prevent stream expiration
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()
# Make sure the FFmpeg process is cleaned up.
source.cleanup()
self.current = None
try:
# We are no longer playing this song...
await self.np.delete()
except discord.HTTPException:
pass
def destroy(self, guild):
"""Disconnect and cleanup the player."""
return self.bot.loop.create_task(self._cog.cleanup(guild))
class Music(commands.Cog):
"""Music related commands."""
__slots__ = ('bot', 'players')
def __init__(self, bot):
self.bot = bot
self.players = {}
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):
"""A local check which applies to all commands in this cog."""
if not ctx.guild:
raise commands.NoPrivateMessage
return True
async def __error(self, ctx, error):
"""A local error handler for all errors arising from commands in this cog."""
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):
"""Retrieve the guild player, or generate one."""
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, ctx):
try:
channel = ctx.author.voice.channel
except AttributeError:
raise InvalidVoiceChannel('No channel to join.')
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.')
embed = discord.Embed(title="Joined A Call")
embed.add_field(name="Connected To :", value=channel, inline=True)
await ctx.send(embed=embed)
#commands.command(name='play', aliases=['sing', 'p'])
async def play_(self, ctx, *, search: str):
await ctx.trigger_typing()
vc = ctx.voice_client
if not vc:
await ctx.invoke(self.connect_)
player = self.get_player(ctx)
# If download is False, source will be a dict which will be used later to regather the stream.
# If download is True, source will be a discord.FFmpegPCMAudio with a VolumeTransformer.
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):
"""Pause the currently playing song."""
vc = ctx.voice_client
if not vc or not vc.is_playing():
return await ctx.send('I am not currently playing anything!')
elif vc.is_paused():
return
vc.pause()
await ctx.send(f'**`{ctx.author}`**: Paused the song!')
#commands.command(name='resume', aliases=['unpause'])
async def resume_(self, ctx):
"""Resume the currently paused song."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
return await ctx.send('I am not currently playing anything!', )
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, ctx):
"""Skip the song."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
return await ctx.send('I am not currently playing anything!')
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):
"""Retrieve a basic queue of upcoming songs."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
return await ctx.send('I am not currently connected to voice!')
player = self.get_player(ctx)
if player.queue.empty():
return await ctx.send('There are currently no more queued songs.')
# Grab up to 5 entries from the queue...
upcoming = list(itertools.islice(player.queue._queue, 0, 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', 'current', 'currentsong', 'playing'])
async def now_playing_(self, ctx):
"""Display information about the currently playing song."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
return await ctx.send('I am not currently connected to voice!', )
player = self.get_player(ctx)
if not player.current:
return await ctx.send('I am not currently playing anything!')
try:
# Remove our previous now_playing message.
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, ctx, *, vol: float):
"""Change the player volume.
Parameters
------------
volume: float or int [Required]
The volume to set the player to in percentage. This must be between 1 and 100.
"""
vc = ctx.voice_client
if not vc or not vc.is_connected():
return await ctx.send('I am not currently connected to voice!', )
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
embed = discord.Embed(title="Volume Message",
description=f'The Volume Was Changed By **{ctx.author.name}**')
embed.add_field(name="Current Volume", value=vol, inline=True)
await ctx.send(embed=embed)
# await ctx.send(f'**`{ctx.author}`**: Set the volume to **{vol}%**')
#commands.command(name='stop', aliases=['leave'])
async def stop_(self, ctx):
"""Stop the currently playing song and destroy the player.
!Warning!
This will destroy the player assigned to your guild, also deleting any queued songs and settings.
"""
vc = ctx.voice_client
if not vc or not vc.is_connected():
return await ctx.send('I am not currently playing anything!')
await self.cleanup(ctx.guild)

discord.py | play audio from url

I want to make my bot playing audio from an url, but I don't want to download the file ...
Here is my code:
#commands.command(name='test')
async def test(self, ctx):
search = "morpheus tutorials discord bot python"
if ctx.message.author.voice == None:
await ctx.send(embed=Embeds.txt("No Voice Channel", "You need to be in a voice channel to use this command!", ctx.author))
return
channel = ctx.message.author.voice.channel
voice = discord.utils.get(ctx.guild.voice_channels, name=channel.name)
voice_client = discord.utils.get(self.client.voice_clients, guild=ctx.guild)
if voice_client == None:
await voice.connect()
else:
await voice_client.move_to(channel)
search = search.replace(" ", "+")
html = urllib.request.urlopen("https://www.youtube.com/results?search_query=" + search)
video_ids = re.findall(r"watch\?v=(\S{11})", html.read().decode())
#################################
await ctx.send("https://www.youtube.com/watch?v=" + video_ids[0])
# AND HERE SHOULD IT PLAY
#################################
I have tryied create_ytdl_player method, but saw that its no longer supported what can I do?
use pafy.
First import some stuff and set FFmpeg options...
import pafy
from discord import FFmpegPCMAudio, PCMVolumeTransformer
FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5','options': '-vn'}
Then play it
#commands.command(name='test')
async def test(self, ctx):
search = "morpheus tutorials discord bot python"
if ctx.message.author.voice == None:
await ctx.send(embed=Embeds.txt("No Voice Channel", "You need to be in a voice channel to use this command!", ctx.author))
return
channel = ctx.message.author.voice.channel
voice = discord.utils.get(ctx.guild.voice_channels, name=channel.name)
voice_client = discord.utils.get(self.client.voice_clients, guild=ctx.guild)
if voice_client == None:
voice_client = await voice.connect()
else:
await voice_client.move_to(channel)
search = search.replace(" ", "+")
html = urllib.request.urlopen("https://www.youtube.com/results?search_query=" + search)
video_ids = re.findall(r"watch\?v=(\S{11})", html.read().decode())
await ctx.send("https://www.youtube.com/watch?v=" + video_ids[0])
song = pafy.new(video_ids[0]) # creates a new pafy object
audio = song.getbestaudio() # gets an audio source
source = FFmpegPCMAudio(audio.url, **FFMPEG_OPTIONS) # converts the youtube audio source into a source discord can use
voice_client.play(source) # play the source
If the source argument is a str it is directly fed as input to ffmpeg binary by FFmpegPCMAudio internally.
ffmpeg supports urls as input out of the box. so you can just pass the url instead of file path.
source = FFmpegPCMAudio("https://example.com/demo.mp3", executable="ffmpeg")
ctx.voice_client.play(source, after=None)
Replace the bot with client if you need to. Then, try this out:
import asyncio
import discord
import youtube_dl
from discord.ext import commands
# Suppress noise about console usage from errors
youtube_dl.utils.bug_reports_message = lambda: ''
ytdl_format_options = {
'format': 'bestaudio/best',
'outtmpl': '%(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' # bind to ipv4 since ipv6 addresses cause issues sometimes
}
ffmpeg_options = {
'options': '-vn'
}
ytdl = youtube_dl.YoutubeDL(ytdl_format_options)
class YTDLSource(discord.PCMVolumeTransformer):
def __init__(self, source, *, data, volume=0.5):
super().__init__(source, volume)
self.data = data
self.title = data.get('title')
self.url = data.get('url')
#classmethod
async def from_url(cls, url, *, loop=None, stream=False):
loop = loop or asyncio.get_event_loop()
data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=not stream))
if 'entries' in data:
# take first item from a playlist
data = data['entries'][0]
filename = data['url'] if stream else ytdl.prepare_filename(data)
return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data)
class Music(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.command(description="joins a voice channel")
async def join(self, ctx):
if ctx.author.voice is None or ctx.author.voice.channel is None:
return await ctx.send('You need to be in a voice channel to use this command!')
voice_channel = ctx.author.voice.channel
if ctx.voice_client is None:
vc = await voice_channel.connect()
else:
await ctx.voice_client.move_to(voice_channel)
vc = ctx.voice_client
#commands.command(description="streams music")
async def play(self, ctx, *, url):
async with ctx.typing():
player = await YTDLSource.from_url(url, loop=self.bot.loop, stream=True)
ctx.voice_client.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
await ctx.send('Now playing: {}'.format(player.title))
#commands.command(description="stops and disconnects the bot from voice")
async def leave(self, ctx):
await ctx.voice_client.disconnect()
#play.before_invoke
async def ensure_voice(self, ctx):
if ctx.voice_client is None:
if ctx.author.voice:
await ctx.author.voice.channel.connect()
else:
await ctx.send("You are not connected to a voice channel.")
raise commands.CommandError("Author not connected to a voice channel.")
elif ctx.voice_client.is_playing():
ctx.voice_client.stop()
def setup(bot):
bot.add_cog(Music(bot))
Not only will this play from URL, but it will also play from the name of the video itself.

What is the best method to queue songs using wavelink library?

I'm new to this library. I want to make my bot to have a queue option, I already tried declaring an empty list queue=[] and use
#commands.command()
async def play(self, ctx, *, query: str):
while True:
tracks = await self.bot.wavelink.get_tracks(f'ytsearch:{query}')
if not tracks:
continue
else:
break
player = self.bot.wavelink.get_player(ctx.guild.id)
if not player.is_connected:
await ctx.invoke(self.connect_)
channel = ctx.author.voice.channel
if player.is_playing:
if any(f"{channel.id}" in s for s in queue):
await ctx.channel.send(embed=discord.Embed(description='Your queue exceed maximum capacity of 1 element',color=0xfd2121))
else:
queue.append(f'{channel.id}:{tracks[0]}')
await ctx.channel.send(embed=discord.Embed(description=f"Your song has been queued\n{tracks[0]}",color=0xfd2121))
else:
await ctx.channel.send(embed=discord.Embed(description=f'Now playing {tracks[0]} .', color=0xfd2121))
await player.play(tracks[0])
while player.is_playing:
await asyncio.sleep(1)
if not any(f"{channel.id}" in s for s in queue):
await player.disconnect()
else:
res = [i for i in queue if f'{channel.id}' in i]
sw=''.join(res)
while True:
sw2 = await self.bot.wavelink.get_tracks(f'ytsearch:{sw[19:]}')
if not sw2:
continue
else:
break
player = self.bot.wavelink.get_player(ctx.guild.id)
await player.play(sw2[0])
queue.remove(f'{channel.id}:{sw[19:]}')
but this can only contain 1 queue per channel id and the scripts are way too complicated. I saw some people are using asyncio.Queue - some kind of background process - can anybody provide me with an example of using asyncio.Queue to queue up songs?
Here is a simple example of setting up a queue system with asyncio.Queue() and asyncio.Event(), utilising wavelink to handle audio.
import wavelink
import asyncio
from discord.ext import commands
client = commands.Bot(command_prefix='!')
songs = asyncio.Queue()
play_next_song = asyncio.Event()
if not hasattr(client, 'wavelink'):
client.wavelink = wavelink.Client(bot=client)
async def on_event_hook(event):
if isinstance(event, (wavelink.TrackEnd, wavelink.TrackException)):
play_next_song.set()
async def audio_player_task():
await client.wait_until_ready()
node = await client.wavelink.initiate_node(host='127.0.0.1',
port=2333,
rest_uri='http://127.0.0.1:2333',
password='PASSWORD',
identifier='TEST',
region='us_central')
node.set_hook(on_event_hook)
while True:
play_next_song.clear()
song, guild_id = await songs.get()
player = client.wavelink.get_player(guild_id)
await player.play(song)
await play_next_song.wait()
#client.command()
async def play(ctx, *, query: str):
tracks = await client.wavelink.get_tracks(f'ytsearch:{query}')
if not tracks:
return await ctx.send('Could not find any songs with that query.')
player = client.wavelink.get_player(ctx.guild.id)
if not player.is_connected:
await player.connect(ctx.author.voice.channel.id)
await ctx.send(f'Added {str(tracks[0])} to the queue.')
queue_item = (tracks[0], ctx.guild.id)
await songs.put(queue_item)
client.loop.create_task(audio_player_task())
client.run('token')

Discord.py Rewrite - ASyncIO put() on queue not working properly

Error Output
AttributeError: 'member_descriptor' object has no attribute 'put'
I currently have a MusicPlayer Class for my bot to play music the way I would like. I believe my command is causing the issue but I am not sure. I have never heard of a member_descriptor and upon looking it up there doesnt seem to be much on this error. I would like to just add songs to the queue and play them in that order, if their is no songs in the queue, it should just play it.
Below is my Class and Command. Any help is very appreciated!
UPDATED
MusicPlayer Class
class MusicPlayer():
__slots__ = ("client", "_guild", "_ctxs", "_channel", "_cog", "np", "volume", "current", "colour", "task")
queue = asyncio.Queue()
next = asyncio.Event()
def __init__(self, ctx, client):
self.client = client
self._guild = ctx.guild
self._ctxs = ctx
self._channel = ctx.channel
self._cog = ctx.cog
self.np = None
self.volume = defaultvolume
self.current = None
self.colour = self.client.defaultcolour
self.task = self.client.loop.create_task(self.player_loop())
async def player_loop(self):
await self.client.wait_until_ready()
while True:
self.next.clear()
try:
async with timeout(300):
self.current = await queue.get()
except asyncio.CancelledError:
return
except asyncio.TimeoutError:
guild = self._guild
vc = guild.voice_client
self.destroy(guild)
if not vc: return
await self._ctxs.send(":point_right: **I disconnected myself from the **`{}`** voice channel as I was not playing audio for 5 minutes!**".format(vc.channel.name))
return
except:
self.destroy(self._guild)
await self._ctxs.send(":thumbsdown: **Error: getting next song failed!** Please retry later!")
return
self._ctxs.voice_client.play(self.current, after=lambda: self.client.loop.call_soon_threadsafe(next.set))
self.current.volume = self.volume
thumbnail = self.current.thumbnail if self.current.thumbnail else self.client.user.avatar_url
self.colour = await self.client.get_average_colour(thumbnail)
embednps = discord.Embed(colour=self.colour)
embednps.add_field(name="Now Playing", value=f"```{self.current.title}```", inline=False)
embednps.add_field(name="Link", value=f"[URL]({self.current.web_url})", inline=True)
embednps.add_field(name="Duration", value=self.client.time_from_seconds(self.current.duration), inline=True)
embednps.add_field(name="Channel", value=f"{self.current.uploader}", inline=False)
embednps.set_thumbnail(url=f"{thumbnail}")
embednps.set_footer(text=f"Requested by {self.current.requester}", icon_url=self.current.requester.avatar_url)
self.np = await self._channel.send(embed=embednps)
await next.wait()
print("Terminated")
# Cleanup player
self.current.cleanup()
self.current = None
async def add_song(self, player):
return await self.queue.put(player)
def destroy(self, guild):
return self.client.loop.create_task(self._cog.cleanup(guild))
Play Command
#commands.command(aliases=['yt', 'youtube'])
async def play(self, ctx, *, url=None):
await ctx.message.delete()
channel = ctx.message.author.voice.channel
if url is None:
await ctx.send("Music: Please specify a Youtube URL. Syntax (!play {URL})", delete_after=7)
return
if ctx.guild.voice_client is None:
if not ctx.author.voice:
await ctx.send("Music: Please join a Voice Channel or use join command.", delete_after=7)
return
await channel.connect()
else:
if not ctx.author.voice:
await ctx.send("Music: Please join a Voice Channel or use join command.", delete_after=7)
return
if ctx.guild.voice_client.channel != ctx.message.author.voice.channel:
await ctx.guild.voice_client.move_to(channel)
async with ctx.typing():
player = await YTDLSource.from_url(url, loop=self.client.loop, stream=True)
if ctx.guild.voice_client.is_playing():
await MusicPlayer.add_song(MusicPlayer, player)
await ctx.send('Music: {} has now been added to the Queue'.format(player.title), delete_after=7)
return
voice_channel = ctx.guild.voice_client
voice_channel.play(player, after=lambda: self.client.loop.call_soon_threadsafe(MusicPlayer.next.set))
await ctx.send('Music: Now playing {}'.format(player.title), delete_after=7)
The class MusicPlayer has a descriptor queue that is created by __slots__. You're accessing that descriptor directly when you do MusicPlayer.queue instead of accessing an attribute of an instance, which you would do with music_player.queue.
At some point you need to create a MusicPlayer object from your class and use that instead of the class itself.

Changing presence discord status

Hi I have been working on a music bot for Discord which is a VOIP app gamers use. On here you can sent a presence to show what game you're playing and so on. In this case I want to show just some text that has been requested. Here it says on discord.py documentation this is how you would add it.
This is an example how I have added it but once the bot is online this doesn't show and I have updated to the lastest discord.py version v0.16.6. If anyone could help me where I'm going wrong here id appreciate it.
Thanks
async def change_status(self):
await self.bot.change_presence(game=discord.Game(name="Test"))`
and in the full code
import asyncio
import discord
from discord.ext import commands
from .utils import checks
if not discord.opus.is_loaded():
# the 'opus' library here is opus.dll on windows
# or libopus.so on linux in the current directory
# you should replace this with the location the
# opus library is located in and with the proper filename.
# note that on windows this DLL is automatically provided for you
discord.opus.load_opus('opus')
def __init__(self, bot):
self.bot = bot
class VoiceEntry:
def __init__(self, message, player):
self.requester = message.author
self.channel = message.channel
self.player = player
def __str__(self):
fmt = ' {0.title} uploaded by {0.uploader} and requested by {1.display_name}'
duration = self.player.duration
if duration:
fmt = fmt + ' [length: {0[0]}m {0[1]}s]'.format(divmod(duration, 60))
return fmt.format(self.player, self.requester)
class VoiceState:
def __init__(self, bot):
self.current = None
self.voice = None
self.bot = bot
self.play_next_song = asyncio.Event()
self.songs = asyncio.Queue()
self.skip_votes = set() # a set of user_ids that voted
self.audio_player = self.bot.loop.create_task(self.audio_player_task())
def is_playing(self):
if self.voice is None or self.current is None:
return False
player = self.current.player
return not player.is_done()
#property
def player(self):
return self.current.player
def skip(self):
self.skip_votes.clear()
if self.is_playing():
self.player.stop()
def toggle_next(self):
self.bot.loop.call_soon_threadsafe(self.play_next_song.set)
async def change_status(self):
await self.bot.change_presence(game=discord.Game(name="Test"))
async def audio_player_task(self):
while True:
self.play_next_song.clear()
self.current = await self.songs.get()
await self.bot.send_message(self.current.channel, '**Now playing**' + str(self.current))
self.current.player.start()
await self.play_next_song.wait()
class Music:
"""Voice related commands.
Works in multiple servers at once.
"""
def __init__(self, bot):
self.bot = bot
self.voice_states = {}
def get_voice_state(self, server):
state = self.voice_states.get(server.id)
if state is None:
state = VoiceState(self.bot)
self.voice_states[server.id] = state
return state
async def create_voice_client(self, channel):
voice = await self.bot.join_voice_channel(channel)
state = self.get_voice_state(channel.server)
state.voice = voice
def __unload(self):
for state in self.voice_states.values():
try:
state.audio_player.cancel()
if state.voice:
self.bot.loop.create_task(state.voice.disconnect())
except:
pass
#commands.command(pass_context=True, no_pm=True)
#checks.mod_or_permissions()
async def join(self, ctx, *, channel : discord.Channel):
"""Joins a voice channel."""
try:
await self.create_voice_client(channel)
except discord.ClientException:
await self.bot.say('Already in a voice channel...')
except discord.InvalidArgument:
await self.bot.say('This is not a voice channel...')
else:
await self.bot.say('Ready to play audio in ' + channel.name)
#commands.command(pass_context=True, no_pm=True)
async def summon(self, ctx):
"""Summons the bot to join your voice channel."""
summoned_channel = ctx.message.author.voice_channel
if summoned_channel is None:
await self.bot.say('Are you sure your in a channel?')
return False
state = self.get_voice_state(ctx.message.server)
if state.voice is None:
state.voice = await self.bot.join_voice_channel(summoned_channel)
else:
await state.voice.move_to(summoned_channel)
return True
#commands.command(pass_context=True, no_pm=True)
#commands.cooldown(5, 300, commands.BucketType.user)
async def play(self, ctx, *, song : str):
"""Plays a song.
If there is a song currently in the queue, then it is
queued until the next song is done playing.
This command automatically searches as well from YouTube.
The list of supported sites can be found here:
https://rg3.github.io/youtube-dl/supportedsites.html
"""
state = self.get_voice_state(ctx.message.server)
opts = {
'default_search': 'auto',
'quiet': True,
}
if state.voice is None:
success = await ctx.invoke(self.summon)
await self.bot.say("Loading the song please be patient..")
if not success:
return
try:
player = await state.voice.create_ytdl_player(song, ytdl_options=opts, after=state.toggle_next)
except Exception as e:
fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```'
await self.bot.send_message(ctx.message.channel, fmt.format(type(e).__name__, e))
else:
player.volume = 0.6
entry = VoiceEntry(ctx.message, player)
await self.bot.say('**Enqueued** ' + str(entry))
await state.songs.put(entry)
#commands.command(pass_context=True, no_pm=True)
async def volume(self, ctx, value : int):
"""Sets the volume of the currently playing song."""
state = self.get_voice_state(ctx.message.server)
if state.is_playing():
player = state.player
player.volume = value / 100
await self.bot.say('Set the volume to {:.0%}'.format(player.volume))
#commands.command(pass_context=True, no_pm=True)
async def pause(self, ctx):
""" Pauses the currently playing song. """
state = self.get_voice_state(ctx.message.server)
if state.is_playing():
player = state.player
player.pause()
await self.bot.say("Song Paused")
logging.info("Song paused")
#commands.command(pass_context=True, no_pm=True)
async def resume(self, ctx):
"""Resumes the currently played song."""
state = self.get_voice_state(ctx.message.server)
if state.is_playing():
player = state.player
player.resume()
await self.bot.say("Song Resumed")
#commands.command(pass_context=True, no_pm=True)
async def stop(self, ctx):
"""Stops playing audio and leaves the voice channel.
This also clears the queue.
"""
server = ctx.message.server
state = self.get_voice_state(server)
if state.is_playing():
player = state.player
player.stop()
try:
state.audio_player.cancel()
del self.voice_states[server.id]
await state.voice.disconnect()
await self.bot.say("Cleared the queue and disconnected from voice channel ")
except:
pass
#commands.command(pass_context=True, no_pm=True)
async def skip(self, ctx):
"""Vote to skip a song. The song requester can automatically skip.
3 skip votes are needed for the song to be skipped.
"""
state = self.get_voice_state(ctx.message.server)
if not state.is_playing():
await self.bot.say('Not playing any music right now...')
return
voter = ctx.message.author
if voter == state.current.requester:
await self.bot.say('Requester requested skipping song...')
state.skip()
elif voter.id not in state.skip_votes:
state.skip_votes.add(voter.id)
total_votes = len(state.skip_votes)
if total_votes >= 3:
await self.bot.say('Skip vote passed, skipping song...')
state.skip()
else:
await self.bot.say('Skip vote added, currently at [{}/3]'.format(total_votes))
else:
await self.bot.say('You have already voted to skip this song.')
#commands.command(pass_context=True, no_pm=True)
async def playing(self, ctx):
"""Shows info about the currently played song."""
state = self.get_voice_state(ctx.message.server)
if state.current is None:
await self.bot.say('Not playing anything.')
else:
skip_count = len(state.skip_votes)
await self.bot.say('**Now playing** {} [skips: {}/3]'.format(state.current, skip_count))
def setup(bot):
bot.add_cog(Music(bot))
print('Music is loaded')
Try this:
# Setting `Playing ` status
await bot.change_presence(activity=discord.Game(name="a game"))
# Setting `Streaming ` status
await bot.change_presence(activity=discord.Streaming(name="My Stream", url=my_twitch_url))
# Setting `Listening ` status
await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name="a song"))
# Setting `Watching ` status
await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name="a movie"))
Use this:
await bot.change_presence(status=discord.Status.idle, activity=discord.Game(name='Enter status here'))
Also, you can use await bot.change_presence(game=discord.Game(name='Enter status here'), status=discord.Status.do_not_disturb) to swiitch to a DnD status.
Use this instead:
await client.change_presence(game=discord.Game(name="Test", type=3))
Changing "type" to 1 will set a "streaming" status instead of a "playing" one.

Categories