Play function in discord.py throwing errors - python

I am trying to make a music bot for my server.
everything is working fine like the bot connecting to the channel but it is showing this error after joining the channel.
my code looks like this.
import discord
from discord.ext import commands
from random import choice
import youtube_dl
import asyncio
import urllib.parse, urllib.request, re
client = commands.Bot(command_prefix=commands.when_mentioned_or(">"), description='Relatively simple music bot example')
def colour():
l = [
1752220, 3066993, 3447003, 10181046, 15844367,
15105570, 15158332, 3426654, 1146986, 2067276,
2123412, 7419530, 12745742, 11027200, 10038562,
2899536, 16580705, 12320855
]
return choice(l)
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'
}
ffmpeg_options = {'options': '-vn'}
ytdl = youtube_dl.YoutubeDL(ytdl_format_options)
class YTDLSource(discord.PCMVolumeTransformer):
def __init__(self, source, *, data, volume=1):
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,
before_options= '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5'),
data=data
)
async def join(ctx):
author = ctx.author.voice
if not author:
em = discord.Embed(
title = 'Opps! 😥',
description = 'You are not connected to any voice channel. \nTry again after connecting to a voice channel.',
colour = discord.Colour.red()
)
await ctx.send(embed=em)
return 1
else:
author = ctx.author.voice.channel
voiceClient = ctx.voice_client
if not voiceClient:
await author.connect()
else:
await ctx.send("**Already connected to :**" + author)
return 0
async def search(ctx, url=""):
if 'http://www.youtube.com' in url:
return url
else:
l = url.split(' ')
j = ''
for i in l:
j += i
j += '+'
url = j
htm_content = urllib.request.urlopen(
'http://www.youtube.com/results?search_query=' + url
)
search_results = re.findall(r"watch\?v=(\S{11})", htm_content.read().decode())
std = 'http://www.youtube.com/watch?v='
url = str(std) + str(search_results[0])
return url
class Music(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.command(aliases=["p"])
async def play(self, ctx, *, url=""):
w = await join(ctx)
if w == 1:
print("User not connected")
else:
if url == '':
embed = discord.Embed(
title = 'Opps! 😥',
description = "You didn't specified any song to play. \nPlease try again but this time specify a song name or url to play.",
colour = discord.Colour.orange()
)
await ctx.send(embed=embed)
else:
url = await search(ctx, url)
async with ctx.typing():
player = await YTDLSource.from_url(url, loop= ctx.bot.loop, stream=True) # before_options='-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5'
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))
client.add_cog(Music(client))
client.run(os.environ['token'])
I don't know what's the problem cause i coded this bot on repl.it and when i tried running it from it's shell everything is working fine but when i hosted this to heroku this error is coming.
Can anyone help.
Thanks for the help in advance 😊

ffmpeg has to exist as an executable binary file on the system you are trying to run your discord bot on. Including the ffmpeg-python library only links ffmpeg and python together, because it's only a binder between to two.
Make sure to download and install ffmpeg for your environment, then you can use ffmpeg-python library to do stuff in your bot's python code.
Installation does vary depending on what OS, so make sure you follow steps accordingly.

Related

"Failed to generate image" when using custom Discord bot that uses DALL-E to generate images

I am trying to build a Discord bot that takes the /create and generates images using the OpenAI DALL-E model, based on the user's input. However, after the bot prompts the message "What text would you like to generate an image for?", I input the text, but I get the error "Failed to generate image". What can I do? Thank you!
This is code that functions:
import os
import json
import requests
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.members = True
bot = commands.Bot(command_prefix='!', intents=intents)
#bot.command(name='create')
async def create_image(ctx):
await ctx.send("What text would you like to generate an image for?")
def check(m):
return m.author == ctx.author and m.channel == ctx.channel
try:
text = await bot.wait_for('message', check=check, timeout=60.0)
except asyncio.TimeoutError:
await ctx.send("Timed out, please try again.")
return
text = text.content
api_key = os.environ.get("API_KEY")
headers = {"Content-Type": "application/json"}
data = """
{
"""
data += f'"model": "image-alpha-001",'
data += f'"prompt": "{text}",'
data += """
"num_images":1,
"size":"1024x1024",
"response_format":"url"
}
"""
resp = requests.post(
"https://api.openai.com/v1/images/generations",
headers=headers,
data=data,
auth=("", api_key),
)
if resp.status_code != 200:
await ctx.send("Failed to generate image.")
return
response_text = json.loads(resp.text)
image_url = response_text['data'][0]['url']
await ctx.send(image_url)
bot.run('TOKEN')

youtubedl stuck on 'downloading webpage' in python music bot

I have everything setup, and everything installed properly. When I try to run the bot with !play command, the traceback is just on downloading web page. no errors at all. I'm suspecting it might be an error with the source, but I am not sure.
#commands.command()
async def play(self, ctx, url):
if ctx.voice_client is None:
if ctx.author.voice:
await ctx.author.voice.channel.connect()
else:
await ctx.send("You're not connected to a voice channel.")
raise commands.CommandError("Author not connected to voice channel.")
elif ctx.voice_client.is_playing():
ctx.voice_client.stop()
FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnected_delay_max 5', 'options': '-vn'}
YDL_OPTIONS = {'format':"bestaudio"}
vc = ctx.voice_client
with youtube_dl.YoutubeDL(YDL_OPTIONS) as ydl:
info = ydl.extract_info(url, download=False)
url2 = info["formats"][0]['url']
source = await discord.FFmpegOpusAudio.from_probe(url2, **FFMPEG_OPTIONS)
vc.play(source)
I changed your code a little bit.
You will need the YoutubeSearch module
pip install youtube-search
from youtube_search import YoutubeSearch
#client.command()
async def play(ctx,*, song):
voice_state = ctx.member.voice
if voice_state == None:
await ctx.send("You are not in a voice channel!")
return
if voice_state != None:
await ctx.author.voice.channel.connect()
results = YoutubeSearch(song, max_results=1).to_dict()
print(results)
song_name = results[0]['title']
url_suffix = results[0]['url_suffix']
url = "https://youtube.com"+url_suffix
playinge = discord.Embed(
title = "Now Playing",
description = f"[{song_name}]({url})",
color = discord.Colour.green()
)
ydl_opts = {'format': 'bestaudio'}
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
URL = info['formats'][0]['url']
vc = ctx.voice_client
vc.play(discord.FFmpegPCMAudio(URL))
await ctx.send(embed=playinge)
Try this.

discord.py bot random image from a folder but each file can only be shown once

