I'm making music bot with discord.py and using heroku. bot is playing music my localhost but heroku host is not playing music.
I found what cause a bug.
It working nicely in heroku-20, but not working in heroku-22 stack.
How can I use heroku-22 stack without error?
What can I do?
I'm sorry my english is bad.
my code:
import youtube_dl
import discord
from discord import app_commands,Interaction
import asyncio
import os
class MyClient(discord.Client):
async def on_ready(self):
await self.wait_until_ready()
await tree.sync()
print(f"login at {self.user}")
intents= discord.Intents.all()
client = MyClient(intents=intents)
tree = app_commands.CommandTree(client)
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=True):
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)
queue={}
#tree.command(name="play", description="play music")
async def playmusic(interaction:Interaction,url_title:str,playfirst:bool=False):
await interaction.response.send_message("I'm looking for music!!")
guild=str(interaction.guild.id)
try:
queue[guild]
except KeyError:
queue[guild]=[]
if interaction.user.voice is None:
await interaction.edit_original_response(content="you must join any voice channel")
else:
voice_client: discord.VoiceClient = discord.utils.get(client.voice_clients, guild=interaction.guild)
if voice_client == None:
await interaction.user.voice.channel.connect()
voice_client: discord.VoiceClient = discord.utils.get(client.voice_clients, guild=interaction.guild)
player = await YTDLSource.from_url(url_title, loop=None)
if playfirst and len(queue[guild])>1:
queue[guild].insert(1,player)
else:
queue[guild].append(player)
if not voice_client.is_playing():
voice_client.play(player,after=None)
await interaction.edit_original_response(content=f"{player.title} playing!!")
else:
await interaction.edit_original_response(content=f"{player.title} enqueued!")
await asyncio.sleep(7)
await interaction.delete_original_response()
client.run(token)
My entire code:
from discord.ext.commands import Bot, CheckFailure, has_role
import discord
import os
import random
from replit import db
from keep_alive import keep_alive
from statistics import mode
from operators import embeds
from datetime import date
#embed preset
'''
embed = discord.Embed(title="title", description="desc", color=PURPLE)
embed.set_author(name="Jäger Bot", icon_url="https://cdn.discordapp.com/avatars/816636186323189780/019dbb41d6f5b301c1fce6980d487f23.webp?size=128")
embed.add_field(name="name", value="value", inline=True)
embed.set_footer(text="requested by " + authormention + "| " + send_date)
await ctx.send(embed=embed)
'''
bot = Bot(command_prefix="j!", help_command=None)
# bot ready and activity
#bot.event
async def on_ready():
print("Bot started")
game = discord.Game("with Jäger")
await bot.change_presence(status=discord.Status.online, activity=game)
# --------------------
PURPLE = 0x800080
STAFF_ROLE_ID = 817476654297383002
SOTW_ROLE_ID = 817458661982994463
FORMER_SOTW_ROLE_ID = 817030310663028736
JAGER_ROLE_ID = 818950349016137739
today = date.today()
global is_voting
is_voting = False
global send_date
send_date = today.strftime("%d-%m-%Y")
#update db
#bot.event
async def update_db(key, value):
if key in db.keys():
old_value = db[key]
old_value.append(value)
db[key] = old_value
else:
db[key] = [value]
#keys:
#nominating_perons ppl who have nominated
#nominated_persons ppl who have been nominated
#voting_perons ppl who have voted
#voted_persons ppl who have been voted for
#----commands----
#help
#bot.command()
async def help(ctx ,category=None):
print("command: help")
staff_category = "staff"
sotw_category = "sotw"
misc_category = "misc"
if category is None:
print("no category")
embed = discord.Embed(title="Select a category with `j!help \"category\"`", description="Prefix = j!", color=PURPLE)
embed.set_author(name="Jäger Bot | Help menu", icon_url="https://cdn.discordapp.com/avatars/816636186323189780/019dbb41d6f5b301c1fce6980d487f23.webp?size=128")
embed.add_field(name="Categories:", value="`staff` `sotw` `misc`", inline=True)
embed.set_footer(text="Made by ZoutigeWolf#0002 | Big thx to gang#5039 for helping")
await ctx.send(embed=embed)
else:
if category == staff_category:
print("staffhelp")
staff = discord.Embed(title="Staff commands", description="prefix = j!", color=PURPLE)
staff.set_author(name="Jäger Bot | Help menu", icon_url="https://cdn.discordapp.com/avatars/816636186323189780/019dbb41d6f5b301c1fce6980d487f23.webp?size=128")
staff.add_field(name="startvote", value="Starts the voting process", inline=False)
staff.add_field(name="finishvote", value="Finishes the voting process", inline=False)
staff.add_field(name="list <key> | debug only", value="Shows all the items in a specific list", inline=False)
staff.add_field(name="clear <key> | debug only", value="Clears all the items in a specific list", inline=False)
staff.add_field(name="ph <key> | debug only", value="Adds a `place_holder` to a specific list", inline=False)
staff.set_footer(text="Made by ZoutigeWolf#0002 | Big thx to gang#5039 for helping")
await ctx.send(embed=staff)
else:
if category == sotw_category:
print("sotwhelp")
sotw = discord.Embed(title="Sotw commands", description="prefix = j!", color=PURPLE)
sotw.set_author(name="Jäger Bot | Help menu", icon_url="https://cdn.discordapp.com/avatars/816636186323189780/019dbb41d6f5b301c1fce6980d487f23.webp?size=128")
sotw.add_field(name="nominate <member>", value="Nominate a member", inline=False)
sotw.add_field(name="vote <member>", value="Vote for a member", inline=False)
sotw.set_footer(text="Made by ZoutigeWolf#0002 | Big thx to gang#5039 for helping")
await ctx.send(embed=sotw)
else:
if category == misc_category:
print("mischelp")
misc = discord.Embed(title="Misc commands", description="prefix = j!", color=PURPLE)
misc.set_author(name="Jäger Bot | Help menu", icon_url="https://cdn.discordapp.com/avatars/816636186323189780/019dbb41d6f5b301c1fce6980d487f23.webp?size=128")
misc.add_field(name="quote", value="A random siege operator quote", inline=False)
misc.add_field(name="profile <operator>", value="Shows the profile of the specified siege operator", inline=True)
misc.add_field(name="shoot <member>", value="Shoot someone", inline=False)
misc.add_field(name="sexy <member>", value="Shows how sexy someone is", inline=False)
misc.set_footer(text="Made by ZoutigeWolf#0002 | Big thx to gang#5039 for helping")
await ctx.send(embed=misc)
else:
print("wrong category")
embed = discord.Embed(title="Select a category with `j!help \"category\"`", description="Prefix = j!", color=PURPLE)
embed.set_author(name="Jäger Bot | Help menu", icon_url="https://cdn.discordapp.com/avatars/816636186323189780/019dbb41d6f5b301c1fce6980d487f23.webp?size=128")
embed.add_field(name="Categories:", value="`staff` `sotw` `miscellaneous`", inline=True)
embed.set_footer(text="Made by ZoutigeWolf#0002 | Big thx to gang#5039 for helping")
await ctx.send(embed=embed)
#help end
#SOTW
#nominate
#bot.command()
async def nominate(ctx, nominatedperson: discord.Member):
print("command: nominate")
print(nominatedperson)
nominated_person = nominatedperson.mention
nominating_person = ctx.author.mention
former_sotw_role = discord.utils.get(ctx.guild.roles, id=FORMER_SOTW_ROLE_ID)
if not is_voting:
print("is_voting was false")
nominated_persons = db["nominated_persons"]
nominating_persons = db["nominating_persons"]
if nominating_person not in nominating_persons:
print("nominating person wasnt in nominating persons")
if nominated_person in nominated_persons:
print("nominated_person was in nominated_persons")
await ctx.send("That member has already been nominated!")
else:
print("nominated_person wasnt in nominated_persons")
former_sotw_role = discord.utils.get(ctx.guild.roles, id=FORMER_SOTW_ROLE_ID)
if former_sotw_role in nominatedperson.roles:
print("nominated_person was former sotw")
await ctx.send("That member has already been SOTW this year!")
else:
print("nominated_person wasnt former sotw")
embed = discord.Embed(title="Member nominated:", description=nominated_person, color=PURPLE)
embed.set_author(name="Jäger Bot", icon_url="https://cdn.discordapp.com/avatars/816636186323189780/019dbb41d6f5b301c1fce6980d487f23.webp?size=128")
embed.add_field(name="By:", value=nominating_person, inline=True)
embed.set_footer(text=send_date)
print(nominated_persons)
await update_db(nominated_persons, nominated_person)
await update_db(nominating_persons, nominating_person)
await ctx.send(embed=embed)
else:
print("nominating_person was in nominating_persons")
await ctx.send("You already nominated someone")
else:
print("is_voting was true")
await ctx.send("You can't nominate while we are voting for the next SOTW!")
#nominate end
#SOTW end
#insert place_holder
#bot.command()
#has_role(STAFF_ROLE_ID)
async def ph(ctx, key):
print("command: ph")
await update_db(key, "place_holder")
await ctx.send("Placeholder has been added to " + key)
#ph.error
async def ph_error(ctx, error):
if isinstance(error, CheckFailure):
await ctx.send("Only staff members can use this command")
#insert place_holder end
#list
#bot.command()
#has_role(STAFF_ROLE_ID)
async def list(ctx, to_list):
print("command: list")
if to_list in db.keys():
requester = ctx.author
requestermention = str(requester)
items = db[to_list]
embed = discord.Embed(title="List", description=to_list, color=PURPLE)
embed.set_author(name="Jäger Bot", icon_url="https://cdn.discordapp.com/avatars/816636186323189780/019dbb41d6f5b301c1fce6980d487f23.webp?size=128")
embed.add_field(name="Items:", value=items, inline=True)
embed.set_footer(text="requested by " + requestermention + " | " + send_date)
await ctx.send(embed=embed)
else:
await ctx.send("That list doesn't exist")
#list.error
async def list_error(ctx, error):
if isinstance(error, CheckFailure):
await ctx.send("Only staff members can use this command")
#list end
#list keys
#bot.command()
async def keys(ctx):
allkeys = db.keys()
await ctx.send(allkeys)
#list keys end
#clear
#bot.command()
#has_role(STAFF_ROLE_ID)
async def clear(ctx, to_clear):
print("command: clear")
str_userid = ctx.author.id
str_zoutigewolfid = "298516275322290188"
userid = int(str_userid)
zoutigewolfid = int(str_zoutigewolfid)
if userid == zoutigewolfid:
if to_clear in db.keys():
del db[to_clear]
await ctx.send("List " + to_clear + " has been cleared")
else:
await ctx.send("That list doesn't exist")
else:
await ctx.send("Only my daddy Wolf can use this command!")
#clear.error
async def clear_error(ctx, error):
if isinstance(error, CheckFailure):
await ctx.send("Only staff members can use this command")
#clear end
#quote
# preset {"title": "", "description": ""},
quotes_list = [
{"title": "A really big fucking hole coming right up!", "description": "Jordan 'Thermite' Trace"},
{"title": "Pass those plates around!", "description":"Julien 'Rook' Nizan"},
{"title": "You can stop worrying about grenades now!", "description": "Marius 'Jäger' Streicher"},
{"title": "if it runs on batteries, I'll see it!", "description": "Monica 'IQ' Weiss"},
{"title": "Fookin' laser sights!", "description": "Mike 'Thatcher' Baker"},
{"title": "Toxic babes are in position!", "description": "James 'Smoke' Porter"},
{"title": "Jammah in position!", "description": "Mark 'Mute' R. Chandar"},
{"title": "EDD primed, let them come!", "description": "Maxim 'Kapkan' Basuda"},
{"title": "Mine flying out!", "description": "Elżbieta 'Ela' Bosak"},
{"title": "You're mine bloody asshole!", "description": "Taina 'Caveira' Pereira"},
{"title": "Malbodan haengdong-iji (말보단 행동이지)", "description": "Chul 'Vigil' Kyung Hwa"},
{"title": "Launching override!", "description": "Grace 'Dokkaebi' Nam"},
{"title": "Camera in position!", "description": "Meghan 'Valkyrie' J. Castellano"},
{"title": "As my friend would say: A really big fucking hole coming right up.", "description": "Yumiko 'Hibana' Imagawa"},
{"title": "Big brother coming in for overwatch!", "description": "Olivier 'Lion' Flament"},
{"title": "Time for a wakeup call", "description": "Grace 'Dokkaebi' Nam"},
{"title": "No runnin' in the halls!", "description": "Morowa 'Clash' Evans"},
{"title": "LMG mounted and loaded!", "description": "Alexsandr 'Tachanka' Senaviev, RIP old tachanka"},
{"title": "Rtila active!", "description": "Jalal 'Kaid' El Fassi"},
{"title": "Now you see me, now you don't!", "description": "Elena 'Mira' María Álvarez"},
{"title": "Why do it yourself when robots do it better?", "description": "Masaru 'Echo' Enatsu"},
{"title": "Bullseye, got us a drone", "description": "Max 'Mozzie' Goose"},
]
#bot.command()
async def quote(ctx):
requester = ctx.author
requestermention = str(requester)
random_index = random.randint(0,21)
quote = quotes_list[random_index]
embed = discord.Embed(**quote, color=PURPLE)
embed.set_footer(text="Requested by " + requestermention + " | " + send_date)
await ctx.send(embed=embed)
#quote end
#operator profile
#bot.command()
async def profile(ctx, operator_name):
await ctx.send(embed=embeds[operator_name])
#operator profile end
#sexy
#bot.command()
async def sexy(ctx, person: discord.Member):
jager_role = discord.utils.get(ctx.guild.roles, id=JAGER_ROLE_ID)
sexyperson = person.mention
if jager_role in person.roles:
print("person was jager")
embed = discord.Embed(title="Sexy meter", description="Lord Jäger is 69420% sexy", color=PURPLE)
await ctx.send(embed=embed)
else:
print("person wasnt jager")
outputint = random.randint(0,100)
output = str(outputint)
embed = discord.Embed(title="Sexy meter", description=sexyperson + " is " + output + "% sexy", color=PURPLE)
await ctx.send(embed=embed)
#sexy end
#gun list
guns_list = [
"MP5",
"M4",
"AK-47",
"MP7",
"SCAR-L",
"Desert Eagle",
"MP5K",
"AUG A2",
"M249",
"AR15"
]
#title list
title_list = [
"Elimination",
"Homicide",
"Assasination",
"Drive-by",
"Sniped",
"Popped",
"Capped",
"No-scoped",
]
#shoot
#bot.command()
async def shoot(ctx, person: discord.Member):
print("command: shoot")
shooter = ctx.author.mention
shotperson = person.mention
shooterid = ctx.author.id
shotpersonid = person.id
randomnumber = random.randint(0,9)
randomgun = guns_list[randomnumber]
randomtitlenumber = random.randint(0,7)
title = title_list[randomtitlenumber]
if shooterid != shotpersonid:
embed = discord.Embed(title=title, description=shooter + " shot " + shotperson + " with " + randomgun, color=PURPLE)
await ctx.send(embed=embed)
else:
await ctx.send("Shooting yourself is a very dumb idea")
#shoot end
#anti nword
racist_words = [
"nigga",
"niggas",
"nigger",
"niggers",
"/Vigga",
"/Viggas",
"/Vigger",
"/Viggers",
"niga",
"nega",
"negga",
"nibba",
"/vigga",
"/viggas",
"/vigger",
"/viggers",
]
# racist detector 9000
"""
#bot.event
async def on_message(msg):
if any(word in msg.content for word in racist_words):
print("someone said the n word")
await msg.channel.send(msg.author.mention + " did you just seriously say the n-word?")
await msg.channel.send("Not poggers bro, not poggers")
await msg.channel.send(f"<#&{STAFF_ROLE_ID}> come get this bitch")
await msg.delete()
await bot.process_commands(msg)
"""
#racist end
#commands end
keep_alive()
bot.run(os.getenv("TOKEN"))
when i do the command j!nominate #discord mention it gives the error "Command raised an exception: TypeError: unhashable type: 'list'" but dont remember hashing any list. can someone please tell me why this is happening? the database is formatted like this ["1", "2", "3"] if you need to know
error:
Ignoring exception in command nominate:
Traceback (most recent call last):
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 85, in wrapped
ret = await coro(*args, **kwargs)
File "main.py", line 148, in nominate
await update_db(nominated_persons, nominated_person)
File "main.py", line 53, in update_db
db[key] = [value]
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/replit/database/database.py", line 182, in __setitem__
r = self.sess.post(self.db_url, data={key: j})
TypeError: unhashable type: 'list'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/bot.py", line 902, in invoke
await ctx.command.invoke(ctx)
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 864, in invoke
await injected(*ctx.args, **ctx.kwargs)
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 94, in wrapped
raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: TypeError: unhashable type: 'list'
Untested but try the following
async def update_db(key, value):
if key in db.keys():
db[key].append(value)
else:
db[key] = [value]
Can you see why you were getting the TypeError by comparing my code with yours?
Ok, thanks for updating the question with the full code and traceback.
The offending line is db[key] = [value] that throws a TypeError:unhashable type list,
which means you passed a list type as key argument for the update_db function.
That cannot be done because, as the traceback clearly states, you cannot hash a list type (meaning you cannot access a dictionary value using a key that is a list type).
But the real source of the pain is right above that line:
if key in db.keys():
key = db[key]
key.append(value)
db[key] = key
That's a lot of key, isn't it? The problem here is that you used the variable name key referring to different things and you ended up with a bit of confusion, so I suggest you use different variables to indicate different things:
async def update_db(key, value):
if key in db.keys():
old_value = db[key]
old_value.append(value)
db[key] = old_value
else:
db[key] = [value]
In this way it is the value that is actually updated in the database and not the key, so the latter would never end up being a list.
This last one is just out of curiosity. Why are you defining a function to update the database, aren't there defined methods to do that? What db are you using?
I FOUND THE ANSWER!
the problem was with await update_db(nominated_persons, nominated_person)
as it is like that, it doesn't update the db because nominated_persons is undefined, so i defined it with nominated_persons = db["nominated_persons"]. but then i would get the hash error because that is a list and i cant use that, so i just did nominated_persons = "nominated_persons" so the nominated_persons is defined as the word "nominated_persons". so now it works
i feel so stupid
here is the updated code:
#bot.command()
async def nominate(ctx, nominatedperson: discord.Member):
print("command: nominate")
nominated_person = nominatedperson.mention
nominated_persons = "nominated_persons"
await update_db(nominated_persons, nominated_person)
await ctx.send("whatever")
(Side note: you can just put “nominated_persons” in the update_db command you don’t have to refer to it with a variable like i did.)
My discord bot raises TypeError when I give a youtube url in the play command.
It works when I give a music name but doesn't work when I give a url like https://youtu.be/ylVnYh-b3Qg......
I could not understand where the problem is...
Is it in my play command or the MusicSource....
Thanks in advance for your kind help.
This is the code I have written:
class MusicSource(discord.PCMVolumeTransformer):
youtube_dl.utils.bug_reports_message = lambda: '' # Suppressing Random Youtube_Dl warnings
ytdl_format_options = { # options for the youtube_dl module, for the audio quality
'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': 'ytsearch', # We will use Youtube for searching musics
'source_address': '0.0.0.0', # IPv6 causes problem sometimes so we are using IPv4
'youtube_include_dash_manifest': False # Turning off dash manifest
# (Enabling it sometimes makes the bot not play anything)
}
ytdl = youtube_dl.YoutubeDL(ytdl_format_options) # Sending the options to the Youtube_DL class
def __init__(self, source: discord.AudioSource, *, data):
self.volume = 0.4
super().__init__(source, self.volume)
self.data = data
self.title = data.get('title')
self.url = data.get('url')
self.link = data.get('webpage_url') # Fetching the youtube link for the song and
# will be used later for sending it to the user
self.time = data.get('duration')
self.img = data.get('thumbnail')
self.artist = data.get('artist')
self.likes = data.get('like_count')
self.dislikes = data.get('dislike_count')
self.albm = data.get('album')
#classmethod
async def from_url(cls, url, *, loop=None, stream=False, timestamp=0): # The player that plays the
# audio from the url
ffmpeg_options = {
'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
'options': f'-vn -ss {timestamp}'
}
loop = loop or asyncio.get_event_loop()
data = await loop.run_in_executor(None, lambda: cls.ytdl.extract_info(url, download=not stream))
if 'entries' in data:
data = data['entries'][0] # Taking the first item from the search results
filename = data['url'] if stream else cls.ytdl.prepare_filename(data)
return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data)
And the join and play command:
class Music(commands.Cog):
def __init__(self, client):
self.client = client
self.player = dict()
self.links = dict()
self.auto_loop_is_on = dict()
#commands.command(name="join", aliases=["j", "connect", "come", "fuck-on"])
async def join_voice(self, ctx: commands.Context):
voice_channel = ctx.author.voice
if voice_channel is None:
return await ctx.send("`Please connect to a voice channel first!`")
if ctx.voice_client is not None:
await ctx.voice_client.move_to(voice_channel.channel)
return await ctx.send("`Voice channel shifted!`")
elif ctx.voice_client is None:
self.player[str(ctx.guild.id)] = None
self.links[str(ctx.guild.id)] = list()
self.auto_loop_is_on[str(ctx.guild.id)] = False
await voice_channel.channel.connect()
return await ctx.send("`Voice channel connected! Use <play> command to play a music`")
#commands.command(name="play", aliases=["p"])
async def play_(self, ctx: commands.Context, timestamp: Optional[int] = 0, *, url):
if ctx.voice_client is None or ctx.author.voice is None:
return await ctx.send("`Not connected to any voice!`")
if ctx.voice_client.is_playing():
self.links[str(ctx.guild.id)].append(url)
await ctx.send(f"`Song {url} added to queue. `")
elif not ctx.voice_client.is_playing():
self.links[str(ctx.guild.id)].append(url)
await ctx.send("`Loading awesomeness! Wait a bit. `")
async with ctx.typing():
self.player[str(ctx.guild.id)] = await MusicSource.from_url(url=self.links[str(ctx.guild.id)][0],
loop=self.client.loop, stream=True,
timestamp=timestamp)
ctx.voice_client.play(self.player[str(ctx.guild.id)], after=lambda x: self.play_next(ctx))
And the Error I get:
Ignoring exception in on_command_error
Traceback (most recent call last):
File "C:\Users\Ronny\Python3.9\lib\site-packages\discord\ext\commands\core.py", line 85, in wrapped
ret = await coro(*args, **kwargs)
File "C:\Users\Ronny\PycharmProjects\XENON_DISCORD\Bot_Features\music.py", line 108, in play_
ctx.voice_client.play(self.player[str(ctx.guild.id)], after=None)
File "C:\Users\Ronny\Python3.9\lib\site-packages\discord\voice_client.py", line 561, in play
raise TypeError('source must an AudioSource not {0.__class__.__name__}'.format(source))
TypeError: source must an AudioSource not NoneType
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Users\Ronny\Python3.9\lib\site-packages\discord\client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "C:\Users\Ronny\PycharmProjects\XENON_DISCORD\XENON.py", line 82, in on_command_error
raise err
File "C:\Users\Ronny\Python3.9\lib\site-packages\discord\ext\commands\bot.py", line 902, in invoke
await ctx.command.invoke(ctx)
File "C:\Users\Ronny\Python3.9\lib\site-packages\discord\ext\commands\core.py", line 864, in invoke
await injected(*ctx.args, **ctx.kwargs)
File "C:\Users\Ronny\Python3.9\lib\site-packages\discord\ext\commands\core.py", line 94, in wrapped
raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: TypeError: source must an AudioSource not NoneType
Try this out, it should work for both a YouTube song name and a YouTube URL:
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))