aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/constants.py4
-rw-r--r--bot/exts/__init__.py2
-rw-r--r--bot/exts/christmas/advent_of_code/_helpers.py15
-rw-r--r--bot/exts/christmas/hanukkah_embed.py5
-rw-r--r--bot/exts/easter/bunny_name_generator.py6
-rw-r--r--bot/exts/evergreen/avatar_modification/_effects.py12
-rw-r--r--bot/exts/evergreen/avatar_modification/avatar_modify.py12
-rw-r--r--bot/exts/evergreen/battleship.py26
-rw-r--r--bot/exts/evergreen/bookmark.py4
-rw-r--r--bot/exts/evergreen/cheatsheet.py4
-rw-r--r--bot/exts/evergreen/coinflip.py5
-rw-r--r--bot/exts/evergreen/connect_four.py30
-rw-r--r--bot/exts/evergreen/emoji.py6
-rw-r--r--bot/exts/evergreen/error_handler.py3
-rw-r--r--bot/exts/evergreen/fun.py5
-rw-r--r--bot/exts/evergreen/game.py16
-rw-r--r--bot/exts/evergreen/help.py8
-rw-r--r--bot/exts/evergreen/issues.py10
-rw-r--r--bot/exts/evergreen/minesweeper.py12
-rw-r--r--bot/exts/evergreen/movie.py10
-rw-r--r--bot/exts/evergreen/reddit.py8
-rw-r--r--bot/exts/evergreen/snakes/_converter.py4
-rw-r--r--bot/exts/evergreen/snakes/_snakes_cog.py8
-rw-r--r--bot/exts/evergreen/snakes/_utils.py46
-rw-r--r--bot/exts/evergreen/source.py4
-rw-r--r--bot/exts/evergreen/space.py6
-rw-r--r--bot/exts/evergreen/tic_tac_toe.py24
-rw-r--r--bot/exts/evergreen/trivia_quiz.py4
-rw-r--r--bot/exts/evergreen/wikipedia.py4
-rw-r--r--bot/exts/evergreen/wolfram.py14
-rw-r--r--bot/exts/evergreen/xkcd.py4
-rw-r--r--bot/exts/halloween/hacktober-issue-finder.py6
-rw-r--r--bot/exts/halloween/hacktoberstats.py14
-rw-r--r--bot/exts/halloween/halloween_facts.py3
-rw-r--r--bot/exts/halloween/spookyrating.py3
-rw-r--r--bot/exts/halloween/timeleft.py3
-rw-r--r--bot/exts/internal_eval/_helpers.py19
-rw-r--r--bot/exts/internal_eval/_internal_eval.py4
-rw-r--r--bot/exts/utils/extensions.py7
-rw-r--r--bot/exts/valentines/be_my_valentine.py7
-rw-r--r--bot/exts/valentines/valentine_zodiac.py4
-rw-r--r--bot/utils/__init__.py5
-rw-r--r--bot/utils/checks.py3
-rw-r--r--bot/utils/converters.py4
-rw-r--r--bot/utils/decorators.py37
-rw-r--r--bot/utils/extensions.py3
-rw-r--r--bot/utils/pagination.py5
-rw-r--r--bot/utils/randomization.py7
48 files changed, 227 insertions, 228 deletions
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: