mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-05 18:58:53 -05:00
discord.py 2.0 update (3d914e08->2.0.1) (#5709)
This commit is contained in:
parent
d7d6ab46f4
commit
f02528378f
@ -31,8 +31,8 @@ In terms of scope, global rules will be checked first, then server rules.
|
||||
For each of those, the first rule pertaining to one of the following models will be used:
|
||||
|
||||
1. User
|
||||
2. Voice channel
|
||||
3. Text channel (parent text channel in case of invocations in threads)
|
||||
2. Voice channel a user is connected to
|
||||
3. The channel command was issued in (parent channel in case of invocations in threads)
|
||||
4. Channel category
|
||||
5. Roles, highest to lowest
|
||||
6. Server (can only be in global rules)
|
||||
|
||||
@ -21,9 +21,7 @@ Basic Usage
|
||||
|
||||
class MyCog(commands.Cog):
|
||||
@commands.command()
|
||||
async def balance(self, ctx, user: discord.Member = None):
|
||||
if user is None:
|
||||
user = ctx.author
|
||||
async def balance(self, ctx, user: discord.Member = commands.Author):
|
||||
bal = await bank.get_balance(user)
|
||||
currency = await bank.get_currency_name(ctx.guild)
|
||||
await ctx.send(
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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 | "
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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."))
|
||||
|
||||
@ -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))
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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 (
|
||||
perms_to_check = ["speak"]
|
||||
if not voice_mute:
|
||||
perms_to_check.extend(
|
||||
(
|
||||
"send_messages",
|
||||
"send_messages_in_threads",
|
||||
"create_public_threads",
|
||||
"create_private_threads",
|
||||
"speak",
|
||||
):
|
||||
)
|
||||
)
|
||||
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,
|
||||
|
||||
@ -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"]:
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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"])
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -38,7 +38,7 @@ class IssueDiagnoserBase:
|
||||
self,
|
||||
bot: Red,
|
||||
original_ctx: commands.Context,
|
||||
channel: Union[discord.TextChannel, discord.Thread],
|
||||
channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
|
||||
author: discord.Member,
|
||||
command: commands.Command,
|
||||
) -> None:
|
||||
|
||||
@ -220,8 +220,6 @@ class Red(
|
||||
self._main_dir = bot_dir
|
||||
self._cog_mgr = CogManager()
|
||||
self._use_team_features = cli_flags.use_team_features
|
||||
# to prevent multiple calls to app info during startup
|
||||
self._app_info = None
|
||||
super().__init__(*args, help_command=None, **kwargs)
|
||||
# Do not manually use the help formatter attribute here, see `send_help_for`,
|
||||
# for a documented API. The internals of this object are still subject to change.
|
||||
@ -815,7 +813,7 @@ class Red(
|
||||
return False
|
||||
|
||||
if guild:
|
||||
assert isinstance(channel, (discord.abc.GuildChannel, discord.Thread))
|
||||
assert isinstance(channel, (discord.TextChannel, discord.VoiceChannel, discord.Thread))
|
||||
if not can_user_send_messages_in(guild.me, channel):
|
||||
return False
|
||||
if not (await self.ignored_channel_or_guild(message)):
|
||||
@ -1207,16 +1205,12 @@ class Red(
|
||||
if self.rpc_enabled:
|
||||
await self.rpc.initialize(self.rpc_port)
|
||||
|
||||
async def _pre_fetch_owners(self) -> None:
|
||||
app_info = await self.application_info()
|
||||
|
||||
if app_info.team:
|
||||
def _setup_owners(self) -> None:
|
||||
if self.application.team:
|
||||
if self._use_team_features:
|
||||
self.owner_ids.update(m.id for m in app_info.team.members)
|
||||
self.owner_ids.update(m.id for m in self.application.team.members)
|
||||
elif self._owner_id_overwrite is None:
|
||||
self.owner_ids.add(app_info.owner.id)
|
||||
|
||||
self._app_info = app_info
|
||||
self.owner_ids.add(self.application.owner.id)
|
||||
|
||||
if not self.owner_ids:
|
||||
raise _NoOwnerSet("Bot doesn't have any owner set!")
|
||||
@ -1229,7 +1223,7 @@ class Red(
|
||||
await self.connect()
|
||||
|
||||
async def setup_hook(self) -> None:
|
||||
await self._pre_fetch_owners()
|
||||
self._setup_owners()
|
||||
await self._pre_connect()
|
||||
|
||||
async def send_help_for(
|
||||
@ -1249,7 +1243,12 @@ class Red(
|
||||
async def embed_requested(
|
||||
self,
|
||||
channel: Union[
|
||||
discord.TextChannel, commands.Context, discord.User, discord.Member, discord.Thread
|
||||
discord.TextChannel,
|
||||
discord.VoiceChannel,
|
||||
commands.Context,
|
||||
discord.User,
|
||||
discord.Member,
|
||||
discord.Thread,
|
||||
],
|
||||
*,
|
||||
command: Optional[commands.Command] = None,
|
||||
@ -1260,7 +1259,7 @@ class Red(
|
||||
|
||||
Arguments
|
||||
---------
|
||||
channel : Union[`discord.TextChannel`, `commands.Context`, `discord.User`, `discord.Member`, `discord.Thread`]
|
||||
channel : Union[`discord.TextChannel`, `discord.VoiceChannel`, `commands.Context`, `discord.User`, `discord.Member`, `discord.Thread`]
|
||||
The target messageable object to check embed settings for.
|
||||
|
||||
Keyword Arguments
|
||||
@ -1307,7 +1306,7 @@ class Red(
|
||||
"You cannot pass a GroupChannel, DMChannel, or PartialMessageable to this method."
|
||||
)
|
||||
|
||||
if isinstance(channel, (discord.TextChannel, discord.Thread)):
|
||||
if isinstance(channel, (discord.TextChannel, discord.VoiceChannel, discord.Thread)):
|
||||
channel_id = channel.parent_id if isinstance(channel, discord.Thread) else channel.id
|
||||
|
||||
if check_permissions and not channel.permissions_for(channel.guild.me).embed_links:
|
||||
@ -1371,7 +1370,7 @@ class Red(
|
||||
scopes = ("bot", "applications.commands") if commands_scope else ("bot",)
|
||||
perms_int = data["invite_perm"]
|
||||
permissions = discord.Permissions(perms_int)
|
||||
return discord.utils.oauth_url(self._app_info.id, permissions=permissions, scopes=scopes)
|
||||
return discord.utils.oauth_url(self.application_id, permissions=permissions, scopes=scopes)
|
||||
|
||||
async def is_invite_url_public(self) -> bool:
|
||||
"""
|
||||
@ -1613,7 +1612,6 @@ class Red(
|
||||
cogname: str,
|
||||
/,
|
||||
*,
|
||||
# DEP-WARN: MISSING is implementation detail
|
||||
guild: Optional[discord.abc.Snowflake] = discord.utils.MISSING,
|
||||
guilds: List[discord.abc.Snowflake] = discord.utils.MISSING,
|
||||
) -> Optional[commands.Cog]:
|
||||
@ -1725,7 +1723,6 @@ class Red(
|
||||
/,
|
||||
*,
|
||||
override: bool = False,
|
||||
# DEP-WARN: MISSING is implementation detail
|
||||
guild: Optional[discord.abc.Snowflake] = discord.utils.MISSING,
|
||||
guilds: List[discord.abc.Snowflake] = discord.utils.MISSING,
|
||||
) -> None:
|
||||
@ -1880,7 +1877,7 @@ class Red(
|
||||
|
||||
async def get_owner_notification_destinations(
|
||||
self,
|
||||
) -> List[Union[discord.TextChannel, discord.User]]:
|
||||
) -> List[Union[discord.TextChannel, discord.VoiceChannel, discord.User]]:
|
||||
"""
|
||||
Gets the users and channels to send to
|
||||
"""
|
||||
|
||||
@ -45,7 +45,6 @@ from .help import (
|
||||
)
|
||||
from .requires import (
|
||||
CheckPredicate as CheckPredicate,
|
||||
DM_PERMS as DM_PERMS,
|
||||
GlobalPermissionModel as GlobalPermissionModel,
|
||||
GuildPermissionModel as GuildPermissionModel,
|
||||
PermissionModel as PermissionModel,
|
||||
@ -189,4 +188,14 @@ from discord.ext.commands import (
|
||||
bot_has_any_role as bot_has_any_role,
|
||||
before_invoke as before_invoke,
|
||||
after_invoke as after_invoke,
|
||||
CurrentChannel as CurrentChannel,
|
||||
Author as Author,
|
||||
param as param,
|
||||
MissingRequiredAttachment as MissingRequiredAttachment,
|
||||
Parameter as Parameter,
|
||||
ForumChannelConverter as ForumChannelConverter,
|
||||
CurrentGuild as CurrentGuild,
|
||||
Range as Range,
|
||||
RangeError as RangeError,
|
||||
parameter as parameter,
|
||||
)
|
||||
|
||||
@ -339,7 +339,7 @@ if TYPE_CHECKING or os.getenv("BUILDING_DOCS", False):
|
||||
...
|
||||
|
||||
@property
|
||||
def channel(self) -> Union[discord.TextChannel, discord.Thread]:
|
||||
def channel(self) -> Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]:
|
||||
...
|
||||
|
||||
@property
|
||||
|
||||
@ -842,12 +842,12 @@ class RedHelpFormatter(HelpFormatterABC):
|
||||
if (
|
||||
not use_DMs # we're not in DMs
|
||||
and delete_delay > 0 # delete delay is enabled
|
||||
and ctx.channel.permissions_for(ctx.me).manage_messages # we can manage messages
|
||||
and ctx.bot_permissions.manage_messages # we can manage messages
|
||||
):
|
||||
# We need to wrap this in a task to not block after-sending-help interactions.
|
||||
# The channel has to be TextChannel or Thread as we can't bulk-delete from DMs
|
||||
async def _delete_delay_help(
|
||||
channel: Union[discord.TextChannel, discord.Thread],
|
||||
channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
|
||||
messages: List[discord.Message],
|
||||
delay: int,
|
||||
):
|
||||
|
||||
@ -40,7 +40,6 @@ if TYPE_CHECKING:
|
||||
|
||||
__all__ = [
|
||||
"CheckPredicate",
|
||||
"DM_PERMS",
|
||||
"GlobalPermissionModel",
|
||||
"GuildPermissionModel",
|
||||
"PermissionModel",
|
||||
@ -75,6 +74,7 @@ GlobalPermissionModel = Union[
|
||||
discord.User,
|
||||
discord.VoiceChannel,
|
||||
discord.TextChannel,
|
||||
discord.ForumChannel,
|
||||
discord.CategoryChannel,
|
||||
discord.Role,
|
||||
discord.Guild,
|
||||
@ -83,6 +83,7 @@ GuildPermissionModel = Union[
|
||||
discord.Member,
|
||||
discord.VoiceChannel,
|
||||
discord.TextChannel,
|
||||
discord.ForumChannel,
|
||||
discord.CategoryChannel,
|
||||
discord.Role,
|
||||
discord.Guild,
|
||||
@ -90,22 +91,6 @@ GuildPermissionModel = Union[
|
||||
PermissionModel = Union[GlobalPermissionModel, GuildPermissionModel]
|
||||
CheckPredicate = Callable[["Context"], Union[Optional[bool], Awaitable[Optional[bool]]]]
|
||||
|
||||
# Here we are trying to model DM permissions as closely as possible. The only
|
||||
# discrepancy I've found is that users can pin messages, but they cannot delete them.
|
||||
# This means manage_messages is only half True, so it's left as False.
|
||||
# This is also the same as the permissions returned when `permissions_for` is used in DM.
|
||||
DM_PERMS = discord.Permissions.none()
|
||||
DM_PERMS.update(
|
||||
add_reactions=True,
|
||||
attach_files=True,
|
||||
embed_links=True,
|
||||
external_emojis=True,
|
||||
mention_everyone=True,
|
||||
read_message_history=True,
|
||||
read_messages=True,
|
||||
send_messages=True,
|
||||
)
|
||||
|
||||
|
||||
class PrivilegeLevel(enum.IntEnum):
|
||||
"""Enumeration for special privileges."""
|
||||
@ -520,15 +505,11 @@ class Requires:
|
||||
return await self._transition_state(ctx)
|
||||
|
||||
async def _verify_bot(self, ctx: "Context") -> None:
|
||||
if ctx.guild is None:
|
||||
bot_user = ctx.bot.user
|
||||
else:
|
||||
bot_user = ctx.guild.me
|
||||
cog = ctx.cog
|
||||
if cog and await ctx.bot.cog_disabled_in_guild(cog, ctx.guild):
|
||||
if ctx.guild is not None and cog and await ctx.bot.cog_disabled_in_guild(cog, ctx.guild):
|
||||
raise discord.ext.commands.DisabledCommand()
|
||||
|
||||
bot_perms = ctx.channel.permissions_for(bot_user)
|
||||
bot_perms = ctx.bot_permissions
|
||||
if not (bot_perms.administrator or bot_perms >= self.bot_perms):
|
||||
raise BotMissingPermissions(missing=self._missing_perms(self.bot_perms, bot_perms))
|
||||
|
||||
@ -574,7 +555,7 @@ class Requires:
|
||||
return False
|
||||
|
||||
if self.user_perms is not None:
|
||||
user_perms = ctx.channel.permissions_for(ctx.author)
|
||||
user_perms = ctx.permissions
|
||||
if user_perms.administrator or user_perms >= self.user_perms:
|
||||
return True
|
||||
|
||||
@ -633,17 +614,6 @@ class Requires:
|
||||
return True
|
||||
return await discord.utils.async_all(check(ctx) for check in self.checks)
|
||||
|
||||
@staticmethod
|
||||
def _get_perms_for(ctx: "Context", user: discord.abc.User) -> discord.Permissions:
|
||||
if ctx.guild is None:
|
||||
return DM_PERMS
|
||||
else:
|
||||
return ctx.channel.permissions_for(user)
|
||||
|
||||
@classmethod
|
||||
def _get_bot_perms(cls, ctx: "Context") -> discord.Permissions:
|
||||
return cls._get_perms_for(ctx, ctx.guild.me if ctx.guild else ctx.bot.user)
|
||||
|
||||
@staticmethod
|
||||
def _missing_perms(
|
||||
required: discord.Permissions, actual: discord.Permissions
|
||||
@ -656,13 +626,6 @@ class Requires:
|
||||
relative_complement = required.value & ~actual.value
|
||||
return discord.Permissions(relative_complement)
|
||||
|
||||
@staticmethod
|
||||
def _member_as_user(member: discord.abc.User) -> discord.User:
|
||||
if isinstance(member, discord.Member):
|
||||
# noinspection PyProtectedMember
|
||||
return member._user
|
||||
return member
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<Requires privilege_level={self.privilege_level!r} user_perms={self.user_perms!r} "
|
||||
|
||||
@ -591,7 +591,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
"""
|
||||
|
||||
# Can't check this as a command check, and want to prompt DMs as an option.
|
||||
if not ctx.channel.permissions_for(ctx.me).attach_files:
|
||||
if not ctx.bot_permissions.attach_files:
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
return await ctx.send(_("I need to be able to attach files (try in DMs?)."))
|
||||
|
||||
@ -1349,7 +1349,12 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
@embedset.command(name="channel")
|
||||
@checks.guildowner_or_permissions(administrator=True)
|
||||
@commands.guild_only()
|
||||
async def embedset_channel(self, ctx: commands.Context, enabled: bool = None):
|
||||
async def embedset_channel(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
channel: Union[discord.TextChannel, discord.VoiceChannel, discord.ForumChannel],
|
||||
enabled: bool = None,
|
||||
):
|
||||
"""
|
||||
Set's a channel's embed setting.
|
||||
|
||||
@ -1361,27 +1366,20 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
To see full evaluation order of embed settings, run `[p]help embedset`.
|
||||
|
||||
**Examples:**
|
||||
- `[p]embedset channel False` - Disables embeds in this channel.
|
||||
- `[p]embedset channel` - Resets value to use guild default.
|
||||
- `[p]embedset channel #text-channel False` - Disables embeds in the #text-channel.
|
||||
- `[p]embedset channel #forum-channel disable` - Disables embeds in the #forum-channel.
|
||||
- `[p]embedset channel #text-channel` - Resets value to use guild default in the #text-channel .
|
||||
|
||||
**Arguments:**
|
||||
- `<channel>` - The text, voice, or forum channel to set embed setting for.
|
||||
- `[enabled]` - Whether to use embeds in this channel. Leave blank to reset to default.
|
||||
"""
|
||||
if isinstance(ctx.channel, discord.Thread):
|
||||
await ctx.send(
|
||||
_(
|
||||
"This setting cannot be set for threads. If you want to set this for"
|
||||
" the parent channel, send the command in that channel."
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
if enabled is None:
|
||||
await self.bot._config.channel(ctx.channel).embeds.clear()
|
||||
await self.bot._config.channel(channel).embeds.clear()
|
||||
await ctx.send(_("Embeds will now fall back to the global setting."))
|
||||
return
|
||||
|
||||
await self.bot._config.channel(ctx.channel).embeds.set(enabled)
|
||||
await self.bot._config.channel(channel).embeds.set(enabled)
|
||||
await ctx.send(
|
||||
_("Embeds are now {} for this channel.").format(
|
||||
_("enabled") if enabled else _("disabled")
|
||||
@ -2247,7 +2245,11 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
|
||||
@modlogset.command(aliases=["channel"], name="modlog")
|
||||
@commands.guild_only()
|
||||
async def modlogset_modlog(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
||||
async def modlogset_modlog(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
channel: Union[discord.TextChannel, discord.VoiceChannel] = None,
|
||||
):
|
||||
"""Set a channel as the modlog.
|
||||
|
||||
Omit `[channel]` to disable the modlog.
|
||||
@ -3131,7 +3133,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
view=SetApiView(default_service=service),
|
||||
)
|
||||
else:
|
||||
if ctx.channel.permissions_for(ctx.me).manage_messages:
|
||||
if ctx.bot_permissions.manage_messages:
|
||||
await ctx.message.delete()
|
||||
await ctx.bot.set_shared_api_tokens(service, **tokens)
|
||||
await ctx.send(_("`{service}` API tokens have been set.").format(service=service))
|
||||
@ -3241,7 +3243,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
|
||||
@_set_ownernotifications.command(name="adddestination")
|
||||
async def _set_ownernotifications_adddestination(
|
||||
self, ctx: commands.Context, *, channel: discord.TextChannel
|
||||
self, ctx: commands.Context, *, channel: Union[discord.TextChannel, discord.VoiceChannel]
|
||||
):
|
||||
"""
|
||||
Adds a destination text channel to receive owner notifications.
|
||||
@ -3263,7 +3265,10 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
name="removedestination", aliases=["remdestination", "deletedestination", "deldestination"]
|
||||
)
|
||||
async def _set_ownernotifications_removedestination(
|
||||
self, ctx: commands.Context, *, channel: Union[discord.TextChannel, int]
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
*,
|
||||
channel: Union[discord.TextChannel, discord.VoiceChannel, int],
|
||||
):
|
||||
"""
|
||||
Removes a destination text channel from receiving owner notifications.
|
||||
@ -4136,8 +4141,11 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
async def diagnoseissues(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread]],
|
||||
member: Union[discord.Member, discord.User],
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]
|
||||
] = commands.CurrentChannel,
|
||||
# avoid non-default argument following default argument by using empty param()
|
||||
member: Union[discord.Member, discord.User] = commands.param(),
|
||||
*,
|
||||
command_name: str,
|
||||
) -> None:
|
||||
@ -4155,12 +4163,10 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
- `<member>` - The member that should be considered as the command caller.
|
||||
- `<command_name>` - The name of the command to test.
|
||||
"""
|
||||
if channel is None:
|
||||
channel = ctx.channel
|
||||
if not isinstance(channel, (discord.TextChannel, discord.Thread)):
|
||||
if ctx.guild is None:
|
||||
await ctx.send(
|
||||
_(
|
||||
"The text channel or thread needs to be passed"
|
||||
"A text channel, voice channel, or thread needs to be passed"
|
||||
" when using this command in DMs."
|
||||
)
|
||||
)
|
||||
@ -5124,9 +5130,13 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
async def ignore_channel(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.CategoryChannel, discord.Thread]
|
||||
] = None,
|
||||
channel: Union[
|
||||
discord.TextChannel,
|
||||
discord.VoiceChannel,
|
||||
discord.ForumChannel,
|
||||
discord.CategoryChannel,
|
||||
discord.Thread,
|
||||
] = commands.CurrentChannel,
|
||||
):
|
||||
"""
|
||||
Ignore commands in the channel, thread, or category.
|
||||
@ -5144,8 +5154,6 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
**Arguments:**
|
||||
- `<channel>` - The channel to ignore. This can also be a thread or category channel.
|
||||
"""
|
||||
if not channel:
|
||||
channel = ctx.channel
|
||||
if not await self.bot._ignored_cache.get_ignored_channel(channel):
|
||||
await self.bot._ignored_cache.set_ignored_channel(channel, True)
|
||||
await ctx.send(_("Channel added to ignore list."))
|
||||
@ -5180,9 +5188,13 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
async def unignore_channel(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.CategoryChannel, discord.Thread]
|
||||
] = None,
|
||||
channel: Union[
|
||||
discord.TextChannel,
|
||||
discord.VoiceChannel,
|
||||
discord.ForumChannel,
|
||||
discord.CategoryChannel,
|
||||
discord.Thread,
|
||||
] = commands.CurrentChannel,
|
||||
):
|
||||
"""
|
||||
Remove a channel, thread, or category from the ignore list.
|
||||
@ -5198,9 +5210,6 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
**Arguments:**
|
||||
- `<channel>` - The channel to unignore. This can also be a thread or category channel.
|
||||
"""
|
||||
if not channel:
|
||||
channel = ctx.channel
|
||||
|
||||
if await self.bot._ignored_cache.get_ignored_channel(channel):
|
||||
await self.bot._ignored_cache.set_ignored_channel(channel, False)
|
||||
await ctx.send(_("Channel removed from ignore list."))
|
||||
@ -5225,7 +5234,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
|
||||
async def count_ignored(self, ctx: commands.Context):
|
||||
category_channels: List[discord.CategoryChannel] = []
|
||||
text_channels: List[discord.TextChannel] = []
|
||||
channels: List[Union[discord.TextChannel, discord.VoiceChannel, discord.ForumChannel]] = []
|
||||
threads: List[discord.Thread] = []
|
||||
if await self.bot._ignored_cache.get_ignored_guild(ctx.guild):
|
||||
return _("This server is currently being ignored.")
|
||||
@ -5234,7 +5243,19 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
if await self.bot._ignored_cache.get_ignored_channel(channel.category):
|
||||
category_channels.append(channel.category)
|
||||
if await self.bot._ignored_cache.get_ignored_channel(channel, check_category=False):
|
||||
text_channels.append(channel)
|
||||
channels.append(channel)
|
||||
for channel in ctx.guild.voice_channels:
|
||||
if channel.category and channel.category not in category_channels:
|
||||
if await self.bot._ignored_cache.get_ignored_channel(channel.category):
|
||||
category_channels.append(channel.category)
|
||||
if await self.bot._ignored_cache.get_ignored_channel(channel, check_category=False):
|
||||
channels.append(channel)
|
||||
for channel in ctx.guild.forum_channels:
|
||||
if channel.category and channel.category not in category_channels:
|
||||
if await self.bot._ignored_cache.get_ignored_channel(channel.category):
|
||||
category_channels.append(channel.category)
|
||||
if await self.bot._ignored_cache.get_ignored_channel(channel, check_category=False):
|
||||
channels.append(channel)
|
||||
for thread in ctx.guild.threads:
|
||||
if await self.bot._ignored_cache.get_ignored_channel(thread, check_category=False):
|
||||
threads.append(thread)
|
||||
@ -5242,9 +5263,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
cat_str = (
|
||||
humanize_list([c.name for c in category_channels]) if category_channels else _("None")
|
||||
)
|
||||
chan_str = (
|
||||
humanize_list([c.mention for c in text_channels]) if text_channels else _("None")
|
||||
)
|
||||
chan_str = humanize_list([c.mention for c in channels]) if channels else _("None")
|
||||
thread_str = humanize_list([c.mention for c in threads]) if threads else _("None")
|
||||
msg = _(
|
||||
"Currently ignored categories: {categories}\n"
|
||||
@ -5255,7 +5274,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
|
||||
# Removing this command from forks is a violation of the GPLv3 under which it is licensed.
|
||||
# Otherwise interfering with the ability for this command to be accessible is also a violation.
|
||||
@commands.cooldown(1, 180, lambda msg: (msg.channel.id, msg.author.id))
|
||||
@commands.cooldown(1, 180, lambda ctx: (ctx.message.channel.id, ctx.message.author.id))
|
||||
@commands.command(
|
||||
cls=commands.commands._AlwaysAvailableCommand,
|
||||
name="licenseinfo",
|
||||
|
||||
@ -30,7 +30,6 @@ from .utils._internal_utils import (
|
||||
expected_version,
|
||||
fetch_latest_red_version_info,
|
||||
send_to_owners_with_prefix_replaced,
|
||||
get_converter,
|
||||
)
|
||||
from .utils.chat_formatting import inline, bordered, format_perms_list, humanize_timedelta
|
||||
|
||||
@ -71,7 +70,7 @@ def init_events(bot, cli_flags):
|
||||
guilds = len(bot.guilds)
|
||||
users = len(set([m for m in bot.get_all_members()]))
|
||||
|
||||
invite_url = discord.utils.oauth_url(bot._app_info.id, scopes=("bot",))
|
||||
invite_url = discord.utils.oauth_url(bot.application_id, scopes=("bot",))
|
||||
|
||||
prefixes = cli_flags.prefix or (await bot._config.prefix())
|
||||
lang = await bot._config.locale()
|
||||
@ -222,7 +221,7 @@ def init_events(bot, cli_flags):
|
||||
await ctx.send_help()
|
||||
elif isinstance(error, commands.BadArgument):
|
||||
if isinstance(error.__cause__, ValueError):
|
||||
converter = get_converter(ctx.current_parameter)
|
||||
converter = ctx.current_parameter.converter
|
||||
argument = ctx.current_argument
|
||||
if converter is int:
|
||||
await ctx.send(_('"{argument}" is not an integer.').format(argument=argument))
|
||||
|
||||
@ -349,12 +349,12 @@ class Case:
|
||||
self.message = message
|
||||
|
||||
@property
|
||||
def parent_channel(self) -> Optional[discord.TextChannel]:
|
||||
def parent_channel(self) -> Optional[Union[discord.TextChannel, discord.ForumChannel]]:
|
||||
"""
|
||||
The parent text channel of the thread in `channel`.
|
||||
The parent text/forum channel of the thread in `channel`.
|
||||
|
||||
This will be `None` if `channel` is not a thread
|
||||
and when the parent text channel is not in cache (probably due to removal).
|
||||
and when the parent text/forum channel is not in cache (probably due to removal).
|
||||
"""
|
||||
if self.parent_channel_id is None:
|
||||
return None
|
||||
@ -645,13 +645,18 @@ class Case:
|
||||
|
||||
@classmethod
|
||||
async def from_json(
|
||||
cls, mod_channel: discord.TextChannel, bot: Red, case_number: int, data: dict, **kwargs
|
||||
cls,
|
||||
mod_channel: Union[discord.TextChannel, discord.VoiceChannel],
|
||||
bot: Red,
|
||||
case_number: int,
|
||||
data: dict,
|
||||
**kwargs,
|
||||
):
|
||||
"""Get a Case object from the provided information
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mod_channel: discord.TextChannel
|
||||
mod_channel: `discord.TextChannel` or `discord.VoiceChannel`
|
||||
The mod log channel for the guild
|
||||
bot: Red
|
||||
The bot's instance. Needed to get the target user
|
||||
@ -1228,7 +1233,9 @@ async def register_casetypes(new_types: List[dict]) -> List[CaseType]:
|
||||
return type_list
|
||||
|
||||
|
||||
async def get_modlog_channel(guild: discord.Guild) -> discord.TextChannel:
|
||||
async def get_modlog_channel(
|
||||
guild: discord.Guild,
|
||||
) -> Union[discord.TextChannel, discord.VoiceChannel]:
|
||||
"""
|
||||
Get the current modlog channel.
|
||||
|
||||
@ -1239,7 +1246,7 @@ async def get_modlog_channel(guild: discord.Guild) -> discord.TextChannel:
|
||||
|
||||
Returns
|
||||
-------
|
||||
`discord.TextChannel`
|
||||
`discord.TextChannel` or `discord.VoiceChannel`
|
||||
The channel object representing the modlog channel.
|
||||
|
||||
Raises
|
||||
@ -1259,7 +1266,7 @@ async def get_modlog_channel(guild: discord.Guild) -> discord.TextChannel:
|
||||
|
||||
|
||||
async def set_modlog_channel(
|
||||
guild: discord.Guild, channel: Union[discord.TextChannel, None]
|
||||
guild: discord.Guild, channel: Union[discord.TextChannel, discord.VoiceChannel, None]
|
||||
) -> bool:
|
||||
"""
|
||||
Changes the modlog channel
|
||||
@ -1268,7 +1275,7 @@ async def set_modlog_channel(
|
||||
----------
|
||||
guild: `discord.Guild`
|
||||
The guild to set a mod log channel for
|
||||
channel: `discord.TextChannel` or `None`
|
||||
channel: `discord.TextChannel`, `discord.VoiceChannel`, or `None`
|
||||
The channel to be set as modlog channel
|
||||
|
||||
Returns
|
||||
|
||||
@ -154,7 +154,11 @@ class IgnoreManager:
|
||||
self._cached_guilds: Dict[int, bool] = {}
|
||||
|
||||
async def get_ignored_channel(
|
||||
self, channel: Union[discord.TextChannel, discord.Thread], check_category: bool = True
|
||||
self,
|
||||
channel: Union[
|
||||
discord.TextChannel, discord.VoiceChannel, discord.ForumChannel, discord.Thread
|
||||
],
|
||||
check_category: bool = True,
|
||||
) -> bool:
|
||||
ret: bool
|
||||
|
||||
@ -181,7 +185,13 @@ class IgnoreManager:
|
||||
|
||||
async def set_ignored_channel(
|
||||
self,
|
||||
channel: Union[discord.TextChannel, discord.Thread, discord.CategoryChannel],
|
||||
channel: Union[
|
||||
discord.TextChannel,
|
||||
discord.VoiceChannel,
|
||||
discord.Thread,
|
||||
discord.ForumChannel,
|
||||
discord.CategoryChannel,
|
||||
],
|
||||
set_to: bool,
|
||||
):
|
||||
cid: int = channel.id
|
||||
|
||||
@ -34,7 +34,9 @@ from discord.utils import maybe_coroutine
|
||||
from redbot.core import commands
|
||||
|
||||
if TYPE_CHECKING:
|
||||
GuildMessageable = Union[commands.GuildContext, discord.abc.GuildChannel, discord.Thread]
|
||||
GuildMessageable = Union[
|
||||
commands.GuildContext, discord.TextChannel, discord.VoiceChannel, discord.Thread
|
||||
]
|
||||
DMMessageable = Union[commands.DMContext, discord.Member, discord.User, discord.DMChannel]
|
||||
|
||||
__all__ = (
|
||||
|
||||
@ -32,7 +32,6 @@ from typing import (
|
||||
import aiohttp
|
||||
import discord
|
||||
import pkg_resources
|
||||
from discord.ext.commands.converter import get_converter # DEP-WARN
|
||||
from fuzzywuzzy import fuzz, process
|
||||
from rich.progress import ProgressColumn
|
||||
from rich.progress_bar import ProgressBar
|
||||
@ -60,7 +59,6 @@ __all__ = (
|
||||
"deprecated_removed",
|
||||
"RichIndefiniteBarColumn",
|
||||
"cli_level_to_log_level",
|
||||
"get_converter",
|
||||
)
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
@ -10,7 +10,8 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
async def mass_purge(
|
||||
messages: List[discord.Message], channel: Union[discord.TextChannel, discord.Thread]
|
||||
messages: List[discord.Message],
|
||||
channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
|
||||
):
|
||||
"""Bulk delete messages from a channel.
|
||||
|
||||
@ -26,7 +27,7 @@ async def mass_purge(
|
||||
----------
|
||||
messages : `list` of `discord.Message`
|
||||
The messages to bulk delete.
|
||||
channel : `discord.TextChannel` or `discord.Thread`
|
||||
channel : `discord.TextChannel`, `discord.VoiceChannel`, or `discord.Thread`
|
||||
The channel to delete messages from.
|
||||
|
||||
Raises
|
||||
@ -247,7 +248,7 @@ async def check_permissions(ctx: "Context", perms: Dict[str, bool]) -> bool:
|
||||
return True
|
||||
elif not perms:
|
||||
return False
|
||||
resolved = ctx.channel.permissions_for(ctx.author)
|
||||
resolved = ctx.permissions
|
||||
|
||||
return resolved.administrator or all(
|
||||
getattr(resolved, name, None) == value for name, value in perms.items()
|
||||
|
||||
@ -67,7 +67,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
def same_context(
|
||||
cls,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the message fits the described context.
|
||||
@ -76,7 +78,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
----------
|
||||
ctx : Optional[Context]
|
||||
The current invocation context.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
The channel we expect a message in. If unspecified,
|
||||
defaults to ``ctx.channel``. If ``ctx`` is unspecified
|
||||
too, the message's channel will be ignored.
|
||||
@ -104,7 +106,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
def cancelled(
|
||||
cls,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the message is ``[p]cancel``.
|
||||
@ -113,7 +117,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
----------
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -133,7 +137,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
def yes_or_no(
|
||||
cls,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the message is "yes"/"y" or "no"/"n".
|
||||
@ -145,7 +151,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
----------
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -176,7 +182,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
def valid_int(
|
||||
cls,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response is an integer.
|
||||
@ -187,7 +195,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
----------
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -216,7 +224,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
def valid_float(
|
||||
cls,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response is a float.
|
||||
@ -227,7 +237,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
----------
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -256,7 +266,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
def positive(
|
||||
cls,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response is a positive number.
|
||||
@ -267,7 +279,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
----------
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -300,7 +312,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
def valid_role(
|
||||
cls,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread]] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response refers to a role in the current guild.
|
||||
@ -313,7 +325,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
----------
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -344,7 +356,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
def valid_member(
|
||||
cls,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread]] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response refers to a member in the current guild.
|
||||
@ -357,7 +369,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
----------
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -392,7 +404,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
def valid_text_channel(
|
||||
cls,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread]] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response refers to a text channel in the current guild.
|
||||
@ -405,7 +417,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
----------
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -440,7 +452,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
def has_role(
|
||||
cls,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread]] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response refers to a role which the author has.
|
||||
@ -454,7 +466,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
----------
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -479,7 +491,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
return False
|
||||
|
||||
role = self._find_role(guild, m.content)
|
||||
if role is None or role not in user.roles:
|
||||
if role is None or user.get_role(role.id) is None:
|
||||
return False
|
||||
|
||||
self.result = role
|
||||
@ -492,7 +504,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
cls,
|
||||
value: str,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response is equal to the specified value.
|
||||
@ -503,7 +517,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
The value to compare the response with.
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -522,7 +536,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
cls,
|
||||
value: str,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response *as lowercase* is equal to the specified value.
|
||||
@ -533,7 +549,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
The value to compare the response with.
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -552,7 +568,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
cls,
|
||||
value: Union[int, float],
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response is less than the specified value.
|
||||
@ -563,7 +581,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
The value to compare the response with.
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -583,7 +601,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
cls,
|
||||
value: Union[int, float],
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response is greater than the specified value.
|
||||
@ -594,7 +614,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
The value to compare the response with.
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -614,7 +634,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
cls,
|
||||
length: int,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response's length is less than the specified length.
|
||||
@ -625,7 +647,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
The value to compare the response's length with.
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -644,7 +666,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
cls,
|
||||
length: int,
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response's length is greater than the specified length.
|
||||
@ -655,7 +679,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
The value to compare the response's length with.
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -674,7 +698,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
cls,
|
||||
collection: Sequence[str],
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response is contained in the specified collection.
|
||||
@ -688,7 +714,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
The collection containing valid responses.
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -718,7 +744,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
cls,
|
||||
collection: Sequence[str],
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Same as :meth:`contained_in`, but the response is set to lowercase before matching.
|
||||
@ -729,7 +757,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
The collection containing valid lowercase responses.
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -759,7 +787,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
cls,
|
||||
pattern: Union[Pattern[str], str],
|
||||
ctx: Optional[commands.Context] = None,
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
|
||||
channel: Optional[
|
||||
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
|
||||
] = None,
|
||||
user: Optional[discord.abc.User] = None,
|
||||
) -> "MessagePredicate":
|
||||
"""Match if the response matches the specified regex pattern.
|
||||
@ -774,7 +804,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
The pattern to search for in the response.
|
||||
ctx : Optional[Context]
|
||||
Same as ``ctx`` in :meth:`same_context`.
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
|
||||
Same as ``channel`` in :meth:`same_context`.
|
||||
user : Optional[discord.abc.User]
|
||||
Same as ``user`` in :meth:`same_context`.
|
||||
@ -816,7 +846,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
|
||||
@staticmethod
|
||||
def _get_guild(
|
||||
ctx: Optional[commands.Context],
|
||||
channel: Optional[Union[discord.TextChannel, discord.Thread]],
|
||||
channel: Optional[Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]],
|
||||
user: Optional[discord.Member],
|
||||
) -> discord.Guild:
|
||||
if ctx is not None:
|
||||
|
||||
@ -57,7 +57,7 @@ class Tunnel(metaclass=TunnelMeta):
|
||||
----------
|
||||
sender: `discord.Member`
|
||||
The person who opened the tunnel
|
||||
origin: `discord.TextChannel` or `discord.Thread`
|
||||
origin: `discord.TextChannel`, `discord.VoiceChannel`, or `discord.Thread`
|
||||
The channel in which it was opened
|
||||
recipient: `discord.User`
|
||||
The user on the other end of the tunnel
|
||||
@ -67,7 +67,7 @@ class Tunnel(metaclass=TunnelMeta):
|
||||
self,
|
||||
*,
|
||||
sender: discord.Member,
|
||||
origin: Union[discord.TextChannel, discord.Thread],
|
||||
origin: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
|
||||
recipient: discord.User,
|
||||
):
|
||||
self.sender = sender
|
||||
@ -164,14 +164,14 @@ class Tunnel(metaclass=TunnelMeta):
|
||||
if images_only and a.height is None:
|
||||
# if this is None, it's not an image
|
||||
continue
|
||||
_fp = io.BytesIO()
|
||||
try:
|
||||
await a.save(_fp, use_cached=use_cached)
|
||||
file = await a.to_file()
|
||||
except discord.HTTPException as e:
|
||||
# this is required, because animated webp files aren't cached
|
||||
if not (e.status == 415 and images_only and use_cached):
|
||||
raise
|
||||
files.append(discord.File(_fp, filename=a.filename))
|
||||
else:
|
||||
files.append(file)
|
||||
return files
|
||||
|
||||
# Backwards-compatible typo fix (GH-2496)
|
||||
|
||||
@ -282,12 +282,10 @@ class RedRichHandler(RichHandler):
|
||||
|
||||
def init_logging(level: int, location: pathlib.Path, cli_flags: argparse.Namespace) -> None:
|
||||
root_logger = logging.getLogger()
|
||||
base_logger = logging.getLogger("red")
|
||||
base_logger.setLevel(level)
|
||||
root_logger.setLevel(level)
|
||||
# DEBUG logging for discord.py is a bit too ridiculous :)
|
||||
dpy_logger = logging.getLogger("discord")
|
||||
dpy_logger.setLevel(logging.WARNING)
|
||||
warnings_logger = logging.getLogger("py.warnings")
|
||||
warnings_logger.setLevel(logging.WARNING)
|
||||
dpy_logger.setLevel(logging.INFO)
|
||||
|
||||
rich_console = rich.get_console()
|
||||
rich.reconfigure(tab_size=4)
|
||||
|
||||
@ -51,7 +51,7 @@ install_requires =
|
||||
colorama==0.4.4
|
||||
commonmark==0.9.1
|
||||
contextlib2==21.6.0
|
||||
discord.py @ git+https://github.com/Rapptz/discord.py@3d914e08e0c7df370987592affb3655d2a12f7d1#egg=discord.py
|
||||
discord.py==2.0.1
|
||||
distro==1.6.0; sys_platform == "linux"
|
||||
fuzzywuzzy==0.18.0
|
||||
idna==3.2
|
||||
@ -66,7 +66,7 @@ install_requires =
|
||||
pytz==2021.1
|
||||
PyYAML==5.4.1
|
||||
Red-Commons==1.0.0
|
||||
Red-Lavalink==0.11.0rc0
|
||||
Red-Lavalink==0.11.0rc1
|
||||
rich==10.9.0
|
||||
schema==0.7.4
|
||||
six==1.16.0
|
||||
|
||||
@ -50,8 +50,21 @@ def test_dpy_commands_reexports():
|
||||
dpy_attrs.add(attr_name)
|
||||
|
||||
missing_attrs = dpy_attrs - set(commands.__dict__.keys())
|
||||
# temporarily ignore things related to app commands as the work on that is done separately
|
||||
missing_attrs -= {
|
||||
"GroupCog",
|
||||
"HybridGroup",
|
||||
"hybrid_group",
|
||||
"hybrid_command",
|
||||
"HybridCommand",
|
||||
"HybridCommandError",
|
||||
}
|
||||
|
||||
assert not missing_attrs
|
||||
if missing_attrs:
|
||||
pytest.fail(
|
||||
"redbot.core.commands is missing these names from discord.ext.commands: "
|
||||
+ ", ".join(missing_attrs)
|
||||
)
|
||||
|
||||
|
||||
def test_converter_timedelta():
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user