Browse Source

Fixed some bugs with the queue logic when pausing or stopping, made join an admin command, added error handling in the join command, outputs to all commands, improved current outputs, added yt alias for the play command, added a check to see if the user is in the voice channel when using the play command and have that check ignore admins, fixed logic for whether a video needs to be played or queued, added better commenting to all commands, and added remove command to remove items from the queue. Voice should be finished.

tags/v1.6.0
Roxie Gibson 6 years ago
parent
commit
35a1270d95
2 changed files with 92 additions and 35 deletions
  1. +91
    -34
      Roxbot/cogs/voice.py
  2. +1
    -1
      Roxbot/err_handle.py

+ 91
- 34
Roxbot/cogs/voice.py View File

@@ -109,8 +109,11 @@ class Voice:
"""Background task designed to help the bot move on to the next video in the queue"""
if ctx.voice_client.source == self.now_playing[ctx.guild.id]:
sleep_for = 0.5
while ctx.voice_client.is_playing():
await asyncio.sleep(sleep_for)
try:
while ctx.voice_client.is_playing() or ctx.voice_client.is_paused():
await asyncio.sleep(sleep_for)
except AttributeError:
return # This is to stop any errors appearing if the bot suddenly leaves voice chat.
if self.playlist[ctx.guild.id]:
player = self.playlist[ctx.guild.id].pop(0)
if player.get("stream", False) is True:
@@ -120,7 +123,6 @@ class Voice:
await ctx.invoke(command, url=player)
self.skip_votes[ctx.guild.id] = []


def _queue_song(self, ctx, video, stream):
"""Fuction to queue up a video into the playlist."""
video["stream"] = stream
@@ -136,18 +138,25 @@ class Voice:
self.now_playing[guild.id] = None
self.queue_logic[guild.id] = None

@checks.is_admin_or_mod()
@commands.command()
async def join(self, ctx, *, channel: discord.VoiceChannel = None):
"""Joins the voice channel your in."""
# Get channel
if channel is None:
channel = ctx.author.voice.channel
try:
channel = ctx.author.voice.channel
except AttributeError:
raise commands.CommandError("Failed to join voice channel. Please specify a channel or join one for Roxbot to join.")

# Join VoiceChannel
if ctx.voice_client is not None:
return await ctx.voice_client.move_to(channel)

await channel.connect()
await ctx.voice_client.move_to(channel)
else:
await channel.connect()
return await ctx.send("Joined {0.name} :ok_hand:".format(channel))

@commands.command(hidden=True)
@commands.command(hidden=True, enabled=False)
async def play_local(self, ctx, *, query):
"""Plays a file from the local filesystem."""
# TODO: Playlist stuff maybe
@@ -157,12 +166,19 @@ class Voice:
await ctx.send('Now playing: {}'.format(query))

@commands.cooldown(1, 0.5, commands.BucketType.guild)
@commands.command()
@commands.command(aliases=["yt"])
async def play(self, ctx, *, url, stream=False):
"""Plays from a url or search query (almost anything youtube_dl supports)"""
voice = guild_settings.get(ctx.guild).voice
guild = ctx.guild

# Checks if invoker is in voice with the bot. Skips admins and mods and owner.
if not checks._is_admin_or_mod(ctx):
if not ctx.author.voice:
raise commands.CommandError("You're not in the same voice channel as Roxbot.")
if ctx.author.voice.channel != ctx.voice_client.channel:
raise commands.CommandError("You're not in the same voice channel as Roxbot.")

# For internal speed. This should make the playlist management quicker when play is being invoked internally.
if isinstance(url, dict):
video = url
@@ -185,7 +201,8 @@ class Voice:
raise commands.CommandError("Cannot play video, duration is bigger than the max duration allowed.")

# Actual playing stuff section.
if not ctx.voice_client.is_playing() and self.am_queuing[guild.id] is False:
# If not playing and not queuing, and not paused, play the song. Otherwise queue it.
if (not ctx.voice_client.is_playing() and self.am_queuing[guild.id] is False) and not ctx.voice_client.is_paused():
self.am_queuing[guild.id] = True

async with ctx.typing():
@@ -197,19 +214,29 @@ class Voice:

# Create task to deal with what to do when the video ends or is skipped and how to handle the queue
self.queue_logic[ctx.guild.id] = self.bot.loop.create_task(self._queue_logic(ctx))
await ctx.send('Now playing: {}'.format(player.title))

embed = discord.Embed(
title="Now playing: '{0.title}' from {0.host}".format(player),
description= "Uploaded by: {0.uploader}\nURL: {0.webpage_url}".format(player),
colour=0xDEADBF
)
embed.set_image(url=player.thumbnail_url)
await ctx.send(embed=embed)
else:
# Queue the song as there is already a song playing or paused.
self._queue_song(ctx, video, stream)

# Sleep because if not, queued up things will send first and probably freak out users or something
while self.am_queuing[guild.id] is True:
await asyncio.sleep(0.5)
await ctx.send("{} added to queue".format(video.get("title")))
embed = discord.Embed(description='Added "{}" to queue'.format(video.get("title")), colour=0xDEADBF)
await ctx.send(embed=embed)

