diff --git a/docs/changelog_3_1_0.rst b/docs/changelog_3_1_0.rst index 109a11e08..d56af23ee 100644 --- a/docs/changelog_3_1_0.rst +++ b/docs/changelog_3_1_0.rst @@ -169,7 +169,7 @@ Trivia Utility Functions ----------------- - * New: ``chat_formatting.humaize_timedelta`` (`#2412`_) + * New: ``chat_formatting.humanize_timedelta`` (`#2412`_) * ``Tunnel`` - Spelling correction of method name - changed ``files_from_attatch`` to ``files_from_attach`` (old name is left for backwards compatibility) (`#2496`_) * ``Tunnel`` - fixed behavior of ``react_close()``, now when tunnel closes message will be sent to other end (`#2507`_) * ``chat_formatting.humanize_list`` - Improved error handling of empty lists (`#2597`_) diff --git a/docs/install_linux_mac.rst b/docs/install_linux_mac.rst index edab2a99b..22d0c492e 100644 --- a/docs/install_linux_mac.rst +++ b/docs/install_linux_mac.rst @@ -188,23 +188,17 @@ Choose one of the following commands to install Red. If you're not inside an activated virtual environment, include the ``--user`` flag with all ``python3.7 -m pip`` commands. -To install without audio support: +To install without MongoDB support: .. code-block:: none python3.7 -m pip install -U Red-DiscordBot -Or, to install with audio support: +Or, to install with MongoDB support: .. code-block:: none - python3.7 -m pip install -U Red-DiscordBot[voice] - -Or, install with audio and MongoDB support: - -.. code-block:: none - - python3.7 -m pip install -U Red-DiscordBot[voice,mongo] + python3.7 -m pip install -U Red-DiscordBot[mongo] .. note:: diff --git a/docs/install_windows.rst b/docs/install_windows.rst index 076f83abd..d913cdb48 100644 --- a/docs/install_windows.rst +++ b/docs/install_windows.rst @@ -62,23 +62,17 @@ Installing Red If you're not inside an activated virtual environment, include the ``--user`` flag with all ``pip`` commands. - * No audio: + * No MongoDB support: .. code-block:: none python -m pip install -U Red-DiscordBot - * With audio: + * With MongoDB support: .. code-block:: none - python -m pip install -U Red-DiscordBot[voice] - - * With audio and MongoDB support: - - .. code-block:: none - - python -m pip install -U Red-DiscordBot[voice,mongo] + python -m pip install -U Red-DiscordBot[mongo] .. note:: diff --git a/redbot/__init__.py b/redbot/__init__.py index 42dbf140e..dcc031cac 100644 --- a/redbot/__init__.py +++ b/redbot/__init__.py @@ -174,7 +174,7 @@ class VersionInfo: ) -__version__ = "3.1.0" +__version__ = "3.1.1" version_info = VersionInfo.from_str(__version__) # Filter fuzzywuzzy slow sequence matcher warning diff --git a/redbot/cogs/downloader/downloader.py b/redbot/cogs/downloader/downloader.py index adce10923..f609e58a6 100644 --- a/redbot/cogs/downloader/downloader.py +++ b/redbot/cogs/downloader/downloader.py @@ -199,7 +199,8 @@ class Downloader(commands.Cog): if not deps: return await ctx.send_help() repo = Repo("", "", "", Path.cwd(), loop=ctx.bot.loop) - success = await repo.install_raw_requirements(deps, self.LIB_PATH) + async with ctx.typing(): + success = await repo.install_raw_requirements(deps, self.LIB_PATH) if success: await ctx.send(_("Libraries installed.")) diff --git a/redbot/cogs/downloader/installable.py b/redbot/cogs/downloader/installable.py index 143701a6a..772aec4a9 100644 --- a/redbot/cogs/downloader/installable.py +++ b/redbot/cogs/downloader/installable.py @@ -114,6 +114,8 @@ class Installable(RepoJSONMixin): if self._location.is_file(): copy_func = shutil.copy2 else: + # clear copy_tree's cache to make sure missing directories are created (GH-2690) + distutils.dir_util._path_created = {} copy_func = distutils.dir_util.copy_tree # noinspection PyBroadException diff --git a/redbot/cogs/general/general.py b/redbot/cogs/general/general.py index ec7a35ac6..133ca58ba 100644 --- a/redbot/cogs/general/general.py +++ b/redbot/cogs/general/general.py @@ -307,14 +307,17 @@ class General(commands.Cog): messages = [] for ud in data["list"]: ud.setdefault("example", "N/A") - description = _("{definition}\n\n**Example:** {example}").format(**ud) - if len(description) > 2048: - description = "{}...".format(description[:2045]) - message = _( "<{permalink}>\n {word} by {author}\n\n{description}\n\n" "{thumbs_down} Down / {thumbs_up} Up, Powered by Urban Dictionary." - ).format(word=ud.pop("word").capitalize(), description=description, **ud) + ).format(word=ud.pop("word").capitalize(), description="{description}", **ud) + max_desc_len = 2000 - len(message) + + description = _("{definition}\n\n**Example:** {example}").format(**ud) + if len(description) > max_desc_len: + description = "{}...".format(description[: max_desc_len - 3]) + + message = message.format(description=description) messages.append(message) if messages is not None and len(messages) > 0: diff --git a/redbot/cogs/mod/names.py b/redbot/cogs/mod/names.py index d072aa233..4a4bb4edf 100644 --- a/redbot/cogs/mod/names.py +++ b/redbot/cogs/mod/names.py @@ -145,7 +145,7 @@ class ModInfo(MixinMeta): if voice_state and voice_state.channel: data.add_field( name=_("Current voice channel"), - value="{0.name} (ID {0.id})".format(voice_state.channel), + value="{0.mention} ID: {0.id}".format(voice_state.channel), inline=False, ) data.set_footer(text=_("Member #{} | User ID: {}").format(member_number, user.id)) @@ -164,7 +164,7 @@ class ModInfo(MixinMeta): await ctx.send(embed=data) @commands.command() - async def names(self, ctx: commands.Context, user: discord.Member): + async def names(self, ctx: commands.Context, *, user: discord.Member): """Show previous names and nicknames of a user.""" names, nicks = await self.get_names_and_nicks(user) msg = "" diff --git a/redbot/cogs/streams/streams.py b/redbot/cogs/streams/streams.py index 9ba5c398e..3893dcf18 100644 --- a/redbot/cogs/streams/streams.py +++ b/redbot/cogs/streams/streams.py @@ -128,10 +128,9 @@ class Streams(commands.Cog): stream = PicartoStream(name=channel_name) await self.check_online(ctx, stream) - @staticmethod - async def check_online(ctx: commands.Context, stream): + async def check_online(self, ctx: commands.Context, stream): try: - embed = await stream.is_online() + info = await stream.is_online() except OfflineStream: await ctx.send(_("That user is offline.")) except StreamNotFound: @@ -155,6 +154,14 @@ class Streams(commands.Cog): _("Something went wrong whilst trying to contact the stream service's API.") ) else: + if isinstance(info, tuple): + embed, is_rerun = info + ignore_reruns = await self.db.guild(ctx.channel.guild).ignore_reruns() + if ignore_reruns and is_rerun: + await ctx.send(_("That user is offline.")) + return + else: + embed = info await ctx.send(embed=embed) @commands.group() diff --git a/redbot/cogs/trivia/trivia.py b/redbot/cogs/trivia/trivia.py index 14709c8ae..8a9dd8970 100644 --- a/redbot/cogs/trivia/trivia.py +++ b/redbot/cogs/trivia/trivia.py @@ -57,7 +57,7 @@ class Trivia(commands.Cog): settings_dict = await settings.all() msg = box( _( - "**Current settings**\n" + "Current settings\n" "Bot gains points: {bot_plays}\n" "Answer time limit: {delay} seconds\n" "Lack of response timeout: {timeout} seconds\n" diff --git a/redbot/core/bot.py b/redbot/core/bot.py index f122ee5ff..877c54eb7 100644 --- a/redbot/core/bot.py +++ b/redbot/core/bot.py @@ -52,6 +52,10 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin): custom_info=None, help__page_char_limit=1000, help__max_pages_in_guild=2, + help__use_menus=False, + help__show_hidden=False, + help__verify_checks=True, + help__verify_exists=False, help__tagline="", disabled_commands=[], disabled_command_msg="That command is disabled.", diff --git a/redbot/core/commands/help.py b/redbot/core/commands/help.py index 10fc576fe..3f671df7e 100644 --- a/redbot/core/commands/help.py +++ b/redbot/core/commands/help.py @@ -77,14 +77,6 @@ class RedHelpFormatter: should not need or want a shared state. """ - # Class vars for things which should be configurable at a later date but aren't now - # Technically, someone can just use a cog to switch these in real time for now. - - USE_MENU = False - CONFIRM_UNAVAILABLE_COMMAND_EXISTENCES = False - SHOW_HIDDEN = False - VERIFY_CHECKS = True - async def send_help(self, ctx: Context, help_for: HelpTarget = None): """ This delegates to other functions. @@ -102,7 +94,7 @@ class RedHelpFormatter: await self.command_not_found(ctx, help_for) return except NoSubCommand as exc: - if self.CONFIRM_UNAVAILABLE_COMMAND_EXISTENCES: + if await ctx.bot.db.help.verify_exists(): await self.subcommand_not_found(ctx, exc.last, exc.not_found) return help_for = exc.last @@ -138,7 +130,7 @@ class RedHelpFormatter: async def format_command_help(self, ctx: Context, obj: commands.Command): - send = self.CONFIRM_UNAVAILABLE_COMMAND_EXISTENCES + send = await ctx.bot.db.help.verify_exists() if not send: async for _ in self.help_filter_func(ctx, (obj,), bypass_hidden=True): # This is a really lazy option for not @@ -182,8 +174,14 @@ class RedHelpFormatter: emb["fields"].append(field) if subcommands: + + def shorten_line(a_line: str) -> str: + if len(a_line) < 70: # embed max width needs to be lower + return a_line + return a_line[:67] + "..." + subtext = "\n".join( - f"**{name}** {command.short_doc}" + shorten_line(f"**{name}** {command.short_doc}") for name, command in sorted(subcommands.items()) ) for i, page in enumerate(pagify(subtext, page_length=1000, shorten_by=0)): @@ -208,7 +206,7 @@ class RedHelpFormatter: doc_max_width = 80 - max_width for nm, com in sorted(cmds): width_gap = discord.utils._string_width(nm) - len(nm) - doc = command.short_doc + doc = com.short_doc if len(doc) > doc_max_width: doc = doc[: doc_max_width - 3] + "..." yield nm, doc, max_width - width_gap @@ -251,6 +249,12 @@ class RedHelpFormatter: author_info = {"name": f"{ctx.me.display_name} Help Menu", "icon_url": ctx.me.avatar_url} + if not field_groups: # This can happen on single command without a docstring + embed = discord.Embed(color=color, **embed_dict["embed"]) + embed.set_author(**author_info) + embed.set_footer(**embed_dict["footer"]) + pages.append(embed) + for i, group in enumerate(field_groups, 1): embed = discord.Embed(color=color, **embed_dict["embed"]) @@ -271,8 +275,8 @@ class RedHelpFormatter: async def format_cog_help(self, ctx: Context, obj: commands.Cog): - commands = await self.get_cog_help_mapping(ctx, obj) - if not (commands or self.CONFIRM_UNAVAILABLE_COMMAND_EXISTENCES): + coms = await self.get_cog_help_mapping(ctx, obj) + if not (coms or await ctx.bot.db.help.verify_exists()): return description = obj.help @@ -285,9 +289,16 @@ class RedHelpFormatter: if description: emb["embed"]["title"] = f"*{description[:2044]}*" - if commands: + if coms: + + def shorten_line(a_line: str) -> str: + if len(a_line) < 70: # embed max width needs to be lower + return a_line + return a_line[:67] + "..." + command_text = "\n".join( - f"**{name}** {command.short_doc}" for name, command in sorted(commands.items()) + shorten_line(f"**{name}** {command.short_doc}") + for name, command in sorted(coms.items()) ) for i, page in enumerate(pagify(command_text, page_length=1000, shorten_by=0)): if i == 0: @@ -300,11 +311,11 @@ class RedHelpFormatter: await self.make_and_send_embeds(ctx, emb) else: - commands_text = None - commands_header = None - if commands: + subtext = None + subtext_header = None + if coms: subtext_header = "Commands:" - max_width = max(discord.utils._string_width(name) for name in commands.keys()) + max_width = max(discord.utils._string_width(name) for name in coms.keys()) def width_maker(cmds): doc_max_width = 80 - max_width @@ -316,20 +327,17 @@ class RedHelpFormatter: yield nm, doc, max_width - width_gap subtext = "\n".join( - f" {name:<{width}} {doc}" - for name, doc, width in width_maker(commands.items()) + f" {name:<{width}} {doc}" for name, doc, width in width_maker(coms.items()) ) - to_page = "\n\n".join( - filter(None, (description, signature[1:-1], subtext_header, subtext)) - ) + to_page = "\n\n".join(filter(None, (description, subtext_header, subtext))) pages = [box(p) for p in pagify(to_page)] await self.send_pages(ctx, pages, embed=False) async def format_bot_help(self, ctx: Context): - commands = await self.get_bot_help_mapping(ctx) - if not commands: + coms = await self.get_bot_help_mapping(ctx) + if not coms: return description = ctx.bot.description or "" @@ -343,15 +351,21 @@ class RedHelpFormatter: if description: emb["embed"]["title"] = f"*{description[:2044]}*" - for cog_name, data in commands: + for cog_name, data in coms: if cog_name: title = f"**__{cog_name}:__**" else: title = f"**__No Category:__**" + def shorten_line(a_line: str) -> str: + if len(a_line) < 70: # embed max width needs to be lower + return a_line + return a_line[:67] + "..." + cog_text = "\n".join( - f"**{name}** {command.short_doc}" for name, command in sorted(data.items()) + shorten_line(f"**{name}** {command.short_doc}") + for name, command in sorted(data.items()) ) for i, page in enumerate(pagify(cog_text, page_length=1000, shorten_by=0)): @@ -362,11 +376,12 @@ class RedHelpFormatter: await self.make_and_send_embeds(ctx, emb) else: + to_join = [] if description: - to_join = [f"{description}\n"] + to_join.append(f"{description}\n") names = [] - for k, v in commands: + for k, v in coms: names.extend(list(v.name for v in v.values())) max_width = max( @@ -382,7 +397,7 @@ class RedHelpFormatter: doc = doc[: doc_max_width - 3] + "..." yield nm, doc, max_width - width_gap - for cog_name, data in commands: + for cog_name, data in coms: title = f"{cog_name}:" if cog_name else "No Category:" to_join.append(title) @@ -401,17 +416,25 @@ class RedHelpFormatter: """ This does most of actual filtering. """ + + show_hidden = bypass_hidden or await ctx.bot.db.help.show_hidden() + verify_checks = await ctx.bot.db.help.verify_checks() + # TODO: Settings for this in core bot db for obj in objects: - if self.VERIFY_CHECKS and not (self.SHOW_HIDDEN or bypass_hidden): + if verify_checks and not show_hidden: # Default Red behavior, can_see includes a can_run check. if await obj.can_see(ctx): yield obj - elif self.VERIFY_CHECKS: - if await obj.can_run(ctx): + elif verify_checks: + try: + can_run = await obj.can_run(ctx) + except discord.DiscordException: + can_run = False + if can_run: yield obj - elif not (self.SHOW_HIDDEN or bypass_hidden): - if getattr(obj, "hidden", False): # Cog compatibility + elif not show_hidden: + if not getattr(obj, "hidden", False): # Cog compatibility yield obj else: yield obj @@ -426,17 +449,17 @@ class RedHelpFormatter: if fuzzy_commands: ret = await format_fuzzy_results(ctx, fuzzy_commands, embed=use_embeds) if use_embeds: - ret.set_author() + ret.set_author(name=f"{ctx.me.display_name} Help Menu", icon_url=ctx.me.avatar_url) tagline = (await ctx.bot.db.help.tagline()) or self.get_default_tagline(ctx) ret.set_footer(text=tagline) await ctx.send(embed=ret) else: await ctx.send(ret) - elif self.CONFIRM_UNAVAILABLE_COMMAND_EXISTENCES: - ret = T_("Command *{command_name}* not found.").format(command_name=command_name) + elif await ctx.bot.db.help.verify_exists(): + ret = T_("Help topic for *{command_name}* not found.").format(command_name=help_for) if use_embeds: - emb = discord.Embed(color=(await ctx.embed_color()), description=ret) - emb.set_author(name=f"{ctx.me.display_name} Help Menu", icon_url=ctx.me.avatar_url) + ret = discord.Embed(color=(await ctx.embed_color()), description=ret) + ret.set_author(name=f"{ctx.me.display_name} Help Menu", icon_url=ctx.me.avatar_url) tagline = (await ctx.bot.db.help.tagline()) or self.get_default_tagline(ctx) ret.set_footer(text=tagline) await ctx.send(embed=ret) @@ -447,10 +470,17 @@ class RedHelpFormatter: """ Sends an error """ - ret = T_("Command *{command_name}* has no subcommands.").format( - command_name=command.qualified_name + ret = T_("Command *{command_name}* has no subcommand named *{not_found}*.").format( + command_name=command.qualified_name, not_found=not_found[0] ) - await ctx.send(ret) + if await ctx.embed_requested(): + ret = discord.Embed(color=(await ctx.embed_color()), description=ret) + ret.set_author(name=f"{ctx.me.display_name} Help Menu", icon_url=ctx.me.avatar_url) + tagline = (await ctx.bot.db.help.tagline()) or self.get_default_tagline(ctx) + ret.set_footer(text=tagline) + await ctx.send(embed=ret) + else: + await ctx.send(ret) @staticmethod def parse_command(ctx, help_for: str): @@ -489,19 +519,40 @@ class RedHelpFormatter: Sends pages based on settings. """ - if not self.USE_MENU: + if not ( + ctx.channel.permissions_for(ctx.me).add_reactions and await ctx.bot.db.help.use_menus() + ): max_pages_in_guild = await ctx.bot.db.help.max_pages_in_guild() destination = ctx.author if len(pages) > max_pages_in_guild else ctx if embed: for page in pages: - await destination.send(embed=page) + try: + await destination.send(embed=page) + except discord.Forbidden: + await ctx.send( + T_( + "I couldn't send the help message to you in DM. " + "Either you blocked me or you disabled DMs in this server." + ) + ) else: for page in pages: - await destination.send(page) + try: + await destination.send(page) + except discord.Forbidden: + await ctx.send( + T_( + "I couldn't send the help message to you in DM. " + "Either you blocked me or you disabled DMs in this server." + ) + ) else: - await menus.menu(ctx, pages, menus.DEFAULT_CONTROLS) + if len(pages) > 1: + await menus.menu(ctx, pages, menus.DEFAULT_CONTROLS) + else: + await menus.menu(ctx, pages, {"\N{CROSS MARK}": menus.close_menu}) @commands.command(name="help", hidden=True, i18n=T_) diff --git a/redbot/core/core_commands.py b/redbot/core/core_commands.py index 97f1bbcc3..6adc88b74 100644 --- a/redbot/core/core_commands.py +++ b/redbot/core/core_commands.py @@ -529,6 +529,7 @@ class Core(commands.Cog, CoreLogic): """Loads packages""" if not cogs: return await ctx.send_help() + cogs = tuple(map(lambda cog: cog.rstrip(","), cogs)) async with ctx.typing(): loaded, failed, not_found, already_loaded, failed_with_reason = await self._load(cogs) @@ -571,6 +572,7 @@ class Core(commands.Cog, CoreLogic): """Unloads packages""" if not cogs: return await ctx.send_help() + cogs = tuple(map(lambda cog: cog.rstrip(","), cogs)) unloaded, failed = await self._unload(cogs) if unloaded: @@ -589,6 +591,7 @@ class Core(commands.Cog, CoreLogic): """Reloads packages""" if not cogs: return await ctx.send_help() + cogs = tuple(map(lambda cog: cog.rstrip(","), cogs)) async with ctx.typing(): loaded, failed, not_found, already_loaded, failed_with_reason = await self._reload( cogs @@ -1080,6 +1083,80 @@ class Core(commands.Cog, CoreLogic): """Manage settings for the help command.""" pass + @helpset.command(name="usemenus") + async def helpset_usemenus(self, ctx: commands.Context, use_menus: bool = None): + """ + Allows the help command to be sent as a paginated menu instead of seperate + messages. + + This defaults to False. + Using this without a setting will toggle. + """ + if use_menus is None: + use_menus = not await ctx.bot.db.help.use_menus() + await ctx.bot.db.help.use_menus.set(use_menus) + if use_menus: + await ctx.send(_("Help will use menus.")) + else: + await ctx.send(_("Help will not use menus.")) + + @helpset.command(name="showhidden") + async def helpset_showhidden(self, ctx: commands.Context, show_hidden: bool = None): + """ + This allows the help command to show hidden commands + + This defaults to False. + Using this without a setting will toggle. + """ + if show_hidden is None: + show_hidden = not await ctx.bot.db.help.show_hidden() + await ctx.bot.db.help.show_hidden.set(show_hidden) + if show_hidden: + await ctx.send(_("Help will not filter hidden commands")) + else: + await ctx.send(_("Help will filter hidden commands.")) + + @helpset.command(name="verifychecks") + async def helpset_permfilter(self, ctx: commands.Context, verify: bool = None): + """ + Sets if commands which can't be run in the current context should be + filtered from help + + Defaults to True. + Using this without a setting will toggle. + """ + if verify is None: + verify = not await ctx.bot.db.help.verify_checks() + await ctx.bot.db.help.verify_checks.set(verify) + if verify: + await ctx.send(_("Help will only show for commands which can be run.")) + else: + await ctx.send(_("Help will show up without checking if the commands can be run.")) + + @helpset.command(name="verifyexists") + async def helpset_verifyexists(self, ctx: commands.Context, verify: bool = None): + """ + This allows the bot to respond indicating the existence of a specific + help topic even if the user can't use it. + + Note: This setting on it's own does not fully prevent command enumeration. + + Defaults to False. + Using this without a setting will toggle. + """ + if verify is None: + verify = not await ctx.bot.db.help.verify_exists() + await ctx.bot.db.help.verify_exists.set(verify) + if verify: + await ctx.send(_("Help will verify the existence of help topics.")) + else: + await ctx.send( + _( + "Help will only verify the existence of " + "help topics via fuzzy help (if enabled)." + ) + ) + @helpset.command(name="pagecharlimit") async def helpset_pagecharlimt(self, ctx: commands.Context, limit: int): """Set the character limit for each page in the help message. @@ -1602,7 +1679,7 @@ class Core(commands.Cog, CoreLogic): """ user = isinstance(user_or_role, discord.Member) - if user and await ctx.bot.is_owner(obj): + if user and await ctx.bot.is_owner(user_or_role): await ctx.send(_("You cannot blacklist an owner!")) return diff --git a/redbot/core/events.py b/redbot/core/events.py index 165d51aa7..aacc88017 100644 --- a/redbot/core/events.py +++ b/redbot/core/events.py @@ -126,7 +126,7 @@ def init_events(bot, cli_flags): owners.append(owner) for co_owner in bot._co_owners: - co_owner = await bot.get_user(co_owner) + co_owner = bot.get_user(co_owner) if co_owner is not None: owners.append(co_owner) @@ -191,7 +191,7 @@ def init_events(bot, cli_flags): await ctx.send(error.args[0]) else: await ctx.send_help() - elif isinstance(error, commands.BadArgument): + elif isinstance(error, commands.UserInputError): await ctx.send_help() elif isinstance(error, commands.DisabledCommand): disabled_message = await bot.db.disabled_command_msg() diff --git a/redbot/core/utils/menus.py b/redbot/core/utils/menus.py index b60929e09..6305b19ff 100644 --- a/redbot/core/utils/menus.py +++ b/redbot/core/utils/menus.py @@ -4,6 +4,7 @@ # Ported to Red V3 by Palm\_\_ (https://github.com/palmtree5) import asyncio import contextlib +import functools from typing import Union, Iterable, Optional import discord @@ -60,7 +61,10 @@ async def menu( ): raise RuntimeError("All pages must be of the same type") for key, value in controls.items(): - if not asyncio.iscoroutinefunction(value): + maybe_coro = value + if isinstance(value, functools.partial): + maybe_coro = value.func + if not asyncio.iscoroutinefunction(maybe_coro): raise RuntimeError("Function must be a coroutine") current_page = pages[page] diff --git a/redbot/launcher.py b/redbot/launcher.py index ffe935d41..602e8528e 100644 --- a/redbot/launcher.py +++ b/redbot/launcher.py @@ -254,7 +254,7 @@ async def reset_red(): "please select option 5 in the launcher." ) await asyncio.sleep(2) - print("\nIf you continue you will remove these instanes.\n") + print("\nIf you continue you will remove these instances.\n") for instance in list(instances.keys()): print(" - {}".format(instance)) await asyncio.sleep(3) diff --git a/redbot/setup.py b/redbot/setup.py index be6b75d8a..3814e8b5d 100644 --- a/redbot/setup.py +++ b/redbot/setup.py @@ -440,7 +440,7 @@ async def remove_instance(instance): collection = await db.get_collection(name) await collection.drop() else: - pth = Path(instance_data["DATA_PATH"]) + pth = Path(instance_vals["DATA_PATH"]) safe_delete(pth) save_config(instance, {}, remove=True) print("The instance {} has been removed\n".format(instance))