From 3cb17116be9c8d23334424a4a05d7a1c2ea01ecc Mon Sep 17 00:00:00 2001 From: Kowlin <10947836+Kowlin@users.noreply.github.com> Date: Thu, 22 Dec 2022 23:41:52 +0100 Subject: [PATCH 01/43] Revert ``mock`` and ``mockmsg`` back to a guild_only state (#5926) --- redbot/core/dev_commands.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/redbot/core/dev_commands.py b/redbot/core/dev_commands.py index 27db6af4ed0..9fea9dc8c93 100644 --- a/redbot/core/dev_commands.py +++ b/redbot/core/dev_commands.py @@ -359,9 +359,10 @@ async def pause(self, ctx, toggle: Optional[bool] = None): else: await ctx.send(_("The REPL session in this channel is now paused.")) + @commands.guild_only() @commands.command() @checks.is_owner() - async def mock(self, ctx, user: discord.User, *, command): + async def mock(self, ctx, user: discord.Member, *, command): """Mock another user invoking a command. The prefix must not be entered. @@ -372,9 +373,10 @@ async def mock(self, ctx, user: discord.User, *, command): ctx.bot.dispatch("message", msg) + @commands.guild_only() @commands.command(name="mockmsg") @checks.is_owner() - async def mock_msg(self, ctx, user: discord.User, *, content: str = ""): + async def mock_msg(self, ctx, user: discord.Member, *, content: str = ""): """Dispatch a message event as if it were sent by a different user. Current message is used as a base (including attachments, embeds, etc.), From 1c7178a10b2ae2ae017956c8a3dcbd95ba8fce00 Mon Sep 17 00:00:00 2001 From: Kreusada <67752638+Kreusada@users.noreply.github.com> Date: Thu, 22 Dec 2022 22:49:44 +0000 Subject: [PATCH 02/43] Add automodule reference for the antispam module (#5641) Co-authored-by: Jakub Kuczys Co-authored-by: Flame442 <34169552+Flame442@users.noreply.github.com> --- docs/framework_utils.rst | 6 +++ redbot/core/utils/antispam.py | 99 +++++++++++++++++++++++++++++------ 2 files changed, 89 insertions(+), 16 deletions(-) diff --git a/docs/framework_utils.rst b/docs/framework_utils.rst index 29a3b844710..9d719a795f7 100644 --- a/docs/framework_utils.rst +++ b/docs/framework_utils.rst @@ -80,3 +80,9 @@ Utility UI .. automodule:: redbot.core.utils.views :members: + +AntiSpam +======== + +.. automodule:: redbot.core.utils.antispam + :members: diff --git a/redbot/core/utils/antispam.py b/redbot/core/utils/antispam.py index b2b6c9b3306..4a49bc4c133 100644 --- a/redbot/core/utils/antispam.py +++ b/redbot/core/utils/antispam.py @@ -2,21 +2,84 @@ from typing import Tuple, List from collections import namedtuple -Interval = Tuple[timedelta, int] -AntiSpamInterval = namedtuple("AntiSpamInterval", ["period", "frequency"]) +_AntiSpamInterval = namedtuple("_AntiSpamInterval", ["period", "frequency"]) class AntiSpam: """ - Custom class which is more flexible than using discord.py's - `commands.cooldown()` + A class that can be used to count the number of events that happened + within specified intervals. Later, it can be checked whether the specified + maximum count for any of the specified intervals has been exceeded. - Can be intialized with a custom set of intervals - These should be provided as a list of tuples in the form - (timedelta, quantity) + Examples + -------- + Tracking whether the number of reports sent by a user within a single guild is spammy: - Where quantity represents the maximum amount of times - something should be allowed in an interval. + .. code-block:: python + + class MyCog(commands.Cog): + INTERVALS = [ + # More than one report within last 5 seconds is considered spam. + (datetime.timedelta(seconds=5), 1), + # More than 3 reports within the last 5 minutes are considered spam. + (datetime.timedelta(minutes=5), 3), + # More than 10 reports within the last hour are considered spam. + (datetime.timedelta(hours=1), 10), + # More than 24 reports within a single day (last 24 hours) are considered spam. + (datetime.timedelta(days=1), 24), + ] + + def __init__(self, bot): + self.bot = bot + self.antispam = {} + + @commands.guild_only() + @commands.command() + async def report(self, ctx, content): + # We want to track whether a single user within a single guild + # sends a spammy number of reports. + key = (ctx.guild.id, ctx.author.id) + + if key not in self.antispam: + # Create an instance of the AntiSpam class with given intervals. + self.antispam[key] = AntiSpam(self.INTERVALS) + # If you want to use the default intervals, you can use: AntiSpam([]) + + # Check if the user sent too many reports recently. + # The `AntiSpam.spammy` property is `True` if, for any interval, + # the number of events that happened within that interval + # exceeds the number specified for that interval. + if self.antispam[key].spammy: + await ctx.send( + "You've sent too many reports recently, please try again later." + ) + return + + # Make any other early-return checks. + + # Record the event. + self.antispam[key].stamp() + + # Save the report. + # self.config... + + # Send a message to the user. + await ctx.send("Your report has been submitted.") + + Parameters + ---------- + intervals : List[Tuple[datetime.timedelta, int]] + A list of tuples in the format (timedelta, int), + where the timedelta represents the length of the interval, + and the int represents the maximum number of times something + can happen within that interval. + + If an empty list is provided, the following defaults will be used: + + * 3 per 5 seconds + * 5 per 1 minute + * 10 per 1 hour + * 24 per 1 day """ # TODO : Decorator interface for command check using `spammy` @@ -30,13 +93,13 @@ class AntiSpam: (timedelta(days=1), 24), ] - def __init__(self, intervals: List[Interval]): + def __init__(self, intervals: List[Tuple[timedelta, int]]): self.__event_timestamps = [] - _itvs = intervals if intervals else self.default_intervals - self.__intervals = [AntiSpamInterval(*x) for x in _itvs] + _itvs = intervals or self.default_intervals + self.__intervals = [_AntiSpamInterval(*x) for x in _itvs] self.__discard_after = max([x.period for x in self.__intervals]) - def __interval_check(self, interval: AntiSpamInterval): + def __interval_check(self, interval: _AntiSpamInterval): return ( len([t for t in self.__event_timestamps if (t + interval.period) > datetime.utcnow()]) >= interval.frequency @@ -45,14 +108,18 @@ def __interval_check(self, interval: AntiSpamInterval): @property def spammy(self): """ - use this to check if any interval criteria are met + Whether, for any interval, the number of events that happened + within that interval exceeds the number specified for that interval. """ return any(self.__interval_check(x) for x in self.__intervals) def stamp(self): """ - Use this to mark an event that counts against the intervals - as happening now + Mark an event timestamp against the list of antispam intervals happening right now. + Use this after all checks have passed, and your action is taking place. + + The stamp will last until the corresponding interval duration + has expired (set when this AntiSpam object was initiated). """ self.__event_timestamps.append(datetime.utcnow()) self.__event_timestamps = [ From 9bfc3ecbce04550882158c8f06ba6d9e6b81a486 Mon Sep 17 00:00:00 2001 From: Flame442 <34169552+Flame442@users.noreply.github.com> Date: Sun, 25 Dec 2022 09:25:40 -0500 Subject: [PATCH 03/43] Fix tests badge in readme (#5934) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0da03e226b1..05f2c65778a 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@

- GitHub Actions + GitHub Actions Red on readthedocs.org From 0e58897bfcc67a245fa421549900fae2cd9d1b55 Mon Sep 17 00:00:00 2001 From: Kowlin <10947836+Kowlin@users.noreply.github.com> Date: Sun, 25 Dec 2022 15:40:35 +0100 Subject: [PATCH 04/43] Add SECURITY.md to the repo (#5929) Co-authored-by: Jakub Kuczys --- SECURITY.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..eb11217f1ec --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,38 @@ +# Security Policy + +## Supported Versions + +The table below explains the current state of our versions. Currently, only version +3.4 and higher are supported and receive security updates. Versions lower than 3.4 +are considered End of Life and will not receive any security updates. + +| Version | Branch | Security Updates | End of Life | +|---------------|------------|--------------------|--------------------| +| < 2.0 | master | :x: | :white_check_mark: | +| >= 2.0, < 3.0 | develop | :x: | :white_check_mark: | +| >= 3.0, < 3.4 | V3/develop | :x: | :white_check_mark: | +| >= 3.4 | V3/develop | :white_check_mark: | :x: | + + +## Reporting a Vulnerability + +For reporting vulnerabilities within Red-DiscordBot we make use of GitHub's +private vulnerability reporting feature (More information can be found +[here](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability)). +This ensures that all maintainers and key members have access to the reported +vulnerability. + +### Opening a Vulnerability Report + +To open a vulnerability report please fill out [this form](https://github.com/Cog-Creators/Red-DiscordBot/security/advisories/new) + +You will be asked to provide a summary, details and proof of concept for your vulnerability report. +We ask that you fill out this form to the best of your ability, with as many details as possible. +Furthermore, you'll be asked to provide affected products and severity. +These fields are optional and will be filled appropriately by the maintainers if not provided. + +### Timeline + +We will try to answer your report within 7 days. If you haven't received an answer by then, we suggest you reach +out to us privately. This can best be done via our [Discord server](https://discord.gg/red), and contacting +a member who has the Staff role. From e8c044a9bf6f1178e52c4bd5301a3bddfd965f4b Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Sun, 25 Dec 2022 22:27:07 +0100 Subject: [PATCH 05/43] Use different exit codes for critical errors vs configuration errors (#5674) Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> --- docs/autostart_systemd.rst | 5 +++-- redbot/__init__.py | 2 +- redbot/__main__.py | 28 ++++++++++++++-------------- redbot/core/bot.py | 12 ++---------- redbot/core/cli.py | 23 +++++++++++++++++++++-- redbot/core/data_manager.py | 5 +++-- redbot/core/rpc.py | 4 +++- redbot/launcher.py | 4 ++-- redbot/setup.py | 21 +++++++++++---------- 9 files changed, 60 insertions(+), 44 deletions(-) diff --git a/docs/autostart_systemd.rst b/docs/autostart_systemd.rst index 229eb95f17a..45e2b018d26 100644 --- a/docs/autostart_systemd.rst +++ b/docs/autostart_systemd.rst @@ -52,9 +52,10 @@ Paste the following in the file, and replace all instances of :code:`username` w User=username Group=username Type=idle - Restart=always + Restart=on-abnormal RestartSec=15 - RestartPreventExitStatus=0 + RestartForceExitStatus=1 + RestartForceExitStatus=26 TimeoutStopSec=10 [Install] diff --git a/redbot/__init__.py b/redbot/__init__.py index 1a8690f042a..04727a960ca 100644 --- a/redbot/__init__.py +++ b/redbot/__init__.py @@ -27,7 +27,7 @@ f"Python {'.'.join(map(str, MIN_PYTHON_VERSION))} is required to run Red, but you have " f"{_sys.version}! Please update Python." ) - _sys.exit(1) + _sys.exit(78) class VersionInfo: diff --git a/redbot/__main__.py b/redbot/__main__.py index f9de75e0357..ddd6eef85c6 100644 --- a/redbot/__main__.py +++ b/redbot/__main__.py @@ -53,13 +53,13 @@ def list_instances(): "No instances have been configured! Configure one " "using `redbot-setup` before trying to run the bot!" ) - sys.exit(1) + sys.exit(ExitCodes.CONFIGURATION_ERROR) else: text = "Configured Instances:\n\n" for instance_name in _get_instance_names(): text += "{}\n".format(instance_name) print(text) - sys.exit(0) + sys.exit(ExitCodes.SHUTDOWN) async def debug_info(*args: Any) -> None: @@ -80,10 +80,10 @@ async def edit_instance(red, cli_flags): if data_path is None and copy_data: print("--copy-data can't be used without --edit-data-path argument") - sys.exit(1) + sys.exit(ExitCodes.INVALID_CLI_USAGE) if new_name is None and confirm_overwrite: print("--overwrite-existing-instance can't be used without --edit-instance-name argument") - sys.exit(1) + sys.exit(ExitCodes.INVALID_CLI_USAGE) if ( no_prompt and all(to_change is None for to_change in (token, owner, new_name, data_path)) @@ -94,7 +94,7 @@ async def edit_instance(red, cli_flags): " Available arguments (check help for more information):" " --edit-instance-name, --edit-data-path, --copy-data, --owner, --token, --prefix" ) - sys.exit(1) + sys.exit(ExitCodes.INVALID_CLI_USAGE) await _edit_token(red, token, no_prompt) await _edit_prefix(red, prefix, no_prompt) @@ -357,10 +357,10 @@ async def run_bot(red: Red, cli_flags: Namespace) -> None: token = new_token else: log.critical("Token and prefix must be set in order to login.") - sys.exit(1) + sys.exit(ExitCodes.CONFIGURATION_ERROR) if cli_flags.dry_run: - sys.exit(0) + sys.exit(ExitCodes.SHUTDOWN) try: # `async with red:` is unnecessary here because we call red.close() in shutdown handler await red.start(token) @@ -371,8 +371,8 @@ async def run_bot(red: Red, cli_flags: Namespace) -> None: if confirm("\nDo you want to reset the token?"): await red._config.token.set("") print("Token has been reset.") - sys.exit(0) - sys.exit(1) + sys.exit(ExitCodes.SHUTDOWN) + sys.exit(ExitCodes.CONFIGURATION_ERROR) except discord.PrivilegedIntentsRequired: console = rich.get_console() console.print( @@ -381,7 +381,7 @@ async def run_bot(red: Red, cli_flags: Namespace) -> None: "https://docs.discord.red/en/stable/bot_application_guide.html#enabling-privileged-intents", style="red", ) - sys.exit(1) + sys.exit(ExitCodes.CONFIGURATION_ERROR) except _NoOwnerSet: print( "Bot doesn't have any owner set!\n" @@ -399,7 +399,7 @@ async def run_bot(red: Red, cli_flags: Namespace) -> None: "c) pass owner ID(s) when launching Red with --owner" " (and --co-owner if you need more than one) flag\n" ) - sys.exit(1) + sys.exit(ExitCodes.CONFIGURATION_ERROR) return None @@ -410,12 +410,12 @@ def handle_early_exit_flags(cli_flags: Namespace): elif cli_flags.version: print("Red V3") print("Current Version: {}".format(__version__)) - sys.exit(0) + sys.exit(ExitCodes.SHUTDOWN) elif cli_flags.debuginfo: early_exit_runner(cli_flags, debug_info) elif not cli_flags.instance_name and (not cli_flags.no_instance or cli_flags.edit): print("Error: No instance name was provided!") - sys.exit(1) + sys.exit(ExitCodes.INVALID_CLI_USAGE) async def shutdown_handler(red, signal_type=None, exit_code=None): @@ -553,7 +553,7 @@ def main(): asyncio.set_event_loop(None) loop.stop() loop.close() - exit_code = red._shutdown_mode if red is not None else 1 + exit_code = red._shutdown_mode if red is not None else ExitCodes.CRITICAL sys.exit(exit_code) diff --git a/redbot/core/bot.py b/redbot/core/bot.py index 03e3366936e..a055d9617e7 100644 --- a/redbot/core/bot.py +++ b/redbot/core/bot.py @@ -11,7 +11,6 @@ import functools from collections import namedtuple, OrderedDict from datetime import datetime -from enum import IntEnum from importlib.machinery import ModuleSpec from pathlib import Path from typing import ( @@ -39,6 +38,7 @@ from discord.ext.commands import when_mentioned_or from . import Config, i18n, commands, errors, drivers, modlog, bank +from .cli import ExitCodes from .cog_manager import CogManager, CogManagerUI from .core_commands import Core from .data_manager import cog_data_path @@ -69,7 +69,7 @@ log = logging.getLogger("red") -__all__ = ("Red", "ExitCodes") +__all__ = ("Red",) NotMessage = namedtuple("NotMessage", "guild") @@ -2190,11 +2190,3 @@ async def wrapper(func, stype, sname): failed_cogs=failures["cog"], unhandled=failures["unhandled"], ) - - -class ExitCodes(IntEnum): - # This needs to be an int enum to be used - # with sys.exit - CRITICAL = 1 - SHUTDOWN = 0 - RESTART = 26 diff --git a/redbot/core/cli.py b/redbot/core/cli.py index bf37f3a8a59..169f2eb79c5 100644 --- a/redbot/core/cli.py +++ b/redbot/core/cli.py @@ -2,6 +2,7 @@ import asyncio import logging import sys +from enum import IntEnum from typing import Optional import discord @@ -10,6 +11,21 @@ from redbot.core.utils._internal_utils import cli_level_to_log_level +# This needs to be an int enum to be used +# with sys.exit +class ExitCodes(IntEnum): + #: Clean shutdown (through signals, keyboard interrupt, [p]shutdown, etc.). + SHUTDOWN = 0 + #: An unrecoverable error occurred during application's runtime. + CRITICAL = 1 + #: The CLI command was used incorrectly, such as when the wrong number of arguments are given. + INVALID_CLI_USAGE = 2 + #: Restart was requested by the bot owner (probably through [p]restart command). + RESTART = 26 + #: Some kind of configuration error occurred. + CONFIGURATION_ERROR = 78 # Exit code borrowed from os.EX_CONFIG. + + def confirm(text: str, default: Optional[bool] = None) -> bool: if default is None: options = "y/n" @@ -23,9 +39,12 @@ def confirm(text: str, default: Optional[bool] = None) -> bool: while True: try: value = input(f"{text}: [{options}] ").lower().strip() - except (KeyboardInterrupt, EOFError): + except KeyboardInterrupt: + print("\nAborted!") + sys.exit(ExitCodes.SHUTDOWN) + except EOFError: print("\nAborted!") - sys.exit(1) + sys.exit(ExitCodes.INVALID_CLI_USAGE) if value in ("y", "yes"): return True if value in ("n", "no"): diff --git a/redbot/core/data_manager.py b/redbot/core/data_manager.py index 655dd138fd1..762a99a7100 100644 --- a/redbot/core/data_manager.py +++ b/redbot/core/data_manager.py @@ -12,6 +12,7 @@ from discord.utils import deprecated from . import commands +from .cli import ExitCodes __all__ = [ "create_temp_config", @@ -118,7 +119,7 @@ def load_basic_configuration(instance_name_: str): "You need to configure the bot instance using `redbot-setup`" " prior to running the bot." ) - sys.exit(1) + sys.exit(ExitCodes.CONFIGURATION_ERROR) try: basic_config = config[instance_name] except KeyError: @@ -126,7 +127,7 @@ def load_basic_configuration(instance_name_: str): "Instance with this name doesn't exist." " You can create new instance using `redbot-setup` prior to running the bot." ) - sys.exit(1) + sys.exit(ExitCodes.INVALID_CLI_USAGE) def _base_data_path() -> Path: diff --git a/redbot/core/rpc.py b/redbot/core/rpc.py index 6308f9cb075..b2ee0eb4a57 100644 --- a/redbot/core/rpc.py +++ b/redbot/core/rpc.py @@ -8,6 +8,8 @@ import logging +from redbot.core.cli import ExitCodes + log = logging.getLogger("red.rpc") __all__ = ["RPC", "RPCMixin", "get_name"] @@ -89,7 +91,7 @@ async def initialize(self, port: int): ) except Exception as exc: log.exception("RPC setup failure", exc_info=exc) - sys.exit(1) + sys.exit(ExitCodes.CRITICAL) else: await self._site.start() log.debug("Created RPC server listener on port %s", port) diff --git a/redbot/launcher.py b/redbot/launcher.py index f726590a5f5..31b753c9e01 100644 --- a/redbot/launcher.py +++ b/redbot/launcher.py @@ -20,7 +20,7 @@ create_backup, ) from redbot.core import __version__, version_info as red_version_info, VersionInfo -from redbot.core.cli import confirm +from redbot.core.cli import ExitCodes, confirm from redbot.core.data_manager import load_existing_config if sys.platform == "linux": @@ -155,7 +155,7 @@ def main(): req_ver=".".join(map(str, MIN_PYTHON_VERSION)), sys_ver=sys.version ) ) # Don't make an f-string, these may not exist on the python version being rejected! - sys.exit(1) + sys.exit(ExitCodes.CONFIGURATION_ERROR) if INTERACTIVE_MODE: main_menu(flags_to_pass) diff --git a/redbot/setup.py b/redbot/setup.py index b4af36a804a..0c2618a4038 100644 --- a/redbot/setup.py +++ b/redbot/setup.py @@ -21,6 +21,7 @@ cli_level_to_log_level, ) from redbot.core import config, data_manager, drivers +from redbot.core.cli import ExitCodes from redbot.core.data_manager import appdir, config_dir, config_file from redbot.core.drivers import BackendType, IdentifierData @@ -30,7 +31,7 @@ config_dir.mkdir(parents=True, exist_ok=True) except PermissionError: print("You don't have permission to write to '{}'\nExiting...".format(config_dir)) - sys.exit(1) + sys.exit(ExitCodes.CONFIGURATION_ERROR) instance_data = data_manager.load_existing_config() if instance_data is None: @@ -77,7 +78,7 @@ def get_data_dir(*, instance_name: str, data_path: Optional[Path], interactive: "We were unable to check your chosen directory." " Provided path may contain an invalid character." ) - sys.exit(1) + sys.exit(ExitCodes.INVALID_CLI_USAGE) if not exists: try: @@ -85,15 +86,15 @@ def get_data_dir(*, instance_name: str, data_path: Optional[Path], interactive: except OSError: print( "We were unable to create your chosen directory." - " You may need to restart this process with admin" - " privileges." + " You may need to create the directory and set proper permissions" + " for it manually before it can be used as the data directory." ) - sys.exit(1) + sys.exit(ExitCodes.INVALID_CLI_USAGE) print("You have chosen {} to be your data directory.".format(data_path)) if not click.confirm("Please confirm", default=True): print("Please start the process over.") - sys.exit(0) + sys.exit(ExitCodes.CRITICAL) return str(data_path.resolve()) @@ -143,7 +144,7 @@ def get_name(name: str) -> str: " and can only include characters A-z, numbers," " and non-consecutive underscores (_) and periods (.)." ) - sys.exit(1) + sys.exit(ExitCodes.INVALID_CLI_USAGE) return name while len(name) == 0: @@ -191,7 +192,7 @@ def basic_setup( "Providing instance name through --instance-name is required" " when using non-interactive mode." ) - sys.exit(1) + sys.exit(ExitCodes.INVALID_CLI_USAGE) if interactive: print( @@ -225,14 +226,14 @@ def basic_setup( "Are you absolutely certain you want to continue?", default=False ): print("Not continuing") - sys.exit(0) + sys.exit(ExitCodes.SHUTDOWN) else: print( "An instance with this name already exists.\n" "If you want to remove the existing instance and replace it with this one," " run this command with --overwrite-existing-instance flag." ) - sys.exit(1) + sys.exit(ExitCodes.INVALID_CLI_USAGE) save_config(name, default_dirs) if interactive: From f02491a0926d10818a014aa0596b5c78aea3b48d Mon Sep 17 00:00:00 2001 From: sravan Date: Mon, 26 Dec 2022 12:20:39 +0530 Subject: [PATCH 06/43] Update intents screenshot (#5936) --- .../bot-guide/discord_privileged_intents.png | Bin 0 -> 78964 bytes .../discord_privileged_intents_until_aug_31.png | Bin 81214 -> 0 bytes docs/bot_application_guide.rst | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 docs/.resources/bot-guide/discord_privileged_intents.png delete mode 100644 docs/.resources/bot-guide/discord_privileged_intents_until_aug_31.png diff --git a/docs/.resources/bot-guide/discord_privileged_intents.png b/docs/.resources/bot-guide/discord_privileged_intents.png new file mode 100644 index 0000000000000000000000000000000000000000..367da8756299c9afb857ae02998f08160ee4f73d GIT binary patch literal 78964 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ąVxxDV>b-Z-dTb8TC-D6kA)*6zj8&ZX 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 literal 0 HcmV?d00001 diff --git a/docs/.resources/bot-guide/discord_privileged_intents_until_aug_31.png b/docs/.resources/bot-guide/discord_privileged_intents_until_aug_31.png deleted file mode 100644 index 87ca71bfba8df9bac3e57cb56d6ead49606664c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81214 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%0DSCejkWCeESv?dRPMH}*H6FdwgIBP6v#*o_Y(f*l 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 diff --git a/docs/bot_application_guide.rst b/docs/bot_application_guide.rst index abf4e29ec43..dccfbddd0d0 100644 --- a/docs/bot_application_guide.rst +++ b/docs/bot_application_guide.rst @@ -69,7 +69,7 @@ Enabling Privileged Intents 5. Scroll down to the "Privileged Gateway Intents" section, enable all three privileged intents and save your changes. - .. image:: /.resources/bot-guide/discord_privileged_intents_until_aug_31.png + .. image:: /.resources/bot-guide/discord_privileged_intents.png :alt: The privileged gateway intents selector. .. warning:: From e88884edb6d08880aa78677e01b2a9a9e4b7fd9b Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Tue, 27 Dec 2022 00:39:17 +0100 Subject: [PATCH 07/43] Fix usage of file/folder names without suffix in Downloader (#5938) --- redbot/cogs/downloader/downloader.py | 3 ++- redbot/cogs/downloader/installable.py | 4 ++-- redbot/cogs/downloader/repo_manager.py | 12 ++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/redbot/cogs/downloader/downloader.py b/redbot/cogs/downloader/downloader.py index ee60a2e74ef..413bfc8aa77 100644 --- a/redbot/cogs/downloader/downloader.py +++ b/redbot/cogs/downloader/downloader.py @@ -529,7 +529,8 @@ async def _repo_add( - `[p]repo add 26-Cogs https://github.com/Twentysix26/x26-Cogs` - `[p]repo add Laggrons-Dumb-Cogs https://github.com/retke/Laggrons-Dumb-Cogs v3` - Repo names can only contain characters A-z, numbers, underscores, and hyphens. + Repo names can only contain characters A-z, numbers, underscores, hyphens, and dots (but they cannot start or end with a dot). + The branch will be the default branch if not specified. **Arguments** diff --git a/redbot/cogs/downloader/installable.py b/redbot/cogs/downloader/installable.py index c203116e0f2..abda7d92488 100644 --- a/redbot/cogs/downloader/installable.py +++ b/redbot/cogs/downloader/installable.py @@ -84,7 +84,7 @@ def __init__(self, location: Path, repo: Optional[Repo] = None, commit: str = "" self._location = location self.repo = repo - self.repo_name = self._location.parent.stem + self.repo_name = self._location.parent.name self.commit = commit self.end_user_data_statement: str @@ -129,7 +129,7 @@ async def copy_to(self, target_dir: Path) -> bool: # noinspection PyBroadException try: - copy_func(src=str(self._location), dst=str(target_dir / self._location.stem)) + copy_func(src=str(self._location), dst=str(target_dir / self._location.name)) except: # noqa: E722 log.exception("Error occurred when copying path: %s", self._location) return False diff --git a/redbot/cogs/downloader/repo_manager.py b/redbot/cogs/downloader/repo_manager.py index 3cb164ed866..14baf11d0f6 100644 --- a/redbot/cogs/downloader/repo_manager.py +++ b/redbot/cogs/downloader/repo_manager.py @@ -998,7 +998,7 @@ def available_libraries(self) -> Tuple[Installable, ...]: @classmethod async def from_folder(cls, folder: Path, branch: str = "") -> Repo: - repo = cls(name=folder.stem, url="", branch=branch, commit="", folder_path=folder) + repo = cls(name=folder.name, url="", branch=branch, commit="", folder_path=folder) repo.url = await repo.current_url() if branch == "": repo.branch = await repo.current_branch() @@ -1214,14 +1214,14 @@ async def _load_repos(self, set_repos: bool = False) -> Dict[str, Repo]: if not folder.is_dir(): continue try: - branch = await self.config.repos.get_raw(folder.stem, default="") - ret[folder.stem] = await Repo.from_folder(folder, branch) + branch = await self.config.repos.get_raw(folder.name, default="") + ret[folder.name] = await Repo.from_folder(folder, branch) if branch == "": - await self.config.repos.set_raw(folder.stem, value=ret[folder.stem].branch) + await self.config.repos.set_raw(folder.name, value=ret[folder.name].branch) except errors.NoRemoteURL: - log.warning("A remote URL does not exist for repo %s", folder.stem) + log.warning("A remote URL does not exist for repo %s", folder.name) except errors.DownloaderException as err: - log.error("Ignoring repo %s due to error.", folder.stem, exc_info=err) + log.error("Ignoring repo %s due to error.", folder.name, exc_info=err) # Downloader should NOT remove the repo on generic errors like this one. # We were removing whole repo folder here in the past, # but it's quite destructive for such a generic error. From 7af2ed13ec37a3397c9ee83c50e616df36c3a642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pred=C3=A4?= <46051820+PredaaA@users.noreply.github.com> Date: Tue, 27 Dec 2022 01:29:54 +0100 Subject: [PATCH 08/43] Update disable commands logic during cogs loading (#5550) Co-authored-by: Flame442 <34169552+Flame442@users.noreply.github.com> --- redbot/core/events.py | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/redbot/core/events.py b/redbot/core/events.py index 69cb64e6868..bf2b45643d1 100644 --- a/redbot/core/events.py +++ b/redbot/core/events.py @@ -382,14 +382,10 @@ async def on_message(message, /): @bot.event async def on_command_add(command: commands.Command): - disabled_commands = await bot._config.disabled_commands() - if command.qualified_name in disabled_commands: - command.enabled = False - guild_data = await bot._config.all_guilds() - async for guild_id, data in AsyncIter(guild_data.items(), steps=100): - disabled_commands = data.get("disabled_commands", []) - if command.qualified_name in disabled_commands: - command.disable_in(discord.Object(id=guild_id)) + if command.cog is not None: + return + + await _disable_command_no_cog(command) async def _guild_added(guild: discord.Guild): disabled_commands = await bot._config.guild(guild).disabled_commands() @@ -424,3 +420,26 @@ async def on_cog_add(cog: commands.Cog): uuid = c.unique_identifier group_data = c.custom_groups await bot._config.custom("CUSTOM_GROUPS", c.cog_name, uuid).set(group_data) + + await _disable_commands_cog(cog) + + async def _disable_command( + command: commands.Command, global_disabled: list, guilds_data: dict + ): + if command.qualified_name in global_disabled: + command.enabled = False + for guild_id, data in guilds_data.items(): + guild_disabled_cmds = data.get("disabled_commands", []) + if command.qualified_name in guild_disabled_cmds: + command.disable_in(discord.Object(id=guild_id)) + + async def _disable_commands_cog(cog: commands.Cog): + global_disabled = await bot._config.disabled_commands() + guilds_data = await bot._config.all_guilds() + for command in cog.walk_commands(): + await _disable_command(command, global_disabled, guilds_data) + + async def _disable_command_no_cog(command: commands.Command): + global_disabled = await bot._config.disabled_commands() + guilds_data = await bot._config.all_guilds() + await _disable_command(command, global_disabled, guilds_data) From f890f65c35c276c56ce2c1634baaef4092311b57 Mon Sep 17 00:00:00 2001 From: Kowlin <10947836+Kowlin@users.noreply.github.com> Date: Tue, 27 Dec 2022 11:29:05 +0100 Subject: [PATCH 09/43] Updated excluded features (#5919) Co-authored-by: Zephyrkul <23347632+Zephyrkul@users.noreply.github.com> --- redbot/cogs/general/general.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/redbot/cogs/general/general.py b/redbot/cogs/general/general.py index 1f22a100f4a..c981f7e6d57 100644 --- a/redbot/cogs/general/general.py +++ b/redbot/cogs/general/general.py @@ -422,6 +422,8 @@ def _bitsize(num: int): "NEW_THREAD_PERMISSIONS", "TEXT_IN_VOICE_ENABLED", "THREADS_ENABLED", + # available to everyone sometime after forum channel release + "PRIVATE_THREADS", } custom_feature_names = { "VANITY_URL": "Vanity URL", From 14f142da2bcd4d9518e187a18b8f63eca7153b5c Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Tue, 27 Dec 2022 14:28:34 +0100 Subject: [PATCH 10/43] Update installation URLs for Homebrew and Chocolatey (#5776) Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> --- docs/install_guides/mac.rst | 2 +- docs/install_guides/windows.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/install_guides/mac.rst b/docs/install_guides/mac.rst index 15a3b438414..e2127f61cb1 100644 --- a/docs/install_guides/mac.rst +++ b/docs/install_guides/mac.rst @@ -16,7 +16,7 @@ following, then press Enter: .. prompt:: bash - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" After the installation, install the required packages by pasting the commands and pressing enter, one-by-one: diff --git a/docs/install_guides/windows.rst b/docs/install_guides/windows.rst index d35a00890d1..5584154d0f1 100644 --- a/docs/install_guides/windows.rst +++ b/docs/install_guides/windows.rst @@ -30,7 +30,7 @@ Then run each of the following commands: Set-ExecutionPolicy Bypass -Scope Process -Force [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 - iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) + iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) choco upgrade git --params "/GitOnlyOnPath /WindowsTerminal" -y choco upgrade visualstudio2022-workload-vctools -y choco upgrade python3 -y --version 3.9.9 From 43ab6e2ef5f4cf6c905351dc6484dd204e34f72f Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Tue, 27 Dec 2022 14:42:20 +0100 Subject: [PATCH 11/43] Add missing empty line in error output of Trivia unit test (#5659) Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> --- tests/cogs/test_trivia.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/cogs/test_trivia.py b/tests/cogs/test_trivia.py index 9aaed02758f..8d8d9baae24 100644 --- a/tests/cogs/test_trivia.py +++ b/tests/cogs/test_trivia.py @@ -21,7 +21,7 @@ def test_trivia_lists(): problem_lists.append((l.stem, f"YAML error:\n{e!s}")) if problem_lists: - msg = "" - for name, error in problem_lists: - msg += f"- {name}:\n{textwrap.indent(error, ' ')}" + msg = "\n".join( + f"- {name}:\n{textwrap.indent(error, ' ')}" for name, error in problem_lists + ) raise TypeError("The following lists contain errors:\n" + msg) From 1ab303bce782ef9a47d175c83bc22d085aeea1b0 Mon Sep 17 00:00:00 2001 From: Draper <27962761+Drapersniper@users.noreply.github.com> Date: Tue, 27 Dec 2022 19:33:50 +0000 Subject: [PATCH 12/43] Fix managed LL subprocess's stdout overflowing and deadlocking (#5903) Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> Co-authored-by: Jakub Kuczys --- redbot/cogs/audio/errors.py | 8 -------- redbot/cogs/audio/manager.py | 32 ++++++++++++++++---------------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/redbot/cogs/audio/errors.py b/redbot/cogs/audio/errors.py index 05d3d49c9a7..9bddf87aaa5 100644 --- a/redbot/cogs/audio/errors.py +++ b/redbot/cogs/audio/errors.py @@ -51,14 +51,6 @@ class NoProcessFound(ManagedLavalinkNodeException): """Exception thrown when the managed node process is not found""" -class IncorrectProcessFound(ManagedLavalinkNodeException): - """Exception thrown when the managed node process is incorrect""" - - -class TooManyProcessFound(ManagedLavalinkNodeException): - """Exception thrown when zombie processes are suspected""" - - class LavalinkDownloadFailed(ManagedLavalinkNodeException, RuntimeError): """Downloading the Lavalink jar failed. diff --git a/redbot/cogs/audio/manager.py b/redbot/cogs/audio/manager.py index b0c39687216..921d0c9cc57 100644 --- a/redbot/cogs/audio/manager.py +++ b/redbot/cogs/audio/manager.py @@ -13,7 +13,6 @@ import aiohttp import lavalink -import psutil import rich.progress import yaml from discord.backoff import ExponentialBackoff @@ -32,8 +31,6 @@ UnexpectedJavaResponseException, EarlyExitException, ManagedLavalinkNodeException, - TooManyProcessFound, - IncorrectProcessFound, NoProcessFound, NodeUnhealthy, ) @@ -261,12 +258,12 @@ def __init__(self, config: Config, cog: "Audio", timeout: Optional[int] = None) self.ready: asyncio.Event = asyncio.Event() self._config = config self._proc: Optional[asyncio.subprocess.Process] = None # pylint:disable=no-member - self._node_pid: Optional[int] = None self._shutdown: bool = False self.start_monitor_task = None self.timeout = timeout self.cog = cog self._args = [] + self._pipe_task = None @property def path(self) -> Optional[str]: @@ -292,6 +289,11 @@ def ll_branch(self) -> Optional[str]: def build_time(self) -> Optional[str]: return self._buildtime + async def _pipe_output(self): + with contextlib.suppress(asyncio.CancelledError): + async for __ in self._proc.stdout: + pass + async def _start(self, java_path: str) -> None: arch_name = platform.machine() self._java_exc = java_path @@ -333,8 +335,7 @@ async def _start(self, java_path: str) -> None: stderr=asyncio.subprocess.STDOUT, ) ) - self._node_pid = self._proc.pid - log.info("Managed Lavalink node started. PID: %s", self._node_pid) + log.info("Managed Lavalink node started. PID: %s", self._proc.pid) try: await asyncio.wait_for(self._wait_for_launcher(), timeout=self.timeout) except asyncio.TimeoutError: @@ -450,6 +451,7 @@ async def _wait_for_launcher(self) -> None: if b"Lavalink is ready to accept connections." in line: self.ready.set() log.info("Managed Lavalink node is ready to receive requests.") + self._pipe_task = asyncio.create_task(self._pipe_output()) break if _FAILED_TO_START.search(line): raise ManagedLavalinkStartFailure( @@ -469,19 +471,17 @@ async def shutdown(self) -> None: async def _partial_shutdown(self) -> None: self.ready.clear() - # In certain situations to await self._proc.wait() is invalid so waiting on it waits forever. if self._shutdown is True: # For convenience, calling this method more than once or calling it before starting it # does nothing. return - if self._node_pid: - with contextlib.suppress(psutil.Error): - p = psutil.Process(self._node_pid) - p.terminate() - p.kill() + if self._pipe_task: + self._pipe_task.cancel() + if self._proc is not None: + self._proc.terminate() + await self._proc.wait() self._proc = None self._shutdown = True - self._node_pid = None async def _download_jar(self) -> None: log.info("Downloading Lavalink.jar...") @@ -595,12 +595,12 @@ async def start_monitor(self, java_path: str): while True: try: self._shutdown = False - if self._node_pid is None or not psutil.pid_exists(self._node_pid): + if self._proc is None or self._proc.returncode is not None: self.ready.clear() await self._start(java_path=java_path) while True: await self.wait_until_ready(timeout=self.timeout) - if not psutil.pid_exists(self._node_pid): + if self._proc.returncode is not None: raise NoProcessFound try: node = lavalink.get_all_nodes()[0] @@ -628,7 +628,7 @@ async def start_monitor(self, java_path: str): except Exception as exc: log.debug(exc, exc_info=exc) raise NodeUnhealthy(str(exc)) - except (TooManyProcessFound, IncorrectProcessFound, NoProcessFound): + except NoProcessFound: await self._partial_shutdown() except asyncio.TimeoutError: delay = backoff.delay() From d9dd37b867cf34aba87d623e9acc60ba2ec7e426 Mon Sep 17 00:00:00 2001 From: Julien Mauroy Date: Tue, 27 Dec 2022 21:51:13 +0100 Subject: [PATCH 13/43] Remove multiple paths (#5859) Co-authored-by: Kreusada <67752638+Kreusada@users.noreply.github.com> Co-authored-by: Jakub Kuczys --- docs/cog_guides/cog_manager_ui.rst | 10 ++--- redbot/core/cog_manager.py | 70 +++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/docs/cog_guides/cog_manager_ui.rst b/docs/cog_guides/cog_manager_ui.rst index 7c9cf73cb06..fc26911235b 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 a4d192b8344..db361e503b2 100644 --- a/redbot/core/cog_manager.py +++ b/redbot/core/cog_manager.py @@ -4,9 +4,10 @@ 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 .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 @@ async def addpath(self, ctx: commands.Context, *, path: Path): 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) + + 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]) + ) + ) - await ctx.bot._cog_mgr.remove_path(to_remove) - await ctx.send(_("Path successfully removed.")) + 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: From abb0101420d2b22ebf93b4a8326c28f51b8b5c0e Mon Sep 17 00:00:00 2001 From: Kreusada <67752638+Kreusada@users.noreply.github.com> Date: Tue, 27 Dec 2022 20:58:01 +0000 Subject: [PATCH 14/43] [CogManagerUI] Resolve core path under `bot._cog_mgr.CORE_PATH` (#5142) Co-authored-by: Jakub Kuczys --- redbot/core/cog_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redbot/core/cog_manager.py b/redbot/core/cog_manager.py index db361e503b2..a4b122389f3 100644 --- a/redbot/core/cog_manager.py +++ b/redbot/core/cog_manager.py @@ -52,7 +52,7 @@ class CogManager: bot directory. """ - CORE_PATH = Path(redbot.cogs.__path__[0]) + CORE_PATH = Path(redbot.cogs.__path__[0]).resolve() def __init__(self): self.config = Config.get_conf(self, 2938473984732, True) From 99479342ea9a12e7bf298508e1233c6c3b31373d Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Tue, 27 Dec 2022 23:22:56 +0100 Subject: [PATCH 15/43] Fix ordering of 3.0.0.dev1 and 3.0.0a1.dev1 versions (#5932) --- redbot/__init__.py | 10 +++++++++- tests/core/test_version.py | 20 ++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/redbot/__init__.py b/redbot/__init__.py index 04727a960ca..caed5eddd32 100644 --- a/redbot/__init__.py +++ b/redbot/__init__.py @@ -149,12 +149,20 @@ def _generate_comparison_tuples( ] ] = [] for obj in (self, other): + if ( + obj.releaselevel == obj.FINAL + and obj.post_release is None + and obj.dev_release is not None + ): + releaselevel = -1 + else: + releaselevel = obj._RELEASE_LEVELS.index(obj.releaselevel) tups.append( ( obj.major, obj.minor, obj.micro, - obj._RELEASE_LEVELS.index(obj.releaselevel), + releaselevel, obj.serial if obj.serial is not None else _inf, obj.post_release if obj.post_release is not None else -_inf, obj.dev_release if obj.dev_release is not None else _inf, diff --git a/tests/core/test_version.py b/tests/core/test_version.py index 12c55c74901..a978e270aa8 100644 --- a/tests/core/test_version.py +++ b/tests/core/test_version.py @@ -2,6 +2,7 @@ import os import sys from packaging.requirements import Requirement +from packaging.version import Version import pytest @@ -16,10 +17,20 @@ def test_version_working(): # When adding more of these, ensure they are added in ascending order of precedence version_tests = ( + "3.0.0.dev1", + "3.0.0.dev2", + "3.0.0a32.dev12", + "3.0.0a32", "3.0.0a32.post10.dev12", + "3.0.0a32.post10", + "3.0.0b23.dev4", + "3.0.0b23", + "3.0.0b23.post5.dev16", + "3.0.0b23.post5", "3.0.0rc1.dev1", "3.0.0rc1", "3.0.0", + "3.0.0.post1.dev1", "3.0.1.dev1", "3.0.1.dev2+gdbaf31e", "3.0.1.dev2+gdbaf31e.dirty", @@ -37,10 +48,11 @@ def test_version_info_str_parsing(): def test_version_info_lt(): - for next_idx, cur in enumerate(version_tests[:-1], start=1): - cur_test = VersionInfo.from_str(cur) - next_test = VersionInfo.from_str(version_tests[next_idx]) - assert cur_test < next_test + for version_cls in (Version, VersionInfo.from_str): + for next_idx, cur in enumerate(version_tests[:-1], start=1): + cur_test = version_cls(cur) + next_test = version_cls(version_tests[next_idx]) + assert cur_test < next_test def test_version_info_gt(): From 19ebd025956f8e2b63e980e6d4fac22b0a128b7d Mon Sep 17 00:00:00 2001 From: Honkertonken <94032937+Honkertonken@users.noreply.github.com> Date: Wed, 28 Dec 2022 05:22:02 +0530 Subject: [PATCH 16/43] Change/Update casing in findcog command. (#5772) Co-authored-by: Jakub Kuczys --- redbot/cogs/downloader/downloader.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/redbot/cogs/downloader/downloader.py b/redbot/cogs/downloader/downloader.py index 413bfc8aa77..c9242dfb62e 100644 --- a/redbot/cogs/downloader/downloader.py +++ b/redbot/cogs/downloader/downloader.py @@ -1741,8 +1741,8 @@ async def findcog(self, ctx: commands.Context, command_name: str) -> None: # Check if in installed cogs cog = command.cog if cog: - cog_name = self.cog_name_from_instance(cog) - installed, cog_installable = await self.is_installed(cog_name) + cog_pkg_name = self.cog_name_from_instance(cog) + installed, cog_installable = await self.is_installed(cog_pkg_name) if installed: made_by = ( humanize_list(cog_installable.author) @@ -1759,17 +1759,21 @@ async def findcog(self, ctx: commands.Context, command_name: str) -> None: if cog_installable.repo is None else cog_installable.repo.name ) - cog_name = cog_installable.name + cog_pkg_name = cog_installable.name elif cog.__module__.startswith("redbot."): # core commands or core cog made_by = "Cog Creators" repo_url = "https://github.com/Cog-Creators/Red-DiscordBot" - cog_name = cog.__class__.__name__ + module_fragments = cog.__module__.split(".") + if module_fragments[1] == "core": + cog_pkg_name = "N/A - Built-in commands" + else: + cog_pkg_name = module_fragments[2] repo_name = "Red-DiscordBot" else: # assume not installed via downloader made_by = _("Unknown") repo_url = _("None - this cog wasn't installed via downloader") - cog_name = cog.__class__.__name__ repo_name = _("Unknown") + cog_name = cog.__class__.__name__ else: msg = _("This command is not provided by a cog.") await ctx.send(msg) @@ -1778,7 +1782,8 @@ async def findcog(self, ctx: commands.Context, command_name: str) -> None: if await ctx.embed_requested(): embed = discord.Embed(color=(await ctx.embed_colour())) embed.add_field(name=_("Command:"), value=command_name, inline=False) - embed.add_field(name=_("Cog name:"), value=cog_name, inline=False) + embed.add_field(name=_("Cog package name:"), value=cog_pkg_name, inline=True) + embed.add_field(name=_("Cog name:"), value=cog_name, inline=True) embed.add_field(name=_("Made by:"), value=made_by, inline=False) embed.add_field(name=_("Repo name:"), value=repo_name, inline=False) embed.add_field(name=_("Repo URL:"), value=repo_url, inline=False) @@ -1790,12 +1795,18 @@ async def findcog(self, ctx: commands.Context, command_name: str) -> None: else: msg = _( - "Command: {command}\nCog name: {cog}\nMade by: {author}\nRepo name: {repo_name}\nRepo URL: {repo_url}\n" + "Command: {command}\n" + "Cog package name: {cog_pkg}\n" + "Cog name: {cog}\n" + "Made by: {author}\n" + "Repo name: {repo_name}\n" + "Repo URL: {repo_url}\n" ).format( command=command_name, + cog_pkg=cog_pkg_name, + cog=cog_name, author=made_by, repo_url=repo_url, - cog=cog_name, repo_name=repo_name, ) if installed and cog_installable.repo is not None and cog_installable.repo.branch: From 0e97c26b2d22d724550a6ce4e1097037a6b8cdd1 Mon Sep 17 00:00:00 2001 From: Flame442 <34169552+Flame442@users.noreply.github.com> Date: Thu, 29 Dec 2022 00:50:18 -0500 Subject: [PATCH 17/43] Test implicit subclass type conversion in config defaults/sets (#5874) Co-authored-by: Jakub Kuczys --- tests/core/test_config.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/core/test_config.py b/tests/core/test_config.py index 473b4da2891..bef10426de9 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -1,6 +1,7 @@ import asyncio from unittest.mock import patch import pytest +from collections import Counter # region Register Tests @@ -593,6 +594,21 @@ async def test_raw_with_partial_primary_keys(config): assert await config.custom("CUSTOM", "primary_key").identifier() is False +@pytest.mark.asyncio +async def test_cast_subclass_default(config): + # regression test for GH-5557/GH-5585 + config.register_global(foo=Counter({})) + assert type(config.defaults["GLOBAL"]["foo"]) is dict + assert config.defaults["GLOBAL"]["foo"] == {} + stored_value = await config.foo() + assert type(stored_value) is dict + assert stored_value == {} + await config.foo.set(Counter({"bar": 1})) + stored_value = await config.foo() + assert type(stored_value) is dict + assert stored_value == {"bar": 1} + + """ Following PARAMS can be generated with: from functools import reduce From 9b1171ea8cc6765903d03f2d04ea4cba5704a6cb Mon Sep 17 00:00:00 2001 From: Karlo Prikratki Date: Thu, 29 Dec 2022 17:55:11 +0100 Subject: [PATCH 18/43] Use Discord's relative timestamps as command cooldown countdown (#5893) Co-authored-by: Jakub Kuczys --- redbot/cogs/economy/economy.py | 43 ++++++++++------------------------ redbot/core/events.py | 10 ++++---- 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/redbot/cogs/economy/economy.py b/redbot/cogs/economy/economy.py index 973884c06da..f3d6ded7802 100644 --- a/redbot/cogs/economy/economy.py +++ b/redbot/cogs/economy/economy.py @@ -2,6 +2,7 @@ import logging import random from collections import defaultdict, deque, namedtuple +from datetime import datetime, timezone, timedelta from enum import Enum from math import ceil from typing import cast, Iterable, Union, Literal @@ -339,11 +340,13 @@ async def payday(self, ctx: commands.Context): ) else: - dtime = self.display_time(next_payday - cur_time) + relative_time = discord.utils.format_dt( + datetime.now(timezone.utc) + timedelta(seconds=next_payday - cur_time), "R" + ) await ctx.send( - _( - "{author.mention} Too soon. For your next payday you have to wait {time}." - ).format(author=author, time=dtime) + _("{author.mention} Too soon. Your next payday is {relative_time}.").format( + author=author, relative_time=relative_time + ) ) else: # Gets the users latest successfully payday and adds the guilds payday time @@ -394,11 +397,13 @@ async def payday(self, ctx: commands.Context): ) ) else: - dtime = self.display_time(next_payday - cur_time) + relative_time = discord.utils.format_dt( + datetime.now(timezone.utc) + timedelta(seconds=next_payday - cur_time), "R" + ) await ctx.send( - _( - "{author.mention} Too soon. For your next payday you have to wait {time}." - ).format(author=author, time=dtime) + _("{author.mention} Too soon. Your next payday is {relative_time}.").format( + author=author, relative_time=relative_time + ) ) @commands.command() @@ -891,25 +896,3 @@ async def rolepaydayamount(self, ctx: commands.Context, role: discord.Role, cred num=humanize_number(creds), currency=credits_name, role_name=role.name ) ) - - # What would I ever do without stackoverflow? - @staticmethod - def display_time(seconds, granularity=2): - intervals = ( # Source: http://stackoverflow.com/a/24542445 - (_("weeks"), 604800), # 60 * 60 * 24 * 7 - (_("days"), 86400), # 60 * 60 * 24 - (_("hours"), 3600), # 60 * 60 - (_("minutes"), 60), - (_("seconds"), 1), - ) - - result = [] - - for name, count in intervals: - value = seconds // count - if value: - seconds -= value * count - if value == 1: - name = name.rstrip("s") - result.append("{} {}".format(value, name)) - return ", ".join(result[:granularity]) diff --git a/redbot/core/events.py b/redbot/core/events.py index bf2b45643d1..3811f38be3e 100644 --- a/redbot/core/events.py +++ b/redbot/core/events.py @@ -317,10 +317,12 @@ async def on_command_error(ctx, error, unhandled_by_cog=False): new_ctx = await bot.get_context(ctx.message) await bot.invoke(new_ctx) return - if delay := humanize_timedelta(seconds=error.retry_after): - msg = _("This command is on cooldown. Try again in {delay}.").format(delay=delay) - else: - msg = _("This command is on cooldown. Try again in 1 second.") + relative_time = discord.utils.format_dt( + datetime.now(timezone.utc) + timedelta(seconds=error.retry_after), "R" + ) + msg = _("This command is on cooldown. Try again {relative_time}.").format( + relative_time=relative_time + ) await ctx.send(msg, delete_after=error.retry_after) elif isinstance(error, commands.MaxConcurrencyReached): if error.per is commands.BucketType.default: From d3308af0e238c7d279d3cbe66b8b031290a431e0 Mon Sep 17 00:00:00 2001 From: Karlo Prikratki Date: Thu, 29 Dec 2022 20:18:26 +0100 Subject: [PATCH 19/43] [Streams] Add toggleable button to stream alerts (#5856) Co-authored-by: Jakub Kuczys --- docs/cog_guides/streams.rst | 16 ++++++++++++++ redbot/cogs/streams/streams.py | 38 +++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/docs/cog_guides/streams.rst b/docs/cog_guides/streams.rst index cdc87375bd5..23aefd078c3 100644 --- a/docs/cog_guides/streams.rst +++ b/docs/cog_guides/streams.rst @@ -338,6 +338,22 @@ To set the Twitch API tokens, follow these steps: .. attention:: These tokens are sensitive and should only be used in a private channel or in DM with the bot. +.. _streams-command-streamset-usebuttons: + +^^^^^^^^^^^^^^^^^^^^ +streamset usebuttons +^^^^^^^^^^^^^^^^^^^^ + +**Syntax** + +.. code-block:: none + + [p]streamset usebuttons + +**Description** + +Toggle whether to use buttons for stream alerts. + .. _streams-command-picarto: ^^^^^^^ diff --git a/redbot/cogs/streams/streams.py b/redbot/cogs/streams/streams.py index 6de0774edb3..75951595364 100644 --- a/redbot/cogs/streams/streams.py +++ b/redbot/cogs/streams/streams.py @@ -61,6 +61,7 @@ class Streams(commands.Cog): "live_message_nomention": False, "ignore_reruns": False, "ignore_schedule": False, + "use_buttons": False, } role_defaults = {"mention": False} @@ -289,7 +290,18 @@ async def check_online( return else: embed = info - await ctx.send(embed=embed) + + use_buttons: bool = await self.config.guild(ctx.channel.guild).use_buttons() + view = None + if use_buttons: + stream_url = embed.url + view = discord.ui.View() + view.add_item( + discord.ui.Button( + label=_("Watch the stream"), style=discord.ButtonStyle.link, url=stream_url + ) + ) + await ctx.send(embed=embed, view=view) @commands.group() @commands.guild_only() @@ -705,6 +717,19 @@ async def ignore_schedule(self, ctx: commands.Context): await self.config.guild(guild).ignore_schedule.set(True) await ctx.send(_("Streams schedules will no longer send an alert.")) + @streamset.command(name="usebuttons") + @commands.guild_only() + async def use_buttons(self, ctx: commands.Context): + """Toggle whether to use buttons for stream alerts.""" + guild = ctx.guild + current_setting: bool = await self.config.guild(guild).use_buttons() + if current_setting: + await self.config.guild(guild).use_buttons.set(False) + await ctx.send(_("I will no longer use buttons in stream alerts.")) + else: + await self.config.guild(guild).use_buttons.set(True) + await ctx.send(_("I will use buttons in stream alerts.")) + async def add_or_remove(self, ctx: commands.Context, stream, discord_channel): if discord_channel.id not in stream.channels: stream.channels.append(discord_channel.id) @@ -773,10 +798,21 @@ async def _send_stream_alert( *, is_schedule: bool = False, ): + use_buttons: bool = await self.config.guild(channel.guild).use_buttons() + view = None + if use_buttons: + stream_url = embed.url + view = discord.ui.View() + view.add_item( + discord.ui.Button( + label=_("Watch the stream"), style=discord.ButtonStyle.link, url=stream_url + ) + ) m = await channel.send( content, embed=embed, allowed_mentions=discord.AllowedMentions(roles=True, everyone=True), + view=view, ) message_data = {"guild": m.guild.id, "channel": m.channel.id, "message": m.id} if is_schedule: From 519acedf46acc9c132a3d7b3498c9afaaf9f67b9 Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Fri, 30 Dec 2022 03:21:57 +0100 Subject: [PATCH 20/43] Make some dependency changes, support Python 3.10 and 3.11 (#5611) Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> --- .github/workflows/tests.yml | 14 +-- docs/cog_guides/customcommands.rst | 2 +- docs/conf.py | 6 +- ...nv.rst => _create-env-with-venv-intro.rst} | 15 ---- .../_includes/_create-env-with-venv-outro.rst | 10 +++ .../_includes/create-env-with-venv3.10.rst | 7 ++ .../_includes/create-env-with-venv3.11.rst | 7 ++ .../_includes/create-env-with-venv3.9.rst | 7 ++ .../install-guide-rhel8-derivatives.rst | 4 +- .../install-guide-rhel9-derivatives.rst | 4 +- .../_includes/install-python-pyenv.rst | 8 +- .../_includes/install-python310-pyenv.rst | 27 ++++++ ...8-pyenv.rst => install-python39-pyenv.rst} | 8 +- docs/install_guides/arch.rst | 16 +++- docs/install_guides/centos-7.rst | 4 +- docs/install_guides/debian-10.rst | 2 +- docs/install_guides/debian-11.rst | 4 +- docs/install_guides/fedora.rst | 4 +- docs/install_guides/mac.rst | 6 +- docs/install_guides/opensuse-leap-15.rst | 8 +- docs/install_guides/opensuse-tumbleweed.rst | 4 +- docs/install_guides/raspberry-pi-os-10.rst | 9 +- docs/install_guides/raspberry-pi-os-11.rst | 4 +- docs/install_guides/ubuntu-1804.rst | 10 ++- docs/install_guides/ubuntu-2004.rst | 14 +-- docs/install_guides/ubuntu-2204.rst | 14 +-- docs/install_guides/ubuntu-non-lts.rst | 17 +--- docs/install_guides/windows.rst | 6 +- docs/version_guarantees.rst | 1 - pyproject.toml | 7 +- redbot/__init__.py | 20 ++++- .../cogs/audio/core/utilities/local_tracks.py | 4 +- redbot/cogs/audio/core/utilities/queue.py | 16 ++-- redbot/cogs/customcom/customcom.py | 10 +-- redbot/core/bot.py | 2 + redbot/core/data_manager.py | 22 +++-- redbot/core/rpc.py | 13 ++- redbot/core/utils/_internal_utils.py | 12 +-- redbot/logging.py | 13 ++- redbot/pytest/rpc.py | 6 +- requirements/base.in | 15 ++-- requirements/base.txt | 88 +++++++++++-------- requirements/extra-doc.txt | 22 +++-- requirements/extra-postgres.txt | 2 +- requirements/extra-style.txt | 10 +-- requirements/extra-test.txt | 30 ++++--- setup.py | 4 +- tests/cogs/downloader/test_downloader.py | 18 ---- tests/cogs/downloader/test_git.py | 37 -------- tests/cogs/test_alias.py | 6 -- tests/cogs/test_economy.py | 8 -- tests/cogs/test_mod.py | 3 - tests/core/test_cog_manager.py | 6 -- tests/core/test_config.py | 58 ------------ tests/core/test_installation.py | 1 - tests/core/test_utils.py | 3 - tests/core/test_version.py | 2 +- tools/dev-requirements.txt | 2 +- tox.ini | 11 +-- 59 files changed, 322 insertions(+), 371 deletions(-) rename docs/install_guides/_includes/{create-env-with-venv.rst => _create-env-with-venv-intro.rst} (71%) create mode 100644 docs/install_guides/_includes/_create-env-with-venv-outro.rst create mode 100644 docs/install_guides/_includes/create-env-with-venv3.10.rst create mode 100644 docs/install_guides/_includes/create-env-with-venv3.11.rst create mode 100644 docs/install_guides/_includes/create-env-with-venv3.9.rst create mode 100644 docs/install_guides/_includes/install-python310-pyenv.rst rename docs/install_guides/_includes/{install-python38-pyenv.rst => install-python39-pyenv.rst} (83%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4783b8b7c0f..3ce7319283b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,8 +27,11 @@ jobs: python_version: "3.9" friendly_name: Python 3.9 - Tests - tox_env: py310 - python_version: "3.10-dev" - friendly_name: Python 3.10-dev - Tests + python_version: "3.10" + friendly_name: Python 3.10 - Tests + - tox_env: py311 + python_version: "3.11" + friendly_name: Python 3.11 - Tests - tox_env: style friendly_name: Style - tox_env: docs @@ -46,7 +49,7 @@ jobs: - name: Install tox run: | python -m pip install --upgrade pip - pip install 'tox<4' + pip install tox - name: Tox test env: TOXENV: ${{ matrix.tox_env }} @@ -59,7 +62,8 @@ jobs: python_version: - "3.8" - "3.9" - - "3.10-dev" + - "3.10" + - "3.11" fail-fast: false name: Tox - Postgres services: @@ -82,7 +86,7 @@ jobs: - name: Install tox run: | python -m pip install --upgrade pip - pip install 'tox<4' + pip install tox - name: Tox test env: TOXENV: postgres diff --git a/docs/cog_guides/customcommands.rst b/docs/cog_guides/customcommands.rst index c6ba9794419..e79526f5b50 100644 --- a/docs/cog_guides/customcommands.rst +++ b/docs/cog_guides/customcommands.rst @@ -267,7 +267,7 @@ customcom search Searches through custom commands, according to the query. -Uses fuzzywuzzy searching to find close matches. +Uses fuzzy searching to find close matches. **Arguments:** diff --git a/docs/conf.py b/docs/conf.py index 73eec3ea4b5..a99240fae57 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -82,7 +82,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -246,8 +246,8 @@ # :dpy_docs:`link text ` extlinks = { "dpy_docs": (f"{dpy_docs_url}/%s", None), - "issue": ("https://github.com/Cog-Creators/Red-DiscordBot/issues/%s", "#"), - "ghuser": ("https://github.com/%s", "@"), + "issue": ("https://github.com/Cog-Creators/Red-DiscordBot/issues/%s", "#%s"), + "ghuser": ("https://github.com/%s", "@%s"), } # Doctest diff --git a/docs/install_guides/_includes/create-env-with-venv.rst b/docs/install_guides/_includes/_create-env-with-venv-intro.rst similarity index 71% rename from docs/install_guides/_includes/create-env-with-venv.rst rename to docs/install_guides/_includes/_create-env-with-venv-intro.rst index b51ba8b4bee..dde85481a72 100644 --- a/docs/install_guides/_includes/create-env-with-venv.rst +++ b/docs/install_guides/_includes/_create-env-with-venv-intro.rst @@ -21,18 +21,3 @@ to keep it in a location which is easy to type out the path to. From now, we'll ``redenv`` and it will be located in your home directory. Create your virtual environment with the following command: - -.. prompt:: bash - - python3.9 -m venv ~/redenv - -And activate it with the following command: - -.. prompt:: bash - - source ~/redenv/bin/activate - -.. important:: - - You must activate the virtual environment with the above command every time you open a new - shell to run, install or update Red. diff --git a/docs/install_guides/_includes/_create-env-with-venv-outro.rst b/docs/install_guides/_includes/_create-env-with-venv-outro.rst new file mode 100644 index 00000000000..e3b9eca4b9f --- /dev/null +++ b/docs/install_guides/_includes/_create-env-with-venv-outro.rst @@ -0,0 +1,10 @@ +And activate it with the following command: + +.. prompt:: bash + + source ~/redenv/bin/activate + +.. important:: + + You must activate the virtual environment with the above command every time you open a new + shell to run, install or update Red. diff --git a/docs/install_guides/_includes/create-env-with-venv3.10.rst b/docs/install_guides/_includes/create-env-with-venv3.10.rst new file mode 100644 index 00000000000..af858034ce0 --- /dev/null +++ b/docs/install_guides/_includes/create-env-with-venv3.10.rst @@ -0,0 +1,7 @@ +.. include:: _includes/_create-env-with-venv-intro.rst + +.. prompt:: bash + + python3.10 -m venv ~/redenv + +.. include:: _includes/_create-env-with-venv-outro.rst diff --git a/docs/install_guides/_includes/create-env-with-venv3.11.rst b/docs/install_guides/_includes/create-env-with-venv3.11.rst new file mode 100644 index 00000000000..1f77150d4e5 --- /dev/null +++ b/docs/install_guides/_includes/create-env-with-venv3.11.rst @@ -0,0 +1,7 @@ +.. include:: _includes/_create-env-with-venv-intro.rst + +.. prompt:: bash + + python3.11 -m venv ~/redenv + +.. include:: _includes/_create-env-with-venv-outro.rst diff --git a/docs/install_guides/_includes/create-env-with-venv3.9.rst b/docs/install_guides/_includes/create-env-with-venv3.9.rst new file mode 100644 index 00000000000..b2aed286986 --- /dev/null +++ b/docs/install_guides/_includes/create-env-with-venv3.9.rst @@ -0,0 +1,7 @@ +.. include:: _includes/_create-env-with-venv-intro.rst + +.. prompt:: bash + + python3.9 -m venv ~/redenv + +.. include:: _includes/_create-env-with-venv-outro.rst diff --git a/docs/install_guides/_includes/install-guide-rhel8-derivatives.rst b/docs/install_guides/_includes/install-guide-rhel8-derivatives.rst index 8f64ce01473..7052a25dbe5 100644 --- a/docs/install_guides/_includes/install-guide-rhel8-derivatives.rst +++ b/docs/install_guides/_includes/install-guide-rhel8-derivatives.rst @@ -13,7 +13,7 @@ Install them with dnf: sudo dnf -y update sudo dnf -y group install development - sudo dnf -y install python39 python39-pip python39-devel java-11-openjdk-headless nano git + sudo dnf -y install python39 python39-devel java-11-openjdk-headless nano git Set ``java`` executable to point to Java 11: @@ -23,6 +23,6 @@ Set ``java`` executable to point to Java 11: .. Include common instructions: -.. include:: _includes/create-env-with-venv.rst +.. include:: _includes/create-env-with-venv3.9.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/_includes/install-guide-rhel9-derivatives.rst b/docs/install_guides/_includes/install-guide-rhel9-derivatives.rst index 83dd9c52b5a..9e149cc5601 100644 --- a/docs/install_guides/_includes/install-guide-rhel9-derivatives.rst +++ b/docs/install_guides/_includes/install-guide-rhel9-derivatives.rst @@ -11,10 +11,10 @@ Install them with dnf: .. prompt:: bash - sudo dnf -y install python39 git java-11-openjdk-headless @development nano + sudo dnf -y install python39 python3-devel git java-11-openjdk-headless @development nano .. Include common instructions: -.. include:: _includes/create-env-with-venv.rst +.. include:: _includes/create-env-with-venv3.9.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/_includes/install-python-pyenv.rst b/docs/install_guides/_includes/install-python-pyenv.rst index 438ca33ba14..ff6f88ecc06 100644 --- a/docs/install_guides/_includes/install-python-pyenv.rst +++ b/docs/install_guides/_includes/install-python-pyenv.rst @@ -2,7 +2,7 @@ Installing Python with pyenv ---------------------------- -On distributions where Python 3.9 needs to be compiled from source, we recommend the use of pyenv. +On distributions where Python 3.11 needs to be compiled from source, we recommend the use of pyenv. This simplifies the compilation process and has the added bonus of simplifying setting up Red in a virtual environment. @@ -10,7 +10,7 @@ virtual environment. .. prompt:: bash - CONFIGURE_OPTS=--enable-optimizations pyenv install 3.9.9 -v + CONFIGURE_OPTS=--enable-optimizations pyenv install 3.11.1 -v This may take a long time to complete, depending on your hardware. For some machines (such as Raspberry Pis and micro-tier VPSes), it may take over an hour; in this case, you may wish to remove @@ -22,6 +22,6 @@ After that is finished, run: .. prompt:: bash - pyenv global 3.9.9 + pyenv global 3.11.1 -Pyenv is now installed and your system should be configured to run Python 3.9. +Pyenv is now installed and your system should be configured to run Python 3.11. diff --git a/docs/install_guides/_includes/install-python310-pyenv.rst b/docs/install_guides/_includes/install-python310-pyenv.rst new file mode 100644 index 00000000000..c604092927c --- /dev/null +++ b/docs/install_guides/_includes/install-python310-pyenv.rst @@ -0,0 +1,27 @@ +---------------------------- +Installing Python with pyenv +---------------------------- + +On distributions where Python 3.10 needs to be compiled from source, we recommend the use of pyenv. +This simplifies the compilation process and has the added bonus of simplifying setting up Red in a +virtual environment. + +.. include:: _includes/_install-pyenv-and-setup-path.rst + +.. prompt:: bash + + CONFIGURE_OPTS=--enable-optimizations pyenv install 3.10.9 -v + +This may take a long time to complete, depending on your hardware. For some machines (such as +Raspberry Pis and micro-tier VPSes), it may take over an hour; in this case, you may wish to remove +the ``CONFIGURE_OPTS=--enable-optimizations`` part from the front of the command, which will +drastically reduce the install time. However, be aware that this will make Python run about 10% +slower. + +After that is finished, run: + +.. prompt:: bash + + pyenv global 3.10.9 + +Pyenv is now installed and your system should be configured to run Python 3.10. diff --git a/docs/install_guides/_includes/install-python38-pyenv.rst b/docs/install_guides/_includes/install-python39-pyenv.rst similarity index 83% rename from docs/install_guides/_includes/install-python38-pyenv.rst rename to docs/install_guides/_includes/install-python39-pyenv.rst index f4831486808..ce312eb087d 100644 --- a/docs/install_guides/_includes/install-python38-pyenv.rst +++ b/docs/install_guides/_includes/install-python39-pyenv.rst @@ -2,7 +2,7 @@ Installing Python with pyenv ---------------------------- -On distributions where Python 3.8 needs to be compiled from source, we recommend the use of pyenv. +On distributions where Python 3.9 needs to be compiled from source, we recommend the use of pyenv. This simplifies the compilation process and has the added bonus of simplifying setting up Red in a virtual environment. @@ -10,7 +10,7 @@ virtual environment. .. prompt:: bash - CONFIGURE_OPTS=--enable-optimizations pyenv install 3.8.12 -v + CONFIGURE_OPTS=--enable-optimizations pyenv install 3.9.16 -v This may take a long time to complete, depending on your hardware. For some machines (such as Raspberry Pis and micro-tier VPSes), it may take over an hour; in this case, you may wish to remove @@ -22,6 +22,6 @@ After that is finished, run: .. prompt:: bash - pyenv global 3.8.12 + pyenv global 3.9.16 -Pyenv is now installed and your system should be configured to run Python 3.8. +Pyenv is now installed and your system should be configured to run Python 3.9. diff --git a/docs/install_guides/arch.rst b/docs/install_guides/arch.rst index f69adf25705..0b472fb4a80 100644 --- a/docs/install_guides/arch.rst +++ b/docs/install_guides/arch.rst @@ -16,10 +16,22 @@ Install the pre-requirements with pacman: .. prompt:: bash - sudo pacman -Syu python python-pip git jre11-openjdk-headless base-devel nano + sudo pacman -Syu git jre11-openjdk-headless base-devel nano + +On Arch Linux, Python 3.9 can be installed from the Arch User Repository (AUR) from the ``python39`` package. + +The manual build process is the Arch-supported install method for AUR packages. You can install ``python39`` package with the following commands: + +.. prompt:: bash + + git clone https://aur.archlinux.org/python39.git /tmp/python39 + cd /tmp/python39 + makepkg -sicL + cd - + rm -rf /tmp/python39 .. Include common instructions: -.. include:: _includes/create-env-with-venv.rst +.. include:: _includes/create-env-with-venv3.9.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/centos-7.rst b/docs/install_guides/centos-7.rst index bdce4faf92c..f49e59dcab7 100644 --- a/docs/install_guides/centos-7.rst +++ b/docs/install_guides/centos-7.rst @@ -37,7 +37,9 @@ In order to install Git 2.11 or greater, we recommend adding the IUS repository: .. Include common instructions: -.. include:: _includes/install-python-pyenv.rst +.. Python 3.10 requires OpenSSL 1.1.1 which CentOS 7 doesn't provide in base repository. + +.. include:: _includes/install-python39-pyenv.rst .. include:: _includes/create-env-with-pyenv-virtualenv.rst diff --git a/docs/install_guides/debian-10.rst b/docs/install_guides/debian-10.rst index d71d105c96a..47ee4e28d1b 100644 --- a/docs/install_guides/debian-10.rst +++ b/docs/install_guides/debian-10.rst @@ -18,7 +18,7 @@ Debian Buster. This guide will tell you how. First, run the following commands: .. prompt:: bash sudo apt update - sudo apt -y install make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev libgdbm-dev uuid-dev python3-openssl git openjdk-11-jre-headless nano + sudo apt -y install make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev libgdbm-dev uuid-dev python3-openssl git openjdk-11-jre-headless nano CXX=/usr/bin/g++ .. Include common instructions: diff --git a/docs/install_guides/debian-11.rst b/docs/install_guides/debian-11.rst index 19488943a17..4ea9bd0141b 100644 --- a/docs/install_guides/debian-11.rst +++ b/docs/install_guides/debian-11.rst @@ -18,10 +18,10 @@ with apt: .. prompt:: bash sudo apt update - sudo apt -y install python3 python3-dev python3-venv python3-pip git openjdk-11-jre-headless build-essential nano + sudo apt -y install python3 python3-dev python3-venv git openjdk-11-jre-headless build-essential nano .. Include common instructions: -.. include:: _includes/create-env-with-venv.rst +.. include:: _includes/create-env-with-venv3.9.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/fedora.rst b/docs/install_guides/fedora.rst index 9492b72cda2..79de09f6857 100644 --- a/docs/install_guides/fedora.rst +++ b/docs/install_guides/fedora.rst @@ -17,10 +17,10 @@ them with dnf: .. prompt:: bash - sudo dnf -y install python39 git java-11-openjdk-headless @development-tools nano + sudo dnf -y install python3.10 python3.10-devel git java-11-openjdk-headless @development-tools nano .. Include common instructions: -.. include:: _includes/create-env-with-venv.rst +.. include:: _includes/create-env-with-venv3.10.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/mac.rst b/docs/install_guides/mac.rst index e2127f61cb1..b3c44e32fde 100644 --- a/docs/install_guides/mac.rst +++ b/docs/install_guides/mac.rst @@ -23,7 +23,7 @@ one-by-one: .. prompt:: bash - brew install python@3.9 + brew install python@3.11 brew install git brew tap homebrew/cask-versions brew install --cask temurin11 @@ -34,11 +34,11 @@ To fix this, you should run these commands: .. prompt:: bash profile=$([ -n "$ZSH_VERSION" ] && echo ~/.zprofile || ([ -f ~/.bash_profile ] && echo ~/.bash_profile || echo ~/.profile)) - echo 'export PATH="$(brew --prefix)/opt/python@3.9/bin:$PATH"' >> "$profile" + echo 'export PATH="$(brew --prefix)/opt/python@3.11/bin:$PATH"' >> "$profile" source "$profile" .. Include common instructions: -.. include:: _includes/create-env-with-venv.rst +.. include:: _includes/create-env-with-venv3.11.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/opensuse-leap-15.rst b/docs/install_guides/opensuse-leap-15.rst index a6e9be37eb6..e5f24d692ec 100644 --- a/docs/install_guides/opensuse-leap-15.rst +++ b/docs/install_guides/opensuse-leap-15.rst @@ -1,7 +1,7 @@ .. _install-opensuse-leap-15: ===================================== -Installing Red on openSUSE Leap 15.3+ +Installing Red on openSUSE Leap 15.4+ ===================================== .. include:: _includes/supported-arch-x64+aarch64.rst @@ -12,16 +12,16 @@ Installing Red on openSUSE Leap 15.3+ Installing the pre-requirements ------------------------------- -openSUSE Leap 15.3+ has all required dependencies available in official repositories. Install them +openSUSE Leap 15.4+ has all required dependencies available in official repositories. Install them with zypper: .. prompt:: bash - sudo zypper -n install python39-base python39-pip git-core java-11-openjdk-headless nano + sudo zypper -n install python310 python310-devel git-core java-11-openjdk-headless nano sudo zypper -n install -t pattern devel_basis .. Include common instructions: -.. include:: _includes/create-env-with-venv.rst +.. include:: _includes/create-env-with-venv3.10.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/opensuse-tumbleweed.rst b/docs/install_guides/opensuse-tumbleweed.rst index 588789360d2..047b5eaad96 100644 --- a/docs/install_guides/opensuse-tumbleweed.rst +++ b/docs/install_guides/opensuse-tumbleweed.rst @@ -17,11 +17,11 @@ with zypper: .. prompt:: bash - sudo zypper -n install python39-base python39-pip git-core java-11-openjdk-headless nano + sudo zypper -n install python311 python311-devel git-core java-11-openjdk-headless nano sudo zypper -n install -t pattern devel_basis .. Include common instructions: -.. include:: _includes/create-env-with-venv.rst +.. include:: _includes/create-env-with-venv3.11.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/raspberry-pi-os-10.rst b/docs/install_guides/raspberry-pi-os-10.rst index 5d28a491467..48bf582daea 100644 --- a/docs/install_guides/raspberry-pi-os-10.rst +++ b/docs/install_guides/raspberry-pi-os-10.rst @@ -35,15 +35,20 @@ Installing the pre-requirements We recommend installing pyenv as a method of installing non-native versions of Python on Raspberry Pi OS. This guide will tell you how. First, run the following commands: +.. cmake is necessary to be able to successfuly build rapidfuzz. + .. prompt:: bash sudo apt update - sudo apt -y install make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev libgdbm-dev uuid-dev python3-openssl git openjdk-11-jre-headless nano + sudo apt -y install cmake make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev libgdbm-dev uuid-dev python3-openssl git openjdk-11-jre-headless nano CXX=/usr/bin/g++ .. Include common instructions: -.. include:: _includes/install-python38-pyenv.rst +.. We should only build and install even versions of Python on Raspberry Pi OS as odd +.. versions are part of piwheels and can cause installs of pip packages that won't work. + +.. include:: _includes/install-python310-pyenv.rst .. include:: _includes/create-env-with-pyenv-virtualenv.rst diff --git a/docs/install_guides/raspberry-pi-os-11.rst b/docs/install_guides/raspberry-pi-os-11.rst index 1882d2eb60c..5e8eeead535 100644 --- a/docs/install_guides/raspberry-pi-os-11.rst +++ b/docs/install_guides/raspberry-pi-os-11.rst @@ -33,10 +33,10 @@ with apt: .. prompt:: bash sudo apt update - sudo apt -y install python3 python3-dev python3-venv python3-pip git openjdk-11-jre-headless build-essential nano + sudo apt -y install python3 python3-dev python3-venv git openjdk-11-jre-headless build-essential nano .. Include common instructions: -.. include:: _includes/create-env-with-venv.rst +.. include:: _includes/create-env-with-venv3.9.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/ubuntu-1804.rst b/docs/install_guides/ubuntu-1804.rst index 9cd4d60168b..a967fb5334b 100644 --- a/docs/install_guides/ubuntu-1804.rst +++ b/docs/install_guides/ubuntu-1804.rst @@ -12,6 +12,10 @@ Installing Red on Ubuntu 18.04 LTS Installing the pre-requirements ------------------------------- +.. Git 2.17.0-2.22.0 have an issue with partial clone which is used in pip for git installs. +.. Not incredibly important perhaps but this ppa is recommended by git-scm.com/download/linux +.. so it should be fine. + We recommend adding the ``git-core`` ppa to install Git 2.11 or greater: .. prompt:: bash @@ -20,7 +24,7 @@ We recommend adding the ``git-core`` ppa to install Git 2.11 or greater: sudo apt -y install software-properties-common sudo add-apt-repository -y ppa:git-core/ppa -We recommend adding the ``deadsnakes`` ppa to install Python 3.9: +We recommend adding the ``deadsnakes`` ppa to install Python 3.11: .. prompt:: bash @@ -30,10 +34,10 @@ Now install the pre-requirements with apt: .. prompt:: bash - sudo apt -y install python3.9 python3.9-dev python3.9-venv python3-pip git openjdk-11-jre-headless build-essential nano + sudo apt -y install python3.11 python3.11-dev python3.11-venv git openjdk-11-jre-headless build-essential nano .. Include common instructions: -.. include:: _includes/create-env-with-venv.rst +.. include:: _includes/create-env-with-venv3.11.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/ubuntu-2004.rst b/docs/install_guides/ubuntu-2004.rst index 074973e61af..0ad76922a62 100644 --- a/docs/install_guides/ubuntu-2004.rst +++ b/docs/install_guides/ubuntu-2004.rst @@ -12,22 +12,16 @@ Installing Red on Ubuntu 20.04 LTS Installing the pre-requirements ------------------------------- -We recommend adding the ``git-core`` ppa to install Git 2.11 or greater: +Ubuntu 20.04 LTS has all required packages available in official repositories. Install them +with apt: .. prompt:: bash sudo apt update - sudo apt -y install software-properties-common - sudo add-apt-repository -y ppa:git-core/ppa - -Now install the pre-requirements with apt: - -.. prompt:: bash - - sudo apt -y install python3.9 python3.9-dev python3.9-venv python3-pip git openjdk-11-jre-headless build-essential nano + sudo apt -y install python3.9 python3.9-dev python3.9-venv git openjdk-11-jre-headless build-essential nano .. Include common instructions: -.. include:: _includes/create-env-with-venv.rst +.. include:: _includes/create-env-with-venv3.9.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/ubuntu-2204.rst b/docs/install_guides/ubuntu-2204.rst index 74e68372e76..50df7c0ad13 100644 --- a/docs/install_guides/ubuntu-2204.rst +++ b/docs/install_guides/ubuntu-2204.rst @@ -12,22 +12,16 @@ Installing Red on Ubuntu 22.04 LTS Installing the pre-requirements ------------------------------- -We recommend adding the ``deadsnakes`` ppa to install Python 3.9: +Ubuntu 22.04 LTS has all required packages available in official repositories. Install them +with apt: .. prompt:: bash sudo apt update - sudo apt -y install software-properties-common - sudo add-apt-repository -y ppa:deadsnakes/ppa - -Now install the pre-requirements with apt: - -.. prompt:: bash - - sudo apt -y install python3.9 python3.9-dev python3.9-venv python3-pip git openjdk-11-jre-headless build-essential nano + sudo apt -y install python3.10 python3.10-dev python3.10-venv git openjdk-11-jre-headless build-essential nano .. Include common instructions: -.. include:: _includes/create-env-with-venv.rst +.. include:: _includes/create-env-with-venv3.10.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/ubuntu-non-lts.rst b/docs/install_guides/ubuntu-non-lts.rst index b12365279a4..b2db202af7e 100644 --- a/docs/install_guides/ubuntu-non-lts.rst +++ b/docs/install_guides/ubuntu-non-lts.rst @@ -12,26 +12,15 @@ Installing Red on Ubuntu non-LTS versions Installing the pre-requirements ------------------------------- -We recommend adding the ``git-core`` ppa to install Git 2.11 or greater: +Now install the pre-requirements with apt: .. prompt:: bash sudo apt update - sudo apt -y install software-properties-common - sudo add-apt-repository -yu ppa:git-core/ppa - -Now, to install non-native version of python on non-LTS versions of Ubuntu, we recommend -installing pyenv. To do this, first run the following commands: - -.. prompt:: bash - - sudo apt -y install make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev libgdbm-dev uuid-dev python3-openssl git openjdk-11-jre-headless nano - CXX=/usr/bin/g++ + sudo apt -y install python3.11 python3.11-dev python3.11-venv git openjdk-11-jre-headless build-essential nano .. Include common instructions: -.. include:: _includes/install-python-pyenv.rst - -.. include:: _includes/create-env-with-pyenv-virtualenv.rst +.. include:: _includes/create-env-with-venv3.11.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/install_guides/windows.rst b/docs/install_guides/windows.rst index 5584154d0f1..de3ed786629 100644 --- a/docs/install_guides/windows.rst +++ b/docs/install_guides/windows.rst @@ -33,7 +33,7 @@ Then run each of the following commands: iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) choco upgrade git --params "/GitOnlyOnPath /WindowsTerminal" -y choco upgrade visualstudio2022-workload-vctools -y - choco upgrade python3 -y --version 3.9.9 + choco upgrade python311 -y For Audio support, you should also run the following command before exiting: @@ -57,7 +57,7 @@ Manually installing dependencies * `MSVC Build tools `_ -* `Python 3.8.1 - 3.9.x `_ +* `Python 3.8.1 - 3.11.x `_ .. attention:: Please make sure that the box to add Python to PATH is CHECKED, otherwise you may run into issues when trying to run Red. @@ -104,7 +104,7 @@ Then create your virtual environment with the following command .. prompt:: batch - py -3.9 -m venv "%userprofile%\redenv" + py -3.11 -m venv "%userprofile%\redenv" And activate it with the following command diff --git a/docs/version_guarantees.rst b/docs/version_guarantees.rst index 2ad9f1dfe05..758a32f115b 100644 --- a/docs/version_guarantees.rst +++ b/docs/version_guarantees.rst @@ -64,7 +64,6 @@ Debian 10 Buster x86-64, aarch64, armv7l 2022-08-14 (`End of Debian 11 Bullseye x86-64, aarch64, armv7l ~2024-09 (`End of life `__) Fedora Linux 35 x86-64, aarch64 2022-11-15 (`End of Life `__) Fedora Linux 36 x86-64, aarch64 2023-05-16 (`End of Life `__) -openSUSE Leap 15.3 x86-64, aarch64 2022-11-30 (`end of maintenance life cycle `__) openSUSE Leap 15.4 x86-64, aarch64 2023-11-30 (`end of maintenance life cycle `__) openSUSE Tumbleweed x86-64, aarch64 forever (support is only provided for an up-to-date system) Oracle Linux 8 x86-64, aarch64 2029-07-31 (`End of Premier Support `__) diff --git a/pyproject.toml b/pyproject.toml index 08529574eb5..fa005b9e25a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,8 @@ classifiers = [ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Communications :: Chat", ] dynamic = ["version", "requires-python", "dependencies", "optional-dependencies"] @@ -42,7 +44,7 @@ red-discordbot = "redbot.pytest" [tool.black] line-length = 99 -required-version = '22.1.0' +required-version = '22' target-version = ['py38'] include = '\.py$' force-exclude = ''' @@ -50,3 +52,6 @@ force-exclude = ''' redbot\/vendored )/ ''' + +[tool.pytest.ini_options] +asyncio_mode = 'auto' diff --git a/redbot/__init__.py b/redbot/__init__.py index caed5eddd32..967459d06e5 100644 --- a/redbot/__init__.py +++ b/redbot/__init__.py @@ -269,8 +269,12 @@ def _ensure_no_colorama(): from rich.console import detect_legacy_windows if not detect_legacy_windows(): - import colorama - import colorama.initialise + try: + import colorama + import colorama.initialise + except ModuleNotFoundError: + # colorama is not Red's primary dependency so it might not be present + return colorama.deinit() @@ -300,8 +304,6 @@ def _early_init(): __version__, version_info = VersionInfo._get_version() -# Filter fuzzywuzzy slow sequence matcher warning -_warnings.filterwarnings("ignore", module=r"fuzzywuzzy.*") # Show DeprecationWarning _warnings.filterwarnings("default", category=DeprecationWarning) @@ -320,3 +322,13 @@ def _early_init(): module="asyncio", message="The loop argument is deprecated since Python 3.8", ) + # DEP-WARN - d.py currently uses audioop module, Danny is aware of the deprecation + # + # DeprecationWarning: 'audioop' is deprecated and slated for removal in Python 3.13 + # import audioop + _warnings.filterwarnings( + "ignore", + category=DeprecationWarning, + module="discord", + message="'audioop' is deprecated and slated for removal", + ) diff --git a/redbot/cogs/audio/core/utilities/local_tracks.py b/redbot/cogs/audio/core/utilities/local_tracks.py index f4a42bae2b4..ed33f215757 100644 --- a/redbot/cogs/audio/core/utilities/local_tracks.py +++ b/redbot/cogs/audio/core/utilities/local_tracks.py @@ -7,7 +7,7 @@ import lavalink from red_commons.logging import getLogger -from fuzzywuzzy import process +from rapidfuzz import process from redbot.core import commands from redbot.core.i18n import Translator from redbot.core.utils import AsyncIter @@ -118,7 +118,7 @@ async def _build_local_search_list( } search_results = process.extract(search_words, to_search_string, limit=50) search_list = [] - async for track_match, percent_match in AsyncIter(search_results): + async for track_match, percent_match, __ in AsyncIter(search_results): if percent_match > 85: search_list.extend( [ diff --git a/redbot/cogs/audio/core/utilities/queue.py b/redbot/cogs/audio/core/utilities/queue.py index 0024a56b078..d6d8c3a8bee 100644 --- a/redbot/cogs/audio/core/utilities/queue.py +++ b/redbot/cogs/audio/core/utilities/queue.py @@ -7,7 +7,7 @@ import lavalink from red_commons.logging import getLogger -from fuzzywuzzy import process +from rapidfuzz import process from redbot.core import commands from redbot.core.i18n import Translator from redbot.core.utils import AsyncIter @@ -116,7 +116,7 @@ async def _build_queue_page( async def _build_queue_search_list( self, queue_list: List[lavalink.Track], search_words: str ) -> List[Tuple[int, str]]: - track_list = [] + tracks = {} async for queue_idx, track in AsyncIter(queue_list).enumerate(start=1): if not self.match_url(track.uri): query = Query.process_input(track, self.local_folder_current_path) @@ -131,14 +131,12 @@ async def _build_queue_search_list( else: track_title = track.title - song_info = {str(queue_idx): track_title} - track_list.append(song_info) - search_results = process.extract(search_words, track_list, limit=50) + tracks[queue_idx] = track_title + search_results = process.extract(search_words, tracks, limit=50) search_list = [] - async for search, percent_match in AsyncIter(search_results): - async for queue_position, title in AsyncIter(search.items()): - if percent_match > 89: - search_list.append((queue_position, title)) + async for title, percent_match, queue_position in AsyncIter(search_results): + if percent_match > 89: + search_list.append((queue_position, title)) return search_list async def _build_queue_search_page( diff --git a/redbot/cogs/customcom/customcom.py b/redbot/cogs/customcom/customcom.py index 6865075a56d..659e45ed951 100644 --- a/redbot/cogs/customcom/customcom.py +++ b/redbot/cogs/customcom/customcom.py @@ -7,7 +7,7 @@ from urllib.parse import quote_plus import discord -from fuzzywuzzy import process +from rapidfuzz import process from redbot.core import Config, checks, commands from redbot.core.i18n import Translator, cog_i18n @@ -317,7 +317,7 @@ async def cc_search(self, ctx: commands.Context, *, query): """ Searches through custom commands, according to the query. - Uses fuzzywuzzy searching to find close matches. + Uses fuzzy searching to find close matches. **Arguments:** @@ -326,10 +326,10 @@ async def cc_search(self, ctx: commands.Context, *, query): cc_commands = await CommandObj.get_commands(self.config.guild(ctx.guild)) extracted = process.extract(query, list(cc_commands.keys())) accepted = [] - for entry in extracted: - if entry[1] > 60: + for key, score, __ in extracted: + if score > 60: # Match was decently strong - accepted.append((entry[0], cc_commands[entry[0]])) + accepted.append((key, cc_commands[key])) else: # Match wasn't strong enough pass diff --git a/redbot/core/bot.py b/redbot/core/bot.py index a055d9617e7..7e58375c05a 100644 --- a/redbot/core/bot.py +++ b/redbot/core/bot.py @@ -1098,6 +1098,8 @@ async def _pre_login(self) -> None: """ This should only be run once, prior to logging in to Discord REST API. """ + await super()._pre_login() + await self._maybe_update_config() self.description = await self._config.description() self._color = discord.Colour(await self._config.color()) diff --git a/redbot/core/data_manager.py b/redbot/core/data_manager.py index 762a99a7100..85169ad8602 100644 --- a/redbot/core/data_manager.py +++ b/redbot/core/data_manager.py @@ -8,7 +8,7 @@ from pathlib import Path from typing import Any, Dict -import appdirs +import platformdirs from discord.utils import deprecated from . import commands @@ -37,22 +37,30 @@ "CORE_PATH_APPEND": "core", } -appdir = appdirs.AppDirs("Red-DiscordBot") -config_dir = Path(appdir.user_config_dir) +appdir = platformdirs.PlatformDirs("Red-DiscordBot") +config_dir = appdir.user_config_path _system_user = sys.platform == "linux" and 0 < os.getuid() < 1000 if _system_user: if Path.home().exists(): # We don't want to break someone just because they created home dir - # but were already using the site_data_dir. + # but were already using the site_data_path. # - # But otherwise, we do want Red to use user_config_dir if home dir exists. - _maybe_config_file = Path(appdir.site_data_dir) / "config.json" + # But otherwise, we do want Red to use user_config_path if home dir exists. + _maybe_config_file = appdir.site_data_path / "config.json" if _maybe_config_file.exists(): config_dir = _maybe_config_file.parent else: - config_dir = Path(appdir.site_data_dir) + config_dir = appdir.site_data_path config_file = config_dir / "config.json" +if not config_file.exists() and sys.platform == "darwin": + # backwards compatibility with the location given by appdirs 1.4.4 (replaced by platformdirs 2) + # which was the same as user_data_path + # https://platformdirs.readthedocs.io/en/stable/changelog.html#platformdirs-2-0-0 + _old_config_location = appdir.user_data_path / "config.json" + if _old_config_location.exists(): + config_dir.mkdir(parents=True, exist_ok=True) + _old_config_location.rename(config_file) def load_existing_config(): diff --git a/redbot/core/rpc.py b/redbot/core/rpc.py index b2ee0eb4a57..b5f0a9f1f07 100644 --- a/redbot/core/rpc.py +++ b/redbot/core/rpc.py @@ -64,14 +64,20 @@ class RPC: RPC server manager. """ + app: web.Application + _rpc: RedRpc + _runner: web.AppRunner + def __init__(self): + self._site: Optional[web.TCPSite] = None + self._started = False + + async def _pre_login(self) -> None: self.app = web.Application() self._rpc = RedRpc() self.app.router.add_route("*", "/", self._rpc.handle_request) self._runner = web.AppRunner(self.app) - self._site: Optional[web.TCPSite] = None - self._started = False async def initialize(self, port: int): """ @@ -134,6 +140,9 @@ def __init__(self, **kwargs): self.rpc_handlers = {} # Uppercase cog name to method + async def _pre_login(self) -> None: + await self.rpc._pre_login() + def register_rpc_handler(self, method): """ Registers a method to act as an RPC handler if the internal RPC server is active. diff --git a/redbot/core/utils/_internal_utils.py b/redbot/core/utils/_internal_utils.py index 8407fdedfe8..5ea383549c9 100644 --- a/redbot/core/utils/_internal_utils.py +++ b/redbot/core/utils/_internal_utils.py @@ -32,7 +32,7 @@ import aiohttp import discord from packaging.requirements import Requirement -from fuzzywuzzy import fuzz, process +from rapidfuzz import fuzz, process from rich.progress import ProgressColumn from rich.progress_bar import ProgressBar from red_commons.logging import VERBOSE, TRACE @@ -147,20 +147,20 @@ async def fuzzy_command_search( return None if commands is None: - choices = set(ctx.bot.walk_commands()) + choices = {c: c.qualified_name for c in ctx.bot.walk_commands()} elif isinstance(commands, collections.abc.AsyncIterator): - choices = {c async for c in commands} + choices = {c: c.qualified_name async for c in commands} else: - choices = set(commands) + choices = {c: c.qualified_name for c in commands} - # Do the scoring. `extracted` is a list of tuples in the form `(command, score)` + # Do the scoring. `extracted` is a list of tuples in the form `(cmd_name, score, cmd)` extracted = process.extract(term, choices, limit=5, scorer=fuzz.QRatio) if not extracted: return None # Filter through the fuzzy-matched commands. matched_commands = [] - for command, score in extracted: + for __, score, command in extracted: if score < min_score: # Since the list is in decreasing order of score, we can exit early. break diff --git a/redbot/logging.py b/redbot/logging.py index 5b5ef5991ac..0e21793e52e 100644 --- a/redbot/logging.py +++ b/redbot/logging.py @@ -10,7 +10,7 @@ from os import isatty import rich -from pygments.styles.monokai import MonokaiStyle +from pygments.styles.monokai import MonokaiStyle # DEP-WARN from pygments.token import ( Comment, Error, @@ -22,16 +22,14 @@ Token, ) from rich._log_render import LogRender # DEP-WARN -from rich.console import render_group -from rich.containers import Renderables +from rich.console import group from rich.highlighter import NullHighlighter from rich.logging import RichHandler from rich.style import Style -from rich.syntax import ANSISyntaxTheme, PygmentsSyntaxTheme -from rich.table import Table +from rich.syntax import ANSISyntaxTheme, PygmentsSyntaxTheme # DEP-WARN from rich.text import Text from rich.theme import Theme -from rich.traceback import PathHighlighter, Traceback +from rich.traceback import PathHighlighter, Traceback # DEP-WARN MAX_OLD_LOGS = 8 @@ -151,7 +149,8 @@ class FixedMonokaiStyle(MonokaiStyle): class RedTraceback(Traceback): - @render_group() + # DEP-WARN + @group() def _render_stack(self, stack): for obj in super()._render_stack.__wrapped__(self, stack): if obj != "": diff --git a/redbot/pytest/rpc.py b/redbot/pytest/rpc.py index 5ad7900da31..ffe3029d7a1 100644 --- a/redbot/pytest/rpc.py +++ b/redbot/pytest/rpc.py @@ -7,8 +7,10 @@ @pytest.fixture() -def rpc(): - return RPC() +async def rpc(): + rpc = RPC() + await rpc._pre_login() + return rpc @pytest.fixture() diff --git a/requirements/base.in b/requirements/base.in index 53bb09b0e7f..ca4f650f726 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -1,23 +1,24 @@ +aiodns aiohttp aiohttp-json-rpc -aiosqlite -appdirs -apsw-wheels +apsw babel +Brotli click -colorama discord.py -fuzzywuzzy markdown +orjson packaging +platformdirs psutil python-dateutil -python-Levenshtein-wheels -PyNaCl PyYAML +rapidfuzz Red-Commons Red-Lavalink>=0.11.0rc1 rich schema +typing_extensions +yarl distro; sys_platform == "linux" uvloop; sys_platform != "win32" and platform_python_implementation == "CPython" diff --git a/requirements/base.txt b/requirements/base.txt index e69fa803bae..4e707a83eec 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,4 +1,6 @@ -aiohttp==3.7.4.post0 +aiodns==3.0.0 + # via -r base.in +aiohttp==3.8.3 # via # -r base.in # aiohttp-json-rpc @@ -6,28 +8,24 @@ aiohttp==3.7.4.post0 # red-lavalink aiohttp-json-rpc==0.13.3 # via -r base.in -aiosqlite==0.17.0 - # via -r base.in -appdirs==1.4.4 - # via -r base.in -apsw-wheels==3.36.0.post1 +aiosignal==1.3.1 + # via aiohttp +apsw==3.40.0.0 # via -r base.in -async-timeout==3.0.1 +async-timeout==4.0.2 # via aiohttp -attrs==21.2.0 +attrs==22.2.0 # via aiohttp -babel==2.9.1 +babel==2.11.0 + # via -r base.in +brotli==1.0.9 # via -r base.in -cffi==1.14.6 - # via pynacl -chardet==4.0.0 +cffi==1.15.1 + # via pycares +charset-normalizer==2.1.1 # via aiohttp -click==8.0.1 +click==8.1.3 # via -r base.in -colorama==0.4.4 - # via - # -r base.in - # click commonmark==0.9.1 # via rich contextlib2==21.6.0 @@ -36,33 +34,41 @@ discord-py==2.1.0 # via # -r base.in # red-lavalink -fuzzywuzzy==0.18.0 - # via -r base.in -idna==3.2 +frozenlist==1.3.3 + # via + # aiohttp + # aiosignal +idna==3.4 # via yarl -markdown==3.3.4 +importlib-metadata==5.2.0 + # via markdown +markdown==3.4.1 # via -r base.in -multidict==5.1.0 +multidict==6.0.4 # via # aiohttp # yarl +orjson==3.8.3 + # via -r base.in packaging==22.0 # via -r base.in -psutil==5.8.0 +platformdirs==2.6.0 + # via -r base.in +psutil==5.9.4 # via -r base.in -pycparser==2.20 +pycares==4.3.0 + # via aiodns +pycparser==2.21 # via cffi -pygments==2.10.0 +pygments==2.13.0 # via rich -pynacl==1.4.0 - # via -r base.in python-dateutil==2.8.2 # via -r base.in -python-levenshtein-wheels==0.13.2 - # via -r base.in -pytz==2021.1 +pytz==2022.7 # via babel -pyyaml==5.4.1 +pyyaml==6.0 + # via -r base.in +rapidfuzz==2.13.7 # via -r base.in red-commons==1.0.0 # via @@ -70,19 +76,25 @@ red-commons==1.0.0 # red-lavalink red-lavalink==0.11.0rc1 # via -r base.in -rich==10.9.0 +rich==12.6.0 # via -r base.in -schema==0.7.4 +schema==0.7.5 # via -r base.in six==1.16.0 # via python-dateutil -typing-extensions==3.10.0.2 - # via rich -yarl==1.6.3 +typing-extensions==4.4.0 + # via + # -r base.in + # rich +yarl==1.8.2 # via # -r base.in # aiohttp -distro==1.6.0; sys_platform == "linux" +zipp==3.11.0 + # via importlib-metadata +colorama==0.4.6; sys_platform == "win32" + # via click +distro==1.8.0; sys_platform == "linux" and sys_platform == "linux" # via -r base.in -uvloop==0.16.0; sys_platform != "win32" and platform_python_implementation == "CPython" +uvloop==0.17.0; (sys_platform != "win32" and platform_python_implementation == "CPython") and sys_platform != "win32" # via -r base.in diff --git a/requirements/extra-doc.txt b/requirements/extra-doc.txt index eed9cb2e351..b38c9ab9407 100644 --- a/requirements/extra-doc.txt +++ b/requirements/extra-doc.txt @@ -1,24 +1,22 @@ alabaster==0.7.12 # via sphinx -certifi==2021.5.30 +certifi==2022.12.7 # via requests -charset-normalizer==2.0.4 - # via requests -docutils==0.16 +docutils==0.17.1 # via # sphinx # sphinx-rtd-theme -imagesize==1.2.0 +imagesize==1.4.1 # via sphinx -jinja2==3.0.1 +jinja2==3.1.2 # via sphinx -markupsafe==2.0.1 +markupsafe==2.1.1 # via jinja2 -requests==2.26.0 +requests==2.28.1 # via sphinx -snowballstemmer==2.1.0 +snowballstemmer==2.2.0 # via sphinx -sphinx==4.1.2 +sphinx==5.3.0 # via # -r extra-doc.in # sphinx-prompt @@ -26,7 +24,7 @@ sphinx==4.1.2 # sphinxcontrib-trio sphinx-prompt==1.5.0 # via -r extra-doc.in -sphinx-rtd-theme==0.5.2 +sphinx-rtd-theme==1.1.1 # via -r extra-doc.in sphinxcontrib-applehelp==1.0.2 # via sphinx @@ -42,5 +40,5 @@ sphinxcontrib-serializinghtml==1.1.5 # via sphinx sphinxcontrib-trio==1.1.2 # via -r extra-doc.in -urllib3==1.26.6 +urllib3==1.26.13 # via requests diff --git a/requirements/extra-postgres.txt b/requirements/extra-postgres.txt index 945c075af2b..35809367b12 100644 --- a/requirements/extra-postgres.txt +++ b/requirements/extra-postgres.txt @@ -1,2 +1,2 @@ -asyncpg==0.24.0 +asyncpg==0.27.0 # via -r extra-postgres.in diff --git a/requirements/extra-style.txt b/requirements/extra-style.txt index dde309b4de9..4d70be2e80a 100644 --- a/requirements/extra-style.txt +++ b/requirements/extra-style.txt @@ -1,12 +1,8 @@ -black==22.1.0 +black==22.12.0 # via -r extra-style.in mypy-extensions==0.4.3 # via black -pathspec==0.9.0 +pathspec==0.10.3 # via black -regex==2021.8.28 - # via black -toml==0.10.2 - # via black -typed-ast==1.4.3 +tomli==2.0.1 # via black diff --git a/requirements/extra-test.txt b/requirements/extra-test.txt index ea453cbf318..770190d81a9 100644 --- a/requirements/extra-test.txt +++ b/requirements/extra-test.txt @@ -1,33 +1,35 @@ -astroid==2.7.3 +astroid==2.12.13 # via pylint +dill==0.3.6 + # via pylint +exceptiongroup==1.1.0 + # via pytest iniconfig==1.1.1 # via pytest -isort==5.9.3 +isort==5.11.4 # via pylint -lazy-object-proxy==1.6.0 +lazy-object-proxy==1.8.0 # via astroid -mccabe==0.6.1 - # via pylint -platformdirs==2.3.0 +mccabe==0.7.0 # via pylint pluggy==1.0.0 # via pytest -py==1.10.0 - # via pytest -pylint==2.10.2 +pylint==2.15.9 # via -r extra-test.in -pytest==6.2.5 +pytest==7.2.0 # via # -r extra-test.in # pytest-asyncio # pytest-mock -pytest-asyncio==0.15.1 +pytest-asyncio==0.20.3 # via -r extra-test.in -pytest-mock==3.6.1 +pytest-mock==3.10.0 # via -r extra-test.in -toml==0.10.2 +tomli==2.0.1 # via # pylint # pytest -wrapt==1.12.1 +tomlkit==0.11.6 + # via pylint +wrapt==1.14.1 # via astroid diff --git a/setup.py b/setup.py index 58d1b8f9fe1..3ecbf527d9e 100644 --- a/setup.py +++ b/setup.py @@ -47,8 +47,8 @@ def extras_combined(*extra_names): python_requires = ">=3.8.1" -if not os.getenv("TOX_RED", False) or sys.version_info < (3, 10): - python_requires += ",<3.10" +if not os.getenv("TOX_RED", False) or sys.version_info < (3, 12): + python_requires += ",<3.12" # Metadata and options defined in pyproject.toml setup( diff --git a/tests/cogs/downloader/test_downloader.py b/tests/cogs/downloader/test_downloader.py index 71e3b811cd9..e60d401f4ae 100644 --- a/tests/cogs/downloader/test_downloader.py +++ b/tests/cogs/downloader/test_downloader.py @@ -65,7 +65,6 @@ def test_existing_git_repo(tmp_path): descendant_rev = "fb99eb7d2d5bed514efc98fe6686b368f8425745" -@pytest.mark.asyncio @pytest.mark.parametrize( "maybe_ancestor_rev,descendant_rev,returncode,expected", [(ancestor_rev, descendant_rev, 0, True), (descendant_rev, ancestor_rev, 1, False)], @@ -86,7 +85,6 @@ async def test_is_ancestor(mocker, repo, maybe_ancestor_rev, descendant_rev, ret assert ret is expected -@pytest.mark.asyncio async def test_is_ancestor_object_raise(mocker, repo): m = _mock_run(mocker, repo, 128, b"", b"fatal: Not a valid object name invalid1") with pytest.raises(UnknownRevision): @@ -104,7 +102,6 @@ async def test_is_ancestor_object_raise(mocker, repo): ) -@pytest.mark.asyncio async def test_is_ancestor_commit_raise(mocker, repo): m = _mock_run( mocker, @@ -130,7 +127,6 @@ async def test_is_ancestor_commit_raise(mocker, repo): ) -@pytest.mark.asyncio async def test_get_file_update_statuses(mocker, repo): old_rev = "c950fc05a540dd76b944719c2a3302da2e2f3090" new_rev = "fb99eb7d2d5bed514efc98fe6686b368f8425745" @@ -160,7 +156,6 @@ async def test_get_file_update_statuses(mocker, repo): } -@pytest.mark.asyncio async def test_is_module_modified(mocker, repo): old_rev = "c950fc05a540dd76b944719c2a3302da2e2f3090" new_rev = "fb99eb7d2d5bed514efc98fe6686b368f8425745" @@ -184,7 +179,6 @@ async def test_is_module_modified(mocker, repo): assert ret is True -@pytest.mark.asyncio async def test_get_full_sha1_success(mocker, repo): commit = "c950fc05a540dd76b944719c2a3302da2e2f3090" m = _mock_run(mocker, repo, 0, commit.encode()) @@ -196,7 +190,6 @@ async def test_get_full_sha1_success(mocker, repo): assert ret == commit -@pytest.mark.asyncio async def test_get_full_sha1_notfound(mocker, repo): m = _mock_run(mocker, repo, 128, b"", b"fatal: Needed a single revision") with pytest.raises(UnknownRevision): @@ -206,7 +199,6 @@ async def test_get_full_sha1_notfound(mocker, repo): ) -@pytest.mark.asyncio async def test_get_full_sha1_ambiguous(mocker, repo): m = _mock_run( mocker, @@ -246,7 +238,6 @@ def test_update_available_modules(repo): ) -@pytest.mark.asyncio async def test_checkout(mocker, repo): commit = "c950fc05a540dd76b944719c2a3302da2e2f3090" m = _mock_run(mocker, repo, 0) @@ -261,7 +252,6 @@ async def test_checkout(mocker, repo): ) -@pytest.mark.asyncio async def test_checkout_ctx_manager(mocker, repo): commit = "c950fc05a540dd76b944719c2a3302da2e2f3090" m = mocker.patch.object(repo, "_checkout", autospec=True, return_value=None) @@ -273,7 +263,6 @@ async def test_checkout_ctx_manager(mocker, repo): m.assert_called_with(old_commit, force_checkout=False) -@pytest.mark.asyncio async def test_checkout_await(mocker, repo): commit = "c950fc05a540dd76b944719c2a3302da2e2f3090" m = mocker.patch.object(repo, "_checkout", autospec=True, return_value=None) @@ -282,7 +271,6 @@ async def test_checkout_await(mocker, repo): m.assert_called_once_with(commit, force_checkout=False) -@pytest.mark.asyncio async def test_clone_with_branch(mocker, repo): branch = repo.branch = "dont_add_commits" commit = "a0ccc2390883c85a361f5a90c72e1b07958939fa" @@ -300,7 +288,6 @@ async def test_clone_with_branch(mocker, repo): ) -@pytest.mark.asyncio async def test_clone_without_branch(mocker, repo): branch = "dont_add_commits" commit = "a0ccc2390883c85a361f5a90c72e1b07958939fa" @@ -318,7 +305,6 @@ async def test_clone_without_branch(mocker, repo): ) -@pytest.mark.asyncio async def test_update(mocker, repo): old_commit = repo.commit new_commit = "a0ccc2390883c85a361f5a90c72e1b07958939fa" @@ -335,7 +321,6 @@ async def test_update(mocker, repo): # old tests -@pytest.mark.asyncio async def test_add_repo(monkeypatch, repo_manager): monkeypatch.setattr("redbot.cogs.downloader.repo_manager.Repo._run", fake_run_noprint) monkeypatch.setattr( @@ -349,7 +334,6 @@ async def test_add_repo(monkeypatch, repo_manager): assert squid.available_modules == () -@pytest.mark.asyncio async def test_lib_install_requirements(monkeypatch, library_installable, repo, tmpdir): monkeypatch.setattr("redbot.cogs.downloader.repo_manager.Repo._run", fake_run_noprint) monkeypatch.setattr( @@ -368,7 +352,6 @@ async def test_lib_install_requirements(monkeypatch, library_installable, repo, assert len(failed) == 0 -@pytest.mark.asyncio async def test_remove_repo(monkeypatch, repo_manager): monkeypatch.setattr("redbot.cogs.downloader.repo_manager.Repo._run", fake_run_noprint) monkeypatch.setattr( @@ -383,7 +366,6 @@ async def test_remove_repo(monkeypatch, repo_manager): assert repo_manager.get_repo("squid") is None -@pytest.mark.asyncio async def test_existing_repo(mocker, repo_manager): repo_manager.does_repo_exist = mocker.MagicMock(return_value=True) diff --git a/tests/cogs/downloader/test_git.py b/tests/cogs/downloader/test_git.py index e1cc1d517e8..a2e99734f70 100644 --- a/tests/cogs/downloader/test_git.py +++ b/tests/cogs/downloader/test_git.py @@ -13,7 +13,6 @@ ) -@pytest.mark.asyncio async def test_git_clone_nobranch(git_repo, tmp_path): p = await git_repo._run( ProcessFormatter().format( @@ -25,7 +24,6 @@ async def test_git_clone_nobranch(git_repo, tmp_path): assert p.returncode == 0 -@pytest.mark.asyncio async def test_git_clone_branch(git_repo, tmp_path): p = await git_repo._run( ProcessFormatter().format( @@ -38,7 +36,6 @@ async def test_git_clone_branch(git_repo, tmp_path): assert p.returncode == 0 -@pytest.mark.asyncio async def test_git_clone_non_existent_branch(git_repo, tmp_path): p = await git_repo._run( ProcessFormatter().format( @@ -51,7 +48,6 @@ async def test_git_clone_non_existent_branch(git_repo, tmp_path): assert p.returncode == 128 -@pytest.mark.asyncio async def test_git_clone_notgit_repo(git_repo, tmp_path): notgit_repo = tmp_path / "test_clone_folder" p = await git_repo._run( @@ -62,7 +58,6 @@ async def test_git_clone_notgit_repo(git_repo, tmp_path): assert p.returncode == 128 -@pytest.mark.asyncio async def test_git_current_branch_master(git_repo): p = await git_repo._run( ProcessFormatter().format(git_repo.GIT_CURRENT_BRANCH, path=git_repo.folder_path) @@ -71,7 +66,6 @@ async def test_git_current_branch_master(git_repo): assert p.stdout.decode().strip() == "master" -@pytest.mark.asyncio async def test_git_current_branch_detached(git_repo): await git_repo._run( ProcessFormatter().format( @@ -87,7 +81,6 @@ async def test_git_current_branch_detached(git_repo): assert p.stderr.decode().strip() == "fatal: ref HEAD is not a symbolic ref" -@pytest.mark.asyncio async def test_git_current_commit_on_branch(git_repo): # HEAD on dont_add_commits (a0ccc2390883c85a361f5a90c72e1b07958939fa) # setup @@ -105,7 +98,6 @@ async def test_git_current_commit_on_branch(git_repo): assert p.stdout.decode().strip() == "a0ccc2390883c85a361f5a90c72e1b07958939fa" -@pytest.mark.asyncio async def test_git_current_commit_detached(git_repo): # detached HEAD state (c950fc05a540dd76b944719c2a3302da2e2f3090) await git_repo._run( @@ -122,7 +114,6 @@ async def test_git_current_commit_detached(git_repo): assert p.stdout.decode().strip() == "c950fc05a540dd76b944719c2a3302da2e2f3090" -@pytest.mark.asyncio async def test_git_latest_commit(git_repo): # HEAD on dont_add_commits (a0ccc2390883c85a361f5a90c72e1b07958939fa) p = await git_repo._run( @@ -134,7 +125,6 @@ async def test_git_latest_commit(git_repo): assert p.stdout.decode().strip() == "a0ccc2390883c85a361f5a90c72e1b07958939fa" -@pytest.mark.asyncio async def test_git_hard_reset(cloned_git_repo, tmp_path): staged_file = cloned_git_repo.folder_path / "staged_file.txt" staged_file.touch() @@ -150,7 +140,6 @@ async def test_git_hard_reset(cloned_git_repo, tmp_path): assert staged_file.exists() is False -@pytest.mark.asyncio async def test_git_pull(git_repo_with_remote, tmp_path): # setup staged_file = Path(git_repo_with_remote.url) / "staged_file.txt" @@ -171,7 +160,6 @@ async def test_git_pull(git_repo_with_remote, tmp_path): assert (git_repo_with_remote.folder_path / "staged_file.txt").exists() -@pytest.mark.asyncio async def test_git_diff_file_status(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -195,7 +183,6 @@ async def test_git_diff_file_status(git_repo): # might need to add test for test_git_log, but it's unused method currently -@pytest.mark.asyncio async def test_git_discover_remote_url(cloned_git_repo, tmp_path): p = await cloned_git_repo._run( ProcessFormatter().format( @@ -206,7 +193,6 @@ async def test_git_discover_remote_url(cloned_git_repo, tmp_path): assert p.stdout.decode().strip() == cloned_git_repo.url -@pytest.mark.asyncio async def test_git_checkout_detached_head(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -218,7 +204,6 @@ async def test_git_checkout_detached_head(git_repo): assert p.returncode == 0 -@pytest.mark.asyncio async def test_git_checkout_branch(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -228,7 +213,6 @@ async def test_git_checkout_branch(git_repo): assert p.returncode == 0 -@pytest.mark.asyncio async def test_git_checkout_non_existent_branch(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -238,7 +222,6 @@ async def test_git_checkout_non_existent_branch(git_repo): assert p.returncode == 1 -@pytest.mark.asyncio async def test_git_get_full_sha1_from_branch_name(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -249,7 +232,6 @@ async def test_git_get_full_sha1_from_branch_name(git_repo): assert p.stdout.decode().strip() == "a0ccc2390883c85a361f5a90c72e1b07958939fa" -@pytest.mark.asyncio async def test_git_get_full_sha1_from_full_hash(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -262,7 +244,6 @@ async def test_git_get_full_sha1_from_full_hash(git_repo): assert p.stdout.decode().strip() == "c950fc05a540dd76b944719c2a3302da2e2f3090" -@pytest.mark.asyncio async def test_git_get_full_sha1_from_short_hash(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -273,7 +254,6 @@ async def test_git_get_full_sha1_from_short_hash(git_repo): assert p.stdout.decode().strip() == "c950fc05a540dd76b944719c2a3302da2e2f3090" -@pytest.mark.asyncio async def test_git_get_full_sha1_from_too_short_hash(git_repo): p = await git_repo._run( ProcessFormatter().format(git_repo.GIT_GET_FULL_SHA1, path=git_repo.folder_path, rev="c95") @@ -282,7 +262,6 @@ async def test_git_get_full_sha1_from_too_short_hash(git_repo): assert p.stderr.decode().strip() == "fatal: Needed a single revision" -@pytest.mark.asyncio async def test_git_get_full_sha1_from_lightweight_tag(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -293,7 +272,6 @@ async def test_git_get_full_sha1_from_lightweight_tag(git_repo): assert p.stdout.decode().strip() == "fb99eb7d2d5bed514efc98fe6686b368f8425745" -@pytest.mark.asyncio async def test_git_get_full_sha1_from_annotated_tag(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -304,7 +282,6 @@ async def test_git_get_full_sha1_from_annotated_tag(git_repo): assert p.stdout.decode().strip() == "a7120330cc179396914e0d6af80cfa282adc124b" -@pytest.mark.asyncio async def test_git_get_full_sha1_from_invalid_ref(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -318,7 +295,6 @@ async def test_git_get_full_sha1_from_invalid_ref(git_repo): @pytest.mark.skipif( GIT_VERSION < (2, 31), reason="This is test for output from Git 2.31 and newer." ) -@pytest.mark.asyncio async def test_git_get_full_sha1_from_ambiguous_commits(git_repo): # 2 ambiguous refs: # branch ambiguous_1 - 95da0b576271cb5bee5f3e075074c03ee05fed05 @@ -341,7 +317,6 @@ async def test_git_get_full_sha1_from_ambiguous_commits(git_repo): @pytest.mark.skipif( GIT_VERSION < (2, 36), reason="This is test for output from Git 2.36 and newer." ) -@pytest.mark.asyncio async def test_git_get_full_sha1_from_ambiguous_tag_and_commit(git_repo): # 2 ambiguous refs: # branch ambiguous_with_tag - c6f0e5ec04d99bdf8c6c78ff20d66d286eecb3ea @@ -364,7 +339,6 @@ async def test_git_get_full_sha1_from_ambiguous_tag_and_commit(git_repo): @pytest.mark.skipif( not ((2, 31) <= GIT_VERSION < (2, 36)), reason="This is test for output from Git >=2.31,<2.36." ) -@pytest.mark.asyncio async def test_git_get_full_sha1_from_ambiguous_tag_and_commit_pre_2_36(git_repo): # 2 ambiguous refs: # branch ambiguous_with_tag - c6f0e5ec04d99bdf8c6c78ff20d66d286eecb3ea @@ -387,7 +361,6 @@ async def test_git_get_full_sha1_from_ambiguous_tag_and_commit_pre_2_36(git_repo @pytest.mark.skipif( GIT_VERSION >= (2, 31), reason="This is test for output from Git older than 2.31." ) -@pytest.mark.asyncio async def test_git_get_full_sha1_from_ambiguous_commits_pre_2_31(git_repo): # 2 ambiguous refs: # branch ambiguous_1 - 95da0b576271cb5bee5f3e075074c03ee05fed05 @@ -410,7 +383,6 @@ async def test_git_get_full_sha1_from_ambiguous_commits_pre_2_31(git_repo): @pytest.mark.skipif( GIT_VERSION >= (2, 31), reason="This is test for output from Git older than 2.31." ) -@pytest.mark.asyncio async def test_git_get_full_sha1_from_ambiguous_tag_and_commit_pre_2_31(git_repo): # 2 ambiguous refs: # branch ambiguous_with_tag - c6f0e5ec04d99bdf8c6c78ff20d66d286eecb3ea @@ -430,7 +402,6 @@ async def test_git_get_full_sha1_from_ambiguous_tag_and_commit_pre_2_31(git_repo ) -@pytest.mark.asyncio async def test_git_is_ancestor_true(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -443,7 +414,6 @@ async def test_git_is_ancestor_true(git_repo): assert p.returncode == 0 -@pytest.mark.asyncio async def test_git_is_ancestor_false(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -456,7 +426,6 @@ async def test_git_is_ancestor_false(git_repo): assert p.returncode == 1 -@pytest.mark.asyncio async def test_git_is_ancestor_invalid_object(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -470,7 +439,6 @@ async def test_git_is_ancestor_invalid_object(git_repo): assert p.stderr.decode().strip() == "fatal: Not a valid object name invalid1" -@pytest.mark.asyncio async def test_git_is_ancestor_invalid_commit(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -486,7 +454,6 @@ async def test_git_is_ancestor_invalid_commit(git_repo): ) -@pytest.mark.asyncio async def test_git_check_if_module_exists_true(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -502,7 +469,6 @@ async def test_git_check_if_module_exists_true(git_repo): @pytest.mark.skipif( GIT_VERSION < (2, 36), reason="This is test for output from Git 2.36 and newer." ) -@pytest.mark.asyncio async def test_git_check_if_module_exists_false(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -521,7 +487,6 @@ async def test_git_check_if_module_exists_false(git_repo): @pytest.mark.skipif( GIT_VERSION >= (2, 36), reason="This is test for output from Git older than 2.31." ) -@pytest.mark.asyncio async def test_git_check_if_module_exists_false_pre_2_36(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -537,7 +502,6 @@ async def test_git_check_if_module_exists_false_pre_2_36(git_repo): ) -@pytest.mark.asyncio async def test_git_find_last_occurrence_existent(git_repo): p = await git_repo._run( ProcessFormatter().format( @@ -552,7 +516,6 @@ async def test_git_find_last_occurrence_existent(git_repo): assert p.stdout.decode().strip() == "a7120330cc179396914e0d6af80cfa282adc124b" -@pytest.mark.asyncio async def test_git_find_last_occurrence_non_existent(git_repo): p = await git_repo._run( ProcessFormatter().format( diff --git a/tests/cogs/test_alias.py b/tests/cogs/test_alias.py index 27fdc98333d..261c85dcb38 100644 --- a/tests/cogs/test_alias.py +++ b/tests/cogs/test_alias.py @@ -7,12 +7,10 @@ def test_is_valid_alias_name(alias): assert alias.is_valid_alias_name("not valid name") is False -@pytest.mark.asyncio async def test_empty_guild_aliases(alias, empty_guild): assert list(await alias._aliases.get_guild_aliases(empty_guild)) == [] -@pytest.mark.asyncio async def test_empty_global_aliases(alias): assert list(await alias._aliases.get_global_aliases()) == [] @@ -25,7 +23,6 @@ async def create_test_global_alias(alias, ctx): await alias._aliases.add_alias(ctx, "test_global", "ping", global_=True) -@pytest.mark.asyncio async def test_add_guild_alias(alias, ctx): await create_test_guild_alias(alias, ctx) @@ -33,7 +30,6 @@ async def test_add_guild_alias(alias, ctx): assert alias_obj.name == "test" -@pytest.mark.asyncio async def test_delete_guild_alias(alias, ctx): await create_test_guild_alias(alias, ctx) alias_obj = await alias._aliases.get_alias(ctx.guild, "test") @@ -46,7 +42,6 @@ async def test_delete_guild_alias(alias, ctx): assert alias_obj is None -@pytest.mark.asyncio async def test_add_global_alias(alias, ctx): await create_test_global_alias(alias, ctx) alias_obj = await alias._aliases.get_alias(ctx.guild, "test_global") @@ -54,7 +49,6 @@ async def test_add_global_alias(alias, ctx): assert alias_obj.name == "test_global" -@pytest.mark.asyncio async def test_delete_global_alias(alias, ctx): await create_test_global_alias(alias, ctx) alias_obj = await alias._aliases.get_alias(ctx.guild, "test_global") diff --git a/tests/cogs/test_economy.py b/tests/cogs/test_economy.py index ec7190339f3..e53bec9d07f 100644 --- a/tests/cogs/test_economy.py +++ b/tests/cogs/test_economy.py @@ -2,7 +2,6 @@ from redbot.pytest.economy import * -@pytest.mark.asyncio async def test_bank_register(bank, ctx): default_bal = await bank.get_default_balance(ctx.guild) assert default_bal == (await bank.get_account(ctx.author)).balance @@ -15,7 +14,6 @@ async def has_account(member, bank): await bank.set_balance(member, balance) -@pytest.mark.asyncio async def test_bank_transfer(bank, member_factory): mbr1 = member_factory.get() mbr2 = member_factory.get() @@ -28,7 +26,6 @@ async def test_bank_transfer(bank, member_factory): assert bal2 + 50 == newbal2 -@pytest.mark.asyncio async def test_bank_set(bank, member_factory): mbr = member_factory.get() await bank.set_balance(mbr, 250) @@ -36,7 +33,6 @@ async def test_bank_set(bank, member_factory): assert acc.balance == 250 -@pytest.mark.asyncio async def test_bank_can_spend(bank, member_factory): mbr = member_factory.get() canspend = await bank.can_spend(mbr, 50) @@ -47,7 +43,6 @@ async def test_bank_can_spend(bank, member_factory): assert canspendnow -@pytest.mark.asyncio async def test_set_bank_name(bank, guild_factory): guild = guild_factory.get() await bank.set_bank_name("Test Bank", guild) @@ -55,7 +50,6 @@ async def test_set_bank_name(bank, guild_factory): assert name == "Test Bank" -@pytest.mark.asyncio async def test_set_currency_name(bank, guild_factory): guild = guild_factory.get() await bank.set_currency_name("Coins", guild) @@ -63,7 +57,6 @@ async def test_set_currency_name(bank, guild_factory): assert name == "Coins" -@pytest.mark.asyncio async def test_set_default_balance(bank, guild_factory): guild = guild_factory.get() await bank.set_default_balance(500, guild) @@ -71,7 +64,6 @@ async def test_set_default_balance(bank, guild_factory): assert default_bal == 500 -@pytest.mark.asyncio async def test_nonint_transaction_amount(bank, member_factory): mbr1 = member_factory.get() mbr2 = member_factory.get() diff --git a/tests/cogs/test_mod.py b/tests/cogs/test_mod.py index 0d2908a4fe8..01c2a8d1597 100644 --- a/tests/cogs/test_mod.py +++ b/tests/cogs/test_mod.py @@ -3,14 +3,12 @@ from redbot.pytest.mod import * -@pytest.mark.asyncio async def test_modlog_register_casetype(mod): ct = {"name": "ban", "default_setting": True, "image": ":hammer:", "case_str": "Ban"} casetype = await mod.register_casetype(**ct) assert casetype is not None -@pytest.mark.asyncio async def test_modlog_case_create(mod, ctx, member_factory): from datetime import datetime, timezone @@ -33,7 +31,6 @@ async def test_modlog_case_create(mod, ctx, member_factory): assert case.created_at == int(created_at.timestamp()) -@pytest.mark.asyncio async def test_modlog_set_modlog_channel(mod, ctx): await mod.set_modlog_channel(ctx.guild, ctx.channel) assert await mod.get_modlog_channel(ctx.guild) == ctx.channel.id diff --git a/tests/core/test_cog_manager.py b/tests/core/test_cog_manager.py index cc34d6f01bf..ac85e3290be 100644 --- a/tests/core/test_cog_manager.py +++ b/tests/core/test_cog_manager.py @@ -7,20 +7,17 @@ @pytest.mark.skip -@pytest.mark.asyncio async def test_ensure_cogs_in_paths(cog_mgr, default_dir): cogs_dir = default_dir / "redbot" / "cogs" assert cogs_dir in await cog_mgr.paths() -@pytest.mark.asyncio async def test_install_path_set(cog_mgr: cog_manager.CogManager, tmpdir): path = Path(str(tmpdir)) await cog_mgr.set_install_path(path) assert await cog_mgr.install_path() == path -@pytest.mark.asyncio async def test_install_path_set_bad(cog_mgr): path = Path("something") @@ -28,14 +25,12 @@ async def test_install_path_set_bad(cog_mgr): await cog_mgr.set_install_path(path) -@pytest.mark.asyncio async def test_add_path(cog_mgr, tmpdir): path = Path(str(tmpdir)) await cog_mgr.add_path(path) assert path in await cog_mgr.paths() -@pytest.mark.asyncio async def test_add_path_already_install_path(cog_mgr, tmpdir): path = Path(str(tmpdir)) await cog_mgr.set_install_path(path) @@ -43,7 +38,6 @@ async def test_add_path_already_install_path(cog_mgr, tmpdir): await cog_mgr.add_path(path) -@pytest.mark.asyncio async def test_remove_path(cog_mgr, tmpdir): path = Path(str(tmpdir)) await cog_mgr.add_path(path) diff --git a/tests/core/test_config.py b/tests/core/test_config.py index bef10426de9..ac34ae6885b 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -5,7 +5,6 @@ # region Register Tests -@pytest.mark.asyncio async def test_config_register_global(config): config.register_global(enabled=False) assert config.defaults["GLOBAL"]["enabled"] is False @@ -17,7 +16,6 @@ def test_config_register_global_badvalues(config): config.register_global(**{"invalid var name": True}) -@pytest.mark.asyncio async def test_config_register_guild(config, empty_guild): config.register_guild(enabled=False, some_list=[], some_dict={}) assert config.defaults[config.GUILD]["enabled"] is False @@ -29,35 +27,30 @@ async def test_config_register_guild(config, empty_guild): assert await config.guild(empty_guild).some_dict() == {} -@pytest.mark.asyncio async def test_config_register_channel(config, empty_channel): config.register_channel(enabled=False) assert config.defaults[config.CHANNEL]["enabled"] is False assert await config.channel(empty_channel).enabled() is False -@pytest.mark.asyncio async def test_config_register_role(config, empty_role): config.register_role(enabled=False) assert config.defaults[config.ROLE]["enabled"] is False assert await config.role(empty_role).enabled() is False -@pytest.mark.asyncio async def test_config_register_member(config, empty_member): config.register_member(some_number=-1) assert config.defaults[config.MEMBER]["some_number"] == -1 assert await config.member(empty_member).some_number() == -1 -@pytest.mark.asyncio async def test_config_register_user(config, empty_user): config.register_user(some_value=None) assert config.defaults[config.USER]["some_value"] is None assert await config.user(empty_user).some_value() is None -@pytest.mark.asyncio async def test_config_force_register_global(config_fr): with pytest.raises(AttributeError): await config_fr.enabled() @@ -70,13 +63,11 @@ async def test_config_force_register_global(config_fr): # Test nested registration -@pytest.mark.asyncio async def test_nested_registration(config): config.register_global(foo__bar__baz=False) assert await config.foo.bar.baz() is False -@pytest.mark.asyncio async def test_nested_registration_asdict(config): defaults = {"bar": {"baz": False}} config.register_global(foo=defaults) @@ -84,7 +75,6 @@ async def test_nested_registration_asdict(config): assert await config.foo.bar.baz() is False -@pytest.mark.asyncio async def test_nested_registration_and_changing(config): defaults = {"bar": {"baz": False}} config.register_global(foo=defaults) @@ -95,7 +85,6 @@ async def test_nested_registration_and_changing(config): await config.foo.set(True) -@pytest.mark.asyncio async def test_doubleset_default(config): config.register_global(foo=True) config.register_global(foo=False) @@ -103,7 +92,6 @@ async def test_doubleset_default(config): assert await config.foo() is False -@pytest.mark.asyncio async def test_nested_registration_multidict(config): defaults = {"foo": {"bar": {"baz": True}}, "blah": True} config.register_global(**defaults) @@ -118,7 +106,6 @@ def test_nested_group_value_badreg(config): config.register_global(foo__bar=False) -@pytest.mark.asyncio async def test_nested_toplevel_reg(config): defaults = {"bar": True, "baz": False} config.register_global(foo=defaults) @@ -127,7 +114,6 @@ async def test_nested_toplevel_reg(config): assert await config.foo.baz() is False -@pytest.mark.asyncio async def test_nested_overlapping(config): config.register_global(foo__bar=True) config.register_global(foo__baz=False) @@ -136,7 +122,6 @@ async def test_nested_overlapping(config): assert await config.foo.baz() is False -@pytest.mark.asyncio async def test_nesting_nofr(config): config.register_global(foo={}) assert await config.foo.bar() is None @@ -144,38 +129,31 @@ async def test_nesting_nofr(config): # region Default Value Overrides -@pytest.mark.asyncio async def test_global_default_override(config): assert await config.enabled(True) is True -@pytest.mark.asyncio async def test_global_default_nofr(config): assert await config.nofr() is None assert await config.nofr(True) is True -@pytest.mark.asyncio async def test_guild_default_override(config, empty_guild): assert await config.guild(empty_guild).enabled(True) is True -@pytest.mark.asyncio async def test_channel_default_override(config, empty_channel): assert await config.channel(empty_channel).enabled(True) is True -@pytest.mark.asyncio async def test_role_default_override(config, empty_role): assert await config.role(empty_role).enabled(True) is True -@pytest.mark.asyncio async def test_member_default_override(config, empty_member): assert await config.member(empty_member).enabled(True) is True -@pytest.mark.asyncio async def test_user_default_override(config, empty_user): assert await config.user(empty_user).some_value(True) is True @@ -184,13 +162,11 @@ async def test_user_default_override(config, empty_user): # region Setting Values -@pytest.mark.asyncio async def test_set_global(config): await config.enabled.set(True) assert await config.enabled() is True -@pytest.mark.asyncio async def test_set_guild(config, empty_guild): await config.guild(empty_guild).enabled.set(True) assert await config.guild(empty_guild).enabled() is True @@ -203,13 +179,11 @@ async def test_set_guild(config, empty_guild): assert await config.guild(empty_guild).some_list() == curr_list -@pytest.mark.asyncio async def test_set_channel(config, empty_channel): await config.channel(empty_channel).enabled.set(True) assert await config.channel(empty_channel).enabled() is True -@pytest.mark.asyncio async def test_set_channel_no_register(config, empty_channel): await config.channel(empty_channel).no_register.set(True) assert await config.channel(empty_channel).no_register() is True @@ -219,14 +193,12 @@ async def test_set_channel_no_register(config, empty_channel): # Dynamic attribute testing -@pytest.mark.asyncio async def test_set_dynamic_attr(config): await config.set_raw("foobar", value=True) assert await config.foobar() is True -@pytest.mark.asyncio async def test_clear_dynamic_attr(config): await config.foo.set(True) await config.clear_raw("foo") @@ -235,13 +207,11 @@ async def test_clear_dynamic_attr(config): await config.get_raw("foo") -@pytest.mark.asyncio async def test_get_dynamic_attr(config): assert await config.get_raw("foobaz", default=True) is True # Member Group testing -@pytest.mark.asyncio async def test_membergroup_allguilds(config, empty_member): await config.member(empty_member).foo.set(False) @@ -249,7 +219,6 @@ async def test_membergroup_allguilds(config, empty_member): assert empty_member.guild.id in all_servers -@pytest.mark.asyncio async def test_membergroup_allmembers(config, empty_member): await config.member(empty_member).foo.set(False) @@ -258,7 +227,6 @@ async def test_membergroup_allmembers(config, empty_member): # Clearing testing -@pytest.mark.asyncio async def test_global_clear(config): config.register_global(foo=True, bar=False) @@ -274,7 +242,6 @@ async def test_global_clear(config): assert await config.bar() is False -@pytest.mark.asyncio async def test_member_clear(config, member_factory): config.register_member(foo=True) @@ -293,7 +260,6 @@ async def test_member_clear(config, member_factory): assert await config.member(m2).foo() is False -@pytest.mark.asyncio async def test_member_clear_all(config, member_factory): server_ids = [] for _ in range(5): @@ -309,7 +275,6 @@ async def test_member_clear_all(config, member_factory): assert len(await config.all_members()) == 0 -@pytest.mark.asyncio async def test_clear_all(config): await config.foo.set(True) assert await config.foo() is True @@ -319,7 +284,6 @@ async def test_clear_all(config): await config.get_raw("foo") -@pytest.mark.asyncio async def test_clear_value(config): await config.foo.set(True) await config.foo.clear() @@ -329,7 +293,6 @@ async def test_clear_value(config): # Get All testing -@pytest.mark.asyncio async def test_user_get_all_from_kind(config, user_factory): config.register_user(foo=False, bar=True) for _ in range(5): @@ -345,7 +308,6 @@ async def test_user_get_all_from_kind(config, user_factory): assert v["bar"] is True -@pytest.mark.asyncio async def test_user_getalldata(config, user_factory): user = user_factory.get() config.register_user(foo=True, bar=False) @@ -359,7 +321,6 @@ async def test_user_getalldata(config, user_factory): assert config.user(user).defaults["foo"] is True -@pytest.mark.asyncio async def test_value_ctxmgr(config): config.register_global(foo_list=[]) @@ -371,7 +332,6 @@ async def test_value_ctxmgr(config): assert "foo" in foo_list -@pytest.mark.asyncio async def test_value_ctxmgr_saves(config): config.register_global(bar_list=[]) @@ -387,7 +347,6 @@ async def test_value_ctxmgr_saves(config): assert "bar" in bar_list -@pytest.mark.asyncio async def test_value_ctxmgr_immutable(config): config.register_global(foo=True) @@ -399,7 +358,6 @@ async def test_value_ctxmgr_immutable(config): assert foo is True -@pytest.mark.asyncio async def test_ctxmgr_no_shared_default(config, member_factory): config.register_member(foo=[]) m1 = member_factory.get() @@ -411,7 +369,6 @@ async def test_ctxmgr_no_shared_default(config, member_factory): assert 1 not in await config.member(m2).foo() -@pytest.mark.asyncio async def test_ctxmgr_no_unnecessary_write(config): config.register_global(foo=[]) foo_value_obj = config.foo @@ -421,7 +378,6 @@ async def test_ctxmgr_no_unnecessary_write(config): set_method.assert_not_called() -@pytest.mark.asyncio async def test_get_then_mutate(config): """Tests that mutating an object after getting it as a value doesn't mutate the data store.""" config.register_global(list1=[]) @@ -432,7 +388,6 @@ async def test_get_then_mutate(config): assert "foo" not in list1 -@pytest.mark.asyncio async def test_set_then_mutate(config): """Tests that mutating an object after setting it as a value doesn't mutate the data store.""" config.register_global(list1=[]) @@ -443,14 +398,12 @@ async def test_set_then_mutate(config): assert "foo" not in list1 -@pytest.mark.asyncio async def test_call_group_fills_defaults(config): config.register_global(subgroup={"foo": True}) subgroup = await config.subgroup() assert "foo" in subgroup -@pytest.mark.asyncio async def test_group_call_ctxmgr_writes(config): config.register_global(subgroup={"foo": True}) async with config.subgroup() as subgroup: @@ -460,7 +413,6 @@ async def test_group_call_ctxmgr_writes(config): assert subgroup == {"foo": True, "bar": False} -@pytest.mark.asyncio async def test_all_works_as_ctxmgr(config): config.register_global(subgroup={"foo": True}) async with config.subgroup.all() as subgroup: @@ -470,7 +422,6 @@ async def test_all_works_as_ctxmgr(config): assert subgroup == {"foo": True, "bar": False} -@pytest.mark.asyncio async def test_get_raw_mixes_defaults(config): config.register_global(subgroup={"foo": True}) await config.subgroup.set_raw("bar", value=False) @@ -479,7 +430,6 @@ async def test_get_raw_mixes_defaults(config): assert subgroup == {"foo": True, "bar": False} -@pytest.mark.asyncio async def test_cast_str_raw(config): await config.set_raw(123, 456, value=True) assert await config.get_raw(123, 456) is True @@ -487,7 +437,6 @@ async def test_cast_str_raw(config): await config.clear_raw("123", 456) -@pytest.mark.asyncio async def test_cast_str_nested(config): config.register_global(foo={}) await config.foo.set({123: True, 456: {789: False}}) @@ -510,7 +459,6 @@ def test_config_custom_doubleinit(config): config.init_custom("TEST", 2) -@pytest.mark.asyncio async def test_config_locks_cache(config, empty_guild): lock1 = config.foo.get_lock() assert lock1 is config.foo.get_lock() @@ -519,7 +467,6 @@ async def test_config_locks_cache(config, empty_guild): assert lock1 is not lock2 -@pytest.mark.asyncio async def test_config_value_atomicity(config): config.register_global(foo=[]) tasks = [] @@ -539,7 +486,6 @@ async def func(): assert len(await config.foo()) == 15 -@pytest.mark.asyncio async def test_config_ctxmgr_atomicity(config): config.register_global(foo=[]) tasks = [] @@ -557,7 +503,6 @@ async def func(): assert len(await config.foo()) == 15 -@pytest.mark.asyncio async def test_set_with_partial_primary_keys(config): config.init_custom("CUSTOM", 3) await config.custom("CUSTOM", "1").set({"11": {"111": {"foo": "bar"}}}) @@ -585,7 +530,6 @@ async def test_set_with_partial_primary_keys(config): assert await config.custom("CUSTOM", "2", "33", "222").foo() == "biz" -@pytest.mark.asyncio async def test_raw_with_partial_primary_keys(config): config.init_custom("CUSTOM", 1) await config.custom("CUSTOM").set_raw("primary_key", "identifier", value=True) @@ -654,7 +598,6 @@ def generate_test_args(print_args=True): @pytest.mark.parametrize("pkeys, raw_args, result", PARAMS) -@pytest.mark.asyncio async def test_config_custom_partial_pkeys_get(config, pkeys, raw_args, result): # setup config.init_custom("TEST", 3) @@ -666,7 +609,6 @@ async def test_config_custom_partial_pkeys_get(config, pkeys, raw_args, result): @pytest.mark.parametrize("pkeys, raw_args, result", PARAMS) -@pytest.mark.asyncio async def test_config_custom_partial_pkeys_set(config, pkeys, raw_args, result): # setup config.init_custom("TEST", 3) diff --git a/tests/core/test_installation.py b/tests/core/test_installation.py index dae662be42d..3023c86972e 100644 --- a/tests/core/test_installation.py +++ b/tests/core/test_installation.py @@ -1,6 +1,5 @@ import pytest -@pytest.mark.asyncio async def test_can_init_bot(red): assert red is not None diff --git a/tests/core/test_utils.py b/tests/core/test_utils.py index 69d22efd4d3..1981cfcfcfb 100644 --- a/tests/core/test_utils.py +++ b/tests/core/test_utils.py @@ -15,7 +15,6 @@ def test_deduplicate_iterables(): assert deduplicate_iterables(*inputs) == expected -@pytest.mark.asyncio async def test_bounded_gather(): status = [0, 0] # num_running, max_running @@ -52,7 +51,6 @@ async def wait_task(i, delay, status, fail=False): assert num_fail == num_failed -@pytest.mark.asyncio async def test_bounded_gather_iter(): status = [0, 0] # num_running, max_running @@ -91,7 +89,6 @@ async def wait_task(i, delay, status, fail=False): @pytest.mark.skip(reason="spams logs with pending task warnings") -@pytest.mark.asyncio async def test_bounded_gather_iter_cancel(): status = [0, 0, 0] # num_running, max_running, num_ran diff --git a/tests/core/test_version.py b/tests/core/test_version.py index a978e270aa8..954ce894740 100644 --- a/tests/core/test_version.py +++ b/tests/core/test_version.py @@ -73,7 +73,7 @@ def test_python_version_has_lower_bound(): @pytest.mark.skipif( - os.getenv("TOX_RED", False) and sys.version_info >= (3, 10), + os.getenv("TOX_RED", False) and sys.version_info >= (3, 12), reason="Testing on yet to be supported Python version.", ) def test_python_version_has_upper_bound(): diff --git a/tools/dev-requirements.txt b/tools/dev-requirements.txt index d99485829a4..7e6fd0def05 100644 --- a/tools/dev-requirements.txt +++ b/tools/dev-requirements.txt @@ -1,2 +1,2 @@ -tox<4 +tox -e .[dev] diff --git a/tox.ini b/tox.ini index 14f1479c476..de84c457add 100644 --- a/tox.ini +++ b/tox.ini @@ -7,14 +7,15 @@ envlist = py38 py39 + py310 + py311 docs style skip_missing_interpreters = True -isolated_build = True [testenv] description = Run tests and basic automatic issue checking. -whitelist_externals = +allowlist_externals = pytest pylint extras = test @@ -27,7 +28,7 @@ commands = [testenv:postgres] description = Run pytest with PostgreSQL backend -whitelist_externals = +allowlist_externals = pytest extras = test, postgres setenv = @@ -46,7 +47,7 @@ commands = [testenv:docs] description = Attempt to build docs with sphinx-build -whitelist_externals = +allowlist_externals = sphinx-build make setenv = @@ -61,7 +62,7 @@ commands = [testenv:style] description = Stylecheck the code with black to see if anything needs changes. -whitelist_externals = +allowlist_externals = make setenv = # This is just for Windows From fa6b2f8c10cbd9545373c2afd60302641605971e Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Fri, 30 Dec 2022 03:24:05 +0100 Subject: [PATCH 21/43] Fix incorrect default argument value in `[p]streamalert twitch` (#5945) --- redbot/cogs/streams/streams.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/redbot/cogs/streams/streams.py b/redbot/cogs/streams/streams.py index 75951595364..0ff5840f814 100644 --- a/redbot/cogs/streams/streams.py +++ b/redbot/cogs/streams/streams.py @@ -315,7 +315,9 @@ async def _twitch( self, ctx: commands.Context, channel_name: str, - discord_channel: Union[discord.TextChannel, discord.VoiceChannel] = None, + discord_channel: Union[ + discord.TextChannel, discord.VoiceChannel + ] = commands.CurrentChannel, ): """Manage Twitch stream notifications.""" await ctx.invoke(self.twitch_alert_channel, channel_name, discord_channel) From 88a348210cbf70fa81f5d2c21310006211c192ea Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Fri, 30 Dec 2022 03:52:49 +0100 Subject: [PATCH 22/43] Add support for sdists and git-archive to `_get_version()` (#5814) Co-authored-by: Jakub Kuczys <6032823+jack1142@users.noreply.github.com> --- .git_archive_info.txt | 2 + .gitattributes | 3 ++ redbot/__init__.py | 106 +++++++++++++++++++++++++++++------------- 3 files changed, 79 insertions(+), 32 deletions(-) create mode 100644 .git_archive_info.txt diff --git a/.git_archive_info.txt b/.git_archive_info.txt new file mode 100644 index 00000000000..e371a768181 --- /dev/null +++ b/.git_archive_info.txt @@ -0,0 +1,2 @@ +$Format:%h$ +$Format:%(describe:tags=true)$ diff --git a/.gitattributes b/.gitattributes index b51a0c77d31..5857211e3ca 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,3 +2,6 @@ # binary file excludsions *.png binary + +# include commit/tag information in `.git_archive_info.txt` when packing with git-archive +.git_archive_info.txt export-subst diff --git a/redbot/__init__.py b/redbot/__init__.py index 967459d06e5..3f243520a0c 100644 --- a/redbot/__init__.py +++ b/redbot/__init__.py @@ -1,3 +1,4 @@ +import os as _os import re as _re import sys as _sys import warnings as _warnings @@ -209,45 +210,86 @@ def __repr__(self) -> str: def _get_version(cls, *, ignore_installed: bool = False) -> _Tuple[str, "VersionInfo"]: if not _VERSION.endswith(".dev1"): return _VERSION, cls.from_str(_VERSION) - try: - import os - path = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) - # we only want to do this for editable installs - if not os.path.exists(os.path.join(path, ".git")): - raise RuntimeError("not a git repository") + project_root = _os.path.abspath(_os.path.dirname(_os.path.dirname(__file__))) + + methods = [ + cls._get_version_from_git_repo, + ] + # `ignore_installed` is `True` when building with setuptools. + if ignore_installed: + methods.append(cls._get_version_from_sdist_pkg_info) + methods.append(cls._get_version_from_git_archive) + else: + methods.append(cls._get_version_from_package_metadata) + exceptions = [] + for method in methods: + try: + version = method(project_root) + except Exception as exc: + exceptions.append(exc) + else: + break + else: + import traceback + + for exc in exceptions: + traceback.print_exception(None, exc, exc.__traceback__) + exc.__traceback__ = None - import subprocess + version = _VERSION - output = subprocess.check_output( - ("git", "describe", "--tags", "--long", "--dirty"), - stderr=subprocess.DEVNULL, - cwd=path, - ) - _, count, commit, *dirty = output.decode("utf-8").strip().split("-", 3) - dirty_suffix = f".{dirty[0]}" if dirty else "" - ver = f"{_VERSION[:-1]}{count}+{commit}{dirty_suffix}" - return ver, cls.from_str(ver) - except Exception: - # `ignore_installed` is `True` when building with setuptools. - if ignore_installed: - # we don't want any failure to raise here but we should print it - import traceback - - traceback.print_exc() + return version, cls.from_str(version) + + @classmethod + def _get_version_from_git_repo(cls, project_root: str) -> str: + # we only want to do this for editable installs + if not _os.path.exists(_os.path.join(project_root, ".git")): + raise RuntimeError("not a git repository") + + import subprocess + + output = subprocess.check_output( + ("git", "describe", "--tags", "--long", "--dirty"), + stderr=subprocess.DEVNULL, + cwd=project_root, + ) + _, count, commit, *dirty = output.decode("utf-8").strip().split("-", 3) + dirty_suffix = f".{dirty[0]}" if dirty else "" + return f"{_VERSION[:-1]}{count}+{commit}{dirty_suffix}" + + @classmethod + def _get_version_from_git_archive(cls, project_root: str) -> str: + with open(_os.path.join(project_root, ".git_archive_info.txt"), encoding="utf-8") as fp: + commit, describe_name = fp.read().splitlines() + if not describe_name: + raise RuntimeError("git archive's describe didn't output anything") + if "%(describe" in describe_name: + # either git-archive was generated with Git < 2.35 or this is not a git-archive + raise RuntimeError("git archive did not support describe output") + _, _, suffix = describe_name.partition("-") + if suffix: + count, _, _ = suffix.partition("-") else: - try: - from importlib.metadata import version + count = "0" + return f"{_VERSION[:-1]}{count}+g{commit}" - ver = version("Red-DiscordBot") - return ver, cls.from_str(ver) - except Exception: - # we don't want any failure to raise here but we should print it - import traceback + @classmethod + def _get_version_from_sdist_pkg_info(cls, project_root: str) -> str: + pkg_info_path = _os.path.join(project_root, "PKG-INFO") + if not _os.path.exists(pkg_info_path): + raise RuntimeError("not an sdist") - traceback.print_exc() + import email + + with open(pkg_info_path, encoding="utf-8") as fp: + return email.message_from_file(fp)["Version"] + + @classmethod + def _get_version_from_package_metadata(cls, project_root: str) -> str: + from importlib.metadata import version - return _VERSION, cls.from_str(_VERSION) + return version("Red-DiscordBot") def _update_event_loop_policy(): From 60a9d4700325c9bcc43d4c0c993750fe0a35461b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pred=C3=A4?= <46051820+PredaaA@users.noreply.github.com> Date: Fri, 30 Dec 2022 04:43:37 +0100 Subject: [PATCH 23/43] Core - Add invoke error message customisation (#5894) Co-authored-by: Jakub Kuczys --- docs/cog_guides/core.rst | 29 +++++++++++++++++++++++++++++ redbot/cogs/trivia/session.py | 11 +++++------ redbot/core/bot.py | 1 + redbot/core/core_commands.py | 33 +++++++++++++++++++++++++++++++-- redbot/core/events.py | 15 ++++++++++----- 5 files changed, 76 insertions(+), 13 deletions(-) diff --git a/docs/cog_guides/core.rst b/docs/cog_guides/core.rst index 2e99181f8f0..be1b76ad875 100644 --- a/docs/cog_guides/core.rst +++ b/docs/cog_guides/core.rst @@ -3080,6 +3080,35 @@ This is only applied to the current server and not globally. **Arguments:** - ``[time]`` - The seconds to wait before deleting the command message. Use -1 to disable. +.. _core-command-set-errormsg: + +"""""""""""" +set errormsg +"""""""""""" + +.. note:: |owner-lock| + +**Syntax** + +.. code-block:: none + + [p]set errormsg [msg] + +**Description** + +Set the message that will be sent on uncaught bot errors. + +To include the command name in the message, use the ``{command}`` placeholder. + +If you omit the ``msg`` argument, the message will be reset to the default one. + +**Examples:** + - ``[p]set errormsg`` - Resets the error message back to the default: "Error in command '{command}'.". If the command invoker is one of the bot owners, the message will also include "Check your console or logs for details.". + - ``[p]set errormsg Oops, the command {command} has failed! Please try again later.`` - Sets the error message to a custom one. + +**Arguments:** + - ``[msg]`` - The custom error message. Must be less than 1000 characters. Omit to reset to the default one. + .. _core-command-set-fuzzy: """"""""" diff --git a/redbot/cogs/trivia/session.py b/redbot/cogs/trivia/session.py index 7c8f873c40b..6c5770a1f30 100644 --- a/redbot/cogs/trivia/session.py +++ b/redbot/cogs/trivia/session.py @@ -118,13 +118,12 @@ def _error_handler(self, fut): self.stop() except Exception as exc: LOG.error("A trivia session has encountered an error.\n", exc_info=exc) - asyncio.create_task( - self.ctx.send( - _( - "An unexpected error occurred in the trivia session.\nCheck your console or logs for details." - ) + msg = _("An unexpected error occurred in the trivia session.") + if self.ctx.author.id in self.ctx.bot.owner_ids: + msg = _( + "An unexpected error occurred in the trivia session.\nCheck your console or logs for details." ) - ) + asyncio.create_task(self.ctx.send(msg)) self.stop() async def run(self): diff --git a/redbot/core/bot.py b/redbot/core/bot.py index 7e58375c05a..06e7846648a 100644 --- a/redbot/core/bot.py +++ b/redbot/core/bot.py @@ -134,6 +134,7 @@ def __init__(self, *args, cli_flags=None, bot_dir: Path = Path.cwd(), **kwargs): invite_commands_scope=False, disabled_commands=[], disabled_command_msg="That command is disabled.", + invoke_error_msg=None, extra_owner_destinations=[], owner_opt_out_list=[], last_system_info__python_version=[3, 7], diff --git a/redbot/core/core_commands.py b/redbot/core/core_commands.py index 0593afed87e..26ec04e755f 100644 --- a/redbot/core/core_commands.py +++ b/redbot/core/core_commands.py @@ -3646,7 +3646,7 @@ async def _set_serverprefix( @_set.command(name="usebuttons") @checks.is_owner() - async def use_buttons(self, ctx: commands.Context, use_buttons: bool = None): + async def _set_usebuttons(self, ctx: commands.Context, use_buttons: bool = None): """ Set a global bot variable for using buttons in menus. @@ -3655,7 +3655,7 @@ async def use_buttons(self, ctx: commands.Context, use_buttons: bool = None): This defaults to False. Using this without a setting will toggle. - **Examples:** + **Examples:** - `[p]set usebuttons True` - Enables using buttons. - `[p]helpset usebuttons` - Toggles the value. @@ -3670,6 +3670,35 @@ async def use_buttons(self, ctx: commands.Context, use_buttons: bool = None): else: await ctx.send(_("I will not use buttons on basic menus.")) + @_set.command(name="errormsg") + @commands.is_owner() + async def _set_errormsg(self, ctx: commands.Context, *, msg: str = None): + """ + Set the message that will be sent on uncaught bot errors. + + To include the command name in the message, use the `{command}` placeholder. + + If you omit the `msg` argument, the message will be reset to the default one. + + **Examples:** + - `[p]set errormsg` - Resets the error message back to the default: "Error in command '{command}'.". If the command invoker is one of the bot owners, the message will also include "Check your console or logs for details.". + - `[p]set errormsg Oops, the command {command} has failed! Please try again later.` - Sets the error message to a custom one. + + **Arguments:** + - `[msg]` - The custom error message. Must be less than 1000 characters. Omit to reset to the default one. + """ + if msg is not None and len(msg) >= 1000: + return await ctx.send(_("The message must be less than 1000 characters.")) + + if msg is not None: + await self.bot._config.invoke_error_msg.set(msg) + content = _("Succesfully updated the error message.") + else: + await self.bot._config.invoke_error_msg.clear() + content = _("Successfully reset the error message back to the default one.") + + await ctx.send(content) + @commands.group() @checks.is_owner() async def helpset(self, ctx: commands.Context): diff --git a/redbot/core/events.py b/redbot/core/events.py index 3811f38be3e..d1651eadf81 100644 --- a/redbot/core/events.py +++ b/redbot/core/events.py @@ -266,16 +266,21 @@ async def on_command_error(ctx, error, unhandled_by_cog=False): "Exception in command '{}'".format(ctx.command.qualified_name), exc_info=error.original, ) - - message = _( - "Error in command '{command}'. Check your console or logs for details." - ).format(command=ctx.command.qualified_name) exception_log = "Exception in command '{}'\n" "".format(ctx.command.qualified_name) exception_log += "".join( traceback.format_exception(type(error), error, error.__traceback__) ) bot._last_exception = exception_log - await ctx.send(inline(message)) + + message = await bot._config.invoke_error_msg() + if not message: + if ctx.author.id in bot.owner_ids: + message = inline( + _("Error in command '{command}'. Check your console or logs for details.") + ) + else: + message = inline(_("Error in command '{command}'.")) + await ctx.send(message.replace("{command}", ctx.command.qualified_name)) elif isinstance(error, commands.CommandNotFound): help_settings = await HelpSettings.from_context(ctx) fuzzy_commands = await fuzzy_command_search( From 82e92a8dc30f8f5a91c759fb1372a28cf5dbf5c9 Mon Sep 17 00:00:00 2001 From: Ryan <50505980+ryan5453@users.noreply.github.com> Date: Thu, 29 Dec 2022 22:48:44 -0500 Subject: [PATCH 24/43] Update enqueued track message to distinguish album from playlist (#5569) Co-authored-by: aleclol <50505980+aleclol@users.noreply.github.com> Co-authored-by: Jakub Kuczys --- redbot/cogs/audio/core/utilities/player.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/redbot/cogs/audio/core/utilities/player.py b/redbot/cogs/audio/core/utilities/player.py index 45edc01e7b7..286d021ac12 100644 --- a/redbot/cogs/audio/core/utilities/player.py +++ b/redbot/cogs/audio/core/utilities/player.py @@ -382,6 +382,8 @@ async def _enqueue_tracks( index = query.track_index if query.start_time: seek = query.start_time + if query.is_url: + playlist_url = query.uri try: result, called_api = await self.api_interface.fetch_track(ctx, player, query) except TrackEnqueueError: @@ -443,12 +445,12 @@ async def _enqueue_tracks( async for track in AsyncIter(tracks): if len(player.queue) >= 10000: continue - query = Query.process_input(track, self.local_folder_current_path) + track_query = Query.process_input(track, self.local_folder_current_path) if not await self.is_query_allowed( self.config, ctx, - f"{track.title} {track.author} {track.uri} " f"{str(query)}", - query_obj=query, + f"{track.title} {track.author} {track.uri} " f"{str(track_query)}", + query_obj=track_query, ): log.debug("Query is not allowed in %r (%s)", ctx.guild.name, ctx.guild.id) continue @@ -489,11 +491,12 @@ async def _enqueue_tracks( playlist_name = escape( playlist_data.name if playlist_data else _("No Title"), formatting=True ) + title = _("Playlist Enqueued") if not query.is_album else _("Album Enqueued") embed = discord.Embed( description=bold(f"[{playlist_name}]({playlist_url})") if playlist_url else playlist_name, - title=_("Playlist Enqueued"), + title=title, ) embed.set_footer( text=_("Added {num} tracks to the queue.{maxlength_msg}").format( From b98156c589063cba5804a07fc323e6b67bfbb342 Mon Sep 17 00:00:00 2001 From: Kreusada <67752638+Kreusada@users.noreply.github.com> Date: Fri, 30 Dec 2022 22:28:44 +0000 Subject: [PATCH 25/43] Use `usage` attr to remove appended underscore for `from_` parameter in `[p]reorderpath` (#5946) --- docs/cog_guides/cog_manager_ui.rst | 4 ++-- redbot/core/cog_manager.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/cog_guides/cog_manager_ui.rst b/docs/cog_guides/cog_manager_ui.rst index fc26911235b..e51473702d6 100644 --- a/docs/cog_guides/cog_manager_ui.rst +++ b/docs/cog_guides/cog_manager_ui.rst @@ -193,7 +193,7 @@ reorderpath .. code-block:: none - [p]reorderpath + [p]reorderpath **Description** @@ -221,7 +221,7 @@ have to put the 3rd path higher than the 2nd path, let's swap them! Type **Arguments** -* ````: The index of the path you want to move. +* ````: The index of the path you want to move. * ````: The location where you want to insert the path. .. _cogmanagerui-command-installpath: diff --git a/redbot/core/cog_manager.py b/redbot/core/cog_manager.py index a4b122389f3..116df588f79 100644 --- a/redbot/core/cog_manager.py +++ b/redbot/core/cog_manager.py @@ -412,7 +412,7 @@ async def removepath(self, ctx: commands.Context, *path_numbers: positive_int): for page in pagify("\n\n".join(parts), ["\n", " "]): await ctx.send(page) - @commands.command() + @commands.command(usage=" ") @checks.is_owner() async def reorderpath(self, ctx: commands.Context, from_: positive_int, to: positive_int): """ From 7db635a05b634bd16b15a8d505b6b66cc58d9fa6 Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Sun, 1 Jan 2023 01:35:06 +0100 Subject: [PATCH 26/43] Change 'Managed' to 'External' in `[p]llset secured`'s output (#5944) --- redbot/cogs/audio/core/commands/llset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redbot/cogs/audio/core/commands/llset.py b/redbot/cogs/audio/core/commands/llset.py index b5bfc9a51f7..0c9668091fd 100644 --- a/redbot/cogs/audio/core/commands/llset.py +++ b/redbot/cogs/audio/core/commands/llset.py @@ -278,7 +278,7 @@ async def command_llsetup_secured(self, ctx: commands.Context): ctx, title=_("Setting Changed"), description=_( - "Managed Lavalink node will now connect using the secured {secured_protocol} protocol.\n\n" + "External Lavalink node will now connect using the secured {secured_protocol} protocol.\n\n" "Run `{p}{cmd}` for it to take effect." ).format( p=ctx.prefix, @@ -291,7 +291,7 @@ async def command_llsetup_secured(self, ctx: commands.Context): ctx, title=_("Setting Changed"), description=_( - "Managed Lavalink node will no longer connect using the secured " + "External Lavalink node will no longer connect using the secured " "{secured_protocol} protocol and wil use {unsecured_protocol} instead .\n\n" "Run `{p}{cmd}` for it to take effect." ).format(p=ctx.prefix, cmd=self.command_audioset_restart.qualified_name), From b493103dcb84633550865a27d867ae70c65f3ccb Mon Sep 17 00:00:00 2001 From: Vexed Date: Mon, 2 Jan 2023 04:24:27 +0000 Subject: [PATCH 27/43] Improve validation in trivia (#5947) Co-authored-by: Jakub Kuczys --- redbot/cogs/trivia/schema.py | 121 +++++++++++++++++++++++++++++++++++ redbot/cogs/trivia/trivia.py | 31 ++------- tests/cogs/test_trivia.py | 53 ++++++++++++++- 3 files changed, 180 insertions(+), 25 deletions(-) create mode 100644 redbot/cogs/trivia/schema.py diff --git a/redbot/cogs/trivia/schema.py b/redbot/cogs/trivia/schema.py new file mode 100644 index 00000000000..bc1af8877e8 --- /dev/null +++ b/redbot/cogs/trivia/schema.py @@ -0,0 +1,121 @@ +import itertools +import re +from typing import Any, NoReturn + +from schema import And, Const, Optional, Schema, SchemaError, SchemaMissingKeyError, Use + +from redbot.core.i18n import Translator + +__all__ = ("TRIVIA_LIST_SCHEMA", "format_schema_error") + +T_ = Translator("Trivia", __file__) +KEY_ERROR_MSG_RE = re.compile(r"Key '(.+)' error:") + + +class SchemaErrorMessage(str): + def format(self, *args: Any, **kwargs: Any) -> str: + return T_(str(self)) + + +def int_or_float(value: Any) -> float: + if not isinstance(value, (float, int)): + raise TypeError("Value needs to be an integer or a float.") + return float(value) + + +def not_str(value: Any) -> float: + if isinstance(value, str): + raise TypeError("Value needs to not be a string.") + return value + + +_ = SchemaErrorMessage +NO_QUESTIONS_ERROR_MSG = _("The trivia list does not contain any questions.") +ALWAYS_MATCH = Optional(Use(lambda x: x)) +MATCH_ALL_BUT_STR = Optional(Use(not_str)) +TRIVIA_LIST_SCHEMA = Schema( + { + Optional("AUTHOR"): And(str, error=_("{key} key must be a text value.")), + Optional("CONFIG"): And( + { + Optional("max_score"): And( + int, + lambda n: n >= 1, + error=_("{key} key in {parent_key} must be a positive integer."), + ), + Optional("timeout"): And( + Use(int_or_float), + lambda n: n > 0.0, + error=_("{key} key in {parent_key} must be a positive number."), + ), + Optional("delay"): And( + Use(int_or_float), + lambda n: n >= 4.0, + error=_( + "{key} key in {parent_key} must be a positive number" + " greater than or equal to 4." + ), + ), + Optional("bot_plays"): Const( + bool, error=_("{key} key in {parent_key} must be either true or false.") + ), + Optional("reveal_answer"): Const( + bool, error=_("{key} key in {parent_key} must be either true or false.") + ), + Optional("payout_multiplier"): And( + Use(int_or_float), + lambda n: n >= 0.0, + error=_("{key} key in {parent_key} must be a non-negative number."), + ), + Optional("use_spoilers"): Const( + bool, error=_("{key} key in {parent_key} must be either true or false.") + ), + # This matches any extra key and always fails validation + # for the purpose of better error messages. + ALWAYS_MATCH: And( + lambda __: False, + error=_("{key} is not a key that can be specified in {parent_key}."), + ), + }, + error=_("{key} should be a 'key: value' mapping."), + ), + str: And( + [str, int, bool, float], + error=_("Value of question {key} is not a list of text values (answers)."), + ), + # This matches any extra key and always fails validation + # for the purpose of better error messages. + MATCH_ALL_BUT_STR: And( + lambda __: False, + error=_("A key of question {key} is not a text value."), + ), + }, + error=_("A trivia list should be a 'key: value' mapping."), +) + + +def format_schema_error(exc: SchemaError) -> str: + if isinstance(exc, SchemaMissingKeyError): + return NO_QUESTIONS_ERROR_MSG.format() + + # dict.fromkeys is used for de-duplication with order preservation + errors = {idx: msg for idx, msg in enumerate(exc.errors) if msg is not None} + if not errors: + return str(exc) + error_idx, error_msg_fmt = errors.popitem() + + autos = dict.fromkeys(msg for msg in itertools.islice(exc.autos, error_idx) if msg is not None) + keys = [match[1] for msg in autos if (match := KEY_ERROR_MSG_RE.fullmatch(msg)) is not None] + key_count = len(keys) + if key_count == 2: + key = keys[-1] + parent_key = keys[-2] + elif key_count == 1: + key = keys[-1] + # should only happen for messages where this field isn't used + parent_key = "UNKNOWN" + else: + # should only happen for messages where neither of the fields are used + key = parent_key = "UNKNOWN" + + return error_msg_fmt.format(key=repr(key), parent_key=repr(parent_key)) diff --git a/redbot/cogs/trivia/trivia.py b/redbot/cogs/trivia/trivia.py index 5b6e23e165f..ca2c8c47d0a 100644 --- a/redbot/cogs/trivia/trivia.py +++ b/redbot/cogs/trivia/trivia.py @@ -4,7 +4,7 @@ import pathlib from collections import Counter from typing import Any, Dict, List, Literal, Union -from schema import Schema, Optional, Or, SchemaError +import schema import io import yaml @@ -23,26 +23,11 @@ from .converters import finite_float from .log import LOG from .session import TriviaSession +from .schema import TRIVIA_LIST_SCHEMA, format_schema_error __all__ = ("Trivia", "UNIQUE_ID", "InvalidListError", "get_core_lists", "get_list") UNIQUE_ID = 0xB3C0E453 -TRIVIA_LIST_SCHEMA = Schema( - { - Optional("AUTHOR"): str, - Optional("CONFIG"): { - Optional("max_score"): int, - Optional("timeout"): Or(int, float), - Optional("delay"): Or(int, float), - Optional("bot_plays"): bool, - Optional("reveal_answer"): bool, - Optional("payout_multiplier"): Or(int, float), - Optional("use_spoilers"): bool, - }, - str: [str, int, bool, float], - } -) - _ = Translator("Trivia", __file__) @@ -120,7 +105,7 @@ async def triviaset_showsettings(self, ctx: commands.Context): @triviaset.command(name="maxscore") async def triviaset_max_score(self, ctx: commands.Context, score: int): """Set the total points required to win.""" - if score < 0: + if score <= 0: await ctx.send(_("Score must be greater than 0.")) return settings = self.config.guild(ctx.guild) @@ -293,18 +278,18 @@ async def trivia_upload(self, ctx: commands.Context): try: await self._save_trivia_list(ctx=ctx, attachment=parsedfile) except yaml.error.MarkedYAMLError as exc: - await ctx.send(_("Invalid syntax: ") + str(exc)) + await ctx.send(_("Invalid syntax:\n") + box(str(exc))) except yaml.error.YAMLError: await ctx.send( _("There was an error parsing the trivia list. See logs for more info.") ) LOG.exception("Custom Trivia file %s failed to upload", parsedfile.filename) - except SchemaError as e: + except schema.SchemaError as exc: await ctx.send( _( "The custom trivia list was not saved." " The file does not follow the proper data format.\n{schema_error}" - ).format(schema_error=box(e)) + ).format(schema_error=box(format_schema_error(exc))) ) @commands.is_owner() @@ -740,8 +725,6 @@ def get_list(path: pathlib.Path) -> Dict[str, Any]: ------ InvalidListError Parsing of list's YAML file failed. - SchemaError - The list does not adhere to the schema. """ with path.open(encoding="utf-8") as file: try: @@ -751,6 +734,6 @@ def get_list(path: pathlib.Path) -> Dict[str, Any]: try: TRIVIA_LIST_SCHEMA.validate(trivia_dict) - except SchemaError as exc: + except schema.SchemaError as exc: raise InvalidListError("The list does not adhere to the schema.") from exc return trivia_dict diff --git a/tests/cogs/test_trivia.py b/tests/cogs/test_trivia.py index 8d8d9baae24..a5795495ee1 100644 --- a/tests/cogs/test_trivia.py +++ b/tests/cogs/test_trivia.py @@ -1,7 +1,17 @@ import textwrap +from typing import Any +import pytest import yaml -from schema import SchemaError +from schema import And, Optional, SchemaError + +from redbot.cogs.trivia.schema import ( + ALWAYS_MATCH, + MATCH_ALL_BUT_STR, + NO_QUESTIONS_ERROR_MSG, + TRIVIA_LIST_SCHEMA, + format_schema_error, +) def test_trivia_lists(): @@ -25,3 +35,44 @@ def test_trivia_lists(): f"- {name}:\n{textwrap.indent(error, ' ')}" for name, error in problem_lists ) raise TypeError("The following lists contain errors:\n" + msg) + + +def _get_error_message(*keys: Any, key: str = "UNKNOWN", parent_key: str = "UNKNOWN") -> str: + if not keys: + return TRIVIA_LIST_SCHEMA._error + + current = TRIVIA_LIST_SCHEMA.schema + for key_name in keys: + if isinstance(current, And): + current = current.args[0] + current = current[key_name] + return str(current._error).format(key=repr(key), parent_key=repr(parent_key)) + + +@pytest.mark.parametrize( + "data,error_msg", + ( + ("text", _get_error_message()), + ({"AUTHOR": 123}, _get_error_message(Optional("AUTHOR"), key="AUTHOR")), + ({"CONFIG": 123}, _get_error_message(Optional("CONFIG"), key="CONFIG")), + ( + {"CONFIG": {"key": "value"}}, + _get_error_message(Optional("CONFIG"), ALWAYS_MATCH, key="key", parent_key="CONFIG"), + ), + ( + {"CONFIG": {"bot_plays": "wrong type"}}, + _get_error_message( + Optional("CONFIG"), Optional("bot_plays"), key="bot_plays", parent_key="CONFIG" + ), + ), + ({"AUTHOR": "Correct type but no questions."}, NO_QUESTIONS_ERROR_MSG), + ({"Question": "wrong type"}, _get_error_message(str, key="Question")), + ({"Question": [{"wrong": "type"}]}, _get_error_message(str, key="Question")), + ({123: "wrong key type"}, _get_error_message(MATCH_ALL_BUT_STR, key="123")), + ), +) +def test_trivia_schema_error_messages(data: Any, error_msg: str): + with pytest.raises(SchemaError) as exc: + TRIVIA_LIST_SCHEMA.validate(data) + + assert format_schema_error(exc.value) == error_msg From f13d910f66dbb38cbf546666807f4eec660ae279 Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Mon, 2 Jan 2023 06:13:12 +0100 Subject: [PATCH 28/43] Disallow NaN and Infinity in Trivia CONFIG schema (#5949) --- redbot/cogs/trivia/schema.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/redbot/cogs/trivia/schema.py b/redbot/cogs/trivia/schema.py index bc1af8877e8..0abc7c5fb30 100644 --- a/redbot/cogs/trivia/schema.py +++ b/redbot/cogs/trivia/schema.py @@ -1,4 +1,5 @@ import itertools +import math import re from typing import Any, NoReturn @@ -18,8 +19,8 @@ def format(self, *args: Any, **kwargs: Any) -> str: def int_or_float(value: Any) -> float: - if not isinstance(value, (float, int)): - raise TypeError("Value needs to be an integer or a float.") + if not isinstance(value, (float, int)) or not math.isfinite(value): + raise TypeError("Value needs to be an integer or a finite float.") return float(value) From eda1288bea1a463f450052f2c3fdd1600176317b Mon Sep 17 00:00:00 2001 From: Lemon Rose <78662983+japandotorg@users.noreply.github.com> Date: Sat, 14 Jan 2023 19:48:01 +0530 Subject: [PATCH 29/43] Add missing help descriptions for flags in redbot-setup (#5818) Co-authored-by: Lemon Rose Co-authored-by: Flame442 <34169552+Flame442@users.noreply.github.com> Co-authored-by: Leet <36166244+leetfin@users.noreply.github.com> Co-authored-by: Jakub Kuczys --- redbot/setup.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/redbot/setup.py b/redbot/setup.py index 0c2618a4038..3e4ba8e8d43 100644 --- a/redbot/setup.py +++ b/redbot/setup.py @@ -363,7 +363,16 @@ async def remove_instance_interaction() -> None: @click.group(invoke_without_command=True) -@click.option("--debug", "--verbose", "-v", count=True) +@click.option( + "--debug", + "--verbose", + "-v", + count=True, + help=( + "Increase the verbosity of the logs, each usage of this flag increases the verbosity" + " level by 1." + ), +) @click.option( "--no-prompt", "interactive", @@ -401,7 +410,15 @@ async def remove_instance_interaction() -> None: "Note: Choosing PostgreSQL will prevent the setup from being completely non-interactive." ), ) -@click.option("--overwrite-existing-instance", type=bool, is_flag=True) +@click.option( + "--overwrite-existing-instance", + type=bool, + is_flag=True, + help=( + "Confirm overwriting of existing instance.\n" + "Note: This removes *metadata* about the existing instance with that name." + ), +) @click.pass_context def cli( ctx: click.Context, From a13870b45f25a62acbc67daf1b5509bfb06aaab3 Mon Sep 17 00:00:00 2001 From: aikaterna <20862007+aikaterna@users.noreply.github.com> Date: Sat, 14 Jan 2023 06:33:03 -0800 Subject: [PATCH 30/43] Remove llsetup alias for llset (#5953) Co-authored-by: Jakub Kuczys --- docs/changelog_3_4_0.rst | 2 +- docs/cog_guides/audio.rst | 258 +++++++++++------------ redbot/cogs/audio/core/abc.py | 2 +- redbot/cogs/audio/core/commands/llset.py | 104 ++++----- redbot/cogs/audio/core/events/dpy.py | 42 ++-- 5 files changed, 204 insertions(+), 204 deletions(-) diff --git a/docs/changelog_3_4_0.rst b/docs/changelog_3_4_0.rst index 2811feba4aa..0f47fc6e9e7 100644 --- a/docs/changelog_3_4_0.rst +++ b/docs/changelog_3_4_0.rst @@ -1281,7 +1281,7 @@ Audio - Added ``[p]audioset restart``, allowing for Lavalink connection to be restarted (:issue:`4446`) - Added ``[p]audioset autodeafen``, allowing for bot to auto-deafen itself when entering voice channel (:issue:`4446`) - Added ``[p]audioset mycountrycode``, allowing Spotify search locale per user (:issue:`4446`) -- Added ``[p]llsetup java``, allowing for a custom Java executable path (:issue:`4446`) +- Added ``[p]llset java``, allowing for a custom Java executable path (:issue:`4446`) - Added ``[p]llset info`` to show Lavalink settings (:issue:`4527`) - Added ``[p]audioset logs`` to download Lavalink logs if the Lavalink server is set to internal (:issue:`4527`) diff --git a/docs/cog_guides/audio.rst b/docs/cog_guides/audio.rst index 13ae747296b..242ff75a7ca 100644 --- a/docs/cog_guides/audio.rst +++ b/docs/cog_guides/audio.rst @@ -3227,7 +3227,7 @@ Set the volume, 1% - 150%. Lavalink Setup Commands ----------------------- -``[p]llsetup`` group commands are used for advanced management of the connection to the Lavalink +``[p]llset`` group commands are used for advanced management of the connection to the Lavalink server. The subcommands are dynamically available depending on whether Red is managing your Lavalink node or if you are connecting to one you manage yourself, or a service that offers Lavalink nodes. @@ -3235,11 +3235,11 @@ nodes. Commands specifically for managed Lavalink nodes can be found in :ref:`this section`, whilst commands for unmanaged Lavalink nodes can be found :ref:`here`. -.. _audio-command-llsetup: +.. _audio-command-llset: -^^^^^^^ -llsetup -^^^^^^^ +^^^^^ +llset +^^^^^ .. note:: |owner-lock| @@ -3247,7 +3247,7 @@ llsetup .. code-block:: none - [p]llsetup + [p]llset **Description** @@ -3258,18 +3258,18 @@ manage an unmanaged (external) or managed Lavalink node. You should not change any command settings in this group command unless you have a valid reason to, e.g. been told by someone in the Red-Discord Bot support - server to do so. Changing llsetup command settings have the potential to break + server to do so. Changing llset command settings have the potential to break Audio cog connection and playback if the wrong settings are used. -"""""""""""""""" -llsetup external -"""""""""""""""" +"""""""""""""" +llset external +"""""""""""""" **Syntax** .. code-block:: none - [p]llsetup external + [p]llset external **Description** @@ -3277,29 +3277,29 @@ Toggle using external Lavalink nodes - requires an existing external Lavalink no Audio to work, if enabled. This command disables the managed Lavalink server: if you do not have an external Lavalink node you will be unable to use Audio while this is enabled. -"""""""""""" -llsetup info -"""""""""""" +"""""""""" +llset info +"""""""""" **Syntax** .. code-block:: none - [p]llsetup info + [p]llset info **Description** Display Lavalink connection settings. -""""""""""""" -llsetup reset -""""""""""""" +""""""""""" +llset reset +""""""""""" **Syntax** .. code-block:: none - [p]llsetup reset + [p]llset reset **Description** @@ -3311,17 +3311,17 @@ Reset all ``[p]llset`` changes back to their default values. Managed Node Management Commands -------------------------------- -.. _audio-command-llsetup-config: +.. _audio-command-llset-config: -^^^^^^^^^^^^^^ -llsetup config -^^^^^^^^^^^^^^ +^^^^^^^^^^^^ +llset config +^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config + [p]llset config **Description** @@ -3330,17 +3330,17 @@ Configure the managed Lavalink node runtime options. All settings under this group will likely cause Audio to malfunction if changed from their defaults, only change settings here if you have been advised to by #support. -.. _audio-command-llsetup-config-bind: +.. _audio-command-llset-config-bind: -^^^^^^^^^^^^^^^^^^^ -llsetup config bind -^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^ +llset config bind +^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config bind [host=localhost] + [p]llset config bind [host=localhost] **Description** @@ -3350,17 +3350,17 @@ Set the managed Lavalink node's binding IP address. * ``[host]``: The node's binding IP address, defaulting to "localhost". -.. _audio-command-llsetup-config-port: +.. _audio-command-llset-config-port: -^^^^^^^^^^^^^^^^^^^ -llsetup config port -^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^ +llset config port +^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config port [port=2333] + [p]llset config port [port=2333] **Description** @@ -3374,33 +3374,33 @@ you already have an application using port 2333 on this device. * ``[port]``: The node's connection port, defaulting to 2333. -.. _audio-command-llsetup-config-server: +.. _audio-command-llset-config-server: -^^^^^^^^^^^^^^^^^^^^^ -llsetup config server -^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^ +llset config server +^^^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config server + [p]llset config server **Description** Configure the managed node authorization and connection settings. -.. _audio-command-llsetup-config-server-buffer: +.. _audio-command-llset-config-server-buffer: -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -llsetup config server buffer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^ +llset config server buffer +^^^^^^^^^^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config server buffer [milliseconds=400] + [p]llset config server buffer [milliseconds=400] **Description** @@ -3412,17 +3412,17 @@ changing it can cause significant playback issues. * ``[milliseconds]`` - The buffer size, defaults to 400. -.. _audio-command-llsetup-config-server-framebuffer: +.. _audio-command-llset-config-server-framebuffer: -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -llsetup config server framebuffer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +llset config server framebuffer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config server framebuffer [milliseconds=1000] + [p]llset config server framebuffer [milliseconds=1000] **Description** @@ -3434,17 +3434,17 @@ changing it can cause significant playback issues. * ``[milliseconds]`` - The framebuffer size, defaults to 1000. -.. _audio-command-llsetup-config-source: +.. _audio-command-llset-config-source: -^^^^^^^^^^^^^^^^^^^^^ -llsetup config source -^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^ +llset config source +^^^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config source + [p]llset config source **Description** @@ -3454,34 +3454,34 @@ By default, all sources are enabled, you should only use commands here to disable a specific source if you have been advised to, disabling sources without background knowledge can cause Audio to break. -.. _audio-command-llsetup-config-source-bandcamp: +.. _audio-command-llset-config-source-bandcamp: -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -llsetup config source bandcamp -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +llset config source bandcamp +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config source bandcamp + [p]llset config source bandcamp **Description** Toggle Bandcamp source on or off. This toggle controls the playback of all Bandcamp related content. -.. _audio-command-llsetup-config-source-http: +.. _audio-command-llset-config-source-http: -^^^^^^^^^^^^^^^^^^^^^^^^^^ -llsetup config source http -^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^ +llset config source http +^^^^^^^^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config source http + [p]llset config source http **Description** @@ -3489,17 +3489,17 @@ Toggle HTTP direct URL usage on or off. This source is used to allow playback from direct HTTP streams (this does not affect direct URL playback for the other sources). -.. _audio-command-llsetup-config-source-local: +.. _audio-command-llset-config-source-local: -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -llsetup config source local -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^ +llset config source local +^^^^^^^^^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config source local + [p]llset config source local **Description** @@ -3507,85 +3507,85 @@ Toggle local file usage on or off. This toggle controls the playback of all local track content, usually found inside the ``localtracks`` folder. -.. _audio-command-llsetup-config-source-soundcloud: +.. _audio-command-llset-config-source-soundcloud: -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -llsetup config source soundcloud -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +llset config source soundcloud +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config source soundcloud + [p]llset config source soundcloud **Description** Toggle SoundCloud source on or off. This toggle controls the playback of all SoundCloud related content. -.. _audio-command-llsetup-config-source-twitch: +.. _audio-command-llset-config-source-twitch: -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -llsetup config source twitch -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^ +llset config source twitch +^^^^^^^^^^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config source twitch + [p]llset config source twitch **Description** Toggle Twitch source on or off. This toggle controls the playback of all Twitch related content. -.. _audio-command-llsetup-config-source-vimeo: +.. _audio-command-llset-config-source-vimeo: -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -llsetup config source vimeo -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^ +llset config source vimeo +^^^^^^^^^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config source vimeo + [p]llset config source vimeo **Description** Toggle Vimeo source on or off. This toggle controls the playback of all Vimeo related content. -.. _audio-command-llsetup-config-source-youtube: +.. _audio-command-llset-config-source-youtube: -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -llsetup config source youtube -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +llset config source youtube +^^^^^^^^^^^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config source youtube + [p]llset config source youtube **Description** Toggle YouTube source on or off (**this includes Spotify**). This toggle controls the playback of all YouTube and Spotify related content. -.. _audio-command-llsetup-config-token: +.. _audio-command-llset-config-token: -^^^^^^^^^^^^^^^^^^^^ -llsetup config token -^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^ +llset config token +^^^^^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup config token [password=youshallnotpass] + [p]llset config token [password=youshallnotpass] **Description** @@ -3597,17 +3597,17 @@ The value by default is ``youshallnotpass``. * ``[password]`` - The node's connection password, defaulting to ``youshallnotpass``. -.. _audio-command-llsetup-heapsize: +.. _audio-command-llset-heapsize: -^^^^^^^^^^^^^^^^ -llsetup heapsize -^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^ +llset heapsize +^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup heapsize [size=3G] + [p]llset heapsize [size=3G] **Description** @@ -3624,17 +3624,17 @@ node will always use this amount of RAM. * ``[size]`` - The node's maximum heap-size, defaulting to ``3G``. -.. _audio-command-llsetup-java: +.. _audio-command-llset-java: -^^^^^^^^^^^^ -llsetup java -^^^^^^^^^^^^ +^^^^^^^^^^ +llset java +^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup java [javapath] + [p]llset java [javapath] **Description** @@ -3650,17 +3650,17 @@ The current supported version is Java 11. * ``[java]`` - The java executable path, leave blank to reset it back to default. -.. _audio-command-llsetup-yaml: +.. _audio-command-llset-yaml: -^^^^^^^^^^^^ -llsetup yaml -^^^^^^^^^^^^ +^^^^^^^^^^ +llset yaml +^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup yaml + [p]llset yaml **Description** @@ -3676,17 +3676,17 @@ Unmanaged Node Management Commands A normal Red user should never have to use these commands unless they are :ref:`managing multiple Red bots with Audio`. -.. _audio-command-llsetup-host: +.. _audio-command-llset-host: -^^^^^^^^^^^^ -llsetup host -^^^^^^^^^^^^ +^^^^^^^^^^ +llset host +^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup host [host=localhost] + [p]llset host [host=localhost] **Description** @@ -3697,17 +3697,17 @@ Audio will use to connect to an external Lavalink node. * ``[host]`` - The connection host, defaulting to "localhost". -.. _audio-command-llsetup-password: +.. _audio-command-llset-password: -^^^^^^^^^^^^^^^^ -llsetup password -^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^ +llset password +^^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup password [password=youshallnotpass] + [p]llset password [password=youshallnotpass] **Description** @@ -3718,17 +3718,17 @@ Audio will use to connect to an external Lavalink node. * ``[password]`` - The connection password, defaulting to "youshallnotpass". -.. _audio-command-llsetup-port: +.. _audio-command-llset-port: -^^^^^^^^^^^^ -llsetup port -^^^^^^^^^^^^ +^^^^^^^^^^ +llset port +^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup port [port=2333] + [p]llset port [port=2333] **Description** @@ -3739,17 +3739,17 @@ Audio will use to connect to an external Lavalink node. * ``[password]`` - The connection password, defaulting to 2333. -.. _audio-command-llsetup-secured: +.. _audio-command-llset-secured: -^^^^^^^^^^^^^^^ -llsetup secured -^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^ +llset secured +^^^^^^^^^^^^^ **Syntax** .. code-block:: none - [p]llsetup secured + [p]llset secured **Description** diff --git a/redbot/cogs/audio/core/abc.py b/redbot/cogs/audio/core/abc.py index 49a516bf2b5..66483f974ee 100644 --- a/redbot/cogs/audio/core/abc.py +++ b/redbot/cogs/audio/core/abc.py @@ -85,7 +85,7 @@ class MixinMeta(ABC): _disconnected_shard: Set[int] @abstractmethod - async def command_llsetup(self, ctx: commands.Context): + async def command_llset(self, ctx: commands.Context): raise NotImplementedError() @commands.command() diff --git a/redbot/cogs/audio/core/commands/llset.py b/redbot/cogs/audio/core/commands/llset.py index 0c9668091fd..4e427dec68f 100644 --- a/redbot/cogs/audio/core/commands/llset.py +++ b/redbot/cogs/audio/core/commands/llset.py @@ -30,10 +30,10 @@ class LavalinkSetupCommands(MixinMeta, metaclass=CompositeMetaClass): - @commands.group(name="llsetup", aliases=["llset"]) + @commands.group(name="llset") @commands.is_owner() @commands.bot_has_permissions(embed_links=True) - async def command_llsetup(self, ctx: commands.Context): + async def command_llset(self, ctx: commands.Context): """`Dangerous commands` Manage Lavalink node configuration settings. This command block holds all commands to manage an unmanaged (external) or managed Lavalink node. @@ -43,9 +43,9 @@ async def command_llsetup(self, ctx: commands.Context): All the commands in here have the potential to break the Audio cog. """ - @command_llsetup.command(name="java") + @command_llset.command(name="java") @has_managed_server() - async def command_llsetup_java(self, ctx: commands.Context, *, java_path: str = "java"): + async def command_llset_java(self, ctx: commands.Context, *, java_path: str = "java"): """Change your Java executable path. This command shouldn't need to be used most of the time, and is only useful if the host machine has conflicting Java versions. @@ -90,9 +90,9 @@ async def command_llsetup_java(self, ctx: commands.Context, *, java_path: str = ), ) - @command_llsetup.command(name="heapsize", aliases=["hs", "ram", "memory"]) + @command_llset.command(name="heapsize", aliases=["hs", "ram", "memory"]) @has_managed_server() - async def command_llsetup_heapsize(self, ctx: commands.Context, size: str = MAX_JAVA_RAM): + async def command_llset_heapsize(self, ctx: commands.Context, size: str = MAX_JAVA_RAM): """Set the managed Lavalink node maximum heap-size. By default, this value is 50% of available RAM in the host machine represented by [1-1024][M|G] (256M, 256G for example) @@ -152,8 +152,8 @@ async def validate_input(cog, arg): ), ) - @command_llsetup.command(name="external", aliases=["unmanaged"]) - async def command_llsetup_external(self, ctx: commands.Context): + @command_llset.command(name="external", aliases=["unmanaged"]) + async def command_llset_external(self, ctx: commands.Context): """Toggle using external Lavalink nodes - requires an existing external Lavalink node for Audio to work, if enabled. This command disables the managed Lavalink server, if you do not have an external Lavalink node you will be unable to use Audio while this is enabled. @@ -189,9 +189,9 @@ async def command_llsetup_external(self, ctx: commands.Context): ), ) - @command_llsetup.command(name="host") + @command_llset.command(name="host") @has_unmanaged_server() - async def command_llsetup_host( + async def command_llset_host( self, ctx: commands.Context, host: str = DEFAULT_LAVALINK_SETTINGS["host"] ): """Set the Lavalink node host. @@ -210,9 +210,9 @@ async def command_llsetup_host( ), ) - @command_llsetup.command(name="password", aliases=["pass", "token"]) + @command_llset.command(name="password", aliases=["pass", "token"]) @has_unmanaged_server() - async def command_llsetup_password( + async def command_llset_password( self, ctx: commands.Context, *, password: str = DEFAULT_LAVALINK_SETTINGS["password"] ): """Set the Lavalink node password. @@ -234,9 +234,9 @@ async def command_llsetup_password( ), ) - @command_llsetup.command(name="port") + @command_llset.command(name="port") @has_unmanaged_server() - async def command_llsetup_wsport( + async def command_llset_wsport( self, ctx: commands.Context, port: int = DEFAULT_LAVALINK_SETTINGS["ws_port"] ): """Set the Lavalink node port. @@ -263,9 +263,9 @@ async def command_llsetup_wsport( ), ) - @command_llsetup.command(name="secured", aliases=["wss"]) + @command_llset.command(name="secured", aliases=["wss"]) @has_unmanaged_server() - async def command_llsetup_secured(self, ctx: commands.Context): + async def command_llset_secured(self, ctx: commands.Context): """Set the Lavalink node connection to secured. This toggle sets the connection type to secured or unsecured when connecting to an external Lavalink node. @@ -299,8 +299,8 @@ async def command_llsetup_secured(self, ctx: commands.Context): secured_protocol=inline("wss://"), ) - @command_llsetup.command(name="info", aliases=["settings"]) - async def command_llsetup_info(self, ctx: commands.Context): + @command_llset.command(name="info", aliases=["settings"]) + async def command_llset_info(self, ctx: commands.Context): """Display Lavalink connection settings.""" configs = await self.config.all() @@ -332,9 +332,9 @@ async def command_llsetup_info(self, ctx: commands.Context): except discord.HTTPException: await ctx.send(_("I need to be able to DM you to send you this info.")) - @command_llsetup.command(name="yaml", aliases=["yml"]) + @command_llset.command(name="yaml", aliases=["yml"]) @has_managed_server() - async def command_llsetup_yaml(self, ctx: commands.Context): + async def command_llset_yaml(self, ctx: commands.Context): """Uploads a copy of the application.yml file used by the managed Lavalink node.""" configs = change_dict_naming_convention(await self.config.yaml.all()) data = yaml.safe_dump(configs) @@ -356,20 +356,20 @@ async def command_llsetup_yaml(self, ctx: commands.Context): finally: temp_file.unlink() - @command_llsetup.group(name="config", aliases=["conf"]) + @command_llset.group(name="config", aliases=["conf"]) @has_managed_server() - async def command_llsetup_config(self, ctx: commands.Context): + async def command_llset_config(self, ctx: commands.Context): """Configure the managed Lavalink node runtime options. All settings under this group will likely cause Audio to malfunction if changed from their defaults, only change settings here if you have been advised to by support. """ - @command_llsetup_config.group(name="server") - async def command_llsetup_config_server(self, ctx: commands.Context): + @command_llset_config.group(name="server") + async def command_llset_config_server(self, ctx: commands.Context): """Configure the managed node authorization and connection settings.""" - @command_llsetup_config.command(name="bind", aliases=["host", "address"]) - async def command_llsetup_config_host( + @command_llset_config.command(name="bind", aliases=["host", "address"]) + async def command_llset_config_host( self, ctx: commands.Context, *, host: str = DEFAULT_LAVALINK_YAML["yaml__server__address"] ): """`Dangerous command` Set the managed Lavalink node's binding IP address. @@ -389,8 +389,8 @@ async def command_llsetup_config_host( ), ) - @command_llsetup_config.command(name="token", aliases=["password", "pass"]) - async def command_llsetup_config_token( + @command_llset_config.command(name="token", aliases=["password", "pass"]) + async def command_llset_config_token( self, ctx: commands.Context, *, @@ -415,8 +415,8 @@ async def command_llsetup_config_token( ), ) - @command_llsetup_config.command(name="port") - async def command_llsetup_config_port( + @command_llset_config.command(name="port") + async def command_llset_config_port( self, ctx: commands.Context, *, port: int = DEFAULT_LAVALINK_YAML["yaml__server__port"] ): """`Dangerous command` Set the managed Lavalink node's connection port. @@ -446,15 +446,15 @@ async def command_llsetup_config_port( ), ) - @command_llsetup_config.group(name="source") - async def command_llsetup_config_source(self, ctx: commands.Context): + @command_llset_config.group(name="source") + async def command_llset_config_source(self, ctx: commands.Context): """`Dangerous command` Toggle audio sources on/off. By default, all sources are enabled, you should only use commands here to disable a specific source if you have been advised to, disabling sources without background knowledge can cause Audio to break. """ - @command_llsetup_config_source.command(name="http") - async def command_llsetup_config_source_http(self, ctx: commands.Context): + @command_llset_config_source.command(name="http") + async def command_llset_config_source_http(self, ctx: commands.Context): """Toggle HTTP direct URL usage on or off. This source is used to allow playback from direct HTTP streams (this does not affect direct URL playback for the other sources). @@ -480,8 +480,8 @@ async def command_llsetup_config_source_http(self, ctx: commands.Context): ).format(p=ctx.prefix, cmd=self.command_audioset_restart.qualified_name), ) - @command_llsetup_config_source.command(name="bandcamp", aliases=["bc"]) - async def command_llsetup_config_source_bandcamp(self, ctx: commands.Context): + @command_llset_config_source.command(name="bandcamp", aliases=["bc"]) + async def command_llset_config_source_bandcamp(self, ctx: commands.Context): """Toggle Bandcamp source on or off. This toggle controls the playback of all Bandcamp related content. @@ -507,8 +507,8 @@ async def command_llsetup_config_source_bandcamp(self, ctx: commands.Context): ).format(p=ctx.prefix, cmd=self.command_audioset_restart.qualified_name), ) - @command_llsetup_config_source.command(name="local") - async def command_llsetup_config_source_local(self, ctx: commands.Context): + @command_llset_config_source.command(name="local") + async def command_llset_config_source_local(self, ctx: commands.Context): """Toggle local file usage on or off. This toggle controls the playback of all local track content, usually found inside the `localtracks` folder. @@ -534,8 +534,8 @@ async def command_llsetup_config_source_local(self, ctx: commands.Context): ).format(p=ctx.prefix, cmd=self.command_audioset_restart.qualified_name), ) - @command_llsetup_config_source.command(name="soundcloud", aliases=["sc"]) - async def command_llsetup_config_source_soundcloud(self, ctx: commands.Context): + @command_llset_config_source.command(name="soundcloud", aliases=["sc"]) + async def command_llset_config_source_soundcloud(self, ctx: commands.Context): """Toggle Soundcloud source on or off. This toggle controls the playback of all SoundCloud related content. @@ -561,8 +561,8 @@ async def command_llsetup_config_source_soundcloud(self, ctx: commands.Context): ).format(p=ctx.prefix, cmd=self.command_audioset_restart.qualified_name), ) - @command_llsetup_config_source.command(name="youtube", aliases=["yt"]) - async def command_llsetup_config_source_youtube(self, ctx: commands.Context): + @command_llset_config_source.command(name="youtube", aliases=["yt"]) + async def command_llset_config_source_youtube(self, ctx: commands.Context): """`Dangerous command` Toggle YouTube source on or off (this includes Spotify). This toggle controls the playback of all YouTube and Spotify related content. @@ -588,8 +588,8 @@ async def command_llsetup_config_source_youtube(self, ctx: commands.Context): ).format(p=ctx.prefix, cmd=self.command_audioset_restart.qualified_name), ) - @command_llsetup_config_source.command(name="twitch") - async def command_llsetup_config_source_twitch(self, ctx: commands.Context): + @command_llset_config_source.command(name="twitch") + async def command_llset_config_source_twitch(self, ctx: commands.Context): """Toggle Twitch source on or off. This toggle controls the playback of all Twitch related content. @@ -615,8 +615,8 @@ async def command_llsetup_config_source_twitch(self, ctx: commands.Context): ).format(p=ctx.prefix, cmd=self.command_audioset_restart.qualified_name), ) - @command_llsetup_config_source.command(name="vimeo") - async def command_llsetup_config_source_vimeo(self, ctx: commands.Context): + @command_llset_config_source.command(name="vimeo") + async def command_llset_config_source_vimeo(self, ctx: commands.Context): """Toggle Vimeo source on or off. This toggle controls the playback of all Vimeo related content. @@ -642,8 +642,8 @@ async def command_llsetup_config_source_vimeo(self, ctx: commands.Context): ).format(p=ctx.prefix, cmd=self.command_audioset_restart.qualified_name), ) - @command_llsetup_config_server.command(name="framebuffer", aliases=["fb", "frame"]) - async def command_llsetup_config_server_framebuffer( + @command_llset_config_server.command(name="framebuffer", aliases=["fb", "frame"]) + async def command_llset_config_server_framebuffer( self, ctx: commands.Context, *, @@ -673,8 +673,8 @@ async def command_llsetup_config_server_framebuffer( ), ) - @command_llsetup_config_server.command(name="buffer", aliases=["b"]) - async def command_llsetup_config_server_buffer( + @command_llset_config_server.command(name="buffer", aliases=["b"]) + async def command_llset_config_server_buffer( self, ctx: commands.Context, *, @@ -704,8 +704,8 @@ async def command_llsetup_config_server_buffer( ), ) - @command_llsetup.command(name="reset") - async def command_llsetup_reset(self, ctx: commands.Context): + @command_llset.command(name="reset") + async def command_llset_reset(self, ctx: commands.Context): """Reset all `llset` changes back to their default values.""" async with ctx.typing(): async with self.config.all() as global_data: diff --git a/redbot/cogs/audio/core/events/dpy.py b/redbot/cogs/audio/core/events/dpy.py index bd7bd55b42a..0f97d65bb7d 100644 --- a/redbot/cogs/audio/core/events/dpy.py +++ b/redbot/cogs/audio/core/events/dpy.py @@ -75,92 +75,92 @@ } DANGEROUS_COMMANDS = { - "command_llsetup_java": _( + "command_llset_java": _( "This command will change the executable path of Java, " "this is useful if you have multiple installations of Java and the default one is causing issues. " "Please don't change this unless you are certain that the Java version you are specifying is supported by Red. " "The default and supported version is currently Java 11." ), - "command_llsetup_heapsize": _( + "command_llset_heapsize": _( "This command will change the maximum RAM allocation for the managed Lavalink node, " "usually you will never have to change this, " "before considering changing it please consult our support team." ), - "command_llsetup_external": _( + "command_llset_external": _( "This command will disable the managed Lavalink node, " "if you toggle this command you must specify an external Lavalink node to connect to, " "if you do not do so Audio will stop working." ), - "command_llsetup_host": _( + "command_llset_host": _( "This command is used to specify the IP which will be used by Red to connect to an external Lavalink node. " ), - "command_llsetup_password": _( + "command_llset_password": _( "This command is used to specify the authentication password used by Red to connect to an " "external Lavalink node." ), - "command_llsetup_secured": _( + "command_llset_secured": _( "This command is used toggle between secured and unsecured connections to an external Lavalink node." ), - "command_llsetup_wsport": _( + "command_llset_wsport": _( "This command is used to specify the connection port used by Red to connect to an external Lavalink node." ), - "command_llsetup_config_host": _( + "command_llset_config_host": _( "This command specifies which network interface and IP the managed Lavalink node will bind to, " "by default this is 'localhost', " "only change this if you want the managed Lavalink node to bind to a specific IP/interface." ), - "command_llsetup_config_token": _( + "command_llset_config_token": _( "This command changes the authentication password required to connect to this managed node." "The default value is 'youshallnotpass'." ), - "command_llsetup_config_port": _( + "command_llset_config_port": _( "This command changes the connection port used to connect to this managed node, " "only change this if the default port '2333' is causing conflicts with existing applications." ), - "command_llsetup_config_source_http": _( + "command_llset_config_source_http": _( "This command toggles the support of direct url streams like Icecast or Shoutcast streams. " "An example is ; " "disabling this will make the bot unable to play any direct url steam content." ), - "command_llsetup_config_source_bandcamp": _( + "command_llset_config_source_bandcamp": _( "This command toggles the support of Bandcamp audio playback. " "An example is ; " "disabling this will make the bot unable to play any Bandcamp content", ), - "command_llsetup_config_source_local": _( + "command_llset_config_source_local": _( "This command toggles the support of local track audio playback. " "An example is `/mnt/data/my_super_funky_track.mp3`; " "disabling this will make the bot unable to play any local track content." ), - "command_llsetup_config_source_soundcloud": _( + "command_llset_config_source_soundcloud": _( "This command toggles the support of SoundCloud playback. " "An example is ; " "disabling this will make the bot unable to play any SoundCloud content." ), - "command_llsetup_config_source_youtube": _( + "command_llset_config_source_youtube": _( "This command toggles the support of YouTube playback (Spotify depends on YouTube). " "Disabling this will make the bot unable to play any YouTube content: " "this includes Spotify." ), - "command_llsetup_config_source_twitch": _( + "command_llset_config_source_twitch": _( "This command toggles the support of Twitch playback. " "An example of this is ; " "disabling this will make the bot unable to play any Twitch content." ), - "command_llsetup_config_source_vimeo": _( + "command_llset_config_source_vimeo": _( "This command toggles the support of Vimeo playback. " "An example of this is ; " "disabling this will make the bot unable to play any Vimeo content." ), - "command_llsetup_config_server_framebuffer": _( + "command_llset_config_server_framebuffer": _( "This setting controls the managed node's framebuffer, " "do not change this unless instructed." ), - "command_llsetup_config_server_buffer": _( + "command_llset_config_server_buffer": _( "This setting controls the managed node's JDA-NAS buffer, " "do not change this unless instructed." ), - "command_llsetup_reset": _("This command will reset every setting changed by `[p]llset`."), + "command_llset_reset": _("This command will reset every setting changed by `[p]llset`."), } _ = _T @@ -172,7 +172,7 @@ async def cog_before_invoke(self, ctx: commands.Context) -> None: # check for unsupported arch # Check on this needs refactoring at a later date # so that we have a better way to handle the tasks - if self.command_llsetup in [ctx.command, ctx.command.root_parent]: + if self.command_llset in [ctx.command, ctx.command.root_parent]: pass elif self.lavalink_connect_task and self.lavalink_connect_task.cancelled(): From 0358aabd1f5a7c4f3f319f85f06a2eb409a728aa Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Sun, 15 Jan 2023 17:01:13 +0100 Subject: [PATCH 31/43] Use newer RTD config syntax (#5774) --- .readthedocs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 477d6aec40c..fd00d359580 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,10 +1,11 @@ version: 2 build: - image: latest + os: "ubuntu-22.04" + tools: + python: "3.8" python: - version: 3.8 install: - method: pip path: . From 794d486bc00374867a26aa78a87aa4336d85c072 Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Thu, 19 Jan 2023 11:54:37 +0100 Subject: [PATCH 32/43] Bump OS version ranges - Fedora to <36, 37>, macOS to <11, 13> (#5974) --- docs/install_guides/fedora.rst | 6 +++--- docs/version_guarantees.rst | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/install_guides/fedora.rst b/docs/install_guides/fedora.rst index 79de09f6857..29ac6a86e5c 100644 --- a/docs/install_guides/fedora.rst +++ b/docs/install_guides/fedora.rst @@ -12,15 +12,15 @@ Installing Red on Fedora Linux Installing the pre-requirements ------------------------------- -Fedora Linux 35 and above has all required packages available in official repositories. Install +Fedora Linux 36 and above has all required packages available in official repositories. Install them with dnf: .. prompt:: bash - sudo dnf -y install python3.10 python3.10-devel git java-11-openjdk-headless @development-tools nano + sudo dnf -y install python3.11 python3.11-devel git java-11-openjdk-headless @development-tools nano .. Include common instructions: -.. include:: _includes/create-env-with-venv3.10.rst +.. include:: _includes/create-env-with-venv3.11.rst .. include:: _includes/install-and-setup-red-unix.rst diff --git a/docs/version_guarantees.rst b/docs/version_guarantees.rst index 758a32f115b..0c38801ad17 100644 --- a/docs/version_guarantees.rst +++ b/docs/version_guarantees.rst @@ -51,9 +51,9 @@ Operating system version Supported architectures Ideally supported u ================================ ======================= ============================================================ Windows 10 x86-64 `End/Retirement Date `__ Windows 11 x86-64 `Retirement Date `__ -macOS 10.15 (Catalina) x86-64 ~2022-10 macOS 11 (Big Sur) x86-64, aarch64 ~2023-10 macOS 12 (Monterey) x86-64, aarch64 ~2024-10 +macOS 13 (Ventura) x86-64, aarch64 ~2025-10 Alma Linux 8 x86-64, aarch64 2029-05-31 (`How long will CloudLinux support AlmaLinux? `__) Alma Linux 9 x86-64, aarch64 2032-05-31 Arch Linux x86-64 forever (support is only provided for an up-to-date system) @@ -62,8 +62,8 @@ CentOS Stream 8 x86-64, aarch64 2024-05-31 (`end of CentOS Stream 9 x86-64, aarch64 2027-05-31 (`expected EOL `__) Debian 10 Buster x86-64, aarch64, armv7l 2022-08-14 (`End of life `__) Debian 11 Bullseye x86-64, aarch64, armv7l ~2024-09 (`End of life `__) -Fedora Linux 35 x86-64, aarch64 2022-11-15 (`End of Life `__) Fedora Linux 36 x86-64, aarch64 2023-05-16 (`End of Life `__) +Fedora Linux 37 x86-64, aarch64 2023-11-14 (`End of Life `__) openSUSE Leap 15.4 x86-64, aarch64 2023-11-30 (`end of maintenance life cycle `__) openSUSE Tumbleweed x86-64, aarch64 forever (support is only provided for an up-to-date system) Oracle Linux 8 x86-64, aarch64 2029-07-31 (`End of Premier Support `__) @@ -79,8 +79,8 @@ Rocky Linux 8 x86-64, aarch64 2029-05-31 (`end-of Rocky Linux 9 x86-64, aarch64 2032-05-31 (`end-of-life `__) Ubuntu 18.04 LTS x86-64, aarch64 2023-04-30 (`End of Standard Support `__) Ubuntu 20.04 LTS x86-64, aarch64 2025-04-30 (`End of Standard Support `__) -Ubuntu 21.10 x86-64, aarch64 2022-07-31 (`End of Standard Support `__) Ubuntu 22.04 LTS x86-64, aarch64 2027-04-30 (`End of Standard Support `__) +Ubuntu 22.10 x86-64, aarch64 2023-07-31 (`End of Standard Support `__) ================================ ======================= ============================================================ ==================== From 2168585ee15deec4d08e78fc5c0f1e0ec6350655 Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Mon, 23 Jan 2023 20:39:15 +0100 Subject: [PATCH 33/43] Improve changelog format (#5602) Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> --- CHANGES.rst | 3241 ++++++++++++++++++++++++++++++++++ docs/changelog.rst | 5 + docs/changelog_3_1_0.rst | 232 --- docs/changelog_3_2_0.rst | 565 ------ docs/changelog_3_3_0.rst | 910 ---------- docs/changelog_3_4_0.rst | 1584 ----------------- docs/conf.py | 3 + docs/index.rst | 11 +- docs/release_notes_3_2_0.rst | 50 - 9 files changed, 3250 insertions(+), 3351 deletions(-) create mode 100644 CHANGES.rst create mode 100644 docs/changelog.rst delete mode 100644 docs/changelog_3_1_0.rst delete mode 100644 docs/changelog_3_2_0.rst delete mode 100644 docs/changelog_3_3_0.rst delete mode 100644 docs/changelog_3_4_0.rst delete mode 100644 docs/release_notes_3_2_0.rst diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 00000000000..fb3f7f9c2a9 --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,3241 @@ +.. Red changelogs + +Redbot 3.4.18 (2022-08-15) +========================== + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`RheingoldRiver` + +Read before updating +-------------------- + +#. openSUSE Leap 15.2 is no longer supported as it has already reached its end of life. +#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): + + - Red 3.4.18 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. + - We've updated our `application.yml file `__ and you should update your instance's ``application.yml`` appropriately. + + +End-user changelog +------------------ + +Removals +******** + +- **Core - OS Support** - openSUSE Leap 15.2 is no longer supported as it has already reached its end of life (:issue:`5777`) + +Fixes +***** + +- |cool| **Cogs - Audio** - Addressed a cipher change that made it impossible to find tracks (:issue:`5822`) +- **Cogs - Audio** - Fixed an issue with ``[p]llset external`` making the bot completely unresponsive when switching to an external Lavalink server (:issue:`5804`, :issue:`5828`) + + +Documentation changes +--------------------- + +Changes +******* + +- Updated the screenshot in `bot_application_guide` to include the message content intent (:issue:`5798`) +- Unpinned Temurin version on Windows as a fixed version is now available (:issue:`5815`) + +---- + +Redbot 3.4.17 (2022-06-07) +========================== + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`Drapersniper`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`Kreusada`, :ghuser:`ltzmax`, :ghuser:`matcha19`, :ghuser:`mina9999`, :ghuser:`ponte-vecchio`, :ghuser:`PredaaA`, :ghuser:`TrustyJAID`, :ghuser:`untir-l`, :ghuser:`Vexed01` + +Read before updating +-------------------- + +#. Fedora 34 is no longer supported as it has already reached its end of life. +#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): + + Red 3.4.17 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. + + +End-user changelog +------------------ + +Additions +********* + +- **Cogs - Trivia - Lists** - Added a trivia list for the FIFA World Cup with questions based on hosts, placements, venues, continental confederations, number of participants, top goal scorers, qualification shocks, and more (:issue:`5639`) + +Changes +******* + +- **Core - Bot Commands** - Added instructions on how to respond to the message received from ``[p]contact`` in the embed footer of the message sent to the bot owner (:issue:`5528`, :issue:`5529`) +- **Core - Bot Commands** - Updated ``[p]servers`` command to escape Discord markdown in server names (:issue:`5696`, :issue:`5744`) +- **Cogs - Audio** - Added timestamps to all embeds sent by Audio cog (:issue:`5632`) +- **Cogs - Audio** - Improved handling of voice connection close codes received from Discord (:issue:`5712`) +- |cool| **Cogs - Downloader** - Added information about the commit hash at which the cog is pinned in the output of ``[p]cog listpinned`` command (:issue:`5551`, :issue:`5563`) +- **Cogs - General** - Updated features list in ``[p]serverinfo`` with the latest changes from Discord (:issue:`5655`) +- **Cogs - Mod** - Updated Red's ban commands to address the breaking change that Discord made in their ban list API endpoint (:issue:`5656`) +- **Cogs - Mutes** - Added proper error handling for VERY long durations in mute commands (:issue:`5605`) +- **Cogs - Permissions** - Updated ``[p]permissions acl setglobal`` and ``[p]permissions acl setserver`` to allow sending the file in a follow-up message (:issue:`5473`, :issue:`5685`) +- **Cogs - Permissions** - ``[p]permissions canrun`` now prepends an emoji to the response to better differentiate between the positive and negative results (:issue:`5711`) +- **Cogs - Trivia** - Allowed passing ``use_spoilers`` setting in the CONFIG section of the trivia list file (:issue:`5566`) +- **Cogs - Trivia - Lists** - Updated ``geography`` trivia list with up-to-date answers and removed questions that lack sources for their claimed answers (:issue:`5638`) +- **Cogs - Trivia - Lists** - Updated Kazakhstan's capital city in the ``worldcapitals`` trivia list (:issue:`5598`, :issue:`5599`) + +Removals +******** + +- **Core - OS Support** - Fedora 34 is no longer supported as it has already reached its end of life (:issue:`5701`) + +Fixes +***** + +- **Core - Bot Commands** - Fixed grammar in the ``[p]uptime`` command (:issue:`5596`) +- **Core - Command-line Interfaces** - Fixed a bug that prevented users from changing the name and data location with ``redbot --edit`` command (:issue:`5545`, :issue:`5540`, :issue:`5541`) +- **Core - Modlog** - Modlog's automated case creation for bans now properly checks that the guild is available before further processing (:issue:`5647`) +- |cool| **Cogs - Audio** - Fixed plain word YT searching with ``[p]play`` and ``[p]search`` commands (:issue:`5712`) +- |cool| **Cogs - Audio** - Fixed YT age-restricted track playback (:issue:`5712`) +- **Cogs - Audio** - Fixed the cog not sending any Track Error message on track decoding errors (:issue:`5716`) +- **Cogs - Audio** - Fixed the ``UnboundLocalError`` exception happening when using ``[p]playlist list`` with an empty playlist (:issue:`5378`, :issue:`5394`) +- **Cogs - Filter** - Fixed a potential memory leak in Filter cog (:issue:`5578`) +- **Cogs - Trivia - Lists** - Fixed spelling error in the answer to one of the questions in ``computers`` trivia list (:issue:`5587`, :issue:`5588`) + + +Developer changelog +------------------- + +Changes +******* + +- **Vendored Packages** - Updated ``discord.ext.menus`` vendor (:issue:`5579`) + + +Documentation changes +--------------------- + +Additions +********* + +- Added CentOS Stream 9, RHEL 9, Alma Linux 9, Oracle Linux 9, and Rocky Linux 9 install guides (:issue:`5537`, :issue:`5721`) +- Added Ubuntu 22.04 install guide (:issue:`5720`) + +Changes +******* + +- Changed the recommended operating system for hosting Red from Ubuntu 20.04 LTS to Ubuntu 22.04 LTS (:issue:`5720`) +- Updated Python version in ``pyenv`` and Windows instructions (:issue:`5719`) +- Replaced install instructions for discontinued AdoptOpenJDK package with Temurin 11 package in the macOS install guide (:issue:`5718`) +- Updated Visual Studio Build Tools version in Windows install guide (:issue:`5702`) +- Updated systemd guide to use the absolute path to ``which`` command to avoid triggering shell aliases on some OSes (:issue:`5547`) +- Emphasized lines that contain text that needs to be replaced by the user (:issue:`5548`) +- Prevented Google and other search engines from indexing versioned documentation (:issue:`5549`) + +Fixes +***** + +- Pinned Temurin version on Windows until a fixed version becomes available (:issue:`5717`) +- Fixed git installation instructions in CentOS 7 install guide (:issue:`5700`) + +---- + +Redbot 3.4.16 (2021-12-31) +========================== + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`jack1142`, :ghuser:`PredaaA` + +This is a hotfix release fixing issues with invite URL API that caused +``[p]invite`` command and ``CORE__INVITE_URL`` RPC method to not work. + +End-user changelog +------------------ + +Fixes +***** + +- **Core - Bot Commands** - Fixed ``[p]invite`` command (:issue:`5517`) + + +Developer changelog +------------------- + +Fixes +***** + +- **RPC methods** - Fixed ``CORE__INVITE_URL`` RPC method (:issue:`5517`) + + +Documentation changes +--------------------- + +Changes +******* + +- Changed Arch install guide to temporarily use ``python39`` AUR package instead of ``python`` package as Red does not currently support Python 3.10 (:issue:`5518`) + +---- + +Redbot 3.4.15 (2021-12-31) +========================== + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`aleclol`, :ghuser:`Arman0334`, :ghuser:`Crossedfall`, :ghuser:`Dav-Git`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`Jan200101`, :ghuser:`Just-Jojo`, :ghuser:`Kowlin`, :ghuser:`Kreusada`, :ghuser:`laggron42`, :ghuser:`ltzmax`, :ghuser:`Parnassius`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`RasmusWL`, :ghuser:`sravan1946`, :ghuser:`Stonedestroyer`, :ghuser:`the-krak3n`, :ghuser:`Tobotimus`, :ghuser:`vertyco`, :ghuser:`Vexed01`, :ghuser:`WreckRox`, :ghuser:`yamikaitou` + +Read before updating +-------------------- + +#. Fedora 33 and CentOS 8 are no longer supported as they have already reached end of life. +#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): + + Red 3.4.15 uses a new Lavalink jar that you MUST manually update from `our GitHub `__ to be able to continue using Audio. + + +End-user changelog +------------------ + +Additions +********* + +- |cool| **Core - Command-line Interfaces** - Added new CLI options for non-interactive usage of ``redbot-setup`` (:issue:`2396`, :issue:`5448`) + + See output of ``redbot-setup --help`` for more information. + +- **Cogs - Admin** - Added ``[p]selfroleset clear`` command which can be used to clear the list of available selfroles in the server (:issue:`5387`) +- **Cogs - Audio** - Added native Mac M1 support for Java runtimes supporting Mac M1 (:issue:`5474`) + +Changes +******* + +- **Core - Bot Commands** - Updated prefix length limit to ``25`` to allow setting bot mention as a prefix (:issue:`5476`) +- **Core - Bot Commands** - Improved ``[p]helpset showaliases`` responses (:issue:`5376`) +- **Core - Bot Commands** - Added plural forms to the responses of ``[p]leave`` command (:issue:`5391`) +- **Core - Bot Commands** - The embed setting for ``[p]help`` command set with ``[p]embedset command`` will now affect all help messages, not just the ones sent when invoking ``[p]help`` command directly (:issue:`5452`) +- **Core - Bot Commands** - ``[p]traceback`` command now indicates that it DMed the command caller with a tick reaction (:issue:`5353`) +- **Core - Command-line Interfaces** - JSON is now more strongly recommended and is used by default for new instances in ``redbot-setup`` (:issue:`5448`) +- **Cogs - Audio** - Enabled JDA-NAS on all system architectures which should limit stuttering/buffering issues on some machines (:issue:`5474`) +- **Cogs - Audio** - The bot will now disconnect from the voice channel when all members are bots if the auto-disconnect setting is enabled (:issue:`5421`) +- **Cogs - CustomCommands** - Added 2000 character limit for custom command responses to prevent Nitro users from adding longer responses than a Discord bot can send (:issue:`5499`) +- **Cogs - Downloader** - Added repo name to the response of ``[p]findcog`` command (:issue:`5382`, :issue:`5383`) +- **Cogs - Mod** - ``[p]voicekick`` now sends a response when the action succeeds (:issue:`5367`) +- |cool| **Cogs - Modlog** - Added the new native Discord timestamps in ``[p]case``, ``[p]casesfor``, and ``[p]listcases`` commands (:issue:`5395`) + +Removals +******** + +- **Core - OS Support** - Fedora 33 and CentOS 8 are no longer supported as they have already reached end of life (:issue:`5440`) +- **Cogs - General** - Removed voice region field from ``[p]serverinfo`` command as Discord no longer provides this setting for servers (:issue:`5449`) + +Fixes +***** + +- Fixed short help for some of the commands in Core Red (:issue:`5502`) +- Confirmation prompts (accepting "yes/no" or "I agree" as the answer) no longer wrongfully translate the answer that needs to be sent when only English answers are accepted by the bot (:issue:`5363`, :issue:`5364`, :issue:`5404`) +- **Core - Bot Commands** - Corrected usage examples in help of ``[p]set api`` and ``[p]set api remove`` (:issue:`5444`) +- **Core - Bot Commands** - ``[p]command enable guild`` and ``[p]command disable guild`` commands no longer error out for commands that *only* check for user permissions, not caller's roles (:issue:`5477`) +- **Core - Command-line Interfaces** - Fixed an issue with instance backup failing for non-JSON storage backends (:issue:`5315`) +- **Core - Command-line Interfaces** - Running Red with ``--no-instance`` CLI flag no longer fails when no instance was ever created by the user (:issue:`5415`, :issue:`5416`) +- **Core - Modlog** - Fixed issues with rendering of modlog cases with usernames written in a right-to-left language (:issue:`5422`) +- |cool| **Cogs - Audio** - Fixed an issue with resuming playback after changing voice channels (:issue:`5170`) +- |cool| **Cogs - Audio** - Fixed issues with Soundcloud private playlists and mobile links (:issue:`5474`) +- |cool| **Cogs - Audio** - Fixed searching music with some of the queries containing quotes or backslashes (:issue:`5474`) +- |cool| **Cogs - Audio** - Fixed an exception caused by unavailable YT tracks in Mix playlists (:issue:`5474`) +- **Cogs - Audio** - Fixed ``IndexError`` in ``[p]queue`` command which occurred when the user provides negative integer as the page number (:issue:`5429`) +- **Cogs - Cleanup** - Restricted ``[p]cleanupset notify`` to only be invokable in server channels (:issue:`5466`) +- **Cogs - Economy** - ``[p]economyset showsettings`` now includes configured role payday amounts (:issue:`5455`, :issue:`5457`) +- **Cogs - Mod** - Fixed an error with ``[p]tempban`` failing to send an invite link when a server has an unset vanity URL (:issue:`5472`) +- **Cogs - Mod** - Fixed explanations of example usage for ``[p]ban``, ``[p]kick``, and ``[p]tempban`` commands (:issue:`5372`) +- **Cogs - Mod** - Fixed a typo in one of ``[p]unban``'s error messages (:issue:`5470`) +- **Cogs - Warnings** - Warning actions no longer error out when the action is set to use a command that *only* checks for user permissions, not caller's roles (:issue:`5477`) + + +Developer changelog +------------------- + +Additions +********* + +- **Core - Bot Class** - Added optional ``check_permissions`` keyword-only argument to `Red.embed_requested()` which, if ``True``, will make the method also check whether the bot can send embeds in the given channel (:issue:`5452`) +- |cool| **Core - Bot Class** - Added `Red.get_invite_url()` and `Red.is_invite_url_public()` that expose the functionality of ``[p]invite`` programmatically (:issue:`5152`, :issue:`5424`) +- |cool| **Core - Commands Package** - Added optional ``message`` argument to `Context.tick()` and `Context.react_quietly()` which is used if adding the reaction doesn't succeed (:issue:`3359`, :issue:`4092`) + +Changes +******* + +- **Cogs - Dev** - ``[p]mockmsg`` now allows mocking attachment-only messages (:issue:`5446`) +- **RPC methods** - Changed the output of ``CORE__LOAD``, ``CORE__RELOAD``, and ``CORE__UNLOAD`` RPC methods to a dictionary (:issue:`5451`, :issue:`5453`) + + +Documentation changes +--------------------- + +Additions +********* + +- |cool| Added install guide for Alma Linux 8.4-8.x and Raspberry Pi OS 11 Bullseye (:issue:`5440`) +- Specified that Red currently requires Python 3.8.1 - 3.9.x (:issue:`5403`) + +Changes +******* + +- Updated the Java distribution used in the Windows install guide to Temurin - rebranded AdoptOpenJDK (:issue:`5403`) +- Improved Mac and pyenv instructions to address common issues with load path configuration (:issue:`5356`) +- Updated the server locations for Hetzner and Contabo in :ref:`host-list` document (:issue:`5475`) +- Updated Python version in ``pyenv`` and Windows instructions (:issue:`5447`) +- Removed LXC from unsupported hosting platforms as many VPS providers utilize that technology (:issue:`5351`) + +Fixes +***** + +- Removed inaccurate note from Unix install guides about install commands also being used for updating Red (:issue:`5439`) + +---- + +Redbot 3.4.14 (2021-09-23) +========================== + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`L33Tech`, :ghuser:`maxbooiii`, :ghuser:`RheingoldRiver` + +Read before updating +-------------------- + +#. Versions of RHEL older than 8.4 (including 7) and versions of CentOS older than 8.4 (excluding 7) are no longer supported. +#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): + + Red 3.4.14 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. + + +End-user changelog +------------------ + +Additions +********* + +- |cool| **Core - Bot Commands** - Added the new native Discord timestamp in the ``[p]uptime`` command (:issue:`5323`) + +Changes +******* + +- **Core - Command-line Interfaces** - ``redbot-setup delete`` command no longer requires database connection if the data deletion was not requested (:issue:`5312`, :issue:`5313`) + +Fixes +***** + +- |cool| **Cogs - Audio** - Fixed intermittent 403 Forbidden errors (:issue:`5329`) +- **Cogs - Modlog** - Fixed formatting of **Last modified at** field in Modlog cases (:issue:`5317`) + + +Documentation changes +--------------------- + +Additions +********* + +- |cool| Added install guide for CentOS Stream 8, Oracle Linux 8.4-8.x, and Rocky Linux 8 (:issue:`5328`) + +Changes +******* + +- |cool| Each operating system now has a dedicated install guide (:issue:`5328`) +- Install guides for RHEL derivatives no longer require the use of pyenv (:issue:`5328`) + +Fixes +***** + +- Fixed Raspberry Pi OS install guide (:issue:`5314`, :issue:`5328`) + +---- + +Redbot 3.4.13 (2021-09-09) +========================== + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`Arman0334`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`fredster33`, :ghuser:`Injabie3`, :ghuser:`jack1142`, :ghuser:`Just-Jojo`, :ghuser:`Kowlin`, :ghuser:`Kreusada`, :ghuser:`leblancg`, :ghuser:`maxbooiii`, :ghuser:`npc203`, :ghuser:`palmtree5`, :ghuser:`phenom4n4n`, :ghuser:`PredaaA`, :ghuser:`qenu`, :ghuser:`TheDataLeek`, :ghuser:`Twentysix26`, :ghuser:`TwinDragon`, :ghuser:`Vexed01` + +Read before updating +-------------------- + +#. If you're hosting a public/big bot (>75 servers) or strive to scale your bot at that level, you should read :doc:`our stance on (privileged) intents and public bots `. +#. Fedora 32 is no longer supported as it has already reached end of life. +#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): + + Red 3.4.13 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. + + +End-user changelog +------------------ + +Additions +********* + +- |cool| **Core** - Red 3.4.13 is the first release to (finally) support Python 3.9! (:issue:`4655`, :issue:`5121`) +- |cool| **Core - Bot Commands** - Added a new ``[p]diagnoseissues`` command to allow the bot owners to diagnose issues with various command checks with ease (:issue:`4717`, :issue:`5243`) + + Since some of us are pretty excited about this feature, here's a very small teaser showing a part of what it can do: + + .. figure:: https://user-images.githubusercontent.com/6032823/132610057-d6c65d67-c244-4f0b-9458-adfbe0c68cab.png +- **Core - Bot Commands** - Added a setting for ``[p]help``'s reaction timeout (:issue:`5205`) + + This can be changed with ``[p]helpset reacttimeout`` command +- **Cogs - Alias** - Added commands for editing existing aliases (:issue:`5108`) +- **Cogs - Audio** - Added a per-guild max volume setting (:issue:`5165`) + + This can be changed with the ``[p]audioset maxvolume`` command +- |cool| **Cogs - Cleanup** - All ``[p]cleanup`` commands will now send a notification with the number of deleted messages. The notification is deleted automatically after 5 seconds (:issue:`5218`) + + This can be disabled with the ``[p]cleanupset notify`` command +- **Cogs - Filter** - Added ``[p]filter clear`` and ``[p]filter channel clear`` commands for clearing the server's/channel's filter list (:issue:`4841`, :issue:`4981`) + +Changes +******* + +- **Core - Bot Commands** - Revamped the ``[p]debuginfo`` to make it more useful for... You guessed it, debugging! (:issue:`4997`, :issue:`5156`) + + More specifically, added information about CPU and RAM, bot's instance name and owners +- |cool| **Core - Bot Commands** - Added the new native Discord timestamps in Modlog cases, ``[p]userinfo``, ``[p]serverinfo``, and ``[p]tempban`` (:issue:`5155`, :issue:`5241`) +- **Core - Bot Commands** - The ``[p]invite`` command will now add a tick reaction after it DMs an invite link to the user (:issue:`5184`) +- |cool| **Core - Command-line Interfaces** - The formatting of Red's console logs has been updated to make it more copy-paste friendly (:issue:`4868`, :issue:`5181`) +- **Core - Command-line Interfaces** - The console error about missing Privileged Intents stands out more now (:issue:`5184`) +- **Core - Dependencies** - Upgraded all Red's dependencies (:issue:`5121`) +- **Cogs - Admin** - The ``[p]selfroleset add`` and ``[p]selfroleset remove`` commands can now be used to add multiple selfroles at once (:issue:`5237`, :issue:`5238`) +- **Cogs - Audio** - ``[p]summon`` will now indicate that it has succeeded or failed to summon the bot (:issue:`5186`) +- |cool| **Cogs - Cleanup** - The ``[p]cleanup user`` command can now be used to clean messages of a user that is no longer in the server (:issue:`5169`) +- **Cogs - Downloader** - The dot character (``.``) can now be used in repo names. No more issues with adding repositories using the commands provided by the Cog Index! (:issue:`5214`) +- |cool| **Cogs - Mod** - The DM message from the ``[p]tempban`` command will now include the ban reason if ``[p]modset dm`` setting is enabled (:issue:`4836`, :issue:`4837`) +- **Cogs - Streams** - Made small optimizations in regards to stream alerts (:issue:`4968`) +- **Cogs - Trivia** - Added schema validation of the custom trivia files (:issue:`4571`, :issue:`4659`) + +Removals +******** + +- **Core - OS Support** - Fedora 32 is no longer supported as it has already reached end of life (:issue:`5121`) + +Fixes +***** + +- **Core - Bot Commands** - Fixed a bunch of errors related to the missing permissions and channels/messages no longer existing (:issue:`5109`, :issue:`5163`, :issue:`5172`, :issue:`5191`) +- **Cogs - Audio** - Fixed an issue with short clips being cutoff when auto-disconnect on queue end is enabled (:issue:`5158`, :issue:`5188`) +- |cool| **Cogs - Audio** - Fixed fetching of age-restricted tracks (:issue:`5233`) +- |cool| **Cogs - Audio** - Fixed searching of YT Music (:issue:`5233`) +- |cool| **Cogs - Audio** - Fixed playback from SoundCloud (:issue:`5233`) +- **Cogs - Downloader** - Added a few missing line breaks (:issue:`5185`, :issue:`5187`) +- **Cogs - Mod** - Fixed an error with handling of temporary ban expirations while the guild is unavailable due to Discord outage (:issue:`5173`) +- **Cogs - Mod** - The ``[p]rename`` command will no longer permit changing nicknames of members that are not lower in the role hierarchy than the command caller (:issue:`5187`, :issue:`5211`) +- **Cogs - Streams** - Fixed an issue with some YouTube streamers getting removed from stream alerts after a while (:issue:`5195`, :issue:`5223`) +- |cool| **Cogs - Warnings** - 0 point warnings are, once again, allowed. (:issue:`5177`, :issue:`5178`) + + +Developer changelog +------------------- + +Additions +********* + +- |cool| **Core - Bot Class** - Added more APIs for allowlists and blocklists (:issue:`5206`) + + Here's the list of the methods that were added to the ``bot`` object: + + - `Red.add_to_blacklist()` + - `Red.remove_from_blacklist()` + - `Red.get_blacklist()` + - `Red.clear_blacklist()` + - `Red.add_to_whitelist()` + - `Red.remove_from_whitelist()` + - `Red.get_whitelist()` + - `Red.clear_whitelist()` +- |cool| **Core - Commands Package** - Added `RelativedeltaConverter` and `parse_relativedelta` to the ``redbot.core.commands`` package (:issue:`5000`) + + This converter and function return `dateutil.relativedelta.relativedelta` object that represents a relative delta. + In addition to regular timedelta arguments, it also accepts months and years! +- **Core - Commands Package** - Added `CommandConverter` and `CogConverter` to the ``redbot.core.commands`` package (:issue:`5037`) + + +Documentation changes +--------------------- + +Additions +********* + +- Added a document about (privileged) intents and our stance regarding "public bots" (:issue:`5216`, :issue:`5221`) +- |cool| Added install instructions for Debian 11 Bullseye (:issue:`5213`, :issue:`5217`) +- Added Oracle Cloud's Always Free offering to the :ref:`host-list` (:issue:`5225`) + +Changes +******* + +- |cool| Updated the commands in the install guide for Mac OS to work properly on Apple Silicon devices (:issue:`5234`) + +Fixes +***** + +- Fixed the examples of commands that are only available to people with the mod role (:issue:`5180`) +- Fixed few other small issues with the documentation :) (:issue:`5048`, :issue:`5092`, :issue:`5149`, :issue:`5207`, :issue:`5209`, :issue:`5215`, :issue:`5219`, :issue:`5220`) + +---- + +Redbot 3.4.12 (2021-06-17) +========================== + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`Just-Jojo`, :ghuser:`Kowlin`, :ghuser:`Kreusada`, :ghuser:`npc203`, :ghuser:`PredaaA`, :ghuser:`retke`, :ghuser:`Stonedestroyer` + +This is a hotfix release related to Red ceasing to use the Audio Global API service. + +End-user changelog +------------------ + +Additions +********* + +- **Core - Bot Commands** - ``applications.commands`` scope can now be included in the invite URL returned from ``[p]invite`` by enabling it with``[p]inviteset commandscope`` + +Changes +******* + +- **Core - Bot Commands** - ``[p]set serverprefix`` command will now prevent the user from setting a prefix with length greater than 20 characters (:issue:`5091`, :issue:`5117`) +- **Core - Bot Commands** - ``[p]set prefix`` command will now warn the user when trying to set a prefix with length greater than 20 characters (:issue:`5091`, :issue:`5117`) +- |cool| **Cogs - Audio** - All local caches are now enabled by default (:issue:`5140`) + +Removals +******** + +- **Cogs - Audio** - Global API service will no longer be used in Audio and as such support for it has been removed from the cog (:issue:`5143`) + +Fixes +***** + +- **Cogs - Audio** - Updated URL of the curated playlist (:issue:`5135`) +- **Cogs - Filter** - Fixed an edge case that caused the cog to sometimes check contents of DM messages (:issue:`5125`) +- **Cogs - Warnings** - Prevented users from applying 0 or less points in custom warning reasons (:issue:`5119`, :issue:`5120`) + +Developer changelog +------------------- + +Changes +******* + +- **Cogs - Dev** - ``[p]debug`` command will now confirm the code finished running with a tick reaction (:issue:`5107`) + +---- + +Redbot 3.4.11 (2021-06-12) +========================== + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`Onii-Chan-Discord` + +This is a hotfix release fixing a crash involving guild uploaded stickers. + +End-user changelog +------------------ + +Changes +******* + +- **Core - Dependencies** - discord.py version has been bumped to 1.7.3 (:issue:`5129`) + + +Documentation changes +--------------------- + +Fixes +***** + +- Links to the CogBoard in Red's documentation have been updated to use the new domain (:issue:`5124`) + +---- + +Redbot 3.4.10 (2021-05-28) +========================== + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`aleclol`, :ghuser:`benno1237`, :ghuser:`bobloy`, :ghuser:`BoyDownTown`, :ghuser:`Danstr5544`, :ghuser:`DeltaXWizard`, :ghuser:`Drapersniper`, :ghuser:`Fabian-Evolved`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`Kreusada`, :ghuser:`Lifeismana`, :ghuser:`Obi-Wan3`, :ghuser:`OofChair`, :ghuser:`palmtree5`, :ghuser:`plofts`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`TrustyJAID`, :ghuser:`Vexed01` + +Read before updating +-------------------- + +#. PM2 process manager is no longer supported as it is not a viable solution due to certain parts of its behavior. + + We highly recommend you to switch to one of the other supported solutions: + - `autostart_systemd` + - `autostart_mac` + + If you experience any issues when trying to configure it, you can join `our discord server `__ and ask in the **support** channel for help. +#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): + + - Red 3.4.10 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. + - We've updated our `application.yml file `__ and you should update your instance's ``application.yml`` appropriately. + + +End-user changelog +------------------ + +Additions +********* + +- **Cogs - Streams** - In message template, ``{stream.display_name}`` can now be used to refer to streamer's display name (:issue:`5050`, :issue:`5066`) + + - This is not always the same as ``{stream}`` which refers to the streamer's channel or username + +Changes +******* + +- Rephrased a few strings and fixed maaaaany grammar issues and typos (:issue:`4793`, :issue:`4832`, :issue:`4955`, :issue:`4966`, :issue:`5015`, :issue:`5019`, :issue:`5029`, :issue:`5038`, :issue:`5055`, :issue:`5080`, :issue:`5081`) +- **Cogs - Admin** - The cog will now log when it leaves a guild due to the serverlock (:issue:`5008`, :issue:`5073`) +- **Cogs - Audio** - The ``[p]audiostats`` command can now only be used by bot owners (:issue:`5017`) +- **Cogs - Audio** - The cog will now check whether it has speak permissions in the channel before performing any actions (:issue:`5012`) +- **Cogs - Audio** - Improved logging in Audio cog (:issue:`5044`) +- **Cogs - Cleanup** - Clarified that ``[p]cleanup`` commands only delete the messages from the current channel (:issue:`5070`) +- **Cogs - Downloader** - ``[p]repo remove`` can now remove multiple repos at the same time (:issue:`4765`, :issue:`5082`) +- **Cogs - General** - The ``[p]urban`` command will now use the default embed color of the bot (:issue:`5014`) +- **Cogs - Modlog** - Modlog will no longer try editing the case's Discord message once it knows that it no longer exists (:issue:`4975`) +- **Cogs - Modlog** - ``[p]modlogset resetcases`` will now ask for confirmation before proceeding (:issue:`4976`) +- **Cogs - Streams** - - Improved logging of API errors in Streams cog (:issue:`4995`) + +Removals +******** + +- **Cogs - Streams** - Smashcast service has been closed and for that reason we have removed support for it from the cog (:issue:`5039`, :issue:`5040`) + +Fixes +***** + +- **Core - Bot Commands** - Added missing information about the ``showaliases`` setting in ``[p]helpset showsettings`` (:issue:`4971`) +- **Core - Bot Commands** - The help command no longer errors when it doesn't have permission to read message history and menus are enabled (:issue:`4959`, :issue:`5030`) +- **Core - Bot Commands** - Fixed a bug in ``[p]embedset user`` that made it impossible to reset the user's embed setting (:issue:`4962`) +- **Core - Bot Commands** - ``[p]embedset command`` and its subcommands now properly check whether any of the passed command's parents require Embed Links permission (:issue:`4962`) +- **Core - Bot Commands** - Fixed an issue with Red reloading unrelated modules when using ``[p]load`` and ``[p]reload`` (:issue:`4956`, :issue:`4958`) +- |cool| **Core - Command-line Interfaces** - Fixed terminal colors on Windows (:issue:`5063`) +- **Core - Command-line Interfaces** - Fixed the ``--rich-traceback-extra-lines`` flag (:issue:`5028`) +- **Cogs - Audio** - Fixed an issue that made it possible to remove Aikaterna's curated tracks playlist (:issue:`5018`) +- |cool| **Cogs - Audio** - Fixed auto-resume of auto play after Lavalink restart (:issue:`5051`) +- **Cogs - Audio** - Fixed an error with ``[p]audiostats`` caused by players not always having their connection time stored (:issue:`5046`) +- **Cogs - Audio** - Fixed track resuming in a certain edge case (:issue:`4996`) +- **Cogs - Audio** - Fixed an error in ``[p]audioset restart`` (:issue:`4987`) +- **Cogs - Audio** - Fixed an issue with Audio failing when it's missing permissions to send a message in the notification channel (:issue:`4960`) +- |cool| **Cogs - Audio** - Fixed fetching of age-restricted tracks (:issue:`5085`) +- **Cogs - Audio** - Fixed an issue with SoundCloud URLs that ended with a slash (``/``) character (:issue:`5085`) +- **Cogs - CustomCommands** - ``[p]customcom create simple`` no longer errors for a few specific names (:issue:`5026`, :issue:`5027`) +- **Cogs - Downloader** - ``[p]cog install`` now properly shows the repo name rather than ``{repo.name}`` (:issue:`4954`) +- **Cogs - Mod** - ``[p]mute`` no longer errors on muting a bot user if the ``senddm`` option is enabled (:issue:`5071`) +- **Cogs - Mutes** - Forbidden errors during the channel mute are now handled properly in a rare edge case (:issue:`4994`) +- |cool| **Cogs - Streams** - Fixed Picarto support (:issue:`4969`, :issue:`4970`) +- **Cogs - Streams** - ``[p]twitchstream``, ``[p]youtubestream``, and ``[p]picarto`` commands can no longer be run in DMs (:issue:`5036`, :issue:`5035`) +- |cool| **Cogs - Streams** - Fixed Twitch stream alerts for streams that use localized display names (:issue:`5050`, :issue:`5066`) +- **Cogs - Streams** - The cog no longer errors when trying to delete a cached message from a channel that no longer exists (:issue:`5032`, :issue:`5031`) +- **Cogs - Warnings** - The warn action is now taken *after* sending the warn message to the member (:issue:`4713`, :issue:`5004`) + + +Developer changelog +------------------- + +Changes +******* + +- **Core - Dependencies** - Bumped discord.py to 1.7.2 (:issue:`5066`) +- **Cogs - Dev** - ``[p]eval``, ``[p]repl``, and ``[p]debug`` commands now, in addition to ``py``, support code blocks with ``python`` syntax (:issue:`5083`) + +Fixes +***** + +- **Core - Command-line Interfaces** - The log messages shown by the global error handler will now show the trace properly for task done callbacks (:issue:`4980`) +- **Cogs - Dev** - ``[p]eval``, ``[p]repl``, and ``[p]debug`` commands no longer fail to send very long syntax errors (:issue:`5041`) + + +Documentation changes +--------------------- + +Additions +********* + +- |cool| Added `a guide for making auto-restart service on Mac ` (:issue:`4082`, :issue:`5020`) +- |cool| Added `cog guide for core commands ` (:issue:`1734`, :issue:`4597`) +- |cool| Added `cog guide for Mod cog ` (:issue:`1734`, :issue:`4886`) +- |cool| Added `cog guide for Modlog cog ` (:issue:`1734`, :issue:`4919`) +- |cool| Added `cog guide for Mutes cog ` (:issue:`1734`, :issue:`4875`) +- |cool| Added `cog guide for Permissions cog ` (:issue:`1734`, :issue:`4985`) +- |cool| Added `cog guide for Reports cog ` (:issue:`1734`, :issue:`4882`) +- |cool| Added `cog guide for Warnings cog ` (:issue:`1734`, :issue:`4920`) +- |cool| Added :ref:`a guide about Trivia list creation ` (:issue:`4595`, :issue:`5023`) +- Added the documentation for `redbot.core.modlog.Case` (:issue:`4979`) +- Added information on how to set the bot not to start on boot anymore to auto-restart docs (:issue:`5020`) + +Changes +******* + +- Updated Python version in ``pyenv`` and Windows instructions (:issue:`5025`) +- Cog creation guide now includes the ``bot`` as an argument to the cog class (:issue:`4988`) + +Removals +******** + +- Removed PM2 guide (:issue:`4991`) + +---- + +Redbot 3.4.9 (2021-04-06) +========================= + +This is a hotfix release fixing an issue with command error handling. + +discord.py version has been bumped to 1.7.1. + +Thanks again to :ghuser:`Rapptz` for quick response on this issue. + +---- + +Redbot 3.4.8 (2021-04-06) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`6days9weeks`, :ghuser:`aikaterna`, :ghuser:`Drapersniper`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`kingslayer268`, :ghuser:`Kowlin`, :ghuser:`Kreusada`, :ghuser:`Obi-Wan3`, :ghuser:`OofChair`, :ghuser:`palmtree5`, :ghuser:`phenom4n4n`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`rijusougata13`, :ghuser:`TheDiscordHistorian`, :ghuser:`Tobotimus`, :ghuser:`TrustyJAID`, :ghuser:`Twentysix26`, :ghuser:`Vexed01` + +Read before updating +-------------------- + +#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): + + Red 3.4.8 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. + +#. Fedora 31 and OpenSUSE Leap 15.1 are no longer supported as they have already reached end of life. + + +End-user changelog +------------------ + +Additions +********* + +- |cool| **Core** - Added per-command embed settings (:issue:`4049`) + + - See help of ``[p]embedset`` and ``[p]embedset command`` command group for more information +- **Core** - An error message will now be shown when a command that is only available in NSFW channels is used in a non-NSFW channel (:issue:`4933`) +- |cool| **Core - Bot Commands** - ``[p]leave`` accepts server IDs now (:issue:`4831`) +- |cool| **Cogs - Trivia** - Added a new option for hiding the answer to the Trivia answer in a spoiler (:issue:`4700`, :issue:`4877`) + + - ``[p]triviaset usespoilers`` command can be used to enable/disable this option + +Changes +******* + +- |cool| **Core - Bot Commands** - The ``[p]servers`` command uses menus now (:issue:`4720`, :issue:`4831`) +- |cool| **Core - Bot Commands** - Commands for listing global and local allowlists and blocklists will now, in addition to IDs, contain user/role names (:issue:`4839`) +- **Core - Bot Commands** - Added more singular and plural forms in a bunch of commands in the bot (:issue:`4004`, :issue:`4898`) +- |cool| **Core - Command-line Interfaces** - Added a progress bar to ``redbot-setup convert`` (:issue:`2952`) +- **Cogs - Audio** - Improved playlist extraction (:issue:`4932`) +- |cool| **Cogs - Cleanup** - ``[p]cleanup before`` and ``[p]cleanup after`` commands can now be used without a message ID if the invocation message replies to some message (:issue:`4790`) +- **Cogs - Filter** - Added meaningful error messages for incorrect arguments in the ``[p]bank set`` command (:issue:`4789`, :issue:`4801`) +- **Cogs - Mod** - Improved performance of checking tempban expirations (:issue:`4907`) +- **Cogs - Mutes** - Vastly improved performance of automatic unmute handling (:issue:`4906`) +- **Cogs - Streams** - Streams cog should now load faster on bots that have many stream alerts set up (:issue:`4731`, :issue:`4742`) +- **Cogs - Streams** - Checking Twitch streams will now make less API calls (:issue:`4938`) +- **Cogs - Streams** - Ratelimits from Twitch API are now properly handled (:issue:`4808`, :issue:`4883`) +- **Cogs - Warnings** - Embeds now use the default embed color of the bot (:issue:`4878`) + +Removals +******** + +- **Core - Command-line Interfaces** - Removed the option to drop the entire PostgreSQL database in ``redbot-setup delete`` due to limitations of PostgreSQL (:issue:`3699`, :issue:`3833`) + +Fixes +***** + +- |cool| **Core** - Messages sent interactively in DM channels no longer fail (:issue:`4876`) +- **Core - Help** - Fixed how the command signature is shown in help for subcommands that have group args (:issue:`4928`) +- **Cogs - Alias** - Fixed issues with command aliases for commands that take an arbitrary, but non-zero, number of arguments (e.g. ``[p]load``) (:issue:`4766`, :issue:`4871`) +- |cool| **Cogs - Audio** - Fixed stuttering (:issue:`4565`) +- |cool| **Cogs - Audio** - Fixed random disconnects (:issue:`4565`) +- |cool| **Cogs - Audio** - Fixed the issues causing the player to be stuck on 00:00 (:issue:`4565`) +- |cool| **Cogs - Audio** - Fixed ghost players (:issue:`4565`) +- |cool| **Cogs - Audio** - Audio will no longer stop playing after a while (:issue:`4565`) +- **Cogs - Audio** - Fixed playlist loading for playlists with over 100 songs (:issue:`4932`) +- **Cogs - Audio** - Fixed an issue with alerts causing errors in playlists being loaded (:issue:`4932`) +- **Cogs - Audio** - Fixed an issue with consent pages appearing while trying to load songs or playlists (:issue:`4932`) +- **Cogs - Downloader** - Improved compatibility with Git 2.31 and newer (:issue:`4897`) +- **Cogs - Mod** - Fixed tracking of nicknames that were set just before nick reset (:issue:`4830`) +- **Cogs - Streams** - Fixed possible memory leak related to automatic message deletion (:issue:`4731`, :issue:`4742`) +- **Cogs - Streams** - Streamer accounts that no longer exist are now properly handled (:issue:`4735`, :issue:`4746`) +- **Cogs - Streams** - Fixed stream alerts being sent even after unloading Streams cog (:issue:`4940`) +- **Cogs - Warnings** - Fixed output of ``[p]warnings`` command for members that are no longer in the server (:issue:`4900`, :issue:`4904`) + + +Developer changelog +------------------- + +Changes +******* + +- **Core - Dependencies** - Bumped discord.py version to 1.7.0 (:issue:`4928`) + +Deprecations +************ + +- **Core - Bot Class** - Added ``guild`` parameter to `bot.allowed_by_whitelist_blacklist() ` which is meant to replace the deprecated ``guild_id`` parameter (:issue:`4905`, :issue:`4914`) + + - Read the method's documentation for more information +- **Core - Commands Package** - Deprecated importing ``GuildConverter`` from ``redbot.core.commands.converter`` namespace (:issue:`4928`) + + - ``discord.Guild`` or ``GuildConverter`` from ``redbot.core.commands`` should be used instead + +Fixes +***** + +- **Core - Bot Class** - Fixed ``on_red_api_tokens_update`` not being dispatched when the tokens were removed with ``[p]set api remove`` (:issue:`4916`, :issue:`4917`) + + +Documentation changes +--------------------- + +Additions +********* + +- |cool| Added `cog guide for Image cog ` (:issue:`4821`) + +Changes +******* + +- Added a note about updating cogs in update message and documentation (:issue:`4910`) +- `getting-started` now contains an explanation of parameters that can take an arbitrary number of arguments (:issue:`4888`, :issue:`4889`) +- All shell commands in the documentation are now prefixed with an unselectable prompt (:issue:`4908`) +- `systemd-service-guide` now asks the user to create the new service file using ``nano`` text editor (:issue:`4869`, :issue:`4870`) + + - Instructions for all Linux-based operating systems now recommend to install ``nano`` +- Updated Python version in ``pyenv`` and Windows instructions (:issue:`4864`, :issue:`4942`) +- Added a warning to Arch Linux install guide about the instructions being out-of-date (:issue:`4866`) + +Fixes +***** + +- Updated Mac install guide with new ``brew`` commands (:issue:`4865`) + +---- + +Redbot 3.4.7 (2021-02-26) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`elijabesu`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`kreusada`, :ghuser:`palmtree5`, :ghuser:`TrustyJAID` + +End-user changelog +------------------ + +Security +******** + +- **Cogs - Mutes** - Added proper permission checks to ``[p]muteset senddm`` and ``[p]muteset showmoderator`` (:issue:`4849`) + +Changes +******* + +- **Core - Bot Commands** - Updated the ``[p]info`` command to more clearly indicate that the instance is owned by a team (:issue:`4851`) + +Fixes +***** + +- **Cogs - General** - Updated the ``[p]lmgtfy`` command to use the new domain (:issue:`4840`) +- **Cogs - Mutes** - Fixed minor issues with error messages in Mutes cog (:issue:`4847`, :issue:`4850`, :issue:`4853`) + + +Documentation changes +--------------------- + +Additions +********* + +- |cool| Added `cog guide for General cog ` (:issue:`4797`) +- |cool| Added `cog guide for Trivia cog ` (:issue:`4566`) + +---- + +Redbot 3.4.6 (2021-02-16) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`aleclol`, :ghuser:`Andeeeee`, :ghuser:`bobloy`, :ghuser:`BreezeQS`, :ghuser:`Danstr5544`, :ghuser:`Dav-Git`, :ghuser:`Elysweyr`, :ghuser:`Fabian-Evolved`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`Injabie3`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`kreusada`, :ghuser:`leblancg`, :ghuser:`maxbooiii`, :ghuser:`NeuroAssassin`, :ghuser:`phenom4n4n`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`retke`, :ghuser:`siu3334`, :ghuser:`Strafee`, :ghuser:`TheWyn`, :ghuser:`TrustyJAID`, :ghuser:`Vexed01`, :ghuser:`yamikaitou` + +Read before updating +-------------------- + +#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): + + Red 3.4.6 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. + + +End-user changelog +------------------ + +Security +******** + +- **Cogs - Mutes** - Added more role hierarchy checks to ensure permission escalations cannot occur on servers with a careless configuration (:issue:`4741`) + +Additions +********* + +- |cool| **Core - Help** - Help now includes command aliases in the command help (:issue:`3040`) + + - This can be disabled with ``[p]helpset showaliases`` command +- **Cogs - Mod** - Added two new settings for disabling username and nickname tracking (:issue:`4799`) + + - Added a command ``[p]modset trackallnames`` that disables username tracking and overrides the nickname tracking setting for all guilds + - Added a command ``[p]modset tracknicknames`` that disables nickname tracking in a specific guild +- **Cogs - Mod** - Added a command ``[p]modset deletenames`` that deletes all stored usernames and nicknames (:issue:`4827`) +- **Cogs - Modlog** - Added a command ``[p]listcases`` that allows you to see multiple cases for a user at once (:issue:`4426`) +- |cool| **Cogs - Mutes** - A DM can now be sent to the (un)muted user on mute and unmute (:issue:`3752`, :issue:`4563`) + + - Added ``[p]muteset senddm`` to set whether the DM should be sent (function disabled by default) + - Added ``[p]muteset showmoderator`` to set whether the DM sent to the user should include the name of the moderator that muted the user (function disabled by default) +- **Cogs - Trivia - Lists** - Added new Who's That Pokémon - Gen. VI trivia list (:issue:`4785`) + +Changes +******* + +- **Core - Bot Commands** - Added a friendly error message to ``[p]load`` that is shown when trying to load a cog with a command name that is already taken by a different cog (:issue:`3870`) +- |cool| **Core - Command-line Interfaces** - Improvements and fixes for our new (colorful) logging (:issue:`4702`, :issue:`4726`) + + - The colors used have been adjusted to be readable on many more terminal applications + - The ``NO_COLOR`` environment variable can now be set to forcefully disable all colors in the console output + - Tracebacks will now use the full width of the terminal again + - Tracebacks no longer contain multiple lines per stack level (this can now be changed with the flag ``--rich-traceback-extra-lines``) + - Disabled syntax highlighting on the log messages + - Dev cog no longer captures logging output + - Added some cool features for developers + + - Added the flag ``--rich-traceback-extra-lines`` which can be used to set the number of additional lines in tracebacks + - Added the flag ``--rich-traceback-show-locals`` which enables showing local variables in tracebacks + + - Improved and fixed a few other minor things +- **Core - Dependencies** - Red's dependencies have been bumped (:issue:`4572`) +- **Cogs - Admin** - ``[p]selfrole`` can now be used without a subcommand and passed with a selfrole directly to add/remove it from the user running the command (:issue:`4826`) +- **Cogs - Audio** - Improved detection of embed players for fallback on age-restricted YT tracks (:issue:`4818`, :issue:`4819`) +- **Cogs - Audio** - Improved MP4/AAC decoding (:issue:`4818`, :issue:`4819`) +- **Cogs - Audio** - Requests for YT tracks are now retried if the initial request causes a connection reset (:issue:`4818`, :issue:`4819`) +- **Cogs - Cleanup** - Renamed the ``[p]cleanup spam`` command to ``[p]cleanup duplicates``, with the old name kept as an alias for the time being (:issue:`4814`) +- **Cogs - Economy** - ``[p]economyset rolepaydayamount`` can now remove the previously set payday amount (:issue:`4661`, :issue:`4758`) +- **Cogs - Filter** - Added a case type ``filterhit`` which is used to log filter hits (:issue:`4676`, :issue:`4739`) +- **Cogs - Mod** - Added usage examples to ``[p]kick``, ``[p]ban``, ``[p]massban``, and ``[p]tempban`` (:issue:`4712`, :issue:`4715`) +- **Cogs - Mod** - Updated DM on kick/ban to use bot's default embed color (:issue:`4822`) +- **Cogs - Modlog** - Added typing indicator to ``[p]casesfor`` command (:issue:`4426`) +- **Cogs - Reports** - Reports now use the default embed color of the bot (:issue:`4800`) +- **Cogs - Trivia** - Payout for trivia sessions ending in a tie now gets split between all the players with the highest score (:issue:`3931`, :issue:`4649`) +- **Cogs - Trivia - Lists** - Updated answers regarding some of the hero's health and abilities in the ``overwatch`` trivia list (:issue:`4805`) + +Fixes +***** + +- Various grammar fixes (:issue:`4705`, :issue:`4748`, :issue:`4750`, :issue:`4763`, :issue:`4788`, :issue:`4792`, :issue:`4810`) +- **Core - Bot Commands** - Fixed command usage in the help messages for few commands in Red (:issue:`4599`, :issue:`4733`) +- **Core - Bot Commands** - Fixed errors in ``[p]command defaultdisablecog`` and ``[p]command defaultenablecog`` commands (:issue:`4767`, :issue:`4768`) +- **Core - Bot Commands** - ``[p]command listdisabled guild`` can no longer be run in DMs (:issue:`4771`, :issue:`4772`) +- |cool| **Core - Command-line Interfaces** - Fixed the rotation of Red's logs that could before result in big disk usage (:issue:`4405`, :issue:`4738`) +- **Core - Command-line Interfaces** - Fixed errors appearing when using Ctrl+C to interrupt ``redbot --edit`` (:issue:`3777`, :issue:`4572`) +- **Cogs - Cleanup** - Fixed an error from passing an overly large integer as a message ID to ``[p]cleanup after`` and ``[p]cleanup before`` (:issue:`4791`) +- **Cogs - Mod** - The ``[p]tempban`` command no longer errors out when trying to ban a user in a guild with the vanity url feature that doesn't have a vanity url set (:issue:`4714`) +- **Cogs - Mod** - Fixed an edge case in role hierarchy checks (:issue:`4740`) +- **Cogs - Mutes** - Fixed an edge case in role hierarchy checks (:issue:`4740`) +- **Cogs - Mutes** - The modlog reason no longer contains leading whitespace when it's passed *after* the mute time (:issue:`4749`) +- **Cogs - Mutes** - Help descriptions of the cog and its commands now get translated properly (:issue:`4815`) +- **Cogs - Streams** - Fixed incorrect timezone offsets for some YouTube stream schedules (:issue:`4693`, :issue:`4694`) +- **Cogs - Streams** - Fixed meaningless errors happening when the YouTube API key becomes invalid or when the YouTube quota is exceeded (:issue:`4745`) + + +Developer changelog +------------------- + +Additions +********* + +- **Core - Bot Class** - Added an event ``on_red_before_identify`` that is dispatched before IDENTIFYing a session (:issue:`4647`) +- **Core - Utils Package** - Added a function `redbot.core.utils.chat_formatting.spoiler()` that wraps the given text in a spoiler (:issue:`4754`) +- |cool| **Cogs - Dev** - Cogs can now add their own variables to the environment of ``[p]debug``, ``[p]eval``, and ``[p]repl`` commands (:issue:`4667`) + + - Variables can be added and removed from the environment of Dev cog using two new methods: + + - `bot.add_dev_env_value() ` + - `bot.remove_dev_env_value() ` + +Changes +******* + +- **Core - Dependencies** - Updated versions of the libraries used in Red: discord.py to 1.6.0, aiohttp to 3.7.3 (:issue:`4728`) + +Fixes +***** + +- **Cogs - Dev** - Help descriptions of the cog and its commands now get translated properly (:issue:`4815`) + +Documentation changes +--------------------- + +Additions +********* + +- |cool| Added `cog guide for Filter cog ` (:issue:`4579`) + +Changes +******* + +- Added information about the Red Index to `guide_publish_cogs` (:issue:`4778`) +- Restructured the host list (:issue:`4710`) +- Clarified how to use pm2 with ``pyenv virtualenv`` (:issue:`4709`) +- Updated Python version in ``pyenv`` and Windows instructions (:issue:`4770`) + +Fixes +***** + +- Updated the pip command for Red with the postgres extra in Linux/macOS install guide to work on zsh shell (:issue:`4697`) + +---- + +Redbot 3.4.5 (2020-12-24) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`Injabie3`, :ghuser:`NeuroAssassin` + +This is a hotfix release fixing an issue with Streams cog failing to load. + +End-user changelog +------------------ + +Fixes +***** + +- **Cogs - Streams** - Fixed Streams failing to load and work properly (:issue:`4687`, :issue:`4688`) + +---- + +Redbot 3.4.4 (2020-12-24) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`bobloy`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`kreus7`, :ghuser:`NeuroAssassin`, :ghuser:`npc203`, :ghuser:`palmtree5`, :ghuser:`phenom4n4n`, :ghuser:`Predeactor`, :ghuser:`retke`, :ghuser:`siu3334`, :ghuser:`Vexed01`, :ghuser:`yamikaitou` + +Read before updating +-------------------- + +#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): + + Red 3.4.4 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. + +#. Ubuntu 16.04 is no longer supported as it will soon reach its end of life and it is no longer viable for us to maintain support for it. + + While you might still be able to run Red on it, we will no longer put any resources into supporting it. If you're using Ubuntu 16.04, we highly recommend that you upgrade to the latest LTS version of Ubuntu. + + +End-user changelog +------------------ + +Additions +********* + +- |cool| **Core - Command-line Interfaces** - Red's logging will now shine in your terminal more than ever (:issue:`4577`) +- |cool| **Cogs - Streams** - YouTube stream schedules are now announced before the stream (:issue:`4615`) + + - Alerts about YouTube stream schedules can be disabled with a new ``[p]streamset ignoreschedule`` command (:issue:`4615`) +- **Cogs - Trivia - Lists** - Added ``whosthatpokemon5`` trivia list containing Pokémon from the 5th generation (:issue:`4646`) +- **Cogs - Trivia - Lists** - Added ``geography`` trivia list (:issue:`4618`) + +Changes +******* + +- **Core** - Added a friendly error when the duration provided to commands that use the ``commands.TimedeltaConverter`` converter is out of the maximum bounds allowed by Python interpreter (:issue:`4019`, :issue:`4628`, :issue:`4630`) +- **Core - Bot Commands** - Improved consistency of command usage in the help messages within all commands in Core Red (:issue:`4589`) +- **Cogs - Audio** - Added more friendly messages for 429 errors to let users know they have been temporarily banned from accessing the service instead of a generic Lavalink error (:issue:`4683`) +- **Cogs - Audio** - Environment information will now be appended to Lavalink tracebacks in the spring.log (:issue:`4683`) +- **Cogs - Cleanup** - ``[p]cleanup self`` will now delete the command message when the bot has permissions to do so (:issue:`4640`) +- **Cogs - Economy** - ``[p]economyset slotmin`` and ``[p]economyset slotmax`` now warn when the new value will cause the slots command to not work (:issue:`4583`) +- **Cogs - General** - Updated features list in ``[p]serverinfo`` with the latest changes from Discord (:issue:`4678`) +- **Cogs - Streams** - Improved error logging (:issue:`4680`) + +Fixes +***** + +- **Core - Bot Commands** - Fixed an error when removing path from a different operating system than the bot is currently running on with ``[p]removepath`` (:issue:`2609`, :issue:`4662`, :issue:`4466`) +- **Cogs - Audio** - Fixed ``[p]llset java`` failing to set the Java executable path (:issue:`4621`, :issue:`4624`) +- |cool| **Cogs - Audio** - Fixed SoundCloud playback (:issue:`4683`) +- |cool| **Cogs - Audio** - Fixed YouTube age-restricted track playback (:issue:`4683`) +- **Cogs - Mod** - ``[p]ban`` command will no longer error out when the given reason is too long (:issue:`4187`, :issue:`4189`) +- |cool| **Cogs - Streams** - Scheduled YouTube streams now work properly with the cog (:issue:`3691`, :issue:`4615`) + + +Developer changelog +------------------- + +Additions +********* + +- **Core - Utils Package** - `get_audit_reason()` can now be passed a ``shorten`` keyword argument which will automatically shorten the returned audit reason to fit the max length allowed by Discord audit logs (:issue:`4189`) +- |cool| **Cogs - Dev** - Added new ``[p]bypasscooldown`` command that allows owners to bypass command cooldowns (:issue:`4440`) + +Changes +******* + +- **Core - Bot Class** - ``bot.remove_command()`` now returns the command object of the removed command as does the equivalent method from `discord.ext.commands.Bot` class (:issue:`4636`) + + +Documentation changes +--------------------- + +Additions +********* + +- |cool| Added `cog guide for Downloader cog ` (:issue:`4511`) +- |cool| Added `cog guide for Economy cog ` (:issue:`4519`) +- |cool| Added `cog guide for Streams cog ` (:issue:`4521`) +- Added `guide_cog_creators` document (:issue:`4637`) + +Removals +******** + +- Removed install instructions for Ubuntu 16.04 (:issue:`4650`) + +---- + +Redbot 3.4.3 (2020-11-16) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`bobloy`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`KianBral`, :ghuser:`maxbooiii`, :ghuser:`phenom4n4n`, :ghuser:`Predeactor`, :ghuser:`retke` + +Read before updating +-------------------- + +#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): + + Red 3.4.3 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. + +End-user changelog +------------------ + +Additions +********* + +- **Core - Bot Commands** - Added ``[p]set competing`` command that allows users to set the bot's competing status (:issue:`4607`, :issue:`4609`) +- |cool| **Cogs - Audio** - Added support for SoundCloud HLS streams (:issue:`4608`) + +Changes +******* + +- **Cogs - Audio** - Improved AAC audio handling (:issue:`4608`) +- **Cogs - Trivia** - ``[p]triviaset custom upload`` now ensures that the filename is lowercase when uploading (:issue:`4594`) + +Fixes +***** + +- **Cogs - Audio** - Volume changes on ARM systems running a 64 bit OS will now work again (:issue:`4608`) +- |cool| **Cogs - Audio** - Fixed only 100 results being returned on a Youtube playlist (:issue:`4608`) +- |cool| **Cogs - Audio** - Fixed YouTube VOD duration being set to unknown (:issue:`3885`, :issue:`4608`) +- |cool| **Cogs - Audio** - Fixed some YouTube livestreams getting stuck (:issue:`4608`) +- **Cogs - Audio** - Fixed internal Lavalink manager failing for Java with untypical version formats (:issue:`4608`) +- **Cogs - Economy** - The ``[p]leaderboard`` command no longer fails in DMs when a global bank is used (:issue:`4569`) +- **Cogs - Mod** - The ban reason is now properly set in the audit log and modlog when using the ``[p]massban`` command (:issue:`4575`) +- **Cogs - Mod** - The ``[p]userinfo`` command now shows the new Competing activity (:issue:`4610`, :issue:`4611`) +- **Cogs - Modlog** - The ``[p]case`` and ``[p]casesfor`` commands no longer fail when the bot doesn't have Read Message History permission in the modlog channel (:issue:`4587`, :issue:`4588`) +- **Cogs - Mutes** - Fixed automatic remuting on member join for indefinite mutes (:issue:`4568`) + + +Developer changelog +------------------- + +Fixes +***** + +- **Core - Modlog** - ``modlog.get_case()`` and methods using it no longer raise when the bot doesn't have Read Message History permission in the modlog channel (:issue:`4587`, :issue:`4588`) + + +Documentation changes +--------------------- + +Additions +********* + +- |cool| Added `guide for Cog Manager UI ` (:issue:`4152`) +- |cool| Added `cog guide for CustomCommands cog ` (:issue:`4490`) + +---- + +Redbot 3.4.2 (2020-10-28) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`Drapersniper`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`PredaaA`, :ghuser:`Stonedestroyer` + +Read before updating +-------------------- + +#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): + + Red 3.4.2 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. + +End-user changelog +------------------ + +Changes +******* + +- **Core - Command-line Interfaces** - Added info about the metadata file to ``redbot --debuginfo`` (:issue:`4557`) +- **Cogs - Audio** - Commands in ``[p]llset`` group can now be used in DMs (:issue:`4562`) +- **Cogs - Streams** - Added error messages when exceeding the YouTube quota in the Streams cog (:issue:`4552`) +- **Cogs - Streams** - Improved logging for unexpected errors in the Streams cog (:issue:`4552`) + +Fixes +***** + +- **Cogs - Audio** - Fixed the ``[p]local search`` command (:issue:`4553`) +- |cool| **Cogs - Audio** - Fixed random "Something broke when playing the track." errors for YouTube tracks (:issue:`4559`) +- **Cogs - Mod** - Fixed ``[p]massban`` not working for banning members that are in the server (:issue:`4556`, :issue:`4555`) + + +Documentation changes +--------------------- + +Additions +********* + +- |cool| Added `cog guide for Cleanup cog ` (:issue:`4488`) + +Changes +******* + +- Removed multi-line commands from Linux install guides to avoid confusing readers (:issue:`4550`) + +---- + +Redbot 3.4.1 (2020-10-27) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`absj30`, :ghuser:`aikaterna`, :ghuser:`bobloy`, :ghuser:`chloecormier`, :ghuser:`Dav-Git`, :ghuser:`Drapersniper`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`Generaleoley`, :ghuser:`hisztendahl`, :ghuser:`jack1142`, :ghuser:`KaiGucci`, :ghuser:`Kowlin`, :ghuser:`maxbooiii`, :ghuser:`MeatyChunks`, :ghuser:`NeuroAssassin`, :ghuser:`nfitzen`, :ghuser:`palmtree5`, :ghuser:`phenom4n4n`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`PythonTryHard`, :ghuser:`SharkyTheKing`, :ghuser:`Stonedestroyer`, :ghuser:`thisisjvgrace`, :ghuser:`TrustyJAID`, :ghuser:`TurnrDev`, :ghuser:`Vexed01`, :ghuser:`Vuks69`, :ghuser:`xBlynd`, :ghuser:`zephyrkul` + +Read before updating +-------------------- + +#. This release fixes a security issue in Mod cog. See `Security changelog below ` for more information. +#. This Red update bumps discord.py to version 1.5.1, which explicitly requests Discord intents. Red requires all Privileged Intents to be enabled. More information can be found at :ref:`enabling-privileged-intents`. +#. Mutes functionality has been moved from the Mod cog to a new separate cog (Mutes) featuring timed and role-based mutes. If you were using it (or want to start now), you can load the new cog with ``[p]load mutes``. You can see the full `Removals changelog below `. +#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): + + We've updated our `application.yml file `__ and you should update your instance's ``application.yml`` appropriately. + Please ensure that the WS port in Audio's settings (``[p]llset wsport``) is set to the port from the ``application.yml``. + +End-user changelog +------------------ + +.. _important-341-2: + +Security +******** + +**NOTE:** If you can't update immediately, we recommend globally disabling the affected command until you can. + +- **Cogs - Mod** - Fixed unauthorized privilege escalation exploit in ``[p]massban`` (also called ``[p]hackban``) command. Full security advisory `can be found on our GitHub `__. + +Additions +********* + +- **Core - Bot Commands** - Added ``[p]set api list`` to list all currently set API services, without tokens (:issue:`4370`) +- **Core - Bot Commands** - Added ``[p]set api remove`` to remove API services, including tokens (:issue:`4370`) +- **Core - Bot Commands** - Added ``[p]helpset usetick``, toggling command message being ticked when help is sent to DM (:issue:`4467`, :issue:`4075`) +- |cool| **Core - i18n** - Locales and regional formats can now be set in individual guilds using ``[p]set locale`` and ``[p]set regionalformat`` (:issue:`3896`, :issue:`1970`) + + - Global locale and regional format setters have been renamed to ``[p]set globallocale`` and ``[p]set globalregionalformat`` +- **Cogs - Audio** - Added the Global Audio API, to cut down on Youtube 429 errors and allow Spotify playback past user's quota. (:issue:`4446`) +- |cool| **Cogs - Audio** - Added persistent queues, allowing for queues to be restored on a bot restart or cog reload (:issue:`4446`) +- **Cogs - Audio** - Added ``[p]audioset restart``, allowing for Lavalink connection to be restarted (:issue:`4446`) +- |cool| **Cogs - Audio** - Added ``[p]audioset autodeafen``, allowing for bot to auto-deafen itself when entering voice channel (:issue:`4446`) +- **Cogs - Audio** - Added ``[p]audioset mycountrycode``, allowing Spotify search locale per user (:issue:`4446`) +- **Cogs - Audio** - Added ``[p]llset java``, allowing for a custom Java executable path (:issue:`4446`) +- **Cogs - Audio** - Added ``[p]llset info`` to show Lavalink settings (:issue:`4527`) +- **Cogs - Audio** - Added ``[p]audioset logs`` to download Lavalink logs if the Lavalink server is set to internal (:issue:`4527`) +- **Cogs - Mod** - Added ``[p]modset mentionspam strict`` allowing for duplicated mentions to count towards the mention spam cap (:issue:`4359`) +- |cool| **Cogs - Mod** - Added a default tempban duration for ``[p]tempban`` (:issue:`4473`, :issue:`3992`) +- |cool| **Cogs - Mutes** - Added ``[p]muteset forcerole`` to make mutes role based, instead of permission based (:issue:`3634`) +- |cool| **Cogs - Mutes** - Added an optional time argument to all mutes, to specify when the user should be unmuted (:issue:`3634`) +- **Cogs - Trivia - Lists** - Added new MLB trivia list (:issue:`4455`) +- **Cogs - Trivia - Lists** - Added new Who's That Pokémon - Gen. IV trivia list (:issue:`4434`) +- **Cogs - Trivia - Lists** - Added new Hockey trivia list (:issue:`4384`) + +Changes +******* + +- Replaced a few instances of Red with the bot name in command docstrings (:issue:`4470`) +- **Core - Bot Commands** - Added a default color field to ``[p]set showsettings`` (:issue:`4498`, :issue:`4497`) +- **Core - Bot Commands** - Added the datapath and metadata file to ``[p]debuginfo`` (:issue:`4524`) +- **Core - Bot Commands** - Added a list of disabled intents to ``[p]debuginfo`` (:issue:`4423`) +- **Core - Dependencies** - Bumped discord.py dependency to version 1.5.1 (:issue:`4423`) +- **Cogs - Audio** - Removed lavalink logs from being added to backup (:issue:`4453`, :issue:`4452`) +- **Cogs - Audio** - Removed stream durations from being in queue duration (:issue:`4513`) +- **Cogs - Cleanup** - Allowed ``[p]cleanup self`` to work in DMs for all users (:issue:`4481`) +- **Cogs - Economy** - Added an embed option for ``[p]leaderboard`` (:issue:`4184`, :issue:`4104`) +- |cool| **Cogs - Mod** - Added an option to ban users not in the guild to ``[p]ban`` (:issue:`4422`, :issue:`4419`) +- **Cogs - Mod** - Renamed ``[p]hackban`` to ``[p]massban``, keeping ``[p]hackban`` as an alias, allowing for multiple users to be banned at once (:issue:`4422`, :issue:`4419`) +- **Cogs - Mutes** - Changed ``[p]mute`` to only handle serverwide muting, ``[p]mute voice`` and ``[p]mute channel`` have been moved to separate commands called ``[p]mutechannel`` and ``[p]mutevoice`` (:issue:`3634`) +- |cool| **Cogs - Mutes** - Mute commands can now take multiple user arguments, to mute multiple users at a time (:issue:`3634`) +- **Cogs - Warnings** - Added bool arguments to toggle commands to improve consistency (:issue:`4409`) + +.. _important-341-1: + +Removals +******** + +- **Cogs - Mod** - Moved mutes to a separate, individual cog (:issue:`3634`) + +Fixes +***** + +- Fixed grammar in places scattered throughout bot (:issue:`4500`) +- **Core** - Fixed an ungraceful error being raised when a bot left a guild while a menu was open (:issue:`3902`) +- **Core - Bot Commands** - Fixed an incorrect error being reported on ``[p]set name`` when the passed name was longer than 32 characters (:issue:`4364`, :issue:`4363`) +- **Core - Bot Commands** - Fixed ``[p]set nickname`` erroring when the passed name was longer than 32 characters (:issue:`4364`, :issue:`4363`) +- **Core - Bot Commands** - Fixed an ungraceful error being raised when running ``[p]traceback`` with closed DMs (:issue:`4329`) +- **Core - Bot Commands** - Fixed errors that could arise from invalid URLs in ``[p]set avatar`` (:issue:`4437`) +- **Core - Bot Commands** - Fixed an error being raised with ``[p]set nickname`` when no nickname was provided (:issue:`4451`) +- **Core - Bot Commands** - Fixed and clarified errors being raised with ``[p]set username`` (:issue:`4463`) +- **Core - Bot Commands** - Fixed an ungraceful error being raised when the output of ``[p]unload`` is larger than 2k characters (:issue:`4469`) +- **Core - Bot Commands** - Fixed an ungraceful error being raised when running ``[p]choose`` with empty options (:issue:`4499`) +- **Core - Bot Commands** - Fixed info missing on the non-embed version of ``[p]debuginfo`` (:issue:`4524`) +- **Core - Dependencies** - Properly define supported Python versions to be lower than 3.9 (:issue:`4538`) +- **Cogs - Audio** - Scattered grammar and typo fixes (:issue:`4446`) +- |cool| **Cogs - Audio** - Fixed Bandcamp playback (:issue:`4504`) +- |cool| **Cogs - Audio** - Fixed YouTube playlist playback (:issue:`4504`) +- |cool| **Cogs - Audio** - Fixed YouTube searching issues (:issue:`4504`) +- |cool| **Cogs - Audio** - Fixed YouTube age restricted track playback (:issue:`4504`) +- **Cogs - Audio** - Fixed the Audio cog not being translated when setting locale (:issue:`4492`, :issue:`4495`) +- |cool| **Cogs - Audio** - Fixed tracks getting stuck at 0:00 after long player sessions (:issue:`4529`) +- **Cogs - CustomCommands** - Fixed an ungraceful error being thrown on ``[p]cc edit`` (:issue:`4325`) +- **Cogs - General** - Fixed issues with text not being properly URL encoded (:issue:`4024`) +- **Cogs - General** - Fixed an ungraceful error occurring when a title is longer than 256 characters in ``[p]urban`` (:issue:`4474`) +- **Cogs - General** - Changed "boosters" to "boosts" in ``[p]serverinfo`` to clarify what the number represents (:issue:`4507`) +- **Cogs - Mod** - Fixed nicknames not being properly stored and logged (:issue:`4131`) +- **Cogs - Mod** - Fixed plural typos in ``[p]userinfo`` (:issue:`4397`, :issue:`4379`) +- **Cogs - Modlog** - Fixed an error being raised when running ``[p]casesfor`` and ``[p]case`` (:issue:`4415`) +- **Cogs - Modlog** - Long reasons in Modlog are now properly shortened in message content (:issue:`4541`) +- **Cogs - Trivia - Lists** - Fixed incorrect order of Machamp and Machoke questions (:issue:`4424`) +- **Cogs - Warnings** - Fixed users being able to warn users above them in hierarchy (:issue:`4100`) + + +Developer changelog +------------------- + +| **Important:** +| 1. Red now allows users to set locale per guild, which requires 3rd-party cogs to set contextual locale manually in code ran outside of command's context. See the `Additions changelog below ` for more information. + +.. _important-dev-341-1: + +Additions +********* + +- **Core - Bot Class** - Added `bot.get_or_fetch_user() ` and `bot.get_or_fetch_member() ` methods (:issue:`4403`, :issue:`4402`) +- **Core - Bot Class** - Added `bot.remove_shared_api_services() ` to remove all keys and tokens associated with an API service (:issue:`4370`) +- **Core - Bot Class** - Added an option to return all tokens for an API service if ``service_name`` is not specified in `bot.get_shared_api_tokens() ` (:issue:`4370`) +- **Core - Dependencies** - Added ``[all]`` and ``[dev]`` extras to the ``Red-DiscordBot`` package (:issue:`4443`) +- |cool| **Core - i18n** - Added API for setting contextual locales (:issue:`3896`, :issue:`1970`) + + - New function added: `redbot.core.i18n.set_contextual_locales_from_guild()` + - Contextual locale is automatically set for commands and only needs to be done manually for things like event listeners; see `recommendations-for-cog-creators` for more information +- **Core - Modlog** - Added ``last_known_username`` parameter to `modlog.create_case()` function (:issue:`4326`) +- |cool| **Core - Utils Package** - Added `redbot.core.utils.get_end_user_data_statement()` and `redbot.core.utils.get_end_user_data_statement_or_raise()` to attempt to fetch a cog's End User Data Statement (:issue:`4404`) +- **Core - Utils Package** - Added `redbot.core.utils.chat_formatting.quote()` to quote text in a message (:issue:`4425`) +- |cool| **Cogs - Dev** - Added ``[p]repl pause`` to pause/resume the REPL session in the current channel (:issue:`4366`) +- |cool| **Cogs - Downloader** - Added JSON schema files for ``info.json`` files (:issue:`4375`) + +Changes +******* + +- **Core - Bank** - Bank API methods now consistently throw TypeError if a non-integer amount is supplied (:issue:`4376`) +- **Core - Commands Package** - Moved ``redbot.core.checks.bot_in_a_guild()`` to `redbot.core.commands.bot_in_a_guild()` (old name has been left as an alias) (:issue:`4515`, :issue:`4510`) +- |cool| **Core - Modlog** - Added an option to accept a ``discord.Object`` in `modlog.create_case()` (:issue:`4326`) + +Deprecations +************ + +- **Core - Utils Package** - Deprecated ``redbot.core.utils.mod.is_allowed_by_hierarchy()`` (:issue:`4435`) + +Fixes +***** + +- **Core - Modlog** - Fixed an error being raised with a deleted channel in `Case.message_content()` (:issue:`4415`) + + +Documentation changes +--------------------- + +Additions +********* + +- Added custom group documentation and tutorial (:issue:`4416`, :issue:`2896`) +- Added guide to creating a Bot Application in Discord Developer Portal, with enabling intents (:issue:`4502`) + +Changes +******* + +- Clarified that naive ``datetime`` objects will be treated as local times for parameters ``created_at`` and ``until`` in `modlog.create_case()` (:issue:`4389`) +- Replaced the link to the approved repository list on CogBoard and references to ``cogs.red`` with a link to new Red Index (:issue:`4439`) +- Improved documentation about arguments in command syntax (:issue:`4058`) + +---- + +Redbot 3.4.0 (2020-08-17) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`Dav-Git`, :ghuser:`DevilXD`, :ghuser:`douglas-cpp`, :ghuser:`Drapersniper`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`kablekompany`, :ghuser:`Kowlin`, :ghuser:`maxbooiii`, :ghuser:`MeatyChunks`, :ghuser:`mikeshardmind`, :ghuser:`NeuroAssassin`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`retke`, :ghuser:`SharkyTheKing`, :ghuser:`thisisjvgrace`, :ghuser:`Tinonb`, :ghuser:`TrustyJAID`, :ghuser:`Twentysix26`, :ghuser:`Vexed01`, :ghuser:`zephyrkul` + +Read before updating +-------------------- + +#. Red 3.4 comes with support for data deletion requests. Bot owners should read `red_core_data_statement` to ensure they know what information about their users is stored by the bot. +#. Debian Stretch, Fedora 30 and lower, and OpenSUSE Leap 15.0 and lower are no longer supported as they have already reached end of life. +#. There's been a change in behavior of ``[p]tempban``. Look at `Changes changelog for Mod cog ` for full details. +#. There's been a change in behavior of announcements in Admin cog. Look at `Changes changelog for Admin cog ` for full details. +#. Red 3.4 comes with breaking changes for cog developers. Look at `Developer changelog ` for full details. + +End-user changelog +------------------ + +Security +******** + +- **Cogs - Streams** - Fixed critical vulnerability that could allow remote code execution (CVE-2020-15147), see `security advisory GHSA-7257-96vg-qf6x `__ for more information (:issue:`4183`) + +Additions +********* + +- |cool| **Core** - Added per-guild cog disabling (:issue:`4043`, :issue:`3945`) + + - Bot owners can set the default state for a cog using ``[p]command defaultdisablecog`` and ``[p]command defaultenablecog`` commands + - Guild owners can enable/disable cogs for their guild using ``[p]command disablecog`` and ``[p]command enablecog`` commands + - Cogs disabled in the guild can be listed with ``[p]command listdisabledcogs`` +- |cool| **Core** - Added support for data deletion requests; see `red_core_data_statement` for more information (:issue:`4045`) +- **Core - Bot Commands** - Added ``[p]helpset showsettings`` command (:issue:`4013`, :issue:`4022`) +- |cool| **Cogs - Mod** - Users can now set mention spam triggers which will warn or kick the user. See ``[p]modset mentionspam`` for more information (:issue:`3786`, :issue:`4038`) +- **Cogs - Trivia - Lists** - Added ``whosthatpokemon2`` trivia containing Pokémons from 2nd generation (:issue:`4102`) +- **Cogs - Trivia - Lists** - Added ``whosthatpokemon3`` trivia containing Pokémons from 3rd generation (:issue:`4141`) + +.. _important-340-1: + +Changes +******* + +- ``[p]set nickname``, ``[p]set serverprefix``, ``[p]streamalert``, and ``[p]streamset`` commands now can be run by users with permissions related to the actions they're making (:issue:`4109`) +- Updated Red's emoji usage to ensure consistent rendering accross different devices (:issue:`4106`, :issue:`4105`, :issue:`4127`) +- **Core - Bot Commands** - ``[p]licenseinfo`` now has a 3 minute cooldown to prevent a single user from spamming channel by using it (:issue:`4110`) +- **Core - Bot Commands** - Whitelist and blacklist are now called allowlist and blocklist. Old names have been left as aliases (:issue:`4138`) +- **Core - Command-line Interfaces** - Red now logs clearer error if it can't find package to load in any cog path during bot startup (:issue:`4079`) +- |cool| **Cogs - Admin** - ``[p]announce`` will now only send announcements to guilds that have explicitly configured text channel to send announcements to using ``[p]announceset channel`` command (:issue:`4088`, :issue:`4089`) +- |cool| **Cogs - Downloader** - ``[p]cog info`` command now shows end user data statement made by the cog creator (:issue:`4169`) +- |cool| **Cogs - Downloader** - ``[p]cog update`` command will now notify the user if cog's end user data statement has changed since last update (:issue:`4169`) +- **Cogs - General** - Updated features list in ``[p]serverinfo`` with the latest changes from Discord (:issue:`4116`) +- **Cogs - General** - Simple version of ``[p]serverinfo`` now shows info about more detailed ``[p]serverinfo 1`` (:issue:`4121`) +- **Cogs - Mod** - ``[p]tempban`` now respects default days setting (``[p]modset defaultdays``) (:issue:`3993`) +- |cool| **Cogs - Mod** - ``[p]mute voice`` and ``[p]unmute voice`` now take action instantly if bot has Move Members permission (:issue:`4064`) +- **Cogs - Mod** - Added typing to ``[p](un)mute guild`` to indicate that mute is being processed (:issue:`4066`, :issue:`4172`) +- **Cogs - Modlog** - Added timestamp to text version of ``[p]casesfor`` and ``[p]case`` commands (:issue:`4118`, :issue:`4137`) +- **Cogs - Streams** - Stream alerts will no longer make roles temporarily mentionable if bot has "Mention @everyone, @here, and All Roles" permission in the channel (:issue:`4182`) +- **Cogs - Streams** - Hitbox commands have been renamed to smashcast (:issue:`4161`) +- **Cogs - Streams** - Improve error messages for invalid channel names/IDs (:issue:`4147`, :issue:`4148`) + +Removals +******** + +- **Cogs - Streams** - Mixer service has been closed and for that reason we've removed support for it from the cog (:issue:`4072`) + +Fixes +***** + +- Fixed timestamp storage in few places in Red (:issue:`4017`) + + +.. _important-340-3: + +Developer changelog +------------------- + +| **Important:** +| 1. Red now offers cog disabling API, which should be respected by 3rd-party cogs in guild-related actions happening outside of command's context. See the `Additions changelog below ` for more information. +| 2. Red now provides data request API, which should be supported by all 3rd-party cogs. See the changelog entries in the `Additions changelog below ` for more information. + +Breaking Changes +**************** + +- |cool| By default, none of the ``.send()`` methods mention roles or ``@everyone/@here`` (:issue:`3845`) + + - see `discord.AllowedMentions` and ``allowed_mentions`` kwarg of ``.send()`` methods, if your cog requires to mention roles or ``@everyone/@here`` +- Method/attribute names starting with ``red_`` or being in the form of ``__red_*__`` are now reserved. See `version_guarantees` for more information (:issue:`4085`) +- Removed things past deprecation time: (:issue:`4163`) + + - ``redbot.core.commands.APIToken`` + - ``loop`` kwarg from `bounded_gather_iter()`, `bounded_gather()`, and `start_adding_reactions()` +- **Core** - Cog package names (i.e. name of the folder the cog is in and the name used when loading the cog) now have to be `valid Python identifiers `__ (:issue:`3605`, :issue:`3679`) +- **Core - Commands Package** - `Context.maybe_send_embed()` now supresses all mentions, including user mentions (:issue:`4192`) +- **Core - Commands Package** - The default value of the ``filter`` keyword argument in `Context.send()` has been changed to ``None`` (:issue:`3845`) +- **Core - Utils Package** - `humanize_list()` no longer raises `IndexError` for empty sequences (:issue:`2982`) + +.. _important-dev-340-1: + +Additions +********* + +- |cool| **Core** - Added cog disabling API (:issue:`4043`, :issue:`3945`) + + - New methods added: `bot.cog_disabled_in_guild() `, `bot.cog_disabled_in_guild_raw() ` + - Cog disabling is automatically applied for commands and only needs to be done manually for things like event listeners; see `recommendations-for-cog-creators` for more information +- |cool| **Core** - Added data request API (:issue:`4045`, :issue:`4169`) + + - New special methods added to `redbot.core.commands.Cog`: `red_get_data_for_user()` (documented provisionally), `red_delete_data_for_user()` + - New special module level variable added: ``__red_end_user_data_statement__`` + - These methods and variables should be added by all cogs according to their documentation; see `recommendations-for-cog-creators` for more information + - New ``info.json`` key added: ``end_user_data_statement``; see `Info.json format documentation ` for more information +- **Core - Bot Class** - Added `bot.message_eligible_as_command() ` utility method which can be used to determine if a message may be responded to as a command (:issue:`4077`) +- |cool| **Core - Commands Package** - Added a provisional API for replacing the help formatter. See `documentation ` for more details (:issue:`4011`) +- **Core - Commands Package** - `commands.NoParseOptional ` is no longer provisional and is now fully supported part of API (:issue:`4142`) + +Changes +******* + +- **Core - Bot Class** - `bot.ignored_channel_or_guild() ` now accepts `discord.Message` objects (:issue:`4077`) +- |cool| **Core - Commands Package** - Autohelp in group commands is now sent *after* invoking the group, which allows before invoke hooks to prevent autohelp from getting triggered (:issue:`4129`) +- **Core - Utils Package** - `humanize_list()` now accepts ``locale`` and ``style`` keyword arguments. See its documentation for more information (:issue:`2982`) +- |cool| **Core - Utils Package** - `humanize_list()` is now properly localized (:issue:`2906`, :issue:`2982`) +- **Core - Utils Package** - `humanize_list()` now accepts empty sequences (:issue:`2982`) +- **Core - Utils Package** - ``bordered()`` now uses ``+`` for corners if keyword argument ``ascii_border`` is set to `True` (:issue:`4097`) +- **Vendored Packages** - Updated ``discord.ext.menus`` vendor (:issue:`4167`) + +Fixes +***** + +- **Core - Commands Package** - Red no longer fails to run subcommands of a command group allowed or denied by permission hook (:issue:`3956`) +- **Core - RPC** - RPC functionality no longer makes Red hang for a minute on shutdown (:issue:`4134`, :issue:`4143`) + + +Documentation changes +--------------------- + +Additions +********* + +- |cool| Added admin user guide (:issue:`3081`) +- |cool| Added alias user guide (:issue:`3084`) +- |cool| Added bank user guide (:issue:`4149`) + +Removals +******** + +- Removed install instructions for Debian Stretch (:issue:`4099`) + +---- + +Redbot 3.3.12 (2020-08-18) +========================== + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`Dav-Git`, :ghuser:`douglas-cpp`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`MeatyChunks`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`thisisjvgrace`, :ghuser:`Vexed01`, :ghuser:`zephyrkul` + +End-user changelog +------------------ + +Security +******** + +- **Cogs - Streams** - Fixed critical vulnerability that could allow remote code execution (CVE-2020-15147), see `security advisory GHSA-7257-96vg-qf6x `__ for more information (:issue:`4183`) + +Additions +********* + +- **Cogs - Trivia - Lists** - Added ``whosthatpokemon2`` trivia containing Pokémons from 2nd generation (:issue:`4102`) +- **Cogs - Trivia - Lists** - Added ``whosthatpokemon3`` trivia containing Pokémons from 3rd generation (:issue:`4141`) + +Changes +******* + +- **Core - Command-line Interfaces** - Red now logs clearer error if it can't find package to load in any cog path during bot startup (:issue:`4079`) +- **Cogs - General** - Updated features list in ``[p]serverinfo`` with the latest changes from Discord (:issue:`4116`) +- **Cogs - General** - Simple version of ``[p]serverinfo`` now shows info about more detailed ``[p]serverinfo 1`` (:issue:`4121`) +- **Cogs - Mod** - ``[p]mute voice`` and ``[p]unmute voice`` now take action instantly if bot has Move Members permission (:issue:`4064`) +- **Cogs - Mod** - Added typing to ``[p](un)mute guild`` to indicate that mute is being processed (:issue:`4066`, :issue:`4172`) +- **Cogs - Streams** - Improve error messages for invalid channel names/IDs (:issue:`4147`, :issue:`4148`) + +---- + +Redbot 3.3.11 (2020-08-10) +========================== + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`douglas-cpp`, :ghuser:`Drapersniper`, :ghuser:`Flame`, :ghuser:`jack1142`, :ghuser:`MeatyChunks`, :ghuser:`Vexed01`, :ghuser:`yamikaitou` + +End-user changelog +------------------ + +Security +******** + +- **Cogs - Trivia** - Fixed critical vulnerability that could allow remote code execution (CVE-2020-15140), see `security advisory GHSA-7257-96vg-qf6x `__ for more information (:issue:`4175`) + +Fixes +***** + +- **Cogs - Audio** - Audio should now work again on all voice regions (:issue:`4162`, :issue:`4168`) +- **Cogs - Audio** - Removed an edge case where an unfriendly error message was sent in Audio cog (:issue:`3879`) +- **Cogs - Cleanup** - Fixed a bug causing ``[p]cleanup`` commands to clear all messages within last 2 weeks when ``0`` is passed as the amount of messages to delete (:issue:`4114`, :issue:`4115`) +- **Cogs - CustomCommands** - ``[p]cc show`` now sends an error message when command with the provided name couldn't be found (:issue:`4108`) +- **Cogs - Downloader** - ``[p]findcog`` no longer fails for 3rd-party cogs without any author (:issue:`4032`, :issue:`4042`) +- **Cogs - Downloader** - Update commands no longer crash when a different repo is added under a repo name that was once used (:issue:`4086`) +- **Cogs - Permissions** - ``[p]permissions removeserverrule`` and ``[p]permissions removeglobalrule`` no longer error when trying to remove a rule that doesn't exist (:issue:`4028`, :issue:`4036`) +- **Cogs - Warnings** - ``[p]warn`` now sends an error message (instead of no feedback) when an unregistered reason is used by someone who doesn't have Administrator permission (:issue:`3839`, :issue:`3840`) + +---- + +Redbot 3.3.10 (2020-07-09) +========================== + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`bobloy`, :ghuser:`Dav-Git`, :ghuser:`Drapersniper`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`Injabie3`, :ghuser:`jack1142`, :ghuser:`mikeshardmind`, :ghuser:`MiniJennJenn`, :ghuser:`NeuroAssassin`, :ghuser:`thisisjvgrace`, :ghuser:`Vexed01` + +End-user changelog +------------------ + +Additions +********* + +- **Cogs - Downloader** - Added ``[p]cog listpinned`` subcommand to see currently pinned cogs (:issue:`3974`) +- **Cogs - Filter** - Added ``[p]filter list`` to show filtered words, and removed DMs when no subcommand was passed (:issue:`3973`) +- **Cogs - Trivia - Lists** - Added new ``lotr`` trivia list (:issue:`3980`) +- **Cogs - Trivia - Lists** - Added new ``r6seige`` trivia list (:issue:`4026`) + +Changes +******* + +- **Core - Bot Commands** - Added settings view commands for nearly all cogs. (:issue:`4041`) +- **Core - Bot Commands** - Added more strings to be fully translatable by i18n. (:issue:`4044`) +- **Core - Bot Commands** - Clarified that ``[p]embedset user`` only affects commands executed in DMs (:issue:`3972`, :issue:`3953`) +- **Core - Command-line Interfaces** - Red now prints a link to Getting Started guide if the bot isn't in any server (:issue:`3906`) +- **Core - Command-line Interfaces** - Added the option of using dots in the instance name when creating your instances (:issue:`3920`) +- **Core - Command-line Interfaces** - Added a confirmation when using hyphens in instance names to discourage the use of them (:issue:`3920`) +- **Core - Dependencies** - Bumped the Discord.py requirement from 1.3.3 to 1.3.4 (:issue:`4053`) +- **Cogs - Audio** - Added information about internally managed jar to ``[p]audioset info`` (:issue:`3915`) +- **Cogs - Downloader** - Added embed version of ``[p]findcog`` (:issue:`3965`, :issue:`3944`) +- **Cogs - Mod** - Added option to delete messages within the passed amount of days with ``[p]tempban`` (:issue:`3958`) +- **Cogs - Mod** - Reduced the number of API calls made to the storage APIs (:issue:`3910`) +- **Cogs - Mod** - Prevented an issue whereby the author may lock themself out of using the bot via whitelists (:issue:`3903`) +- **Cogs - Mod** - Improved error response in ``[p]modset banmentionspam`` (:issue:`3951`, :issue:`3949`) +- **Cogs - Modlog** - Improved error response in ``[p]modlogset modlog`` (:issue:`3951`, :issue:`3949`) +- **Cogs - Permissions** - Uploaded YAML files now accept integer commands without quotes (:issue:`3987`, :issue:`3185`) +- **Cogs - Permissions** - Uploaded YAML files now accept command rules with empty dictionaries (:issue:`3987`, :issue:`3961`) +- **Cogs - Trivia - Lists** - Updated ``greekmyth`` to include more answer variations (:issue:`3970`) + +Fixes +***** + +- **Core - Bot Commands** - Fixed delayed help when ``[p]set deletedelay`` is enabled (:issue:`3884`, :issue:`3883`) +- **Core - Bot Commands** - Fixed grammar errors and added full stops in various core commands (:issue:`4023`) +- **Cogs - Audio** - Twitch playback and YouTube searching should be functioning again. (:issue:`4055`) +- **Cogs - Downloader** - Fixed unnecessary typing when running downloader commands (:issue:`3964`, :issue:`3948`) +- **Cogs - Downloader** - Fixed ``[p]findcog`` not differentiating between core cogs and local cogs(:issue:`3969`, :issue:`3966`) +- **Cogs - Image** - Updated instructions for obtaining and setting the GIPHY API key (:issue:`3994`) +- **Cogs - Mod** - Added the ability to permanently ban a temporarily banned user with ``[p]hackban`` (:issue:`4025`) +- **Cogs - Mod** - Fixed the passed reason not being used when using ``[p]tempban`` (:issue:`3958`) +- **Cogs - Mod** - Fixed invite being sent with ``[p]tempban`` even when no invite was set (:issue:`3991`) +- **Cogs - Mod** - Fixed exceptions being ignored or not sent to log files in special cases (:issue:`3895`) +- **Cogs - Mod** - Fixed migration owner notifications being sent even when migration was not necessary (:issue:`3911`. :issue:`3909`) +- **Cogs - Streams** - Fixed Streams cog sending multiple owner notifications about twitch secret not set (:issue:`3901`, :issue:`3587`) +- **Cogs - Streams** - Fixed old bearer tokens not being invalidated when the API key is updated (:issue:`3990`, :issue:`3917`) +- **Cogs - Streams** - Fixed commands being translated where they should not be (:issue:`3938`, :issue:`3919`) +- **Cogs - Trivia - Lists** - Fixed URLs in ``whosthatpokemon`` (:issue:`3975`, :issue:`3023`) +- **Cogs - Trivia - Lists** - Fixed trivia files ``leagueults`` and ``sports`` (:issue:`4026`) + + +Developer changelog +------------------- + +Additions +********* + +- **Core - Utils Package** - Added the methods `map() `, `find() `, and `next() ` to `AsyncIter` (:issue:`3921`, :issue:`3887`) +- **Vendored Packages** - Vendored the ``discord.ext.menus`` module (:issue:`4039`) + +Changes +******* + +- **Core - Utils Package** - Added new ``discord.com`` domain to ``INVITE_URL_RE`` common filter (:issue:`4012`) + +Deprecations +************ + +- **Core - Commands Package** - Updated deprecation times for ``APIToken``, and loops being passed to various functions to the first minor release (represented by ``X`` in ``3.X.0``) after 2020-08-05 (:issue:`3608`) +- **Cogs - Downloader** - Updated deprecation warnings for shared libs to reflect that they have been moved for an undefined time (:issue:`3608`) + +Fixes +***** + +- **Core - Utils Package** - Fixed incorrect role mention regex in `MessagePredicate` (:issue:`4030`) + +---- + +Redbot 3.3.9 (2020-06-12) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`Dav-Git`, :ghuser:`Drapersniper`, :ghuser:`Flame442`, :ghuser:`mikeshardmind`, :ghuser:`NeuroAssassin`, :ghuser:`Predeactor`, :ghuser:`Vexed01` + +Read before updating +-------------------- + +#. Bot owners can no longer restrict access to some commands in Permissions cog using global permissions rules. Look at `Security changelog ` for full details. +#. There's been a change in behavior of warning messages. Look at `Additions changelog ` for full details. + + +End-user changelog +------------------ + +.. _important-339-2: + +Security +******** + +- **Cogs - Mod** - ``[p]tempban`` now properly respects Discord's hierarchy rules (:issue:`3957`) + + **NOTE**: If you can't update immediately, we recommend disabling the affected command until you can. +- **Cogs - Permissions** - **Both global and server rules** can no longer prevent guild owners from accessing commands for changing server rules. Bot owners can still use ``[p]command disable`` if they wish to completely disable any command in Permissions cog (:issue:`3955`, :issue:`3107`) + + Full list of affected commands: + + - ``[p]permissions acl getserver`` + - ``[p]permissions acl setserver`` + - ``[p]permissions acl updateserver`` + - ``[p]permissions addserverrule`` + - ``[p]permissions removeserverrule`` + - ``[p]permissions setdefaultserverrule`` + - ``[p]permissions clearserverrules`` + - ``[p]permissions canrun`` + - ``[p]permissions explain`` + +.. _important-339-1: + +Additions +********* + +- **Cogs - Warnings** - Warnings sent to users don't show the moderator who warned the user by default now. Newly added ``[p]warningset showmoderators`` command can be used to switch this behaviour (:issue:`3781`) + +Changes +******* + +- **Core - Bot Commands** - ``[p]info`` command can now be used when bot doesn't have Embed Links permission (:issue:`3907`, :issue:`3102`) +- **Core - Bot Commands** - Improved instructions on obtaining user ID in help of ``[p]dm`` command (:issue:`3946`) +- **Core - Command-line Interfaces** - Red's start up message now shows storage type (:issue:`3935`) +- **Cogs - Alias** - ``[p]alias global`` group, ``[p]alias help``, and ``[p]alias show`` commands can now be used in DMs (:issue:`3941`, :issue:`3940`) +- **Cogs - Bank** - ``[p]bankset`` now displays bank's scope (:issue:`3954`) + +Fixes +***** + +- Added missing help message for Downloader, Reports and Streams cogs (:issue:`3892`) +- **Core - Bot Commands** - Fixed ungraceful error that happened in ``[p]set custominfo`` when provided text was too long (:issue:`3923`) +- **Core - Bot Commands** - Cooldown in ``[p]contact`` no longer applies when it's used without any arguments (:issue:`3942`) +- **Cogs - Audio** - Audio now properly ignores streams when max length is enabled (:issue:`3878`, :issue:`3877`) +- **Cogs - Audio** - Commands that should work in DMs no longer error (:issue:`3880`) +- **Cogs - Audio** - Fixed ``[p]audioset autoplay`` being available in DMs (:issue:`3899`) +- **Cogs - Audio** - Typo fix (:issue:`3889`, :issue:`3900`) +- **Cogs - Filter** - Fixed behavior of detecting quotes in commands for adding/removing filtered words (:issue:`3925`) +- **Cogs - Mod** - Preemptive fix for d.py 1.4 (:issue:`3891`) +- **Cogs - Warnings** - Warn channel functionality has been fixed (:issue:`3781`) + + +Developer changelog +------------------- + +Additions +********* + +- **Core - Bot Class** - Added `bot.set_prefixes() ` method that allows developers to set global/server prefixes (:issue:`3890`) + + +Documentation changes +--------------------- + +Changes +******* + +- Added Oracle Cloud to free hosting section in :ref:`host-list` (:issue:`3916`) + +---- + +Redbot 3.3.8 (2020-05-29) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`Bakersbakebread`, :ghuser:`DariusStClair`, :ghuser:`Dav-Git`, :ghuser:`Drapersniper`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`mikeshardmind`, :ghuser:`NeuroAssassin`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`qaisjp`, :ghuser:`Tobotimus` + +End-user changelog +------------------ + +Additions +********* + +- **Cogs - Audio** - Added new option (settable with ``[p]audioset lyrics``) that makes Audio cog prefer (prioritize) tracks with lyrics (:issue:`3519`) +- **Cogs - Audio** - Added global daily (historical) queues (:issue:`3518`) +- **Cogs - Audio** - Added ``[p]audioset countrycode`` that allows to set the country code for spotify searches (:issue:`3528`) + +Changes +******* + +- Few clarifications and typo fixes in few command help docstrings (:issue:`3817`, :issue:`3823`, :issue:`3837`, :issue:`3851`, :issue:`3861`) +- **Core** - Red now includes information on how to update when sending information about being out of date (:issue:`3744`) +- **Cogs - Alias** - ``[p]alias help`` should now work more reliably (:issue:`3864`) +- **Cogs - Audio** - ``[p]local play`` no longer enqueues tracks from nested folders (:issue:`3528`) +- **Cogs - Audio** - ``[p]disconnect`` now allows to disconnect if both DJ mode and voteskip aren't enabled (:issue:`3502`, :issue:`3485`) +- **Cogs - Audio** - Many UX improvements and fixes, including, among other things: + + - Creating playlists without explicitly passing ``-scope`` no longer causes errors (:issue:`3500`) + - ``[p]playlist list`` now shows all accessible playlists if ``--scope`` flag isn't used (:issue:`3518`) + - ``[p]remove`` now also accepts a track URL in addition to queue index (:issue:`3201`) + - ``[p]playlist upload`` now accepts a playlist file uploaded in the message with a command (:issue:`3251`) + - Commands now send friendly error messages for common errors like lost Lavalink connection or bot not connected to voice channel (:issue:`3503`, :issue:`3528`, :issue:`3353`, :issue:`3712`) +- **Cogs - Mod** - ``[p]userinfo`` now shows default avatar when no avatar is set (:issue:`3819`) + +Fixes +***** + +- **Core** - Made important fixes to how PostgreSQL data backend saves data in bulks (:issue:`3829`) +- **Core** - Using backslashes in bot's username/nickname no longer causes issues (:issue:`3826`, :issue:`3825`) +- **Core - Bot Commands** - Fixed ``[p]localwhitelist`` and ``[p]localblacklist`` commands (:issue:`3857`) +- **Cogs - Admin** - Fixed server lock (:issue:`3815`, :issue:`3814`) +- **Cogs - Alias** - Added pagination to ``[p]alias list`` and ``[p]alias global list`` to avoid errors for users with a lot of aliases (:issue:`3844`, :issue:`3834`) +- **Cogs - Audio** - Twitch playback is functional once again (:issue:`3873`) +- **Cogs - Audio** - Recent errors with YouTube playback should be resolved (:issue:`3873`) +- **Cogs - Audio** - Fixed ``[p]local search`` (:issue:`3528`, :issue:`3501`) +- **Cogs - Audio** - Local folders with special characters should work properly now (:issue:`3528`, :issue:`3467`) +- **Cogs - Audio** - Audio no longer fails to take the last spot in the voice channel with user limit (:issue:`3528`) +- **Cogs - Audio** - Fixed ``[p]playlist dedupe`` not removing tracks (:issue:`3518`) +- **Cogs - CustomCommands** - ``[p]customcom create`` no longer allows spaces in custom command names (:issue:`3816`) +- **Cogs - Modlog** - Fixed (again) ``AttributeError`` for cases whose moderator doesn't share the server with the bot (:issue:`3805`, :issue:`3784`, :issue:`3778`) +- **Cogs - Permissions** - Commands for settings ACL using yaml files now properly works on PostgreSQL data backend (:issue:`3829`, :issue:`3796`) +- **Cogs - Warnings** - Warnings cog no longer allows to warn bot users (:issue:`3855`, :issue:`3854`) + + +Developer changelog +------------------- + +| **Important:** +| If you're using RPC, please see the full annoucement about current state of RPC in main Red server + `by clicking here `__. + +Changes +******* + +- **Core - Bot Class** - Red now inherits from `discord.ext.commands.AutoShardedBot` for better compatibility with code expecting d.py bot (:issue:`3822`) +- **Core - Bot Class** - All bot owner IDs can now be found under ``bot.owner_ids`` attribute (:issue:`3793`) + + - Note: If you want to use this on bot startup (e.g. in cog's initialisation), you need to await ``bot.wait_until_red_ready()`` first + +Fixes +***** + +- Libraries using ``pkg_resources`` (like ``humanize`` or ``google-api-python-client``) that were installed through Downloader should now work properly (:issue:`3843`) +- **Cogs - Downloader** - Downloader no longer removes the repo when it fails to load it (:issue:`3867`) + + +Documentation changes +--------------------- + +Changes +******* + +- Added information about provisional status of RPC (:issue:`3862`) +- Revised install instructions (:issue:`3847`) +- Improved navigation in `document about updating Red ` (:issue:`3856`, :issue:`3849`) + +---- + +Redbot 3.3.7 (2020-04-28) +========================= + +This is a hotfix release fixing issue with generating messages for new cases in Modlog. + +---- + +Redbot 3.3.6 (2020-04-27) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`Drapersniper`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`MiniJennJenn`, :ghuser:`NeuroAssassin`, :ghuser:`PredaaA`, :ghuser:`TrustyJAID`, :ghuser:`yamikaitou` + +End-user changelog +------------------ + +Additions +********* + +- **Core - Bot Commands** - Added ``[p]set avatar remove`` subcommand for removing bot's avatar (:issue:`3757`) +- **Cogs - CustomCommands** - Added ``[p]cc raw`` command that gives you the raw response of a custom command for ease of copy pasting (:issue:`3795`) + +Changes +******* + +- **Core** - Various optimizations + + - Reduced calls to data backend when loading bot's commands (:issue:`3764`) + - Reduced calls to data backend when showing help for cogs/commands (:issue:`3766`) + - Improved performance for bots with big amount of guilds (:issue:`3767`) + - Mod cog no longer fetches guild's bans every 60 seconds when handling unbanning for tempbans (:issue:`3783`) + - Reduced the bot load for messages starting with a prefix when fuzzy search is disabled (:issue:`3718`) + - Aliases in Alias cog are now cached for better performance (:issue:`3788`) +- **Core - Bot Commands** - ``[p]set avatar`` now supports setting avatar using attachment (:issue:`3747`) +- **Core - Bot Commands** - ``[p]debuginfo`` now shows used storage type (:issue:`3794`) +- **Cogs - Trivia - Lists** - Updated ``leagueoflegends`` list with new changes to League of Legends (`b8ac70e `__) + +Fixes +***** + +- **Core** - Fixed big delays in commands that happened when the bot was owner-less (or if it only used co-owners feature) and command caller wasn't the owner (:issue:`3782`) +- **Core - Bot Commands** - Fixed list of ignored channels that is shown in ``[p]ignore``/``[p]unignore`` (:issue:`3746`) +- **Core - Command-line Interfaces** - Converting from and to Postgres driver with ``redbot-setup convert`` have been fixed (:issue:`3714`, :issue:`3115`) +- **Cogs - Audio** - Age-restricted tracks, live streams, and mix playlists from YouTube should work in Audio again (:issue:`3791`) +- **Cogs - Audio** - SoundCloud sets and playlists with more than 50 tracks should work in Audio again (:issue:`3791`) +- **Cogs - Modlog** - Fixed ``AttributeError`` for cases whose moderator doesn't share the server with the bot (:issue:`3784`, :issue:`3778`) +- **Cogs - Streams** - Fixed incorrect stream URLs for Twitch channels that have localised display name (:issue:`3773`, :issue:`3772`) +- **Cogs - Trivia** - Fixed the error in ``[p]trivia stop`` that happened when there was no ongoing trivia session in the channel (:issue:`3774`) +- **Cogs - Trivia - Lists** - Corrected spelling of Compact Disc in ``games`` list (:issue:`3759`, :issue:`3758`) + + +Developer changelog +------------------- + +Additions +********* + +- **Core - Utils Package** - Added `redbot.core.utils.AsyncIter` utility class which allows you to wrap regular iterable into async iterator yielding items and sleeping for ``delay`` seconds every ``steps`` items (:issue:`3767`, :issue:`3776`) + +Changes +******* + +- **Core - Utils Package** - `bold()`, `italics()`, `strikethrough()`, and `underline()` now accept ``escape_formatting`` argument that can be used to disable escaping of markdown formatting in passed text (:issue:`3742`) + +Fixes +***** + +- **Core - Config** - JSON driver will now properly have only one lock per cog name (:issue:`3780`) + + +Documentation changes +--------------------- + +Additions +********* + +- Added `document about updating Red ` (:issue:`3790`) +- Updated install docs to include Ubuntu 20.04 (:issue:`3792`) + +Changes +******* + +- ``pyenv`` instructions will now update ``pyenv`` if it's already installed (:issue:`3740`) +- Updated Python version in ``pyenv`` instructions (:issue:`3740`) + +---- + +Redbot 3.3.5 (2020-04-09) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`jack1142`, :ghuser:`Kowlin` + +End-user changelog +------------------ + +Changes +******* + +- **Core - Bot Commands** - "Outdated" field no longer shows in ``[p]info`` when Red is up-to-date (:issue:`3730`) + +Fixes +***** + +- **Cogs - Alias** - Fixed regression in ``[p]alias add`` that caused it to reject commands containing arguments (:issue:`3734`) + +---- + +Redbot 3.3.4 (2020-04-05) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`jack1142`, :ghuser:`kennnyshiwa` + +End-user changelog +------------------ + +Changes +******* + +- **Cogs - Alias** - ``[p]alias add`` now sends an error when command user tries to alias doesn't exist (:issue:`3710`, :issue:`3545`) + +Fixes +***** + +- **Core - Bot Commands** - Fixed checks related to bank's global state that were used in commands in Bank, Economy and Trivia cogs (:issue:`3707`) + + +Developer changelog +------------------- + +Changes +******* + +- **Core - Dependencies** - Bump dependencies, including update to discord.py 1.3.3 (:issue:`3723`) +- **Core - Utils Package** - `redbot.core.utils.common_filters.filter_invites` now filters ``discord.io/discord.li`` invites links (:issue:`3717`) + +Fixes +***** + +- **Core - Utils Package** - Fixed false-positives in `redbot.core.utils.common_filters.filter_invites` (:issue:`3717`) + + +Documentation changes +--------------------- + +Changes +******* + +- Versions of pre-requirements are now included in Windows install guide (:issue:`3708`) + +---- + +Redbot 3.3.3 (2020-03-28) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`AnonGuy`, :ghuser:`Dav-Git`, :ghuser:`FancyJesse`, :ghuser:`Ianardo-DiCaprio`, :ghuser:`jack1142`, :ghuser:`kennnyshiwa`, :ghuser:`Kowlin`, :ghuser:`NeuroAssassin`, :ghuser:`PredaaA`, :ghuser:`Stonedestroyer`, :ghuser:`TrustyJAID` + +End-user changelog +------------------ + +Security +******** + +- **Cogs - Cleanup** - Removed regex support in ``[p]cleanup self`` (:issue:`3704`) + +Additions +********* + +- **Core - i18n** - Added ``[p]set regionalformat`` command that allows users to set regional formatting that is different from bot's locale (:issue:`3677`, :issue:`3588`) +- **Cogs - Cleanup** - Added ``[p]cleanup spam`` command that deletes duplicate messages from the last X messages and keeps only one copy (:issue:`3688`) +- **Cogs - CustomCommands** - Added ``[p]cc search`` command that allows users to search through created custom commands (:issue:`2573`) +- **Cogs - Trivia** - Added ``[p]triviaset custom upload/delete/list`` commands for managing custom trivia lists from Discord (:issue:`3420`, :issue:`3307`) +- **Cogs - Warnings** - Sending warnings to warned user can now be disabled with ``[p]warnset toggledm`` command (:issue:`2929`, :issue:`2800`) +- **Cogs - Warnings** - Added ``[p]warnset warnchannel`` command that allows to set a channel where warnings should be sent to instead of the channel command was called in (:issue:`2929`, :issue:`2800`) +- **Cogs - Warnings** - Added ``[p]warnset togglechannel`` command that allows to disable sending warn message in guild channel (:issue:`2929`, :issue:`2800`) + +Changes +******* + +- **Core** - Delete delay for command messages has been moved from Mod cog to Core (:issue:`3638`, :issue:`3636`) +- **Core** - Command errors (i.e. command on cooldown, dm-only and guild-only commands, etc) can now be translated (:issue:`3665`, :issue:`2988`) +- **Core - Bot Commands** - ``[p]set locale`` allows any valid locale now, not just locales for which Red has translations (:issue:`3676`, :issue:`3596`) +- **Core - Bot Commands** - Permissions for commands in Bank, Economy and Trivia cogs can now be overridden by Permissions cog (:issue:`3672`, :issue:`3233`) +- **Core - Bot Commands** - Added ``[p]set playing`` and ``[p]set streaming`` aliases for respectively ``[p]set game`` and ``[p]set stream`` (:issue:`3646`, :issue:`3590`) +- **Core - Command-line Interfaces** - ``redbot-setup`` now prints link to Getting started guide at the end of the setup (:issue:`3027`) +- **Cogs - Downloader** - ``[p]cog checkforupdates`` now includes information about cogs that can't be installed due to Red/Python version requirements (:issue:`3678`, :issue:`3448`) +- **Cogs - Downloader** - Improved error messages for unexpected errors in ``[p]repo add`` (:issue:`3656`) +- **Cogs - General** - Added more detailed mode to ``[p]serverinfo`` command that can be accessed with ``[p]serverinfo 1`` (:issue:`2382`, :issue:`3659`) +- **Cogs - Image** - Users can now specify how many images should be returned in ``[p]imgur search`` and ``[p]imgur subreddit`` using ``[count]`` argument (:issue:`3667`, :issue:`3044`) +- **Cogs - Image** - ``[p]imgur search`` and ``[p]imgur subreddit`` now return one image by default (:issue:`3667`, :issue:`3044`) +- **Cogs - Mod** - ``[p]userinfo`` now shows user's activities (:issue:`3669`) +- **Cogs - Mod** - ``[p]userinfo`` now shows status icon near the username (:issue:`3669`) +- **Cogs - Modlog** - Modlog's cases now keep last known username to prevent losing that information from case's message on edit (:issue:`3674`, :issue:`3443`) +- **Cogs - Permissions** - Commands for setting default rules now error when user tries to deny access to command designated as being always available (:issue:`3504`, :issue:`3465`) +- **Cogs - Streams** - Preview picture for YouTube stream alerts is now bigger (:issue:`3689`, :issue:`3685`) +- **Cogs - Streams** - Failures in Twitch API authentication are now logged (:issue:`3657`) +- **Cogs - Warnings** - ``[p]warn`` now tells the moderator when bot wasn't able to send the warning to the user (:issue:`3653`, :issue:`3633`) + +Fixes +***** + +- All owner notifications in core cogs now use proper prefixes in messages (:issue:`3632`) +- **Core** - Fixed various bugs with blacklist and whitelist (:issue:`3643`, :issue:`3642`) +- **Core** - Outages of ``pypi.org`` no longer prevent the bot from starting (:issue:`3663`) +- **Core** - Fixed formatting of help strings in fuzzy search results (:issue:`3673`, :issue:`3507`) +- **Core** - Fixed few deprecation warnings related to menus and uvloop (:issue:`3644`, :issue:`3700`) +- **Core - Bot Commands** - ``[p]set game`` no longer errors when trying to clear the status (:issue:`3630`, :issue:`3628`) +- **Core - Bot Commands** - Whitelist and blacklist commands now properly require passing at least one user (or role in case of local whitelist/blacklist) (:issue:`3652`, :issue:`3645`) +- **Cogs - Downloader** - Fix misleading error appearing when repo name is already taken in ``[p]repo add`` (:issue:`3695`) +- **Cogs - Downloader** - Prevent encoding errors from crashing ``[p]cog update`` (:issue:`3639`, :issue:`3637`) +- **Cogs - Mod** - Muting no longer fails if user leaves while applying overwrite (:issue:`3627`) +- **Cogs - Mod** - Fixed error that happened when Mod cog was loaded for the first time during bot startup (:issue:`3632`, :issue:`3626`) +- **Cogs - Streams** - Fixed an error that happened when no game was set on Twitch stream (:issue:`3631`) +- **Cogs - Streams** - YouTube channels with a livestream that doesn't have any current viewer are now properly showing as streaming (:issue:`3690`) +- **Cogs - Trivia** - Trivia sessions no longer error on payout when winner's balance would exceed max balance (:issue:`3666`, :issue:`3584`) +- **Cogs - Trivia** - Non-finite numbers can no longer be passed to ``[p]triviaset timelimit``, ``[p]triviaset stopafter`` and ``[p]triviaset payout`` (:issue:`3668`, :issue:`3583`) + + +Developer changelog +------------------- + +Fixes +***** + +- Deprecation warnings issued by Red now use correct stack level so that the cog developers can find the cause of them (:issue:`3644`) +- **Core - Utils Package** - `redbot.core.utils.menus.menu()` now checks permissions *before* trying to clear reactions (:issue:`3589`, :issue:`3145`) +- **Cogs - Dev** - Added ``__name__`` to environment's globals (:issue:`3649`, :issue:`3648`) + + +Documentation changes +--------------------- + +Changes +******* + +- Windows install instructions now use ``choco upgrade`` commands instead of ``choco install`` to ensure up-to-date packages (:issue:`3684`) + +Fixes +***** + +- Fixed install instructions for Mac (:issue:`3675`, :issue:`3436`) + +---- + +Redbot 3.3.2 (2020-02-28) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`chasehult`, :ghuser:`Dav-Git`, :ghuser:`DiscordLiz`, :ghuser:`Drapersniper`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`Hedlund01`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`mikeshardmind`, :ghuser:`PredaaA`, :ghuser:`Stonedestroyer`, :ghuser:`trundler-dev`, :ghuser:`TrustyJAID`, :ghuser:`zephyrkul` + +End-user changelog +------------------ + +Additions +********* + +- **Cogs - Streams** - Added ``[p]streamset timer`` command which can be used to control how often the cog checks for live streams (:issue:`3237`) + +Changes +******* + +- **Core** - Ignored guilds/channels and whitelist/blacklist are now cached for performance (:issue:`3472`) +- **Core** - Ignored guilds/channels have been moved from Mod cog to Core (:issue:`3472`) +- **Core - Bot Commands** - ``[p]ignore channel`` command can now also ignore channel categories (:issue:`3472`) +- **Core - Bot Commands** - Improved user experience of ``[p]set game/listening/watching/`` commands (:issue:`3562`) +- **Core - Bot Commands** - Added ``[p]licenceinfo`` alias for ``[p]licenseinfo`` command to conform with non-American English (:issue:`3460`) +- **Cogs - Downloader** - Added better logging of errors when Downloader fails to add a repo (:issue:`3558`) +- **Cogs - Mod** - ``[p]hackban`` and ``[p]unban`` commands support user mentions now (:issue:`3524`) +- **Cogs - Streams** - Significantly reduce the quota usage for YouTube stream alerts (:issue:`3237`) +- **Cogs - Warnings** - Users can now pass a reason to ``[p]unwarn`` command (:issue:`3490`, :issue:`3093`) +- **Cogs - Warnings** - Use more reliant way of checking if command is bot owner only in ``[p]warnaction`` (Warnings cog) (:issue:`3516`, :issue:`3515`) + +Fixes +***** + +- Core cogs will now send bot mention prefix properly in places where discord doesn't render mentions (:issue:`3579`, :issue:`3591`, :issue:`3499`) +- **Core** - Stop using deprecated code in Core (:issue:`3610`) +- **Core - Bot Commands** - Fixed a bug with ``[p]blacklist add`` that made it impossible to blacklist users that bot doesn't share a server with (:issue:`3472`, :issue:`3220`) +- **Core - Bot Commands** - Update PyPI domain in ``[p]info`` and update checker (:issue:`3607`) +- **Cogs - Admin** - ``[p]announce`` will now only send error message if an actual errors occurs (:issue:`3514`, :issue:`3513`) +- **Cogs - Alias** - ``[p]alias help`` will now properly work in non-English locales (:issue:`3546`) +- **Cogs - Audio** - Users should be able to play age-restricted tracks from YouTube again (:issue:`3620`) +- **Cogs - Downloader** - Downloader will no longer fail because of invalid ``info.json`` files (:issue:`3533`, :issue:`3456`) +- **Cogs - Economy** - Next payday time will now be adjusted for users when payday time is changed (:issue:`3496`, :issue:`3438`) +- **Cogs - Image** - Fixed load error for users that updated Red from version lower than 3.1 to version 3.2 or newer (:issue:`3617`) +- **Cogs - Streams** - Fixed stream alerts for Twitch (:issue:`3487`) +- **Cogs - Trivia** - Added better handling for errors in trivia session (:issue:`3606`) +- **Cogs - Trivia - Lists** - Removed empty answers in trivia lists (:issue:`3581`) + + +Developer changelog +------------------- + +Security +******** + +- **Core - Commands Package** - Subcommands of command group with ``invoke_without_command=True`` will again inherit this group's checks (:issue:`3614`) + +Additions +********* + +- **Cogs - Dev** - Allow for top-level `await`, `async for` and `async with` in ``[p]debug`` and ``[p]repl`` commands (:issue:`3508`) + +Changes +******* + +- **Core - Command-line Interfaces** - Added traceback logging to task exception handling (:issue:`3517`) +- **Core - Command-line Interfaces** - Bot will now show deprecation warnings in logs (:issue:`3527`, :issue:`3615`) +- **Core - Commands Package** - Developers can now create a command from an async function wrapped in `functools.partial` (:issue:`3542`) +- **Core - Dependencies** - Updated all our dependencies - we're using discord.py 1.3.2 now (:issue:`3609`) +- **Core - Utils Package** - Added clearer error when page is of a wrong type in `redbot.core.utils.menus.menu()` (:issue:`3571`) +- **Cogs - Downloader** - Downloader will now replace ``[p]`` with clean prefix same as it does in help command (:issue:`3592`) +- **Cogs - Downloader** - Added schema validation to ``info.json`` file processing - it should now be easier to notice any issues with those files (:issue:`3533`, :issue:`3442`) + +Fixes +***** + +- **Core - Config** - Fixed Config's singletons (:issue:`3137`, :issue:`3136`) + + +Documentation changes +--------------------- + +Additions +********* + +- Added guidelines for Cog Creators in `guide_cog_creation` document (:issue:`3568`) + +Changes +******* + +- Restructured virtual environment instructions to improve user experience (:issue:`3495`, :issue:`3411`, :issue:`3412`) +- Getting started guide now explains use of quotes for arguments with spaces (:issue:`3555`, :issue:`3111`) +- ``latest`` version of docs now displays a warning about possible differences from current stable release (:issue:`3570`) +- Made systemd guide clearer on obtaining username and python path (:issue:`3537`, :issue:`3462`) +- Improved indication of instructions for different venv types in systemd guide (:issue:`3538`) +- Service file in `autostart_systemd` now also waits for network connection to be ready (:issue:`3549`) +- Hid alias of ``randomize_colour`` in docs (:issue:`3491`) +- Added separate headers for each event predicate class for better navigation (:issue:`3595`, :issue:`3164`) +- Improved wording of explanation for ``required_cogs`` key in `guide_publish_cogs` (:issue:`3520`) + +---- + +Redbot 3.3.1 (2020-02-05) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`Flame442`, :ghuser:`flyingmongoose`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`mikeshardmind`, :ghuser:`palmtree5`, :ghuser:`PredaaA` + +End-user changelog +------------------ + +Additions +********* + +- **Core - Command-line Interfaces** - Added a cli flag (``--message-cache-size``) for setting a max size of message cache (:issue:`3473`, :issue:`3474`) + +Changes +******* + +- Some functions have been changed to no longer use deprecated asyncio functions (:issue:`3509`) +- **Core - Command-line Interfaces** - Prefix can now be edited from command line using ``redbot --edit`` (:issue:`3481`, :issue:`3486`) +- **Cogs - Mod** - The short help text for ``[p]modset dm`` has been made more useful (:issue:`3488`) + +Fixes +***** + +- **Core - Bot Commands** - ``[p]dm`` no longer allows owners to have the bot attempt to DM itself (:issue:`3477`, :issue:`3478`) +- **Cogs - Mod** - Hackban now works properly without being provided a number of days (:issue:`3476`, :issue:`3475`) + + +Developer changelog +------------------- + +Deprecations +************ + +- **Core - Utils Package** - Passing the event loop explicitly in `bounded_gather()`, `bounded_gather_iter()`, and `start_adding_reactions()` is deprecated and will be removed in 3.4 (:issue:`3509`) + + +Documentation changes +--------------------- + +Additions +********* + +- Added section to install docs for CentOS 8 (:issue:`3461`, :issue:`3463`) + +Changes +******* + +- Added ``-e`` flag to ``journalctl`` command in systemd guide so that it takes the user to the end of logs automatically (:issue:`3483`) +- Improved usage of apt update in docs (:issue:`3464`) + +---- + +Redbot 3.3.0 (2020-01-26) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`DevilXD`, :ghuser:`Drapersniper`, :ghuser:`Flame442`, :ghuser:`Ianardo-DiCaprio`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`mikeshardmind`, :ghuser:`Stonedestroyer`, :ghuser:`zephyrkul` + +End-user changelog +------------------ + +Additions +********* + +- **Core** - Team applications are now supported (:issue:`2781`, :issue:`3445`) + + - Added new ``--team-members-are-owners`` flag that will make Red treat owners of the team application as bot owners +- **Core - Bot Commands** - Embed use can now be configured per channel with new ``[p]embedset channel`` command (:issue:`3152`, :issue:`3418`) +- **Cogs - Mod** - You can set a default amount of days to clean up when banning with ``[p]ban`` and ``[p]tempban`` (:issue:`2441`, :issue:`2930`, :issue:`3437`) +- **Cogs - Mod** - Users can now optionally be DMed their ban reason (:issue:`2649`, :issue:2990`) + +Changes +******* + +- **Core - Help** - Help is now self consistent in the extra formatting used (:issue:`3451`) +- **Cogs - Admin** - Role granting/removing commands will now notify when the user already has/doesn't have a role when attempting to add/remove it (:issue:`3010`, :issue:`3408`) +- **Cogs - Audio** - Playlist searching is now more intuitive (:issue:`3430`) +- **Cogs - Downloader** - Some user facing messages were improved (:issue:`3409`) +- **Cogs - Mod** - ``[p]slowmode`` should no longer error on nonsensical time quantities (:issue:`3453`) + +Fixes +***** + +- **Cogs - Audio** - ``[p]audioset dc`` and ``[p]repeat`` commands no longer interfere with each other (:issue:`3425`, :issue:`3426`) +- **Cogs - Cleanup** - Fixed a rare edge case involving messages that were deleted during cleanup (:issue:`3414`) +- **Cogs - CustomCommands** - ``[p]cc create random`` no longer errors when exiting an interactive menu (:issue:`3416`, :issue:`3417`) +- **Cogs - Downloader** - Downloader's initialization can no longer time out at startup (:issue:`3415`, :issue:`3440`, :issue:`3444`) +- **Cogs - General** - ``[p]roll`` command will no longer attempt to roll obscenely large amounts (:issue:`3284`, :issue:`3395`) +- **Cogs - Permissions** - Now has stronger enforcement of prioritizing botwide settings + + +Developer changelog +------------------- + +Breaking Changes +**************** + +- **Core - Commands Package** - Importing submodules of ``discord.ext.commands`` from ``redbot.core.commands`` will no longer work (:issue:`3410`) +- **Core - Commands Package** - ``PermState.ALLOWED_STATES`` from ``redbot.core.commands.requires`` has been moved to a global variable called ``PermStateAllowedStates`` in the same module (:issue:`3410`) +- **Core - Commands Package** - ``PermState.TRANSITIONS`` from ``redbot.core.commands.requires`` has been moved to a global variable called ``PermStateAllowedStates`` in the same module (:issue:`3410`) +- **Core - Commands Package** - Use of ``@asyncio.coroutine`` is no longer supported. Use ``async def`` instead (:issue:`3410`) + +Changes +******* + +- **Core - Commands Package** - The commands module has been slightly restructured to provide more useful data to developers (:issue:`3410`) +- **Core - Dependencies** - We now use discord.py 1.3.1 (:issue:`3445`) + +Deprecations +************ + +- **Cogs - Downloader** - Updated deprecation warnings for shared libs to reflect that they will instead be removed in 3.4 (:issue:`3449`) + +Fixes +***** + +- **Core - Commands Package** - Fixed an issue with default units in `TimedeltaConverter` (:issue:`3453`) + + +Documentation changes +--------------------- + +Fixes +***** + +- We've made some small fixes to inaccurate instructions about installing with pyenv (:issue:`3434`) + +---- + +Redbot 3.2.3 (2020-01-17) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`Dav-Git`, :ghuser:`Drapersniper`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`mikeshardmind`, :ghuser:`Redjumpman`, :ghuser:`Stonedestroyer`, :ghuser:`TrustyJAID` + +End-user changelog +------------------ + +Additions +********* + +- **Core** - The bot's description is now configurable through ``[p]set description`` (:issue:`3340`) + +Changes +******* + +- **Core** - Further improvements have been made to bot startup and shutdown (:issue:`3358`, :issue:`3392`) +- **Core** - Prefixes are now cached for performance (:issue:`3148`, :issue:`3150`) +- **Core** - The bot now ensures it has at least the bare neccessary permissions before running commands (:issue:`3304`, :issue:`3305`, :issue:`3361`) +- **Core - Bot Commands** - The ``[p]servers`` command now also shows the ids (:issue:`3224`, :issue:`3393`) +- **Cogs - Audio** - Reduced cooldowns for some of the playlist commands (:issue:`3342`) +- **Cogs - Downloader** - Improved a few user facing messages (:issue:`3343`) +- **Cogs - Downloader** - Added logging of failures (:issue:`3372`) + +Fixes +***** + +- **Core** - Embed settings (``[p]embedset``) for ``[p]help`` now work the same as for other commands (:issue:`3382`) +- **Core - Command-line Interfaces** - Deleting instances works as intended again (:issue:`3338`, :issue:`3384`) +- **Cogs - Admin** - The selfrole command now has reasonable expectations about hierarchy (:issue:`3331`) +- **Cogs - Audio** - Audio now properly disconnects the bot when ``[p]audioset dc`` is turned on, even if ``[p]audioset notify`` is being used (:issue:`3349`, :issue:`3350`) +- **Cogs - Audio** - Symbolic links now work as intended for local tracks (:issue:`3332`, :issue:`3376`) +- **Cogs - Audio** - ``[p]bumpplay`` now shows the correct remaining time until the bumped track is played (:issue:`3373`, :issue:`3375`) +- **Cogs - Audio** - Multiple user facing messages have been made more correct (:issue:`3347`, :issue:`3348`, :issue:`3374`) +- **Cogs - Downloader** - Added pagination of output on cog update when it's too long for single message (:issue:`3385`, :issue:`3388`) + + +Developer changelog +------------------- + +Additions +********* + +- **Core** - Added the means for cog creators to use a global preinvoke hook (:issue:`3369`) +- **Core - Commands Package** - New features added for cog creators to further customize help behavior (:issue:`3339`) + + - Check out our command reference for details on new ``format_help_for_context`` method +- **Core - Commands Package** - ``[botname]`` is now replaced with the bot's display name in help text (:issue:`3339`) + + +Documentation changes +--------------------- + +Additions +********* + +- Added proper support for Ubuntu non-LTS (:issue:`3330`, :issue:`3336`) +- Added link to our GitHub in the documentation (:issue:`3306`) + +Changes +******* + +- Added a note about how to update Red to the install guides (:issue:`3400`) +- Clarified some information about breaking changes in Red 3.2.0 changelog (:issue:`3367`) +- Improved the structure of the Linux/Mac install guide to make it more clear to the user which sections they should be following (:issue:`3365`) +- Added more details to the API key reference (:issue:`3400`) +- Updated the documentation to **require** the usage of virtual environment for installing and running Red (:issue:`3351`) +- Updated auto-restart guides to use Python's ``-O`` flag to enable assert optimizations (:issue:`3354`) + +Fixes +***** + +- Updated the documentation with the minimum supported git version (:issue:`3371`) +- Fixed install instructions for Debian to also work with Debian Stretch (:issue:`3352`) + +---- + +Redbot 3.2.2 (2020-01-10) +========================= + +End-user changelog +------------------ + +Fixes +***** + +- **Core - Bot Commands** - Fixed pagination issue in ``[p]help`` command (:issue:`3323`, :issue:`3324`) + + +Documentation changes +--------------------- + +Fixes +***** + +- Corrected venv docs to use the actually supported Python version (:issue:`3325`, :issue:`3324`) + +---- + +Redbot 3.2.1 (2020-01-10) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`mikeshardmind`, :ghuser:`palmtree5` + +End-user changelog +------------------ + +Changes +******* + +- **Cogs - Modlog** - Modlog will now log an error with unexpected case type key (and any other keys) rather than crash (:issue:`3318`) + +Fixes +***** + +- **Core - Command-line Interfaces** - Fixed Mongo conversion from being incorrectly blocked (:issue:`3316`, :issue:`3319`) +- **Cogs - Admin** - Fixed announcer not creating a message for success feedback (:issue:`3320`) + +---- + +Redbot 3.2.0 (2020-01-09) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`Aurorum`, :ghuser:`Bakersbakebread`, :ghuser:`DevilXD`, :ghuser:`DiscordLiz`, :ghuser:`DJtheRedstoner`, :ghuser:`Drapersniper`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`Ianardo-DiCaprio`, :ghuser:`jack1142`, :ghuser:`jerbob`, :ghuser:`jonasbohmann`, :ghuser:`kennnyshiwa`, :ghuser:`Kowlin`, :ghuser:`mikeshardmind`, :ghuser:`palmtree5`, :ghuser:`PredaaA`, :ghuser:`RealFriesi`, :ghuser:`retke`, :ghuser:`Tobotimus`, :ghuser:`Vexed01`, :ghuser:`wereii`, :ghuser:`yamikaitou`, :ghuser:`ZeLarpMaster`, :ghuser:`zephyrkul` + +Read before updating +-------------------- + +#. Red 3.2 dropped support for the MongoDB driver. When updating your instance from an older version, be sure to use instructions for **your current version** from the `document about updating Red ` to be able to still start your instance after the update. +#. Red 3.2 requires Python 3.8.1 or newer. In order to be able to update, you'll first have to install appropriate versions of your dependencies so be sure to use instructions for **your current version** from the `document about updating Red `. + + .. note:: + + You may get a notification from the downloader cog about needing to refetch dependencies. + This is expected and it will walk you through everything and do as much as it can for you. + +#. Red 3.2 comes with improvements which required breaking changes for 3rd party cogs. When you update to 3.2, your cogs may not be compatible if the author has not handled + the changes yet. If you're a cog creator, you can look at `Developer changelog ` for full details. + + +End-user changelog +------------------ + +Breaking Changes +**************** + +- **Core** - Removed the mongo driver (:issue:`3099`, :issue:`3108`) +- **Core - Dependencies** - Updated the required minimum Python version to 3.8.1, and the required minimum JRE version to Java 11 (:issue:`3245`) + +Additions +********* + +- **Core** - Added a config driver for PostgreSQL (:issue:`2723`) +- **Core - Bot Commands** - Added ``[p]licenseinfo`` (:issue:`3090`) +- **Core - Bot Commands** - Added a command to list disabled commands globally or per guild (:issue:`3118`) +- **Core - Command-line Interfaces** - Added the cli flag ``redbot --edit`` which is used to edit the instance name, token, owner, and datapath (:issue:`3060`) +- **Core - Command-line Interfaces** - Added ``redbot-setup backup`` (:issue:`3235`) +- **Core - Command-line Interfaces** - Added ``redbot --debuginfo`` flag which shows useful information for debugging (:issue:`3183`) +- **Cogs - Audio** - Added support for nested folders in the localtrack folder (:issue:`270`) +- **Cogs - Audio** - Audio now auto pauses the queue when the voice channel is empty (:issue:`721`) +- **Cogs - Audio** - All playlist commands now accept optional arguments, use ``[p]help playlist `` for more details (:issue:`2861`) +- **Cogs - Audio** - ``[p]playlist rename`` will now allow users to rename existing playlists (:issue:`2861`) +- **Cogs - Audio** - ``[p]playlist update`` will now allow users to update non-custom Playlists to the latest available tracks (:issue:`2861`) +- **Cogs - Audio** - There are now 3 different scopes of playlist. To define them, use the ``--scope`` argument + + ``Global Playlist`` + + - These playlists will be available in all servers the bot is in. + - These can be managed by the Bot Owner only. + + ``Server Playlist`` + + - These playlists will only be available in the server they were created in. + - These can be managed by the Bot Owner, Guild Owner, Mods, Admins, DJs, and the Creator (if the DJ role is disabled). + + ``User Playlist`` + + - These playlists will be available in all servers both the bot and the creator are in. + - These can be managed by the Bot Owner and Creator only. (:issue:`2861`) +- **Cogs - Audio** - ``[p]audioset cache`` can now be used to set the cache level. **It's off by default** (:issue:`2904`) +- **Cogs - Audio** - ``[p]genre`` can now be used to play spotify playlists (:issue:`2904`) +- **Cogs - Audio** - ``[p]audioset cacheage`` can now be used to set the maximum age of an entry in the cache. **Default is 365 days** (:issue:`2904`) +- **Cogs - Audio** - ``[p]audioset autoplay`` can now be used to enable auto play once the queue runs out (:issue:`2904`) +- **Cogs - Audio** - ``[p]queue shuffle`` can now be used to shuffle the queue manually (:issue:`2904`) +- **Cogs - Audio** - ``[p]queue clean self`` can now be used to remove all songs you requested from the queue (:issue:`2904`) +- **Cogs - Audio** - ``[p]audioset restrictions`` can now be used to add or remove keywords which songs must have or are not allowed to have (:issue:`2904`) +- **Cogs - Audio** - ``[p]playlist dedupe`` can now be used to remove duplicated tracks from a playlist (:issue:`2904`) +- **Cogs - Audio** - ``[p]autoplay`` can now be used to play a random song (:issue:`2904`) +- **Cogs - Audio** - ``[p]bumpplay`` can now be used to add a song to the front of the queue (:issue:`2940`) +- **Cogs - Audio** - ``[p]shuffle`` now has an additional argument to tell the bot whether it should shuffle bumped tracks (:issue:`2940`) +- **Cogs - Audio** - Added global whitelist/blacklist commands (:issue:`3047`) +- **Cogs - Audio** - Added self-managed daily playlists in the GUILD scope, these are called "Daily playlist - YYYY-MM-DD" and auto delete after 7 days (:issue:`3199`) +- **Cogs - Bank** - Added ``[p]bankset maxbal`` to set the maximum bank balance (:issue:`2926`) +- **Cogs - Economy** - Added new commands for pruning bank accounts (:issue:`2845`) + + - ``[p]bank prune user`` - This will delete a user's bank account. + - ``[p]bank prune local`` - This will prune the bank of accounts for users who are no longer in the server. + - ``[p]bank prune global`` - This will prune the global bank of accounts for users who do not share any servers with the bot. +- **Cogs - Downloader** - Added ``[p]repo update [repos]`` which updates repos without updating the cogs from them (:issue:`2527`) +- **Cogs - Downloader** - Added ``[p]cog installversion `` which installs cogs from a specified revision (commit, tag) of the given repo. When using this command, the cog will automatically be pinned (:issue:`2527`) +- **Cogs - Downloader** - Added ``[p]cog pin `` and ``[p]cog unpin `` for pinning cogs. Cogs that are pinned will not be updated when using update commands (:issue:`2527`) +- **Cogs - Downloader** - Added ``[p]cog checkforupdates`` that lists which cogs can be updated (including pinned cog) without updating them (:issue:`2527`) +- **Cogs - Downloader** - Added ``[p]cog updateallfromrepos `` that updates all cogs from the given repos (:issue:`2527`) +- **Cogs - Downloader** - Added ``[p]cog updatetoversion [cogs]`` that updates all cogs or ones of user's choosing to chosen revision of the given repo (:issue:`2527`) +- **Cogs - Downloader** - Added ``[p]cog reinstallreqs`` that reinstalls cog requirements and shared libraries for all installed cogs (:issue:`3167`) +- **Cogs - Trivia - Lists** - Added trivia lists for Prince and Michael Jackson lyrics (:issue:`12`) + +Changes +******* + +- Red now takes less time to fetch cases, unban members, and list warnings (:issue:`2964`) +- **Core** - JSON config files are now stored without indentation, this is to reduce the file size and increase the performance of write operations (:issue:`2921`) +- **Core** - Red now handles more things prior to connecting to discord to reduce issues during the initial load (:issue:`3045`) +- **Core** - Red will now send a message when the invoked command is DM-only (:issue:`3057`) +- **Core** - The lib folder is now cleared on minor Python version changes. ``[p]cog reinstallreqs`` in Downloader can be used to regenerate the lib folder for a new Python version (:issue:`3274`) +- **Core** - If Red detects operating system or architecture change, it will now warn the owner about possible problems with the lib folder (:issue:`3274`) +- **Core - Bot Commands** - Changed ``[p]info`` to say "This bot is an..." instead of "This is an..." for clarity (:issue:`3121`) +- **Core - Bot Commands** - Added the Python executable field to ``[p]debuginfo`` (:issue:`3184`) +- **Core - Command-line Interfaces** - Added the option to modify the RPC port with the ``--rpc-port`` flag (:issue:`2429`) +- **Core - Command-line Interfaces** - ``--[no-]backup``, ``--[no-]drop-db`` and ``--[no-]remove-datapath`` in the ``redbot-setup delete`` command are now on/off flags (:issue:`2958`) +- **Core - Command-line Interfaces** - The confirmation prompts in ``redbot-setup`` now have default values for user convenience (:issue:`2958`) +- **Core - Command-line Interfaces** - ``redbot-setup delete`` now has the option to leave Red's data untouched on database backends (:issue:`2962`) +- **Core - Command-line Interfaces** - All ``y/n`` confirmations in cli commands are now unified (:issue:`3060`) +- **Core - Command-line Interfaces** - ``redbot-setup`` will now use the instance name in default data paths to avoid creating a second instance with the same data path (:issue:`3171`) +- **Core - Command-line Interfaces** - Instance names can now only include characters A-z, numbers, underscores, and hyphens. Old instances are unaffected by this change (:issue:`3171`) +- **Core - Command-line Interfaces** - When Red prompts for a token, it will now print a link to the guide explaining how to obtain a token (:issue:`3204`) +- **Core - Command-line Interfaces** - ``redbot-setup`` will no longer log to disk (:issue:`3269`) +- **Core - Dependencies** - Bumped dependency versions (:issue:`3288`) +- **Core - Dependencies** - Bumped Red-Lavalink version (:issue:`3290`) +- **Core - Modlog** - Modlog no longer generates cases without being told to for actions the bot did (:issue:`2897`) +- **Core - Modlog** - Modlog is now much faster at creating cases, especially in large servers (:issue:`2908`) +- **Cogs - Admin** - Changed ``[p]announce ignore`` and ``[p]announce channel`` to ``[p]announceset ignore`` and ``[p]announceset channel`` (:issue:`3250`) +- **Cogs - Admin** - Changed ``[p]selfrole `` to ``[p]selfrole add ``, changed ``[p]selfrole add`` to ``[p]selfroleset add`` , and changed ``[p]selfrole delete`` to ``[p]selfroleset remove`` (:issue:`3250`) +- **Cogs - Admin** - Added custom issue messages for adding and removing roles, this makes it easier to create translations (:issue:`3016`) +- **Cogs - Audio** - ``[p]playlist download`` will now compress playlists larger than the server attachment limit and attempt to send that (:issue:`3279`) +- **Cogs - Audio** - ``[p]playlist upload`` will now load playlists generated via ``[p]playlist download`` much faster if the playlist uses the new scheme (:issue:`2861`) +- **Cogs - Audio** - ``[p]playlist`` commands now can be used by everyone regardless of DJ settings, however it will respect DJ settings when creating/modifying playlists in the server scope (:issue:`2861`) +- **Cogs - Audio** - Spotify, Youtube Data, and Lavalink API calls can now be cached to avoid repeated calls in the future, see ``[p]audioset cache`` (:issue:`2890`) +- **Cogs - Audio** - Playlists will now start playing as soon as first track is loaded (:issue:`2890`) +- **Cogs - Audio** - ``[p]audioset localpath`` can set a path anywhere in your machine now. Note: This path needs to be visible by ``Lavalink.jar`` (:issue:`2904`) +- **Cogs - Audio** - ``[p]queue`` now works when there are no tracks in the queue, showing the track currently playing (:issue:`2904`) +- **Cogs - Audio** - ``[p]audioset settings`` now reports Red Lavalink version (:issue:`2904`) +- **Cogs - Audio** - Adding and removing reactions in Audio is no longer a blocking action (:issue:`2904`) +- **Cogs - Audio** - When shuffle is on, queue now shows the correct play order (:issue:`2904`) +- **Cogs - Audio** - ``[p]seek`` and ``[p]skip`` can now be used by user if they are the song requester while DJ mode is enabled and votes are disabled (:issue:`2904`) +- **Cogs - Audio** - Adding a playlist and an album to a saved playlist now skips tracks already in the playlist (:issue:`2904`) +- **Cogs - Audio** - DJ mode is now turned off if the DJ role is deleted (:issue:`2904`) +- **Cogs - Audio** - When playing a localtrack, ``[p]play`` and ``[p]bumpplay`` no longer require the use of the prefix "localtracks\\" (:issue:`2904`) + + Before: ``[p]bumpplay localtracks\ENM\501 - Inside The Machine.mp3`` + Now: ``[p]bumpplay ENM\501 - Inside The Machine.mp3`` + Now nested folders: ``[p]bumpplay Parent Folder\Nested Folder\track.mp3`` +- **Cogs - Audio** - Removed commas in explanations about how to set API keys (:issue:`2905`) +- **Cogs - Audio** - Expanded local track support to all file formats (m3u, m4a, mp4, etc) (:issue:`2940`) +- **Cogs - Audio** - Cooldowns are now reset upon failure of commands that have a cooldown timer (:issue:`2940`) +- **Cogs - Audio** - Improved the explanation in the help string for ``[p]audioset emptydisconnect`` (:issue:`3051`) +- **Cogs - Audio** - Added a typing indicator to playlist dedupe (:issue:`3058`) +- **Cogs - Audio** - Exposed clearer errors to users in the play commands (:issue:`3085`) +- **Cogs - Audio** - Improved error handling when the player is unable to play multiple tracks in the sequence (:issue:`3165`) +- **Cogs - CustomCommands** - The group command ``[p]cc create`` can now be used to create simple CCs without specifying "simple" (:issue:`1767`) +- **Cogs - CustomCommands** - Added a query option for CC typehints for URL-based CCs (:issue:`3228`) +- **Cogs - CustomCommands** - Now uses the ``humanize_list()`` utility for iterable parameter results, e.g. ``{#:Role.members}`` (:issue:`3277`) +- **Cogs - Downloader** - During cog update, Downloader will now check if the Python and bot versions match requirements provided by the cog author(s) in ``info.json`` (:issue:`1866`) +- **Cogs - Downloader** - ``[p]cog install`` now accepts multiple cog names (:issue:`2527`) +- **Cogs - Downloader** - When passing cogs to ``[p]cog update``, it will now only update those cogs, not all cogs from the repo those cogs are from (:issue:`2527`) +- **Cogs - Downloader** - Added error messages for failures when installing/reinstalling requirements and copying cogs and shared libraries (:issue:`2571`) +- **Cogs - Downloader** - ``[p]findcog`` now uses sanitized urls (without HTTP Basic Auth fragments) (:issue:`3129`) +- **Cogs - Downloader** - ``[p]repo info`` will now show the repo's url, branch, and authors (:issue:`3225`) +- **Cogs - Downloader** - ``[p]cog info`` will now show cog authors (:issue:`3225`) +- **Cogs - Downloader** - ``[p]findcog`` will now show the repo's branch (:issue:`3225`) +- **Cogs - Economy** - Slots now has a 62.5% expected payout and will not inflate economy when spammed (:issue:`2875`) +- **Cogs - Image** - Updated the ``[p]giphycreds`` command to match the formatting of the other API commands (:issue:`2905`) +- **Cogs - Image** - Removed commas from explanations about how to set API keys (:issue:`2905`) +- **Cogs - Mod** - ``[p]slowmode`` now accepts integer-only inputs as seconds (:issue:`2884`) +- **Cogs - Permissions** - Better explained the usage of commands with the ```` argument (:issue:`2991`) +- **Cogs - Streams** - Removed commas from explanations about how to set API keys (:issue:`2905`) + +Removals +******** + +- **Core - Bot Commands** - ``[p]set owner`` and ``[p]set token`` have been removed in favor of ``redbot --edit`` (:issue:`2928`) +- **Core - Bot Commands** - Removed ``[p]backup``. Use the cli command ``redbot-setup backup`` instead (:issue:`3235`) +- **Core - Command-line Interfaces** - Removed a lot of the functionality of ``redbot-launcher`` and deprecated what's left (:issue:`3289`) +- **Cogs - Downloader** - Shared libraries are marked for removal in Red 3.4 (:issue:`3106`) + +Fixes +***** + +- **Core** - Red no longer types infinitely when a command with a cooldown is called within the last second of a cooldown. (:issue:`2985`) +- **Core** - Added a 3rd-party lib folder to ``sys.path`` before loading cogs. This prevents issues with 3rd-party cogs failing to load when Downloader is not loaded to install requirements (:issue:`3036`) +- **Core** - Red will now properly send an error message when the invoked command is guild-only (:issue:`3057`) +- **Core** - Red now always appends the 3rd-party lib folder to the end of ``sys.path`` to avoid shadowing Red's dependencies (:issue:`3062`) +- **Core** - Guild owners are no longer affected by the local whitelist and blacklist (:issue:`3221`) +- **Core - Bot Commands** - The ``[p]invite`` command no longer errors when a user has the bot blocked or DMs disabled in the server (:issue:`2948`) +- **Core - Bot Commands** - Cleaned up the ``[p]inviteset public`` and ``[p]inviteset perms`` help strings (:issue:`2963`) +- **Core - Bot Commands** - ```[p]embedset user`` now only affects DM's (:issue:`2966`) +- **Core - Bot Commands** - Fixed the help text and response of ``[p]set usebotcolor`` to accurately reflect what the command is doing (:issue:`2974`) +- **Core - Bot Commands** - Fixed an error in ``[p]uptime`` when the uptime is under a second (:issue:`3009`) +- **Core - Bot Commands** - Added quotation marks to the response of ``[p]helpset tagline`` so that two consecutive full stops do not appear (:issue:`3010`) +- **Core - Bot Commands** - Red will now prevent users from locking themselves out with ``[p]localblacklist`` (:issue:`3207`) +- **Core - Bot Commands** - Fixed formatting issues in commands that list whitelisted/blacklisted users/roles when the list is empty (:issue:`3219`) +- **Core - Bot Commands** - Red will now prevent users from locking the guild owner out with ``[p]localblacklist`` (unless the command caller is bot owner) (:issue:`3221`) +- **Core - Command-line Interfaces** - Stopped using the ``:`` character in backup's filename - Windows doesn't accept it (:issue:`2954`) +- **Core - Command-line Interfaces** - ``redbot-setup delete`` no longer errors with "unexpected keyword argument" (:issue:`2955`) +- **Core - Command-line Interfaces** - ``redbot-setup delete`` no longer prompts about backup when the user passes the option ``--no-prompt`` (:issue:`2956`) +- **Core - Command-line Interfaces** - Fixed an unfriendly error when the provided instance name doesn't exist (:issue:`2968`) +- **Core - Command-line Interfaces** - Removed f-string usage in the launcher to prevent our error handling from causing an error (:issue:`3002`) +- **Core - Command-line Interfaces** - Arguments ``--co-owner`` and ``--load-cogs`` now properly require at least one argument to be passed (:issue:`3060`) +- **Core - Command-line Interfaces** - Fixed the generation of the ``repos.json`` file in the backup process (:issue:`3114`) +- **Core - Command-line Interfaces** - Added handling for invalid folder names in the data path gracefully in ``redbot-setup`` and ``redbot --edit`` (:issue:`3171`) +- **Core - Command-line Interfaces** - ``--owner`` and ``-p`` cli flags now work when added from launcher (:issue:`3174`) +- **Core - Help** - Help now properly hides disabled commands (:issue:`2863`) +- **Core - Help** - Fixed help ending up a little too large for Discord embed limits (:issue:`3208`) +- **Core - Modlog** - Modlog entries now show up properly without the mod cog loaded (:issue:`2897`) +- **Core - Modlog** - Removed potential for additional bad API calls per ban/unban (:issue:`2945`) +- **Cogs - Admin** - Fixed ``[p]announce`` failing after encountering an error attempting to message the bot owner (:issue:`3166`) +- **Cogs - Admin** - Improved the clarity of user facing messages when the user is not allowed to do something due to Discord hierarchy rules (:issue:`3250`) +- **Cogs - Admin** - Fixed some role managing commands not properly checking if Red had Manage Roles permission before attempting to manage roles (:issue:`3250`) +- **Cogs - Admin** - Fixed commands from ``[p]editrole`` command group not checking if roles to be edited are higher than Red's highest role before trying to edit them (:issue:`3250`) +- **Cogs - Admin** - Fixed ``[p]announce ignore`` and ``[p]announce channel`` not being able to be used by guild owners and administrators (:issue:`3250`) +- **Cogs - Audio** - ``[p]playlist remove`` now removes the playlist url if the playlist was created through ``[p]playlist save`` (:issue:`2861`) +- **Cogs - Audio** - Users are no longer able to accidentally overwrite existing playlist if a new one with the same name is created/renamed (:issue:`2861`) +- **Cogs - Audio** - ``[p]audioset settings`` no longer shows lavalink JAR version (:issue:`2904`) +- **Cogs - Audio** - Fixed a ``KeyError: loadType`` when trying to play tracks (:issue:`2904`) +- **Cogs - Audio** - ``[p]audioset settings`` now properly considers co-owners as owners (:issue:`2904`) +- **Cogs - Audio** - Fixed track indexes being off by 1 in ``[p]search`` (:issue:`2940`) +- **Cogs - Audio** - Fixed an issue where updating your Spotify and YouTube Data API tokens did not refresh them (:issue:`3047`) +- **Cogs - Audio** - Fixed an issue where the blacklist was not being applied correctly (:issue:`3047`) +- **Cogs - Audio** - Fixed an issue in ``[p]audioset restrictions blacklist list`` where it would call the list a ``Whitelist`` (:issue:`3047`) +- **Cogs - Audio** - Red's status is now properly cleared on emptydisconnect (:issue:`3050`) +- **Cogs - Audio** - Fixed a console spam caused sometimes when auto disconnect and auto pause are used (:issue:`3123`) +- **Cogs - Audio** - Fixed an error that was thrown when running ``[p]audioset dj`` (:issue:`3165`) +- **Cogs - Audio** - Fixed a crash that could happen when the bot can't connect to the Lavalink node (:issue:`3238`) +- **Cogs - Audio** - Restricted the number of songs shown in the queue to first 500 to avoid heartbeats (:issue:`3279`) +- **Cogs - Audio** - Added more cooldowns to playlist commands and restricted the queue and playlists to 10k songs to avoid bot errors (:issue:`3286`) +- **Cogs - Audio** - Lavalink will now be restarted after an unexpected shutdown (:issue:`3033`) +- **Cogs - Audio** - Track descriptions are now escaped so that they do not break markdown (:issue:`3047`) +- **Cogs - Audio** - Fixed an issue where calling Audio commands when not in a voice channel could result in a crash (:issue:`3120`) +- **Cogs - Audio** - Fixed an issue where some YouTube playlists were being recognised as single tracks (:issue:`3104`) +- **Cogs - Downloader** - Made the regex for repo names use raw strings to stop causing a ``DeprecationWarning`` for invalid escape sequences (:issue:`2571`) +- **Cogs - Downloader** - Downloader will no longer attempt to install cogs that are already installed (:issue:`2571`) +- **Cogs - Downloader** - Repo names can now only contain the characters listed in the help text (A-Z, 0-9, underscores, and hyphens) (:issue:`2827`) +- **Cogs - Downloader** - ``[p]findcog`` no longer attempts to find a cog for commands without a cog (:issue:`2902`) +- **Cogs - Downloader** - Downloader will no longer attempt to install a cog with same name as another cog that is already installed (:issue:`2927`) +- **Cogs - Downloader** - Added error handling for when a remote repository or branch is deleted; now it notifies which repository failed and continues to update the others (:issue:`2936`) +- **Cogs - Downloader** - ``[p]cog install`` will no longer error if a cog has an empty install message (:issue:`3024`) +- **Cogs - Downloader** - Fixed an error on ``[p]repo add`` from empty string values for the ``install_msg`` info.json field (:issue:`3153`) +- **Cogs - Downloader** - Disabled all git auth prompts when adding/updating a repo with Downloader (:issue:`3159`) +- **Cogs - Downloader** - ``[p]findcog`` now properly works for cogs with less typical folder structure (:issue:`3177`) +- **Cogs - Downloader** - ``[p]cog uninstall`` now fully unloads cog - the bot will not try to load it on next startup (:issue:`3179`) +- **Cogs - Economy** - Fixed a crash seen when calling economy commands in DM with a global bank (:issue:`2997`) +- **Cogs - Mod** - ``[p]userinfo`` no longer breaks when a user has an absurd number of roles (:issue:`2910`) +- **Cogs - Mod** - Fixed Mod cog not recording username changes for ``[p]names`` and ``[p]userinfo`` commands (:issue:`2918`) +- **Cogs - Mod** - Fixed ``[p]modset deletedelay`` deleting non-command messages (:issue:`2924`) +- **Cogs - Mod** - Fixed an error when reloading Mod (:issue:`2932`) +- **Cogs - Modlog** - Fixed an error in ``[p]reason`` when setting the reason for a case without a moderator (:issue:`2908`) +- **Cogs - Permissions** - Defaults are now cleared properly when clearing all rules (:issue:`3037`) +- **Cogs - Permissions** - Fixed an issue with clearing rules in permissions (:issue:`3014`) +- **Cogs - Streams** - Fixed a ``TypeError`` in the ``TwitchStream`` class when calling Twitch client_id from Red shared APIs tokens (:issue:`3042`) +- **Cogs - Streams** - Changed the ``stream_alert`` function for Twitch alerts to make it work with how the ``TwitchStream`` class works now (:issue:`3042`) +- **Cogs - Trivia** - Fixed a bug where ``[p]trivia leaderboard`` failed to run (:issue:`2911`) +- **Cogs - Trivia - Lists** - Fixed a typo in Ahsoka Tano's name in the Starwars trivia list (:issue:`2909`) +- **Cogs - Trivia - Lists** - Fixed a typo in the Greek mythology trivia list regarding Hermes' staff (:issue:`2994`) +- **Cogs - Trivia - Lists** - Fixed a question in the Overwatch trivia list that accepted blank responses (:issue:`2996`) +- **Cogs - Trivia - Lists** - Fixed questions and answers that were incorrect in the Clash Royale trivia list (:issue:`3236`) + + +.. _important-320-1: + +Developer changelog +------------------- + +Breaking Changes +**************** + +- **Core** - Extension's ``setup()`` function should no longer assume that we are, or even will be connected to Discord (:issue:`3073`) + + This also means that cog creators should no longer use ``bot.wait_until_ready()`` inside it +- **Core - Bank** - Removed ``bank.MAX_BALANCE``, use `redbot.core.bank.get_max_balance()` from now on (:issue:`2926`) +- **Core - Commands Package** - Reserved some command names for internal Red use. These are available programatically as ``redbot.core.commands.RESERVED_COMMAND_NAMES`` (:issue:`2973`) +- **Core - Commands Package** - Qualified command names are limited to a maximum of 60 characters (:issue:`3223`) +- **Core - Bot Class** - The main bot config is no longer directly accessible to cogs. New methods have been added for use where this is concerned (:issue:`2967`) + + New methods for this include: + + - `Red.get_shared_api_tokens()` + - `Red.set_shared_api_tokens()` + - `Red.get_embed_colour()` (and its alias - ``get_embed_color()``) + - `Red.get_admin_roles()` + - `Red.get_admin_role_ids()` + - `Red.get_mod_roles()` + - `Red.get_mod_role_ids()` +- **Core - Bot Class** - Removed ``bot._counter``, Made a few more attributes private (``cog_mgr``, ``main_dir``) (:issue:`2976`) +- **Core - Modlog** - Modlog casetypes no longer have an attribute for auditlog action type (:issue:`2897`) +- **Core - Modlog** - Removed ``redbot.core.modlog.get_next_case_number()`` (:issue:`2908`) + +Additions +********* + +- Added a few methods and classes replacing direct config access (which is no longer supported) (:issue:`2976`) + + - `Red.allowed_by_whitelist_blacklist()` + - `Red.get_valid_prefixes()` + - `Red.remove_shared_api_tokens()` + - `redbot.core.commands.help.HelpSettings` +- **Core - Bot Class** - Added the method `Red.wait_until_red_ready()` that waits until Red's post connection startup is done (:issue:`3273`) +- **Core - Bot Class** - New event ``on_red_api_tokens_update`` is now dispatched when shared api keys for a service are updated (:issue:`3134`) +- **Core - Config** - Added functions to acquire locks on Config groups and values. These locks are acquired by default when calling a value as a context manager. See `Value.get_lock()` for details (:issue:`2654`) +- **Core - Config** - Added methods to Config for accessing things by id without mocked objects (:issue:`2804`) + + - `Config.guild_from_id()` + - `Config.user_from_id()` + - `Config.role_from_id()` + - `Config.channel_from_id()` + - `Config.member_from_ids()` + - This one requires multiple ids, one for the guild, one for the user + - Consequence of discord's object model +- **Core - Modlog** - Added ``redbot.core.modlog.get_latest_case()`` to fetch the case object for the most recent Modlog case (:issue:`2908`) +- **Core - Utils Package** - Added `redbot.core.utils.chat_formatting.humanize_number()` function to convert numbers into text that respects the current locale (:issue:`2836`) +- **Core - Utils Package** - Added the function `redbot.core.utils.chat_formatting.text_to_file()` to prepare a long text to be sent as a file (:issue:`2849`) +- **Core - Utils Package** - Added ``use_cached`` and ``images_only`` kwargs to `redbot.core.utils.tunnel.Tunnel.files_from_attach()` (:issue:`2885`) +- **Cogs - Audio** - New events dispatched by Audio (:issue:`2904`) + + - ``on_red_audio_track_start(guild: discord.Guild, track: lavalink.Track, requester: discord.Member)`` + - ``on_red_audio_track_end(guild: discord.Guild, track: lavalink.Track, requester: discord.Member)`` + - ``on_red_audio_track_enqueue(guild: discord.Guild, track: lavalink.Track, requester: discord.Member)`` + - ``on_red_audio_track_auto_play(guild: discord.Guild, track: lavalink.Track, requester: discord.Member)`` + - ``on_red_audio_queue_end(guild: discord.Guild, track: lavalink.Track, requester: discord.Member)`` + - ``on_red_audio_audio_disconnect(guild: discord.Guild)`` + - ``on_red_audio_skip_track(guild: discord.Guild, track: lavalink.Track, requester: discord.Member)`` + +Changes +******* + +- **Core - Bot Class** - `Red.send_filtered()` now returns the message that is sent (:issue:`3052`) +- **Core - Bot Class** - `Red.send_to_owners()` and `Red.get_owner_notification_destinations()` now log when they are not able to find the owner notification destination (:issue:`3273`) +- **Core - Commands Package** - Allowed passing ``cls`` in the `redbot.core.commands.group()` decorator (:issue:`2881`) +- **Core - Modlog** - Some generic modlog casetypes are now pre-registered for cog creator use (:issue:`2897`) + +Removals +******** + +- **Core - Utils Package** - Removed the functions ``safe_delete``, ``fuzzy_command_search``, ``format_fuzzy_results`` and ``create_backup`` from ``redbot.core.utils`` (:issue:`3240`) + +Fixes +***** + +- **Core - Bank** - Bank functions now check the recipient balance before transferring and stop the transfer if the recipient's balance will go above the maximum allowed balance (:issue:`2923`) +- **Core - Bot Class** - Fixed `Red.remove_command()` throwing an error when trying to remove a non-existent command (:issue:`2888`) +- **Core - Bot Class** - Fixed ``is_automod_immune``'s handling of the guild check and added support for checking webhooks (:issue:`3100`) +- **Core - Bot Class** - ``Red.owner_id`` is now set in the post connection startup (:issue:`3273`) +- **Core - Bot Class** - `Red.send_to_owners()` and `Red.get_owner_notification_destinations()` now wait until Red is done with post connection startup to ensure owner ID is available (:issue:`3273`) +- **Core - Commands Package** - `Command.can_see()` now works as intended for disabled commands (:issue:`2892`) +- **Core - Commands Package** - Fixed ``Context.clean_prefix`` issues resulting from undocumented changes from discord (:issue:`3249`) +- **Core - Utils Package** - Fixed `MessagePredicate.greater()` and `MessagePredicate.less()` allowing any valid int instead of only valid ints/floats that are greater/less than the given value (:issue:`3004`) +- **Core - Utils Package** - Fixed an attribute error that can be raised in `redbot.core.utils.chat_formatting.humanize_timedelta()` if ``seconds = 0`` (:issue:`3231`) + + +Documentation changes +--------------------- + +Additions +********* + +- Started the user guides covering cogs and the user interface of the bot. This includes, for now, a "Getting started" guide (:issue:`1734`) +- Added documentation for PM2 support (:issue:`2105`) +- Updated linux install docs, adding sections for Fedora Linux, Debian/Raspbian Buster, and openSUSE (:issue:`2558`) +- Created documentation covering what we consider a developer facing breaking change and the guarantees regarding them (:issue:`2882`) +- Added notes explaining the best practices with config (:issue:`3149`) +- Added a "Publishing cogs for V3" document explaining how to make user's cogs work with Downloader (:issue:`3234`) + +Changes +******* + +- Reworded the virtual environment guide to make it sound less scary (:issue:`2920`) +- Added more information about `redbot.core.utils.chat_formatting.humanize_timedelta()` into the docs (:issue:`2986`) +- Added a direct link to the "Installing Red" section in "Installing using powershell and chocolatey" (:issue:`2995`) +- Updated Git PATH install (Windows), capitalized some words, stopped mentioning the launcher (:issue:`2998`) +- Added autostart documentation for Red users who installed Red inside of a virtual environment (:issue:`3005`) +- Updated the Cog Creation guide with a note regarding the Develop version as well as the folder layout for local cogs (:issue:`3021`) +- Added links to the getting started guide at the end of installation guides (:issue:`3025`) +- Added proper docstrings to enums that show in drivers docs (:issue:`3035`) +- Discord.py doc links will now always use the docs for the currently used version of discord.py (:issue:`3053`) +- Added ``|DPY_VERSION|`` substitution that will automatically get replaced by the current discord.py version (:issue:`3053`, :issue:`3082`) +- Added MS Azure to the host list (:issue:`3083`) +- Added information about ``info.json``'s ``min_python_version`` key in Downloader Framework docs (:issue:`3124`) +- Documented additional attributes in Context (:issue:`3151`) +- Updated Windows docs with up to date dependency instructions (:issue:`3188`) +- Added a line about setuptools and wheel (:issue:`3262`) +- Ensured development builds are not advertised to the wrong audience (:issue:`3292`) +- Clarified the usage intent of some of the chat formatting functions (:issue:`3292`) + +Removals +******** + +- Removed API References for Downloader (:issue:`3234`) + +Fixes +***** + +- Fixed the user parameter being labeled as ``discord.TextChannel`` instead of ``discord.abc.User`` in ``redbot.core.utils.predicates`` (:issue:`2914`) +- Driver docs no longer show twice (:issue:`2972`) +- Added missing descriptions for function returns (:issue:`3054`) +- Fixed broken docs for ``redbot.core.commands.Context.react_quietly`` (:issue:`3257`) +- Updated the docs footer copyright to 2018-2019 (:issue:`3105`) +- Updated copyright notices on License and RTD config to 2020 (:issue:`3259`) + +---- + +Redbot 3.1.9 (2020-01-08) +========================= + +This is a maintenance release patching a denial of service issue with Audio. + +---- + +Redbot 3.1.8 (2019-11-19) +========================= + +This is a hotfix release updating discord.py to fix a full bot crash when emoji reaction is added/removed. +This was caused by Discord API changes. + +---- + +Redbot 3.1.7 (2019-11-05) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`mikeshardmind` + +End-user changelog +------------------ + +Changes +******* + +- **Cogs - Audio** - Improved handling of user facing errors (`989e16b `__) + +Fixes +***** + +- **Core - Dependencies** - Added partial mitigation for issues with running Red on Python 3.8 (`1c64abe `__) +- **Cogs - Audio** - Fixed issues with SoundCloud playback (`989e16b `__) + +---- + +Redbot 3.1.6 (2019-10-18) +========================= + +This is a hotfix release updating discord.py for a critical issue related to voice connections. + +---- + +Redbot 3.1.5 (2019-07-31) +========================= + +This is a maintenance release fixing issues with playback of YouTube tracks. + +---- + +Redbot 3.1.4 (2019-07-16) +========================= + +This is a hotfix release fixing issues with broken custom commands and modlog cases. + +---- + +Redbot 3.1.3 (2019-07-14) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`Bakersbakebread`, :ghuser:`DevilXD`, :ghuser:`DiscordLiz`, :ghuser:`Drapersniper`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`kennnyshiwa`, :ghuser:`Kowlin`, :ghuser:`lizzyd710`, :ghuser:`MeatyChunks`, :ghuser:`mikeshardmind`, :ghuser:`NeuroAssassin`, :ghuser:`PredaaA`, :ghuser:`retke`, :ghuser:`Tobotimus`, :ghuser:`yamikaitou` + +End-user changelog +------------------ + +Additions +********* + +- **Core - Bot Commands** - Added new settings for the invite returned by ``[p]invite`` command (:issue:`1847`) + + - ``[p]inviteset public`` - Defines if the command should be accessible for users that aren't bot owners. + - ``[p]inviteset perms`` - Sets permissions for bot's managed role that can get created automatically when bot is invited. + + For more information, see help of each of the listed commands. +- **Cogs - Audio** - Added a ``[p]eq`` command group that allows to manage the Audio equalizer (:issue:`2787`, :issue:`2813`) +- **Cogs - Audio** - Added a ``[p]summon`` command that summons the bot to the voice channel (:issue:`2786`) + +Changes +******* + +- **Core** - A server can now have multiple admin and mod roles (:issue:`2783`) +- **Core - Dependencies** - Improved overall performance on Linux and Mac systems by swapping asyncio loop implementation to uvloop (:issue:`2819`) +- **Cogs - Audio** - Added support for armv6l, aarch32, and aarch64 architectures (:issue:`2755`) +- **Cogs - Audio** - Improved error handling and added retrying to jar download and Lavalink connection (:issue:`2764`) +- **Cogs - Audio** - Internal Lavalink manager now accepts any jar with a build number greater than or equal to our release build (:issue:`2656`, :issue:`2785`) +- **Cogs - Audio** - Increased Lavalink connection timeout to 50 seconds to ensure Lavalink manages to start before the time runs out on lower-end devices (:issue:`2784`) +- **Cogs - Filter** - Updated name filtering to be consistent with message content filtering (:issue:`2740`, :issue:`2794`) +- **Cogs - Mod** - ``[p]userinfo`` command now mentions the roles the user has (:issue:`2759`) +- **Cogs - Modlog** - Improved efficiency of case storage (:issue:`2766`) + +Fixes +***** + +- **Core** - Fixed broken fuzzy help (:issue:`2768`) +- **Core** - Fixed a race condition that could allow a user to run commands they are denied to run by Permissions cog for a short moment before the cog is loaded (:issue:`2857`) +- **Core - Bot Commands** - ``[p]command disable`` and its subcommands now ensure that the command to disable is not ``[p]command`` or any of its subcommands to prevent lockout (:issue:`2770`) +- **Core - Bot Commands** - Fixed an issue with error message being sent multiple times when help command was unable to DM the user (:issue:`2790`) +- **Core - Bot Commands** - Fixed broken link in help of ``[p]set color`` (:issue:`2715`, :issue:`2803`) +- **Core - Bot Commands** - Fixed user output and exception handling on cog load/reload (:issue:`2767`) +- **Core - Help** - Fixed substitution of ``[p]`` in command descriptions in non-embedded help output (:issue:`2846`) +- **Cogs - Audio** - Added missing bot permission checks to commands in Audio cog (:issue:`2756`) +- **Cogs - Audio** - Fixed an issue with jar downloading on mixed-filesystem environments (:issue:`2682`, :issue:`2765`) +- **Cogs - Audio** - Fixed an issue with ``[p]playlist copy`` and ``[p]playlist queue`` failing when the prefix contains certain characters (:issue:`2788`, :issue:`2789`) +- **Cogs - Audio** - Fixed an issue that caused ``[p]shuffle`` and ``[p]repeat`` to send an error message when the user is not in the voice channel (:issue:`2811`, :issue:`2812`, :issue:`2842`) +- **Cogs - Filter** - Fixed caching issue that caused filter to use an old list of words to filter (:issue:`2810`) +- **Cogs - Permissions** - Commands for adding/removing rules in ``[p]permissions`` command group now no longer ignore invalid arguments (:issue:`2851`, :issue:`2865`) +- **Cogs - Trivia - Lists** - Fixed answers for Beethoven-related questions in ``entertainment`` trivia list (:issue:`2318`, :issue:`2823`) + + +Developer changelog +------------------- + +Additions +********* + +- **Core** - Added ``UserFeedbackCheckFailure`` (:issue:`2761`) +- **Core - Bank** - Added `redbot.core.bank.cost()` (:issue:`2761`) +- **Core - Commands Package** - Added (optional) ``default_unit`` keyword argument to `TimedeltaConverter` (:issue:`2753`) +- **Core - Commands Package** - Added `Context.react_quietly()` (:issue:`2834`) + +Fixes +***** + +- **Core - Config** - Fixed cache issues with Config when the developer accidentally tries to set an object that isn't JSON serializable (:issue:`2793`, :issue:`2796`) +- **Core - Config** - Fixed an issue with identifiers that contain ``$`` or ``.`` which has caused a KeyError exception regardless of whether such key existed in the data (:issue:`2832`) + + +Documentation changes +--------------------- + +Changes +******* + +- Added a warning about the PATH changes to Windows install guide (:issue:`2791`) + +Fixes +***** + +- Fixed code examples in Bank, Config, and ModLog API documentation (:issue:`2775`, :issue:`2780`, :issue:`2860`) +- Fixed the code example for the documentation of `Command.error` decorator and added a note with clarifications (:issue:`2760`) + +---- + +Redbot 3.1.2 (2019-05-31) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`bren0xa`, :ghuser:`DevilXD`, :ghuser:`DiscordLiz`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`mikeshardmind`, :ghuser:`NeuroAssassin`, :ghuser:`palmtree5`, :ghuser:`PredaaA`, :ghuser:`retke`, :ghuser:`Stonedestroyer`, :ghuser:`Tobotimus`, :ghuser:`yamikaitou`, :ghuser:`zephyrkul` + +End-user changelog +------------------ + +Additions +********* + +- **Core** - Added a generic system that can be used by cog creators to send notifications meant for bot owners (:issue:`2665`, :issue:`2738`, :issue:`2745`) +- **Core - Bot Commands** - Added ``[p]debuginfo`` command (:issue:`2728`) + + This comes with some commands that allow to manage the destinations for the owner notifications. + See the help of commands in ``[p]set ownernotifications`` command group for more information. +- **Core - Help** - Added a few new settings for bot's help (:issue:`2667`, :issue:`2681`, :issue:`2676`) + + - ``[p]helpset usemenus`` - Allows the help command to be sent as a paginated menu. + - ``[p]helpset showhidden`` - Allows the help command to show hidden commands. + - ``[p]helpset verifychecks`` - Sets if commands which can't be run in the current context should be filtered from help. + - ``[p]helpset verifyexists`` - Allows the bot to respond indicating the existence of a specific help topic even if the user can't use it. + + For more information, see help of each of the listed commands. +- **Cogs - Mod** - Added ``[p]slowmode`` command (:issue:`2734`) + +Changes +******* + +- **Core - Bot Commands** - ``[p]load``, ``[p]unload``, and ``[p]reload`` commands now strip commas from the passed cogs to aid with copy-pasting (:issue:`2693`) +- **Core - Bot Commands** - Improved naming consistency of subcommands that *delete* something (:issue:`2731`) +- **Core - Bot Commands** - ``[p]set api`` command now allows the user to separate their keys and values with space in addition to commas and semicolons (:issue:`2692`) +- **Cogs - Downloader** - ``[p]pipinstall`` now indicates that it's doing something (:issue:`2700`) +- **Cogs - Mod** - ``[p]names`` command no longer requires quoting usernames that contain spaces (:issue:`2675`) +- **Cogs - Mod** - ``[p]userinfo`` command now mentions the voice channel the user is in (:issue:`2680`) + +Fixes +***** + +- **Core** - Fixed update notification for bots that have co-owners (:issue:`2677`) +- **Core** - Fixed an issue where bad user input didn't result in the bot sending help for the command (:issue:`2707`) +- **Core - Help** - Fixed an issue with incorrect subcommand descriptions being shown in non-embed help (:issue:`2678`) +- **Core - Help** - Fixed help for commands with no docstring (:issue:`2415`, :issue:`2722`) +- **Core - Help** - Help menu no longer blocks settings preview in command groups like ``[p]set`` (:issue:`2712`, :issue:`2725`) +- **Core - Bot Commands** - Fixed few more issues with help command (:issue:`2676`) +- **Core - Bot Commands** - Fixed error handling in ``[p]load`` command (:issue:`2686`, :issue:`2688`) +- **Core - Bot Commands** - Fixed an issue with long cog descriptions in help command (:issue:`2730`) +- **Core - Command-line Interfaces** - Fixed ``redbot-setup delete`` command failing to delete data path (:issue:`2709`) +- **Cogs - Downloader** - Fixed problems with installing a cog again after uninstalling (:issue:`2685`, :issue:`2690`) +- **Cogs - General** - Fixed ``[p]urban`` command failure for very long phrase definitions (:issue:`2683`, :issue:`2684`) +- **Cogs - General** - Fixed issues with ``[p]gif`` and ``[p]gifr`` commands. The bot owner now needs to provide an API key in order to use these commands (:issue:`2653`) +- **Cogs - Streams** - Fixed an issue with stream commands not properly dealing with stream reruns (:issue:`2679`) +- **Cogs - Streams** - Fixed a regression that caused stream alerts for non-Twitch users to not work anymore (:issue:`2724`, :issue:`2699`) + + +Developer changelog +------------------- + +Additions +********* + +- **Core - Bot Class** - Added `Red.send_to_owners()` and `Red.get_owner_notification_destinations()` (:issue:`2665`, :issue:`2738`) +- **Core - Commands Package** - Added `DictConverter` (:issue:`2692`) +- **Core - Commands Package** - Added `TimedeltaConverter` and `parse_timedelta()` (:issue:`2736`) +- **Core - Commands Package** - Added ``assume_yes`` attribute to `redbot.core.commands.Context` (:issue:`2746`) + +Changes +******* + +- **Core - Utils Package** - `menu()` now accepts `functools.partial` (:issue:`2718`, :issue:`2720`) + +---- + +Redbot 3.1.1 (2019-05-15) +========================= + +This is a hotfix release fixing issues related to fuzzy command search that were happening with the new help formatter. + +---- + +Redbot 3.1.0 (2019-05-15) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`aikaterna`, :ghuser:`bobloy`, :ghuser:`calebj`, :ghuser:`DiscordLiz`, :ghuser:`EgonSpengler`, :ghuser:`entchen66`, :ghuser:`FixedThink`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`kennnyshiwa`, :ghuser:`Kowlin`, :ghuser:`lionirdeadman`, :ghuser:`mikeshardmind`, :ghuser:`NeuroAssassin`, :ghuser:`NIXC`, :ghuser:`palmtree5`, :ghuser:`PredaaA`, :ghuser:`retke`, :ghuser:`Seputaes`, :ghuser:`Sitryk`, :ghuser:`tekulvw`, :ghuser:`Tobotimus`, :ghuser:`TrustyJAID`, :ghuser:`Twentysix26`, :ghuser:`zephyrkul` + +End-user changelog +------------------ + +Known Issues +************ + +- **Core - Bot Commands** - Backup support for Mongo is currently broken (:issue:`2579`) + +Additions +********* + +- **Core - Bot Commands** - Added new ``[p]datapath`` command that prints the bot's data path (:issue:`2652`) +- **Core - Command-line Interfaces** - Added ``redbot-setup convert`` command which can be used to convert between data backends (:issue:`2579`) +- **Cogs - Audio** - Added Spotify support (:issue:`2328`) +- **Cogs - Audio** - ``[p]local folder`` now accepts folder name as (optional) argument (:issue:`2457`) +- **Cogs - Audio** - Added track length restriction (:issue:`2465`) +- **Cogs - Audio** - Added option for disconnection at queue end, see ``[p]audioset dc`` (:issue:`2472`) +- **Cogs - Audio** - Added ``[p]queue clean`` and ``[p]queue clear`` (:issue:`2476`) +- **Cogs - Audio** - Added ``[p]playlist download`` (:issue:`2482`) +- **Cogs - Mod** - Added the command ``[p]voicekick`` to kick members from a voice channel with optional modlog case (:issue:`2639`) +- **Cogs - Streams** - Added support for setting custom stream alert messages per server (:issue:`2600`) +- **Cogs - Streams** - Added ability to exclude Twitch stream reruns (:issue:`2620`) + +Changes +******* + +- **Core** - Error messages about cooldowns will now show more friendly representation of cooldown's expiration time (:issue:`2412`) +- **Core** - Cooldown messages are now auto-deleted after cooldown expiration expired (:issue:`2469`) +- **Core** - Updated Mongo driver to support large guilds (:issue:`2536`) +- **Core - Command-line Interfaces** - ``redbot --version`` will now give you current version of Red (:issue:`2567`) +- **Core - Help** - Redesigned help and its formatter (:issue:`2628`) +- **Cogs - Audio** - Changed ``[p]pause`` to a toggle (:issue:`2461`) +- **Cogs - Audio** - Removed command aliases (``dc``, ``np``, ``n``, ``song``, ``track``, ``q``, ``forceskip``, ``fs``, ``s``) (:issue:`2462`) +- **Cogs - Audio** - ``[p]seek`` command can now seek to position (:issue:`2470`) +- **Cogs - Audio** - Audio now matches Red V2 behavior for changing voice channels (:issue:`2521`) +- **Cogs - Downloader** - The ``[p]cog install`` command will not allow to install cogs which aren't suitable for installed version of Red anymore (:issue:`2605`) +- **Cogs - Downloader** - The ``[p]cog install`` command will now tell the user that cog has to be loaded after the install (:issue:`2523`) +- **Cogs - Downloader** - The ``[p]cog uninstall`` command allows to uninstall multiple cogs now (:issue:`2592`) +- **Cogs - Filter** - Significantly improved performance of Filter cog on large servers (:issue:`2509`) +- **Cogs - Mod** - Admins can now decide how many times message has to be repeated before cog's ``deleterepeats`` functionality removes it (:issue:`2437`) +- **Cogs - Permissions** - Removed ``[p]p`` alias for ``[p]permissions`` command (:issue:`2467`) +- **Cogs - Streams** - Twitch stream reruns are now marked as reruns in embed title (:issue:`2620`) + +Removals +******** + +- **Cogs - DataConverter** - DataConverter has been completely removed from Red (:issue:`2554`) + +Fixes +***** + +- **Core - Bot Commands** - Fixed local blacklist/whitelist management commands (:issue:`2531`) +- **Core - Bot Commands** - ``[p]set locale`` now only accepts actual locales (:issue:`2553`) +- **Core - Bot Commands** - ``[p]listlocales`` now includes ``en-US`` locale (:issue:`2553`) +- **Core - Command-line Interfaces** - Fixed the list of available extras in the ``redbot-launcher`` (:issue:`2588`) +- **Core - i18n** - Changed default locale from ``en`` to ``en-US`` (:issue:`2642`) +- **Cogs - Audio** - Fixed ``[p]audioset status`` (:issue:`2481`) +- **Cogs - Audio** - Fixed an issue where queuing song from ``[p]search`` command did not do anything (:issue:`2513`) +- **Cogs - Audio** - Bot will no longer complain about permissions when trying to connect to user-limited channel, if it has "Move Members" permission (:issue:`2525`) +- **Cogs - Audio** - Fixed an issue on ``[p]audiostats`` command that occurred when there were more than 20 servers to display (:issue:`2533`) +- **Cogs - Audio** - Fixed the link shown by ``[p]prev`` command (:issue:`2556`) +- **Cogs - Audio** - Fixed an issue with ``[p]playlist queue`` that occurred when the bot was connected but wasn't playing anything (:issue:`2586`) +- **Cogs - Audio** - Fixed an issue with setting DJ role in ``[p]audioset dj`` command (:issue:`2606`) +- **Cogs - Downloader** - Fixed a bug that caused Downloader to include submodules on cog list (:issue:`2590`) +- **Cogs - Downloader** - The error message sent by ``[p]cog install`` when required libraries fail to install is now properly formatted (:issue:`2576`) +- **Cogs - Downloader** - The ``[p]cog uninstall`` command will now remove cog from installed cogs list even if it can't find the cog in install path anymore (:issue:`2595`) +- **Cogs - Mod** - Fixed ``[p]ban`` not allowing to omit ``days`` argument (:issue:`2602`) +- **Cogs - Trivia - Lists** - Fixed dead image link for Sao Tome and Principe flag in ``worldflags`` trivia (:issue:`2540`) + + +Developer changelog +------------------- + +Breaking Changes +**************** + +- **Core - Config** - We now record custom group primary key lengths in the core config object (:issue:`2550`) +- **Cogs - Downloader** - Cog Developers now have to use ``min_bot_version`` key instead of ``bot_version`` to specify minimum version of Red supported by the cog in ``info.json``, see more information in :ref:`info-json-format` (:issue:`2605`) + +Additions +********* + +- **Core** - Added a ``on_message_without_command`` event that is dispatched when bot gets an event for a message that doesn't contain a command (:issue:`2338`) +- **Core - Config** - Introduced `Config.init_custom()` method (:issue:`2545`) +- **Core - Utils Package** - Added `chat_formatting.humanize_timedelta()` (:issue:`2412`) +- **Cogs - Downloader** - Added ``max_bot_version`` key to ``info.json`` that allows to specify maximum supported version of Red supported by the cog in ``info.json``, see more information in :ref:`info-json-format`. (:issue:`2605`) + +Changes +******* + +- **Core** - Usage of ``yaml.load`` will now warn about its security issues (:issue:`2326`) +- **Core - Config** - Migrated internal UUIDs to maintain cross platform consistency (:issue:`2604`) +- **Core - Utils Package** - Improved error handling of empty lists in `chat_formatting.humanize_list()` (:issue:`2597`) +- **Core - Dependencies** - Red is now no longer vendoring discord.py and installs it from PyPI (:issue:`2587`) +- **Core - Dependencies** - Upgraded discord.py dependency to version 1.0.1 (:issue:`2587`) + +Fixes +***** + +- **Core - Utils Package** - Fixed spelling of the `Tunnel`'s method from ``files_from_attatch()`` to `files_from_attach() `; old name was left for backwards compatibility (:issue:`2496`) +- **Core - Utils Package** - Fixed behavior of ``Tunnel.react_close()`` - now when tunnel closes, the message will be sent to the other end (:issue:`2507`) + +---- + +Redbot 3.0.2 (2019-02-24) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`Tobotimus`, :ghuser:`ZeLarpMaster` + +End-user changelog +------------------ + +Fixes +***** + +- **Cogs - Permissions** - Fixed rules loading for cogs (`431cdf1 `__) +- **Cogs - Trivia - Lists** - Fixed a typo in ``cars`` trivia (:issue:`2475`) + +---- + +Redbot 3.0.1 (2019-02-17) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`calebj`, :ghuser:`DiscordLiz`, :ghuser:`mikeshardmind`, :ghuser:`PredaaA`, :ghuser:`Redjumpman`, :ghuser:`Tobotimus`, :ghuser:`Twentysix26`, :ghuser:`ZeLarpMaster`, :ghuser:`zephyrkul` + +End-user changelog +------------------ + +Changes +******* + +- **Core - Bot Commands** - Improve some of the core commands to not require double quotes for arguments with spaces, if they're the last argument required by the command (:issue:`2407`) +- **Core - Bot Commands** - Using ``[p]load`` command now sends help message when it's used without arguments (:issue:`2432`) +- **Cogs - Downloader** - The ``[p]pipinstall`` command now sends help message when it's used without arguments (`eebed27 `__) +- **Cogs - Mod** - Usernames and nicknames listed in ``[p]names`` and ``[p]userinfo`` commands now have the spoiler markdown escaped (:issue:`2401`) +- **Cogs - Modlog** - Usernames listed in modlog cases now have the spoiler markdown escaped (:issue:`2401`) +- **Cogs - Warnings** - Members can now also be passed with username, nickname, or user mention to ``[p]warnings`` and ``[p]unwarn`` commands (:issue:`2403`, :issue:`2404`) + +Fixes +***** + +- **Core** - Messages sent interactively (i.e. prompting user whether they would like to view the next message) now no longer cause errors if bot's prompt message gets removed by other means (:issue:`2380`, :issue:`2447`) +- **Core - Bot Commands** - Fixed error in ``[p]servers`` command that was happening when bot's prompt message was deleted before the prompt time was over (:issue:`2400`) +- **Core - Command-line Interfaces** - Fixed behavior of CLI arguments in ``redbot-launcher`` (:issue:`2432`) +- **Cogs - Audio** - Fixed issues with setting external Lavalink (:issue:`2306`, :issue:`2460`) +- **Cogs - Audio** - Audio now cleans up zombie players from guilds it's no longer in (:issue:`2414`) +- **Cogs - Downloader** - Fixed issues with cloning that happened if instance's data path had spaces (:issue:`2421`) +- **Cogs - Mod** - ``[p]userinfo`` now accounts for guild's lurkers (:issue:`2406`, :issue:`2426`) +- **Cogs - Permissions** - Fixed rule precedence issues for default rules (:issue:`2313`, :issue:`2422`) + + +Developer changelog +------------------- + +Additions +********* + +- **Core - Utils Package** - Added `escape_spoilers()` and `escape_spoilers_and_mass_mentions()` methods for escaping strings with spoiler markdown (:issue:`2401`) + +Fixes +***** + +- **Core - Utils Package** - ``MessagePredicate.lower_contained_in()`` now actually lowers the message content before trying to match (:issue:`2399`) + +---- + +Redbot 3.0.0 (2019-01-28) +========================= + +First stable release of Red V3. +Changelogs for this and previous versions can be found on `our GitHub releases page `__. diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 00000000000..26a877a07e4 --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,5 @@ +========= +Changelog +========= + +.. include:: ../CHANGES.rst diff --git a/docs/changelog_3_1_0.rst b/docs/changelog_3_1_0.rst deleted file mode 100644 index e067ae80490..00000000000 --- a/docs/changelog_3_1_0.rst +++ /dev/null @@ -1,232 +0,0 @@ -.. v3.1.0 Changelog - -#################### -v3.1.0 Release Notes -#################### - ----------------------- -Mongo Driver Migration ----------------------- - -Due to the required changes of the Mongo driver for Config, all existing Mongo users will need to -complete the below instructions to continue to use Mongo after updating to 3.1. -This includes **all** users, regardless of any prior migration attempt to a development version of -3.1. - - #. Upgrade to 3.1 - #. Convert all existing Mongo instances to JSON using the new converters - #. Start each bot instance while using JSON and load any and all cogs you have in order to successfully preserve data. - #. Turn each instance off and convert back to Mongo. - **NOTE:** No data is wiped from your Mongo database when converting to JSON. - You may want to use a *new* database name when converting back to Mongo in order to not have duplicate data. - -------------- -Setup Utility -------------- - -New commands were introduced to simplify the conversion/editing/removal process both on our end and the users end. -Please use ``redbot-setup --help`` to learn how to use the new features. - -.. HINT:: - - Converting to JSON: ``redbot-setup convert json`` - - Converting to Mongo: ``redbot-setup convert mongo`` - -################ -v3.1.0 Changelog -################ - ------ -Audio ------ - - * Add Spotify support (`#2328`_) - * Play local folders via text command (`#2457`_) - * Change pause to a toggle (`#2461`_) - * Remove aliases (`#2462`_) - * Add track length restriction (`#2465`_) - * Seek command can now seek to position (`#2470`_) - * Add option for dc at queue end (`#2472`_) - * Emptydisconnect and status refactor (`#2473`_) - * Queue clean and queue clear addition (`#2476`_) - * Fix for audioset status (`#2481`_) - * Playlist download addition (`#2482`_) - * Add songs when search-queuing (`#2513`_) - * Match v2 behavior for channel change (`#2521`_) - * Bot will no longer complain about permissions when trying to connect to user-limited channel, if it has "Move Members" permission (`#2525`_) - * Fix issue on audiostats command when more than 20 servers to display (`#2533`_) - * Fix for prev command display (`#2556`_) - * Fix for localtrack playing (`#2557`_) - * Fix for playlist queue when not playing (`#2586`_) - * Track search and append fixes (`#2591`_) - * DJ role should ask for a role (`#2606`_) - ----- -Core ----- - - * Warn on usage of ``yaml.load`` (`#2326`_) - * New Event dispatch: ``on_message_without_command`` (`#2338`_) - * Improve output format of cooldown messages (`#2412`_) - * Delete cooldown messages when expired (`#2469`_) - * Fix local blacklist/whitelist management (`#2531`_) - * ``[p]set locale`` now only accepts actual locales (`#2553`_) - * ``[p]listlocales`` now displays ``en-US`` (`#2553`_) - * ``redbot --version`` will now give you current version of Red (`#2567`_) - * Redesign help and related formatter (`#2628`_) - * Default locale changed from ``en`` to ``en-US`` (`#2642`_) - * New command ``[p]datapath`` that prints the bot's datapath (`#2652`_) - ------- -Config ------- - - * Updated Mongo driver to support large guilds (`#2536`_) - * Introduced ``init_custom`` method on Config objects (`#2545`_) - * We now record custom group primary key lengths in the core config object (`#2550`_) - * Migrated internal UUIDs to maintain cross platform consistency (`#2604`_) - -------------- -DataConverter -------------- - - * It's dead jim (Removal) (`#2554`_) - ----------- -discord.py ----------- - - * No longer vendoring discord.py (`#2587`_) - * Upgraded discord.py dependency to version 1.0.1 (`#2587`_) - ----------- -Downloader ----------- - - * ``[p]cog install`` will now tell user that cog has to be loaded (`#2523`_) - * The message when libraries fail to install is now formatted (`#2576`_) - * Fixed bug, that caused Downloader to include submodules on cog list (`#2590`_) - * ``[p]cog uninstall`` allows to uninstall multiple cogs now (`#2592`_) - * ``[p]cog uninstall`` will now remove cog from installed cogs even if it can't find the cog in install path anymore (`#2595`_) - * ``[p]cog install`` will not allow to install cogs which aren't suitable for installed version of Red anymore (`#2605`_) - * Cog Developers now have to use ``min_bot_version`` in form of version string instead of ``bot_version`` in info.json and they can also use ``max_bot_version`` to specify maximum version of Red, more in :ref:`info-json-format`. (`#2605`_) - ------- -Filter ------- - - * Filter performs significantly better on large servers. (`#2509`_) - --------- -Launcher --------- - -* Fixed extras in the launcher (`#2588`_) - ---- -Mod ---- - - * Admins can now decide how many times message has to be repeated before ``deleterepeats`` removes it (`#2437`_) - * Fix: make ``[p]ban [days]`` optional as per the doc (`#2602`_) - * Added the command ``voicekick`` to kick members from a voice channel with optional mod case. (`#2639`_) - ------------ -Permissions ------------ - - * Removed: ``p`` alias for ``permissions`` command (`#2467`_) - -------------- -Setup Scripts -------------- - - * ``redbot-setup`` now uses the click CLI library (`#2579`_) - * ``redbot-setup convert`` now used to convert between libraries (`#2579`_) - * Backup support for Mongo is currently broken (`#2579`_) - -------- -Streams -------- - - * Add support for custom stream alert messages per guild (`#2600`_) - * Add ability to exclude rerun Twitch streams, and note rerun streams in embed status (`#2620`_) - ------ -Tests ------ - - * Test for ``trivia`` cog uses explicitly utf-8 encoding for checking yaml files (`#2565`_) - ------- -Trivia ------- - - * Fix of dead image link for Sao Tome and Principe in ``worldflags`` trivia (`#2540`_) - ------------------ -Utility Functions ------------------ - - * 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`_) - -.. _#2326: https://github.com/Cog-Creators/Red-DiscordBot/pull/2326 -.. _#2328: https://github.com/Cog-Creators/Red-DiscordBot/pull/2328 -.. _#2338: https://github.com/Cog-Creators/Red-DiscordBot/pull/2338 -.. _#2412: https://github.com/Cog-Creators/Red-DiscordBot/pull/2412 -.. _#2437: https://github.com/Cog-Creators/Red-DiscordBot/pull/2437 -.. _#2457: https://github.com/Cog-Creators/Red-DiscordBot/pull/2457 -.. _#2461: https://github.com/Cog-Creators/Red-DiscordBot/pull/2461 -.. _#2462: https://github.com/Cog-Creators/Red-DiscordBot/pull/2462 -.. _#2465: https://github.com/Cog-Creators/Red-DiscordBot/pull/2465 -.. _#2467: https://github.com/Cog-Creators/Red-DiscordBot/pull/2467 -.. _#2469: https://github.com/Cog-Creators/Red-DiscordBot/pull/2469 -.. _#2470: https://github.com/Cog-Creators/Red-DiscordBot/pull/2470 -.. _#2472: https://github.com/Cog-Creators/Red-DiscordBot/pull/2472 -.. _#2473: https://github.com/Cog-Creators/Red-DiscordBot/pull/2473 -.. _#2476: https://github.com/Cog-Creators/Red-DiscordBot/pull/2476 -.. _#2481: https://github.com/Cog-Creators/Red-DiscordBot/pull/2481 -.. _#2482: https://github.com/Cog-Creators/Red-DiscordBot/pull/2482 -.. _#2496: https://github.com/Cog-Creators/Red-DiscordBot/pull/2496 -.. _#2507: https://github.com/Cog-Creators/Red-DiscordBot/pull/2507 -.. _#2509: https://github.com/Cog-Creators/Red-DiscordBot/pull/2509 -.. _#2513: https://github.com/Cog-Creators/Red-DiscordBot/pull/2513 -.. _#2521: https://github.com/Cog-Creators/Red-DiscordBot/pull/2521 -.. _#2523: https://github.com/Cog-Creators/Red-DiscordBot/pull/2523 -.. _#2525: https://github.com/Cog-Creators/Red-DiscordBot/pull/2525 -.. _#2531: https://github.com/Cog-Creators/Red-DiscordBot/pull/2531 -.. _#2533: https://github.com/Cog-Creators/Red-DiscordBot/pull/2533 -.. _#2536: https://github.com/Cog-Creators/Red-DiscordBot/pull/2536 -.. _#2540: https://github.com/Cog-Creators/Red-DiscordBot/pull/2540 -.. _#2545: https://github.com/Cog-Creators/Red-DiscordBot/pull/2545 -.. _#2550: https://github.com/Cog-Creators/Red-DiscordBot/pull/2550 -.. _#2553: https://github.com/Cog-Creators/Red-DiscordBot/pull/2553 -.. _#2554: https://github.com/Cog-Creators/Red-DiscordBot/pull/2554 -.. _#2556: https://github.com/Cog-Creators/Red-DiscordBot/pull/2556 -.. _#2557: https://github.com/Cog-Creators/Red-DiscordBot/pull/2557 -.. _#2565: https://github.com/Cog-Creators/Red-DiscordBot/pull/2565 -.. _#2567: https://github.com/Cog-Creators/Red-DiscordBot/pull/2567 -.. _#2576: https://github.com/Cog-Creators/Red-DiscordBot/pull/2576 -.. _#2579: https://github.com/Cog-Creators/Red-DiscordBot/pull/2579 -.. _#2586: https://github.com/Cog-Creators/Red-DiscordBot/pull/2586 -.. _#2587: https://github.com/Cog-Creators/Red-DiscordBot/pull/2587 -.. _#2588: https://github.com/Cog-Creators/Red-DiscordBot/pull/2588 -.. _#2590: https://github.com/Cog-Creators/Red-DiscordBot/pull/2590 -.. _#2591: https://github.com/Cog-Creators/Red-DiscordBot/pull/2591 -.. _#2592: https://github.com/Cog-Creators/Red-DiscordBot/pull/2592 -.. _#2595: https://github.com/Cog-Creators/Red-DiscordBot/pull/2595 -.. _#2597: https://github.com/Cog-Creators/Red-DiscordBot/pull/2597 -.. _#2600: https://github.com/Cog-Creators/Red-DiscordBot/pull/2600 -.. _#2602: https://github.com/Cog-Creators/Red-DiscordBot/pull/2602 -.. _#2604: https://github.com/Cog-Creators/Red-DiscordBot/pull/2604 -.. _#2605: https://github.com/Cog-Creators/Red-DiscordBot/pull/2605 -.. _#2606: https://github.com/Cog-Creators/Red-DiscordBot/pull/2606 -.. _#2620: https://github.com/Cog-Creators/Red-DiscordBot/pull/2620 -.. _#2628: https://github.com/Cog-Creators/Red-DiscordBot/pull/2628 -.. _#2639: https://github.com/Cog-Creators/Red-DiscordBot/pull/2639 -.. _#2642: https://github.com/Cog-Creators/Red-DiscordBot/pull/2642 -.. _#2652: https://github.com/Cog-Creators/Red-DiscordBot/pull/2652 diff --git a/docs/changelog_3_2_0.rst b/docs/changelog_3_2_0.rst deleted file mode 100644 index ce594cde5f1..00000000000 --- a/docs/changelog_3_2_0.rst +++ /dev/null @@ -1,565 +0,0 @@ -.. 3.2.x Changelogs - -Redbot 3.2.3 (2020-01-17) -========================= - -Core Bot Changes ----------------- - -- Further improvements have been made to bot startup and shutdown. -- Prefixes are now cached for performance. -- Added the means for cog creators to use a global preinvoke hook. -- The bot now ensures it has at least the bare neccessary permissions before running commands. -- Deleting instances works as intended again. -- Sinbad stopped fighting it and embraced the entrypoint madness. - -Core Commands -------------- - -- The servers command now also shows the ids. - -Admin Cog ---------- - -- The selfrole command now has reasonable expectations about hierarchy. - -Help Formatter --------------- - -- ``[botname]`` is now replaced with the bot's display name in help text. -- New features added for cog creators to further customize help behavior. - - - Check out our command reference for details on new ``format_help_for_context`` method. -- Embed settings are now consistent. - -Downloader ----------- - -- Improved a few user facing messages. -- Added pagination of output on cog update. -- Added logging of failures. - -Docs ----- - -There's more detail to the below changes, so go read the docs. -For some reason, documenting documentation changes is hard. - -- Added instructions about git version. -- Clarified instructions for installation and update. -- Added more details to the API key reference. -- Fixed some typos and versioning mistakes. - - -Audio ------ - -Draper did things. - -- No seriously, Draper did things. -- Wait you wanted details? Ok, I guess we can share those. -- Audio properly disconnects with autodisconnect, even if notify is being used. -- Symbolic links now work as intended for local tracks. -- Bump play now shows the correct time till next track. -- Multiple user facing messages have been made more correct. - -Redbot 3.2.2 (2020-01-10) -========================= - -Hotfixes --------- - -- Fix Help Pagination issue - -Docs ----- - -- Correct venv docs - - -Redbot 3.2.1 (2020-01-10) -========================= - -Hotfixes --------- - -- Fix Mongo conversion from being incorrectly blocked -- Fix announcer not creating a message for success feedback -- Log an error with creating case types rather than crash - - -Redbot 3.2.0 (2020-01-09) -========================= -Core Bot Changes ----------------- - -Breaking Changes -~~~~~~~~~~~~~~~~ - -- Modlog casetypes no longer have an attribute for auditlog action type. (`#2897 `_) -- Removed ``redbot.core.modlog.get_next_case_number()``. (`#2908 `_) -- Removed ``bank.MAX_BALANCE``, use ``bank.get_max_balance()`` from now on. (`#2926 `_) -- The main bot config is no longer directly accessible to cogs. New methods have been added for use where this is concerned. - New methods for this include - - - ``bot.get_shared_api_tokens`` - - ``bot.set_shared_api_tokens`` - - ``bot.get_embed_color`` - - ``bot.get_embed_colour`` - - ``bot.get_admin_roles`` - - ``bot.get_admin_role_ids`` - - ``bot.get_mod_roles`` - - ``bot.get_mod_role_ids`` (`#2967 `_) -- Reserved some command names for internal Red use. These are available programatically as ``redbot.core.commands.RESERVED_COMMAND_NAMES``. (`#2973 `_) -- Removed ``bot._counter``, Made a few more attrs private (``cog_mgr``, ``main_dir``). (`#2976 `_) -- Extension's ``setup()`` function should no longer assume that we are, or even will be connected to Discord. - This also means that cog creators should no longer use ``bot.wait_until_ready()`` inside it. (`#3073 `_) -- Removed the mongo driver. (`#3099 `_) - - -Bug Fixes -~~~~~~~~~ - -- Help now properly hides disabled commands. (`#2863 `_) -- Fixed ``bot.remove_command`` throwing an error when trying to remove a non-existent command. (`#2888 `_) -- ``Command.can_see`` now works as intended for disabled commands. (`#2892 `_) -- Modlog entries now show up properly without the mod cog loaded. (`#2897 `_) -- Fixed an error in ``[p]reason`` when setting the reason for a case without a moderator. (`#2908 `_) -- Bank functions now check the recipient balance before transferring and stop the transfer if the recipient's balance will go above the maximum allowed balance. (`#2923 `_) -- Removed potential for additional bad API calls per ban/unban. (`#2945 `_) -- The ``[p]invite`` command no longer errors when a user has the bot blocked or DMs disabled in the server. (`#2948 `_) -- Stopped using the ``:`` character in backup's filename - Windows doesn't accept it. (`#2954 `_) -- ``redbot-setup delete`` no longer errors with "unexpected keyword argument". (`#2955 `_) -- ``redbot-setup delete`` no longer prompts about backup when the user passes the option ``--no-prompt``. (`#2956 `_) -- Cleaned up the ``[p]inviteset public`` and ``[p]inviteset perms`` help strings. (`#2963 `_) -- ```[p]embedset user`` now only affects DM's. (`#2966 `_) -- Fixed an unfriendly error when the provided instance name doesn't exist. (`#2968 `_) -- Fixed the help text and response of ``[p]set usebotcolor`` to accurately reflect what the command is doing. (`#2974 `_) -- Red no longer types infinitely when a command with a cooldown is called within the last second of a cooldown. (`#2985 `_) -- Removed f-string usage in the launcher to prevent our error handling from causing an error. (`#3002 `_) -- Fixed ``MessagePredicate.greater`` and ``MessagePredicate.less`` allowing any valid int instead of only valid ints/floats that are greater/less than the given value. (`#3004 `_) -- Fixed an error in ``[p]uptime`` when the uptime is under a second. (`#3009 `_) -- Added quotation marks to the response of ``[p]helpset tagline`` so that two consecutive full stops do not appear. (`#3010 `_) -- Fixed an issue with clearing rules in permissions. (`#3014 `_) -- Lavalink will now be restarted after an unexpected shutdown. (`#3033 `_) -- Added a 3rd-party lib folder to ``sys.path`` before loading cogs. This prevents issues with 3rd-party cogs failing to load when Downloader is not loaded to install requirements. (`#3036 `_) -- Escaped track descriptions so that they do not break markdown. (`#3047 `_) -- Red will now properly send a message when the invoked command is guild-only. (`#3057 `_) -- Arguments ``--co-owner`` and ``--load-cogs`` now properly require at least one argument to be passed. (`#3060 `_) -- Now always appends the 3rd-party lib folder to the end of ``sys.path`` to avoid shadowing Red's dependencies. (`#3062 `_) -- Fixed ``is_automod_immune``'s handling of the guild check and added support for checking webhooks. (`#3100 `_) -- Fixed the generation of the ``repos.json`` file in the backup process. (`#3114 `_) -- Fixed an issue where calling audio commands when not in a voice channel could result in a crash. (`#3120 `_) -- Added handling for invalid folder names in the data path gracefully in ``redbot-setup`` and ``redbot --edit``. (`#3171 `_) -- ``--owner`` and ``-p`` cli flags now work when added from launcher. (`#3174 `_) -- Red will now prevent users from locking themselves out with localblacklist. (`#3207 `_) -- Fixed help ending up a little too large for discord embed limits. (`#3208 `_) -- Fixed formatting issues in commands that list whitelisted/blacklisted users/roles when the list is empty. (`#3219 `_) -- Red will now prevent users from locking the guild owner out with localblacklist (unless the command caller is bot owner). (`#3221 `_) -- Guild owners are no longer affected by the local whitelist and blacklist. (`#3221 `_) -- Fixed an attribute error that can be raised in ``humanize_timedelta`` if ``seconds = 0``. (`#3231 `_) -- Fixed ``ctx.clean_prefix`` issues resulting from undocumented changes from discord. (`#3249 `_) -- ``redbot.core.bot.Bot.owner_id`` is now set in the post connection startup. (`#3273 `_) -- ``redbot.core.bot.Bot.send_to_owners()`` and ``redbot.core.bot.Bot.get_owner_notification_destinations()`` now wait until Red is done with post connection startup to ensure owner ID is available. (`#3273 `_) - - -Enhancements -~~~~~~~~~~~~ - -- Added the option to modify the RPC port with the ``--rpc-port`` flag. (`#2429 `_) -- Slots now has a 62.5% expected payout and will not inflate economy when spammed. (`#2875 `_) -- Allowed passing ``cls`` in the ``redbot.core.commands.group()`` decorator. (`#2881 `_) -- Red's Help Formatter is now considered to have a stable API. (`#2892 `_) -- Modlog no longer generates cases without being told to for actions the bot did. (`#2897 `_) -- Some generic modlog casetypes are now pre-registered for cog creator use. (`#2897 `_) -- ModLog is now much faster at creating cases, especially in large servers. (`#2908 `_) -- JSON config files are now stored without indentation, this is to reduce the file size and increase the performance of write operations. (`#2921 `_) -- ``--[no-]backup``, ``--[no-]drop-db`` and ``--[no-]remove-datapath`` in the ``redbot-setup delete`` command are now on/off flags. (`#2958 `_) -- The confirmation prompts in ``redbot-setup`` now have default values for user convenience. (`#2958 `_) -- ``redbot-setup delete`` now has the option to leave Red's data untouched on database backends. (`#2962 `_) -- Red now takes less time to fetch cases, unban members, and list warnings. (`#2964 `_) -- Red now handles more things prior to connecting to discord to reduce issues during the initial load. (`#3045 `_) -- ``bot.send_filtered`` now returns the message that is sent. (`#3052 `_) -- Red will now send a message when the invoked command is DM-only. (`#3057 `_) -- All ``y/n`` confirmations in cli commands are now unified. (`#3060 `_) -- Changed ``[p]info`` to say "This bot is an..." instead of "This is an..." for clarity. (`#3121 `_) -- ``redbot-setup`` will now use the instance name in default data paths to avoid creating a second instance with the same data path. (`#3171 `_) -- Instance names can now only include characters A-z, numbers, underscores, and hyphens. Old instances are unaffected by this change. (`#3171 `_) -- Clarified that ``[p]backup`` saves the **bot's** data in the help text. (`#3172 `_) -- Added ``redbot --debuginfo`` flag which shows useful information for debugging. (`#3183 `_) -- Added the Python executable field to ``[p]debuginfo``. (`#3184 `_) -- When Red prompts for a token, it will now print a link to the guide explaining how to obtain a token. (`#3204 `_) -- ``redbot-setup`` will no longer log to disk. (`#3269 `_) -- ``redbot.core.bot.Bot.send_to_owners()`` and ``redbot.core.bot.Bot.get_owner_notification_destinations()`` now log when they are not able to find the owner notification destination. (`#3273 `_) -- The lib folder is now cleared on minor Python version changes. ``[p]cog reinstallreqs`` in Downloader can be used to regenerate the lib folder for a new Python version. (`#3274 `_) -- If Red detects operating system or architecture change, it will now warn the owner about possible problems with the lib folder. (`#3274 `_) -- ``[p]playlist download`` will now compress playlists larger than the server attachment limit and attempt to send that. (`#3279 `_) - - -New Features -~~~~~~~~~~~~ - -- Added functions to acquire locks on Config groups and values. These locks are acquired by default when calling a value as a context manager. See ``Value.get_lock`` for details. (`#2654 `_) -- Added a config driver for PostgreSQL. (`#2723 `_) -- Added methods to Config for accessing things by id without mocked objects - - - ``Config.guild_from_id`` - - ``Config.user_from_id`` - - ``Config.role_from_id`` - - ``Config.channel_from_id`` - - ``Config.member_from_ids`` - - This one requires multiple ids, one for the guild, one for the user - - Consequence of discord's object model (`#2804 `_) -- New method ``humanize_number`` in ``redbot.core.utils.chat_formatting`` to convert numbers into text that respects the current locale. (`#2836 `_) -- Added new commands to Economy - - - ``[p]bank prune user`` - This will delete a user's bank account. - - ``[p]bank prune local`` - This will prune the bank of accounts for users who are no longer in the server. - - ``[p]bank prune global`` - This will prune the global bank of accounts for users who do not share any servers with the bot. (`#2845 `_) -- Red now uses towncrier for changelog generation. (`#2872 `_) -- Added ``redbot.core.modlog.get_latest_case`` to fetch the case object for the most recent ModLog case. (`#2908 `_) -- Added ``[p]bankset maxbal`` to set the maximum bank balance. (`#2926 `_) -- Added a few methods and classes replacing direct config access (which is no longer supported) - - - ``redbot.core.Red.allowed_by_whitelist_blacklist`` - - ``redbot.core.Red.get_valid_prefixes`` - - ``redbot.core.Red.clear_shared_api_tokens`` - - ``redbot.core.commands.help.HelpSettings`` (`#2976 `_) -- Added the cli flag ``redbot --edit`` which is used to edit the instance name, token, owner, and datapath. (`#3060 `_) -- Added ``[p]licenseinfo``. (`#3090 `_) -- Ensured that people can migrate from MongoDB. (`#3108 `_) -- Added a command to list disabled commands globally or per guild. (`#3118 `_) -- New event ``on_red_api_tokens_update`` is now dispatched when shared api keys for a service are updated. (`#3134 `_) -- Added ``redbot-setup backup``. (`#3235 `_) -- Added the method ``redbot.core.bot.Bot.wait_until_red_ready()`` that waits until Red's post connection startup is done. (`#3273 `_) - - -Removals -~~~~~~~~ - -- ``[p]set owner`` and ``[p]set token`` have been removed in favor of managing server side. (`#2928 `_) -- Shared libraries are marked for removal in Red 3.4. (`#3106 `_) -- Removed ``[p]backup``. Use the cli command ``redbot-setup backup`` instead. (`#3235 `_) -- Removed the functions ``safe_delete``, ``fuzzy_command_search``, ``format_fuzzy_results`` and ``create_backup`` from ``redbot.core.utils``. (`#3240 `_) -- Removed a lot of the launcher's handled behavior. (`#3289 `_) - - -Miscellaneous changes -~~~~~~~~~~~~~~~~~~~~~ - -- `#2527 `_, `#2571 `_, `#2723 `_, `#2836 `_, `#2849 `_, `#2861 `_, `#2885 `_, `#2890 `_, `#2897 `_, `#2904 `_, `#2924 `_, `#2939 `_, `#2940 `_, `#2941 `_, `#2949 `_, `#2953 `_, `#2964 `_, `#2986 `_, `#2993 `_, `#2997 `_, `#3008 `_, `#3017 `_, `#3048 `_, `#3059 `_, `#3080 `_, `#3089 `_, `#3104 `_, `#3106 `_, `#3129 `_, `#3152 `_, `#3160 `_, `#3168 `_, `#3173 `_, `#3176 `_, `#3186 `_, `#3192 `_, `#3193 `_, `#3195 `_, `#3202 `_, `#3214 `_, `#3223 `_, `#3229 `_, `#3245 `_, `#3247 `_, `#3248 `_, `#3250 `_, `#3254 `_, `#3255 `_, `#3256 `_, `#3258 `_, `#3261 `_, `#3275 `_, `#3276 `_, `#3293 `_, `#3278 `_, `#3285 `_, `#3296 `_, - - -Dependency changes -~~~~~~~~~~~~~~~~~~~~~~~ - -- Added ``pytest-mock`` requirement to ``tests`` extra. (`#2571 `_) -- Updated the python minimum requirement to 3.8.1, updated JRE to Java 11. (`#3245 `_) -- Bumped dependency versions. (`#3288 `_) -- Bumped red-lavalink version. (`#3290 `_) - - -Documentation Changes -~~~~~~~~~~~~~~~~~~~~~ - -- Started the user guides covering cogs and the user interface of the bot. This includes, for now, a "Getting started" guide. (`#1734 `_) -- Added documentation for PM2 support. (`#2105 `_) -- Updated linux install docs, adding sections for Fedora Linux, Debian/Raspbian Buster, and openSUSE. (`#2558 `_) -- Created documentation covering what we consider a developer facing breaking change and the guarantees regarding them. (`#2882 `_) -- Fixed the user parameter being labeled as ``discord.TextChannel`` instead of ``discord.abc.User`` in ``redbot.core.utils.predicates``. (`#2914 `_) -- Updated towncrier info in the contribution guidelines to explain how to create a changelog for a standalone PR. (`#2915 `_) -- Reworded the virtual environment guide to make it sound less scary. (`#2920 `_) -- Driver docs no longer show twice. (`#2972 `_) -- Added more information about ``redbot.core.utils.humanize_timedelta`` into the docs. (`#2986 `_) -- Added a direct link to the "Installing Red" section in "Installing using powershell and chocolatey". (`#2995 `_) -- Updated Git PATH install (Windows), capitalized some words, stopped mentioning the launcher. (`#2998 `_) -- Added autostart documentation for Red users who installed Red inside of a virtual environment. (`#3005 `_) -- Updated the Cog Creation guide with a note regarding the Develop version as well as the folder layout for local cogs. (`#3021 `_) -- Added links to the getting started guide at the end of installation guides. (`#3025 `_) -- Added proper docstrings to enums that show in drivers docs. (`#3035 `_) -- Discord.py doc links will now always use the docs for the currently used version of discord.py. (`#3053 `_) -- Added ``|DPY_VERSION|`` substitution that will automatically get replaced by the current discord.py version. (`#3053 `_) -- Added missing descriptions for function returns. (`#3054 `_) -- Stopped overwriting the ``docs/prolog.txt`` file in ``conf.py``. (`#3082 `_) -- Fixed some typos and wording, added MS Azure to the host list. (`#3083 `_) -- Updated the docs footer copyright to 2019. (`#3105 `_) -- Added a deprecation note about shared libraries in the Downloader Framework docs. (`#3106 `_) -- Updated the apikey framework documentation. Changed ``bot.get_shared_api_keys()`` to ``bot.get_shared_api_tokens()``. (`#3110 `_) -- Added information about ``info.json``'s ``min_python_version`` key in Downloader Framework docs. (`#3124 `_) -- Added an event reference for the ``on_red_api_tokens_update`` event in the Shared API Keys docs. (`#3134 `_) -- Added notes explaining the best practices with config. (`#3149 `_) -- Documented additional attributes in Context. (`#3151 `_) -- Updated Windows docs with up to date dependency instructions. (`#3188 `_) -- Added a "Publishing cogs for V3" document explaining how to make user's cogs work with Downloader. (`#3234 `_) -- Fixed broken docs for ``redbot.core.commands.Context.react_quietly``. (`#3257 `_) -- Updated copyright notices on License and RTD config to 2020. (`#3259 `_) -- Added a line about setuptools and wheel. (`#3262 `_) -- Ensured development builds are not advertised to the wrong audience. (`#3292 `_) -- Clarified the usage intent of some of the chat formatting functions. (`#3292 `_) - - -Admin ------ - -Breaking Changes -~~~~~~~~~~~~~~~~ - -- Changed ``[p]announce ignore`` and ``[p]announce channel`` to ``[p]announceset ignore`` and ``[p]announceset channel``. (`#3250 `_) -- Changed ``[p]selfrole `` to ``[p]selfrole add ``, changed ``[p]selfrole add`` to ``[p]selfroleset add`` , and changed ``[p]selfrole delete`` to ``[p]selfroleset remove``. (`#3250 `_) - - -Bug Fixes -~~~~~~~~~ - -- Fixed ``[p]announce`` failing after encountering an error attempting to message the bot owner. (`#3166 `_) -- Improved the clarity of user facing messages when the user is not allowed to do something due to Discord hierarchy rules. (`#3250 `_) -- Fixed some role managing commands not properly checking if Red had ``manage_roles`` perms before attempting to manage roles. (`#3250 `_) -- Fixed ``[p]editrole`` commands not checking if roles to be edited are higher than Red's highest role before trying to edit them. (`#3250 `_) -- Fixed ``[p]announce ignore`` and ``[p]announce channel`` not being able to be used by guild owners and administrators. (`#3250 `_) - - -Enhancements -~~~~~~~~~~~~ - -- Added custom issue messages for adding and removing roles, this makes it easier to create translations. (`#3016 `_) - - -Audio ------ - -Bug Fixes -~~~~~~~~~ - -- ``[p]playlist remove`` now removes the playlist url if the playlist was created through ``[p]playlist save``. (`#2861 `_) -- Users are no longer able to accidentally overwrite existing playlist if a new one with the same name is created/renamed. (`#2861 `_) -- ``[p]audioset settings`` no longer shows lavalink JAR version. (`#2904 `_) -- Fixed a ``KeyError: loadType`` when trying to play tracks. (`#2904 `_) -- ``[p]audioset settings`` now uses ``ctx.is_owner()`` to check if the context author is the bot owner. (`#2904 `_) -- Fixed track indexs being off by 1 in ``[p]search``. (`#2940 `_) -- Fixed an issue where updating your Spotify and YouTube Data API tokens did not refresh them. (`#3047 `_) -- Fixed an issue where the blacklist was not being applied correctly. (`#3047 `_) -- Fixed an issue in ``[p]audioset restrictions blacklist list`` where it would call the list a ``Whitelist``. (`#3047 `_) -- Red's status is now properly cleared on emptydisconnect. (`#3050 `_) -- Fixed a console spam caused sometimes when auto disconnect and auto pause are used. (`#3123 `_) -- Fixed an error that was thrown when running ``[p]audioset dj``. (`#3165 `_) -- Fixed a crash that could happen when the bot can't connect to the lavalink node. (`#3238 `_) -- Restricted the number of songs shown in the queue to first 500 to avoid heartbeats. (`#3279 `_) -- Added more cooldowns to playlist commands and restricted the queue and playlists to 10k songs to avoid bot errors. (`#3286 `_) - - -Enhancements -~~~~~~~~~~~~ - -- ``[p]playlist upload`` will now load playlists generated via ``[p]playlist download`` much faster if the playlist uses the new scheme. (`#2861 `_) -- ``[p]playlist`` commands now can be used by everyone regardless of DJ settings, however it will respect DJ settings when creating/modifying playlists in the server scope. (`#2861 `_) -- Spotify, Youtube Data, and Lavalink API calls can be cached to avoid repeated calls in the future, see ``[p]audioset cache``. (`#2890 `_) -- Playlists will now start playing as soon as first track is loaded. (`#2890 `_) -- ``[p]audioset localpath`` can set a path anywhere in your machine now. Note: This path needs to be visible by ``Lavalink.jar``. (`#2904 `_) -- ``[p]queue`` now works when there are no tracks in the queue, showing the track currently playing. (`#2904 `_) -- ``[p]audioset settings`` now reports Red Lavalink version. (`#2904 `_) -- Adding and removing reactions in Audio is no longer a blocking action. (`#2904 `_) -- When shuffle is on, queue now shows the correct play order. (`#2904 `_) -- ``[p]seek`` and ``[p]skip`` can be used by user if they are the song requester while DJ mode is enabled and votes are disabled. (`#2904 `_) -- Adding a playlist and an album to a saved playlist skips tracks already in the playlist. (`#2904 `_) -- DJ mode is now turned off if the DJ role is deleted. (`#2904 `_) -- When playing a localtrack, ``[p]play`` and ``[p]bumpplay`` no longer require the use of the prefix "localtracks\\". - - Before: ``[p]bumpplay localtracks\\ENM\\501 - Inside The Machine.mp3`` - Now: ``[p]bumpplay ENM\\501 - Inside The Machine.mp3`` - Now nested folders: ``[p]bumpplay Parent Folder\\Nested Folder\\track.mp3`` (`#2904 `_) -- Removed commas in explanations about how to set API keys. (`#2905 `_) -- Expanded local track support to all file formats (m3u, m4a, mp4, etc). (`#2940 `_) -- Cooldowns are now reset upon failure of commands that have a cooldown timer. (`#2940 `_) -- Improved the explanation in the help string for ``[p]audioset emptydisconnect``. (`#3051 `_) -- Added a typing indicator to playlist dedupe. (`#3058 `_) -- Exposed clearer errors to users in the play commands. (`#3085 `_) -- Better error handling when the player is unable to play multiple tracks in the sequence. (`#3165 `_) - - -New Features -~~~~~~~~~~~~ - -- Added support for nested folders in the localtrack folder. (`#270 `_) -- Now auto pauses the queue when the voice channel is empty. (`#721 `_) -- All Playlist commands now accept optional arguments, use ``[p]help playlist `` for more details. (`#2861 `_) -- ``[p]playlist rename`` will now allow users to rename existing playlists. (`#2861 `_) -- ``[p]playlist update`` will now allow users to update non-custom Playlists to the latest available tracks. (`#2861 `_) -- There are now 3 different scopes of playlist. To define them, use the ``--scope`` argument. - - ``Global Playlist`` - - - These playlists will be available in all servers the bot is in. - - These can be managed by the Bot Owner only. - - ``Server Playlist`` - - - These playlists will only be available in the server they were created in. - - These can be managed by the Bot Owner, Guild Owner, Mods, Admins, DJs, and the Creator (if the DJ role is disabled). - - ``User Playlist`` - - - These playlists will be available in all servers both the bot and the creator are in. - - These can be managed by the Bot Owner and Creator only. (`#2861 `_) -- ``[p]audioset cache`` can be used to set the cache level. **It's off by default**. (`#2904 `_) -- ``[p]genre`` can be used to play spotify playlists. (`#2904 `_) -- ``[p]audioset cacheage`` can be used to set the maximum age of an entry in the cache. **Default is 365 days**. (`#2904 `_) -- ``[p]audioset autoplay`` can be used to enable auto play once the queue runs out. (`#2904 `_) -- New events dispatched by Audio. - - - ``on_red_audio_track_start(guild: discord.Guild, track: lavalink.Track, requester: discord.Member)`` - - ``on_red_audio_track_end(guild: discord.Guild, track: lavalink.Track, requester: discord.Member)`` - - ``on_red_audio_track_enqueue(guild: discord.Guild, track: lavalink.Track, requester: discord.Member)`` - - ``on_red_audio_track_auto_play(guild: discord.Guild, track: lavalink.Track, requester: discord.Member)`` - - ``on_red_audio_queue_end(guild: discord.Guild, track: lavalink.Track, requester: discord.Member)`` - - ``on_red_audio_audio_disconnect(guild: discord.Guild)`` - - ``on_red_audio_skip_track(guild: discord.Guild, track: lavalink.Track, requester: discord.Member)`` (`#2904 `_) -- ``[p]queue shuffle`` can be used to shuffle the queue manually. (`#2904 `_) -- ``[p]queue clean self`` can be used to remove all songs you requested from the queue. (`#2904 `_) -- ``[p]audioset restrictions`` can be used to add or remove keywords which songs must have or are not allowed to have. (`#2904 `_) -- ``[p]playlist dedupe`` can be used to remove duplicated tracks from a playlist. (`#2904 `_) -- ``[p]autoplay`` can be used to play a random song. (`#2904 `_) -- ``[p]bumpplay`` can be used to add a song to the front of the queue. (`#2940 `_) -- ``[p]shuffle`` has an additional argument to tell the bot whether it should shuffle bumped tracks. (`#2940 `_) -- Added global whitelist/blacklist commands. (`#3047 `_) -- Added self-managed daily playlists in the GUILD scope, these are called "Daily playlist - YYYY-MM-DD" and auto delete after 7 days. (`#3199 `_) - - -CustomCom ---------- - -Enhancements -~~~~~~~~~~~~ - -- The group command ``[p]cc create`` can now be used to create simple CCs without specifying "simple". (`#1767 `_) -- Added a query option for CC typehints for URL-based CCs. (`#3228 `_) -- Now uses the ``humanize_list`` utility for iterable parameter results, e.g. ``{#:Role.members}``. (`#3277 `_) - - -Downloader ----------- - -Bug Fixes -~~~~~~~~~ - -- Made the regex for repo names use raw strings to stop causing a ``DeprecationWarning`` for invalid escape sequences. (`#2571 `_) -- Downloader will no longer attempt to install cogs that are already installed. (`#2571 `_) -- Repo names can now only contain the characters listed in the help text (A-Z, 0-9, underscores, and hyphens). (`#2827 `_) -- ``[p]findcog`` no longer attempts to find a cog for commands without a cog. (`#2902 `_) -- Downloader will no longer attempt to install a cog with same name as another cog that is already installed. (`#2927 `_) -- Added error handling for when a remote repository or branch is deleted, now notifies the which repository failed and continues to update the others. (`#2936 `_) -- ``[p]cog install`` will no longer error if a cog has an empty install message. (`#3024 `_) -- Made ``redbot.cogs.downloader.repo_manager.Repo.clean_url`` work with relative urls. This property is ``str`` type now. (`#3141 `_) -- Fixed an error on repo add from empty string values for the ``install_msg`` info.json field. (`#3153 `_) -- Disabled all git auth prompts when adding/updating a repo with Downloader. (`#3159 `_) -- ``[p]findcog`` now properly works for cogs with less typical folder structure. (`#3177 `_) -- ``[p]cog uninstall`` now fully unloads cog - the bot will not try to load it on next startup. (`#3179 `_) - - -Enhancements -~~~~~~~~~~~~ - -- Downloader will now check if the Python and bot versions match requirements in ``info.json`` during update. (`#1866 `_) -- ``[p]cog install`` now accepts multiple cog names. (`#2527 `_) -- When passing cogs to ``[p]cog update``, it will now only update those cogs, not all cogs from the repo those cogs are from. (`#2527 `_) -- Added error messages for failures when installing/reinstalling requirements and copying cogs and shared libraries. (`#2571 `_) -- ``[p]findcog`` now uses sanitized urls (without HTTP Basic Auth fragments). (`#3129 `_) -- ``[p]repo info`` will now show the repo's url, branch, and authors. (`#3225 `_) -- ``[p]cog info`` will now show cog authors. (`#3225 `_) -- ``[p]findcog`` will now show the repo's branch. (`#3225 `_) - - -New Features -~~~~~~~~~~~~ - -- Added ``[p]repo update [repos]`` which updates repos without updating the cogs from them. (`#2527 `_) -- Added ``[p]cog installversion `` which installs cogs from a specified revision (commit, tag) of the given repo. When using this command, the cog will automatically be pinned. (`#2527 `_) -- Added ``[p]cog pin `` and ``[p]cog unpin `` for pinning cogs. Cogs that are pinned will not be updated when using update commands. (`#2527 `_) -- Added ``[p]cog checkforupdates`` that lists which cogs can be updated (including pinned cog) without updating them. (`#2527 `_) -- Added ``[p]cog updateallfromrepos `` that updates all cogs from the given repos. (`#2527 `_) -- Added ``[p]cog updatetoversion [cogs]`` that updates all cogs or ones of user's choosing to chosen revision of the given repo. (`#2527 `_) -- Added ``[p]cog reinstallreqs`` that reinstalls cog requirements and shared libraries for all installed cogs. (`#3167 `_) - - -Documentation Changes -~~~~~~~~~~~~~~~~~~~~~ - -- Added ``redbot.cogs.downloader.installable.InstalledModule`` to Downloader's framework docs. (`#2527 `_) -- Removed API References for Downloader. (`#3234 `_) - - -Image ------ - -Enhancements -~~~~~~~~~~~~ - -- Updated the giphycreds command to match the formatting of the other API commands. (`#2905 `_) -- Removed commas from explanations about how to set API keys. (`#2905 `_) - - -Mod ---- - -Bug Fixes -~~~~~~~~~ - -- ``[p]userinfo`` no longer breaks when a user has an absurd numbers of roles. (`#2910 `_) -- Fixed Mod cog not recording username changes for ``[p]names`` and ``[p]userinfo`` commands. (`#2918 `_) -- Fixed ``[p]modset deletedelay`` deleting non-command messages. (`#2924 `_) -- Fixed an error when reloading Mod. (`#2932 `_) - - -Enhancements -~~~~~~~~~~~~ - -- Slowmode now accepts integer-only inputs as seconds. (`#2884 `_) - - -Permissions ------------ - -Bug Fixes -~~~~~~~~~ - -- Defaults are now cleared properly when clearing all rules. (`#3037 `_) - - -Enhancements -~~~~~~~~~~~~ - -- Better explained the usage of commands with the ```` argument. (`#2991 `_) - - -Streams -------- - -Bug Fixes -~~~~~~~~~ - -- Fixed a ``TypeError`` in the ``TwitchStream`` class when calling Twitch client_id from Red shared APIs tokens. (`#3042 `_) -- Changed the ``stream_alert`` function for Twitch alerts to make it work with how the ``TwitchStream`` class works now. (`#3042 `_) - - -Enhancements -~~~~~~~~~~~~ - -- Removed commas from explanations about how to set API keys. (`#2905 `_) - - -Trivia ------- - -Bug Fixes -~~~~~~~~~ - -- Fixed a typo in Ahsoka Tano's name in the Starwars trivia list. (`#2909 `_) -- Fixed a bug where ``[p]trivia leaderboard`` failed to run. (`#2911 `_) -- Fixed a typo in the Greek mythology trivia list regarding Hermes' staff. (`#2994 `_) -- Fixed a question in the Overwatch trivia list that accepted blank responses. (`#2996 `_) -- Fixed questions and answers that were incorrect in the Clash Royale trivia list. (`#3236 `_) - - -Enhancements -~~~~~~~~~~~~ - -- Added trivia lists for Prince and Michael Jackson lyrics. (`#12 `_) diff --git a/docs/changelog_3_3_0.rst b/docs/changelog_3_3_0.rst deleted file mode 100644 index 7f8f2046aa4..00000000000 --- a/docs/changelog_3_3_0.rst +++ /dev/null @@ -1,910 +0,0 @@ -.. 3.3.x Changelogs - -Redbot 3.3.12 (2020-08-18) -========================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`Dav-Git`, :ghuser:`douglas-cpp`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`MeatyChunks`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`thisisjvgrace`, :ghuser:`Vexed01`, :ghuser:`zephyrkul` - -End-user changelog ------------------- - -Core Bot -******** - -- Red now logs clearer error if it can't find package to load in any cog path during bot startup (:issue:`4079`) - -Mod -*** - -- ``[p]mute voice`` and ``[p]unmute voice`` now take action instantly if bot has Move Members permission (:issue:`4064`) -- Added typing to ``[p](un)mute guild`` to indicate that mute is being processed (:issue:`4066`, :issue:`4172`) - -Streams -******* - -- Improve error messages for invalid channel names/IDs (:issue:`4147`, :issue:`4148`) - -Trivia Lists -************ - -- Added ``whosthatpokemon2`` trivia containing Pokémons from 2nd generation (:issue:`4102`) -- Added ``whosthatpokemon3`` trivia containing Pokémons from 3rd generation (:issue:`4141`) - - -Miscellaneous -------------- - -- Updated features list in ``[p]serverinfo`` with the latest changes from Discord (:issue:`4116`) -- Simple version of ``[p]serverinfo`` now shows info about more detailed ``[p]serverinfo 1`` (:issue:`4121`) - - -Redbot 3.3.11 (2020-08-10) -========================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`douglas-cpp`, :ghuser:`Drapersniper`, :ghuser:`Flame`, :ghuser:`jack1142`, :ghuser:`MeatyChunks`, :ghuser:`Vexed01`, :ghuser:`yamikaitou` - -End-user changelog ------------------- - -Audio -***** - -- Audio should now work again on all voice regions (:issue:`4162`, :issue:`4168`) -- Removed an edge case where an unfriendly error message was sent in Audio cog (:issue:`3879`) - -Cleanup -******* - -- Fixed a bug causing ``[p]cleanup`` commands to clear all messages within last 2 weeks when ``0`` is passed as the amount of messages to delete (:issue:`4114`, :issue:`4115`) - -CustomCommands -************** - -- ``[p]cc show`` now sends an error message when command with the provided name couldn't be found (:issue:`4108`) - -Downloader -********** - -- ``[p]findcog`` no longer fails for 3rd-party cogs without any author (:issue:`4032`, :issue:`4042`) -- Update commands no longer crash when a different repo is added under a repo name that was once used (:issue:`4086`) - -Permissions -*********** - -- ``[p]permissions removeserverrule`` and ``[p]permissions removeglobalrule`` no longer error when trying to remove a rule that doesn't exist (:issue:`4028`, :issue:`4036`) - -Warnings -******** - -- ``[p]warn`` now sends an error message (instead of no feedback) when an unregistered reason is used by someone who doesn't have Administrator permission (:issue:`3839`, :issue:`3840`) - - -Redbot 3.3.10 (2020-07-09) -========================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`bobloy`, :ghuser:`Dav-Git`, :ghuser:`Drapersniper`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`Injabie3`, :ghuser:`jack1142`, :ghuser:`mikeshardmind`, :ghuser:`MiniJennJenn`, :ghuser:`NeuroAssassin`, :ghuser:`thisisjvgrace`, :ghuser:`Vexed01` - -End-user changelog ------------------- - -Audio -***** - -- Added information about internally managed jar to ``[p]audioset info`` (:issue:`3915`) -- Updated to Lavaplayer 1.3.50 -- Twitch playback and YouTube searching should be functioning again. - -Core Bot -******** - -- Fixed delayed help when ``[p]set deletedelay`` is enabled (:issue:`3884`, :issue:`3883`) -- Bumped the Discord.py requirement from 1.3.3 to 1.3.4 (:issue:`4053`) -- Added settings view commands for nearly all cogs. (:issue:`4041`) -- Added more strings to be fully translatable by i18n. (:issue:`4044`) - -Downloader -********** - -- Added ``[p]cog listpinned`` subcommand to see currently pinned cogs (:issue:`3974`) -- Fixed unnecessary typing when running downloader commands (:issue:`3964`, :issue:`3948`) -- Added embed version of ``[p]findcog`` (:issue:`3965`, :issue:`3944`) -- Fixed ``[p]findcog`` not differentiating between core cogs and local cogs(:issue:`3969`, :issue:`3966`) - -Filter -****** - -- Added ``[p]filter list`` to show filtered words, and removed DMs when no subcommand was passed (:issue:`3973`) - -Image -***** - -- Updated instructions for obtaining and setting the GIPHY API key (:issue:`3994`) - -Mod -*** - -- Added option to delete messages within the passed amount of days with ``[p]tempban`` (:issue:`3958`) -- Added the ability to permanently ban a temporary banned user with ``[p]hackban`` (:issue:`4025`) -- Fixed the passed reason not being used when using ``[p]tempban`` (:issue:`3958`) -- Fixed invite being sent with ``[p]tempban`` even when no invite was set (:issue:`3991`) -- Prevented an issue whereby the author may lock themself out of using the bot via whitelists (:issue:`3903`) -- Reduced the number of API calls made to the storage APIs (:issue:`3910`) - -Permissions -*********** - -- Uploaded YAML files now accept integer commands without quotes (:issue:`3987`, :issue:`3185`) -- Uploaded YAML files now accept command rules with empty dictionaries (:issue:`3987`, :issue:`3961`) - -Streams -******* - -- Fixed streams cog sending multiple owner notifications about twitch secret not set (:issue:`3901`, :issue:`3587`) -- Fixed old bearer tokens not being invalidated when the API key is updated (:issue:`3990`, :issue:`3917`) - -Trivia Lists -************ - -- Fixed URLs in ``whosthatpokemon`` (:issue:`3975`, :issue:`3023`) -- Fixed trivia files ``leagueults`` and ``sports`` (:issue:`4026`) -- Updated ``greekmyth`` to include more answer variations (:issue:`3970`) -- Added new ``lotr`` trivia list (:issue:`3980`) -- Added new ``r6seige`` trivia list (:issue:`4026`) - - -Developer changelog -------------------- - -- Added the utility functions ``map``, ``find``, and ``next`` to ``AsyncIter`` (:issue:`3921`, :issue:`3887`) -- Updated deprecation times for ``APIToken``, and loops being passed to various functions to the first minor release (represented by ``X`` in ``3.X.0``) after 2020-08-05 (:issue:`3608`) -- Updated deprecation warnings for shared libs to reflect that they have been moved for an undefined time (:issue:`3608`) -- Added new ``discord.com`` domain to ``INVITE_URL_RE`` common filter (:issue:`4012`) -- Fixed incorrect role mention regex in ``MessagePredicate`` (:issue:`4030`) -- Vendor the ``discord.ext.menus`` module (:issue:`4039`) - - -Miscellaneous -------------- - -- Improved error responses for when Modlog and Autoban on mention spam were already disabled (:issue:`3951`, :issue:`3949`) -- Clarified that ``[p]embedset user`` only affects commands executed in DMs (:issue:`3972`, :issue:`3953`) -- Added link to Getting Started guide if the bot was not in any guilds (:issue:`3906`) -- Fixed exceptions being ignored or not sent to log files in special cases (:issue:`3895`) -- Added the option of using dots in the instance name when creating your instances (:issue:`3920`) -- Added a confirmation when using hyphens in instance names to discourage the use of them (:issue:`3920`) -- Fixed migration owner notifications being sent even when migration was not necessary (:issue:`3911`. :issue:`3909`) -- Fixed commands being translated where they should not be (:issue:`3938`, :issue:`3919`) -- Fixed grammar errors and added full stopts in ``core_commands.py`` (:issue:`4023`) - - -Redbot 3.3.9 (2020-06-12) -========================= - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`Dav-Git`, :ghuser:`Drapersniper`, :ghuser:`Flame442`, :ghuser:`mikeshardmind`, :ghuser:`NeuroAssassin`, :ghuser:`Predeactor`, :ghuser:`Vexed01` -| -| **Read before updating**: -| 1. Bot owners can no longer restrict access to some commands in Permissions cog using global permissions rules. Look at `Permissions changelog ` for full details. -| 2. There's been a change in behavior of warning messages. Look at `Warnings changelog ` for full details. - - -End-user changelog ------------------- - -Security -******** - -**NOTE**: If you can't update immediately, we recommend disabling the affected command until you can. - -- **Mod** - ``[p]tempban`` now properly respects Discord's hierarchy rules (:issue:`3957`) - -Core Bot -******** - -- ``[p]info`` command can now be used when bot doesn't have Embed Links permission (:issue:`3907`, :issue:`3102`) -- Fixed ungraceful error that happened in ``[p]set custominfo`` when provided text was too long (:issue:`3923`) -- Red's start up message now shows storage type (:issue:`3935`) - -Audio -***** - -- Audio now properly ignores streams when max length is enabled (:issue:`3878`, :issue:`3877`) -- Commands that should work in DMs no longer error (:issue:`3880`) - -Filter -****** - -- Fixed behavior of detecting quotes in commands for adding/removing filtered words (:issue:`3925`) - -.. _important-339-2: - -Permissions -*********** - -- **Both global and server rules** can no longer prevent guild owners from accessing commands for changing server rules. Bot owners can still use ``[p]command disable`` if they wish to completely disable any command in Permissions cog (:issue:`3955`, :issue:`3107`) - - Full list of affected commands: - - - ``[p]permissions acl getserver`` - - ``[p]permissions acl setserver`` - - ``[p]permissions acl updateserver`` - - ``[p]permissions addserverrule`` - - ``[p]permissions removeserverrule`` - - ``[p]permissions setdefaultserverrule`` - - ``[p]permissions clearserverrules`` - - ``[p]permissions canrun`` - - ``[p]permissions explain`` - -.. _important-339-1: - -Warnings -******** - -- Warnings sent to users don't show the moderator who warned the user by default now. Newly added ``[p]warningset showmoderators`` command can be used to switch this behaviour (:issue:`3781`) -- Warn channel functionality has been fixed (:issue:`3781`) - - -Developer changelog -------------------- - -Core Bot -******** - -- Added `bot.set_prefixes() ` method that allows developers to set global/server prefixes (:issue:`3890`) - - -Documentation changes ---------------------- - -- Added Oracle Cloud to free hosting section in :ref:`host-list` (:issue:`3916`) - -Miscellaneous -------------- - -- Added missing help message for Downloader, Reports and Streams cogs (:issue:`3892`) -- **Core Bot** - cooldown in ``[p]contact`` no longer applies when it's used without any arguments (:issue:`3942`) -- **Core Bot** - improved instructions on obtaining user ID in help of ``[p]dm`` command (:issue:`3946`) -- **Alias** - ``[p]alias global`` group, ``[p]alias help``, and ``[p]alias show`` commands can now be used in DMs (:issue:`3941`, :issue:`3940`) -- **Audio** - Typo fix (:issue:`3889`, :issue:`3900`) -- **Audio** - Fixed ``[p]audioset autoplay`` being available in DMs (:issue:`3899`) -- **Bank** - ``[p]bankset`` now displays bank's scope (:issue:`3954`) -- **Mod** - Preemptive fix for d.py 1.4 (:issue:`3891`) - - -Redbot 3.3.8 (2020-05-29) -================================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`Bakersbakebread`, :ghuser:`DariusStClair`, :ghuser:`Dav-Git`, :ghuser:`Drapersniper`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`mikeshardmind`, :ghuser:`NeuroAssassin`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`qaisjp`, :ghuser:`Tobotimus` - -End-user changelog ------------------- - -Core Bot -******** - -- Important fixes to how PostgreSQL data backend saves data in bulks (:issue:`3829`) -- Fixed ``[p]localwhitelist`` and ``[p]localblacklist`` commands (:issue:`3857`) -- Red now includes information on how to update when sending information about being out of date (:issue:`3744`) -- Using backslashes in bot's username/nickname no longer causes issues (:issue:`3826`, :issue:`3825`) - -Admin -***** - -- Fixed server lock (:issue:`3815`, :issue:`3814`) - -Alias -***** - -- Added pagination to ``[p]alias list`` and ``[p]alias global list`` to avoid errors for users with a lot of aliases (:issue:`3844`, :issue:`3834`) -- ``[p]alias help`` should now work more reliably (:issue:`3864`) - -Audio -***** - -- Twitch playback is functional once again (:issue:`3873`) -- Recent errors with YouTube playback should be resolved (:issue:`3873`) -- Added new option (settable with ``[p]audioset lyrics``) that makes Audio cog prefer (prioritize) tracks with lyrics (:issue:`3519`) -- Added global daily (historical) queues (:issue:`3518`) -- Added ``[p]audioset countrycode`` that allows to set the country code for spotify searches (:issue:`3528`) -- Fixed ``[p]local search`` (:issue:`3528`, :issue:`3501`) -- Local folders with special characters should work properly now (:issue:`3528`, :issue:`3467`) -- Audio no longer fails to take the last spot in the voice channel with user limit (:issue:`3528`) -- ``[p]local play`` no longer enqueues tracks from nested folders (:issue:`3528`) -- Fixed ``[p]playlist dedupe`` not removing tracks (:issue:`3518`) -- ``[p]disconnect`` now allows to disconnect if both DJ mode and voteskip aren't enabled (:issue:`3502`, :issue:`3485`) -- Many UX improvements and fixes, including, among other things: - - - Creating playlists without explicitly passing ``-scope`` no longer causes errors (:issue:`3500`) - - ``[p]playlist list`` now shows all accessible playlists if ``--scope`` flag isn't used (:issue:`3518`) - - ``[p]remove`` now also accepts a track URL in addition to queue index (:issue:`3201`) - - ``[p]playlist upload`` now accepts a playlist file uploaded in the message with a command (:issue:`3251`) - - Commands now send friendly error messages for common errors like lost Lavalink connection or bot not connected to voice channel (:issue:`3503`, :issue:`3528`, :issue:`3353`, :issue:`3712`) - -CustomCommands -************** - -- ``[p]customcom create`` no longer allows spaces in custom command names (:issue:`3816`) - -Mod -*** - -- ``[p]userinfo`` now shows default avatar when no avatar is set (:issue:`3819`) - -Modlog -****** - -- Fixed (again) ``AttributeError`` for cases whose moderator doesn't share the server with the bot (:issue:`3805`, :issue:`3784`, :issue:`3778`) - -Permissions -*********** - -- Commands for settings ACL using yaml files now properly works on PostgreSQL data backend (:issue:`3829`, :issue:`3796`) - -Warnings -******** - -- Warnings cog no longer allows to warn bot users (:issue:`3855`, :issue:`3854`) - - -Developer changelog -------------------- - -| **Important:** -| If you're using RPC, please see the full annoucement about current state of RPC in main Red server - `by clicking here `_. - - -Core Bot -******** - -- Red now inherits from `discord.ext.commands.AutoShardedBot` for better compatibility with code expecting d.py bot (:issue:`3822`) -- Libraries using ``pkg_resources`` (like ``humanize`` or ``google-api-python-client``) that were installed through Downloader should now work properly (:issue:`3843`) -- All bot owner IDs can now be found under ``bot.owner_ids`` attribute (:issue:`3793`) - - - Note: If you want to use this on bot startup (e.g. in cog's initialisation), you need to await ``bot.wait_until_red_ready()`` first - - -Documentation changes ---------------------- - -- Added information about provisional status of RPC (:issue:`3862`) -- Revised install instructions (:issue:`3847`) -- Improved navigation in `document about updating Red ` (:issue:`3856`, :issue:`3849`) - - -Miscellaneous -------------- - -- Few clarifications and typo fixes in few command help docstrings (:issue:`3817`, :issue:`3823`, :issue:`3837`, :issue:`3851`, :issue:`3861`) -- **Downloader** - Downloader no longer removes the repo when it fails to load it (:issue:`3867`) - - -Redbot 3.3.7 (2020-04-28) -========================= - -This is a hotfix release fixing issue with generating messages for new cases in Modlog. - - -Redbot 3.3.6 (2020-04-27) -========================= - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`Drapersniper`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`MiniJennJenn`, :ghuser:`NeuroAssassin`, :ghuser:`PredaaA`, :ghuser:`TrustyJAID`, :ghuser:`yamikaitou` - -End-user changelog ------------------- - -Core Bot -******** - -- Converting from and to Postgres driver with ``redbot-setup convert`` have been fixed (:issue:`3714`, :issue:`3115`) -- Fixed big delays in commands that happened when the bot was owner-less (or if it only used co-owners feature) and command caller wasn't the owner (:issue:`3782`) -- Various optimizations - - - Reduced calls to data backend when loading bot's commands (:issue:`3764`) - - Reduced calls to data backend when showing help for cogs/commands (:issue:`3766`) - - Improved performance for bots with big amount of guilds (:issue:`3767`) - - Mod cog no longer fetches guild's bans every 60 seconds when handling unbanning for tempbans (:issue:`3783`) - - Reduced the bot load for messages starting with a prefix when fuzzy search is disabled (:issue:`3718`) - - Aliases in Alias cog are now cached for better performance (:issue:`3788`) - -Core Commands -************* - -- ``[p]set avatar`` now supports setting avatar using attachment (:issue:`3747`) -- Added ``[p]set avatar remove`` subcommand for removing bot's avatar (:issue:`3757`) -- Fixed list of ignored channels that is shown in ``[p]ignore``/``[p]unignore`` (:issue:`3746`) - -Audio -***** - -- Age-restricted tracks, live streams, and mix playlists from YouTube should work in Audio again (:issue:`3791`) -- SoundCloud sets and playlists with more than 50 tracks should work in Audio again (:issue:`3791`) - -CustomCommands -************** - -- Added ``[p]cc raw`` command that gives you the raw response of a custom command for ease of copy pasting (:issue:`3795`) - -Modlog -****** - -- Fixed ``AttributeError`` for cases whose moderator doesn't share the server with the bot (:issue:`3784`, :issue:`3778`) - -Streams -******* - -- Fixed incorrect stream URLs for Twitch channels that have localised display name (:issue:`3773`, :issue:`3772`) - -Trivia -****** - -- Fixed the error in ``[p]trivia stop`` that happened when there was no ongoing trivia session in the channel (:issue:`3774`) - -Trivia Lists -************ - -- Updated ``leagueoflegends`` list with new changes to League of Legends (`b8ac70e `_) - - -Developer changelog -------------------- - -Utility Functions -***************** - -- Added `redbot.core.utils.AsyncIter` utility class which allows you to wrap regular iterable into async iterator yielding items and sleeping for ``delay`` seconds every ``steps`` items (:issue:`3767`, :issue:`3776`) -- `bold()`, `italics()`, `strikethrough()`, and `underline()` now accept ``escape_formatting`` argument that can be used to disable escaping of markdown formatting in passed text (:issue:`3742`) - - -Documentation changes ---------------------- - -- Added `document about updating Red ` (:issue:`3790`) -- ``pyenv`` instructions will now update ``pyenv`` if it's already installed (:issue:`3740`) -- Updated Python version in ``pyenv`` instructions (:issue:`3740`) -- Updated install docs to include Ubuntu 20.04 (:issue:`3792`) - - -Miscellaneous -------------- - -- **Config** - JSON driver will now properly have only one lock per cog name (:issue:`3780`) -- **Core Commands** - ``[p]debuginfo`` now shows used storage type (:issue:`3794`) -- **Trivia** - Corrected spelling of Compact Disc in ``games`` list (:issue:`3759`, :issue:`3758`) - - -Redbot 3.3.5 (2020-04-09) -========================= - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`jack1142`, :ghuser:`Kowlin` - -End-user changelog ------------------- - -Core Bot -******** - -- "Outdated" field no longer shows in ``[p]info`` when Red is up-to-date (:issue:`3730`) - -Alias -***** - -- Fixed regression in ``[p]alias add`` that caused it to reject commands containing arguments (:issue:`3734`) - - -Redbot 3.3.4 (2020-04-05) -========================= - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`jack1142`, :ghuser:`kennnyshiwa` - -End-user changelog ------------------- - -Core Bot -******** - -- Fixed checks related to bank's global state that were used in commands in Bank, Economy and Trivia cogs (:issue:`3707`) - -Alias -***** - -- ``[p]alias add`` now sends an error when command user tries to alias doesn't exist (:issue:`3710`, :issue:`3545`) - -Developer changelog -------------------- - -Core Bot -******** - -- Bump dependencies, including update to discord.py 1.3.3 (:issue:`3723`) - -Utility Functions -***************** - -- `redbot.core.utils.common_filters.filter_invites` now filters ``discord.io/discord.li`` invites links (:issue:`3717`) -- Fixed false-positives in `redbot.core.utils.common_filters.filter_invites` (:issue:`3717`) - -Documentation changes ---------------------- - -- Versions of pre-requirements are now included in Windows install guide (:issue:`3708`) - - -Redbot 3.3.3 (2020-03-28) -========================= - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`AnonGuy`, :ghuser:`Dav-Git`, :ghuser:`FancyJesse`, :ghuser:`Ianardo-DiCaprio`, :ghuser:`jack1142`, :ghuser:`kennnyshiwa`, :ghuser:`Kowlin`, :ghuser:`NeuroAssassin`, :ghuser:`PredaaA`, :ghuser:`Stonedestroyer`, :ghuser:`TrustyJAID` - -End-user changelog ------------------- - -Core Bot -******** - -- Delete delay for command messages has been moved from Mod cog to Core (:issue:`3638`, :issue:`3636`) -- Fixed various bugs with blacklist and whitelist (:issue:`3643`, :issue:`3642`) -- Added ``[p]set regionalformat`` command that allows users to set regional formatting that is different from bot's locale (:issue:`3677`, :issue:`3588`) -- ``[p]set locale`` allows any valid locale now, not just locales for which Red has translations (:issue:`3676`, :issue:`3596`) -- Permissions for commands in Bank, Economy and Trivia cogs can now be overridden by Permissions cog (:issue:`3672`, :issue:`3233`) -- Outages of ``pypi.org`` no longer prevent the bot from starting (:issue:`3663`) -- Fixed formatting of help strings in fuzzy search results (:issue:`3673`, :issue:`3507`) -- Fixed few deprecation warnings related to menus and uvloop (:issue:`3644`, :issue:`3700`) - -Core Commands -************* - -- ``[p]set game`` no longer errors when trying to clear the status (:issue:`3630`, :issue:`3628`) -- All owner notifcations in Core now use proper prefixes in messages (:issue:`3632`) -- Added ``[p]set playing`` and ``[p]set streaming`` aliases for respectively ``[p]set game`` and ``[p]set stream`` (:issue:`3646`, :issue:`3590`) - -ModLog -****** - -- Modlog's cases now keep last known username to prevent losing that information from case's message on edit (:issue:`3674`, :issue:`3443`) - -CustomCom -********* - -- Added ``[p]cc search`` command that allows users to search through created custom commands (:issue:`2573`) - -Cleanup -******* - -- Added ``[p]cleanup spam`` command that deletes duplicate messages from the last X messages and keeps only one copy (:issue:`3688`) -- Removed regex support in ``[p]cleanup self`` (:issue:`3704`) - -Downloader -********** - -- ``[p]cog checkforupdates`` now includes information about cogs that can't be installed due to Red/Python version requirements (:issue:`3678`, :issue:`3448`) - -General -******* - -- Added more detailed mode to ``[p]serverinfo`` command that can be accessed with ``[p]serverinfo 1`` (:issue:`2382`, :issue:`3659`) - -Image -***** - -- Users can now specify how many images should be returned in ``[p]imgur search`` and ``[p]imgur subreddit`` using ``[count]`` argument (:issue:`3667`, :issue:`3044`) -- ``[p]imgur search`` and ``[p]imgur subreddit`` now return one image by default (:issue:`3667`, :issue:`3044`) - -Mod -*** - -- ``[p]userinfo`` now shows user's activities (:issue:`3669`) -- ``[p]userinfo`` now shows status icon near the username (:issue:`3669`) -- Muting no longer fails if user leaves while applying overwrite (:issue:`3627`) -- Fixed error that happened when Mod cog was loaded for the first time during bot startup (:issue:`3632`, :issue:`3626`) - -Permissions -*********** - -- Commands for setting default rules now error when user tries to deny access to command designated as being always available (:issue:`3504`, :issue:`3465`) - -Streams -******* - -- Fixed an error that happened when no game was set on Twitch stream (:issue:`3631`) -- Preview picture for YouTube stream alerts is now bigger (:issue:`3689`, :issue:`3685`) -- YouTube channels with a livestream that doesn't have any current viewer are now properly showing as streaming (:issue:`3690`) -- Failures in Twitch API authentication are now logged (:issue:`3657`) - -Trivia -****** - -- Added ``[p]triviaset custom upload/delete/list`` commands for managing custom trivia lists from Discord (:issue:`3420`, :issue:`3307`) -- Trivia sessions no longer error on payout when winner's balance would exceed max balance (:issue:`3666`, :issue:`3584`) - -Warnings -******** - -- Sending warnings to warned user can now be disabled with ``[p]warnset toggledm`` command (:issue:`2929`, :issue:`2800`) -- Added ``[p]warnset warnchannel`` command that allows to set a channel where warnings should be sent to instead of the channel command was called in (:issue:`2929`, :issue:`2800`) -- Added ``[p]warnset togglechannel`` command that allows to disable sending warn message in guild channel (:issue:`2929`, :issue:`2800`) -- ``[p]warn`` now tells the moderator when bot wasn't able to send the warning to the user (:issue:`3653`, :issue:`3633`) - - -Developer changelog -------------------- - -Core Bot -******** - -- Deprecation warnings issued by Red now use correct stack level so that the cog developers can find the cause of them (:issue:`3644`) - -Dev Cog -******* - -- Add ``__name__`` to environment's globals (:issue:`3649`, :issue:`3648`) - - -Documentation changes ---------------------- - -- Fixed install instructions for Mac (:issue:`3675`, :issue:`3436`) -- Windows install instructions now use ``choco upgrade`` commands instead of ``choco install`` to ensure up-to-date packages (:issue:`3684`) - - -Miscellaneous -------------- - -- **Core Bot** - Command errors (i.e. command on cooldown, dm-only and guild-only commands, etc) can now be translated (:issue:`3665`, :issue:`2988`) -- **Core Bot** - ``redbot-setup`` now prints link to Getting started guide at the end of the setup (:issue:`3027`) -- **Core Bot** - Whitelist and blacklist commands now properly require passing at least one user (or role in case of local whitelist/blacklist) (:issue:`3652`, :issue:`3645`) -- **Downloader** - Fix misleading error appearing when repo name is already taken in ``[p]repo add`` (:issue:`3695`) -- **Downloader** - Improved error messages for unexpected errors in ``[p]repo add`` (:issue:`3656`) -- **Downloader** - Prevent encoding errors from crashing ``[p]cog update`` (:issue:`3639`, :issue:`3637`) -- **Trivia** - Non-finite numbers can no longer be passed to ``[p]triviaset timelimit``, ``[p]triviaset stopafter`` and ``[p]triviaset payout`` (:issue:`3668`, :issue:`3583`) -- **Utility Functions** - `redbot.core.utils.menus.menu()` now checks permissions *before* trying to clear reactions (:issue:`3589`, :issue:`3145`) - - -Redbot 3.3.2 (2020-02-28) -========================= - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`chasehult`, :ghuser:`Dav-Git`, :ghuser:`DiscordLiz`, :ghuser:`Drapersniper`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`Hedlund01`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`mikeshardmind`, :ghuser:`PredaaA`, :ghuser:`Stonedestroyer`, :ghuser:`trundleroo`, :ghuser:`TrustyJAID`, :ghuser:`zephyrkul` - -End-user changelog ------------------- - -Core Bot -******** - -- Ignored guilds/channels and whitelist/blacklist are now cached for performance (:issue:`3472`) -- Ignored guilds/channels have been moved from Mod cog to Core (:issue:`3472`) -- ``[p]ignore channel`` command can now also ignore channel categories (:issue:`3472`) - -Core Commands -************* - -- Core cogs will now send bot mention prefix properly in places where discord doesn't render mentions (:issue:`3579`, :issue:`3591`, :issue:`3499`) -- Fix a bug with ``[p]blacklist add`` that made it impossible to blacklist users that bot doesn't share a server with (:issue:`3472`, :issue:`3220`) -- Improve user experience of ``[p]set game/listening/watching/`` commands (:issue:`3562`) -- Add ``[p]licenceinfo`` alias for ``[p]licenseinfo`` command to conform with non-American English (:issue:`3460`) - -Admin -***** - -- ``[p]announce`` will now only send error message if an actual errors occurs (:issue:`3514`, :issue:`3513`) - -Alias -***** - -- ``[p]alias help`` will now properly work in non-English locales (:issue:`3546`) - -Audio -***** - -- Users should be able to play age-restricted tracks from YouTube again (:issue:`3620`) - -Economy -******* - -- Next payday time will now be adjusted for users when payday time is changed (:issue:`3496`, :issue:`3438`) - -Downloader -********** - -- Downloader will no longer fail because of invalid ``info.json`` files (:issue:`3533`, :issue:`3456`) -- Add better logging of errors when Downloader fails to add a repo (:issue:`3558`) - -Image -***** - -- Fix load error for users that updated Red from version lower than 3.1 to version 3.2 or newer (:issue:`3617`) - -Mod -*** - -- ``[p]hackban`` and ``[p]unban`` commands support user mentions now (:issue:`3524`) -- Ignored guilds/channels have been moved from Mod cog to Core (:issue:`3472`) - -Streams -******* - -- Fix stream alerts for Twitch (:issue:`3487`) -- Significantly reduce the quota usage for YouTube stream alerts (:issue:`3237`) -- Add ``[p]streamset timer`` command which can be used to control how often the cog checks for live streams (:issue:`3237`) - -Trivia -****** - -- Add better handling for errors in trivia session (:issue:`3606`) - -Trivia Lists -************ - -- Remove empty answers in trivia lists (:issue:`3581`) - -Warnings -******** - -- Users can now pass a reason to ``[p]unwarn`` command (:issue:`3490`, :issue:`3093`) - - -Developer changelog -------------------- - -Core Bot -******** - -- Updated all our dependencies - we're using discord.py 1.3.2 now (:issue:`3609`) -- Add traceback logging to task exception handling (:issue:`3517`) -- Developers can now create a command from an async function wrapped in `functools.partial` (:issue:`3542`) -- Bot will now show deprecation warnings in logs (:issue:`3527`, :issue:`3615`) -- Subcommands of command group with ``invoke_without_command=True`` will again inherit this group's checks (:issue:`3614`) - -Config -****** - -- Fix Config's singletons (:issue:`3137`, :issue:`3136`) - -Utility Functions -***************** - -- Add clearer error when page is of a wrong type in `redbot.core.utils.menus.menu()` (:issue:`3571`) - -Dev Cog -******* - -- Allow for top-level `await`, `async for` and `async with` in ``[p]debug`` and ``[p]repl`` commands (:issue:`3508`) - -Downloader -********** - -- Downloader will now replace ``[p]`` with clean prefix same as it does in help command (:issue:`3592`) -- Add schema validation to ``info.json`` file processing - it should now be easier to notice any issues with those files (:issue:`3533`, :issue:`3442`) - - -Documentation changes ---------------------- - -- Add guidelines for Cog Creators in `guide_cog_creation` document (:issue:`3568`) -- Restructure virtual environment instructions to improve user experience (:issue:`3495`, :issue:`3411`, :issue:`3412`) -- Getting started guide now explain use of quotes for arguments with spaces (:issue:`3555`, :issue:`3111`) -- ``latest`` version of docs now displays a warning about possible differences from current stable release (:issue:`3570`) -- Make systemd guide clearer on obtaining username and python path (:issue:`3537`, :issue:`3462`) -- Indicate instructions for different venv types in systemd guide better (:issue:`3538`) -- Service file in `autostart_systemd` now also waits for network connection to be ready (:issue:`3549`) -- Hide alias of ``randomize_colour`` in docs (:issue:`3491`) -- Add separate headers for each event predicate class for better navigation (:issue:`3595`, :issue:`3164`) -- Improve wording of explanation for ``required_cogs`` key in `guide_publish_cogs` (:issue:`3520`) - - -Miscellaneous -------------- - -- Use more reliant way of checking if command is bot owner only in ``[p]warnaction`` (Warnings cog) (:issue:`3516`, :issue:`3515`) -- Update PyPI domain in ``[p]info`` and update checker (:issue:`3607`) -- Stop using deprecated code in core (:issue:`3610`) - - -Redbot 3.3.1 (2020-02-05) -========================= - - -Core Bot --------- - -- Add a cli flag for setting a max size of message cache -- Allow to edit prefix from command line using ``redbot --edit``. -- Some functions have been changed to no longer use deprecated asyncio functions - -Core Commands -------------- - -- The short help text for dm has been made more useful -- dm no longer allows owners to have the bot attempt to DM itself - -Utils ------ - -- Passing the event loop explicitly in utils is deprecated (Removal in 3.4) - -Mod Cog -------- - -- Hackban now works properly without being provided a number of days - -Documentation Changes ---------------------- - -- Add ``-e`` flag to ``journalctl`` command in systemd guide so that it takes the user to the end of logs automatically. -- Added section to install docs for CentOS 8 -- Improve usage of apt update in docs - -Redbot 3.3.0 (2020-01-26) -========================= - -Core Bot --------- - -- The bot's description is now configurable. -- We now use discord.py 1.3.1, this comes with added teams support. -- The commands module has been slightly restructured to provide more useful data to developers. -- Help is now self consistent in the extra formatting used. - -Core Commands -------------- - -- Slowmode should no longer error on nonsensical time quantities. -- Embed use can be configured per channel as well. - -Documentation -------------- - -- We've made some small fixes to inaccurate instructions about installing with pyenv. -- Notes about deprecating in 3.3 have been altered to 3.4 to match the intended timeframe. - -Admin ------ - -- Gives feedback when adding or removing a role doesn't make sense. - -Audio ------ - -- Playlist finding is more intuitive. -- disconnect and repeat commands no longer interfere with eachother. - -CustomCom ---------- - -- No longer errors when exiting an interactive menu. - -Cleanup -------- - -- A rare edge case involving messages which are deleted during cleanup and are the only message was fixed. - -Downloader ----------- - -- Some user facing messages were improved. -- Downloader's initialization can no longer time out at startup. - -General -------- - -- Roll command will no longer attempt to roll obscenely large amounts. - -Mod ---- - -- You can set a default amount of days to clean up when banning. -- Ban and hackban now use that default. -- Users can now optionally be DMed their ban reason. - -Permissions ------------ - -- Now has stronger enforcement of prioritizing botwide settings. diff --git a/docs/changelog_3_4_0.rst b/docs/changelog_3_4_0.rst deleted file mode 100644 index 0f47fc6e9e7..00000000000 --- a/docs/changelog_3_4_0.rst +++ /dev/null @@ -1,1584 +0,0 @@ -.. 3.4.x Changelogs - -Redbot 3.4.18 (2022-08-15) -========================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`RheingoldRiver` - -Read before updating --------------------- - -#. openSUSE Leap 15.2 is no longer supported as it has already reached its end of life. -#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): - - - Red 3.4.18 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. - - We've updated our `application.yml file `__ and you should update your instance's ``application.yml`` appropriately. - - -End-user changelog ------------------- - -Core Bot -******** - -- openSUSE Leap 15.2 is no longer supported as it has already reached its end of life (:issue:`5777`) - -Audio -***** - -- Addressed a cipher change that made it impossible to find tracks (:issue:`5822`) -- Fixed an issue with ``[p]llset external`` making the bot completely unresponsive when switching to an external Lavalink server (:issue:`5804`, :issue:`5828`) - - -Documentation changes ---------------------- - -- Updated the screenshot in `bot_application_guide` to include the message content intent (:issue:`5798`) -- Unpinned Temurin version on Windows as a fixed version is now available (:issue:`5815`) - - -Redbot 3.4.17 (2022-06-07) -========================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`Drapersniper`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`Kreusada`, :ghuser:`ltzmax`, :ghuser:`matcha19`, :ghuser:`mina9999`, :ghuser:`ponte-vecchio`, :ghuser:`PredaaA`, :ghuser:`TrustyJAID`, :ghuser:`untir-l`, :ghuser:`Vexed01` - -Read before updating --------------------- - -#. Fedora 34 is no longer supported as it has already reached its end of life. -#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): - - Red 3.4.17 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. - - -End-user changelog ------------------- - -Core Bot -******** - -- Fedora 33 is no longer supported as it has already reached its end of life (:issue:`5701`) -- Added instructions on how to respond to the message received from ``[p]contact`` in the embed footer of the message sent to the bot owner (:issue:`5528`, :issue:`5529`) -- Updated ``[p]servers`` command to escape Discord markdown in server names (:issue:`5696`, :issue:`5744`) -- Fixed a bug that prevented users from changing the name and data location with ``redbot --edit`` command (:issue:`5545`, :issue:`5540`, :issue:`5541`) -- Fixed grammar in the ``[p]uptime`` command (:issue:`5596`) - -Audio -***** - -- Added timestamps to all embeds sent by Audio cog (:issue:`5632`) -- Improved handling of voice connection close codes received from Discord (:issue:`5712`) -- Fixed plain word YT searching with ``[p]play`` and ``[p]search`` commands (:issue:`5712`) -- Fixed YT age-restricted track playback (:issue:`5712`) -- Fixed the cog not sending any Track Error message on track decoding errors (:issue:`5716`) -- Fixed the ``UnboundLocalError`` exception happening when using ``[p]playlist list`` with an empty playlist (:issue:`5378`, :issue:`5394`) - -Downloader -********** - -- Added information about the commit hash at which the cog is pinned in the output of ``[p]cog listpinned`` command (:issue:`5551`, :issue:`5563`) - -Filter -****** - -- Fixed a potential memory leak in Filter cog (:issue:`5578`) - -General -******* - -- Updated features list in ``[p]serverinfo`` with the latest changes from Discord (:issue:`5655`) - -Mod -*** - -- Updated Red's ban commands to address the breaking change that Discord made in their ban list API endpoint (:issue:`5656`) - -Modlog -****** - -- Modlog's automated case creation for bans now properly checks that the guild is available before further processing (:issue:`5647`) - -Mutes -***** - -- Added proper error handling for VERY long durations in mute commands (:issue:`5605`) - -Permissions -*********** - -- Updated ``[p]permissions acl setglobal`` and ``[p]permissions acl setserver`` to allow sending the file in a follow-up message (:issue:`5473`, :issue:`5685`) -- ``[p]permissions canrun`` now prepends an emoji to the response to better differentiate between the positive and negative results (:issue:`5711`) - -Trivia -****** - -- Allowed passing ``use_spoilers`` setting in the CONFIG section of the trivia list file (:issue:`5566`) - -Trivia Lists -************ - -- Added a trivia list for the FIFA World Cup with questions based on hosts, placements, venues, continental confederations, number of participants, top goal scorers, qualification shocks, and more (:issue:`5639`) -- Updated ``geography`` trivia list with up-to-date answers and removed questions that lack sources for their claimed answers (:issue:`5638`) -- Updated Kazakhstan's capital city in the ``worldcapitals`` trivia list (:issue:`5598`, :issue:`5599`) -- Fixed spelling error in the answer to one of the questions in ``computers`` trivia list (:issue:`5587`, :issue:`5588`) - - -Developer changelog -------------------- - -- Updated ``discord.ext.menus`` vendor (:issue:`5579`) - - -Documentation changes ---------------------- - -- Added CentOS Stream 9, RHEL 9, Alma Linux 9, Oracle Linux 9, and Rocky Linux 9 install guides (:issue:`5537`, :issue:`5721`) -- Added Ubuntu 22.04 install guide (:issue:`5720`) -- Changed the recommended operating system for hosting Red from Ubuntu 20.04 LTS to Ubuntu 22.04 LTS (:issue:`5720`) -- Updated Python version in ``pyenv`` and Windows instructions (:issue:`5719`) -- Replaced install instructions for discontinued AdoptOpenJDK package with Temurin 11 package in the macOS install guide (:issue:`5718`) -- Updated Visual Studio Build Tools version in Windows install guide (:issue:`5702`) -- Updated systemd guide to use the absolute path to ``which`` command to avoid triggering shell aliases on some OSes (:issue:`5547`) -- Emphasized lines that contain text that needs to be replaced by the user (:issue:`5548`) -- Prevented Google and other search engines from indexing versioned documentation (:issue:`5549`) -- Pinned Temurin version on Windows until a fixed version becomes available (:issue:`5717`) -- Fixed git installation instructions in CentOS 7 install guide (:issue:`5700`) - - -Redbot 3.4.16 (2021-12-31) -========================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`jack1142`, :ghuser:`PredaaA` - -This is a hotfix release fixing issues with invite URL API that caused -``[p]invite`` command and ``CORE__INVITE_URL`` RPC method to not work. - -End-user changelog ------------------- - -- **Core Bot** - Fixed ``[p]invite`` command (:issue:`5517`) - - -Developer changelog -------------------- - -- Fixed ``CORE__INVITE_URL`` RPC method (:issue:`5517`) - - -Documentation changes ---------------------- - -- Changed Arch install guide to temporarily use ``python39`` AUR package instead of ``python`` package as Red does not currently support Python 3.10 (:issue:`5518`) - - -Redbot 3.4.15 (2021-12-31) -========================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`aleclol`, :ghuser:`Arman0334`, :ghuser:`Crossedfall`, :ghuser:`Dav-Git`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`Jan200101`, :ghuser:`Just-Jojo`, :ghuser:`Kowlin`, :ghuser:`Kreusada`, :ghuser:`laggron42`, :ghuser:`ltzmax`, :ghuser:`Parnassius`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`RasmusWL`, :ghuser:`sravan1946`, :ghuser:`Stonedestroyer`, :ghuser:`the-krak3n`, :ghuser:`Tobotimus`, :ghuser:`vertyco`, :ghuser:`Vexed01`, :ghuser:`WreckRox`, :ghuser:`yamikaitou` - -Read before updating --------------------- - -#. Fedora 33 and CentOS 8 are no longer supported as they have already reached end of life. -#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): - - Red 3.4.15 uses a new Lavalink jar that you MUST manually update from `our GitHub `__ to be able to continue using Audio. - - -End-user changelog ------------------- - -Core Bot -******** - -- Added new CLI options for non-interactive usage of ``redbot-setup`` (:issue:`2396`, :issue:`5448`) - - See output of ``redbot-setup --help`` for more information. - -- JSON is now more strongly recommended and is used by default for new instances in ``redbot-setup`` (:issue:`5448`) -- The embed setting for ``[p]help`` command set with ``[p]embedset command`` will now affect all help messages, not just the ones sent when invoking ``[p]help`` command directly (:issue:`5452`) -- ``[p]traceback`` command now indicates that it DMed the command caller with a tick reaction (:issue:`5353`) -- Improved ``[p]helpset showaliases`` responses (:issue:`5376`) -- Added plural forms to the responses of ``[p]leave`` command (:issue:`5391`) -- Fedora 33 and CentOS 8 are no longer supported as they have already reached end of life (:issue:`5440`) -- Corrected usage examples in help of ``[p]set api`` and ``[p]set api remove`` (:issue:`5444`) -- Updated prefix length limit to ``25`` to allow setting bot mention as a prefix (:issue:`5476`) -- Confirmation prompts (accepting "yes/no" or "I agree" as the answer) no longer wrongfully translate the answer that needs to be sent when only English answers are accepted by the bot (:issue:`5363`, :issue:`5364`, :issue:`5404`) -- Fixed short help for some of the commands in Core Red (:issue:`5502`) -- Fixed issues with rendering of modlog cases with usernames written in a right-to-left language (:issue:`5422`) -- Fixed an issue with instance backup failing for non-JSON storage backends (:issue:`5315`) -- Running Red with ``--no-instance`` CLI flag no longer fails when no instance was ever created by the user (:issue:`5415`, :issue:`5416`) -- ``[p]command enable guild`` and ``[p]command disable guild`` commands no longer error out for commands that *only* check for user permissions, not caller's roles (:issue:`5477`) - -Admin -***** - -- Added ``[p]selfroleset clear`` command which can be used to clear the list of available selfroles in the server (:issue:`5387`) - -Audio -***** - -- Added native Mac M1 support for Java runtimes supporting Mac M1 (:issue:`5474`) -- Enabled JDA-NAS on all system architectures which should limit stuttering/buffering issues on some machines (:issue:`5474`) -- The bot will now disconnect from the voice channel when all members are bots if the auto-disconnect setting is enabled (:issue:`5421`) -- Fixed an issue with resuming playback after changing voice channels (:issue:`5170`) -- Fixed issues with SoundCloud private playlists and mobile links (:issue:`5474`) -- Fixed searching music with some of the queries containing quotes or backslashes (:issue:`5474`) -- Fixed an exception caused by unavailable YT tracks in Mix playlists (:issue:`5474`) -- Fixed ``IndexError`` in ``[p]queue`` command which occurred when the user provides negative integer as the page number (:issue:`5429`) - -Cleanup -******* - -- Restricted ``[p]cleanupset notify`` to only be invokable in server channels (:issue:`5466`) - -Custom Commands -*************** - -- Added 2000 character limit for custom command responses to prevent Nitro users from adding longer responses than a Discord bot can send (:issue:`5499`) - -Dev Cog -******* - -- ``[p]mockmsg`` now allows mocking attachment-only messages (:issue:`5446`) - -Downloader -********** - -- Added repo name to the response of ``[p]findcog`` command (:issue:`5382`, :issue:`5383`) - -Economy -******* - -- ``[p]economyset showsettings`` now includes configured role payday amounts (:issue:`5455`, :issue:`5457`) - -General -******* - -- Removed voice region field from ``[p]serverinfo`` command as Discord no longer provides this setting for servers (:issue:`5449`) - -Mod -*** - -- ``[p]voicekick`` now sends a response when the action succeeds (:issue:`5367`) -- Fixed an error with ``[p]tempban`` failing to send an invite link when a server has an unset vanity URL (:issue:`5472`) -- Fixed explanations of example usage for ``[p]ban``, ``[p]kick``, and ``[p]tempban`` commands (:issue:`5372`) -- Fixed a typo in one of ``[p]unban``'s error messages (:issue:`5470`) - -Modlog -****** - -- Added the new native Discord timestamps in ``[p]case``, ``[p]casesfor``, and ``[p]listcases`` commands (:issue:`5395`) - -Warnings -******** - -- Warning actions no longer error out when the action is set to use a command that *only* checks for user permissions, not caller's roles (:issue:`5477`) - - -Developer changelog -------------------- - -- Added optional ``message`` argument to `Context.tick()` and `Context.react_quietly()` which is used if adding the reaction doesn't succeed (:issue:`3359`, :issue:`4092`) -- Added optional ``check_permissions`` keyword-only argument to `Red.embed_requested()` which, if ``True``, will make the method also check whether the bot can send embeds in the given channel (:issue:`5452`) -- Added `Red.get_invite_url()` and `Red.is_invite_url_public()` that expose the functionality of ``[p]invite`` programmatically (:issue:`5152`, :issue:`5424`) -- Changed the output of ``CORE__LOAD``, ``CORE__RELOAD``, and ``CORE__UNLOAD`` RPC methods to a dictionary (:issue:`5451`, :issue:`5453`) - - -Documentation changes ---------------------- - -- Added install guide for Alma Linux 8.4-8.x and Raspberry Pi OS 11 Bullseye (:issue:`5440`) -- Updated the Java distribution used in the Windows install guide to Temurin - rebranded AdoptOpenJDK (:issue:`5403`) -- Improved Mac and pyenv instructions to address common issues with load path configuration (:issue:`5356`) -- Updated the server locations for Hetzner and Contabo in :ref:`host-list` document (:issue:`5475`) -- Updated Python version in ``pyenv`` and Windows instructions (:issue:`5447`) -- Removed inaccurate note from Unix install guides about install commands also being used for updating Red (:issue:`5439`) -- Removed LXC from unsupported hosting platforms as many VPS providers utilize that technology (:issue:`5351`) -- Specified that Red currently requires Python 3.8.1 - 3.9.x (:issue:`5403`) - - -Redbot 3.4.14 (2021-09-23) -========================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`L33Tech`, :ghuser:`maxbooiii`, :ghuser:`RheingoldRiver` - -Read before updating --------------------- - -#. Versions of RHEL older than 8.4 (including 7) and versions of CentOS older than 8.4 (excluding 7) are no longer supported. -#. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): - - Red 3.4.14 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. - - -End-user changelog ------------------- - -- **Core Bot** - Added the new native Discord timestamp in the ``[p]uptime`` command (:issue:`5323`) -- **Core Bot** - ``redbot-setup delete`` command no longer requires database connection if the data deletion was not requested (:issue:`5312`, :issue:`5313`) -- **Audio** - Fixed intermittent 403 Forbidden errors (:issue:`5329`) -- **Modlog** - Fixed formatting of **Last modified at** field in Modlog cases (:issue:`5317`) - - -Documentation changes ---------------------- - -- Each operating system now has a dedicated install guide (:issue:`5328`) -- Fixed Raspberry Pi OS install guide (:issue:`5314`, :issue:`5328`) -- Added install guide for CentOS Stream 8, Oracle Linux 8.4-8.x, and Rocky Linux 8 (:issue:`5328`) -- Install guides for RHEL derivatives no longer require the use of pyenv (:issue:`5328`) - - -Redbot 3.4.13 (2021-09-09) -========================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`Arman0334`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`fredster33`, :ghuser:`Injabie3`, :ghuser:`jack1142`, :ghuser:`Just-Jojo`, :ghuser:`Kowlin`, :ghuser:`Kreusada`, :ghuser:`leblancg`, :ghuser:`maxbooiii`, :ghuser:`npc203`, :ghuser:`palmtree5`, :ghuser:`phenom4n4n`, :ghuser:`PredaaA`, :ghuser:`qenu`, :ghuser:`TheDataLeek`, :ghuser:`Twentysix26`, :ghuser:`TwinDragon`, :ghuser:`Vexed01` - -Read before updating --------------------- - -1. If you're hosting a public/big bot (>75 servers) or strive to scale your bot at that level, you should read :doc:`our stance on (privileged) intents and public bots `. -2. Fedora 32 is no longer supported as it has already reached end of life. -3. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): - - Red 3.4.13 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. - - -End-user changelog ------------------- - -Core Bot -******** - -- Added a new ``[p]diagnoseissues`` command to allow the bot owners to diagnose issues with various command checks with ease (:issue:`4717`, :issue:`5243`) - - Since some of us are pretty excited about this feature, here's a very small teaser showing a part of what it can do: - - .. figure:: https://user-images.githubusercontent.com/6032823/132610057-d6c65d67-c244-4f0b-9458-adfbe0c68cab.png - -- Revamped the ``[p]debuginfo`` to make it more useful for... You guessed it, debugging! (:issue:`4997`, :issue:`5156`) - - More specifically, added information about CPU and RAM, bot's instance name and owners - -- The formatting of Red's console logs has been updated to make it more copy-paste friendly (:issue:`4868`, :issue:`5181`) -- Added the new native Discord timestamps in Modlog cases, ``[p]userinfo``, ``[p]serverinfo``, and ``[p]tempban`` (:issue:`5155`, :issue:`5241`) -- Added a setting for ``[p]help``'s reaction timeout (:issue:`5205`) - - This can be changed with ``[p]helpset reacttimeout`` command - -- Red 3.4.13 is the first release to (finally) support Python 3.9! (:issue:`4655`, :issue:`5121`) -- Upgraded all Red's dependencies (:issue:`5121`) -- Fedora 32 is no longer supported as it has already reached end of life (:issue:`5121`) -- Fixed a bunch of errors related to the missing permissions and channels/messages no longer existing (:issue:`5109`, :issue:`5163`, :issue:`5172`, :issue:`5191`) - -Admin -***** - -- The ``[p]selfroleset add`` and ``[p]selfroleset remove`` commands can now be used to add multiple selfroles at once (:issue:`5237`, :issue:`5238`) - -Alias -***** - -- Added commands for editing existing aliases (:issue:`5108`) - -Audio -***** - -- Added a per-guild max volume setting (:issue:`5165`) - - This can be changed with the ``[p]audioset maxvolume`` command - -- Fixed an issue with short clips being cutoff when auto-disconnect on queue end is enabled (:issue:`5158`, :issue:`5188`) -- Fixed fetching of age-restricted tracks (:issue:`5233`) -- Fixed searching of YT Music (:issue:`5233`) -- Fixed playback from SoundCloud (:issue:`5233`) -- ``[p]summon`` will now indicate that it has succeeded or failed to summon the bot (:issue:`5186`) - -Cleanup -******* - -- The ``[p]cleanup user`` command can now be used to clean messages of a user that is no longer in the server (:issue:`5169`) -- All ``[p]cleanup`` commands will now send a notification with the number of deleted messages. The notification is deleted automatically after 5 seconds (:issue:`5218`) - - This can be disabled with the ``[p]cleanupset notify`` command - -Downloader -********** - -- The dot character (``.``) can now be used in repo names. No more issues with adding repositories using the commands provided by the Cog Index! (:issue:`5214`) - -Filter -****** - -- Added ``[p]filter clear`` and ``[p]filter channel clear`` commands for clearing the server's/channel's filter list (:issue:`4841`, :issue:`4981`) - -Mod -*** - -- Fixed an error with handling of temporary ban expirations while the guild is unavailable due to Discord outage (:issue:`5173`) -- The DM message from the ``[p]tempban`` command will now include the ban reason if ``[p]modset dm`` setting is enabled (:issue:`4836`, :issue:`4837`) -- The ``[p]rename`` command will no longer permit changing nicknames of members that are not lower in the role hierarchy than the command caller (:issue:`5187`, :issue:`5211`) - -Streams -******* - -- Fixed an issue with some YouTube streamers getting removed from stream alerts after a while (:issue:`5195`, :issue:`5223`) -- Made small optimizations in regards to stream alerts (:issue:`4968`) - -Trivia -****** - -- Added schema validation of the custom trivia files (:issue:`4571`, :issue:`4659`) - -Warnings -******** - -- 0 point warnings are, once again, allowed. (:issue:`5177`, :issue:`5178`) - - -Developer changelog -------------------- - -- Added `RelativedeltaConverter` and `parse_relativedelta` to the ``redbot.core.commands`` package (:issue:`5000`) - - This converter and function return `dateutil.relativedelta.relativedelta` object that represents a relative delta. - In addition to regular timedelta arguments, it also accepts months and years! - -- Added more APIs for allowlists and blocklists (:issue:`5206`) - - Here's the list of the methods that were added to the ``bot`` object: - - - `Red.add_to_blacklist()` - - `Red.remove_from_blacklist()` - - `Red.get_blacklist()` - - `Red.clear_blacklist()` - - `Red.add_to_whitelist()` - - `Red.remove_from_whitelist()` - - `Red.get_whitelist()` - - `Red.clear_whitelist()` - -- Added `CommandConverter` and `CogConverter` to the ``redbot.core.commands`` package (:issue:`5037`) - - -Documentation changes ---------------------- - -- Added a document about (privileged) intents and our stance regarding "public bots" (:issue:`5216`, :issue:`5221`) -- Added install instructions for Debian 11 Bullseye (:issue:`5213`, :issue:`5217`) -- Added Oracle Cloud's Always Free offering to the :ref:`host-list` (:issue:`5225`) -- Updated the commands in the install guide for Mac OS to work properly on Apple Silicon devices (:issue:`5234`) -- Fixed the examples of commands that are only available to people with the mod role (:issue:`5180`) -- Fixed few other small issues with the documentation :) (:issue:`5048`, :issue:`5092`, :issue:`5149`, :issue:`5207`, :issue:`5209`, :issue:`5215`, :issue:`5219`, :issue:`5220`) - - -Miscellaneous -------------- - -- **Core Bot** - The console error about missing Privileged Intents stands out more now (:issue:`5184`) -- **Core Bot** - The ``[p]invite`` command will now add a tick reaction after it DMs an invite link to the user (:issue:`5184`) -- **Downloader** - Added a few missing line breaks (:issue:`5185`, :issue:`5187`) - - -Redbot 3.4.12 (2021-06-17) -========================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`Just-Jojo`, :ghuser:`Kowlin`, :ghuser:`Kreusada`, :ghuser:`npc203`, :ghuser:`PredaaA`, :ghuser:`retke`, :ghuser:`Stonedestroyer` - -This is a hotfix release related to Red ceasing to use the Audio Global API service. - -Full changelog --------------- - -- **Audio** - Updated URL of the curated playlist (:issue:`5135`) -- **Audio** - All local caches are now enabled by default (:issue:`5140`) -- **Audio** - Global API service will no longer be used in Audio and as such support for it has been removed from the cog (:issue:`5143`) -- **Core Bot** - ``[p]set serverprefix`` command will now prevent the user from setting a prefix with length greater than 20 characters (:issue:`5091`, :issue:`5117`) -- **Core Bot** - ``[p]set prefix`` command will now warn the user when trying to set a prefix with length greater than 20 characters (:issue:`5091`, :issue:`5117`) -- **Core Bot** - ``applications.commands`` scope can now be included in the invite URL returned from ``[p]invite`` by enabling it with``[p]inviteset commandscope`` -- **Dev Cog** - ``[p]debug`` command will now confirm the code finished running with a tick reaction (:issue:`5107`) -- **Filter** - Fixed an edge case that caused the cog to sometimes check contents of DM messages (:issue:`5125`) -- **Warnings** - Prevented users from applying 0 or less points in custom warning reasons (:issue:`5119`, :issue:`5120`) - - -Redbot 3.4.11 (2021-06-12) -========================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`Onii-Chan-Discord` - -This is a hotfix release fixing a crash involving guild uploaded stickers. - -Full changelog --------------- - -- discord.py version has been bumped to 1.7.3 (:issue:`5129`) -- Links to the CogBoard in Red's documentation have been updated to use the new domain (:issue:`5124`) - - -Redbot 3.4.10 (2021-05-28) -========================== - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`aleclol`, :ghuser:`benno1237`, :ghuser:`bobloy`, :ghuser:`BoyDownTown`, :ghuser:`Danstr5544`, :ghuser:`DeltaXWizard`, :ghuser:`Drapersniper`, :ghuser:`Fabian-Evolved`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`Kreusada`, :ghuser:`Lifeismana`, :ghuser:`Obi-Wan3`, :ghuser:`OofChair`, :ghuser:`palmtree5`, :ghuser:`plofts`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`TrustyJAID`, :ghuser:`Vexed01` - -Read before updating --------------------- - -1. PM2 process manager is no longer supported as it is not a viable solution due to certain parts of its behavior. - - We highly recommend you to switch to one of the other supported solutions: - - `autostart_systemd` - - `autostart_mac` - - If you experience any issues when trying to configure it, you can join `our discord server `__ and ask in the **support** channel for help. -2. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): - - - Red 3.4.10 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. - - We've updated our `application.yml file `__ and you should update your instance's ``application.yml`` appropriately. - - -End-user changelog ------------------- - -Core Bot -******** - -- Fixed terminal colors on Windows (:issue:`5063`) -- Fixed the ``--rich-traceback-extra-lines`` flag (:issue:`5028`) -- Added missing information about the ``showaliases`` setting in ``[p]helpset showsettings`` (:issue:`4971`) -- The help command no longer errors when it doesn't have permission to read message history and menus are enabled (:issue:`4959`, :issue:`5030`) -- Fixed a bug in ``[p]embedset user`` that made it impossible to reset the user's embed setting (:issue:`4962`) -- ``[p]embedset command`` and its subcommands now properly check whether any of the passed command's parents require Embed Links permission (:issue:`4962`) -- Fixed an issue with Red reloading unrelated modules when using ``[p]load`` and ``[p]reload`` (:issue:`4956`, :issue:`4958`) - -Admin -***** - -- The cog will now log when it leaves a guild due to the serverlock (:issue:`5008`, :issue:`5073`) - -Audio -***** - -- Fixed an issue that made it possible to remove Aikaterna's curated tracks playlist (:issue:`5018`) -- Fixed auto-resume of auto play after Lavalink restart (:issue:`5051`) -- The ``[p]audiostats`` command can now only be used by bot owners (:issue:`5017`) -- Fixed an error with ``[p]audiostats`` caused by players not always having their connection time stored (:issue:`5046`) -- Fixed track resuming in a certain edge case (:issue:`4996`) -- Fixed an error in ``[p]audioset restart`` (:issue:`4987`) -- The cog will now check whether it has speak permissions in the channel before performing any actions (:issue:`5012`) -- Fixed an issue with Audio failing when it's missing permissions to send a message in the notification channel (:issue:`4960`) -- Fixed fetching of age-restricted tracks (:issue:`5085`) -- Fixed an issue with SoundCloud URLs that ended with a slash (``/``) character (:issue:`5085`) - -Custom Commands -*************** - -- ``[p]customcom create simple`` no longer errors for a few specific names (:issue:`5026`, :issue:`5027`) - -Downloader -********** - -- ``[p]repo remove`` can now remove multiple repos at the same time (:issue:`4765`, :issue:`5082`) -- ``[p]cog install`` now properly shows the repo name rather than ``{repo.name}`` (:issue:`4954`) - -Mod -*** - -- ``[p]mute`` no longer errors on muting a bot user if the ``senddm`` option is enabled (:issue:`5071`) - -Mutes -***** - -- Forbidden errors during the channel mute are now handled properly in a rare edge case (:issue:`4994`) - -Modlog -****** - -- ``[p]modlogset resetcases`` will now ask for confirmation before proceeding (:issue:`4976`) -- Modlog will no longer try editing the case's Discord message once it knows that it no longer exists (:issue:`4975`) - -Streams -******* - -- Fixed Picarto support (:issue:`4969`, :issue:`4970`) -- ``[p]twitchstream``, ``[p]youtubestream``, and ``[p]picarto`` commands can no longer be run in DMs (:issue:`5036`, :issue:`5035`) -- Smashcast service has been closed and for that reason we have removed support for it from the cog (:issue:`5039`, :issue:`5040`) -- Fixed Twitch stream alerts for streams that use localized display names (:issue:`5050`, :issue:`5066`) -- The cog no longer errors when trying to delete a cached message from a channel that no longer exists (:issue:`5032`, :issue:`5031`) -- In message template, ``{stream.display_name}`` can now be used to refer to streamer's display name (:issue:`5050`, :issue:`5066`) - - - This is not always the same as ``{stream}`` which refers to the streamer's channel or username - -Warnings -******** - -- The warn action is now taken *after* sending the warn message to the member (:issue:`4713`, :issue:`5004`) - - -Developer changelog -------------------- - -- Bumped discord.py to 1.7.2 (:issue:`5066`) -- The log messages shown by the global error handler will now show the trace properly for task done callbacks (:issue:`4980`) -- **Dev** - ``[p]eval``, ``[p]repl``, and ``[p]debug`` commands no longer fail to send very long syntax errors (:issue:`5041`) -- **Dev** - ``[p]eval``, ``[p]repl``, and ``[p]debug`` commands now, in addition to ``py``, support code blocks with ``python`` syntax (:issue:`5083`) - - -Documentation changes ---------------------- - -- Added `a guide for making auto-restart service on Mac ` (:issue:`4082`, :issue:`5020`) -- Added `cog guide for core commands ` (:issue:`1734`, :issue:`4597`) -- Added `cog guide for Mod cog ` (:issue:`1734`, :issue:`4886`) -- Added `cog guide for Modlog cog ` (:issue:`1734`, :issue:`4919`) -- Added `cog guide for Mutes cog ` (:issue:`1734`, :issue:`4875`) -- Added `cog guide for Permissions cog ` (:issue:`1734`, :issue:`4985`) -- Added `cog guide for Reports cog ` (:issue:`1734`, :issue:`4882`) -- Added `cog guide for Warnings cog ` (:issue:`1734`, :issue:`4920`) -- Added :ref:`a guide about Trivia list creation ` (:issue:`4595`, :issue:`5023`) -- Added the documentation for `redbot.core.modlog.Case` (:issue:`4979`) -- Removed PM2 guide (:issue:`4991`) - - -Miscellaneous -------------- - -- Clarified that ``[p]cleanup`` commands only delete the messages from the current channel (:issue:`5070`) -- Updated Python version in ``pyenv`` and Windows instructions (:issue:`5025`) -- Added information on how to set the bot not to start on boot anymore to auto-restart docs (:issue:`5020`) -- Improved logging in Audio cog (:issue:`5044`) -- Improved logging of API errors in Streams cog (:issue:`4995`) -- The command ``[p]urban`` from the General cog will now use the default embed color of the bot (:issue:`5014`) -- Cog creation guide now includes the ``bot`` as an argument to the cog class (:issue:`4988`) -- Rephrased a few strings and fixed maaaaany grammar issues and typos (:issue:`4793`, :issue:`4832`, :issue:`4955`, :issue:`4966`, :issue:`5015`, :issue:`5019`, :issue:`5029`, :issue:`5038`, :issue:`5055`, :issue:`5080`, :issue:`5081`) - - -Redbot 3.4.9 (2021-04-06) -========================= - -This is a hotfix release fixing an issue with command error handling. - -discord.py version has been bumped to 1.7.1. - -Thanks again to :ghuser:`Rapptz` for quick response on this issue. - - -Redbot 3.4.8 (2021-04-06) -========================= -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`6days9weeks`, :ghuser:`aikaterna`, :ghuser:`Drapersniper`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`kingslayer268`, :ghuser:`Kowlin`, :ghuser:`Kreusada`, :ghuser:`Obi-Wan3`, :ghuser:`OofChair`, :ghuser:`palmtree5`, :ghuser:`phenom4n4n`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`rijusougata13`, :ghuser:`TheDiscordHistorian`, :ghuser:`Tobotimus`, :ghuser:`TrustyJAID`, :ghuser:`Twentysix26`, :ghuser:`Vexed01` - -Read before updating --------------------- - -1. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): - - Red 3.4.8 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. - -2. Fedora 31 and OpenSUSE Leap 15.1 are no longer supported as they have already reached end of life. - - -End-user changelog ------------------- - -Core Bot -******** - -- Added per-command embed settings (:issue:`4049`) - - - See help of ``[p]embedset`` and ``[p]embedset command`` command group for more information - -- The ``[p]servers`` command uses menus now (:issue:`4720`, :issue:`4831`) -- ``[p]leave`` accepts server IDs now (:issue:`4831`) -- Commands for listing global and local allowlists and blocklists will now, in addition to IDs, contain user/role names (:issue:`4839`) -- Messages sent interactively in DM channels no longer fail (:issue:`4876`) -- An error message will now be shown when a command that is only available in NSFW channels is used in a non-NSFW channel (:issue:`4933`) -- Added more singular and plural forms in a bunch of commands in the bot (:issue:`4004`, :issue:`4898`) -- Removed the option to drop the entire PostgreSQL database in ``redbot-setup delete`` due to limitations of PostgreSQL (:issue:`3699`, :issue:`3833`) -- Added a progress bar to ``redbot-setup convert`` (:issue:`2952`) -- Fixed how the command signature is shown in help for subcommands that have group args (:issue:`4928`) - -Alias -***** - -- Fixed issues with command aliases for commands that take an arbitrary, but non-zero, number of arguments (e.g. ``[p]load``) (:issue:`4766`, :issue:`4871`) - -Audio -***** - -- Fixed stuttering (:issue:`4565`) -- Fixed random disconnects (:issue:`4565`) -- Fixed the issues causing the player to be stuck on 00:00 (:issue:`4565`) -- Fixed ghost players (:issue:`4565`) -- Audio will no longer stop playing after a while (:issue:`4565`) -- Fixed playlist loading for playlists with over 100 songs (:issue:`4932`) -- Fixed an issue with alerts causing errors in playlists being loaded (:issue:`4932`) -- Improved playlist extraction (:issue:`4932`) -- Fixed an issue with consent pages appearing while trying to load songs or playlists (:issue:`4932`) - -Cleanup -******* - -- ``[p]cleanup before`` and ``[p]cleanup after`` commands can now be used without a message ID if the invocation message replies to some message (:issue:`4790`) - -Downloader -********** - -- Improved compatibility with Git 2.31 and newer (:issue:`4897`) - -Filter -****** - -- Added meaningful error messages for incorrect arguments in the ``[p]bank set`` command (:issue:`4789`, :issue:`4801`) - -Mod -*** - -- Improved performance of checking tempban expirations (:issue:`4907`) -- Fixed tracking of nicknames that were set just before nick reset (:issue:`4830`) - -Mutes -***** - -- Vastly improved performance of automatic unmute handling (:issue:`4906`) - -Streams -******* - -- Streams cog should now load faster on bots that have many stream alerts set up (:issue:`4731`, :issue:`4742`) -- Fixed possible memory leak related to automatic message deletion (:issue:`4731`, :issue:`4742`) -- Streamer accounts that no longer exist are now properly handled (:issue:`4735`, :issue:`4746`) -- Fixed stream alerts being sent even after unloading Streams cog (:issue:`4940`) -- Checking Twitch streams will now make less API calls (:issue:`4938`) -- Ratelimits from Twitch API are now properly handled (:issue:`4808`, :issue:`4883`) - -Trivia -****** - -- Added a new option for hiding the answer to the Trivia answer in a spoiler (:issue:`4700`, :issue:`4877`) - - - ``[p]triviaset usespoilers`` command can be used to enable/disable this option - -Warnings -******** - -- Fixed output of ``[p]warnings`` command for members that are no longer in the server (:issue:`4900`, :issue:`4904`) -- Embeds now use the default embed color of the bot (:issue:`4878`) - - -Developer changelog -------------------- - -- Bumped discord.py version to 1.7.0 (:issue:`4928`) -- Deprecated importing ``GuildConverter`` from ``redbot.core.commands.converter`` namespace (:issue:`4928`) - - - ``discord.Guild`` or ``GuildConverter`` from ``redbot.core.commands`` should be used instead -- Added ``guild`` parameter to `bot.allowed_by_whitelist_blacklist() ` which is meant to replace the deprecated ``guild_id`` parameter (:issue:`4905`, :issue:`4914`) - - - Read the method's documentation for more information -- Fixed ``on_red_api_tokens_update`` not being dispatched when the tokens were removed with ``[p]set api remove`` (:issue:`4916`, :issue:`4917`) - - -Documentation changes ---------------------- - -- Added a note about updating cogs in update message and documentation (:issue:`4910`) -- Added `cog guide for Image cog ` (:issue:`4821`) -- Updated Mac install guide with new ``brew`` commands (:issue:`4865`) -- `getting-started` now contains an explanation of parameters that can take an arbitrary number of arguments (:issue:`4888`, :issue:`4889`) -- Added a warning to Arch Linux install guide about the instructions being out-of-date (:issue:`4866`) -- All shell commands in the documentation are now prefixed with an unselectable prompt (:issue:`4908`) -- `systemd-service-guide` now asks the user to create the new service file using ``nano`` text editor (:issue:`4869`, :issue:`4870`) - - - Instructions for all Linux-based operating systems now recommend to install ``nano`` -- Updated Python version in ``pyenv`` and Windows instructions (:issue:`4864`, :issue:`4942`) - - -Redbot 3.4.7 (2021-02-26) -========================= -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`elijabesu`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`kreusada`, :ghuser:`palmtree5`, :ghuser:`TrustyJAID` - -End-user changelog ------------------- - -- Added proper permission checks to ``[p]muteset senddm`` and ``[p]muteset showmoderator`` (:issue:`4849`) -- Updated the ``[p]lmgtfy`` command to use the new domain (:issue:`4840`) -- Updated the ``[p]info`` command to more clearly indicate that the instance is owned by a team (:issue:`4851`) -- Fixed minor issues with error messages in Mutes cog (:issue:`4847`, :issue:`4850`, :issue:`4853`) - -Documentation changes ---------------------- - -- Added `cog guide for General cog ` (:issue:`4797`) -- Added `cog guide for Trivia cog ` (:issue:`4566`) - - -Redbot 3.4.6 (2021-02-16) -========================= -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`aleclol`, :ghuser:`Andeeeee`, :ghuser:`bobloy`, :ghuser:`BreezeQS`, :ghuser:`Danstr5544`, :ghuser:`Dav-Git`, :ghuser:`Elysweyr`, :ghuser:`Fabian-Evolved`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`Injabie3`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`kreusada`, :ghuser:`leblancg`, :ghuser:`maxbooiii`, :ghuser:`NeuroAssassin`, :ghuser:`phenom4n4n`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`retke`, :ghuser:`siu3334`, :ghuser:`Strafee`, :ghuser:`TheWyn`, :ghuser:`TrustyJAID`, :ghuser:`Vexed01`, :ghuser:`yamikaitou` - -Read before updating --------------------- - -1. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): - - Red 3.4.6 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. - - -End-user changelog ------------------- - -Core Bot -******** - -- Fixed the rotation of Red's logs that could before result in big disk usage (:issue:`4405`, :issue:`4738`) -- Fixed command usage in the help messages for few commands in Red (:issue:`4599`, :issue:`4733`) -- Fixed errors in ``[p]command defaultdisablecog`` and ``[p]command defaultenablecog`` commands (:issue:`4767`, :issue:`4768`) -- ``[p]command listdisabled guild`` can no longer be run in DMs (:issue:`4771`, :issue:`4772`) -- Improvements and fixes for our new (colorful) logging (:issue:`4702`, :issue:`4726`) - - - The colors used have been adjusted to be readable on many more terminal applications - - The ``NO_COLOR`` environment variable can now be set to forcefully disable all colors in the console output - - Tracebacks will now use the full width of the terminal again - - Tracebacks no longer contain multiple lines per stack level (this can now be changed with the flag ``--rich-traceback-extra-lines``) - - Disabled syntax highlighting on the log messages - - Dev cog no longer captures logging output - - Added some cool features for developers - - - Added the flag ``--rich-traceback-extra-lines`` which can be used to set the number of additional lines in tracebacks - - Added the flag ``--rich-traceback-show-locals`` which enables showing local variables in tracebacks - - - Improved and fixed a few other minor things - -- Added a friendly error message to ``[p]load`` that is shown when trying to load a cog with a command name that is already taken by a different cog (:issue:`3870`) -- Help now includes command aliases in the command help (:issue:`3040`) - - - This can be disabled with ``[p]helpset showaliases`` command - -- Fixed errors appearing when using Ctrl+C to interrupt ``redbot --edit`` (:issue:`3777`, :issue:`4572`) - -Admin -***** - -- ``[p]selfrole`` can now be used without a subcommand and passed with a selfrole directly to add/remove it from the user running the command (:issue:`4826`) - -Audio -***** - -- Improved detection of embed players for fallback on age-restricted YT tracks (:issue:`4818`, :issue:`4819`) -- Improved MP4/AAC decoding (:issue:`4818`, :issue:`4819`) -- Requests for YT tracks are now retried if the initial request causes a connection reset (:issue:`4818`, :issue:`4819`) - -Cleanup -******* - -- Renamed the ``[p]cleanup spam`` command to ``[p]cleanup duplicates``, with the old name kept as an alias for the time being (:issue:`4814`) -- Fixed an error from passing an overly large integer as a message ID to ``[p]cleanup after`` and ``[p]cleanup before`` (:issue:`4791`) - -Dev Cog -******* - -- Help descriptions of the cog and its commands now get translated properly (:issue:`4815`) - -Economy -******* - -- ``[p]economyset rolepaydayamount`` can now remove the previously set payday amount (:issue:`4661`, :issue:`4758`) - -Filter -****** - -- Added a case type ``filterhit`` which is used to log filter hits (:issue:`4676`, :issue:`4739`) - -Mod -*** - -- The ``[p]tempban`` command no longer errors out when trying to ban a user in a guild with the vanity url feature that doesn't have a vanity url set (:issue:`4714`) -- Fixed an edge case in role hierarchy checks (:issue:`4740`) -- Added two new settings for disabling username and nickname tracking (:issue:`4799`) - - - Added a command ``[p]modset trackallnames`` that disables username tracking and overrides the nickname tracking setting for all guilds - - Added a command ``[p]modset tracknicknames`` that disables nickname tracking in a specific guild - -- Added a command ``[p]modset deletenames`` that deletes all stored usernames and nicknames (:issue:`4827`) -- Added usage examples to ``[p]kick``, ``[p]ban``, ``[p]massban``, and ``[p]tempban`` (:issue:`4712`, :issue:`4715`) -- Updated DM on kick/ban to use bot's default embed color (:issue:`4822`) - -Modlog -****** - -- Added a command ``[p]listcases`` that allows you to see multiple cases for a user at once (:issue:`4426`) -- Added typing indicator to ``[p]casesfor`` command (:issue:`4426`) - -Mutes -***** - -- Fixed an edge case in role hierarchy checks (:issue:`4740`) -- The modlog reason no longer contains leading whitespace when it's passed *after* the mute time (:issue:`4749`) -- A DM can now be sent to the (un)muted user on mute and unmute (:issue:`3752`, :issue:`4563`) - - - Added ``[p]muteset senddm`` to set whether the DM should be sent (function disabled by default) - - Added ``[p]muteset showmoderator`` to set whether the DM sent to the user should include the name of the moderator that muted the user (function disabled by default) - -- Added more role hierarchy checks to ensure permission escalations cannot occur on servers with a careless configuration (:issue:`4741`) -- Help descriptions of the cog and its commands now get translated properly (:issue:`4815`) - -Reports -******* - -- Reports now use the default embed color of the bot (:issue:`4800`) - -Streams -******* - -- Fixed incorrect timezone offsets for some YouTube stream schedules (:issue:`4693`, :issue:`4694`) -- Fixed meaningless errors happening when the YouTube API key becomes invalid or when the YouTube quota is exceeded (:issue:`4745`) - -Trivia -****** - -- Payout for trivia sessions ending in a tie now gets split between all the players with the highest score (:issue:`3931`, :issue:`4649`) - -Trivia Lists -************ - -- Added new Who's That Pokémon - Gen. VI trivia list (:issue:`4785`) -- Updated answers regarding some of the hero's health and abilities in the ``overwatch`` trivia list (:issue:`4805`) - - -Developer changelog -------------------- - -Core Bot -******** - -- Updated versions of the libraries used in Red: discord.py to 1.6.0, aiohttp to 3.7.3 (:issue:`4728`) -- Added an event ``on_red_before_identify`` that is dispatched before IDENTIFYing a session (:issue:`4647`) - -Utility Functions -***************** - -- Added a function `redbot.core.utils.chat_formatting.spoiler()` that wraps the given text in a spoiler (:issue:`4754`) - -Dev Cog -******* - -- Cogs can now add their own variables to the environment of ``[p]debug``, ``[p]eval``, and ``[p]repl`` commands (:issue:`4667`) - - - Variables can be added and removed from the environment of Dev cog using two new methods: - - - `bot.add_dev_env_value() ` - - `bot.remove_dev_env_value() ` - - -Documentation changes ---------------------- - -- Added `cog guide for Filter cog ` (:issue:`4579`) -- Added information about the Red Index to `guide_publish_cogs` (:issue:`4778`) -- Restructured the host list (:issue:`4710`) -- Clarified how to use pm2 with ``pyenv virtualenv`` (:issue:`4709`) -- Updated the pip command for Red with the postgres extra in Linux/macOS install guide to work on zsh shell (:issue:`4697`) -- Updated Python version in ``pyenv`` and Windows instructions (:issue:`4770`) - - -Miscellaneous -------------- - -- Various grammar fixes (:issue:`4705`, :issue:`4748`, :issue:`4750`, :issue:`4763`, :issue:`4788`, :issue:`4792`, :issue:`4810`) -- Red's dependencies have been bumped (:issue:`4572`) - - -Redbot 3.4.5 (2020-12-24) -========================= -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`Injabie3`, :ghuser:`NeuroAssassin` - -End-user changelog ------------------- - -Streams -******* - -- Fixed Streams failing to load and work properly (:issue:`4687`, :issue:`4688`) - - -Redbot 3.4.4 (2020-12-24) -========================= - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`bobloy`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`kreus7`, :ghuser:`NeuroAssassin`, :ghuser:`npc203`, :ghuser:`palmtree5`, :ghuser:`phenom4n4n`, :ghuser:`Predeactor`, :ghuser:`retke`, :ghuser:`siu3334`, :ghuser:`Vexed01`, :ghuser:`yamikaitou` - -Read before updating --------------------- - -1. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): - - Red 3.4.4 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. - -2. Ubuntu 16.04 is no longer supported as it will soon reach its end of life and it is no longer viable for us to maintain support for it. - - While you might still be able to run Red on it, we will no longer put any resources into supporting it. If you're using Ubuntu 16.04, we highly recommend that you upgrade to the latest LTS version of Ubuntu. - - -End-user changelog ------------------- - -Core Bot -******** - -- Red's logging will now shine in your terminal more than ever (:issue:`4577`) -- Improved consistency of command usage in the help messages within all commands in Core Red (:issue:`4589`) -- Added a friendly error when the duration provided to commands that use the ``commands.TimedeltaConverter`` converter is out of the maximum bounds allowed by Python interpreter (:issue:`4019`, :issue:`4628`, :issue:`4630`) -- Fixed an error when removing path from a different operating system than the bot is currently running on with ``[p]removepath`` (:issue:`2609`, :issue:`4662`, :issue:`4466`) - -Audio -***** - -- Fixed ``[p]llset java`` failing to set the Java executable path (:issue:`4621`, :issue:`4624`) -- Fixed SoundCloud playback (:issue:`4683`) -- Fixed YouTube age-restricted track playback (:issue:`4683`) -- Added more friendly messages for 429 errors to let users know they have been temporarily banned from accessing the service instead of a generic Lavalink error (:issue:`4683`) -- Environment information will now be appended to Lavalink tracebacks in the spring.log (:issue:`4683`) - -Cleanup -******* - -- ``[p]cleanup self`` will now delete the command message when the bot has permissions to do so (:issue:`4640`) - -Dev -*** - -- Added new ``[p]bypasscooldown`` command that allows owners to bypass command cooldowns (:issue:`4440`) - -Economy -******* - -- ``[p]economyset slotmin`` and ``[p]economyset slotmax`` now warn when the new value will cause the slots command to not work (:issue:`4583`) - -General -******* - -- Updated features list in ``[p]serverinfo`` with the latest changes from Discord (:issue:`4678`) - -Mod -*** - -- ``[p]ban`` command will no longer error out when the given reason is too long (:issue:`4187`, :issue:`4189`) - -Streams -******* - -- Scheduled YouTube streams now work properly with the cog (:issue:`3691`, :issue:`4615`) -- YouTube stream schedules are now announced before the stream (:issue:`4615`) - - - Alerts about YouTube stream schedules can be disabled with a new ``[p]streamset ignoreschedule`` command (:issue:`4615`) - -- Improved error logging (:issue:`4680`) - -Trivia Lists -************ - -- Added ``whosthatpokemon5`` trivia list containing Pokémon from the 5th generation (:issue:`4646`) -- Added ``geography`` trivia list (:issue:`4618`) - - -Developer changelog -------------------- - -- `get_audit_reason()` can now be passed a ``shorten`` keyword argument which will automatically shorten the returned audit reason to fit the max length allowed by Discord audit logs (:issue:`4189`) -- ``bot.remove_command()`` now returns the command object of the removed command as does the equivalent method from `discord.ext.commands.Bot` class (:issue:`4636`) - - -Documentation changes ---------------------- - -- Added `cog guide for Downloader cog ` (:issue:`4511`) -- Added `cog guide for Economy cog ` (:issue:`4519`) -- Added `cog guide for Streams cog ` (:issue:`4521`) -- Added `guide_cog_creators` document (:issue:`4637`) -- Removed install instructions for Ubuntu 16.04 (:issue:`4650`) - - -Redbot 3.4.3 (2020-11-16) -========================= - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`bobloy`, :ghuser:`Flame442`, :ghuser:`jack1142`, :ghuser:`KianBral`, :ghuser:`maxbooiii`, :ghuser:`phenom4n4n`, :ghuser:`Predeactor`, :ghuser:`retke` - -Read before updating --------------------- - -1. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): - - Red 3.4.3 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. - -End-user changelog ------------------- - -Core Bot -******** - -- Added ``[p]set competing`` command that allows users to set the bot's competing status (:issue:`4607`, :issue:`4609`) - -Audio -***** - -- Volume changes on ARM systems running a 64 bit OS will now work again (:issue:`4608`) -- Fixed only 100 results being returned on a Youtube playlist (:issue:`4608`) -- Fixed YouTube VOD duration being set to unknown (:issue:`3885`, :issue:`4608`) -- Fixed some YouTube livestreams getting stuck (:issue:`4608`) -- Fixed internal Lavalink manager failing for Java with untypical version formats (:issue:`4608`) -- Improved AAC audio handling (:issue:`4608`) -- Added support for SoundCloud HLS streams (:issue:`4608`) - -Economy -******* - -- The ``[p]leaderboard`` command no longer fails in DMs when a global bank is used (:issue:`4569`) - -Mod -*** - -- The ban reason is now properly set in the audit log and modlog when using the ``[p]massban`` command (:issue:`4575`) -- The ``[p]userinfo`` command now shows the new Competing activity (:issue:`4610`, :issue:`4611`) - -Modlog -****** - -- The ``[p]case`` and ``[p]casesfor`` commands no longer fail when the bot doesn't have Read Message History permission in the modlog channel (:issue:`4587`, :issue:`4588`) - -Mutes -***** - -- Fixed automatic remuting on member join for indefinite mutes (:issue:`4568`) - -Trivia -****** - -- ``[p]triviaset custom upload`` now ensures that the filename is lowercase when uploading (:issue:`4594`) - -Developer changelog -------------------- - -- ``modlog.get_case()`` and methods using it no longer raise when the bot doesn't have Read Message History permission in the modlog channel (:issue:`4587`, :issue:`4588`) - -Documentation changes ---------------------- - -- Added `guide for Cog Manager UI ` (:issue:`4152`) -- Added `cog guide for CustomCommands cog ` (:issue:`4490`) - - -Redbot 3.4.2 (2020-10-28) -========================= - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`aikaterna`, :ghuser:`Drapersniper`, :ghuser:`jack1142`, :ghuser:`Kowlin`, :ghuser:`PredaaA`, :ghuser:`Stonedestroyer` - -Read before updating --------------------- - -1. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): - - Red 3.4.2 uses a new Lavalink jar that you will need to manually update from `our GitHub `__. - -End-user changelog ------------------- - -- **Core Bot** - Added info about the metadata file to ``redbot --debuginfo`` (:issue:`4557`) -- **Audio** - Fixed the ``[p]local search`` command (:issue:`4553`) -- **Audio** - Fixed random "Something broke when playing the track." errors for YouTube tracks (:issue:`4559`) -- **Audio** - Commands in ``[p]llset`` group can now be used in DMs (:issue:`4562`) -- **Mod** - Fixed ``[p]massban`` not working for banning members that are in the server (:issue:`4556`, :issue:`4555`) -- **Streams** - Added error messages when exceeding the YouTube quota in the Streams cog (:issue:`4552`) -- **Streams** - Improved logging for unexpected errors in the Streams cog (:issue:`4552`) - -Documentation changes ---------------------- - -- Added `cog guide for Cleanup cog ` (:issue:`4488`) -- Removed multi-line commands from Linux install guides to avoid confusing readers (:issue:`4550`) - - -Redbot 3.4.1 (2020-10-27) -========================= - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`absj30`, :ghuser:`aikaterna`, :ghuser:`bobloy`, :ghuser:`chloecormier`, :ghuser:`Dav-Git`, :ghuser:`Drapersniper`, :ghuser:`fixator10`, :ghuser:`Flame442`, :ghuser:`flaree`, :ghuser:`Generaleoley`, :ghuser:`hisztendahl`, :ghuser:`jack1142`, :ghuser:`KaiGucci`, :ghuser:`Kowlin`, :ghuser:`maxbooiii`, :ghuser:`MeatyChunks`, :ghuser:`NeuroAssassin`, :ghuser:`nfitzen`, :ghuser:`palmtree5`, :ghuser:`phenom4n4n`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`PythonTryHard`, :ghuser:`SharkyTheKing`, :ghuser:`Stonedestroyer`, :ghuser:`thisisjvgrace`, :ghuser:`TrustyJAID`, :ghuser:`TurnrDev`, :ghuser:`Vexed01`, :ghuser:`Vuks69`, :ghuser:`xBlynd`, :ghuser:`zephyrkul` - -Read before updating --------------------- - -1. This release fixes a security issue in Mod cog. See `Security changelog below ` for more information. -2. This Red update bumps discord.py to version 1.5.1, which explicitly requests Discord intents. Red requires all Privileged Intents to be enabled. More information can be found at :ref:`enabling-privileged-intents`. -3. Mutes functionality has been moved from the Mod cog to a new separate cog (Mutes) featuring timed and role-based mutes. If you were using it (or want to start now), you can load the new cog with ``[p]load mutes``. You can see the full `Mutes changelog below `. -4. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point): - - We've updated our `application.yml file `__ and you should update your instance's ``application.yml`` appropriately. - Please ensure that the WS port in Audio's settings (``[p]llset wsport``) is set to the port from the ``application.yml``. - -End-user changelog ------------------- - -.. _important-341-2: - -Security -******** - -**NOTE:** If you can't update immediately, we recommend globally disabling the affected command until you can. - -- **Mod** - Fixed unauthorized privilege escalation exploit in ``[p]massban`` (also called ``[p]hackban``) command. Full security advisory `can be found on our GitHub `__. - -Core Bot -******** - -- Fixed an incorrect error being reported on ``[p]set name`` when the passed name was longer than 32 characters (:issue:`4364`, :issue:`4363`) -- Fixed ``[p]set nickname`` erroring when the passed name was longer than 32 characters (:issue:`4364`, :issue:`4363`) -- Fixed an ungraceful error being raised when running ``[p]traceback`` with closed DMs (:issue:`4329`) -- Fixed errors that could arise from invalid URLs in ``[p]set avatar`` (:issue:`4437`) -- Fixed an error being raised with ``[p]set nickname`` when no nickname was provided (:issue:`4451`) -- Fixed and clarified errors being raised with ``[p]set username`` (:issue:`4463`) -- Fixed an ungraceful error being raised when the output of ``[p]unload`` is larger than 2k characters (:issue:`4469`) -- Fixed an ungraceful error being raised when running ``[p]choose`` with empty options (:issue:`4499`) -- Fixed an ungraceful error being raised when a bot left a guild while a menu was open (:issue:`3902`) -- Fixed info missing on the non-embed version of ``[p]debuginfo`` (:issue:`4524`) -- Added ``[p]set api list`` to list all currently set API services, without tokens (:issue:`4370`) -- Added ``[p]set api remove`` to remove API services, including tokens (:issue:`4370`) -- Added ``[p]helpset usetick``, toggling command message being ticked when help is sent to DM (:issue:`4467`, :issue:`4075`) -- Added a default color field to ``[p]set showsettings`` (:issue:`4498`, :issue:`4497`) -- Added the datapath and metadata file to ``[p]debuginfo`` (:issue:`4524`) -- Added a list of disabled intents to ``[p]debuginfo`` (:issue:`4423`) -- Bumped discord.py dependency to version 1.5.1 (:issue:`4423`) -- Locales and regional formats can now be set in individual guilds using ``[p]set locale`` and ``[p]set regionalformat`` (:issue:`3896`, :issue:`1970`) - - - Global locale and regional format setters have been renamed to ``[p]set globallocale`` and ``[p]set globalregionalformat`` - -Audio -***** - -- Scattered grammar and typo fixes (:issue:`4446`) -- Fixed Bandcamp playback (:issue:`4504`) -- Fixed YouTube playlist playback (:issue:`4504`) -- Fixed YouTube searching issues (:issue:`4504`) -- Fixed YouTube age restricted track playback (:issue:`4504`) -- Fixed the Audio cog not being translated when setting locale (:issue:`4492`, :issue:`4495`) -- Fixed tracks getting stuck at 0:00 after long player sessions (:issue:`4529`) -- Removed lavalink logs from being added to backup (:issue:`4453`, :issue:`4452`) -- Removed stream durations from being in queue duration (:issue:`4513`) -- Added the Global Audio API, to cut down on Youtube 429 errors and allow Spotify playback past user's quota. (:issue:`4446`) -- Added persistent queues, allowing for queues to be restored on a bot restart or cog reload (:issue:`4446`) -- Added ``[p]audioset restart``, allowing for Lavalink connection to be restarted (:issue:`4446`) -- Added ``[p]audioset autodeafen``, allowing for bot to auto-deafen itself when entering voice channel (:issue:`4446`) -- Added ``[p]audioset mycountrycode``, allowing Spotify search locale per user (:issue:`4446`) -- Added ``[p]llset java``, allowing for a custom Java executable path (:issue:`4446`) -- Added ``[p]llset info`` to show Lavalink settings (:issue:`4527`) -- Added ``[p]audioset logs`` to download Lavalink logs if the Lavalink server is set to internal (:issue:`4527`) - -Cleanup -******* - -- Allowed ``[p]cleanup self`` to work in DMs for all users (:issue:`4481`) - -Custom Commands -*************** - -- Fixed an ungraceful error being thrown on ``[p]cc edit`` (:issue:`4325`) - -Dev -*** - -- Added ``[p]repl pause`` to pause/resume the REPL session in the current channel (:issue:`4366`) - -Economy -******* - -- Added an embed option for ``[p]leaderboard`` (:issue:`4184`, :issue:`4104`) - -General -******* - -- Fixed issues with text not being properly URL encoded (:issue:`4024`) -- Fixed an ungraceful error occurring when a title is longer than 256 characters in ``[p]urban`` (:issue:`4474`) -- Changed "boosters" to "boosts" in ``[p]serverinfo`` to clarify what the number represents (:issue:`4507`) - -Mod -*** - -- Added ``[p]modset mentionspam strict`` allowing for duplicated mentions to count towards the mention spam cap (:issue:`4359`) -- Added an option to ban users not in the guild to ``[p]ban`` (:issue:`4422`, :issue:`4419`) -- Added a default tempban duration for ``[p]tempban`` (:issue:`4473`, :issue:`3992`) -- Fixed nicknames not being properly stored and logged (:issue:`4131`) -- Fixed plural typos in ``[p]userinfo`` (:issue:`4397`, :issue:`4379`) -- Renamed ``[p]hackban`` to ``[p]massban``, keeping ``[p]hackban`` as an alias, allowing for multiple users to be banned at once (:issue:`4422`, :issue:`4419`) -- Moved mutes to a separate, individual cog (:issue:`3634`) - -.. _important-341-1: - -Mutes -***** - -- Added ``[p]muteset forcerole`` to make mutes role based, instead of permission based (:issue:`3634`) -- Added an optional time argument to all mutes, to specify when the user should be unmuted (:issue:`3634`) -- Changed ``[p]mute`` to only handle serverwide muting, ``[p]mute voice`` and ``[p]mute channel`` have been moved to separate commands called ``[p]mutechannel`` and ``[p]mutevoice`` (:issue:`3634`) -- Mute commands can now take multiple user arguments, to mute multiple users at a time (:issue:`3634`) - -Modlog -****** - -- Fixed an error being raised when running ``[p]casesfor`` and ``[p]case`` (:issue:`4415`) -- Long reasons in Modlog are now properly shortened in message content (:issue:`4541`) - -Trivia Lists -************ - -- Fixed incorrect order of Machamp and Machoke questions (:issue:`4424`) -- Added new MLB trivia list (:issue:`4455`) -- Added new Who's That Pokémon - Gen. IV trivia list (:issue:`4434`) -- Added new Hockey trivia list (:issue:`4384`) - -Warnings -******** - -- Fixed users being able to warn users above them in hierarchy (:issue:`4100`) -- Added bool arguments to toggle commands to improve consistency (:issue:`4409`) - -Developer changelog -------------------- - -| **Important:** -| 1. Red now allows users to set locale per guild, which requires 3rd-party cogs to set contextual locale manually in code ran outside of command's context. See the `Core Bot changelog below ` for more information. - -.. _important-dev-341-1: - -Core Bot -******** - -- Added API for setting contextual locales (:issue:`3896`, :issue:`1970`) - - - New function added: `redbot.core.i18n.set_contextual_locales_from_guild()` - - Contextual locale is automatically set for commands and only needs to be done manually for things like event listeners; see `recommendations-for-cog-creators` for more information - -- Added `bot.remove_shared_api_services() ` to remove all keys and tokens associated with an API service (:issue:`4370`) -- Added an option to return all tokens for an API service if ``service_name`` is not specified in `bot.get_shared_api_tokens() ` (:issue:`4370`) -- Added `bot.get_or_fetch_user() ` and `bot.get_or_fetch_member() ` methods (:issue:`4403`, :issue:`4402`) -- Moved ``redbot.core.checks.bot_in_a_guild()`` to `redbot.core.commands.bot_in_a_guild()` (old name has been left as an alias) (:issue:`4515`, :issue:`4510`) - -Bank -**** - -- Bank API methods now consistently throw TypeError if a non-integer amount is supplied (:issue:`4376`) - -Mod -*** - -- Deprecated ``redbot.core.utils.mod.is_allowed_by_hierarchy`` (:issue:`4435`) - -Modlog -****** - -- Added an option to accept a ``discord.Object`` in case creation (:issue:`4326`) -- Added ``last_known_username`` parameter to `modlog.create_case()` function (:issue:`4326`) -- Fixed an error being raised with a deleted channel in `Case.message_content()` (:issue:`4415`) - -Utility -******* - -- Added `redbot.core.utils.get_end_user_data_statement()` and `redbot.core.utils.get_end_user_data_statement_or_raise()` to attempt to fetch a cog's End User Data Statement (:issue:`4404`) -- Added `redbot.core.utils.chat_formatting.quote()` to quote text in a message (:issue:`4425`) - -Documentation changes ---------------------- - -Config -****** - -- Added custom group documentation and tutorial (:issue:`4416`, :issue:`2896`) - -Modlog -****** - -- Clarified that naive ``datetime`` objects will be treated as local times for parameters ``created_at`` and ``until`` in `modlog.create_case()` (:issue:`4389`) - -Other -***** - -- Added guide to creating a Bot Application in Discord Developer Portal, with enabling intents (:issue:`4502`) - -Miscellaneous -------------- - -- Added JSON schema files for ``info.json`` files (:issue:`4375`) -- Added ``[all]`` and ``[dev]`` bundled install extras (:issue:`4443`) -- Replaced the link to the approved repository list on CogBoard and references to ``cogs.red`` with a link to new Red Index (:issue:`4439`) -- Improved documentation about arguments in command syntax (:issue:`4058`) -- Replaced a few instances of Red with the bot name in command docstrings (:issue:`4470`) -- Fixed grammar in places scattered throughout bot (:issue:`4500`) -- Properly define supported Python versions to be lower than 3.9 (:issue:`4538`) - - -Redbot 3.4.0 (2020-08-17) -========================= - -| Thanks to all these amazing people that contributed to this release: -| :ghuser:`Dav-Git`, :ghuser:`DevilXD`, :ghuser:`douglas-cpp`, :ghuser:`Drapersniper`, :ghuser:`flaree`, :ghuser:`jack1142`, :ghuser:`kablekompany`, :ghuser:`Kowlin`, :ghuser:`maxbooiii`, :ghuser:`MeatyChunks`, :ghuser:`mikeshardmind`, :ghuser:`NeuroAssassin`, :ghuser:`PredaaA`, :ghuser:`Predeactor`, :ghuser:`retke`, :ghuser:`SharkyTheKing`, :ghuser:`thisisjvgrace`, :ghuser:`Tinonb`, :ghuser:`TrustyJAID`, :ghuser:`Twentysix26`, :ghuser:`Vexed01`, :ghuser:`zephyrkul` -| -| **Read before updating**: -| 1. Red 3.4 comes with support for data deletion requests. Bot owners should read `red_core_data_statement` to ensure they know what information about their users is stored by the bot. -| 2. Debian Stretch, Fedora 30 and lower, and OpenSUSE Leap 15.0 and lower are no longer supported as they have already reached end of life. -| 3. There's been a change in behavior of ``[p]tempban``. Look at `Mod changelog ` for full details. -| 4. There's been a change in behavior of announcements in Admin cog. Look at `Admin changelog ` for full details. -| 5. Red 3.4 comes with breaking changes for cog developers. Look at `Developer changelog ` for full details. - -End-user changelog ------------------- - -Core Bot -******** - -- Added per-guild cog disabling (:issue:`4043`, :issue:`3945`) - - - Bot owners can set the default state for a cog using ``[p]command defaultdisablecog`` and ``[p]command defaultenablecog`` commands - - Guild owners can enable/disable cogs for their guild using ``[p]command disablecog`` and ``[p]command enablecog`` commands - - Cogs disabled in the guild can be listed with ``[p]command listdisabledcogs`` - -- Added support for data deletion requests; see `red_core_data_statement` for more information (:issue:`4045`) -- Red now logs clearer error if it can't find package to load in any cog path during bot startup (:issue:`4079`) -- ``[p]licenseinfo`` now has a 3 minute cooldown to prevent a single user from spamming channel by using it (:issue:`4110`) -- Added ``[p]helpset showsettings`` command (:issue:`4013`, :issue:`4022`) -- Updated Red's emoji usage to ensure consistent rendering accross different devices (:issue:`4106`, :issue:`4105`, :issue:`4127`) -- Whitelist and blacklist are now called allowlist and blocklist. Old names have been left as aliases (:issue:`4138`) - -.. _important-340-2: - -Admin -***** - -- ``[p]announce`` will now only send announcements to guilds that have explicitly configured text channel to send announcements to using ``[p]announceset channel`` command (:issue:`4088`, :issue:`4089`) - -Downloader -********** - -- ``[p]cog info`` command now shows end user data statement made by the cog creator (:issue:`4169`) -- ``[p]cog update`` command will now notify the user if cog's end user data statement has changed since last update (:issue:`4169`) - -.. _important-340-1: - -Mod -*** - -- ``[p]tempban`` now respects default days setting (``[p]modset defaultdays``) (:issue:`3993`) -- Users can now set mention spam triggers which will warn or kick the user. See ``[p]modset mentionspam`` for more information (:issue:`3786`, :issue:`4038`) -- ``[p]mute voice`` and ``[p]unmute voice`` now take action instantly if bot has Move Members permission (:issue:`4064`) -- Added typing to ``[p](un)mute guild`` to indicate that mute is being processed (:issue:`4066`, :issue:`4172`) - -ModLog -****** - -- Added timestamp to text version of ``[p]casesfor`` and ``[p]case`` commands (:issue:`4118`, :issue:`4137`) - -Streams -******* - -- Stream alerts will no longer make roles temporarily mentionable if bot has "Mention @everyone, @here, and All Roles" permission in the channel (:issue:`4182`) -- Mixer service has been closed and for that reason we've removed support for it from the cog (:issue:`4072`) -- Hitbox commands have been renamed to smashcast (:issue:`4161`) -- Improve error messages for invalid channel names/IDs (:issue:`4147`, :issue:`4148`) - -Trivia Lists -************ - -- Added ``whosthatpokemon2`` trivia containing Pokémons from 2nd generation (:issue:`4102`) -- Added ``whosthatpokemon3`` trivia containing Pokémons from 3rd generation (:issue:`4141`) - -.. _important-340-3: - -Developer changelog -------------------- - -| **Important:** -| 1. Red now offers cog disabling API, which should be respected by 3rd-party cogs in guild-related actions happening outside of command's context. See the `Core Bot changelog below ` for more information. -| 2. Red now provides data request API, which should be supported by all 3rd-party cogs. See the changelog entries in the `Core Bot changelog below ` for more information. - -Breaking changes -**************** - -- By default, none of the ``.send()`` methods mention roles or ``@everyone/@here`` (:issue:`3845`) - - - see `discord.AllowedMentions` and ``allowed_mentions`` kwarg of ``.send()`` methods, if your cog requires to mention roles or ``@everyone/@here`` - -- `Context.maybe_send_embed()` now supresses all mentions, including user mentions (:issue:`4192`) -- The default value of the ``filter`` keyword argument has been changed to ``None`` (:issue:`3845`) -- Cog package names (i.e. name of the folder the cog is in and the name used when loading the cog) now have to be `valid Python identifiers `__ (:issue:`3605`, :issue:`3679`) -- Method/attribute names starting with ``red_`` or being in the form of ``__red_*__`` are now reserved. See `version_guarantees` for more information (:issue:`4085`) -- `humanize_list()` no longer raises `IndexError` for empty sequences (:issue:`2982`) -- Removed things past deprecation time: (:issue:`4163`) - - - ``redbot.core.commands.APIToken`` - - ``loop`` kwarg from `bounded_gather_iter()`, `bounded_gather()`, and `start_adding_reactions()` - -.. _important-dev-340-1: - -Core Bot -******** - -- Added cog disabling API (:issue:`4043`, :issue:`3945`) - - - New methods added: `bot.cog_disabled_in_guild() `, `bot.cog_disabled_in_guild_raw() ` - - Cog disabling is automatically applied for commands and only needs to be done manually for things like event listeners; see `recommendations-for-cog-creators` for more information - -- Added data request API (:issue:`4045`, :issue:`4169`) - - - New special methods added to `redbot.core.commands.Cog`: `red_get_data_for_user()` (documented provisionally), `red_delete_data_for_user()` - - New special module level variable added: ``__red_end_user_data_statement__`` - - These methods and variables should be added by all cogs according to their documentation; see `recommendations-for-cog-creators` for more information - - New ``info.json`` key added: ``end_user_data_statement``; see `Info.json format documentation ` for more information - -- Added `bot.message_eligible_as_command() ` utility method which can be used to determine if a message may be responded to as a command (:issue:`4077`) -- Added a provisional API for replacing the help formatter. See `documentation ` for more details (:issue:`4011`) -- `bot.ignored_channel_or_guild() ` now accepts `discord.Message` objects (:issue:`4077`) -- `commands.NoParseOptional ` is no longer provisional and is now fully supported part of API (:issue:`4142`) -- Red no longer fails to run subcommands of a command group allowed or denied by permission hook (:issue:`3956`) -- Autohelp in group commands is now sent *after* invoking the group, which allows before invoke hooks to prevent autohelp from getting triggered (:issue:`4129`) -- RPC functionality no longer makes Red hang for a minute on shutdown (:issue:`4134`, :issue:`4143`) - -Vendored packages -***************** - -- Updated ``discord.ext.menus`` vendor (:issue:`4167`) - -Utility Functions -***************** - -- `humanize_list()` now accepts ``locale`` and ``style`` keyword arguments. See its documentation for more information (:issue:`2982`) -- `humanize_list()` is now properly localized (:issue:`2906`, :issue:`2982`) -- `humanize_list()` now accepts empty sequences (:issue:`2982`) - - -Documentation changes ---------------------- - -- Removed install instructions for Debian Stretch (:issue:`4099`) -- Added admin user guide (:issue:`3081`) -- Added alias user guide (:issue:`3084`) -- Added bank user guide (:issue:`4149`) - - -Miscellaneous -------------- - -- Updated features list in ``[p]serverinfo`` with the latest changes from Discord (:issue:`4116`) -- Simple version of ``[p]serverinfo`` now shows info about more detailed ``[p]serverinfo 1`` (:issue:`4121`) -- ``[p]set nickname``, ``[p]set serverprefix``, ``[p]streamalert``, and ``[p]streamset`` commands now can be run by users with permissions related to the actions they're making (:issue:`4109`) -- ``bordered()`` now uses ``+`` for corners if keyword argument ``ascii_border`` is set to `True` (:issue:`4097`) -- Fixed timestamp storage in few places in Red (:issue:`4017`) diff --git a/docs/conf.py b/docs/conf.py index a99240fae57..2f4e23587ae 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -111,6 +111,9 @@ # Adds d.py version to available substitutions in all files rst_prolog += f"\n.. |DPY_VERSION| replace:: {dpy_version}" +# Add release highlight indicator to available substitutions in all files +rst_prolog += f"\n.. |cool| replace:: \N{HEAVY BLACK HEART}\N{VARIATION SELECTOR-16}" + # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for diff --git a/docs/index.rst b/docs/index.rst index 8e23f37997e..4b66fa180ba 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -79,20 +79,11 @@ Welcome to Red - Discord Bot's documentation! framework_utils version_guarantees -.. toctree:: - :maxdepth: 2 - :caption: Changelogs: - - changelog_3_4_0 - changelog_3_3_0 - release_notes_3_2_0 - changelog_3_2_0 - changelog_3_1_0 - .. toctree:: :maxdepth: 2 :caption: Others + changelog host-list diff --git a/docs/release_notes_3_2_0.rst b/docs/release_notes_3_2_0.rst deleted file mode 100644 index 75eb9d63ed6..00000000000 --- a/docs/release_notes_3_2_0.rst +++ /dev/null @@ -1,50 +0,0 @@ -.. v3.2.0 Release Notes - -################################## -Red DiscordBot 3.2.0 Release Notes -################################## - - -Please read the following prior to updating. - -- 3.2 comes with improvements which required breaking changes for 3rd party cogs. - When you update to 3.2, your cogs may not be compatible if the author has not handled - the changes yet. - - -- 3.2 requires Python 3.8.1. - This was done so that we could better handle some behavior which was not fixed for Python 3.7. - If you need help updating, our install docs will cover everything you need to know to update. - - .. note:: - - You may get a notification from the downloader cog about needing to refetch dependencies - This is expected, and it will walk you through everything and do as much as it can for you. - - -- 3.2 dropped support for the MongoDB driver - - - If you were not using the MongoDB driver, this does not effect you. - - If you were using a 3rd party cog which required MongoDB, it probably still does. - - If you were using the MongoDB driver, prior to launching your instance, - you will need to run the following commands to convert - - .. code:: - - python -m pip install dnspython~=1.16.0 motor~=2.0.0 pymongo~=3.8.0 - redbot-setup convert [instancename] json - - -- 3.2 comes with many feature upgrades. A brief high level list of these is below. - - - A metric ton of bugfixes - - Bot shutdown is handled significantly better - - Audio is much more powerful - - We've made it easier for cog creators to interact with the core bot APIs safely - - We've supplied cog creators with additional tools - - -.. note:: - - The full list of changes is much longer than we can include here, - but our changelog has the fine details. From c390b89bd280d5e4f811b0f5e1946d0d4c375023 Mon Sep 17 00:00:00 2001 From: keqking <80868588+keqking@users.noreply.github.com> Date: Tue, 24 Jan 2023 01:19:05 +0530 Subject: [PATCH 34/43] [Core] fix error in `[p]ignore list` (#5973) --- redbot/core/core_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redbot/core/core_commands.py b/redbot/core/core_commands.py index 26ec04e755f..af7bff75853 100644 --- a/redbot/core/core_commands.py +++ b/redbot/core/core_commands.py @@ -5357,7 +5357,7 @@ async def count_ignored(self, ctx: commands.Context): category_channels.append(channel.category) if await self.bot._ignored_cache.get_ignored_channel(channel, check_category=False): channels.append(channel) - for channel in ctx.guild.forum_channels: + for channel in ctx.guild.forums: if channel.category and channel.category not in category_channels: if await self.bot._ignored_cache.get_ignored_channel(channel.category): category_channels.append(channel.category) From d0f22a777383b9b3d26d18b06a458cae7b06ba49 Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Tue, 31 Jan 2023 17:02:49 +0100 Subject: [PATCH 35/43] Fallback to asyncio's default exception handler when possible (#5813) Co-authored-by: Jakub Kuczys <6032823+jack1142@users.noreply.github.com> --- redbot/__main__.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/redbot/__main__.py b/redbot/__main__.py index ddd6eef85c6..87a2733070b 100644 --- a/redbot/__main__.py +++ b/redbot/__main__.py @@ -450,14 +450,7 @@ def global_exception_handler(red, loop, context): # These will get handled later when it *also* kills loop.run_forever if exc is not None and isinstance(exc, (KeyboardInterrupt, SystemExit)): return - # Maybe in the future we should handle some of the other things - # that the default exception handler handles, but this should work fine for now. - log.critical( - "Caught unhandled exception in %s:\n%s", - context.get("future", "event loop"), - context["message"], - exc_info=exc, - ) + loop.default_exception_handler(context) def red_exception_handler(red, red_task: asyncio.Future): From e0c335eda25238bb62d6bf20ccfb86a9c18121d4 Mon Sep 17 00:00:00 2001 From: Leet <36166244+leetfin@users.noreply.github.com> Date: Fri, 3 Feb 2023 13:52:43 -0500 Subject: [PATCH 36/43] [Trivia] Handle FileNotFoundError when adding a custom trivia list (#5950) --- redbot/cogs/trivia/trivia.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/redbot/cogs/trivia/trivia.py b/redbot/cogs/trivia/trivia.py index ca2c8c47d0a..3364b6186d8 100644 --- a/redbot/cogs/trivia/trivia.py +++ b/redbot/cogs/trivia/trivia.py @@ -690,8 +690,18 @@ async def _save_trivia_list( TRIVIA_LIST_SCHEMA.validate(trivia_dict) buffer.seek(0) - with file.open("wb") as fp: - fp.write(buffer.read()) + try: + with file.open("wb") as fp: + fp.write(buffer.read()) + except FileNotFoundError as e: + await ctx.send( + _( + "There was an error saving the file.\n" + "Please check the filename and try again, as it could be longer than your system supports." + ) + ) + return + await ctx.send(_("Saved Trivia list as {filename}.").format(filename=filename)) def _get_trivia_session( From 7e7d5322b714e6242c86753557b10dd901da4f4e Mon Sep 17 00:00:00 2001 From: aikaterna <20862007+aikaterna@users.noreply.github.com> Date: Sun, 12 Feb 2023 13:07:46 -0800 Subject: [PATCH 37/43] [Audio] Use more of the newer Managed/Unmanaged terminology (#5952) Co-authored-by: Jakub Kuczys --- docs/cog_guides/audio.rst | 35 ++++++++++++++---------- redbot/cogs/audio/core/commands/llset.py | 32 +++++++++++----------- redbot/cogs/audio/core/events/dpy.py | 2 +- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/docs/cog_guides/audio.rst b/docs/cog_guides/audio.rst index 242ff75a7ca..0fd228cc47b 100644 --- a/docs/cog_guides/audio.rst +++ b/docs/cog_guides/audio.rst @@ -376,7 +376,7 @@ necessary modifications. Firstly, stop all Red bots. For each bot using Audio: 1. Start the bot. -2. Run the command ``[p]llset external``. +2. Run the command ``[p]llset unmanaged``. 3. Stop the bot. Next, open a command prompt/terminal window. Navigate to ``/cogs/Audio`` for any of your bot @@ -806,7 +806,7 @@ Do not use quotes in these commands. For example, ``[p]llset host 192.168.10.101 .. code-block:: none - [p]llset external + [p]llset unmanaged [p]llset host "yourlavalinkip" [p]llset port "port" [p]llset password "password" @@ -3252,7 +3252,7 @@ llset **Description** Manage Lavalink node configuration settings. This command holds all commands to -manage an unmanaged (external) or managed Lavalink node. +manage an unmanaged (user-managed) or managed (bot-managed) Lavalink node. .. warning:: @@ -3261,21 +3261,28 @@ manage an unmanaged (external) or managed Lavalink node. server to do so. Changing llset command settings have the potential to break Audio cog connection and playback if the wrong settings are used. -"""""""""""""" -llset external -"""""""""""""" +""""""""""""""" +llset unmanaged +""""""""""""""" **Syntax** .. code-block:: none - [p]llset external + [p]llset unmanaged + +or + +.. code-block:: none + + [p]llsetup unmanaged **Description** -Toggle using external Lavalink nodes - requires an existing external Lavalink node for -Audio to work, if enabled. This command disables the managed Lavalink server: if you do -not have an external Lavalink node you will be unable to use Audio while this is enabled. +Toggle using unmanaged (user-managed) Lavalink nodes - requires an existing Lavalink +node for Audio to work, if enabled. This command disables the managed (bot-managed) +Lavalink server: if you do not have an unmanaged Lavalink node set up, you will be +unable to use Audio while this is enabled. """""""""" llset info @@ -3691,7 +3698,7 @@ llset host **Description** Set the Lavalink node host. This command sets the connection host which -Audio will use to connect to an external Lavalink node. +Audio will use to connect to an unmanaged Lavalink node. **Arguments** @@ -3712,7 +3719,7 @@ llset password **Description** Set the Lavalink node password. This command sets the connection password which -Audio will use to connect to an external Lavalink node. +Audio will use to connect to an unmanaged Lavalink node. **Arguments** @@ -3733,7 +3740,7 @@ llset port **Description** Set the Lavalink node port. This command sets the connection port which -Audio will use to connect to an external Lavalink node. +Audio will use to connect to an unmanaged Lavalink node. **Arguments** @@ -3754,4 +3761,4 @@ llset secured **Description** Set the Lavalink node connection to secured. This toggle sets the connection type -to secured or unsecured when connecting to an external Lavalink node. +to secured or unsecured when connecting to an unmanaged Lavalink node. diff --git a/redbot/cogs/audio/core/commands/llset.py b/redbot/cogs/audio/core/commands/llset.py index 4e427dec68f..312f0e7549f 100644 --- a/redbot/cogs/audio/core/commands/llset.py +++ b/redbot/cogs/audio/core/commands/llset.py @@ -36,7 +36,7 @@ class LavalinkSetupCommands(MixinMeta, metaclass=CompositeMetaClass): async def command_llset(self, ctx: commands.Context): """`Dangerous commands` Manage Lavalink node configuration settings. - This command block holds all commands to manage an unmanaged (external) or managed Lavalink node. + This command block holds all commands to configure an unmanaged (user maintained) or managed (bot maintained) Lavalink node. You should not mess with any command in here unless you have a valid reason to, i.e. been told by someone in the Red-Discord Bot support server to do so. @@ -152,11 +152,11 @@ async def validate_input(cog, arg): ), ) - @command_llset.command(name="external", aliases=["unmanaged"]) - async def command_llset_external(self, ctx: commands.Context): - """Toggle using external Lavalink nodes - requires an existing external Lavalink node for Audio to work, if enabled. + @command_llset.command(name="unmanaged", aliases=["external"]) + async def command_llset_unmanaged(self, ctx: commands.Context): + """Toggle using external (unmanaged) Lavalink nodes - requires an existing Lavalink node for Audio to work, if enabled. - This command disables the managed Lavalink server, if you do not have an external Lavalink node you will be unable to use Audio while this is enabled. + This command disables the managed Lavalink server. If you do not have another Lavalink node set up, you will be unable to use Audio while this is enabled. """ external = await self.config.use_external_lavalink() await self.config.use_external_lavalink.set(not external) @@ -164,7 +164,7 @@ async def command_llset_external(self, ctx: commands.Context): if external: embed = discord.Embed( title=_("Setting Changed"), - description=_("External Lavalink server: {true_or_false}.").format( + description=_("Unmanaged Lavalink server: {true_or_false}.").format( true_or_false=inline(_("Enabled") if not external else _("Disabled")) ), ) @@ -173,7 +173,7 @@ async def command_llset_external(self, ctx: commands.Context): await self.send_embed_msg( ctx, title=_("Setting Changed"), - description=_("External Lavalink server: {true_or_false}.").format( + description=_("Unmanaged Lavalink server: {true_or_false}.").format( true_or_false=inline(_("Enabled") if not external else _("Disabled")) ), ) @@ -196,14 +196,14 @@ async def command_llset_host( ): """Set the Lavalink node host. - This command sets the connection host which Audio will use to connect to an external Lavalink node. + This command sets the connection host which Audio will use to connect to an unmanaged Lavalink node. """ await self.config.host.set(host) await self.send_embed_msg( ctx, title=_("Setting Changed"), description=_( - "External Lavalink node host set to {host}. " + "Unmanaged Lavalink node host set to {host}. " "Run `{p}{cmd}` for it to take effect." ).format( host=inline(host), p=ctx.prefix, cmd=self.command_audioset_restart.qualified_name @@ -217,7 +217,7 @@ async def command_llset_password( ): """Set the Lavalink node password. - This command sets the connection password which Audio will use to connect to an external Lavalink node. + This command sets the connection password which Audio will use to connect to an unmanaged Lavalink node. """ await self.config.password.set(str(password)) @@ -225,7 +225,7 @@ async def command_llset_password( ctx, title=_("Setting Changed"), description=_( - "External Lavalink node password set to {password}. " + "Unmanaged Lavalink node password set to {password}. " "Run `{p}{cmd}` for it to take effect." ).format( password=inline(password), @@ -241,7 +241,7 @@ async def command_llset_wsport( ): """Set the Lavalink node port. - This command sets the connection port which Audio will use to connect to an external Lavalink node. + This command sets the connection port which Audio will use to connect to an unmanaged Lavalink node. """ if port < 0 or port > 65535: return await self.send_embed_msg( @@ -254,7 +254,7 @@ async def command_llset_wsport( ctx, title=_("Setting Changed"), description=_( - "External Lavalink node port set to {port}. " + "Unmanaged Lavalink node port set to {port}. " "Run `{p}{cmd}` for it to take effect." ).format( port=inline(str(port)), @@ -268,7 +268,7 @@ async def command_llset_wsport( async def command_llset_secured(self, ctx: commands.Context): """Set the Lavalink node connection to secured. - This toggle sets the connection type to secured or unsecured when connecting to an external Lavalink node. + This toggle sets the connection type to secured or unsecured when connecting to an unmanaged Lavalink node. """ state = await self.config.secured_ws() await self.config.secured_ws.set(not state) @@ -278,7 +278,7 @@ async def command_llset_secured(self, ctx: commands.Context): ctx, title=_("Setting Changed"), description=_( - "External Lavalink node will now connect using the secured {secured_protocol} protocol.\n\n" + "Unmanaged Lavalink node will now connect using the secured {secured_protocol} protocol.\n\n" "Run `{p}{cmd}` for it to take effect." ).format( p=ctx.prefix, @@ -291,7 +291,7 @@ async def command_llset_secured(self, ctx: commands.Context): ctx, title=_("Setting Changed"), description=_( - "External Lavalink node will no longer connect using the secured " + "Unmanaged Lavalink node will no longer connect using the secured " "{secured_protocol} protocol and wil use {unsecured_protocol} instead .\n\n" "Run `{p}{cmd}` for it to take effect." ).format(p=ctx.prefix, cmd=self.command_audioset_restart.qualified_name), diff --git a/redbot/cogs/audio/core/events/dpy.py b/redbot/cogs/audio/core/events/dpy.py index 0f97d65bb7d..6c8934d9016 100644 --- a/redbot/cogs/audio/core/events/dpy.py +++ b/redbot/cogs/audio/core/events/dpy.py @@ -86,7 +86,7 @@ "usually you will never have to change this, " "before considering changing it please consult our support team." ), - "command_llset_external": _( + "command_llset_unmanaged": _( "This command will disable the managed Lavalink node, " "if you toggle this command you must specify an external Lavalink node to connect to, " "if you do not do so Audio will stop working." From 6c32ff58e4247c15e5f54032d8cddb7b49b57b0a Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Sun, 12 Feb 2023 23:34:00 +0100 Subject: [PATCH 38/43] Revamp of automatically applied PR labels (#5954) Co-authored-by: Jakub Kuczys <6032823+jack1142@users.noreply.github.com> --- .github/labeler.yml | 345 ++++++++++++------ .github/workflows/auto_labeler_issues.yml | 3 +- .github/workflows/auto_labeler_pr.yml | 15 +- .../check_label_pattern_exhaustiveness.yaml | 23 ++ .../check_label_pattern_exhaustiveness.py | 215 +++++++++++ 5 files changed, 490 insertions(+), 111 deletions(-) create mode 100644 .github/workflows/check_label_pattern_exhaustiveness.yaml create mode 100644 .github/workflows/scripts/check_label_pattern_exhaustiveness.py diff --git a/.github/labeler.yml b/.github/labeler.yml index a27da3e815b..caceb8ae472 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,188 +1,319 @@ -"Category: Admin": +"Category: CI": + - .github/workflows/**/* + + +"Category: Cogs - Admin": # Source - redbot/cogs/admin/* # Docs - docs/cog_guides/admin.rst -"Category: Alias": + - docs/.resources/admin/**/* +"Category: Cogs - Alias": # Source - redbot/cogs/alias/* # Docs - docs/cog_guides/alias.rst -"Category: Audio Cog": + # Tests + - redbot/pytest/alias.py + - tests/cogs/test_alias.py + - docs/.resources/alias/**/* +"Category: Cogs - Audio": + # Source - any: - # Source - redbot/cogs/audio/**/* - # Docs - - docs/cog_guides/audio.rst - all: - "!redbot/cogs/audio/**/locales/*" -"Category: Bank API": - # Source - - redbot/core/bank.py - # Docs - - docs/framework_bank.rst -"Category: Bot Core": - # Source - - redbot/* - - redbot/core/__init__.py - - redbot/core/_debuginfo.py - - redbot/core/_diagnoser.py - - redbot/core/_sharedlibdeprecation.py - - redbot/core/bot.py - - redbot/core/checks.py - - redbot/core/cli.py - - redbot/core/cog_manager.py - - redbot/core/core_commands.py - - redbot/core/data_manager.py - - redbot/core/errors.py - - redbot/core/events.py - - redbot/core/global_checks.py - - redbot/core/settings_caches.py # Docs - - docs/framework_apikeys.rst - - docs/framework_bot.rst - - docs/framework_cogmanager.rst - - docs/framework_datamanager.rst - - docs/framework_events.rst - - docs/cog_guides/cog_manager_ui.rst - - docs/cog_guides/core.rst -"Category: CI": - - .github/workflows/* -"Category: Cleanup Cog": + - docs/cog_guides/audio.rst +"Category: Cogs - Bank": [] # historical label for a removed cog +"Category: Cogs - Cleanup": # Source - redbot/cogs/cleanup/* # Docs - docs/cog_guides/cleanup.rst -"Category: Command Module": - # Source - - any: - # Source - - redbot/core/commands/* - # Docs - - docs/framework_checks.rst - - docs/framework_commands.rst - all: - - "!redbot/core/commands/help.py" -"Category: Config": - # Source - - redbot/core/drivers/* - - redbot/core/config.py - # Docs - - docs/framework_config.rst -"Category: CustomCom": +"Category: Cogs - CustomCommands": # Source - redbot/cogs/customcom/* # Docs - docs/cog_customcom.rst - docs/cog_guides/customcommands.rst -"Category: Dev Cog": +"Category: Cogs - Dev": # Source - redbot/core/dev_commands.py # Docs - docs/cog_guides/dev.rst -"Category: Docs": - - docs/**/* -"Category: Downloader": +"Category: Cogs - Downloader": # Source - redbot/cogs/downloader/* # Docs - docs/cog_guides/downloader.rst -"Category: Economy Cog": + # Tests + - redbot/pytest/downloader.py + - redbot/pytest/downloader_testrepo.* + - tests/cogs/downloader/**/* +"Category: Cogs - Economy": # Source - redbot/cogs/economy/* # Docs - docs/cog_guides/economy.rst -"Category: Filter": + # Tests + - redbot/pytest/economy.py + - tests/cogs/test_economy.py +"Category: Cogs - Filter": # Source - redbot/cogs/filter/* # Docs - docs/cog_guides/filter.rst -"Category: General Cog": +"Category: Cogs - General": # Source - redbot/cogs/general/* # Docs - docs/cog_guides/general.rst -"Category: Help": - - redbot/core/commands/help.py -"Category: i18n": - # Source - - redbot/core/i18n.py - # Locale files - - redbot/**/locales/* - # Docs - - docs/framework_i18n.rst -"Category: Image": +"Category: Cogs - Image": # Source - redbot/cogs/image/* # Docs - docs/cog_guides/image.rst -"Category: Meta": - - ./* - - .github/* - - .github/ISSUE_TEMPLATE/* - - .github/PULL_REQUEST_TEMPLATE/* - - schema/* - - tools/* -"Category: Mod Cog": +"Category: Cogs - Mod": # Source - redbot/cogs/mod/* # Docs - docs/cog_guides/mod.rst -"Category: Modlog API": - # Source - - redbot/core/generic_casetypes.py - - redbot/core/modlog.py - # Docs - - docs/framework_modlog.rst -"Category: Modlog Cog": + # Tests + - redbot/pytest/mod.py + - tests/cogs/test_mod.py +"Category: Cogs - Modlog": # Source - redbot/cogs/modlog/* # Docs - docs/cog_guides/modlog.rst -"Category: Mutes Cog": +"Category: Cogs - Mutes": # Source - redbot/cogs/mutes/* # Docs - docs/cog_guides/mutes.rst -"Category: Permissions": +"Category: Cogs - Permissions": # Source - redbot/cogs/permissions/* # Docs - docs/cog_guides/permissions.rst - docs/cog_permissions.rst -"Category: Reports Cog": + # Tests + - redbot/pytest/permissions.py + - tests/cogs/test_permissions.py +"Category: Cogs - Reports": # Source - redbot/cogs/reports/* # Docs - docs/cog_guides/reports.rst -"Category: RPC/ZMQ API": - # Source - - redbot/core/rpc.py - # Docs - - docs/framework_rpc.rst -"Category: Streams": +"Category: Cogs - Streams": # Source - redbot/cogs/streams/* # Docs - docs/cog_guides/streams.rst -"Category: Tests": - - redbot/pytest/* - - tests/**/* -"Category: Trivia Cog": +"Category: Cogs - Trivia": # Source - redbot/cogs/trivia/* # Docs - docs/cog_guides/trivia.rst - docs/guide_trivia_list_creation.rst -"Category: Trivia Lists": + - docs/.resources/trivia/**/* + # Tests + - tests/cogs/test_trivia.py +"Category: Cogs - Trivia - Lists": - redbot/cogs/trivia/data/lists/* -"Category: Utility Functions": +"Category: Cogs - Warnings": # Source - - redbot/core/utils/* + - redbot/cogs/warnings/* + # Docs + - docs/cog_guides/warnings.rst + + +"Category: Core - API - Audio": [] # potential future feature +"Category: Core - API - Bank": + # Source + - redbot/core/bank.py + # Docs + - docs/framework_bank.rst +"Category: Core - API - Commands Package": + # Source + - any: + - redbot/core/commands/* + - "!redbot/core/commands/help.py" + # this isn't in commands package but it just re-exports things from it + - redbot/core/checks.py + # Docs + - docs/framework_checks.rst + - docs/framework_commands.rst + # Tests + - tests/core/test_commands.py +"Category: Core - API - Config": + # Source + - any: + - redbot/core/drivers/**/* + - "!redbot/core/drivers/**/locales/*" + - redbot/core/config.py + # Docs + - docs/framework_config.rst + # Tests + - tests/core/test_config.py +"Category: Core - API - Other": + # Source + - redbot/__init__.py + - redbot/core/__init__.py + - redbot/core/cog_manager.py # TODO: privatize cog manager module + - redbot/core/data_manager.py + - redbot/core/errors.py + # Docs + - docs/framework_cogmanager.rst # TODO: privatize cog manager module + - docs/framework_datamanager.rst + # Tests + - redbot/pytest/cog_manager.py # TODO: privatize cog manager module + - redbot/pytest/data_manager.py + - tests/core/test_cog_manager.py + - tests/core/test_data_manager.py + - tests/core/test_version.py +"Category: Core - API - Utils Package": + # Source + - any: + - redbot/core/utils/* + - "!redbot/core/utils/_internal_utils.py" # Docs - docs/framework_utils.rst -"Category: Warnings": + # Tests + - tests/core/test_utils.py +"Category: Core - Bot Class": # Source - - redbot/cogs/warnings/* + - redbot/core/bot.py # Docs - - docs/cog_guides/warnings.rst + - docs/framework_apikeys.rst + - docs/framework_bot.rst +"Category: Core - Bot Commands": + # Source + - redbot/core/core_commands.py + - redbot/core/_diagnoser.py + # Docs + - docs/.resources/cog_manager_ui/**/* + - docs/cog_guides/cog_manager_ui.rst + - docs/cog_guides/core.rst +"Category: Core - Command-line Interfaces": + - redbot/__main__.py + - redbot/launcher.py + - redbot/logging.py + - redbot/core/_debuginfo.py + - redbot/core/cli.py + - redbot/setup.py +"Category: Core - Help": + - redbot/core/commands/help.py +"Category: Core - i18n": + # Source + - redbot/core/i18n.py + # Locale files + - redbot/**/locales/* + # Docs + - docs/framework_i18n.rst +"Category: Core - Modlog": + # Source + - redbot/core/generic_casetypes.py + - redbot/core/modlog.py + # Docs + - docs/framework_modlog.rst +"Category: Core - Other Internals": + # Source + - redbot/core/_sharedlibdeprecation.py + - redbot/core/events.py + - redbot/core/global_checks.py + - redbot/core/settings_caches.py + - redbot/core/utils/_internal_utils.py + # Tests + - redbot/pytest/__init__.py + - redbot/pytest/core.py + - tests/core/test_installation.py +"Category: Core - RPC/ZMQ": + # Source + - redbot/core/rpc.py + # Docs + - docs/framework_rpc.rst + # Tests + - redbot/pytest/rpc.py + - tests/core/test_rpc.py + - tests/rpc_test.html + + +"Category: Docker": [] # potential future feature + + +"Category: Docs - Changelogs": + - docs/changelog_*.rst + - docs/release_notes_*.rst +"Category: Docs - For Developers": + - docs/framework_events.rst + - docs/guide_cog_creation.rst + - docs/guide_cog_creators.rst + - docs/guide_migration.rst + - docs/guide_publish_cogs.rst +"Category: Docs - Install Guides": + - docs/about_venv.rst + - docs/autostart_*.rst + - docs/.resources/bot-guide/**/* + - docs/bot_application_guide.rst + - docs/install_guides/**/* + - docs/update_red.rst +"Category: Docs - Other": + - docs/host-list.rst + - docs/index.rst + - docs/version_guarantees.rst + - README.md +"Category: Docs - User Guides": + - docs/getting_started.rst + - docs/intents.rst + - docs/red_core_data_statement.rst + # TODO: move these to `docs/.resources/getting_started` subfolder + - docs/.resources/red-console.png + - docs/.resources/code-grant.png + - docs/.resources/instances-ssh-button.png + - docs/.resources/ssh-output.png + + +"Category: Meta": + # top-level files + - any: + - '*' + - '!README.md' + # .gitattributes files + - '**/.gitattributes' + # GitHub configuration files, with the exception of CI configuration + - .github/* + - .github/ISSUE_TEMPLATE/* + - .github/PULL_REQUEST_TEMPLATE/* + # documentation configuration, extensions, scripts, templates, etc. + - docs/conf.py + - docs/_ext/**/* + - docs/_html/**/* + - docs/make.bat + - docs/Makefile + - docs/prolog.txt + - docs/_templates/**/* + # empty file + - redbot/cogs/__init__.py + # can't go more meta than that :) + # TODO: remove this useless file + - redbot/meta.py + # py.typed file + - redbot/py.typed + # requirements files + - requirements/* + # schema files + - schema/* + # tests configuration, global fixtures, etc. + - tests/conftest.py + - tests/__init__.py + - tests/*/__init__.py + # repository tools + - tools/* + + +# "Category: RPC/ZMQ methods": [] # can't be matched by file patterns + + +"Category: Vendored Packages": + - redbot/vendored/**/* diff --git a/.github/workflows/auto_labeler_issues.yml b/.github/workflows/auto_labeler_issues.yml index 6d4ce5a27b9..2dc30274d7f 100644 --- a/.github/workflows/auto_labeler_issues.yml +++ b/.github/workflows/auto_labeler_issues.yml @@ -7,8 +7,7 @@ permissions: issues: write jobs: - build: - + apply_triage_label_to_issues: runs-on: ubuntu-latest steps: - name: Apply Triage Label diff --git a/.github/workflows/auto_labeler_pr.yml b/.github/workflows/auto_labeler_pr.yml index 759c27b5430..8939e91f386 100644 --- a/.github/workflows/auto_labeler_pr.yml +++ b/.github/workflows/auto_labeler_pr.yml @@ -1,16 +1,27 @@ name: Auto Labeler - PRs on: pull_request_target: + types: + - opened + - synchronize + - reopened + - labeled + - unlabeled permissions: pull-requests: write jobs: - build: + label_pull_requests: runs-on: ubuntu-latest steps: - name: Apply Type Label uses: actions/labeler@v4 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" - sync-labels: "" # this is a temporary workaround, see #4844 + sync-labels: true + + - name: Label documentation-only changes. + uses: Jackenmen/label-doconly-changes@v1 + env: + LDC_LABELS: Docs-only diff --git a/.github/workflows/check_label_pattern_exhaustiveness.yaml b/.github/workflows/check_label_pattern_exhaustiveness.yaml new file mode 100644 index 00000000000..8cef9dad4c8 --- /dev/null +++ b/.github/workflows/check_label_pattern_exhaustiveness.yaml @@ -0,0 +1,23 @@ +name: Check label pattern exhaustiveness +on: + pull_request: + push: + +jobs: + check_label_pattern_exhaustiveness: + name: Check label pattern exhaustiveness + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.8" + - name: Install script's pre-requirements + run: | + python -m pip install -U pip + python -m pip install -U pathspec pyyaml rich + - name: Check label pattern exhaustiveness + run: | + python .github/workflows/scripts/check_label_pattern_exhaustiveness.py diff --git a/.github/workflows/scripts/check_label_pattern_exhaustiveness.py b/.github/workflows/scripts/check_label_pattern_exhaustiveness.py new file mode 100644 index 00000000000..1982b0b6936 --- /dev/null +++ b/.github/workflows/scripts/check_label_pattern_exhaustiveness.py @@ -0,0 +1,215 @@ +import itertools +import operator +import os +import subprocess +from pathlib import Path +from typing import Any, Dict, Iterable, List, Optional +from typing_extensions import Self + +import rich +import yaml +from rich.console import Console, ConsoleOptions, RenderResult +from rich.tree import Tree +from pathspec import PathSpec +from pathspec.patterns.gitwildmatch import GitWildMatchPattern + + +ROOT_PATH = Path(__file__).resolve().parents[3] + + +class Matcher: + def __init__(self, *, any: Iterable[str] = (), all: Iterable[str] = ()) -> None: + self.any_patterns = tuple(any) + self.any_specs = self._get_pathspecs(self.any_patterns) + self.all_patterns = tuple(all) + self.all_specs = self._get_pathspecs(self.all_patterns) + + def __repr__(self) -> str: + return f"Matcher(any={self.any_patterns!r}, all={self.all_patterns!r})" + + def __eq__(self, other: Any) -> bool: + if isinstance(other, self.__class__): + return ( + self.any_patterns == other.any_patterns and self.all_patterns == other.all_patterns + ) + return NotImplemented + + def __hash__(self) -> int: + return hash((self.any_patterns, self.all_patterns)) + + @classmethod + def _get_pathspecs(cls, patterns: Iterable[str]) -> List[PathSpec]: + return tuple( + PathSpec.from_lines(GitWildMatchPattern, cls._get_pattern_lines(pattern)) + for pattern in patterns + ) + + @staticmethod + def _get_pattern_lines(pattern: str) -> List[str]: + # an approximation of actions/labeler's minimatch globs + if pattern.startswith("!"): + pattern_lines = ["*", f"!/{pattern[1:]}"] + else: + pattern_lines = [f"/{pattern}"] + if pattern.endswith("*") and "**" not in pattern: + pattern_lines.append(f"!/{pattern}/") + return pattern_lines + + @classmethod + def get_label_matchers(cls) -> Dict[str, List[Self]]: + with open(ROOT_PATH / ".github/labeler.yml", encoding="utf-8") as fp: + label_definitions = yaml.safe_load(fp) + label_matchers: Dict[str, List[Matcher]] = {} + for label_name, matcher_definitions in label_definitions.items(): + matchers = label_matchers[label_name] = [] + for idx, matcher_data in enumerate(matcher_definitions): + if isinstance(matcher_data, str): + matchers.append(cls(any=[matcher_data])) + elif isinstance(matcher_data, dict): + matchers.append( + cls(any=matcher_data.pop("any", []), all=matcher_data.pop("all", [])) + ) + if matcher_data: + raise RuntimeError( + f"Unexpected keys at index {idx} for label {label_name!r}: " + + ", ".join(map(repr, matcher_data)) + ) + elif matcher_data is not None: + raise RuntimeError(f"Unexpected type at index {idx} for label {label_name!r}") + + return label_matchers + + +class PathNode: + def __init__(self, parent_tree: Tree, path: Path, *, label: Optional[str] = None) -> None: + self.parent_tree = parent_tree + self.path = path + self.label = label + + def __rich__(self) -> str: + if self.label is not None: + return self.label + return self.path.name + + +class DirectoryTree: + def __init__(self, label: str) -> None: + self.root = Tree(PathNode(Tree(""), Path(), label=label)) + self._previous = self.root + + def __bool__(self) -> bool: + return bool(self.root.children) + + def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult: + yield from self.root.__rich_console__(console, options) + + def add(self, file: Path) -> Tree: + common_path = Path(os.path.commonpath([file.parent, self._previous.label.path])) + + parent_tree = self._previous + while parent_tree != self.root and parent_tree.label.path != common_path: + parent_tree = parent_tree.label.parent_tree + + for part in file.relative_to(common_path).parts: + if parent_tree.label.path.name == "locales": + if not parent_tree.children: + parent_tree.add(PathNode(parent_tree, parent_tree.label.path / "*.po")) + continue + parent_tree = parent_tree.add(PathNode(parent_tree, parent_tree.label.path / part)) + + self._previous = parent_tree + return parent_tree + + +class App: + def __init__(self) -> None: + self.exit_code = 0 + self.label_matchers = Matcher.get_label_matchers() + self.tracked_files = [ + Path(filename) + for filename in subprocess.check_output( + ("git", "ls-tree", "-r", "HEAD", "--name-only"), encoding="utf-8", cwd=ROOT_PATH + ).splitlines() + ] + self.matches_per_label = {label_name: set() for label_name in self.label_matchers} + self.matches_per_file = [] + self.used_matchers = set() + + def run(self) -> int: + old_cwd = os.getcwd() + try: + os.chdir(ROOT_PATH) + self._run() + finally: + os.chdir(old_cwd) + return self.exit_code + + def _run(self) -> None: + self._collect_match_information() + self._show_matches_per_label() + self._show_files_without_labels() + self._show_files_with_multiple_labels() + self._show_unused_matchers() + + def _collect_match_information(self) -> None: + tmp_matches_per_file = {file: [] for file in self.tracked_files} + + for file in self.tracked_files: + for label_name, matchers in self.label_matchers.items(): + matched = False + for matcher in matchers: + if all( + path_spec.match_file(file) + for path_spec in itertools.chain(matcher.all_specs, matcher.any_specs) + ): + self.matches_per_label[label_name].add(file) + matched = True + self.used_matchers.add(matcher) + if matched: + tmp_matches_per_file[file].append(label_name) + + self.matches_per_file = sorted(tmp_matches_per_file.items(), key=operator.itemgetter(0)) + + def _show_matches_per_label(self) -> None: + for label_name, files in self.matches_per_label.items(): + top_tree = DirectoryTree(f"{label_name}:") + for file in sorted(files): + top_tree.add(file) + rich.print(top_tree) + print() + + def _show_files_without_labels(self) -> None: + top_tree = DirectoryTree("\n--- Not matched ---") + for file, labels in self.matches_per_file: + if not labels: + top_tree.add(file) + if top_tree: + self.exit_code = 1 + rich.print(top_tree) + else: + print("--- All files match at least one label's patterns ---") + + def _show_files_with_multiple_labels(self) -> None: + top_tree = DirectoryTree("\n--- Matched by more than one label ---") + for file, labels in self.matches_per_file: + if len(labels) > 1: + tree = top_tree.add(file) + for label_name in labels: + tree.add(label_name) + if top_tree: + rich.print(top_tree) + else: + print("--- None of the files are matched by more than one label's patterns ---") + + def _show_unused_matchers(self) -> None: + for label_name, matchers in self.label_matchers.items(): + for idx, matcher in enumerate(matchers): + if matcher not in self.used_matchers: + print( + f"--- Matcher {idx} for label {label_name!r} does not match any files! ---" + ) + self.exit_code = 1 + + +if __name__ == "__main__": + raise SystemExit(App().run()) From 9811e4e87143867b3c1406ca4a74918ce44d0972 Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Mon, 13 Feb 2023 00:17:51 +0100 Subject: [PATCH 39/43] Fix labeler patterns for changelogs (#5987) --- .github/labeler.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index caceb8ae472..f90d1d9b184 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -243,8 +243,8 @@ "Category: Docs - Changelogs": - - docs/changelog_*.rst - - docs/release_notes_*.rst + - CHANGES.rst + - docs/changelog.rst "Category: Docs - For Developers": - docs/framework_events.rst - docs/guide_cog_creation.rst @@ -279,6 +279,7 @@ - any: - '*' - '!README.md' + - '!CHANGES.rst' # .gitattributes files - '**/.gitattributes' # GitHub configuration files, with the exception of CI configuration From a89a27cadf56b142892a02f8fbb0e0db06c51106 Mon Sep 17 00:00:00 2001 From: Lioness100 Date: Mon, 13 Feb 2023 21:36:09 -0500 Subject: [PATCH 40/43] docs: fix typos (#5989) Co-authored-by: Flame442 <34169552+Flame442@users.noreply.github.com> --- docs/cog_guides/alias.rst | 2 +- docs/cog_guides/cog_manager_ui.rst | 4 ++-- docs/cog_guides/core.rst | 2 +- docs/framework_config.rst | 4 ++-- docs/getting_started.rst | 2 +- docs/install_guides/raspberry-pi-os-10.rst | 2 +- docs/install_guides/windows.rst | 2 +- docs/prolog.txt | 2 +- redbot/__init__.py | 2 +- redbot/cogs/alias/alias.py | 4 ++-- redbot/cogs/audio/core/utilities/playlists.py | 10 +++++----- redbot/cogs/mod/names.py | 2 +- redbot/cogs/mutes/converters.py | 2 +- redbot/cogs/mutes/mutes.py | 4 ++-- redbot/cogs/reports/reports.py | 2 +- redbot/cogs/streams/streams.py | 2 +- redbot/cogs/streams/streamtypes.py | 4 ++-- redbot/cogs/trivia/trivia.py | 6 +++--- redbot/core/_diagnoser.py | 4 ++-- redbot/core/bot.py | 2 +- redbot/core/commands/help.py | 4 ++-- redbot/core/core_commands.py | 4 ++-- redbot/core/rpc.py | 2 +- redbot/core/settings_caches.py | 4 ++-- redbot/core/utils/tunnel.py | 2 +- redbot/core/utils/views.py | 2 +- redbot/vendored/discord/ext/menus/__init__.py | 2 +- tests/core/test_utils.py | 2 +- 28 files changed, 43 insertions(+), 43 deletions(-) diff --git a/docs/cog_guides/alias.rst b/docs/cog_guides/alias.rst index 85c179c28e6..dd49db36189 100644 --- a/docs/cog_guides/alias.rst +++ b/docs/cog_guides/alias.rst @@ -68,7 +68,7 @@ be replaced by the first argument of your alias: # this alias will execute the following command: [p]ban Slime#3160 7 Spam bot. -For a more detailed explaination, read :ref:`this `. +For a more detailed explanation, read :ref:`this `. .. _alias-commands: diff --git a/docs/cog_guides/cog_manager_ui.rst b/docs/cog_guides/cog_manager_ui.rst index e51473702d6..f6f8979dbbb 100644 --- a/docs/cog_guides/cog_manager_ui.rst +++ b/docs/cog_guides/cog_manager_ui.rst @@ -13,7 +13,7 @@ find detailed docs about usage and commands. included in the cogs paths and it cannot be unloaded. It contains needed commands for cog management. -.. _cogmanaerui-usage: +.. _cogmanagerui-usage: ----- Usage @@ -243,7 +243,7 @@ Shows the install path, or sets a new one. If you want to set a new path, the same rules as for :ref:`addpath ` apply -.. warning:: If you edit the install path, the cogs won't be transfered. +.. warning:: If you edit the install path, the cogs won't be transferred. **Arguments** diff --git a/docs/cog_guides/core.rst b/docs/cog_guides/core.rst index be1b76ad875..ccc59156329 100644 --- a/docs/cog_guides/core.rst +++ b/docs/cog_guides/core.rst @@ -1274,7 +1274,7 @@ embedset command server **Description** -Sets a commmand's embed setting for the current server. +Sets a command's embed setting for the current server. If set, this is used instead of the server default to determine whether or not to use embeds. diff --git a/docs/framework_config.rst b/docs/framework_config.rst index 2827cd5827c..61b0a4b0ea8 100644 --- a/docs/framework_config.rst +++ b/docs/framework_config.rst @@ -128,7 +128,7 @@ Notice a few things in the above examples: self.config..variable_name.set(new_value) It is also possible to use :code:`async with` syntax to get and set config -values. When entering the statement, the config value is retreived, and on exit, +values. When entering the statement, the config value is retrieved, and on exit, it is saved. This puts a safeguard on any code within the :code:`async with` block such that if it breaks from the block in any way (whether it be from :code:`return`, :code:`break`, :code:`continue` or an exception), the value will @@ -262,7 +262,7 @@ Now let's see an example that uses multiple identifiers: from redbot.core import Config, commands, checks - class ChannelAccesss(commands.Cog): + class ChannelAccess(commands.Cog): def __init__(self): self.config = Config.get_conf(self, identifier=1234567890) default_access = { diff --git a/docs/getting_started.rst b/docs/getting_started.rst index f43a4f44dbc..1bc9e64eb94 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -20,7 +20,7 @@ window like this: .. image:: .resources/red-console.png -.. _gettings-started-invite: +.. _getting-started-invite: ------------------------- Invite Red to your server diff --git a/docs/install_guides/raspberry-pi-os-10.rst b/docs/install_guides/raspberry-pi-os-10.rst index 48bf582daea..654f23a0360 100644 --- a/docs/install_guides/raspberry-pi-os-10.rst +++ b/docs/install_guides/raspberry-pi-os-10.rst @@ -35,7 +35,7 @@ Installing the pre-requirements We recommend installing pyenv as a method of installing non-native versions of Python on Raspberry Pi OS. This guide will tell you how. First, run the following commands: -.. cmake is necessary to be able to successfuly build rapidfuzz. +.. cmake is necessary to be able to successfully build rapidfuzz. .. prompt:: bash diff --git a/docs/install_guides/windows.rst b/docs/install_guides/windows.rst index de3ed786629..724569828d5 100644 --- a/docs/install_guides/windows.rst +++ b/docs/install_guides/windows.rst @@ -52,7 +52,7 @@ Manually installing dependencies .. attention:: There are additional configuration steps required which are not documented for installing dependencies manually. - These dependencies are only listed seperately here for + These dependencies are only listed separately here for reference purposes. * `MSVC Build tools `_ diff --git a/docs/prolog.txt b/docs/prolog.txt index 06e5356f12a..a8f13661843 100644 --- a/docs/prolog.txt +++ b/docs/prolog.txt @@ -1,5 +1,5 @@ .. This file will be run at the beginning of all files. - You can add the subsitutions you need. + You can add the substitutions you need. .. this is a .txt so sphinx doesn't error because it's missing in the index diff --git a/redbot/__init__.py b/redbot/__init__.py index 3f243520a0c..8f496364986 100644 --- a/redbot/__init__.py +++ b/redbot/__init__.py @@ -334,7 +334,7 @@ def _update_logger_class(): def _early_init(): - # This function replaces logger so we preferrably (though not necessarily) want that to happen + # This function replaces logger so we preferably (though not necessarily) want that to happen # before importing anything that calls `logging.getLogger()`, i.e. `asyncio`. _update_logger_class() _update_event_loop_policy() diff --git a/redbot/cogs/alias/alias.py b/redbot/cogs/alias/alias.py index 5c63187416a..03e6654caa8 100644 --- a/redbot/cogs/alias/alias.py +++ b/redbot/cogs/alias/alias.py @@ -339,7 +339,7 @@ async def _edit_alias(self, ctx: commands.Context, alias_name: str, *, command): try: if await self._aliases.edit_alias(ctx, alias_name, command): await ctx.send( - _("The alias with the trigger `{name}` has been edited sucessfully.").format( + _("The alias with the trigger `{name}` has been edited successfully.").format( name=alias_name ) ) @@ -372,7 +372,7 @@ async def _edit_global_alias(self, ctx: commands.Context, alias_name: str, *, co try: if await self._aliases.edit_alias(ctx, alias_name, command, global_=True): await ctx.send( - _("The alias with the trigger `{name}` has been edited sucessfully.").format( + _("The alias with the trigger `{name}` has been edited successfully.").format( name=alias_name ) ) diff --git a/redbot/cogs/audio/core/utilities/playlists.py b/redbot/cogs/audio/core/utilities/playlists.py index 08ec3e4b41a..a5301c8fb1e 100644 --- a/redbot/cogs/audio/core/utilities/playlists.py +++ b/redbot/cogs/audio/core/utilities/playlists.py @@ -31,7 +31,7 @@ log = getLogger("red.cogs.Audio.cog.Utilities.playlists") _ = Translator("Audio", Path(__file__)) -CURRATED_DATA = ( +CURATED_DATA = ( "https://gist.githubusercontent.com/aikaterna/4b5de6c420cd6f12b83cb895ca2de16a/raw/json" ) @@ -262,9 +262,9 @@ async def get_playlist_match( colour=await context.embed_colour(), ) msg = await context.send(embed=embed) - avaliable_emojis = ReactionPredicate.NUMBER_EMOJIS[1:] - avaliable_emojis.append("🔟") - emojis = avaliable_emojis[: len(correct_scope_matches)] + available_emojis = ReactionPredicate.NUMBER_EMOJIS[1:] + available_emojis.append("🔟") + emojis = available_emojis[: len(correct_scope_matches)] emojis.append("\N{CROSS MARK}") start_adding_reactions(msg, emojis) pred = ReactionPredicate.with_emojis(emojis, msg, user=context.author) @@ -686,7 +686,7 @@ def humanize_scope( async def _get_bundled_playlist_tracks(self): async with aiohttp.ClientSession(json_serialize=json.dumps) as session: async with session.get( - CURRATED_DATA + f"?timestamp={int(time.time())}", + CURATED_DATA + f"?timestamp={int(time.time())}", headers={"content-type": "application/json"}, ) as response: if response.status != 200: diff --git a/redbot/cogs/mod/names.py b/redbot/cogs/mod/names.py index 3cc5970bce0..17b3beb377d 100644 --- a/redbot/cogs/mod/names.py +++ b/redbot/cogs/mod/names.py @@ -76,7 +76,7 @@ async def rename(self, ctx: commands.Context, member: discord.Member, *, nicknam if exc.status == 400: # BAD REQUEST await ctx.send(_("That nickname is invalid.")) else: - await ctx.send(_("An unexpected error has occured.")) + await ctx.send(_("An unexpected error has occurred.")) else: await ctx.send(_("Done.")) diff --git a/redbot/cogs/mutes/converters.py b/redbot/cogs/mutes/converters.py index ac3737d5d40..46cbabc3030 100644 --- a/redbot/cogs/mutes/converters.py +++ b/redbot/cogs/mutes/converters.py @@ -33,7 +33,7 @@ class MuteTime(Converter): """ This will parse my defined multi response pattern and provide usable formats - to be used in multiple reponses + to be used in multiple responses """ async def convert( diff --git a/redbot/cogs/mutes/mutes.py b/redbot/cogs/mutes/mutes.py index 3ca1d40482c..0d1af83f110 100644 --- a/redbot/cogs/mutes/mutes.py +++ b/redbot/cogs/mutes/mutes.py @@ -428,7 +428,7 @@ async def _auto_channel_unmute_user_multi( for result in results: if not result: continue - _mmeber, channel, reason = result + _member, channel, reason = result unmuted_channels.remove(channel) modlog_reason = _("Automatic unmute") @@ -1571,7 +1571,7 @@ async def mute_user( "user": user, } # TODO: This typing is ugly and should probably be an object on its own - # along with this entire method and some othe refactorization + # along with this entire method and some other refactorization # v1.0.0 is meant to look ugly right :') if permissions.administrator: ret["reason"] = _(MUTE_UNMUTE_ISSUES["is_admin"]) diff --git a/redbot/cogs/reports/reports.py b/redbot/cogs/reports/reports.py index 24c049efdea..c81cf3358d7 100644 --- a/redbot/cogs/reports/reports.py +++ b/redbot/cogs/reports/reports.py @@ -34,7 +34,7 @@ class Reports(commands.Cog): default_report = {"report": {}} - # This can be made configureable later if it + # This can be made configurable later if it # becomes an issue. # Intervals should be a list of tuples in the form # (period: timedelta, max_frequency: int) diff --git a/redbot/cogs/streams/streams.py b/redbot/cogs/streams/streams.py index 0ff5840f814..4ca4600b0e4 100644 --- a/redbot/cogs/streams/streams.py +++ b/redbot/cogs/streams/streams.py @@ -944,7 +944,7 @@ async def check_streams(self): await role.edit(mentionable=False) await self.save_streams() except Exception as e: - log.error("An error has occured with Streams. Please report it.", exc_info=e) + log.error("An error has occurred with Streams. Please report it.", exc_info=e) if to_remove: for stream in to_remove: diff --git a/redbot/cogs/streams/streamtypes.py b/redbot/cogs/streams/streamtypes.py index 659e135f935..dce1af2ef50 100644 --- a/redbot/cogs/streams/streamtypes.py +++ b/redbot/cogs/streams/streamtypes.py @@ -198,7 +198,7 @@ async def is_online(self): log.debug(f"livestreams for {self.name}: {self.livestreams}") log.debug(f"not_livestreams for {self.name}: {self.not_livestreams}") # This is technically redundant since we have the - # info from the RSS ... but incase you don't wanna deal with fully rewritting the + # info from the RSS ... but incase you don't wanna deal with fully rewriting the # code for this part, as this is only a 2 quota query. if self.livestreams: params = { @@ -330,7 +330,7 @@ def display_name(self, value: str) -> None: async def wait_for_rate_limit_reset(self) -> None: """Check rate limits in response header and ensure we're following them. - From python-twitch-client and adaptated to asyncio from Trusty-cogs: + From python-twitch-client and adapted to asyncio from Trusty-cogs: https://github.com/tsifrer/python-twitch-client/blob/master/twitch/helix/base.py https://github.com/TrustyJAID/Trusty-cogs/blob/master/twitch/twitch_api.py """ diff --git a/redbot/cogs/trivia/trivia.py b/redbot/cogs/trivia/trivia.py index 3364b6186d8..235ff2a60a4 100644 --- a/redbot/cogs/trivia/trivia.py +++ b/redbot/cogs/trivia/trivia.py @@ -154,7 +154,7 @@ async def triviaset_allowoverride(self, ctx: commands.Context, enabled: bool): ) @triviaset.command(name="usespoilers", usage="") - async def trivaset_use_spoilers(self, ctx: commands.Context, enabled: bool): + async def triviaset_use_spoilers(self, ctx: commands.Context, enabled: bool): """Set if bot will display the answers in spoilers. If enabled, the bot will use spoilers to hide answers. @@ -167,7 +167,7 @@ async def trivaset_use_spoilers(self, ctx: commands.Context, enabled: bool): await ctx.send(_("Alright, I won't use spoilers to hide answers anymore.")) @triviaset.command(name="botplays", usage="") - async def trivaset_bot_plays(self, ctx: commands.Context, enabled: bool): + async def triviaset_bot_plays(self, ctx: commands.Context, enabled: bool): """Set whether or not the bot gains points. If enabled, the bot will gain a point if no one guesses correctly. @@ -180,7 +180,7 @@ async def trivaset_bot_plays(self, ctx: commands.Context, enabled: bool): await ctx.send(_("Alright, I won't embarrass you at trivia anymore.")) @triviaset.command(name="revealanswer", usage="") - async def trivaset_reveal_answer(self, ctx: commands.Context, enabled: bool): + async def triviaset_reveal_answer(self, ctx: commands.Context, enabled: bool): """Set whether or not the answer is revealed. If enabled, the bot will reveal the answer if no one guesses correctly diff --git a/redbot/core/_diagnoser.py b/redbot/core/_diagnoser.py index 42976f992a9..a8c32c60a01 100644 --- a/redbot/core/_diagnoser.py +++ b/redbot/core/_diagnoser.py @@ -143,7 +143,7 @@ async def _check_can_bot_send_messages(self) -> CheckResult: # While the following 2 checks could show even more precise error message, # it would require a usage of private attribute rather than the public API - # which increases maintanance burden for not that big of benefit. + # which increases maintenance burden for not that big of benefit. async def _check_ignored_issues(self) -> CheckResult: label = _("Check if the channel and the server aren't set to be ignored") if await self.bot.ignored_channel_or_guild(self.message): @@ -816,7 +816,7 @@ class RootDiagnosersMixin( async def _check_global_call_once_checks_issues(self) -> CheckResult: label = _("Global 'call once' checks") # To avoid running core's global checks twice, we just run them all regularly - # and if it turns out that invokation would end here, we go back and check each of + # and if it turns out that invocation would end here, we go back and check each of # core's global check individually to give more precise error message. try: can_run = await self.bot.can_run(self.ctx, call_once=True) diff --git a/redbot/core/bot.py b/redbot/core/bot.py index 06e7846648a..9c136117ae1 100644 --- a/redbot/core/bot.py +++ b/redbot/core/bot.py @@ -700,7 +700,7 @@ async def allowed_by_whitelist_blacklist( If given a member, this will additionally check guild lists - If omiting a user or member, you must provide a value for ``who_id`` + If omitting a user or member, you must provide a value for ``who_id`` You may also provide a value for ``guild`` in this case diff --git a/redbot/core/commands/help.py b/redbot/core/commands/help.py index 5fe8d6b476d..ebf15ff0424 100644 --- a/redbot/core/commands/help.py +++ b/redbot/core/commands/help.py @@ -510,7 +510,7 @@ async def make_and_send_embeds(self, ctx, embed_dict: dict, help_settings: HelpS offset += len(embed_dict["embed"]["title"]) # In order to only change the size of embeds when necessary for this rather - # than change the existing behavior for people uneffected by this + # than change the existing behavior for people unaffected by this # we're only modifying the page char limit should they be impacted. # We could consider changing this to always just subtract the offset, # But based on when this is being handled (very end of 3.2 release) @@ -519,7 +519,7 @@ async def make_and_send_embeds(self, ctx, embed_dict: dict, help_settings: HelpS # This is still necessary with the max interaction above # While we could subtract 100% of the time the offset from page_char_limit # the intent here is to shorten again - # *only* when necessary, by the exact neccessary amount + # *only* when necessary, by the exact necessary amount # To retain a visual match with prior behavior. page_char_limit = 5500 - offset elif page_char_limit < 250: diff --git a/redbot/core/core_commands.py b/redbot/core/core_commands.py index af7bff75853..e03b554b696 100644 --- a/redbot/core/core_commands.py +++ b/redbot/core/core_commands.py @@ -1320,7 +1320,7 @@ async def embedset_command_guild( self, ctx: commands.GuildContext, command: CommandConverter, enabled: bool = None ): """ - Sets a commmand's embed setting for the current server. + Sets a command's embed setting for the current server. If set, this is used instead of the server default to determine whether or not to use embeds. @@ -3692,7 +3692,7 @@ async def _set_errormsg(self, ctx: commands.Context, *, msg: str = None): if msg is not None: await self.bot._config.invoke_error_msg.set(msg) - content = _("Succesfully updated the error message.") + content = _("Successfully updated the error message.") else: await self.bot._config.invoke_error_msg.clear() content = _("Successfully reset the error message back to the default one.") diff --git a/redbot/core/rpc.py b/redbot/core/rpc.py index b5f0a9f1f07..58eddfff403 100644 --- a/redbot/core/rpc.py +++ b/redbot/core/rpc.py @@ -174,7 +174,7 @@ def register_rpc_handler(self, method): def unregister_rpc_handler(self, method): """ - Unregisters an RPC method handler. + Deregisters an RPC method handler. This will be called automatically for you on cog unload and will pass silently if the method is not previously registered. diff --git a/redbot/core/settings_caches.py b/redbot/core/settings_caches.py index 7d3ecd7aed5..259a15dc893 100644 --- a/redbot/core/settings_caches.py +++ b/redbot/core/settings_caches.py @@ -14,7 +14,7 @@ class PrefixManager: def __init__(self, config: Config, cli_flags: Namespace): self._config: Config = config - self._global_prefix_overide: Optional[List[str]] = ( + self._global_prefix_override: Optional[List[str]] = ( sorted(cli_flags.prefix, reverse=True) or None ) self._cached: Dict[Optional[int], List[str]] = {} @@ -32,7 +32,7 @@ async def get_prefixes(self, guild: Optional[discord.Guild] = None) -> List[str] if not ret: ret = await self.get_prefixes(None) else: - ret = self._global_prefix_overide or (await self._config.prefix()) + ret = self._global_prefix_override or (await self._config.prefix()) self._cached[gid] = ret.copy() diff --git a/redbot/core/utils/tunnel.py b/redbot/core/utils/tunnel.py index 9e8c2f7c4d1..614f567028f 100644 --- a/redbot/core/utils/tunnel.py +++ b/redbot/core/utils/tunnel.py @@ -179,7 +179,7 @@ async def files_from_attach( async def close_because_disabled(self, close_message: str): """ - Sends a mesage to both ends of the tunnel that the tunnel is now closed. + Sends a message to both ends of the tunnel that the tunnel is now closed. Parameters ---------- diff --git a/redbot/core/utils/views.py b/redbot/core/utils/views.py index eeeb7bec74e..4bcd4c25bfd 100644 --- a/redbot/core/utils/views.py +++ b/redbot/core/utils/views.py @@ -352,7 +352,7 @@ def _format_keys(keys: Optional[Dict[str, str]]) -> Optional[str]: async def on_submit(self, interaction: discord.Interaction): if not await interaction.client.is_owner( interaction.user - ): # Prevent non-bot owners from somehow aquiring and saving the modal. + ): # Prevent non-bot owners from somehow acquiring and saving the modal. return await interaction.response.send_message( _("This modal is for bot owners only. Whoops!"), ephemeral=True ) diff --git a/redbot/vendored/discord/ext/menus/__init__.py b/redbot/vendored/discord/ext/menus/__init__.py index 282610a3fea..c0a86f8e324 100644 --- a/redbot/vendored/discord/ext/menus/__init__.py +++ b/redbot/vendored/discord/ext/menus/__init__.py @@ -657,7 +657,7 @@ async def on_menu_button_error(self, exc): Handles reporting of errors while updating the menu from events. The default behaviour is to log the exception. - This may be overriden by subclasses. + This may be overridden by subclasses. Parameters ---------- diff --git a/tests/core/test_utils.py b/tests/core/test_utils.py index 1981cfcfcfb..01c354f1c03 100644 --- a/tests/core/test_utils.py +++ b/tests/core/test_utils.py @@ -44,7 +44,7 @@ async def wait_task(i, delay, status, fail=False): if isinstance(result, RuntimeError): num_failed += 1 else: - assert result == i # verify_permissions original orde + assert result == i # verify_permissions original order assert 0 <= result < num_tasks assert 0 < status[1] <= num_concurrent From d9c46342d442a3c1ed156d9a4dfaa0cff13a5f88 Mon Sep 17 00:00:00 2001 From: Tom Marcot <113185315+BigPeep0doo@users.noreply.github.com> Date: Tue, 14 Feb 2023 05:36:40 +0100 Subject: [PATCH 41/43] Fix typos issues (#5977) Co-authored-by: Flame442 <34169552+Flame442@users.noreply.github.com> --- redbot/cogs/trivia/data/lists/2016.yaml | 2 +- redbot/cogs/trivia/data/lists/anime.yaml | 2 +- .../cogs/trivia/data/lists/artandliterature.yaml | 16 ++++++++-------- redbot/cogs/trivia/data/lists/boombeach.yaml | 2 +- redbot/cogs/trivia/data/lists/disney.yaml | 6 +++--- redbot/cogs/trivia/data/lists/entertainment.yaml | 14 +++++++------- redbot/cogs/trivia/data/lists/general.yaml | 6 +++--- redbot/cogs/trivia/data/lists/greekmyth.yaml | 2 +- redbot/cogs/trivia/data/lists/hockey.yaml | 2 +- .../cogs/trivia/data/lists/leagueoflegends.yaml | 4 ++-- redbot/cogs/trivia/data/lists/lotr.yaml | 4 ++-- redbot/cogs/trivia/data/lists/music.yaml | 2 +- redbot/cogs/trivia/data/lists/overwatch.yaml | 8 ++++---- redbot/cogs/trivia/data/lists/pokemon.yaml | 12 ++++++------ redbot/cogs/trivia/data/lists/r6siege.yaml | 4 ++-- redbot/cogs/trivia/data/lists/slogans.yaml | 2 +- redbot/cogs/trivia/data/lists/sports.yaml | 2 +- redbot/cogs/trivia/data/lists/starwars.yaml | 2 +- redbot/cogs/trivia/data/lists/warcraft.yaml | 6 +++--- redbot/cogs/trivia/data/lists/worldcup.yaml | 2 +- 20 files changed, 50 insertions(+), 50 deletions(-) diff --git a/redbot/cogs/trivia/data/lists/2016.yaml b/redbot/cogs/trivia/data/lists/2016.yaml index 9bc9661737f..ac24b2484ec 100644 --- a/redbot/cogs/trivia/data/lists/2016.yaml +++ b/redbot/cogs/trivia/data/lists/2016.yaml @@ -110,7 +110,7 @@ November 18 - Denton Cooley passed away. He was known for performing the first i - artificial heart November 2 - Oleg Popov passed away. What was his profession?: - Clown -November 20 - A train derails in what country killing more than 140 people?: +November 20 - In which country does a train derail and kill more than 140 people?: - India November 25 - This man passed away. He was the 16th Prime Minister and 17th President of Cuba.: - Fidel Castro diff --git a/redbot/cogs/trivia/data/lists/anime.yaml b/redbot/cogs/trivia/data/lists/anime.yaml index f3247a67678..39c098aadfe 100644 --- a/redbot/cogs/trivia/data/lists/anime.yaml +++ b/redbot/cogs/trivia/data/lists/anime.yaml @@ -418,7 +418,7 @@ AUTHOR: JennJenn - Crimson (Fullmetal Alchemist) The Father of the Homunculi can stop which style of alchemy?: - Western Armestris -(Fullmetal Alchemist) What Language is Riza's Tattoo Writen In?: +(Fullmetal Alchemist) What Language is Riza's Tattoo written in?: - Latin (Fullmetal Alchemist) What are Colonel Mustang's gloves made of?: - ignition cloth diff --git a/redbot/cogs/trivia/data/lists/artandliterature.yaml b/redbot/cogs/trivia/data/lists/artandliterature.yaml index cc5200259eb..f979fed77cd 100644 --- a/redbot/cogs/trivia/data/lists/artandliterature.yaml +++ b/redbot/cogs/trivia/data/lists/artandliterature.yaml @@ -16,8 +16,8 @@ A band of painted or sculpted decoration, often at the top of a wall?: - Frieze A composition made of cut and pasted pieces of materials, sometimes with images added by the artist?: - Collage -A decorative art movement that emerged in the late nineteenth century. Characterized by dense assymmetrical ornamentation in sinuos forms, it is often symbolic and of an erotic nature?: -- Art noveau +A decorative art movement that emerged in the late nineteenth century. Characterized by dense asymmetrical ornamentation in sinuos forms, it is often symbolic and of an erotic nature?: +- Art nouveau A figurative movement that emerged in the United States and Britain in the late 1960s and 1970s. The subject matter, usually everyday scenes, is portrayed in an extremely detailed, exacting style. It is also called superrealism, especially when referring to sculpture?: - Photorealism A flat board used by a painter to mix and hold colors, traditionally oblong, with a hole for the thumb; also, a range of colors used by a particular painter?: @@ -83,7 +83,7 @@ An Italian movement c.1909-1919. It attempted to integrate the dynamism of the m - Futurism An abstract movement in Europe and the United States, begun in the mid-1950's and based on the effect of optical patterns?: - Op art -An artwork humoously excaggerating the qualities, defects, or pecularities of a person or idea?: +An artwork humoously excaggerating the qualities, defects, or peculiarities of a person or idea?: - Caricature An eighteenth-century European style, originating in France. In reaction to the grandeur and massiveness of the baroque, it employed refined, elegant, highly decorative forms?: - Rococco @@ -539,7 +539,7 @@ Which Algerian born French author's works included 'L'Etranger' and 'La Peste'?: Which American artist is known for a portrait of his mother?: - James Whistler - whistler -Which American auther wrote the novel 'Roots'?: +Which American author wrote the novel 'Roots'?: - Alex Haley - haley Which American author wrote Jaws?: @@ -584,7 +584,7 @@ Which US clarinetist player's real name was Arthur Jacob Shaw?: Which US dramatist was once married to Marylin Monroe and penned the plays "Death Of A Salesman" and "The Crucible"?: - Arthur Miller - miller -Which Welsh poet died of alcohol poisoning the year he publsihed his collected poems?: +Which Welsh poet died of alcohol poisoning the year he published his collected poems?: - Dylan Thomas - thomas Which antipodean opera singer sung at Prince Charles' wedding to Lady Diana Spencer?: @@ -644,7 +644,7 @@ Which famous book contains the line 'Mr & Mrs Dursley of number 4 Privet Drive w - Harry Potter And The Philosophers Stone Which famous book contains the line 'Once upon a time there was a little chimney sweep and his name was tom'?: - The Water Babies -"Which famous play begins with the line: 'When shall we three meet again, in thunder, lightening, or in rain'?": +"Which famous play begins with the line: 'When shall we three meet again, in thunder, lightning, or in rain'?": - Macbeth Which famous sculptor was refused entry to the French Academy 3 times?: - August Rodin @@ -748,7 +748,7 @@ Who did Macduff kill?: Who drew drawings of absurd mechanical contrivances?: - William Heath Robinson - Robinson -Who had decieved the Lord of Rohan for a number of years?: +Who had deceived the Lord of Rohan for a number of years?: - Wormtongue Who is Karen Blixen better known as?: - Isaak Dinesen @@ -1024,5 +1024,5 @@ With what art movement was Salvador Dali associated?: - Surrealism Women's magazine launched by New York in the 70's?: - Ms -Works of a culturally homogenous people without formal training, generally according to regional traditions and involving crafts?: +Works of a culturally homogeneous people without formal training, generally according to regional traditions and involving crafts?: - Folk art diff --git a/redbot/cogs/trivia/data/lists/boombeach.yaml b/redbot/cogs/trivia/data/lists/boombeach.yaml index 06165669e2e..857445edd32 100644 --- a/redbot/cogs/trivia/data/lists/boombeach.yaml +++ b/redbot/cogs/trivia/data/lists/boombeach.yaml @@ -214,7 +214,7 @@ What is the maximum level for a Boom Mine?: What is the maximum percentage chance of an invasion the Radar can display?: - 17 - 17% -What is the maxiumum boosted GBE for someone with 6 GBE statues?: +What is the maximum boosted GBE for someone with 6 GBE statues?: - 185 What is the most expensive troop to fully load a landing craft with?: - Cryoneer diff --git a/redbot/cogs/trivia/data/lists/disney.yaml b/redbot/cogs/trivia/data/lists/disney.yaml index 52e0eea6e26..a0548d75e7f 100644 --- a/redbot/cogs/trivia/data/lists/disney.yaml +++ b/redbot/cogs/trivia/data/lists/disney.yaml @@ -733,7 +733,7 @@ In Toy Story, what song is playing in the car when Woody and Buzz are chasing it - Hakuna Matata In Toy Story, what was in the package that Sid was waiting for in the mail?: - Rocket -In Toy Story, who accidently pushed Buzz out the window?: +In Toy Story, who accidentally pushed Buzz out the window?: - Woody In Toy Story, who does Mr. Potato Head get stuck with as a moving buddy?: - Rex @@ -1668,7 +1668,7 @@ What movie inspired Walt Disney when he witnessed an experiment with Carbon Diox - The Absent Minded Professor What movie is the character Taffyta Muttonfudge from?: - Wreck it Ralph -What musical group was originally planed for the voice of the vultures in The Jungle Book but turned it down?: +What musical group was originally planned for the voice of the vultures in The Jungle Book but turned it down?: - The Beatles What musical segment of Fantasia is commonly associated with Christmas?: - The Nutcracker Suite @@ -1718,7 +1718,7 @@ What song from the Lion King was originally left out of the film but put back in - Can You Feel the Love Tonight What song is played during the opening credits of Pinocchio?: - When you Wish Upon a Star -What song plays as Lady and Tramp share spagetti meatballs in Lady and the Tramp?: +What song plays as Lady and Tramp share spaghetti meatballs in Lady and the Tramp?: - Bella Notte What sound was recorded to create the wind sounds in the Movie Wall-E?: - Niagara Falls diff --git a/redbot/cogs/trivia/data/lists/entertainment.yaml b/redbot/cogs/trivia/data/lists/entertainment.yaml index 4bf1e7c1645..7c041f69d1c 100644 --- a/redbot/cogs/trivia/data/lists/entertainment.yaml +++ b/redbot/cogs/trivia/data/lists/entertainment.yaml @@ -421,7 +421,7 @@ Tess Trueheart married which plainclothes detective?: - Dick Tracy The Hard Rock Cafe is named after a song by what band?: - The Doors -The Who had a guiness world record for what?: +The Who had a guinness world record for what?: - Loudest Band The Who's rock musical stars Elton John. It's called ________?: - Tommy @@ -458,10 +458,10 @@ This band's highly original video for "Whip it," characterized by red flower pot - Devo This electronic instrument's creator was surnamed Moog, and his models are worth a fortune! Other brands include Roland, Korg, and Casio?: - Synthesizer -This female artist enjoyed sucess on both popular and country & western stations with such tunes as "Let Me Be There" and "Have You Never Been Mellow"?: +This female artist enjoyed success on both popular and country & western stations with such tunes as "Let Me Be There" and "Have You Never Been Mellow"?: - Olivia Newton-John - olivia newton john -This film starring Julie Andrews and Christopher Plummer wont he best picture Oscar for 1965?: +This film starring Julie Andrews and Christopher Plummer won the best picture Oscar for 1965?: - The Sound of Music This film starring Richard Beymer and Natalie Wood won the best picture Oscar for 1961?: - West Side Story @@ -674,7 +674,7 @@ What is the name of Jaleel White's character in the tv series 'Family ties'?: - Steve Urkel What is the name of Pierce Brosnan's first James Bond film?: - Goldeneye -What is the name of Yogi Bear's best freind?: +What is the name of Yogi Bear's best friend?: - Boo Boo What is the name of the Family Circus's dog?: - Barf @@ -1043,7 +1043,7 @@ Who is married to Valerie Bertanelli?: - Eddie Van Halen Who is stationed at Camp Swampy in the comic strips?: - Beetle bailey -Who is the autor of the song 'Blue Suede Shoes'?: +Who is the author of the song 'Blue Suede Shoes'?: - Carl Perkins Who is the elder statesman of 'british blues', and fronted 'The Bluesbreakers'?: - John Mayall @@ -1317,7 +1317,7 @@ Who wrote 'Roll Over Beethoven'?: - Chuck Berry Who wrote Tubular Bells?: - Mike Oldfield -Who wrote and preformed the soundtrack for Live and let die?: +Who wrote and performed the soundtrack for Live and let die?: - Paul McCartney and Wings Who wrote the Nutcracker Suite?: - Tchaikovsky @@ -1330,7 +1330,7 @@ Who wrote the opera 'Tosca'?: Who wrote the opera 'norma'?: - Vincenzo Bellini Who wrote the oprea 'La Traviata'?: -- Guiseppe Verdi +- Giuseppe Verdi Who wrote the song 'Do They Know It's Christmas' with Bob Geldof?: - Midge Ure Who wrote the song 'Do They Know It's Christmas' with Midge Ure?: diff --git a/redbot/cogs/trivia/data/lists/general.yaml b/redbot/cogs/trivia/data/lists/general.yaml index 4523428b07b..69f92e4a32d 100644 --- a/redbot/cogs/trivia/data/lists/general.yaml +++ b/redbot/cogs/trivia/data/lists/general.yaml @@ -999,7 +999,7 @@ What is the name for a male witch?: - Warlock What is the name given to a word or phrase that reads the same backward and forward?: - Palindrome -What is the name given to the change in pitch accompanied by an approaching or receeding sound source?: +What is the name given to the change in pitch accompanied by an approaching or receding sound source?: - Doppler Effect - Doppler What is the name of Batman's sidekick and ward?: @@ -1220,7 +1220,7 @@ What year was the movie Ben Hur with Charlton Heston made?: - 1959 What year were PopTarts invented?: - 1964 -What's the abreviation of 'picture element'?: +What's the abbreviation of 'picture element'?: - Pixel What's the adjective used for the planes flying faster than the speed of sound?: - Supersonic @@ -1585,7 +1585,7 @@ Whose nose grew when he told a lie?: Whose painting is used on Woody Allen's 'Midnight in Paris' movie poster?: - Van Gogh - Vincent van Gogh -Whose quote is "fly like a butterfly sting like a bee"?: +Whose quote is "Float like a butterfly, sting like a bee"?: - Muhammad Ali Wikipedia's spherical logo features what symbol for Greek W, also a fatty acid name?: - Omega diff --git a/redbot/cogs/trivia/data/lists/greekmyth.yaml b/redbot/cogs/trivia/data/lists/greekmyth.yaml index c367f64dc68..bc84af50f9a 100644 --- a/redbot/cogs/trivia/data/lists/greekmyth.yaml +++ b/redbot/cogs/trivia/data/lists/greekmyth.yaml @@ -127,7 +127,7 @@ Which goddess is associated with the rose?: - Aphrodite Which goddess was associated with the spirit of divine retribution against those who succumb to hubris?: - Nemesis -Which godess is associated with the Spring?: +Which goddess is associated with the Spring?: - Persephone Which hero completed 12 labors?: - Hercules diff --git a/redbot/cogs/trivia/data/lists/hockey.yaml b/redbot/cogs/trivia/data/lists/hockey.yaml index 6265570e11b..5a8c7d35c15 100644 --- a/redbot/cogs/trivia/data/lists/hockey.yaml +++ b/redbot/cogs/trivia/data/lists/hockey.yaml @@ -2366,7 +2366,7 @@ Who is the most famous Avs enforcer?: # Colorado Avalanche Trivia by icseN#8889 In the 2000-01 season, the Avs won the Stanley Cup when they had a 52 win season. What other year did the Avs have 52 wins? Format as [YYYY-YY].: - 2013-14 -- 2013-2014 # Not necessary but just incase +- 2013-2014 # Not necessary but just in case Quebec traded Eric Lindros to the Flyers receiving players like Peter Forsberg, Mike Ricci, Ron Hextall, and more + $15,000,000 and 2 of Philly's first round picks (1993 and 1994). The 1994 pick was traded but who was drafted with the 1993 Flyers pick?: - Jocelyn Thibault - Thibault diff --git a/redbot/cogs/trivia/data/lists/leagueoflegends.yaml b/redbot/cogs/trivia/data/lists/leagueoflegends.yaml index 69415aa9c88..d80e822cada 100644 --- a/redbot/cogs/trivia/data/lists/leagueoflegends.yaml +++ b/redbot/cogs/trivia/data/lists/leagueoflegends.yaml @@ -142,7 +142,7 @@ What does Jax use for a weapon (classic skin)?: - Lamppost What does Kled's secondary bar measure?: - Courage -What does Pax Jax weild as a weapon?: +What does Pax Jax wield as a weapon?: - Cardboard Tube - Cardboardtube What does Zoe call Aurelion Sol?: @@ -338,7 +338,7 @@ What type of food does Kai'sa prefer to eat?: - Peach What type of poison does Teemo use in his blowgun?: - Arjunta -What type of weapon does Yorick weild?: +What type of weapon does Yorick wield?: - Monk's Spade - monks spade - spade diff --git a/redbot/cogs/trivia/data/lists/lotr.yaml b/redbot/cogs/trivia/data/lists/lotr.yaml index 8498a5a9b67..954a2cabdef 100644 --- a/redbot/cogs/trivia/data/lists/lotr.yaml +++ b/redbot/cogs/trivia/data/lists/lotr.yaml @@ -115,7 +115,7 @@ AUTHOR: owo - John Rhys-Davies "Which of the following items did none of the Hobbits receive as a gift from the Elves of Lorien?\nA) Cloaks\nB) Daggers\nC) Rope\nD) Pipes": - D -"I accidently knocked a piece of armor down the well in Moria!\nA) Merry Brandybuck\nB) Frodo Baggins\nC) Sam Gamgee\nD) Pippin Took": +"I accidentally knocked a piece of armor down the well in Moria!\nA) Merry Brandybuck\nB) Frodo Baggins\nC) Sam Gamgee\nD) Pippin Took": - D "In \"The Two Towers\", name the breakfast item Smeagol brings to Frodo that Sam cooks?": - Rabbits @@ -212,7 +212,7 @@ AUTHOR: owo - Marton Csokas "Peter Jackson admitted that the toughest goodbye for him was after Elijah Woods last pickup scene. Where was this scene?": - Bag End -"Who sucessfully lit the beacon at Minas Tirith?": +"Who successfully lit the beacon at Minas Tirith?": - Pippin "I got the ring after Sauron had it. However, I refused to throw it into the fires of Mount Doom, where it needed to go in order to permanently defeat Sauron and his evil army. Who played me?": - Harry Sinclair diff --git a/redbot/cogs/trivia/data/lists/music.yaml b/redbot/cogs/trivia/data/lists/music.yaml index a2965a54c80..0c2b4b23e99 100644 --- a/redbot/cogs/trivia/data/lists/music.yaml +++ b/redbot/cogs/trivia/data/lists/music.yaml @@ -240,7 +240,7 @@ Which pop group had their first number one in the UK with a cover version of A-H - A1 Which group formed in Oxford in the late 1980s and took their name from a song by Talking Heads?: - Radiohead -Which Spice Girl realeased the album "Schizophonic"?: +Which Spice Girl released the album "Schizophonic"?: - Geri Halliwell In the song 'The Twelve Days Of Christmas', what did my true love give to me on the 12th day?: - 12 Drummers Drumming diff --git a/redbot/cogs/trivia/data/lists/overwatch.yaml b/redbot/cogs/trivia/data/lists/overwatch.yaml index 904b0f6cbf1..152b4404abd 100644 --- a/redbot/cogs/trivia/data/lists/overwatch.yaml +++ b/redbot/cogs/trivia/data/lists/overwatch.yaml @@ -103,7 +103,7 @@ In the cinematic Infiltration which company's CEO was going to be assassinated?: - Volskaya Industries In the cinematic Infiltration who was secretly collaborated with the omnics?: - Katya Volskaya -In the comic Going Legit what was the name of the company that hired the heros?: +In the comic Going Legit what was the name of the company that hired the heroes?: - Hyde Global In the first web comic who was blamed for the robbery?: - McCree @@ -161,7 +161,7 @@ On which map was this screenshot taken? http://i.imgur.com/vxqkKQ0.jpg: On which map was this screenshot taken? http://i.imgur.com/xuO8PDw.jpg: - Volskaya Industries - Volskaya -Orignally when Overwatch was released players could choose to stack multiples of any hero on any team,it was later limited to 1 of each hero per team, and an arcade mode where heros tacking was again possble was added, what is this mode called?: +Originally when Overwatch was released players could choose to stack multiples of any hero on any team, it was later limited to 1 of each hero per team, and an arcade mode where hero stacking was again possible was added, what is this mode called?: - No Limits Over the design period for Overwatch, Bastion had several different ults before he got Tank mode how many including his current one did he have?: - "4: a mine, bouncing grenades, shooting though walls and tank mode" @@ -318,7 +318,7 @@ What is Sombra's Ultimate called?: - EMP What is Sombra's ultimate called?: - EMP -What is Symmetra obssessed with?: +What is Symmetra obsessed with?: - Order What is Symmetra's real name?: - Satya Vaswani @@ -381,7 +381,7 @@ What is the lowest competitive rank?: - Bronze What is the model name of D.Va's mech?: - MEKA -What is the name of Ana's "Pixel Spray" achivement?: +What is the name of Ana's "Pixel Spray" achievement?: - Naptime What is the name of Ana's default voice line?: - Justice delivered diff --git a/redbot/cogs/trivia/data/lists/pokemon.yaml b/redbot/cogs/trivia/data/lists/pokemon.yaml index 4fcdef2777f..b62cd21646d 100644 --- a/redbot/cogs/trivia/data/lists/pokemon.yaml +++ b/redbot/cogs/trivia/data/lists/pokemon.yaml @@ -179,7 +179,7 @@ In Gold/Silver/Crystal/Heart Gold/Soul Silver, Team Rocket stole an item and hid - Machine Part In Ruby/Sapphire/Emerald Pokéblock feeders in the Safari Zone attract Pokémon based on their what?: - Nature -In Ruby/Sapphire/Emerald you receieve an egg in Lavaridge town. Which Pokémon hatches from the egg?: +In Ruby/Sapphire/Emerald you receive an egg in Lavaridge town. Which Pokémon hatches from the egg?: - Wynaut In what Generation did Flash become a TM?: - 4 @@ -282,7 +282,7 @@ The MissingNo. glitch in Red and Blue duplicates which item in your bag?: The Special/Physical split happened in what generation?: - 4 - Four -The Substitute doll was orignally based off of which Pokémon?: +The Substitute doll was originally based off of which Pokémon?: - Rhydon The Unova Region is based off of what real life city?: - New York City @@ -614,7 +614,7 @@ What is the only Pokémon from Generation 6 that can Mega evolve?: What is the only Pokémon that learns Light of Ruin?: - Eternal Flower Floette - eternal floette -What is the only Pokémon that recieved both a pre-evolution and evolution in the same generation?: +What is the only Pokémon that received both a pre-evolution and evolution in the same generation?: - Roselia What is the only Pokémon to have a lower base stat total than its pre-evolved form?: - Shedinja @@ -646,7 +646,7 @@ What is the only game to not require Surf (or Lapras/Sharpedo) in order to reach - black - black & white - white & black -What is the only game where Lance doesnt use a Dragonite?: +What is the only game where Lance doesn't use a Dragonite?: - Pokemon Stadium What is the only move Smeargle cannot copy?: - Chatter @@ -731,7 +731,7 @@ What item is this http://i.imgur.com/tSmriuj.png ?: - Dawnstone What item is used to summon Dialga?: - Adamant Orb -What item protects aginst effects caused by using a contact move on a target?: +What item protects against effects caused by using a contact move on a target?: - Protective Pads - protective pad What item summons Arceus?: @@ -808,7 +808,7 @@ What stat does the ability Rattled raise upon activation?: What stone evolves a Floette?: - Shiny Stone - Shiny -What suprising Pokémon can Skitty infamously breed with?: +What surprising Pokémon can Skitty infamously breed with?: - Wailord What town is Ash from?: - Pallet Town diff --git a/redbot/cogs/trivia/data/lists/r6siege.yaml b/redbot/cogs/trivia/data/lists/r6siege.yaml index f66857e6c85..9424476dc05 100644 --- a/redbot/cogs/trivia/data/lists/r6siege.yaml +++ b/redbot/cogs/trivia/data/lists/r6siege.yaml @@ -316,9 +316,9 @@ In 2004, Ying had an altercation with which operator regarding Ying's perception In Patch 4.2 and 5.2, Blackbeard's Rifle-Shield had its HP reduced to 150 and 60 respectively. What was the original value?: - 800 - Eight hundred -In Portugese, what does "Capitao" mean?: +In Portuguese, what does "Capitao" mean?: - Captain -In Portugese, what does "Caveira" mean?: +In Portuguese, what does "Caveira" mean?: - Skull In Terrorist Hunt, how many seconds does a bomb take to finish defusing?: - 60 diff --git a/redbot/cogs/trivia/data/lists/slogans.yaml b/redbot/cogs/trivia/data/lists/slogans.yaml index 95ea6359120..ebb73bdd86d 100644 --- a/redbot/cogs/trivia/data/lists/slogans.yaml +++ b/redbot/cogs/trivia/data/lists/slogans.yaml @@ -510,7 +510,7 @@ When it rains it pours.: - Morton When you care enough to send the very best: - Hallmark -When youre crazy for chicken.: +When you're crazy for chicken.: - El Pollo Loco Where a kid can be a kid: - Chuck E. Cheese's diff --git a/redbot/cogs/trivia/data/lists/sports.yaml b/redbot/cogs/trivia/data/lists/sports.yaml index dd7c1b1a8da..42c70c9dbaa 100644 --- a/redbot/cogs/trivia/data/lists/sports.yaml +++ b/redbot/cogs/trivia/data/lists/sports.yaml @@ -247,7 +247,7 @@ Which boxer has never lost or tied in a match?: - Mayweather Which country won the 2012 UEFA European Championship?: - Spain -Which five-time Grand Slam tennis champion tested postive for a banned substance at the 2016 Australian Open?: +Which five-time Grand Slam tennis champion tested positive for a banned substance at the 2016 Australian Open?: - Maria Sharapova Which hockey player has won the most Stanley Cups with 11 wins?: - Henri Richard diff --git a/redbot/cogs/trivia/data/lists/starwars.yaml b/redbot/cogs/trivia/data/lists/starwars.yaml index 82e48b70b15..4221d51044c 100644 --- a/redbot/cogs/trivia/data/lists/starwars.yaml +++ b/redbot/cogs/trivia/data/lists/starwars.yaml @@ -772,7 +772,7 @@ Who was the leader of the pirates?: - Onaka Who was the model for the Clone Trooper?: - Jango Fett -Who wass Wedge Antilles gunner during the Battle of Hoth?: +Who was Wedge Antilles gunner during the Battle of Hoth?: - Wes Janson - Janson Who wrote the original Star Wars music soundtrack?: diff --git a/redbot/cogs/trivia/data/lists/warcraft.yaml b/redbot/cogs/trivia/data/lists/warcraft.yaml index 8dc0bb176f8..043b8ce67ea 100644 --- a/redbot/cogs/trivia/data/lists/warcraft.yaml +++ b/redbot/cogs/trivia/data/lists/warcraft.yaml @@ -62,7 +62,7 @@ Dalaran once rested in Silverpine Forest before being transported to Northrend, Dark Blue is the default color for which class?: - Shaman - Shamans -Dark Green is the defualt color for which class?: +Dark Green is the default color for which class?: - Demon Hunter - Demon Hunters Discovering all zones award what title?: @@ -235,7 +235,7 @@ The Sunreavers are a group of blood elves who represent the horde in Dalaran. Th - Aethas "The Tauren racial mount is the:": - Kodo -"The achivement \"And They Would All Go Down Together\" reads: Defeat the 4 Horsemen in Naxxramas on Normal Difficulty, ensuring that they all die within __ seconds of each other.": +"The achievement \"And They Would All Go Down Together\" reads: Defeat the 4 Horsemen in Naxxramas on Normal Difficulty, ensuring that they all die within __ seconds of each other.": - 15 - fifteen The acronym GM refers to?: @@ -393,7 +393,7 @@ What is the name of the artist that is the inspiration for the male night elf da - Michael jackson - mj - Micheal jackson -What is the name of the buff you recieve after eating food made with your Cooking profession?: +What is the name of the buff you receive after eating food made with your Cooking profession?: - Well Fed What is the name of the capital of the Zandalar Tribe and all other troll tribes on Azeroth?: - Zuldazar diff --git a/redbot/cogs/trivia/data/lists/worldcup.yaml b/redbot/cogs/trivia/data/lists/worldcup.yaml index 038ff2a4a6d..cceafbb424e 100644 --- a/redbot/cogs/trivia/data/lists/worldcup.yaml +++ b/redbot/cogs/trivia/data/lists/worldcup.yaml @@ -376,7 +376,7 @@ The continental confederation "OFC" governs sport in which continent so that the # Number of teams -From 2026 into the forseeable future, the tournaments will include how many teams?: +From 2026 into the foreseeable future, the tournaments will include how many teams?: - 48 From 2002 to 2022, the tournaments included how many teams?: - 32 From 8de6b977005c39f8e02954d5a2fd78d3e6ec178c Mon Sep 17 00:00:00 2001 From: Kreusada <67752638+Kreusada@users.noreply.github.com> Date: Thu, 2 Mar 2023 14:43:55 +0000 Subject: [PATCH 42/43] Add JSON Schema for Trivia Lists (#5565) Co-authored-by: Jakub Kuczys --- redbot/cogs/trivia/schema.py | 1 + schema/trivia.schema.json | 61 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 schema/trivia.schema.json diff --git a/redbot/cogs/trivia/schema.py b/redbot/cogs/trivia/schema.py index 0abc7c5fb30..19511a21c24 100644 --- a/redbot/cogs/trivia/schema.py +++ b/redbot/cogs/trivia/schema.py @@ -36,6 +36,7 @@ def not_str(value: Any) -> float: MATCH_ALL_BUT_STR = Optional(Use(not_str)) TRIVIA_LIST_SCHEMA = Schema( { + Optional("$schema"): And(str, error=_("{key} key must be a text value.")), Optional("AUTHOR"): And(str, error=_("{key} key must be a text value.")), Optional("CONFIG"): And( { diff --git a/schema/trivia.schema.json b/schema/trivia.schema.json new file mode 100644 index 00000000000..e83bf2ec75e --- /dev/null +++ b/schema/trivia.schema.json @@ -0,0 +1,61 @@ +{ + "$id": "https://raw.githubusercontent.com/Cog-Creators/Red-DiscordBot/V3/develop/schema/trivia.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Red-DiscordBot Trivia List file", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "format": "uri" + }, + "AUTHOR": { + "type": "string", + "description": "Author of the Trivia list." + }, + "CONFIG": { + "type": "object", + "description": "The configuration for a trivia session.", + "properties": { + "bot_plays": { + "type": "boolean", + "description": "Whether or not the bot gains points during the session." + }, + "delay": { + "type": "number", + "description": "The maximum number of seconds permitted to answer a question, must be a positive number greater than or equal to 4.0.", + "minimum": 4.0 + }, + "max_score": { + "type": "integer", + "description": "Number of points required in order to win the trivia, must be a positive integer.", + "exclusiveMinimum": 0 + }, + "payout_multiplier": { + "type": "number", + "description": "The payout multiplier, must be a positive number or zero.", + "minimum": 0 + }, + "reveal_answer": { + "type": "boolean", + "description": "Whether or not to reveal the answer when the question times out." + }, + "timeout": { + "type": "number", + "description": "Number of seconds that need to pass until trivia stops due to no response, must be a positive number greater than 0.0.", + "exclusiveMinimum": 0 + }, + "use_spoilers": { + "type": "boolean", + "description": "Whether to hide the answers in spoilers when revealing the question's answers." + } + }, + "additionalProperties": false + } + }, + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } +} From 2ab204438e2d3d45c9e29f6a78a32aa579a042bf Mon Sep 17 00:00:00 2001 From: Vexed Date: Thu, 2 Mar 2023 14:54:51 +0000 Subject: [PATCH 43/43] Document setting overrides in Trivia list format (#5390) Co-authored-by: Jakub Kuczys --- docs/guide_trivia_list_creation.rst | 63 +++++++++++++++++++++-- redbot/cogs/trivia/data/lists/sports.yaml | 10 ++-- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/docs/guide_trivia_list_creation.rst b/docs/guide_trivia_list_creation.rst index d51f957251b..7eba84f9a48 100644 --- a/docs/guide_trivia_list_creation.rst +++ b/docs/guide_trivia_list_creation.rst @@ -111,11 +111,11 @@ As you've added more questions, your file should look something like this: - Chelsea - chelsea f.c. How much money is a US Olympic gold medalist awarded?: - - $25,000 - - 25,000 - - 25k - - 25000 - - $25000 + - $37,500 + - 37,500 + - 37.5k + - 37500 + - $37500 You can keep adding questions until you are satisfied, and then you can upload and play your very own trivia! See :ref:`[p]triviaset custom ` for more information. @@ -123,3 +123,56 @@ play your very own trivia! See :ref:`[p]triviaset custom `_ for reference. + +-------------------------- +Optional: Custom Overrides +-------------------------- + +Once you've got the hang of the question-answer format, +you might want to add some custom overrides with the CONFIG key - in a similar way to the AUTHOR key. +These will override the settings set with :ref:`[p]triviaset `. +For example, with a trivia list which has questions that are quick to answer you could decrease the time limit +and require a higher score to win. + +Here are all the overrides available: + +.. code-block:: yaml + + CONFIG: + bot_plays: true or false # bot gains points if no one answers correctly + delay: positive number # answer time limit (seconds), must be greater than or equal to 4 + timeout: positive number # timeout for no responses (seconds), must be greater than delay + max_score: positive integer # points required to win + reveal_answer: true or false # reveal answer on timeout + payout_multiplier: non-negative number # payout multiplier + use_spoilers: true or false # use spoilers in answers + +So, your final file might look something like this: + +.. code-block:: yaml + + AUTHOR: Red + CONFIG: + bot_plays: false + use_spoilers: true + delay: 20 + payout_multiplier: 0.5 + max_score: 20 + How many days are there in a regular year?: + - 365 + - three hundred and sixty five + "Who is the #1 followed user on Twitter?": + - Barack Obama + - Obama + What is the only sea without any coasts?: + - Sargasso + - Sargasso Sea + Who won the Premier League in 2015?: + - Chelsea + - chelsea f.c. + How much money is a US Olympic gold medallist awarded?: + - $37,500 + - 37,500 + - 37.5k + - 37500 + - $37500 diff --git a/redbot/cogs/trivia/data/lists/sports.yaml b/redbot/cogs/trivia/data/lists/sports.yaml index 42c70c9dbaa..bc64103e43b 100644 --- a/redbot/cogs/trivia/data/lists/sports.yaml +++ b/redbot/cogs/trivia/data/lists/sports.yaml @@ -32,11 +32,11 @@ How many world records did swimmer Mark Spitz set when he won seven gold medals - 7 - seven How much money is a US Olympic gold medalist awarded?: -- $25,000 -- 25,000 -- 25k -- 25000 -- $25000 +- $37,500 +- 37,500 +- 37.5k +- 37500 +- $37500 In 1998, what Major League Baseball player broke the single season home run record previously set by Roger Maris?: - Mark McGwire In golf, what do you call a score of 4 under par on any given hole?: