diff options
Diffstat (limited to 'bot/exts/evergreen/snakes')
| -rw-r--r-- | bot/exts/evergreen/snakes/__init__.py | 7 | ||||
| -rw-r--r-- | bot/exts/evergreen/snakes/_converter.py | 12 | ||||
| -rw-r--r-- | bot/exts/evergreen/snakes/_snakes_cog.py | 297 | ||||
| -rw-r--r-- | bot/exts/evergreen/snakes/_utils.py | 34 | 
4 files changed, 175 insertions, 175 deletions
diff --git a/bot/exts/evergreen/snakes/__init__.py b/bot/exts/evergreen/snakes/__init__.py index bc42f0c2..7740429b 100644 --- a/bot/exts/evergreen/snakes/__init__.py +++ b/bot/exts/evergreen/snakes/__init__.py @@ -1,12 +1,11 @@  import logging -from discord.ext import commands - +from bot.bot import Bot  from bot.exts.evergreen.snakes._snakes_cog import Snakes  log = logging.getLogger(__name__) -def setup(bot: commands.Bot) -> None: -    """Snakes Cog load.""" +def setup(bot: Bot) -> None: +    """Load the Snakes Cog."""      bot.add_cog(Snakes(bot)) diff --git a/bot/exts/evergreen/snakes/_converter.py b/bot/exts/evergreen/snakes/_converter.py index eee248cf..0ca10d6c 100644 --- a/bot/exts/evergreen/snakes/_converter.py +++ b/bot/exts/evergreen/snakes/_converter.py @@ -24,8 +24,8 @@ class Snake(Converter):          await self.build_list()          name = name.lower() -        if name == 'python': -            return 'Python (programming language)' +        if name == "python": +            return "Python (programming language)"          def get_potential(iterable: Iterable, *, threshold: int = 80) -> List[str]:              nonlocal name @@ -47,12 +47,12 @@ class Snake(Converter):          if name.lower() in self.special_cases:              return self.special_cases.get(name.lower(), name.lower()) -        names = {snake['name']: snake['scientific'] for snake in self.snakes} +        names = {snake["name"]: snake["scientific"] for snake in self.snakes}          all_names = names.keys() | names.values()          timeout = len(all_names) * (3 / 4)          embed = discord.Embed( -            title='Found multiple choices. Please choose the correct one.', colour=0x59982F) +            title="Found multiple choices. Please choose the correct one.", colour=0x59982F)          embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar_url)          name = await disambiguate(ctx, get_potential(all_names), timeout=timeout, embed=embed) @@ -70,7 +70,7 @@ class Snake(Converter):          if cls.special_cases is None:              with (SNAKE_RESOURCES / "special_snakes.json").open(encoding="utf8") as snakefile:                  special_cases = json.load(snakefile) -            cls.special_cases = {snake['name'].lower(): snake for snake in special_cases} +            cls.special_cases = {snake["name"].lower(): snake for snake in special_cases}      @classmethod      async def random(cls) -> str: @@ -81,5 +81,5 @@ class Snake(Converter):          so I can get it from here.          """          await cls.build_list() -        names = [snake['scientific'] for snake in cls.snakes] +        names = [snake["scientific"] for snake in cls.snakes]          return random.choice(names) diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index 3732b559..62795aef 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -9,15 +9,15 @@ import textwrap  import urllib  from functools import partial  from io import BytesIO -from typing import Any, Dict, List +from typing import Any, Dict, List, Optional -import aiohttp  import async_timeout  from PIL import Image, ImageDraw, ImageFont  from discord import Colour, Embed, File, Member, Message, Reaction  from discord.errors import HTTPException -from discord.ext.commands import Bot, Cog, CommandError, Context, bot_has_permissions, group +from discord.ext.commands import Cog, CommandError, Context, bot_has_permissions, group +from bot.bot import Bot  from bot.constants import ERROR_REPLIES, Tokens  from bot.exts.evergreen.snakes import _utils as utils  from bot.exts.evergreen.snakes._converter import Snake @@ -143,8 +143,8 @@ class Snakes(Cog):      https://github.com/python-discord/code-jam-1      """ -    wiki_brief = re.compile(r'(.*?)(=+ (.*?) =+)', flags=re.DOTALL) -    valid_image_extensions = ('gif', 'png', 'jpeg', 'jpg', 'webp') +    wiki_brief = re.compile(r"(.*?)(=+ (.*?) =+)", flags=re.DOTALL) +    valid_image_extensions = ("gif", "png", "jpeg", "jpg", "webp")      def __init__(self, bot: Bot):          self.active_sal = {} @@ -183,28 +183,28 @@ class Snakes(Cog):          # Get the size of the snake icon, configure the height of the image box (yes, it changes)          icon_width = 347  # Hardcoded, not much i can do about that          icon_height = int((icon_width / snake.width) * snake.height) -        frame_copies = icon_height // CARD['frame'].height + 1 +        frame_copies = icon_height // CARD["frame"].height + 1          snake.thumbnail((icon_width, icon_height))          # Get the dimensions of the final image -        main_height = icon_height + CARD['top'].height + CARD['bottom'].height -        main_width = CARD['frame'].width +        main_height = icon_height + CARD["top"].height + CARD["bottom"].height +        main_width = CARD["frame"].width          # Start creating the foreground          foreground = Image.new("RGBA", (main_width, main_height), (0, 0, 0, 0)) -        foreground.paste(CARD['top'], (0, 0)) +        foreground.paste(CARD["top"], (0, 0))          # Generate the frame borders to the correct height          for offset in range(frame_copies): -            position = (0, CARD['top'].height + offset * CARD['frame'].height) -            foreground.paste(CARD['frame'], position) +            position = (0, CARD["top"].height + offset * CARD["frame"].height) +            foreground.paste(CARD["frame"], position)          # Add the image and bottom part of the image -        foreground.paste(snake, (36, CARD['top'].height))  # Also hardcoded :( -        foreground.paste(CARD['bottom'], (0, CARD['top'].height + icon_height)) +        foreground.paste(snake, (36, CARD["top"].height))  # Also hardcoded :( +        foreground.paste(CARD["bottom"], (0, CARD["top"].height + icon_height))          # Setup the background -        back = random.choice(CARD['backs']) +        back = random.choice(CARD["backs"])          back_copies = main_height // back.height + 1          full_image = Image.new("RGBA", (main_width, main_height), (0, 0, 0, 0)) @@ -216,11 +216,11 @@ class Snakes(Cog):          full_image.paste(foreground, (0, 0), foreground)          # Get the first two sentences of the info -        description = '.'.join(content['info'].split(".")[:2]) + '.' +        description = ".".join(content["info"].split(".")[:2]) + "."          # Setup positioning variables          margin = 36 -        offset = CARD['top'].height + icon_height + margin +        offset = CARD["top"].height + icon_height + margin          # Create blank rectangle image which will be behind the text          rectangle = Image.new( @@ -242,12 +242,12 @@ class Snakes(Cog):          # Draw the text onto the final image          draw = ImageDraw.Draw(full_image)          for line in textwrap.wrap(description, 36): -            draw.text([margin + 4, offset], line, font=CARD['font']) -            offset += CARD['font'].getsize(line)[1] +            draw.text([margin + 4, offset], line, font=CARD["font"]) +            offset += CARD["font"].getsize(line)[1]          # Get the image contents as a BufferIO object          buffer = BytesIO() -        full_image.save(buffer, 'PNG') +        full_image.save(buffer, "PNG")          buffer.seek(0)          return buffer @@ -275,13 +275,13 @@ class Snakes(Cog):          return message -    async def _fetch(self, session: aiohttp.ClientSession, url: str, params: dict = None) -> dict: +    async def _fetch(self, url: str, params: Optional[dict] = None) -> dict:          """Asynchronous web request helper method."""          if params is None:              params = {}          async with async_timeout.timeout(10): -            async with session.get(url, params=params) as response: +            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: @@ -309,96 +309,95 @@ class Snakes(Cog):          """          snake_info = {} -        async with aiohttp.ClientSession() as session: -            params = { -                'format': 'json', -                'action': 'query', -                'list': 'search', -                'srsearch': name, -                'utf8': '', -                'srlimit': '1', -            } - -            json = await self._fetch(session, URL, params=params) - -            # Wikipedia does have a error page -            try: -                pageid = json["query"]["search"][0]["pageid"] -            except KeyError: -                # Wikipedia error page ID(?) -                pageid = 41118 -            except IndexError: -                return None - -            params = { -                'format': 'json', -                'action': 'query', -                'prop': 'extracts|images|info', -                'exlimit': 'max', -                'explaintext': '', -                'inprop': 'url', -                'pageids': pageid -            } +        params = { +            "format": "json", +            "action": "query", +            "list": "search", +            "srsearch": name, +            "utf8": "", +            "srlimit": "1", +        } -            json = await self._fetch(session, URL, params=params) +        json = await self._fetch(URL, params=params) -            # Constructing dict - handle exceptions later -            try: -                snake_info["title"] = json["query"]["pages"][f"{pageid}"]["title"] -                snake_info["extract"] = json["query"]["pages"][f"{pageid}"]["extract"] -                snake_info["images"] = json["query"]["pages"][f"{pageid}"]["images"] -                snake_info["fullurl"] = json["query"]["pages"][f"{pageid}"]["fullurl"] -                snake_info["pageid"] = json["query"]["pages"][f"{pageid}"]["pageid"] -            except KeyError: -                snake_info["error"] = True - -            if snake_info["images"]: -                i_url = 'https://commons.wikimedia.org/wiki/Special:FilePath/' -                image_list = [] -                map_list = [] -                thumb_list = [] - -                # Wikipedia has arbitrary images that are not snakes -                banned = [ -                    'Commons-logo.svg', -                    'Red%20Pencil%20Icon.png', -                    'distribution', -                    'The%20Death%20of%20Cleopatra%20arthur.jpg', -                    'Head%20of%20holotype', -                    'locator', -                    'Woma.png', -                    '-map.', -                    '.svg', -                    'ange.', -                    'Adder%20(PSF).png' -                ] - -                for image in snake_info["images"]: -                    # Images come in the format of `File:filename.extension` -                    file, sep, filename = image["title"].partition(':') -                    filename = filename.replace(" ", "%20")  # Wikipedia returns good data! - -                    if not filename.startswith('Map'): -                        if any(ban in filename for ban in banned): -                            pass -                        else: -                            image_list.append(f"{i_url}{filename}") -                            thumb_list.append(f"{i_url}{filename}?width=100") +        # Wikipedia does have a error page +        try: +            pageid = json["query"]["search"][0]["pageid"] +        except KeyError: +            # Wikipedia error page ID(?) +            pageid = 41118 +        except IndexError: +            return None + +        params = { +            "format": "json", +            "action": "query", +            "prop": "extracts|images|info", +            "exlimit": "max", +            "explaintext": "", +            "inprop": "url", +            "pageids": pageid +        } + +        json = await self._fetch(URL, params=params) + +        # Constructing dict - handle exceptions later +        try: +            snake_info["title"] = json["query"]["pages"][f"{pageid}"]["title"] +            snake_info["extract"] = json["query"]["pages"][f"{pageid}"]["extract"] +            snake_info["images"] = json["query"]["pages"][f"{pageid}"]["images"] +            snake_info["fullurl"] = json["query"]["pages"][f"{pageid}"]["fullurl"] +            snake_info["pageid"] = json["query"]["pages"][f"{pageid}"]["pageid"] +        except KeyError: +            snake_info["error"] = True + +        if snake_info["images"]: +            i_url = "https://commons.wikimedia.org/wiki/Special:FilePath/" +            image_list = [] +            map_list = [] +            thumb_list = [] + +            # Wikipedia has arbitrary images that are not snakes +            banned = [ +                "Commons-logo.svg", +                "Red%20Pencil%20Icon.png", +                "distribution", +                "The%20Death%20of%20Cleopatra%20arthur.jpg", +                "Head%20of%20holotype", +                "locator", +                "Woma.png", +                "-map.", +                ".svg", +                "ange.", +                "Adder%20(PSF).png" +            ] + +            for image in snake_info["images"]: +                # Images come in the format of `File:filename.extension` +                file, sep, filename = image["title"].partition(":") +                filename = filename.replace(" ", "%20")  # Wikipedia returns good data! + +                if not filename.startswith("Map"): +                    if any(ban in filename for ban in banned): +                        pass                      else: -                        map_list.append(f"{i_url}{filename}") +                        image_list.append(f"{i_url}{filename}") +                        thumb_list.append(f"{i_url}{filename}?width=100") +                else: +                    map_list.append(f"{i_url}{filename}") -            snake_info["image_list"] = image_list -            snake_info["map_list"] = map_list -            snake_info["thumb_list"] = thumb_list -            snake_info["name"] = name +        snake_info["image_list"] = image_list +        snake_info["map_list"] = map_list +        snake_info["thumb_list"] = thumb_list +        snake_info["name"] = name -            match = self.wiki_brief.match(snake_info['extract']) -            info = match.group(1) if match else None +        match = self.wiki_brief.match(snake_info["extract"]) +        info = match.group(1) if match else None -            if info: -                info = info.replace("\n", "\n\n")  # Give us some proper paragraphs. +        if info: +            info = info.replace("\n", "\n\n")  # Give us some proper paragraphs. -            snake_info["info"] = info +        snake_info["info"] = info          return snake_info @@ -423,7 +422,7 @@ class Snakes(Cog):          try:              reaction, user = await ctx.bot.wait_for("reaction_add", timeout=45.0, check=predicate)          except asyncio.TimeoutError: -            await ctx.channel.send(f"You took too long. The correct answer was **{options[answer]}**.") +            await ctx.send(f"You took too long. The correct answer was **{options[answer]}**.")              await message.clear_reactions()              return @@ -438,13 +437,13 @@ class Snakes(Cog):      # endregion      # region: Commands -    @group(name='snakes', aliases=('snake',), invoke_without_command=True) +    @group(name="snakes", aliases=("snake",), invoke_without_command=True)      async def snakes_group(self, ctx: Context) -> None:          """Commands from our first code jam."""          await invoke_help_command(ctx)      @bot_has_permissions(manage_messages=True) -    @snakes_group.command(name='antidote') +    @snakes_group.command(name="antidote")      @locked()      async def antidote_command(self, ctx: Context) -> None:          """ @@ -586,7 +585,7 @@ class Snakes(Cog):          log.debug("Ending pagination and removing all reactions...")          await board_id.clear_reactions() -    @snakes_group.command(name='draw') +    @snakes_group.command(name="draw")      async def draw_command(self, ctx: Context) -> None:          """          Draws a random snek using Perlin noise. @@ -621,10 +620,10 @@ class Snakes(Cog):                  bg_color=bg_color              )              png_bytes = utils.frame_to_png_bytes(image_frame) -            file = File(png_bytes, filename='snek.png') +            file = File(png_bytes, filename="snek.png")              await ctx.send(file=file) -    @snakes_group.command(name='get') +    @snakes_group.command(name="get")      @bot_has_permissions(manage_messages=True)      @locked()      async def get_command(self, ctx: Context, *, name: Snake = None) -> None: @@ -642,8 +641,9 @@ class Snakes(Cog):              else:                  data = await self._get_snek(name) -            if data.get('error'): -                return await ctx.send('Could not fetch data from Wikipedia.') +            if data.get("error"): +                await ctx.send("Could not fetch data from Wikipedia.") +                return              description = data["info"] @@ -661,19 +661,19 @@ class Snakes(Cog):              # Build and send the embed.              embed = Embed( -                title=data.get("title", data.get('name')), +                title=data.get("title", data.get("name")),                  description=description,                  colour=0x59982F,              ) -            emoji = 'https://emojipedia-us.s3.amazonaws.com/thumbs/60/google/3/snake_1f40d.png' -            image = next((url for url in data['image_list'] +            emoji = "https://emojipedia-us.s3.amazonaws.com/thumbs/60/google/3/snake_1f40d.png" +            image = next((url for url in data["image_list"]                            if url.endswith(self.valid_image_extensions)), emoji)              embed.set_image(url=image)              await ctx.send(embed=embed) -    @snakes_group.command(name='guess', aliases=('identify',)) +    @snakes_group.command(name="guess", aliases=("identify",))      @locked()      async def guess_command(self, ctx: Context) -> None:          """ @@ -693,11 +693,11 @@ class Snakes(Cog):                  data = await self._get_snek(snake) -                image = next((url for url in data['image_list'] +                image = next((url for url in data["image_list"]                                if url.endswith(self.valid_image_extensions)), None)              embed = Embed( -                title='Which of the following is the snake in the image?', +                title="Which of the following is the snake in the image?",                  description="\n".join(                      f"{'ABCD'[snakes.index(snake)]}: {snake}" for snake in snakes),                  colour=SNAKE_COLOR @@ -708,7 +708,7 @@ class Snakes(Cog):          options = {f"{'abcd'[snakes.index(snake)]}": snake for snake in snakes}          await self._validate_answer(ctx, guess, answer, options) -    @snakes_group.command(name='hatch') +    @snakes_group.command(name="hatch")      async def hatch_command(self, ctx: Context) -> None:          """          Hatches your personal snake. @@ -720,7 +720,7 @@ class Snakes(Cog):          snake_image = utils.snakes[snake_name]          # Hatch the snake -        message = await ctx.channel.send(embed=Embed(description="Hatching your snake :snake:...")) +        message = await ctx.send(embed=Embed(description="Hatching your snake :snake:..."))          await asyncio.sleep(1)          for stage in utils.stages: @@ -737,9 +737,9 @@ class Snakes(Cog):              text=" Owner: {0}#{1}".format(ctx.message.author.name, ctx.message.author.discriminator)          ) -        await ctx.channel.send(embed=my_snake_embed) +        await ctx.send(embed=my_snake_embed) -    @snakes_group.command(name='movie') +    @snakes_group.command(name="movie")      async def movie_command(self, ctx: Context) -> None:          """          Gets a random snake-related movie from TMDB. @@ -800,12 +800,12 @@ class Snakes(Cog):          embed.set_thumbnail(url="https://i.imgur.com/LtFtC8H.png")          try: -            await ctx.channel.send(embed=embed) +            await ctx.send(embed=embed)          except HTTPException as err: -            await ctx.channel.send("An error occurred while fetching a snake-related movie!") +            await ctx.send("An error occurred while fetching a snake-related movie!")              raise err from None -    @snakes_group.command(name='quiz') +    @snakes_group.command(name="quiz")      @locked()      async def quiz_command(self, ctx: Context) -> None:          """ @@ -828,10 +828,10 @@ class Snakes(Cog):              )          ) -        quiz = await ctx.channel.send("", embed=embed) +        quiz = await ctx.send(embed=embed)          await self._validate_answer(ctx, quiz, answer, options) -    @snakes_group.command(name='name', aliases=('name_gen',)) +    @snakes_group.command(name="name", aliases=("name_gen",))      async def name_command(self, ctx: Context, *, name: str = None) -> None:          """          Snakifies a username. @@ -855,7 +855,7 @@ class Snakes(Cog):          This was written by Iceman, and modified for inclusion into the bot by lemon.          """          snake_name = await self._get_snake_name() -        snake_name = snake_name['name'] +        snake_name = snake_name["name"]          snake_prefix = ""          # Set aside every word in the snake name except the last. @@ -900,9 +900,10 @@ class Snakes(Cog):              color=SNAKE_COLOR          ) -        return await ctx.send(embed=embed) +        await ctx.send(embed=embed) +        return -    @snakes_group.command(name='sal') +    @snakes_group.command(name="sal")      @locked()      async def sal_command(self, ctx: Context) -> None:          """ @@ -921,7 +922,7 @@ class Snakes(Cog):          await game.open_game() -    @snakes_group.command(name='about') +    @snakes_group.command(name="about")      async def about_command(self, ctx: Context) -> None:          """Show an embed with information about the event, its participants, and its winners."""          contributors = [ @@ -964,9 +965,9 @@ class Snakes(Cog):              )          ) -        await ctx.channel.send(embed=embed) +        await ctx.send(embed=embed) -    @snakes_group.command(name='card') +    @snakes_group.command(name="card")      async def card_command(self, ctx: Context, *, name: Snake = None) -> None:          """          Create an interesting little card from a snake. @@ -976,7 +977,7 @@ class Snakes(Cog):          # Get the snake data we need          if not name:              name_obj = await self._get_snake_name() -            name = name_obj['scientific'] +            name = name_obj["scientific"]              content = await self._get_snek(name)          elif isinstance(name, dict): @@ -990,7 +991,7 @@ class Snakes(Cog):              stream = BytesIO()              async with async_timeout.timeout(10): -                async with self.bot.http_session.get(content['image_list'][0]) as response: +                async with self.bot.http_session.get(content["image_list"][0]) as response:                      stream.write(await response.read())              stream.seek(0) @@ -1001,10 +1002,10 @@ class Snakes(Cog):          # Send it!          await ctx.send(              f"A wild {content['name'].title()} appears!", -            file=File(final_buffer, filename=content['name'].replace(" ", "") + ".png") +            file=File(final_buffer, filename=content["name"].replace(" ", "") + ".png")          ) -    @snakes_group.command(name='fact') +    @snakes_group.command(name="fact")      async def fact_command(self, ctx: Context) -> None:          """          Gets a snake-related fact. @@ -1018,9 +1019,9 @@ class Snakes(Cog):              color=SNAKE_COLOR,              description=question          ) -        await ctx.channel.send(embed=embed) +        await ctx.send(embed=embed) -    @snakes_group.command(name='snakify') +    @snakes_group.command(name="snakify")      async def snakify_command(self, ctx: Context, *, message: str = None) -> None:          """          How would I talk if I were a snake? @@ -1059,9 +1060,9 @@ class Snakes(Cog):              )              embed.description = f"*{self._snakify(message)}*" -            await ctx.channel.send(embed=embed) +            await ctx.send(embed=embed) -    @snakes_group.command(name='video', aliases=('get_video',)) +    @snakes_group.command(name="video", aliases=("get_video",))      async def video_command(self, ctx: Context, *, search: str = None) -> None:          """          Gets a YouTube video about snakes. @@ -1072,13 +1073,13 @@ class Snakes(Cog):          """          # Are we searching for anything specific?          if search: -            query = search + ' snake' +            query = search + " snake"          else:              snake = await self._get_snake_name() -            query = snake['name'] +            query = snake["name"]          # Build the URL and make the request -        url = 'https://www.googleapis.com/youtube/v3/search' +        url = "https://www.googleapis.com/youtube/v3/search"          response = await self.bot.http_session.get(              url,              params={ @@ -1094,14 +1095,14 @@ class Snakes(Cog):          # Send the user a video          if len(data) > 0:              num = random.randint(0, len(data) - 1) -            youtube_base_url = 'https://www.youtube.com/watch?v=' -            await ctx.channel.send( +            youtube_base_url = "https://www.youtube.com/watch?v=" +            await ctx.send(                  content=f"{youtube_base_url}{data[num]['id']['videoId']}"              )          else:              log.warning(f"YouTube API error. Full response looks like {response}") -    @snakes_group.command(name='zen') +    @snakes_group.command(name="zen")      async def zen_command(self, ctx: Context) -> None:          """          Gets a random quote from the Zen of Python, except as if spoken by a snake. @@ -1120,7 +1121,7 @@ class Snakes(Cog):          # Embed and send          embed.description = zen_quote -        await ctx.channel.send( +        await ctx.send(              embed=embed          )      # endregion diff --git a/bot/exts/evergreen/snakes/_utils.py b/bot/exts/evergreen/snakes/_utils.py index 7d6caf04..d58ee279 100644 --- a/bot/exts/evergreen/snakes/_utils.py +++ b/bot/exts/evergreen/snakes/_utils.py @@ -321,7 +321,7 @@ def create_snek_frame(          image_dimensions[Y] / 2 - (dimension_range[Y] / 2 + min_dimensions[Y])      ) -    image = Image.new(mode='RGB', size=image_dimensions, color=bg_color) +    image = Image.new(mode="RGB", size=image_dimensions, color=bg_color)      draw = ImageDraw(image)      for index in range(1, len(points)):          point = points[index] @@ -345,7 +345,7 @@ def create_snek_frame(  def frame_to_png_bytes(image: Image) -> io.BytesIO:      """Convert image to byte stream."""      stream = io.BytesIO() -    image.save(stream, format='PNG') +    image.save(stream, format="PNG")      stream.seek(0)      return stream @@ -373,7 +373,7 @@ class SnakeAndLaddersGame:          self.snakes = snakes          self.ctx = context          self.channel = self.ctx.channel -        self.state = 'booting' +        self.state = "booting"          self.started = False          self.author = self.ctx.author          self.players = [] @@ -413,7 +413,7 @@ class SnakeAndLaddersGame:              "**Snakes and Ladders**: A new game is about to start!",              file=File(                  str(SNAKE_RESOURCES / "snakes_and_ladders" / "banner.jpg"), -                filename='Snakes and Ladders.jpg' +                filename="Snakes and Ladders.jpg"              )          )          startup = await self.channel.send( @@ -423,7 +423,7 @@ class SnakeAndLaddersGame:          for emoji in STARTUP_SCREEN_EMOJI:              await startup.add_reaction(emoji) -        self.state = 'waiting' +        self.state = "waiting"          while not self.started:              try: @@ -460,7 +460,7 @@ class SnakeAndLaddersGame:          self.players.append(user)          self.player_tiles[user.id] = 1 -        avatar_bytes = await user.avatar_url_as(format='jpeg', size=PLAYER_ICON_IMAGE_SIZE).read() +        avatar_bytes = await user.avatar_url_as(format="jpeg", size=PLAYER_ICON_IMAGE_SIZE).read()          im = Image.open(io.BytesIO(avatar_bytes)).resize((BOARD_PLAYER_SIZE, BOARD_PLAYER_SIZE))          self.avatar_images[user.id] = im @@ -475,7 +475,7 @@ class SnakeAndLaddersGame:              if user == p:                  await self.channel.send(user.mention + " You are already in the game.", delete_after=10)                  return -        if self.state != 'waiting': +        if self.state != "waiting":              await self.channel.send(user.mention + " You cannot join at this time.", delete_after=10)              return          if len(self.players) is MAX_PLAYERS: @@ -510,7 +510,7 @@ class SnakeAndLaddersGame:                      delete_after=10                  ) -                if self.state != 'waiting' and len(self.players) == 0: +                if self.state != "waiting" and len(self.players) == 0:                      await self.channel.send("**Snakes and Ladders**: The game has been surrendered!")                      is_surrendered = True                      self._destruct() @@ -535,12 +535,12 @@ class SnakeAndLaddersGame:              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 not self.state == "waiting":              await self.channel.send(user.mention + " The game cannot be started at this time.", delete_after=10)              return -        self.state = 'starting' -        player_list = ', '.join(user.mention for user in self.players) +        self.state = "starting" +        player_list = ", ".join(user.mention for user in self.players)          await self.channel.send("**Snakes and Ladders**: The game is starting!\nPlayers: " + player_list)          await self.start_round() @@ -556,7 +556,7 @@ class SnakeAndLaddersGame:                  ))              ) -        self.state = 'roll' +        self.state = "roll"          for user in self.players:              self.round_has_rolled[user.id] = False          board_img = Image.open(str(SNAKE_RESOURCES / "snakes_and_ladders" / "board.jpg")) @@ -574,8 +574,8 @@ class SnakeAndLaddersGame:              board_img.paste(self.avatar_images[player.id],                              box=(x_offset, y_offset)) -        board_file = File(frame_to_png_bytes(board_img), filename='Board.jpg') -        player_list = '\n'.join((user.mention + ": Tile " + str(self.player_tiles[user.id])) for user in self.players) +        board_file = File(frame_to_png_bytes(board_img), filename="Board.jpg") +        player_list = "\n".join((user.mention + ": Tile " + str(self.player_tiles[user.id])) for user in self.players)          # Store and send new messages          temp_board = await self.channel.send( @@ -644,7 +644,7 @@ class SnakeAndLaddersGame:          if user.id not in self.player_tiles:              await self.channel.send(user.mention + " You are not in the match.", delete_after=10)              return -        if self.state != 'roll': +        if self.state != "roll":              await self.channel.send(user.mention + " You may not roll at this time.", delete_after=10)              return          if self.round_has_rolled[user.id]: @@ -673,7 +673,7 @@ class SnakeAndLaddersGame:      async def _complete_round(self) -> None:          """At the conclusion of a round check to see if there's been a winner.""" -        self.state = 'post_round' +        self.state = "post_round"          # check for winner          winner = self._check_winner() @@ -688,7 +688,7 @@ class SnakeAndLaddersGame:      def _check_winner(self) -> Member:          """Return a winning member if we're in the post-round state and there's a winner.""" -        if self.state != 'post_round': +        if self.state != "post_round":              return None          return next((player for player in self.players if self.player_tiles[player.id] == 100),                      None)  |