mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 03:08:55 -05:00
Fix file endings (#6002)
This commit is contained in:
parent
f06b734e15
commit
0a5aa94cde
@ -1,48 +1,48 @@
|
|||||||
.. _autostart_windows:
|
.. _autostart_windows:
|
||||||
|
|
||||||
==============================================
|
==============================================
|
||||||
Setting up auto-restart using batch on Windows
|
Setting up auto-restart using batch on Windows
|
||||||
==============================================
|
==============================================
|
||||||
|
|
||||||
.. note:: This guide assumes that you already have a working Red instance.
|
.. note:: This guide assumes that you already have a working Red instance.
|
||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
Creating the batch file
|
Creating the batch file
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
Create a new text document anywhere you want to. This file will be used to launch the bot, so you may want to put it somewhere convenient, like Documents or Desktop.
|
Create a new text document anywhere you want to. This file will be used to launch the bot, so you may want to put it somewhere convenient, like Documents or Desktop.
|
||||||
|
|
||||||
Open that document in Notepad, and paste the following text in it:
|
Open that document in Notepad, and paste the following text in it:
|
||||||
|
|
||||||
.. code-block:: batch
|
.. code-block:: batch
|
||||||
|
|
||||||
@ECHO OFF
|
@ECHO OFF
|
||||||
:RED
|
:RED
|
||||||
CALL "%userprofile%\redenv\Scripts\activate.bat"
|
CALL "%userprofile%\redenv\Scripts\activate.bat"
|
||||||
python -O -m redbot <your instance name>
|
python -O -m redbot <your instance name>
|
||||||
|
|
||||||
IF %ERRORLEVEL% NEQ 0 (
|
IF %ERRORLEVEL% NEQ 0 (
|
||||||
ECHO Restarting Red...
|
ECHO Restarting Red...
|
||||||
GOTO RED
|
GOTO RED
|
||||||
)
|
)
|
||||||
|
|
||||||
Replace ``<your instance name>`` with the instance name of your bot.
|
Replace ``<your instance name>`` with the instance name of your bot.
|
||||||
If you created your VENV at a location other than the recommended one, replace ``%userprofile%\redenv\Scripts\activate.bat`` with the path to your VENV.
|
If you created your VENV at a location other than the recommended one, replace ``%userprofile%\redenv\Scripts\activate.bat`` with the path to your VENV.
|
||||||
|
|
||||||
Click "File", "Save as". Change the dropdown "Save as type" to "All Files (*.*)". Set the filename to ``start_redbot.bat``, and click save.
|
Click "File", "Save as". Change the dropdown "Save as type" to "All Files (*.*)". Set the filename to ``start_redbot.bat``, and click save.
|
||||||
|
|
||||||
There should now be a new file in the location you created the text document in. You can delete that text document as it is no longer needed.
|
There should now be a new file in the location you created the text document in. You can delete that text document as it is no longer needed.
|
||||||
You can now use the ``start_redbot.bat`` batch file to launch Red by double clicking it.
|
You can now use the ``start_redbot.bat`` batch file to launch Red by double clicking it.
|
||||||
This script will automatically restart red when the ``[p]restart`` command is used or when the bot shuts down abnormally.
|
This script will automatically restart red when the ``[p]restart`` command is used or when the bot shuts down abnormally.
|
||||||
|
|
||||||
-------------------------
|
-------------------------
|
||||||
Launch the bot on startup
|
Launch the bot on startup
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
Create a shortcut of your ``start_redbot.bat`` file.
|
Create a shortcut of your ``start_redbot.bat`` file.
|
||||||
|
|
||||||
Open the "Run" dialogue box using Windows Key + R.
|
Open the "Run" dialogue box using Windows Key + R.
|
||||||
|
|
||||||
Enter ``shell:startup`` if you want the bot to launch only when the current user logs in, or ``shell:common startup`` if you want the bot to launch when any user logs in.
|
Enter ``shell:startup`` if you want the bot to launch only when the current user logs in, or ``shell:common startup`` if you want the bot to launch when any user logs in.
|
||||||
|
|
||||||
Drag the shortcut into the folder that is opened. The bot will now launch on startup.
|
Drag the shortcut into the folder that is opened. The bot will now launch on startup.
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
.. tree module docs
|
.. tree module docs
|
||||||
|
|
||||||
====
|
====
|
||||||
Tree
|
Tree
|
||||||
====
|
====
|
||||||
|
|
||||||
Red uses a subclass of discord.py's ``CommandTree`` object in order to allow Cog Creators to add application commands to their cogs without worrying about the command count limit and to support caching ``AppCommand`` objects. When an app command is added to the bot's tree, it will not show up in ``tree.get_commands`` or other similar methods unless the command is "enabled" with ``[p]slash enable`` (similar to "load"ing a cog) and ``tree.red_check_enabled`` has been run since the command was added to the tree.
|
Red uses a subclass of discord.py's ``CommandTree`` object in order to allow Cog Creators to add application commands to their cogs without worrying about the command count limit and to support caching ``AppCommand`` objects. When an app command is added to the bot's tree, it will not show up in ``tree.get_commands`` or other similar methods unless the command is "enabled" with ``[p]slash enable`` (similar to "load"ing a cog) and ``tree.red_check_enabled`` has been run since the command was added to the tree.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
If you are adding app commands to the tree during load time, the loading process will call ``tree.red_check_enabled`` for your cog and its app commands. If you are adding app commands to the bot **outside of load time**, a call to ``tree.red_check_enabled`` after adding the commands is required to ensure the commands will appear properly.
|
If you are adding app commands to the tree during load time, the loading process will call ``tree.red_check_enabled`` for your cog and its app commands. If you are adding app commands to the bot **outside of load time**, a call to ``tree.red_check_enabled`` after adding the commands is required to ensure the commands will appear properly.
|
||||||
|
|
||||||
If application commands from your cog show up in ``[p]slash list`` as enabled from an ``(unknown)`` cog and disabled from your cog at the same time, you did not follow the instructions above. You must manually call ``tree.red_check_enabled`` **after** adding the commands to the tree.
|
If application commands from your cog show up in ``[p]slash list`` as enabled from an ``(unknown)`` cog and disabled from your cog at the same time, you did not follow the instructions above. You must manually call ``tree.red_check_enabled`` **after** adding the commands to the tree.
|
||||||
|
|
||||||
.. automodule:: redbot.core.tree
|
.. automodule:: redbot.core.tree
|
||||||
|
|
||||||
RedTree
|
RedTree
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
||||||
.. autoclass:: RedTree
|
.. autoclass:: RedTree
|
||||||
:members:
|
:members:
|
||||||
|
|||||||
@ -1,332 +1,332 @@
|
|||||||
import discord
|
import discord
|
||||||
from discord.abc import Snowflake
|
from discord.abc import Snowflake
|
||||||
from discord.utils import MISSING
|
from discord.utils import MISSING
|
||||||
from discord.app_commands import (
|
from discord.app_commands import (
|
||||||
Command,
|
Command,
|
||||||
Group,
|
Group,
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
AppCommand,
|
AppCommand,
|
||||||
AppCommandError,
|
AppCommandError,
|
||||||
BotMissingPermissions,
|
BotMissingPermissions,
|
||||||
CheckFailure,
|
CheckFailure,
|
||||||
CommandAlreadyRegistered,
|
CommandAlreadyRegistered,
|
||||||
CommandInvokeError,
|
CommandInvokeError,
|
||||||
CommandNotFound,
|
CommandNotFound,
|
||||||
CommandOnCooldown,
|
CommandOnCooldown,
|
||||||
NoPrivateMessage,
|
NoPrivateMessage,
|
||||||
TransformerError,
|
TransformerError,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .i18n import Translator
|
from .i18n import Translator
|
||||||
from .utils.chat_formatting import humanize_list, inline
|
from .utils.chat_formatting import humanize_list, inline
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import List, Dict, Tuple, Union, Optional, Sequence
|
from typing import List, Dict, Tuple, Union, Optional, Sequence
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger("red")
|
log = logging.getLogger("red")
|
||||||
|
|
||||||
_ = Translator(__name__, __file__)
|
_ = Translator(__name__, __file__)
|
||||||
|
|
||||||
|
|
||||||
class RedTree(discord.app_commands.CommandTree):
|
class RedTree(discord.app_commands.CommandTree):
|
||||||
"""A container that holds application command information.
|
"""A container that holds application command information.
|
||||||
|
|
||||||
Internally does not actually add commands to the tree unless they are
|
Internally does not actually add commands to the tree unless they are
|
||||||
enabled with ``[p]slash enable``, to support Red's modularity.
|
enabled with ``[p]slash enable``, to support Red's modularity.
|
||||||
See ``discord.app_commands.CommandTree`` for more information.
|
See ``discord.app_commands.CommandTree`` for more information.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
# Same structure as superclass
|
# Same structure as superclass
|
||||||
self._disabled_global_commands: Dict[str, Union[Command, Group]] = {}
|
self._disabled_global_commands: Dict[str, Union[Command, Group]] = {}
|
||||||
self._disabled_context_menus: Dict[Tuple[str, Optional[int], int], ContextMenu] = {}
|
self._disabled_context_menus: Dict[Tuple[str, Optional[int], int], ContextMenu] = {}
|
||||||
|
|
||||||
def add_command(
|
def add_command(
|
||||||
self,
|
self,
|
||||||
command: Union[Command, ContextMenu, Group],
|
command: Union[Command, ContextMenu, Group],
|
||||||
/,
|
/,
|
||||||
*args,
|
*args,
|
||||||
guild: Optional[Snowflake] = MISSING,
|
guild: Optional[Snowflake] = MISSING,
|
||||||
guilds: Sequence[Snowflake] = MISSING,
|
guilds: Sequence[Snowflake] = MISSING,
|
||||||
override: bool = False,
|
override: bool = False,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Adds an application command to the tree.
|
"""Adds an application command to the tree.
|
||||||
|
|
||||||
Commands will be internally stored until enabled by ``[p]slash enable``.
|
Commands will be internally stored until enabled by ``[p]slash enable``.
|
||||||
"""
|
"""
|
||||||
# Allow guild specific commands to bypass the internals for development
|
# Allow guild specific commands to bypass the internals for development
|
||||||
if guild is not MISSING or guilds is not MISSING:
|
if guild is not MISSING or guilds is not MISSING:
|
||||||
return super().add_command(
|
return super().add_command(
|
||||||
command, *args, guild=guild, guilds=guilds, override=override, **kwargs
|
command, *args, guild=guild, guilds=guilds, override=override, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(command, ContextMenu):
|
if isinstance(command, ContextMenu):
|
||||||
name = command.name
|
name = command.name
|
||||||
type = command.type.value
|
type = command.type.value
|
||||||
key = (name, None, type)
|
key = (name, None, type)
|
||||||
|
|
||||||
# Handle cases where the command already is in the tree
|
# Handle cases where the command already is in the tree
|
||||||
if not override and key in self._disabled_context_menus:
|
if not override and key in self._disabled_context_menus:
|
||||||
raise CommandAlreadyRegistered(name, None)
|
raise CommandAlreadyRegistered(name, None)
|
||||||
if key in self._context_menus:
|
if key in self._context_menus:
|
||||||
if not override:
|
if not override:
|
||||||
raise discord.errors.CommandAlreadyRegistered(name, None)
|
raise discord.errors.CommandAlreadyRegistered(name, None)
|
||||||
del self._context_menus[key]
|
del self._context_menus[key]
|
||||||
|
|
||||||
self._disabled_context_menus[key] = command
|
self._disabled_context_menus[key] = command
|
||||||
return
|
return
|
||||||
|
|
||||||
if not isinstance(command, (Command, Group)):
|
if not isinstance(command, (Command, Group)):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Expected an application command, received {command.__class__.__name__} instead"
|
f"Expected an application command, received {command.__class__.__name__} instead"
|
||||||
)
|
)
|
||||||
|
|
||||||
root = command.root_parent or command
|
root = command.root_parent or command
|
||||||
name = root.name
|
name = root.name
|
||||||
|
|
||||||
# Handle cases where the command already is in the tree
|
# Handle cases where the command already is in the tree
|
||||||
if not override and name in self._disabled_global_commands:
|
if not override and name in self._disabled_global_commands:
|
||||||
raise discord.errors.CommandAlreadyRegistered(name, None)
|
raise discord.errors.CommandAlreadyRegistered(name, None)
|
||||||
if name in self._global_commands:
|
if name in self._global_commands:
|
||||||
if not override:
|
if not override:
|
||||||
raise discord.errors.CommandAlreadyRegistered(name, None)
|
raise discord.errors.CommandAlreadyRegistered(name, None)
|
||||||
del self._global_commands[name]
|
del self._global_commands[name]
|
||||||
|
|
||||||
self._disabled_global_commands[name] = root
|
self._disabled_global_commands[name] = root
|
||||||
|
|
||||||
def remove_command(
|
def remove_command(
|
||||||
self,
|
self,
|
||||||
command: str,
|
command: str,
|
||||||
/,
|
/,
|
||||||
*args,
|
*args,
|
||||||
guild: Optional[Snowflake] = None,
|
guild: Optional[Snowflake] = None,
|
||||||
type: discord.AppCommandType = discord.AppCommandType.chat_input,
|
type: discord.AppCommandType = discord.AppCommandType.chat_input,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Optional[Union[Command, ContextMenu, Group]]:
|
) -> Optional[Union[Command, ContextMenu, Group]]:
|
||||||
"""Removes an application command from this tree."""
|
"""Removes an application command from this tree."""
|
||||||
if guild is not None:
|
if guild is not None:
|
||||||
return super().remove_command(command, *args, guild=guild, type=type, **kwargs)
|
return super().remove_command(command, *args, guild=guild, type=type, **kwargs)
|
||||||
if type is discord.AppCommandType.chat_input:
|
if type is discord.AppCommandType.chat_input:
|
||||||
return self._disabled_global_commands.pop(command, None) or super().remove_command(
|
return self._disabled_global_commands.pop(command, None) or super().remove_command(
|
||||||
command, *args, guild=guild, type=type, **kwargs
|
command, *args, guild=guild, type=type, **kwargs
|
||||||
)
|
)
|
||||||
elif type in (discord.AppCommandType.user, discord.AppCommandType.message):
|
elif type in (discord.AppCommandType.user, discord.AppCommandType.message):
|
||||||
key = (command, None, type.value)
|
key = (command, None, type.value)
|
||||||
return self._disabled_context_menus.pop(key, None) or super().remove_command(
|
return self._disabled_context_menus.pop(key, None) or super().remove_command(
|
||||||
command, *args, guild=guild, type=type, **kwargs
|
command, *args, guild=guild, type=type, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
def clear_commands(
|
def clear_commands(
|
||||||
self,
|
self,
|
||||||
*args,
|
*args,
|
||||||
guild: Optional[Snowflake],
|
guild: Optional[Snowflake],
|
||||||
type: Optional[discord.AppCommandType] = None,
|
type: Optional[discord.AppCommandType] = None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Clears all application commands from the tree."""
|
"""Clears all application commands from the tree."""
|
||||||
if guild is not None:
|
if guild is not None:
|
||||||
return super().clear_commands(*args, guild=guild, type=type, **kwargs)
|
return super().clear_commands(*args, guild=guild, type=type, **kwargs)
|
||||||
|
|
||||||
if type is None or type is discord.AppCommandType.chat_input:
|
if type is None or type is discord.AppCommandType.chat_input:
|
||||||
self._global_commands.clear()
|
self._global_commands.clear()
|
||||||
self._disabled_global_commands.clear()
|
self._disabled_global_commands.clear()
|
||||||
|
|
||||||
if type is None:
|
if type is None:
|
||||||
self._disabled_context_menus.clear()
|
self._disabled_context_menus.clear()
|
||||||
else:
|
else:
|
||||||
self._disabled_context_menus = {
|
self._disabled_context_menus = {
|
||||||
(name, _guild_id, value): cmd
|
(name, _guild_id, value): cmd
|
||||||
for (name, _guild_id, value), cmd in self._disabled_context_menus.items()
|
for (name, _guild_id, value), cmd in self._disabled_context_menus.items()
|
||||||
if value != type.value
|
if value != type.value
|
||||||
}
|
}
|
||||||
return super().clear_commands(*args, guild=guild, type=type, **kwargs)
|
return super().clear_commands(*args, guild=guild, type=type, **kwargs)
|
||||||
|
|
||||||
async def sync(self, *args, guild: Optional[Snowflake] = None, **kwargs) -> List[AppCommand]:
|
async def sync(self, *args, guild: Optional[Snowflake] = None, **kwargs) -> List[AppCommand]:
|
||||||
"""Wrapper to store command IDs when commands are synced."""
|
"""Wrapper to store command IDs when commands are synced."""
|
||||||
commands = await super().sync(*args, guild=guild, **kwargs)
|
commands = await super().sync(*args, guild=guild, **kwargs)
|
||||||
if guild:
|
if guild:
|
||||||
return commands
|
return commands
|
||||||
async with self.client._config.all() as cfg:
|
async with self.client._config.all() as cfg:
|
||||||
for command in commands:
|
for command in commands:
|
||||||
if command.type is discord.AppCommandType.chat_input:
|
if command.type is discord.AppCommandType.chat_input:
|
||||||
cfg["enabled_slash_commands"][command.name] = command.id
|
cfg["enabled_slash_commands"][command.name] = command.id
|
||||||
elif command.type is discord.AppCommandType.message:
|
elif command.type is discord.AppCommandType.message:
|
||||||
cfg["enabled_message_commands"][command.name] = command.id
|
cfg["enabled_message_commands"][command.name] = command.id
|
||||||
elif command.type is discord.AppCommandType.user:
|
elif command.type is discord.AppCommandType.user:
|
||||||
cfg["enabled_user_commands"][command.name] = command.id
|
cfg["enabled_user_commands"][command.name] = command.id
|
||||||
return commands
|
return commands
|
||||||
|
|
||||||
async def red_check_enabled(self) -> None:
|
async def red_check_enabled(self) -> None:
|
||||||
"""Restructures the commands in this tree, enabling commands that are enabled and disabling commands that are disabled.
|
"""Restructures the commands in this tree, enabling commands that are enabled and disabling commands that are disabled.
|
||||||
|
|
||||||
After running this function, the tree will be populated with enabled commands only.
|
After running this function, the tree will be populated with enabled commands only.
|
||||||
If commands are manually added to the tree outside of the standard cog loading process, this must be run
|
If commands are manually added to the tree outside of the standard cog loading process, this must be run
|
||||||
for them to be usable.
|
for them to be usable.
|
||||||
"""
|
"""
|
||||||
enabled_commands = await self.client.list_enabled_app_commands()
|
enabled_commands = await self.client.list_enabled_app_commands()
|
||||||
|
|
||||||
to_add_commands = []
|
to_add_commands = []
|
||||||
to_add_context = []
|
to_add_context = []
|
||||||
to_remove_commands = []
|
to_remove_commands = []
|
||||||
to_remove_context = []
|
to_remove_context = []
|
||||||
|
|
||||||
# Add commands
|
# Add commands
|
||||||
for command in enabled_commands["slash"]:
|
for command in enabled_commands["slash"]:
|
||||||
if command in self._disabled_global_commands:
|
if command in self._disabled_global_commands:
|
||||||
to_add_commands.append(command)
|
to_add_commands.append(command)
|
||||||
|
|
||||||
# Add context
|
# Add context
|
||||||
for command in enabled_commands["message"]:
|
for command in enabled_commands["message"]:
|
||||||
key = (command, None, discord.AppCommandType.message.value)
|
key = (command, None, discord.AppCommandType.message.value)
|
||||||
if key in self._disabled_context_menus:
|
if key in self._disabled_context_menus:
|
||||||
to_add_context.append(key)
|
to_add_context.append(key)
|
||||||
for command in enabled_commands["user"]:
|
for command in enabled_commands["user"]:
|
||||||
key = (command, None, discord.AppCommandType.user.value)
|
key = (command, None, discord.AppCommandType.user.value)
|
||||||
if key in self._disabled_context_menus:
|
if key in self._disabled_context_menus:
|
||||||
to_add_context.append(key)
|
to_add_context.append(key)
|
||||||
|
|
||||||
# Remove commands
|
# Remove commands
|
||||||
for command in self._global_commands:
|
for command in self._global_commands:
|
||||||
if command not in enabled_commands["slash"]:
|
if command not in enabled_commands["slash"]:
|
||||||
to_remove_commands.append((command, discord.AppCommandType.chat_input))
|
to_remove_commands.append((command, discord.AppCommandType.chat_input))
|
||||||
|
|
||||||
# Remove context
|
# Remove context
|
||||||
for command, guild_id, command_type in self._context_menus:
|
for command, guild_id, command_type in self._context_menus:
|
||||||
if guild_id is not None:
|
if guild_id is not None:
|
||||||
continue
|
continue
|
||||||
if (
|
if (
|
||||||
discord.AppCommandType(command_type) is discord.AppCommandType.message
|
discord.AppCommandType(command_type) is discord.AppCommandType.message
|
||||||
and command not in enabled_commands["message"]
|
and command not in enabled_commands["message"]
|
||||||
):
|
):
|
||||||
to_remove_context.append((command, discord.AppCommandType.message))
|
to_remove_context.append((command, discord.AppCommandType.message))
|
||||||
elif (
|
elif (
|
||||||
discord.AppCommandType(command_type) is discord.AppCommandType.user
|
discord.AppCommandType(command_type) is discord.AppCommandType.user
|
||||||
and command not in enabled_commands["user"]
|
and command not in enabled_commands["user"]
|
||||||
):
|
):
|
||||||
to_remove_context.append((command, discord.AppCommandType.user))
|
to_remove_context.append((command, discord.AppCommandType.user))
|
||||||
|
|
||||||
# Actually add/remove
|
# Actually add/remove
|
||||||
for command in to_add_commands:
|
for command in to_add_commands:
|
||||||
super().add_command(self._disabled_global_commands[command])
|
super().add_command(self._disabled_global_commands[command])
|
||||||
del self._disabled_global_commands[command]
|
del self._disabled_global_commands[command]
|
||||||
for key in to_add_context:
|
for key in to_add_context:
|
||||||
super().add_command(self._disabled_context_menus[key])
|
super().add_command(self._disabled_context_menus[key])
|
||||||
del self._disabled_context_menus[key]
|
del self._disabled_context_menus[key]
|
||||||
for command, type in to_remove_commands:
|
for command, type in to_remove_commands:
|
||||||
com = super().remove_command(command, type=type)
|
com = super().remove_command(command, type=type)
|
||||||
self._disabled_global_commands[command] = com
|
self._disabled_global_commands[command] = com
|
||||||
for command, type in to_remove_context:
|
for command, type in to_remove_context:
|
||||||
com = super().remove_command(command, type=type)
|
com = super().remove_command(command, type=type)
|
||||||
self._disabled_context_menus[(command, None, type.value)] = com
|
self._disabled_context_menus[(command, None, type.value)] = com
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def _send_from_interaction(interaction, *args, **kwargs):
|
async def _send_from_interaction(interaction, *args, **kwargs):
|
||||||
"""Util for safely sending a message from an interaction."""
|
"""Util for safely sending a message from an interaction."""
|
||||||
if interaction.response.is_done():
|
if interaction.response.is_done():
|
||||||
if interaction.is_expired():
|
if interaction.is_expired():
|
||||||
return await interaction.channel.send(*args, **kwargs)
|
return await interaction.channel.send(*args, **kwargs)
|
||||||
return await interaction.followup.send(*args, ephemeral=True, **kwargs)
|
return await interaction.followup.send(*args, ephemeral=True, **kwargs)
|
||||||
return await interaction.response.send_message(*args, ephemeral=True, **kwargs)
|
return await interaction.response.send_message(*args, ephemeral=True, **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _is_submodule(parent: str, child: str):
|
def _is_submodule(parent: str, child: str):
|
||||||
return parent == child or child.startswith(parent + ".")
|
return parent == child or child.startswith(parent + ".")
|
||||||
|
|
||||||
async def on_error(
|
async def on_error(
|
||||||
self, interaction: discord.Interaction, error: AppCommandError, /, *args, **kwargs
|
self, interaction: discord.Interaction, error: AppCommandError, /, *args, **kwargs
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Fallback error handler for app commands."""
|
"""Fallback error handler for app commands."""
|
||||||
if isinstance(error, CommandNotFound):
|
if isinstance(error, CommandNotFound):
|
||||||
await self._send_from_interaction(interaction, _("Command not found."))
|
await self._send_from_interaction(interaction, _("Command not found."))
|
||||||
log.warning(
|
log.warning(
|
||||||
f"Application command {error.name} could not be resolved. "
|
f"Application command {error.name} could not be resolved. "
|
||||||
"It may be from a cog that was updated or unloaded. "
|
"It may be from a cog that was updated or unloaded. "
|
||||||
"Consider running [p]slash sync to resolve this issue."
|
"Consider running [p]slash sync to resolve this issue."
|
||||||
)
|
)
|
||||||
elif isinstance(error, CommandInvokeError):
|
elif isinstance(error, CommandInvokeError):
|
||||||
log.exception(
|
log.exception(
|
||||||
"Exception in command '{}'".format(error.command.qualified_name),
|
"Exception in command '{}'".format(error.command.qualified_name),
|
||||||
exc_info=error.original,
|
exc_info=error.original,
|
||||||
)
|
)
|
||||||
exception_log = "Exception in command '{}'\n" "".format(error.command.qualified_name)
|
exception_log = "Exception in command '{}'\n" "".format(error.command.qualified_name)
|
||||||
exception_log += "".join(
|
exception_log += "".join(
|
||||||
traceback.format_exception(type(error), error, error.__traceback__)
|
traceback.format_exception(type(error), error, error.__traceback__)
|
||||||
)
|
)
|
||||||
interaction.client._last_exception = exception_log
|
interaction.client._last_exception = exception_log
|
||||||
|
|
||||||
message = await interaction.client._config.invoke_error_msg()
|
message = await interaction.client._config.invoke_error_msg()
|
||||||
if not message:
|
if not message:
|
||||||
if interaction.user.id in interaction.client.owner_ids:
|
if interaction.user.id in interaction.client.owner_ids:
|
||||||
message = inline(
|
message = inline(
|
||||||
_("Error in command '{command}'. Check your console or logs for details.")
|
_("Error in command '{command}'. Check your console or logs for details.")
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
message = inline(_("Error in command '{command}'."))
|
message = inline(_("Error in command '{command}'."))
|
||||||
await self._send_from_interaction(
|
await self._send_from_interaction(
|
||||||
interaction, message.replace("{command}", error.command.qualified_name)
|
interaction, message.replace("{command}", error.command.qualified_name)
|
||||||
)
|
)
|
||||||
elif isinstance(error, TransformerError):
|
elif isinstance(error, TransformerError):
|
||||||
if error.__cause__:
|
if error.__cause__:
|
||||||
log.exception("Error in an app command transformer.", exc_info=error.__cause__)
|
log.exception("Error in an app command transformer.", exc_info=error.__cause__)
|
||||||
await self._send_from_interaction(interaction, str(error))
|
await self._send_from_interaction(interaction, str(error))
|
||||||
elif isinstance(error, BotMissingPermissions):
|
elif isinstance(error, BotMissingPermissions):
|
||||||
formatted = [
|
formatted = [
|
||||||
'"' + perm.replace("_", " ").title() + '"' for perm in error.missing_permissions
|
'"' + perm.replace("_", " ").title() + '"' for perm in error.missing_permissions
|
||||||
]
|
]
|
||||||
formatted = humanize_list(formatted).replace("Guild", "Server")
|
formatted = humanize_list(formatted).replace("Guild", "Server")
|
||||||
if len(error.missing_permissions) == 1:
|
if len(error.missing_permissions) == 1:
|
||||||
msg = _("I require the {permission} permission to execute that command.").format(
|
msg = _("I require the {permission} permission to execute that command.").format(
|
||||||
permission=formatted
|
permission=formatted
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
msg = _("I require {permission_list} permissions to execute that command.").format(
|
msg = _("I require {permission_list} permissions to execute that command.").format(
|
||||||
permission_list=formatted
|
permission_list=formatted
|
||||||
)
|
)
|
||||||
await self._send_from_interaction(interaction, msg)
|
await self._send_from_interaction(interaction, msg)
|
||||||
elif isinstance(error, NoPrivateMessage):
|
elif isinstance(error, NoPrivateMessage):
|
||||||
# Seems to be only called normally by the has_role check
|
# Seems to be only called normally by the has_role check
|
||||||
await self._send_from_interaction(
|
await self._send_from_interaction(
|
||||||
interaction, _("That command is not available in DMs.")
|
interaction, _("That command is not available in DMs.")
|
||||||
)
|
)
|
||||||
elif isinstance(error, CommandOnCooldown):
|
elif isinstance(error, CommandOnCooldown):
|
||||||
relative_time = discord.utils.format_dt(
|
relative_time = discord.utils.format_dt(
|
||||||
datetime.now(timezone.utc) + timedelta(seconds=error.retry_after), "R"
|
datetime.now(timezone.utc) + timedelta(seconds=error.retry_after), "R"
|
||||||
)
|
)
|
||||||
msg = _("This command is on cooldown. Try again {relative_time}.").format(
|
msg = _("This command is on cooldown. Try again {relative_time}.").format(
|
||||||
relative_time=relative_time
|
relative_time=relative_time
|
||||||
)
|
)
|
||||||
await self._send_from_interaction(interaction, msg, delete_after=error.retry_after)
|
await self._send_from_interaction(interaction, msg, delete_after=error.retry_after)
|
||||||
elif isinstance(error, CheckFailure):
|
elif isinstance(error, CheckFailure):
|
||||||
await self._send_from_interaction(
|
await self._send_from_interaction(
|
||||||
interaction, _("You are not permitted to use this command.")
|
interaction, _("You are not permitted to use this command.")
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
log.exception(type(error).__name__, exc_info=error)
|
log.exception(type(error).__name__, exc_info=error)
|
||||||
|
|
||||||
# DEP-WARN
|
# DEP-WARN
|
||||||
def _remove_with_module(self, name: str, *args, **kwargs) -> None:
|
def _remove_with_module(self, name: str, *args, **kwargs) -> None:
|
||||||
"""Handles cases where a module raises an exception in the loading process, but added commands to the tree.
|
"""Handles cases where a module raises an exception in the loading process, but added commands to the tree.
|
||||||
|
|
||||||
Duplication of the logic in the super class, but for the containers used by this subclass.
|
Duplication of the logic in the super class, but for the containers used by this subclass.
|
||||||
"""
|
"""
|
||||||
super()._remove_with_module(name, *args, **kwargs)
|
super()._remove_with_module(name, *args, **kwargs)
|
||||||
remove = []
|
remove = []
|
||||||
for key, cmd in self._disabled_context_menus.items():
|
for key, cmd in self._disabled_context_menus.items():
|
||||||
if cmd.module is not None and self._is_submodule(name, cmd.module):
|
if cmd.module is not None and self._is_submodule(name, cmd.module):
|
||||||
remove.append(key)
|
remove.append(key)
|
||||||
|
|
||||||
for key in remove:
|
for key in remove:
|
||||||
del self._disabled_context_menus[key]
|
del self._disabled_context_menus[key]
|
||||||
|
|
||||||
remove = []
|
remove = []
|
||||||
for key, cmd in self._disabled_global_commands.items():
|
for key, cmd in self._disabled_global_commands.items():
|
||||||
if cmd.module is not None and self._is_submodule(name, cmd.module):
|
if cmd.module is not None and self._is_submodule(name, cmd.module):
|
||||||
remove.append(key)
|
remove.append(key)
|
||||||
|
|
||||||
for key in remove:
|
for key in remove:
|
||||||
del self._disabled_global_commands[key]
|
del self._disabled_global_commands[key]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user