From ffda1f7069065fd4ce2b2fe781fbcb5793406f99 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Tue, 31 Aug 2021 15:01:10 +0200 Subject: Migrate to Discord.py 2.0a0 Since the Discord.py repository has been archived, we can switch to the latest commit of 2.0a0, knowing no breaking change will occur (still pinned to the commit just in case). This commit also solves two small problems due to that fix, the avatar interface changing and Embed.name disappearing. Quite a painless migration. --- bot/exts/utils/extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/utils/extensions.py') diff --git a/bot/exts/utils/extensions.py b/bot/exts/utils/extensions.py index 64e404d2..a95def4e 100644 --- a/bot/exts/utils/extensions.py +++ b/bot/exts/utils/extensions.py @@ -155,7 +155,7 @@ class Extensions(commands.Cog): embed.set_author( name="Extensions List", url=Client.github_bot_repo, - icon_url=str(self.bot.user.avatar_url) + icon_url=str(self.bot.user.display_avatar.url) ) lines = [] -- cgit v1.2.3 From 745cd1d6d3d6227d2a1e82cf25611d76221c40cd Mon Sep 17 00:00:00 2001 From: decorator-factory <42166884+decorator-factory@users.noreply.github.com> Date: Sat, 7 Aug 2021 05:23:03 +0300 Subject: Fix type annotations --- bot/constants.py | 4 +- bot/exts/__init__.py | 2 +- bot/exts/christmas/advent_of_code/_helpers.py | 15 ++++--- bot/exts/christmas/hanukkah_embed.py | 5 +-- bot/exts/easter/bunny_name_generator.py | 6 +-- bot/exts/evergreen/avatar_modification/_effects.py | 12 +++--- .../evergreen/avatar_modification/avatar_modify.py | 12 +++--- bot/exts/evergreen/battleship.py | 26 ++++++------ bot/exts/evergreen/bookmark.py | 4 +- bot/exts/evergreen/cheatsheet.py | 4 +- bot/exts/evergreen/coinflip.py | 5 +-- bot/exts/evergreen/connect_four.py | 30 +++++++------- bot/exts/evergreen/emoji.py | 6 +-- bot/exts/evergreen/error_handler.py | 3 +- bot/exts/evergreen/fun.py | 5 ++- bot/exts/evergreen/game.py | 16 ++++---- bot/exts/evergreen/help.py | 8 ++-- bot/exts/evergreen/issues.py | 10 ++--- bot/exts/evergreen/minesweeper.py | 12 +++--- bot/exts/evergreen/movie.py | 10 ++--- bot/exts/evergreen/reddit.py | 8 ++-- bot/exts/evergreen/snakes/_converter.py | 4 +- bot/exts/evergreen/snakes/_snakes_cog.py | 8 ++-- bot/exts/evergreen/snakes/_utils.py | 46 +++++++++++----------- bot/exts/evergreen/source.py | 4 +- bot/exts/evergreen/space.py | 6 +-- bot/exts/evergreen/tic_tac_toe.py | 24 +++++------ bot/exts/evergreen/trivia_quiz.py | 4 +- bot/exts/evergreen/wikipedia.py | 4 +- bot/exts/evergreen/wolfram.py | 14 +++---- bot/exts/evergreen/xkcd.py | 4 +- bot/exts/halloween/hacktober-issue-finder.py | 6 +-- bot/exts/halloween/hacktoberstats.py | 14 +++---- bot/exts/halloween/halloween_facts.py | 3 +- bot/exts/halloween/spookyrating.py | 3 +- bot/exts/halloween/timeleft.py | 3 +- bot/exts/internal_eval/_helpers.py | 19 +++++---- bot/exts/internal_eval/_internal_eval.py | 4 +- bot/exts/utils/extensions.py | 7 ++-- bot/exts/valentines/be_my_valentine.py | 7 ++-- bot/exts/valentines/valentine_zodiac.py | 4 +- bot/utils/__init__.py | 5 ++- bot/utils/checks.py | 3 +- bot/utils/converters.py | 4 +- bot/utils/decorators.py | 37 ++++++++--------- bot/utils/extensions.py | 3 +- bot/utils/pagination.py | 5 ++- bot/utils/randomization.py | 7 ++-- 48 files changed, 227 insertions(+), 228 deletions(-) (limited to 'bot/exts/utils/extensions.py') diff --git a/bot/constants.py b/bot/constants.py index 2730106b..2313bfdb 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -3,7 +3,7 @@ import enum import logging from datetime import datetime from os import environ -from typing import Dict, NamedTuple +from typing import NamedTuple __all__ = ( "AdventOfCode", @@ -56,7 +56,7 @@ class AdventOfCodeLeaderboard: return self._session -def _parse_aoc_leaderboard_env() -> Dict[str, AdventOfCodeLeaderboard]: +def _parse_aoc_leaderboard_env() -> dict[str, AdventOfCodeLeaderboard]: """ Parse the environment variable containing leaderboard information. diff --git a/bot/exts/__init__.py b/bot/exts/__init__.py index 13f484ac..cb9c5ae8 100644 --- a/bot/exts/__init__.py +++ b/bot/exts/__init__.py @@ -1,6 +1,6 @@ import logging import pkgutil -from typing import Iterator +from collections.abc import Iterator __all__ = ("get_package_names",) diff --git a/bot/exts/christmas/advent_of_code/_helpers.py b/bot/exts/christmas/advent_of_code/_helpers.py index e26a17ca..b64b44a6 100644 --- a/bot/exts/christmas/advent_of_code/_helpers.py +++ b/bot/exts/christmas/advent_of_code/_helpers.py @@ -5,8 +5,7 @@ import json import logging import math import operator -import typing -from typing import Tuple +from typing import Any, Optional import aiohttp import arrow @@ -71,7 +70,7 @@ class FetchingLeaderboardFailedError(Exception): """Raised when one or more leaderboards could not be fetched at all.""" -def leaderboard_sorting_function(entry: typing.Tuple[str, dict]) -> typing.Tuple[int, int]: +def leaderboard_sorting_function(entry: tuple[str, dict]) -> tuple[int, int]: """ Provide a sorting value for our leaderboard. @@ -155,7 +154,7 @@ def _parse_raw_leaderboard_data(raw_leaderboard_data: dict) -> dict: return {"daily_stats": daily_stats, "leaderboard": sorted_leaderboard} -def _format_leaderboard(leaderboard: typing.Dict[str, dict]) -> str: +def _format_leaderboard(leaderboard: dict[str, dict]) -> str: """Format the leaderboard using the AOC_TABLE_TEMPLATE.""" leaderboard_lines = [HEADER] for rank, data in enumerate(leaderboard.values(), start=1): @@ -171,7 +170,7 @@ def _format_leaderboard(leaderboard: typing.Dict[str, dict]) -> str: return "\n".join(leaderboard_lines) -async def _leaderboard_request(url: str, board: int, cookies: dict) -> typing.Optional[dict]: +async def _leaderboard_request(url: str, board: str, cookies: dict) -> dict[str, Any]: """Make a leaderboard request using the specified session cookie.""" async with aiohttp.request("GET", url, headers=AOC_REQUEST_HEADER, cookies=cookies) as resp: # The Advent of Code website redirects silently with a 200 response if a @@ -188,7 +187,7 @@ async def _leaderboard_request(url: str, board: int, cookies: dict) -> typing.Op return await resp.json() -async def _fetch_leaderboard_data() -> typing.Dict[str, typing.Any]: +async def _fetch_leaderboard_data() -> dict[str, Any]: """Fetch data for all leaderboards and return a pooled result.""" year = AdventOfCode.year @@ -333,7 +332,7 @@ def get_summary_embed(leaderboard: dict) -> discord.Embed: return aoc_embed -async def get_public_join_code(author: discord.Member) -> typing.Optional[str]: +async def get_public_join_code(author: discord.Member) -> Optional[str]: """ Get the join code for one of the non-staff leaderboards. @@ -398,7 +397,7 @@ def is_in_advent() -> bool: return arrow.now(EST).day in range(1, 25) and arrow.now(EST).month == 12 -def time_left_to_est_midnight() -> Tuple[datetime.datetime, datetime.timedelta]: +def time_left_to_est_midnight() -> tuple[datetime.datetime, datetime.timedelta]: """Calculate the amount of time left until midnight EST/UTC-5.""" # Change all time properties back to 00:00 todays_midnight = arrow.now(EST).replace( diff --git a/bot/exts/christmas/hanukkah_embed.py b/bot/exts/christmas/hanukkah_embed.py index 119f2446..00125be3 100644 --- a/bot/exts/christmas/hanukkah_embed.py +++ b/bot/exts/christmas/hanukkah_embed.py @@ -1,6 +1,5 @@ import datetime import logging -from typing import List from discord import Embed from discord.ext import commands @@ -26,7 +25,7 @@ class HanukkahEmbed(commands.Cog): self.hanukkah_months = [] self.hanukkah_years = [] - async def get_hanukkah_dates(self) -> List[str]: + async def get_hanukkah_dates(self) -> list[str]: """Gets the dates for hanukkah festival.""" hanukkah_dates = [] async with self.bot.http_session.get(HEBCAL_URL) as response: @@ -101,7 +100,7 @@ class HanukkahEmbed(commands.Cog): await ctx.send(embed=embed) - def hanukkah_dates_split(self, hanukkah_dates: List[str]) -> None: + def hanukkah_dates_split(self, hanukkah_dates: list[str]) -> None: """We are splitting the dates for hanukkah into days, months and years.""" for date in hanukkah_dates: self.hanukkah_days.append(date[8:10]) diff --git a/bot/exts/easter/bunny_name_generator.py b/bot/exts/easter/bunny_name_generator.py index 3e97373f..4c3137de 100644 --- a/bot/exts/easter/bunny_name_generator.py +++ b/bot/exts/easter/bunny_name_generator.py @@ -3,7 +3,7 @@ import logging import random import re from pathlib import Path -from typing import List, Union +from typing import Optional from discord.ext import commands @@ -18,7 +18,7 @@ class BunnyNameGenerator(commands.Cog): """Generate a random bunny name, or bunnify your Discord username!""" @staticmethod - def find_separators(displayname: str) -> Union[List[str], None]: + def find_separators(displayname: str) -> Optional[list[str]]: """Check if Discord name contains spaces so we can bunnify an individual word in the name.""" new_name = re.split(r"[_.\s]", displayname) if displayname not in new_name: @@ -26,7 +26,7 @@ class BunnyNameGenerator(commands.Cog): return None @staticmethod - def find_vowels(displayname: str) -> str: + def find_vowels(displayname: str) -> Optional[str]: """ Finds vowels in the user's display name. diff --git a/bot/exts/evergreen/avatar_modification/_effects.py b/bot/exts/evergreen/avatar_modification/_effects.py index 92244207..df741973 100644 --- a/bot/exts/evergreen/avatar_modification/_effects.py +++ b/bot/exts/evergreen/avatar_modification/_effects.py @@ -1,8 +1,8 @@ import math import random -import typing as t from io import BytesIO from pathlib import Path +from typing import Callable, Optional import discord from PIL import Image, ImageDraw, ImageOps @@ -18,7 +18,7 @@ class PfpEffects: """ @staticmethod - def apply_effect(image_bytes: bytes, effect: t.Callable, filename: str, *args) -> discord.File: + def apply_effect(image_bytes: bytes, effect: Callable, filename: str, *args) -> discord.File: """Applies the given effect to the image passed to it.""" im = Image.open(BytesIO(image_bytes)) im = im.convert("RGBA") @@ -32,7 +32,7 @@ class PfpEffects: return discord.File(bufferedio, filename=filename) @staticmethod - def closest(x: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: + def closest(x: tuple[int, int, int]) -> tuple[int, int, int]: """ Finds the closest "easter" colour to a given pixel. @@ -40,7 +40,7 @@ class PfpEffects: """ r1, g1, b1 = x - def distance(point: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: + def distance(point: tuple[int, int, int]) -> int: """Finds the difference between a pastel colour and the original pixel colour.""" r2, g2, b2 = point return (r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2 @@ -108,7 +108,7 @@ class PfpEffects: return image @staticmethod - def easterify_effect(image: Image.Image, overlay_image: t.Optional[Image.Image] = None) -> Image.Image: + def easterify_effect(image: Image.Image, overlay_image: Optional[Image.Image] = None) -> Image.Image: """ Applies the easter effect to the given image. @@ -212,7 +212,7 @@ class PfpEffects: return new_imgs @staticmethod - def join_images(images: t.List[Image.Image]) -> Image.Image: + def join_images(images: list[Image.Image]) -> Image.Image: """ Stitches all the image squares into a new image. diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index fd586613..2be09be2 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -3,10 +3,10 @@ import json import logging import math import string -import typing as t import unicodedata from concurrent.futures import ThreadPoolExecutor from pathlib import Path +from typing import Callable, Optional, TypeVar, Union import discord from discord.ext import commands @@ -25,12 +25,12 @@ FILENAME_STRING = "{effect}_{author}.png" MAX_SQUARES = 10_000 -T = t.TypeVar("T") +T = TypeVar("T") GENDER_OPTIONS = json.loads(Path("bot/resources/pride/gender_options.json").read_text("utf8")) -async def in_executor(func: t.Callable[..., T], *args) -> T: +async def in_executor(func: Callable[..., T], *args) -> T: """ Runs the given synchronous function `func` in an executor. @@ -65,7 +65,7 @@ class AvatarModify(commands.Cog): def __init__(self, bot: Bot) -> None: self.bot = bot - async def _fetch_user(self, user_id: int) -> t.Optional[discord.User]: + async def _fetch_user(self, user_id: int) -> Optional[discord.User]: """ Fetches a user and handles errors. @@ -121,7 +121,7 @@ class AvatarModify(commands.Cog): await ctx.send(embed=embed, file=file) @avatar_modify.command(name="reverse", root_aliases=("reverse",)) - async def reverse(self, ctx: commands.Context, *, text: t.Optional[str]) -> None: + async def reverse(self, ctx: commands.Context, *, text: Optional[str]) -> None: """ Reverses the sent text. @@ -158,7 +158,7 @@ class AvatarModify(commands.Cog): await ctx.send(embed=embed, file=file) @avatar_modify.command(aliases=("easterify",), root_aliases=("easterify", "avatareasterify")) - async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: + async def avatareasterify(self, ctx: commands.Context, *colours: Union[discord.Colour, str]) -> None: """ This "Easterifies" the user's avatar. diff --git a/bot/exts/evergreen/battleship.py b/bot/exts/evergreen/battleship.py index c2f2079c..46694f6a 100644 --- a/bot/exts/evergreen/battleship.py +++ b/bot/exts/evergreen/battleship.py @@ -2,9 +2,9 @@ import asyncio import logging import random import re -import typing from dataclasses import dataclass from functools import partial +from typing import Match, Optional import discord from discord.ext import commands @@ -19,20 +19,20 @@ log = logging.getLogger(__name__) class Square: """Each square on the battleship grid - if they contain a boat and if they've been aimed at.""" - boat: typing.Optional[str] + boat: Optional[str] aimed: bool -Grid = typing.List[typing.List[Square]] -EmojiSet = typing.Dict[typing.Tuple[bool, bool], str] +Grid = list[list[Square]] +EmojiSet = dict[tuple[bool, bool], str] @dataclass class Player: """Each player in the game - their messages for the boards and their current grid.""" - user: typing.Optional[discord.Member] - board: typing.Optional[discord.Message] + user: Optional[discord.Member] + board: Optional[discord.Message] opponent_board: discord.Message grid: Grid @@ -110,10 +110,10 @@ class Game: self.gameover: bool = False - self.turn: typing.Optional[discord.Member] = None - self.next: typing.Optional[discord.Member] = None + self.turn: Optional[discord.Member] = None + self.next: Optional[discord.Member] = None - self.match: typing.Optional[typing.Match] = None + self.match: Optional[Match] = None self.surrender: bool = False self.setup_grids() @@ -233,7 +233,7 @@ class Game: self.bot.loop.create_task(message.add_reaction(CROSS_EMOJI)) return bool(self.match) - async def take_turn(self) -> typing.Optional[Square]: + async def take_turn(self) -> Optional[Square]: """Lets the player who's turn it is choose a square.""" square = None turn_message = await self.turn.user.send( @@ -268,7 +268,7 @@ class Game: await turn_message.delete() return square - async def hit(self, square: Square, alert_messages: typing.List[discord.Message]) -> None: + async def hit(self, square: Square, alert_messages: list[discord.Message]) -> None: """Occurs when a player successfully aims for a ship.""" await self.turn.user.send("Hit!", delete_after=3.0) alert_messages.append(await self.next.user.send("Hit!")) @@ -324,8 +324,8 @@ class Battleship(commands.Cog): def __init__(self, bot: Bot) -> None: self.bot = bot - self.games: typing.List[Game] = [] - self.waiting: typing.List[discord.Member] = [] + self.games: list[Game] = [] + self.waiting: list[discord.Member] = [] def predicate( self, diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 98b0f2bf..f936be61 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -1,7 +1,7 @@ import asyncio import logging import random -import typing as t +from typing import Optional import discord from discord.ext import commands @@ -92,7 +92,7 @@ class Bookmark(commands.Cog): async def bookmark( self, ctx: commands.Context, - target_message: t.Optional[WrappedMessageConverter], + target_message: Optional[WrappedMessageConverter], *, title: str = "Bookmark" ) -> None: diff --git a/bot/exts/evergreen/cheatsheet.py b/bot/exts/evergreen/cheatsheet.py index ae7793c9..f37d3f66 100644 --- a/bot/exts/evergreen/cheatsheet.py +++ b/bot/exts/evergreen/cheatsheet.py @@ -1,6 +1,6 @@ import random import re -import typing as t +from typing import Union from urllib.parse import quote_plus from discord import Embed @@ -52,7 +52,7 @@ class CheatSheet(commands.Cog): ) return embed - def result_fmt(self, url: str, body_text: str) -> t.Tuple[bool, t.Union[str, Embed]]: + def result_fmt(self, url: str, body_text: str) -> tuple[bool, Union[str, Embed]]: """Format Result.""" if body_text.startswith("# 404 NOT FOUND"): embed = self.fmt_error_embed() diff --git a/bot/exts/evergreen/coinflip.py b/bot/exts/evergreen/coinflip.py index d1762463..7810da89 100644 --- a/bot/exts/evergreen/coinflip.py +++ b/bot/exts/evergreen/coinflip.py @@ -1,5 +1,4 @@ import random -from typing import Tuple from discord.ext import commands @@ -10,8 +9,8 @@ from bot.constants import Emojis class CoinSide(commands.Converter): """Class used to convert the `side` parameter of coinflip command.""" - HEADS: Tuple[str] = ("h", "head", "heads") - TAILS: Tuple[str] = ("t", "tail", "tails") + HEADS: tuple[str, ...] = ("h", "head", "heads") + TAILS: tuple[str, ...] = ("t", "tail", "tails") async def convert(self, ctx: commands.Context, side: str) -> str: """Converts the provided `side` into the corresponding string.""" diff --git a/bot/exts/evergreen/connect_four.py b/bot/exts/evergreen/connect_four.py index 5c82ffee..0f83bf8a 100644 --- a/bot/exts/evergreen/connect_four.py +++ b/bot/exts/evergreen/connect_four.py @@ -1,7 +1,7 @@ import asyncio import random -import typing from functools import partial +from typing import Optional, Union import discord import emojis @@ -14,8 +14,8 @@ from bot.constants import Emojis NUMBERS = list(Emojis.number_emojis.values()) CROSS_EMOJI = Emojis.incident_unactioned -Coordinate = typing.Optional[typing.Tuple[int, int]] -EMOJI_CHECK = typing.Union[discord.Emoji, str] +Coordinate = Optional[tuple[int, int]] +EMOJI_CHECK = Union[discord.Emoji, str] class Game: @@ -26,8 +26,8 @@ class Game: bot: Bot, channel: discord.TextChannel, player1: discord.Member, - player2: typing.Optional[discord.Member], - tokens: typing.List[str], + player2: Optional[discord.Member], + tokens: list[str], size: int = 7 ) -> None: @@ -48,7 +48,7 @@ class Game: self.player_inactive = None @staticmethod - def generate_board(size: int) -> typing.List[typing.List[int]]: + def generate_board(size: int) -> list[list[int]]: """Generate the connect 4 board.""" return [[0 for _ in range(size)] for _ in range(size)] @@ -185,7 +185,7 @@ class AI: self.game = game self.mention = bot.user.mention - def get_possible_places(self) -> typing.List[Coordinate]: + def get_possible_places(self) -> list[Coordinate]: """Gets all the coordinates where the AI could possibly place a counter.""" possible_coords = [] for column_num in range(self.game.grid_size): @@ -196,7 +196,7 @@ class AI: break return possible_coords - def check_ai_win(self, coord_list: typing.List[Coordinate]) -> typing.Optional[Coordinate]: + def check_ai_win(self, coord_list: list[Coordinate]) -> Optional[Coordinate]: """ Check AI win. @@ -209,7 +209,7 @@ class AI: if self.game.check_win(coords, 2): return coords - def check_player_win(self, coord_list: typing.List[Coordinate]) -> typing.Optional[Coordinate]: + def check_player_win(self, coord_list: list[Coordinate]) -> Optional[Coordinate]: """ Check Player win. @@ -223,11 +223,11 @@ class AI: return coords @staticmethod - def random_coords(coord_list: typing.List[Coordinate]) -> Coordinate: + def random_coords(coord_list: list[Coordinate]) -> Coordinate: """Picks a random coordinate from the possible ones.""" return random.choice(coord_list) - def play(self) -> typing.Union[Coordinate, bool]: + def play(self) -> Union[Coordinate, bool]: """ Plays for the AI. @@ -258,8 +258,8 @@ class ConnectFour(commands.Cog): def __init__(self, bot: Bot) -> None: self.bot = bot - self.games: typing.List[Game] = [] - self.waiting: typing.List[discord.Member] = [] + self.games: list[Game] = [] + self.waiting: list[discord.Member] = [] self.tokens = [":white_circle:", ":blue_circle:", ":red_circle:"] @@ -330,7 +330,7 @@ class ConnectFour(commands.Cog): @staticmethod def check_emojis( e1: EMOJI_CHECK, e2: EMOJI_CHECK - ) -> typing.Tuple[bool, typing.Optional[str]]: + ) -> tuple[bool, Optional[str]]: """Validate the emojis, the user put.""" if isinstance(e1, str) and emojis.count(e1) != 1: return False, e1 @@ -341,7 +341,7 @@ class ConnectFour(commands.Cog): async def _play_game( self, ctx: commands.Context, - user: typing.Optional[discord.Member], + user: Optional[discord.Member], board_size: int, emoji1: str, emoji2: str diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index 11615214..8bca38c0 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -3,7 +3,7 @@ import random import textwrap from collections import defaultdict from datetime import datetime -from typing import List, Optional, Tuple +from typing import Optional from discord import Color, Embed, Emoji from discord.ext import commands @@ -21,7 +21,7 @@ class Emojis(commands.Cog): """A collection of commands related to emojis in the server.""" @staticmethod - def embed_builder(emoji: dict) -> Tuple[Embed, List[str]]: + def embed_builder(emoji: dict) -> tuple[Embed, list[str]]: """Generates an embed with the emoji names and count.""" embed = Embed( color=Colours.orange, @@ -52,7 +52,7 @@ class Emojis(commands.Cog): return embed, msg @staticmethod - def generate_invalid_embed(emojis: list) -> Tuple[Embed, List[str]]: + def generate_invalid_embed(emojis: list) -> tuple[Embed, list[str]]: """Generates error embed for invalid emoji categories.""" embed = Embed( color=Colours.soft_red, diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index a280c725..3fa8be39 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -2,7 +2,8 @@ import difflib import logging import math import random -from typing import Iterable, Union +from collections.abc import Iterable +from typing import Union from discord import Embed, Message from discord.ext import commands diff --git a/bot/exts/evergreen/fun.py b/bot/exts/evergreen/fun.py index 3b266e1b..1783d7b4 100644 --- a/bot/exts/evergreen/fun.py +++ b/bot/exts/evergreen/fun.py @@ -2,8 +2,9 @@ import functools import json import logging import random +from collections.abc import Iterable from pathlib import Path -from typing import Callable, Iterable, Tuple, Union +from typing import Callable, Optional, Union from discord import Embed, Message from discord.ext import commands @@ -182,7 +183,7 @@ class Fun(Cog): await self._caesar_cipher(ctx, offset, msg, left_shift=True) @staticmethod - async def _get_text_and_embed(ctx: Context, text: str) -> Tuple[str, Union[Embed, None]]: + async def _get_text_and_embed(ctx: Context, text: str) -> tuple[str, Optional[Embed]]: """ Attempts to extract the text and embed from a possible link to a discord Message. diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index 32fe9263..f9c150e6 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -5,7 +5,7 @@ import re from asyncio import sleep from datetime import datetime as dt, timedelta from enum import IntEnum -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Optional from aiohttp import ClientSession from discord import Embed @@ -151,7 +151,7 @@ class Games(Cog): self.bot = bot self.http_session: ClientSession = bot.http_session - self.genres: Dict[str, int] = {} + self.genres: dict[str, int] = {} self.headers = BASE_HEADERS self.bot.loop.create_task(self.renew_access_token()) @@ -342,7 +342,7 @@ class Games(Cog): sort: Optional[str] = None, additional_body: str = "", offset: int = 0 - ) -> List[Dict[str, Any]]: + ) -> list[dict[str, Any]]: """ Get list of games from IGDB API by parameters that is provided. @@ -365,7 +365,7 @@ class Games(Cog): async with self.http_session.post(url=f"{BASE_URL}/games", data=body, headers=self.headers) as resp: return await resp.json() - async def create_page(self, data: Dict[str, Any]) -> Tuple[str, str]: + async def create_page(self, data: dict[str, Any]) -> tuple[str, str]: """Create content of Game Page.""" # Create cover image URL from template url = COVER_URL.format(**{"image_id": data["cover"]["image_id"] if "cover" in data else ""}) @@ -399,7 +399,7 @@ class Games(Cog): return page, url - async def search_games(self, search_term: str) -> List[str]: + async def search_games(self, search_term: str) -> list[str]: """Search game from IGDB API by string, return listing of pages.""" lines = [] @@ -422,7 +422,7 @@ class Games(Cog): return lines - async def get_companies_list(self, limit: int, offset: int = 0) -> List[Dict[str, Any]]: + async def get_companies_list(self, limit: int, offset: int = 0) -> list[dict[str, Any]]: """ Get random Game Companies from IGDB API. @@ -438,7 +438,7 @@ class Games(Cog): async with self.http_session.post(url=f"{BASE_URL}/companies", data=body, headers=self.headers) as resp: return await resp.json() - async def create_company_page(self, data: Dict[str, Any]) -> Tuple[str, str]: + async def create_company_page(self, data: dict[str, Any]) -> tuple[str, str]: """Create good formatted Game Company page.""" # Generate URL of company logo url = LOGO_URL.format(**{"image_id": data["logo"]["image_id"] if "logo" in data else ""}) @@ -462,7 +462,7 @@ class Games(Cog): return page, url - async def get_best_results(self, query: str) -> List[Tuple[float, str]]: + async def get_best_results(self, query: str) -> list[tuple[float, str]]: """Get best match result of genre when original genre is invalid.""" results = [] for genre in self.genres: diff --git a/bot/exts/evergreen/help.py b/bot/exts/evergreen/help.py index bfb5db17..86f0e72d 100644 --- a/bot/exts/evergreen/help.py +++ b/bot/exts/evergreen/help.py @@ -3,7 +3,7 @@ import asyncio import itertools import logging from contextlib import suppress -from typing import List, NamedTuple, Union +from typing import NamedTuple, Union from discord import Colour, Embed, HTTPException, Message, Reaction, User from discord.ext import commands @@ -34,7 +34,7 @@ class Cog(NamedTuple): name: str description: str - commands: List[Command] + commands: list[Command] log = logging.getLogger(__name__) @@ -343,7 +343,7 @@ class HelpSession: for category, cmds in grouped: await self._format_command_category(paginator, category, list(cmds)) - async def _format_command_category(self, paginator: LinePaginator, category: str, cmds: List[Command]) -> None: + async def _format_command_category(self, paginator: LinePaginator, category: str, cmds: list[Command]) -> None: cmds = sorted(cmds, key=lambda c: c.name) cat_cmds = [] for command in cmds: @@ -373,7 +373,7 @@ class HelpSession: paginator.add_line(details) - async def _format_command(self, command: Command) -> List[str]: + async def _format_command(self, command: Command) -> list[str]: # skip if hidden and hide if session is set to if command.hidden and not self._show_hidden: return [] diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 00810de8..8a7ebed0 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -1,8 +1,8 @@ import logging import random import re -import typing as t from dataclasses import dataclass +from typing import Optional, Union import discord from discord.ext import commands @@ -62,7 +62,7 @@ AUTOMATIC_REGEX = re.compile( class FoundIssue: """Dataclass representing an issue found by the regex.""" - organisation: t.Optional[str] + organisation: Optional[str] repository: str number: str @@ -106,7 +106,7 @@ class Issues(commands.Cog): number: int, repository: str, user: str - ) -> t.Union[IssueState, FetchError]: + ) -> Union[IssueState, FetchError]: """ Retrieve an issue from a GitHub repository. @@ -162,9 +162,9 @@ class Issues(commands.Cog): @staticmethod def format_embed( - results: t.List[t.Union[IssueState, FetchError]], + results: list[Union[IssueState, FetchError]], user: str, - repository: t.Optional[str] = None + repository: Optional[str] = None ) -> discord.Embed: """Take a list of IssueState or FetchError and format a Discord embed for them.""" description_list = [] diff --git a/bot/exts/evergreen/minesweeper.py b/bot/exts/evergreen/minesweeper.py index 932358f9..75d13d88 100644 --- a/bot/exts/evergreen/minesweeper.py +++ b/bot/exts/evergreen/minesweeper.py @@ -1,7 +1,8 @@ import logging -import typing +from collections.abc import Iterator from dataclasses import dataclass from random import randint, random +from typing import Union import discord from discord.ext import commands @@ -33,7 +34,7 @@ MESSAGE_MAPPING = { log = logging.getLogger(__name__) -GameBoard = typing.List[typing.List[typing.Union[str, int]]] +GameBoard = list[list[Union[str, int]]] @dataclass @@ -47,14 +48,11 @@ class Game: activated_on_server: bool -GamesDict = typing.Dict[int, Game] - - class Minesweeper(commands.Cog): """Play a game of Minesweeper.""" def __init__(self) -> None: - self.games: GamesDict = {} # Store the currently running games + self.games: dict[int, Game] = {} @commands.group(name="minesweeper", aliases=("ms",), invoke_without_command=True) async def minesweeper_group(self, ctx: commands.Context) -> None: @@ -62,7 +60,7 @@ class Minesweeper(commands.Cog): await invoke_help_command(ctx) @staticmethod - def get_neighbours(x: int, y: int) -> typing.Generator[typing.Tuple[int, int], None, None]: + def get_neighbours(x: int, y: int) -> Iterator[tuple[int, int]]: """Get all the neighbouring x and y including it self.""" for x_ in [x - 1, x, x + 1]: for y_ in [y - 1, y, y + 1]: diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index c6af4bcd..a04eeb41 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -1,7 +1,7 @@ import logging import random from enum import Enum -from typing import Any, Dict, List, Tuple +from typing import Any from aiohttp import ClientSession from discord import Embed @@ -107,7 +107,7 @@ class Movie(Cog): """Show all currently available genres for .movies command.""" await ctx.send(f"Current available genres: {', '.join('`' + genre.name + '`' for genre in MovieGenres)}") - async def get_movies_data(self, client: ClientSession, genre_id: str, page: int) -> List[Dict[str, Any]]: + async def get_movies_data(self, client: ClientSession, genre_id: str, page: int) -> list[dict[str, Any]]: """Return JSON of TMDB discover request.""" # Define params of request params = { @@ -126,7 +126,7 @@ class Movie(Cog): async with client.get(url, params=params) as resp: return await resp.json() - async def get_pages(self, client: ClientSession, movies: Dict[str, Any], amount: int) -> List[Tuple[str, str]]: + async def get_pages(self, client: ClientSession, movies: dict[str, Any], amount: int) -> list[tuple[str, str]]: """Fetch all movie pages from movies dictionary. Return list of pages.""" pages = [] @@ -139,7 +139,7 @@ class Movie(Cog): return pages - async def get_movie(self, client: ClientSession, movie: int) -> Dict: + async def get_movie(self, client: ClientSession, movie: int) -> dict[str, Any]: """Get Movie by movie ID from TMDB. Return result dictionary.""" if not isinstance(movie, int): raise ValueError("Error while fetching movie from TMDB, movie argument must be integer. ") @@ -148,7 +148,7 @@ class Movie(Cog): async with client.get(url, params=MOVIE_PARAMS) as resp: return await resp.json() - async def create_page(self, movie: Dict[str, Any]) -> Tuple[str, str]: + async def create_page(self, movie: dict[str, Any]) -> tuple[str, str]: """Create page from TMDB movie request result. Return formatted page + image.""" text = "" diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index 4df170c6..e6cb5337 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -4,7 +4,7 @@ import random import textwrap from collections import namedtuple from datetime import datetime, timedelta -from typing import List, Union +from typing import Union from aiohttp import BasicAuth, ClientError from discord import Colour, Embed, TextChannel @@ -59,7 +59,7 @@ class Reddit(Cog): """Get the #reddit channel object from the bot's cache.""" return self.bot.get_channel(Channels.reddit) - def build_pagination_pages(self, posts: List[dict], paginate: bool) -> Union[List[tuple], str]: + def build_pagination_pages(self, posts: list[dict], paginate: bool) -> Union[list[tuple], str]: """Build embed pages required for Paginator.""" pages = [] first_page = "" @@ -180,7 +180,7 @@ class Reddit(Cog): else: log.warning(f"Unable to revoke access token: status {response.status}.") - async def fetch_posts(self, route: str, *, amount: int = 25, params: dict = None) -> List[dict]: + async def fetch_posts(self, route: str, *, amount: int = 25, params: dict = None) -> list[dict]: """A helper method to fetch a certain amount of Reddit posts at a given route.""" # Reddit's JSON responses only provide 25 posts at most. if not 25 >= amount > 0: @@ -213,7 +213,7 @@ class Reddit(Cog): async def get_top_posts( self, subreddit: Subreddit, time: str = "all", amount: int = 5, paginate: bool = False - ) -> Union[Embed, List[tuple]]: + ) -> Union[Embed, list[tuple]]: """ Get the top amount of posts for a given subreddit within a specified timeframe. diff --git a/bot/exts/evergreen/snakes/_converter.py b/bot/exts/evergreen/snakes/_converter.py index 75212107..765b983d 100644 --- a/bot/exts/evergreen/snakes/_converter.py +++ b/bot/exts/evergreen/snakes/_converter.py @@ -1,7 +1,7 @@ import json import logging import random -from typing import Iterable, List +from collections.abc import Iterable import discord from discord.ext.commands import Context, Converter @@ -27,7 +27,7 @@ class Snake(Converter): if name == "python": return "Python (programming language)" - def get_potential(iterable: Iterable, *, threshold: int = 80) -> List[str]: + def get_potential(iterable: Iterable, *, threshold: int = 80) -> list[str]: nonlocal name potential = [] diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index 225df948..ecf2832f 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -9,7 +9,7 @@ import textwrap import urllib from functools import partial from io import BytesIO -from typing import Any, Dict, List, Optional +from typing import Any, Optional import async_timeout from PIL import Image, ImageDraw, ImageFont @@ -284,7 +284,7 @@ class Snakes(Cog): async with self.bot.http_session.get(url, params=params) as response: return await response.json() - def _get_random_long_message(self, messages: List[str], retries: int = 10) -> str: + def _get_random_long_message(self, messages: list[str], retries: int = 10) -> str: """ Fetch a message that's at least 3 words long, if possible to do so in retries attempts. @@ -299,7 +299,7 @@ class Snakes(Cog): return long_message - async def _get_snek(self, name: str) -> Dict[str, Any]: + async def _get_snek(self, name: str) -> dict[str, Any]: """ Fetches all the data from a wikipedia article about a snake. @@ -401,7 +401,7 @@ class Snakes(Cog): return snake_info - async def _get_snake_name(self) -> Dict[str, str]: + async def _get_snake_name(self) -> dict[str, str]: """Gets a random snake name.""" return random.choice(self.snake_names) diff --git a/bot/exts/evergreen/snakes/_utils.py b/bot/exts/evergreen/snakes/_utils.py index 2c825c7c..c0a36f48 100644 --- a/bot/exts/evergreen/snakes/_utils.py +++ b/bot/exts/evergreen/snakes/_utils.py @@ -6,7 +6,6 @@ import math import random from itertools import product from pathlib import Path -from typing import List, Tuple from PIL import Image from PIL.ImageDraw import ImageDraw @@ -95,25 +94,25 @@ BOARD = { 16: 6 } -DEFAULT_SNAKE_COLOR: int = 0x15c7ea -DEFAULT_BACKGROUND_COLOR: int = 0 -DEFAULT_IMAGE_DIMENSIONS: Tuple[int] = (200, 200) -DEFAULT_SNAKE_LENGTH: int = 22 -DEFAULT_SNAKE_WIDTH: int = 8 -DEFAULT_SEGMENT_LENGTH_RANGE: Tuple[int] = (7, 10) -DEFAULT_IMAGE_MARGINS: Tuple[int] = (50, 50) -DEFAULT_TEXT: str = "snek\nit\nup" -DEFAULT_TEXT_POSITION: Tuple[int] = ( +DEFAULT_SNAKE_COLOR = 0x15c7ea +DEFAULT_BACKGROUND_COLOR = 0 +DEFAULT_IMAGE_DIMENSIONS = (200, 200) +DEFAULT_SNAKE_LENGTH = 22 +DEFAULT_SNAKE_WIDTH = 8 +DEFAULT_SEGMENT_LENGTH_RANGE = (7, 10) +DEFAULT_IMAGE_MARGINS = (50, 50) +DEFAULT_TEXT = "snek\nit\nup" +DEFAULT_TEXT_POSITION = ( 10, 10 ) -DEFAULT_TEXT_COLOR: int = 0xf2ea15 +DEFAULT_TEXT_COLOR = 0xf2ea15 X = 0 Y = 1 ANGLE_RANGE = math.pi * 2 -def get_resource(file: str) -> List[dict]: +def get_resource(file: str) -> list[dict]: """Load Snake resources JSON.""" return json.loads((SNAKE_RESOURCES / f"{file}.json").read_text("utf-8")) @@ -140,7 +139,7 @@ class PerlinNoiseFactory(object): Licensed under ISC """ - def __init__(self, dimension: int, octaves: int = 1, tile: Tuple[int] = (), unbias: bool = False): + def __init__(self, dimension: int, octaves: int = 1, tile: tuple[int, ...] = (), unbias: bool = False): """ Create a new Perlin noise factory in the given number of dimensions. @@ -168,7 +167,7 @@ class PerlinNoiseFactory(object): self.gradient = {} - def _generate_gradient(self) -> Tuple[float, ...]: + def _generate_gradient(self) -> tuple[float, ...]: """ Generate a random unit vector at each grid point. @@ -278,13 +277,14 @@ class PerlinNoiseFactory(object): def create_snek_frame( perlin_factory: PerlinNoiseFactory, perlin_lookup_vertical_shift: float = 0, - image_dimensions: Tuple[int] = DEFAULT_IMAGE_DIMENSIONS, image_margins: Tuple[int] = DEFAULT_IMAGE_MARGINS, + image_dimensions: tuple[int, int] = DEFAULT_IMAGE_DIMENSIONS, + image_margins: tuple[int, int] = DEFAULT_IMAGE_MARGINS, snake_length: int = DEFAULT_SNAKE_LENGTH, snake_color: int = DEFAULT_SNAKE_COLOR, bg_color: int = DEFAULT_BACKGROUND_COLOR, - segment_length_range: Tuple[int] = DEFAULT_SEGMENT_LENGTH_RANGE, snake_width: int = DEFAULT_SNAKE_WIDTH, - text: str = DEFAULT_TEXT, text_position: Tuple[int] = DEFAULT_TEXT_POSITION, - text_color: Tuple[int] = DEFAULT_TEXT_COLOR -) -> Image: + segment_length_range: tuple[int, int] = DEFAULT_SEGMENT_LENGTH_RANGE, snake_width: int = DEFAULT_SNAKE_WIDTH, + text: str = DEFAULT_TEXT, text_position: tuple[float, float] = DEFAULT_TEXT_POSITION, + text_color: int = DEFAULT_TEXT_COLOR +) -> Image.Image: """ Creates a single random snek frame using Perlin noise. @@ -293,7 +293,7 @@ def create_snek_frame( """ start_x = random.randint(image_margins[X], image_dimensions[X] - image_margins[X]) start_y = random.randint(image_margins[Y], image_dimensions[Y] - image_margins[Y]) - points = [(start_x, start_y)] + points: list[tuple[float, float]] = [(start_x, start_y)] for index in range(0, snake_length): angle = perlin_factory.get_plain_noise( @@ -307,8 +307,8 @@ def create_snek_frame( )) # normalize bounds - min_dimensions = [start_x, start_y] - max_dimensions = [start_x, start_y] + min_dimensions: list[float] = [start_x, start_y] + max_dimensions: list[float] = [start_x, start_y] for point in points: min_dimensions[X] = min(point[X], min_dimensions[X]) min_dimensions[Y] = min(point[Y], min_dimensions[Y]) @@ -702,7 +702,7 @@ class SnakeAndLaddersGame: """Clean up the finished game object.""" del self.snakes.active_sal[self.channel] - def _board_coordinate_from_index(self, index: int) -> Tuple[int, int]: + def _board_coordinate_from_index(self, index: int) -> tuple[int, int]: """Convert the tile number to the x/y coordinates for graphical purposes.""" y_level = 9 - math.floor((index - 1) / 10) is_reversed = math.floor((index - 1) / 10) % 2 != 0 diff --git a/bot/exts/evergreen/source.py b/bot/exts/evergreen/source.py index fc209bc3..7572ce51 100644 --- a/bot/exts/evergreen/source.py +++ b/bot/exts/evergreen/source.py @@ -1,6 +1,6 @@ import inspect from pathlib import Path -from typing import Optional, Tuple +from typing import Optional from discord import Embed from discord.ext import commands @@ -26,7 +26,7 @@ class BotSource(commands.Cog): embed = await self.build_embed(source_item) await ctx.send(embed=embed) - def get_source_link(self, source_item: SourceType) -> Tuple[str, str, Optional[int]]: + def get_source_link(self, source_item: SourceType) -> tuple[str, str, Optional[int]]: """ Build GitHub link of source item, return this link, file location and first line number. diff --git a/bot/exts/evergreen/space.py b/bot/exts/evergreen/space.py index 5e87c6d5..48ad0f96 100644 --- a/bot/exts/evergreen/space.py +++ b/bot/exts/evergreen/space.py @@ -1,7 +1,7 @@ import logging import random from datetime import date, datetime -from typing import Any, Dict, Optional +from typing import Any, Optional from urllib.parse import urlencode from discord import Embed @@ -203,10 +203,10 @@ class Space(Cog): async def fetch_from_nasa( self, endpoint: str, - additional_params: Optional[Dict[str, Any]] = None, + additional_params: Optional[dict[str, Any]] = None, base: Optional[str] = NASA_BASE_URL, use_api_key: bool = True - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Fetch information from NASA API, return result.""" params = {} if use_api_key: diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 164e056d..5c4f8051 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -1,6 +1,6 @@ import asyncio import random -import typing as t +from typing import Callable, Optional, Union import discord from discord.ext.commands import Cog, Context, check, group, guild_only @@ -15,7 +15,7 @@ CONFIRMATION_MESSAGE = ( ) -def check_win(board: t.Dict[int, str]) -> bool: +def check_win(board: dict[int, str]) -> bool: """Check from board, is any player won game.""" return any( ( @@ -42,7 +42,7 @@ class Player: self.ctx = ctx self.symbol = symbol - async def get_move(self, board: t.Dict[int, str], msg: discord.Message) -> t.Tuple[bool, t.Optional[int]]: + async def get_move(self, board: dict[int, str], msg: discord.Message) -> tuple[bool, Optional[int]]: """ Get move from user. @@ -75,7 +75,7 @@ class AI: def __init__(self, symbol: str): self.symbol = symbol - async def get_move(self, board: t.Dict[int, str], _: discord.Message) -> t.Tuple[bool, int]: + async def get_move(self, board: dict[int, str], _: discord.Message) -> tuple[bool, int]: """Get move from AI. AI use Minimax strategy.""" possible_moves = [i for i, emoji in board.items() if emoji in list(Emojis.number_emojis.values())] @@ -104,7 +104,7 @@ class AI: class Game: """Class that contains information and functions about Tic Tac Toe game.""" - def __init__(self, players: t.List[t.Union[Player, AI]], ctx: Context): + def __init__(self, players: list[Union[Player, AI]], ctx: Context): self.players = players self.ctx = ctx self.board = { @@ -122,13 +122,13 @@ class Game: self.current = self.players[0] self.next = self.players[1] - self.winner: t.Optional[t.Union[Player, AI]] = None - self.loser: t.Optional[t.Union[Player, AI]] = None + self.winner: Optional[Union[Player, AI]] = None + self.loser: Optional[Union[Player, AI]] = None self.over = False self.canceled = False self.draw = False - async def get_confirmation(self) -> t.Tuple[bool, t.Optional[str]]: + async def get_confirmation(self) -> tuple[bool, Optional[str]]: """ Ask does user want to play TicTacToe against requester. First player is always requester. @@ -227,14 +227,14 @@ class Game: self.over = True -def is_channel_free() -> t.Callable: +def is_channel_free() -> Callable: """Check is channel where command will be invoked free.""" async def predicate(ctx: Context) -> bool: return all(game.channel != ctx.channel for game in ctx.cog.games if not game.over) return check(predicate) -def is_requester_free() -> t.Callable: +def is_requester_free() -> Callable: """Check is requester not already in any game.""" async def predicate(ctx: Context) -> bool: return all( @@ -247,13 +247,13 @@ class TicTacToe(Cog): """TicTacToe cog contains tic-tac-toe game commands.""" def __init__(self): - self.games: t.List[Game] = [] + self.games: list[Game] = [] @guild_only() @is_channel_free() @is_requester_free() @group(name="tictactoe", aliases=("ttt", "tic"), invoke_without_command=True) - async def tic_tac_toe(self, ctx: Context, opponent: t.Optional[discord.User]) -> None: + async def tic_tac_toe(self, ctx: Context, opponent: Optional[discord.User]) -> None: """Tic Tac Toe game. Play against friends or AI. Use reactions to add your mark to field.""" if opponent == ctx.author: await ctx.send("You can't play against yourself.") diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index bc25cbf7..d754cf02 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -5,7 +5,7 @@ import operator import random from dataclasses import dataclass from pathlib import Path -from typing import Callable, List, Optional +from typing import Callable, Optional import discord from discord.ext import commands @@ -559,7 +559,7 @@ class TriviaQuiz(commands.Cog): @staticmethod async def send_answer( channel: discord.TextChannel, - answers: List[str], + answers: list[str], answer_is_correct: bool, question_dict: dict, q_left: int, diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index 27e68397..fc67c070 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -2,7 +2,7 @@ import logging import re from datetime import datetime from html import unescape -from typing import List +from typing import Optional from discord import Color, Embed, TextChannel from discord.ext import commands @@ -43,7 +43,7 @@ class WikipediaSearch(commands.Cog): def __init__(self, bot: Bot): self.bot = bot - async def wiki_request(self, channel: TextChannel, search: str) -> List[str]: + async def wiki_request(self, channel: TextChannel, search: str) -> Optional[list[str]]: """Search wikipedia search string and return formatted first 10 pages found.""" params = WIKI_PARAMS | {"srlimit": 10, "srsearch": search} async with self.bot.http_session.get(url=SEARCH_API, params=params) as resp: diff --git a/bot/exts/evergreen/wolfram.py b/bot/exts/evergreen/wolfram.py index 26674d37..54ff5d92 100644 --- a/bot/exts/evergreen/wolfram.py +++ b/bot/exts/evergreen/wolfram.py @@ -1,6 +1,6 @@ import logging from io import BytesIO -from typing import Callable, List, Optional, Tuple +from typing import Callable, Optional from urllib.parse import urlencode import arrow @@ -54,7 +54,7 @@ async def send_embed( await ctx.send(embed=embed, file=f) -def custom_cooldown(*ignore: List[int]) -> Callable: +def custom_cooldown(*ignore: int) -> Callable: """ Implement per-user and per-guild cooldowns for requests to the Wolfram API. @@ -105,7 +105,7 @@ def custom_cooldown(*ignore: List[int]) -> Callable: return check(predicate) -async def get_pod_pages(ctx: Context, bot: Bot, query: str) -> Optional[List[Tuple]]: +async def get_pod_pages(ctx: Context, bot: Bot, query: str) -> Optional[list[tuple]]: """Get the Wolfram API pod pages for the provided query.""" async with ctx.typing(): params = { @@ -133,22 +133,22 @@ async def get_pod_pages(ctx: Context, bot: Bot, query: str) -> Optional[List[Tup f"processing a wolfram request: {log_full_url}, Response: {json}" ) await send_embed(ctx, message) - return + return None message = "Something went wrong internally with your request, please notify staff!" log.warning(f"Something went wrong getting a response from wolfram: {log_full_url}, Response: {json}") await send_embed(ctx, message) - return + return None if not result["success"]: message = f"I couldn't find anything for {query}." await send_embed(ctx, message) - return + return None if not result["numpods"]: message = "Could not find any results." await send_embed(ctx, message) - return + return None pods = result["pods"] pages = [] diff --git a/bot/exts/evergreen/xkcd.py b/bot/exts/evergreen/xkcd.py index c98830bc..7c4fc30e 100644 --- a/bot/exts/evergreen/xkcd.py +++ b/bot/exts/evergreen/xkcd.py @@ -1,7 +1,7 @@ import logging import re from random import randint -from typing import Dict, Optional, Union +from typing import Optional, Union from discord import Embed from discord.ext import tasks @@ -21,7 +21,7 @@ class XKCD(Cog): def __init__(self, bot: Bot) -> None: self.bot = bot - self.latest_comic_info: Dict[str, Union[str, int]] = {} + self.latest_comic_info: dict[str, Union[str, int]] = {} self.get_latest_comic_info.start() def cog_unload(self) -> None: diff --git a/bot/exts/halloween/hacktober-issue-finder.py b/bot/exts/halloween/hacktober-issue-finder.py index 20a06770..e3053851 100644 --- a/bot/exts/halloween/hacktober-issue-finder.py +++ b/bot/exts/halloween/hacktober-issue-finder.py @@ -1,7 +1,7 @@ import datetime import logging import random -from typing import Dict, Optional +from typing import Optional import discord from discord.ext import commands @@ -49,7 +49,7 @@ class HacktoberIssues(commands.Cog): embed = self.format_embed(issue) await ctx.send(embed=embed) - async def get_issues(self, ctx: commands.Context, option: str) -> Optional[Dict]: + async def get_issues(self, ctx: commands.Context, option: str) -> Optional[dict]: """Get a list of the python issues with the label 'hacktoberfest' from the Github api.""" if option == "beginner": if (ctx.message.created_at - self.cache_timer_beginner).seconds <= 60: @@ -96,7 +96,7 @@ class HacktoberIssues(commands.Cog): return data @staticmethod - def format_embed(issue: Dict) -> discord.Embed: + def format_embed(issue: dict) -> discord.Embed: """Format the issue data into a embed.""" title = issue["title"] issue_url = issue["url"].replace("api.", "").replace("/repos/", "/") diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 24106a5e..3661cf16 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -3,7 +3,7 @@ import random import re from collections import Counter from datetime import datetime, timedelta -from typing import List, Optional, Tuple, Union +from typing import Optional, Union from urllib.parse import quote_plus import discord @@ -139,7 +139,7 @@ class HacktoberStats(commands.Cog): else: await ctx.send(f"No valid Hacktoberfest PRs found for '{github_username}'") - async def build_embed(self, github_username: str, prs: List[dict]) -> discord.Embed: + async def build_embed(self, github_username: str, prs: list[dict]) -> discord.Embed: """Return a stats embed built from github_username's PRs.""" logging.info(f"Building Hacktoberfest embed for GitHub user: '{github_username}'") in_review, accepted = await self._categorize_prs(prs) @@ -185,7 +185,7 @@ class HacktoberStats(commands.Cog): logging.info(f"Hacktoberfest PR built for GitHub user '{github_username}'") return stats_embed - async def get_october_prs(self, github_username: str) -> Optional[List[dict]]: + async def get_october_prs(self, github_username: str) -> Optional[list[dict]]: """ Query GitHub's API for PRs created during the month of October by github_username. @@ -302,7 +302,7 @@ class HacktoberStats(commands.Cog): return await resp.json() @staticmethod - def _has_label(pr: dict, labels: Union[List[str], str]) -> bool: + def _has_label(pr: dict, labels: Union[list[str], str]) -> bool: """ Check if a PR has label 'labels'. @@ -368,7 +368,7 @@ class HacktoberStats(commands.Cog): exp = r"https?:\/\/api.github.com\/repos\/([/\-\_\.\w]+)" return re.findall(exp, in_url)[0] - async def _categorize_prs(self, prs: List[dict]) -> tuple: + async def _categorize_prs(self, prs: list[dict]) -> tuple: """ Categorize PRs into 'in_review' and 'accepted' and returns as a tuple. @@ -391,7 +391,7 @@ class HacktoberStats(commands.Cog): return in_review, accepted @staticmethod - def _build_prs_string(prs: List[tuple], user: str) -> str: + def _build_prs_string(prs: list[tuple], user: str) -> str: """ Builds a discord embed compatible string for a list of PRs. @@ -424,7 +424,7 @@ class HacktoberStats(commands.Cog): return "contributions" @staticmethod - def _author_mention_from_context(ctx: commands.Context) -> Tuple[str, str]: + def _author_mention_from_context(ctx: commands.Context) -> tuple[str, str]: """Return stringified Message author ID and mentionable string from commands.Context.""" author_id = str(ctx.author.id) author_mention = ctx.author.mention diff --git a/bot/exts/halloween/halloween_facts.py b/bot/exts/halloween/halloween_facts.py index 5ad8cc57..ba3b5d17 100644 --- a/bot/exts/halloween/halloween_facts.py +++ b/bot/exts/halloween/halloween_facts.py @@ -3,7 +3,6 @@ import logging import random from datetime import timedelta from pathlib import Path -from typing import Tuple import discord from discord.ext import commands @@ -32,7 +31,7 @@ FACTS = list(enumerate(FACTS)) class HalloweenFacts(commands.Cog): """A Cog for displaying interesting facts about Halloween.""" - def random_fact(self) -> Tuple[int, str]: + def random_fact(self) -> tuple[int, str]: """Return a random fact from the loaded facts.""" return random.choice(FACTS) diff --git a/bot/exts/halloween/spookyrating.py b/bot/exts/halloween/spookyrating.py index 105d2164..f566fac2 100644 --- a/bot/exts/halloween/spookyrating.py +++ b/bot/exts/halloween/spookyrating.py @@ -3,7 +3,6 @@ import json import logging import random from pathlib import Path -from typing import Dict import discord from discord.ext import commands @@ -13,7 +12,7 @@ from bot.constants import Colours log = logging.getLogger(__name__) -data: Dict[str, Dict[str, str]] = json.loads(Path("bot/resources/halloween/spooky_rating.json").read_text("utf8")) +data: dict[str, dict[str, str]] = json.loads(Path("bot/resources/halloween/spooky_rating.json").read_text("utf8")) SPOOKY_DATA = sorted((int(key), value) for key, value in data.items()) diff --git a/bot/exts/halloween/timeleft.py b/bot/exts/halloween/timeleft.py index e80025dc..55109599 100644 --- a/bot/exts/halloween/timeleft.py +++ b/bot/exts/halloween/timeleft.py @@ -1,6 +1,5 @@ import logging from datetime import datetime -from typing import Tuple from discord.ext import commands @@ -21,7 +20,7 @@ class TimeLeft(commands.Cog): return start <= now <= end @staticmethod - def load_date() -> Tuple[datetime, datetime, datetime]: + def load_date() -> tuple[datetime, datetime, datetime]: """Return of a tuple of the current time and the end and start times of the next October.""" now = datetime.utcnow() year = now.year diff --git a/bot/exts/internal_eval/_helpers.py b/bot/exts/internal_eval/_helpers.py index 3a50b9f3..6fb07f61 100644 --- a/bot/exts/internal_eval/_helpers.py +++ b/bot/exts/internal_eval/_helpers.py @@ -8,14 +8,13 @@ import logging import sys import traceback import types -import typing - +from typing import Any, Optional, Union log = logging.getLogger(__name__) # A type alias to annotate the tuples returned from `sys.exc_info()` -ExcInfo = typing.Tuple[typing.Type[Exception], Exception, types.TracebackType] -Namespace = typing.Dict[str, typing.Any] +ExcInfo = tuple[type[Exception], Exception, types.TracebackType] +Namespace = dict[str, Any] # This will be used as an coroutine function wrapper for the code # to be evaluated. The wrapper contains one `pass` statement which @@ -93,7 +92,7 @@ class EvalContext: self.eval_tree = None @property - def dependencies(self) -> typing.Dict[str, typing.Any]: + def dependencies(self) -> dict[str, Any]: """ Return a mapping of the dependencies for the wrapper function. @@ -111,17 +110,17 @@ class EvalContext: } @property - def locals(self) -> typing.Dict[str, typing.Any]: + def locals(self) -> dict[str, Any]: """Return a mapping of names->values needed for evaluation.""" return {**collections.ChainMap(self.dependencies, self.context_vars, self._locals)} @locals.setter - def locals(self, locals_: typing.Dict[str, typing.Any]) -> None: + def locals(self, locals_: dict[str, Any]) -> None: """Update the contextual mapping of names to values.""" log.trace(f"Updating {self._locals} with {locals_}") self._locals.update(locals_) - def prepare_eval(self, code: str) -> typing.Optional[str]: + def prepare_eval(self, code: str) -> Optional[str]: """Prepare an evaluation by processing the code and setting up the context.""" self.code = code @@ -195,7 +194,7 @@ class WrapEvalCodeTree(ast.NodeTransformer): new_tree = self.visit(self.wrapper) return ast.fix_missing_locations(new_tree) - def visit_Pass(self, node: ast.Pass) -> typing.List[ast.AST]: # noqa: N802 + def visit_Pass(self, node: ast.Pass) -> list[ast.AST]: # noqa: N802 """ Replace the `_ast.Pass` node in the wrapper function by the eval AST. @@ -213,7 +212,7 @@ class CaptureLastExpression(ast.NodeTransformer): self.tree = tree self.last_node = list(ast.iter_child_nodes(tree))[-1] - def visit_Expr(self, node: ast.Expr) -> typing.Union[ast.Expr, ast.Assign]: # noqa: N802 + def visit_Expr(self, node: ast.Expr) -> Union[ast.Expr, ast.Assign]: # noqa: N802 """ Replace the Expr node that is last child node of Module with an assignment. diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index b7749144..4f6b4321 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -1,7 +1,7 @@ import logging import re import textwrap -import typing +from typing import Optional import discord from discord.ext import commands @@ -82,7 +82,7 @@ class InternalEval(commands.Cog): return shortened_output - async def _upload_output(self, output: str) -> typing.Optional[str]: + async def _upload_output(self, output: str) -> Optional[str]: """Upload `internal eval` output to our pastebin and return the url.""" try: async with self.bot.http_session.post( diff --git a/bot/exts/utils/extensions.py b/bot/exts/utils/extensions.py index a95def4e..521bfb47 100644 --- a/bot/exts/utils/extensions.py +++ b/bot/exts/utils/extensions.py @@ -1,7 +1,8 @@ import functools import logging -import typing as t +from collections.abc import Mapping from enum import Enum +from typing import Optional from discord import Colour, Embed from discord.ext import commands @@ -170,7 +171,7 @@ class Extensions(commands.Cog): log.debug(f"{ctx.author} requested a list of all cogs. Returning a paginated list.") await LinePaginator.paginate(lines, ctx, embed, max_size=1200, empty=False) - def group_extension_statuses(self) -> t.Mapping[str, str]: + def group_extension_statuses(self) -> Mapping[str, str]: """Return a mapping of extension names and statuses to their categories.""" categories = {} @@ -219,7 +220,7 @@ class Extensions(commands.Cog): return msg - def manage(self, action: Action, ext: str) -> t.Tuple[str, t.Optional[str]]: + def manage(self, action: Action, ext: str) -> tuple[str, Optional[str]]: """Apply an action to an extension and return the status message and any error message.""" verb = action.name.lower() error_msg = None diff --git a/bot/exts/valentines/be_my_valentine.py b/bot/exts/valentines/be_my_valentine.py index 8b522a72..c238027a 100644 --- a/bot/exts/valentines/be_my_valentine.py +++ b/bot/exts/valentines/be_my_valentine.py @@ -2,7 +2,6 @@ import logging import random from json import loads from pathlib import Path -from typing import Tuple import discord from discord.ext import commands @@ -146,7 +145,7 @@ class BeMyValentine(commands.Cog): else: await ctx.author.send(f"Your message has been sent to {user}") - def valentine_check(self, valentine_type: str) -> Tuple[str, str]: + def valentine_check(self, valentine_type: str) -> tuple[str, str]: """Return the appropriate Valentine type & title based on the invoking user's input.""" if valentine_type is None: return self.random_valentine() @@ -162,13 +161,13 @@ class BeMyValentine(commands.Cog): return valentine_type, "A message for" @staticmethod - def random_emoji() -> Tuple[str, str]: + def random_emoji() -> tuple[str, str]: """Return two random emoji from the module-defined constants.""" emoji_1 = random.choice(HEART_EMOJIS) emoji_2 = random.choice(HEART_EMOJIS) return emoji_1, emoji_2 - def random_valentine(self) -> Tuple[str, str]: + def random_valentine(self) -> tuple[str, str]: """Grabs a random poem or a compliment (any message).""" valentine_poem = random.choice(self.valentines["valentine_poems"]) valentine_compliment = random.choice(self.valentines["valentine_compliments"]) diff --git a/bot/exts/valentines/valentine_zodiac.py b/bot/exts/valentines/valentine_zodiac.py index d862ee63..339ea646 100644 --- a/bot/exts/valentines/valentine_zodiac.py +++ b/bot/exts/valentines/valentine_zodiac.py @@ -4,7 +4,7 @@ import logging import random from datetime import datetime from pathlib import Path -from typing import Tuple, Union +from typing import Union import discord from discord.ext import commands @@ -25,7 +25,7 @@ class ValentineZodiac(commands.Cog): self.zodiacs, self.zodiac_fact = self.load_comp_json() @staticmethod - def load_comp_json() -> Tuple[dict, dict]: + def load_comp_json() -> tuple[dict, dict]: """Load zodiac compatibility from static JSON resource.""" explanation_file = Path("bot/resources/valentines/zodiac_explanation.json") compatibility_file = Path("bot/resources/valentines/zodiac_compatibility.json") diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py index bef12d25..91682dbc 100644 --- a/bot/utils/__init__.py +++ b/bot/utils/__init__.py @@ -2,8 +2,9 @@ import asyncio import contextlib import re import string +from collections.abc import Iterable from datetime import datetime -from typing import Iterable, List, Optional +from typing import Optional import discord from discord.ext.commands import BadArgument, Context @@ -32,7 +33,7 @@ def resolve_current_month() -> Month: async def disambiguate( ctx: Context, - entries: List[str], + entries: list[str], *, timeout: float = 30, entries_per_page: int = 20, diff --git a/bot/utils/checks.py b/bot/utils/checks.py index 438ec750..d5c06728 100644 --- a/bot/utils/checks.py +++ b/bot/utils/checks.py @@ -1,6 +1,7 @@ import datetime import logging -from typing import Callable, Container, Iterable, Optional +from collections.abc import Container, Iterable +from typing import Callable, Optional from discord.ext.commands import ( BucketType, diff --git a/bot/utils/converters.py b/bot/utils/converters.py index fe2c980c..7227a406 100644 --- a/bot/utils/converters.py +++ b/bot/utils/converters.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Tuple, Union +from typing import Union import discord from discord.ext import commands @@ -23,7 +23,7 @@ class CoordinateConverter(commands.Converter): """Converter for Coordinates.""" @staticmethod - async def convert(ctx: commands.Context, coordinate: str) -> Tuple[int, int]: + async def convert(ctx: commands.Context, coordinate: str) -> tuple[int, int]: """Take in a coordinate string and turn it into an (x, y) tuple.""" if len(coordinate) not in (2, 3): raise commands.BadArgument("Invalid co-ordinate provided.") diff --git a/bot/utils/decorators.py b/bot/utils/decorators.py index c0783144..2612ff81 100644 --- a/bot/utils/decorators.py +++ b/bot/utils/decorators.py @@ -2,9 +2,10 @@ import asyncio import functools import logging import random -import typing as t from asyncio import Lock +from collections.abc import Container from functools import wraps +from typing import Callable, Union from weakref import WeakValueDictionary from discord import Colour, Embed @@ -32,7 +33,7 @@ class InMonthCheckFailure(CheckFailure): pass -def seasonal_task(*allowed_months: Month, sleep_time: t.Union[float, int] = ONE_DAY) -> t.Callable: +def seasonal_task(*allowed_months: Month, sleep_time: Union[float, int] = ONE_DAY) -> Callable: """ Perform the decorated method periodically in `allowed_months`. @@ -44,7 +45,7 @@ def seasonal_task(*allowed_months: Month, sleep_time: t.Union[float, int] = ONE_ The wrapped task is responsible for waiting for the bot to be ready, if necessary. """ - def decorator(task_body: t.Callable) -> t.Callable: + def decorator(task_body: Callable) -> Callable: @functools.wraps(task_body) async def decorated_task(*args, **kwargs) -> None: """Call `task_body` once every `sleep_time` seconds in `allowed_months`.""" @@ -63,13 +64,13 @@ def seasonal_task(*allowed_months: Month, sleep_time: t.Union[float, int] = ONE_ return decorator -def in_month_listener(*allowed_months: Month) -> t.Callable: +def in_month_listener(*allowed_months: Month) -> Callable: """ Shield a listener from being invoked outside of `allowed_months`. The check is performed against current UTC month. """ - def decorator(listener: t.Callable) -> t.Callable: + def decorator(listener: Callable) -> Callable: @functools.wraps(listener) async def guarded_listener(*args, **kwargs) -> None: """Wrapped listener will abort if not in allowed month.""" @@ -84,7 +85,7 @@ def in_month_listener(*allowed_months: Month) -> t.Callable: return decorator -def in_month_command(*allowed_months: Month) -> t.Callable: +def in_month_command(*allowed_months: Month) -> Callable: """ Check whether the command was invoked in one of `enabled_months`. @@ -106,7 +107,7 @@ def in_month_command(*allowed_months: Month) -> t.Callable: return commands.check(predicate) -def in_month(*allowed_months: Month) -> t.Callable: +def in_month(*allowed_months: Month) -> Callable: """ Universal decorator for season-locking commands and listeners alike. @@ -124,7 +125,7 @@ def in_month(*allowed_months: Month) -> t.Callable: manually set to True - this causes a circumvention of the group's callback and the seasonal check applied to it. """ - def decorator(callable_: t.Callable) -> t.Callable: + def decorator(callable_: Callable) -> Callable: # Functions decorated as commands are turned into instances of `Command` if isinstance(callable_, Command): logging.debug(f"Command {callable_.qualified_name} will be locked to {human_months(allowed_months)}") @@ -144,7 +145,7 @@ def in_month(*allowed_months: Month) -> t.Callable: return decorator -def with_role(*role_ids: int) -> t.Callable: +def with_role(*role_ids: int) -> Callable: """Check to see whether the invoking user has any of the roles specified in role_ids.""" async def predicate(ctx: Context) -> bool: if not ctx.guild: # Return False in a DM @@ -167,7 +168,7 @@ def with_role(*role_ids: int) -> t.Callable: return commands.check(predicate) -def without_role(*role_ids: int) -> t.Callable: +def without_role(*role_ids: int) -> Callable: """Check whether the invoking user does not have all of the roles specified in role_ids.""" async def predicate(ctx: Context) -> bool: if not ctx.guild: # Return False in a DM @@ -187,7 +188,7 @@ def without_role(*role_ids: int) -> t.Callable: return commands.check(predicate) -def whitelist_check(**default_kwargs: t.Container[int]) -> t.Callable[[Context], bool]: +def whitelist_check(**default_kwargs: Container[int]) -> Callable[[Context], bool]: """ Checks if a message is sent in a whitelisted context. @@ -222,8 +223,8 @@ def whitelist_check(**default_kwargs: t.Container[int]) -> t.Callable[[Context], kwargs[arg] = new_value # Merge containers - elif isinstance(default_value, t.Container): - if isinstance(new_value, t.Container): + elif isinstance(default_value, Container): + if isinstance(new_value, Container): kwargs[arg] = (*default_value, *new_value) else: kwargs[arg] = new_value @@ -279,7 +280,7 @@ def whitelist_check(**default_kwargs: t.Container[int]) -> t.Callable[[Context], return predicate -def whitelist_override(bypass_defaults: bool = False, **kwargs: t.Container[int]) -> t.Callable: +def whitelist_override(bypass_defaults: bool = False, **kwargs: Container[int]) -> Callable: """ Override global whitelist context, with the kwargs specified. @@ -288,7 +289,7 @@ def whitelist_override(bypass_defaults: bool = False, **kwargs: t.Container[int] This decorator has to go before (below) below the `command` decorator. """ - def inner(func: t.Callable) -> t.Callable: + def inner(func: Callable) -> Callable: func.override = kwargs func.override_reset = bypass_defaults return func @@ -296,7 +297,7 @@ def whitelist_override(bypass_defaults: bool = False, **kwargs: t.Container[int] return inner -def locked() -> t.Union[t.Callable, None]: +def locked() -> Union[Callable, None]: """ Allows the user to only run one instance of the decorated command at a time. @@ -304,11 +305,11 @@ def locked() -> t.Union[t.Callable, None]: This decorator has to go before (below) the `command` decorator. """ - def wrap(func: t.Callable) -> t.Union[t.Callable, None]: + def wrap(func: Callable) -> Union[Callable, None]: func.__locks = WeakValueDictionary() @wraps(func) - async def inner(self: t.Callable, ctx: Context, *args, **kwargs) -> t.Union[t.Callable, None]: + async def inner(self: Callable, ctx: Context, *args, **kwargs) -> Union[Callable, None]: lock = func.__locks.setdefault(ctx.author.id, Lock()) if lock.locked(): embed = Embed() diff --git a/bot/utils/extensions.py b/bot/utils/extensions.py index cd491c4b..cbb8f15e 100644 --- a/bot/utils/extensions.py +++ b/bot/utils/extensions.py @@ -1,7 +1,8 @@ import importlib import inspect import pkgutil -from typing import Iterator, NoReturn +from collections.abc import Iterator +from typing import NoReturn from discord.ext.commands import Context diff --git a/bot/utils/pagination.py b/bot/utils/pagination.py index b1062c09..013ef9e7 100644 --- a/bot/utils/pagination.py +++ b/bot/utils/pagination.py @@ -1,6 +1,7 @@ import asyncio import logging -from typing import Iterable, List, Optional, Tuple +from collections.abc import Iterable +from typing import Optional from discord import Embed, Member, Reaction from discord.abc import User @@ -313,7 +314,7 @@ class ImagePaginator(Paginator): self.images.append(image) @classmethod - async def paginate(cls, pages: List[Tuple[str, str]], ctx: Context, embed: Embed, + async def paginate(cls, pages: list[tuple[str, str]], ctx: Context, embed: Embed, prefix: str = "", suffix: str = "", timeout: int = 300, exception_on_empty_embed: bool = False) -> None: """ diff --git a/bot/utils/randomization.py b/bot/utils/randomization.py index 8f47679a..3360ef44 100644 --- a/bot/utils/randomization.py +++ b/bot/utils/randomization.py @@ -1,6 +1,7 @@ import itertools import random -import typing as t +from collections.abc import Iterable +from typing import Any class RandomCycle: @@ -10,11 +11,11 @@ class RandomCycle: The iterable is reshuffled after each full cycle. """ - def __init__(self, iterable: t.Iterable) -> None: + def __init__(self, iterable: Iterable) -> None: self.iterable = list(iterable) self.index = itertools.cycle(range(len(iterable))) - def __next__(self) -> t.Any: + def __next__(self) -> Any: idx = next(self.index) if idx == 0: -- cgit v1.2.3 From a4351738cc074f42b60521c122e8712888c50498 Mon Sep 17 00:00:00 2001 From: wookie184 Date: Wed, 1 Sep 2021 12:49:50 +0100 Subject: Start and end codeblocks with newlines to avoid android rendering them inline --- bot/exts/evergreen/emoji.py | 2 +- bot/exts/evergreen/error_handler.py | 4 ++-- bot/exts/evergreen/githubinfo.py | 2 +- bot/exts/evergreen/help.py | 2 +- bot/exts/halloween/hacktoberstats.py | 4 ++-- bot/exts/pride/pride_leader.py | 2 +- bot/exts/utils/extensions.py | 8 ++++---- bot/exts/valentines/valentine_zodiac.py | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) (limited to 'bot/exts/utils/extensions.py') diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index 11615214..3d2bf9cb 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -65,7 +65,7 @@ class Emojis(commands.Cog): emoji_dict[emoji.name.split("_")[0]].append(emoji) error_comp = ", ".join(emoji_dict) - msg.append(f"These are the valid emoji categories:\n```{error_comp}```") + msg.append(f"These are the valid emoji categories:\n```\n{error_comp}\n```") return embed, msg @commands.group(name="emoji", invoke_without_command=True) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index a280c725..1014f6b2 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -74,7 +74,7 @@ class CommandErrorHandler(commands.Cog): if isinstance(error, commands.UserInputError): self.revert_cooldown_counter(ctx.command, ctx.message) - usage = f"```{ctx.prefix}{parent_command}{ctx.command} {ctx.command.signature}```" + usage = f"```\n{ctx.prefix}{parent_command}{ctx.command} {ctx.command.signature}\n```" embed = self.error_embed( f"Your input was invalid: {error}\n\nUsage:{usage}" ) @@ -107,7 +107,7 @@ class CommandErrorHandler(commands.Cog): self.revert_cooldown_counter(ctx.command, ctx.message) embed = self.error_embed( "The argument you provided was invalid: " - f"{error}\n\nUsage:\n```{ctx.prefix}{parent_command}{ctx.command} {ctx.command.signature}```" + f"{error}\n\nUsage:\n```\n{ctx.prefix}{parent_command}{ctx.command} {ctx.command.signature}\n```" ) await ctx.send(embed=embed) return diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index d29f3aa9..bbc9061a 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -66,7 +66,7 @@ class GithubInfo(commands.Cog): embed = discord.Embed( title=f"`{user_data['login']}`'s GitHub profile info", - description=f"```{user_data['bio']}```\n" if user_data["bio"] else "", + description=f"```\n{user_data['bio']}\n```\n" if user_data["bio"] else "", colour=discord.Colour.blurple(), url=user_data["html_url"], timestamp=datetime.strptime(user_data["created_at"], "%Y-%m-%dT%H:%M:%SZ") diff --git a/bot/exts/evergreen/help.py b/bot/exts/evergreen/help.py index bfb5db17..0c0922d6 100644 --- a/bot/exts/evergreen/help.py +++ b/bot/exts/evergreen/help.py @@ -308,7 +308,7 @@ class HelpSession: signature = self._get_command_params(self.query) parent = self.query.full_parent_name + " " if self.query.parent else "" - paginator.add_line(f"**```{prefix}{parent}{signature}```**") + paginator.add_line(f"**```\n{prefix}{parent}{signature}\n```**") aliases = [f"`{alias}`" if not parent else f"`{parent} {alias}`" for alias in self.query.aliases] aliases += [f"`{alias}`" for alias in getattr(self.query, "root_aliases", ())] aliases = ", ".join(sorted(aliases)) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 24106a5e..e7ba2f50 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -61,8 +61,8 @@ class HacktoberStats(commands.Cog): else: msg = ( f"{author_mention}, you have not linked a GitHub account\n\n" - f"You can link your GitHub account using:\n```{ctx.prefix}hackstats link github_username```\n" - f"Or query GitHub stats directly using:\n```{ctx.prefix}hackstats github_username```" + f"You can link your GitHub account using:\n```\n{ctx.prefix}hackstats link github_username\n```\n" + f"Or query GitHub stats directly using:\n```\n{ctx.prefix}hackstats github_username\n```" ) await ctx.send(msg) return diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index 8e88183b..8cfe0ebc 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -51,7 +51,7 @@ class PrideLeader(commands.Cog): valid_names = "\n".join(valid_names) error_msg = "Did you mean?" - embed.description = f"{error_msg}\n```{valid_names}```" + embed.description = f"{error_msg}\n```\n{valid_names}\n```" embed.set_footer(text="To add more pride leaders, feel free to open a pull request!") return embed diff --git a/bot/exts/utils/extensions.py b/bot/exts/utils/extensions.py index a95def4e..2f72a302 100644 --- a/bot/exts/utils/extensions.py +++ b/bot/exts/utils/extensions.py @@ -60,7 +60,7 @@ class Extension(commands.Converter): names = "\n".join(matches) raise commands.BadArgument( f":x: `{argument}` is an ambiguous extension name. " - f"Please use one of the following fully-qualified names.```\n{names}```" + f"Please use one of the following fully-qualified names.```\n{names}\n```" ) elif matches: return matches[0] @@ -110,7 +110,7 @@ class Extensions(commands.Cog): blacklisted = "\n".join(UNLOAD_BLACKLIST & set(extensions)) if blacklisted: - msg = f":x: The following extension(s) may not be unloaded:```{blacklisted}```" + msg = f":x: The following extension(s) may not be unloaded:```\n{blacklisted}\n```" else: if "*" in extensions or "**" in extensions: extensions = set(self.bot.extensions.keys()) - UNLOAD_BLACKLIST @@ -213,7 +213,7 @@ class Extensions(commands.Cog): if failures: failures = "\n".join(f"{ext}\n {err}" for ext, err in failures.items()) - msg += f"\nFailures:```{failures}```" + msg += f"\nFailures:```\n{failures}\n```" log.debug(f"Batch {verb}ed extensions.") @@ -240,7 +240,7 @@ class Extensions(commands.Cog): log.exception(f"Extension '{ext}' failed to {verb}.") error_msg = f"{e.__class__.__name__}: {e}" - msg = f":x: Failed to {verb} extension `{ext}`:\n```{error_msg}```" + msg = f":x: Failed to {verb} extension `{ext}`:\n```\n{error_msg}\n```" else: msg = f":ok_hand: Extension successfully {verb}ed: `{ext}`." log.debug(msg[10:]) diff --git a/bot/exts/valentines/valentine_zodiac.py b/bot/exts/valentines/valentine_zodiac.py index d862ee63..2a9069b1 100644 --- a/bot/exts/valentines/valentine_zodiac.py +++ b/bot/exts/valentines/valentine_zodiac.py @@ -108,7 +108,7 @@ class ValentineZodiac(commands.Cog): except ValueError as e: final_embed = discord.Embed() final_embed.color = Colours.soft_red - final_embed.description = f"Zodiac sign could not be found because.\n```{e}```" + final_embed.description = f"Zodiac sign could not be found because.\n```\n{e}\n```" log.info(f"Error in 'zodiac date' command:\n{e}.") else: final_embed = self.zodiac_build_embed(zodiac_sign_based_on_date) -- cgit v1.2.3