aboutsummaryrefslogtreecommitdiffstats
path: root/bot/exts/fun
diff options
context:
space:
mode:
authorGravatar ChrisJL <[email protected]>2023-05-09 16:01:01 +0100
committerGravatar GitHub <[email protected]>2023-05-09 16:01:01 +0100
commitc3e23e60278d34658f801bd7d7ed721d5a272637 (patch)
treee159a0fae7850d706d713cf2b49dfed2140ce655 /bot/exts/fun
parentBump sentry-sdk from 1.21.1 to 1.22.1 (#1273) (diff)
parentMove unshared contants inside modules (diff)
Merge pull request #1270 from python-discord/migrate-to-ruff
Migrate to ruff
Diffstat (limited to 'bot/exts/fun')
-rw-r--r--bot/exts/fun/anagram.py2
-rw-r--r--bot/exts/fun/battleship.py20
-rw-r--r--bot/exts/fun/catify.py30
-rw-r--r--bot/exts/fun/connect_four.py29
-rw-r--r--bot/exts/fun/duck_game.py12
-rw-r--r--bot/exts/fun/game.py34
-rw-r--r--bot/exts/fun/hangman.py9
-rw-r--r--bot/exts/fun/latex.py19
-rw-r--r--bot/exts/fun/madlibs.py2
-rw-r--r--bot/exts/fun/minesweeper.py11
-rw-r--r--bot/exts/fun/movie.py12
-rw-r--r--bot/exts/fun/quack.py6
-rw-r--r--bot/exts/fun/snakes/_snakes_cog.py107
-rw-r--r--bot/exts/fun/snakes/_utils.py44
-rw-r--r--bot/exts/fun/space.py32
-rw-r--r--bot/exts/fun/tic_tac_toe.py25
-rw-r--r--bot/exts/fun/trivia_quiz.py14
-rw-r--r--bot/exts/fun/uwu.py6
-rw-r--r--bot/exts/fun/wonder_twins.py4
-rw-r--r--bot/exts/fun/xkcd.py5
20 files changed, 209 insertions, 214 deletions
diff --git a/bot/exts/fun/anagram.py b/bot/exts/fun/anagram.py
index d8ea6a55..8210d1d5 100644
--- a/bot/exts/fun/anagram.py
+++ b/bot/exts/fun/anagram.py
@@ -15,7 +15,7 @@ log = logging.getLogger(__name__)
TIME_LIMIT = 60
# anagram.json file contains all the anagrams
-with open(Path("bot/resources/fun/anagram.json"), "r") as f:
+with open(Path("bot/resources/fun/anagram.json")) as f:
ANAGRAMS_ALL = json.load(f)
diff --git a/bot/exts/fun/battleship.py b/bot/exts/fun/battleship.py
index a8039cf2..4a552605 100644
--- a/bot/exts/fun/battleship.py
+++ b/bot/exts/fun/battleship.py
@@ -4,7 +4,6 @@ import random
import re
from dataclasses import dataclass
from functools import partial
-from typing import Optional
import discord
from discord.ext import commands
@@ -19,7 +18,7 @@ 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: Optional[str]
+ boat: str | None
aimed: bool
@@ -31,8 +30,8 @@ EmojiSet = dict[tuple[bool, bool], str]
class Player:
"""Each player in the game - their messages for the boards and their current grid."""
- user: Optional[discord.Member]
- board: Optional[discord.Message]
+ user: discord.Member | None
+ board: discord.Message | None
opponent_board: discord.Message
grid: Grid
@@ -110,10 +109,10 @@ class Game:
self.gameover: bool = False
- self.turn: Optional[Player] = None
- self.next: Optional[Player] = None
+ self.turn: Player | None = None
+ self.next: Player | None = None
- self.match: Optional[re.Match] = None
+ self.match: re.Match | None = None
self.surrender: bool = False
self.setup_grids()
@@ -135,7 +134,7 @@ class Game:
for row in player.grid
]
- rows = ["".join([number] + row) for number, row in zip(NUMBERS, grid)]
+ rows = ["".join([number] + row) for number, row in zip(NUMBERS, grid, strict=True)]
return "\n".join([LETTERS] + rows)
@staticmethod
@@ -215,7 +214,7 @@ class Game:
(self.p1, "board"), (self.p2, "board")
)
- for board, location in zip(boards, locations):
+ for board, location in zip(boards, locations, strict=True):
player, attr = location
if getattr(player, attr):
await getattr(player, attr).edit(content=board)
@@ -232,8 +231,9 @@ class Game:
if not self.match:
self.bot.loop.create_task(message.add_reaction(CROSS_EMOJI))
return bool(self.match)
+ return None
- async def take_turn(self) -> Optional[Square]:
+ async def take_turn(self) -> Square | None:
"""Lets the player who's turn it is choose a square."""
square = None
turn_message = await self.turn.user.send(
diff --git a/bot/exts/fun/catify.py b/bot/exts/fun/catify.py
index 6e8c75ba..c1677cd8 100644
--- a/bot/exts/fun/catify.py
+++ b/bot/exts/fun/catify.py
@@ -1,21 +1,22 @@
import random
from contextlib import suppress
-from typing import Optional
from discord import AllowedMentions, Embed, Forbidden
from discord.ext import commands
from bot.bot import Bot
-from bot.constants import Cats, Colours, NEGATIVE_REPLIES
+from bot.constants import Colours, NEGATIVE_REPLIES
from bot.utils import helpers
+CATS = ["ᓚᘏᗢ", "ᘡᘏᗢ", "🐈", "ᓕᘏᗢ", "ᓇᘏᗢ", "ᓂᘏᗢ", "ᘣᘏᗢ", "ᕦᘏᗢ", "ᕂᘏᗢ"]
+
class Catify(commands.Cog):
"""Cog for the catify command."""
@commands.command(aliases=("ᓚᘏᗢify", "ᓚᘏᗢ"))
@commands.cooldown(1, 5, commands.BucketType.user)
- async def catify(self, ctx: commands.Context, *, text: Optional[str]) -> None:
+ async def catify(self, ctx: commands.Context, *, text: str | None) -> None:
"""
Convert the provided text into a cat themed sentence by interspercing cats throughout text.
@@ -36,13 +37,12 @@ class Catify(commands.Cog):
await ctx.send(embed=embed)
return
- else:
- display_name += f" | {random.choice(Cats.cats)}"
+ display_name += f" | {random.choice(CATS)}"
- await ctx.send(f"Your catified nickname is: `{display_name}`", allowed_mentions=AllowedMentions.none())
+ await ctx.send(f"Your catified nickname is: `{display_name}`", allowed_mentions=AllowedMentions.none())
- with suppress(Forbidden):
- await ctx.author.edit(nick=display_name)
+ with suppress(Forbidden):
+ await ctx.author.edit(nick=display_name)
else:
if len(text) >= 1500:
embed = Embed(
@@ -58,21 +58,21 @@ class Catify(commands.Cog):
name = name.lower()
if "cat" in name:
if random.randint(0, 5) == 5:
- string_list[index] = name.replace("cat", f"**{random.choice(Cats.cats)}**")
+ string_list[index] = name.replace("cat", f"**{random.choice(CATS)}**")
else:
- string_list[index] = name.replace("cat", random.choice(Cats.cats))
- for element in Cats.cats:
- if element in name:
- string_list[index] = name.replace(element, "cat")
+ string_list[index] = name.replace("cat", random.choice(CATS))
+ for cat in CATS:
+ if cat in name:
+ string_list[index] = name.replace(cat, "cat")
string_len = len(string_list) // 3 or len(string_list)
for _ in range(random.randint(1, string_len)):
# insert cat at random index
if random.randint(0, 5) == 5:
- string_list.insert(random.randint(0, len(string_list)), f"**{random.choice(Cats.cats)}**")
+ string_list.insert(random.randint(0, len(string_list)), f"**{random.choice(CATS)}**")
else:
- string_list.insert(random.randint(0, len(string_list)), random.choice(Cats.cats))
+ string_list.insert(random.randint(0, len(string_list)), random.choice(CATS))
text = helpers.suppress_links(" ".join(string_list))
await ctx.send(
diff --git a/bot/exts/fun/connect_four.py b/bot/exts/fun/connect_four.py
index 0d870a6e..6544dc48 100644
--- a/bot/exts/fun/connect_four.py
+++ b/bot/exts/fun/connect_four.py
@@ -1,7 +1,6 @@
import asyncio
import random
from functools import partial
-from typing import Optional, Union
import discord
import emojis
@@ -14,8 +13,8 @@ from bot.constants import Emojis
NUMBERS = list(Emojis.number_emojis.values())
CROSS_EMOJI = Emojis.incident_unactioned
-Coordinate = Optional[tuple[int, int]]
-EMOJI_CHECK = Union[discord.Emoji, str]
+Coordinate = tuple[int, int] | None
+EMOJI_CHECK = discord.Emoji | str
class Game:
@@ -26,7 +25,7 @@ class Game:
bot: Bot,
channel: discord.TextChannel,
player1: discord.Member,
- player2: Optional[discord.Member],
+ player2: discord.Member | None,
tokens: list[str],
size: int = 7
):
@@ -73,7 +72,7 @@ class Game:
await self.message.edit(content=None, embed=embed)
async def game_over(
- self, action: str, player1: Union[ClientUser, Member], player2: Union[ClientUser, Member]
+ self, action: str, player1: ClientUser | Member, player2: ClientUser | Member
) -> None:
"""Announces to public chat."""
if action == "win":
@@ -134,12 +133,12 @@ class Game:
reaction, user = await self.bot.wait_for("reaction_add", check=self.predicate, timeout=30.0)
except asyncio.TimeoutError:
await self.channel.send(f"{self.player_active.mention}, you took too long. Game over!")
- return
+ return None
else:
await message.delete()
if str(reaction.emoji) == CROSS_EMOJI:
await self.game_over("quit", self.player_active, self.player_inactive)
- return
+ return None
await self.message.remove_reaction(reaction, user)
@@ -197,7 +196,7 @@ class AI:
break
return possible_coords
- def check_ai_win(self, coord_list: list[Coordinate]) -> Optional[Coordinate]:
+ def check_ai_win(self, coord_list: list[Coordinate]) -> Coordinate:
"""
Check AI win.
@@ -205,12 +204,13 @@ class AI:
with 10% chance of not winning and returning None
"""
if random.randint(1, 10) == 1:
- return
+ return None
for coords in coord_list:
if self.game.check_win(coords, 2):
return coords
+ return None
- def check_player_win(self, coord_list: list[Coordinate]) -> Optional[Coordinate]:
+ def check_player_win(self, coord_list: list[Coordinate]) -> Coordinate | None:
"""
Check Player win.
@@ -218,17 +218,18 @@ class AI:
from winning with 25% of not blocking them and returning None.
"""
if random.randint(1, 4) == 1:
- return
+ return None
for coords in coord_list:
if self.game.check_win(coords, 1):
return coords
+ return None
@staticmethod
def random_coords(coord_list: list[Coordinate]) -> Coordinate:
"""Picks a random coordinate from the possible ones."""
return random.choice(coord_list)
- def play(self) -> Union[Coordinate, bool]:
+ def play(self) -> Coordinate | bool:
"""
Plays for the AI.
@@ -331,7 +332,7 @@ class ConnectFour(commands.Cog):
@staticmethod
def check_emojis(
e1: EMOJI_CHECK, e2: EMOJI_CHECK
- ) -> tuple[bool, Optional[str]]:
+ ) -> tuple[bool, str | None]:
"""Validate the emojis, the user put."""
if isinstance(e1, str) and emojis.count(e1) != 1:
return False, e1
@@ -342,7 +343,7 @@ class ConnectFour(commands.Cog):
async def _play_game(
self,
ctx: commands.Context,
- user: Optional[discord.Member],
+ user: discord.Member | None,
board_size: int,
emoji1: str,
emoji2: str
diff --git a/bot/exts/fun/duck_game.py b/bot/exts/fun/duck_game.py
index a2612e51..3f34ece7 100644
--- a/bot/exts/fun/duck_game.py
+++ b/bot/exts/fun/duck_game.py
@@ -42,7 +42,7 @@ CARD_HEIGHT = 97
EMOJI_WRONG = "\u274C"
-ANSWER_REGEX = re.compile(r'^\D*(\d+)\D+(\d+)\D+(\d+)\D*$')
+ANSWER_REGEX = re.compile(r"^\D*(\d+)\D+(\d+)\D+(\d+)\D*$")
HELP_TEXT = """
**Each card has 4 features**
@@ -97,7 +97,7 @@ def get_card_image(card: tuple[int]) -> Image:
def as_trinary(card: tuple[int]) -> int:
"""Find the card's unique index by interpreting its features as trinary."""
- return int(''.join(str(x) for x in card), base=3)
+ return int("".join(str(x) for x in card), base=3)
class DuckGame:
@@ -156,7 +156,7 @@ class DuckGame:
# which is prevented by the triangle iteration.
completion = tuple(
feat_a if feat_a == feat_b else 3-feat_a-feat_b
- for feat_a, feat_b in zip(card_a, card_b)
+ for feat_a, feat_b in zip(card_a, card_b, strict=True)
)
try:
idx_c = self.board.index(completion)
@@ -178,8 +178,8 @@ class DuckGamesDirector(commands.Cog):
self.current_games = {}
@commands.group(
- name='duckduckduckgoose',
- aliases=['dddg', 'ddg', 'duckduckgoose', 'duckgoose'],
+ name="duckduckduckgoose",
+ aliases=["dddg", "ddg", "duckduckgoose", "duckgoose"],
invoke_without_command=True
)
@commands.cooldown(rate=1, per=2, type=commands.BucketType.channel)
@@ -218,7 +218,7 @@ class DuckGamesDirector(commands.Cog):
return
game = self.current_games[channel.id]
- if msg.content.strip().lower() == 'goose':
+ if msg.content.strip().lower() == "goose":
# If all of the solutions have been claimed, i.e. the "goose" call is correct.
if len(game.solutions) == len(game.claimed_answers):
try:
diff --git a/bot/exts/fun/game.py b/bot/exts/fun/game.py
index c38dc063..b2b18f04 100644
--- a/bot/exts/fun/game.py
+++ b/bot/exts/fun/game.py
@@ -2,9 +2,9 @@ import difflib
import logging
import random
import re
-from datetime import datetime as dt, timedelta
+from datetime import UTC, datetime, timedelta
from enum import IntEnum
-from typing import Any, Optional
+from typing import Any
from aiohttp import ClientSession
from discord import Embed
@@ -255,7 +255,7 @@ class Games(Cog):
self.genres[genre_name] = genre
@group(name="games", aliases=("game",), invoke_without_command=True)
- async def games(self, ctx: Context, amount: Optional[int] = 5, *, genre: Optional[str]) -> None:
+ async def games(self, ctx: Context, amount: int | None = 5, *, genre: str | None) -> None:
"""
Get random game(s) by genre from IGDB. Use .games genres command to get all available genres.
@@ -293,7 +293,8 @@ class Games(Cog):
f"{f'Maybe you meant `{display_possibilities}`?' if display_possibilities else ''}"
)
return
- elif len(possibilities) == 1:
+
+ if len(possibilities) == 1:
games = await self.get_games_list(
amount, self.genres[possibilities[0][1]], offset=random.randint(0, 150)
)
@@ -368,8 +369,8 @@ class Games(Cog):
async def get_games_list(
self,
amount: int,
- genre: Optional[str] = None,
- sort: Optional[str] = None,
+ genre: str | None = None,
+ sort: str | None = None,
additional_body: str = "",
offset: int = 0
) -> list[dict[str, Any]]:
@@ -398,10 +399,13 @@ class Games(Cog):
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 ""})
+ url = COVER_URL.format(image_id=data.get("cover", {}).get("image_id", ""))
# Get release date separately with checking
- release_date = dt.utcfromtimestamp(data["first_release_date"]).date() if "first_release_date" in data else "?"
+ if "first_release_date" in data:
+ release_date = datetime.fromtimestamp(data["first_release_date"], tz=UTC).date()
+ else:
+ release_date = "?"
# Create Age Ratings value
rating = ", ".join(
@@ -434,7 +438,7 @@ class Games(Cog):
lines = []
# Define request body of IGDB API request and do request
- body = SEARCH_BODY.format(**{"term": search_term})
+ body = SEARCH_BODY.format(term=search_term)
async with self.http_session.post(url=f"{BASE_URL}/games", data=body, headers=self.headers) as resp:
data = await resp.json()
@@ -460,10 +464,10 @@ class Games(Cog):
returning results.
"""
# Create request body from template
- body = COMPANIES_LIST_BODY.format(**{
- "limit": limit,
- "offset": offset
- })
+ body = COMPANIES_LIST_BODY.format(
+ limit=limit,
+ offset=offset,
+ )
async with self.http_session.post(url=f"{BASE_URL}/companies", data=body, headers=self.headers) as resp:
return await resp.json()
@@ -471,10 +475,10 @@ class Games(Cog):
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 ""})
+ url = LOGO_URL.format(image_id=data.get("logo", {}).get("image_id", ""))
# Try to get found date of company
- founded = dt.utcfromtimestamp(data["start_date"]).date() if "start_date" in data else "?"
+ founded = datetime.fromtimestamp(data["start_date"], tz=UTC).date() if "start_date" in data else "?"
# Generate list of games, that company have developed or published
developed = ", ".join(game["name"] for game in data["developed"]) if "developed" in data else "?"
diff --git a/bot/exts/fun/hangman.py b/bot/exts/fun/hangman.py
index f385a955..7a02a552 100644
--- a/bot/exts/fun/hangman.py
+++ b/bot/exts/fun/hangman.py
@@ -89,7 +89,7 @@ class Hangman(commands.Cog):
word = choice(filtered_words)
# `pretty_word` is used for comparing the indices where the guess of the user is similar to the word
# The `user_guess` variable is prettified by adding spaces between every dash, and so is the `pretty_word`
- pretty_word = ''.join([f"{letter} " for letter in word])[:-1]
+ pretty_word = "".join([f"{letter} " for letter in word])[:-1]
user_guess = ("_ " * len(word))[:-1]
tries = 6
guessed_letters = set()
@@ -104,7 +104,7 @@ class Hangman(commands.Cog):
))
# Game loop
- while user_guess.replace(' ', '') != word:
+ while user_guess.replace(" ", "") != word:
# Edit the message to the current state of the game
await original_message.edit(embed=self.create_embed(tries, user_guess))
@@ -136,7 +136,7 @@ class Hangman(commands.Cog):
continue
# Checks for repeated guesses
- elif normalized_content in guessed_letters:
+ if normalized_content in guessed_letters:
already_guessed_embed = Embed(
title=choice(NEGATIVE_REPLIES),
description=f"You have already guessed `{normalized_content}`, try again!",
@@ -146,12 +146,11 @@ class Hangman(commands.Cog):
continue
# Checks for correct guesses from the user
- elif normalized_content in word:
+ if normalized_content in word:
positions = {idx for idx, letter in enumerate(pretty_word) if letter == normalized_content}
user_guess = "".join(
[normalized_content if index in positions else dash for index, dash in enumerate(user_guess)]
)
-
else:
tries -= 1
diff --git a/bot/exts/fun/latex.py b/bot/exts/fun/latex.py
index 8af05413..12f2d0b6 100644
--- a/bot/exts/fun/latex.py
+++ b/bot/exts/fun/latex.py
@@ -1,19 +1,22 @@
import hashlib
+import logging
import os
import re
import string
from io import BytesIO
from pathlib import Path
-from typing import BinaryIO, Optional
+from typing import BinaryIO
import discord
from PIL import Image
+from aiohttp import web
from discord.ext import commands
from bot.bot import Bot
from bot.constants import Channels, WHITELISTED_CHANNELS
from bot.utils.decorators import whitelist_override
+log = logging.getLogger(__name__)
FORMATTED_CODE_REGEX = re.compile(
r"(?P<delim>(?P<block>```)|``?)" # code delimiter: 1-3 backticks; (?P=block) only matches if it's a block
r"(?(block)(?:(?P<lang>[a-z]+)\n)?)" # if we're in a block, match optional language (only letters plus newline)
@@ -45,8 +48,7 @@ def _prepare_input(text: str) -> str:
"""Extract latex from a codeblock, if it is in one."""
if match := FORMATTED_CODE_REGEX.match(text):
return match.group("code")
- else:
- return text
+ return text
def _process_image(data: bytes, out_file: BinaryIO) -> None:
@@ -65,7 +67,7 @@ def _process_image(data: bytes, out_file: BinaryIO) -> None:
class InvalidLatexError(Exception):
"""Represents an error caused by invalid latex."""
- def __init__(self, logs: Optional[str]):
+ def __init__(self, logs: str | None):
super().__init__(logs)
self.logs = logs
@@ -89,7 +91,7 @@ class Latex(commands.Cog):
) as response:
_process_image(await response.read(), out_file)
- async def _upload_to_pastebin(self, text: str) -> Optional[str]:
+ async def _upload_to_pastebin(self, text: str) -> str | None:
"""Uploads `text` to the paste service, returning the url if successful."""
try:
async with self.bot.http_session.post(
@@ -100,9 +102,8 @@ class Latex(commands.Cog):
response_json = await response.json()
if "key" in response_json:
return f"{PASTEBIN_URL}/{response_json['key']}.txt?noredirect"
- except Exception:
- # 400 (Bad Request) means there are too many characters
- pass
+ except web.HTTPClientError as e:
+ log.info("Error when uploading latex output to pastebin. %s", e)
@commands.command()
@commands.max_concurrency(1, commands.BucketType.guild, wait=True)
@@ -112,7 +113,7 @@ class Latex(commands.Cog):
query = _prepare_input(query)
# the hash of the query is used as the filename in the cache.
- query_hash = hashlib.md5(query.encode()).hexdigest()
+ query_hash = hashlib.md5(query.encode()).hexdigest() # noqa: S324
image_path = CACHE_DIRECTORY / f"{query_hash}.png"
async with ctx.typing():
if not image_path.exists():
diff --git a/bot/exts/fun/madlibs.py b/bot/exts/fun/madlibs.py
index 075dde75..c14e8a3a 100644
--- a/bot/exts/fun/madlibs.py
+++ b/bot/exts/fun/madlibs.py
@@ -117,7 +117,7 @@ class Madlibs(commands.Cog):
self.checks.remove(author_check)
story = []
- for value, blank in zip(random_template["value"], blanks):
+ for value, blank in zip(random_template["value"], blanks, strict=True):
story.append(f"{value}__{blank}__")
# In each story template, there is always one more "value"
diff --git a/bot/exts/fun/minesweeper.py b/bot/exts/fun/minesweeper.py
index f16b1db2..69be88d3 100644
--- a/bot/exts/fun/minesweeper.py
+++ b/bot/exts/fun/minesweeper.py
@@ -2,7 +2,6 @@ import logging
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 +32,7 @@ MESSAGE_MAPPING = {
log = logging.getLogger(__name__)
-GameBoard = list[list[Union[str, int]]]
+GameBoard = list[list[str | int]]
@dataclass
@@ -205,9 +204,9 @@ class Minesweeper(commands.Cog):
for y in range(10)
):
return False
- else:
- await self.won(ctx)
- return True
+
+ await self.won(ctx)
+ return True
async def reveal_one(
self,
@@ -227,7 +226,7 @@ class Minesweeper(commands.Cog):
await self.lost(ctx)
revealed[y][x] = "x" # mark bomb that made you lose with a x
return True
- elif board[y][x] == 0:
+ if board[y][x] == 0:
self.reveal_zeros(revealed, board, x, y)
return await self.check_if_won(ctx, revealed, board)
diff --git a/bot/exts/fun/movie.py b/bot/exts/fun/movie.py
index 21183e0f..3d36b119 100644
--- a/bot/exts/fun/movie.py
+++ b/bot/exts/fun/movie.py
@@ -63,17 +63,17 @@ class Movie(Cog):
@group(name="movies", aliases=("movie",), invoke_without_command=True)
async def movies(self, ctx: Context, genre: str = "", amount: int = 5) -> None:
"""
- Get random movies by specifying genre. Also support amount parameter,\
- that define how much movies will be shown.
+ Get random movies by specifying genre.
- Default 5. Use .movies genres to get all available genres.
+ The amount parameter, that defines how many movies will be shown, defaults to 5.
+ Use `.movies genres` to get all available genres.
"""
# Check is there more than 20 movies specified, due TMDB return 20 movies
# per page, so this is max. Also you can't get less movies than 1, just logic
if amount > 20:
await ctx.send("You can't get more than 20 movies at once. (TMDB limits)")
return
- elif amount < 1:
+ if amount < 1:
await ctx.send("You can't get less than 1 movie.")
return
@@ -179,8 +179,8 @@ class Movie(Cog):
text += "__**Some Numbers**__\n"
- budget = f"{movie['budget']:,d}" if movie['budget'] else "?"
- revenue = f"{movie['revenue']:,d}" if movie['revenue'] else "?"
+ budget = f"{movie['budget']:,d}" if movie["budget"] else "?"
+ revenue = f"{movie['revenue']:,d}" if movie["revenue"] else "?"
if movie["runtime"] is not None:
duration = divmod(movie["runtime"], 60)
diff --git a/bot/exts/fun/quack.py b/bot/exts/fun/quack.py
index bb0cd731..9bb024fc 100644
--- a/bot/exts/fun/quack.py
+++ b/bot/exts/fun/quack.py
@@ -1,6 +1,6 @@
import logging
import random
-from typing import Literal, Optional
+from typing import Literal
import discord
from discord.ext import commands
@@ -8,7 +8,7 @@ from discord.ext import commands
from bot.bot import Bot
from bot.constants import Colours, NEGATIVE_REPLIES
-API_URL = 'https://quackstack.pythondiscord.com'
+API_URL = "https://quackstack.pythondiscord.com"
log = logging.getLogger(__name__)
@@ -25,7 +25,7 @@ class Quackstack(commands.Cog):
ctx: commands.Context,
ducktype: Literal["duck", "manduck"] = "duck",
*,
- seed: Optional[str] = None
+ seed: str | None = None
) -> None:
"""
Use the Quackstack API to generate a random duck.
diff --git a/bot/exts/fun/snakes/_snakes_cog.py b/bot/exts/fun/snakes/_snakes_cog.py
index 892a3dd2..eca462c6 100644
--- a/bot/exts/fun/snakes/_snakes_cog.py
+++ b/bot/exts/fun/snakes/_snakes_cog.py
@@ -9,7 +9,7 @@ import textwrap
import urllib
from functools import partial
from io import BytesIO
-from typing import Any, Optional
+from typing import Any
import async_timeout
from PIL import Image, ImageDraw, ImageFont
@@ -274,7 +274,7 @@ class Snakes(Cog):
return message
- async def _fetch(self, url: str, params: Optional[dict] = None) -> dict:
+ async def _fetch(self, url: str, params: dict | None = None) -> dict:
"""Asynchronous web request helper method."""
if params is None:
params = {}
@@ -518,52 +518,51 @@ class Snakes(Cog):
log.debug("Antidote timed out waiting for a reaction")
break # We're done, no reactions for the last 5 minutes
- if antidote_tries < 10:
- if antidote_guess_count < 4:
- if reaction.emoji in ANTIDOTE_EMOJI:
- antidote_guess_list.append(reaction.emoji)
- antidote_guess_count += 1
-
- if antidote_guess_count == 4: # Guesses complete
- antidote_guess_count = 0
- page_guess_list[antidote_tries] = " ".join(antidote_guess_list)
-
- # Now check guess
- for i in range(0, len(antidote_answer)):
- if antidote_guess_list[i] == antidote_answer[i]:
- guess_result.append(TICK_EMOJI)
- elif antidote_guess_list[i] in antidote_answer:
- guess_result.append(BLANK_EMOJI)
- else:
- guess_result.append(CROSS_EMOJI)
- guess_result.sort()
- page_result_list[antidote_tries] = " ".join(guess_result)
-
- # Rebuild the board
- board = []
- for i in range(0, 10):
- board.append(f"`{i+1:02d}` "
- f"{page_guess_list[i]} - "
- f"{page_result_list[i]}")
- board.append(EMPTY_UNICODE)
-
- # Remove Reactions
- for emoji in antidote_guess_list:
- await board_id.remove_reaction(emoji, user)
-
- if antidote_guess_list == antidote_answer:
- win = True
-
- antidote_tries += 1
- guess_result = []
- antidote_guess_list = []
-
- antidote_embed.clear_fields()
- antidote_embed.add_field(name=f"{10 - antidote_tries} "
- f"guesses remaining",
- value="\n".join(board))
- # Redisplay the board
- await board_id.edit(embed=antidote_embed)
+ if antidote_tries < 10 and antidote_guess_count < 4:
+ if reaction.emoji in ANTIDOTE_EMOJI:
+ antidote_guess_list.append(reaction.emoji)
+ antidote_guess_count += 1
+
+ if antidote_guess_count == 4: # Guesses complete
+ antidote_guess_count = 0
+ page_guess_list[antidote_tries] = " ".join(antidote_guess_list)
+
+ # Now check guess
+ for i in range(0, len(antidote_answer)):
+ if antidote_guess_list[i] == antidote_answer[i]:
+ guess_result.append(TICK_EMOJI)
+ elif antidote_guess_list[i] in antidote_answer:
+ guess_result.append(BLANK_EMOJI)
+ else:
+ guess_result.append(CROSS_EMOJI)
+ guess_result.sort()
+ page_result_list[antidote_tries] = " ".join(guess_result)
+
+ # Rebuild the board
+ board = []
+ for i in range(0, 10):
+ board.append(f"`{i+1:02d}` "
+ f"{page_guess_list[i]} - "
+ f"{page_result_list[i]}")
+ board.append(EMPTY_UNICODE)
+
+ # Remove Reactions
+ for emoji in antidote_guess_list:
+ await board_id.remove_reaction(emoji, user)
+
+ if antidote_guess_list == antidote_answer:
+ win = True
+
+ antidote_tries += 1
+ guess_result = []
+ antidote_guess_list = []
+
+ antidote_embed.clear_fields()
+ antidote_embed.add_field(name=f"{10 - antidote_tries} "
+ f"guesses remaining",
+ value="\n".join(board))
+ # Redisplay the board
+ await board_id.edit(embed=antidote_embed)
# Winning / Ending Screen
if win is True:
@@ -746,10 +745,10 @@ class Snakes(Cog):
await message.delete()
# Build and send the embed.
- my_snake_embed = Embed(description=":tada: Congrats! You hatched: **{0}**".format(snake_name))
+ my_snake_embed = Embed(description=f":tada: Congrats! You hatched: **{snake_name}**")
my_snake_embed.set_thumbnail(url=snake_image)
my_snake_embed.set_footer(
- text=" Owner: {0}#{1}".format(ctx.author.name, ctx.author.discriminator)
+ text=f" Owner: {ctx.author.name}#{ctx.author.discriminator}"
)
await ctx.send(embed=my_snake_embed)
@@ -832,7 +831,7 @@ class Snakes(Cog):
# Prepare a question.
question = random.choice(self.snake_quizzes)
answer = question["answerkey"]
- options = {key: question["options"][key] for key in ANSWERS_EMOJI.keys()}
+ options = {key: question["options"][key] for key in ANSWERS_EMOJI}
# Build and send the embed.
embed = Embed(
@@ -879,10 +878,7 @@ class Snakes(Cog):
snake_name = snake_name.split()[-1]
# If no name is provided, use whoever called the command.
- if name:
- user_name = name
- else:
- user_name = ctx.author.display_name
+ user_name = name if name else ctx.author.display_name
# Get the index of the vowel to slice the username at
user_slice_index = len(user_name)
@@ -1148,3 +1144,4 @@ class Snakes(Cog):
embed.description = "Could not generate the snake card! Please try again."
embed.title = random.choice(ERROR_REPLIES)
await ctx.send(embed=embed)
+ # endregion
diff --git a/bot/exts/fun/snakes/_utils.py b/bot/exts/fun/snakes/_utils.py
index 182fa9d9..5332cde2 100644
--- a/bot/exts/fun/snakes/_utils.py
+++ b/bot/exts/fun/snakes/_utils.py
@@ -6,7 +6,6 @@ import math
import random
from itertools import product
from pathlib import Path
-from typing import Union
from PIL import Image
from PIL.ImageDraw import ImageDraw
@@ -132,7 +131,7 @@ def lerp(t: float, a: float, b: float) -> float:
return a + t * (b - a)
-class PerlinNoiseFactory(object):
+class PerlinNoiseFactory:
"""
Callable that produces Perlin noise for an arbitrary point in an arbitrary number of dimensions.
@@ -396,7 +395,7 @@ class SnakeAndLaddersGame:
Listen for reactions until players have joined, and the game has been started.
"""
- def startup_event_check(reaction_: Reaction, user_: Union[User, Member]) -> bool:
+ def startup_event_check(reaction_: Reaction, user_: User | Member) -> bool:
"""Make sure that this reaction is what we want to operate on."""
return (
all((
@@ -445,14 +444,12 @@ class SnakeAndLaddersGame:
# Allow game author or non-playing moderation staff to cancel a waiting game
await self.cancel_game()
return
- else:
- await self.player_leave(user)
- elif reaction.emoji == START_EMOJI:
- if self.ctx.author == user:
- self.started = True
- await self.start_game(user)
- await startup.delete()
- break
+ await self.player_leave(user)
+ elif reaction.emoji == START_EMOJI and self.ctx.author == user:
+ self.started = True
+ await self.start_game(user)
+ await startup.delete()
+ break
await startup.remove_reaction(reaction.emoji, user)
@@ -461,7 +458,7 @@ class SnakeAndLaddersGame:
await self.cancel_game()
return # We're done, no reactions for the last 5 minutes
- async def _add_player(self, user: Union[User, Member]) -> None:
+ async def _add_player(self, user: User | Member) -> None:
"""Add player to game."""
self.players.append(user)
self.player_tiles[user.id] = 1
@@ -470,7 +467,7 @@ class SnakeAndLaddersGame:
im = Image.open(io.BytesIO(avatar_bytes)).resize((BOARD_PLAYER_SIZE, BOARD_PLAYER_SIZE))
self.avatar_images[user.id] = im
- async def player_join(self, user: Union[User, Member]) -> None:
+ async def player_join(self, user: User | Member) -> None:
"""
Handle players joining the game.
@@ -496,7 +493,7 @@ class SnakeAndLaddersGame:
delete_after=10
)
- async def player_leave(self, user: Union[User, Member]) -> bool:
+ async def player_leave(self, user: User | Member) -> bool:
"""
Handle players leaving the game.
@@ -531,17 +528,17 @@ class SnakeAndLaddersGame:
await self.channel.send("**Snakes and Ladders**: Game has been canceled.")
self._destruct()
- async def start_game(self, user: Union[User, Member]) -> None:
+ async def start_game(self, user: User | Member) -> None:
"""
Allow the game author to begin the game.
The game cannot be started if the game is in a waiting state.
"""
- if not user == self.author:
+ if user != self.author:
await self.channel.send(user.mention + " Only the author of the game can start it.", delete_after=10)
return
- if not self.state == "waiting":
+ if self.state != "waiting":
await self.channel.send(user.mention + " The game cannot be started at this time.", delete_after=10)
return
@@ -552,7 +549,7 @@ class SnakeAndLaddersGame:
async def start_round(self) -> None:
"""Begin the round."""
- def game_event_check(reaction_: Reaction, user_: Union[User, Member]) -> bool:
+ def game_event_check(reaction_: Reaction, user_: User | Member) -> bool:
"""Make sure that this reaction is what we want to operate on."""
return (
all((
@@ -626,8 +623,7 @@ class SnakeAndLaddersGame:
# Only allow non-playing moderation staff to cancel a running game
await self.cancel_game()
return
- else:
- is_surrendered = await self.player_leave(user)
+ is_surrendered = await self.player_leave(user)
await self.positions.remove_reaction(reaction.emoji, user)
@@ -645,7 +641,7 @@ class SnakeAndLaddersGame:
if not is_surrendered:
await self._complete_round()
- async def player_roll(self, user: Union[User, Member]) -> None:
+ async def player_roll(self, user: User | Member) -> None:
"""Handle the player's roll."""
if user.id not in self.player_tiles:
await self.channel.send(user.mention + " You are not in the match.", delete_after=10)
@@ -692,7 +688,7 @@ class SnakeAndLaddersGame:
await self.channel.send("**Snakes and Ladders**: " + winner.mention + " has won the game! :tada:")
self._destruct()
- def _check_winner(self) -> Union[User, Member]:
+ def _check_winner(self) -> User | Member:
"""Return a winning member if we're in the post-round state and there's a winner."""
if self.state != "post_round":
return None
@@ -717,6 +713,6 @@ class SnakeAndLaddersGame:
return x_level, y_level
@staticmethod
- def _is_moderator(user: Union[User, Member]) -> bool:
+ def _is_moderator(user: User | Member) -> bool:
"""Return True if the user is a Moderator."""
- return any(role.id in MODERATION_ROLES for role in getattr(user, 'roles', []))
+ return any(role.id in MODERATION_ROLES for role in getattr(user, "roles", []))
diff --git a/bot/exts/fun/space.py b/bot/exts/fun/space.py
index c688b281..3a666bfc 100644
--- a/bot/exts/fun/space.py
+++ b/bot/exts/fun/space.py
@@ -1,7 +1,7 @@
import logging
import random
-from datetime import date, datetime
-from typing import Any, Optional
+from datetime import UTC, date, datetime
+from typing import Any
from urllib.parse import urlencode
from discord import Embed
@@ -53,7 +53,7 @@ class Space(Cog):
await self.bot.invoke_help_command(ctx)
@space.command(name="apod")
- async def apod(self, ctx: Context, date: Optional[str]) -> None:
+ async def apod(self, ctx: Context, date: str | None) -> None:
"""
Get Astronomy Picture of Day from NASA API. Date is optional parameter, what formatting is YYYY-MM-DD.
@@ -63,13 +63,13 @@ class Space(Cog):
# Parse date to params, when provided. Show error message when invalid formatting
if date:
try:
- apod_date = datetime.strptime(date, "%Y-%m-%d").date()
+ apod_date = datetime.strptime(date, "%Y-%m-%d").replace(tzinfo=UTC).date()
except ValueError:
await ctx.send(f"Invalid date {date}. Please make sure your date is in format YYYY-MM-DD.")
return
- now = datetime.now().date()
- if APOD_MIN_DATE > apod_date or now < apod_date:
+ now = datetime.now(tz=UTC).date()
+ if apod_date < APOD_MIN_DATE or now < apod_date:
await ctx.send(f"Date must be between {APOD_MIN_DATE.isoformat()} and {now.isoformat()} (today).")
return
@@ -86,7 +86,7 @@ class Space(Cog):
)
@space.command(name="nasa")
- async def nasa(self, ctx: Context, *, search_term: Optional[str]) -> None:
+ async def nasa(self, ctx: Context, *, search_term: str | None) -> None:
"""Get random NASA information/facts + image. Support `search_term` parameter for more specific search."""
params = {
"media_type": "image"
@@ -111,11 +111,11 @@ class Space(Cog):
)
@space.command(name="epic")
- async def epic(self, ctx: Context, date: Optional[str]) -> None:
+ async def epic(self, ctx: Context, date: str | None) -> None:
"""Get a random image of the Earth from the NASA EPIC API. Support date parameter, format is YYYY-MM-DD."""
if date:
try:
- show_date = datetime.strptime(date, "%Y-%m-%d").date().isoformat()
+ show_date = datetime.strptime(date, "%Y-%m-%d").replace(tzinfo=UTC).date().isoformat()
except ValueError:
await ctx.send(f"Invalid date {date}. Please make sure your date is in format YYYY-MM-DD.")
return
@@ -147,7 +147,7 @@ class Space(Cog):
async def mars(
self,
ctx: Context,
- date: Optional[DateConverter],
+ date: DateConverter | None,
rover: str = "curiosity"
) -> None:
"""
@@ -158,10 +158,8 @@ class Space(Cog):
rover = rover.lower()
if rover not in self.rovers:
await ctx.send(
- (
- f"Invalid rover `{rover}`.\n"
- f"**Rovers:** `{'`, `'.join(f'{r.capitalize()}' for r in self.rovers)}`"
- )
+ f"Invalid rover `{rover}`.\n"
+ f"**Rovers:** `{'`, `'.join(f'{r.capitalize()}' for r in self.rovers)}`"
)
return
@@ -203,8 +201,8 @@ class Space(Cog):
async def fetch_from_nasa(
self,
endpoint: str,
- additional_params: Optional[dict[str, Any]] = None,
- base: Optional[str] = NASA_BASE_URL,
+ additional_params: dict[str, Any] | None = None,
+ base: str | None = NASA_BASE_URL,
use_api_key: bool = True
) -> dict[str, Any]:
"""Fetch information from NASA API, return result."""
@@ -219,7 +217,7 @@ class Space(Cog):
async with self.http_session.get(url=f"{base}/{endpoint}?{urlencode(params)}") as resp:
return await resp.json()
- def create_nasa_embed(self, title: str, description: str, image: str, footer: Optional[str] = "") -> Embed:
+ def create_nasa_embed(self, title: str, description: str, image: str, footer: str | None = "") -> Embed:
"""Generate NASA commands embeds. Required: title, description and image URL, footer (addition) is optional."""
return Embed(
title=title,
diff --git a/bot/exts/fun/tic_tac_toe.py b/bot/exts/fun/tic_tac_toe.py
index fa2a7531..d4ae7107 100644
--- a/bot/exts/fun/tic_tac_toe.py
+++ b/bot/exts/fun/tic_tac_toe.py
@@ -1,6 +1,6 @@
import asyncio
import random
-from typing import Callable, Optional, Union
+from collections.abc import Callable
import discord
from discord.ext.commands import Cog, Context, check, group
@@ -42,7 +42,7 @@ class Player:
self.ctx = ctx
self.symbol = symbol
- async def get_move(self, board: dict[int, str], msg: discord.Message) -> tuple[bool, Optional[int]]:
+ async def get_move(self, board: dict[int, str], msg: discord.Message) -> tuple[bool, int | None]:
"""
Get move from user.
@@ -106,7 +106,7 @@ class AI:
class Game:
"""Class that contains information and functions about Tic Tac Toe game."""
- def __init__(self, players: list[Union[Player, AI]], ctx: Context):
+ def __init__(self, players: list[Player | AI], ctx: Context):
self.players = players
self.ctx = ctx
self.channel = ctx.channel
@@ -125,13 +125,13 @@ class Game:
self.current = self.players[0]
self.next = self.players[1]
- self.winner: Optional[Union[Player, AI]] = None
- self.loser: Optional[Union[Player, AI]] = None
+ self.winner: Player | AI | None = None
+ self.loser: Player | AI | None = None
self.over = False
self.canceled = False
self.draw = False
- async def get_confirmation(self) -> tuple[bool, Optional[str]]:
+ async def get_confirmation(self) -> tuple[bool, str | None]:
"""
Ask does user want to play TicTacToe against requester. First player is always requester.
@@ -171,10 +171,10 @@ class Game:
await confirm_message.delete()
if reaction.emoji == Emojis.confirmation:
return True, None
- else:
- self.over = True
- self.canceled = True
- return False, "User declined"
+
+ self.over = True
+ self.canceled = True
+ return False, "User declined"
@staticmethod
async def add_reactions(msg: discord.Message) -> None:
@@ -186,7 +186,8 @@ class Game:
"""Get formatted tic-tac-toe board for message."""
board = list(self.board.values())
return "\n".join(
- (f"{board[line]} {board[line + 1]} {board[line + 2]}" for line in range(0, len(board), 3))
+ f"{board[line]} {board[line + 1]} {board[line + 2]}"
+ for line in range(0, len(board), 3)
)
async def play(self) -> None:
@@ -256,7 +257,7 @@ class TicTacToe(Cog):
@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: Optional[discord.User]) -> None:
+ async def tic_tac_toe(self, ctx: Context, opponent: discord.User | None) -> 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/fun/trivia_quiz.py b/bot/exts/fun/trivia_quiz.py
index 31652374..28cd4657 100644
--- a/bot/exts/fun/trivia_quiz.py
+++ b/bot/exts/fun/trivia_quiz.py
@@ -6,10 +6,10 @@ import random
import re
import string
from collections import defaultdict
+from collections.abc import Callable
from dataclasses import dataclass
-from datetime import datetime, timedelta
+from datetime import UTC, datetime, timedelta
from pathlib import Path
-from typing import Callable, Optional
import discord
from discord.ext import commands, tasks
@@ -249,7 +249,7 @@ class TriviaQuiz(commands.Cog):
wiki_questions = []
# trivia_quiz.json follows a pattern, every new category starts with the next century.
start_id = 501
- yesterday = datetime.strftime(datetime.now() - timedelta(1), '%Y/%m/%d')
+ yesterday = datetime.strftime(datetime.now(tz=UTC) - timedelta(1), "%Y/%m/%d")
while error_fetches < MAX_ERROR_FETCH_TRIES:
async with self.bot.http_session.get(url=WIKI_FEED_API_URL.format(date=yesterday)) as r:
@@ -267,7 +267,7 @@ class TriviaQuiz(commands.Cog):
# Normalize the wikipedia article title to remove all punctuations from it
for word in re.split(r"[\s-]", title := article["normalizedtitle"]):
cleaned_title = re.sub(
- rf'\b{word.strip(string.punctuation)}\b', word, title, flags=re.IGNORECASE
+ rf"\b{word.strip(string.punctuation)}\b", word, title, flags=re.IGNORECASE
)
# Since the extract contains the article name sometimes this would replace all the matching words
@@ -279,7 +279,7 @@ class TriviaQuiz(commands.Cog):
for word in re.split(r"[\s-]", cleaned_title):
word = word.strip(string.punctuation)
secret_word = r"\*" * len(word)
- question = re.sub(rf'\b{word}\b', f"**{secret_word}**", question, flags=re.IGNORECASE)
+ question = re.sub(rf"\b{word}\b", f"**{secret_word}**", question, flags=re.IGNORECASE)
formatted_article_question = {
"id": start_id,
@@ -307,7 +307,7 @@ class TriviaQuiz(commands.Cog):
return json.loads(p.read_text(encoding="utf-8"))
@commands.group(name="quiz", aliases=("trivia", "triviaquiz"), invoke_without_command=True)
- async def quiz_game(self, ctx: commands.Context, category: Optional[str], questions: Optional[int]) -> None:
+ async def quiz_game(self, ctx: commands.Context, category: str | None, questions: int | None) -> None:
"""
Start a quiz!
@@ -550,7 +550,7 @@ class TriviaQuiz(commands.Cog):
if self.game_status[ctx.channel.id]:
# Check if the author is the game starter or a moderator.
if ctx.author == self.game_owners[ctx.channel.id] or any(
- role.id in MODERATION_ROLES for role in getattr(ctx.author, 'roles', [])
+ role.id in MODERATION_ROLES for role in getattr(ctx.author, "roles", [])
):
self.game_status[ctx.channel.id] = False
del self.game_owners[ctx.channel.id]
diff --git a/bot/exts/fun/uwu.py b/bot/exts/fun/uwu.py
index 7a9d55d0..488c68f3 100644
--- a/bot/exts/fun/uwu.py
+++ b/bot/exts/fun/uwu.py
@@ -30,7 +30,7 @@ EMOJIS = [
"o.O",
"-.-",
">w<",
- "σωσ",
+ "σωσ", # noqa: RUF001
"òωó",
"ʘwʘ",
":3",
@@ -74,7 +74,7 @@ class Emoji:
return bot.get_emoji(self.uid) is not None
@classmethod
- def from_match(cls, match: tuple[str, str, str]) -> t.Optional['Emoji']:
+ def from_match(cls, match: tuple[str, str, str]) -> t.Optional["Emoji"]:
"""Creates an Emoji from a regex match tuple."""
if not match or len(match) != 3 or not match[2].isdecimal():
return None
@@ -155,7 +155,7 @@ class Uwu(Cog):
return input_string
@commands.command(name="uwu", aliases=("uwuwize", "uwuify",))
- async def uwu_command(self, ctx: Context, *, text: t.Optional[str] = None) -> None:
+ async def uwu_command(self, ctx: Context, *, text: str | None = None) -> None:
"""
Echo an uwuified version the passed text.
diff --git a/bot/exts/fun/wonder_twins.py b/bot/exts/fun/wonder_twins.py
index 0c5b0a76..9385780d 100644
--- a/bot/exts/fun/wonder_twins.py
+++ b/bot/exts/fun/wonder_twins.py
@@ -11,8 +11,8 @@ class WonderTwins(Cog):
"""Cog for a Wonder Twins inspired command."""
def __init__(self):
- with open(Path.cwd() / "bot" / "resources" / "fun" / "wonder_twins.yaml", "r", encoding="utf-8") as f:
- info = yaml.load(f, Loader=yaml.FullLoader)
+ with open(Path.cwd() / "bot" / "resources" / "fun" / "wonder_twins.yaml", encoding="utf-8") as f:
+ info = yaml.safe_load(f)
self.water_types = info["water_types"]
self.objects = info["objects"]
self.adjectives = info["adjectives"]
diff --git a/bot/exts/fun/xkcd.py b/bot/exts/fun/xkcd.py
index 380c3c80..7b34795c 100644
--- a/bot/exts/fun/xkcd.py
+++ b/bot/exts/fun/xkcd.py
@@ -1,7 +1,6 @@
import logging
import re
from random import randint
-from typing import Optional, Union
from discord import Embed
from discord.ext import tasks
@@ -21,7 +20,7 @@ class XKCD(Cog):
def __init__(self, bot: Bot):
self.bot = bot
- self.latest_comic_info: dict[str, Union[str, int]] = {}
+ self.latest_comic_info: dict[str, str | int] = {}
self.get_latest_comic_info.start()
def cog_unload(self) -> None:
@@ -38,7 +37,7 @@ class XKCD(Cog):
log.debug(f"Failed to get latest XKCD comic information. Status code {resp.status}")
@command(name="xkcd")
- async def fetch_xkcd_comics(self, ctx: Context, comic: Optional[str]) -> None:
+ async def fetch_xkcd_comics(self, ctx: Context, comic: str | None) -> None:
"""
Getting an xkcd comic's information along with the image.