I'm fairly new to python and I can't seem to figure out how to make this work. any help?
I'm trying to make a command that makes my discord bot display a random image from a folder. There are around 2000 images in the folder. I want everyone to be able to use the command only once. The images are all unique and I don't want people getting the same image. how would I do this?
# Import Discord Package
import discord
from discord.ext import commands
# Client
client = commands.Bot(command_prefix='/')
# Display random image
# Command that shows Mintbot's version
#client.command(name='version')
async def version(context):
myEmbed = discord.Embed(title="Mintbot", description="Test version", color=800080)
myEmbed.add_field(name="Version Code", value="v1.0.0", inline=False)
myEmbed.set_footer(text="Work In Progress!")
await context.message.channel.send(embed=myEmbed)
# Welcome Message
#client.event
async def on_ready():
general_channel = client.get_channel(931259149260554283)
await general_channel.send('Hello, Mintbot here!')
# Disconnect message
#client.event
async def on_disconnect():
general_channel = client.get_channel(931259149260554283)
await general_channel.send('Turning Off')
# Mintbot Version - Embed
#client.event
async def on_message(message):
if message.content == 'what version is mintbot?':
general_channel = client.get_channel(931259149260554283)
myEmbed = discord.Embed(title="Mintbot", description="Test version", color=800080)
myEmbed.add_field(name="Version Code", value="v1.0.0", inline=False)
myEmbed.set_footer(text="Work In Progress!")
await general_channel.send(embed=myEmbed)
await client.process_commands(message)
# Run the client on the server
client.run('mytoken')
Make sure that you have a json file named " list.json ".
import discord
from discord.ext import commands
import os
import json
import random
# Client
client = commands.Bot(command_prefix='/')
# Reading images
directory = "images" # Change this to your folder directory without / on the end
img = []
total_img = -1
for filename in os.listdir(directory):
img += [filename]
total_img += 1
# Display random image
#client.command()
async def image(context):
with open("list.json", "r") as f:
sent = json.load(f)
if not str("sent") in sent:
sent = {}
sent["sent"] = []
with open("list.json", "w") as f:
json.dump(sent, f , indent=4)
with open("list.json", "r") as f:
sent = json.load(f)
if not str(context.author.id) in sent:
sent[str(context.author.id)] = True
with open("list.json", "w") as f:
json.dump(sent, f , indent=4)
data = sent["sent"]
random_number = None
tries = 0
while random_number == None:
tries += 1
number = random.randint(0, total_img)
if not number in data:
random_number = number
if int(tries + 1) == total_img:
await context.send("All pictures has been sent")
return
file = discord.File(f"{directory}/{img[random_number]}")
await context.send(file=file)
sent["sent"] += [random_number]
with open("list.json", "w") as f:
json.dump(sent, f , indent=4)
# Command that shows Mintbot's version
#client.command(name='version')
async def version(context):
myEmbed = discord.Embed(title="Mintbot", description="Test version", color=800080)
myEmbed.add_field(name="Version Code", value="v1.0.0", inline=False)
myEmbed.set_footer(text="Work In Progress!")
await context.message.channel.send(embed=myEmbed)
# Welcome Message
#client.event
async def on_ready():
general_channel = client.get_channel(931259149260554283)
await general_channel.send('Hello, Mintbot here!')
# Disconnect message
#client.event
async def on_disconnect():
general_channel = client.get_channel(931259149260554283)
await general_channel.send('Turning Off')
# Mintbot Version - Embed
#client.event
async def on_message(message):
if message.content == 'what version is mintbot?':
general_channel = client.get_channel(931259149260554283)
myEmbed = discord.Embed(title="Mintbot", description="Test version", color=800080)
myEmbed.add_field(name="Version Code", value="v1.0.0", inline=False)
myEmbed.set_footer(text="Work In Progress!")
await general_channel.send(embed=myEmbed)
await client.process_commands(message)
# Run the client on the server
client.run(token)

python discord music bot youtube_dl

import discord
import youtube_dl
from discord.ext import commands
-----------------------------------------------
#cat.command(pass_context=True)
async def play(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
await channel.connect()
server = ctx.message.guild
voice_channel = server.voice.client
async with ctx.typing():
player = await YTDLSource.from_url(url, loop = client.loop)
voice_channel.play(player)
await ctx.send(f'**Music:**{player.title}')
Is there any way to fix this error?
AttributeError: 'Guild' object has no attribute 'voice'
Check this working example.
import discord
import youtube_dl
from discord.ext import commands
ydl_opts = {
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}],
}
def endSong(guild, path):
os.remove(path)
#cat.command(pass_context=True)
async def play(ctx, url):
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
voice_client = await channel.connect()
guild = ctx.message.guild
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
file = ydl.extract_info(url, download=True)
path = str(file['title']) + "-" + str(file['id'] + ".mp3")
voice_client.play(discord.FFmpegPCMAudio(path), after=lambda x: endSong(guild, path))
voice_client.source = discord.PCMVolumeTransformer(voice_client.source, 1)
await ctx.send(f'**Music: **{url}')
Optional, useful function
If you want you can make your bot leave the voice channel after the song stops playing. Add this at the end of your code:
while voice_client.is_playing():
await asyncio.sleep(1)
else:
await voice_client.disconnect()
print("Disconnected")
Try to replace if not ctx.message.author.voice: with if 'voice' not in ctx.message.author:

