Adding a queue command to discord.py music bot - python

I'm looking for a way to add the following to my music bot:
A queue function, allowing songs to be inputted while another plays. This would then go through the play function after the first song ends
Being able to display this queue
Removing songs from/changing positions of the songs in the queue
How would I go about doing this? Below is my code.
from discord.ext import commands
from dotenv import load_dotenv
from keep_alive import keep_alive
from youtube_dl import YoutubeDL
from discord.utils import get
load_dotenv()
TOKEN = os.getenv('TOKEN')
bot = commands.Bot(command_prefix='!', case_insensitive=True)
#bot.event
async def on_ready():
print(f'{bot.user} has successfully connected to Discord!')
#bot.command(aliases=['p'])
async def play(ctx, url: str = None):
YDL_OPTIONS = {'format': 'bestaudio/best', 'noplaylist':'True'}
FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
voice = get(bot.voice_clients, guild=ctx.guild)
try:
channel = ctx.message.author.voice.channel
except AttributeError:
await ctx.send('Bruh, join a voice channel')
return None
if not voice:
await channel.connect()
await ctx.send("Music/Audio will begin shortly, unless no URL provided.")
voice = get(bot.voice_clients, guild=ctx.guild)
with YoutubeDL(YDL_OPTIONS) as ydl:
info = ydl.extract_info(url, download=False)
I_URL = info['formats'][0]['url']
source = await discord.FFmpegOpusAudio.from_probe(I_URL, **FFMPEG_OPTIONS)
voice.play(source)
voice.is_playing()
await ctx.send("Playing audio.")
#bot.command()
async def pause(ctx):
voice = discord.utils.get(bot.voice_clients, guild=ctx.guild)
if voice.is_playing():
voice.pause()
await ctx.send("Audio is paused.")
if voice.is_not_playing():
await ctx.send("Currently no audio is playing.")
#bot.command()
async def resume(ctx):
voice = discord.utils.get(bot.voice_clients, guild=ctx.guild)
if voice.is_paused():
voice.resume()
await ctx.send("Resumed audio.")
if voice.is_not_paused():
await ctx.send("The audio is not paused.")
#bot.command()
async def stop(ctx):
voice = discord.utils.get(bot.voice_clients, guild=ctx.guild)
voice.stop()
await ctx.send("Stopped playback.")
#bot.command(aliases=['l'])
async def leave(ctx):
voice = discord.utils.get(bot.voice_clients, guild=ctx.guild)
await voice.disconnect()
await ctx.send("Left voice channel.")
keep_alive()
bot.run(TOKEN

first you need to create an empty queue outside your command
queues = {}
and a function to check if there's a song in queues it will play that song
#check queue
queues = {}
def check_queue(ctx, id):
if queues[id] !={}:
voice = ctx.guild.voice_client
source = queues[id].pop(0)
voice.play(source, after=lambda x=0: check_queue(ctx, ctx.message.guild.id))
and inside the play command create a function to add the next song to queue if a song is already being played and add the check queue function
if voice.is_playing():
guild_id = ctx.message.guild.id
if guild_id in queues:
queues[guild_id].append(source)
else:
queues[guild_id] = [source]
else:
voice.play(source, after=lambda x=0: check_queue(ctx, ctx.message.guild.id))
This way is to create a queue and play the next only and still cant display or change the queue
Need help Discord bot queue

Related

Discord bot / commands not showing up

When I type / in my Discord server chat, the commands don't show up. I tried everything and don't know why it won't work. Here is the code of the bot:
import discord
import asyncio
from discord.ext import commands, tasks
import os
import random
from discord.ext import commands
from discord.utils import get
from discord import FFmpegPCMAudio
from discord import TextChannel
from youtube_dl import YoutubeDL
import sys
import spotipy
import spotipy.util as util
import youtube_dl
intents = discord.Intents.all()
bot = commands.Bot(command_prefix=".", intents=intents)
# Set the intents for the bot
intents.members = True
intents.presences = True
intents.typing = True
intents.message_content = True
client = commands.Bot(command_prefix='.', intents=intents)
audio_data = None
CLIENT_ID = "YOUR_ID"
CLIENT_SECRET = "SECRET"
REDIRECT_URI = "http://localhost:8888/callback"
USERNAME = "your-username"
scope = "user-read-private user-read-playback-state user-modify-playback-state"
token = util.prompt_for_user_token(USERNAME, scope, client_id=CLIENT_ID, client_secret=CLIENT_SECRET, redirect_uri=REDIRECT_URI)
spotify_api = spotipy.Spotify(auth=token)
players = {}
#client.event # check if bot is ready
async def on_ready():
print('Bot online')
#client.command()
async def entra(ctx):
channel = ctx.message.author.voice.channel
voice = get(client.voice_clients, guild=ctx.guild)
if voice and voice.is_connected():
await voice.move_to(channel)
else:
voice = await channel.connect()
#client.command(name='leave', aliases=['esci', 'quit'], pass_context=True)
async def leave(ctx):
voice_client = ctx.voice_client
if voice_client is not None:
await voice_client.disconnect()
await ctx.send('๐Ÿค™')
else:
await ctx.send('๐Ÿ˜')
#client.command(name='avvia', aliases=['ascolta', 'play'], description="riproduce link youtube")
async def play(ctx, url):
channel = ctx.message.author.voice.channel
voice = get(client.voice_clients, guild=ctx.guild)
if voice and voice.is_connected():
await voice.move_to(channel)
else:
voice = await channel.connect()
YDL_OPTIONS = {'format': 'bestaudio', 'noplaylist': 'True'}
FFMPEG_OPTIONS = {
'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
voice = get(client.voice_clients, guild=ctx.guild)
# Check if the given url is a Spotify track or playlist
if "open.spotify.com/track/" in url:
# Extract the track id from the url
track_id = url.split("/")[-1]
# Get the track data from Spotify
track_data = spotify_api.track(track_id)
# Set the audio data to the track's preview url
audio_data = track_data["preview_url"]
await ctx.send("รจ possibile solo sentire i 30 secondi di preview di una canzone tramite link di spotify perche spotify รจ stronzo ๐Ÿ˜")
elif "open.spotify.com/playlist/" in url:
# Extract the playlist id from the url
playlist_id = url.split("/")[-1]
# Get the playlist data from Spotify
playlist_data = spotify_api.playlist(playlist_id)
# Get the playlist's track data
track_data = spotify_api.playlist_tracks(playlist_id)
# Set the audio data to the first track's preview url
audio_data = track_data[0]["preview_url"]
await ctx.send(
"รจ possibile solo sentire i 30 secondi di preview di una canzone tramite link di spotify perche spotify รจ stronzo ๐Ÿ˜")
elif "youtube.com" in url:
# The url is not a Spotify track or playlist, so use YoutubeDL to extract the audio data
with YoutubeDL(YDL_OPTIONS) as ydl:
info = ydl.extract_info(url, download=False)
audio_data = info['url']
else:
await ctx.send('๐Ÿ˜')
if not voice.is_playing():
# Play the audio data
voice.play(FFmpegPCMAudio(audio_data, **FFMPEG_OPTIONS))
voice.is_playing()
if ctx.message.author == "Aq3ila":
await ctx.send("musica di merda incoming ๐Ÿ’€๐Ÿ’€๐Ÿ’€")
else:
await ctx.send("๐ŸŽต")
return
else:
await ctx.send("impara ad'aspettare")
# command to resume voice if it is paused
#client.command()
async def riavvia(ctx):
voice = get(client.voice_clients, guild=ctx.guild)
if not voice.is_playing():
voice.resume()
await ctx.send('riavviando')
# command to pause voice if it is playing
#client.command()
async def pausa(ctx):
voice = get(client.voice_clients, guild=ctx.guild)
if voice.is_playing():
voice.pause()
await ctx.send('messo in pausa')
# command to stop voice
#client.command(name='ferma', aliases=['stop', 'annulla'], description="ferma la canzone in riproduzione")
async def stop(ctx):
voice = get(client.voice_clients, guild=ctx.guild)
if voice.is_playing():
voice.stop()
await ctx.send('๐Ÿ’ฅ๐Ÿ’ข๐Ÿ’ฅ๐Ÿ’ข')
client.run("YOUR_TOKEN")
I don't know why it won't work.
First of all, you've included your Bot Token in the bot.run(). Delete that and reset your token right now.
Next: All of your commands are text commands, i.e. They're triggered when a message has the bot prefix in it.
Slash commands are integrated into the Discord client itself. They don't need a message to trigger them but can be run directly from the client.
I'll show you the method I use to make slash commands, but there are other ways to do this.
To make a slash command, first have this in your on_ready:
#bot.event
async def on_ready():
#sync your commands and saves information about them in a var
synced = await bot.tree.sync()
#print how many commands were synced.
print(f"Synced {len(synced)} command(s)")
Afterwards, create commands with the #bot.tree.command decorator.
For example:
#bot.tree.command(name="command_nam", description="command_description")
async def unique_command_name(interaction: discord.Interaction):
#code here
Here, instead of Context or ctx, you're using the Interaction class. You can check out how Interactions work in the docs.
Edit: as stated in a comment down below, don't do this with bot's that have a lot of command that you restart often. It's just an alternative method for bot's that aren't general-purpose.

Music with Hikari

My music bot built with hikari is unable to join. I have copied the code that I had used from discord.py. It worked properly on discord.py but wont work on hikari for some reason.
Here's the code:
#bot.command
#lightbulb.command('join', 'Makes bot join a voice channel')
#lightbulb.implements(lightbulb.PrefixCommand)
async def join(ctx):
try:
channel = ctx.message.author.voice.channel
await channel.connect()
await ctx.respond("I\'m in")
except:
await ctx.respond("You need to be connected to a voice channel in order for me to join")
#bot.command
#lightbulb.command('play', 'Plays track in connected voice channel')
#lightbulb.implements(lightbulb.PrefixCommand)
async def play(ctx, q: str):
YDL_OPTIONS = {'default_search': 'auto', 'format': 'bestaudio', 'noplaylist':'True'}
FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
voice = ctx.message.guild.voice_client
with YoutubeDL(YDL_OPTIONS) as ydl:
try:
info = ydl.extract_info(q, download=False)
a = info['entries'][0]['formats'][0]['url']
queue.append(a)
if voice.is_playing():
await ctx.respond('I have queued the track')
else:
idk = queue[0]
queue.pop(0)
voice.play(FFmpegPCMAudio(idk, **FFMPEG_OPTIONS))
except:
info = ydl.extract_info(q, download=False)
a = info['url']
queue.append(a)
if voice.is_playing():
await ctx.respond('I have queued the track')
elif len(queue)==0:
idk = queue[0]
voice.play(FFmpegPCMAudio(idk, **FFMPEG_OPTIONS))
else:
idk = queue[0]
queue.pop(0)
voice.play(FFmpegPCMAudio(idk, **FFMPEG_OPTIONS))
Your command implementation is not the way that Lightbulb handles commands. Command arguments are not passed as arguments to the callback function, they are accessed via ctx.options.your_option_here.
Reference: The option decorator
Example command that accepts an argument:
#bot.command
#lightbulb.option("q", "The track to play.")
#lightbulb.command("play", "Plays track in connected voice channel.")
#lightbulb.implements(lightbulb.PrefixCommand)
async def play(ctx: lightbulb.Context) -> None:
await ctx.respond(f"The song you want to play is {ctx.options.q}!")
Many of the attributes/methods you are using like ctx.message.guild.voice_client, ctx.message.author.voice.channel, and await channel.connect() just do not exist in Hikari.
You can't transfer your discord.py code over 1:1.Hikari does implement the discord voice API, however you will need to read the documentation.
Connecting to a voice channel:
await bot.update_voice_state(ctx.guild_id, channel_id)
Disconnecting from a voice channel:
await bot.update_voice_state(ctx.guild_id, None)

I am trying to make a bot that plays an mp3 file by a command but it doesn't work with no error

I made so it connects to the voice channel by the command .join and it supposed to play a sound that i saved. This is made for a voice channel with chill music so that they don't have to copy and paste a YouTube link. I am using python 3.9
import discord
from discord.ext import commands
bot = discord.Client()
bot = commands.Bot(command_prefix=".")
#bot.command()
async def join(ctx):
if ctx.author.voice is None:
await ctx.send("You are not in a voice channel")
voice_channel = ctx.author.voice.channel
if ctx.voice_client is None:
await voice_channel.connect()
guild = ctx.guild
voice_client: discord.VoiceClient = discord.utils.get(bot.voice_clients, guild=guild)
audio_source = discord.FFmpegPCMAudio('music.mp3')
if not voice_client.is_playing():
voice_client.play(audio_source, after=None)
else:
await ctx.voice_client.move_to(voice_channel)
bot.run("token")
Try this instead:
import discord
from discord.ext import commands
from youtube_dl import YoutubeDL
bot = discord.Client()
bot = commands.Bot(command_prefix=".")
#bot.command()
async def join(ctx):
if ctx.author.voice is None:
await ctx.send("You are not in a voice channel")
return
voice_channel = ctx.author.voice.channel
if ctx.voice_client is None:
await voice_channel.connect()
else:
await ctx.voice_client.move_to(voice_channel)
audio_source = discord.PCMVolumeTransformer(discord.FFmpegPCMAudio('music.mp3'))
if not ctx.voice_client.is_playing():
ctx.voice_client.play(audio_source, after=lambda e: print('Player error: %s' % e) if e else None)
bot.run("token")

Discord.py exception statement

I'm making a music bot with discord.py and I'm trying to use an except: statement to figure out if it is a valid url or not so I know to search for it or not. I'm trying to figure out what exception to put in the statement but when I try discord.ext.commands.errors.CommandInvokeError(e) there is a syntax error saying that e is not defined. I know that I can leave the space after the except: keyword blank but I want to try and figure it out because flake8 is highlighting it. Please help.
Here is a picture of the syntax highlight.
Here is the code:
import discord
from discord.ext import commands
import youtube_dl
import os
client = commands.Bot(command_prefix="?")
#client.event
async def on_ready():
print("Bot is ready.")
game = discord.Game("Music")
await client.change_presence(activity=game)
#client.command()
async def play(ctx, url: str = None):
if url is None:
await ctx.send("Put YouTube video's link after the \"play\" command.")
return
song_there = os.path.isfile("song.mp3")
try:
if song_there:
os.remove("song.mp3")
except PermissionError:
await ctx.send("Wait for the currently playing music to end or use the #\"stop\" command.")
channel = discord.utils.get(ctx.guild.voice_channels, name="music lounge")
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
if voice is not None:
if not voice.is_connected():
voice = await channel.connect()
else:
voice = await channel.connect()
ydl_opts = {
"format": "bestaudio",
"postprocessors": [{
"key": "FFmpegExtractAudio",
"preferredcodec": "mp3",
"preferredquality": "192"
}]
}
try:
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
await ctx.send("Downloaded music...")
except:
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
info_dict = ydl.extract_info(f"ytsearch: {url}", download=True)
for url in info_dict["entries"]:
print()
title = info_dict.get("entries")[0].get("title")
await ctx.send(f"Downloaded {title}")
for file in os.listdir("./"):
if file.endswith(".mp3"):
os.rename(file, "song.mp3")
voice.play(discord.FFmpegPCMAudio("song.mp3"))
#client.command()
async def leave(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
if voice is not None:
if voice.is_connected():
await voice.disconnect()
else:
await ctx.send("The bot is not connected to a voice channel.")
#client.command()
async def pause(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
if voice is not None:
if voice.is_playing():
voice.pause()
else:
await ctx.send("Currently no audio is playing.")
else:
await ctx.send("The bot is not connected to a voice channel.")
#client.command()
async def resume(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
if voice is not None:
if voice.is_paused():
voice.resume()
else:
await ctx.send("The audio is not paused.")
else:
await ctx.send("The bot is not connected to a voice channel.")
#client.command()
async def stop(ctx):
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
if voice is not None:
if voice.is_playing():
voice.stop()
else:
await ctx.send("Currently no audio is playing.")
else:
await ctx.send("The bot is not connected to a voice channel.")
client.run("TOKEN")

How to make a discord bot loop audio? [discord.py]

I want to make a bot play a piece of audio and, when the audio finishes, it will replay the audio.
What I have:
#client.command()
async def play(ctx):
await ctx.channel.purge(limit=1)
channel = ctx.author.voice.channel
if channel:
print(channel.id)
await channel.connect()
guild = ctx.guild
audio_source = discord.FFmpegPCMAudio('audio.mp3')
voice_client: discord.VoiceClient = discord.utils.get(client.voice_clients, guild=guild)
if not voice_client.is_playing():
voice_client.play(audio_source, after=None)
discord.VoiceClient.Play() has an after parameter that is called when the audio stream ends. Normally, it should be used to display error messages but you can use it to repeat the song like so:
#client.command()
async def play(ctx):
await ctx.channel.purge(limit=1)
channel = ctx.author.voice.channel
voice = get(self.bot.voice_clients, guild=ctx.guild)
def repeat(guild, voice, audio):
voice.play(audio, after=lambda e: repeat(guild, voice, audio))
voice.is_playing()
if channel and not voice.is_playing():
audio = discord.FFmpegPCMAudio('audio.mp3')
voice.play(audio, after=lambda e: repeat(ctx.guild, voice, audio))
voice.is_playing()

Categories