mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 03:08:55 -05:00
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:
parent
03d72dd5fa
commit
560e1dfc3d
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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.")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user