Add per-command embed settings (embed_requested) (#4049)

* Add per-command embed settings

* Update `[p]embedset showsettings`

* Use command.qualified_name in `[p]embedset` commands

* Fix the call to subcommand and simplify it

* And I oversimplified this

* Update end user messages to use 'guild' rather than 'server'

* meh

* This should be a named field

* Add check for Embed Links perm to confuse users a bit less

* Wrap this string in _()

* Let's use a different exception then...

* Let's clear the setting when we can in whole `[p]embedset`

* Add the order of checking to help of `[p]embedset`

* Add note about full evaluation order to subcommands
This commit is contained in:
jack1142 2021-04-04 21:37:46 +02:00 committed by GitHub
parent 03d72dd5fa
commit 560e1dfc3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 240 additions and 43 deletions

View File

@ -53,6 +53,7 @@ from .utils import common_filters, AsyncIter
from .utils._internal_utils import send_to_owners_with_prefix_replaced from .utils._internal_utils import send_to_owners_with_prefix_replaced
CUSTOM_GROUPS = "CUSTOM_GROUPS" CUSTOM_GROUPS = "CUSTOM_GROUPS"
COMMAND_SCOPE = "COMMAND"
SHARED_API_TOKENS = "SHARED_API_TOKENS" SHARED_API_TOKENS = "SHARED_API_TOKENS"
log = logging.getLogger("red") log = logging.getLogger("red")
@ -155,6 +156,12 @@ class RedBase(
self._config.init_custom(CUSTOM_GROUPS, 2) self._config.init_custom(CUSTOM_GROUPS, 2)
self._config.register_custom(CUSTOM_GROUPS) self._config.register_custom(CUSTOM_GROUPS)
# {COMMAND_NAME: {GUILD_ID: {...}}}
# GUILD_ID=0 for global setting
self._config.init_custom(COMMAND_SCOPE, 2)
self._config.register_custom(COMMAND_SCOPE, embeds=None)
# TODO: add cache for embed settings
self._config.init_custom(SHARED_API_TOKENS, 2) self._config.init_custom(SHARED_API_TOKENS, 2)
self._config.register_custom(SHARED_API_TOKENS) self._config.register_custom(SHARED_API_TOKENS)
self._prefix_cache = PrefixManager(self._config, cli_flags) self._prefix_cache = PrefixManager(self._config, cli_flags)
@ -1022,7 +1029,12 @@ class RedBase(
ctx, help_for, from_help_command=from_help_command ctx, help_for, from_help_command=from_help_command
) )
async def embed_requested(self, channel, user, command=None) -> bool: async def embed_requested(
self,
channel: Union[discord.abc.GuildChannel, discord.abc.PrivateChannel],
user: discord.abc.User,
command: Optional[commands.Command] = None,
) -> bool:
""" """
Determine if an embed is requested for a response. Determine if an embed is requested for a response.
@ -1032,26 +1044,38 @@ class RedBase(
The channel to check embed settings for. The channel to check embed settings for.
user : `discord.abc.User` user : `discord.abc.User`
The user to check embed settings for. The user to check embed settings for.
command command : `commands.Command`, optional
(Optional) the command ran. The command ran.
Returns Returns
------- -------
bool bool
:code:`True` if an embed is requested :code:`True` if an embed is requested
""" """
async def get_command_setting(guild_id: int) -> Optional[bool]:
if command is None:
return None
scope = self._config.custom(COMMAND_SCOPE, command.qualified_name, guild_id)
return await scope.embeds()
if isinstance(channel, discord.abc.PrivateChannel): if isinstance(channel, discord.abc.PrivateChannel):
user_setting = await self._config.user(user).embeds() if (user_setting := await self._config.user(user).embeds()) is not None:
if user_setting is not None:
return user_setting return user_setting
else: else:
channel_setting = await self._config.channel(channel).embeds() if (channel_setting := await self._config.channel(channel).embeds()) is not None:
if channel_setting is not None:
return channel_setting return channel_setting
guild_setting = await self._config.guild(channel.guild).embeds()
if guild_setting is not None: if (command_setting := await get_command_setting(channel.guild.id)) is not None:
return command_setting
if (guild_setting := await self._config.guild(channel.guild).embeds()) is not None:
return guild_setting return guild_setting
# XXX: maybe this should be checked before guild setting?
if (global_command_setting := await get_command_setting(0)) is not None:
return global_command_setting
global_setting = await self._config.embeds() global_setting = await self._config.embeds()
return global_setting return global_setting

View File

@ -1049,22 +1049,62 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
use embeds as a response to a command (for use embeds as a response to a command (for
commands that support it). The default is to commands that support it). The default is to
use embeds. use embeds.
The embed settings are checked until the first True/False in this order:
- In guild context:
1. Channel override ([p]embedset channel)
2. Server command override ([p]embedset command server)
3. Server override ([p]embedset server)
4. Global command override ([p]embedset command global)
5. Global setting ([p]embedset global)
- In DM context:
1. User override ([p]embedset user)
2. Global command override ([p]embedset command global)
3. Global setting ([p]embedset global)
""" """
@embedset.command(name="showsettings") @embedset.command(name="showsettings")
async def embedset_showsettings(self, ctx: commands.Context): async def embedset_showsettings(self, ctx: commands.Context, command_name: str = None) -> None:
"""Show the current embed settings.""" """Show the current embed settings."""
if command_name is not None:
command_obj: Optional[commands.Command] = ctx.bot.get_command(command_name)
if command_obj is None:
await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.")
)
return
# qualified name might be different if alias was passed to this command
command_name = command_obj.qualified_name
text = _("Embed settings:\n\n") text = _("Embed settings:\n\n")
global_default = await self.bot._config.embeds() global_default = await self.bot._config.embeds()
text += _("Global default: {}\n").format(global_default) text += _("Global default: {value}\n").format(value=global_default)
if command_name is not None:
scope = self.bot._config.custom("COMMAND", command_name, 0)
global_command_setting = await scope.embeds()
text += _("Global command setting for {command} command: {value}\n").format(
command=inline(command_name), value=global_command_setting
)
if ctx.guild: if ctx.guild:
guild_setting = await self.bot._config.guild(ctx.guild).embeds() guild_setting = await self.bot._config.guild(ctx.guild).embeds()
text += _("Guild setting: {}\n").format(guild_setting) text += _("Guild setting: {value}\n").format(value=guild_setting)
if command_name is not None:
scope = self.bot._config.custom("COMMAND", command_name, ctx.guild.id)
command_setting = await scope.embeds()
text += _("Server command setting for {command} command: {value}\n").format(
command=inline(command_name), value=command_setting
)
if ctx.channel: if ctx.channel:
channel_setting = await self.bot._config.channel(ctx.channel).embeds() channel_setting = await self.bot._config.channel(ctx.channel).embeds()
text += _("Channel setting: {}\n").format(channel_setting) text += _("Channel setting: {value}\n").format(value=channel_setting)
user_setting = await self.bot._config.user(ctx.author).embeds() user_setting = await self.bot._config.user(ctx.author).embeds()
text += _("User setting: {}").format(user_setting) text += _("User setting: {value}").format(value=user_setting)
await ctx.send(box(text)) await ctx.send(box(text))
@embedset.command(name="global") @embedset.command(name="global")
@ -1076,12 +1116,16 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
This is used as a fallback if the user This is used as a fallback if the user
or guild hasn't set a preference. The or guild hasn't set a preference. The
default is to use embeds. default is to use embeds.
To see full evaluation order of embed settings, run `[p]help embedset`
""" """
current = await self.bot._config.embeds() current = await self.bot._config.embeds()
await self.bot._config.embeds.set(not current) if current:
await ctx.send( await self.bot._config.embeds.set(False)
_("Embeds are now {} by default.").format(_("disabled") if current else _("enabled")) await ctx.send(_("Embeds are now disabled by default."))
) else:
await self.bot._config.embeds.clear()
await ctx.send(_("Embeds are now enabled by default."))
@embedset.command(name="server", aliases=["guild"]) @embedset.command(name="server", aliases=["guild"])
@checks.guildowner_or_permissions(administrator=True) @checks.guildowner_or_permissions(administrator=True)
@ -1095,16 +1139,139 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
If set, this is used instead of the global default If set, this is used instead of the global default
to determine whether or not to use embeds. This is to determine whether or not to use embeds. This is
used for all commands done in a guild channel except used for all commands done in a server channel.
for help commands.
To see full evaluation order of embed settings, run `[p]help embedset`
""" """
await self.bot._config.guild(ctx.guild).embeds.set(enabled)
if enabled is None: if enabled is None:
await self.bot._config.guild(ctx.guild).embeds.clear()
await ctx.send(_("Embeds will now fall back to the global setting.")) await ctx.send(_("Embeds will now fall back to the global setting."))
return
await self.bot._config.guild(ctx.guild).embeds.set(enabled)
await ctx.send(
_("Embeds are now enabled for this guild.")
if enabled
else _("Embeds are now disabled for this guild.")
)
@checks.guildowner_or_permissions(administrator=True)
@embedset.group(name="command", invoke_without_command=True)
async def embedset_command(
self, ctx: commands.Context, command_name: str, enabled: bool = None
) -> None:
"""
Toggle the command's embed setting.
If you're the bot owner, this will change command's embed setting
globally by default.
To see full evaluation order of embed settings, run `[p]help embedset`
"""
# Select the scope based on the author's privileges
if await ctx.bot.is_owner(ctx.author):
await self.embedset_command_global(ctx, command_name, enabled)
else:
await self.embedset_command_guild(ctx, command_name, enabled)
def _check_if_command_requires_embed_links(self, command_obj: commands.Command) -> None:
for command in itertools.chain((command_obj,), command_obj.parents):
if command_obj.requires.bot_perms.embed_links:
# a slight abuse of this exception to save myself two lines later...
raise commands.UserFeedbackCheckFailure(
_(
"The passed command requires Embed Links permission"
" and therefore cannot be set to not use embeds."
)
)
@commands.is_owner()
@embedset_command.command(name="global")
async def embedset_command_global(
self, ctx: commands.Context, command_name: str, enabled: bool = None
):
"""
Toggle the commmand's embed setting.
If enabled is None, the setting will be unset and
the global default will be used instead.
If set, this is used instead of the global default
to determine whether or not to use embeds.
To see full evaluation order of embed settings, run `[p]help embedset`
"""
command_obj: Optional[commands.Command] = ctx.bot.get_command(command_name)
if command_obj is None:
await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.")
)
return
self._check_if_command_requires_embed_links(command_obj)
# qualified name might be different if alias was passed to this command
command_name = command_obj.qualified_name
if enabled is None:
await self.bot._config.custom("COMMAND", command_name, 0).embeds.clear()
await ctx.send(_("Embeds will now fall back to the global setting."))
return
await self.bot._config.custom("COMMAND", command_name, 0).embeds.set(enabled)
if enabled:
await ctx.send(
_("Embeds are now enabled for {command_name} command.").format(
command_name=inline(command_name)
)
)
else: else:
await ctx.send( await ctx.send(
_("Embeds are now {} for this guild.").format( _("Embeds are now disabled for {command_name} command.").format(
_("enabled") if enabled else _("disabled") command_name=inline(command_name)
)
)
@commands.guild_only()
@embedset_command.command(name="server", aliases=["guild"])
async def embedset_command_guild(
self, ctx: commands.GuildContext, command_name: str, enabled: bool = None
):
"""
Toggle the commmand's embed setting.
If enabled is None, the setting will be unset and
the server default will be used instead.
If set, this is used instead of the server default
to determine whether or not to use embeds.
To see full evaluation order of embed settings, run `[p]help embedset`
"""
command_obj: Optional[commands.Command] = ctx.bot.get_command(command_name)
if command_obj is None:
await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.")
)
return
self._check_if_command_requires_embed_links(command_obj)
# qualified name might be different if alias was passed to this command
command_name = command_obj.qualified_name
if enabled is None:
await self.bot._config.custom("COMMAND", command_name, ctx.guild.id).embeds.clear()
await ctx.send(_("Embeds will now fall back to the server setting."))
return
await self.bot._config.custom("COMMAND", command_name, ctx.guild.id).embeds.set(enabled)
if enabled:
await ctx.send(
_("Embeds are now enabled for {command_name} command.").format(
command_name=inline(command_name)
)
)
else:
await ctx.send(
_("Embeds are now disabled for {command_name} command.").format(
command_name=inline(command_name)
) )
) )
@ -1118,15 +1285,18 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
If enabled is None, the setting will be unset and If enabled is None, the setting will be unset and
the guild default will be used instead. the guild default will be used instead.
If set, this is used instead of the guild default If set, this is used instead of the guild and command default
to determine whether or not to use embeds. This is to determine whether or not to use embeds. This is
used for all commands done in a channel except used for all commands done in a channel.
for help commands.
To see full evaluation order of embed settings, run `[p]help embedset`
""" """
await self.bot._config.channel(ctx.channel).embeds.set(enabled)
if enabled is None: if enabled is None:
await self.bot._config.channel(ctx.channel).embeds.clear()
await ctx.send(_("Embeds will now fall back to the global setting.")) await ctx.send(_("Embeds will now fall back to the global setting."))
else: return
await self.bot._config.channel(ctx.channel).embeds.set(enabled)
await ctx.send( await ctx.send(
_("Embeds are now {} for this channel.").format( _("Embeds are now {} for this channel.").format(
_("enabled") if enabled else _("disabled") _("enabled") if enabled else _("disabled")
@ -1144,11 +1314,14 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
If set, this is used instead of the global default If set, this is used instead of the global default
to determine whether or not to use embeds. This is to determine whether or not to use embeds. This is
used for all commands executed in a DM with the bot. used for all commands executed in a DM with the bot.
To see full evaluation order of embed settings, run `[p]help embedset`
""" """
await self.bot._config.user(ctx.author).embeds.set(enabled)
if enabled is None: if enabled is None:
await self.bot._config.user(ctx.author).embeds.clear()
await ctx.send(_("Embeds will now fall back to the global setting.")) await ctx.send(_("Embeds will now fall back to the global setting."))
else:
await self.bot._config.user(ctx.author).embeds.set(enabled)
await ctx.send( await ctx.send(
_("Embeds are now enabled for you in DMs.") _("Embeds are now enabled for you in DMs.")
if enabled if enabled
@ -3268,7 +3441,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@command_disable.command(name="global") @command_disable.command(name="global")
async def command_disable_global(self, ctx: commands.Context, *, command: str): async def command_disable_global(self, ctx: commands.Context, *, command: str):
"""Disable a command globally.""" """Disable a command globally."""
command_obj: commands.Command = ctx.bot.get_command(command) command_obj: Optional[commands.Command] = ctx.bot.get_command(command)
if command_obj is None: if command_obj is None:
await ctx.send( await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.") _("I couldn't find that command. Please note that it is case sensitive.")
@ -3302,7 +3475,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@command_disable.command(name="server", aliases=["guild"]) @command_disable.command(name="server", aliases=["guild"])
async def command_disable_guild(self, ctx: commands.Context, *, command: str): async def command_disable_guild(self, ctx: commands.Context, *, command: str):
"""Disable a command in this server only.""" """Disable a command in this server only."""
command_obj: commands.Command = ctx.bot.get_command(command) command_obj: Optional[commands.Command] = ctx.bot.get_command(command)
if command_obj is None: if command_obj is None:
await ctx.send( await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.") _("I couldn't find that command. Please note that it is case sensitive.")
@ -3352,7 +3525,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@command_enable.command(name="global") @command_enable.command(name="global")
async def command_enable_global(self, ctx: commands.Context, *, command: str): async def command_enable_global(self, ctx: commands.Context, *, command: str):
"""Enable a command globally.""" """Enable a command globally."""
command_obj: commands.Command = ctx.bot.get_command(command) command_obj: Optional[commands.Command] = ctx.bot.get_command(command)
if command_obj is None: if command_obj is None:
await ctx.send( await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.") _("I couldn't find that command. Please note that it is case sensitive.")
@ -3374,7 +3547,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@command_enable.command(name="server", aliases=["guild"]) @command_enable.command(name="server", aliases=["guild"])
async def command_enable_guild(self, ctx: commands.Context, *, command: str): async def command_enable_guild(self, ctx: commands.Context, *, command: str):
"""Enable a command in this server.""" """Enable a command in this server."""
command_obj: commands.Command = ctx.bot.get_command(command) command_obj: Optional[commands.Command] = ctx.bot.get_command(command)
if command_obj is None: if command_obj is None:
await ctx.send( await ctx.send(
_("I couldn't find that command. Please note that it is case sensitive.") _("I couldn't find that command. Please note that it is case sensitive.")