diff --git a/changelog.d/3090.feature.rst b/changelog.d/3090.feature.rst new file mode 100644 index 000000000..ca19a6c6d --- /dev/null +++ b/changelog.d/3090.feature.rst @@ -0,0 +1 @@ +adds a licenseinfo command \ No newline at end of file diff --git a/redbot/__main__.py b/redbot/__main__.py index 8f61504bb..b9a87cecc 100644 --- a/redbot/__main__.py +++ b/redbot/__main__.py @@ -26,8 +26,8 @@ from redbot.core.cog_manager import CogManagerUI from redbot.core.global_checks import init_global_checks from redbot.core.events import init_events from redbot.core.cli import interactive_config, confirm, parse_cli_flags +from redbot.core.core_commands import Core, license_info_command from redbot.setup import get_data_dir, get_name, save_config -from redbot.core.core_commands import Core from redbot.core.dev_commands import Dev from redbot.core import __version__, modlog, bank, data_manager, drivers from signal import SIGTERM @@ -223,7 +223,7 @@ async def sigterm_handler(red, log): def main(): - description = "Red V3 (c) Cog Creators" + description = "Red V3" cli_flags = parse_cli_flags(sys.argv[1:]) if cli_flags.list_instances: list_instances() @@ -282,6 +282,7 @@ def main(): red.add_cog(Core(red)) red.add_cog(CogManagerUI()) + red.add_command(license_info_command) if cli_flags.dev: red.add_cog(Dev()) # noinspection PyProtectedMember diff --git a/redbot/cogs/permissions/permissions.py b/redbot/cogs/permissions/permissions.py index e3cd995b0..7910ac986 100644 --- a/redbot/cogs/permissions/permissions.py +++ b/redbot/cogs/permissions/permissions.py @@ -299,6 +299,14 @@ class Permissions(commands.Cog): if not who_or_what: await ctx.send_help() return + if isinstance(cog_or_command.obj, commands.commands._AlwaysAvailableCommand): + await ctx.send( + _( + "This command is designated as being always available and " + "cannot be modified by permission rules." + ) + ) + return for w in who_or_what: await self._add_rule( rule=cast(bool, allow_or_deny), @@ -334,6 +342,14 @@ class Permissions(commands.Cog): if not who_or_what: await ctx.send_help() return + if isinstance(cog_or_command.obj, commands.commands._AlwaysAvailableCommand): + await ctx.send( + _( + "This command is designated as being always available and " + "cannot be modified by permission rules." + ) + ) + return for w in who_or_what: await self._add_rule( rule=cast(bool, allow_or_deny), diff --git a/redbot/core/commands/commands.py b/redbot/core/commands/commands.py index 7d120b14f..c2adf2ee1 100644 --- a/redbot/core/commands/commands.py +++ b/redbot/core/commands/commands.py @@ -699,3 +699,29 @@ def get_command_disabler(guild: discord.Guild) -> Callable[["Context"], Awaitabl __command_disablers[guild] = disabler return disabler + + +# This is intentionally left out of `__all__` as it is not intended for general use +class _AlwaysAvailableCommand(Command): + """ + This should be used only for informational commands + which should not be disabled or removed + + These commands cannot belong to a cog. + + These commands do not respect most forms of checks, and + should only be used with that in mind. + + This particular class is not supported for 3rd party use + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.cog is not None: + raise TypeError("This command may not be added to a cog") + + async def can_run(self, ctx, *args, **kwargs) -> bool: + return not ctx.author.bot + + async def _verify_checks(self, ctx) -> bool: + return not ctx.author.bot diff --git a/redbot/core/core_commands.py b/redbot/core/core_commands.py index 0d304de9a..98344c772 100644 --- a/redbot/core/core_commands.py +++ b/redbot/core/core_commands.py @@ -1136,7 +1136,7 @@ class Core(commands.Cog, CoreLogic): @checks.is_owner() async def api(self, ctx: commands.Context, service: str, *, tokens: TokenConverter): """Set various external API tokens. - + This setting will be asked for by some 3rd party cogs and some core cogs. To add the keys provide the service name and the tokens as a comma separated @@ -1162,7 +1162,7 @@ class Core(commands.Cog, CoreLogic): Allows the help command to be sent as a paginated menu instead of seperate messages. - This defaults to False. + This defaults to False. Using this without a setting will toggle. """ if use_menus is None: @@ -1907,6 +1907,12 @@ class Core(commands.Cog, CoreLogic): ) return + if isinstance(command_obj, commands.commands._AlwaysAvailableCommand): + await ctx.send( + _("This command is designated as being always available and cannot be disabled.") + ) + return + async with ctx.bot._config.disabled_commands() as disabled_commands: if command not in disabled_commands: disabled_commands.append(command_obj.qualified_name) @@ -1935,6 +1941,12 @@ class Core(commands.Cog, CoreLogic): ) return + if isinstance(command_obj, commands.commands._AlwaysAvailableCommand): + await ctx.send( + _("This command is designated as being always available and cannot be disabled.") + ) + return + if command_obj.requires.privilege_level > await PrivilegeLevel.from_ctx(ctx): await ctx.send(_("You are not allowed to disable that command.")) return @@ -2215,3 +2227,21 @@ class Core(commands.Cog, CoreLogic): async def rpc_reload(self, request): await self.rpc_unload(request) await self.rpc_load(request) + + +# 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.command(cls=commands.commands._AlwaysAvailableCommand, name="licenseinfo", i18n=_) +async def license_info_command(ctx): + """ + Get info about Red's licenses + """ + + message = ( + "This bot is an instance of Red-DiscordBot (hereafter refered to as Red)\n" + "Red is a free and open source application made available to the public and " + "licensed under the GNU GPLv3. The full text of this license is available to you at " + "" + ) + await ctx.send(message) + # We need a link which contains a thank you to other projects which we use at some point.