diff --git a/.bandit.yml b/.bandit.yml index 4b100f56830..9cb044f4334 100644 --- a/.bandit.yml +++ b/.bandit.yml @@ -393,4 +393,3 @@ weak_cryptographic_key: weak_key_size_ec_medium: 224 weak_key_size_rsa_high: 1024 weak_key_size_rsa_medium: 2048 - diff --git a/.codeclimate.yml b/.codeclimate.yml index 3f29a0496bb..69197d0b428 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -40,9 +40,9 @@ checks: plugins: bandit: enabled: false - radon: + radon: enabled: false - config: + config: threshold: "D" duplication: enabled: false diff --git a/.codespell-ignore-lines b/.codespell-ignore-lines new file mode 100644 index 00000000000..c30ffcc6eab --- /dev/null +++ b/.codespell-ignore-lines @@ -0,0 +1,84 @@ +- Poison Sting +- Exort +- Ether Shock +- FO 12 +- S'engager pour la vie (To enlist for life) +- S'engager pour la vie +Who says "It'll sting a lot"?: +- Singed +Who is known as the "Revered Inventor"?: +- Tynar Rouge +- doesnt have one +Kelsey Grammer sings and plays the theme song for which TV show?: +The initials of the band NIN stand for?: +What job did Sting have before he was a rock star?: +- obi wan kenobi +- Kelsey Grammer +- Midge Ure +With which period in music do we associate with composers such as Bach, Handel and Vivaldi?: +Who has an achievement named "Did That Sting?"?: +Vorsprung durch Technik. (literally, advantage through technology): +"Sam uses Sting to kill some Orcs. Which \"Lord Of The Rings\" movie had that happen?": +- Sting +What American League pitcher threw a perfect game in game 5 of the 1956 World Series? Don Larsen, Pee Wee Reese, Johnny Kucks: +According to the song "Heigh Ho", what else do the dwarfs dig for besides diamonds?: +In "The Tigger Movie," what does Tigger go looking for?: +In Cars what color was Mater before he rusted?: +- Buzz Lightyear +- The Tigger Movie +What kind of tea does Buzz Lightyear drink in Toy Story?: +The classical composers Bach, Brahms, Handel, Strauss, and Schumann are all what nationality?: +What superhero was portrayed by Christian Bale?: +Who is Ned Stark's youngest daughter in the TV series 'Game of Thrones'?: +Whose quote is "fly like a butterfly sting like a bee"?: +- ND +What state is ND?: +- Chanel +- Ba +- Nd +- Te +- Kiri Te Kanawa +- Varian Wrynn +Who was Varian Wrynn's father?: +- Gameboy +(Fullmetal Alchemist) Solf J. Kimblee is known as the ___ Alchemist?: +Maka Albarn is from what anime?: + @_set_ownernotifications.command(name="optin") +"[Programming Languages]Influential programming language, published in 1970 by Niklaus Wirth as a small and efficient language intended to encourage good programming practices using structured programming and data structuring.": +How many Hero Tokens are in this crate? http://i.imgur.com/fSYlcgi.jpg: +If you have level 5 critters how many critters will be in each crate?: +- capetown +- Baton Rouge + - Watchog +How many clones were ready when Obi Wan went to Kamino?: +What planet did Obi Wan exile himself to?: +Where did Obi Wan, Padme, and Anakin go to be executed?: +Who did Obi Wan Love?: +- Obi Wan +- Obi Wan Kenobi +- I Just Cant Stop Loving You +- Lip Rouge +What do the initials of the band NIN stand for?: +Who wrote the song 'Do They Know It's Christmas' with Midge Ure?: +- Tigger +Who does Obi Wan duel on Tatooine?: +macOS 11 (Big Sur) x86-64, aarch64 ~2023-10 + [p]set ownernotifications optin +- Restricted ``[p]cleanupset notify`` to only be invokable in server channels (:issue:`5466`) +What is the name of the Covert operations arm of Overwatch?: +set ownernotifications optin +Who has a skin that references Lu Bu?: +AUTHOR: -Fulcrum#2658, A Wild Ferrothorn#0598, Blue™#3621, Cana#3619, Cardinal Billy#0833, Dynazide#9318, Filmnerdasaurus#3163, Firestarrox#6915, Fuzzysqurl#7058, GabeN#2515, Gates#9008, Gecko#3416, General Dan🎖#7690, Goals4Cory#3999, LaidenCee#5555, Marvelous_Chaos#7350, Mat#0317, Max Brown#3790, Michael#0575, Nate999#1285, Nikea8#0012, PenguinPride87#3572, Phantasmagoria#8041, Phinocio#6969, Pikaboo#2147, QuantiumP#1157, RWang95#3383, RandomBoltsFan#8134, SamT323#0323, Scaldera93#6969, Sigma#8016, Simba#4996, Spyders#1674, TheMiniatureMan#1024, TheRussianRocket#3638, TheVargTrain#6508, The_Notorious_BEN#0475, Theseolous#4208, Tooo#0123, TrustyJAID#0001, Tsar Peter the Great#1909, Windy City Hawkey#7814, Zero Very Cool#8863, aschwan41#0699, astrocreep#7211, brownboy102#2181, goobymaster#0025, guccipotato#7117, haha#0673, icseN#8889, investinsoup#7801, kopiikat#6135, kwando#1313, rpac62#7637, rufus#1996, sandman730#7796, scipio#4358, spookybecca#2969, tommybigshot#2455, and zonk#2716 +Which player won the Hart trophy and the Conn Smythe trophy in the same season, and then repeated the feat a second time?: +Who holds the record for fastest ever first career NHL goal at 15 seconds TOI?: +- Frank Nighbor +- Nighbor +Who was the first NHL player to win 3 or more individual awards in a season, winning the Hart, Art Ross, and Lady Byng?: +Who scored the GWG in 3rd OT of Game 2 of the 2015 Western Conference final?: +- Al Secord +- Secord +Which member of the Avalanche was a founding member of the HDA?: +# Vancouver Canucks Trivia by Tooo#0123 +- Ambien +- Ore +- Zefer diff --git a/.codespell-ignore-words b/.codespell-ignore-words new file mode 100644 index 00000000000..3719bb0cc50 --- /dev/null +++ b/.codespell-ignore-words @@ -0,0 +1,6 @@ +splitted +derails +bridget +rime +reay +gord diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index a5eacf103d3..a87112f2df5 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,5 +1,5 @@ - diff --git a/.github/ISSUE_TEMPLATE/04_feature_request.yml b/.github/ISSUE_TEMPLATE/04_feature_request.yml index 125e7ebf7c2..b1291fd337f 100644 --- a/.github/ISSUE_TEMPLATE/04_feature_request.yml +++ b/.github/ISSUE_TEMPLATE/04_feature_request.yml @@ -31,13 +31,13 @@ body: - Describe what it should do - Note whether it is to extend existing functionality or introduce new functionality - If you are requesting a cog to be included in core: + If you are requesting a cog to be included in core: - Describe the functionality in as much detail as possible - Include the command structure, if possible - Please note that unless it's something that should be core functionality, - we reserve the right to reject your suggestion and point you to our cog + we reserve the right to reject your suggestion and point you to our cog board to request it for a third-party cog - + If you are requesting a command: - Include what cog it should be in and a name for the command - Describe the intended functionality for the command @@ -49,4 +49,3 @@ body: attributes: label: Anything else? description: Let us know if you have anything else to share. - diff --git a/.github/PULL_REQUEST_TEMPLATE/bugfix.md b/.github/PULL_REQUEST_TEMPLATE/bugfix.md index e0215ce01a3..2c4b2bc1186 100644 --- a/.github/PULL_REQUEST_TEMPLATE/bugfix.md +++ b/.github/PULL_REQUEST_TEMPLATE/bugfix.md @@ -7,8 +7,8 @@ To be used for pull requests that fix a bug #### Describe the bug being fixed - diff --git a/.github/workflows/scripts/compile_requirements.py b/.github/workflows/scripts/compile_requirements.py index 37aab01b84c..5d35414dd3b 100644 --- a/.github/workflows/scripts/compile_requirements.py +++ b/.github/workflows/scripts/compile_requirements.py @@ -4,7 +4,6 @@ import sys from pathlib import Path - GITHUB_OUTPUT = os.environ["GITHUB_OUTPUT"] REQUIREMENTS_FOLDER = Path(__file__).parents[3].absolute() / "requirements" os.chdir(REQUIREMENTS_FOLDER) diff --git a/.github/workflows/scripts/get_milestone_number_by_exact_title.js b/.github/workflows/scripts/get_milestone_number_by_exact_title.js index b7d734b5dd5..b525cc6c3c1 100644 --- a/.github/workflows/scripts/get_milestone_number_by_exact_title.js +++ b/.github/workflows/scripts/get_milestone_number_by_exact_title.js @@ -15,7 +15,7 @@ module.exports = (async function ({github, context}) { $repo_owner: String! $repo_name: String! $milestone_title: String! - ) { + ) { repository(owner:$repo_owner name:$repo_name) { milestones(query:$milestone_title states:OPEN first:100) { nodes { diff --git a/.github/workflows/scripts/merge_requirements.py b/.github/workflows/scripts/merge_requirements.py index 4ad2822b207..eb14b066c12 100644 --- a/.github/workflows/scripts/merge_requirements.py +++ b/.github/workflows/scripts/merge_requirements.py @@ -5,7 +5,6 @@ from packaging.markers import Marker from packaging.requirements import Requirement - REQUIREMENTS_FOLDER = Path(__file__).parents[3].absolute() / "requirements" os.chdir(REQUIREMENTS_FOLDER) diff --git a/.gitignore b/.gitignore index bf8bf731ab1..4c6c022a5bc 100644 --- a/.gitignore +++ b/.gitignore @@ -225,4 +225,3 @@ bh_unicode_properties.cache # Sublime-github package stores a github token in this file # https://packagecontrol.io/packages/sublime-github GitHub.sublime-settings - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a0682cafd90..0ba6bb1124e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,7 +55,7 @@ The following requirements must be installed prior to setting up: - Python 3.8.1 or greater - git - pip - + If you're not on Windows, you should also have GNU make installed, and you can optionally install [pyenv](https://github.com/pyenv/pyenv), which can help you run tests for different python versions. 1. Fork and clone the repository to a directory on your local machine. diff --git a/LICENSE b/LICENSE index ab11b148487..e5f4ab604d6 100644 --- a/LICENSE +++ b/LICENSE @@ -675,9 +675,9 @@ the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . -The Red-DiscordBot project contains subcomponents in audio.py that have a -separate copyright notice and license terms. Your use of the source code for -these subcomponents is subject to the terms and conditions of the following +The Red-DiscordBot project contains subcomponents in audio.py that have a +separate copyright notice and license terms. Your use of the source code for +these subcomponents is subject to the terms and conditions of the following licenses. This product bundles methods from https://github.com/Just-Some-Bots/MusicBot/ diff --git a/README.md b/README.md index 05f2c65778a..6ed899391de 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Red is a fully modular bot – meaning all features and commands can be enabled/disabled to your liking, making it completely customizable. This is a *self-hosted bot* – meaning you will need to host and maintain your own instance. You can turn Red into an admin bot, music bot, trivia bot, -new best friend or all of these together! +new best friend or all of these together! [Installation](#installation) is easy, and you do **NOT** need to know anything about coding! Aside from installing and updating, every part of the bot can be controlled from within Discord. @@ -84,7 +84,7 @@ community of cog repositories.** # Installation -**The following platforms are officially supported:** +**The following platforms are officially supported:** - [Windows](https://docs.discord.red/en/stable/install_guides/windows.html) - [MacOS](https://docs.discord.red/en/stable/install_guides/mac.html) diff --git a/docs/autostart_mac.rst b/docs/autostart_mac.rst index 4a6b7cc71b4..47a91c77733 100644 --- a/docs/autostart_mac.rst +++ b/docs/autostart_mac.rst @@ -18,7 +18,7 @@ Copy the output of that command. Now run :code:`sudo nano /Library/LaunchDaemons/red.plist` -Paste the following and replace the following: +Paste the following and replace the following: - :code:`username` (but not :code:`UserName`) with your Mac username - :code:`path` with the path you copied earlier @@ -62,7 +62,7 @@ Paste the following and replace the following: .. note:: - You may add any additional arguments you need to add to the :code:`redbot` command by + You may add any additional arguments you need to add to the :code:`redbot` command by adding them to the end of the array under :code:`ProgramArguments` .. note:: diff --git a/docs/changelog_3_2_0.rst b/docs/changelog_3_2_0.rst index ce594cde5f1..367f326de6c 100644 --- a/docs/changelog_3_2_0.rst +++ b/docs/changelog_3_2_0.rst @@ -9,7 +9,7 @@ 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. +- The bot now ensures it has at least the bare necessary permissions before running commands. - Deleting instances works as intended again. - Sinbad stopped fighting it and embraced the entrypoint madness. @@ -28,7 +28,7 @@ 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. @@ -110,7 +110,7 @@ Breaking Changes - ``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 `_) +- Reserved some command names for internal Red use. These are available programmatically 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 `_) @@ -335,7 +335,7 @@ Bug Fixes - ``[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 track indexes 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 `_) diff --git a/docs/changelog_3_3_0.rst b/docs/changelog_3_3_0.rst index 7f8f2046aa4..9810144854a 100644 --- a/docs/changelog_3_3_0.rst +++ b/docs/changelog_3_3_0.rst @@ -354,7 +354,7 @@ Developer changelog ------------------- | **Important:** -| If you're using RPC, please see the full annoucement about current state of RPC in main Red server +| If you're using RPC, please see the full announcement about current state of RPC in main Red server `by clicking here `_. @@ -562,7 +562,7 @@ 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`) +- All owner notifications 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 @@ -874,7 +874,7 @@ Audio ----- - Playlist finding is more intuitive. -- disconnect and repeat commands no longer interfere with eachother. +- disconnect and repeat commands no longer interfere with each other. CustomCom --------- diff --git a/docs/changelog_3_4_0.rst b/docs/changelog_3_4_0.rst index 0f47fc6e9e7..8a95e0e86d6 100644 --- a/docs/changelog_3_4_0.rst +++ b/docs/changelog_3_4_0.rst @@ -1456,7 +1456,7 @@ Core Bot - 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`) +- Updated Red's emoji usage to ensure consistent rendering across 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: @@ -1517,7 +1517,7 @@ Breaking changes - 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`) +- `Context.maybe_send_embed()` now suppresses 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`) diff --git a/docs/cog_guides/alias.rst b/docs/cog_guides/alias.rst index 85c179c28e6..2f418dbcd25 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: @@ -158,7 +158,7 @@ Here are more examples: * * Full command: ``[p]repo add SinbadCogs https://github.com/mikeshardmind/SinbadCogs v3`` - + * Alias: ``[p]alias add newrepo repo add {2} https://github.com/{1}/{2}`` * Invoked with alias: ``[p]newrepo mikeshardmind SinbadCogs v3`` diff --git a/docs/cog_guides/audio.rst b/docs/cog_guides/audio.rst index 242ff75a7ca..0cd8540c0bd 100644 --- a/docs/cog_guides/audio.rst +++ b/docs/cog_guides/audio.rst @@ -20,7 +20,7 @@ command name, like ``[p]help playlist append``. In this guide, you will see references to "Lavalink" or the "Lavalink.jar". `Lavalink `_ is the Java-based audio backend we use to be able to play music through the bot. Most users will not have to worry much about Lavalink or what it is, as Audio manages this process for you -by default. Advanced users can read more about Lavalink and special cases under the +by default. Advanced users can read more about Lavalink and special cases under the :ref:`Lavalink - Red Community-Supported Advanced Usage` section below. You will also see references to ``managed`` or ``unmanaged`` in regards to the Lavalink.jar. @@ -32,7 +32,7 @@ for your bot to use. Any time Red is updated and you update your bot, it will pr updated Lavalink.jar for your bot. It is important to keep your bot updated as fixes are provided for Lavalink frequently. .. warning:: - + All commands should be used in a Discord server, and not in Direct Messages as Audio is not available there. .. _basic-audio-use: @@ -80,10 +80,10 @@ The following commands are used for controlling the audio being played, such as Frequently Asked Questions -------------------------- -**Q: I used a playlist link with some of the playlist related commands and it tells me that it can't find it. +**Q: I used a playlist link with some of the playlist related commands and it tells me that it can't find it. How can I use this playlist link with playlist commands in audio?** - Audio uses Red playlists in its commands that take playlist arguments. + Audio uses Red playlists in its commands that take playlist arguments. These playlists can be created and modified using the ``[p]playlist`` group command. When a playlist or song(s) are saved as a Red playlist, it is assigned an ID automatically, and it is also assigned the one-word name you provided it when creating the playlist. @@ -100,33 +100,33 @@ How can I use this playlist link with playlist commands in audio?** **Q: How do I get the bot to disconnect from the channel when it's done playing?** - ``[p]audioset dc`` will make the bot auto-disconnect when playback completes and the - queue is empty. - ``[p]audioset emptydisconnect`` with a seconds argument greater than 0 will make the bot - auto-disconnect once it's alone in the channel, after the amount of seconds given to the + ``[p]audioset dc`` will make the bot auto-disconnect when playback completes and the + queue is empty. + ``[p]audioset emptydisconnect`` with a seconds argument greater than 0 will make the bot + auto-disconnect once it's alone in the channel, after the amount of seconds given to the command. This setting takes precedence over ``[p]audioset dc`` if both settings are active. **Q: How do I use localtracks?** See the :ref:`local tracks section`. - + **Q: My console is saying that "Port 2333 is already in use". How can I fix this?** If you are trying to run multiple bots with Audio, you should follow our guide on - :ref:`setting up Audio for multiple bots`. Otherwise, another process is using the + :ref:`setting up Audio for multiple bots`. Otherwise, another process is using the port, so you need to figure out what is using port 2333 and terminate/disconnect it yourself. - + **Q: My terminal is saying that I "must install Java 11 for Lavalink to run". How can I fix this?** You are getting this error because you have a different version of Java installed, or you don't have Java installed at all. As the error states, Java 11 is required, and can be installed from `here `__. - + If you have Java 11 installed, and are still getting this error, you will have to manually tell Audio where your Java install is located. Use ``[p]llset java ``, to make Audio launch Lavalink with a specific Java binary. To do this, you will need to locate your ``java.exe``/``java`` file in your **Java 11 install**. - + Alternatively, update your PATH settings so that Java 11 is the one used by ``java``. However, you should confirm that nothing other than Red is running on the machine that requires Java. @@ -188,10 +188,10 @@ Owner-Only Audioset Commands Guild-based Audioset Commands ----------------------------- -* ``[p]audioset notify`` - Toggle extra messages: Audio will display a notification message when a track starts, - showing the song title, artist, and the thumbnail (if enabled and present). This notify message follows the last +* ``[p]audioset notify`` - Toggle extra messages: Audio will display a notification message when a track starts, + showing the song title, artist, and the thumbnail (if enabled and present). This notify message follows the last invoking Audio command - if an Audio command is used in one channel and this setting is on, the notify messages - will start to appear in the channel where the command was used. If another Audio command is used in another + will start to appear in the channel where the command was used. If another Audio command is used in another channel, notify messages will start appearing in the second channel instead of the first command channel. * ``[p]audioset maxvolume`` - Set the max volume for the guild. * ``[p]audioset autodeafen`` - Toggle the bot being auto-deafened upon voice channel join. @@ -204,9 +204,9 @@ Guild-based Audioset Commands Spotify Playback and API Keys ------------------------------ -We will never be able to play directly from Spotify itself as it is against their Terms of Service. Audio can play +We will never be able to play directly from Spotify itself as it is against their Terms of Service. Audio can play single tracks or playlists from Spotify by looking up the song(s) on YouTube and playing those tracks instead. -This is possible by providing your bot with a YouTube API key and a Spotify API key. Instructions for setting both +This is possible by providing your bot with a YouTube API key and a Spotify API key. Instructions for setting both can be found under ``[p]audioset youtubeapi`` and ``[p]audioset spotifyapi``. The YouTube API keys that are being given out currently only have 10000 units of quota per day, which is equivalent to @@ -214,7 +214,7 @@ The YouTube API keys that are being given out currently only have 10000 units of lookups. For example, with a 500 song Spotify playlist, the bot will be able to fetch the first 100 songs the first day the Spotify playlist URL is used with Audio, then the next day it will be able to use the first 100 lookups from the local cache, and use the API credits to look up the next 100 songs. After 5 days of playing the Spotify playlist -through Audio, that playlist will be fully cached locally until the cached entries are set to expire and will not require +through Audio, that playlist will be fully cached locally until the cached entries are set to expire and will not require any API credits to play songs. The following commands are relevant: @@ -360,14 +360,14 @@ Setting up Multiple Red Instances with Audio on the Same Host .. warning:: - This section provides instructions for setting up an unmanaged Lavalink node that is on the same machine - as the Red bot(s) that need to connect to it. This configuration is supported by the Red community, so + This section provides instructions for setting up an unmanaged Lavalink node that is on the same machine + as the Red bot(s) that need to connect to it. This configuration is supported by the Red community, so if you need additional help, feel free to join the `Red Support Server `__ and ask in the #support channel. - If you are looking to set up a remote, unmanaged Lavalink node on a different vps or host than the Red - bot(s) that will connect to it, we provide basic instructions in this guide :ref:`here`, but that - configuration is partially unsupported as we do not provide help with network configuration or system - administration. You will be responsible for configuring your network, firewall, and other system + If you are looking to set up a remote, unmanaged Lavalink node on a different vps or host than the Red + bot(s) that will connect to it, we provide basic instructions in this guide :ref:`here`, but that + configuration is partially unsupported as we do not provide help with network configuration or system + administration. You will be responsible for configuring your network, firewall, and other system properties to enable playback and for the bot to connect to the remote unmanaged Lavalink server. If you are wanting to use multiple bots with Audio on the same machine, you'll need to make a few @@ -383,11 +383,11 @@ Next, open a command prompt/terminal window. Navigate to ``/cogs/Audio instances - it doesn't matter which bot as all your bots will now use this single instance of Lavalink. You can find your ```` with the ``[p]datapath`` command. -Now you need to determine your RAM needs. If your bot has 1GB RAM available, Lavalink should be restricted -to perhaps 384MB -> 768MB, depending on the cogs you have installed. If your bot has 2GB of RAM available, -a good amount may be 512MB -> 1GB. +Now you need to determine your RAM needs. If your bot has 1GB RAM available, Lavalink should be restricted +to perhaps 384MB -> 768MB, depending on the cogs you have installed. If your bot has 2GB of RAM available, +a good amount may be 512MB -> 1GB. -Run the following command, where ``Xmx`` specifies the RAM value you have just determined. The MB suffix +Run the following command, where ``Xmx`` specifies the RAM value you have just determined. The MB suffix is M and the GB suffix is G. .. code-block:: ini @@ -435,7 +435,7 @@ Now we need to create a service file so that systemd can do its magic. Run the f .. code-block:: sh sudo -e /etc/systemd/system/lavalink.service - + Next, paste in the example below, but replacing the following: * ```` - You can find your Java path by running ``which java``. @@ -444,9 +444,9 @@ Next, paste in the example below, but replacing the following: .. code-block:: ini - [Unit] - Description=lavalink - After=multi-user.target + [Unit] + Description=lavalink + After=multi-user.target [Service] ExecStart= -Djdk.tls.client.protocols=TLSv1.2 -jar < Lavalink path >/Lavalink.jar @@ -464,7 +464,7 @@ Next, paste in the example below, but replacing the following: Finally, we need to start and enable the service. Run the following commands, separately. .. code-block:: sh - + sudo systemctl start lavalink sudo systemctl enable lavalink @@ -474,7 +474,7 @@ service runs in the background. Finally, you can run the following to retrieve logs for the service, when you need them: .. code-block:: sh - + sudo journalctl -u lavalink .. _obtaining-the-latest-lavalink: @@ -489,7 +489,7 @@ Stop your bot. Download the Lavalink.jar file from `here `__, in the #general channel. @@ -543,14 +543,14 @@ following commands one by one. sudo apt upgrade -y sudo apt install curl nano -y -If you have no preference in which Java version you install on your target system, Red +If you have no preference in which Java version you install on your target system, Red uses OpenJDK 11 in the managed Lavalink configuration. It can be installed by running: .. code-block:: sh sudo apt install openjdk-11-jre-headless -y -Otherwise, Lavalink works well with most versions of Java 11, 13, 15, 16, 17, and 18. Azul +Otherwise, Lavalink works well with most versions of Java 11, 13, 15, 16, 17, and 18. Azul Zulu builds are suggested, see `here `__ for more information. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -600,7 +600,7 @@ First, let's open the file. You can use any text editor you want, but in this gu Run the following command: .. code-block:: sh - + nano application.yml You will be dropped into the nano text editor with ``application.yml`` opened. The two important fields that we will modify @@ -629,11 +629,11 @@ Now that Lavalink has been installed and configured, we can start it up. To do s that you are inside the lavalink folder, of course: .. code-block:: sh - + java -Djdk.tls.client.protocols=TLSv1.2 -jar Lavalink.jar On successful start, Lavalink will greet you with a line mentioning that it is ready to accept connections and you can now -try connecting to it with your bot. +try connecting to it with your bot. Since we did not configure autostart for Lavalink, you will have to keep the console window open or it will be shut down and all connections will be dropped. This is similar to how it happens in Red-Discordbot itself. @@ -683,7 +683,7 @@ highly recommended. But if you know what you are doing, you can skip it if you w First, run the following commands: .. code-block:: sh - + cd cd lavalink nano auto_update.sh @@ -700,9 +700,9 @@ Now save the file and exit (``CTRL+X``, then ``y``). Now, run the following command, which will make the script possible to run: .. code-block:: sh - + chmod a+rx auto_update.sh - + If you did it right, the command itself will not output anything. And when running ``ls``, the script will show up in green. """""""""""""""""""""""""""""" @@ -757,16 +757,16 @@ Hit ``CTRL+X``, ``y`` and then ENTER to save and exit. We have now registered La Starting and Enabling the Lavalink Service """""""""""""""""""""""""""""""""""""""""" -Now run the following command to start the Lavalink service and wait for 10-15 seconds: +Now run the following command to start the Lavalink service and wait for 10-15 seconds: .. code-block:: sh - + sudo systemctl start lavalink You can check the service status with the following command: .. code-block:: sh - + sudo journalctl -u lavalink Keep in mind this will occupy your terminal and you have to hit CTRL+C to stop it before doing something else. @@ -831,7 +831,7 @@ audioset .. code-block:: none - [p]audioset + [p]audioset **Description** @@ -849,7 +849,7 @@ audioset autodeafen .. code-block:: none - [p]audioset autodeafen + [p]audioset autodeafen **Description** @@ -867,7 +867,7 @@ audioset autoplay .. code-block:: none - [p]audioset autoplay + [p]audioset autoplay **Description** @@ -930,7 +930,7 @@ audioset autoplay reset .. code-block:: none - [p]audioset autoplay reset + [p]audioset autoplay reset **Description** @@ -946,7 +946,7 @@ audioset autoplay toggle .. code-block:: none - [p]audioset autoplay toggle + [p]audioset autoplay toggle **Description** @@ -1029,7 +1029,7 @@ audioset dailyqueue .. code-block:: none - [p]audioset dailyqueue + [p]audioset dailyqueue **Description** @@ -1047,7 +1047,7 @@ audioset dc .. code-block:: none - [p]audioset dc + [p]audioset dc **Description** @@ -1066,7 +1066,7 @@ audioset dj .. code-block:: none - [p]audioset dj + [p]audioset dj **Description** @@ -1121,7 +1121,7 @@ audioset globaldailyqueue .. code-block:: none - [p]audioset globaldailyqueue + [p]audioset globaldailyqueue **Description** @@ -1181,7 +1181,7 @@ audioset logs .. code-block:: none - [p]audioset logs + [p]audioset logs **Description** @@ -1199,7 +1199,7 @@ audioset lyrics .. code-block:: none - [p]audioset lyrics + [p]audioset lyrics **Description** @@ -1271,7 +1271,7 @@ audioset notify .. code-block:: none - [p]audioset notify + [p]audioset notify **Description** @@ -1289,7 +1289,7 @@ audioset persistqueue .. code-block:: none - [p]audioset persistqueue + [p]audioset persistqueue **Description** @@ -1308,7 +1308,7 @@ audioset restart .. code-block:: none - [p]audioset restart + [p]audioset restart **Description** @@ -1326,7 +1326,7 @@ audioset restrict .. code-block:: none - [p]audioset restrict + [p]audioset restrict **Description** @@ -1348,7 +1348,7 @@ audioset restrictions .. code-block:: none - [p]audioset restrictions + [p]audioset restrictions **Description** @@ -1364,7 +1364,7 @@ audioset restrictions blacklist .. code-block:: none - [p]audioset restrictions blacklist + [p]audioset restrictions blacklist **Description** @@ -1396,7 +1396,7 @@ audioset restrictions blacklist clear .. code-block:: none - [p]audioset restrictions blacklist clear + [p]audioset restrictions blacklist clear **Description** @@ -1428,7 +1428,7 @@ audioset restrictions blacklist list .. code-block:: none - [p]audioset restrictions blacklist list + [p]audioset restrictions blacklist list **Description** @@ -1446,7 +1446,7 @@ audioset restrictions global .. code-block:: none - [p]audioset restrictions global + [p]audioset restrictions global **Description** @@ -1462,7 +1462,7 @@ audioset restrictions global blacklist .. code-block:: none - [p]audioset restrictions global blacklist + [p]audioset restrictions global blacklist **Description** @@ -1494,7 +1494,7 @@ audioset restrictions global blacklist clear .. code-block:: none - [p]audioset restrictions global blacklist clear + [p]audioset restrictions global blacklist clear **Description** @@ -1526,7 +1526,7 @@ audioset restrictions global blacklist list .. code-block:: none - [p]audioset restrictions global blacklist list + [p]audioset restrictions global blacklist list **Description** @@ -1542,7 +1542,7 @@ audioset restrictions global whitelist .. code-block:: none - [p]audioset restrictions global whitelist + [p]audioset restrictions global whitelist **Description** @@ -1575,7 +1575,7 @@ audioset restrictions global whitelist clear .. code-block:: none - [p]audioset restrictions global whitelist clear + [p]audioset restrictions global whitelist clear **Description** @@ -1607,7 +1607,7 @@ audioset restrictions global whitelist list .. code-block:: none - [p]audioset restrictions global whitelist list + [p]audioset restrictions global whitelist list **Description** @@ -1623,7 +1623,7 @@ audioset restrictions whitelist .. code-block:: none - [p]audioset restrictions whitelist + [p]audioset restrictions whitelist **Description** @@ -1656,7 +1656,7 @@ audioset restrictions whitelist clear .. code-block:: none - [p]audioset restrictions whitelist clear + [p]audioset restrictions whitelist clear **Description** @@ -1688,7 +1688,7 @@ audioset restrictions whitelist list .. code-block:: none - [p]audioset restrictions whitelist list + [p]audioset restrictions whitelist list **Description** @@ -1722,7 +1722,7 @@ audioset settings .. code-block:: none - [p]audioset settings + [p]audioset settings **Description** @@ -1740,7 +1740,7 @@ audioset spotifyapi .. code-block:: none - [p]audioset spotifyapi + [p]audioset spotifyapi **Description** @@ -1758,7 +1758,7 @@ audioset status .. code-block:: none - [p]audioset status + [p]audioset status **Description** @@ -1776,7 +1776,7 @@ audioset thumbnail .. code-block:: none - [p]audioset thumbnail + [p]audioset thumbnail **Description** @@ -1812,7 +1812,7 @@ audioset youtubeapi .. code-block:: none - [p]audioset youtubeapi + [p]audioset youtubeapi **Description** @@ -1830,7 +1830,7 @@ audiostats .. code-block:: none - [p]audiostats + [p]audiostats **Description** @@ -1848,7 +1848,7 @@ autoplay .. code-block:: none - [p]autoplay + [p]autoplay **Description** @@ -1896,7 +1896,7 @@ disconnect .. code-block:: none - [p]disconnect + [p]disconnect **Description** @@ -1912,7 +1912,7 @@ eq .. code-block:: none - [p]eq + [p]eq **Description** @@ -1949,7 +1949,7 @@ eq list .. code-block:: none - [p]eq list + [p]eq list **Description** @@ -1981,7 +1981,7 @@ eq reset .. code-block:: none - [p]eq reset + [p]eq reset **Description** @@ -2034,7 +2034,7 @@ genre .. code-block:: none - [p]genre + [p]genre **Description** @@ -2050,7 +2050,7 @@ local .. code-block:: none - [p]local + [p]local **Description** @@ -2087,7 +2087,7 @@ local play .. code-block:: none - [p]local play + [p]local play **Description** @@ -2128,7 +2128,7 @@ now .. code-block:: none - [p]now + [p]now **Description** @@ -2144,7 +2144,7 @@ pause .. code-block:: none - [p]pause + [p]pause **Description** @@ -2160,7 +2160,7 @@ percent .. code-block:: none - [p]percent + [p]percent **Description** @@ -2195,7 +2195,7 @@ playlist .. code-block:: none - [p]playlist + [p]playlist **Description** @@ -2936,7 +2936,7 @@ prev .. code-block:: none - [p]prev + [p]prev **Description** @@ -2968,7 +2968,7 @@ queue clean .. code-block:: none - [p]queue clean + [p]queue clean **Description** @@ -2984,7 +2984,7 @@ queue cleanself .. code-block:: none - [p]queue cleanself + [p]queue cleanself **Description** @@ -3000,7 +3000,7 @@ queue clear .. code-block:: none - [p]queue clear + [p]queue clear **Description** @@ -3032,7 +3032,7 @@ queue shuffle .. code-block:: none - [p]queue shuffle + [p]queue shuffle **Description** @@ -3064,7 +3064,7 @@ repeat .. code-block:: none - [p]repeat + [p]repeat **Description** @@ -3116,7 +3116,7 @@ shuffle .. code-block:: none - [p]shuffle + [p]shuffle **Description** @@ -3132,7 +3132,7 @@ shuffle bumped .. code-block:: none - [p]shuffle bumped + [p]shuffle bumped **Description** @@ -3151,7 +3151,7 @@ sing .. code-block:: none - [p]sing + [p]sing **Description** @@ -3183,7 +3183,7 @@ stop .. code-block:: none - [p]stop + [p]stop **Description** @@ -3199,7 +3199,7 @@ summon .. code-block:: none - [p]summon + [p]summon **Description** @@ -3227,12 +3227,12 @@ Set the volume, 1% - 150%. Lavalink Setup Commands ----------------------- -``[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 +``[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. -Commands specifically for managed Lavalink nodes can be found in :ref:`this section`, +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-llset: @@ -3247,7 +3247,7 @@ llset .. code-block:: none - [p]llset + [p]llset **Description** @@ -3256,9 +3256,9 @@ manage an unmanaged (external) or managed Lavalink node. .. warning:: - 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 llset command settings have the potential to break + 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 llset command settings have the potential to break Audio cog connection and playback if the wrong settings are used. """""""""""""" @@ -3437,7 +3437,7 @@ changing it can cause significant playback issues. .. _audio-command-llset-config-source: ^^^^^^^^^^^^^^^^^^^ -llset config source +llset config source ^^^^^^^^^^^^^^^^^^^ **Syntax** diff --git a/docs/cog_guides/cleanup.rst b/docs/cog_guides/cleanup.rst index edc0fd468e3..8b759e05c19 100644 --- a/docs/cog_guides/cleanup.rst +++ b/docs/cog_guides/cleanup.rst @@ -44,7 +44,7 @@ cleanup .. code-block:: none - [p]cleanup + [p]cleanup **Description** @@ -307,7 +307,7 @@ cleanupset .. code-block:: none - [p]cleanupset + [p]cleanupset **Description** diff --git a/docs/cog_guides/cog_manager_ui.rst b/docs/cog_guides/cog_manager_ui.rst index e51473702d6..ef087f20bb1 100644 --- a/docs/cog_guides/cog_manager_ui.rst +++ b/docs/cog_guides/cog_manager_ui.rst @@ -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..7f4ca066015 100644 --- a/docs/cog_guides/core.rst +++ b/docs/cog_guides/core.rst @@ -38,7 +38,7 @@ allowlist .. code-block:: none - [p]allowlist + [p]allowlist .. tip:: Alias: ``whitelist`` @@ -84,7 +84,7 @@ allowlist clear .. code-block:: none - [p]allowlist clear + [p]allowlist clear **Description** @@ -105,7 +105,7 @@ allowlist list .. code-block:: none - [p]allowlist list + [p]allowlist list **Description** @@ -151,7 +151,7 @@ autoimmune .. code-block:: none - [p]autoimmune + [p]autoimmune **Description** @@ -215,7 +215,7 @@ autoimmune list .. code-block:: none - [p]autoimmune list + [p]autoimmune list **Description** @@ -359,7 +359,7 @@ bankset prune .. code-block:: none - [p]bankset prune + [p]bankset prune **Description** @@ -562,7 +562,7 @@ blocklist .. code-block:: none - [p]blocklist + [p]blocklist .. tip:: Aliases: ``blacklist``, ``denylist`` @@ -605,7 +605,7 @@ blocklist clear .. code-block:: none - [p]blocklist clear + [p]blocklist clear **Description** @@ -624,7 +624,7 @@ blocklist list .. code-block:: none - [p]blocklist list + [p]blocklist list **Description** @@ -668,7 +668,7 @@ command .. code-block:: none - [p]command + [p]command **Description** @@ -980,7 +980,7 @@ command listdisabled .. code-block:: none - [p]command listdisabled + [p]command listdisabled **Description** @@ -1002,7 +1002,7 @@ command listdisabled global .. code-block:: none - [p]command listdisabled global + [p]command listdisabled global **Description** @@ -1021,7 +1021,7 @@ command listdisabled guild .. code-block:: none - [p]command listdisabled guild + [p]command listdisabled guild **Description** @@ -1040,7 +1040,7 @@ command listdisabledcogs .. code-block:: none - [p]command listdisabledcogs + [p]command listdisabledcogs **Description** @@ -1139,7 +1139,7 @@ embedset .. code-block:: none - [p]embedset + [p]embedset **Description** @@ -1302,7 +1302,7 @@ embedset global .. code-block:: none - [p]embedset global + [p]embedset global **Description** @@ -1418,7 +1418,7 @@ helpset .. code-block:: none - [p]helpset + [p]helpset **Description** @@ -1558,7 +1558,7 @@ helpset resetformatter .. code-block:: none - [p]helpset resetformatter + [p]helpset resetformatter **Description** @@ -1577,7 +1577,7 @@ helpset resetsettings .. code-block:: none - [p]helpset resetsettings + [p]helpset resetsettings **Description** @@ -1650,7 +1650,7 @@ helpset showsettings .. code-block:: none - [p]helpset showsettings + [p]helpset showsettings **Description** @@ -1819,7 +1819,7 @@ ignore .. code-block:: none - [p]ignore + [p]ignore **Description** @@ -1870,7 +1870,7 @@ ignore list .. code-block:: none - [p]ignore list + [p]ignore list **Description** @@ -1891,7 +1891,7 @@ ignore server .. code-block:: none - [p]ignore server + [p]ignore server .. tip:: Alias: ``ignore guild`` @@ -1915,7 +1915,7 @@ info .. code-block:: none - [p]info + [p]info **Description** @@ -1931,7 +1931,7 @@ invite .. code-block:: none - [p]invite + [p]invite **Description** @@ -1956,7 +1956,7 @@ inviteset .. code-block:: none - [p]inviteset + [p]inviteset **Description** @@ -2056,7 +2056,7 @@ licenseinfo .. code-block:: none - [p]licenseinfo + [p]licenseinfo .. tip:: Alias: ``licenceinfo`` @@ -2105,7 +2105,7 @@ localallowlist .. code-block:: none - [p]localallowlist + [p]localallowlist .. tip:: Alias: ``localwhitelist`` @@ -2152,13 +2152,13 @@ localallowlist clear .. code-block:: none - [p]localallowlist clear + [p]localallowlist clear **Description** Clears the allowlist. -This disables the local allowlist and clears all entires. +This disables the local allowlist and clears all entries. **Example:** - ``[p]localallowlist clear`` @@ -2173,7 +2173,7 @@ localallowlist list .. code-block:: none - [p]localallowlist list + [p]localallowlist list **Description** @@ -2220,7 +2220,7 @@ localblocklist .. code-block:: none - [p]localblocklist + [p]localblocklist .. tip:: Alias: ``localblacklist`` @@ -2264,7 +2264,7 @@ localblocklist clear .. code-block:: none - [p]localblocklist clear + [p]localblocklist clear **Description** @@ -2285,7 +2285,7 @@ localblocklist list .. code-block:: none - [p]localblocklist list + [p]localblocklist list **Description** @@ -2330,7 +2330,7 @@ modlogset .. code-block:: none - [p]modlogset + [p]modlogset **Description** @@ -2392,7 +2392,7 @@ modlogset resetcases .. code-block:: none - [p]modlogset resetcases + [p]modlogset resetcases **Description** @@ -2408,7 +2408,7 @@ mydata .. code-block:: none - [p]mydata + [p]mydata **Description** @@ -2426,7 +2426,7 @@ mydata 3rdparty .. code-block:: none - [p]mydata 3rdparty + [p]mydata 3rdparty **Description** @@ -2447,7 +2447,7 @@ mydata forgetme .. code-block:: none - [p]mydata forgetme + [p]mydata forgetme **Description** @@ -2471,7 +2471,7 @@ mydata getmydata .. code-block:: none - [p]mydata getmydata + [p]mydata getmydata **Description** @@ -2489,7 +2489,7 @@ mydata ownermanagement .. code-block:: none - [p]mydata ownermanagement + [p]mydata ownermanagement **Description** @@ -2505,7 +2505,7 @@ mydata ownermanagement allowuserdeletions .. code-block:: none - [p]mydata ownermanagement allowuserdeletions + [p]mydata ownermanagement allowuserdeletions **Description** @@ -2572,7 +2572,7 @@ mydata ownermanagement disallowuserdeletions .. code-block:: none - [p]mydata ownermanagement disallowuserdeletions + [p]mydata ownermanagement disallowuserdeletions **Description** @@ -2643,7 +2643,7 @@ mydata whatdata .. code-block:: none - [p]mydata whatdata + [p]mydata whatdata **Description** @@ -2721,7 +2721,7 @@ servers .. code-block:: none - [p]servers + [p]servers **Description** @@ -2740,7 +2740,7 @@ set .. code-block:: none - [p]set + [p]set **Description** @@ -2790,7 +2790,7 @@ set api list .. code-block:: none - [p]set api list + [p]set api list **Description** @@ -2882,7 +2882,7 @@ set bot avatar remove .. code-block:: none - [p]set bot avatar remove + [p]set bot avatar remove .. tip:: Alias: ``set bot avatar clear`` @@ -3121,7 +3121,7 @@ set fuzzy .. code-block:: none - [p]set fuzzy + [p]set fuzzy **Description** @@ -3247,7 +3247,7 @@ set ownernotifications .. code-block:: none - [p]set ownernotifications + [p]set ownernotifications **Description** @@ -3288,7 +3288,7 @@ set ownernotifications listdestinations .. code-block:: none - [p]set ownernotifications listdestinations + [p]set ownernotifications listdestinations **Description** @@ -3307,7 +3307,7 @@ set ownernotifications optin .. code-block:: none - [p]set ownernotifications optin + [p]set ownernotifications optin **Description** @@ -3332,7 +3332,7 @@ set ownernotifications optout .. code-block:: none - [p]set ownernotifications optout + [p]set ownernotifications optout **Description** @@ -3646,7 +3646,7 @@ set serverfuzzy .. code-block:: none - [p]set serverfuzzy + [p]set serverfuzzy **Description** @@ -3707,7 +3707,7 @@ set showsettings .. code-block:: none - [p]set showsettings + [p]set showsettings **Description** @@ -3975,7 +3975,7 @@ set usebotcolour .. code-block:: none - [p]set usebotcolour + [p]set usebotcolour .. tip:: Alias: ``set usebotcolor`` @@ -3988,7 +3988,7 @@ Otherwise, the colour used will be the colour of the bot's top role. **Example:** - ``[p]set usebotcolour`` - + .. _core-command-set-usebuttons: """""""""""""" @@ -4002,7 +4002,7 @@ set usebuttons .. code-block:: none [p]set usebuttons [use_buttons] - + **Description** Set a global bot variable for using buttons in menus. When enabled, all usage of @@ -4087,7 +4087,7 @@ unignore .. code-block:: none - [p]unignore + [p]unignore **Description** @@ -4132,7 +4132,7 @@ unignore server .. code-block:: none - [p]unignore server + [p]unignore server .. tip:: Alias: ``unignore guild`` @@ -4180,7 +4180,7 @@ uptime .. code-block:: none - [p]uptime + [p]uptime **Description** diff --git a/docs/cog_guides/customcommands.rst b/docs/cog_guides/customcommands.rst index e79526f5b50..e4ea9380e20 100644 --- a/docs/cog_guides/customcommands.rst +++ b/docs/cog_guides/customcommands.rst @@ -42,7 +42,7 @@ customcom .. code-block:: none - [p]customcom + [p]customcom .. tip:: Alias: ``cc`` @@ -220,7 +220,7 @@ customcom list .. code-block:: none - [p]customcom list + [p]customcom list **Description** diff --git a/docs/cog_guides/dev.rst b/docs/cog_guides/dev.rst index 6b5cc3164e1..10732822bdd 100644 --- a/docs/cog_guides/dev.rst +++ b/docs/cog_guides/dev.rst @@ -190,7 +190,7 @@ the content and author of the message are replaced with the given arguments. * ````: The member to mock. |user-input-quotes| * ````: The content used for the message. -.. note:: +.. note:: If ``content`` isn't passed, the message needs to contain embeds, attachments, or anything else that makes the message non-empty. @@ -205,7 +205,7 @@ repl .. code-block:: none - [p]repl + [p]repl **Description** diff --git a/docs/cog_guides/downloader.rst b/docs/cog_guides/downloader.rst index 1e4b2cc2dcd..477ccead733 100644 --- a/docs/cog_guides/downloader.rst +++ b/docs/cog_guides/downloader.rst @@ -48,7 +48,7 @@ cog .. code-block:: none - [p]cog + [p]cog **Description** @@ -64,7 +64,7 @@ cog checkforupdates .. code-block:: none - [p]cog checkforupdates + [p]cog checkforupdates **Description** @@ -185,7 +185,7 @@ cog listpinned .. code-block:: none - [p]cog listpinned + [p]cog listpinned **Description** @@ -410,7 +410,7 @@ repo .. code-block:: none - [p]repo + [p]repo **Description** @@ -504,7 +504,7 @@ repo list .. code-block:: none - [p]repo list + [p]repo list **Description** diff --git a/docs/cog_guides/economy.rst b/docs/cog_guides/economy.rst index 498f376c3eb..6e8a9601fee 100644 --- a/docs/cog_guides/economy.rst +++ b/docs/cog_guides/economy.rst @@ -38,7 +38,7 @@ bank .. code-block:: none - [p]bank + [p]bank **Description** @@ -136,7 +136,7 @@ economyset .. code-block:: none - [p]economyset + [p]economyset **Description** @@ -228,7 +228,7 @@ economyset showsettings .. code-block:: none - [p]economyset showsettings + [p]economyset showsettings **Description** @@ -343,7 +343,7 @@ payday .. code-block:: none - [p]payday + [p]payday **Description** @@ -361,7 +361,7 @@ payouts .. code-block:: none - [p]payouts + [p]payouts **Description** diff --git a/docs/cog_guides/filter.rst b/docs/cog_guides/filter.rst index e8c9167d39b..a69105b7059 100644 --- a/docs/cog_guides/filter.rst +++ b/docs/cog_guides/filter.rst @@ -44,7 +44,7 @@ filter .. code-block:: none - [p]filter + [p]filter **Description** @@ -88,7 +88,7 @@ filter channel .. code-block:: none - [p]filter channel + [p]filter channel **Description** @@ -148,7 +148,7 @@ filter channel list .. code-block:: none - [p]filter channel list + [p]filter channel list **Description** @@ -234,7 +234,7 @@ filter list .. code-block:: none - [p]filter list + [p]filter list **Description** @@ -250,7 +250,7 @@ filter names .. code-block:: none - [p]filter names + [p]filter names **Description** @@ -270,7 +270,7 @@ filterset .. code-block:: none - [p]filterset + [p]filterset **Description** diff --git a/docs/cog_guides/general.rst b/docs/cog_guides/general.rst index 782c82f9053..97f008a52f2 100644 --- a/docs/cog_guides/general.rst +++ b/docs/cog_guides/general.rst @@ -44,7 +44,7 @@ Here's a list of all commands available for this cog. **Description** -Ask 8 ball a question. +Ask 8 ball a question. .. note:: Your question must end with a question mark. diff --git a/docs/cog_guides/mod.rst b/docs/cog_guides/mod.rst index 9c37d36440e..146f20baf1a 100644 --- a/docs/cog_guides/mod.rst +++ b/docs/cog_guides/mod.rst @@ -19,7 +19,7 @@ find detailed docs about usage and commands. Usage ----- -A range of highly customizable moderation tools used to protect your +A range of highly customizable moderation tools used to protect your guild from users who cannot follow the rules. @@ -142,7 +142,7 @@ modset .. code-block:: none - [p]modset + [p]modset **Description** @@ -276,7 +276,7 @@ modset hierarchy .. code-block:: none - [p]modset hierarchy + [p]modset hierarchy **Description** @@ -296,7 +296,7 @@ modset mentionspam .. code-block:: none - [p]modset mentionspam + [p]modset mentionspam **Description** @@ -406,7 +406,7 @@ modset reinvite .. code-block:: none - [p]modset reinvite + [p]modset reinvite **Description** @@ -425,7 +425,7 @@ modset showsettings .. code-block:: none - [p]modset showsettings + [p]modset showsettings **Description** @@ -489,7 +489,7 @@ movedeletedelay .. code-block:: none - [p]movedeletedelay + [p]movedeletedelay **Description** @@ -507,7 +507,7 @@ moveignoredchannels .. code-block:: none - [p]moveignoredchannels + [p]moveignoredchannels **Description** diff --git a/docs/cog_guides/mutes.rst b/docs/cog_guides/mutes.rst index cbab3fba56f..93263421022 100644 --- a/docs/cog_guides/mutes.rst +++ b/docs/cog_guides/mutes.rst @@ -39,7 +39,7 @@ activemutes .. code-block:: none - [p]activemutes + [p]activemutes **Description** @@ -113,7 +113,7 @@ muteset .. code-block:: none - [p]muteset + [p]muteset **Description** @@ -241,8 +241,8 @@ Sets the role to be applied when muting a user. If no role is setup the bot will attempt to mute a user by setting channel overwrites in all channels to prevent the user from sending messages. -.. Note:: - +.. Note:: + If no role is setup a user may be able to leave the server and rejoin no longer being muted. @@ -284,7 +284,7 @@ muteset settings .. code-block:: none - [p]muteset settings + [p]muteset settings .. tip:: Alias: ``muteset showsettings`` @@ -407,4 +407,4 @@ Unmute a user in their current voice channel. **Arguments** * ````: A space separated list of usernames, ID's, or mentions. -* ``[reason]``: The reason for the unmute. \ No newline at end of file +* ``[reason]``: The reason for the unmute. diff --git a/docs/cog_guides/permissions.rst b/docs/cog_guides/permissions.rst index 57b6c04d8bb..07b14a5fe78 100644 --- a/docs/cog_guides/permissions.rst +++ b/docs/cog_guides/permissions.rst @@ -50,7 +50,7 @@ permissions .. code-block:: none - [p]permissions + [p]permissions **Description** @@ -68,7 +68,7 @@ permissions acl .. code-block:: none - [p]permissions acl + [p]permissions acl **Description** @@ -88,7 +88,7 @@ permissions acl getglobal .. code-block:: none - [p]permissions acl getglobal + [p]permissions acl getglobal **Description** @@ -106,7 +106,7 @@ permissions acl getserver .. code-block:: none - [p]permissions acl getserver + [p]permissions acl getserver **Description** @@ -124,13 +124,13 @@ permissions acl setglobal .. code-block:: none - [p]permissions acl setglobal + [p]permissions acl setglobal **Description** Set global rules with a YAML file. -.. warning:: +.. warning:: This will override reset *all* global rules to the rules specified in the uploaded file. @@ -149,13 +149,13 @@ permissions acl setserver .. code-block:: none - [p]permissions acl setserver + [p]permissions acl setserver **Description** Set rules for this server with a YAML file. -.. warning:: +.. warning:: This will override reset *all* rules in this server to the rules specified in the uploaded file. @@ -171,7 +171,7 @@ permissions acl updateglobal .. code-block:: none - [p]permissions acl updateglobal + [p]permissions acl updateglobal **Description** @@ -192,7 +192,7 @@ permissions acl updateserver .. code-block:: none - [p]permissions acl updateserver + [p]permissions acl updateserver **Description** @@ -211,7 +211,7 @@ permissions acl yamlexample .. code-block:: none - [p]permissions acl yamlexample + [p]permissions acl yamlexample **Description** @@ -301,7 +301,7 @@ permissions clearglobalrules .. code-block:: none - [p]permissions clearglobalrules + [p]permissions clearglobalrules **Description** @@ -319,7 +319,7 @@ permissions clearserverrules .. code-block:: none - [p]permissions clearserverrules + [p]permissions clearserverrules **Description** @@ -335,7 +335,7 @@ permissions explain .. code-block:: none - [p]permissions explain + [p]permissions explain **Description** diff --git a/docs/cog_guides/reports.rst b/docs/cog_guides/reports.rst index 24d429cf5ad..142e26f2734 100644 --- a/docs/cog_guides/reports.rst +++ b/docs/cog_guides/reports.rst @@ -94,7 +94,7 @@ reportset .. code-block:: none - [p]reportset + [p]reportset **Description** @@ -134,8 +134,8 @@ reportset toggle .. code-block:: none - [p]reportset toggle + [p]reportset toggle **Description** -Enable or disable reporting for this server. +Enable or disable reporting for this server. diff --git a/docs/cog_guides/streams.rst b/docs/cog_guides/streams.rst index 23aefd078c3..6a471211cd7 100644 --- a/docs/cog_guides/streams.rst +++ b/docs/cog_guides/streams.rst @@ -19,8 +19,8 @@ find detailed docs about usage and commands. Usage ----- -This cog provides commands to check if a channel -on a supported streaming service is live as well +This cog provides commands to check if a channel +on a supported streaming service is live as well as to create and manage alerts for channels. Supported streaming services are: @@ -29,9 +29,9 @@ Supported streaming services are: - Youtube - Picarto -Youtube and Twitch both require setting authentication -details for commands for those services to work. See -:ref:`[p]streamset twitchtoken ` and +Youtube and Twitch both require setting authentication +details for commands for those services to work. See +:ref:`[p]streamset twitchtoken ` and :ref:`[p]streamset youtubekey ` for more information. @@ -50,7 +50,7 @@ streamset **Syntax** .. code-block:: none - + [p]streamset **Description** @@ -66,12 +66,12 @@ streamset autodelete **Syntax** .. code-block:: none - + [p]streamset autodelete **Description** -Toggles automatic deletion of stream alerts when the +Toggles automatic deletion of stream alerts when the stream goes offline. **Arguments** @@ -87,7 +87,7 @@ streamset ignorereruns **Syntax** .. code-block:: none - + [p]streamset ignorereruns **Description** @@ -105,7 +105,7 @@ streamset mention **Syntax** .. code-block:: none - + [p]streamset mention **Description** @@ -121,7 +121,7 @@ streamset mention all **Syntax** .. code-block:: none - + [p]streamset mention all **Description** @@ -137,7 +137,7 @@ streamset mention online **Syntax** .. code-block:: none - + [p]streamset mention online **Description** @@ -153,7 +153,7 @@ streamset mention role **Syntax** .. code-block:: none - + [p]streamset mention role **Description** @@ -173,7 +173,7 @@ streamset message **Syntax** .. code-block:: none - + [p]streamset message **Description** @@ -189,7 +189,7 @@ streamset message mention **Syntax** .. code-block:: none - + [p]streamset message mention **Description** @@ -218,7 +218,7 @@ streamset message nomention **Syntax** .. code-block:: none - + [p]streamset message nomention **Description** @@ -245,7 +245,7 @@ streamset message clear **Syntax** .. code-block:: none - + [p]streamset message clear **Description** @@ -261,7 +261,7 @@ streamset timer **Syntax** .. code-block:: none - + [p]streamset timer **Description** @@ -283,7 +283,7 @@ streamset youtubekey **Syntax** .. code-block:: none - + [p]streamset youtubekey **Description** @@ -303,7 +303,7 @@ To get one, do the following: 4. Copy your API key and run the command ``[p]set api youtube api_key `` -.. attention:: These tokens are sensitive and should only be +.. attention:: These tokens are sensitive and should only be used in a private channel or in DM with the bot. .. _streams-command-streamset-twitchtoken: @@ -315,7 +315,7 @@ streamset twitchtoken **Syntax** .. code-block:: none - + [p]streamset twitchtoken **Description** @@ -335,7 +335,7 @@ To set the Twitch API tokens, follow these steps: 5. Copy your client ID and your client secret into: ``[p]set api twitch client_id client_secret `` -.. attention:: These tokens are sensitive and should only be +.. attention:: These tokens are sensitive and should only be used in a private channel or in DM with the bot. .. _streams-command-streamset-usebuttons: @@ -403,7 +403,7 @@ youtubestream **Syntax** .. code-block:: none - + [p]youtubestream **Description** @@ -423,7 +423,7 @@ streamalert **Syntax** .. code-block:: none - + [p]streamalert **Description** @@ -439,7 +439,7 @@ streamalert list **Syntax** .. code-block:: none - + [p]streamalert list **Description** @@ -455,12 +455,12 @@ streamalert picarto **Syntax** .. code-block:: none - + [p]streamalert picarto **Description** -Toggle alerts in the current channel for the +Toggle alerts in the current channel for the specified Picarto channel. **Arguments** @@ -476,12 +476,12 @@ streamalert twitch channel **Syntax** .. code-block:: none - + [p]streamalert twitch channel **Description** -Toggle alerts in the current channel for the +Toggle alerts in the current channel for the specified Twitch channel. **Arguments** @@ -497,12 +497,12 @@ streamalert youtube **Syntax** .. code-block:: none - + [p]streamalert youtube **Description** -Toggle alerts in the current channel for the +Toggle alerts in the current channel for the specified Picarto channel. **Arguments** @@ -518,7 +518,7 @@ streamalert stop **Syntax** .. code-block:: none - + [p]streamalert stop [disable-all=No] **Description** @@ -527,7 +527,7 @@ Disable all stream alerts for this channel or server. **Arguments** -* ``[disable-all]``: Defaults to ``no``. If this is set to ``yes``, all - stream alerts in the current server will be disabled. - If ``no`` or unspecified, all stream alerts in the - current channel will be stopped. +* ``[disable-all]``: Defaults to ``no``. If this is set to ``yes``, all + stream alerts in the current server will be disabled. + If ``no`` or unspecified, all stream alerts in the + current channel will be stopped. diff --git a/docs/cog_guides/trivia.rst b/docs/cog_guides/trivia.rst index 3996e29f0e4..fbddc740845 100644 --- a/docs/cog_guides/trivia.rst +++ b/docs/cog_guides/trivia.rst @@ -19,9 +19,9 @@ find detailed docs about usage and commands. Usage ----- -This cog allows for playing trivia with others. You may -choose to play just one category at a time or choose -multiple to add variety to your game. You can even create +This cog allows for playing trivia with others. You may +choose to play just one category at a time or choose +multiple to add variety to your game. You can even create your own lists! .. _trivia-commands: @@ -133,9 +133,9 @@ triviaset payout **Description** -Sets the payout multiplier. +Sets the payout multiplier. -If a user wins trivia when at least 3 users are playing, they will receive credits; +If a user wins trivia when at least 3 users are playing, they will receive credits; the amount received is determined by multiplying their total score by this multiplier. **Arguments** @@ -200,7 +200,7 @@ triviaset stopafter **Description** -Sets how long the bot should wait before stopping the trivia +Sets how long the bot should wait before stopping the trivia session due to lack of response. **Arguments** @@ -247,7 +247,7 @@ triviaset custom Manage custom trivia lists. -.. tip:: +.. tip:: Looking to learn how to create your own trivia lists? See :ref:`here ` for more information. @@ -268,7 +268,7 @@ triviaset custom upload **Description** -Upload a custom trivia list. The bot will prompt you to upload +Upload a custom trivia list. The bot will prompt you to upload your list as an attachment in Discord. .. _trivia-command-triviaset-custom-list: @@ -327,7 +327,7 @@ trivia Start a trivia session on the specified category. -Multiple categories can be listed, in which case the trivia session +Multiple categories can be listed, in which case the trivia session will use all of the specified lists to select questions from. **Arguments** @@ -348,8 +348,8 @@ trivia leaderboard **Description** -Shows the trivia leaderboard. Defaults to the top ten in the -current server, sorted by total wins. The subcommands provide +Shows the trivia leaderboard. Defaults to the top ten in the +current server, sorted by total wins. The subcommands provide more customized leaderboards. .. _trivia-command-trivia-leaderboard-global: diff --git a/docs/cog_guides/warnings.rst b/docs/cog_guides/warnings.rst index a0264e24765..56bdd44ec7b 100644 --- a/docs/cog_guides/warnings.rst +++ b/docs/cog_guides/warnings.rst @@ -40,7 +40,7 @@ actionlist .. code-block:: none - [p]actionlist + [p]actionlist **Description** @@ -56,7 +56,7 @@ mywarnings .. code-block:: none - [p]mywarnings + [p]mywarnings **Description** @@ -74,7 +74,7 @@ reasonlist .. code-block:: none - [p]reasonlist + [p]reasonlist **Description** @@ -140,7 +140,7 @@ warnaction .. code-block:: none - [p]warnaction + [p]warnaction **Description** @@ -231,7 +231,7 @@ warningset .. code-block:: none - [p]warningset + [p]warningset **Description** @@ -349,7 +349,7 @@ warnreason .. code-block:: none - [p]warnreason + [p]warnreason **Description** @@ -393,7 +393,7 @@ warnreason delete .. code-block:: none [p]warnreason delete - + **Description** Delete a warning reason. diff --git a/docs/conf.py b/docs/conf.py index a99240fae57..a1eb4671240 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,11 +21,19 @@ import sys import time -sys.path.insert(0, os.path.abspath("..")) -sys.path.insert(0, os.path.abspath("_ext")) +from docutils import nodes +from sphinx.transforms import SphinxTransform -os.environ["BUILDING_DOCS"] = "1" +# this `if` keeps flake8 happy about doing imports after making path changes (E402) +if True: + sys.path.insert(0, os.path.abspath("..")) + sys.path.insert(0, os.path.abspath("_ext")) + os.environ["BUILDING_DOCS"] = "1" + +from discord import __version__ as dpy_version, version_info as dpy_version_info + +from redbot.core import __version__ # -- General configuration ------------------------------------------------ @@ -69,9 +77,6 @@ # |version| and |release|, also used in various other places throughout the # built documents. # -from redbot.core import __version__ -from discord import __version__ as dpy_version, version_info as dpy_version_info - # The short X.Y version. version = __version__ # The full version, including alpha/beta/rc tags. @@ -260,10 +265,6 @@ autodoc_typehints = "none" -from docutils import nodes -from sphinx.transforms import SphinxTransform - - # d.py's |coro| substitution leaks into our docs because we don't replace some of the docstrings class IgnoreCoroSubstitution(SphinxTransform): default_priority = 210 diff --git a/docs/framework_apikeys.rst b/docs/framework_apikeys.rst index 128150f4509..7ae8024b106 100644 --- a/docs/framework_apikeys.rst +++ b/docs/framework_apikeys.rst @@ -14,7 +14,7 @@ Twitch has a client ID and client secret so a user should be asked to input ``[p]set api twitch client_id,1234ksdjf client_secret,1234aldlfkd`` -and when accessed in the code it should be done by +and when accessed in the code it should be done by .. code-block:: python @@ -26,7 +26,7 @@ Example: ``[p]set api youtube api_key,1234ksdjf`` -and when accessed in the code it should be done by +and when accessed in the code it should be done by .. code-block:: python diff --git a/docs/framework_commands.rst b/docs/framework_commands.rst index 38a57470706..82b069e87dc 100644 --- a/docs/framework_commands.rst +++ b/docs/framework_commands.rst @@ -5,7 +5,7 @@ Commands Package ================ This package acts almost identically to :doc:`discord.ext.commands `; i.e. -all of the attributes from discord.py's are also in ours. +all of the attributes from discord.py's are also in ours. Some of these attributes, however, have been slightly modified, while others have been added to extend functionalities used throughout the bot, as outlined below. @@ -20,9 +20,9 @@ extend functionalities used throughout the bot, as outlined below. .. autoclass:: redbot.core.commands.Cog .. automethod:: format_help_for_context - + .. automethod:: red_get_data_for_user - + .. automethod:: red_delete_data_for_user .. autoclass:: redbot.core.commands.GroupCog diff --git a/docs/framework_config.rst b/docs/framework_config.rst index 2827cd5827c..c18e0f116c1 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 @@ -172,7 +172,7 @@ If you're looking to clear data for a single guild/member/channel/role/user, you want to use :py:meth:`Group.clear` as that will clear the data only for the specified thing. -If using :py:meth:`Config.clear_all`, it will reset all data everywhere. +If using :py:meth:`Config.clear_all`, it will reset all data everywhere. There are other methods provided to reset data from a particular scope. For example, :py:meth:`Config.clear_all_guilds` resets all guild data. For member @@ -474,7 +474,7 @@ Best practices and performance notes ************************************ Config prioritizes being a safe data store without developers needing to -know how end users have configured their bot. +know how end users have configured their bot. This does come with some performance costs, so keep the following in mind when choosing to develop using config @@ -482,12 +482,12 @@ develop using config * Config use in events should be kept minimal and should only occur after confirming the event needs to interact with config -* Caching frequently used things, especially things used by events, +* Caching frequently used things, especially things used by events, results in faster and less event loop blocking code. * Only use config's context managers when you intend to modify data. -* While config is a great general use option, it may not always be the right one for you. +* While config is a great general use option, it may not always be the right one for you. As a cog developer, even though config doesn't require one, you can choose to require a database or store to something such as an sqlite database stored within your cog's datapath. diff --git a/docs/framework_i18n.rst b/docs/framework_i18n.rst index b305a8e12e3..61072100404 100644 --- a/docs/framework_i18n.rst +++ b/docs/framework_i18n.rst @@ -15,7 +15,7 @@ Basic Usage from redbot.core import commands from redbot.core.i18n import Translator, cog_i18n - + _ = Translator("ExampleCog", __file__) @cog_i18n(_) diff --git a/docs/getting_started.rst b/docs/getting_started.rst index f43a4f44dbc..e5f587e22a2 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -86,7 +86,7 @@ parameters. Arguments enclosed in ``[ ]`` are **optional** for the command; you can decide whether to use them or not. - + If your argument includes spaces like ``Hello world!``, most of the time you will need to place it in double quotes like this: ``"Hello world!"``. Sometimes (especially for the last argument) these double quotes are not @@ -237,7 +237,7 @@ Moderator A moderator is a step above the average users. You can set multiple moderator roles with the ``[p]set roles addmodrole`` and ``[p]set roles removemodrole`` commands. -For example, in the filter cog, a mod will be able to use the various commands +For example, in the filter cog, a mod will be able to use the various commands under ``[p]filter`` (such as adding and removing filtered words), but they will not be able to modify the cog settings with the ``[p]filterset`` command. @@ -345,7 +345,7 @@ The cog guides are formatted the same. They're divided into 3 sections: A line that will show how the command must be invoked, with the arguments. * **Aliases** - + Each command may have one or more aliases, which are alternative command names you can use to invoke the same command. For example, ``[p]set colour`` can also be invoked with ``[p]set color``. If there are aliases for a command, they will diff --git a/docs/guide_cog_creation.rst b/docs/guide_cog_creation.rst index 0bde5a68aab..9cbf82051ea 100644 --- a/docs/guide_cog_creation.rst +++ b/docs/guide_cog_creation.rst @@ -58,7 +58,7 @@ a text editor or IDE (examples include `Sublime Text 3 `_, `Atom `_, and `PyCharm `_). -.. attention:: +.. attention:: While you can intentionally override Red's cogs/extensions, this may break things. We would prefer if people wanted custom behavior for any core cog/extension, an issue and/or PR is made @@ -128,8 +128,8 @@ have successfully created a cog! .. note:: **Package/Folder layout** - You must make sure you structure your local path correctly or - you get an error about missing the setup function. As cogs are + You must make sure you structure your local path correctly or + you get an error about missing the setup function. As cogs are considered packages, they are each contained within separate folders. The folder you need to add using :code:`[p]addpath` is the parent folder of these package folders. Below is an example @@ -146,11 +146,11 @@ have successfully created a cog! ---- coolcog ------ __init__.py ------ coolcog.py - + You would then use :code:`[p]addpath D:\red-cogs` to add the path and then you can use :code:`[p]load mycog` or :code:`[p]load coolcog` to load them - + You can also take a look at `our cookiecutter `_, for help creating the right structure. ------------------- diff --git a/docs/guide_migration.rst b/docs/guide_migration.rst index 2ddec7909b6..ffd186129fe 100644 --- a/docs/guide_migration.rst +++ b/docs/guide_migration.rst @@ -15,7 +15,7 @@ Red as a package ---------------- V3 makes Red a package that is installed with :code:`pip`. Please -keep this in mind when writing cogs as this affects how imports +keep this in mind when writing cogs as this affects how imports should be done (for example, to import :code:`pagify` in V2, one would do :code:`from .utils.chat_formatting import pagify`; in V3, this becomes :code:`from redbot.core.utils.chat_formatting import pagify`) @@ -53,4 +53,4 @@ Mod Log V3 introduces Mod Log as an API, thus allowing for cogs to add custom case types that will appear in a server's mod log channel. Be sure to checkout -:doc:`/framework_modlog` for more on Mod Log` +:doc:`/framework_modlog` for more on Mod Log` diff --git a/docs/guide_publish_cogs.rst b/docs/guide_publish_cogs.rst index db5162ca762..d392da2a7f2 100644 --- a/docs/guide_publish_cogs.rst +++ b/docs/guide_publish_cogs.rst @@ -31,8 +31,8 @@ take a look at `our example template ` for more information. -Still stuck? Take a look at +Still stuck? Take a look at `the core trivia lists `_ for reference. diff --git a/docs/host-list.rst b/docs/host-list.rst index 20e69600529..2f52901d24d 100644 --- a/docs/host-list.rst +++ b/docs/host-list.rst @@ -13,15 +13,15 @@ Hosting Information | For your instance of Red to stay online 24/7, it needs to be hosted on a dedicated system. - This page contains hosting related information and advice for beginners in + This page contains hosting related information and advice for beginners in topics such as picking a provider. First, we would like to make something clear: .. warning:: Due to their inability to handle Red's data structure and meet the - conditions of being a supported platform; platforms such as Heroku, - Pterodactyl, repl.it, Termux and alike are **NOT** officially supported. + conditions of being a supported platform; platforms such as Heroku, + Pterodactyl, repl.it, Termux and alike are **NOT** officially supported. Docker support found in GitHub is also a work in progress and not ready for daily use. Workarounds for getting Red running on those platforms are imperfect due to Red's nature. You will not be able to receive @@ -33,9 +33,9 @@ Hosting on a VPS or Dedicated Server ------------------------------------ | You can host Red in a VPS running Linux or Windows. Using a Linux VPS is the - recommended option. Dedicated servers also work but are overpowered and cost - ineffective unless one plans to run a very large bot or use their server for - more than just hosting Red. If you have already created an instance, Red can be moved to a different + recommended option. Dedicated servers also work but are overpowered and cost + ineffective unless one plans to run a very large bot or use their server for + more than just hosting Red. If you have already created an instance, Red can be moved to a different server for hosting with a backup/restore process. More information and guidance about this process is available in the `Red Support Server `_. @@ -50,9 +50,9 @@ Hosting on a VPS or Dedicated Server Self Hosting ------------ -| It's possible to self host Red with your own hardware. A Raspberry Pi 3 - will have enough computing capacity to handle a small to medium sized bot. - You can also host on your own computer or rack server. Any modern hardware +| It's possible to self host Red with your own hardware. A Raspberry Pi 3 + will have enough computing capacity to handle a small to medium sized bot. + You can also host on your own computer or rack server. Any modern hardware should work without issues. However, this option leaves you responsible for keeping the bot online by paying for electricity costs and dealing with power outages. @@ -64,11 +64,11 @@ Choosing a Provider each having their pros and cons, this list is mainly intended to act as a starting point. You should conduct your own research and come to a conclusion depending on your needs and budget, taking into account - providers not listed here if desired. The key is the provider offering + providers not listed here if desired. The key is the provider offering an OS supported by Red. .. tip:: - You will have better results with Audio when the region in your Discord + You will have better results with Audio when the region in your Discord server settings is closer to the bulk of the server's audience and the location you picked for your Red host. @@ -80,7 +80,7 @@ Average Providers | `Scaleway `_ is a VPS and dedicated server provider French in origin with locations in Poland and Netherlands. -| `DigitalOcean `_ is a US based cloud services company +| `DigitalOcean `_ is a US based cloud services company with locations available worldwide, the VPS service is provided under the brand name "Droplet". @@ -114,7 +114,7 @@ Average Providers | `LowEndBox `_ is a website where hosting providers are discussed and curated, often with lower costs and less known providers. -| `AlphaVps `_ is a Bulgaria VPS and dedicated server provider +| `AlphaVps `_ is a Bulgaria VPS and dedicated server provider with locations in Los Angeles, New York, England, Germany and Bulgaria. -------------------- 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..81d02c4376f 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 `_ @@ -91,7 +91,7 @@ to keep it in a location which is easy to type out the path to. From now, we'll Start with opening a command prompt (open Start, search for "command prompt", then click it). -.. note:: +.. note:: You shouldn't run command prompt as administrator when creating your virtual environment, or running Red. diff --git a/docs/intents.rst b/docs/intents.rst index 2bdd548f0ac..43023c19d70 100644 --- a/docs/intents.rst +++ b/docs/intents.rst @@ -131,4 +131,4 @@ Discord staff Slash commands might very well turn out to be a big undertaking for the Red team to implement, even more now that our underlying library, `discord.py `_, has been discontinued. |br| The time window that Discord is giving us to adapt is very narrow: **Red will likely not be able to support slash -commands for April 2022** and you should plan accordingly. \ No newline at end of file +commands for April 2022** and you should plan accordingly. 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/docs/red_core_data_statement.rst b/docs/red_core_data_statement.rst index ab7045d351d..f3f44c37b6b 100644 --- a/docs/red_core_data_statement.rst +++ b/docs/red_core_data_statement.rst @@ -11,7 +11,7 @@ What data Red collects ---------------------- Red and the cogs included with it collect some amount of data -about users for the bot's normal operations. +about users for the bot's normal operations. The bot will keep track of a short history of usernames/nicknames. It will also remember which actions were taken using your Discord account (such as creating a playlist) @@ -23,7 +23,7 @@ for anything other than the portion of the Red's functionality that necessitated 3rd party extensions may store additional data beyond what Red does by default. You can use the command ``[p]mydata 3rdparty`` -to view statements about how extensions use your data made by the authors of +to view statements about how extensions use your data made by the authors of the specific 3rd party extensions an instance of Red has installed. How can I delete data Red has about me? @@ -50,10 +50,10 @@ There are a handful of these available to bot owners in the command group The most pertinent one if asked to delete data by a member of Trust & Safety is -``[p]mydata ownermanagement processdiscordrequest`` +``[p]mydata ownermanagement processdiscordrequest`` This will cause the bot to get rid of or disassociate all data -from the specified user ID. +from the specified user ID. .. warning:: diff --git a/docs/release_notes_3_2_0.rst b/docs/release_notes_3_2_0.rst index 75eb9d63ed6..3d8d9aa6852 100644 --- a/docs/release_notes_3_2_0.rst +++ b/docs/release_notes_3_2_0.rst @@ -17,20 +17,20 @@ Please read the following prior to updating. 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 @@ -44,7 +44,7 @@ Please read the following prior to updating. - We've supplied cog creators with additional tools -.. note:: - +.. note:: + The full list of changes is much longer than we can include here, but our changelog has the fine details. diff --git a/docs/update_red.rst b/docs/update_red.rst index 33e4522b3b1..c74ebba778f 100644 --- a/docs/update_red.rst +++ b/docs/update_red.rst @@ -64,7 +64,7 @@ If you have Red 3.2.0 or newer, you can upgrade by following these steps: 1. Shut your bot down. 2. Activate your virtual environment. - + If you used ``venv`` for your virtual environment, use: .. prompt:: bash diff --git a/docs/version_guarantees.rst b/docs/version_guarantees.rst index 0c38801ad17..59fa861a4dc 100644 --- a/docs/version_guarantees.rst +++ b/docs/version_guarantees.rst @@ -87,14 +87,14 @@ Ubuntu 22.10 x86-64, aarch64 2023-07-31 (`End of Developer Guarantees ==================== -Anything in the ``redbot.core`` module or any of its submodules +Anything in the ``redbot.core`` module or any of its submodules which is not private (even if not documented) should not break without notice. Anything in the ``redbot.cogs`` and ``redbot.vendored`` modules or any of their submodules is specifically excluded from being guaranteed. Method names and names of attributes of classes, functions, extensions, and modules -provided by or provided to the bot should not begin with +provided by or provided to the bot should not begin with ``red_`` or be of the form ``__red_*__`` except as documented. This allows us to add certain optional features non-breakingly without a name conflict. @@ -111,6 +111,6 @@ Breaking changes in Red will be noted in the changelog with a special section. Breaking changes may only occur on a minor or major version bump. -A change not covered by our guarantees may not be considered breaking for these purposes, +A change not covered by our guarantees may not be considered breaking for these purposes, while still being documented as a breaking change in internal documentation for the purposes of other internal APIs. diff --git a/make.ps1 b/make.ps1 index 370a21837f1..89d4d9099d0 100644 --- a/make.ps1 +++ b/make.ps1 @@ -94,4 +94,3 @@ switch ($command) { break } } - diff --git a/redbot/__init__.py b/redbot/__init__.py index 3f243520a0c..4ce3bb82e69 100644 --- a/redbot/__init__.py +++ b/redbot/__init__.py @@ -13,7 +13,6 @@ Union as _Union, ) - MIN_PYTHON_VERSION = (3, 8, 1) __all__ = [ @@ -320,7 +319,7 @@ def _ensure_no_colorama(): colorama.deinit() - def _colorama_wrap_stream(stream, *args, **kwargs): + def _colorama_wrap_stream(stream, *_args, **_kwargs): return stream colorama.wrap_stream = _colorama_wrap_stream @@ -334,7 +333,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() @@ -353,7 +352,9 @@ def _early_init(): if not any(_re.match("^-(-debug|d+|-verbose|v+)$", i) for i in _sys.argv): # DEP-WARN # Individual warnings - tracked in https://github.com/Cog-Creators/Red-DiscordBot/issues/3529 - # DeprecationWarning: an integer is required (got type float). Implicit conversion to integers using __int__ is deprecated, and may be removed in a future version of Python. + # DeprecationWarning: an integer is required (got type float). + # Implicit conversion to integers using __int__ is deprecated, + # and may be removed in a future version of Python. _warnings.filterwarnings("ignore", category=DeprecationWarning, module="importlib", lineno=219) # DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10 # stdin, stdout, stderr = await tasks.gather(stdin, stdout, stderr, diff --git a/redbot/__main__.py b/redbot/__main__.py index ddd6eef85c6..7eb73b21f6a 100644 --- a/redbot/__main__.py +++ b/redbot/__main__.py @@ -1,36 +1,34 @@ from redbot import _early_init -# this needs to be called as early as possible -_early_init() +# this `if` keeps flake8 happy about doing imports after function call (E402) +if True: + # this needs to be called as early as possible + _early_init() import asyncio import functools -import getpass import json import logging import os -import pip -import platform import shutil import signal import sys from argparse import Namespace from copy import deepcopy from pathlib import Path -from typing import Any, Awaitable, Callable, NoReturn, Union +from typing import Any, Awaitable, Callable, Union import discord import rich import redbot.logging from redbot import __version__ -from redbot.core.bot import Red, ExitCodes, _NoOwnerSet -from redbot.core.cli import interactive_config, confirm, parse_cli_flags -from redbot.setup import get_data_dir, get_name, save_config from redbot.core import data_manager, drivers from redbot.core._debuginfo import DebugInfo from redbot.core._sharedlibdeprecation import SharedLibImportWarner - +from redbot.core.bot import ExitCodes, Red, _NoOwnerSet +from redbot.core.cli import confirm, interactive_config, parse_cli_flags +from redbot.setup import get_data_dir, get_name, save_config log = logging.getLogger("red.main") @@ -62,7 +60,7 @@ def list_instances(): sys.exit(ExitCodes.SHUTDOWN) -async def debug_info(*args: Any) -> None: +async def debug_info(*_args: Any) -> None: """Shows debug information useful for debugging.""" print(await DebugInfo().get_text()) @@ -378,7 +376,8 @@ async def run_bot(red: Red, cli_flags: Namespace) -> None: console.print( "Red requires all Privileged Intents to be enabled.\n" "You can find out how to enable Privileged Intents with this guide:\n" - "https://docs.discord.red/en/stable/bot_application_guide.html#enabling-privileged-intents", + "https://docs.discord.red" + "/en/stable/bot_application_guide.html#enabling-privileged-intents", style="red", ) sys.exit(ExitCodes.CONFIGURATION_ERROR) @@ -438,11 +437,12 @@ async def shutdown_handler(red, signal_type=None, exit_code=None): finally: # Then cancels all outstanding tasks other than ourselves pending = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()] - [task.cancel() for task in pending] + for task in pending: + task.cancel() await asyncio.gather(*pending, return_exceptions=True) -def global_exception_handler(red, loop, context): +def global_exception_handler(_loop, context): """ Logs unhandled exceptions in other tasks """ @@ -493,8 +493,8 @@ def main(): if cli_flags.no_instance: print( "\033[1m" - "Warning: The data will be placed in a temporary folder and removed on next system " - "reboot." + "Warning: The data will be placed in a temporary folder and removed on next system" + " reboot." "\033[0m" ) cli_flags.instance_name = "temporary_red" @@ -513,8 +513,7 @@ def main(): s, lambda s=s: asyncio.create_task(shutdown_handler(red, s)) ) - exc_handler = functools.partial(global_exception_handler, red) - loop.set_exception_handler(exc_handler) + loop.set_exception_handler(global_exception_handler) # We actually can't (just) use asyncio.run here # We probably could if we didn't support windows, but we might run into # a scenario where this isn't true if anyone works on RPC more in the future diff --git a/redbot/cogs/admin/admin.py b/redbot/cogs/admin/admin.py index 403180830f0..303c9fdfa92 100644 --- a/redbot/cogs/admin/admin.py +++ b/redbot/cogs/admin/admin.py @@ -3,6 +3,7 @@ from typing import Tuple, Union import discord + from redbot.core import Config, checks, commands from redbot.core.i18n import Translator, cog_i18n from redbot.core.utils.chat_formatting import box @@ -89,7 +90,7 @@ def __init__(self, bot): async def cog_load(self) -> None: await self.handle_migrations() - async def red_delete_data_for_user(self, **kwargs): + async def red_delete_data_for_user(self, **_kwargs): """Nothing to delete""" return @@ -254,7 +255,6 @@ async def removerole( @checks.admin_or_permissions(manage_roles=True) async def editrole(self, ctx: commands.Context): """Edit role settings.""" - pass @editrole.command(name="colour", aliases=["color"]) async def editrole_colour( @@ -353,7 +353,6 @@ async def announce_cancel(self, ctx): @checks.guildowner_or_permissions(administrator=True) async def announceset(self, ctx): """Change how announcements are sent in this guild.""" - pass @announceset.command(name="channel") async def announceset_channel( @@ -444,7 +443,6 @@ async def selfrole_list(self, ctx: commands.Context): @checks.admin_or_permissions(manage_roles=True) async def selfroleset(self, ctx: commands.Context): """Manage selfroles.""" - pass @selfroleset.command(name="add") async def selfroleset_add(self, ctx: commands.Context, *roles: discord.Role): @@ -491,7 +489,9 @@ async def selfroleset_remove(self, ctx: commands.Context, *roles: SelfRole): if not self.pass_user_hierarchy_check(ctx, role): await ctx.send( _( - "I cannot let you remove {role.name} from being a selfrole because that role is higher than or equal to your highest role in the Discord hierarchy." + "I cannot let you remove {role.name} from being a selfrole" + " because that role is higher than or equal to your highest role" + " in the Discord hierarchy." ).format(role=role) ) return @@ -531,7 +531,9 @@ async def selfroleset_clear(self, ctx: commands.Context): if not self.pass_user_hierarchy_check(ctx, role): await ctx.send( _( - "I cannot clear the selfroles because the selfrole '{role.name}' is higher than or equal to your highest role in the Discord hierarchy." + "I cannot clear the selfroles because the selfrole" + " '{role.name}' is higher than or equal to your highest role" + " in the Discord hierarchy." ).format(role=role) ) return @@ -558,11 +560,14 @@ async def on_guild_join(self, guild: discord.Guild): if await self.config.serverlocked(): if len(self.bot.guilds) == 1: # will be 0 once left log.warning( - f"Leaving guild '{guild.name}' ({guild.id}) due to serverlock. You can " - "temporarily disable serverlock by starting up the bot with the --no-cogs flag." + "Leaving guild %r (%r) due to serverlock. You can" + " temporarily disable serverlock by starting up the bot with" + " the --no-cogs flag.", + guild.name, + guild.id, ) else: - log.info(f"Leaving guild '{guild.name}' ({guild.id}) due to serverlock.") + log.info("Leaving guild %r (%r) due to serverlock.", guild.name, guild.id) await guild.leave() diff --git a/redbot/cogs/admin/announcer.py b/redbot/cogs/admin/announcer.py index 60ba497f70b..7b618ac22ec 100644 --- a/redbot/cogs/admin/announcer.py +++ b/redbot/cogs/admin/announcer.py @@ -2,6 +2,7 @@ from typing import Optional import discord + from redbot.core import commands from redbot.core.i18n import Translator from redbot.core.utils import AsyncIter @@ -26,7 +27,6 @@ def __init__(self, ctx: commands.Context, message: str, config=None): def start(self): """ Starts an announcement. - :return: """ if self.active is None: self.active = True @@ -35,7 +35,6 @@ def start(self): def cancel(self): """ Cancels a running announcement. - :return: """ self.active = False diff --git a/redbot/cogs/admin/converters.py b/redbot/cogs/admin/converters.py index 54955893439..e1311aa8e20 100644 --- a/redbot/cogs/admin/converters.py +++ b/redbot/cogs/admin/converters.py @@ -1,4 +1,5 @@ import discord + from redbot.core import commands from redbot.core.i18n import Translator from redbot.core.utils import AsyncIter diff --git a/redbot/cogs/alias/__init__.py b/redbot/cogs/alias/__init__.py index c7784cbd140..3b92c70ddf3 100644 --- a/redbot/cogs/alias/__init__.py +++ b/redbot/cogs/alias/__init__.py @@ -1,6 +1,7 @@ -from .alias import Alias from redbot.core.bot import Red +from .alias import Alias + async def setup(bot: Red) -> None: await bot.add_cog(Alias(bot)) diff --git a/redbot/cogs/alias/alias.py b/redbot/cogs/alias/alias.py index 5c63187416a..02b3549e4ed 100644 --- a/redbot/cogs/alias/alias.py +++ b/redbot/cogs/alias/alias.py @@ -3,16 +3,17 @@ from copy import copy from re import search from string import Formatter -from typing import Dict, List, Literal +from typing import List, Literal import discord -from redbot.core import Config, commands, checks + +from redbot.core import Config, checks, commands +from redbot.core.bot import Red from redbot.core.i18n import Translator, cog_i18n from redbot.core.utils.chat_formatting import box, pagify from redbot.core.utils.menus import menu -from redbot.core.bot import Red -from .alias_entry import AliasEntry, AliasCache, ArgParseError +from .alias_entry import AliasCache, AliasEntry, ArgParseError _ = Translator("Alias", __file__) @@ -143,7 +144,7 @@ async def get_prefix(self, message: discord.Message) -> str: """ content = message.content prefix_list = await self.bot.command_prefix(self.bot, message) - prefixes = sorted(prefix_list, key=lambda pfx: len(pfx), reverse=True) + prefixes = sorted(prefix_list, key=len, reverse=True) for p in prefixes: if content.startswith(p): return p @@ -190,12 +191,10 @@ async def paginate_alias_list( @commands.group() async def alias(self, ctx: commands.Context): """Manage command aliases.""" - pass @alias.group(name="global") async def global_(self, ctx: commands.Context): """Manage global aliases.""" - pass @checks.mod_or_permissions(manage_guild=True) @alias.command(name="add") @@ -339,7 +338,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 +371,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/alias/alias_entry.py b/redbot/cogs/alias/alias_entry.py index 49cf224ab6b..23d88e00c34 100644 --- a/redbot/cogs/alias/alias_entry.py +++ b/redbot/cogs/alias/alias_entry.py @@ -1,9 +1,10 @@ -from typing import Tuple, Dict, Optional, List, Union from re import findall +from typing import Dict, List, Optional, Tuple, Union import discord from discord.ext.commands.view import StringView # DEP-WARN -from redbot.core import commands, Config + +from redbot.core import Config, commands from redbot.core.i18n import Translator from redbot.core.utils import AsyncIter @@ -49,7 +50,6 @@ def get_extra_args_from_alias(self, message: discord.Message, prefix: str) -> st Whitespace will be trimmed from both ends. :param message: :param prefix: - :param alias: :return: """ known_content_length = len(prefix) + len(self.name) @@ -194,7 +194,7 @@ def format_command_for_alias(command: str) -> str: try: indices = [int(a[0]) for a in indices] except IndexError: - raise ArgParseError(_("Arguments must be specified with a number.")) + raise ArgParseError(_("Arguments must be specified with a number.")) from None low = min(indices) indices = [a - low for a in indices] high = max(indices) diff --git a/redbot/cogs/audio/apis/__init__.py b/redbot/cogs/audio/apis/__init__.py index 2203547a287..e9b764619df 100644 --- a/redbot/cogs/audio/apis/__init__.py +++ b/redbot/cogs/audio/apis/__init__.py @@ -8,3 +8,14 @@ spotify, youtube, ) + +__all__ = ( + "api_utils", + "global_db", + "interface", + "local_db", + "playlist_interface", + "playlist_wrapper", + "spotify", + "youtube", +) diff --git a/redbot/cogs/audio/apis/global_db.py b/redbot/cogs/audio/apis/global_db.py index 732fec4209e..464bfde04a4 100644 --- a/redbot/cogs/audio/apis/global_db.py +++ b/redbot/cogs/audio/apis/global_db.py @@ -1,7 +1,6 @@ import asyncio import contextlib import json - from copy import copy from pathlib import Path from typing import TYPE_CHECKING, Mapping, Optional, Union diff --git a/redbot/cogs/audio/apis/interface.py b/redbot/cogs/audio/apis/interface.py index 1fa55ab0f93..aee1ace69ea 100644 --- a/redbot/cogs/audio/apis/interface.py +++ b/redbot/cogs/audio/apis/interface.py @@ -4,7 +4,6 @@ import json import random import time - from collections import namedtuple from pathlib import Path from typing import TYPE_CHECKING, Callable, List, MutableMapping, Optional, Tuple, Union, cast @@ -12,9 +11,9 @@ import aiohttp import discord import lavalink +from lavalink.rest_api import LoadResult, LoadType from red_commons.logging import getLogger -from lavalink.rest_api import LoadResult, LoadType from redbot.core import Config, commands from redbot.core.bot import Red from redbot.core.commands import Cog, Context @@ -81,6 +80,7 @@ def close(self) -> None: async def get_random_track_from_db(self, tries=0) -> Optional[MutableMapping]: """Get a random track from the local database and return it.""" + del tries track: Optional[MutableMapping] = {} try: query_data = {} @@ -166,7 +166,7 @@ async def run_all_pending_tasks(self) -> None: log.trace("Running pending writes to database") try: tasks: MutableMapping = {"update": [], "insert": [], "global": []} - async for k, task in AsyncIter(self._tasks.items()): + async for task in AsyncIter(self._tasks.values()): async for t, args in AsyncIter(task.items()): tasks[t].append(args) self._tasks = {} @@ -288,7 +288,7 @@ async def fetch_from_spotify_api( ctx: Context = None, ) -> Union[List[MutableMapping], List[str]]: """Gets track info from spotify API.""" - + del ctx if recursive is False: (call, params) = self.spotify_api.spotify_format_call(query_type, uri) results = await self.spotify_api.make_get_call(call, params) @@ -343,7 +343,7 @@ async def fetch_from_spotify_api( except KeyError: raise SpotifyFetchError( _("This doesn't seem to be a valid Spotify playlist/album URL or code.") - ) + ) from None return tracks async def spotify_query( @@ -847,7 +847,7 @@ async def fetch_track( valid_global_entry = True if valid_global_entry: log.trace("Querying Global DB api for %r", query) - results, called_api = results, False + called_api = False if valid_global_entry: pass elif lazy is True: @@ -870,8 +870,8 @@ async def fetch_track( results = await player.load_tracks(query_string) except KeyError: results = None - except RuntimeError: - raise TrackEnqueueError + except RuntimeError as exc: + raise TrackEnqueueError from exc if results is None: results = LoadResult({"loadType": "LOAD_FAILED", "playlistInfo": {}, "tracks": []}) valid_global_entry = False diff --git a/redbot/cogs/audio/apis/local_db.py b/redbot/cogs/audio/apis/local_db.py index 8d5ee91e149..8e5228eba24 100644 --- a/redbot/cogs/audio/apis/local_db.py +++ b/redbot/cogs/audio/apis/local_db.py @@ -177,7 +177,7 @@ async def _fetch_one( return None if self.fetch_result is None: return None - return self.fetch_result(*row) + return self.fetch_result(*row) # pylint: disable=not-callable async def _fetch_all( self, values: MutableMapping @@ -196,7 +196,7 @@ async def _fetch_all( except Exception as exc: log.verbose("Failed to completed fetch from database", exc_info=exc) async for row in AsyncIter(row_result): - output.append(self.fetch_result(*row)) + output.append(self.fetch_result(*row)) # pylint: disable=not-callable return output async def _fetch_random( @@ -227,7 +227,7 @@ async def _fetch_random( return None if self.fetch_result is None: return None - return self.fetch_result(*row) + return self.fetch_result(*row) # pylint: disable=not-callable class YouTubeTableWrapper(BaseWrapper): diff --git a/redbot/cogs/audio/apis/persist_queue_wrapper.py b/redbot/cogs/audio/apis/persist_queue_wrapper.py index acd89a55c90..5f97f2fe778 100644 --- a/redbot/cogs/audio/apis/persist_queue_wrapper.py +++ b/redbot/cogs/audio/apis/persist_queue_wrapper.py @@ -2,7 +2,6 @@ import json import time from pathlib import Path - from types import SimpleNamespace from typing import TYPE_CHECKING, List, Union @@ -92,7 +91,7 @@ async def fetch_all(self) -> List[QueueFetchResult]: log.verbose("Failed to complete playlist fetch from database", exc_info=exc) return [] - async for index, row in AsyncIter(row_result).enumerate(start=1): + async for row in AsyncIter(row_result): output.append(QueueFetchResult(*row)) return output diff --git a/redbot/cogs/audio/apis/playlist_interface.py b/redbot/cogs/audio/apis/playlist_interface.py index 356e13ec15f..353b43d134e 100644 --- a/redbot/cogs/audio/apis/playlist_interface.py +++ b/redbot/cogs/audio/apis/playlist_interface.py @@ -1,5 +1,4 @@ from pathlib import Path - from typing import List, MutableMapping, Optional, Union import discord diff --git a/redbot/cogs/audio/apis/playlist_wrapper.py b/redbot/cogs/audio/apis/playlist_wrapper.py index 090dbaac3d1..9b63af8605a 100644 --- a/redbot/cogs/audio/apis/playlist_wrapper.py +++ b/redbot/cogs/audio/apis/playlist_wrapper.py @@ -1,7 +1,6 @@ import concurrent import json from pathlib import Path - from types import SimpleNamespace from typing import List, MutableMapping, Optional diff --git a/redbot/cogs/audio/apis/spotify.py b/redbot/cogs/audio/apis/spotify.py index 05b8d74a468..bceb6556236 100644 --- a/redbot/cogs/audio/apis/spotify.py +++ b/redbot/cogs/audio/apis/spotify.py @@ -3,7 +3,6 @@ import json import time from pathlib import Path - from typing import TYPE_CHECKING, List, Mapping, MutableMapping, Optional, Tuple, Union import aiohttp diff --git a/redbot/cogs/audio/apis/youtube.py b/redbot/cogs/audio/apis/youtube.py index 93bb3943845..4f7f544bb01 100644 --- a/redbot/cogs/audio/apis/youtube.py +++ b/redbot/cogs/audio/apis/youtube.py @@ -1,6 +1,5 @@ import json from pathlib import Path - from typing import TYPE_CHECKING, Mapping, Optional, Union import aiohttp diff --git a/redbot/cogs/audio/audio_dataclasses.py b/redbot/cogs/audio/audio_dataclasses.py index 221d9734062..da2aa0ad3f9 100644 --- a/redbot/cogs/audio/audio_dataclasses.py +++ b/redbot/cogs/audio/audio_dataclasses.py @@ -4,7 +4,6 @@ import os import posixpath import re - from pathlib import Path, PosixPath, WindowsPath from typing import ( AsyncIterator, @@ -92,6 +91,7 @@ class LocalPath: _all_music_ext = _FULLY_SUPPORTED_MUSIC_EXT + _PARTIALLY_SUPPORTED_MUSIC_EXT def __init__(self, path, localtrack_folder, **kwargs): + self._hash = None self._localtrack_folder = localtrack_folder self._path = path if isinstance(path, (Path, WindowsPath, PosixPath, LocalPath)): @@ -283,11 +283,9 @@ def __eq__(self, other): return NotImplemented def __hash__(self): - try: - return self._hash - except AttributeError: + if self._hash is None: self._hash = hash(tuple(self.path._cparts)) - return self._hash + return self._hash def __lt__(self, other): if isinstance(other, LocalPath): @@ -405,7 +403,7 @@ def __str__(self): def process_input( cls, query: Union[LocalPath, lavalink.Track, "Query", str], - _local_folder_current_path: Path, + local_folder_current_path: Path, **kwargs, ) -> "Query": """Process the input query into its type. @@ -414,8 +412,10 @@ def process_input( ---------- query : Union[Query, LocalPath, lavalink.Track, str] The query string or LocalPath object. - _local_folder_current_path: Path + local_folder_current_path: Path The Current Local Track folder + **kwargs + Keyword arguments that will be passed through to Query constructor after parsing. Returns ------- Query @@ -441,11 +441,11 @@ def process_input( query = query.uri possible_values.update(dict(**kwargs)) - possible_values.update(cls._parse(query, _local_folder_current_path, **kwargs)) - return cls(query, _local_folder_current_path, **possible_values) + possible_values.update(cls._parse(query, local_folder_current_path, **kwargs)) + return cls(query, local_folder_current_path, **possible_values) @staticmethod - def _parse(track, _local_folder_current_path: Path, **kwargs) -> MutableMapping: + def _parse(track, local_folder_current_path: Path, **kwargs) -> MutableMapping: """Parse a track into all the relevant metadata.""" returning: MutableMapping = {} if ( @@ -487,7 +487,7 @@ def _parse(track, _local_folder_current_path: Path, **kwargs) -> MutableMapping: track = _RE_REMOVE_START.sub("", track, 1) returning["queryforced"] = track - _localtrack = LocalPath(track, _local_folder_current_path) + _localtrack = LocalPath(track, local_folder_current_path) if _localtrack.exists(): if _localtrack.is_file(): returning["local"] = True diff --git a/redbot/cogs/audio/converters.py b/redbot/cogs/audio/converters.py index bb6180752c7..fc95ad87965 100644 --- a/redbot/cogs/audio/converters.py +++ b/redbot/cogs/audio/converters.py @@ -2,7 +2,6 @@ import functools import re from pathlib import Path - from typing import Final, MutableMapping, Optional, Pattern, Tuple, Union import discord @@ -35,24 +34,24 @@ _SCOPE_HELP: Final[str] = _( """ Scope must be a valid version of one of the following: -​ ​ ​ ​ Global -​ ​ ​ ​ Guild -​ ​ ​ ​ User +\u200b \u200b \u200b \u200b Global +\u200b \u200b \u200b \u200b Guild +\u200b \u200b \u200b \u200b User """ ) _USER_HELP: Final[str] = _( """ Author must be a valid version of one of the following: -​ ​ ​ ​ User ID -​ ​ ​ ​ User Mention -​ ​ ​ ​ User Name#123 +\u200b \u200b \u200b \u200b User ID +\u200b \u200b \u200b \u200b User Mention +\u200b \u200b \u200b \u200b User Name#123 """ ) _GUILD_HELP: Final[str] = _( """ Guild must be a valid version of one of the following: -​ ​ ​ ​ Guild ID -​ ​ ​ ​ Exact guild name +\u200b \u200b \u200b \u200b Guild ID +\u200b \u200b \u200b \u200b Exact guild name """ ) diff --git a/redbot/cogs/audio/core/__init__.py b/redbot/cogs/audio/core/__init__.py index d78d4c56baa..8ce3f6802bc 100644 --- a/redbot/cogs/audio/core/__init__.py +++ b/redbot/cogs/audio/core/__init__.py @@ -1,10 +1,9 @@ import asyncio import datetime import json - from collections import Counter, defaultdict from pathlib import Path -from typing import Mapping, Dict +from typing import Dict, Mapping import aiohttp import discord @@ -17,16 +16,18 @@ from redbot.core.utils.antispam import AntiSpam from ..utils import ( + DEFAULT_LAVALINK_SETTINGS, + DEFAULT_LAVALINK_YAML, CacheLevel, PlaylistScope, - DEFAULT_LAVALINK_YAML, - DEFAULT_LAVALINK_SETTINGS, ) from . import abc, cog_utils, commands, events, tasks, utilities from .cog_utils import CompositeMetaClass _ = Translator("Audio", Path(__file__)) +__all__ = ("abc", "cog_utils", "commands", "events", "tasks", "utilities", "Audio") + @cog_i18n(_) class Audio( diff --git a/redbot/cogs/audio/core/abc.py b/redbot/cogs/audio/core/abc.py index 66483f974ee..8d7415c96c7 100644 --- a/redbot/cogs/audio/core/abc.py +++ b/redbot/cogs/audio/core/abc.py @@ -2,21 +2,20 @@ import asyncio import datetime - from abc import ABC, abstractmethod from collections import Counter, defaultdict from pathlib import Path from typing import ( - Set, TYPE_CHECKING, Any, + Dict, List, Mapping, MutableMapping, Optional, + Set, Tuple, Union, - Dict, ) import aiohttp diff --git a/redbot/cogs/audio/core/cog_utils.py b/redbot/cogs/audio/core/cog_utils.py index 9b5f56dcfe2..eb776b64887 100644 --- a/redbot/cogs/audio/core/cog_utils.py +++ b/redbot/cogs/audio/core/cog_utils.py @@ -1,8 +1,8 @@ +import struct from abc import ABC -from typing import Final from base64 import b64decode from io import BytesIO -import struct +from typing import Final from redbot import VersionInfo from redbot.core import commands @@ -13,7 +13,6 @@ __author__ = ["aikaterna", "Draper"] _SCHEMA_VERSION: Final[int] = 3 -_OWNER_NOTIFICATION: Final[int] = 1 LazyGreedyConverter = get_lazy_converter("--") PlaylistConverter = get_playlist_converter() @@ -25,8 +24,6 @@ class CompositeMetaClass(type(commands.Cog), type(ABC)): coexist with discord.py's metaclass """ - pass - # Both DataReader and DataWriter are taken from https://github.com/Devoxin/Lavalink.py/blob/master/lavalink/datarw.py # These are licenced under MIT, Thanks Devoxin for putting these together! @@ -86,8 +83,8 @@ def write_int(self, i): enc = struct.pack(">i", i) self._write(enc) - def write_long(self, l): - enc = struct.pack(">Q", l) + def write_long(self, long): + enc = struct.pack(">Q", long) self._write(enc) def write_utf(self, s): diff --git a/redbot/cogs/audio/core/commands/audioset.py b/redbot/cogs/audio/core/commands/audioset.py index 2ad0e0bbf61..d13c863c119 100644 --- a/redbot/cogs/audio/core/commands/audioset.py +++ b/redbot/cogs/audio/core/commands/audioset.py @@ -3,7 +3,6 @@ import os import tarfile from pathlib import Path - from typing import Union import discord @@ -464,32 +463,32 @@ async def command_audioset_autoplay_playlist( """Set a playlist to auto-play songs from. **Usage**: - ​ ​ ​ ​ `[p]audioset autoplay playlist playlist_name_OR_id [args]` + \u200b \u200b \u200b \u200b `[p]audioset autoplay playlist playlist_name_OR_id [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200bGlobal + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]audioset autoplay playlist MyGuildPlaylist` - ​ ​ ​ ​ `[p]audioset autoplay playlist MyGlobalPlaylist --scope Global` - ​ ​ ​ ​ `[p]audioset autoplay playlist PersonalPlaylist --scope User --author Draper` + \u200b \u200b \u200b \u200b `[p]audioset autoplay playlist MyGuildPlaylist` + \u200b \u200b \u200b \u200b `[p]audioset autoplay playlist MyGlobalPlaylist --scope Global` + \u200b \u200b \u200b \u200b `[p]audioset autoplay playlist PersonalPlaylist --scope User --author Draper` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -1249,7 +1248,7 @@ async def command_audioset_vote(self, ctx: commands.Context, percent: int): async def command_audioset_youtubeapi(self, ctx: commands.Context): """Instructions to set the YouTube API key.""" message = _( - f"1. Go to Google Developers Console and log in with your Google account.\n" + "1. Go to Google Developers Console and log in with your Google account.\n" "(https://console.developers.google.com/)\n" "2. You should be prompted to create a new project (name does not matter).\n" "3. Click on Enable APIs and Services at the top.\n" diff --git a/redbot/cogs/audio/core/commands/controller.py b/redbot/cogs/audio/core/commands/controller.py index 8cc9714697b..e4bdd6889a7 100644 --- a/redbot/cogs/audio/core/commands/controller.py +++ b/redbot/cogs/audio/core/commands/controller.py @@ -2,7 +2,6 @@ import contextlib import time from pathlib import Path - from typing import Optional, Union import discord @@ -88,10 +87,19 @@ async def command_now(self, ctx: commands.Context): if not self._player_check(ctx): return await self.send_embed_msg(ctx, title=_("Nothing playing.")) emoji = { - "prev": "\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\N{VARIATION SELECTOR-16}", + "prev": ( + "\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}" + "\N{VARIATION SELECTOR-16}" + ), "stop": "\N{BLACK SQUARE FOR STOP}\N{VARIATION SELECTOR-16}", - "pause": "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}", - "next": "\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\N{VARIATION SELECTOR-16}", + "pause": ( + "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}" + "\N{VARIATION SELECTOR-16}" + ), + "next": ( + "\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}" + "\N{VARIATION SELECTOR-16}" + ), "close": "\N{CROSS MARK}", } expected = tuple(emoji.values()) diff --git a/redbot/cogs/audio/core/commands/llset.py b/redbot/cogs/audio/core/commands/llset.py index 4e427dec68f..33fd50db5f8 100644 --- a/redbot/cogs/audio/core/commands/llset.py +++ b/redbot/cogs/audio/core/commands/llset.py @@ -12,18 +12,18 @@ from redbot.core.i18n import Translator from redbot.core.utils.chat_formatting import box, inline -from ..abc import MixinMeta -from ..cog_utils import CompositeMetaClass from ...utils import ( - MAX_JAVA_RAM, - DEFAULT_LAVALINK_YAML, DEFAULT_LAVALINK_SETTINGS, + DEFAULT_LAVALINK_YAML, + MAX_JAVA_RAM, change_dict_naming_convention, + get_max_allocation_size, has_managed_server, has_unmanaged_server, sizeof_fmt, - get_max_allocation_size, ) +from ..abc import MixinMeta +from ..cog_utils import CompositeMetaClass log = getLogger("red.cogs.Audio.cog.Commands.lavalink_setup") _ = Translator("Audio", Path(__file__)) @@ -292,7 +292,7 @@ async def command_llset_secured(self, ctx: commands.Context): title=_("Setting Changed"), description=_( "External Lavalink node will no longer connect using the secured " - "{secured_protocol} protocol and wil use {unsecured_protocol} instead .\n\n" + "{secured_protocol} protocol and will 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), unsecured_protocol=inline("ws://"), @@ -343,7 +343,7 @@ async def command_llset_yaml(self, ctx: commands.Context): to_write.write(playlist_data) to_write.seek(0) datapath = cog_data_path(raw_name="Audio") - temp_file = datapath / f"application.dump.yaml" + temp_file = datapath / "application.dump.yaml" try: with temp_file.open("wb") as application_file: application_file.write(to_write.read()) diff --git a/redbot/cogs/audio/core/commands/localtracks.py b/redbot/cogs/audio/core/commands/localtracks.py index f1dd483e0c4..d18bc5d1830 100644 --- a/redbot/cogs/audio/core/commands/localtracks.py +++ b/redbot/cogs/audio/core/commands/localtracks.py @@ -31,11 +31,11 @@ async def command_local_folder(self, ctx: commands.Context, *, folder: str = Non """Play all songs in a localtracks folder. **Usage**: - ​ ​ ​ ​ `[p]local folder` - ​ ​ ​ ​ ​ ​ ​ ​ Open a menu to pick a folder to queue. + \u200b \u200b \u200b \u200b `[p]local folder` + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b Open a menu to pick a folder to queue. - ​ ​ `[p]local folder folder_name` - ​ ​ ​ ​ ​ ​ ​ ​ Queues all of the tracks inside the folder_name folder. + \u200b \u200b `[p]local folder folder_name` + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b Queues all of the tracks inside the folder_name folder. """ if not await self.localtracks_folder_exists(ctx): return @@ -66,12 +66,12 @@ async def command_local_play(self, ctx: commands.Context): To play an entire folder, use `[p]help local folder` for instructions. **Usage**: - ​ ​ ​ ​ `[p]local play` - ​ ​ ​ ​ ​ ​ ​ ​ Open a menu to pick a track. + \u200b \u200b \u200b \u200b `[p]local play` + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b Open a menu to pick a track. - ​ ​ ​ ​ `[p]play localtracks\\album_folder\\song_name.mp3` - ​ ​ ​ ​ `[p]play album_folder\\song_name.mp3` - ​ ​ ​ ​ ​ ​ ​ ​ Use a direct link relative to the localtracks folder. + \u200b \u200b \u200b \u200b `[p]play localtracks\\album_folder\\song_name.mp3` + \u200b \u200b \u200b \u200b `[p]play album_folder\\song_name.mp3` + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b Use a direct link relative to the localtracks folder. """ if not await self.localtracks_folder_exists(ctx): return @@ -94,6 +94,7 @@ async def _local_folder_menu( timeout: float, emoji: str, ): + del pages, controls, timeout if message: with contextlib.suppress(discord.HTTPException): await message.delete() diff --git a/redbot/cogs/audio/core/commands/player.py b/redbot/cogs/audio/core/commands/player.py index a925c7a4632..59171f1448b 100644 --- a/redbot/cogs/audio/core/commands/player.py +++ b/redbot/cogs/audio/core/commands/player.py @@ -2,14 +2,12 @@ import math import time from pathlib import Path - from typing import MutableMapping import discord import lavalink -from red_commons.logging import getLogger - from lavalink import NodeNotFound +from red_commons.logging import getLogger from redbot.core import commands from redbot.core.commands import UserInputOptional @@ -363,6 +361,7 @@ async def _category_search_menu( timeout: float, emoji: str, ): + del pages, controls, timeout if message: output = await self._genre_search_button_action(ctx, category_list, emoji, page) with contextlib.suppress(discord.HTTPException): @@ -378,6 +377,7 @@ async def _playlist_search_menu( timeout: float, emoji: str, ): + del pages, controls, timeout if message: output = await self._genre_search_button_action( ctx, playlists_list, emoji, page, playlist=True @@ -646,7 +646,8 @@ async def command_search(self, ctx: commands.Context, *, query: str): if not isinstance(query, (str, list, Query)): raise RuntimeError( - f"Expected 'query' to be a string, list or Query object but received: {type(query)} - this is an unexpected argument type, please report it." + "Expected 'query' to be a string, list or Query object but received:" + f" {type(query)} - this is an unexpected argument type, please report it." ) async def _search_menu( @@ -658,6 +659,7 @@ async def _search_menu( timeout: float, emoji: str, ): + del pages, controls, timeout if message: await self._search_button_action(ctx, tracks, emoji, page) with contextlib.suppress(discord.HTTPException): diff --git a/redbot/cogs/audio/core/commands/playlists.py b/redbot/cogs/audio/core/commands/playlists.py index 746b4588193..603de3bb95b 100644 --- a/redbot/cogs/audio/core/commands/playlists.py +++ b/redbot/cogs/audio/core/commands/playlists.py @@ -4,7 +4,6 @@ import os import tarfile import time - from io import BytesIO from pathlib import Path from typing import cast @@ -44,15 +43,15 @@ async def command_playlist(self, ctx: commands.Context): """Playlist configuration options. Scope info: - ​ ​ ​ ​ **Global**: - ​ ​ ​ ​ ​ ​ ​ ​ Visible to all users of this bot. - ​ ​ ​ ​ ​ ​ ​ ​ Only editable by bot owner. - ​ ​ ​ ​ **Guild**: - ​ ​ ​ ​ ​ ​ ​ ​ Visible to all users in this guild. - ​ ​ ​ ​ ​ ​ ​ ​ Editable by bot owner, guild owner, guild admins, guild mods, DJ role and playlist creator. - ​ ​ ​ ​ **User**: - ​ ​ ​ ​ ​ ​ ​ ​ Visible to all bot users, if --author is passed. - ​ ​ ​ ​ ​ ​ ​ ​ Editable by bot owner and the playlist creator. + \u200b \u200b \u200b \u200b **Global**: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b Visible to all users of this bot. + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b Only editable by bot owner. + \u200b \u200b \u200b \u200b **Guild**: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b Visible to all users in this guild. + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b Editable by bot owner, guild owner, guild admins, guild mods, DJ role and playlist creator. + \u200b \u200b \u200b \u200b **User**: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b Visible to all bot users, if --author is passed. + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b Editable by bot owner and the playlist creator. """ @command_playlist.command( @@ -71,32 +70,32 @@ async def command_playlist_append( The track(s) will be appended to the end of the playlist. **Usage**: - ​ ​ ​ ​ `[p]playlist append playlist_name_OR_id track_name_OR_url [args]` + \u200b \u200b \u200b \u200b `[p]playlist append playlist_name_OR_id track_name_OR_url [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist append MyGuildPlaylist Hello by Adele` - ​ ​ ​ ​ `[p]playlist append MyGlobalPlaylist Hello by Adele --scope Global` - ​ ​ ​ ​ `[p]playlist append MyGlobalPlaylist Hello by Adele --scope Global --Author Draper#6666` + \u200b \u200b \u200b \u200b `[p]playlist append MyGuildPlaylist Hello by Adele` + \u200b \u200b \u200b \u200b `[p]playlist append MyGlobalPlaylist Hello by Adele --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist append MyGlobalPlaylist Hello by Adele --scope Global --Author Draper#6666` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -228,36 +227,36 @@ async def command_playlist_copy( """Copy a playlist from one scope to another. **Usage**: - ​ ​ ​ ​ `[p]playlist copy playlist_name_OR_id [args]` + \u200b \u200b \u200b \u200b `[p]playlist copy playlist_name_OR_id [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --from-scope - ​ ​ ​ ​ ​ ​ ​ ​ --from-author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --from-guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --from-scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --from-author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --from-guild [guild] **Only the bot owner can use this** - ​ ​ ​ ​ ​ ​ ​ ​ --to-scope - ​ ​ ​ ​ ​ ​ ​ ​ --to-author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --to-guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --to-scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --to-author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --to-guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist copy MyGuildPlaylist --from-scope Guild --to-scope Global` - ​ ​ ​ ​ `[p]playlist copy MyGlobalPlaylist --from-scope Global --to-author Draper#6666 --to-scope User` - ​ ​ ​ ​ `[p]playlist copy MyPersonalPlaylist --from-scope user --to-author Draper#6666 --to-scope Guild --to-guild Red - Discord Bot` + \u200b \u200b \u200b \u200b `[p]playlist copy MyGuildPlaylist --from-scope Guild --to-scope Global` + \u200b \u200b \u200b \u200b `[p]playlist copy MyGlobalPlaylist --from-scope Global --to-author Draper#6666 --to-scope User` + \u200b \u200b \u200b \u200b `[p]playlist copy MyPersonalPlaylist --from-scope user --to-author Draper#6666 --to-scope Guild --to-guild Red - Discord Bot` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -342,7 +341,8 @@ async def command_playlist_copy( ctx, title=_("Playlist Copied"), description=_( - "Playlist {name} (`{from_id}`) copied from {from_scope} to {to_scope} (`{to_id}`)." + "Playlist {name} (`{from_id}`) copied from {from_scope}" + " to {to_scope} (`{to_id}`)." ).format( name=from_playlist.name, from_id=from_playlist.id, @@ -359,32 +359,32 @@ async def command_playlist_create( """Create an empty playlist. **Usage**: - ​ ​ ​ ​ `[p]playlist create playlist_name [args]` + \u200b \u200b \u200b \u200b `[p]playlist create playlist_name [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist create MyGuildPlaylist` - ​ ​ ​ ​ `[p]playlist create MyGlobalPlaylist --scope Global` - ​ ​ ​ ​ `[p]playlist create MyPersonalPlaylist --scope User` + \u200b \u200b \u200b \u200b `[p]playlist create MyGuildPlaylist` + \u200b \u200b \u200b \u200b `[p]playlist create MyGlobalPlaylist --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist create MyPersonalPlaylist --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -436,32 +436,32 @@ async def command_playlist_delete( """Delete a saved playlist. **Usage**: - ​ ​ ​ ​ `[p]playlist delete playlist_name_OR_id [args]` + \u200b \u200b \u200b \u200b `[p]playlist delete playlist_name_OR_id [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist delete MyGuildPlaylist` - ​ ​ ​ ​ `[p]playlist delete MyGlobalPlaylist --scope Global` - ​ ​ ​ ​ `[p]playlist delete MyPersonalPlaylist --scope User` + \u200b \u200b \u200b \u200b `[p]playlist delete MyGuildPlaylist` + \u200b \u200b \u200b \u200b `[p]playlist delete MyGlobalPlaylist --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist delete MyPersonalPlaylist --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -524,32 +524,32 @@ async def command_playlist_remdupe( """Remove duplicate tracks from a saved playlist. **Usage**: - ​ ​ ​ ​ `[p]playlist dedupe playlist_name_OR_id [args]` + \u200b \u200b \u200b \u200b `[p]playlist dedupe playlist_name_OR_id [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist dedupe MyGuildPlaylist` - ​ ​ ​ ​ `[p]playlist dedupe MyGlobalPlaylist --scope Global` - ​ ​ ​ ​ `[p]playlist dedupe MyPersonalPlaylist --scope User` + \u200b \u200b \u200b \u200b `[p]playlist dedupe MyGuildPlaylist` + \u200b \u200b \u200b \u200b `[p]playlist dedupe MyGlobalPlaylist --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist dedupe MyPersonalPlaylist --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -657,32 +657,32 @@ async def command_playlist_download( for the v2 variable. **Usage**: - ​ ​ ​ ​ `[p]playlist download playlist_name_OR_id [v2=True_OR_False] [args]` + \u200b \u200b \u200b \u200b `[p]playlist download playlist_name_OR_id [v2=True_OR_False] [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist download MyGuildPlaylist True` - ​ ​ ​ ​ `[p]playlist download MyGlobalPlaylist False --scope Global` - ​ ​ ​ ​ `[p]playlist download MyPersonalPlaylist --scope User` + \u200b \u200b \u200b \u200b `[p]playlist download MyGuildPlaylist True` + \u200b \u200b \u200b \u200b `[p]playlist download MyGlobalPlaylist False --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist download MyPersonalPlaylist --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -788,32 +788,32 @@ async def command_playlist_info( """Retrieve information from a saved playlist. **Usage**: - ​ ​ ​ ​ `[p]playlist info playlist_name_OR_id [args]` + \u200b \u200b \u200b \u200b `[p]playlist info playlist_name_OR_id [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist info MyGuildPlaylist` - ​ ​ ​ ​ `[p]playlist info MyGlobalPlaylist --scope Global` - ​ ​ ​ ​ `[p]playlist info MyPersonalPlaylist --scope User` + \u200b \u200b \u200b \u200b `[p]playlist info MyGuildPlaylist` + \u200b \u200b \u200b \u200b `[p]playlist info MyGlobalPlaylist --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist info MyPersonalPlaylist --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -848,7 +848,7 @@ async def command_playlist_info( ) track_len = len(playlist.tracks) - msg = "​" + msg = "\u200b" if track_len > 0: spaces = "\N{EN SPACE}" * (len(str(len(playlist.tracks))) + 2) async for track_idx, track in AsyncIter(playlist.tracks).enumerate(start=1): @@ -909,32 +909,32 @@ async def command_playlist_list( """List saved playlists. **Usage**: - ​ ​ ​ ​ `[p]playlist list [args]` + \u200b \u200b \u200b \u200b `[p]playlist list [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist list` - ​ ​ ​ ​ `[p]playlist list --scope Global` - ​ ​ ​ ​ `[p]playlist list --scope User` + \u200b \u200b \u200b \u200b `[p]playlist list` + \u200b \u200b \u200b \u200b `[p]playlist list --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist list --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -1062,32 +1062,32 @@ async def command_playlist_queue( """Save the queue to a playlist. **Usage**: - ​ ​ ​ ​ `[p]playlist queue playlist_name [args]` + \u200b \u200b \u200b \u200b `[p]playlist queue playlist_name [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist queue MyGuildPlaylist` - ​ ​ ​ ​ `[p]playlist queue MyGlobalPlaylist --scope Global` - ​ ​ ​ ​ `[p]playlist queue MyPersonalPlaylist --scope User` + \u200b \u200b \u200b \u200b `[p]playlist queue MyGuildPlaylist` + \u200b \u200b \u200b \u200b `[p]playlist queue MyGlobalPlaylist --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist queue MyPersonalPlaylist --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -1170,32 +1170,32 @@ async def command_playlist_remove( """Remove a track from a playlist by url. **Usage**: - ​ ​ ​ ​ `[p]playlist remove playlist_name_OR_id url [args]` + \u200b \u200b \u200b \u200b `[p]playlist remove playlist_name_OR_id url [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist remove MyGuildPlaylist https://www.youtube.com/watch?v=MN3x-kAbgFU` - ​ ​ ​ ​ `[p]playlist remove MyGlobalPlaylist https://www.youtube.com/watch?v=MN3x-kAbgFU --scope Global` - ​ ​ ​ ​ `[p]playlist remove MyPersonalPlaylist https://www.youtube.com/watch?v=MN3x-kAbgFU --scope User` + \u200b \u200b \u200b \u200b `[p]playlist remove MyGuildPlaylist https://www.youtube.com/watch?v=MN3x-kAbgFU` + \u200b \u200b \u200b \u200b `[p]playlist remove MyGlobalPlaylist https://www.youtube.com/watch?v=MN3x-kAbgFU --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist remove MyPersonalPlaylist https://www.youtube.com/watch?v=MN3x-kAbgFU --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -1286,32 +1286,32 @@ async def command_playlist_save( """Save a playlist from a url. **Usage**: - ​ ​ ​ ​ `[p]playlist save name url [args]` + \u200b \u200b \u200b \u200b `[p]playlist save name url [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist save MyGuildPlaylist https://www.youtube.com/playlist?list=PLx0sYbCqOb8Q_CLZC2BdBSKEEB59BOPUM` - ​ ​ ​ ​ `[p]playlist save MyGlobalPlaylist https://www.youtube.com/playlist?list=PLx0sYbCqOb8Q_CLZC2BdBSKEEB59BOPUM --scope Global` - ​ ​ ​ ​ `[p]playlist save MyPersonalPlaylist https://open.spotify.com/playlist/1RyeIbyFeIJVnNzlGr5KkR --scope User` + \u200b \u200b \u200b \u200b `[p]playlist save MyGuildPlaylist https://www.youtube.com/playlist?list=PLx0sYbCqOb8Q_CLZC2BdBSKEEB59BOPUM` + \u200b \u200b \u200b \u200b `[p]playlist save MyGlobalPlaylist https://www.youtube.com/playlist?list=PLx0sYbCqOb8Q_CLZC2BdBSKEEB59BOPUM --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist save MyPersonalPlaylist https://open.spotify.com/playlist/1RyeIbyFeIJVnNzlGr5KkR --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -1410,32 +1410,32 @@ async def command_playlist_start( """Load a playlist into the queue. **Usage**: - ​ ​ ​ ​` [p]playlist start playlist_name_OR_id [args]` + \u200b \u200b \u200b \u200b` [p]playlist start playlist_name_OR_id [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist start MyGuildPlaylist` - ​ ​ ​ ​ `[p]playlist start MyGlobalPlaylist --scope Global` - ​ ​ ​ ​ `[p]playlist start MyPersonalPlaylist --scope User` + \u200b \u200b \u200b \u200b `[p]playlist start MyGuildPlaylist` + \u200b \u200b \u200b \u200b `[p]playlist start MyGlobalPlaylist --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist start MyPersonalPlaylist --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -1586,32 +1586,32 @@ async def command_playlist_update( """Updates all tracks in a playlist. **Usage**: - ​ ​ ​ ​ `[p]playlist update playlist_name_OR_id [args]` + \u200b \u200b \u200b \u200b `[p]playlist update playlist_name_OR_id [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist update MyGuildPlaylist` - ​ ​ ​ ​ `[p]playlist update MyGlobalPlaylist --scope Global` - ​ ​ ​ ​ `[p]playlist update MyPersonalPlaylist --scope User` + \u200b \u200b \u200b \u200b `[p]playlist update MyGuildPlaylist` + \u200b \u200b \u200b \u200b `[p]playlist update MyGlobalPlaylist --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist update MyPersonalPlaylist --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -1755,32 +1755,32 @@ async def command_playlist_upload( V3 Playlist made with `[p]playlist download` will load a lot faster. **Usage**: - ​ ​ ​ ​ `[p]playlist upload [args]` + \u200b \u200b \u200b \u200b `[p]playlist upload [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist upload` - ​ ​ ​ ​ `[p]playlist upload --scope Global` - ​ ​ ​ ​ `[p]playlist upload --scope User` + \u200b \u200b \u200b \u200b `[p]playlist upload` + \u200b \u200b \u200b \u200b `[p]playlist upload --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist upload --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( @@ -1896,8 +1896,8 @@ async def command_playlist_upload( ctx, title=_("Unable to Get Track"), description=_( - "I'm unable to get a track from Lavalink node at the moment, try again in a few " - "minutes." + "I'm unable to get a track from Lavalink node at the moment," + " try again in a few minutes." ), ) except Exception as e: @@ -1919,32 +1919,32 @@ async def command_playlist_rename( """Rename an existing playlist. **Usage**: - ​ ​ ​ ​ `[p]playlist rename playlist_name_OR_id new_name [args]` + \u200b \u200b \u200b \u200b `[p]playlist rename playlist_name_OR_id new_name [args]` **Args**: - ​ ​ ​ ​ The following are all optional: - ​ ​ ​ ​ ​ ​ ​ ​ --scope - ​ ​ ​ ​ ​ ​ ​ ​ --author [user] - ​ ​ ​ ​ ​ ​ ​ ​ --guild [guild] **Only the bot owner can use this** + \u200b \u200b \u200b \u200b The following are all optional: + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --scope + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --author [user] + \u200b \u200b \u200b \u200b \u200b \u200b \u200b \u200b --guild [guild] **Only the bot owner can use this** **Scope** is one of the following: - ​ ​ ​ ​ Global - ​ ​ ​ ​ Guild - ​ ​ ​ ​ User + \u200b \u200b \u200b \u200b Global + \u200b \u200b \u200b \u200b Guild + \u200b \u200b \u200b \u200b User **Author** can be one of the following: - ​ ​ ​ ​ User ID - ​ ​ ​ ​ User Mention - ​ ​ ​ ​ User Name#123 + \u200b \u200b \u200b \u200b User ID + \u200b \u200b \u200b \u200b User Mention + \u200b \u200b \u200b \u200b User Name#123 **Guild** can be one of the following: - ​ ​ ​ ​ Guild ID - ​ ​ ​ ​ Exact guild name + \u200b \u200b \u200b \u200b Guild ID + \u200b \u200b \u200b \u200b Exact guild name Example use: - ​ ​ ​ ​ `[p]playlist rename MyGuildPlaylist RenamedGuildPlaylist` - ​ ​ ​ ​ `[p]playlist rename MyGlobalPlaylist RenamedGlobalPlaylist --scope Global` - ​ ​ ​ ​ `[p]playlist rename MyPersonalPlaylist RenamedPersonalPlaylist --scope User` + \u200b \u200b \u200b \u200b `[p]playlist rename MyGuildPlaylist RenamedGuildPlaylist` + \u200b \u200b \u200b \u200b `[p]playlist rename MyGlobalPlaylist RenamedGlobalPlaylist --scope Global` + \u200b \u200b \u200b \u200b `[p]playlist rename MyPersonalPlaylist RenamedPersonalPlaylist --scope User` """ if self.playlist_api is None: return await self.send_embed_msg( diff --git a/redbot/cogs/audio/core/commands/queue.py b/redbot/cogs/audio/core/commands/queue.py index d2a51fdb2c9..350e1c2be54 100644 --- a/redbot/cogs/audio/core/commands/queue.py +++ b/redbot/cogs/audio/core/commands/queue.py @@ -2,7 +2,6 @@ import contextlib import math from pathlib import Path - from typing import MutableMapping, Optional import discord @@ -50,6 +49,7 @@ async def _queue_menu( timeout: float, emoji: str, ): + del pages, controls, page, timeout, emoji if message: await ctx.send_help(self.command_queue) with contextlib.suppress(discord.HTTPException): @@ -118,10 +118,19 @@ async def _queue_menu( return emoji = { - "prev": "\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\N{VARIATION SELECTOR-16}", + "prev": ( + "\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}" + "\N{VARIATION SELECTOR-16}" + ), "stop": "\N{BLACK SQUARE FOR STOP}\N{VARIATION SELECTOR-16}", - "pause": "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}", - "next": "\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\N{VARIATION SELECTOR-16}", + "pause": ( + "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}" + "\N{VARIATION SELECTOR-16}" + ), + "next": ( + "\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}" + "\N{VARIATION SELECTOR-16}" + ), "close": "\N{CROSS MARK}", } expected = tuple(emoji.values()) diff --git a/redbot/cogs/audio/core/events/cog.py b/redbot/cogs/audio/core/events/cog.py index 8d5acdd96de..fd58cab48e9 100644 --- a/redbot/cogs/audio/core/events/cog.py +++ b/redbot/cogs/audio/core/events/cog.py @@ -2,7 +2,6 @@ import datetime import time from pathlib import Path - from typing import Optional import discord @@ -26,6 +25,7 @@ class AudioEvents(MixinMeta, metaclass=CompositeMetaClass): async def on_red_audio_track_start( self, guild: discord.Guild, track: lavalink.Track, requester: discord.Member ): + del requester if not (track and guild): return @@ -149,6 +149,7 @@ async def on_red_audio_track_start( async def on_red_audio_queue_end( self, guild: discord.Guild, track: lavalink.Track, requester: discord.Member ): + del requester if not (track and guild): return if self.api_interface is not None and self.playlist_api is not None: @@ -163,6 +164,7 @@ async def on_red_audio_queue_end( async def on_red_audio_track_enqueue( self, guild: discord.Guild, track: lavalink.Track, requester: discord.Member ): + del requester if not (track and guild): return persist_cache = self._persist_queue_cache.setdefault( @@ -177,6 +179,7 @@ async def on_red_audio_track_enqueue( async def on_red_audio_track_end( self, guild: discord.Guild, track: lavalink.Track, requester: discord.Member ): + del requester if not (track and guild): return if self.api_interface is not None and self.playlist_api is not None: @@ -195,6 +198,7 @@ async def on_red_audio_track_auto_play( requester: discord.Member, player: lavalink.Player, ): + del track, requester if not guild: return notify_channel = guild.get_channel_or_thread(player.fetch("notify_channel")) diff --git a/redbot/cogs/audio/core/events/dpy.py b/redbot/cogs/audio/core/events/dpy.py index 0f97d65bb7d..2652b4e6d4f 100644 --- a/redbot/cogs/audio/core/events/dpy.py +++ b/redbot/cogs/audio/core/events/dpy.py @@ -2,7 +2,6 @@ import contextlib import random import re - from collections import OrderedDict from pathlib import Path from string import ascii_letters, digits @@ -10,19 +9,18 @@ import discord import lavalink -from red_commons.logging import getLogger - from aiohttp import ClientConnectorError from discord.ext.commands import CheckFailure from lavalink import NodeNotFound, PlayerNotFound +from red_commons.logging import getLogger from redbot.core import commands from redbot.core.i18n import Translator from redbot.core.utils import can_user_send_messages_in from redbot.core.utils.antispam import AntiSpam -from redbot.core.utils.chat_formatting import box, humanize_list, underline, bold +from redbot.core.utils.chat_formatting import bold, box, humanize_list, underline -from ...errors import TrackEnqueueError, AudioError +from ...errors import AudioError, TrackEnqueueError from ..abc import MixinMeta from ..cog_utils import CompositeMetaClass @@ -282,10 +280,10 @@ async def cog_before_invoke(self, ctx: commands.Context) -> None: and m.author.id == ctx.author.id, timeout=120, ) - except asyncio.TimeoutError: + except asyncio.TimeoutError as exc: with contextlib.suppress(discord.HTTPException): await sent.add_reaction("\N{CROSS MARK}") - raise commands.CheckFailure + raise commands.CheckFailure from exc else: if message.content.strip() != token: with contextlib.suppress(discord.HTTPException): diff --git a/redbot/cogs/audio/core/events/lavalink.py b/redbot/cogs/audio/core/events/lavalink.py index 2f03247382e..244a95ed68a 100644 --- a/redbot/cogs/audio/core/events/lavalink.py +++ b/redbot/cogs/audio/core/events/lavalink.py @@ -10,6 +10,7 @@ from red_commons.logging import getLogger from redbot.core.i18n import Translator, set_contextual_locales_from_guild + from ...errors import DatabaseError, TrackEnqueueError from ..abc import MixinMeta from ..cog_utils import CompositeMetaClass @@ -182,8 +183,8 @@ async def lavalink_event_handler( notify_channel, title=_("Unable to Get Track"), description=_( - "I'm unable to get a track from the Lavalink node at the moment, try again in a few " - "minutes." + "I'm unable to get a track from the Lavalink node at the moment," + " try again in a few minutes." ), ) return @@ -315,7 +316,8 @@ async def lavalink_event_handler( colour=await self.bot.get_embed_color(message_channel), title=_("Track Stuck"), description=_( - "Playback of the song has stopped due to an unexpected error.\n{error}" + "Playback of the song has stopped due to an unexpected error.\n" + "{error}" ).format(error=description), ) else: diff --git a/redbot/cogs/audio/core/events/red.py b/redbot/cogs/audio/core/events/red.py index 0ee079a2389..ee0ff24bfeb 100644 --- a/redbot/cogs/audio/core/events/red.py +++ b/redbot/cogs/audio/core/events/red.py @@ -6,6 +6,7 @@ from redbot.core import commands from redbot.core.i18n import Translator + from ..abc import MixinMeta from ..cog_utils import CompositeMetaClass diff --git a/redbot/cogs/audio/core/tasks/lavalink.py b/redbot/cogs/audio/core/tasks/lavalink.py index d3c96103082..ea5f5e10721 100644 --- a/redbot/cogs/audio/core/tasks/lavalink.py +++ b/redbot/cogs/audio/core/tasks/lavalink.py @@ -6,6 +6,7 @@ from redbot.core import data_manager from redbot.core.i18n import Translator + from ...manager import ServerManager from ..abc import MixinMeta from ..cog_utils import CompositeMetaClass @@ -31,6 +32,7 @@ def lavalink_restart_connect(self, manual: bool = False) -> None: ) async def lavalink_attempt_connect(self, timeout: int = 50, manual: bool = False) -> None: + del manual self.lavalink_connection_aborted = False max_retries = 5 retry_count = 0 diff --git a/redbot/cogs/audio/core/tasks/player.py b/redbot/cogs/audio/core/tasks/player.py index f2040171d14..40ab2271e19 100644 --- a/redbot/cogs/audio/core/tasks/player.py +++ b/redbot/cogs/audio/core/tasks/player.py @@ -1,7 +1,6 @@ import asyncio import time from pathlib import Path - from typing import Dict import lavalink diff --git a/redbot/cogs/audio/core/tasks/startup.py b/redbot/cogs/audio/core/tasks/startup.py index c38e65dd18b..f14294b1c18 100644 --- a/redbot/cogs/audio/core/tasks/startup.py +++ b/redbot/cogs/audio/core/tasks/startup.py @@ -1,7 +1,6 @@ import asyncio import itertools from pathlib import Path - from typing import Optional import lavalink diff --git a/redbot/cogs/audio/core/utilities/equalizer.py b/redbot/cogs/audio/core/utilities/equalizer.py index 303bbb56fa7..bc9f5c9c9ae 100644 --- a/redbot/cogs/audio/core/utilities/equalizer.py +++ b/redbot/cogs/audio/core/utilities/equalizer.py @@ -1,6 +1,5 @@ import asyncio import contextlib - from typing import List import discord diff --git a/redbot/cogs/audio/core/utilities/formatting.py b/redbot/cogs/audio/core/utilities/formatting.py index 4ca3b0bf521..19de321c8c3 100644 --- a/redbot/cogs/audio/core/utilities/formatting.py +++ b/redbot/cogs/audio/core/utilities/formatting.py @@ -2,14 +2,12 @@ import re import time from pathlib import Path - from typing import List, Optional import discord import lavalink -from red_commons.logging import getLogger - from lavalink import NodeNotFound +from red_commons.logging import getLogger from redbot.core import commands from redbot.core.i18n import Translator diff --git a/redbot/cogs/audio/core/utilities/local_tracks.py b/redbot/cogs/audio/core/utilities/local_tracks.py index ed33f215757..96799b9f5e4 100644 --- a/redbot/cogs/audio/core/utilities/local_tracks.py +++ b/redbot/cogs/audio/core/utilities/local_tracks.py @@ -1,13 +1,12 @@ import contextlib - from pathlib import Path from typing import List, Union import discord import lavalink +from rapidfuzz import process from red_commons.logging import getLogger -from rapidfuzz import process from redbot.core import commands from redbot.core.i18n import Translator from redbot.core.utils import AsyncIter diff --git a/redbot/cogs/audio/core/utilities/miscellaneous.py b/redbot/cogs/audio/core/utilities/miscellaneous.py index 959b6135186..1d7fdbc42fb 100644 --- a/redbot/cogs/audio/core/utilities/miscellaneous.py +++ b/redbot/cogs/audio/core/utilities/miscellaneous.py @@ -275,7 +275,6 @@ def format_time(self, time: int) -> str: return f"{day}{hour}{minutes}{sec}" async def get_lyrics_status(self, ctx: Context) -> bool: - global _prefer_lyrics_cache prefer_lyrics = _prefer_lyrics_cache.setdefault( ctx.guild.id, await self.config.guild(ctx.guild).prefer_lyrics() ) @@ -379,9 +378,7 @@ def decode_track(self, track: str, decode_errors: str = "ignore") -> MutableMapp reader = DataReader(track) flags = (reader.read_int() & 0xC0000000) >> 30 - (version,) = ( - struct.unpack("B", reader.read_byte()) if flags & 1 != 0 else 1 - ) # pylint: disable=unused-variable + (version,) = struct.unpack("B", reader.read_byte()) if flags & 1 != 0 else 1 title = reader.read_utf().decode(errors=decode_errors) author = reader.read_utf().decode() @@ -389,8 +386,8 @@ def decode_track(self, track: str, decode_errors: str = "ignore") -> MutableMapp identifier = reader.read_utf().decode() is_stream = reader.read_boolean() uri = reader.read_utf().decode() if reader.read_boolean() else None - source = reader.read_utf().decode() - position = reader.read_long() # noqa: F841 pylint: disable=unused-variable + reader.read_utf().decode() # source + reader.read_long() # position track_object = { "track": track, diff --git a/redbot/cogs/audio/core/utilities/parsers.py b/redbot/cogs/audio/core/utilities/parsers.py index 44b5ffc85d6..9e586b52334 100644 --- a/redbot/cogs/audio/core/utilities/parsers.py +++ b/redbot/cogs/audio/core/utilities/parsers.py @@ -1,6 +1,5 @@ import re import struct - from typing import Final, Optional import aiohttp diff --git a/redbot/cogs/audio/core/utilities/player.py b/redbot/cogs/audio/core/utilities/player.py index 286d021ac12..d21cf835306 100644 --- a/redbot/cogs/audio/core/utilities/player.py +++ b/redbot/cogs/audio/core/utilities/player.py @@ -1,14 +1,12 @@ import time from pathlib import Path - from typing import List, Optional, Tuple, Union import aiohttp import discord import lavalink -from red_commons.logging import getLogger - from lavalink import NodeNotFound, PlayerNotFound +from red_commons.logging import getLogger from redbot.core import commands from redbot.core.i18n import Translator diff --git a/redbot/cogs/audio/core/utilities/playlists.py b/redbot/cogs/audio/core/utilities/playlists.py index 08ec3e4b41a..57a6f834e30 100644 --- a/redbot/cogs/audio/core/utilities/playlists.py +++ b/redbot/cogs/audio/core/utilities/playlists.py @@ -6,7 +6,6 @@ import random import time from pathlib import Path - from typing import List, MutableMapping, Optional, Tuple, Union import aiohttp @@ -270,12 +269,12 @@ async def get_playlist_match( pred = ReactionPredicate.with_emojis(emojis, msg, user=context.author) try: await context.bot.wait_for("reaction_add", check=pred, timeout=60) - except asyncio.TimeoutError: + except asyncio.TimeoutError as exc: with contextlib.suppress(discord.HTTPException): await msg.delete() raise TooManyMatches( _("Too many matches found and you did not select which one you wanted.") - ) + ) from exc if emojis[pred.result] == "\N{CROSS MARK}": with contextlib.suppress(discord.HTTPException): await msg.delete() @@ -625,8 +624,8 @@ async def fetch_playlist_tracks( ctx, title=_("Unable to Get Track"), description=_( - "I'm unable to get a track from Lavalink node at the moment, try again in a few " - "minutes." + "I'm unable to get a track from Lavalink node at the moment," + " try again in a few minutes." ), ) except Exception as e: @@ -654,8 +653,8 @@ async def fetch_playlist_tracks( ctx, title=_("Unable to Get Track"), description=_( - "I'm unable to get a track from Lavalink node at the moment, try again in a few " - "minutes." + "I'm unable to get a track from Lavalink node at the moment," + " try again in a few minutes." ), ) except Exception as e: diff --git a/redbot/cogs/audio/core/utilities/queue.py b/redbot/cogs/audio/core/utilities/queue.py index d6d8c3a8bee..07c9f74e01f 100644 --- a/redbot/cogs/audio/core/utilities/queue.py +++ b/redbot/cogs/audio/core/utilities/queue.py @@ -1,13 +1,12 @@ import math from pathlib import Path - from typing import List, Tuple import discord import lavalink +from rapidfuzz import process from red_commons.logging import getLogger -from rapidfuzz import process from redbot.core import commands from redbot.core.i18n import Translator from redbot.core.utils import AsyncIter diff --git a/redbot/cogs/audio/core/utilities/validation.py b/redbot/cogs/audio/core/utilities/validation.py index 4ba82c96f9e..eb558c9f618 100644 --- a/redbot/cogs/audio/core/utilities/validation.py +++ b/redbot/cogs/audio/core/utilities/validation.py @@ -1,5 +1,4 @@ import re - from typing import Final, List, Optional, Pattern, Set, Union from urllib.parse import urlparse @@ -69,9 +68,6 @@ async def is_query_allowed( """Checks if the query is allowed in this server or globally.""" if ctx_or_channel: guild = ctx_or_channel.guild - channel = ( - ctx_or_channel.channel if isinstance(ctx_or_channel, Context) else ctx_or_channel - ) query = query.lower().strip() else: guild = None diff --git a/redbot/cogs/audio/manager.py b/redbot/cogs/audio/manager.py index 921d0c9cc57..0ed24226283 100644 --- a/redbot/cogs/audio/manager.py +++ b/redbot/cogs/audio/manager.py @@ -9,7 +9,7 @@ import shlex import shutil import tempfile -from typing import ClassVar, Final, List, Optional, Pattern, Tuple, Union, TYPE_CHECKING +from typing import TYPE_CHECKING, ClassVar, Final, List, Optional, Pattern, Tuple, Union import aiohttp import lavalink @@ -18,28 +18,27 @@ from discord.backoff import ExponentialBackoff from red_commons.logging import getLogger -from redbot.core import data_manager, Config +from redbot.core import Config, data_manager from redbot.core.i18n import Translator from .errors import ( - LavalinkDownloadFailed, + EarlyExitException, InvalidArchitectureException, + LavalinkDownloadFailed, ManagedLavalinkAlreadyRunningException, + ManagedLavalinkNodeException, ManagedLavalinkPreviouslyShutdownException, - UnsupportedJavaException, ManagedLavalinkStartFailure, - UnexpectedJavaResponseException, - EarlyExitException, - ManagedLavalinkNodeException, - NoProcessFound, NodeUnhealthy, + NoProcessFound, + UnexpectedJavaResponseException, + UnsupportedJavaException, ) from .utils import ( change_dict_naming_convention, get_max_allocation_size, replace_p_with_prefix, ) -from ...core.utils import AsyncIter if TYPE_CHECKING: from . import Audio @@ -257,7 +256,7 @@ class ServerManager: def __init__(self, config: Config, cog: "Audio", timeout: Optional[int] = None) -> None: self.ready: asyncio.Event = asyncio.Event() self._config = config - self._proc: Optional[asyncio.subprocess.Process] = None # pylint:disable=no-member + self._proc: Optional[asyncio.subprocess.Process] = None self._shutdown: bool = False self.start_monitor_task = None self.timeout = timeout @@ -327,13 +326,11 @@ async def _start(self, java_path: str) -> None: ) ) try: - self._proc = ( - await asyncio.subprocess.create_subprocess_exec( # pylint:disable=no-member - *args, - cwd=str(LAVALINK_DOWNLOAD_DIR), - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.STDOUT, - ) + self._proc = await asyncio.subprocess.create_subprocess_exec( + *args, + cwd=str(LAVALINK_DOWNLOAD_DIR), + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.STDOUT, ) log.info("Managed Lavalink node started. PID: %s", self._proc.pid) try: @@ -351,7 +348,7 @@ async def _start(self, java_path: str) -> None: async def process_settings(self): data = change_dict_naming_convention(await self._config.yaml.all()) - with open(LAVALINK_APP_YML, "w") as f: + with open(LAVALINK_APP_YML, "w", encoding="utf-8") as f: yaml.safe_dump(data, f) async def _get_jar_args(self) -> Tuple[List[str], Optional[str]]: @@ -414,13 +411,11 @@ async def _has_java(self) -> Tuple[bool, Optional[Tuple[int, int]]]: async def _get_java_version(self) -> Tuple[int, int]: """This assumes we've already checked that java exists.""" - _proc: asyncio.subprocess.Process = ( - await asyncio.create_subprocess_exec( # pylint:disable=no-member - self._java_exc, - "-version", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) + _proc: asyncio.subprocess.Process = await asyncio.create_subprocess_exec( + self._java_exc, + "-version", + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, ) # java -version outputs to stderr _, err = await _proc.communicate() @@ -496,7 +491,7 @@ async def _download_jar(self) -> None: should_retry=False, ) elif 400 <= response.status < 600: - # Other bad responses should be raised but we should retry just incase + # Other bad responses should be raised but we should retry just in case raise LavalinkDownloadFailed(response=response, should_retry=True) fd, path = tempfile.mkstemp() file = open(fd, "wb") @@ -534,7 +529,7 @@ async def _is_up_to_date(self): return True args, _ = await self._get_jar_args() args.append("--version") - _proc = await asyncio.subprocess.create_subprocess_exec( # pylint:disable=no-member + _proc = await asyncio.subprocess.create_subprocess_exec( *args, cwd=str(LAVALINK_DOWNLOAD_DIR), stdout=asyncio.subprocess.PIPE, @@ -627,7 +622,7 @@ async def start_monitor(self, java_path: str): return # lavalink_restart_connect will cause a new monitor task to be created. except Exception as exc: log.debug(exc, exc_info=exc) - raise NodeUnhealthy(str(exc)) + raise NodeUnhealthy(str(exc)) from exc except NoProcessFound: await self._partial_shutdown() except asyncio.TimeoutError: diff --git a/redbot/cogs/audio/sql_statements.py b/redbot/cogs/audio/sql_statements.py index 93d834d4237..86506c3b349 100644 --- a/redbot/cogs/audio/sql_statements.py +++ b/redbot/cogs/audio/sql_statements.py @@ -94,7 +94,7 @@ """ # Data Deletion -# This is intentionally 2 seperate transactions due to concerns +# This is intentionally 2 separate transactions due to concerns # Draper had. This should prevent it from being a large issue, # as this is no different than triggering a bulk deletion now. HANDLE_DISCORD_DATA_DELETION_QUERY: Final[ @@ -561,7 +561,7 @@ LAVALINK_FETCH_ALL_ENTRIES_GLOBAL: Final[ str ] = """ -SELECT query, data +SELECT query, data FROM lavalink """ diff --git a/redbot/cogs/audio/utils.py b/redbot/cogs/audio/utils.py index 42a591426e9..9bae71bcd0a 100644 --- a/redbot/cogs/audio/utils.py +++ b/redbot/cogs/audio/utils.py @@ -1,11 +1,8 @@ -import asyncio -import contextlib import math import platform import re import sys import time - from enum import Enum, unique from pathlib import Path from typing import MutableMapping, Tuple, Union @@ -22,8 +19,8 @@ _ = Translator("Audio", Path(__file__)) -def get_max_allocation_size(exec) -> Tuple[int, bool]: - if platform.architecture(exec)[0] == "64bit": +def get_max_allocation_size(executable: str) -> Tuple[int, bool]: + if platform.architecture(executable)[0] == "64bit": max_heap_allowed = psutil.virtual_memory().total thinks_is_64_bit = True else: @@ -257,7 +254,7 @@ def spotify(self, value): class Notifier: def __init__( - self, ctx: commands.Context, message: discord.Message, updates: MutableMapping, **kwargs + self, ctx: commands.Context, message: discord.Message, updates: MutableMapping, **_kwargs ): self.context = ctx self.message = message diff --git a/redbot/cogs/cleanup/__init__.py b/redbot/cogs/cleanup/__init__.py index 80657a70ec8..ade350051f4 100644 --- a/redbot/cogs/cleanup/__init__.py +++ b/redbot/cogs/cleanup/__init__.py @@ -1,6 +1,7 @@ -from .cleanup import Cleanup from redbot.core.bot import Red +from .cleanup import Cleanup + async def setup(bot: Red) -> None: await bot.add_cog(Cleanup(bot)) diff --git a/redbot/cogs/cleanup/checks.py b/redbot/cogs/cleanup/checks.py index 325f564c00c..1f5eff77dc8 100644 --- a/redbot/cogs/cleanup/checks.py +++ b/redbot/cogs/cleanup/checks.py @@ -1,5 +1,5 @@ from redbot.core.commands import Context, permissions_check -from redbot.core.utils.mod import is_mod_or_superior, check_permissions +from redbot.core.utils.mod import check_permissions, is_mod_or_superior def check_self_permissions(): diff --git a/redbot/cogs/cleanup/cleanup.py b/redbot/cogs/cleanup/cleanup.py index 12d36fe7150..30d9baea223 100644 --- a/redbot/cogs/cleanup/cleanup.py +++ b/redbot/cogs/cleanup/cleanup.py @@ -5,13 +5,14 @@ import discord -from redbot.core import checks, commands, Config +from redbot.core import Config, checks, commands from redbot.core.bot import Red from redbot.core.commands import RawUserIdConverter from redbot.core.i18n import Translator, cog_i18n from redbot.core.utils.chat_formatting import humanize_number -from redbot.core.utils.mod import slow_deletion, mass_purge +from redbot.core.utils.mod import mass_purge, slow_deletion from redbot.core.utils.predicates import MessagePredicate + from .checks import check_self_permissions from .converters import PositiveInt, RawMessageIds, positive_int @@ -37,7 +38,7 @@ def __init__(self, bot: Red): self.config = Config.get_conf(self, 8927348724, force_registration=True) self.config.register_guild(notify=True) - async def red_delete_data_for_user(self, **kwargs): + async def red_delete_data_for_user(self, **_kwargs): """Nothing to delete""" return @@ -172,7 +173,6 @@ async def get_message_from_reference( @commands.group() async def cleanup(self, ctx: commands.Context): """Base command for deleting messages.""" - pass @cleanup.command() @commands.guild_only() @@ -437,19 +437,19 @@ async def between( channel = ctx.channel author = ctx.author try: - mone = await channel.fetch_message(one) + after = await channel.fetch_message(one) except discord.errors.NotFound: return await ctx.send( _("Could not find a message with the ID of {id}.".format(id=one)) ) try: - mtwo = await channel.fetch_message(two) + before = await channel.fetch_message(two) except discord.errors.NotFound: return await ctx.send( _("Could not find a message with the ID of {id}.".format(id=two)) ) to_delete = await self.get_messages_for_deletion( - channel=channel, before=mtwo, after=mone, delete_pinned=delete_pinned + channel=channel, before=before, after=after, delete_pinned=delete_pinned ) to_delete.append(ctx.message) reason = "{}({}) deleted {} messages in channel #{}.".format( @@ -623,10 +623,7 @@ async def cleanup_self( return # You can always delete your own messages, this is needed to purge - can_mass_purge = False - if type(author) is discord.Member: - me = ctx.guild.me - can_mass_purge = ctx.bot_permissions.manage_messages + can_mass_purge = type(author) is discord.Member and ctx.bot_permissions.manage_messages if match_pattern: @@ -740,7 +737,6 @@ def check(m): @commands.admin_or_permissions(manage_messages=True) async def cleanupset(self, ctx: commands.Context): """Manage the settings for the cleanup command.""" - pass @commands.guild_only() @cleanupset.command(name="notify") diff --git a/redbot/cogs/cleanup/converters.py b/redbot/cogs/cleanup/converters.py index 6cefc5c2d33..5c6995195bb 100644 --- a/redbot/cogs/cleanup/converters.py +++ b/redbot/cogs/cleanup/converters.py @@ -1,4 +1,4 @@ -from typing import NewType, TYPE_CHECKING +from typing import TYPE_CHECKING, NewType from redbot.core.commands import BadArgument, Context, Converter from redbot.core.i18n import Translator @@ -26,7 +26,7 @@ def positive_int(arg: str) -> int: try: ret = int(arg) except ValueError: - raise BadArgument(_("{arg} is not an integer.").format(arg=inline(arg))) + raise BadArgument(_("{arg} is not an integer.").format(arg=inline(arg))) from None if ret <= 0: raise BadArgument(_("{arg} is not a positive integer.").format(arg=inline(arg))) return ret diff --git a/redbot/cogs/customcom/customcom.py b/redbot/cogs/customcom/customcom.py index 659e45ed951..2a5fe33d026 100644 --- a/redbot/cogs/customcom/customcom.py +++ b/redbot/cogs/customcom/customcom.py @@ -1,9 +1,9 @@ import asyncio -import re import random +import re from datetime import datetime, timedelta from inspect import Parameter -from typing import Iterable, List, Mapping, Tuple, Dict, Set, Literal, Union +from typing import Dict, Iterable, List, Literal, Mapping, Set, Tuple, Union from urllib.parse import quote_plus import discord @@ -11,8 +11,8 @@ from redbot.core import Config, checks, commands from redbot.core.i18n import Translator, cog_i18n -from redbot.core.utils import menus, AsyncIter -from redbot.core.utils.chat_formatting import box, pagify, escape, humanize_list +from redbot.core.utils import AsyncIter, menus +from redbot.core.utils.chat_formatting import box, escape, humanize_list, pagify from redbot.core.utils.predicates import MessagePredicate _ = Translator("CustomCommands", __file__) @@ -63,7 +63,7 @@ async def redact_author_ids(self, user_id: int): for guild_id in all_guilds.keys(): await asyncio.sleep(0) async with self.config.guild_from_id(guild_id).commands() as all_commands: - async for com_name, com_info in AsyncIter(all_commands.items(), steps=100): + async for com_info in AsyncIter(all_commands.values(), steps=100): if not com_info: continue @@ -185,9 +185,9 @@ async def edit( pred = MessagePredicate.yes_or_no(ctx) try: await self.bot.wait_for("message", check=pred, timeout=30) - except asyncio.TimeoutError: + except asyncio.TimeoutError as exc: await ctx.send(_("Response timed out, please try again later.")) - raise CommandNotEdited() + raise CommandNotEdited() from exc if pred.result is True: response = await self.get_responses(ctx=ctx) else: @@ -196,9 +196,9 @@ async def edit( resp = await self.bot.wait_for( "message", check=MessagePredicate.same_context(ctx), timeout=180 ) - except asyncio.TimeoutError: + except asyncio.TimeoutError as exc: await ctx.send(_("Response timed out, please try again later.")) - raise CommandNotEdited() + raise CommandNotEdited() from exc response = resp.content if response: @@ -264,7 +264,6 @@ async def red_delete_data_for_user( @commands.guild_only() async def customcom(self, ctx: commands.Context): """Base command for Custom Commands management.""" - pass @customcom.command(name="raw") async def cc_raw(self, ctx: commands.Context, command: str.lower): @@ -275,10 +274,10 @@ async def cc_raw(self, ctx: commands.Context, command: str.lower): **Arguments:** - `` The custom command to get the raw response of.""" - commands = await self.config.guild(ctx.guild).commands() - if command not in commands: + commands_ = await self.config.guild(ctx.guild).commands() + if command not in commands_: return await ctx.send("That command doesn't exist.") - command = commands[command] + command = commands_[command] if isinstance(command["response"], str): raw = discord.utils.escape_markdown(command["response"]) if len(raw) > 2000: @@ -560,7 +559,7 @@ async def cc_list(self, ctx: commands.Context): results = self.prepare_command_list(ctx, sorted(cc_dict.items(), key=lambda t: t[0])) if await ctx.embed_requested(): - # We need a space before the newline incase the CC preview ends in link (GH-2295) + # We need a space before the newline in case the CC preview ends in link (GH-2295) content = " \n".join(map("**{0[0]}** {0[1]}".format, results)) pages = list(pagify(content, page_length=1024)) embed_pages = [] @@ -680,14 +679,13 @@ async def on_message_without_command(self, message): if not ctx.command_failed: await self.cc_command(*ctx.args, **ctx.kwargs, raw_response=raw_response) + # fake command to take advantage of discord.py's parsing and events async def cc_callback(self, *args, **kwargs) -> None: """ Custom command. Created via the CustomCom cog. See `[p]customcom` for more details. """ - # fake command to take advantage of discord.py's parsing and events - pass async def cc_command(self, ctx, *cc_args, raw_response, **cc_kwargs) -> None: cc_args = (*cc_args, *cc_kwargs.values()) diff --git a/redbot/cogs/downloader/converters.py b/redbot/cogs/downloader/converters.py index 483918d4a6f..4563b66866a 100644 --- a/redbot/cogs/downloader/converters.py +++ b/redbot/cogs/downloader/converters.py @@ -1,6 +1,8 @@ import discord + from redbot.core import commands from redbot.core.i18n import Translator + from .installable import InstalledModule _ = Translator("Koala", __file__) diff --git a/redbot/cogs/downloader/downloader.py b/redbot/cogs/downloader/downloader.py index c9242dfb62e..141eaf62296 100644 --- a/redbot/cogs/downloader/downloader.py +++ b/redbot/cogs/downloader/downloader.py @@ -4,26 +4,27 @@ import re import shutil import sys -from pathlib import Path -from typing import Tuple, Union, Iterable, Collection, Optional, Dict, Set, List, cast from collections import defaultdict +from pathlib import Path +from typing import Collection, Dict, Iterable, List, Optional, Set, Tuple, Union, cast import discord -from redbot.core import checks, commands, Config, version_info as red_version_info + +from redbot.core import Config, checks, commands, version_info as red_version_info from redbot.core.bot import Red from redbot.core.data_manager import cog_data_path from redbot.core.i18n import Translator, cog_i18n from redbot.core.utils import can_user_react_in -from redbot.core.utils.chat_formatting import box, pagify, humanize_list, inline +from redbot.core.utils.chat_formatting import box, humanize_list, inline, pagify from redbot.core.utils.menus import start_adding_reactions from redbot.core.utils.predicates import MessagePredicate, ReactionPredicate from . import errors from .checks import do_install_agreement from .converters import InstalledCog -from .installable import InstallableType, Installable, InstalledModule +from .installable import Installable, InstallableType, InstalledModule from .log import log -from .repo_manager import RepoManager, Repo +from .repo_manager import Repo, RepoManager _ = Translator("Downloader", __file__) @@ -92,7 +93,7 @@ def cog_unload(self): if self._init_task is not None: self._init_task.cancel() - async def red_delete_data_for_user(self, **kwargs): + async def red_delete_data_for_user(self, **_kwargs): """Nothing to delete""" return @@ -517,7 +518,6 @@ async def pipinstall(self, ctx: commands.Context, *deps: str) -> None: @checks.is_owner() async def repo(self, ctx: commands.Context) -> None: """Base command for repository management.""" - pass @repo.command(name="add") async def _repo_add( @@ -701,7 +701,6 @@ async def _repo_update(self, ctx: commands.Context, *repos: Repo) -> None: @checks.is_owner() async def cog(self, ctx: commands.Context) -> None: """Base command for cog installation management commands.""" - pass @cog.command(name="reinstallreqs", hidden=True) async def _cog_reinstallreqs(self, ctx: commands.Context) -> None: @@ -1280,7 +1279,8 @@ async def _cog_update_logic( ) else: message += _( - "Cogs from provided repo are already up to date with this revision." + "Cogs from provided repo are already up to date" + " with this revision." ) else: if cogs: diff --git a/redbot/cogs/downloader/errors.py b/redbot/cogs/downloader/errors.py index ee0c6ab2ebe..729878e8d69 100644 --- a/redbot/cogs/downloader/errors.py +++ b/redbot/cogs/downloader/errors.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import List, TYPE_CHECKING +from typing import TYPE_CHECKING, List if TYPE_CHECKING: from .repo_manager import Candidate @@ -30,8 +30,6 @@ class DownloaderException(Exception): Base class for Downloader exceptions. """ - pass - class GitException(DownloaderException): """ @@ -49,8 +47,6 @@ class InvalidRepoName(DownloaderException): the message for a more detailed reason. """ - pass - class CopyingError(DownloaderException): """ @@ -58,8 +54,6 @@ class CopyingError(DownloaderException): during copying of module's files. """ - pass - class ExistingGitRepo(DownloaderException): """ @@ -67,8 +61,6 @@ class ExistingGitRepo(DownloaderException): git repo already exists. """ - pass - class MissingGitRepo(DownloaderException): """ @@ -76,16 +68,12 @@ class MissingGitRepo(DownloaderException): does not. """ - pass - class CloningError(GitException): """ Thrown when git clone returns a non zero exit code. """ - pass - class CurrentHashError(GitException): """ @@ -93,8 +81,6 @@ class CurrentHashError(GitException): to determine the current commit hash. """ - pass - class HardResetError(GitException): """ @@ -102,40 +88,30 @@ class HardResetError(GitException): (usually prior to a repo update). """ - pass - class UpdateError(GitException): """ Thrown when git pull returns a non zero error code. """ - pass - class GitDiffError(GitException): """ Thrown when a git diff fails. """ - pass - class NoRemoteURL(GitException): """ Thrown when no remote URL exists for a repo. """ - pass - class UnknownRevision(GitException): """ Thrown when specified revision cannot be found. """ - pass - class AmbiguousRevision(GitException): """ @@ -151,5 +127,3 @@ class PipError(DownloaderException): """ Thrown when pip returns a non-zero return code. """ - - pass diff --git a/redbot/cogs/downloader/installable.py b/redbot/cogs/downloader/installable.py index abda7d92488..35fbad6dd34 100644 --- a/redbot/cogs/downloader/installable.py +++ b/redbot/cogs/downloader/installable.py @@ -6,14 +6,14 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple, Union, cast -from .log import log +from redbot.core import VersionInfo + from .info_schemas import INSTALLABLE_SCHEMA, update_mixin from .json_mixins import RepoJSONMixin - -from redbot.core import VersionInfo +from .log import log if TYPE_CHECKING: - from .repo_manager import RepoManager, Repo + from .repo_manager import Repo, RepoManager class InstallableType(IntEnum): @@ -127,10 +127,9 @@ async def copy_to(self, target_dir: Path) -> bool: else: copy_func = functools.partial(shutil.copytree, dirs_exist_ok=True) - # noinspection PyBroadException try: copy_func(src=str(self._location), dst=str(target_dir / self._location.name)) - except: # noqa: E722 + except OSError: log.exception("Error occurred when copying path: %s", self._location) return False return True diff --git a/redbot/cogs/downloader/repo_manager.py b/redbot/cogs/downloader/repo_manager.py index 14baf11d0f6..8682836da2a 100644 --- a/redbot/cogs/downloader/repo_manager.py +++ b/redbot/cogs/downloader/repo_manager.py @@ -5,14 +5,13 @@ import keyword import os import pkgutil +import re import shlex import shutil -import re -import yarl from concurrent.futures import ThreadPoolExecutor from pathlib import Path -from subprocess import run as sp_run, PIPE, CompletedProcess from string import Formatter +from subprocess import PIPE, CompletedProcess, run as sp_run from sys import executable from typing import ( Any, @@ -28,9 +27,11 @@ ) import discord -from redbot.core import data_manager, commands, Config -from redbot.core.utils._internal_utils import safe_delete +import yarl + +from redbot.core import Config, commands, data_manager from redbot.core.i18n import Translator +from redbot.core.utils._internal_utils import safe_delete from . import errors from .installable import Installable, InstallableType, InstalledModule @@ -52,9 +53,7 @@ class Candidate(NamedTuple): description: str -class _RepoCheckoutCtxManager( - Awaitable[None], AsyncContextManager[None] -): # pylint: disable=duplicate-bases +class _RepoCheckoutCtxManager(Awaitable[None], AsyncContextManager[None]): def __init__( self, repo: Repo, @@ -505,17 +504,6 @@ def _update_available_modules(self) -> Tuple[Installable, ...]: :return: List of available modules. """ curr_modules = [] - """ - for name in self.folder_path.iterdir(): - if name.is_dir(): - spec = importlib.util.spec_from_file_location( - name.stem, location=str(name.parent) - ) - if spec is not None: - curr_modules.append( - Installable(location=name) - ) - """ for file_finder, name, is_pkg in pkgutil.iter_modules(path=[str(self.folder_path)]): if not name.isidentifier() or keyword.iskeyword(name): # reject package names that can't be valid python identifiers @@ -968,9 +956,8 @@ async def install_raw_requirements( if p.returncode != 0: log.error( - "Something went wrong when installing" - " the following requirements:" - " {}".format(", ".join(requirements)) + "Something went wrong when installing the following requirements: %s", + ", ".join(requirements), ) return False return True @@ -1141,7 +1128,7 @@ async def delete_repo(self, name: str) -> None: except KeyError: pass - async def update_repo(self, repo_name: str) -> Tuple[Repo, Tuple[str, str]]: + async def update_repo(self, name: str) -> Tuple[Repo, Tuple[str, str]]: """Update repo with provided name. Parameters @@ -1155,7 +1142,7 @@ async def update_repo(self, repo_name: str) -> Tuple[Repo, Tuple[str, str]]: A 2-`tuple` with Repo object and a 2-`tuple` of `str` containing old and new commit hashes. """ - repo = self._repos[repo_name] + repo = self._repos[name] old, new = await repo.update() return (repo, (old, new)) diff --git a/redbot/cogs/economy/__init__.py b/redbot/cogs/economy/__init__.py index b117218fa39..1c5788ae2bb 100644 --- a/redbot/cogs/economy/__init__.py +++ b/redbot/cogs/economy/__init__.py @@ -1,4 +1,5 @@ from redbot.core.bot import Red + from .economy import Economy diff --git a/redbot/cogs/economy/converters.py b/redbot/cogs/economy/converters.py index c24ae98be62..8c70fb38c73 100644 --- a/redbot/cogs/economy/converters.py +++ b/redbot/cogs/economy/converters.py @@ -1,4 +1,4 @@ -from typing import NewType, TYPE_CHECKING +from typing import TYPE_CHECKING, NewType from redbot.core.commands import BadArgument from redbot.core.i18n import Translator @@ -16,7 +16,7 @@ def positive_int(arg: str) -> int: try: ret = int(arg) except ValueError: - raise BadArgument(_("{arg} is not an integer.").format(arg=inline(arg))) + raise BadArgument(_("{arg} is not an integer.").format(arg=inline(arg))) from None if ret <= 0: raise BadArgument(_("{arg} is not a positive integer.").format(arg=inline(arg))) return ret diff --git a/redbot/cogs/economy/economy.py b/redbot/cogs/economy/economy.py index f3d6ded7802..6a21a710705 100644 --- a/redbot/cogs/economy/economy.py +++ b/redbot/cogs/economy/economy.py @@ -2,20 +2,21 @@ import logging import random from collections import defaultdict, deque, namedtuple -from datetime import datetime, timezone, timedelta +from datetime import datetime, timedelta, timezone from enum import Enum from math import ceil -from typing import cast, Iterable, Union, Literal +from typing import TYPE_CHECKING, Iterable, Literal, cast import discord -from redbot.core import Config, bank, commands, errors, checks -from redbot.core.commands.converter import TimedeltaConverter +from redbot.core import Config, bank, checks, commands, errors from redbot.core.bot import Red +from redbot.core.commands.converter import TimedeltaConverter from redbot.core.i18n import Translator, cog_i18n from redbot.core.utils import AsyncIter from redbot.core.utils.chat_formatting import box, humanize_number -from redbot.core.utils.menus import close_menu, menu +from redbot.core.utils.menus import menu + from .converters import positive_int T_ = Translator("Economy", __file__) @@ -108,7 +109,7 @@ def __init__(self, argument): "Invalid value, the argument must be an integer," " optionally preceded with a `+` or `-` sign." ) - ) + ) from None if argument and argument[0] in allowed: if self.sum < 0: self.operation = "withdraw" @@ -126,6 +127,12 @@ def __init__(self, argument): self.operation = "set" +if TYPE_CHECKING: + Duration = timedelta +else: + Duration = TimedeltaConverter(default_unit="seconds") + + @cog_i18n(_) class Economy(commands.Cog): """Get rich and have fun with imaginary currency!""" @@ -179,7 +186,6 @@ async def red_delete_data_for_user( @commands.group(name="bank") async def _bank(self, ctx: commands.Context): """Base command to manage the bank.""" - pass @_bank.command() async def balance(self, ctx: commands.Context, user: discord.Member = commands.Author): @@ -301,7 +307,8 @@ async def payday(self, ctx: commands.Context): credits_name = await bank.get_currency_name(ctx.guild) if await bank.is_global(): # Role payouts will not be used - # Gets the latest time the user used the command successfully and adds the global payday time + # Gets the latest time the user used the command successfully + # and adds the global payday time next_payday = ( await self.config.user(author).next_payday() + await self.config.PAYDAY_TIME() ) @@ -594,9 +601,7 @@ async def slot_machine(author, channel, bid): sign = " " if i == 1: sign = ">" - slot += "{}{} {} {}\n".format( - sign, *[c.value for c in row] # pylint: disable=no-member - ) + slot += "{}{} {} {}\n".format(sign, *[c.value for c in row]) payout = PAYOUTS.get(rows[1]) if not payout: @@ -624,7 +629,8 @@ async def slot_machine(author, channel, bid): await channel.send( _( "You've reached the maximum amount of {currency}! " - "Please spend some more \N{GRIMACING FACE}\n{old_balance} -> {new_balance}!" + "Please spend some more \N{GRIMACING FACE}\n" + "{old_balance} -> {new_balance}!" ).format( currency=await bank.get_currency_name(getattr(channel, "guild", None)), old_balance=humanize_number(then), @@ -768,9 +774,7 @@ async def slotmax(self, ctx: commands.Context, bid: positive_int): ) @economyset.command() - async def slottime( - self, ctx: commands.Context, *, duration: TimedeltaConverter(default_unit="seconds") - ): + async def slottime(self, ctx: commands.Context, *, duration: Duration): """Set the cooldown for the slot machine. Examples: @@ -791,9 +795,7 @@ async def slottime( await ctx.send(_("Cooldown is now {num} seconds.").format(num=seconds)) @economyset.command() - async def paydaytime( - self, ctx: commands.Context, *, duration: TimedeltaConverter(default_unit="seconds") - ): + async def paydaytime(self, ctx: commands.Context, *, duration: Duration): """Set the cooldown for the payday command. Examples: diff --git a/redbot/cogs/filter/__init__.py b/redbot/cogs/filter/__init__.py index 342e8319479..a11dd0899f9 100644 --- a/redbot/cogs/filter/__init__.py +++ b/redbot/cogs/filter/__init__.py @@ -1,6 +1,7 @@ -from .filter import Filter from redbot.core.bot import Red +from .filter import Filter + async def setup(bot: Red) -> None: await bot.add_cog(Filter(bot)) diff --git a/redbot/cogs/filter/filter.py b/redbot/cogs/filter/filter.py index c2d2f889729..fa69dfa68d5 100644 --- a/redbot/cogs/filter/filter.py +++ b/redbot/cogs/filter/filter.py @@ -1,15 +1,15 @@ import asyncio -import discord import re -from datetime import timezone -from typing import Union, Set, Literal, Optional +from typing import Literal, Optional, Set, Union + +import discord -from redbot.core import checks, Config, modlog, commands +from redbot.core import Config, checks, commands, modlog from redbot.core.bot import Red from redbot.core.i18n import Translator, cog_i18n, set_contextual_locales_from_guild -from redbot.core.utils.predicates import MessagePredicate from redbot.core.utils import AsyncIter -from redbot.core.utils.chat_formatting import pagify, humanize_list +from redbot.core.utils.chat_formatting import humanize_list, pagify +from redbot.core.utils.predicates import MessagePredicate _ = Translator("Filter", __file__) @@ -83,7 +83,6 @@ async def register_casetypes() -> None: @checks.admin_or_permissions(manage_guild=True) async def filterset(self, ctx: commands.Context): """Base command to manage filter settings.""" - pass @filterset.command(name="defaultname") async def filter_default_name(self, ctx: commands.Context, name: str): @@ -150,7 +149,6 @@ async def _filter(self, ctx: commands.Context): Use double quotes to add or remove sentences. """ - pass @_filter.command(name="clear") async def _filter_clear(self, ctx: commands.Context): @@ -199,7 +197,6 @@ async def _filter_channel(self, ctx: commands.Context): Use double quotes to add or remove sentences. """ - pass @_filter_channel.command(name="clear") async def _channel_clear(self, ctx: commands.Context): diff --git a/redbot/cogs/general/general.py b/redbot/cogs/general/general.py index c981f7e6d57..67865c8aaa3 100644 --- a/redbot/cogs/general/general.py +++ b/redbot/cogs/general/general.py @@ -1,22 +1,24 @@ import datetime import time +import urllib.parse from enum import Enum -from random import randint, choice +from random import choice, randint from typing import Final -import urllib.parse + import aiohttp import discord + from redbot.core import commands from redbot.core.bot import Red from redbot.core.i18n import Translator, cog_i18n -from redbot.core.utils.menus import menu from redbot.core.utils.chat_formatting import ( bold, escape, - italics, humanize_number, humanize_timedelta, + italics, ) +from redbot.core.utils.menus import menu _ = T_ = Translator("General", __file__) @@ -78,7 +80,7 @@ def __init__(self, bot: Red) -> None: self.bot = bot self.stopwatches = {} - async def red_delete_data_for_user(self, **kwargs): + async def red_delete_data_for_user(self, **_kwargs): """Nothing to delete""" return @@ -313,7 +315,8 @@ def _bitsize(num: int): if ctx.bot.shard_count > 1 else "" ) - # Logic from: https://github.com/TrustyJAID/Trusty-cogs/blob/master/serverstats/serverstats.py#L159 + # Logic from: + # https://github.com/TrustyJAID/Trusty-cogs/blob/master/serverstats/serverstats.py#L159 online_stats = { _("Humans: "): lambda x: not x.bot, _(" • Bots: "): lambda x: x.bot, @@ -402,7 +405,10 @@ def _bitsize(num: int): data.add_field( name=_("Misc:"), value=_( - "AFK channel: {afk_chan}\nAFK timeout: {afk_timeout}\nCustom emojis: {emoji_count}\nRoles: {role_count}" + "AFK channel: {afk_chan}\n" + "AFK timeout: {afk_timeout}\n" + "Custom emojis: {emoji_count}\n" + "Roles: {role_count}" ).format( afk_chan=bold(str(guild.afk_channel)) if guild.afk_channel diff --git a/redbot/cogs/image/image.py b/redbot/cogs/image/image.py index b5f3ede889e..14a9a3b8d0e 100644 --- a/redbot/cogs/image/image.py +++ b/redbot/cogs/image/image.py @@ -3,9 +3,9 @@ import aiohttp -from redbot.core.i18n import Translator, cog_i18n -from redbot.core import checks, Config, commands +from redbot.core import Config, checks, commands from redbot.core.commands import UserInputOptional +from redbot.core.i18n import Translator, cog_i18n _ = Translator("Image", __file__) @@ -35,7 +35,7 @@ async def cog_load(self) -> None: async def cog_unload(self): await self.session.close() - async def red_delete_data_for_user(self, **kwargs): + async def red_delete_data_for_user(self, **_kwargs): """Nothing to delete""" return @@ -45,7 +45,6 @@ async def _imgur(self, ctx): Make sure to set the Client ID using `[p]imgurcreds`. """ - pass @_imgur.command(name="search", usage="[count] ") async def imgur_search(self, ctx, count: UserInputOptional[int] = 1, *, term: str): diff --git a/redbot/cogs/mod/__init__.py b/redbot/cogs/mod/__init__.py index 41b4f151592..da312dbac17 100644 --- a/redbot/cogs/mod/__init__.py +++ b/redbot/cogs/mod/__init__.py @@ -1,4 +1,5 @@ from redbot.core.bot import Red + from .mod import Mod diff --git a/redbot/cogs/mod/abc.py b/redbot/cogs/mod/abc.py index cfdd5a6596b..7d108aa3518 100644 --- a/redbot/cogs/mod/abc.py +++ b/redbot/cogs/mod/abc.py @@ -1,7 +1,8 @@ from abc import ABC, abstractmethod -from typing import List, Tuple, Optional +from typing import Optional import discord + from redbot.core import Config, commands from redbot.core.bot import Red diff --git a/redbot/cogs/mod/events.py b/redbot/cogs/mod/events.py index 4d53c12181c..b396232ec28 100644 --- a/redbot/cogs/mod/events.py +++ b/redbot/cogs/mod/events.py @@ -1,10 +1,11 @@ import logging -from datetime import timezone from collections import defaultdict, deque import discord -from redbot.core import i18n, modlog, commands + +from redbot.core import commands, i18n, modlog from redbot.core.utils.mod import is_mod_or_superior + from .abc import MixinMeta _ = i18n.Translator("Mod", __file__) @@ -56,9 +57,9 @@ async def check_mention_spam(self, message): await guild.ban(author, reason=_("Mention spam (Autoban)")) except discord.HTTPException: log.warning( - "Failed to ban a member ({member}) for mention spam in server {guild}.".format( - member=author.id, guild=guild.id - ) + "Failed to ban a member (%s) for mention spam in server %s.", + author.id, + guild.id, ) else: await modlog.create_case( @@ -80,9 +81,9 @@ async def check_mention_spam(self, message): await guild.kick(author, reason=_("Mention Spam (Autokick)")) except discord.HTTPException: log.warning( - "Failed to kick a member ({member}) for mention spam in server {guild}".format( - member=author.id, guild=guild.id - ) + "Failed to kick a member (%s) for mention spam in server %s.", + author.id, + guild.id, ) else: await modlog.create_case( @@ -102,18 +103,18 @@ async def check_mention_spam(self, message): if mentions >= mention_spam["warn"]: try: await author.send(_("Please do not mass mention people!")) - except (discord.HTTPException, discord.Forbidden): + except discord.HTTPException: try: await message.channel.send( _("{member}, Please do not mass mention people!").format( member=author.mention ) ) - except (discord.HTTPException, discord.Forbidden): + except discord.HTTPException: log.warning( - "Failed to warn a member ({member}) for mention spam in server {guild}".format( - member=author.id, guild=guild.id - ) + "Failed to warn a member (%s) for mention spam in server %s.", + author.id, + guild.id, ) return False diff --git a/redbot/cogs/mod/kickban.py b/redbot/cogs/mod/kickban.py index 89ba9af4f65..f61087481cb 100644 --- a/redbot/cogs/mod/kickban.py +++ b/redbot/cogs/mod/kickban.py @@ -5,17 +5,19 @@ from typing import Dict, List, Optional, Tuple, Union import discord -from redbot.core import commands, i18n, checks, modlog -from redbot.core.commands import UserInputOptional, RawUserIdConverter + +from redbot.core import checks, commands, i18n, modlog +from redbot.core.commands import RawUserIdConverter from redbot.core.utils import AsyncIter from redbot.core.utils.chat_formatting import ( - pagify, - humanize_number, bold, - humanize_list, format_perms_list, + humanize_list, + humanize_number, + pagify, ) from redbot.core.utils.mod import get_audit_reason + from .abc import MixinMeta from .utils import is_allowed_by_hierarchy @@ -46,21 +48,22 @@ async def get_invite_for_reinvite(ctx: commands.Context, max_age: int = 86400) - # doesn't grant temporary membership # (i.e. they won't be kicked on disconnect) return inv.url - else: # No existing invite found that is valid - channels_and_perms = ( - (channel, channel.permissions_for(guild.me)) for channel in guild.text_channels - ) - channel = next( - (channel for channel, perms in channels_and_perms if perms.create_instant_invite), - None, - ) - if channel is None: - return "" - try: - # Create invite that expires after max_age - return (await channel.create_invite(max_age=max_age)).url - except discord.HTTPException: - return "" + + # No existing invite found that is valid + channels_and_perms = ( + (channel, channel.permissions_for(guild.me)) for channel in guild.text_channels + ) + channel = next( + (channel for channel, perms in channels_and_perms if perms.create_instant_invite), + None, + ) + if channel is None: + return "" + try: + # Create invite that expires after max_age + return (await channel.create_invite(max_age=max_age)).url + except discord.HTTPException: + return "" @staticmethod async def _voice_perm_check( @@ -177,9 +180,10 @@ async def ban_user( if removed_temp: log.info( - "{}({}) upgraded the tempban for {} to a permaban.".format( - author.name, author.id, user.id - ) + "%r (%r) upgraded the tempban for %r to a permaban.", + author.name, + author.id, + user.id, ) success_message = _( "User with ID {user_id} was upgraded from a temporary to a permanent ban." @@ -189,9 +193,13 @@ async def ban_user( try: await guild.ban(user, reason=audit_reason, delete_message_seconds=days * 86400) log.info( - "{}({}) {}ned {}({}), deleting {} days worth of messages.".format( - author.name, author.id, ban_type, username, user.id, str(days) - ) + "%r (%r) %sned %r (%r), deleting %r days worth of messages.", + author.name, + author.id, + ban_type, + username, + user.id, + days, ) success_message = _("Done. That felt good.") except discord.Forbidden: @@ -200,9 +208,12 @@ async def ban_user( return False, _("User with ID {user_id} not found").format(user_id=user.id) except Exception: log.exception( - "{}({}) attempted to {} {}({}), but an error occurred.".format( - author.name, author.id, ban_type, username, user.id - ) + "%r (%r) attempted to %s %r (%r) but an error occurred.", + author.name, + author.id, + ban_type, + username, + user.id, ) return False, _("An unexpected error occurred.") @@ -267,11 +278,13 @@ async def _check_guild_tempban_expirations( # 50013: Missing permissions error code or 403: Forbidden status if e.code == 50013 or e.status == 403: log.info( - f"Failed to unban ({uid}) user from " - f"{guild.name}({guild.id}) guild due to permissions." + "Failed to unban (%r) user from %r (%r) guild due to permissions.", + uid, + guild.name, + guild.id, ) break # skip the rest of this guild - log.info(f"Failed to unban member: error code: {e.code}") + log.info("Failed to unban member: error code: %r", e.code) else: # user unbanned successfully guild_tempbans.remove(uid) @@ -333,14 +346,16 @@ async def kick(self, ctx: commands.Context, member: discord.Member, *, reason: s await member.send(embed=em) try: await guild.kick(member, reason=audit_reason) - log.info("{}({}) kicked {}({})".format(author.name, author.id, member.name, member.id)) + log.info("%r (%r) kicked %r (%r)", author.name, author.id, member.name, member.id) except discord.errors.Forbidden: await ctx.send(_("I'm not allowed to do that.")) except Exception: log.exception( - "{}({}) attempted to kick {}({}), but an error occurred.".format( - author.name, author.id, member.name, member.id - ) + "%r (%r) attempted to kick %r (%r) but an error occurred.", + author.name, + author.id, + member.name, + member.id, ) else: await modlog.create_case( @@ -430,7 +445,8 @@ async def show_results(): text += "\n".join(errors.values()) if upgrades: text += _( - "\nFollowing user IDs have been upgraded from a temporary to a permanent ban:\n" + "\nFollowing user IDs have been upgraded" + " from a temporary to a permanent ban:\n" ) text += humanize_list(upgrades) @@ -463,7 +479,8 @@ def remove_processed(ids): for user_id in user_ids: if user_id in tempbans: - # We need to check if a user is tempbanned here because otherwise they won't be processed later on. + # We need to check if a user is tempbanned here + # because otherwise they won't be processed later on. continue try: await guild.fetch_ban(discord.Object(user_id)) @@ -531,9 +548,10 @@ def remove_processed(ids): tempbans.remove(user_id) upgrades.append(str(user_id)) log.info( - "{}({}) upgraded the tempban for {} to a permaban.".format( - author.name, author.id, user_id - ) + "%r (%r) upgraded the tempban for %r to a permaban.", + author.name, + author.id, + user_id, ) banned.append(user_id) else: @@ -541,7 +559,7 @@ def remove_processed(ids): await guild.ban( user, reason=audit_reason, delete_message_seconds=days * 86400 ) - log.info("{}({}) hackbanned {}".format(author.name, author.id, user_id)) + log.info("%r (%r) hackbanned %r", author.name, author.id, user_id) except discord.NotFound: errors[user_id] = _("User with ID {user_id} not found").format( user_id=user_id @@ -716,24 +734,31 @@ async def softban(self, ctx: commands.Context, member: discord.Member, *, reason return except discord.HTTPException: log.exception( - "{}({}) attempted to softban {}({}), but an error occurred trying to ban them.".format( - author.name, author.id, member.name, member.id - ) + "%r (%r) attempted to softban %r (%r) but an error occurred trying to ban them.", + author.name, + author.id, + member.name, + member.id, ) return try: await guild.unban(member) except discord.HTTPException: log.exception( - "{}({}) attempted to softban {}({}), but an error occurred trying to unban them.".format( - author.name, author.id, member.name, member.id - ) + "%r (%r) attempted to softban %r (%r) but an error occurred trying to unban them.", + author.name, + author.id, + member.name, + member.id, ) return else: log.info( - "{}({}) softbanned {}({}), deleting 1 day worth " - "of messages.".format(author.name, author.id, member.name, member.id) + "%r (%r) softbanned %r (%r), deleting 1 day worth of messages.", + author.name, + author.id, + member.name, + member.id, ) await modlog.create_case( self.bot, diff --git a/redbot/cogs/mod/mod.py b/redbot/cogs/mod/mod.py index 144d2ceed4d..d7fe76b6291 100644 --- a/redbot/cogs/mod/mod.py +++ b/redbot/cogs/mod/mod.py @@ -1,23 +1,20 @@ import asyncio -import logging -import re from abc import ABC from collections import defaultdict -from typing import List, Tuple, Literal +from typing import Literal -import discord -from redbot.core.utils import AsyncIter - -from redbot.core import Config, modlog, commands +from redbot.core import Config, commands from redbot.core.bot import Red from redbot.core.i18n import Translator, cog_i18n +from redbot.core.utils import AsyncIter from redbot.core.utils._internal_utils import send_to_owners_with_prefix_replaced from redbot.core.utils.chat_formatting import inline + from .events import Events from .kickban import KickBanMixin from .names import ModInfo -from .slowmode import Slowmode from .settings import ModSettings +from .slowmode import Slowmode _ = T_ = Translator("Mod", __file__) @@ -30,8 +27,6 @@ class CompositeMetaClass(type(commands.Cog), type(ABC)): coexist with discord.py's metaclass """ - pass - @cog_i18n(_) class Mod( diff --git a/redbot/cogs/mod/names.py b/redbot/cogs/mod/names.py index 3cc5970bce0..872b30ea434 100644 --- a/redbot/cogs/mod/names.py +++ b/redbot/cogs/mod/names.py @@ -2,13 +2,15 @@ from typing import cast import discord -from redbot.core import commands, i18n, checks + +from redbot.core import checks, commands, i18n from redbot.core.utils.common_filters import ( + escape_spoilers_and_mass_mentions, filter_invites, filter_various_mentions, - escape_spoilers_and_mass_mentions, ) from redbot.core.utils.mod import get_audit_reason + from .abc import MixinMeta from .utils import is_allowed_by_hierarchy @@ -76,7 +78,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.")) @@ -236,10 +238,10 @@ async def userinfo(self, ctx, *, member: discord.Member = None): # In embed.fields.2.value: Must be 1024 or fewer in length. if len(role_str) > 1024: # Alternative string building time. - # This is not the most optimal, but if you're hitting this, you are losing more time + # This is not the most optimal, but if you're hitting this, you're losing more time # to every single check running on users than the occasional user info invoke - # We don't start by building this way, since the number of times we hit this should be - # infinitesimally small compared to when we don't across all uses of Red. + # We don't start by building this way, since the number of times we hit this + # should be infinitesimally small compared to when we don't across all uses of Red. continuation_string = _( "and {numeric_number} more roles not displayed due to embed limits." ) diff --git a/redbot/cogs/mod/settings.py b/redbot/cogs/mod/settings.py index 12fb07130cf..e59c9e8bdab 100644 --- a/redbot/cogs/mod/settings.py +++ b/redbot/cogs/mod/settings.py @@ -1,9 +1,9 @@ import asyncio from collections import defaultdict, deque -from typing import Optional from datetime import timedelta +from typing import TYPE_CHECKING -from redbot.core import commands, i18n, checks +from redbot.core import checks, commands, i18n from redbot.core.utils import AsyncIter from redbot.core.utils.chat_formatting import box, humanize_timedelta, inline @@ -11,6 +11,11 @@ _ = i18n.Translator("Mod", __file__) +if TYPE_CHECKING: + Duration = timedelta +else: + Duration = commands.TimedeltaConverter(minimum=timedelta(seconds=1), default_unit="seconds") + class ModSettings(MixinMeta): """ @@ -394,9 +399,7 @@ async def defaultduration( self, ctx: commands.Context, *, - duration: commands.TimedeltaConverter( - minimum=timedelta(seconds=1), default_unit="seconds" - ), + duration: Duration, ): """Set the default time to be used when a user is tempbanned. diff --git a/redbot/cogs/mod/slowmode.py b/redbot/cogs/mod/slowmode.py index ad6f3ce458c..3bdb9b164c2 100644 --- a/redbot/cogs/mod/slowmode.py +++ b/redbot/cogs/mod/slowmode.py @@ -1,13 +1,24 @@ -import discord -import re -from .abc import MixinMeta from datetime import timedelta -from redbot.core import commands, i18n, checks +from typing import TYPE_CHECKING + +import discord + +from redbot.core import commands, i18n from redbot.core.utils.chat_formatting import humanize_timedelta +from .abc import MixinMeta + _ = i18n.Translator("Mod", __file__) +if TYPE_CHECKING: + SlowmodeInterval = timedelta +else: + SlowmodeInterval = commands.TimedeltaConverter( + minimum=timedelta(seconds=0), maximum=timedelta(hours=6), default_unit="seconds" + ) + + class Slowmode(MixinMeta): """ Commands regarding channel slowmode management. @@ -21,9 +32,7 @@ async def slowmode( self, ctx, *, - interval: commands.TimedeltaConverter( - minimum=timedelta(seconds=0), maximum=timedelta(hours=6), default_unit="seconds" - ) = timedelta(seconds=0), + interval: SlowmodeInterval = timedelta(seconds=0), ): """Changes thread's or text channel's slowmode setting. diff --git a/redbot/cogs/modlog/__init__.py b/redbot/cogs/modlog/__init__.py index b6090d398bb..f44b8cd8a40 100644 --- a/redbot/cogs/modlog/__init__.py +++ b/redbot/cogs/modlog/__init__.py @@ -1,4 +1,5 @@ from redbot.core.bot import Red + from .modlog import ModLog diff --git a/redbot/cogs/modlog/modlog.py b/redbot/cogs/modlog/modlog.py index 90790020fd4..2650afa8fe9 100644 --- a/redbot/cogs/modlog/modlog.py +++ b/redbot/cogs/modlog/modlog.py @@ -1,16 +1,13 @@ -import asyncio from datetime import datetime, timezone - from typing import Optional, Union import discord -from redbot.core import checks, commands, modlog +from redbot.core import commands, modlog from redbot.core.bot import Red from redbot.core.i18n import Translator, cog_i18n -from redbot.core.utils.chat_formatting import bold, box, pagify +from redbot.core.utils.chat_formatting import bold, pagify from redbot.core.utils.menus import menu -from redbot.core.utils.predicates import MessagePredicate _ = Translator("ModLog", __file__) @@ -23,7 +20,7 @@ def __init__(self, bot: Red): super().__init__() self.bot = bot - async def red_delete_data_for_user(self, **kwargs): + async def red_delete_data_for_user(self, **_kwargs): """Nothing to delete""" return diff --git a/redbot/cogs/mutes/__init__.py b/redbot/cogs/mutes/__init__.py index bce1e30dbc9..e65e4e3e37c 100644 --- a/redbot/cogs/mutes/__init__.py +++ b/redbot/cogs/mutes/__init__.py @@ -1,4 +1,5 @@ from redbot.core.bot import Red + from .mutes import Mutes diff --git a/redbot/cogs/mutes/abc.py b/redbot/cogs/mutes/abc.py index 4bfc8e96ddc..3c5f934979a 100644 --- a/redbot/cogs/mutes/abc.py +++ b/redbot/cogs/mutes/abc.py @@ -1,8 +1,9 @@ from abc import ABC, abstractmethod -from typing import List, Tuple, Optional, Dict, Union from datetime import datetime +from typing import Dict, Optional, Union import discord + from redbot.core import Config, commands from redbot.core.bot import Red diff --git a/redbot/cogs/mutes/converters.py b/redbot/cogs/mutes/converters.py index ac3737d5d40..0a297dbe0a3 100644 --- a/redbot/cogs/mutes/converters.py +++ b/redbot/cogs/mutes/converters.py @@ -1,11 +1,11 @@ import logging import re -from typing import Union, Dict from datetime import timedelta +from typing import Dict, Union from discord.ext.commands.converter import Converter -from redbot.core import commands -from redbot.core import i18n + +from redbot.core import commands, i18n log = logging.getLogger("red.cogs.mutes") @@ -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( @@ -58,6 +58,6 @@ async def convert( except OverflowError: raise commands.BadArgument( _("The time provided is too long; use a more reasonable time.") - ) + ) from None result["reason"] = argument.strip() return result diff --git a/redbot/cogs/mutes/mutes.py b/redbot/cogs/mutes/mutes.py index 3ca1d40482c..73cb53d9aaa 100644 --- a/redbot/cogs/mutes/mutes.py +++ b/redbot/cogs/mutes/mutes.py @@ -1,29 +1,29 @@ import asyncio import contextlib -import discord import logging - from abc import ABC -from typing import cast, Optional, Dict, List, Tuple, Literal, Union from datetime import datetime, timedelta, timezone +from typing import Dict, List, Literal, Optional, Tuple, Union, cast -from .converters import MuteTime -from .voicemutes import VoiceMutes +import discord +from redbot.core import Config, checks, commands, i18n, modlog from redbot.core.bot import Red -from redbot.core import commands, checks, i18n, modlog, Config from redbot.core.utils import AsyncIter, bounded_gather, can_user_react_in from redbot.core.utils.chat_formatting import ( bold, - humanize_timedelta, humanize_list, + humanize_timedelta, inline, pagify, ) -from redbot.core.utils.mod import get_audit_reason from redbot.core.utils.menus import start_adding_reactions +from redbot.core.utils.mod import get_audit_reason from redbot.core.utils.predicates import MessagePredicate, ReactionPredicate +from .converters import MuteTime +from .voicemutes import VoiceMutes + T_ = i18n.Translator("Mutes", __file__) _ = lambda s: s @@ -50,7 +50,8 @@ "unknown_channel": _("The channel I tried to mute or unmute the user in isn't found."), "role_missing": _("The mute role no longer exists."), "voice_mute_permission": _( - "Because I don't have the Move Members permission, this will take into effect when the user rejoins." + "Because I don't have the Move Members permission," + " this will take into effect when the user rejoins." ), "is_not_voice_mute": _( "That user is channel muted in their current voice channel, not just voice muted." @@ -71,8 +72,6 @@ class CompositeMetaClass(type(commands.Cog), type(ABC)): coexist with discord.py's metaclass """ - pass - @i18n.cog_i18n(_) class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass): @@ -81,6 +80,7 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass): """ def __init__(self, bot: Red): + super().__init__() self.bot = bot self.config = Config.get_conf(self, 49615220001, force_registration=True) default_guild = { @@ -150,7 +150,7 @@ async def red_delete_data_for_user( ) all_members = await self.config.all_members() for g_id, data in all_members.items(): - for m_id, mutes in data.items(): + for m_id in data: if m_id == user_id: await self.config.member_from_ids(g_id, m_id).clear() @@ -196,7 +196,7 @@ async def _schema_0_to_1(self): if (channel := self.bot.get_channel(channel_id)) is None: channel = await self.bot.fetch_channel(channel_id) async with self.config.channel_from_id(channel_id).muted_users() as muted_users: - for mute_id, mute_data in muted_users.items(): + for mute_data in muted_users.values(): mute_data["guild"] = channel.guild.id except (discord.NotFound, discord.Forbidden): await self.config.channel_from_id(channel_id).clear() @@ -272,7 +272,7 @@ async def _clean_tasks(self): if task.done(): try: - r = task.result() + task.result() except Exception: if is_debug: log.exception("Dead task when trying to unmute") @@ -297,7 +297,7 @@ async def _handle_server_unmutes(self): task_name = f"server-unmute-{g_id}-{u_id}" if task_name in self._unmute_tasks: continue - log.debug(f"Creating task: {task_name}") + log.debug("Creating task: %s", task_name) self._unmute_tasks[task_name] = asyncio.create_task( self._auto_unmute_user(guild, self._server_mutes[guild.id][u_id]) ) @@ -362,7 +362,7 @@ async def _handle_channel_unmutes(self): """This is where the logic for handling channel unmutes is taken care of""" log.debug("Checking channel unmutes") multiple_mutes = {} - for c_id, c_data in self._channel_mutes.items(): + for c_id in self._channel_mutes: for u_id in self._channel_mutes[c_id]: if ( not self._channel_mutes[c_id][u_id] @@ -391,7 +391,7 @@ async def _handle_channel_unmutes(self): task_name = f"server-unmute-channels-{guild.id}-{user}" if task_name in self._unmute_tasks: continue - log.debug(f"Creating task: {task_name}") + log.debug("Creating task: %s", task_name) member = guild.get_member(user) self._unmute_tasks[task_name] = asyncio.create_task( self._auto_channel_unmute_user_multi(member, guild, channels) @@ -400,7 +400,7 @@ async def _handle_channel_unmutes(self): else: for channel, mute_data in channels.items(): task_name = f"channel-unmute-{channel}-{user}" - log.debug(f"Creating task: {task_name}") + log.debug("Creating task: %s", task_name) if task_name in self._unmute_tasks: continue self._unmute_tasks[task_name] = asyncio.create_task( @@ -720,7 +720,7 @@ async def on_guild_channel_update( if not user: send_dm_notification = False user = discord.Object(id=user_id) - log.debug(f"{user} - {type(user)}") + log.debug("%s - %s", user, type(user)) to_del.append(user_id) log.debug("creating case") if voice_mute: @@ -785,7 +785,6 @@ async def on_member_join(self, member: discord.Member): @commands.guild_only() async def muteset(self, ctx: commands.Context): """Mute settings.""" - pass @muteset.command() @commands.guild_only() @@ -807,13 +806,15 @@ async def showmoderator(self, ctx, true_or_false: bool): if true_or_false: await ctx.send( _( - "I will include the name of the moderator who issued the mute when sending a DM to a user." + "I will include the name of the moderator" + " who issued the mute when sending a DM to a user." ) ) else: await ctx.send( _( - "I will not include the name of the moderator who issued the mute when sending a DM to a user." + "I will not include the name of the moderator" + " who issued the mute when sending a DM to a user." ) ) @@ -953,7 +954,7 @@ async def make_mute_role(self, ctx: commands.Context, *, name: str): name=name, permissions=perms, reason=_("Mute role setup") ) await self.config.guild(ctx.guild).mute_role.set(role.id) - # save the role early incase of issue later + # save the role early in case of issue later except discord.errors.Forbidden: return await ctx.send(_("I could not create a muted role in this server.")) errors = [] @@ -1176,7 +1177,7 @@ async def mute( ctx: commands.Context, users: commands.Greedy[discord.Member], *, - time_and_reason: MuteTime = {}, + time_and_reason: MuteTime = None, ): """Mute users. @@ -1190,6 +1191,7 @@ async def mute( `[p]mute @member1 3 days` """ + time_and_reason = time_and_reason or {} if not users: return await ctx.send_help() if ctx.me in users: @@ -1224,7 +1226,7 @@ async def mute( if success["success"]: success_list.append(user) if success["channels"]: - # incase we only muted a user in 1 channel not all + # in case we only muted a user in 1 channel not all issue_list.append(success) await modlog.create_case( self.bot, @@ -1328,7 +1330,7 @@ async def channel_mute( ctx: commands.Context, users: commands.Greedy[discord.Member], *, - time_and_reason: MuteTime = {}, + time_and_reason: MuteTime = None, ): """Mute a user in the current text channel (or in the parent of the current thread). @@ -1341,6 +1343,7 @@ async def channel_mute( `[p]mutechannel @member1 @member2 spam 5 hours` `[p]mutechannel @member1 3 days` """ + time_and_reason = time_and_reason or {} if not users: return await ctx.send_help() if ctx.me in users: @@ -1571,7 +1574,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"]) @@ -1592,8 +1595,9 @@ async def mute_user( if not guild.me.guild_permissions.manage_roles or role >= guild.me.top_role: ret["reason"] = _(MUTE_UNMUTE_ISSUES["permissions_issue_role"]) return ret - # This is here to prevent the modlog case from happening on role updates - # we need to update the cache early so it's there before we receive the member_update event + # This is here to prevent the modlog case from happening on role updates. + # We need to update the cache early so it's there + # before we receive the member_update event. if guild.id not in self._server_mutes: self._server_mutes[guild.id] = {} diff --git a/redbot/cogs/mutes/voicemutes.py b/redbot/cogs/mutes/voicemutes.py index 1d1a58c385d..e41b37dca22 100644 --- a/redbot/cogs/mutes/voicemutes.py +++ b/redbot/cogs/mutes/voicemutes.py @@ -1,18 +1,18 @@ -from typing import Optional, Tuple, Union -from datetime import timezone, timedelta, datetime -from .abc import MixinMeta +from datetime import datetime, timedelta, timezone +from typing import Optional, Tuple import discord -from redbot.core import commands, checks, i18n, modlog + +from redbot.core import commands, i18n, modlog from redbot.core.utils.chat_formatting import ( - bold, - humanize_timedelta, + format_perms_list, humanize_list, + humanize_timedelta, pagify, - format_perms_list, ) from redbot.core.utils.mod import get_audit_reason +from .abc import MixinMeta from .converters import MuteTime _ = i18n.Translator("Mutes", __file__) @@ -71,7 +71,7 @@ async def voice_mute( ctx: commands.Context, users: commands.Greedy[discord.Member], *, - time_and_reason: MuteTime = {}, + time_and_reason: MuteTime = None, ): """Mute a user in their current voice channel. @@ -83,6 +83,7 @@ async def voice_mute( Examples: `[p]voicemute @member1 @member2 spam 5 hours` `[p]voicemute @member1 3 days`""" + time_and_reason = time_and_reason or {} if not users: return await ctx.send_help() if ctx.me in users: diff --git a/redbot/cogs/permissions/converters.py b/redbot/cogs/permissions/converters.py index 308fd71a7cb..4b27bc5443c 100644 --- a/redbot/cogs/permissions/converters.py +++ b/redbot/cogs/permissions/converters.py @@ -1,6 +1,6 @@ import itertools import re -from typing import NamedTuple, Union, Optional +from typing import NamedTuple, Optional, Union import discord diff --git a/redbot/cogs/permissions/permissions.py b/redbot/cogs/permissions/permissions.py index 8856f9cc2a2..e3471ef4d6c 100644 --- a/redbot/cogs/permissions/permissions.py +++ b/redbot/cogs/permissions/permissions.py @@ -2,25 +2,26 @@ import io import textwrap from copy import copy -from typing import Union, Optional, Dict, List, Tuple, Any, Iterator, ItemsView, Literal, cast +from typing import Any, Dict, ItemsView, Iterator, List, Literal, Optional, Tuple, Union, cast import discord import yaml -from schema import And, Or, Schema, SchemaError, Optional as UseOptional +from schema import And, Optional as UseOptional, Or, Schema, SchemaError + from redbot.core import checks, commands, config from redbot.core.bot import Red from redbot.core.i18n import Translator, cog_i18n from redbot.core.utils import can_user_react_in from redbot.core.utils.chat_formatting import box, error, success from redbot.core.utils.menus import start_adding_reactions -from redbot.core.utils.predicates import ReactionPredicate, MessagePredicate +from redbot.core.utils.predicates import MessagePredicate, ReactionPredicate from .converters import ( - CogOrCommand, - RuleType, ClearableRuleType, - GuildUniqueObjectFinder, + CogOrCommand, GlobalUniqueObjectFinder, + GuildUniqueObjectFinder, + RuleType, ) _ = Translator("Permissions", __file__) @@ -203,7 +204,6 @@ async def __permissions_hook(self, ctx: commands.Context) -> Optional[bool]: @commands.group() async def permissions(self, ctx: commands.Context): """Command permission management tools.""" - pass @permissions.command(name="explain") async def permissions_explain(self, ctx: commands.Context): @@ -624,7 +624,7 @@ async def _clear_rules(self, guild_id: int) -> None: self.bot.clear_permission_rules(guild_id, preserve_default_rule=False) for category in (COG, COMMAND): async with self.config.custom(category).all() as all_rules: - for name, rules in all_rules.items(): + for rules in all_rules.values(): rules.pop(str(guild_id), None) async def _permissions_acl_set( diff --git a/redbot/cogs/reports/__init__.py b/redbot/cogs/reports/__init__.py index 36de02e4245..3c91b2451ee 100644 --- a/redbot/cogs/reports/__init__.py +++ b/redbot/cogs/reports/__init__.py @@ -1,4 +1,5 @@ from redbot.core.bot import Red + from .reports import Reports diff --git a/redbot/cogs/reports/reports.py b/redbot/cogs/reports/reports.py index 24c049efdea..cb708e4159f 100644 --- a/redbot/cogs/reports/reports.py +++ b/redbot/cogs/reports/reports.py @@ -1,21 +1,21 @@ -import logging import asyncio -from typing import Union, List, Literal -from datetime import timedelta -from copy import copy import contextlib +import logging +from copy import copy +from datetime import timedelta +from typing import List, Literal, Union + import discord from redbot.core import Config, checks, commands -from redbot.core.utils import AsyncIter -from redbot.core.utils.chat_formatting import pagify, box -from redbot.core.utils.antispam import AntiSpam from redbot.core.bot import Red from redbot.core.i18n import Translator, cog_i18n, set_contextual_locales_from_guild +from redbot.core.utils import AsyncIter +from redbot.core.utils.antispam import AntiSpam +from redbot.core.utils.chat_formatting import box, pagify from redbot.core.utils.predicates import MessagePredicate from redbot.core.utils.tunnel import Tunnel - _ = Translator("Reports", __file__) log = logging.getLogger("red.reports") @@ -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) @@ -81,8 +81,8 @@ async def red_delete_data_for_user( if not steps % 100: await asyncio.sleep(0) # yield context - if ticket.get("report", {}).get("user_id", 0) == user_id: - paths.append((guild_id_str, ticket_number)) + if ticket.get("report", {}).get("user_id", 0) == user_id: + paths.append((guild_id_str, ticket_number)) async with self.config.custom("REPORT").all() as all_reports: async for guild_id_str, ticket_number in AsyncIter(paths, steps=100): @@ -102,7 +102,6 @@ def tunnels(self): @commands.group(name="reportset") async def reportset(self, ctx: commands.Context): """Manage Reports.""" - pass @checks.admin_or_permissions(manage_guild=True) @reportset.command(name="output") @@ -229,7 +228,7 @@ async def send_report(self, ctx: commands.Context, msg: discord.Message, guild: await Tunnel.message_forwarder( destination=channel, content=send_content, embed=em, files=files ) - except (discord.Forbidden, discord.HTTPException): + except discord.HTTPException: return None await self.config.custom("REPORT", guild.id, ticket_number).report.set( @@ -308,7 +307,8 @@ async def report(self, ctx: commands.Context, *, _report: str = ""): if await self.config.guild(ctx.guild).output_channel() is None: await author.send( _( - "This server has no reports channel set up. Please contact a server admin." + "This server has no reports channel set up." + " Please contact a server admin." ) ) else: diff --git a/redbot/cogs/streams/streams.py b/redbot/cogs/streams/streams.py index 0ff5840f814..c0ba2b70df3 100644 --- a/redbot/cogs/streams/streams.py +++ b/redbot/cogs/streams/streams.py @@ -1,36 +1,35 @@ +import asyncio +import contextlib +import logging +import re +from collections import defaultdict +from datetime import datetime +from typing import Dict, List, Optional, Tuple, Union + +import aiohttp import discord -from redbot.core.utils.chat_formatting import humanize_list + +from redbot.core import Config, checks, commands from redbot.core.bot import Red -from redbot.core import checks, commands, Config -from redbot.core.i18n import cog_i18n, Translator, set_contextual_locales_from_guild +from redbot.core.i18n import Translator, cog_i18n, set_contextual_locales_from_guild from redbot.core.utils._internal_utils import send_to_owners_with_prefix_replaced -from redbot.core.utils.chat_formatting import escape, inline, pagify +from redbot.core.utils.chat_formatting import escape, humanize_list, inline, pagify -from .streamtypes import ( - PicartoStream, - Stream, - TwitchStream, - YoutubeStream, -) +from . import streamtypes as _streamtypes from .errors import ( APIError, InvalidTwitchCredentials, InvalidYoutubeCredentials, OfflineStream, StreamNotFound, - StreamsError, YoutubeQuotaExceeded, ) -from . import streamtypes as _streamtypes - -import re -import logging -import asyncio -import aiohttp -import contextlib -from datetime import datetime -from collections import defaultdict -from typing import Optional, List, Tuple, Union, Dict +from .streamtypes import ( + PicartoStream, + Stream, + TwitchStream, + YoutubeStream, +) MAX_RETRY_COUNT = 10 @@ -81,7 +80,7 @@ def __init__(self, bot: Red): self.yt_cid_pattern = re.compile("^UC[-_A-Za-z0-9]{21}[AQgw]$") - async def red_delete_data_for_user(self, **kwargs): + async def red_delete_data_for_user(self, **_kwargs): """Nothing to delete""" return @@ -129,8 +128,8 @@ async def _notify_owner_about_missing_twitch_secret(self) -> None: "5. Copy your client ID and your client secret into:\n" "{command}" "\n\n" - "Note: These tokens are sensitive and should only be used in a private channel " - "or in DM with the bot." + "Note: These tokens are sensitive and should only be used in a private channel" + " or in DM with the bot." ).format( link="https://dev.twitch.tv/console/apps", command=inline( @@ -152,11 +151,12 @@ async def get_twitch_bearer_token(self, api_tokens: Optional[Dict] = None) -> No ) try: tokens["client_secret"] - if notified_owner_missing_twitch_secret is True: - await self.config.notified_owner_missing_twitch_secret.set(False) except KeyError: if notified_owner_missing_twitch_secret is False: asyncio.create_task(self._notify_owner_about_missing_twitch_secret()) + else: + if notified_owner_missing_twitch_secret is True: + await self.config.notified_owner_missing_twitch_secret.set(False) async with aiohttp.ClientSession() as session: async with session.post( "https://id.twitch.tv/oauth2/token", @@ -308,7 +308,6 @@ async def check_online( @checks.mod_or_permissions(manage_channels=True) async def streamalert(self, ctx: commands.Context): """Manage automated stream alerts.""" - pass @streamalert.group(name="twitch", invoke_without_command=True) async def _twitch( @@ -497,7 +496,6 @@ async def stream_alert(self, ctx: commands.Context, _class, channel_name, discor @checks.mod_or_permissions(manage_channels=True) async def streamset(self, ctx: commands.Context): """Manage stream alert settings.""" - pass @streamset.command(name="timer") @checks.is_owner() @@ -569,7 +567,6 @@ async def youtubekey(self, ctx: commands.Context): @commands.guild_only() async def message(self, ctx: commands.Context): """Manage custom messages for stream alerts.""" - pass @message.command(name="mention") @commands.guild_only() @@ -613,7 +610,6 @@ async def clear_message(self, ctx: commands.Context): @commands.guild_only() async def mention(self, ctx: commands.Context): """Manage mention settings for stream alerts.""" - pass @mention.command(aliases=["everyone"]) @commands.guild_only() @@ -781,8 +777,6 @@ async def check_exists(stream): pass except StreamNotFound: return False - except StreamsError: - raise return True async def _stream_alerts(self): @@ -944,7 +938,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..8add920a0ad 100644 --- a/redbot/cogs/streams/streamtypes.py +++ b/redbot/cogs/streams/streamtypes.py @@ -3,26 +3,27 @@ import json import logging import time -from dateutil.parser import parse as parse_time +import xml.etree.ElementTree as ET +from datetime import datetime, timedelta, timezone from random import choice from string import ascii_letters -from datetime import datetime, timedelta, timezone -import xml.etree.ElementTree as ET -from typing import ClassVar, Optional, List, Tuple +from typing import ClassVar, List, Optional, Tuple import aiohttp import discord +from dateutil.parser import parse as parse_time + +from redbot.core.i18n import Translator +from redbot.core.utils.chat_formatting import humanize_number, humanize_timedelta from .errors import ( APIError, - OfflineStream, InvalidTwitchCredentials, InvalidYoutubeCredentials, + OfflineStream, StreamNotFound, YoutubeQuotaExceeded, ) -from redbot.core.i18n import Translator -from redbot.core.utils.chat_formatting import humanize_number, humanize_timedelta TWITCH_BASE_URL = "https://api.twitch.tv" TWITCH_ID_ENDPOINT = TWITCH_BASE_URL + "/helix/users" @@ -47,7 +48,6 @@ def rnd(url): def get_video_ids_from_feed(feed): root = ET.fromstring(feed) - rss_video_ids = [] for child in root.iter("{http://www.w3.org/2005/Atom}entry"): for i in child.iter("{http://www.youtube.com/xml/schemas/2015}videoId"): yield i.text @@ -76,7 +76,7 @@ def display_name(self) -> Optional[str]: async def is_online(self): raise NotImplementedError() - def make_embed(self): + def make_embed(self, data): raise NotImplementedError() def iter_messages(self): @@ -146,9 +146,9 @@ async def is_online(self): for video_id in get_video_ids_from_feed(rssdata): if video_id in self.not_livestreams: - log.debug(f"video_id in not_livestreams: {video_id}") + log.debug("video_id in not_livestreams: %s", video_id) continue - log.debug(f"video_id not in not_livestreams: {video_id}") + log.debug("video_id not in not_livestreams: %s", video_id) params = { "key": self._token["api_key"], "id": video_id, @@ -175,7 +175,7 @@ async def is_online(self): continue video_data = data.get("items", [{}])[0] stream_data = video_data.get("liveStreamingDetails", {}) - log.debug(f"stream_data for {video_id}: {stream_data}") + log.debug("stream_data for %s: %r", video_id, stream_data) if ( stream_data and stream_data != "None" @@ -195,10 +195,10 @@ async def is_online(self): self.not_livestreams.append(video_id) if video_id in self.livestreams: self.livestreams.remove(video_id) - log.debug(f"livestreams for {self.name}: {self.livestreams}") - log.debug(f"not_livestreams for {self.name}: {self.not_livestreams}") + log.debug("livestreams for %s: %r", self.name, self.livestreams) + log.debug("not_livestreams for %s: %r", 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 in case 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 = { @@ -345,7 +345,10 @@ async def wait_for_rate_limit_reset(self) -> None: wait_time = reset_time - current_time + 0.1 await asyncio.sleep(wait_time) - async def get_data(self, url: str, params: dict = {}) -> Tuple[Optional[int], dict]: + async def get_data( + self, url: str, params: Optional[dict] = None + ) -> Tuple[Optional[int], dict]: + params = params or {} header = {"Client-ID": str(self._client_id)} if self._bearer is not None: header["Authorization"] = f"Bearer {self._bearer}" diff --git a/redbot/cogs/trivia/__init__.py b/redbot/cogs/trivia/__init__.py index 77287565151..35fb89134ca 100644 --- a/redbot/cogs/trivia/__init__.py +++ b/redbot/cogs/trivia/__init__.py @@ -1,9 +1,13 @@ """Package for Trivia cog.""" from redbot.core.bot import Red -from .trivia import * -from .session import * -from .log import * +from .trivia import InvalidListError, Trivia, get_core_lists, get_list + +__all__ = ( + "InvalidListError", + "get_core_lists", + "get_list", +) async def setup(bot: Red) -> None: diff --git a/redbot/cogs/trivia/converters.py b/redbot/cogs/trivia/converters.py index 211ed5d4bbe..66cf8e0f296 100644 --- a/redbot/cogs/trivia/converters.py +++ b/redbot/cogs/trivia/converters.py @@ -12,7 +12,7 @@ def finite_float(arg: str) -> float: try: ret = float(arg) except ValueError: - raise commands.BadArgument(_("`{arg}` is not a number.").format(arg=arg)) + raise commands.BadArgument(_("`{arg}` is not a number.").format(arg=arg)) from None if not math.isfinite(ret): raise commands.BadArgument(_("`{arg}` is not a finite number.").format(arg=ret)) return ret diff --git a/redbot/cogs/trivia/data/lists/2015.yaml b/redbot/cogs/trivia/data/lists/2015.yaml index bb802e7d7ef..48edb69adc9 100644 --- a/redbot/cogs/trivia/data/lists/2015.yaml +++ b/redbot/cogs/trivia/data/lists/2015.yaml @@ -22,8 +22,8 @@ Brazil began to nationalise its (What?) in 2015?: - Rainforest Celebrity chef Gordon Ramsey celebrated what birthday in 2015?: - 49 -- fourty-nine -- fourty nine +- forty-nine +- forty nine - 49th Christian is the lead character in the film 2015 adaptation of what extraordinarily successful book?: - Fifty Shades of Grey diff --git a/redbot/cogs/trivia/data/lists/anime.yaml b/redbot/cogs/trivia/data/lists/anime.yaml index f3247a67678..8f90b03f709 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..799ee417489 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,11 +83,11 @@ 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 -An etching tecnique in which a solution of asphalt or resin is used on the plate. It produces prints with rich, gray tones?: +- Rococo +An etching technique in which a solution of asphalt or resin is used on the plate. It produces prints with rich, gray tones?: - Aquatint Artwork, usually paintings, characterized by a simplified style, nonscientific perspective, and bold colors. The artists are generally not professionally trained?: - Naive art @@ -454,7 +454,7 @@ What is the opposite of an utopia?: - Dystopia What is the surname of Cathy in Wuthering Heights?: - Earnshaw -What nationality was Jospeh Conrad?: +What nationality was Joseph Conrad?: - Polish What nationality were most of the Impressionist painters?: - French @@ -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?: @@ -633,7 +633,7 @@ Which famous book begins with the line 'The mole had been working very hard all - The Wind In The Willows "Which famous book begins with the line: 'Marley was dead, to begin with. There was no doubt about that'?": - A Christmas Carol -"Which famous book begins with the line: 'Not long ago, there lived in London a young married couple of Dalmation dogs named Pongo and Misses Pongo'?": +"Which famous book begins with the line: 'Not long ago, there lived in London a young married couple of Dalmatian dogs named Pongo and Misses Pongo'?": - 101 Dalmations Which famous book contains the line 'It is a truth universally acknowledged that a single man in possession of a good fortune must be in want of a wife'?: - Pride & Prejudice @@ -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..6d9ae1380dc 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 @@ -1705,7 +1705,7 @@ What school do the Wildcats of High School Musical attend?: What season of the year was Bambi born in?: - Spring What shape are the earrings worn by Princess Jasmine in Aladdin?: -- Traingles +- Triangles What song does Cinderella sing as she is getting dressed in the morning?: - A Dream is a Wish Your Heart Makes What song does Tony and Joey sing to Lady and the Tramp?: @@ -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..fe4835a7fe3 100644 --- a/redbot/cogs/trivia/data/lists/entertainment.yaml +++ b/redbot/cogs/trivia/data/lists/entertainment.yaml @@ -338,7 +338,7 @@ Name the dog in the Yankee Doodle cartoons?: Name the fastest mouse in all of Mexico?: - Speedy gonzalez Name the late actor who played Obi-Wan Kenobi in Star Wars?: -- Alec Guiness +- Alec Guinness Name the musical film named after a state?: - Oklahoma Name the ranger who was always after Yogi Bear?: @@ -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 @@ -859,7 +859,7 @@ When danger appeared, Quick Draw McGraw became which super hero?: - El KaBong When not fighting crime, what did Underdog do for a living?: - Shoeshine boy -Where are Rocket J. Squirel and Bullwinkle Moose from?: +Where are Rocket J. Squirrel and Bullwinkle Moose from?: - Frostbite Falls Where did Clark Kent attend college?: - Metropolis University @@ -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/finalfantasy.yaml b/redbot/cogs/trivia/data/lists/finalfantasy.yaml index a1c5d587143..2936a278ee5 100644 --- a/redbot/cogs/trivia/data/lists/finalfantasy.yaml +++ b/redbot/cogs/trivia/data/lists/finalfantasy.yaml @@ -1257,7 +1257,7 @@ AUTHOR: KrisDLuna, Iris, Slackeffect, Falzar - "False" (FFXV) There is a real-life car modeled after the Regalia produced by which automotive company?: - Audi -(FFXV) There is a sniper rifle (with useable scope) in the game. True/False?: +(FFXV) There is a sniper rifle (with usable scope) in the game. True/False?: - "True" (FFXV) There is/was a glitch in the game which allowed the player to have Aranea join the party (not just assist during battle). True/False?: - "True" diff --git a/redbot/cogs/trivia/data/lists/games.yaml b/redbot/cogs/trivia/data/lists/games.yaml index 63e4335a829..80761ecaba9 100644 --- a/redbot/cogs/trivia/data/lists/games.yaml +++ b/redbot/cogs/trivia/data/lists/games.yaml @@ -414,7 +414,7 @@ What new button is featured on the PS4's Dualshock 4 controllers?: What number is on the opposite side of the "five" on a dice?: - Two - 2 -What number is the Pokemon "Scyther" in the Pokemon GameBoy games: +What number is the Pokemon "Scyther" in the Pokemon Game Boy games: - 123 "What part of a prehistoric tree grants invulnerability in \"Uncharted 2: Among Thieves\"?": - Sap diff --git a/redbot/cogs/trivia/data/lists/general.yaml b/redbot/cogs/trivia/data/lists/general.yaml index 4523428b07b..c1a7f4c469b 100644 --- a/redbot/cogs/trivia/data/lists/general.yaml +++ b/redbot/cogs/trivia/data/lists/general.yaml @@ -204,9 +204,9 @@ How many cards are there in a deck of cards?: - 52 How many cathedrals are in mainland UK (at early 2000s)?: - 42 -- fourtytwo -- fourty-two -- fourty two +- fortytwo +- forty-two +- forty two How many chambers are there in the human heart?: - Four - 4 @@ -948,7 +948,7 @@ What is the birthstone for April?: - Diamond What is the boiling point of water in degrees Celsius?: - 100 -What is the boiling point of water in degrees Farenheit?: +What is the boiling point of water in degrees Fahrenheit?: - 212 - two hundred and twelve What is the capital of Canada?: @@ -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 diff --git a/redbot/cogs/trivia/data/lists/geography.yaml b/redbot/cogs/trivia/data/lists/geography.yaml index 6c52a59598c..3319e6d9771 100644 --- a/redbot/cogs/trivia/data/lists/geography.yaml +++ b/redbot/cogs/trivia/data/lists/geography.yaml @@ -76,7 +76,7 @@ What mountain is closest to the Moon?: What continent contains the most fresh water?: - Antarctica What ocean is home to 75% of the Earth's volcanoes?: -- Pacific +- Pacific - Pacific Ocean What is the largest city in the world based on surface area?: - New York @@ -158,7 +158,7 @@ What is the earliest known walled city?: What is the oldest city in India?: - Varanasi What is the primary mountain range in Iran?: -- Zagros +- Zagros - Zagros Mountains What is the least populated U.S. state?: - Wyoming 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/harrypotter.yaml b/redbot/cogs/trivia/data/lists/harrypotter.yaml index c51450f0b9d..eb9204f21c4 100644 --- a/redbot/cogs/trivia/data/lists/harrypotter.yaml +++ b/redbot/cogs/trivia/data/lists/harrypotter.yaml @@ -518,7 +518,7 @@ Who stopped Harry and Ron getting into Hogwarts their second year?: Lumos is the spell that produces light from the user’s wand. What spell turns it off?: - Nox How old was the kid that Dudley and his gang beat up that "deserved it"?: -- 10 +- 10 - ten What magical creatures attacked Harry and Dudley in Little Whinging?: - Dementors @@ -608,4 +608,4 @@ How many hours into the past do Harry and Hermione travel in an attempt to rescu - Three How many muggles did Peter Pettigrew kill when he faked his own death?: - 12 -- Twelve \ No newline at end of file +- Twelve diff --git a/redbot/cogs/trivia/data/lists/hockey.yaml b/redbot/cogs/trivia/data/lists/hockey.yaml index 6265570e11b..e9bfcbe3981 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 @@ -2702,4 +2702,4 @@ Name one of six jersey numbers have been retired by the Vancouver Canucks.: - 16 - 19 - 22 -- 33 \ No newline at end of file +- 33 diff --git a/redbot/cogs/trivia/data/lists/leagueoflegends.yaml b/redbot/cogs/trivia/data/lists/leagueoflegends.yaml index 69415aa9c88..a1ccf64d485 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 @@ -618,7 +618,7 @@ Who has a Nosferatu skin?: - Vladimir Who has a Phantom skin?: - Karthus -Who has a Pharoah skin?: +Who has a Pharaoh skin?: - Nidalee Who has a Phoenix skin?: - Quinn 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/mlb.yaml b/redbot/cogs/trivia/data/lists/mlb.yaml index f7be298b28e..b8b1b8665f3 100644 --- a/redbot/cogs/trivia/data/lists/mlb.yaml +++ b/redbot/cogs/trivia/data/lists/mlb.yaml @@ -54,7 +54,7 @@ What was the original name of the Houston Astros? Houston Colt .45s, or Houston - Houston colt - houston colt - Houston Colt 45s -- Houston colt 45s +- Houston colt 45s - houston colt 45s - Houston Colt .45 - Houston colt .45 @@ -96,12 +96,12 @@ What state has produced the fewest MLB players? Idaho, Hawaii, or Alaska: - alaska What was Babe Ruth's first name? George, Babe, Henry: - George - - george + - george What state has produced the most MLB players? New York, Texas, California: - Cali - California - california -- cali +- cali What team became the Washington Nationals in 2005? Washington Senators, Montreal Expos: - Montreal Expos - montreal expos diff --git a/redbot/cogs/trivia/data/lists/music.yaml b/redbot/cogs/trivia/data/lists/music.yaml index a2965a54c80..667ad64a49a 100644 --- a/redbot/cogs/trivia/data/lists/music.yaml +++ b/redbot/cogs/trivia/data/lists/music.yaml @@ -89,8 +89,6 @@ Which band had a hit with "He Ain't Heavy, He's My Brother" in both 1969 and 198 - The Hollies Who had a hit in 1982 with 'Maneater'?: - Hall And Oates -Which pop band had a hit single with 'Open Your Heart' in 1981?: -- The Human League Who sang the TV theme tune to 'Rawhide'?: - Frankie Laine Who was the lead singer of The Four Seasons in the 1960s and 1970s?: @@ -240,7 +238,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..93edb788529 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 heroes tacking 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..1dbc93c1839 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 @@ -497,15 +497,15 @@ _________ Brewers (MLB): _________ Mets (MLB): - New York _________ Phillies (MLB): -- Philadelphia +- Philadelphia _________ Pirates (MLB): -- Pittsburgh +- Pittsburgh _________ Padres (MLB): -- San Diego +- San Diego _________ Giants (MLB): -- San Francisco +- San Francisco _________ Cardinals (MLB): - St. Louis -- St Louis +- St Louis _________ Nationals (MLB): - Washington diff --git a/redbot/cogs/trivia/data/lists/starwars.yaml b/redbot/cogs/trivia/data/lists/starwars.yaml index 82e48b70b15..22a4b81b206 100644 --- a/redbot/cogs/trivia/data/lists/starwars.yaml +++ b/redbot/cogs/trivia/data/lists/starwars.yaml @@ -32,7 +32,7 @@ How did Rogue One recreate Tarkin?: - CGI How fast did the Millennium Falcon do the Kessel Run?: - 12 Parsecs -How fast does Han Solo claim the Millenium Falcon made the Kessel Run in?: +How fast does Han Solo claim the Millennium Falcon made the Kessel Run in?: - less than 12 parsecs - under 12 parsecs - under twelve parsecs @@ -49,7 +49,7 @@ How many Star Wars films did George Lucas intend to create before he changed his How many Stormtroopers hit a main character?: - 1 - one -How many TIE fighters pursue the Millenium Falcon during its escape from the first Death Star?: +How many TIE fighters pursue the Millennium Falcon during its escape from the first Death Star?: - 4 - four How many blades does a temple guards lightsaber have?: @@ -213,13 +213,13 @@ What do the Rebels find on Geonosis?: - a geonosian queen egg - genosian queen egg - last genosian -What docking bay berth housed the Millenium Falcon when Luke Skywalker boarded it for the first time?: +What docking bay berth housed the Millennium Falcon when Luke Skywalker boarded it for the first time?: - 94 What does AT-AT stand for?: - All Terrain Armored Transport What does Admiral Piet call the assembled bounty hunters on his Star Destroyer bridge?: - scum -What does Darth Vader disallow the assembled bounty hunters to do in their pursuit of the Millenium Falcon and its crew?: +What does Darth Vader disallow the assembled bounty hunters to do in their pursuit of the Millennium Falcon and its crew?: - disintegration - disintegrations What does Luke tell Uncle Owen he wants to pick up at Toche Station?: @@ -322,7 +322,7 @@ What it the total amount of payment Obi-Wan Kenobi guarantees Han Solo while in - 10000 What language do the Moisture vaporators speak on the Lars Farmstead?: - binary -What model of light freighter is the Millenium Falcon?: +What model of light freighter is the Millennium Falcon?: - YT-1300 - YT1300 What other TV show did Mark Hamill guest voice on?: @@ -409,7 +409,7 @@ What was Rex in command of?: What was Wedge Antilles comm designation during the Battle of Yavin?: - Red Two - red 2 -What was the callsign of the Rouge Squadron Snowspeeder that located Luke and Han on Hoth?: +What was the callsign of the Rogue Squadron Snowspeeder that located Luke and Han on Hoth?: - Rogue 2 - Rogue Two What was the callsign of the shuttle the Rebels used to penetrate the shield and land on Endor?: @@ -619,7 +619,7 @@ Who is the most hated character in the star wars universe?: Who is the most notable force sensitive to couple Teräs Käsi techniques into their lightsaber style?: - Darth Maul - Maul -Who is the new Admiral that psi is happy thats canon?: +Who is the new Admiral that psi is happy that's canon?: - Thrawn Who is the only pilot to survive the tactical assaults on both Death Stars?: - Wedge Antilles @@ -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/usstateabbreviations.yaml b/redbot/cogs/trivia/data/lists/usstateabbreviations.yaml index 1607fb6b973..0880efe925f 100644 --- a/redbot/cogs/trivia/data/lists/usstateabbreviations.yaml +++ b/redbot/cogs/trivia/data/lists/usstateabbreviations.yaml @@ -198,4 +198,4 @@ What state is WI?: What state is WV?: - West Virginia What state is WY?: -- Wyoming \ No newline at end of file +- Wyoming diff --git a/redbot/cogs/trivia/data/lists/warcraft.yaml b/redbot/cogs/trivia/data/lists/warcraft.yaml index 8dc0bb176f8..8ceccc4c437 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 @@ -710,7 +710,7 @@ Who was the main leader of Cult of the Damned?: - kelthuzad Who's the oldest of the Windrunner sisters?: - Alleria -"Whos missing: Skywall, Firelands, Abyssal Maw...": +"Who's missing: Skywall, Firelands, Abyssal Maw...": - Deepholm You were grinding in Western Plaguelands when you found Wildheart Bracers, which class would want this?: - Druid diff --git a/redbot/cogs/trivia/data/lists/worldcup.yaml b/redbot/cogs/trivia/data/lists/worldcup.yaml index 038ff2a4a6d..f5e78f9e183 100644 --- a/redbot/cogs/trivia/data/lists/worldcup.yaml +++ b/redbot/cogs/trivia/data/lists/worldcup.yaml @@ -300,7 +300,7 @@ Who placed fourth in the 1930 World Cup?: - International Stadium - International Stadium Yokohama - Nissan Stadium -"What was the name of the stadium where the 1998 World Cup final was held? (clue: the hosts were France)": +"What was the name of the stadium where the 1998 World Cup final was held? (clue: the hosts were France)": - Stade de France "What was the name of the stadium where the 1994 World Cup final was held? (clue: the hosts were United States)": - Rose Bowl @@ -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 diff --git a/redbot/cogs/trivia/schema.py b/redbot/cogs/trivia/schema.py index 0abc7c5fb30..958f20b9ccd 100644 --- a/redbot/cogs/trivia/schema.py +++ b/redbot/cogs/trivia/schema.py @@ -1,7 +1,7 @@ import itertools import math import re -from typing import Any, NoReturn +from typing import Any from schema import And, Const, Optional, Schema, SchemaError, SchemaMissingKeyError, Use diff --git a/redbot/cogs/trivia/session.py b/redbot/cogs/trivia/session.py index 6c5770a1f30..25713124d4f 100644 --- a/redbot/cogs/trivia/session.py +++ b/redbot/cogs/trivia/session.py @@ -1,13 +1,16 @@ """Module to manage trivia sessions.""" import asyncio -import time import random +import time from collections import Counter + import discord + from redbot.core import bank, errors from redbot.core.i18n import Translator -from redbot.core.utils.chat_formatting import box, bold, humanize_list, humanize_number +from redbot.core.utils.chat_formatting import bold, box, humanize_list from redbot.core.utils.common_filters import normalize_smartquotes + from .log import LOG __all__ = ["TriviaSession"] @@ -121,7 +124,8 @@ def _error_handler(self, fut): 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." + "An unexpected error occurred in the trivia session.\n" + "Check your console or logs for details." ) asyncio.create_task(self.ctx.send(msg)) self.stop() diff --git a/redbot/cogs/trivia/trivia.py b/redbot/cogs/trivia/trivia.py index ca2c8c47d0a..772aa0b684c 100644 --- a/redbot/cogs/trivia/trivia.py +++ b/redbot/cogs/trivia/trivia.py @@ -1,29 +1,28 @@ """Module for Trivia cog.""" import asyncio -import math +import io import pathlib from collections import Counter from typing import Any, Dict, List, Literal, Union -import schema -import io -import yaml import discord +import schema +import yaml -from redbot.core import Config, commands, checks, bank +from redbot.core import Config, bank, checks, commands from redbot.core.bot import Red from redbot.core.data_manager import cog_data_path from redbot.core.i18n import Translator, cog_i18n from redbot.core.utils import AsyncIter, can_user_react_in -from redbot.core.utils.chat_formatting import box, pagify, bold +from redbot.core.utils.chat_formatting import bold, box, pagify from redbot.core.utils.menus import start_adding_reactions from redbot.core.utils.predicates import MessagePredicate, ReactionPredicate from .checks import trivia_stop_check from .converters import finite_float from .log import LOG -from .session import TriviaSession from .schema import TRIVIA_LIST_SCHEMA, format_schema_error +from .session import TriviaSession __all__ = ("Trivia", "UNIQUE_ID", "InvalidListError", "get_core_lists", "get_list") @@ -34,8 +33,6 @@ class InvalidListError(Exception): """A Trivia list file is in invalid format.""" - pass - @cog_i18n(_) class Trivia(commands.Cog): @@ -220,7 +217,6 @@ async def triviaset_payout_multiplier(self, ctx: commands.Context, multiplier: f @commands.is_owner() async def triviaset_custom(self, ctx: commands.Context): """Manage Custom Trivia lists.""" - pass @triviaset_custom.command(name="list") async def custom_trivia_list(self, ctx: commands.Context): @@ -524,11 +520,13 @@ def _get_leaderboard(data: dict, key: str, top: int): try: priority.remove(key) except ValueError: - raise ValueError(f"{key} is not a valid key.") + raise ValueError(f"{key} is not a valid key.") from None # Put key last in reverse priority priority.append(key) items = data.items() for key in priority: + # https://github.com/PyCQA/pylint/issues/7100#issuecomment-1171283909 + # pylint: disable-next=cell-var-from-loop items = sorted(items, key=lambda t: t[1][key], reverse=True) max_name_len = max(map(lambda m: len(str(m)), data.keys())) # Headers @@ -622,7 +620,7 @@ def get_trivia_list(self, category: str) -> dict: try: path = next(p for p in self._all_lists() if p.stem == category) except StopIteration: - raise FileNotFoundError("Could not find the `{}` category.".format(category)) + raise FileNotFoundError("Could not find the `{}` category.".format(category)) from None return get_list(path) diff --git a/redbot/cogs/warnings/helpers.py b/redbot/cogs/warnings/helpers.py index 5f0a92056d5..36a4329532c 100644 --- a/redbot/cogs/warnings/helpers.py +++ b/redbot/cogs/warnings/helpers.py @@ -1,8 +1,9 @@ -from copy import copy import asyncio +from copy import copy + import discord -from redbot.core import Config, checks, commands +from redbot.core import Config, commands from redbot.core.commands.requires import PrivilegeLevel from redbot.core.i18n import Translator from redbot.core.utils.predicates import MessagePredicate diff --git a/redbot/cogs/warnings/warnings.py b/redbot/cogs/warnings/warnings.py index 708a056ff56..6dca89c6ddf 100644 --- a/redbot/cogs/warnings/warnings.py +++ b/redbot/cogs/warnings/warnings.py @@ -1,27 +1,24 @@ import asyncio import contextlib -from datetime import timezone from collections import namedtuple from copy import copy -from typing import Union, Optional, Literal +from typing import Literal, Union import discord from redbot.cogs.warnings.helpers import ( - warning_points_add_check, - get_command_for_exceeded_points, get_command_for_dropping_points, + get_command_for_exceeded_points, + warning_points_add_check, warning_points_remove_check, ) from redbot.core import Config, checks, commands, modlog from redbot.core.bot import Red from redbot.core.commands import UserInputOptional from redbot.core.i18n import Translator, cog_i18n -from redbot.core.utils import AsyncIter -from redbot.core.utils.chat_formatting import warning, pagify +from redbot.core.utils.chat_formatting import pagify from redbot.core.utils.menus import menu - _ = Translator("Warnings", __file__) @@ -113,7 +110,6 @@ async def register_warningtype(): @checks.guildowner_or_permissions(administrator=True) async def warningset(self, ctx: commands.Context): """Manage settings for Warnings.""" - pass @warningset.command() @commands.guild_only() @@ -144,13 +140,15 @@ async def showmoderator(self, ctx, true_or_false: bool): if true_or_false: await ctx.send( _( - "I will include the name of the moderator who issued the warning when sending a DM to a user." + "I will include the name of the moderator" + " who issued the warning when sending a DM to a user." ) ) else: await ctx.send( _( - "I will not include the name of the moderator who issued the warning when sending a DM to a user." + "I will not include the name of the moderator" + " who issued the warning when sending a DM to a user." ) ) @@ -206,7 +204,6 @@ async def warnaction(self, ctx: commands.Context): user is warned enough so that their points go over this threshold, the action will be executed. """ - pass @warnaction.command(name="add") @commands.guild_only() @@ -268,7 +265,6 @@ async def warnreason(self, ctx: commands.Context): Reasons must be given a name, description and points value. The name of the reason must be given when a user is warned. """ - pass @warnreason.command(name="create", aliases=["add"]) @commands.guild_only() @@ -395,7 +391,8 @@ async def warn( if member.top_role >= ctx.author.top_role and ctx.author != ctx.guild.owner: return await ctx.send( _( - "The person you're trying to warn is equal or higher than you in the discord hierarchy, you cannot warn them." + "The person you're trying to warn is equal or higher than you" + " in the discord hierarchy, you cannot warn them." ) ) guild_settings = await self.config.guild(ctx.guild).all() @@ -616,7 +613,6 @@ async def unwarn( try: user_id = member.id - member = member except AttributeError: user_id = member member = guild.get_member(user_id) diff --git a/redbot/core/__init__.py b/redbot/core/__init__.py index a755816f710..a0841ed262a 100644 --- a/redbot/core/__init__.py +++ b/redbot/core/__init__.py @@ -1,6 +1,6 @@ import discord as _discord -from .. import __version__, version_info, VersionInfo +from .. import VersionInfo, __version__, version_info from .config import Config __all__ = ["Config", "__version__", "version_info", "VersionInfo"] diff --git a/redbot/core/_debuginfo.py b/redbot/core/_debuginfo.py index 7a1fdd4a1d0..805b08ca354 100644 --- a/redbot/core/_debuginfo.py +++ b/redbot/core/_debuginfo.py @@ -16,7 +16,7 @@ from redbot.core.utils.chat_formatting import box -def noop_box(text: str, **kwargs) -> str: +def noop_box(text: str, **_kwargs) -> str: return text @@ -146,7 +146,6 @@ async def _get_red_vars_section(self) -> DebugInfoSection: owners.append(f"{u.id} ({u})") except discord.HTTPException: owners.append(f"{uid} (Unresolvable)") - owners_string = ", ".join(owners) or "None" parts.append(f"Owner(s): {', '.join(owners) or 'None'}") if self.bot is not None: diff --git a/redbot/core/_diagnoser.py b/redbot/core/_diagnoser.py index 42976f992a9..d83deb51ae9 100644 --- a/redbot/core/_diagnoser.py +++ b/redbot/core/_diagnoser.py @@ -8,6 +8,7 @@ from typing import TYPE_CHECKING, Awaitable, Callable, Iterable, List, Optional, Union import discord + from redbot.core import commands from redbot.core.i18n import Translator from redbot.core.utils import can_user_send_messages_in @@ -143,7 +144,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): @@ -372,6 +373,7 @@ async def _check_dpy_can_run(self) -> CheckResult: label = _("Global, cog and command checks") command = self.ctx.command try: + # pylint: disable-next=bad-super-call if await super(commands.Command, command).can_run(self.ctx): return CheckResult(True, label) except commands.DisabledCommand: @@ -584,7 +586,7 @@ async def _check_requires_bot_owner( ) async def _check_requires_permission_hooks( - self, cog_or_command: commands.CogCommandMixin + self, _cog_or_command: commands.CogCommandMixin ) -> CheckResult: label = _("Permission hooks") result = await self.bot.verify_permissions_hooks(self.ctx) @@ -816,7 +818,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/_sharedlibdeprecation.py b/redbot/core/_sharedlibdeprecation.py index db4bffa55ef..7c8321b9a1b 100644 --- a/redbot/core/_sharedlibdeprecation.py +++ b/redbot/core/_sharedlibdeprecation.py @@ -1,5 +1,5 @@ -from importlib.abc import MetaPathFinder import warnings +from importlib.abc import MetaPathFinder class SharedLibDeprecationWarning(DeprecationWarning): @@ -17,6 +17,7 @@ class SharedLibImportWarner(MetaPathFinder): def find_spec(self, fullname, path, target=None) -> None: """This is only supposed to print warnings, it won't ever return module spec.""" + del path, target parts = fullname.split(".") if parts[0] != "cog_shared" or len(parts) != 2: return None diff --git a/redbot/core/bank.py b/redbot/core/bank.py index fc93df0c686..79d1f894c33 100644 --- a/redbot/core/bank.py +++ b/redbot/core/bank.py @@ -3,17 +3,17 @@ import asyncio import logging from datetime import datetime, timezone -from typing import Union, List, Optional, TYPE_CHECKING, Literal from functools import wraps +from typing import TYPE_CHECKING, List, Literal, Optional, Union import discord from redbot.core.utils import AsyncIter from redbot.core.utils.chat_formatting import humanize_number -from . import Config, errors, commands -from .i18n import Translator +from . import Config, commands, errors from .errors import BankPruneError +from .i18n import Translator if TYPE_CHECKING: from .bot import Red @@ -629,16 +629,13 @@ async def get_leaderboard_position( guild = None else: guild = member.guild if hasattr(member, "guild") else None - try: - leaderboard = await get_leaderboard(None, guild) - except TypeError: - raise + + leaderboard = await get_leaderboard(None, guild) + pos = discord.utils.find(lambda x: x[1][0] == member.id, enumerate(leaderboard, 1)) + if pos is None: + return None else: - pos = discord.utils.find(lambda x: x[1][0] == member.id, enumerate(leaderboard, 1)) - if pos is None: - return None - else: - return pos[0] + return pos[0] async def get_account(member: Union[discord.Member, discord.User]) -> Account: @@ -751,7 +748,6 @@ async def get_bank_name(guild: discord.Guild = None) -> str: """ if await is_global(): - global _cache if _cache["bank_name"] is None: _cache["bank_name"] = await _config.bank_name() return _cache["bank_name"] @@ -785,7 +781,6 @@ async def set_bank_name(name: str, guild: discord.Guild = None) -> str: """ if await is_global(): await _config.bank_name.set(name) - global _cache _cache["bank_name"] = name elif guild is not None: await _config.guild(guild).bank_name.set(name) @@ -815,7 +810,6 @@ async def get_currency_name(guild: discord.Guild = None) -> str: """ if await is_global(): - global _cache if _cache["currency"] is None: _cache["currency"] = await _config.currency() return _cache["currency"] @@ -849,7 +843,6 @@ async def set_currency_name(name: str, guild: discord.Guild = None) -> str: """ if await is_global(): await _config.currency.set(name) - global _cache _cache["currency"] = name elif guild is not None: await _config.guild(guild).currency.set(name) @@ -927,7 +920,6 @@ async def set_max_balance(amount: int, guild: discord.Guild = None) -> int: if await is_global(): await _config.max_balance.set(amount) - global _cache _cache["max_balance"] = amount elif guild is not None: await _config.guild(guild).max_balance.set(amount) @@ -1007,7 +999,6 @@ async def set_default_balance(amount: int, guild: discord.Guild = None) -> int: if await is_global(): await _config.default_balance.set(amount) - global _cache _cache["default_balance"] = amount elif guild is not None: await _config.guild(guild).default_balance.set(amount) @@ -1059,13 +1050,13 @@ async def wrapped(*args, **kwargs): ) try: await withdraw_credits(context.author, amount) - except Exception: + except Exception as exc: credits_name = await get_currency_name(context.guild) raise commands.UserFeedbackCheckFailure( _("You need at least {cost} {currency} to use this command.").format( cost=humanize_number(amount), currency=credits_name ) - ) + ) from exc else: try: return await coro(*args, **kwargs) diff --git a/redbot/core/bot.py b/redbot/core/bot.py index 06e7846648a..538ef119e8d 100644 --- a/redbot/core/bot.py +++ b/redbot/core/bot.py @@ -1,43 +1,42 @@ from __future__ import annotations + import asyncio +import contextlib import inspect import logging import os import platform import shutil import sys -import contextlib import weakref -import functools -from collections import namedtuple, OrderedDict +from collections import OrderedDict, namedtuple from datetime import datetime from importlib.machinery import ModuleSpec from pathlib import Path +from types import MappingProxyType from typing import ( - Optional, - Union, - List, - Iterable, - Dict, - NoReturn, - Set, - TypeVar, - Callable, - Awaitable, + TYPE_CHECKING, Any, + Awaitable, + Callable, + Dict, + Iterable, + List, Literal, MutableMapping, + NoReturn, + Optional, Set, + TypeVar, + Union, overload, - TYPE_CHECKING, ) -from types import MappingProxyType import discord from discord.ext import commands as dpy_commands from discord.ext.commands import when_mentioned_or -from . import Config, i18n, commands, errors, drivers, modlog, bank +from . import Config, bank, commands, drivers, errors, i18n, modlog from .cli import ExitCodes from .cog_manager import CogManager, CogManagerUI from .core_commands import Core @@ -45,20 +44,20 @@ from .dev_commands import Dev from .events import init_events from .global_checks import init_global_checks +from .rpc import RPCMixin from .settings_caches import ( - PrefixManager, - IgnoreManager, - WhitelistBlacklistManager, DisabledCogCache, I18nManager, + IgnoreManager, + PrefixManager, + WhitelistBlacklistManager, ) -from .rpc import RPCMixin -from .utils import can_user_send_messages_in, common_filters, AsyncIter +from .utils import AsyncIter, can_user_send_messages_in, common_filters from .utils._internal_utils import send_to_owners_with_prefix_replaced if TYPE_CHECKING: - from discord.ext.commands.hybrid import CommandCallback, ContextT, P from discord import app_commands + from discord.ext.commands.hybrid import CommandCallback, ContextT, P _T = TypeVar("_T") @@ -92,9 +91,7 @@ class _NoOwnerSet(RuntimeError): # d.py autoshardedbot should be at the end # all of our mixins should happen before, # and must include a call to super().__init__ unless they do not provide an init -class Red( - commands.GroupMixin, RPCMixin, dpy_commands.bot.AutoShardedBot -): # pylint: disable=no-member # barely spurious warning caused by shadowing +class Red(commands.GroupMixin, RPCMixin, dpy_commands.bot.AutoShardedBot): """Our subclass of discord.ext.commands.AutoShardedBot""" def __init__(self, *args, cli_flags=None, bot_dir: Path = Path.cwd(), **kwargs): @@ -225,6 +222,7 @@ async def prefix_manager(bot, message) -> List[str]: kwargs["max_messages"] = message_cache_size self._max_messages = message_cache_size + self._color = discord.Colour.default() self._uptime = None self._checked_time_accuracy = None @@ -401,7 +399,6 @@ def _before_invoke(self): # DEP-WARN @_before_invoke.setter def _before_invoke(self, val): # DEP-WARN """Prevent this from being overwritten in super().__init__""" - pass async def _red_before_invoke_method(self, ctx): await self.wait_until_red_ready() @@ -504,7 +501,8 @@ def uptime(self) -> datetime: @uptime.setter def uptime(self, value) -> NoReturn: raise RuntimeError( - "Hey, we're cool with sharing info about the uptime, but don't try and assign to it please." + "Hey, we're cool with sharing info about the uptime," + " but don't try and assign to it please." ) @property @@ -700,7 +698,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 @@ -1221,7 +1219,7 @@ async def _pre_connect(self) -> None: for package in to_remove: del packages[package] if packages: - log.info("Loaded packages: " + ", ".join(packages)) + log.info("Loaded packages: %s", ", ".join(packages)) else: log.info("No packages were loaded.") @@ -1621,6 +1619,7 @@ async def remove_loaded_package(self, pkg_name: str): while pkg_name in curr_pkgs: curr_pkgs.remove(pkg_name) + # pylint: disable-next=arguments-differ,arguments-renamed async def load_extension(self, spec: ModuleSpec): # NB: this completely bypasses `discord.ext.commands.Bot._load_from_module_spec` name = spec.name.split(".")[-1] @@ -1634,7 +1633,7 @@ async def load_extension(self, spec: ModuleSpec): try: await lib.setup(self) - except Exception as e: + except Exception: await self._remove_module_references(lib.__name__) await self._call_module_finalizers(lib, name) raise @@ -2041,7 +2040,7 @@ async def _delete_delay(self, ctx: commands.Context): async def _delete_helper(m): with contextlib.suppress(discord.HTTPException): await m.delete() - log.debug("Deleted command msg {}".format(m.id)) + log.debug("Deleted command msg %s", m.id) await asyncio.sleep(delay) await _delete_helper(message) @@ -2174,10 +2173,10 @@ async def wrapper(func, stype, sname): try: await func(requester=requester, user_id=user_id) except commands.commands.RedUnhandledAPI: - log.warning(f"{stype}.{sname} did not handle data deletion ") + log.warning("%s.%s did not handle data deletion ", stype, sname) failures["unhandled"].append(sname) - except Exception as exc: - log.exception(f"{stype}.{sname} errored when handling data deletion ") + except Exception: + log.exception("%s.%s errored when handling data deletion ", stype, sname) failures[stype].append(sname) handlers = [ diff --git a/redbot/core/checks.py b/redbot/core/checks.py index 67ad77f9664..6f790773e82 100644 --- a/redbot/core/checks.py +++ b/redbot/core/checks.py @@ -1,28 +1,25 @@ import warnings -from typing import Awaitable, TYPE_CHECKING, Dict - -import discord +from typing import TYPE_CHECKING, Awaitable, Dict from .commands import ( + admin, + admin_or_permissions, bot_has_permissions, bot_in_a_guild, - has_permissions, - is_owner, guildowner, guildowner_or_permissions, - admin, - admin_or_permissions, + has_permissions, + is_owner, mod, mod_or_permissions, ) from .utils.mod import ( - is_mod_or_superior as _is_mod_or_superior, - is_admin_or_superior as _is_admin_or_superior, check_permissions as _check_permissions, + is_admin_or_superior as _is_admin_or_superior, + is_mod_or_superior as _is_mod_or_superior, ) if TYPE_CHECKING: - from .bot import Red from .commands import Context __all__ = [ diff --git a/redbot/core/cli.py b/redbot/core/cli.py index 169f2eb79c5..e63cc2d04ef 100644 --- a/redbot/core/cli.py +++ b/redbot/core/cli.py @@ -1,6 +1,4 @@ import argparse -import asyncio -import logging import sys from enum import IntEnum from typing import Optional @@ -104,7 +102,7 @@ def non_negative_int(arg: str) -> int: try: x = int(arg) except ValueError: - raise argparse.ArgumentTypeError("The argument has to be a number.") + raise argparse.ArgumentTypeError("The argument has to be a number.") from None if x < 0: raise argparse.ArgumentTypeError("The argument has to be a non-negative integer.") if x > sys.maxsize: @@ -291,7 +289,8 @@ def parse_cli_flags(args): action="store_true", dest="rich_logging", default=None, - help="Forcefully enables the Rich logging handlers. This is normally enabled for supported active terminals.", + help="Forcefully enables the Rich logging handlers." + " This is normally enabled for supported active terminals.", ) parser.add_argument( "--force-disable-rich-logging", diff --git a/redbot/core/cog_manager.py b/redbot/core/cog_manager.py index 116df588f79..f27c2c874ff 100644 --- a/redbot/core/cog_manager.py +++ b/redbot/core/cog_manager.py @@ -4,19 +4,19 @@ from importlib import import_module, invalidate_caches from importlib.machinery import ModuleSpec from pathlib import Path -from typing import TYPE_CHECKING, Union, List, Optional +from typing import TYPE_CHECKING, List, Optional, Union + +import discord import redbot.cogs from redbot.core.commands import BadArgument from redbot.core.utils import deduplicate_iterables -import discord from . import checks, commands from .config import Config -from .i18n import Translator, cog_i18n from .data_manager import cog_data_path - -from .utils.chat_formatting import box, pagify, humanize_list, inline +from .i18n import Translator, cog_i18n +from .utils.chat_formatting import box, humanize_list, inline, pagify __all__ = ["CogManager"] @@ -331,7 +331,7 @@ def invalidate_caches(): class CogManagerUI(commands.Cog): """Commands to interface with Red's cog manager.""" - async def red_delete_data_for_user(self, **kwargs): + async def red_delete_data_for_user(self, **_kwargs): """Nothing to delete (Core Config is handled in a bot method )""" return diff --git a/redbot/core/commands/__init__.py b/redbot/core/commands/__init__.py index a768c928f37..705c9bd4623 100644 --- a/redbot/core/commands/__init__.py +++ b/redbot/core/commands/__init__.py @@ -1,207 +1,406 @@ -########## SENSITIVE SECTION WARNING ########### +################################################ +# SENSITIVE SECTION WARNING # ################################################ # Any edits of any of the exported names # # may result in a breaking change. # # Ensure no names are removed without warning. # ################################################ +# DEP-WARN: Check this *every* discord.py update +from discord.ext.commands import ( + ArgumentParsingError, + Author, + AutoShardedBot, + BadArgument, + BadBoolArgument, + BadColorArgument, + BadColourArgument, + BadFlagArgument, + BadInviteArgument, + BadLiteralArgument, + BadUnionArgument, + Bot, + BotMissingAnyRole, + BotMissingRole, + BucketType, + CategoryChannelConverter, + ChannelNotFound, + ChannelNotReadable, + CheckAnyFailure, + CheckFailure, + CogMeta, + ColorConverter, + ColourConverter, + CommandError, + CommandInvokeError, + CommandNotFound, + CommandOnCooldown, + CommandRegistrationError, + ConversionError, + Converter, + Cooldown, + CooldownMapping, + CurrentChannel, + CurrentGuild, + DefaultHelpCommand, + DisabledCommand, + DynamicCooldownMapping, + EmojiConverter, + EmojiNotFound, + ExpectedClosingQuoteError, + ExtensionAlreadyLoaded, + ExtensionError, + ExtensionFailed, + ExtensionNotFound, + ExtensionNotLoaded, + Flag, + FlagConverter, + FlagError, + ForumChannelConverter, + GameConverter, + Greedy, + GuildChannelConverter, + GuildConverter, + GuildNotFound, + GuildStickerConverter, + GuildStickerNotFound, + HelpCommand, + HybridCommandError, + IDConverter, + InvalidEndOfQuotedStringError, + InviteConverter, + MaxConcurrency, + MaxConcurrencyReached, + MemberConverter, + MemberNotFound, + MessageConverter, + MessageNotFound, + MinimalHelpCommand, + MissingAnyRole, + MissingFlagArgument, + MissingPermissions, + MissingRequiredArgument, + MissingRequiredAttachment, + MissingRequiredFlag, + MissingRole, + NoEntryPointError, + NoPrivateMessage, + NotOwner, + NSFWChannelRequired, + ObjectConverter, + ObjectNotFound, + Paginator, + Parameter, + PartialEmojiConversionFailure, + PartialEmojiConverter, + PartialMessageConverter, + PrivateMessageOnly, + Range, + RangeError, + RoleConverter, + RoleNotFound, + ScheduledEventConverter, + ScheduledEventNotFound, + StageChannelConverter, + TextChannelConverter, + ThreadConverter, + ThreadNotFound, + TooManyArguments, + TooManyFlags, + UnexpectedQuoteError, + UserConverter, + UserInputError, + UserNotFound, + VoiceChannelConverter, + after_invoke, + before_invoke, + bot_has_any_role, + bot_has_guild_permissions, + bot_has_role, + check, + check_any, + clean_content, + cooldown, + dm_only, + dynamic_cooldown, + flag, + guild_only, + has_any_role, + has_role, + is_nsfw, + max_concurrency, + param, + parameter, + run_converters, + when_mentioned, + when_mentioned_or, +) + from .commands import ( - Cog as Cog, - CogMixin as CogMixin, - CogCommandMixin as CogCommandMixin, - CogGroupMixin as CogGroupMixin, - Command as Command, - Group as Group, - GroupCog as GroupCog, - GroupMixin as GroupMixin, - command as command, - HybridCommand as HybridCommand, - HybridGroup as HybridGroup, - hybrid_command as hybrid_command, - hybrid_group as hybrid_group, - group as group, - RedUnhandledAPI as RedUnhandledAPI, - RESERVED_COMMAND_NAMES as RESERVED_COMMAND_NAMES, + RESERVED_COMMAND_NAMES, + Cog, + CogCommandMixin, + CogGroupMixin, + CogMixin, + Command, + Group, + GroupCog, + GroupMixin, + HybridCommand, + HybridGroup, + RedUnhandledAPI, + command, + group, + hybrid_command, + hybrid_group, ) -from .context import Context as Context, GuildContext as GuildContext, DMContext as DMContext +from .context import Context, DMContext, GuildContext from .converter import ( - DictConverter as DictConverter, - RelativedeltaConverter as RelativedeltaConverter, - TimedeltaConverter as TimedeltaConverter, - get_dict_converter as get_dict_converter, - get_timedelta_converter as get_timedelta_converter, - parse_relativedelta as parse_relativedelta, - parse_timedelta as parse_timedelta, - NoParseOptional as NoParseOptional, - UserInputOptional as UserInputOptional, - RawUserIdConverter as RawUserIdConverter, - CogConverter as CogConverter, - CommandConverter as CommandConverter, + CogConverter, + CommandConverter, + DictConverter, + NoParseOptional, + RawUserIdConverter, + RelativedeltaConverter, + TimedeltaConverter, + UserInputOptional, + get_dict_converter, + get_timedelta_converter, + parse_relativedelta, + parse_timedelta, ) from .errors import ( - BotMissingPermissions as BotMissingPermissions, - UserFeedbackCheckFailure as UserFeedbackCheckFailure, - ArgParserFailure as ArgParserFailure, + ArgParserFailure, + BotMissingPermissions, + UserFeedbackCheckFailure, ) from .help import ( - red_help as red_help, - RedHelpFormatter as RedHelpFormatter, - HelpSettings as HelpSettings, + HelpSettings, + RedHelpFormatter, + red_help, ) from .requires import ( - CheckPredicate as CheckPredicate, - GlobalPermissionModel as GlobalPermissionModel, - GuildPermissionModel as GuildPermissionModel, - PermissionModel as PermissionModel, - PrivilegeLevel as PrivilegeLevel, - PermState as PermState, - Requires as Requires, - permissions_check as permissions_check, - bot_has_permissions as bot_has_permissions, - bot_in_a_guild as bot_in_a_guild, - bot_can_manage_channel as bot_can_manage_channel, - bot_can_react as bot_can_react, - has_permissions as has_permissions, - can_manage_channel as can_manage_channel, - has_guild_permissions as has_guild_permissions, - is_owner as is_owner, - guildowner as guildowner, - guildowner_or_can_manage_channel as guildowner_or_can_manage_channel, - guildowner_or_permissions as guildowner_or_permissions, - admin as admin, - admin_or_can_manage_channel as admin_or_can_manage_channel, - admin_or_permissions as admin_or_permissions, - mod as mod, - mod_or_can_manage_channel as mod_or_can_manage_channel, - mod_or_permissions as mod_or_permissions, + CheckPredicate, + GlobalPermissionModel, + GuildPermissionModel, + PermissionModel, + PermState, + PrivilegeLevel, + Requires, + admin, + admin_or_can_manage_channel, + admin_or_permissions, + bot_can_manage_channel, + bot_can_react, + bot_has_permissions, + bot_in_a_guild, + can_manage_channel, + guildowner, + guildowner_or_can_manage_channel, + guildowner_or_permissions, + has_guild_permissions, + has_permissions, + is_owner, + mod, + mod_or_can_manage_channel, + mod_or_permissions, + permissions_check, ) -### DEP-WARN: Check this *every* discord.py update -from discord.ext.commands import ( - BadArgument as BadArgument, - EmojiConverter as EmojiConverter, - GuildConverter as GuildConverter, - InvalidEndOfQuotedStringError as InvalidEndOfQuotedStringError, - MemberConverter as MemberConverter, - BotMissingRole as BotMissingRole, - PrivateMessageOnly as PrivateMessageOnly, - HelpCommand as HelpCommand, - MinimalHelpCommand as MinimalHelpCommand, - DisabledCommand as DisabledCommand, - ExtensionFailed as ExtensionFailed, - Bot as Bot, - NotOwner as NotOwner, - CategoryChannelConverter as CategoryChannelConverter, - CogMeta as CogMeta, - ConversionError as ConversionError, - UserInputError as UserInputError, - Converter as Converter, - InviteConverter as InviteConverter, - ExtensionError as ExtensionError, - Cooldown as Cooldown, - CheckFailure as CheckFailure, - PartialMessageConverter as PartialMessageConverter, - MessageConverter as MessageConverter, - MissingPermissions as MissingPermissions, - BadUnionArgument as BadUnionArgument, - DefaultHelpCommand as DefaultHelpCommand, - ExtensionNotFound as ExtensionNotFound, - UserConverter as UserConverter, - MissingRole as MissingRole, - CommandOnCooldown as CommandOnCooldown, - MissingAnyRole as MissingAnyRole, - ExtensionNotLoaded as ExtensionNotLoaded, - clean_content as clean_content, - CooldownMapping as CooldownMapping, - ArgumentParsingError as ArgumentParsingError, - RoleConverter as RoleConverter, - CommandError as CommandError, - TextChannelConverter as TextChannelConverter, - UnexpectedQuoteError as UnexpectedQuoteError, - Paginator as Paginator, - BucketType as BucketType, - NoEntryPointError as NoEntryPointError, - CommandInvokeError as CommandInvokeError, - TooManyArguments as TooManyArguments, - Greedy as Greedy, - ExpectedClosingQuoteError as ExpectedClosingQuoteError, - ColourConverter as ColourConverter, - ColorConverter as ColorConverter, - VoiceChannelConverter as VoiceChannelConverter, - StageChannelConverter as StageChannelConverter, - NSFWChannelRequired as NSFWChannelRequired, - IDConverter as IDConverter, - MissingRequiredArgument as MissingRequiredArgument, - GameConverter as GameConverter, - CommandNotFound as CommandNotFound, - BotMissingAnyRole as BotMissingAnyRole, - NoPrivateMessage as NoPrivateMessage, - AutoShardedBot as AutoShardedBot, - ExtensionAlreadyLoaded as ExtensionAlreadyLoaded, - PartialEmojiConverter as PartialEmojiConverter, - check_any as check_any, - max_concurrency as max_concurrency, - CheckAnyFailure as CheckAnyFailure, - MaxConcurrency as MaxConcurrency, - MaxConcurrencyReached as MaxConcurrencyReached, - bot_has_guild_permissions as bot_has_guild_permissions, - CommandRegistrationError as CommandRegistrationError, - GuildNotFound as GuildNotFound, - MessageNotFound as MessageNotFound, - MemberNotFound as MemberNotFound, - UserNotFound as UserNotFound, - ChannelNotFound as ChannelNotFound, - ChannelNotReadable as ChannelNotReadable, - BadColourArgument as BadColourArgument, - RoleNotFound as RoleNotFound, - BadInviteArgument as BadInviteArgument, - EmojiNotFound as EmojiNotFound, - PartialEmojiConversionFailure as PartialEmojiConversionFailure, - BadBoolArgument as BadBoolArgument, - TooManyFlags as TooManyFlags, - MissingRequiredFlag as MissingRequiredFlag, - flag as flag, - FlagError as FlagError, - ObjectNotFound as ObjectNotFound, - GuildStickerNotFound as GuildStickerNotFound, - ThreadNotFound as ThreadNotFound, - GuildChannelConverter as GuildChannelConverter, - run_converters as run_converters, - Flag as Flag, - BadFlagArgument as BadFlagArgument, - BadColorArgument as BadColorArgument, - dynamic_cooldown as dynamic_cooldown, - BadLiteralArgument as BadLiteralArgument, - DynamicCooldownMapping as DynamicCooldownMapping, - ThreadConverter as ThreadConverter, - GuildStickerConverter as GuildStickerConverter, - ObjectConverter as ObjectConverter, - FlagConverter as FlagConverter, - MissingFlagArgument as MissingFlagArgument, - ScheduledEventConverter as ScheduledEventConverter, - ScheduledEventNotFound as ScheduledEventNotFound, - check as check, - guild_only as guild_only, - cooldown as cooldown, - dm_only as dm_only, - is_nsfw as is_nsfw, - has_role as has_role, - has_any_role as has_any_role, - bot_has_role as bot_has_role, - when_mentioned_or as when_mentioned_or, - when_mentioned as when_mentioned, - bot_has_any_role as bot_has_any_role, - before_invoke as before_invoke, - after_invoke as after_invoke, - CurrentChannel as CurrentChannel, - Author as Author, - param as param, - MissingRequiredAttachment as MissingRequiredAttachment, - Parameter as Parameter, - ForumChannelConverter as ForumChannelConverter, - CurrentGuild as CurrentGuild, - Range as Range, - RangeError as RangeError, - parameter as parameter, - HybridCommandError as HybridCommandError, +__all__ = ( + # discord.ext.commands + "ArgumentParsingError", + "Author", + "AutoShardedBot", + "BadArgument", + "BadBoolArgument", + "BadColorArgument", + "BadColourArgument", + "BadFlagArgument", + "BadInviteArgument", + "BadLiteralArgument", + "BadUnionArgument", + "Bot", + "BotMissingAnyRole", + "BotMissingRole", + "BucketType", + "CategoryChannelConverter", + "ChannelNotFound", + "ChannelNotReadable", + "CheckAnyFailure", + "CheckFailure", + "CogMeta", + "ColorConverter", + "ColourConverter", + "CommandError", + "CommandInvokeError", + "CommandNotFound", + "CommandOnCooldown", + "CommandRegistrationError", + "ConversionError", + "Converter", + "Cooldown", + "CooldownMapping", + "CurrentChannel", + "CurrentGuild", + "DefaultHelpCommand", + "DisabledCommand", + "DynamicCooldownMapping", + "EmojiConverter", + "EmojiNotFound", + "ExpectedClosingQuoteError", + "ExtensionAlreadyLoaded", + "ExtensionError", + "ExtensionFailed", + "ExtensionNotFound", + "ExtensionNotLoaded", + "Flag", + "FlagConverter", + "FlagError", + "ForumChannelConverter", + "GameConverter", + "Greedy", + "GuildChannelConverter", + "GuildConverter", + "GuildNotFound", + "GuildStickerConverter", + "GuildStickerNotFound", + "HelpCommand", + "HybridCommandError", + "IDConverter", + "InvalidEndOfQuotedStringError", + "InviteConverter", + "MaxConcurrency", + "MaxConcurrencyReached", + "MemberConverter", + "MemberNotFound", + "MessageConverter", + "MessageNotFound", + "MinimalHelpCommand", + "MissingAnyRole", + "MissingFlagArgument", + "MissingPermissions", + "MissingRequiredArgument", + "MissingRequiredAttachment", + "MissingRequiredFlag", + "MissingRole", + "NoEntryPointError", + "NoPrivateMessage", + "NotOwner", + "NSFWChannelRequired", + "ObjectConverter", + "ObjectNotFound", + "Paginator", + "Parameter", + "PartialEmojiConversionFailure", + "PartialEmojiConverter", + "PartialMessageConverter", + "PrivateMessageOnly", + "Range", + "RangeError", + "RoleConverter", + "RoleNotFound", + "ScheduledEventConverter", + "ScheduledEventNotFound", + "StageChannelConverter", + "TextChannelConverter", + "ThreadConverter", + "ThreadNotFound", + "TooManyArguments", + "TooManyFlags", + "UnexpectedQuoteError", + "UserConverter", + "UserInputError", + "UserNotFound", + "VoiceChannelConverter", + "after_invoke", + "before_invoke", + "bot_has_any_role", + "bot_has_guild_permissions", + "bot_has_role", + "check", + "check_any", + "clean_content", + "cooldown", + "dm_only", + "dynamic_cooldown", + "flag", + "guild_only", + "has_any_role", + "has_role", + "is_nsfw", + "max_concurrency", + "param", + "parameter", + "run_converters", + "when_mentioned", + "when_mentioned_or", + # .commands + "RESERVED_COMMAND_NAMES", + "Cog", + "CogCommandMixin", + "CogGroupMixin", + "CogMixin", + "Command", + "Group", + "GroupCog", + "GroupMixin", + "HybridCommand", + "HybridGroup", + "RedUnhandledAPI", + "command", + "group", + "hybrid_command", + "hybrid_group", + # .context + "Context", + "DMContext", + "GuildContext", + # .converter + "CogConverter", + "CommandConverter", + "DictConverter", + "NoParseOptional", + "RawUserIdConverter", + "RelativedeltaConverter", + "TimedeltaConverter", + "UserInputOptional", + "get_dict_converter", + "get_timedelta_converter", + "parse_relativedelta", + "parse_timedelta", + # .errors + "ArgParserFailure", + "BotMissingPermissions", + "UserFeedbackCheckFailure", + # .help + "HelpSettings", + "RedHelpFormatter", + "red_help", + # .requires + "CheckPredicate", + "GlobalPermissionModel", + "GuildPermissionModel", + "PermissionModel", + "PermState", + "PrivilegeLevel", + "Requires", + "admin", + "admin_or_can_manage_channel", + "admin_or_permissions", + "bot_can_manage_channel", + "bot_can_react", + "bot_has_permissions", + "bot_in_a_guild", + "can_manage_channel", + "guildowner", + "guildowner_or_can_manage_channel", + "guildowner_or_permissions", + "has_guild_permissions", + "has_permissions", + "is_owner", + "mod", + "mod_or_can_manage_channel", + "mod_or_permissions", + "permissions_check", ) diff --git a/redbot/core/commands/commands.py b/redbot/core/commands/commands.py index 09415f5e504..2b587eed7ca 100644 --- a/redbot/core/commands/commands.py +++ b/redbot/core/commands/commands.py @@ -8,45 +8,39 @@ import inspect import io import re -import functools import weakref from typing import ( + TYPE_CHECKING, Any, Awaitable, Callable, - ClassVar, Dict, List, Literal, + MutableMapping, Optional, Tuple, TypeVar, Union, - MutableMapping, - TYPE_CHECKING, - cast, ) import discord from discord.ext.commands import ( - BadArgument, - CommandError, CheckFailure, - DisabledCommand, - command as dpy_command_deco, + Cog as DPYCog, + CogMeta as DPYCogMeta, Command as DPYCommand, + CommandError, + DisabledCommand, + Group as DPYGroup, GroupCog as DPYGroupCog, HybridCommand as DPYHybridCommand, HybridGroup as DPYHybridGroup, - Cog as DPYCog, - CogMeta as DPYCogMeta, - Group as DPYGroup, - Greedy, + command as dpy_command_deco, ) -from .errors import ConversionFailure -from .requires import PermState, PrivilegeLevel, Requires, PermStateAllowedStates from ..i18n import Translator +from .requires import PermState, PermStateAllowedStates, PrivilegeLevel, Requires _T = TypeVar("_T") _CogT = TypeVar("_CogT", bound="Cog") @@ -54,9 +48,10 @@ if TYPE_CHECKING: # circular import avoidance - from .context import Context - from typing_extensions import ParamSpec, Concatenate from discord.ext.commands._types import ContextT, Coro + from typing_extensions import Concatenate, ParamSpec + + from .context import Context _P = ParamSpec("_P") @@ -97,8 +92,6 @@ class RedUnhandledAPI(Exception): """An exception which can be raised to signal a lack of handling specific APIs""" - pass - class CogCommandMixin: """A mixin for cogs and commands.""" @@ -106,7 +99,6 @@ class CogCommandMixin: @property def help(self) -> str: """To be defined by subclasses""" - ... def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -317,7 +309,8 @@ def __init__(self, *args, **kwargs): for name in (self.name, *self.aliases): if name in RESERVED_COMMAND_NAMES: raise RuntimeError( - f"The name `{name}` cannot be set as a command name. It is reserved for internal use." + f"The name `{name}` cannot be set as a command name." + " It is reserved for internal use." ) if len(self.qualified_name) > 60: raise RuntimeError( @@ -481,7 +474,7 @@ async def prepare(self, ctx, /): await self._parse_arguments(ctx) await self.call_before_hooks(ctx) - except: + except: # noqa: E722 if self._max_concurrency is not None: await self._max_concurrency.release(ctx) raise @@ -587,6 +580,7 @@ def clear_rule_for( break return old_rule, new_rule + # pylint: disable-next=useless-parent-delegation def error(self, coro, /): """ A decorator that registers a coroutine as a local error handler. @@ -792,6 +786,7 @@ def help(self): if doc: return inspect.cleandoc(translator(doc)) + # pylint: disable-next=redundant-returns-doc async def red_get_data_for_user(self, *, user_id: int) -> MutableMapping[str, io.BytesIO]: """ @@ -922,7 +917,7 @@ async def red_delete_data_for_user( """ raise RedUnhandledAPI() - async def can_run(self, ctx: "Context", /, **kwargs) -> bool: + async def can_run(self, ctx: "Context", /, **_kwargs) -> bool: """ This really just exists to allow easy use with other methods using can_run on commands and groups such as help formatters. @@ -1188,7 +1183,7 @@ class _AlwaysAvailableMixin: This particular class is not supported for 3rd party use """ - async def can_run(self, ctx, /, *args, **kwargs) -> bool: + async def can_run(self, ctx, /, *_args, **_kwargs) -> bool: return not ctx.author.bot can_see = can_run @@ -1237,7 +1232,7 @@ class _ForgetMeSpecialCommand(_RuleDropper, Command): We need special can_run behavior here """ - async def can_run(self, ctx, /, *args, **kwargs) -> bool: + async def can_run(self, ctx, /, *_args, **_kwargs) -> bool: return await ctx.bot._config.datarequests.allow_user_requests() can_see = can_run diff --git a/redbot/core/commands/context.py b/redbot/core/commands/context.py index ed354dedc28..2c9899f5f58 100644 --- a/redbot/core/commands/context.py +++ b/redbot/core/commands/context.py @@ -3,19 +3,19 @@ import asyncio import contextlib import os -import re -from typing import Iterable, List, Union, Optional, TYPE_CHECKING +from typing import TYPE_CHECKING, Iterable, List, Optional, Union + import discord from discord.ext.commands import Context as DPYContext -from .requires import PermState +from ..utils import can_user_react_in from ..utils.chat_formatting import box, text_to_file from ..utils.predicates import MessagePredicate -from ..utils import can_user_react_in, common_filters +from .requires import PermState if TYPE_CHECKING: - from .commands import Command from ..bot import Red + from .commands import Command TICK = "\N{WHITE HEAVY CHECK MARK}" @@ -92,7 +92,7 @@ async def send(self, content=None, **kwargs): return await super().send(content=content, **kwargs) - async def send_help(self, command=None): + async def send_help(self, command=None): # pylint: disable=arguments-differ """Send the command help message.""" # This allows people to manually use this similarly # to the upstream d.py version, while retaining our use. @@ -307,7 +307,7 @@ def me(self) -> Union[discord.ClientUser, discord.Member]: return self.bot.user -if TYPE_CHECKING or os.getenv("BUILDING_DOCS", False): +if TYPE_CHECKING or os.getenv("BUILDING_DOCS"): class DMContext(Context): """ diff --git a/redbot/core/commands/converter.py b/redbot/core/commands/converter.py index 3f8cc74d069..48e7da0d9a3 100644 --- a/redbot/core/commands/converter.py +++ b/redbot/core/commands/converter.py @@ -8,26 +8,23 @@ import functools import re from datetime import timedelta -from dateutil.relativedelta import relativedelta from typing import ( TYPE_CHECKING, - Generic, + Dict, + List, Optional, Optional as NoParseOptional, - Tuple, - List, - Dict, Type, TypeVar, Union as UserInputOptional, ) -import discord +from dateutil.relativedelta import relativedelta from discord.ext import commands as dpy_commands from discord.ext.commands import BadArgument from ..i18n import Translator -from ..utils.chat_formatting import humanize_timedelta, humanize_list +from ..utils.chat_formatting import humanize_timedelta if TYPE_CHECKING: from .context import Context @@ -137,7 +134,7 @@ def parse_timedelta( except OverflowError: raise BadArgument( _("The time set is way too high, consider setting something reasonable.") - ) + ) from None if maximum and maximum < delta: raise BadArgument( _( @@ -199,7 +196,7 @@ def parse_relativedelta( except OverflowError: raise BadArgument( _("The time set is way too high, consider setting something reasonable.") - ) + ) from None return delta return None @@ -270,7 +267,8 @@ async def convert(self, ctx: "Context", argument: str) -> Dict[str, str]: if TYPE_CHECKING: def get_dict_converter(*expected_keys: str, delims: Optional[List[str]] = None) -> Type[dict]: - ... + del expected_keys, delims + return DictConverter else: @@ -284,6 +282,8 @@ class PartialMeta(type(DictConverter)): type(DictConverter).__call__, *expected_keys, delims=delims ) + # pylint checks `if TYPE_CHECKING`'s definition of `DictConverter` + # pylint: disable-next=invalid-metaclass class ValidatedConverter(DictConverter, metaclass=PartialMeta): pass @@ -349,7 +349,8 @@ def get_timedelta_converter( minimum: Optional[timedelta] = None, allowed_units: Optional[List[str]] = None, ) -> Type[timedelta]: - ... + del default_unit, maximum, minimum, allowed_units + return TimedeltaConverter else: @@ -396,6 +397,8 @@ class PartialMeta(type(DictConverter)): maximum=maximum, ) + # pylint checks `if TYPE_CHECKING`'s definition of `DictConverter` + # pylint: disable-next=invalid-metaclass class ValidatedConverter(TimedeltaConverter, metaclass=PartialMeta): pass @@ -444,7 +447,7 @@ async def convert(self, ctx: "Context", argument: str) -> relativedelta: if not TYPE_CHECKING: - class NoParseOptional: + class NoParseOptional: # noqa: F811 """ This can be used instead of `typing.Optional` to avoid discord.py special casing the conversion behavior. @@ -471,7 +474,7 @@ def __class_getitem__(cls, key): #: #: .. warning:: #: This converter class is still provisional. - UserInputOptional = Optional + UserInputOptional = Optional # noqa: F811 if TYPE_CHECKING: CommandConverter = dpy_commands.Command diff --git a/redbot/core/commands/errors.py b/redbot/core/commands/errors.py index 8c4752bbac8..7b0e3cdf97e 100644 --- a/redbot/core/commands/errors.py +++ b/redbot/core/commands/errors.py @@ -1,5 +1,6 @@ """Errors module for the commands package.""" import inspect + import discord from discord.ext import commands diff --git a/redbot/core/commands/help.py b/redbot/core/commands/help.py index 5fe8d6b476d..989c0e57fb8 100644 --- a/redbot/core/commands/help.py +++ b/redbot/core/commands/help.py @@ -30,20 +30,16 @@ import abc import asyncio from collections import namedtuple -from dataclasses import dataclass, asdict as dc_asdict +from dataclasses import asdict as dc_asdict, dataclass from enum import Enum -from typing import Union, List, AsyncIterator, Iterable, cast +from typing import AsyncIterator, Iterable, List, Union, cast import discord from discord.ext import commands as dpy_commands -from . import commands -from .context import Context from ..i18n import Translator -from ..utils.views import SimpleMenu from ..utils import can_user_react_in, menus -from ..utils.mod import mass_purge -from ..utils._internal_utils import fuzzy_command_search, format_fuzzy_results +from ..utils._internal_utils import format_fuzzy_results, fuzzy_command_search from ..utils.chat_formatting import ( bold, box, @@ -53,6 +49,10 @@ pagify, underline, ) +from ..utils.mod import mass_purge +from ..utils.views import SimpleMenu +from . import commands +from .context import Context __all__ = ["red_help", "RedHelpFormatter", "HelpSettings", "HelpFormatterABC"] @@ -207,7 +207,6 @@ async def send_help( The types subclasses must handle are defined as ``HelpTarget`` """ - ... class RedHelpFormatter(HelpFormatterABC): @@ -365,7 +364,9 @@ async def format_command_help( a_diff = len(aliases) - len(valid_alias_list) aliases_list = [ - f"{ctx.clean_prefix}{command.parent.qualified_name + ' ' if command.parent else ''}{alias}" + f"{ctx.clean_prefix}" + f"{command.parent.qualified_name + ' ' if command.parent else ''}" + f"{alias}" for alias in valid_alias_list ] if len(valid_alias_list) < 10: @@ -483,9 +484,9 @@ def group_embed_fields(fields: List[EmbedField], max_chars=1000): ret.append(curr_group) current_count = f_len curr_group = [f] - else: - if curr_group: - ret.append(curr_group) + + if curr_group: + ret.append(curr_group) return ret @@ -519,7 +520,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/commands/requires.py b/redbot/core/commands/requires.py index 5ef689536d8..4b0b1566d24 100644 --- a/redbot/core/commands/requires.py +++ b/redbot/core/commands/requires.py @@ -26,12 +26,12 @@ ) import discord - from discord.ext.commands import check -from .errors import BotMissingPermissions from redbot.core import utils +from .errors import BotMissingPermissions + if TYPE_CHECKING: from .commands import Command from .context import Context @@ -303,7 +303,7 @@ class Requires: required `privilege_level` _or_ `user_perms`. bot_perms : discord.Permissions The required bot permissions for a command to be executed. This - is not overrideable by other conditions. + is not overridable by other conditions. """ @@ -458,8 +458,8 @@ def reset(self) -> None: This will clear all rules, including defaults. It also resets the `Requires.ready_event`. """ - self._guild_rules.clear() # pylint: disable=no-member - self._global_rules.clear() # pylint: disable=no-member + self._guild_rules.clear() + self._global_rules.clear() self.ready_event.clear() async def verify(self, ctx: "Context") -> bool: @@ -637,7 +637,7 @@ def __repr__(self) -> str: def permissions_check(predicate: CheckPredicate): - """An overwriteable version of `discord.ext.commands.check`. + """An overwritable version of `discord.ext.commands.check`. This has the same behaviour as `discord.ext.commands.check`, however this check can be ignored if the command is allowed @@ -922,12 +922,12 @@ class _IntKeyDict(Dict[int, _T]): def __getitem__(self, key: Any) -> _T: if not isinstance(key, int): raise TypeError("Keys must be of type `int`") - return super().__getitem__(key) # pylint: disable=no-member + return super().__getitem__(key) def __setitem__(self, key: Any, value: _T) -> None: if not isinstance(key, int): raise TypeError("Keys must be of type `int`") - return super().__setitem__(key, value) # pylint: disable=no-member + return super().__setitem__(key, value) class _RulesDict(Dict[Union[int, str], PermState]): @@ -939,12 +939,12 @@ class _RulesDict(Dict[Union[int, str], PermState]): def __getitem__(self, key: Any) -> PermState: if key != Requires.DEFAULT and not isinstance(key, int): raise TypeError(f'Expected "{Requires.DEFAULT}" or int key, not "{key}"') - return super().__getitem__(key) # pylint: disable=no-member + return super().__getitem__(key) def __setitem__(self, key: Any, value: PermState) -> None: if key != Requires.DEFAULT and not isinstance(key, int): raise TypeError(f'Expected "{Requires.DEFAULT}" or int key, not "{key}"') - return super().__setitem__(key, value) # pylint: disable=no-member + return super().__setitem__(key, value) def _validate_perms_dict(perms: Dict[str, bool]) -> None: diff --git a/redbot/core/config.py b/redbot/core/config.py index af3e41a3c8f..5a45865197f 100644 --- a/redbot/core/config.py +++ b/redbot/core/config.py @@ -19,7 +19,7 @@ import discord -from .drivers import IdentifierData, get_driver, ConfigCategory, BaseDriver +from .drivers import BaseDriver, ConfigCategory, IdentifierData, get_driver __all__ = ["Config", "get_latest_confs", "migrate"] @@ -66,7 +66,7 @@ def get_latest_confs() -> Tuple["Config"]: return tuple(ret) -class _ValueCtxManager(Awaitable[_T], AsyncContextManager[_T]): # pylint: disable=duplicate-bases +class _ValueCtxManager(Awaitable[_T], AsyncContextManager[_T]): """Context manager implementation of config values. This class allows mutable config values to be both "get" and "set" from diff --git a/redbot/core/core_commands.py b/redbot/core/core_commands.py index 26ec04e755f..66f34044dfe 100644 --- a/redbot/core/core_commands.py +++ b/redbot/core/core_commands.py @@ -2,61 +2,44 @@ import contextlib import datetime import importlib +import io import itertools import keyword import logging -import io import random -import markdown -import os import re import sys -import platform -import psutil -import getpass -import pip import traceback from pathlib import Path -from redbot.core import data_manager -from redbot.core.utils.menus import menu -from redbot.core.utils.views import SetApiView -from redbot.core.commands import GuildConverter, RawUserIdConverter from string import ascii_letters, digits -from typing import ( - TYPE_CHECKING, - Union, - Tuple, - List, - Optional, - Iterable, - Sequence, - Dict, - Set, - Literal, -) +from typing import TYPE_CHECKING, Dict, Iterable, List, Literal, Optional, Sequence, Union import aiohttp import discord +import markdown from babel import Locale as BabelLocale, UnknownLocaleError -from redbot.core.data_manager import storage_type + +from redbot.core.commands import GuildConverter, RawUserIdConverter +from redbot.core.utils.menus import menu +from redbot.core.utils.views import SetApiView from . import ( __version__, - version_info as red_version_info, + bank, checks, commands, errors, i18n, - bank, modlog, + version_info as red_version_info, ) from ._diagnoser import IssueDiagnoser +from .commands import CogConverter, CommandConverter +from .commands.requires import PrivilegeLevel from .utils import AsyncIter, can_user_send_messages_in from .utils._internal_utils import fetch_latest_red_version_info -from .utils.predicates import MessagePredicate from .utils.chat_formatting import ( box, - escape, humanize_list, humanize_number, humanize_timedelta, @@ -64,9 +47,7 @@ pagify, warning, ) -from .commands import CommandConverter, CogConverter -from .commands.requires import PrivilegeLevel -from .commands.help import HelpMenuSetting +from .utils.predicates import MessagePredicate _entities = { "*": "*", @@ -262,7 +243,7 @@ def maybe_reload(new_name): for name, lib in sys.modules.items() if name == module_name or name.startswith(f"{module_name}.") } - for child_name, lib in children.items(): + for lib in children.values(): importlib._bootstrap._exec(lib.__spec__, lib) async def _unload(self, pkg_names: Iterable[str]) -> Dict[str, List[str]]: @@ -393,7 +374,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic): These commands come loaded with every Red bot, and cover some of the most basic usage of the bot. """ - async def red_delete_data_for_user(self, **kwargs): + async def red_delete_data_for_user(self, **_kwargs): """Nothing to delete (Core Config is handled in a bot method)""" return @@ -1499,7 +1480,6 @@ async def invite(self, ctx): @checks.is_owner() async def inviteset(self, ctx): """Commands to setup [botname]'s invite settings.""" - pass @inviteset.command() async def public(self, ctx, confirm: bool = False): @@ -2142,7 +2122,6 @@ async def bankset_reset(self, ctx, confirmation: bool = False): @bankset.group(name="prune") async def bankset_prune(self, ctx): """Base command for pruning bank accounts.""" - pass @bankset_prune.command(name="server", aliases=["guild", "local"]) @commands.guild_only() @@ -2255,7 +2234,6 @@ async def bankset_prune_user( @checks.guildowner_or_permissions(administrator=True) async def modlogset(self, ctx: commands.Context): """Manage modlog settings.""" - pass @checks.is_owner() @modlogset.command(hidden=True, name="fixcasetypes") @@ -2343,7 +2321,7 @@ async def modlogset_resetcases(self, ctx: commands.Context): ) try: pred = MessagePredicate.yes_or_no(ctx, user=ctx.author) - msg = await ctx.bot.wait_for("message", check=pred, timeout=30) + await ctx.bot.wait_for("message", check=pred, timeout=30) except asyncio.TimeoutError: await ctx.send(_("You took too long to respond.")) return @@ -3230,7 +3208,6 @@ async def _set_ownernotifications(self, ctx: commands.Context): Owner notifications include usage of `[p]contact` and available Red updates. """ - pass @_set_ownernotifications.command(name="optin") async def _set_ownernotifications_optin(self, ctx: commands.Context): @@ -3692,7 +3669,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.") @@ -3707,7 +3684,6 @@ async def helpset(self, ctx: commands.Context): All help settings are applied globally. """ - pass @helpset.command(name="showsettings") async def helpset_showsettings(self, ctx: commands.Context): @@ -4134,12 +4110,13 @@ async def contact(self, ctx: commands.Context, *, message: str): try: await destination.send(embed=e) except discord.Forbidden: - log.exception(f"Contact failed to {destination}({destination.id})") + log.exception("Contact failed to %r (%r)", destination, destination.id) # Should this automatically opt them out? except discord.HTTPException: log.exception( - f"An unexpected error happened while attempting to" - f" send contact to {destination}({destination.id})" + "An unexpected error happened while attempting to send contact to %r (%r)", + destination, + destination.id, ) else: successful = True @@ -4149,12 +4126,13 @@ async def contact(self, ctx: commands.Context, *, message: str): try: await destination.send("{}\n{}".format(content, box(msg_text))) except discord.Forbidden: - log.exception(f"Contact failed to {destination}({destination.id})") + log.exception("Contact failed to %r (%r)", destination, destination.id) # Should this automatically opt them out? except discord.HTTPException: log.exception( - f"An unexpected error happened while attempting to" - f" send contact to {destination}({destination.id})" + "An unexpected error happened while attempting to send contact to %r (%r)", + destination, + destination.id, ) else: successful = True @@ -4314,7 +4292,6 @@ async def allowlist(self, ctx: commands.Context): Use `[p]allowlist clear` to disable the allowlist """ - pass @allowlist.command(name="add", require_var_positional=True) async def allowlist_add(self, ctx: commands.Context, *users: Union[discord.Member, int]): @@ -4401,7 +4378,6 @@ async def blocklist(self, ctx: commands.Context): Use `[p]blocklist clear` to disable the blocklist """ - pass @blocklist.command(name="add", require_var_positional=True) async def blocklist_add(self, ctx: commands.Context, *users: Union[discord.Member, int]): @@ -4496,7 +4472,6 @@ async def localallowlist(self, ctx: commands.Context): Use `[p]localallowlist clear` to disable the allowlist """ - pass @localallowlist.command(name="add", require_var_positional=True) async def localallowlist_add( @@ -4513,7 +4488,6 @@ async def localallowlist_add( **Arguments:** - `` - The users or roles to remove from the local allowlist. """ - names = [getattr(u_or_r, "name", u_or_r) for u_or_r in users_or_roles] uids = {getattr(u_or_r, "id", u_or_r) for u_or_r in users_or_roles} if not (ctx.guild.owner == ctx.author or await self.bot.is_owner(ctx.author)): current_whitelist = await self.bot.get_whitelist(ctx.guild) @@ -4577,7 +4551,6 @@ async def localallowlist_remove( **Arguments:** - `` - The users or roles to remove from the local allowlist. """ - names = [getattr(u_or_r, "name", u_or_r) for u_or_r in users_or_roles] uids = {getattr(u_or_r, "id", u_or_r) for u_or_r in users_or_roles} if not (ctx.guild.owner == ctx.author or await self.bot.is_owner(ctx.author)): current_whitelist = await self.bot.get_whitelist(ctx.guild) @@ -4619,7 +4592,6 @@ async def localblocklist(self, ctx: commands.Context): Use `[p]localblocklist clear` to disable the blocklist """ - pass @localblocklist.command(name="add", require_var_positional=True) async def localblocklist_add( @@ -4719,7 +4691,6 @@ async def localblocklist_clear(self, ctx: commands.Context): @commands.group(name="command") async def command_manager(self, ctx: commands.Context): """Commands to enable and disable commands and cogs.""" - pass @checks.is_owner() @command_manager.command(name="defaultdisablecog") @@ -4933,7 +4904,7 @@ async def command_disable_global(self, ctx: commands.Context, *, command: Comman **Arguments:** - `` - The command to disable globally. """ - if self.command_manager in command.parents or self.command_manager == command: + if self.command_manager in command.parents or self.command_manager is command: await ctx.send( _("The command to disable cannot be `command` or any of its subcommands.") ) @@ -4969,7 +4940,7 @@ async def command_disable_guild(self, ctx: commands.Context, *, command: Command **Arguments:** - `` - The command to disable for the current server. """ - if self.command_manager in command.parents or self.command_manager == command: + if self.command_manager in command.parents or self.command_manager is command: await ctx.send( _("The command to disable cannot be `command` or any of its subcommands.") ) @@ -5098,7 +5069,6 @@ async def autoimmune_group(self, ctx: commands.Context): This includes duplicate message deletion and mention spam from the Mod cog, and filters from the Filter cog. """ - pass @autoimmune_group.command(name="list") async def autoimmune_list(self, ctx: commands.Context): diff --git a/redbot/core/data_manager.py b/redbot/core/data_manager.py index 85169ad8602..c85c7e13146 100644 --- a/redbot/core/data_manager.py +++ b/redbot/core/data_manager.py @@ -197,9 +197,8 @@ def core_data_path() -> Path: return core_path.resolve() -# noinspection PyUnusedLocal @deprecated("bundled_data_path() without calling this function") -def load_bundled_data(cog_instance, init_location: str): +def load_bundled_data(cog_instance, init_location: str): # pylint: disable=unused-argument pass diff --git a/redbot/core/dev_commands.py b/redbot/core/dev_commands.py index 9fea9dc8c93..ae474f6ef6b 100644 --- a/redbot/core/dev_commands.py +++ b/redbot/core/dev_commands.py @@ -10,25 +10,27 @@ is distributed under GNU GPL Version 3. """ +# pylint: disable=eval-used,exec-used + import ast import asyncio -import aiohttp import inspect import io +import re import textwrap import traceback import types -import re from contextlib import redirect_stdout from copy import copy +import aiohttp import discord from . import checks, commands from .commands import NoParseOptional as Optional from .i18n import Translator, cog_i18n from .utils import chat_formatting -from .utils.chat_formatting import pagify +from .utils.chat_formatting import inline, pagify from .utils.predicates import MessagePredicate _ = Translator("Dev", __file__) @@ -40,7 +42,7 @@ class Dev(commands.Cog): """Various development focused utilities.""" - async def red_delete_data_for_user(self, **kwargs): + async def red_delete_data_for_user(self, **_kwargs): """ Because despite my best efforts to advise otherwise, people use ``--dev`` in production @@ -263,8 +265,9 @@ async def repl(self, ctx): else: await ctx.send( _( - "Already running a REPL session in this channel. Resume the REPL with `{}repl resume`." - ).format(ctx.prefix) + "Already running a REPL session in this channel." + " Resume the REPL with {command}." + ).format(inline(f"{ctx.clean_prefix}repl resume")) ) return @@ -274,8 +277,9 @@ async def repl(self, ctx): self.sessions[ctx.channel.id] = True await ctx.send( _( - "Enter code to execute or evaluate. `exit()` or `quit` to exit. `{}repl pause` to pause." - ).format(ctx.prefix) + "Enter code to execute or evaluate. `exit()` or `quit` to exit." + " {command} to pause." + ).format(inline(f"{ctx.clean_prefix}repl pause")) ) while True: @@ -316,7 +320,8 @@ async def repl(self, ctx): try: with redirect_stdout(stdout): if executor is None: - result = types.FunctionType(code, env)() + # https://github.com/PyCQA/pylint/issues/7500 + result = types.FunctionType(code, env)() # pylint: disable=not-callable else: result = executor(code, env) result = await self.maybe_await(result) diff --git a/redbot/core/drivers/__init__.py b/redbot/core/drivers/__init__.py index 82155550c44..ddb7a2cfe5e 100644 --- a/redbot/core/drivers/__init__.py +++ b/redbot/core/drivers/__init__.py @@ -2,7 +2,7 @@ from typing import Optional, Type from .. import data_manager -from .base import IdentifierData, BaseDriver, ConfigCategory +from .base import BaseDriver, ConfigCategory, IdentifierData from .json import JsonDriver from .postgres import PostgresDriver diff --git a/redbot/core/drivers/_mongo.py b/redbot/core/drivers/_mongo.py index a628c1669b6..0b6b5eab3c5 100644 --- a/redbot/core/drivers/_mongo.py +++ b/redbot/core/drivers/_mongo.py @@ -5,14 +5,13 @@ import itertools import re from getpass import getpass -from typing import Match, Pattern, Tuple, Optional, AsyncIterator, Any, Dict, Iterator, List +from typing import Any, AsyncIterator, Dict, Iterator, List, Match, Optional, Pattern, Tuple from urllib.parse import quote_plus try: - # pylint: disable=import-error - import pymongo.errors import motor.core import motor.motor_asyncio + import pymongo.errors except ModuleNotFoundError: motor = None pymongo = None @@ -43,7 +42,7 @@ async def initialize(cls, **storage_details) -> None: password = storage_details["PASSWORD"] database = storage_details.get("DB_NAME", "default_db") - if port is 0: + if port == 0: ports = "" else: ports = ":{}".format(port) @@ -66,7 +65,7 @@ async def teardown(cls) -> None: def get_config_details(): while True: uri = input("Enter URI scheme (mongodb or mongodb+srv): ") - if uri is "": + if uri == "": uri = "mongodb" if uri in ["mongodb", "mongodb+srv"]: @@ -75,7 +74,7 @@ def get_config_details(): print("Invalid URI scheme") host = input("Enter host address: ") - if uri is "mongodb": + if uri == "mongodb": port = int(input("Enter host port: ")) else: port = 0 diff --git a/redbot/core/drivers/base.py b/redbot/core/drivers/base.py index 8ca710e35ce..9aac9481587 100644 --- a/redbot/core/drivers/base.py +++ b/redbot/core/drivers/base.py @@ -1,6 +1,6 @@ import abc import enum -from typing import Tuple, Dict, Any, Union, List, AsyncIterator, Type +from typing import Any, AsyncIterator, Dict, List, Tuple, Type, Union import rich.progress @@ -159,7 +159,7 @@ def to_tuple(self) -> Tuple[str, ...]: class BaseDriver(abc.ABC): - def __init__(self, cog_name: str, identifier: str, **kwargs): + def __init__(self, cog_name: str, identifier: str, **_kwargs): self.cog_name = cog_name self.unique_cog_identifier = identifier @@ -323,6 +323,7 @@ async def delete_all_data(cls, **kwargs) -> None: operates. """ + del kwargs async for cog_name, cog_id in cls.aiter_cogs(): driver = cls(cog_name, cog_id) await driver.clear(IdentifierData(cog_name, cog_id, "", (), (), 0)) diff --git a/redbot/core/drivers/json.py b/redbot/core/drivers/json.py index 4dd60a71c4b..9f9a6520311 100644 --- a/redbot/core/drivers/json.py +++ b/redbot/core/drivers/json.py @@ -10,7 +10,7 @@ from uuid import uuid4 from .. import data_manager, errors -from .base import BaseDriver, IdentifierData, ConfigCategory +from .base import BaseDriver, ConfigCategory, IdentifierData __all__ = ["JsonDriver"] @@ -35,7 +35,7 @@ def finalize_driver(cog_name): if cog_name in _locks: del _locks[cog_name] - for f in _finalizers: + for f in reversed(_finalizers): if not f.alive: _finalizers.remove(f) @@ -149,9 +149,9 @@ async def set(self, identifier_data: IdentifierData, value=None): for i in full_identifiers[:-1]: try: partial = partial.setdefault(i, {}) - except AttributeError: + except AttributeError as exc: # Tried to set sub-field of non-object - raise errors.CannotSetSubfield + raise errors.CannotSetSubfield from exc partial[full_identifiers[-1]] = value_copy await self._save() @@ -229,7 +229,7 @@ def _save_json(path: Path, data: Dict[str, Any]) -> None: If a windows user ends up with tons of temp files, they should consider hosting on something POSIX compatible, or using a different backend instead. - Most users wont encounter this issue, but with high write volumes, + Most users won't encounter this issue, but with high write volumes, without the fsync on both the temp file, and after the replace on the directory, There's no real durability or atomicity guarantee from the filesystem. @@ -252,7 +252,7 @@ def _save_json(path: Path, data: Dict[str, Any]) -> None: tmp_path.replace(path) try: - flag = os.O_DIRECTORY # pylint: disable=no-member + flag = os.O_DIRECTORY except AttributeError: pass else: diff --git a/redbot/core/drivers/postgres/postgres.py b/redbot/core/drivers/postgres/postgres.py index c9c4c17cf0c..7e075d0e6b0 100644 --- a/redbot/core/drivers/postgres/postgres.py +++ b/redbot/core/drivers/postgres/postgres.py @@ -2,16 +2,15 @@ import json import sys from pathlib import Path -from typing import Optional, Any, AsyncIterator, Tuple, Union, Callable, List +from typing import Any, AsyncIterator, Callable, List, Optional, Tuple, Union try: - # pylint: disable=import-error import asyncpg except ModuleNotFoundError: asyncpg = None -from ... import data_manager, errors -from ..base import BaseDriver, IdentifierData, ConfigCategory +from ... import errors +from ..base import BaseDriver, ConfigCategory, IdentifierData from ..log import log __all__ = ["PostgresDriver"] @@ -156,8 +155,8 @@ async def set(self, identifier_data: IdentifierData, value=None): encode_identifier_data(identifier_data), json.dumps(value), ) - except asyncpg.ErrorInAssignmentError: - raise errors.CannotSetSubfield + except asyncpg.ErrorInAssignmentError as exc: + raise errors.CannotSetSubfield from exc async def clear(self, identifier_data: IdentifierData): await self._execute("SELECT red_config.clear($1)", encode_identifier_data(identifier_data)) @@ -167,7 +166,7 @@ async def inc( ) -> Union[int, float]: try: return await self._execute( - f"SELECT red_config.inc($1, $2, $3)", + "SELECT red_config.inc($1, $2, $3)", encode_identifier_data(identifier_data), value, default, diff --git a/redbot/core/errors.py b/redbot/core/errors.py index 7b9bd855dcb..b882c23fc61 100644 --- a/redbot/core/errors.py +++ b/redbot/core/errors.py @@ -3,6 +3,7 @@ import discord from redbot.core.utils.chat_formatting import humanize_number + from .i18n import Translator _ = Translator(__name__, __file__) @@ -27,8 +28,6 @@ class CogLoadError(RedError): """Raised by a cog when it cannot load itself. The message will be sent to the user.""" - pass - class BankError(RedError): """Base error class for bank-related errors.""" diff --git a/redbot/core/events.py b/redbot/core/events.py index d1651eadf81..ff13f4f2113 100644 --- a/redbot/core/events.py +++ b/redbot/core/events.py @@ -1,45 +1,41 @@ import asyncio import contextlib +import importlib.metadata +import logging import platform import sys -import codecs -import logging import traceback from datetime import datetime, timedelta, timezone from typing import Tuple import aiohttp import discord -import importlib.metadata +import rich from packaging.requirements import Requirement -from redbot.core import data_manager +from rich import box +from rich.columns import Columns +from rich.panel import Panel +from rich.table import Table +from rich.text import Text -from redbot.core.commands import RedHelpFormatter, HelpSettings +from redbot.core import data_manager +from redbot.core.commands import HelpSettings, RedHelpFormatter from redbot.core.i18n import ( Translator, - set_contextual_locale, - set_contextual_regional_format, set_contextual_locales_from_guild, ) -from .utils import AsyncIter -from .. import __version__ as red_version, version_info as red_version_info, VersionInfo + +from .. import __version__ as red_version, version_info as red_version_info from . import commands from .config import get_latest_confs from .utils._internal_utils import ( - fuzzy_command_search, - format_fuzzy_results, expected_version, fetch_latest_red_version_info, + format_fuzzy_results, + fuzzy_command_search, send_to_owners_with_prefix_replaced, ) -from .utils.chat_formatting import inline, format_perms_list, humanize_timedelta - -import rich -from rich import box -from rich.table import Table -from rich.columns import Columns -from rich.panel import Panel -from rich.text import Text +from .utils.chat_formatting import format_perms_list, inline log = logging.getLogger("red") @@ -131,7 +127,7 @@ def get_outdated_red_messages(pypi_version: str, py_version_req: str) -> Tuple[s ).format( console=_("Command Prompt") if platform.system() == "Windows" else _("Terminal"), command_1=f'```"{sys.executable}" -m pip install -U "Red-DiscordBot{package_extras}"```', - command_2=f"```[p]cog update```", + command_2="```[p]cog update```", ) outdated_red_message += extra_update return outdated_red_message, rich_outdated_message @@ -205,7 +201,8 @@ async def on_ready(): # We generally shouldn't care if the client supports it or not as Rich deals with it. if not guilds: rich_console.print( - f"Looking for a quick guide on setting up Red? Checkout {Text('https://start.discord.red', style='link https://start.discord.red}')}" + "Looking for a quick guide on setting up Red? Checkout", + Text("https://start.discord.red", style="link https://start.discord.red"), ) if rich_outdated_message: rich_console.print(rich_outdated_message) @@ -263,7 +260,8 @@ async def on_command_error(ctx, error, unhandled_by_cog=False): await ctx.send(disabled_message.replace("{command}", ctx.invoked_with)) elif isinstance(error, commands.CommandInvokeError): log.exception( - "Exception in command '{}'".format(ctx.command.qualified_name), + "Exception in command %r", + ctx.command.qualified_name, exc_info=error.original, ) exception_log = "Exception in command '{}'\n" "".format(ctx.command.qualified_name) diff --git a/redbot/core/i18n.py b/redbot/core/i18n.py index 46ef35f4845..17d841cd55a 100644 --- a/redbot/core/i18n.py +++ b/redbot/core/i18n.py @@ -3,17 +3,18 @@ import contextlib import functools import io -import os import logging -import discord - -from pathlib import Path -from typing import Callable, TYPE_CHECKING, Union, Dict, Optional +import os from contextvars import ContextVar +from pathlib import Path +from typing import TYPE_CHECKING, Dict, Optional, Union import babel.localedata +import discord from babel.core import Locale +from . import commands + if TYPE_CHECKING: from redbot.core.bot import Red @@ -215,7 +216,7 @@ def get_locale_path(cog_folder: Path, extension: str) -> Path: return cog_folder / "locales" / "{}.{}".format(get_locale(), extension) -class Translator(Callable[[str], str]): +class Translator: """Function to get translated strings at runtime.""" def __init__(self, name: str, file_location: Union[str, Path, os.PathLike]): @@ -338,12 +339,6 @@ def get_babel_regional_format(regional_format: Optional[str] = None) -> babel.co return _get_babel_locale(regional_format) -# This import to be down here to avoid circular import issues. -# This will be cleaned up at a later date -# noinspection PyPep8 -from . import commands - - def cog_i18n(translator: Translator): """Get a class decorator to link the translator to this cog.""" diff --git a/redbot/core/modlog.py b/redbot/core/modlog.py index 140f3545dd6..32ba7b1fd30 100644 --- a/redbot/core/modlog.py +++ b/redbot/core/modlog.py @@ -3,22 +3,22 @@ import asyncio import logging from datetime import datetime, timedelta, timezone -from typing import List, Literal, Union, Optional, cast, TYPE_CHECKING +from typing import TYPE_CHECKING, List, Literal, Optional, Union, cast import discord from redbot.core import Config + +from .generic_casetypes import all_generics +from .i18n import Translator, set_contextual_locales_from_guild from .utils import AsyncIter +from .utils.chat_formatting import bold, pagify from .utils.common_filters import ( + escape_spoilers, filter_invites, filter_mass_mentions, filter_urls, - escape_spoilers, ) -from .utils.chat_formatting import bold, pagify -from .i18n import Translator, set_contextual_locales_from_guild - -from .generic_casetypes import all_generics if TYPE_CHECKING: from redbot.core.bot import Red @@ -129,7 +129,7 @@ async def on_member_ban(guild: discord.Guild, member: discord.Member): else: if entry: if entry.user.id != guild.me.id: - # Don't create modlog entires for the bot's own bans, cogs do this. + # Don't create modlog entries for the bot's own bans, cogs do this. mod, reason = entry.user, entry.reason date = entry.created_at await create_case(_bot_ref, guild, date, "ban", member, mod, reason) @@ -169,7 +169,7 @@ async def on_member_unban(guild: discord.Guild, user: discord.User): else: if entry: if entry.user.id != guild.me.id: - # Don't create modlog entires for the bot's own unbans, cogs do this. + # Don't create modlog entries for the bot's own unbans, cogs do this. mod, reason = entry.user, entry.reason date = entry.created_at await create_case(_bot_ref, guild, date, "unban", user, mod, reason) @@ -1236,8 +1236,7 @@ async def register_casetypes(new_types: List[dict]) -> List[CaseType]: pass else: type_list.append(ct) - else: - return type_list + return type_list async def get_modlog_channel( diff --git a/redbot/core/rpc.py b/redbot/core/rpc.py index b5f0a9f1f07..7691bc1f94b 100644 --- a/redbot/core/rpc.py +++ b/redbot/core/rpc.py @@ -1,4 +1,5 @@ import asyncio +import logging import sys from typing import Optional @@ -6,8 +7,6 @@ from aiohttp_json_rpc import JsonRpc from aiohttp_json_rpc.rpc import JsonRpcMethod -import logging - from redbot.core.cli import ExitCodes log = logging.getLogger("red.rpc") @@ -90,7 +89,7 @@ async def initialize(self, port: int): # and isn't subject to a really really stupid but complex # issue on windows with catching specific # exceptions related to shutdown conditions in asyncio applications. - self._started, _discard, self._site = ( + self._started, _, self._site = ( True, await self._runner.setup(), web.TCPSite(self._runner, host="127.0.0.1", port=port, shutdown_timeout=0), diff --git a/redbot/core/settings_caches.py b/redbot/core/settings_caches.py index 7d3ecd7aed5..8565a4832f9 100644 --- a/redbot/core/settings_caches.py +++ b/redbot/core/settings_caches.py @@ -1,9 +1,9 @@ from __future__ import annotations -from typing import Dict, List, Optional, Union, Set, Iterable, Tuple, overload import asyncio from argparse import Namespace from collections import defaultdict +from typing import Dict, Iterable, List, Optional, Set, Union, overload import discord @@ -236,14 +236,10 @@ def __init__(self, config: Config): async def discord_deleted_user(self, user_id: int): async with self._access_lock: - async for guild_id_or_none, ids in AsyncIter( - self._cached_whitelist.items(), steps=100 - ): + async for ids in AsyncIter(self._cached_whitelist.values(), steps=100): ids.discard(user_id) - async for guild_id_or_none, ids in AsyncIter( - self._cached_blacklist.items(), steps=100 - ): + async for ids in AsyncIter(self._cached_blacklist.values(), steps=100): ids.discard(user_id) for grp in (self._config.whitelist, self._config.blacklist): @@ -257,7 +253,7 @@ async def discord_deleted_user(self, user_id: int): # but can't be safe in 3rd party use async with self._config._get_base_group("GUILD").all() as abuse: - for guild_str, guild_data in abuse.items(): + for guild_data in abuse.values(): for l_name in ("whitelist", "blacklist"): try: guild_data[l_name].remove(user_id) diff --git a/redbot/core/utils/__init__.py b/redbot/core/utils/__init__.py index 179343c9af9..4bfe8c4b802 100644 --- a/redbot/core/utils/__init__.py +++ b/redbot/core/utils/__init__.py @@ -1,18 +1,20 @@ from __future__ import annotations + import asyncio import json import logging -from asyncio import as_completed, Semaphore +from asyncio import Semaphore, as_completed from asyncio.futures import isfuture from itertools import chain from pathlib import Path from typing import ( TYPE_CHECKING, Any, - AsyncIterator, AsyncIterable, + AsyncIterator, Awaitable, Callable, + Generator, Iterable, Iterator, List, @@ -22,8 +24,6 @@ Tuple, TypeVar, Union, - Generator, - Coroutine, overload, ) @@ -56,6 +56,7 @@ _T = TypeVar("_T") _S = TypeVar("_S") + # Benchmarked to be the fastest method. def deduplicate_iterables(*iterables): """ @@ -66,8 +67,7 @@ def deduplicate_iterables(*iterables): return list(dict.fromkeys(chain.from_iterable(iterables))) -# https://github.com/PyCQA/pylint/issues/2717 -class AsyncFilter(AsyncIterator[_T], Awaitable[List[_T]]): # pylint: disable=duplicate-bases +class AsyncFilter(AsyncIterator[_T], Awaitable[List[_T]]): """Class returned by `async_filter`. See that function for details. We don't recommend instantiating this class directly. @@ -165,9 +165,9 @@ async def async_enumerate( start : int The index to start from. Defaults to 0. - Returns - ------- - AsyncIterator[Tuple[int, T]] + Yields + ------ + Tuple[int, T] An async iterator of tuples in the form of ``(index, item)``. """ @@ -251,8 +251,6 @@ def bounded_gather( TypeError When invalid parameters are passed """ - loop = asyncio.get_running_loop() - if semaphore is None: if not isinstance(limit, int) or limit <= 0: raise TypeError("limit must be an int > 0") @@ -264,7 +262,7 @@ def bounded_gather( return asyncio.gather(*tasks, return_exceptions=return_exceptions) -class AsyncIter(AsyncIterator[_T], Awaitable[List[_T]]): # pylint: disable=duplicate-bases +class AsyncIter(AsyncIterator[_T], Awaitable[List[_T]]): """Asynchronous iterator yielding items from ``iterable`` that sleeps for ``delay`` seconds every ``steps`` items. @@ -310,8 +308,8 @@ def __aiter__(self) -> AsyncIter[_T]: async def __anext__(self) -> _T: try: item = next(self._iterator) - except StopIteration: - raise StopAsyncIteration + except StopIteration as exc: + raise StopAsyncIteration from exc if self._i == self._steps: self._i = 0 await asyncio.sleep(self._delay) diff --git a/redbot/core/utils/_internal_utils.py b/redbot/core/utils/_internal_utils.py index 5ea383549c9..880b447448b 100644 --- a/redbot/core/utils/_internal_utils.py +++ b/redbot/core/utils/_internal_utils.py @@ -2,7 +2,6 @@ import asyncio import collections.abc -import contextlib import json import logging import os @@ -13,29 +12,25 @@ from datetime import datetime from pathlib import Path from typing import ( - AsyncIterable, + TYPE_CHECKING, AsyncIterator, Awaitable, Callable, - Generator, - Iterable, Iterator, List, Optional, - Union, - TypeVar, - TYPE_CHECKING, Tuple, - cast, + TypeVar, + Union, ) import aiohttp import discord from packaging.requirements import Requirement from rapidfuzz import fuzz, process +from red_commons.logging import TRACE, VERBOSE from rich.progress import ProgressColumn from rich.progress_bar import ProgressBar -from red_commons.logging import VERBOSE, TRACE from redbot import VersionInfo from redbot.core import data_manager @@ -141,7 +136,7 @@ async def fuzzy_command_search( try: await cmd_obj.get(ctx.message, term) - except: + except Exception: pass else: return None @@ -297,7 +292,7 @@ async def wrapped_send(bot, location, content=None, preprocessor=None, **kwargs) await asyncio.gather(*sends) -async def send_to_owners_with_prefix_replaced(bot: Red, content: str, **kwargs): +async def send_to_owners_with_prefix_replaced(bot: Red, content: str): """ This sends something to all owners and their configured extra destinations. diff --git a/redbot/core/utils/antispam.py b/redbot/core/utils/antispam.py index 4a49bc4c133..7caaf94e8fe 100644 --- a/redbot/core/utils/antispam.py +++ b/redbot/core/utils/antispam.py @@ -1,6 +1,6 @@ -from datetime import datetime, timedelta -from typing import Tuple, List from collections import namedtuple +from datetime import datetime, timedelta +from typing import List, Tuple _AntiSpamInterval = namedtuple("_AntiSpamInterval", ["period", "frequency"]) diff --git a/redbot/core/utils/chat_formatting.py b/redbot/core/utils/chat_formatting.py index a36fb761df0..d437fe92c63 100644 --- a/redbot/core/utils/chat_formatting.py +++ b/redbot/core/utils/chat_formatting.py @@ -1,5 +1,4 @@ import datetime -import itertools import textwrap from io import BytesIO from typing import Iterator, List, Optional, Sequence, SupportsInt, Union @@ -202,7 +201,7 @@ def spoiler(text: str, escape_formatting: bool = True) -> str: def pagify( text: str, - delims: Sequence[str] = ["\n"], + delims: Sequence[str] = ("\n",), *, priority: bool = False, escape_mass_mentions: bool = True, @@ -329,7 +328,7 @@ def quote(text: str) -> str: The marked up text. """ - return textwrap.indent(text, "> ", lambda l: True) + return textwrap.indent(text, "> ", lambda line: True) def escape(text: str, *, mass_mentions: bool = False, formatting: bool = False) -> str: @@ -481,7 +480,7 @@ def humanize_timedelta( try: obj = seconds if seconds is not None else timedelta.total_seconds() except AttributeError: - raise ValueError("You must provide either a timedelta or a number of seconds") + raise ValueError("You must provide either a timedelta or a number of seconds") from None seconds = int(obj) periods = [ diff --git a/redbot/core/utils/embed.py b/redbot/core/utils/embed.py index ede53c780d5..bff85563c0a 100644 --- a/redbot/core/utils/embed.py +++ b/redbot/core/utils/embed.py @@ -1,7 +1,7 @@ -import discord - import random +import discord + def randomize_colour(embed: discord.Embed) -> discord.Embed: """ diff --git a/redbot/core/utils/menus.py b/redbot/core/utils/menus.py index 693681c4d44..b8bae6c3cc8 100644 --- a/redbot/core/utils/menus.py +++ b/redbot/core/utils/menus.py @@ -208,7 +208,7 @@ async def menu( ): await message.clear_reactions() else: - raise RuntimeError + raise RuntimeError from None except (discord.Forbidden, RuntimeError): # cannot remove all reactions for key in controls.keys(): try: @@ -238,6 +238,7 @@ async def next_page( Function for showing next page which is suitable for use in ``controls`` mapping that is passed to `menu()`. """ + del emoji if page >= len(pages) - 1: page = 0 # Loop around to the first item else: @@ -258,6 +259,7 @@ async def prev_page( Function for showing previous page which is suitable for use in ``controls`` mapping that is passed to `menu()`. """ + del emoji if page <= 0: page = len(pages) - 1 # Loop around to the last item else: @@ -278,6 +280,7 @@ async def close_menu( Function for closing (deleting) menu which is suitable for use in ``controls`` mapping that is passed to `menu()`. """ + del ctx, pages, controls, page, timeout, emoji with contextlib.suppress(discord.NotFound): await message.delete() diff --git a/redbot/core/utils/mod.py b/redbot/core/utils/mod.py index 09212f8215e..e5a8b154c75 100644 --- a/redbot/core/utils/mod.py +++ b/redbot/core/utils/mod.py @@ -1,6 +1,6 @@ import asyncio from datetime import timedelta -from typing import List, Iterable, Union, TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Union import discord diff --git a/redbot/core/utils/predicates.py b/redbot/core/utils/predicates.py index 11a1d035e5f..2b0e7c145af 100644 --- a/redbot/core/utils/predicates.py +++ b/redbot/core/utils/predicates.py @@ -1,7 +1,7 @@ from __future__ import annotations import re -from typing import Callable, ClassVar, List, Optional, Pattern, Sequence, Tuple, Union, cast +from typing import Callable, ClassVar, Optional, Pattern, Sequence, Tuple, Union, cast import discord @@ -13,7 +13,7 @@ _ROLE_MENTION_RE = re.compile(r"<@&([0-9]{15,20})>$") -class MessagePredicate(Callable[[discord.Message], bool]): +class MessagePredicate: """A simple collection of predicates for message events. These predicates intend to help simplify checks in message events @@ -857,7 +857,7 @@ def _get_guild( return user.guild -class ReactionPredicate(Callable[[discord.Reaction, discord.abc.User], bool]): +class ReactionPredicate: """A collection of predicates for reaction events. All checks are combined with :meth:`ReactionPredicate.same_context`. diff --git a/redbot/core/utils/tunnel.py b/redbot/core/utils/tunnel.py index 9e8c2f7c4d1..97f989ae360 100644 --- a/redbot/core/utils/tunnel.py +++ b/redbot/core/utils/tunnel.py @@ -1,10 +1,12 @@ import asyncio -import discord -from datetime import datetime -from redbot.core.utils.chat_formatting import pagify -import io import weakref +from datetime import datetime from typing import List, Optional, Union + +import discord + +from redbot.core.utils.chat_formatting import pagify + from .common_filters import filter_mass_mentions _instances = weakref.WeakValueDictionary({}) @@ -22,26 +24,17 @@ def __call__(cls, *args, **kwargs): if lockout_tuple in _instances: return _instances[lockout_tuple] - # this is needed because weakvalue dicts can - # change size without warning if an object is discarded - # it can raise a runtime error, so .. - while True: - try: - if not ( - any(lockout_tuple[0] == x[0] for x in _instances.keys()) - or any(lockout_tuple[1] == x[1] for x in _instances.keys()) - ): - # if this isn't temporarily stored, the weakref dict - # will discard this before the return statement, - # causing a key error - temp = super(TunnelMeta, cls).__call__(*args, **kwargs) - _instances[lockout_tuple] = temp - return temp - except: # NOQA: E722 - # Am I really supposed to except a runtime error flake >.> - continue - else: - return None + if not ( + any(lockout_tuple[0] == x[0] for x in _instances.keys()) + or any(lockout_tuple[1] == x[1] for x in _instances.keys()) + ): + # if this isn't temporarily stored, the weakref dict + # will discard this before the return statement, + # causing a key error + temp = super(TunnelMeta, cls).__call__(*args, **kwargs) + _instances[lockout_tuple] = temp + return temp + return None class Tunnel(metaclass=TunnelMeta): @@ -179,7 +172,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..688bf553b4a 100644 --- a/redbot/core/utils/views.py +++ b/redbot/core/utils/views.py @@ -1,13 +1,13 @@ from __future__ import annotations -import discord +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +import discord from discord.ext.commands import BadArgument -from typing import TYPE_CHECKING, Any, List, Optional, Union, Dict + +from redbot.core.commands.converter import get_dict_converter from redbot.core.i18n import Translator from redbot.vendored.discord.ext import menus -from redbot.core.commands.converter import get_dict_converter - if TYPE_CHECKING: from redbot.core.commands import Context @@ -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 ) @@ -427,6 +427,7 @@ async def interaction_check(self, interaction: discord.Interaction) -> bool: style=discord.ButtonStyle.grey, ) async def auth_button(self, interaction: discord.Interaction, button: discord.Button): + del button return await interaction.response.send_modal( SetApiModal(self.default_service, self.default_keys) ) diff --git a/redbot/launcher.py b/redbot/launcher.py index 31b753c9e01..40181dfee9e 100644 --- a/redbot/launcher.py +++ b/redbot/launcher.py @@ -1,31 +1,18 @@ # This file is retained in it's slimmest form which handles autorestart for users on -# windows and osx until we have proper autorestart docs for theses oses +# windows and osx until we have proper autorestart docs for these oses # no new features will be added to this file # issues in this file are to be met with removal, not with fixes. -import getpass +import argparse import os -import platform import subprocess import sys -import argparse -import asyncio -import aiohttp from redbot import MIN_PYTHON_VERSION -from redbot.setup import ( - basic_setup, - remove_instance, - remove_instance_interaction, - create_backup, -) -from redbot.core import __version__, version_info as red_version_info, VersionInfo -from redbot.core.cli import ExitCodes, confirm +from redbot.core import __version__ +from redbot.core.cli import ExitCodes from redbot.core.data_manager import load_existing_config -if sys.platform == "linux": - import distro # pylint: disable=import-error - INTERACTIVE_MODE = not len(sys.argv) > 1 # CLI flags = non-interactive INTRO = "==========================\nRed Discord Bot - Launcher\n==========================\n" @@ -33,7 +20,7 @@ IS_WINDOWS = os.name == "nt" IS_MAC = sys.platform == "darwin" -PYTHON_OK = sys.version_info >= MIN_PYTHON_VERSION or os.getenv("READTHEDOCS", False) +PYTHON_OK = sys.version_info >= MIN_PYTHON_VERSION or os.getenv("READTHEDOCS") def is_venv(): diff --git a/redbot/logging.py b/redbot/logging.py index 0e21793e52e..1f5af05474e 100644 --- a/redbot/logging.py +++ b/redbot/logging.py @@ -3,11 +3,10 @@ import pathlib import re import sys - -from typing import List, Tuple, Optional -from logging import LogRecord from datetime import datetime # This clearly never leads to confusion... +from logging import LogRecord from os import isatty +from typing import List, Optional, Tuple import rich from pygments.styles.monokai import MonokaiStyle # DEP-WARN @@ -31,7 +30,6 @@ from rich.theme import Theme from rich.traceback import PathHighlighter, Traceback # DEP-WARN - MAX_OLD_LOGS = 8 diff --git a/redbot/pytest/.gitattributes b/redbot/pytest/.gitattributes index 31b7e1c6177..defe82550d1 100644 --- a/redbot/pytest/.gitattributes +++ b/redbot/pytest/.gitattributes @@ -1 +1 @@ -downloader_testrepo.export -text \ No newline at end of file +downloader_testrepo.export -text diff --git a/redbot/pytest/__init__.py b/redbot/pytest/__init__.py index bb67a43fa4e..62e7547d5b9 100644 --- a/redbot/pytest/__init__.py +++ b/redbot/pytest/__init__.py @@ -1 +1 @@ -from .core import * +from .core import * # noqa: F401, F403 diff --git a/redbot/pytest/core.py b/redbot/pytest/core.py index ee41822ccb4..e3e27e60646 100644 --- a/redbot/pytest/core.py +++ b/redbot/pytest/core.py @@ -1,12 +1,12 @@ import random +import weakref from collections import namedtuple from pathlib import Path -import weakref import pytest -from redbot.core import Config + +from redbot.core import Config, config as config_module, drivers from redbot.core.bot import Red -from redbot.core import config as config_module, drivers __all__ = [ "override_data_path", diff --git a/redbot/pytest/downloader.py b/redbot/pytest/downloader.py index 2f961fbaca6..0173829459c 100644 --- a/redbot/pytest/downloader.py +++ b/redbot/pytest/downloader.py @@ -1,13 +1,13 @@ -from collections import namedtuple -from pathlib import Path import json -import subprocess as sp import shutil +import subprocess as sp +from collections import namedtuple +from pathlib import Path import pytest -from redbot.cogs.downloader.repo_manager import RepoManager, Repo, ProcessFormatter from redbot.cogs.downloader.installable import Installable, InstalledModule +from redbot.cogs.downloader.repo_manager import Repo, RepoManager __all__ = [ "GIT_VERSION", @@ -45,14 +45,13 @@ async def fake_run_noprint(*args, **kwargs): return res -async def fake_current_commit(*args, **kwargs): +async def fake_current_commit(*_args, **_kwargs): return "fake_result" @pytest.fixture -def repo_manager(tmpdir_factory): +def repo_manager(): rm = RepoManager() - # rm.repos_folder = Path(str(tmpdir_factory.getbasetemp())) / 'repos' return rm @@ -71,7 +70,7 @@ def repo(tmp_path): @pytest.fixture -def bot_repo(event_loop): +def bot_repo(): cwd = Path.cwd() return Repo( name="Red-DiscordBot", @@ -163,7 +162,7 @@ def _init_test_repo(destination: Path): @pytest.fixture(scope="session") -async def _session_git_repo(tmp_path_factory, event_loop): +async def _session_git_repo(tmp_path_factory): # we will import repo only once once per session and duplicate the repo folder repo_path = tmp_path_factory.mktemp("session_git_repo") repo = Repo(name="redbot-testrepo", url="", branch="master", commit="", folder_path=repo_path) @@ -179,7 +178,7 @@ async def _session_git_repo(tmp_path_factory, event_loop): @pytest.fixture -async def git_repo(_session_git_repo, tmp_path, event_loop): +async def git_repo(_session_git_repo, tmp_path): # fixture only copies repo that was imported in _session_git_repo repo_path = tmp_path / "redbot-testrepo" shutil.copytree(_session_git_repo.folder_path, repo_path) @@ -194,7 +193,7 @@ async def git_repo(_session_git_repo, tmp_path, event_loop): @pytest.fixture -async def cloned_git_repo(_session_git_repo, tmp_path, event_loop): +async def cloned_git_repo(_session_git_repo, tmp_path): # don't use this if you want to edit origin repo repo_path = tmp_path / "redbot-cloned_testrepo" repo = Repo( @@ -209,7 +208,7 @@ async def cloned_git_repo(_session_git_repo, tmp_path, event_loop): @pytest.fixture -async def git_repo_with_remote(git_repo, tmp_path, event_loop): +async def git_repo_with_remote(git_repo, tmp_path): # this can safely be used when you want to do changes to origin repo repo_path = tmp_path / "redbot-testrepo_with_remote" repo = Repo( diff --git a/redbot/pytest/downloader_testrepo.export b/redbot/pytest/downloader_testrepo.export index 5e27ae04164..c6ef16b1044 100644 --- a/redbot/pytest/downloader_testrepo.export +++ b/redbot/pytest/downloader_testrepo.export @@ -131,4 +131,3 @@ original-oid c6f028f843389c850e2c20d8dd1f5fa498252764 tagger Cog-Creators 1571919491 +0200 data 37 Annotated tag ambiguous with commit. - diff --git a/redbot/pytest/downloader_testrepo.md b/redbot/pytest/downloader_testrepo.md index 77571a4d691..2766d9dcc9e 100644 --- a/redbot/pytest/downloader_testrepo.md +++ b/redbot/pytest/downloader_testrepo.md @@ -99,4 +99,4 @@ Branch with ambiguous tag (c6f0) ``` downloader_testrepo/ A └── sample_file1.txt -``` \ No newline at end of file +``` diff --git a/redbot/pytest/economy.py b/redbot/pytest/economy.py index 8b20e930e8c..c22ea7ca49c 100644 --- a/redbot/pytest/economy.py +++ b/redbot/pytest/economy.py @@ -1,4 +1,5 @@ import pytest + from redbot.core import bank as bank_module __all__ = ["bank"] diff --git a/redbot/pytest/mod.py b/redbot/pytest/mod.py index 7cadeba986c..d78634d8836 100644 --- a/redbot/pytest/mod.py +++ b/redbot/pytest/mod.py @@ -1,4 +1,5 @@ import pytest + from redbot.core import modlog __all__ = ["mod"] diff --git a/redbot/pytest/rpc.py b/redbot/pytest/rpc.py index ffe3029d7a1..f7ac3428b02 100644 --- a/redbot/pytest/rpc.py +++ b/redbot/pytest/rpc.py @@ -1,7 +1,8 @@ +from unittest.mock import MagicMock + import pytest -from redbot.core.rpc import RPC, RPCMixin -from unittest.mock import MagicMock +from redbot.core.rpc import RPC, RPCMixin __all__ = ["rpc", "rpcmixin", "cog", "existing_func", "existing_multi_func"] @@ -23,16 +24,16 @@ def rpcmixin(): @pytest.fixture() def cog(): class Cog: - async def cofunc(*args, **kwargs): + async def cofunc(self, *args, **kwargs): pass - async def cofunc2(*args, **kwargs): + async def cofunc2(self, *args, **kwargs): pass - async def cofunc3(*args, **kwargs): + async def cofunc3(self, *args, **kwargs): pass - def func(*args, **kwargs): + def func(self, *args, **kwargs): pass return Cog() diff --git a/redbot/setup.py b/redbot/setup.py index 3e4ba8e8d43..cae1be4625d 100644 --- a/redbot/setup.py +++ b/redbot/setup.py @@ -1,29 +1,30 @@ from redbot import _early_init -# this needs to be called as early as possible -_early_init() +# this `if` keeps flake8 happy about doing imports after function call (E402) +if True: + # this needs to be called as early as possible + _early_init() import asyncio import json import logging -import sys import re +import sys from copy import deepcopy from pathlib import Path -from typing import Dict, Any, Optional, Union +from typing import Any, Dict, Optional import click -from redbot.core.cli import confirm +from redbot.core import config, data_manager, drivers +from redbot.core.cli import ExitCodes, confirm +from redbot.core.data_manager import appdir, config_dir, config_file +from redbot.core.drivers import BackendType from redbot.core.utils._internal_utils import ( - safe_delete, - create_backup as red_create_backup, cli_level_to_log_level, + create_backup as red_create_backup, + safe_delete, ) -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 conversion_log = logging.getLogger("red.converter") @@ -166,7 +167,8 @@ def get_name(name: str) -> str: ) name = "" elif "-" in name and not confirm( - "Hyphens (-) in instance names may cause issues. Are you sure you want to continue with this instance name?", + "Hyphens (-) in instance names may cause issues." + " Are you sure you want to continue with this instance name?", default=False, ): name = "" @@ -183,10 +185,7 @@ def basic_setup( interactive: bool, overwrite_existing_instance: bool, ): - """ - Creates the data storage folder. - :return: - """ + """Creates the data storage folder.""" if not interactive and not name: print( "Providing instance name through --instance-name is required" @@ -536,10 +535,10 @@ def convert(instance: str, backend: str) -> None: default_dirs["STORAGE_TYPE"] = target.value default_dirs["STORAGE_DETAILS"] = new_storage_details save_config(instance, default_dirs) - conversion_log.info(f"Conversion to {target} complete.") + conversion_log.info("Conversion to %s complete.", target) else: conversion_log.info( - f"Cannot convert {current_backend.value} to {target.value} at this time." + "Cannot convert %s to %s at this time.", current_backend.value, target.value ) @@ -560,7 +559,8 @@ def backup(instance: str, destination_folder: Path) -> None: def run_cli(): # Setuptools entry point script stuff... try: - cli() # pylint: disable=no-value-for-parameter # click + # https://github.com/PyCQA/pylint/issues/1694 + cli() # pylint: disable=no-value-for-parameter except KeyboardInterrupt: print("Exiting...") else: diff --git a/schema/red_cog.schema.json b/schema/red_cog.schema.json index c7699ba4c51..3340c07a394 100644 --- a/schema/red_cog.schema.json +++ b/schema/red_cog.schema.json @@ -1,98 +1,98 @@ { - "$id": "https://raw.githubusercontent.com/Cog-Creators/Red-DiscordBot/V3/develop/schema/red_cog.schema.json", - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Red-DiscordBot Сog metadata file", - "type": "object", - "properties": { - "author": { - "type": "array", - "description": "List of names of authors of the cog", - "items": { - "type": "string" - } - }, - "description": { - "type": "string", - "description": "A long description of the cog or repo. For cogs, this is displayed when a user executes [p]cog info." - }, - "install_msg": { - "type": "string", - "description": "The message that gets displayed when a cog is installed or a repo is added" - }, - "short": { - "type": "string", - "description": "A short description of the cog or repo. For cogs, this info is displayed when a user executes [p]cog list" - }, - "end_user_data_statement": { - "type": "string", - "description": "A statement explaining what end user data the cog is storing. This is displayed when a user executes [p]cog info. If the statement has changed since last update, user will be informed during the update." - }, - "min_bot_version": { - "type": "string", - "description": "Min version number of Red in the format MAJOR.MINOR.MICRO", - "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)((a|b|rc)(0|[1-9][0-9]*))?(\\.post(0|[1-9][0-9]*))?(\\.dev(0|[1-9][0-9]*))?$" - }, - "max_bot_version": { - "type": "string", - "description": "Max version number of Red in the format MAJOR.MINOR.MICRO, if min_bot_version is newer than max_bot_version, max_bot_version will be ignored", - "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)((a|b|rc)(0|[1-9][0-9]*))?(\\.post(0|[1-9][0-9]*))?(\\.dev(0|[1-9][0-9]*))?$" - }, - "min_python_version": { - "type": "array", - "description": "Min version number of Python in the format [MAJOR, MINOR, PATCH]", - "minItems": 3, - "maxItems": 3, - "items": { - "type": "integer" - } - }, - "hidden": { - "type": "boolean", - "description": "Determines if a cog is visible in the cog list for a repo." - }, - "disabled": { - "type": "boolean", - "description": "Determines if a cog is available for install." - }, - "required_cogs": { - "type": "object", - "description": "A dict of required cogs that this cog depends on in the format {cog_name : repo_url}. Downloader will not deal with this functionality but it may be useful for other cogs.", - "$ref": "#/definitions/required_cog" - }, - "requirements": { - "type": "array", - "description": "List of required libraries that are passed to pip on cog install.", - "items": { - "type": "string" - } - }, - "tags": { - "type": "array", - "description": "A list of strings that are related to the functionality of the cog. Used to aid in searching.", - "uniqueItems": true, - "items": { - "type": "string" - } + "$id": "https://raw.githubusercontent.com/Cog-Creators/Red-DiscordBot/V3/develop/schema/red_cog.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Red-DiscordBot Cog metadata file", + "type": "object", + "properties": { + "author": { + "type": "array", + "description": "List of names of authors of the cog", + "items": { + "type": "string" + } + }, + "description": { + "type": "string", + "description": "A long description of the cog or repo. For cogs, this is displayed when a user executes [p]cog info." + }, + "install_msg": { + "type": "string", + "description": "The message that gets displayed when a cog is installed or a repo is added" + }, + "short": { + "type": "string", + "description": "A short description of the cog or repo. For cogs, this info is displayed when a user executes [p]cog list" + }, + "end_user_data_statement": { + "type": "string", + "description": "A statement explaining what end user data the cog is storing. This is displayed when a user executes [p]cog info. If the statement has changed since last update, user will be informed during the update." + }, + "min_bot_version": { + "type": "string", + "description": "Min version number of Red in the format MAJOR.MINOR.MICRO", + "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)((a|b|rc)(0|[1-9][0-9]*))?(\\.post(0|[1-9][0-9]*))?(\\.dev(0|[1-9][0-9]*))?$" + }, + "max_bot_version": { + "type": "string", + "description": "Max version number of Red in the format MAJOR.MINOR.MICRO, if min_bot_version is newer than max_bot_version, max_bot_version will be ignored", + "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)((a|b|rc)(0|[1-9][0-9]*))?(\\.post(0|[1-9][0-9]*))?(\\.dev(0|[1-9][0-9]*))?$" + }, + "min_python_version": { + "type": "array", + "description": "Min version number of Python in the format [MAJOR, MINOR, PATCH]", + "minItems": 3, + "maxItems": 3, + "items": { + "type": "integer" + } + }, + "hidden": { + "type": "boolean", + "description": "Determines if a cog is visible in the cog list for a repo." + }, + "disabled": { + "type": "boolean", + "description": "Determines if a cog is available for install." + }, + "required_cogs": { + "type": "object", + "description": "A dict of required cogs that this cog depends on in the format {cog_name : repo_url}. Downloader will not deal with this functionality but it may be useful for other cogs.", + "$ref": "#/definitions/required_cog" + }, + "requirements": { + "type": "array", + "description": "List of required libraries that are passed to pip on cog install.", + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "description": "A list of strings that are related to the functionality of the cog. Used to aid in searching.", + "uniqueItems": true, + "items": { + "type": "string" + } + }, + "type": { + "type": "string", + "description": "Optional, defaults to COG. Must be either COG or SHARED_LIBRARY. If SHARED_LIBRARY then hidden will be True.", + "enum": [ + "COG", + "SHARED_LIBRARY" + ] + } }, - "type": { - "type": "string", - "description": "Optional, defaults to COG. Must be either COG or SHARED_LIBRARY. If SHARED_LIBRARY then hidden will be True.", - "enum": [ - "COG", - "SHARED_LIBRARY" - ] - } - }, - "definitions": { - "required_cog": { - "type": "object", - "patternProperties": { - ".+": { - "type": "string", - "format": "uri" + "definitions": { + "required_cog": { + "type": "object", + "patternProperties": { + ".+": { + "type": "string", + "format": "uri" + } + }, + "additionalProperties": false } - }, - "additionalProperties": false } - } } diff --git a/schema/red_cog_repo.schema.json b/schema/red_cog_repo.schema.json index a70af73c51e..782556f6421 100644 --- a/schema/red_cog_repo.schema.json +++ b/schema/red_cog_repo.schema.json @@ -1,27 +1,27 @@ { - "$id": "https://raw.githubusercontent.com/Cog-Creators/Red-DiscordBot/V3/develop/schema/red_cog_repo.schema.json", - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Red-DiscordBot Сog Repo metadata file", - "type": "object", - "properties": { - "author": { - "type": "array", - "description": "List of names of authors of the cog", - "items": { - "type": "string" - } - }, - "description": { - "type": "string", - "description": "A long description of the cog or repo. For cogs, this is displayed when a user executes [p]cog info." - }, - "install_msg": { - "type": "string", - "description": "The message that gets displayed when a cog is installed or a repo is added" - }, - "short": { - "type": "string", - "description": "A short description of the cog or repo. For cogs, this info is displayed when a user executes [p]cog list" + "$id": "https://raw.githubusercontent.com/Cog-Creators/Red-DiscordBot/V3/develop/schema/red_cog_repo.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Red-DiscordBot \u0421og Repo metadata file", + "type": "object", + "properties": { + "author": { + "type": "array", + "description": "List of names of authors of the cog", + "items": { + "type": "string" + } + }, + "description": { + "type": "string", + "description": "A long description of the cog or repo. For cogs, this is displayed when a user executes [p]cog info." + }, + "install_msg": { + "type": "string", + "description": "The message that gets displayed when a cog is installed or a repo is added" + }, + "short": { + "type": "string", + "description": "A short description of the cog or repo. For cogs, this info is displayed when a user executes [p]cog list" + } } - } } diff --git a/setup.py b/setup.py index 3ecbf527d9e..50c7dff977b 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ # Since we're importing `redbot` package, we have to ensure that it's in sys.path. sys.path.insert(0, str(ROOT_FOLDER)) -from redbot import VersionInfo +from redbot import VersionInfo # noqa: E402 version, _ = VersionInfo._get_version(ignore_installed=True) @@ -47,7 +47,7 @@ def extras_combined(*extra_names): python_requires = ">=3.8.1" -if not os.getenv("TOX_RED", False) or sys.version_info < (3, 12): +if not os.getenv("TOX_RED") or sys.version_info < (3, 12): python_requires += ",<3.12" # Metadata and options defined in pyproject.toml diff --git a/tests/cogs/downloader/test_downloader.py b/tests/cogs/downloader/test_downloader.py index e60d401f4ae..c27122d7e1e 100644 --- a/tests/cogs/downloader/test_downloader.py +++ b/tests/cogs/downloader/test_downloader.py @@ -1,7 +1,5 @@ -import asyncio -import pathlib from collections import namedtuple -from typing import Any, NamedTuple +from typing import NamedTuple from pathlib import Path import pytest @@ -10,11 +8,10 @@ from redbot.pytest.downloader import * from redbot.cogs.downloader.repo_manager import Installable -from redbot.cogs.downloader.repo_manager import Candidate, ProcessFormatter, RepoManager, Repo +from redbot.cogs.downloader.repo_manager import Candidate, ProcessFormatter, Repo from redbot.cogs.downloader.errors import ( AmbiguousRevision, ExistingGitRepo, - GitException, UnknownRevision, ) diff --git a/tests/cogs/downloader/test_installable.py b/tests/cogs/downloader/test_installable.py index 825945bafbc..c8fbf19d570 100644 --- a/tests/cogs/downloader/test_installable.py +++ b/tests/cogs/downloader/test_installable.py @@ -1,10 +1,7 @@ -import json -from pathlib import Path - import pytest from redbot.pytest.downloader import * -from redbot.cogs.downloader.installable import Installable, InstallableType +from redbot.cogs.downloader.installable import InstallableType from redbot.core import VersionInfo diff --git a/tests/core/test_config.py b/tests/core/test_config.py index ac34ae6885b..349ea9bb95c 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -373,7 +373,7 @@ async def test_ctxmgr_no_unnecessary_write(config): config.register_global(foo=[]) foo_value_obj = config.foo with patch.object(foo_value_obj, "set") as set_method: - async with foo_value_obj() as foo: + async with foo_value_obj() as _: pass set_method.assert_not_called() diff --git a/tests/core/test_version.py b/tests/core/test_version.py index 954ce894740..0de5f84fa46 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, 12), + os.getenv("TOX_RED") 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/edit_testrepo.py b/tools/edit_testrepo.py index 93a4b29db7c..f6a6480fbc4 100644 --- a/tools/edit_testrepo.py +++ b/tools/edit_testrepo.py @@ -40,7 +40,6 @@ import click - MAIN_DIRECTORY = Path(__file__).absolute().parent.parent TEST_REPO_EXPORT_PTH: Path = MAIN_DIRECTORY / "redbot" / "pytest" / "downloader_testrepo.export"