[Audio] Fix Attribute error raised by is_alone method when channel was None (#3122)

* Fix attribute Fixes #3120

Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com>

* Chore

Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com>
This commit is contained in:
Draper 2019-12-20 06:58:08 +00:00 committed by Michael H
parent 61f467a323
commit 0b042532fd
3 changed files with 58 additions and 65 deletions

View File

@ -0,0 +1 @@
Fixed an issue when calling audio commands when not in a voice channel could result in a crash.

View File

@ -5,7 +5,7 @@ import datetime
import heapq import heapq
import json import json
import logging import logging
import os import math
import random import random
import re import re
import time import time
@ -17,7 +17,6 @@ from typing import List, Optional, Tuple, Union, cast
import aiohttp import aiohttp
import discord import discord
import lavalink import lavalink
import math
from fuzzywuzzy import process from fuzzywuzzy import process
import redbot.core import redbot.core
@ -34,8 +33,9 @@ from redbot.core.utils.menus import (
start_adding_reactions, start_adding_reactions,
) )
from redbot.core.utils.predicates import MessagePredicate, ReactionPredicate from redbot.core.utils.predicates import MessagePredicate, ReactionPredicate
from . import audio_dataclasses from . import audio_dataclasses
from .apis import MusicCache, HAS_SQL, _ERROR from .apis import _ERROR, HAS_SQL, MusicCache
from .checks import can_have_caching from .checks import can_have_caching
from .converters import ComplexScopeParser, ScopeParser, get_lazy_converter, get_playlist_converter from .converters import ComplexScopeParser, ScopeParser, get_lazy_converter, get_playlist_converter
from .equalizer import Equalizer from .equalizer import Equalizer
@ -57,8 +57,26 @@ from .playlists import (
get_playlist, get_playlist,
humanize_scope, humanize_scope,
) )
from .utils import * from .utils import (
CacheLevel,
Notifier,
clear_react,
draw_time,
dynamic_time,
get_description,
is_allowed,
match_url,
match_yt_playlist,
pass_config_to_dependencies,
queue_duration,
remove_react,
rgetattr,
time_convert,
track_creator,
track_limit,
url_check,
userlimit,
)
_ = Translator("Audio", __file__) _ = Translator("Audio", __file__)
@ -1669,9 +1687,7 @@ class Audio(commands.Cog):
if dj_enabled: if dj_enabled:
if not await self._can_instaskip(ctx, ctx.author): if not await self._can_instaskip(ctx, ctx.author):
return await self._embed_msg(ctx, _("You need the DJ role to disconnect.")) return await self._embed_msg(ctx, _("You need the DJ role to disconnect."))
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone( if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
ctx, ctx.author
):
return await self._embed_msg(ctx, _("There are other people listening to music.")) return await self._embed_msg(ctx, _("There are other people listening to music."))
else: else:
await self._embed_msg(ctx, _("Disconnecting...")) await self._embed_msg(ctx, _("Disconnecting..."))
@ -2270,9 +2286,7 @@ class Audio(commands.Cog):
dj_enabled = await self.config.guild(ctx.guild).dj_enabled() dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
vote_enabled = await self.config.guild(ctx.guild).vote_enabled() vote_enabled = await self.config.guild(ctx.guild).vote_enabled()
if dj_enabled or vote_enabled: if dj_enabled or vote_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone( if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
ctx, ctx.author
):
return return
if player.current: if player.current:
@ -2322,9 +2336,7 @@ class Audio(commands.Cog):
ctx, _("You must be in the voice channel pause or resume.") ctx, _("You must be in the voice channel pause or resume.")
) )
if dj_enabled: if dj_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone( if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
ctx, ctx.author
):
return await self._embed_msg( return await self._embed_msg(
ctx, _("You need the DJ role to pause or resume tracks.") ctx, _("You need the DJ role to pause or resume tracks.")
) )
@ -5202,9 +5214,7 @@ class Audio(commands.Cog):
dj_enabled = await self.config.guild(ctx.guild).dj_enabled() dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
player = lavalink.get_player(ctx.guild.id) player = lavalink.get_player(ctx.guild.id)
if dj_enabled: if dj_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone( if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
ctx, ctx.author
):
return await self._embed_msg(ctx, _("You need the DJ role to skip tracks.")) return await self._embed_msg(ctx, _("You need the DJ role to skip tracks."))
if ( if (
not ctx.author.voice or ctx.author.voice.channel != player.channel not ctx.author.voice or ctx.author.voice.channel != player.channel
@ -5522,10 +5532,9 @@ class Audio(commands.Cog):
dj_enabled = await self.config.guild(ctx.guild).dj_enabled() dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
if not self._player_check(ctx) or not player.queue: if not self._player_check(ctx) or not player.queue:
return await self._embed_msg(ctx, _("There's nothing in the queue.")) return await self._embed_msg(ctx, _("There's nothing in the queue."))
if dj_enabled: if dj_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone( if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
ctx, ctx.author
):
return await self._embed_msg(ctx, _("You need the DJ role to clear the queue.")) return await self._embed_msg(ctx, _("You need the DJ role to clear the queue."))
player.queue.clear() player.queue.clear()
await self._embed_msg(ctx, _("The queue has been cleared.")) await self._embed_msg(ctx, _("The queue has been cleared."))
@ -5542,9 +5551,7 @@ class Audio(commands.Cog):
if not self._player_check(ctx) or not player.queue: if not self._player_check(ctx) or not player.queue:
return await self._embed_msg(ctx, _("There's nothing in the queue.")) return await self._embed_msg(ctx, _("There's nothing in the queue."))
if dj_enabled: if dj_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone( if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
ctx, ctx.author
):
return await self._embed_msg(ctx, _("You need the DJ role to clean the queue.")) return await self._embed_msg(ctx, _("You need the DJ role to clean the queue."))
clean_tracks = [] clean_tracks = []
removed_tracks = 0 removed_tracks = 0
@ -5624,10 +5631,8 @@ class Audio(commands.Cog):
"""Shuffles the queue.""" """Shuffles the queue."""
dj_enabled = await self.config.guild(ctx.guild).dj_enabled() dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
if dj_enabled: if dj_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone( if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
ctx, ctx.author return await self._embed_msg(ctx, _("You need the DJ role to shuffle the queue."))
):
return await self._embed_msg(ctx, _("You need the DJ role to clean the queue."))
if not self._player_check(ctx): if not self._player_check(ctx):
return await self._embed_msg(ctx, _("There's nothing in the queue.")) return await self._embed_msg(ctx, _("There's nothing in the queue."))
try: try:
@ -6090,7 +6095,7 @@ class Audio(commands.Cog):
Accepts seconds or a value formatted like 00:00:00 (`hh:mm:ss`) or 00:00 (`mm:ss`).""" Accepts seconds or a value formatted like 00:00:00 (`hh:mm:ss`) or 00:00 (`mm:ss`)."""
dj_enabled = await self.config.guild(ctx.guild).dj_enabled() dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
vote_enabled = await self.config.guild(ctx.guild).vote_enabled() vote_enabled = await self.config.guild(ctx.guild).vote_enabled()
is_alone = await self._is_alone(ctx, ctx.author) is_alone = await self._is_alone(ctx)
is_requester = await self.is_requester(ctx, ctx.author) is_requester = await self.is_requester(ctx, ctx.author)
can_skip = await self._can_instaskip(ctx, ctx.author) can_skip = await self._can_instaskip(ctx, ctx.author)
@ -6212,10 +6217,9 @@ class Audio(commands.Cog):
return await self._embed_msg(ctx, _("Nothing playing.")) return await self._embed_msg(ctx, _("Nothing playing."))
dj_enabled = await self.config.guild(ctx.guild).dj_enabled() dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
vote_enabled = await self.config.guild(ctx.guild).vote_enabled() vote_enabled = await self.config.guild(ctx.guild).vote_enabled()
is_alone = await self._is_alone(ctx, ctx.author) is_alone = await self._is_alone(ctx)
is_requester = await self.is_requester(ctx, ctx.author) is_requester = await self.is_requester(ctx, ctx.author)
can_skip = await self._can_instaskip(ctx, ctx.author) can_skip = await self._can_instaskip(ctx, ctx.author)
if dj_enabled and not vote_enabled: if dj_enabled and not vote_enabled:
if not (can_skip or is_requester) and not is_alone: if not (can_skip or is_requester) and not is_alone:
return await self._embed_msg( return await self._embed_msg(
@ -6228,7 +6232,6 @@ class Audio(commands.Cog):
and skip_to_track > 1 and skip_to_track > 1
): ):
return await self._embed_msg(ctx, _("You can only skip the current track.")) return await self._embed_msg(ctx, _("You can only skip the current track."))
if vote_enabled: if vote_enabled:
if not can_skip: if not can_skip:
if skip_to_track is not None: if skip_to_track is not None:
@ -6296,40 +6299,21 @@ class Audio(commands.Cog):
return False return False
async def _is_alone(self, ctx: commands.Context, member: discord.Member): async def _is_alone(self, ctx: commands.Context):
try: channel_members = rgetattr(ctx, "guild.me.voice.channel.members", [])
user_voice = ctx.guild.get_member(member.id).voice nonbots = sum(m.id != ctx.author.id for m in channel_members if not m.bot)
bot_voice = ctx.guild.get_member(self.bot.user.id).voice return nonbots < 1
nonbots = sum(not m.bot for m in user_voice.channel.members)
if user_voice.channel != bot_voice.channel:
nonbots = nonbots + 1
except AttributeError:
if ctx.guild.get_member(self.bot.user.id).voice is not None:
nonbots = sum(
not m.bot for m in ctx.guild.get_member(self.bot.user.id).voice.channel.members
)
if nonbots == 1:
nonbots = 2
elif ctx.guild.get_member(member.id).voice.channel.members == 1:
nonbots = 1
else:
nonbots = 0
return nonbots <= 1
async def _has_dj_role(self, ctx: commands.Context, member: discord.Member): async def _has_dj_role(self, ctx: commands.Context, member: discord.Member):
dj_role_obj = ctx.guild.get_role(await self.config.guild(ctx.guild).dj_role()) dj_role_obj = ctx.guild.get_role(await self.config.guild(ctx.guild).dj_role())
if dj_role_obj in ctx.guild.get_member(member.id).roles: return dj_role_obj in ctx.guild.get_member(member.id).roles
return True
return False
@staticmethod @staticmethod
async def is_requester(ctx: commands.Context, member: discord.Member): async def is_requester(ctx: commands.Context, member: discord.Member):
try: try:
player = lavalink.get_player(ctx.guild.id) player = lavalink.get_player(ctx.guild.id)
log.debug(f"Current requester is {player.current}") log.debug(f"Current requester is {player.current}")
if player.current.requester.id == member.id: return player.current.requester.id == member.id
return True
return False
except Exception as e: except Exception as e:
log.error(e) log.error(e)
return False return False
@ -6422,9 +6406,7 @@ class Audio(commands.Cog):
ctx, _("You must be in the voice channel to stop the music.") ctx, _("You must be in the voice channel to stop the music.")
) )
if vote_enabled or vote_enabled and dj_enabled: if vote_enabled or vote_enabled and dj_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone( if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
ctx, ctx.author
):
return await self._embed_msg( return await self._embed_msg(
ctx, _("There are other people listening - vote to skip instead.") ctx, _("There are other people listening - vote to skip instead.")
) )
@ -6754,10 +6736,7 @@ class Audio(commands.Cog):
log.error( log.error(
"Exception raised in Audio's emptypause_timer.", exc_info=True "Exception raised in Audio's emptypause_timer.", exc_info=True
) )
finally: pause_times.pop(server.id, None)
pause_times.pop(server.id, None)
else:
pause_times.pop(server.id, None)
servers = stop_times.copy() servers = stop_times.copy()
servers.update(pause_times) servers.update(pause_times)
for sid in servers: for sid in servers:

