diff options
| author | 2023-05-09 16:01:01 +0100 | |
|---|---|---|
| committer | 2023-05-09 16:01:01 +0100 | |
| commit | c3e23e60278d34658f801bd7d7ed721d5a272637 (patch) | |
| tree | e159a0fae7850d706d713cf2b49dfed2140ce655 /bot/exts/events | |
| parent | Bump sentry-sdk from 1.21.1 to 1.22.1 (#1273) (diff) | |
| parent | Move unshared contants inside modules (diff) | |
Merge pull request #1270 from python-discord/migrate-to-ruff
Migrate to ruff
Diffstat (limited to 'bot/exts/events')
| -rw-r--r-- | bot/exts/events/hacktoberfest/hacktober_issue_finder.py (renamed from bot/exts/events/hacktoberfest/hacktober-issue-finder.py) | 9 | ||||
| -rw-r--r-- | bot/exts/events/hacktoberfest/hacktoberstats.py | 37 | ||||
| -rw-r--r-- | bot/exts/events/hacktoberfest/timeleft.py | 14 | ||||
| -rw-r--r-- | bot/exts/events/trivianight/_game.py | 23 | ||||
| -rw-r--r-- | bot/exts/events/trivianight/_questions.py | 12 | ||||
| -rw-r--r-- | bot/exts/events/trivianight/trivianight.py | 16 |
6 files changed, 51 insertions, 60 deletions
diff --git a/bot/exts/events/hacktoberfest/hacktober-issue-finder.py b/bot/exts/events/hacktoberfest/hacktober_issue_finder.py index 4f7bef5d..69aa3924 100644 --- a/bot/exts/events/hacktoberfest/hacktober-issue-finder.py +++ b/bot/exts/events/hacktoberfest/hacktober_issue_finder.py @@ -1,7 +1,6 @@ -import datetime import logging import random -from typing import Optional +from datetime import UTC, datetime import discord from discord.ext import commands @@ -28,9 +27,9 @@ class HacktoberIssues(commands.Cog): def __init__(self, bot: Bot): self.bot = bot self.cache_normal = None - self.cache_timer_normal = datetime.datetime(1, 1, 1) + self.cache_timer_normal = datetime(1, 1, 1, tzinfo=UTC) self.cache_beginner = None - self.cache_timer_beginner = datetime.datetime(1, 1, 1) + self.cache_timer_beginner = datetime(1, 1, 1, tzinfo=UTC) @in_month(Month.OCTOBER) @commands.command() @@ -49,7 +48,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) -> dict | None: """Get a list of the python issues with the label 'hacktoberfest' from the Github api.""" if option == "beginner": if (ctx.message.created_at.replace(tzinfo=None) - self.cache_timer_beginner).seconds <= 60: diff --git a/bot/exts/events/hacktoberfest/hacktoberstats.py b/bot/exts/events/hacktoberfest/hacktoberstats.py index 5bfac93f..c7fd3601 100644 --- a/bot/exts/events/hacktoberfest/hacktoberstats.py +++ b/bot/exts/events/hacktoberfest/hacktoberstats.py @@ -2,8 +2,7 @@ import logging import random import re from collections import Counter -from datetime import datetime, timedelta -from typing import Optional, Union +from datetime import UTC, datetime, timedelta from urllib.parse import quote_plus import discord @@ -16,7 +15,7 @@ from bot.utils.decorators import in_month log = logging.getLogger(__name__) -CURRENT_YEAR = datetime.now().year # Used to construct GH API query +CURRENT_YEAR = datetime.now(tz=UTC).year # Used to construct GH API query PRS_FOR_SHIRT = 4 # Minimum number of PRs before a shirt is awarded REVIEW_DAYS = 14 # number of days needed after PR can be mature @@ -185,7 +184,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) -> list[dict] | None: """ Query GitHub's API for PRs created during the month of October by github_username. @@ -234,9 +233,8 @@ class HacktoberStats(commands.Cog): # Ignore logging non-existent users or users we do not have permission to see if api_message == GITHUB_NONEXISTENT_USER_MESSAGE: log.debug(f"No GitHub user found named '{github_username}'") - return - else: - log.error(f"GitHub API request for '{github_username}' failed with message: {api_message}") + return None + log.error(f"GitHub API request for '{github_username}' failed with message: {api_message}") return [] # No October PRs were found due to error if jsonresp["total_count"] == 0: @@ -246,7 +244,7 @@ class HacktoberStats(commands.Cog): logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") outlist = [] # list of pr information dicts that will get returned - oct3 = datetime(int(CURRENT_YEAR), 10, 3, 23, 59, 59, tzinfo=None) + oct3 = datetime(int(CURRENT_YEAR), 10, 3, 23, 59, 59, tzinfo=UTC) hackto_topics = {} # cache whether each repo has the appropriate topic (bool values) for item in jsonresp["items"]: shortname = self._get_shortname(item["repository_url"]) @@ -255,15 +253,14 @@ class HacktoberStats(commands.Cog): "repo_shortname": shortname, "created_at": datetime.strptime( item["created_at"], "%Y-%m-%dT%H:%M:%SZ" - ), + ).replace(tzinfo=UTC), "number": item["number"] } # If the PR has 'invalid' or 'spam' labels, the PR must be # either merged or approved for it to be included - if self._has_label(item, ["invalid", "spam"]): - if not await self._is_accepted(itemdict): - continue + if self._has_label(item, ["invalid", "spam"]) and not await self._is_accepted(itemdict): + continue # PRs before oct 3 no need to check for topics # continue the loop if 'hacktoberfest-accepted' is labelled then @@ -302,7 +299,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: list[str] | str) -> bool: """ Check if a PR has label 'labels'. @@ -313,7 +310,7 @@ class HacktoberStats(commands.Cog): return False if isinstance(labels, str) and any(label["name"].casefold() == labels for label in pr["labels"]): return True - for item in labels: + for item in labels: # noqa: SIM110 if any(label["name"].casefold() == item for label in pr["labels"]): return True return False @@ -350,10 +347,7 @@ class HacktoberStats(commands.Cog): return False # loop through reviews and check for approval - for item in jsonresp2: - if item.get("status") == "APPROVED": - return True - return False + return any(item.get("status") == "APPROVED" for item in jsonresp2) @staticmethod def _get_shortname(in_url: str) -> str: @@ -378,8 +372,8 @@ class HacktoberStats(commands.Cog): PRs that are accepted must either be merged, approved, or labelled 'hacktoberfest-accepted. """ - now = datetime.now() - oct3 = datetime(CURRENT_YEAR, 10, 3, 23, 59, 59, tzinfo=None) + now = datetime.now(tz=UTC) + oct3 = datetime(CURRENT_YEAR, 10, 3, 23, 59, 59, tzinfo=UTC) in_review = [] accepted = [] for pr in prs: @@ -420,8 +414,7 @@ class HacktoberStats(commands.Cog): """Return "contribution" or "contributions" based on the value of n.""" if n == 1: return "contribution" - else: - return "contributions" + return "contributions" @staticmethod def _author_mention_from_context(ctx: commands.Context) -> tuple[str, str]: diff --git a/bot/exts/events/hacktoberfest/timeleft.py b/bot/exts/events/hacktoberfest/timeleft.py index f470e932..8f46d798 100644 --- a/bot/exts/events/hacktoberfest/timeleft.py +++ b/bot/exts/events/hacktoberfest/timeleft.py @@ -1,5 +1,5 @@ import logging -from datetime import datetime +from datetime import UTC, datetime from discord.ext import commands @@ -9,25 +9,25 @@ log = logging.getLogger(__name__) class TimeLeft(commands.Cog): - """A Cog that tells you how long left until Hacktober is over!""" + """A Cog that tells users how long left until Hacktober is over!""" def in_hacktober(self) -> bool: """Return True if the current time is within Hacktoberfest.""" _, end, start = self.load_date() - now = datetime.utcnow() + now = datetime.now(tz=UTC) return start <= now <= end @staticmethod 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() + """Return of a tuple of the current time and the end and start times of the next Hacktober.""" + now = datetime.now(tz=UTC) year = now.year if now.month > 10: year += 1 - end = datetime(year, 11, 1, 12) # November 1st 12:00 (UTC-12:00) - start = datetime(year, 9, 30, 10) # September 30th 10:00 (UTC+14:00) + end = datetime(year, 11, 1, 12, tzinfo=UTC) # November 1st 12:00 (UTC-12:00) + start = datetime(year, 9, 30, 10, tzinfo=UTC) # September 30th 10:00 (UTC+14:00) return now, end, start @commands.command() diff --git a/bot/exts/events/trivianight/_game.py b/bot/exts/events/trivianight/_game.py index 8b012a17..15126f60 100644 --- a/bot/exts/events/trivianight/_game.py +++ b/bot/exts/events/trivianight/_game.py @@ -1,7 +1,8 @@ import time +from collections.abc import Iterable from random import randrange from string import ascii_uppercase -from typing import Iterable, NamedTuple, Optional, TypedDict +from typing import NamedTuple, TypedDict DEFAULT_QUESTION_POINTS = 10 DEFAULT_QUESTION_TIME = 20 @@ -14,8 +15,8 @@ class QuestionData(TypedDict): description: str answers: list[str] correct: str - points: Optional[int] - time: Optional[int] + points: int | None + time: int | None class UserGuess(NamedTuple): @@ -26,15 +27,15 @@ class UserGuess(NamedTuple): elapsed: float -class QuestionClosed(RuntimeError): +class QuestionClosedError(RuntimeError): """Exception raised when the question is not open for guesses anymore.""" -class AlreadyUpdated(RuntimeError): +class AlreadyUpdatedError(RuntimeError): """Exception raised when the user has already updated their guess once.""" -class AllQuestionsVisited(RuntimeError): +class AllQuestionsVisitedError(RuntimeError): """Exception raised when all of the questions have been visited.""" @@ -90,10 +91,10 @@ class Question: def _update_guess(self, user: int, answer: str) -> UserGuess: """Update an already existing guess.""" if self._started is None: - raise QuestionClosed("Question is not open for answers.") + raise QuestionClosedError("Question is not open for answers.") if self._guesses[user][1] is False: - raise AlreadyUpdated(f"User({user}) has already updated their guess once.") + raise AlreadyUpdatedError(f"User({user}) has already updated their guess once.") self._guesses[user] = (answer, False, time.perf_counter() - self._started) return self._guesses[user] @@ -104,7 +105,7 @@ class Question: return self._update_guess(user, answer) if self._started is None: - raise QuestionClosed("Question is not open for answers.") + raise QuestionClosedError("Question is not open for answers.") self._guesses[user] = (answer, True, time.perf_counter() - self._started) return self._guesses[user] @@ -126,7 +127,7 @@ class TriviaNightGame: self._questions = [Question(q) for q in data] # A copy of the questions to keep for `.trivianight list` self._all_questions = list(self._questions) - self.current_question: Optional[Question] = None + self.current_question: Question | None = None self._points = {} self._speed = {} @@ -148,7 +149,7 @@ class TriviaNightGame: except IndexError: raise ValueError(f"Question number {number} does not exist.") elif len(self._questions) == 0: - raise AllQuestionsVisited("All of the questions have been visited.") + raise AllQuestionsVisitedError("All of the questions have been visited.") else: question = self._questions.pop(randrange(len(self._questions))) diff --git a/bot/exts/events/trivianight/_questions.py b/bot/exts/events/trivianight/_questions.py index 5f1046dc..a0dd545e 100644 --- a/bot/exts/events/trivianight/_questions.py +++ b/bot/exts/events/trivianight/_questions.py @@ -7,7 +7,7 @@ from discord.ui import Button, View from bot.constants import Colours, NEGATIVE_REPLIES -from ._game import AlreadyUpdated, Question, QuestionClosed +from ._game import AlreadyUpdatedError, Question, QuestionClosedError from ._scoreboard import Scoreboard @@ -29,7 +29,7 @@ class AnswerButton(Button): """ try: guess = self.question.guess(interaction.user.id, self.label) - except AlreadyUpdated: + except AlreadyUpdatedError: await interaction.response.send_message( embed=Embed( title=choice(NEGATIVE_REPLIES), @@ -39,7 +39,7 @@ class AnswerButton(Button): ephemeral=True ) return - except QuestionClosed: + except QuestionClosedError: await interaction.response.send_message( embed=Embed( title=choice(NEGATIVE_REPLIES), @@ -91,7 +91,7 @@ class QuestionView(View): - text: A string that represents the question description to 'unicodeify' """ return "".join( - f"{letter}\u200b" if letter not in ('\n', '\t', '`', 'p', 'y') else letter + f"{letter}\u200b" if letter not in ("\n", "\t", "`", "p", "y") else letter for idx, letter in enumerate(text) ) @@ -127,13 +127,13 @@ class QuestionView(View): if len(guesses) != 0: answers_chosen = { answer_choice: len( - tuple(filter(lambda x: x[0] == answer_choice, guesses.values())) # noqa: B023 + tuple(filter(lambda x: x[0] == answer_choice, guesses.values())) ) for answer_choice in labels } answers_chosen = dict( - sorted(list(answers_chosen.items()), key=lambda item: item[1], reverse=True) + sorted(answers_chosen.items(), key=lambda item: item[1], reverse=True) ) for answer, people_answered in answers_chosen.items(): diff --git a/bot/exts/events/trivianight/trivianight.py b/bot/exts/events/trivianight/trivianight.py index 10435551..f90d32e0 100644 --- a/bot/exts/events/trivianight/trivianight.py +++ b/bot/exts/events/trivianight/trivianight.py @@ -1,7 +1,6 @@ import asyncio from json import JSONDecodeError, loads from random import choice -from typing import Optional from discord import Embed from discord.ext import commands @@ -10,7 +9,7 @@ from bot.bot import Bot from bot.constants import Colours, NEGATIVE_REPLIES, POSITIVE_REPLIES, Roles from bot.utils.pagination import LinePaginator -from ._game import AllQuestionsVisited, TriviaNightGame +from ._game import AllQuestionsVisitedError, TriviaNightGame from ._questions import QuestionView from ._scoreboard import Scoreboard @@ -23,8 +22,8 @@ class TriviaNightCog(commands.Cog): def __init__(self, bot: Bot): self.bot = bot - self.game: Optional[TriviaNightGame] = None - self.scoreboard: Optional[Scoreboard] = None + self.game: TriviaNightGame | None = None + self.scoreboard: Scoreboard | None = None self.question_closed: asyncio.Event = None @commands.group(aliases=["tn"], invoke_without_command=True) @@ -46,7 +45,7 @@ class TriviaNightCog(commands.Cog): @trivianight.command() @commands.has_any_role(*TRIVIA_NIGHT_ROLES) - async def load(self, ctx: commands.Context, *, to_load: Optional[str]) -> None: + async def load(self, ctx: commands.Context, *, to_load: str | None) -> None: """ Loads a JSON file from the provided attachment or argument. @@ -76,8 +75,7 @@ class TriviaNightCog(commands.Cog): elif not to_load: raise commands.BadArgument("You didn't attach an attachment nor link a message!") elif ( - to_load.startswith("https://discord.com/channels") - or to_load.startswith("https://discordapp.com/channels") + to_load.startswith(("https://discord.com/channels", "https://discordapp.com/channels")) ): channel_id, message_id = to_load.split("/")[-2:] channel = await ctx.guild.fetch_channel(int(channel_id)) @@ -107,7 +105,7 @@ class TriviaNightCog(commands.Cog): await ctx.send(embed=success_embed) - @trivianight.command(aliases=('next',)) + @trivianight.command(aliases=("next",)) @commands.has_any_role(*TRIVIA_NIGHT_ROLES) async def question(self, ctx: commands.Context, question_number: str = None) -> None: """ @@ -135,7 +133,7 @@ class TriviaNightCog(commands.Cog): try: next_question = self.game.next_question(question_number) - except AllQuestionsVisited: + except AllQuestionsVisitedError: error_embed = Embed( title=choice(NEGATIVE_REPLIES), description="All of the questions have been used.", |