@commands.cooldown(1, 0.5, commands.BucketType.guild)
@commands.command()
async def stream(self, ctx, *, url):
"""Streams given link. Good for Twitch. (same as play, but doesn't predownload)"""
# Just invoke the play command with the stream argument as true. Deals with everything else anyway.
return await ctx.invoke(self.play, url=url, stream=True)

@play.before_invoke
@@ -231,23 +258,11 @@ class Voice:
raise commands.CommandError("Roxbot is not in a voice channel.")

if 0 < volume <= 100:
ctx.voice_client.source.volume = volume / 100
ctx.voice_client.source.volume = volume / 100 # Volume needs to be a float between 0 and 1... kinda
else:
raise commands.CommandError("Volume needs to be between 0-100%")
return await ctx.send("Changed volume to {}%".format(volume))

@checks.is_admin_or_mod()
@commands.command()
async def stop(self, ctx):
"""Stops and disconnects the bot from voice."""
if ctx.voice_client is None:
raise commands.CommandError("Roxbot is not in a voice channel.")
else:
self.playlist[ctx.guild.id] = []
self.now_playing[ctx.guild.id] = None
self.queue_logic[ctx.guild.id].cancel()
return await ctx.voice_client.disconnect()

@commands.command()
async def pause(self, ctx):
"""Pauses the current video, if playing."""
@@ -260,7 +275,7 @@ class Voice:
return await ctx.send("I already am paused!")
else:
ctx.voice_client.pause()
return await ctx.send("Paused {}".format(ctx.voice_client.source.title))
return await ctx.send("Paused '{}'".format(ctx.voice_client.source.title))

@commands.command()
async def resume(self, ctx):
@@ -274,7 +289,7 @@ class Voice:
else:
if ctx.voice_client.is_paused():
ctx.voice_client.resume()
return await ctx.send("Resumed {}".format(ctx.voice_client.source.title))
return await ctx.send("Resumed '{}'".format(ctx.voice_client.source.title))
else:
if ctx.voice_client.is_playing():
return await ctx.send("Can't resume if I'm already playing something!")
@@ -312,12 +327,17 @@ class Voice:

@commands.command(aliases=["np"])
async def nowplaying(self, ctx):
"""Displays the videos now playing"""
"""Displays the video now playing."""
if self.now_playing[ctx.guild.id] is None:
return await ctx.send("Nothing is playing.")
else:
np = ctx.voice_client.source
embed = discord.Embed(title="Now playing: '{}' from {}".format(np.title, np.host), colour=0xDEADBF)
if ctx.voice_client.is_paused():
x = "Paused"
else:
x = "Now Playing"
title = "{0}: '{1.title}' from {1.host}".format(x, np)
embed = discord.Embed(title=title, colour=0xDEADBF)
embed.description = "Uploaded by: {0.uploader}\nURL: {0.webpage_url}".format(np)
embed.set_image(url=np.thumbnail_url)
return await ctx.send(embed=embed)
@@ -328,15 +348,52 @@ class Voice:
output = ""
index = 1
for video in self.playlist[ctx.guild.id]:
output += "{}: '{}' queued by {}\n".format(index, video["title"], video["queued_by"])
output += "{}) '{}' queued by {}\n".format(index, video["title"], video["queued_by"])
index += 1
if output == "":
output = "Nothing is up next. Maybe you should add something!"
return await ctx.send(output)
embed = discord.Embed(title="Queue", description=output)
return await ctx.send(embed=embed)

# TODO: command to remove things from the queue
# TODO: Speed Improvements, better cooldown, reduce errors
# TODO: Clean up outputs. All commands should have outputs
@checks.is_admin_or_mod()
@commands.command()
async def remove(self, ctx, index):
"""Removes a item from the queue with the given index. Can also input all to delete all queued items."""
# Try and convert index into an into. If not possible, just move forward
try:
index = int(index)
except ValueError:
pass

# If not str "all" or an int, raise error.
if index != "all" and not isinstance(index, int):
raise commands.CommandError("No valid option given.")
elif index == "all":
# Remove all queued items
length = len(self.playlist[ctx.guild.id])
self.playlist[ctx.guild.id] = []
return await ctx.send("Removed all queued videos. ({})".format(length))
else:
try:
# Try and remove item using index.
removed = self.playlist[ctx.guild.id].pop(index-1) # -1 because queue index shown starts from 1, not 0
return await ctx.send("Removed '{}' from the queue.".format(removed.get("title", index)))
except IndexError:
raise commands.CommandError("Valid Index not given.")

@checks.is_admin_or_mod()
@commands.command(alaises=["disconnect"])
async def stop(self, ctx):
"""Stops and disconnects the bot from voice."""
if ctx.voice_client is None:
raise commands.CommandError("Roxbot is not in a voice channel.")
else:
# Clear up variables before stopping.
self.playlist[ctx.guild.id] = []
self.now_playing[ctx.guild.id] = None
self.queue_logic[ctx.guild.id].cancel()
await ctx.voice_client.disconnect()
return await ctx.send(":wave:")


def setup(bot_client):

+ 1
- 1
Roxbot/err_handle.py View File

@@ -8,7 +8,7 @@ from Roxbot.settings import guild_settings
class ErrHandle:
def __init__(self, bot_client):
self.bot = bot_client
self.dev = True # For debugging
self.dev = False # For debugging

async def on_error(self, event, *args, **kwargs):
if self.dev:

Loading…
Cancel
Save