View File

@ -1,5 +1,6 @@
import asyncio import asyncio
import contextlib import contextlib
import functools
import os import os
import re import re
import time import time
@ -10,10 +11,9 @@ import lavalink
from redbot.core import Config, commands from redbot.core import Config, commands
from redbot.core.bot import Red from redbot.core.bot import Red
from . import audio_dataclasses from . import audio_dataclasses
from .converters import _pass_config_to_converters from .converters import _pass_config_to_converters
from .playlists import _pass_config_to_playlist from .playlists import _pass_config_to_playlist
__all__ = [ __all__ = [
@ -32,6 +32,7 @@ __all__ = [
"url_check", "url_check",
"userlimit", "userlimit",
"is_allowed", "is_allowed",
"rgetattr",
"CacheLevel", "CacheLevel",
"Notifier", "Notifier",
] ]
@ -240,6 +241,18 @@ def userlimit(channel):
return True return True
def rsetattr(obj, attr, val):
pre, _, post = attr.rpartition(".")
return setattr(rgetattr(obj, pre) if pre else obj, post, val)
def rgetattr(obj, attr, *args):
def _getattr(obj2, attr2):
return getattr(obj2, attr2, *args)
return functools.reduce(_getattr, [obj] + attr.split("."))
class CacheLevel: class CacheLevel:
__slots__ = ("value",) __slots__ = ("value",)