[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 json
import logging
import os
import math
import random
import re
import time
@ -17,7 +17,6 @@ from typing import List, Optional, Tuple, Union, cast
import aiohttp
import discord
import lavalink
import math
from fuzzywuzzy import process
import redbot.core
@ -34,8 +33,9 @@ from redbot.core.utils.menus import (
start_adding_reactions,
)
from redbot.core.utils.predicates import MessagePredicate, ReactionPredicate
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 .converters import ComplexScopeParser, ScopeParser, get_lazy_converter, get_playlist_converter
from .equalizer import Equalizer
@ -57,8 +57,26 @@ from .playlists import (
get_playlist,
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__)
@ -1669,9 +1687,7 @@ class Audio(commands.Cog):
if dj_enabled:
if not await self._can_instaskip(ctx, ctx.author):
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(
ctx, ctx.author
):
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
return await self._embed_msg(ctx, _("There are other people listening to music."))
else:
await self._embed_msg(ctx, _("Disconnecting..."))
@ -2270,9 +2286,7 @@ class Audio(commands.Cog):
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
vote_enabled = await self.config.guild(ctx.guild).vote_enabled()
if dj_enabled or vote_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(
ctx, ctx.author
):
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
return
if player.current:
@ -2322,9 +2336,7 @@ class Audio(commands.Cog):
ctx, _("You must be in the voice channel pause or resume.")
)
if dj_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(
ctx, ctx.author
):
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
return await self._embed_msg(
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()
player = lavalink.get_player(ctx.guild.id)
if dj_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(
ctx, ctx.author
):
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
return await self._embed_msg(ctx, _("You need the DJ role to skip tracks."))
if (
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()
if not self._player_check(ctx) or not player.queue:
return await self._embed_msg(ctx, _("There's nothing in the queue."))
if dj_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(
ctx, ctx.author
):
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
return await self._embed_msg(ctx, _("You need the DJ role to clear the queue."))
player.queue.clear()
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:
return await self._embed_msg(ctx, _("There's nothing in the queue."))
if dj_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(
ctx, ctx.author
):
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
return await self._embed_msg(ctx, _("You need the DJ role to clean the queue."))
clean_tracks = []
removed_tracks = 0
@ -5624,10 +5631,8 @@ class Audio(commands.Cog):
"""Shuffles the queue."""
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
if dj_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(
ctx, ctx.author
):
return await self._embed_msg(ctx, _("You need the DJ role to clean the queue."))
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
return await self._embed_msg(ctx, _("You need the DJ role to shuffle the queue."))
if not self._player_check(ctx):
return await self._embed_msg(ctx, _("There's nothing in the queue."))
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`)."""
dj_enabled = await self.config.guild(ctx.guild).dj_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)
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."))
dj_enabled = await self.config.guild(ctx.guild).dj_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)
can_skip = await self._can_instaskip(ctx, ctx.author)
if dj_enabled and not vote_enabled:
if not (can_skip or is_requester) and not is_alone:
return await self._embed_msg(
@ -6228,7 +6232,6 @@ class Audio(commands.Cog):
and skip_to_track > 1
):
return await self._embed_msg(ctx, _("You can only skip the current track."))
if vote_enabled:
if not can_skip:
if skip_to_track is not None:
@ -6296,40 +6299,21 @@ class Audio(commands.Cog):
return False
async def _is_alone(self, ctx: commands.Context, member: discord.Member):
try:
user_voice = ctx.guild.get_member(member.id).voice
bot_voice = ctx.guild.get_member(self.bot.user.id).voice
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 _is_alone(self, ctx: commands.Context):
channel_members = rgetattr(ctx, "guild.me.voice.channel.members", [])
nonbots = sum(m.id != ctx.author.id for m in channel_members if not m.bot)
return nonbots < 1
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())
if dj_role_obj in ctx.guild.get_member(member.id).roles:
return True
return False
return dj_role_obj in ctx.guild.get_member(member.id).roles
@staticmethod
async def is_requester(ctx: commands.Context, member: discord.Member):
try:
player = lavalink.get_player(ctx.guild.id)
log.debug(f"Current requester is {player.current}")
if player.current.requester.id == member.id:
return True
return False
return player.current.requester.id == member.id
except Exception as e:
log.error(e)
return False
@ -6422,9 +6406,7 @@ class Audio(commands.Cog):
ctx, _("You must be in the voice channel to stop the music.")
)
if vote_enabled or vote_enabled and dj_enabled:
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(
ctx, ctx.author
):
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx):
return await self._embed_msg(
ctx, _("There are other people listening - vote to skip instead.")
)
@ -6754,9 +6736,6 @@ class Audio(commands.Cog):
log.error(
"Exception raised in Audio's emptypause_timer.", exc_info=True
)
finally:
pause_times.pop(server.id, None)
else:
pause_times.pop(server.id, None)
servers = stop_times.copy()
servers.update(pause_times)

View File

@ -1,5 +1,6 @@
import asyncio
import contextlib
import functools
import os
import re
import time
@ -10,10 +11,9 @@ import lavalink
from redbot.core import Config, commands
from redbot.core.bot import Red
from . import audio_dataclasses
from .converters import _pass_config_to_converters
from .playlists import _pass_config_to_playlist
__all__ = [
@ -32,6 +32,7 @@ __all__ = [
"url_check",
"userlimit",
"is_allowed",
"rgetattr",
"CacheLevel",
"Notifier",
]
@ -240,6 +241,18 @@ def userlimit(channel):
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:
__slots__ = ("value",)