diff --git a/docs/cog_guides/cog_manager_ui.rst b/docs/cog_guides/cog_manager_ui.rst index 7c9cf73cb..fc2691123 100644 --- a/docs/cog_guides/cog_manager_ui.rst +++ b/docs/cog_guides/cog_manager_ui.rst @@ -171,17 +171,17 @@ removepath .. code-block:: none - [p]removepath + [p]removepath **Description** -Removes a path from the list of available paths. Its cogs won't be accessible -anymore. +Removes one or more paths from the list of available paths. Its cogs won't be +accessible anymore. **Arguments** -* ````: The number of the path to remove. You can get it with - the :ref:`paths ` command. +* ````: The number of the path(s) to remove. You can get it with + the :ref:`paths ` command. .. _cogmanagerui-command-reorderpath: diff --git a/redbot/core/cog_manager.py b/redbot/core/cog_manager.py index a4d192b83..db361e503 100644 --- a/redbot/core/cog_manager.py +++ b/redbot/core/cog_manager.py @@ -4,9 +4,10 @@ import pkgutil from importlib import import_module, invalidate_caches from importlib.machinery import ModuleSpec from pathlib import Path -from typing import Union, List, Optional +from typing import TYPE_CHECKING, Union, List, Optional import redbot.cogs +from redbot.core.commands import BadArgument from redbot.core.utils import deduplicate_iterables import discord @@ -15,11 +16,26 @@ from .config import Config from .i18n import Translator, cog_i18n from .data_manager import cog_data_path -from .utils.chat_formatting import box, pagify +from .utils.chat_formatting import box, pagify, humanize_list, inline __all__ = ["CogManager"] +# Duplicate of redbot.cogs.cleanup.converters.positive_int +if TYPE_CHECKING: + positive_int = int +else: + + def positive_int(arg: str) -> int: + try: + ret = int(arg) + except ValueError: + raise BadArgument(_("{arg} is not an integer.").format(arg=inline(arg))) + if ret <= 0: + raise BadArgument(_("{arg} is not a positive integer.").format(arg=inline(arg))) + return ret + + class NoSuchCog(ImportError): """Thrown when a cog is missing. @@ -358,39 +374,53 @@ class CogManagerUI(commands.Cog): else: await ctx.send(_("Path successfully added.")) - @commands.command() + @commands.command(require_var_positional=True) @checks.is_owner() - async def removepath(self, ctx: commands.Context, path_number: int): + async def removepath(self, ctx: commands.Context, *path_numbers: positive_int): """ - Removes a path from the available cog paths given the `path_number` from `[p]paths`. + Removes one or more paths from the available cog paths given the `path_numbers` from `[p]paths`. """ - path_number -= 1 - if path_number < 0: - await ctx.send(_("Path numbers must be positive.")) - return + valid: List[Path] = [] + invalid: List[int] = [] cog_paths = await ctx.bot._cog_mgr.user_defined_paths() - try: - to_remove = cog_paths.pop(path_number) - except IndexError: - await ctx.send(_("That is an invalid path number.")) - return + # dict.fromkeys removes duplicates while preserving the order + for path_number in dict.fromkeys(sorted(path_numbers)): + idx = path_number - 1 + try: + to_remove = cog_paths[idx] + except IndexError: + invalid.append(path_number) + else: + await ctx.bot._cog_mgr.remove_path(to_remove) + valid.append(to_remove) - await ctx.bot._cog_mgr.remove_path(to_remove) - await ctx.send(_("Path successfully removed.")) + parts = [] + if valid: + parts.append( + _("The following paths were removed: {paths}").format( + paths=humanize_list([inline(str(path)) for path in valid]) + ) + ) + if invalid: + parts.append( + _("The following path numbers did not exist: {path_numbers}").format( + path_numbers=humanize_list([inline(str(path)) for path in invalid]) + ) + ) + + for page in pagify("\n\n".join(parts), ["\n", " "]): + await ctx.send(page) @commands.command() @checks.is_owner() - async def reorderpath(self, ctx: commands.Context, from_: int, to: int): + async def reorderpath(self, ctx: commands.Context, from_: positive_int, to: positive_int): """ Reorders paths internally to allow discovery of different cogs. """ # Doing this because in the paths command they're 1 indexed from_ -= 1 to -= 1 - if from_ < 0 or to < 0: - await ctx.send(_("Path numbers must be positive.")) - return all_paths = await ctx.bot._cog_mgr.user_defined_paths() try: