diff options
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.",  |