Discord bot does not accept commands when other command is running

I'm developing a discord bot that can find spotify playlists and queue the tracks' youtube counterparts. I have a loop running that uses a list of tracknames to search for a video on youtube, it then grabs the top result and sends to an asynchronous function that plays it. However, whilst the bot is in this loop it does not accept other commands. Can I make this run concurrently with other commands? The following is all the code of my application
from bs4 import BeautifulSoup
import urllib.request
import os
import requests
import base64
import json
import time
import discord
import shutil
from discord.utils import get
from discord import FFmpegPCMAudio
import youtube_dl
import asyncio
from asgiref.sync import async_to_sync
from discord.ext import commands
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
bot = commands.Bot(command_prefix='s- ')
accessToken = ""
clientIDSECRET = os.getenv('SPOTIFY_ID')+':'+os.getenv('SPOTIFY_SECRET')
base64Auth = base64.b64encode(clientIDSECRET.encode("utf-8")).decode('ascii')
trackStrings = []
def pretty_print_POST(req):
print('{}\n{}\r\n{}\r\n\r\n{}'.format(
'-----------START-----------',
req.method + ' ' + req.url,
'\r\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()),
req.body,
))
#bot.event
async def on_ready():
payload={'grant_type':'client_credentials'}
headers = {'Authorization':f'Basic {base64Auth}'}
req = requests.Request('POST', "https://accounts.spotify.com/api/token", data = payload, headers = headers)
prep = req.prepare()
pretty_print_POST(prep)
s = requests.Session()
response = s.send(prep)
global accessToken
accessToken = json.loads(response.content)["access_token"]
print(accessToken)
#bot.command(pass_context=True, aliases=['sp'])
async def spotlist(ctx, userName, playlistName = "", shuffle=False, limit=100, offset=0):
print(limit)
if limit > 100 or limit < 0:
await ctx.send(f'Limit out of bounds! It needs to be between 0 and 100.')
return
playlistId = ""
headers = {'Authorization':f'Bearer {accessToken}'}
if playlistName == "id":
playlistId = userName #Username assumed to be a playlist id instead
else:
playlists = json.loads(requests.get(f'https://api.spotify.com/v1/users/{userName}/playlists', headers=headers).content)
for playlist in playlists["items"]:
if playlist["name"] == playlistName:
playlistId = playlist["id"]
nextURL = f'https://api.spotify.com/v1/playlists/{playlistId}/tracks?offset={offset}&limit={limit}'
while nextURL != None:
trackResponse = requests.get(nextURL, headers = headers)
tracks = json.loads(trackResponse.content)
if(tracks["total"] <= offset):
await ctx.send(f'Offset (third argument) is too large! Your playlist is {tracks["total"]} long.')
return
for track in tracks["items"]:
trackStrings.append(track["track"]["name"] + " " + track["track"]["artists"][0]["name"])
nextURL = tracks["next"]
if(limit != 100):
break
for trackString in trackStrings:
try:
await play(ctx, await SearchVid(trackString))
except:
print("couldn't find song")
#bot.command(pass_context=True, aliases=['j', 'joi'])
async def join(ctx):
channel = ctx.message.author.voice.channel
voice = get(bot.voice_clients, guild=ctx.guild)
if voice and voice.is_connected():
await voice.move_to(channel)
else:
voice = await channel.connect()
#bot.command(pass_context=True, aliases=['l', 'lea'])
async def leave(ctx):
voice = get(bot.voice_clients, guild=ctx.guild)
if voice and voice.is_connected():
await voice.disconnect()
else:
await ctx.send("Don't think I am in a voice channel")
async def playSong(ctx):
voice = get(bot.voice_clients, guild=ctx.guild)
DIR = os.path.abspath(os.path.realpath("Queue"))
try:
first_file = os.listdir(DIR)[0]
except:
print("No more queued song(s)\n")
return
song_path = os.path.abspath(os.path.realpath("Queue") + "\\" + first_file)
async def func(x):
os.remove(song_path)
await playSong(ctx)
try:
voice.play(discord.FFmpegPCMAudio(song_path), after=async_to_sync(func))
await ctx.send(f"playing {first_file}")
voice.source = discord.PCMVolumeTransformer(voice.source)
voice.source.volume = 0.07
except:
print("song already playing")
still_q = len(os.listdir(DIR))
print(f"Songs still in queue: {still_q}")
#bot.command(pass_context=True, aliases=['p', 'pla'])
async def play(ctx, url: str = ""):
await join(ctx)
Queue_infile = os.path.isdir("./Queue")
if Queue_infile is True:
DIR = os.path.abspath(os.path.realpath("Queue"))
try:
_ = os.listdir(DIR)[0]
except:
print("No more queued song(s)\n")
await queue(ctx, url)
await playSong(ctx)
else:
await queue(ctx, url)
await playSong(ctx)
else:
return
#bot.command(pass_context=True, aliases=['s'])
async def skip(ctx):
await stop(ctx)
#bot.command(pass_context=True, aliases=['pa', 'pau'])
async def pause(ctx):
voice = get(bot.voice_clients, guild=ctx.guild)
if voice and voice.is_playing():
print("Music paused")
voice.pause()
await ctx.send("Music paused")
else:
print("Music not playing failed pause")
await ctx.send("Music not playing failed pause")
#bot.command(pass_context=True, aliases=['r', 'res'])
async def resume(ctx):
voice = get(bot.voice_clients, guild=ctx.guild)
if voice and voice.is_paused():
print("Resumed music")
voice.resume()
await ctx.send("Resumed music")
else:
print("Music is not paused")
await ctx.send("Music is not paused")
async def stop(ctx):
voice = get(bot.voice_clients, guild=ctx.guild)
if voice and voice.is_playing():
voice.stop()
else:
print("No music playing failed to stop")
async def SearchVid(textToSearch):
print(textToSearch)
query = urllib.parse.quote(textToSearch)
url = "https://www.youtube.com/results?search_query=" + query
response = urllib.request.urlopen(url)
html = response.read()
soup = BeautifulSoup(html, 'html.parser')
for vid in soup.findAll(attrs={'class':'yt-uix-tile-link'}):
if not vid['href'].startswith("https://googleads.g.doubleclick.net/"):
return 'https://www.youtube.com' + vid['href']
async def queue(ctx, url: str):
Queue_infile = os.path.isdir("./Queue")
if Queue_infile is False:
os.mkdir("Queue")
DIR = os.path.abspath(os.path.realpath("Queue"))
q_num = len(os.listdir(DIR))
queue_path = os.path.abspath(os.path.realpath("Queue") + f"\\{q_num} %(title)s.%(ext)s")
print(queue_path)
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': queue_path,
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}],
}
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
print("Downloading audio now\n")
print(url)
ydl.download([url])
name = os.listdir(DIR)[-1]
await ctx.send("Adding song " + str(name) + " to the queue")
print("Song added to queue\n")
bot.run(TOKEN)
I would definitely suggest using the module threading. I do not currently have enough time to read your code and put it in, though I might edit this answer later.
Refer to:
https://docs.python.org/3/library/threading.html
Threading allows you to run multiple functions at once.
You're using blocking libraries. Discord.py is a asynchronous library and you need to use things that don't block your code. In Frequently Asked Questions you can see a example using requests library, that's blocking and you should use aiohttp to make asynchronous requests instead.

Categories