discord.py 2.0 update (3d914e08->2.0.1) (#5709)

This commit is contained in:
Jakub Kuczys
2022-10-03 16:07:15 +02:00
committed by GitHub
parent d7d6ab46f4
commit f02528378f
44 changed files with 454 additions and 317 deletions

View File

@@ -1,6 +1,6 @@
import asyncio
import logging
from typing import Tuple
from typing import Tuple, Union
import discord
from redbot.core import Config, checks, commands
@@ -153,7 +153,7 @@ class Admin(commands.Cog):
async def _addrole(
self, ctx: commands.Context, member: discord.Member, role: discord.Role, *, check_user=True
):
if role in member.roles:
if member.get_role(role.id) is not None:
await ctx.send(
_("{member.display_name} already has the role {role.name}.").format(
role=role, member=member
@@ -183,7 +183,7 @@ class Admin(commands.Cog):
async def _removerole(
self, ctx: commands.Context, member: discord.Member, role: discord.Role, *, check_user=True
):
if role not in member.roles:
if member.get_role(role.id) is None:
await ctx.send(
_("{member.display_name} does not have the role {role.name}.").format(
role=role, member=member
@@ -214,7 +214,11 @@ class Admin(commands.Cog):
@commands.guild_only()
@checks.admin_or_permissions(manage_roles=True)
async def addrole(
self, ctx: commands.Context, rolename: discord.Role, *, user: discord.Member = None
self,
ctx: commands.Context,
rolename: discord.Role,
*,
user: discord.Member = commands.Author,
):
"""
Add a role to a user.
@@ -222,15 +226,17 @@ class Admin(commands.Cog):
Use double quotes if the role contains spaces.
If user is left blank it defaults to the author of the command.
"""
if user is None:
user = ctx.author
await self._addrole(ctx, user, rolename)
@commands.command()
@commands.guild_only()
@checks.admin_or_permissions(manage_roles=True)
async def removerole(
self, ctx: commands.Context, rolename: discord.Role, *, user: discord.Member = None
self,
ctx: commands.Context,
rolename: discord.Role,
*,
user: discord.Member = commands.Author,
):
"""
Remove a role from a user.
@@ -238,8 +244,6 @@ class Admin(commands.Cog):
Use double quotes if the role contains spaces.
If user is left blank it defaults to the author of the command.
"""
if user is None:
user = ctx.author
await self._removerole(ctx, user, rolename)
@commands.group()
@@ -349,7 +353,9 @@ class Admin(commands.Cog):
pass
@announceset.command(name="channel")
async def announceset_channel(self, ctx, *, channel: discord.TextChannel):
async def announceset_channel(
self, ctx, *, channel: Union[discord.TextChannel, discord.VoiceChannel]
):
"""Change the channel where the bot will send announcements."""
await self.config.guild(ctx.guild).announce_channel.set(channel.id)
await ctx.send(
@@ -389,7 +395,7 @@ class Admin(commands.Cog):
Server admins must have configured the role as user settable.
NOTE: The role is case sensitive!
"""
if selfrole in ctx.author.roles:
if ctx.author.get_role(selfrole.id) is not None:
return await self._removerole(ctx, ctx.author, selfrole, check_user=False)
else:
return await self._addrole(ctx, ctx.author, selfrole, check_user=False)

View File

@@ -196,7 +196,9 @@ class MixinMeta(ABC):
async def is_query_allowed(
self,
config: Config,
ctx_or_channel: Optional[Union[Context, discord.TextChannel, discord.Thread]],
ctx_or_channel: Optional[
Union[Context, discord.TextChannel, discord.VoiceChannel, discord.Thread]
],
query: str,
query_obj: Query,
) -> bool:
@@ -250,7 +252,9 @@ class MixinMeta(ABC):
raise NotImplementedError()
@abstractmethod
def _has_notify_perms(self, channel: Union[discord.TextChannel, discord.Thread]) -> bool:
def _has_notify_perms(
self, channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]
) -> bool:
raise NotImplementedError()
@abstractmethod

View File

@@ -657,7 +657,7 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
if not self._player_check(ctx):
player = await lavalink.connect(
ctx.author.voice.channel,
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
self_deaf=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
)
player.store("notify_channel", ctx.channel.id)
else:
@@ -675,7 +675,7 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
)
await player.move_to(
ctx.author.voice.channel,
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
self_deaf=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
)
await ctx.tick()
except AttributeError:

View File

@@ -85,7 +85,7 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
)
await lavalink.connect(
ctx.author.voice.channel,
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
self_deaf=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
)
except AttributeError:
return await self.send_embed_msg(
@@ -193,7 +193,7 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
)
await lavalink.connect(
ctx.author.voice.channel,
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
self_deaf=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
)
except AttributeError:
return await self.send_embed_msg(
@@ -456,7 +456,7 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
)
await lavalink.connect(
ctx.author.voice.channel,
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
self_deaf=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
)
except AttributeError:
return await self.send_embed_msg(
@@ -572,7 +572,7 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
)
await lavalink.connect(
ctx.author.voice.channel,
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
self_deaf=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
)
except AttributeError:
return await self.send_embed_msg(
@@ -697,7 +697,7 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
)
await lavalink.connect(
ctx.author.voice.channel,
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
self_deaf=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
)
except AttributeError:
return await self.send_embed_msg(

View File

@@ -344,7 +344,7 @@ class QueueCommands(MixinMeta, metaclass=CompositeMetaClass):
)
player = await lavalink.connect(
ctx.author.voice.channel,
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
self_deaf=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
)
player.store("notify_channel", ctx.channel.id)
except AttributeError:

View File

@@ -62,7 +62,7 @@ HUMANIZED_PERM = {
"manage_roles": _("Manage Roles"),
"manage_webhooks": _("Manage Webhooks"),
"manage_emojis": _("Manage Emojis"),
"use_slash_commands": _("Use Slash Commands"),
"use_application_commands": _("Use Application Commands"),
"request_to_speak": _("Request to Speak"),
"manage_events": _("Manage Events"),
"manage_threads": _("Manage Threads"),
@@ -187,7 +187,7 @@ class DpyEvents(MixinMeta, metaclass=CompositeMetaClass):
)
surpass_ignore = (
isinstance(ctx.channel, discord.abc.PrivateChannel)
ctx.guild is None
or await ctx.bot.is_owner(ctx.author)
or await ctx.bot.is_admin(ctx.author)
)
@@ -210,7 +210,7 @@ class DpyEvents(MixinMeta, metaclass=CompositeMetaClass):
)
raise CheckFailure(message=text)
current_perms = ctx.channel.permissions_for(ctx.me)
current_perms = ctx.bot_permissions
if guild and not current_perms.is_superset(self.permission_cache):
current_perms_set = set(iter(current_perms))
expected_perms_set = set(iter(self.permission_cache))

View File

@@ -90,7 +90,11 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
self._ws_resume[guild_id].set()
await self._websocket_closed_handler(
guild=guild, player=player, extra=extra, deafen=deafen, disconnect=disconnect
guild=guild,
player=player,
extra=extra,
self_deaf=deafen,
disconnect=disconnect,
)
except Exception as exc:
log.debug(
@@ -335,7 +339,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
guild: discord.Guild,
player: lavalink.Player,
extra: Dict,
deafen: bool,
self_deaf: bool,
disconnect: bool,
) -> None:
guild_id = guild.id
@@ -415,7 +419,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
if has_perm and player.current and player.is_playing:
player.store("resumes", player.fetch("resumes", 0) + 1)
await player.connect(deafen=deafen)
await player.connect(self_deaf=self_deaf)
await player.resume(player.current, start=player.position, replace=True)
ws_audio_log.info(
"Voice websocket reconnected Reason: Error code %s & Currently playing",
@@ -429,7 +433,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
)
elif has_perm and player.paused and player.current:
player.store("resumes", player.fetch("resumes", 0) + 1)
await player.connect(deafen=deafen)
await player.connect(self_deaf=self_deaf)
await player.resume(
player.current, start=player.position, replace=True, pause=True
)
@@ -445,7 +449,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
)
elif has_perm and (not disconnect) and (not player.is_playing):
player.store("resumes", player.fetch("resumes", 0) + 1)
await player.connect(deafen=deafen)
await player.connect(self_deaf=self_deaf)
ws_audio_log.info(
"Voice websocket reconnected "
"Reason: Error code %s & Not playing, but auto disconnect disabled",
@@ -497,7 +501,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
).currently_auto_playing_in.set([])
elif code in (42069,) and has_perm and player.current and player.is_playing:
player.store("resumes", player.fetch("resumes", 0) + 1)
await player.connect(deafen=deafen)
await player.connect(self_deaf=self_deaf)
await player.resume(player.current, start=player.position, replace=True)
ws_audio_log.info("Player resumed - Reason: Error code %s & %s", code, reason)
ws_audio_log.debug(
@@ -514,7 +518,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
)
await asyncio.sleep(delay)
if has_perm and player.current and player.is_playing:
await player.connect(deafen=deafen)
await player.connect(self_deaf=self_deaf)
await player.resume(player.current, start=player.position, replace=True)
ws_audio_log.info(
"Voice websocket reconnected Reason: Error code %s & Player is active",
@@ -528,7 +532,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
)
elif has_perm and player.paused and player.current:
player.store("resumes", player.fetch("resumes", 0) + 1)
await player.connect(deafen=deafen)
await player.connect(self_deaf=self_deaf)
await player.resume(
player.current, start=player.position, replace=True, pause=True
)
@@ -544,7 +548,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
)
elif has_perm and (not disconnect) and (not player.is_playing):
player.store("resumes", player.fetch("resumes", 0) + 1)
await player.connect(deafen=deafen)
await player.connect(self_deaf=self_deaf)
ws_audio_log.info(
"Voice websocket reconnected "
"to channel %s in guild: %s | "

View File

@@ -138,7 +138,7 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
if not (perms.connect and perms.speak):
vc = None
break
player = await lavalink.connect(vc, deafen=auto_deafen)
player = await lavalink.connect(vc, self_deaf=auto_deafen)
player.store("notify_channel", notify_channel_id)
break
except NodeNotFound:
@@ -222,7 +222,7 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
if not (perms.connect and perms.speak):
vc = None
break
player = await lavalink.connect(vc, deafen=auto_deafen)
player = await lavalink.connect(vc, self_deaf=auto_deafen)
player.store("notify_channel", notify_channel_id)
break
except NodeNotFound:

View File

@@ -100,7 +100,7 @@ class FormattingUtilities(MixinMeta, metaclass=CompositeMetaClass):
try:
await lavalink.connect(
ctx.author.voice.channel,
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
self_deaf=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
)
except AttributeError:
return await self.send_embed_msg(ctx, title=_("Connect to a voice channel first."))

View File

@@ -99,7 +99,9 @@ class MiscellaneousUtilities(MixinMeta, metaclass=CompositeMetaClass):
embed.set_author(name=name)
return await ctx.send(embed=embed)
def _has_notify_perms(self, channel: Union[discord.TextChannel, discord.Thread]) -> bool:
def _has_notify_perms(
self, channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]
) -> bool:
perms = channel.permissions_for(channel.guild.me)
return all((can_user_send_messages_in(channel.guild.me, channel), perms.embed_links))

View File

@@ -114,8 +114,7 @@ class PlayerUtilities(MixinMeta, metaclass=CompositeMetaClass):
dj_role = self._dj_role_cache.setdefault(
ctx.guild.id, await self.config.guild(ctx.guild).dj_role()
)
dj_role_obj = ctx.guild.get_role(dj_role)
return dj_role_obj in ctx.guild.get_member(member.id).roles
return member.get_role(dj_role) is not None
async def is_requester(self, ctx: commands.Context, member: discord.Member) -> bool:
try:
@@ -711,7 +710,7 @@ class PlayerUtilities(MixinMeta, metaclass=CompositeMetaClass):
):
await player.move_to(
user_channel,
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
self_deaf=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
)
return True
else:

View File

@@ -545,7 +545,7 @@ class PlaylistUtilities(MixinMeta, metaclass=CompositeMetaClass):
return False
await lavalink.connect(
ctx.author.voice.channel,
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
self_deaf=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
)
except NodeNotFound:
await self.send_embed_msg(

View File

@@ -60,7 +60,9 @@ class ValidationUtilities(MixinMeta, metaclass=CompositeMetaClass):
async def is_query_allowed(
self,
config: Config,
ctx_or_channel: Optional[Union[Context, discord.TextChannel, discord.Thread]],
ctx_or_channel: Optional[
Union[Context, discord.TextChannel, discord.VoiceChannel, discord.Thread]
],
query: str,
query_obj: Query,
) -> bool:

View File

@@ -75,7 +75,9 @@ class Cleanup(commands.Cog):
@staticmethod
async def get_messages_for_deletion(
*,
channel: Union[discord.TextChannel, discord.DMChannel, discord.Thread],
channel: Union[
discord.TextChannel, discord.VoiceChannel, discord.DMChannel, discord.Thread
],
number: Optional[PositiveInt] = None,
check: Callable[[discord.Message], bool] = lambda x: True,
limit: Optional[PositiveInt] = None,
@@ -129,7 +131,9 @@ class Cleanup(commands.Cog):
async def send_optional_notification(
self,
num: int,
channel: Union[discord.TextChannel, discord.DMChannel, discord.Thread],
channel: Union[
discord.TextChannel, discord.VoiceChannel, discord.DMChannel, discord.Thread
],
*,
subtract_invoking: bool = False,
) -> None:
@@ -149,7 +153,8 @@ class Cleanup(commands.Cog):
@staticmethod
async def get_message_from_reference(
channel: Union[discord.TextChannel, discord.Thread], reference: discord.MessageReference
channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
reference: discord.MessageReference,
) -> Optional[discord.Message]:
message = None
resolved = reference.resolved
@@ -621,7 +626,7 @@ class Cleanup(commands.Cog):
can_mass_purge = False
if type(author) is discord.Member:
me = ctx.guild.me
can_mass_purge = channel.permissions_for(me).manage_messages
can_mass_purge = ctx.bot_permissions.manage_messages
if match_pattern:

View File

@@ -181,7 +181,7 @@ class Economy(commands.Cog):
pass
@_bank.command()
async def balance(self, ctx: commands.Context, user: discord.Member = None):
async def balance(self, ctx: commands.Context, user: discord.Member = commands.Author):
"""Show the user's account balance.
Example:
@@ -192,9 +192,6 @@ class Economy(commands.Cog):
- `<user>` The user to check the balance of. If omitted, defaults to your own balance.
"""
if user is None:
user = ctx.author
bal = await bank.get_balance(user)
currency = await bank.get_currency_name(ctx.guild)
max_bal = await bank.get_max_balance(ctx.guild)

View File

@@ -251,28 +251,25 @@ class Filter(commands.Cog):
await ctx.send(_("I can't send direct messages to you."))
@_filter_channel.command(name="add", require_var_positional=True)
async def filter_channel_add(self, ctx: commands.Context, *words: str):
async def filter_channel_add(
self,
ctx: commands.Context,
channel: Union[discord.TextChannel, discord.VoiceChannel, discord.ForumChannel],
*words: str,
):
"""Add words to the filter.
Use double quotes to add sentences.
Examples:
- `[p]filter channel add word1 word2 word3`
- `[p]filter channel add "This is a sentence"`
- `[p]filter channel add #channel word1 word2 word3`
- `[p]filter channel add #channel "This is a sentence"`
**Arguments:**
- `<channel>` The text, voice, or forum channel to add filtered words to.
- `[words...]` The words or sentences to filter.
"""
channel = ctx.channel
if isinstance(channel, discord.Thread):
await ctx.send(
_(
"Threads can't have a filter list set up. If you want to add words to"
" the list of the parent channel, send the command in that channel."
)
)
return
added = await self.add_to_filter(channel, words)
if added:
self.invalidate_cache(ctx.guild, ctx.channel)
@@ -281,28 +278,25 @@ class Filter(commands.Cog):
await ctx.send(_("Words already in the filter."))
@_filter_channel.command(name="delete", aliases=["remove", "del"], require_var_positional=True)
async def filter_channel_remove(self, ctx: commands.Context, *words: str):
async def filter_channel_remove(
self,
ctx: commands.Context,
channel: Union[discord.TextChannel, discord.VoiceChannel, discord.ForumChannel],
*words: str,
):
"""Remove words from the filter.
Use double quotes to remove sentences.
Examples:
- `[p]filter channel remove word1 word2 word3`
- `[p]filter channel remove "This is a sentence"`
- `[p]filter channel remove #channel word1 word2 word3`
- `[p]filter channel remove #channel "This is a sentence"`
**Arguments:**
- `<channel>` The text, voice, or forum channel to add filtered words to.
- `[words...]` The words or sentences to no longer filter.
"""
channel = ctx.channel
if isinstance(channel, discord.Thread):
await ctx.send(
_(
"Threads can't have a filter list set up. If you want to remove words from"
" the list of the parent channel, send the command in that channel."
)
)
return
removed = await self.remove_from_filter(channel, words)
if removed:
await ctx.send(_("Words removed from filter."))
@@ -371,7 +365,11 @@ class Filter(commands.Cog):
await ctx.send(_("Names and nicknames will now be filtered."))
def invalidate_cache(
self, guild: discord.Guild, channel: Optional[discord.TextChannel] = None
self,
guild: discord.Guild,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.ForumChannel]
] = None,
) -> None:
"""Invalidate a cached pattern"""
self.pattern_cache.pop((guild.id, channel and channel.id), None)
@@ -381,7 +379,11 @@ class Filter(commands.Cog):
self.pattern_cache.pop(keyset, None)
async def add_to_filter(
self, server_or_channel: Union[discord.Guild, discord.TextChannel], words: list
self,
server_or_channel: Union[
discord.Guild, discord.TextChannel, discord.VoiceChannel, discord.ForumChannel
],
words: list,
) -> bool:
added = False
if isinstance(server_or_channel, discord.Guild):
@@ -391,7 +393,7 @@ class Filter(commands.Cog):
cur_list.append(w.lower())
added = True
elif isinstance(server_or_channel, discord.TextChannel):
else:
async with self.config.channel(server_or_channel).filter() as cur_list:
for w in words:
if w.lower() not in cur_list and w:
@@ -401,7 +403,11 @@ class Filter(commands.Cog):
return added
async def remove_from_filter(
self, server_or_channel: Union[discord.Guild, discord.TextChannel], words: list
self,
server_or_channel: Union[
discord.Guild, discord.TextChannel, discord.VoiceChannel, discord.ForumChannel
],
words: list,
) -> bool:
removed = False
if isinstance(server_or_channel, discord.Guild):
@@ -411,7 +417,7 @@ class Filter(commands.Cog):
cur_list.remove(w.lower())
removed = True
elif isinstance(server_or_channel, discord.TextChannel):
else:
async with self.config.channel(server_or_channel).filter() as cur_list:
for w in words:
if w.lower() in cur_list:
@@ -423,7 +429,9 @@ class Filter(commands.Cog):
async def filter_hits(
self,
text: str,
server_or_channel: Union[discord.Guild, discord.TextChannel, discord.Thread],
server_or_channel: Union[
discord.Guild, discord.TextChannel, discord.VoiceChannel, discord.Thread
],
) -> Set[str]:
if isinstance(server_or_channel, discord.Guild):
guild = server_or_channel

View File

@@ -1,3 +1,4 @@
import discord
import re
from .abc import MixinMeta
from datetime import timedelta
@@ -24,11 +25,14 @@ class Slowmode(MixinMeta):
minimum=timedelta(seconds=0), maximum=timedelta(hours=6), default_unit="seconds"
) = timedelta(seconds=0),
):
"""Changes thread's or channel's slowmode setting.
"""Changes thread's or text channel's slowmode setting.
Interval can be anything from 0 seconds to 6 hours.
Use without parameters to disable.
"""
if not isinstance(ctx.channel, (discord.TextChannel, discord.Thread)):
await ctx.send(_("Slowmode can only be set in text channels and threads."))
return
seconds = interval.total_seconds()
await ctx.channel.edit(slowmode_delay=seconds)
if seconds > 0:

View File

@@ -52,6 +52,11 @@ MUTE_UNMUTE_ISSUES = {
"voice_mute_permission": _(
"Because I don't have the Move Members permission, this will take into effect when the user rejoins."
),
"is_not_voice_mute": _(
"That user is channel muted in their current voice channel, not just voice muted."
" If you want to fully unmute this user in the channel,"
" use {command} in their voice channel's text channel instead."
),
}
_ = T_
@@ -503,7 +508,7 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
del muted_users[str(member.id)]
if success["success"]:
if create_case:
if isinstance(channel, discord.VoiceChannel):
if data.get("voice_mute", False):
unmute_type = "vunmute"
notification_title = _("Voice unmute")
else:
@@ -692,16 +697,21 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
o.id: {name: attr for name, attr in p} for o, p in after.overwrites.items()
}
to_del: List[int] = []
for user_id in self._channel_mutes[after.id].keys():
for user_id, mute_data in self._channel_mutes[after.id].items():
unmuted = False
voice_mute = mute_data.get("voice_mute", False)
if user_id in after_perms:
for perm_name in (
"send_messages",
"send_messages_in_threads",
"create_public_threads",
"create_private_threads",
"speak",
):
perms_to_check = ["speak"]
if not voice_mute:
perms_to_check.extend(
(
"send_messages",
"send_messages_in_threads",
"create_public_threads",
"create_private_threads",
)
)
for perm_name in perms_to_check:
unmuted = unmuted or after_perms[user_id][perm_name] is not False
# explicit is better than implicit :thinkies:
if user_id in before_perms and (user_id not in after_perms or unmuted):
@@ -713,7 +723,7 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
log.debug(f"{user} - {type(user)}")
to_del.append(user_id)
log.debug("creating case")
if isinstance(after, discord.VoiceChannel):
if voice_mute:
unmute_type = "vunmute"
notification_title = _("Voice unmute")
else:
@@ -848,7 +858,9 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
@muteset.command(name="notification")
@checks.admin_or_permissions(manage_channels=True)
async def notification_channel_set(
self, ctx: commands.Context, channel: Optional[discord.TextChannel] = None
self,
ctx: commands.Context,
channel: Optional[Union[discord.TextChannel, discord.VoiceChannel]] = None,
):
"""
Set the notification channel for automatic unmute issues.
@@ -932,6 +944,7 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
send_messages_in_threads=False,
create_public_threads=False,
create_private_threads=False,
use_application_commands=False,
speak=False,
add_reactions=False,
)
@@ -979,6 +992,7 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
overs.send_messages_in_threads = False
overs.create_public_threads = False
overs.create_private_threads = False
overs.use_application_commands = False
overs.add_reactions = False
overs.speak = False
try:
@@ -1681,6 +1695,8 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
user: discord.Member,
until: Optional[datetime] = None,
reason: Optional[str] = None,
*,
voice_mute: bool = False,
) -> Dict[str, Optional[Union[discord.abc.GuildChannel, str, bool]]]:
"""Mutes the specified user in the specified channel"""
overwrites = channel.overwrites_for(user)
@@ -1693,16 +1709,7 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
"reason": _(MUTE_UNMUTE_ISSUES["is_admin"]),
}
new_overs: dict = {}
move_channel = False
new_overs.update(
send_messages=False,
send_messages_in_threads=False,
create_public_threads=False,
create_private_threads=False,
add_reactions=False,
speak=False,
)
send_reason = None
if user.voice and user.voice.channel:
if channel.permissions_for(guild.me).move_members:
@@ -1717,16 +1724,38 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
"reason": _(MUTE_UNMUTE_ISSUES["hierarchy_problem"]),
}
old_overs = {k: getattr(overwrites, k) for k in new_overs}
overwrites.update(**new_overs)
if channel.id not in self._channel_mutes:
self._channel_mutes[channel.id] = {}
if user.id in self._channel_mutes[channel.id]:
current_mute = self._channel_mutes.get(channel.id)
# Determine if this is voice mute -> channel mute upgrade
is_mute_upgrade = (
current_mute is not None and not voice_mute and current_mute.get("voice_mute", False)
)
# We want to continue if this is a new mute or a mute upgrade,
# otherwise we should return with failure.
if current_mute is not None and not is_mute_upgrade:
return {
"success": False,
"channel": channel,
"reason": _(MUTE_UNMUTE_ISSUES["already_muted"]),
}
new_overs: Dict[str, Optional[bool]] = {"speak": False}
if not voice_mute:
new_overs.update(
send_messages=False,
send_messages_in_threads=False,
create_public_threads=False,
create_private_threads=False,
use_application_commands=False,
add_reactions=False,
)
old_overs = {k: getattr(overwrites, k) for k in new_overs}
if is_mute_upgrade:
perms_cache = await self.config.member(user).perms_cache()
if "speak" in perms_cache:
old_overs["speak"] = perms_cache["speak"]
overwrites.update(**new_overs)
if not channel.permissions_for(guild.me).manage_permissions:
return {
"success": False,
@@ -1738,6 +1767,7 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
"guild": guild.id,
"member": user.id,
"until": until.timestamp() if until else None,
"voice_mute": voice_mute,
}
try:
await channel.set_permissions(user, overwrite=overwrites, reason=reason)
@@ -1795,6 +1825,8 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
author: discord.Member,
user: discord.Member,
reason: Optional[str] = None,
*,
voice_mute: bool = False,
) -> Dict[str, Optional[Union[discord.abc.GuildChannel, str, bool]]]:
"""Unmutes the specified user in a specified channel"""
overwrites = channel.overwrites_for(user)
@@ -1809,6 +1841,7 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
"send_messages_in_threads": None,
"create_public_threads": None,
"create_private_threads": None,
"use_application_commands": None,
"add_reactions": None,
"speak": None,
}
@@ -1826,13 +1859,21 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass):
overwrites.update(**old_values)
if channel.id in self._channel_mutes and user.id in self._channel_mutes[channel.id]:
del self._channel_mutes[channel.id][user.id]
current_mute = self._channel_mutes[channel.id].pop(user.id)
else:
return {
"success": False,
"channel": channel,
"reason": _(MUTE_UNMUTE_ISSUES["already_unmuted"]),
}
if not current_mute["voice_mute"] and voice_mute:
return {
"success": False,
"channel": channel,
"reason": _(MUTE_UNMUTE_ISSUES["is_not_voice_mute"]).format(
command=inline("unmutechannel")
),
}
if not channel.permissions_for(guild.me).manage_permissions:
return {
"success": False,

View File

@@ -124,7 +124,7 @@ class VoiceMutes(MixinMeta):
audit_reason = get_audit_reason(author, reason, shorten=True)
success = await self.channel_mute_user(
guild, channel, author, user, until, audit_reason
guild, channel, author, user, until, audit_reason, voice_mute=True
)
if success["success"]:
@@ -200,7 +200,7 @@ class VoiceMutes(MixinMeta):
audit_reason = get_audit_reason(author, reason, shorten=True)
success = await self.channel_unmute_user(
guild, channel, author, user, audit_reason
guild, channel, author, user, audit_reason, voice_mute=True
)
if success["success"]:

View File

@@ -221,8 +221,8 @@ class Permissions(commands.Cog):
"Global rules (set by the owner) are checked first, then rules set for servers. If "
"multiple global or server rules apply to the case, the order they are checked in is:\n"
" 1. Rules about a user.\n"
" 2. Rules about the voice channel a user is in.\n"
" 3. Rules about the text channel or a parent of the thread a command was issued in.\n"
" 2. Rules about the voice channel a user is connected to.\n"
" 3. Rules about the channel or a parent of the thread a command was issued in.\n"
" 4. Rules about a role the user has (The highest role they have with a rule will be "
"used).\n"
" 5. Rules about the server a user is in (Global rules only).\n\n"
@@ -330,7 +330,7 @@ class Permissions(commands.Cog):
except discord.Forbidden:
await ctx.send(_("I'm not allowed to DM you."))
else:
if not isinstance(ctx.channel, discord.DMChannel):
if ctx.guild is not None:
await ctx.send(_("I've just sent the file to you via DM."))
finally:
file.close()

View File

@@ -106,7 +106,9 @@ class Reports(commands.Cog):
@checks.admin_or_permissions(manage_guild=True)
@reportset.command(name="output")
async def reportset_output(self, ctx: commands.Context, channel: discord.TextChannel):
async def reportset_output(
self, ctx: commands.Context, channel: Union[discord.TextChannel, discord.VoiceChannel]
):
"""Set the channel where reports will be sent."""
await self.config.guild(ctx.guild).output_channel.set(channel.id)
await ctx.send(_("The report channel has been set."))
@@ -325,7 +327,7 @@ class Reports(commands.Cog):
if ctx.author.id in self.user_cache:
self.user_cache.remove(ctx.author.id)
if ctx.guild and ctx.invoked_subcommand is None:
if ctx.channel.permissions_for(ctx.guild.me).manage_messages:
if ctx.bot_permissions.manage_messages:
try:
await ctx.message.delete()
except discord.NotFound:

View File

@@ -303,14 +303,19 @@ class Streams(commands.Cog):
self,
ctx: commands.Context,
channel_name: str,
discord_channel: discord.TextChannel = None,
discord_channel: Union[discord.TextChannel, discord.VoiceChannel] = None,
):
"""Manage Twitch stream notifications."""
await ctx.invoke(self.twitch_alert_channel, channel_name, discord_channel)
@_twitch.command(name="channel")
async def twitch_alert_channel(
self, ctx: commands.Context, channel_name: str, discord_channel: discord.TextChannel = None
self,
ctx: commands.Context,
channel_name: str,
discord_channel: Union[
discord.TextChannel, discord.VoiceChannel
] = commands.CurrentChannel,
):
"""Toggle alerts in this or the given channel for a Twitch stream."""
if re.fullmatch(r"<#\d+>", channel_name):
@@ -325,14 +330,21 @@ class Streams(commands.Cog):
self,
ctx: commands.Context,
channel_name_or_id: str,
discord_channel: discord.TextChannel = None,
discord_channel: Union[
discord.TextChannel, discord.VoiceChannel
] = commands.CurrentChannel,
):
"""Toggle alerts in this channel for a YouTube stream."""
await self.stream_alert(ctx, YoutubeStream, channel_name_or_id, discord_channel)
@streamalert.command(name="picarto")
async def picarto_alert(
self, ctx: commands.Context, channel_name: str, discord_channel: discord.TextChannel = None
self,
ctx: commands.Context,
channel_name: str,
discord_channel: Union[
discord.TextChannel, discord.VoiceChannel
] = commands.CurrentChannel,
):
"""Toggle alerts in this channel for a Picarto stream."""
await self.stream_alert(ctx, PicartoStream, channel_name, discord_channel)
@@ -401,8 +413,6 @@ class Streams(commands.Cog):
await ctx.send(page)
async def stream_alert(self, ctx: commands.Context, _class, channel_name, discord_channel):
if discord_channel is None:
discord_channel = ctx.channel
if isinstance(discord_channel, discord.Thread):
await ctx.send("Stream alerts cannot be set up in threads.")
return
@@ -757,7 +767,7 @@ class Streams(commands.Cog):
async def _send_stream_alert(
self,
stream,
channel: discord.TextChannel,
channel: Union[discord.TextChannel, discord.VoiceChannel],
embed: discord.Embed,
content: str = None,
*,
@@ -904,7 +914,10 @@ class Streams(commands.Cog):
await self.save_streams()
async def _get_mention_str(
self, guild: discord.Guild, channel: discord.TextChannel, guild_data: dict
self,
guild: discord.Guild,
channel: Union[discord.TextChannel, discord.VoiceChannel],
guild_data: dict,
) -> Tuple[str, List[discord.Role]]:
"""Returns a 2-tuple with the string containing the mentions, and a list of
all roles which need to have their `mentionable` property set back to False.
@@ -930,7 +943,9 @@ class Streams(commands.Cog):
mentions.append(role.mention)
return " ".join(mentions), edited_roles
async def filter_streams(self, streams: list, channel: discord.TextChannel) -> list:
async def filter_streams(
self, streams: list, channel: Union[discord.TextChannel, discord.VoiceChannel]
) -> list:
filtered = []
for stream in streams:
tw_id = str(stream["channel"]["_id"])

View File

@@ -412,7 +412,7 @@ class Trivia(commands.Cog):
subcommands for a more customised leaderboard.
"""
cmd = self.trivia_leaderboard_server
if isinstance(ctx.channel, discord.abc.PrivateChannel):
if ctx.guild is None:
cmd = self.trivia_leaderboard_global
await ctx.invoke(cmd, "wins", 10)
@@ -710,7 +710,7 @@ class Trivia(commands.Cog):
await ctx.send(_("Saved Trivia list as {filename}.").format(filename=filename))
def _get_trivia_session(
self, channel: Union[discord.TextChannel, discord.Thread]
self, channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]
) -> TriviaSession:
return next(
(session for session in self.trivia_sessions if session.ctx.channel == channel), None

View File

@@ -156,7 +156,11 @@ class Warnings(commands.Cog):
@warningset.command()
@commands.guild_only()
async def warnchannel(self, ctx: commands.Context, channel: discord.TextChannel = None):
async def warnchannel(
self,
ctx: commands.Context,
channel: Union[discord.TextChannel, discord.VoiceChannel] = None,
):
"""Set the channel where warnings should be sent to.
Leave empty to use the channel `[p]warn` command was called in.