From 734addece0491fe16656a787e5f032e8f7190168 Mon Sep 17 00:00:00 2001 From: AtieP Date: Mon, 28 Sep 2020 16:54:08 +0200 Subject: Add time utils --- bot/utils/time.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 bot/utils/time.py diff --git a/bot/utils/time.py b/bot/utils/time.py new file mode 100644 index 00000000..3c57e126 --- /dev/null +++ b/bot/utils/time.py @@ -0,0 +1,84 @@ +import datetime + +from dateutil.relativedelta import relativedelta + + +# All these functions are from https://github.com/python-discord/bot/blob/master/bot/utils/time.py +def _stringify_time_unit(value: int, unit: str) -> str: + """ + Returns a string to represent a value and time unit, ensuring that it uses the right plural form of the unit. + + >>> _stringify_time_unit(1, "seconds") + "1 second" + >>> _stringify_time_unit(24, "hours") + "24 hours" + >>> _stringify_time_unit(0, "minutes") + "less than a minute" + """ + if unit == "seconds" and value == 0: + return "0 seconds" + elif value == 1: + return f"{value} {unit[:-1]}" + elif value == 0: + return f"less than a {unit[:-1]}" + else: + return f"{value} {unit}" + + +def humanize_delta(delta: relativedelta, precision: str = "seconds", max_units: int = 6) -> str: + """ + Returns a human-readable version of the relativedelta. + + precision specifies the smallest unit of time to include (e.g. "seconds", "minutes"). + max_units specifies the maximum number of units of time to include (e.g. 1 may include days but not hours). + """ + if max_units <= 0: + raise ValueError("max_units must be positive") + + units = ( + ("years", delta.years), + ("months", delta.months), + ("days", delta.days), + ("hours", delta.hours), + ("minutes", delta.minutes), + ("seconds", delta.seconds), + ) + + # Add the time units that are >0, but stop at accuracy or max_units. + time_strings = [] + unit_count = 0 + for unit, value in units: + if value: + time_strings.append(_stringify_time_unit(value, unit)) + unit_count += 1 + + if unit == precision or unit_count >= max_units: + break + + # Add the 'and' between the last two units, if necessary + if len(time_strings) > 1: + time_strings[-1] = f"{time_strings[-2]} and {time_strings[-1]}" + del time_strings[-2] + + # If nothing has been found, just make the value 0 precision, e.g. `0 days`. + if not time_strings: + humanized = _stringify_time_unit(0, precision) + else: + humanized = ", ".join(time_strings) + + return humanized + + +def time_since(past_datetime: datetime.datetime, precision: str = "seconds", max_units: int = 6) -> str: + """ + Takes a datetime and returns a human-readable string that describes how long ago that datetime was. + + precision specifies the smallest unit of time to include (e.g. "seconds", "minutes"). + max_units specifies the maximum number of units of time to include (e.g. 1 may include days but not hours). + """ + now = datetime.datetime.utcnow() + delta = abs(relativedelta(now, past_datetime)) + + humanized = humanize_delta(delta, precision, max_units) + + return f"{humanized} ago" -- cgit v1.2.3 From 9813bedd3945d5efb42c6e5ca61782604ae8a343 Mon Sep 17 00:00:00 2001 From: AtieP Date: Mon, 28 Sep 2020 16:55:11 +0200 Subject: Add emojis cog --- bot/exts/evergreen/emojis.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 bot/exts/evergreen/emojis.py diff --git a/bot/exts/evergreen/emojis.py b/bot/exts/evergreen/emojis.py new file mode 100644 index 00000000..67331941 --- /dev/null +++ b/bot/exts/evergreen/emojis.py @@ -0,0 +1,39 @@ +import textwrap + +from discord import Color, Embed, Emoji +from discord.ext import commands + +from bot.utils.time import time_since + + +class Emojis(commands.Cog): + """Has commands related to emojis.""" + + @commands.group(name="emoji", invoke_without_command=True) + async def emojis_group(self, ctx: commands.Context, emoji: Emoji) -> None: + """A group of commands related to emojis.""" + # No parameters = same as invoking info subcommand + await ctx.invoke(self.info_command, emoji) + + @emojis_group.command(name="info") + async def info_command(self, ctx: commands.Context, emoji: Emoji) -> None: + """Returns relevant information about a Discord Emoji.""" + emoji_information = Embed( + title=f'Information about "{emoji.name}"', + description=textwrap.dedent(f""" + Name: {emoji.name} + Created: {time_since(emoji.created_at)} + ID: {emoji.id} + Emote source: [Link]({emoji.url}) + """), + color=Color.blurple() + ) + emoji_information.set_thumbnail( + url=str(emoji.url) + ) + await ctx.send(embed=emoji_information) + + +def setup(bot: commands.Bot) -> None: + """Add the Emojis cog into the bot.""" + bot.add_cog(Emojis()) -- cgit v1.2.3 From cb75d950183da1ed169141dbf95a732fed366450 Mon Sep 17 00:00:00 2001 From: AtieP Date: Mon, 28 Sep 2020 17:37:09 +0200 Subject: No need to typecast emoji.url to str --- bot/exts/evergreen/emojis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/emojis.py b/bot/exts/evergreen/emojis.py index 67331941..fb4c8a44 100644 --- a/bot/exts/evergreen/emojis.py +++ b/bot/exts/evergreen/emojis.py @@ -29,7 +29,7 @@ class Emojis(commands.Cog): color=Color.blurple() ) emoji_information.set_thumbnail( - url=str(emoji.url) + url=emoji.url ) await ctx.send(embed=emoji_information) -- cgit v1.2.3 From 68a7bb5f7c12bd167e7bb7fcbdb37e6f4cbd72a1 Mon Sep 17 00:00:00 2001 From: AtieP Date: Mon, 28 Sep 2020 18:45:29 +0200 Subject: Rename the last description line --- bot/exts/evergreen/emojis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/emojis.py b/bot/exts/evergreen/emojis.py index fb4c8a44..950ec245 100644 --- a/bot/exts/evergreen/emojis.py +++ b/bot/exts/evergreen/emojis.py @@ -24,7 +24,7 @@ class Emojis(commands.Cog): Name: {emoji.name} Created: {time_since(emoji.created_at)} ID: {emoji.id} - Emote source: [Link]({emoji.url}) + [Emoji source image]({emoji.url}) """), color=Color.blurple() ) -- cgit v1.2.3 From 2fd63f6a2813c99d7098e444c5a3145f916b2729 Mon Sep 17 00:00:00 2001 From: AtieP <62116490+AtieP@users.noreply.github.com> Date: Mon, 28 Sep 2020 19:11:42 +0200 Subject: Set precision to hours Co-authored-by: gustavwilliam <65498475+gustavwilliam@users.noreply.github.com> --- bot/exts/evergreen/emojis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/emojis.py b/bot/exts/evergreen/emojis.py index 950ec245..9f825e6d 100644 --- a/bot/exts/evergreen/emojis.py +++ b/bot/exts/evergreen/emojis.py @@ -22,7 +22,7 @@ class Emojis(commands.Cog): title=f'Information about "{emoji.name}"', description=textwrap.dedent(f""" Name: {emoji.name} - Created: {time_since(emoji.created_at)} + Created: {time_since(emoji.created_at, precision="hours")} ID: {emoji.id} [Emoji source image]({emoji.url}) """), -- cgit v1.2.3 From d764720481136f786593a67f152ac876ef7b151d Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 2 Dec 2020 21:52:10 +0530 Subject: Add Reddit class and emojis to constants file. --- bot/constants.py | 18 ++++++ bot/exts/evergreen/reddit.py | 128 ------------------------------------------- 2 files changed, 18 insertions(+), 128 deletions(-) delete mode 100644 bot/exts/evergreen/reddit.py diff --git a/bot/constants.py b/bot/constants.py index 6999f321..d1ffd30f 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -17,6 +17,7 @@ __all__ = ( "Roles", "Tokens", "Wolfram", + "Reddit", "RedisConfig", "MODERATION_ROLES", "STAFF_ROLES", @@ -144,6 +145,15 @@ class Emojis: status_dnd = "<:status_dnd:470326272082313216>" status_offline = "<:status_offline:470326266537705472>" + # Reddit emojis + reddit = "<:reddit:676030265734332427>" + reddit_post_text = "<:reddit_post_text:676030265910493204>" + reddit_post_video = "<:reddit_post_video:676030265839190047>" + reddit_post_photo = "<:reddit_post_photo:676030265734201344>" + reddit_upvote = "<:reddit_upvote:755845219890757644>" + reddit_comments = "<:reddit_comments:755845255001014384>" + reddit_users = "<:reddit_users:755845303822974997>" + class Icons: questionmark = "https://cdn.discordapp.com/emojis/512367613339369475.png" @@ -231,6 +241,14 @@ class Source: github_avatar_url = "https://avatars1.githubusercontent.com/u/9919" +class Reddit: + subreddits = ["r/Python"] + + client_id = environ.get("REDDIT_CLIENT_ID") + secret = environ.get("REDDIT_SECRET") + webhook = int(environ.get("REDDIT_WEBHOOK", 635408384794951680)) + + # Default role combinations MODERATION_ROLES = Roles.moderator, Roles.admin, Roles.owner STAFF_ROLES = Roles.helpers, Roles.moderator, Roles.admin, Roles.owner diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py deleted file mode 100644 index 49127bea..00000000 --- a/bot/exts/evergreen/reddit.py +++ /dev/null @@ -1,128 +0,0 @@ -import logging -import random - -import discord -from discord.ext import commands -from discord.ext.commands.cooldowns import BucketType - -from bot.utils.pagination import ImagePaginator - -log = logging.getLogger(__name__) - - -class Reddit(commands.Cog): - """Fetches reddit posts.""" - - def __init__(self, bot: commands.Bot): - self.bot = bot - - async def fetch(self, url: str) -> dict: - """Send a get request to the reddit API and get json response.""" - session = self.bot.http_session - params = { - 'limit': 50 - } - headers = { - 'User-Agent': 'Iceman' - } - - async with session.get(url=url, params=params, headers=headers) as response: - return await response.json() - - @commands.command(name='reddit') - @commands.cooldown(1, 10, BucketType.user) - async def get_reddit(self, ctx: commands.Context, subreddit: str = 'python', sort: str = "hot") -> None: - """ - Fetch reddit posts by using this command. - - Gets a post from r/python by default. - Usage: - --> .reddit [subreddit_name] [hot/top/new] - """ - pages = [] - sort_list = ["hot", "new", "top", "rising"] - if sort.lower() not in sort_list: - await ctx.send(f"Invalid sorting: {sort}\nUsing default sorting: `Hot`") - sort = "hot" - - data = await self.fetch(f'https://www.reddit.com/r/{subreddit}/{sort}/.json') - - try: - posts = data["data"]["children"] - except KeyError: - return await ctx.send('Subreddit not found!') - if not posts: - return await ctx.send('No posts available!') - - if posts[1]["data"]["over_18"] is True: - return await ctx.send( - "You cannot access this Subreddit as it is ment for those who " - "are 18 years or older." - ) - - embed_titles = "" - - # Chooses k unique random elements from a population sequence or set. - random_posts = random.sample(posts, k=5) - - # ----------------------------------------------------------- - # This code below is bound of change when the emojis are added. - - upvote_emoji = self.bot.get_emoji(755845219890757644) - comment_emoji = self.bot.get_emoji(755845255001014384) - user_emoji = self.bot.get_emoji(755845303822974997) - text_emoji = self.bot.get_emoji(676030265910493204) - video_emoji = self.bot.get_emoji(676030265839190047) - image_emoji = self.bot.get_emoji(676030265734201344) - reddit_emoji = self.bot.get_emoji(676030265734332427) - - # ------------------------------------------------------------ - - for i, post in enumerate(random_posts, start=1): - post_title = post["data"]["title"][0:50] - post_url = post['data']['url'] - if post_title == "": - post_title = "No Title." - elif post_title == post_url: - post_title = "Title is itself a link." - - # ------------------------------------------------------------------ - # Embed building. - - embed_titles += f"**{i}.[{post_title}]({post_url})**\n" - image_url = " " - post_stats = f"{text_emoji}" # Set default content type to text. - - if post["data"]["is_video"] is True or "youtube" in post_url.split("."): - # This means the content type in the post is a video. - post_stats = f"{video_emoji} " - - elif post_url.endswith("jpg") or post_url.endswith("png") or post_url.endswith("gif"): - # This means the content type in the post is an image. - post_stats = f"{image_emoji} " - image_url = post_url - - votes = f'{upvote_emoji}{post["data"]["ups"]}' - comments = f'{comment_emoji}\u2002{ post["data"]["num_comments"]}' - post_stats += ( - f"\u2002{votes}\u2003" - f"{comments}" - f'\u2003{user_emoji}\u2002{post["data"]["author"]}\n' - ) - embed_titles += f"{post_stats}\n" - page_text = f"**[{post_title}]({post_url})**\n{post_stats}\n{post['data']['selftext'][0:200]}" - - embed = discord.Embed() - page_tuple = (page_text, image_url) - pages.append(page_tuple) - - # ------------------------------------------------------------------ - - pages.insert(0, (embed_titles, " ")) - embed.set_author(name=f"r/{posts[0]['data']['subreddit']} - {sort}", icon_url=reddit_emoji.url) - await ImagePaginator.paginate(pages, ctx, embed) - - -def setup(bot: commands.Bot) -> None: - """Load the Cog.""" - bot.add_cog(Reddit(bot)) -- cgit v1.2.3 From 04f122348eca25dbbf44afdcc6fcd417aa98bf89 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 2 Dec 2020 21:53:29 +0530 Subject: Add Subreddit converter. --- bot/utils/converters.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/bot/utils/converters.py b/bot/utils/converters.py index 228714c9..27804170 100644 --- a/bot/utils/converters.py +++ b/bot/utils/converters.py @@ -1,5 +1,6 @@ import discord -from discord.ext.commands.converter import MessageConverter +from discord.ext.commands import BadArgument, Context +from discord.ext.commands.converter import Converter, MessageConverter class WrappedMessageConverter(MessageConverter): @@ -14,3 +15,32 @@ class WrappedMessageConverter(MessageConverter): argument = argument[1:-1] return await super().convert(ctx, argument) + + +class Subreddit(Converter): + """Forces a string to begin with "r/" and checks if it's a valid subreddit.""" + + @staticmethod + async def convert(ctx: Context, sub: str) -> str: + """ + Force sub to begin with "r/" and check if it's a valid subreddit. + + If sub is a valid subreddit, return it prepended with "r/" + """ + sub = sub.lower() + + if not sub.startswith("r/"): + sub = f"r/{sub}" + + resp = await ctx.bot.http_session.get( + "https://www.reddit.com/subreddits/search.json", + params={"q": sub} + ) + + json = await resp.json() + if not json["data"]["children"]: + raise BadArgument( + f"The subreddit `{sub}` either doesn't exist, or it has no posts." + ) + + return sub -- cgit v1.2.3 From f4e550bc1db9725577459f9a46d02da038e39c32 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 2 Dec 2020 21:54:14 +0530 Subject: Add sub_clyde utility function. --- bot/utils/messages.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 bot/utils/messages.py diff --git a/bot/utils/messages.py b/bot/utils/messages.py new file mode 100644 index 00000000..a6c035f9 --- /dev/null +++ b/bot/utils/messages.py @@ -0,0 +1,19 @@ +import re +from typing import Optional + + +def sub_clyde(username: Optional[str]) -> Optional[str]: + """ + Replace "e"/"E" in any "clyde" in `username` with a Cyrillic "е"/"E" and return the new string. + + Discord disallows "clyde" anywhere in the username for webhooks. It will return a 400. + Return None only if `username` is None. + """ + def replace_e(match: re.Match) -> str: + char = "е" if match[2] == "e" else "Е" + return match[1] + char + + if username: + return re.sub(r"(clyd)(e)", replace_e, username, flags=re.I) + else: + return username # Empty string or None -- cgit v1.2.3 From 6e32bda97aa91af1d100ea46f7efdf7031f87bff Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 2 Dec 2020 21:54:20 +0530 Subject: Migrate reddit command from Bot repo and add pagination. --- bot/exts/evergreen/reddit.py | 360 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 360 insertions(+) create mode 100644 bot/exts/evergreen/reddit.py diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py new file mode 100644 index 00000000..fb447cda --- /dev/null +++ b/bot/exts/evergreen/reddit.py @@ -0,0 +1,360 @@ +import asyncio +import logging +import random +import textwrap +from collections import namedtuple +from datetime import datetime, timedelta +from typing import List, Union + +from aiohttp import BasicAuth, ClientError +from discord import Colour, Embed, TextChannel +from discord.ext.commands import Cog, Context, group, has_any_role +from discord.ext.tasks import loop +from discord.utils import escape_markdown, sleep_until + +from bot.bot import Bot +from bot.constants import Channels, ERROR_REPLIES, Emojis, Reddit as RedditConfig, STAFF_ROLES +from bot.utils.converters import Subreddit +from bot.utils.messages import sub_clyde +from bot.utils.pagination import ImagePaginator, LinePaginator + +log = logging.getLogger(__name__) + +AccessToken = namedtuple("AccessToken", ["token", "expires_at"]) + + +class Reddit(Cog): + """Track subreddit posts and show detailed statistics about them.""" + + HEADERS = {"User-Agent": "python3:python-discord/bot:1.0.0 (by /u/PythonDiscord)"} + URL = "https://www.reddit.com" + OAUTH_URL = "https://oauth.reddit.com" + MAX_RETRIES = 3 + + def __init__(self, bot: Bot): + self.bot = bot + + self.webhook = None + self.access_token = None + self.client_auth = BasicAuth(RedditConfig.client_id, RedditConfig.secret) + + bot.loop.create_task(self.init_reddit_ready()) + self.auto_poster_loop.start() + + def cog_unload(self) -> None: + """Stop the loop task and revoke the access token when the cog is unloaded.""" + self.auto_poster_loop.cancel() + if self.access_token and self.access_token.expires_at > datetime.utcnow(): + asyncio.create_task(self.revoke_access_token()) + + async def init_reddit_ready(self) -> None: + """Sets the reddit webhook when the cog is loaded.""" + await self.bot.wait_until_guild_available() + if not self.webhook: + self.webhook = await self.bot.fetch_webhook(RedditConfig.webhook) + + @property + def channel(self) -> TextChannel: + """Get the #reddit channel object from the bot's cache.""" + return self.bot.get_channel(Channels.reddit) + + def build_pagination_pages(self, posts: List[dict]) -> List[tuple]: + """Build embed pages required for Paginator.""" + pages = [] + first_page = "" + for i, post in enumerate(posts, start=1): + post_page = "" + image_url = "" + + data = post["data"] + + title = textwrap.shorten(data["title"], width=64, placeholder="...") + + # Normal brackets interfere with Markdown. + title = escape_markdown(title).replace("[", "⦋").replace("]", "⦌") + link = self.URL + data["permalink"] + + first_page += f"**{i}. [{title.replace('*', '')}]({link})**\n" + post_page += f"**{i}. [{title}]({link})**\n\n" + + text = data["selftext"] + if text: + first_page += textwrap.shorten(text, width=128, placeholder="...").replace("*", "") + "\n" + post_page += textwrap.shorten(text, width=252, placeholder="...") + "\n\n" + + ups = data["ups"] + comments = data["num_comments"] + author = data["author"] + + content_type = Emojis.reddit_post_text + if data["is_video"] is True or "youtube" in data["url"].split("."): + # This means the content type in the post is a video. + content_type = f"{Emojis.reddit_post_video}" + + elif any(data["url"].endswith(pic_format) for pic_format in ("jpg", "png", "gif")): + # This means the content type in the post is an image. + content_type = f"{Emojis.reddit_post_photo}" + image_url = data["url"] + + first_page += ( + f"{content_type}\u2003{Emojis.reddit_upvote}{ups}\u2003{Emojis.reddit_comments}" + f"\u2002{comments}\u2003{Emojis.reddit_users}{author}\n\n" + ) + post_page += ( + f"{content_type}\u2003{Emojis.reddit_upvote}{ups}\u2003{Emojis.reddit_comments}\u2002" + f"{comments}\u2003{Emojis.reddit_users}{author}" + ) + + pages.append((post_page, image_url)) + + pages.insert(0, (first_page, "")) + return pages + + async def get_access_token(self) -> None: + """ + Get a Reddit API OAuth2 access token and assign it to self.access_token. + + A token is valid for 1 hour. There will be MAX_RETRIES to get a token, after which the cog + will be unloaded and a ClientError raised if retrieval was still unsuccessful. + """ + for i in range(1, self.MAX_RETRIES + 1): + response = await self.bot.http_session.post( + url=f"{self.URL}/api/v1/access_token", + headers=self.HEADERS, + auth=self.client_auth, + data={ + "grant_type": "client_credentials", + "duration": "temporary" + } + ) + + if response.status == 200 and response.content_type == "application/json": + content = await response.json() + expiration = int(content["expires_in"]) - 60 # Subtract 1 minute for leeway. + self.access_token = AccessToken( + token=content["access_token"], + expires_at=datetime.utcnow() + timedelta(seconds=expiration) + ) + + log.debug(f"New token acquired; expires on UTC {self.access_token.expires_at}") + return + else: + log.debug( + f"Failed to get an access token: " + f"status {response.status} & content type {response.content_type}; " + f"retrying ({i}/{self.MAX_RETRIES})" + ) + + await asyncio.sleep(3) + + self.bot.remove_cog(self.qualified_name) + raise ClientError("Authentication with the Reddit API failed. Unloading the cog.") + + async def revoke_access_token(self) -> None: + """ + Revoke the OAuth2 access token for the Reddit API. + + For security reasons, it's good practice to revoke the token when it's no longer being used. + """ + response = await self.bot.http_session.post( + url=f"{self.URL}/api/v1/revoke_token", + headers=self.HEADERS, + auth=self.client_auth, + data={ + "token": self.access_token.token, + "token_type_hint": "access_token" + } + ) + + if response.status == 204 and response.content_type == "application/json": + self.access_token = None + else: + log.warning(f"Unable to revoke access token: status {response.status}.") + + async def fetch_posts(self, route: str, *, amount: int = 25, params: dict = None) -> List[dict]: + """A helper method to fetch a certain amount of Reddit posts at a given route.""" + # Reddit's JSON responses only provide 25 posts at most. + if not 25 >= amount > 0: + raise ValueError("Invalid amount of subreddit posts requested.") + + # Renew the token if necessary. + if not self.access_token or self.access_token.expires_at < datetime.utcnow(): + await self.get_access_token() + + url = f"{self.OAUTH_URL}/{route}" + for _ in range(self.MAX_RETRIES): + response = await self.bot.http_session.get( + url=url, + headers={**self.HEADERS, "Authorization": f"bearer {self.access_token.token}"}, + params=params + ) + if response.status == 200 and response.content_type == 'application/json': + # Got appropriate response - process and return. + content = await response.json() + posts = content["data"]["children"] + + filtered_posts = [post for post in posts if not post["data"]["over_18"]] + + return filtered_posts[:amount] + + await asyncio.sleep(3) + + log.debug(f"Invalid response from: {url} - status code {response.status}, mimetype {response.content_type}") + return list() # Failed to get appropriate response within allowed number of retries. + + async def get_top_posts( + self, subreddit: Subreddit, time: str = "all", amount: int = 5, paginate: bool = False + ) -> Union[Embed, List[tuple]]: + """ + Get the top amount of posts for a given subreddit within a specified timeframe. + + A time of "all" will get posts from all time, "day" will get top daily posts and "week" will get the top + weekly posts. + + The amount should be between 0 and 25 as Reddit's JSON requests only provide 25 posts at most. + """ + embed = Embed(description="") + + posts = await self.fetch_posts( + route=f"{subreddit}/top", + amount=amount, + params={"t": time} + ) + if not posts: + embed.title = random.choice(ERROR_REPLIES) + embed.colour = Colour.red() + embed.description = ( + "Sorry! We couldn't find any SFW posts from that subreddit. " + "If this problem persists, please let us know." + ) + + return embed + + pages = self.build_pagination_pages(posts) + + if paginate: + return pages + + embed.description += pages[0] + embed.colour = Colour.blurple() + return embed + + @loop() + async def auto_poster_loop(self) -> None: + """Post the top 5 posts daily, and the top 5 posts weekly.""" + # once d.py get support for `time` parameter in loop decorator, + # this can be removed and the loop can use the `time=datetime.time.min` parameter + now = datetime.utcnow() + tomorrow = now + timedelta(days=1) + midnight_tomorrow = tomorrow.replace(hour=0, minute=0, second=0) + + await sleep_until(midnight_tomorrow) + + await self.bot.wait_until_guild_available() + if not self.webhook: + await self.bot.fetch_webhook(RedditConfig.webhook) + + if datetime.utcnow().weekday() == 0: + await self.top_weekly_posts() + # if it's a monday send the top weekly posts + + for subreddit in RedditConfig.subreddits: + top_posts = await self.get_top_posts(subreddit=subreddit, time="day") + username = sub_clyde(f"{subreddit} Top Daily Posts") + message = await self.webhook.send(username=username, embed=top_posts, wait=True) + + if message.channel.is_news(): + await message.publish() + + async def top_weekly_posts(self) -> None: + """Post a summary of the top posts.""" + for subreddit in RedditConfig.subreddits: + # Send and pin the new weekly posts. + top_posts = await self.get_top_posts(subreddit=subreddit, time="week") + username = sub_clyde(f"{subreddit} Top Weekly Posts") + message = await self.webhook.send(wait=True, username=username, embed=top_posts) + + if subreddit.lower() == "r/python": + if not self.channel: + log.warning("Failed to get #reddit channel to remove pins in the weekly loop.") + return + + # Remove the oldest pins so that only 12 remain at most. + pins = await self.channel.pins() + + while len(pins) >= 12: + await pins[-1].unpin() + del pins[-1] + + await message.pin() + + if message.channel.is_news(): + await message.publish() + + @group(name="reddit", invoke_without_command=True) + async def reddit_group(self, ctx: Context) -> None: + """View the top posts from various subreddits.""" + await ctx.send_help(ctx.command) + + @reddit_group.command(name="top") + async def top_command(self, ctx: Context, subreddit: Subreddit = "r/Python") -> None: + """Send the top posts of all time from a given subreddit.""" + async with ctx.typing(): + pages = await self.get_top_posts(subreddit=subreddit, time="all", paginate=True) + + embed = Embed( + title=f"{Emojis.reddit} {subreddit} - Top\n\n", + color=Colour.blurple() + ) + + await ImagePaginator.paginate(pages, ctx, embed) + + @reddit_group.command(name="daily") + async def daily_command(self, ctx: Context, subreddit: Subreddit = "r/Python") -> None: + """Send the top posts of today from a given subreddit.""" + async with ctx.typing(): + pages = await self.get_top_posts(subreddit=subreddit, time="day", paginate=True) + + embed = Embed( + title=f"{Emojis.reddit} {subreddit} - Daily\n\n", + color=Colour.blurple() + ) + + await ImagePaginator.paginate(pages, ctx, embed) + + @reddit_group.command(name="weekly") + async def weekly_command(self, ctx: Context, subreddit: Subreddit = "r/Python") -> None: + """Send the top posts of this week from a given subreddit.""" + async with ctx.typing(): + pages = await self.get_top_posts(subreddit=subreddit, time="week", paginate=True) + + embed = Embed( + title=f"{Emojis.reddit} {subreddit} - Weekly\n\n", + color=Colour.blurple() + ) + + await ImagePaginator.paginate(pages, ctx, embed) + + @has_any_role(*STAFF_ROLES) + @reddit_group.command(name="subreddits", aliases=("subs",)) + async def subreddits_command(self, ctx: Context) -> None: + """Send a paginated embed of all the subreddits we're relaying.""" + embed = Embed() + embed.title = "Relayed subreddits." + embed.colour = Colour.blurple() + + await LinePaginator.paginate( + RedditConfig.subreddits, + ctx, embed, + footer_text="Use the reddit commands along with these to view their posts.", + empty=False, + max_lines=15 + ) + + +def setup(bot: Bot) -> None: + """Load the Reddit cog.""" + if not RedditConfig.secret or not RedditConfig.client_id: + log.error("Credentials not provided, cog not loaded.") + return + bot.add_cog(Reddit(bot)) -- cgit v1.2.3 From 94822085a9f3e1677a8c566796fd1655b0b40ebf Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 9 Dec 2020 20:55:27 +0530 Subject: Changes to command output. --- bot/exts/evergreen/reddit.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index fb447cda..ddc0cc27 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -62,24 +62,24 @@ class Reddit(Cog): """Build embed pages required for Paginator.""" pages = [] first_page = "" - for i, post in enumerate(posts, start=1): + for post in posts: post_page = "" image_url = "" data = post["data"] - title = textwrap.shorten(data["title"], width=64, placeholder="...") + title = textwrap.shorten(data["title"], width=50, placeholder="...") # Normal brackets interfere with Markdown. title = escape_markdown(title).replace("[", "⦋").replace("]", "⦌") link = self.URL + data["permalink"] - first_page += f"**{i}. [{title.replace('*', '')}]({link})**\n" - post_page += f"**{i}. [{title}]({link})**\n\n" + first_page += f"**[{title.replace('*', '')}]({link})**\n" + post_page += f"**[{title}]({link})**\n\n" text = data["selftext"] if text: - first_page += textwrap.shorten(text, width=128, placeholder="...").replace("*", "") + "\n" + first_page += textwrap.shorten(text, width=100, placeholder="...").replace("*", "") + "\n" post_page += textwrap.shorten(text, width=252, placeholder="...") + "\n\n" ups = data["ups"] @@ -107,7 +107,7 @@ class Reddit(Cog): pages.append((post_page, image_url)) - pages.insert(0, (first_page, "")) + pages.insert(0, (first_page, "")) # Using image paginator, hence settings image url to empty string return pages async def get_access_token(self) -> None: @@ -235,6 +235,7 @@ class Reddit(Cog): if paginate: return pages + # Use only starting summary page for #reddit channel posts. embed.description += pages[0] embed.colour = Colour.blurple() return embed @@ -302,8 +303,8 @@ class Reddit(Cog): async with ctx.typing(): pages = await self.get_top_posts(subreddit=subreddit, time="all", paginate=True) + await ctx.send("Here are the top r/Python posts of all time!") embed = Embed( - title=f"{Emojis.reddit} {subreddit} - Top\n\n", color=Colour.blurple() ) @@ -315,8 +316,8 @@ class Reddit(Cog): async with ctx.typing(): pages = await self.get_top_posts(subreddit=subreddit, time="day", paginate=True) + await ctx.send("Here are today's top r/Python posts!") embed = Embed( - title=f"{Emojis.reddit} {subreddit} - Daily\n\n", color=Colour.blurple() ) @@ -328,8 +329,8 @@ class Reddit(Cog): async with ctx.typing(): pages = await self.get_top_posts(subreddit=subreddit, time="week", paginate=True) + await ctx.send("Here are this week's top r/Python posts!") embed = Embed( - title=f"{Emojis.reddit} {subreddit} - Weekly\n\n", color=Colour.blurple() ) -- cgit v1.2.3 From ac68262b8c3ec96f4476db7d4a00ebeb6b4149f8 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 29 Dec 2020 09:40:42 +0530 Subject: Fix bug in auto_poster_loop() regarding embed description. --- bot/exts/evergreen/reddit.py | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index ddc0cc27..f5134105 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -58,7 +58,7 @@ class Reddit(Cog): """Get the #reddit channel object from the bot's cache.""" return self.bot.get_channel(Channels.reddit) - def build_pagination_pages(self, posts: List[dict]) -> List[tuple]: + def build_pagination_pages(self, posts: List[dict], paginate) -> Union[List[tuple], str]: """Build embed pages required for Paginator.""" pages = [] first_page = "" @@ -75,19 +75,17 @@ class Reddit(Cog): link = self.URL + data["permalink"] first_page += f"**[{title.replace('*', '')}]({link})**\n" - post_page += f"**[{title}]({link})**\n\n" text = data["selftext"] if text: first_page += textwrap.shorten(text, width=100, placeholder="...").replace("*", "") + "\n" - post_page += textwrap.shorten(text, width=252, placeholder="...") + "\n\n" ups = data["ups"] comments = data["num_comments"] author = data["author"] content_type = Emojis.reddit_post_text - if data["is_video"] is True or "youtube" in data["url"].split("."): + if data["is_video"] is True or {"youtube", "youtu.be"}.issubset(set(data["url"].split("."))): # This means the content type in the post is a video. content_type = f"{Emojis.reddit_post_video}" @@ -100,12 +98,21 @@ class Reddit(Cog): f"{content_type}\u2003{Emojis.reddit_upvote}{ups}\u2003{Emojis.reddit_comments}" f"\u2002{comments}\u2003{Emojis.reddit_users}{author}\n\n" ) - post_page += ( - f"{content_type}\u2003{Emojis.reddit_upvote}{ups}\u2003{Emojis.reddit_comments}\u2002" - f"{comments}\u2003{Emojis.reddit_users}{author}" - ) - pages.append((post_page, image_url)) + if paginate: + post_page += f"**[{title}]({link})**\n\n" + if text: + post_page += textwrap.shorten(text, width=252, placeholder="...") + "\n\n" + post_page += ( + f"{content_type}\u2003{Emojis.reddit_upvote}{ups}\u2003{Emojis.reddit_comments}\u2002" + f"{comments}\u2003{Emojis.reddit_users}{author}" + ) + + pages.append((post_page, image_url)) + + if not paginate: + # Return the first summery page if pagination is not required + return first_page pages.insert(0, (first_page, "")) # Using image paginator, hence settings image url to empty string return pages @@ -213,7 +220,7 @@ class Reddit(Cog): The amount should be between 0 and 25 as Reddit's JSON requests only provide 25 posts at most. """ - embed = Embed(description="") + embed = Embed() posts = await self.fetch_posts( route=f"{subreddit}/top", @@ -230,13 +237,11 @@ class Reddit(Cog): return embed - pages = self.build_pagination_pages(posts) - if paginate: - return pages + return self.build_pagination_pages(posts, paginate=True) # Use only starting summary page for #reddit channel posts. - embed.description += pages[0] + embed.description = self.build_pagination_pages(posts, paginate=False) embed.colour = Colour.blurple() return embed @@ -303,7 +308,7 @@ class Reddit(Cog): async with ctx.typing(): pages = await self.get_top_posts(subreddit=subreddit, time="all", paginate=True) - await ctx.send("Here are the top r/Python posts of all time!") + await ctx.send(f"Here are the top {subreddit} posts of all time!") embed = Embed( color=Colour.blurple() ) @@ -316,7 +321,7 @@ class Reddit(Cog): async with ctx.typing(): pages = await self.get_top_posts(subreddit=subreddit, time="day", paginate=True) - await ctx.send("Here are today's top r/Python posts!") + await ctx.send(f"Here are today's top {subreddit} posts!") embed = Embed( color=Colour.blurple() ) @@ -329,7 +334,7 @@ class Reddit(Cog): async with ctx.typing(): pages = await self.get_top_posts(subreddit=subreddit, time="week", paginate=True) - await ctx.send("Here are this week's top r/Python posts!") + await ctx.send(f"Here are this week's top {subreddit} posts!") embed = Embed( color=Colour.blurple() ) -- cgit v1.2.3 From c9f0d26601f7d3bf01257fbff9384df76aa381f6 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 27 Jan 2021 13:02:06 +0530 Subject: Fix lint error: Missing type annotation for function arugment `paginate`. --- bot/exts/evergreen/reddit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index f5134105..1a4f9add 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -58,7 +58,7 @@ class Reddit(Cog): """Get the #reddit channel object from the bot's cache.""" return self.bot.get_channel(Channels.reddit) - def build_pagination_pages(self, posts: List[dict], paginate) -> Union[List[tuple], str]: + def build_pagination_pages(self, posts: List[dict], paginate: bool) -> Union[List[tuple], str]: """Build embed pages required for Paginator.""" pages = [] first_page = "" -- cgit v1.2.3 From 0d5da9e1304865034e8a2349d33b132e149ad890 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 5 Feb 2021 19:17:59 +0000 Subject: First pass of easy to produce errors --- bot/exts/easter/easter_riddle.py | 13 ++++++++++++- bot/exts/evergreen/issues.py | 7 ++++++- bot/exts/evergreen/wolfram.py | 3 ++- bot/exts/halloween/candy_collection.py | 3 +++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/bot/exts/easter/easter_riddle.py b/bot/exts/easter/easter_riddle.py index 3c612eb1..a93b3745 100644 --- a/bot/exts/easter/easter_riddle.py +++ b/bot/exts/easter/easter_riddle.py @@ -7,7 +7,7 @@ from pathlib import Path import discord from discord.ext import commands -from bot.constants import Colours +from bot.constants import Colours, NEGATIVE_REPLIES log = logging.getLogger(__name__) @@ -36,6 +36,17 @@ class EasterRiddle(commands.Cog): if self.current_channel: return await ctx.send(f"A riddle is already being solved in {self.current_channel.mention}!") + # Don't let users start in a DM + if not ctx.guild: + await ctx.send( + embed=discord.Embed( + title=random.choice(NEGATIVE_REPLIES), + description="You can't start riddles in DMs", + colour=discord.Colour.red() + ) + ) + return + self.current_channel = ctx.message.channel random_question = random.choice(RIDDLE_QUESTIONS) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 73ebe547..1f22f287 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -178,7 +178,12 @@ class Issues(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - if not( + # Ignore messages from DMs + if not message.guild: + return + + # Ignore messages not in whitelisted categories / channels + if not ( message.channel.category.id in WHITELISTED_CATEGORIES or message.channel.id in WHITELISTED_CHANNELS_ON_MESSAGE ): diff --git a/bot/exts/evergreen/wolfram.py b/bot/exts/evergreen/wolfram.py index 898e8d2a..077a99f5 100644 --- a/bot/exts/evergreen/wolfram.py +++ b/bot/exts/evergreen/wolfram.py @@ -62,7 +62,8 @@ def custom_cooldown(*ignore: List[int]) -> Callable: # if the invoked command is help we don't want to increase the ratelimits since it's not actually # invoking the command/making a request, so instead just check if the user/guild are on cooldown. guild_cooldown = not guildcd.get_bucket(ctx.message).get_tokens() == 0 # if guild is on cooldown - if not any(r.id in ignore for r in ctx.author.roles): # check user bucket if user is not ignored + # check the message is in a guild, and check user bucket if user is not ignored + if ctx.guild and not any(r.id in ignore for r in ctx.author.roles): return guild_cooldown and not usercd.get_bucket(ctx.message).get_tokens() == 0 return guild_cooldown diff --git a/bot/exts/halloween/candy_collection.py b/bot/exts/halloween/candy_collection.py index 0cb37ecd..40e21f40 100644 --- a/bot/exts/halloween/candy_collection.py +++ b/bot/exts/halloween/candy_collection.py @@ -47,6 +47,9 @@ class CandyCollection(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Randomly adds candy or skull reaction to non-bot messages in the Event channel.""" + # Ignore messages in DMs + if not message.guild: + return # make sure its a human message if message.author.bot: return -- cgit v1.2.3 From 4772c5e708742c40048cbb7fd29061ab3ae87931 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Sun, 7 Feb 2021 13:57:28 +0530 Subject: Revert "Fixes Issue Matching Regex" This reverts commit 4239b1d07cd0bba543aca8e7c77cee1d8f437b14. --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 73ebe547..72ca6de4 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -184,7 +184,7 @@ class Issues(commands.Cog): ): return - message_repo_issue_map = re.findall(fr"({self.repo_regex})#(\d+)", message.content) + message_repo_issue_map = re.findall(fr".+?({self.repo_regex})#(\d+)", message.content) links = [] if message_repo_issue_map: -- cgit v1.2.3 From 671be9942521aaa4915a358d0c1d4af13db94e3c Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Sun, 7 Feb 2021 13:57:28 +0530 Subject: Revert "Fixes Issue Matching Regex" This reverts commit 4239b1d07cd0bba543aca8e7c77cee1d8f437b14. --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 73ebe547..72ca6de4 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -184,7 +184,7 @@ class Issues(commands.Cog): ): return - message_repo_issue_map = re.findall(fr"({self.repo_regex})#(\d+)", message.content) + message_repo_issue_map = re.findall(fr".+?({self.repo_regex})#(\d+)", message.content) links = [] if message_repo_issue_map: -- cgit v1.2.3 From b4ee10ad69581fef0cc01c0539c644bb0f885c61 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Wed, 10 Feb 2021 09:26:53 +0530 Subject: Add stackoverflow cog with answers NOTE: I am not linking to the question page itself anywhere --- bot/exts/evergreen/stackoverflow.py | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 bot/exts/evergreen/stackoverflow.py diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py new file mode 100644 index 00000000..ab98c757 --- /dev/null +++ b/bot/exts/evergreen/stackoverflow.py @@ -0,0 +1,44 @@ +from html import unescape +from urllib.parse import quote_plus + +from discord import Embed +from discord.ext.commands import Bot, Cog, Context, command, cooldown + +BASE_URL = "https://api.stackexchange.com/2.2/search/advanced?order=desc&sort=activity&site=stackoverflow&q={query}" +SEARCH_URL = "https://stackoverflow.com/search?q={query}" +SO_COLOR = 0xF98036 + + +class Stackoverflow(Cog): + """A cog which returns the top 5 results of a query from stackoverflow.""" + + def __init__(self, bot: Bot): + self.bot = bot + + @command(name="stackoverflow", aliases=["so"]) + @cooldown(1, 15) + async def stackoverflow(self, ctx: Context, *, search_query: str) -> None: + """Sends the top 5 results from stackoverflow based on a search query.""" + async with self.bot.http_session.get(BASE_URL.format(query=quote_plus(search_query))) as response: + data = await response.json() + + top5 = data["items"][:5] + embed = Embed(title=f"Search results for {search_query!r} - Stackoverflow", + url=SEARCH_URL.format(query=quote_plus(search_query)), + description=f"Here are the top {len(top5)} results:", + color=SO_COLOR) + for item in top5: + embed.add_field( + name=f"{unescape(item['title'])}", + value=(f"{item['score']} upvotes ┃ " + f"{item['view_count']} views ┃ " + f"{item['answer_count']} answers " + ), + inline=False) + embed.set_footer() + await ctx.send(embed=embed) + + +def setup(bot: Bot) -> None: + """Adds the cog to the bot.""" + bot.add_cog(Stackoverflow(bot)) -- cgit v1.2.3 From d8da42d858ce6d20eda941f2141cd199cb66b0a6 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Wed, 10 Feb 2021 22:59:03 +0530 Subject: Add link of question --- bot/exts/evergreen/stackoverflow.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index ab98c757..d388b26d 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -30,10 +30,10 @@ class Stackoverflow(Cog): for item in top5: embed.add_field( name=f"{unescape(item['title'])}", - value=(f"{item['score']} upvotes ┃ " + value=(f"[{item['score']} upvotes ┃ " f"{item['view_count']} views ┃ " - f"{item['answer_count']} answers " - ), + f"{item['answer_count']} answer{'s' if item['answer_count'] != 1 else ''} ]" + f"({item['link']})"), inline=False) embed.set_footer() await ctx.send(embed=embed) -- cgit v1.2.3 From 9e4c24db98755fdc8111f811650fab45ba5d451f Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Wed, 10 Feb 2021 23:07:08 +0530 Subject: Add tags of question and drop the 's' if the score/views are 1. --- bot/exts/evergreen/stackoverflow.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index d388b26d..65c85c3f 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -30,9 +30,10 @@ class Stackoverflow(Cog): for item in top5: embed.add_field( name=f"{unescape(item['title'])}", - value=(f"[{item['score']} upvotes ┃ " - f"{item['view_count']} views ┃ " - f"{item['answer_count']} answer{'s' if item['answer_count'] != 1 else ''} ]" + value=(f"[{item['score']} upvote{'s' if item['score'] != 1 else ''} ┃ " + f"{item['view_count']} view{'s' if item['view_count'] != 1 else ''} ┃ " + f"{item['answer_count']} answer{'s' if item['answer_count'] != 1 else ''} ┃ " + f"Tags: {', '.join(item['tags'])}]" f"({item['link']})"), inline=False) embed.set_footer() -- cgit v1.2.3 From f93cb0d22faf0beeae182d398bd860ac9e67c91b Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Wed, 10 Feb 2021 23:09:12 +0530 Subject: Add footer to embed. --- bot/exts/evergreen/stackoverflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 65c85c3f..6fcd9390 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -36,7 +36,7 @@ class Stackoverflow(Cog): f"Tags: {', '.join(item['tags'])}]" f"({item['link']})"), inline=False) - embed.set_footer() + embed.set_footer(text="View the original link for more results.") await ctx.send(embed=embed) -- cgit v1.2.3 From d24c0fccb1ec1b363d1e18137834bc0c993624af Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Wed, 10 Feb 2021 23:42:12 +0530 Subject: Fix revert --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 72ca6de4..73ebe547 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -184,7 +184,7 @@ class Issues(commands.Cog): ): return - message_repo_issue_map = re.findall(fr".+?({self.repo_regex})#(\d+)", message.content) + message_repo_issue_map = re.findall(fr"({self.repo_regex})#(\d+)", message.content) links = [] if message_repo_issue_map: -- cgit v1.2.3 From 33b9cd8e0c2fd06f1a4415cf6adedd631f4f091c Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Thu, 11 Feb 2021 12:07:12 +0530 Subject: Use sir-lancebot's format. Just import discord.ext.commands --- bot/exts/evergreen/stackoverflow.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 6fcd9390..f272bcda 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -2,22 +2,22 @@ from html import unescape from urllib.parse import quote_plus from discord import Embed -from discord.ext.commands import Bot, Cog, Context, command, cooldown +from discord.ext import commands BASE_URL = "https://api.stackexchange.com/2.2/search/advanced?order=desc&sort=activity&site=stackoverflow&q={query}" SEARCH_URL = "https://stackoverflow.com/search?q={query}" SO_COLOR = 0xF98036 -class Stackoverflow(Cog): +class Stackoverflow(commands.Cog): """A cog which returns the top 5 results of a query from stackoverflow.""" - def __init__(self, bot: Bot): + def __init__(self, bot: commands.Bot): self.bot = bot - @command(name="stackoverflow", aliases=["so"]) - @cooldown(1, 15) - async def stackoverflow(self, ctx: Context, *, search_query: str) -> None: + @commands.command(name="stackoverflow", aliases=["so"]) + @commands.cooldown(1, 15) + async def stackoverflow(self, ctx: commands.Context, *, search_query: str) -> None: """Sends the top 5 results from stackoverflow based on a search query.""" async with self.bot.http_session.get(BASE_URL.format(query=quote_plus(search_query))) as response: data = await response.json() @@ -40,6 +40,6 @@ class Stackoverflow(Cog): await ctx.send(embed=embed) -def setup(bot: Bot) -> None: +def setup(bot: commands.Bot) -> None: """Adds the cog to the bot.""" bot.add_cog(Stackoverflow(bot)) -- cgit v1.2.3 From 42594929d611f0e9f0de3e3e5ddfae3ae8805628 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Thu, 11 Feb 2021 12:09:29 +0530 Subject: use discord's orange colour --- bot/exts/evergreen/stackoverflow.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index f272bcda..f5bcba20 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -1,12 +1,11 @@ from html import unescape from urllib.parse import quote_plus -from discord import Embed +from discord import Colour, Embed from discord.ext import commands BASE_URL = "https://api.stackexchange.com/2.2/search/advanced?order=desc&sort=activity&site=stackoverflow&q={query}" SEARCH_URL = "https://stackoverflow.com/search?q={query}" -SO_COLOR = 0xF98036 class Stackoverflow(commands.Cog): @@ -26,7 +25,7 @@ class Stackoverflow(commands.Cog): embed = Embed(title=f"Search results for {search_query!r} - Stackoverflow", url=SEARCH_URL.format(query=quote_plus(search_query)), description=f"Here are the top {len(top5)} results:", - color=SO_COLOR) + color=Colour.orange) for item in top5: embed.add_field( name=f"{unescape(item['title'])}", -- cgit v1.2.3 From b6b271dd5913a0dd978af5d04b20cdc54b88afcd Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Thu, 11 Feb 2021 14:12:50 +0530 Subject: Apply cooldown to all users instead of global cooldown. --- bot/exts/evergreen/stackoverflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index f5bcba20..2a4b5f88 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -15,7 +15,7 @@ class Stackoverflow(commands.Cog): self.bot = bot @commands.command(name="stackoverflow", aliases=["so"]) - @commands.cooldown(1, 15) + @commands.cooldown(1, 15, commands.cooldowns.BucketType.user) async def stackoverflow(self, ctx: commands.Context, *, search_query: str) -> None: """Sends the top 5 results from stackoverflow based on a search query.""" async with self.bot.http_session.get(BASE_URL.format(query=quote_plus(search_query))) as response: -- cgit v1.2.3 From 2c25c0af4a53a4c280476a70f2b0d3823550a50d Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Fri, 12 Feb 2021 09:20:50 +0530 Subject: remove name param in command --- bot/exts/evergreen/stackoverflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 2a4b5f88..54b6555d 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -14,7 +14,7 @@ class Stackoverflow(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - @commands.command(name="stackoverflow", aliases=["so"]) + @commands.command(aliases=["so"]) @commands.cooldown(1, 15, commands.cooldowns.BucketType.user) async def stackoverflow(self, ctx: commands.Context, *, search_query: str) -> None: """Sends the top 5 results from stackoverflow based on a search query.""" -- cgit v1.2.3 From 6f43eee295ad5561543584baa2095199dba0af38 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Fri, 12 Feb 2021 09:21:37 +0530 Subject: change docstring --- bot/exts/evergreen/stackoverflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 54b6555d..06aa37e7 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -17,7 +17,7 @@ class Stackoverflow(commands.Cog): @commands.command(aliases=["so"]) @commands.cooldown(1, 15, commands.cooldowns.BucketType.user) async def stackoverflow(self, ctx: commands.Context, *, search_query: str) -> None: - """Sends the top 5 results from stackoverflow based on a search query.""" + """Sends the top 5 results of a search query from stackoverflow.""" async with self.bot.http_session.get(BASE_URL.format(query=quote_plus(search_query))) as response: data = await response.json() -- cgit v1.2.3 From 60d0bf4b07363f42e246c8dbae27561ad7815b17 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Fri, 12 Feb 2021 09:23:57 +0530 Subject: change docstring --- bot/exts/evergreen/stackoverflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 06aa37e7..51ae567c 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -40,5 +40,5 @@ class Stackoverflow(commands.Cog): def setup(bot: commands.Bot) -> None: - """Adds the cog to the bot.""" + """Loads Stackoverflow Cog.""" bot.add_cog(Stackoverflow(bot)) -- cgit v1.2.3 From 7ba7149c688ba35d60934fdc68b92b82862065c7 Mon Sep 17 00:00:00 2001 From: MrKomodoDragon Date: Tue, 16 Feb 2021 09:10:57 -0800 Subject: Add .pyfacts command --- bot/exts/evergreen/pythonfacts.py | 23 +++++++++++++++++++++++ bot/resources/evergreen/python_facts.txt | 3 +++ 2 files changed, 26 insertions(+) create mode 100644 bot/exts/evergreen/pythonfacts.py create mode 100644 bot/resources/evergreen/python_facts.txt diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py new file mode 100644 index 00000000..ba792561 --- /dev/null +++ b/bot/exts/evergreen/pythonfacts.py @@ -0,0 +1,23 @@ +import random + +import discord +from discord.ext import commands +from discord.ext.commands.bot import Bot + + +class PythonFacts(commands.Cog): + """Gives a random fun fact about Python.""" + + def __init__(self, bot: Bot) -> None: + self.bot = bot + + @commands.command(name='pythonfact', aliases=['pyfact']) + async def get_python_fact(self, ctx: commands.Context) -> None: + """Gives a Random fun fact about Python.""" + with open('bot/resources/evergreen/python_facts.txt') as file: + await ctx.send(embed=discord.Embed(title='Python Facts', description=f'**{random.choice(list(file))}**')) + + +def setup(bot: commands.Bot) -> None: + """Adding the cog to the bot.""" + bot.add_cog(PythonFacts(bot)) diff --git a/bot/resources/evergreen/python_facts.txt b/bot/resources/evergreen/python_facts.txt new file mode 100644 index 00000000..d9e63a11 --- /dev/null +++ b/bot/resources/evergreen/python_facts.txt @@ -0,0 +1,3 @@ +Python was named after Monty Python, which Guido van Rossum likes. +If you type `import this` in the Python REPL, you'll get a poem about the philosophies about Python. (check it out by doing !zen in <#267659945086812160>) +If you type `import antigravity` in the Python REPL, you'll be directed to an [xkcd comic](https://xkcd.com/353/) about how easy Python is. -- cgit v1.2.3 From b112226ca6ec9b49d25fe99aedb627b259e9b1ca Mon Sep 17 00:00:00 2001 From: MrKomodoDragon <74436682+MrKomodoDragon@users.noreply.github.com> Date: Tue, 16 Feb 2021 10:39:16 -0800 Subject: Change `bot: Bot` to commands.Bot for consistency Co-authored-by: ChrisJL --- bot/exts/evergreen/pythonfacts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index ba792561..58d45b1c 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -8,7 +8,7 @@ from discord.ext.commands.bot import Bot class PythonFacts(commands.Cog): """Gives a random fun fact about Python.""" - def __init__(self, bot: Bot) -> None: + def __init__(self, bot: commands.Bot) -> None: self.bot = bot @commands.command(name='pythonfact', aliases=['pyfact']) -- cgit v1.2.3 From fdfa5c75b74a898512bfac6fa3ac14d452eae05d Mon Sep 17 00:00:00 2001 From: MrKomodoDragon Date: Tue, 16 Feb 2021 10:44:21 -0800 Subject: Remove unused import --- bot/exts/evergreen/pythonfacts.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index ba792561..b1c28d4b 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -2,13 +2,12 @@ import random import discord from discord.ext import commands -from discord.ext.commands.bot import Bot class PythonFacts(commands.Cog): """Gives a random fun fact about Python.""" - def __init__(self, bot: Bot) -> None: + def __init__(self, bot: commands.Bot) -> None: self.bot = bot @commands.command(name='pythonfact', aliases=['pyfact']) -- cgit v1.2.3 From 06d600f0fd927e566d23879638d10c6d66a63a21 Mon Sep 17 00:00:00 2001 From: MrKomodoDragon Date: Tue, 16 Feb 2021 10:48:13 -0800 Subject: Move loading of file outside of command --- bot/exts/evergreen/pythonfacts.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index b1c28d4b..c6592add 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -4,6 +4,10 @@ import discord from discord.ext import commands +with open('bot/resources/evergreen/python_facts.txt') as file: + FACTS = list(file) + + class PythonFacts(commands.Cog): """Gives a random fun fact about Python.""" @@ -13,8 +17,7 @@ class PythonFacts(commands.Cog): @commands.command(name='pythonfact', aliases=['pyfact']) async def get_python_fact(self, ctx: commands.Context) -> None: """Gives a Random fun fact about Python.""" - with open('bot/resources/evergreen/python_facts.txt') as file: - await ctx.send(embed=discord.Embed(title='Python Facts', description=f'**{random.choice(list(file))}**')) + await ctx.send(embed=discord.Embed(title='Python Facts', description=f'**{random.choice(FACTS)}**')) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From 57aec016ac74465212744a9ec4173515dca98ba1 Mon Sep 17 00:00:00 2001 From: MrKomodoDragon <74436682+MrKomodoDragon@users.noreply.github.com> Date: Tue, 16 Feb 2021 17:57:51 -0800 Subject: Remove bold Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/evergreen/pythonfacts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index c6592add..fcb78168 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -17,7 +17,7 @@ class PythonFacts(commands.Cog): @commands.command(name='pythonfact', aliases=['pyfact']) async def get_python_fact(self, ctx: commands.Context) -> None: """Gives a Random fun fact about Python.""" - await ctx.send(embed=discord.Embed(title='Python Facts', description=f'**{random.choice(FACTS)}**')) + await ctx.send(embed=discord.Embed(title='Python Facts', description=random.choice(FACTS))) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From a68e8f558cfee18bdb04c9e9a07cc9d293bd6c71 Mon Sep 17 00:00:00 2001 From: MrKomodoDragon <74436682+MrKomodoDragon@users.noreply.github.com> Date: Tue, 16 Feb 2021 17:58:31 -0800 Subject: Update docstring for Cog Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/evergreen/pythonfacts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index fcb78168..dbc38ef4 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -21,5 +21,5 @@ class PythonFacts(commands.Cog): def setup(bot: commands.Bot) -> None: - """Adding the cog to the bot.""" + """Load PythonFacts Cog.""" bot.add_cog(PythonFacts(bot)) -- cgit v1.2.3 From 6aae339f33ee471bafe9338db5046b774c6b4aad Mon Sep 17 00:00:00 2001 From: MrKomodoDragon <74436682+MrKomodoDragon@users.noreply.github.com> Date: Tue, 16 Feb 2021 18:03:31 -0800 Subject: Change Docstring for command Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/evergreen/pythonfacts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index dbc38ef4..c0cdf111 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -16,7 +16,7 @@ class PythonFacts(commands.Cog): @commands.command(name='pythonfact', aliases=['pyfact']) async def get_python_fact(self, ctx: commands.Context) -> None: - """Gives a Random fun fact about Python.""" + """Sends a Random fun fact about Python.""" await ctx.send(embed=discord.Embed(title='Python Facts', description=random.choice(FACTS))) -- cgit v1.2.3 From 5164ba4e367efbfe78f8dd2b08646fe6605a8b42 Mon Sep 17 00:00:00 2001 From: MrKomodoDragon Date: Tue, 16 Feb 2021 18:21:12 -0800 Subject: Add colors and move embed out of send statement; add more info about Monty Python for Fact 1 --- bot/exts/evergreen/pythonfacts.py | 7 +++++-- bot/resources/evergreen/python_facts.txt | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index c0cdf111..734782b8 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -7,9 +7,11 @@ from discord.ext import commands with open('bot/resources/evergreen/python_facts.txt') as file: FACTS = list(file) +COLORS = [0x4B8BBE, 0xFFD43B, ] + class PythonFacts(commands.Cog): - """Gives a random fun fact about Python.""" + """Sends a random fun fact about Python.""" def __init__(self, bot: commands.Bot) -> None: self.bot = bot @@ -17,7 +19,8 @@ class PythonFacts(commands.Cog): @commands.command(name='pythonfact', aliases=['pyfact']) async def get_python_fact(self, ctx: commands.Context) -> None: """Sends a Random fun fact about Python.""" - await ctx.send(embed=discord.Embed(title='Python Facts', description=random.choice(FACTS))) + embed = discord.Embed(title='Python Facts', description=random.choice(FACTS), colour=random.choice(COLORS)) + await ctx.send(embed=embed) def setup(bot: commands.Bot) -> None: diff --git a/bot/resources/evergreen/python_facts.txt b/bot/resources/evergreen/python_facts.txt index d9e63a11..0abd971b 100644 --- a/bot/resources/evergreen/python_facts.txt +++ b/bot/resources/evergreen/python_facts.txt @@ -1,3 +1,3 @@ -Python was named after Monty Python, which Guido van Rossum likes. +Python was named after Monty Python, a British Comedy Troupe, which Guido van Rossum likes. If you type `import this` in the Python REPL, you'll get a poem about the philosophies about Python. (check it out by doing !zen in <#267659945086812160>) If you type `import antigravity` in the Python REPL, you'll be directed to an [xkcd comic](https://xkcd.com/353/) about how easy Python is. -- cgit v1.2.3 From 09b4e9e6d9a62848747c8e8ec7c3e404f0ad1b92 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 18 Feb 2021 19:22:14 +0000 Subject: Add aiofiles dependancy --- Pipfile | 1 + Pipfile.lock | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Pipfile b/Pipfile index c382902f..d4b74e85 100644 --- a/Pipfile +++ b/Pipfile @@ -14,6 +14,7 @@ sentry-sdk = "~=0.19" PyYAML = "~=5.3.1" "discord.py" = {extras = ["voice"], version = "~=1.5.1"} async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} +aiofiles = "~=0.6" [dev-packages] flake8 = "~=3.8" diff --git a/Pipfile.lock b/Pipfile.lock index bd894ffa..d5f313d1 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "9be419062bd9db364ac9dddfcd50aef9c932384b45850363e482591fe7d12403" + "sha256": "640043d117ada0863e85327e52288393fc6f08568f4bf1aeba53e0443fe7f8f0" }, "pipfile-spec": 6, "requires": { @@ -24,6 +24,14 @@ "index": "pypi", "version": "==2.0.0" }, + "aiofiles": { + "hashes": [ + "sha256:bd3019af67f83b739f8e4053c6c0512a7f545b9a8d91aaeab55e6e0f9d123c27", + "sha256:e0281b157d3d5d59d803e3f4557dcc9a3dff28a4dd4829a9ff478adae50ca092" + ], + "index": "pypi", + "version": "==0.6.0" + }, "aiohttp": { "hashes": [ "sha256:1a4160579ffbc1b69e88cb6ca8bb0fbd4947dfcbf9fb1e2a4fc4c7a4a986c1fe", @@ -406,10 +414,11 @@ }, "sentry-sdk": { "hashes": [ - "sha256:3693cb47ba8d90c004ac002425770b32aaf0c83a846ec48e2d1364e7db1d072d" + "sha256:4ae8d1ced6c67f1c8ea51d82a16721c166c489b76876c9f2c202b8a50334b237", + "sha256:e75c8c58932bda8cd293ea8e4b242527129e1caaec91433d21b8b2f20fee030b" ], "index": "pypi", - "version": "==0.20.1" + "version": "==0.20.3" }, "six": { "hashes": [ -- cgit v1.2.3 From f63af81bb9f1613ca2b4e00c3f12978a02a86983 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 18 Feb 2021 19:23:04 +0000 Subject: Move gender options to a resource file --- bot/resources/pride/gender_options.json | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 bot/resources/pride/gender_options.json diff --git a/bot/resources/pride/gender_options.json b/bot/resources/pride/gender_options.json new file mode 100644 index 00000000..062742fb --- /dev/null +++ b/bot/resources/pride/gender_options.json @@ -0,0 +1,41 @@ +{ + "agender": "agender", + "androgyne": "androgyne", + "androgynous": "androgyne", + "aromantic": "aromantic", + "aro": "aromantic", + "ace": "asexual", + "asexual": "asexual", + "bigender": "bigender", + "bisexual": "bisexual", + "bi": "bisexual", + "demiboy": "demiboy", + "demigirl": "demigirl", + "demi": "demisexual", + "demisexual": "demisexual", + "gay": "gay", + "lgbt": "gay", + "queer": "gay", + "homosexual": "gay", + "fluid": "genderfluid", + "genderfluid": "genderfluid", + "genderqueer": "genderqueer", + "intersex": "intersex", + "lesbian": "lesbian", + "non-binary": "nonbinary", + "enby": "nonbinary", + "nb": "nonbinary", + "nonbinary": "nonbinary", + "omnisexual": "omnisexual", + "omni": "omnisexual", + "pansexual": "pansexual", + "pan": "pansexual", + "pangender": "pangender", + "poly": "polysexual", + "polysexual": "polysexual", + "polyamory": "polyamory", + "polyamorous": "polyamory", + "transgender": "transgender", + "trans": "transgender", + "trigender": "trigender" +} -- cgit v1.2.3 From dd59719df6a36f6cea98faba262fa171ce3289f0 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 18 Feb 2021 19:25:47 +0000 Subject: Merge all avatar cogs into one. Also includes some in_executor and max_concurrency code. --- bot/exts/easter/avatar_easterifier.py | 128 ------------- bot/exts/evergreen/8bitify.py | 54 ------ bot/exts/evergreen/pfp_modify.py | 337 ++++++++++++++++++++++++++++++++++ bot/exts/halloween/spookyavatar.py | 52 ------ bot/exts/pride/pride_avatar.py | 177 ------------------ 5 files changed, 337 insertions(+), 411 deletions(-) delete mode 100644 bot/exts/easter/avatar_easterifier.py delete mode 100644 bot/exts/evergreen/8bitify.py create mode 100644 bot/exts/evergreen/pfp_modify.py delete mode 100644 bot/exts/halloween/spookyavatar.py delete mode 100644 bot/exts/pride/pride_avatar.py diff --git a/bot/exts/easter/avatar_easterifier.py b/bot/exts/easter/avatar_easterifier.py deleted file mode 100644 index 8e8a3500..00000000 --- a/bot/exts/easter/avatar_easterifier.py +++ /dev/null @@ -1,128 +0,0 @@ -import asyncio -import logging -from io import BytesIO -from pathlib import Path -from typing import Tuple, Union - -import discord -from PIL import Image -from PIL.ImageOps import posterize -from discord.ext import commands - -log = logging.getLogger(__name__) - -COLOURS = [ - (255, 247, 0), (255, 255, 224), (0, 255, 127), (189, 252, 201), (255, 192, 203), - (255, 160, 122), (181, 115, 220), (221, 160, 221), (200, 162, 200), (238, 130, 238), - (135, 206, 235), (0, 204, 204), (64, 224, 208) -] # Pastel colours - Easter-like - - -class AvatarEasterifier(commands.Cog): - """Put an Easter spin on your avatar or image!""" - - def __init__(self, bot: commands.Bot): - self.bot = bot - - @staticmethod - def closest(x: Tuple[int, int, int]) -> Tuple[int, int, int]: - """ - Finds the closest easter colour to a given pixel. - - Returns a merge between the original colour and the closest colour - """ - r1, g1, b1 = x - - def distance(point: Tuple[int, int, int]) -> Tuple[int, int, int]: - """Finds the difference between a pastel colour and the original pixel colour.""" - r2, g2, b2 = point - return ((r1 - r2)**2 + (g1 - g2)**2 + (b1 - b2)**2) - - closest_colours = sorted(COLOURS, key=lambda point: distance(point)) - r2, g2, b2 = closest_colours[0] - r = (r1 + r2) // 2 - g = (g1 + g2) // 2 - b = (b1 + b2) // 2 - - return (r, g, b) - - @commands.command(pass_context=True, aliases=["easterify"]) - async def avatareasterify(self, ctx: commands.Context, *colours: Union[discord.Colour, str]) -> None: - """ - This "Easterifies" the user's avatar. - - Given colours will produce a personalised egg in the corner, similar to the egg_decorate command. - If colours are not given, a nice little chocolate bunny will sit in the corner. - Colours are split by spaces, unless you wrap the colour name in double quotes. - Discord colour names, HTML colour names, XKCD colour names and hex values are accepted. - """ - async def send(*args, **kwargs) -> str: - """ - This replaces the original ctx.send. - - When invoking the egg decorating command, the egg itself doesn't print to to the channel. - Returns the message content so that if any errors occur, the error message can be output. - """ - if args: - return args[0] - - async with ctx.typing(): - - # Grabs image of avatar - image_bytes = await ctx.author.avatar_url_as(size=256).read() - - old = Image.open(BytesIO(image_bytes)) - old = old.convert("RGBA") - - # Grabs alpha channel since posterize can't be used with an RGBA image. - alpha = old.getchannel("A").getdata() - old = old.convert("RGB") - old = posterize(old, 6) - - data = old.getdata() - setted_data = set(data) - new_d = {} - - for x in setted_data: - new_d[x] = self.closest(x) - await asyncio.sleep(0) # Ensures discord doesn't break in the background. - new_data = [(*new_d[x], alpha[i]) if x in new_d else x for i, x in enumerate(data)] - - im = Image.new("RGBA", old.size) - im.putdata(new_data) - - if colours: - send_message = ctx.send - ctx.send = send # Assigns ctx.send to a fake send - egg = await ctx.invoke(self.bot.get_command("eggdecorate"), *colours) - if isinstance(egg, str): # When an error message occurs in eggdecorate. - return await send_message(egg) - - ratio = 64 / egg.height - egg = egg.resize((round(egg.width * ratio), round(egg.height * ratio))) - egg = egg.convert("RGBA") - im.alpha_composite(egg, (im.width - egg.width, (im.height - egg.height)//2)) # Right centre. - ctx.send = send_message # Reassigns ctx.send - else: - bunny = Image.open(Path("bot/resources/easter/chocolate_bunny.png")) - im.alpha_composite(bunny, (im.width - bunny.width, (im.height - bunny.height)//2)) # Right centre. - - bufferedio = BytesIO() - im.save(bufferedio, format="PNG") - - bufferedio.seek(0) - - file = discord.File(bufferedio, filename="easterified_avatar.png") # Creates file to be used in embed - embed = discord.Embed( - name="Your Lovely Easterified Avatar", - description="Here is your lovely avatar, all bright and colourful\nwith Easter pastel colours. Enjoy :D" - ) - embed.set_image(url="attachment://easterified_avatar.png") - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) - - await ctx.send(file=file, embed=embed) - - -def setup(bot: commands.Bot) -> None: - """Avatar Easterifier Cog load.""" - bot.add_cog(AvatarEasterifier(bot)) diff --git a/bot/exts/evergreen/8bitify.py b/bot/exts/evergreen/8bitify.py deleted file mode 100644 index 54e68f80..00000000 --- a/bot/exts/evergreen/8bitify.py +++ /dev/null @@ -1,54 +0,0 @@ -from io import BytesIO - -import discord -from PIL import Image -from discord.ext import commands - - -class EightBitify(commands.Cog): - """Make your avatar 8bit!""" - - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - - @staticmethod - def pixelate(image: Image) -> Image: - """Takes an image and pixelates it.""" - return image.resize((32, 32), resample=Image.NEAREST).resize((1024, 1024), resample=Image.NEAREST) - - @staticmethod - def quantize(image: Image) -> Image: - """Reduces colour palette to 256 colours.""" - return image.quantize() - - @commands.command(name="8bitify") - async def eightbit_command(self, ctx: commands.Context) -> None: - """Pixelates your avatar and changes the palette to an 8bit one.""" - async with ctx.typing(): - image_bytes = await ctx.author.avatar_url.read() - avatar = Image.open(BytesIO(image_bytes)) - avatar = avatar.convert("RGBA").resize((1024, 1024)) - - eightbit = self.pixelate(avatar) - eightbit = self.quantize(eightbit) - - bufferedio = BytesIO() - eightbit.save(bufferedio, format="PNG") - bufferedio.seek(0) - - file = discord.File(bufferedio, filename="8bitavatar.png") - - embed = discord.Embed( - title="Your 8-bit avatar", - description='Here is your avatar. I think it looks all cool and "retro"' - ) - - embed.set_image(url="attachment://8bitavatar.png") - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) - - await ctx.send(file=file, embed=embed) - - -def setup(bot: commands.Bot) -> None: - """Cog load.""" - bot.add_cog(EightBitify(bot)) diff --git a/bot/exts/evergreen/pfp_modify.py b/bot/exts/evergreen/pfp_modify.py new file mode 100644 index 00000000..f356d2f6 --- /dev/null +++ b/bot/exts/evergreen/pfp_modify.py @@ -0,0 +1,337 @@ +import asyncio +import json +import logging +import os +import typing as t +from concurrent.futures import ThreadPoolExecutor +from io import BytesIO +from pathlib import Path + +import aiofiles +import aiohttp +import discord +from PIL import Image, ImageDraw, ImageOps, UnidentifiedImageError +from discord.ext import commands + +from bot.constants import Colours +from bot.utils.halloween import spookifications + +log = logging.getLogger(__name__) + +EASTER_COLOURS = [ + (255, 247, 0), (255, 255, 224), (0, 255, 127), (189, 252, 201), (255, 192, 203), + (255, 160, 122), (181, 115, 220), (221, 160, 221), (200, 162, 200), (238, 130, 238), + (135, 206, 235), (0, 204, 204), (64, 224, 208) +] # Pastel colours - Easter-like + +_EXECUTOR = ThreadPoolExecutor(10) + + +async def in_thread(func: t.Callable, *args) -> asyncio.Future: + """Allows non-async functions to work in async functions.""" + loop = asyncio.get_event_loop() + return await loop.run_in_executor(_EXECUTOR, func, *args) + + +class PfpModify(commands.Cog): + """Various commands for users to change their own profile picture.""" + + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + self.bot.loop.create_task(self.init_cog()) + + async def init_cog(self) -> None: + """Initial load from resources asynchronously.""" + async with aiofiles.open('bot/resources/pride/gender_options.json') as f: + self.GENDER_OPTIONS = json.loads(await f.read()) + + @staticmethod + def closest(x: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: + """ + Finds the closest easter colour to a given pixel. + + Returns a merge between the original colour and the closest colour + """ + r1, g1, b1 = x + + def distance(point: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: + """Finds the difference between a pastel colour and the original pixel colour.""" + r2, g2, b2 = point + return ((r1 - r2)**2 + (g1 - g2)**2 + (b1 - b2)**2) + + closest_colours = sorted(EASTER_COLOURS, key=lambda point: distance(point)) + r2, g2, b2 = closest_colours[0] + r = (r1 + r2) // 2 + g = (g1 + g2) // 2 + b = (b1 + b2) // 2 + + return (r, g, b) + + @staticmethod + def crop_avatar(avatar: Image) -> Image: + """This crops the avatar given into a circle.""" + mask = Image.new("L", avatar.size, 0) + draw = ImageDraw.Draw(mask) + draw.ellipse((0, 0) + avatar.size, fill=255) + avatar.putalpha(mask) + return avatar + + @staticmethod + def crop_ring(ring: Image, px: int) -> Image: + """This crops the given ring into a circle.""" + mask = Image.new("L", ring.size, 0) + draw = ImageDraw.Draw(mask) + draw.ellipse((0, 0) + ring.size, fill=255) + draw.ellipse((px, px, 1024-px, 1024-px), fill=0) + ring.putalpha(mask) + return ring + + def process_options(self, option: str, pixels: int) -> t.Tuple[str, int, str]: + """Does some shared preprocessing for the prideavatar commands.""" + return option.lower(), max(0, min(512, pixels)), self.GENDER_OPTIONS.get(option) + + def process_image( + self, + image_bytes: bytes, + pixels: int, + flag: str + ) -> discord.File: + """Constructs and returns the final image. Used by the pride commands.""" + # This line can raise UnidentifiedImageError and must be handled by the calling func. + avatar = Image.open(BytesIO(image_bytes)) + avatar = avatar.convert("RGBA").resize((1024, 1024)) + + avatar = self.crop_avatar(avatar) + + ring = Image.open(Path(f"bot/resources/pride/flags/{flag}.png")).resize((1024, 1024)) + ring = ring.convert("RGBA") + ring = self.crop_ring(ring, pixels) + + avatar.alpha_composite(ring, (0, 0)) + bufferedio = BytesIO() + avatar.save(bufferedio, format="PNG") + bufferedio.seek(0) + + return discord.File(bufferedio, filename="pride_avatar.png") # Creates file to be used in embed + + async def send_image( + self, + ctx: commands.Context, + image_bytes: bytes, + pixels: int, + flag: str, + option: str + ) -> None: + """Gets and sends the image in an embed. Used by the pride commands.""" + try: + file = await in_thread(self.process_image, image_bytes, pixels, flag) + except UnidentifiedImageError: + ctx.send("Cannot identify image from provided URL") + return + + embed = discord.Embed( + name="Your Lovely Pride Avatar", + description=f"Here is your lovely avatar, surrounded by\n a beautiful {option} flag. Enjoy :D" + ) + embed.set_image(url="attachment://pride_avatar.png") + embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + await ctx.send(file=file, embed=embed) + + @commands.max_concurrency(1, commands.BucketType.guild, wait=True) + @commands.group() + async def pfp_modify(self, ctx: commands.Context) -> None: + """Groups all of the pfp modifing commands to allow a single concurrency limit.""" + if not ctx.invoked_subcommand: + await ctx.send_help(ctx.command) + + @pfp_modify.command(name="8bitify") + async def eightbit_command(self, ctx: commands.Context) -> None: + """Pixelates your avatar and changes the palette to an 8bit one.""" + async with ctx.typing(): + image_bytes = await ctx.author.avatar_url.read() + avatar = Image.open(BytesIO(image_bytes)) + avatar = avatar.convert("RGBA").resize((1024, 1024)) + + # Pixilate and quantize + eightbit = avatar.resize((32, 32), resample=Image.NEAREST).resize((1024, 1024), resample=Image.NEAREST) + eightbit = eightbit.quantize() + + bufferedio = BytesIO() + eightbit.save(bufferedio, format="PNG") + bufferedio.seek(0) + + file = discord.File(bufferedio, filename="8bitavatar.png") + + embed = discord.Embed( + title="Your 8-bit avatar", + description='Here is your avatar. I think it looks all cool and "retro"' + ) + + embed.set_image(url="attachment://8bitavatar.png") + embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + + await ctx.send(file=file, embed=embed) + + @pfp_modify.command(pass_context=True, aliases=["easterify"]) + async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: + """ + This "Easterifies" the user's avatar. + + Given colours will produce a personalised egg in the corner, similar to the egg_decorate command. + If colours are not given, a nice little chocolate bunny will sit in the corner. + Colours are split by spaces, unless you wrap the colour name in double quotes. + Discord colour names, HTML colour names, XKCD colour names and hex values are accepted. + """ + async def send(*args, **kwargs) -> str: + """ + This replaces the original ctx.send. + + When invoking the egg decorating command, the egg itself doesn't print to to the channel. + Returns the message content so that if any errors occur, the error message can be output. + """ + if args: + return args[0] + + async with ctx.typing(): + + # Grabs image of avatar + image_bytes = await ctx.author.avatar_url_as(size=256).read() + + old = Image.open(BytesIO(image_bytes)) + old = old.convert("RGBA") + + # Grabs alpha channel since posterize can't be used with an RGBA image. + alpha = old.getchannel("A").getdata() + old = old.convert("RGB") + old = ImageOps.posterize(old, 6) + + data = old.getdata() + setted_data = set(data) + new_d = {} + + for x in setted_data: + new_d[x] = self.closest(x) + await asyncio.sleep(0) # Ensures discord doesn't break in the background. + new_data = [(*new_d[x], alpha[i]) if x in new_d else x for i, x in enumerate(data)] + + im = Image.new("RGBA", old.size) + im.putdata(new_data) + + if colours: + send_message = ctx.send + ctx.send = send # Assigns ctx.send to a fake send + egg = await ctx.invoke(self.bot.get_command("eggdecorate"), *colours) + if isinstance(egg, str): # When an error message occurs in eggdecorate. + return await send_message(egg) + + ratio = 64 / egg.height + egg = egg.resize((round(egg.width * ratio), round(egg.height * ratio))) + egg = egg.convert("RGBA") + im.alpha_composite(egg, (im.width - egg.width, (im.height - egg.height)//2)) # Right centre. + ctx.send = send_message # Reassigns ctx.send + else: + bunny = Image.open(Path("bot/resources/easter/chocolate_bunny.png")) + im.alpha_composite(bunny, (im.width - bunny.width, (im.height - bunny.height)//2)) # Right centre. + + bufferedio = BytesIO() + im.save(bufferedio, format="PNG") + + bufferedio.seek(0) + + file = discord.File(bufferedio, filename="easterified_avatar.png") # Creates file to be used in embed + embed = discord.Embed( + name="Your Lovely Easterified Avatar", + description="Here is your lovely avatar, all bright and colourful\nwith Easter pastel colours. Enjoy :D" + ) + embed.set_image(url="attachment://easterified_avatar.png") + embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + + await ctx.send(file=file, embed=embed) + + @pfp_modify.group(aliases=["avatarpride", "pridepfp", "prideprofile"], invoke_without_command=True) + async def prideavatar(self, ctx: commands.Context, option: str = "lgbt", pixels: int = 64) -> None: + """ + This surrounds an avatar with a border of a specified LGBT flag. + + This defaults to the LGBT rainbow flag if none is given. + The amount of pixels can be given which determines the thickness of the flag border. + This has a maximum of 512px and defaults to a 64px border. + The full image is 1024x1024. + """ + option, pixels, flag = self.process_options(option, pixels) + if flag is None: + return await ctx.send("I don't have that flag!") + + async with ctx.typing(): + image_bytes = await ctx.author.avatar_url.read() + await self.send_image(ctx, image_bytes, pixels, flag, option) + + @prideavatar.command() + async def image(self, ctx: commands.Context, url: str, option: str = "lgbt", pixels: int = 64) -> None: + """ + This surrounds the image specified by the URL with a border of a specified LGBT flag. + + This defaults to the LGBT rainbow flag if none is given. + The amount of pixels can be given which determines the thickness of the flag border. + This has a maximum of 512px and defaults to a 64px border. + The full image is 1024x1024. + """ + option, pixels, flag = self.process_options(option, pixels) + if flag is None: + return await ctx.send("I don't have that flag!") + + async with ctx.typing(): + async with aiohttp.ClientSession() as session: + try: + response = await session.get(url) + except aiohttp.client_exceptions.ClientConnectorError: + return await ctx.send("Cannot connect to provided URL!") + except aiohttp.client_exceptions.InvalidURL: + return await ctx.send("Invalid URL!") + if response.status != 200: + return await ctx.send("Bad response from provided URL!") + image_bytes = await response.read() + await self.send_image(ctx, image_bytes, pixels, flag, option) + + @prideavatar.command() + async def flags(self, ctx: commands.Context) -> None: + """This lists the flags that can be used with the prideavatar command.""" + choices = sorted(set(self.GENDER_OPTIONS.values())) + options = "• " + "\n• ".join(choices) + embed = discord.Embed( + title="I have the following flags:", + description=options, + colour=Colours.soft_red + ) + + await ctx.send(embed=embed) + + @pfp_modify.command( + name='savatar', + aliases=('spookyavatar', 'spookify'), + brief='Spookify an user\'s avatar.' + ) + async def spooky_avatar(self, ctx: commands.Context, user: discord.Member = None) -> None: + """A command to print the user's spookified avatar.""" + if user is None: + user = ctx.message.author + + async with ctx.typing(): + embed = discord.Embed(colour=0xFF0000) + embed.title = "Is this you or am I just really paranoid?" + embed.set_author(name=str(user.name), icon_url=user.avatar_url) + + image_bytes = await ctx.author.avatar_url.read() + im = Image.open(BytesIO(image_bytes)) + modified_im = spookifications.get_random_effect(im) + modified_im.save(str(ctx.message.id)+'.png') + f = discord.File(str(ctx.message.id)+'.png') + embed.set_image(url='attachment://'+str(ctx.message.id)+'.png') + + await ctx.send(file=f, embed=embed) + os.remove(str(ctx.message.id)+'.png') + + +def setup(bot: commands.Bot) -> None: + """Load the PfpModify cog.""" + bot.add_cog(PfpModify(bot)) diff --git a/bot/exts/halloween/spookyavatar.py b/bot/exts/halloween/spookyavatar.py deleted file mode 100644 index 2d7df678..00000000 --- a/bot/exts/halloween/spookyavatar.py +++ /dev/null @@ -1,52 +0,0 @@ -import logging -import os -from io import BytesIO - -import aiohttp -import discord -from PIL import Image -from discord.ext import commands - -from bot.utils.halloween import spookifications - -log = logging.getLogger(__name__) - - -class SpookyAvatar(commands.Cog): - """A cog that spookifies an avatar.""" - - def __init__(self, bot: commands.Bot): - self.bot = bot - - async def get(self, url: str) -> bytes: - """Returns the contents of the supplied URL.""" - async with aiohttp.ClientSession() as session: - async with session.get(url) as resp: - return await resp.read() - - @commands.command(name='savatar', aliases=('spookyavatar', 'spookify'), - brief='Spookify an user\'s avatar.') - async def spooky_avatar(self, ctx: commands.Context, user: discord.Member = None) -> None: - """A command to print the user's spookified avatar.""" - if user is None: - user = ctx.message.author - - async with ctx.typing(): - embed = discord.Embed(colour=0xFF0000) - embed.title = "Is this you or am I just really paranoid?" - embed.set_author(name=str(user.name), icon_url=user.avatar_url) - - image_bytes = await ctx.author.avatar_url.read() - im = Image.open(BytesIO(image_bytes)) - modified_im = spookifications.get_random_effect(im) - modified_im.save(str(ctx.message.id)+'.png') - f = discord.File(str(ctx.message.id)+'.png') - embed.set_image(url='attachment://'+str(ctx.message.id)+'.png') - - await ctx.send(file=f, embed=embed) - os.remove(str(ctx.message.id)+'.png') - - -def setup(bot: commands.Bot) -> None: - """Spooky avatar Cog load.""" - bot.add_cog(SpookyAvatar(bot)) diff --git a/bot/exts/pride/pride_avatar.py b/bot/exts/pride/pride_avatar.py deleted file mode 100644 index 2eade796..00000000 --- a/bot/exts/pride/pride_avatar.py +++ /dev/null @@ -1,177 +0,0 @@ -import logging -from io import BytesIO -from pathlib import Path -from typing import Tuple - -import aiohttp -import discord -from PIL import Image, ImageDraw, UnidentifiedImageError -from discord.ext.commands import Bot, Cog, Context, group - -from bot.constants import Colours - -log = logging.getLogger(__name__) - -OPTIONS = { - "agender": "agender", - "androgyne": "androgyne", - "androgynous": "androgyne", - "aromantic": "aromantic", - "aro": "aromantic", - "ace": "asexual", - "asexual": "asexual", - "bigender": "bigender", - "bisexual": "bisexual", - "bi": "bisexual", - "demiboy": "demiboy", - "demigirl": "demigirl", - "demi": "demisexual", - "demisexual": "demisexual", - "gay": "gay", - "lgbt": "gay", - "queer": "gay", - "homosexual": "gay", - "fluid": "genderfluid", - "genderfluid": "genderfluid", - "genderqueer": "genderqueer", - "intersex": "intersex", - "lesbian": "lesbian", - "non-binary": "nonbinary", - "enby": "nonbinary", - "nb": "nonbinary", - "nonbinary": "nonbinary", - "omnisexual": "omnisexual", - "omni": "omnisexual", - "pansexual": "pansexual", - "pan": "pansexual", - "pangender": "pangender", - "poly": "polysexual", - "polysexual": "polysexual", - "polyamory": "polyamory", - "polyamorous": "polyamory", - "transgender": "transgender", - "trans": "transgender", - "trigender": "trigender" -} - - -class PrideAvatar(Cog): - """Put an LGBT spin on your avatar!""" - - def __init__(self, bot: Bot): - self.bot = bot - - @staticmethod - def crop_avatar(avatar: Image) -> Image: - """This crops the avatar into a circle.""" - mask = Image.new("L", avatar.size, 0) - draw = ImageDraw.Draw(mask) - draw.ellipse((0, 0) + avatar.size, fill=255) - avatar.putalpha(mask) - return avatar - - @staticmethod - def crop_ring(ring: Image, px: int) -> Image: - """This crops the ring into a circle.""" - mask = Image.new("L", ring.size, 0) - draw = ImageDraw.Draw(mask) - draw.ellipse((0, 0) + ring.size, fill=255) - draw.ellipse((px, px, 1024-px, 1024-px), fill=0) - ring.putalpha(mask) - return ring - - @staticmethod - def process_options(option: str, pixels: int) -> Tuple[str, int, str]: - """Does some shared preprocessing for the prideavatar commands.""" - return option.lower(), max(0, min(512, pixels)), OPTIONS.get(option) - - async def process_image(self, ctx: Context, image_bytes: bytes, pixels: int, flag: str, option: str) -> None: - """Constructs the final image, embeds it, and sends it.""" - try: - avatar = Image.open(BytesIO(image_bytes)) - except UnidentifiedImageError: - return await ctx.send("Cannot identify image from provided URL") - avatar = avatar.convert("RGBA").resize((1024, 1024)) - - avatar = self.crop_avatar(avatar) - - ring = Image.open(Path(f"bot/resources/pride/flags/{flag}.png")).resize((1024, 1024)) - ring = ring.convert("RGBA") - ring = self.crop_ring(ring, pixels) - - avatar.alpha_composite(ring, (0, 0)) - bufferedio = BytesIO() - avatar.save(bufferedio, format="PNG") - bufferedio.seek(0) - - file = discord.File(bufferedio, filename="pride_avatar.png") # Creates file to be used in embed - embed = discord.Embed( - name="Your Lovely Pride Avatar", - description=f"Here is your lovely avatar, surrounded by\n a beautiful {option} flag. Enjoy :D" - ) - embed.set_image(url="attachment://pride_avatar.png") - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) - await ctx.send(file=file, embed=embed) - - @group(aliases=["avatarpride", "pridepfp", "prideprofile"], invoke_without_command=True) - async def prideavatar(self, ctx: Context, option: str = "lgbt", pixels: int = 64) -> None: - """ - This surrounds an avatar with a border of a specified LGBT flag. - - This defaults to the LGBT rainbow flag if none is given. - The amount of pixels can be given which determines the thickness of the flag border. - This has a maximum of 512px and defaults to a 64px border. - The full image is 1024x1024. - """ - option, pixels, flag = self.process_options(option, pixels) - if flag is None: - return await ctx.send("I don't have that flag!") - - async with ctx.typing(): - image_bytes = await ctx.author.avatar_url.read() - await self.process_image(ctx, image_bytes, pixels, flag, option) - - @prideavatar.command() - async def image(self, ctx: Context, url: str, option: str = "lgbt", pixels: int = 64) -> None: - """ - This surrounds the image specified by the URL with a border of a specified LGBT flag. - - This defaults to the LGBT rainbow flag if none is given. - The amount of pixels can be given which determines the thickness of the flag border. - This has a maximum of 512px and defaults to a 64px border. - The full image is 1024x1024. - """ - option, pixels, flag = self.process_options(option, pixels) - if flag is None: - return await ctx.send("I don't have that flag!") - - async with ctx.typing(): - async with aiohttp.ClientSession() as session: - try: - response = await session.get(url) - except aiohttp.client_exceptions.ClientConnectorError: - return await ctx.send("Cannot connect to provided URL!") - except aiohttp.client_exceptions.InvalidURL: - return await ctx.send("Invalid URL!") - if response.status != 200: - return await ctx.send("Bad response from provided URL!") - image_bytes = await response.read() - await self.process_image(ctx, image_bytes, pixels, flag, option) - - @prideavatar.command() - async def flags(self, ctx: Context) -> None: - """This lists the flags that can be used with the prideavatar command.""" - choices = sorted(set(OPTIONS.values())) - options = "• " + "\n• ".join(choices) - embed = discord.Embed( - title="I have the following flags:", - description=options, - colour=Colours.soft_red - ) - - await ctx.send(embed=embed) - - -def setup(bot: Bot) -> None: - """Cog load.""" - bot.add_cog(PrideAvatar(bot)) -- cgit v1.2.3 From e79a7730a3d8151622670f9ba38d05f2b267e2ec Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 18 Feb 2021 19:32:14 +0000 Subject: Log what func is being ran in the executor. --- bot/exts/evergreen/pfp_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/pfp_modify.py b/bot/exts/evergreen/pfp_modify.py index f356d2f6..4245432e 100644 --- a/bot/exts/evergreen/pfp_modify.py +++ b/bot/exts/evergreen/pfp_modify.py @@ -29,6 +29,7 @@ _EXECUTOR = ThreadPoolExecutor(10) async def in_thread(func: t.Callable, *args) -> asyncio.Future: """Allows non-async functions to work in async functions.""" + log.trace(f"Running {func.__name__} in an executor.") loop = asyncio.get_event_loop() return await loop.run_in_executor(_EXECUTOR, func, *args) @@ -138,7 +139,6 @@ class PfpModify(commands.Cog): await ctx.send(file=file, embed=embed) @commands.max_concurrency(1, commands.BucketType.guild, wait=True) - @commands.group() async def pfp_modify(self, ctx: commands.Context) -> None: """Groups all of the pfp modifing commands to allow a single concurrency limit.""" if not ctx.invoked_subcommand: -- cgit v1.2.3 From 032d4ae8300ed4570e0b471dd49f628f446cb1fa Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 18 Feb 2021 19:43:26 +0000 Subject: Add root alias support for commands --- bot/__init__.py | 9 +++++++++ bot/bot.py | 41 ++++++++++++++++++++++++++++++++++++++++ bot/command.py | 18 ++++++++++++++++++ bot/exts/evergreen/help.py | 4 +++- bot/exts/evergreen/pfp_modify.py | 3 ++- 5 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 bot/command.py diff --git a/bot/__init__.py b/bot/__init__.py index bdb18666..c8550537 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -2,10 +2,13 @@ import asyncio import logging import logging.handlers import os +from functools import partial, partialmethod from pathlib import Path import arrow +from discord.ext import commands +from bot.command import Command from bot.constants import Client @@ -70,3 +73,9 @@ logging.getLogger().info('Logging initialization complete') # On Windows, the selector event loop is required for aiodns. if os.name == "nt": asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + +# Monkey-patch discord.py decorators to use the Command subclass which supports root aliases. +# Must be patched before any cogs are added. +commands.command = partial(commands.command, cls=Command) +commands.GroupMixin.command = partialmethod(commands.GroupMixin.command, cls=Command) diff --git a/bot/bot.py b/bot/bot.py index 97b09243..f51139bb 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -64,6 +64,26 @@ class Bot(commands.Bot): super().add_cog(cog) log.info(f"Cog loaded: {cog.qualified_name}") + def add_command(self, command: commands.Command) -> None: + """Add `command` as normal and then add its root aliases to the bot.""" + super().add_command(command) + self._add_root_aliases(command) + + def remove_command(self, name: str) -> Optional[commands.Command]: + """ + Remove a command/alias as normal and then remove its root aliases from the bot. + + Individual root aliases cannot be removed by this function. + To remove them, either remove the entire command or manually edit `bot.all_commands`. + """ + command = super().remove_command(name) + if command is None: + # Even if it's a root alias, there's no way to get the Bot instance to remove the alias. + return + + self._remove_root_aliases(command) + return command + async def on_command_error(self, context: commands.Context, exception: DiscordException) -> None: """Check command errors for UserInputError and reset the cooldown if thrown.""" if isinstance(exception, commands.UserInputError): @@ -124,6 +144,27 @@ class Bot(commands.Bot): """ await self._guild_available.wait() + def _add_root_aliases(self, command: commands.Command) -> None: + """Recursively add root aliases for `command` and any of its subcommands.""" + if isinstance(command, commands.Group): + for subcommand in command.commands: + self._add_root_aliases(subcommand) + + for alias in getattr(command, "root_aliases", ()): + if alias in self.all_commands: + raise commands.CommandRegistrationError(alias, alias_conflict=True) + + self.all_commands[alias] = command + + def _remove_root_aliases(self, command: commands.Command) -> None: + """Recursively remove root aliases for `command` and any of its subcommands.""" + if isinstance(command, commands.Group): + for subcommand in command.commands: + self._remove_root_aliases(subcommand) + + for alias in getattr(command, "root_aliases", ()): + self.all_commands.pop(alias, None) + _allowed_roles = [discord.Object(id_) for id_ in constants.MODERATION_ROLES] diff --git a/bot/command.py b/bot/command.py new file mode 100644 index 00000000..0fb900f7 --- /dev/null +++ b/bot/command.py @@ -0,0 +1,18 @@ +from discord.ext import commands + + +class Command(commands.Command): + """ + A `discord.ext.commands.Command` subclass which supports root aliases. + + A `root_aliases` keyword argument is added, which is a sequence of alias names that will act as + top-level commands rather than being aliases of the command's group. It's stored as an attribute + also named `root_aliases`. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.root_aliases = kwargs.get("root_aliases", []) + + if not isinstance(self.root_aliases, (list, tuple)): + raise TypeError("Root aliases of a command must be a list or a tuple of strings.") diff --git a/bot/exts/evergreen/help.py b/bot/exts/evergreen/help.py index 91147243..f557e42e 100644 --- a/bot/exts/evergreen/help.py +++ b/bot/exts/evergreen/help.py @@ -289,7 +289,9 @@ class HelpSession: parent = self.query.full_parent_name + ' ' if self.query.parent else '' paginator.add_line(f'**```{prefix}{parent}{signature}```**') - aliases = ', '.join(f'`{a}`' for a in self.query.aliases) + aliases = [f"`{alias}`" if not parent else f"`{parent} {alias}`" for alias in self.query.aliases] + aliases += [f"`{alias}`" for alias in getattr(self.query, "root_aliases", ())] + aliases = ", ".join(sorted(aliases)) if aliases: paginator.add_line(f'**Can also use:** {aliases}\n') diff --git a/bot/exts/evergreen/pfp_modify.py b/bot/exts/evergreen/pfp_modify.py index 4245432e..a3f7e3f8 100644 --- a/bot/exts/evergreen/pfp_modify.py +++ b/bot/exts/evergreen/pfp_modify.py @@ -139,12 +139,13 @@ class PfpModify(commands.Cog): await ctx.send(file=file, embed=embed) @commands.max_concurrency(1, commands.BucketType.guild, wait=True) + @commands.group() async def pfp_modify(self, ctx: commands.Context) -> None: """Groups all of the pfp modifing commands to allow a single concurrency limit.""" if not ctx.invoked_subcommand: await ctx.send_help(ctx.command) - @pfp_modify.command(name="8bitify") + @pfp_modify.command(name="8bitify", root_aliases=("8bitify",)) async def eightbit_command(self, ctx: commands.Context) -> None: """Pixelates your avatar and changes the palette to an 8bit one.""" async with ctx.typing(): -- cgit v1.2.3 From c7de5a2577b4f2975b3fab683f2d7459c5bda636 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 18 Feb 2021 22:04:27 +0000 Subject: Extend root aliases to support commands.Group --- bot/__init__.py | 6 +++++- bot/exts/evergreen/pfp_modify.py | 9 +++++++-- bot/group.py | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 bot/group.py diff --git a/bot/__init__.py b/bot/__init__.py index c8550537..d0992912 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -10,6 +10,7 @@ from discord.ext import commands from bot.command import Command from bot.constants import Client +from bot.group import Group # Configure the "TRACE" logging level (e.g. "log.trace(message)") @@ -75,7 +76,10 @@ if os.name == "nt": asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) -# Monkey-patch discord.py decorators to use the Command subclass which supports root aliases. +# Monkey-patch discord.py decorators to use the both the Command and Group subclasses which supports root aliases. # Must be patched before any cogs are added. commands.command = partial(commands.command, cls=Command) commands.GroupMixin.command = partialmethod(commands.GroupMixin.command, cls=Command) + +commands.group = partial(commands.group, cls=Group) +commands.GroupMixin.group = partialmethod(commands.GroupMixin.group, cls=Group) diff --git a/bot/exts/evergreen/pfp_modify.py b/bot/exts/evergreen/pfp_modify.py index a3f7e3f8..8a3eb77c 100644 --- a/bot/exts/evergreen/pfp_modify.py +++ b/bot/exts/evergreen/pfp_modify.py @@ -173,7 +173,7 @@ class PfpModify(commands.Cog): await ctx.send(file=file, embed=embed) - @pfp_modify.command(pass_context=True, aliases=["easterify"]) + @pfp_modify.command(pass_context=True, aliases=["easterify"], root_aliases=("easterify", "avatareasterify")) async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: """ This "Easterifies" the user's avatar. @@ -249,7 +249,11 @@ class PfpModify(commands.Cog): await ctx.send(file=file, embed=embed) - @pfp_modify.group(aliases=["avatarpride", "pridepfp", "prideprofile"], invoke_without_command=True) + @pfp_modify.group( + aliases=["avatarpride", "pridepfp", "prideprofile"], + root_aliases=("prideavatar", "avatarpride", "pridepfp", "prideprofile"), + invoke_without_command=True + ) async def prideavatar(self, ctx: commands.Context, option: str = "lgbt", pixels: int = 64) -> None: """ This surrounds an avatar with a border of a specified LGBT flag. @@ -310,6 +314,7 @@ class PfpModify(commands.Cog): @pfp_modify.command( name='savatar', aliases=('spookyavatar', 'spookify'), + root_aliases=('spookyavatar', 'spookify'), brief='Spookify an user\'s avatar.' ) async def spooky_avatar(self, ctx: commands.Context, user: discord.Member = None) -> None: diff --git a/bot/group.py b/bot/group.py new file mode 100644 index 00000000..77092adf --- /dev/null +++ b/bot/group.py @@ -0,0 +1,18 @@ +from discord.ext import commands + + +class Group(commands.Group): + """ + A `discord.ext.commands.Group` subclass which supports root aliases. + + A `root_aliases` keyword argument is added, which is a sequence of alias names that will act as + top-level commands rather than being aliases of the command's group. It's stored as an attribute + also named `root_aliases`. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.root_aliases = kwargs.get("root_aliases", []) + + if not isinstance(self.root_aliases, (list, tuple)): + raise TypeError("Root aliases of a command must be a list or a tuple of strings.") -- cgit v1.2.3 From a3a6550b80543c3ce7da28cf00b450b561e57ca9 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 19 Feb 2021 23:25:08 +0000 Subject: Share common code. --- bot/exts/evergreen/pfp_modify.py | 239 ++++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 117 deletions(-) diff --git a/bot/exts/evergreen/pfp_modify.py b/bot/exts/evergreen/pfp_modify.py index 8a3eb77c..5fb3b89d 100644 --- a/bot/exts/evergreen/pfp_modify.py +++ b/bot/exts/evergreen/pfp_modify.py @@ -1,16 +1,15 @@ import asyncio import json import logging -import os import typing as t from concurrent.futures import ThreadPoolExecutor from io import BytesIO from pathlib import Path import aiofiles -import aiohttp import discord -from PIL import Image, ImageDraw, ImageOps, UnidentifiedImageError +from PIL import Image, ImageDraw, ImageOps +from aiohttp import client_exceptions from discord.ext import commands from bot.constants import Colours @@ -69,7 +68,7 @@ class PfpModify(commands.Cog): return (r, g, b) @staticmethod - def crop_avatar(avatar: Image) -> Image: + def crop_avatar_circle(avatar: Image) -> Image: """This crops the avatar given into a circle.""" mask = Image.new("L", avatar.size, 0) draw = ImageDraw.Draw(mask) @@ -87,58 +86,71 @@ class PfpModify(commands.Cog): ring.putalpha(mask) return ring - def process_options(self, option: str, pixels: int) -> t.Tuple[str, int, str]: - """Does some shared preprocessing for the prideavatar commands.""" - return option.lower(), max(0, min(512, pixels)), self.GENDER_OPTIONS.get(option) + @staticmethod + def _apply_effect(image_bytes: bytes, effect: t.Callable, *args) -> discord.File: + im = Image.open(BytesIO(image_bytes)) + im = im.convert("RGBA") + im = effect(im, *args) - def process_image( - self, - image_bytes: bytes, + bufferedio = BytesIO() + im.save(bufferedio, format="PNG") + bufferedio.seek(0) + + return discord.File(bufferedio, filename="modified_avatar.png") + + @staticmethod + def pridify_effect( + image: Image, pixels: int, flag: str - ) -> discord.File: - """Constructs and returns the final image. Used by the pride commands.""" - # This line can raise UnidentifiedImageError and must be handled by the calling func. - avatar = Image.open(BytesIO(image_bytes)) - avatar = avatar.convert("RGBA").resize((1024, 1024)) - - avatar = self.crop_avatar(avatar) + ) -> Image: + """Applies the pride effect to the given image.""" + image = image.resize((1024, 1024)) + image = PfpModify.crop_avatar_circle(image) ring = Image.open(Path(f"bot/resources/pride/flags/{flag}.png")).resize((1024, 1024)) ring = ring.convert("RGBA") - ring = self.crop_ring(ring, pixels) - - avatar.alpha_composite(ring, (0, 0)) - bufferedio = BytesIO() - avatar.save(bufferedio, format="PNG") - bufferedio.seek(0) + ring = PfpModify.crop_ring(ring, pixels) - return discord.File(bufferedio, filename="pride_avatar.png") # Creates file to be used in embed + image.alpha_composite(ring, (0, 0)) + return image - async def send_image( - self, - ctx: commands.Context, - image_bytes: bytes, - pixels: int, - flag: str, - option: str - ) -> None: - """Gets and sends the image in an embed. Used by the pride commands.""" - try: - file = await in_thread(self.process_image, image_bytes, pixels, flag) - except UnidentifiedImageError: - ctx.send("Cannot identify image from provided URL") - return + @staticmethod + def eight_bitify_effect(image: Image) -> Image: + """Applies the 8bit effect to the given image.""" + image = image.resize((32, 32), resample=Image.NEAREST).resize((1024, 1024), resample=Image.NEAREST) + return image.quantize() - embed = discord.Embed( - name="Your Lovely Pride Avatar", - description=f"Here is your lovely avatar, surrounded by\n a beautiful {option} flag. Enjoy :D" - ) - embed.set_image(url="attachment://pride_avatar.png") - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) - await ctx.send(file=file, embed=embed) + @staticmethod + def easterify_effect(image: Image, overlay_image: Image = None) -> Image: + """Applies the easter effect to the given image.""" + if overlay_image: + ratio = 64 / overlay_image.height + overlay_image = overlay_image.resize(( + round(overlay_image.width * ratio), + round(overlay_image.height * ratio) + )) + overlay_image = overlay_image.convert("RGBA") + else: + overlay_image = overlay_image = Image.open(Path("bot/resources/easter/chocolate_bunny.png")) + + alpha = image.getchannel("A").getdata() + image = image.convert("RGB") + image = ImageOps.posterize(image, 6) + + data = image.getdata() + setted_data = set(data) + new_d = {} + + for x in setted_data: + new_d[x] = PfpModify.closest(x) + new_data = [(*new_d[x], alpha[i]) if x in new_d else x for i, x in enumerate(data)] + + im = Image.new("RGBA", image.size) + im.putdata(new_data) + im.alpha_composite(overlay_image, (im.width - overlay_image.width, (im.height - overlay_image.height)//2)) + return im - @commands.max_concurrency(1, commands.BucketType.guild, wait=True) @commands.group() async def pfp_modify(self, ctx: commands.Context) -> None: """Groups all of the pfp modifing commands to allow a single concurrency limit.""" @@ -150,25 +162,18 @@ class PfpModify(commands.Cog): """Pixelates your avatar and changes the palette to an 8bit one.""" async with ctx.typing(): image_bytes = await ctx.author.avatar_url.read() - avatar = Image.open(BytesIO(image_bytes)) - avatar = avatar.convert("RGBA").resize((1024, 1024)) - - # Pixilate and quantize - eightbit = avatar.resize((32, 32), resample=Image.NEAREST).resize((1024, 1024), resample=Image.NEAREST) - eightbit = eightbit.quantize() - - bufferedio = BytesIO() - eightbit.save(bufferedio, format="PNG") - bufferedio.seek(0) - - file = discord.File(bufferedio, filename="8bitavatar.png") + file = await in_thread( + self._apply_effect, + image_bytes, + self.eight_bitify_effect + ) embed = discord.Embed( title="Your 8-bit avatar", description='Here is your avatar. I think it looks all cool and "retro"' ) - embed.set_image(url="attachment://8bitavatar.png") + embed.set_image(url="attachment://modified_avatar.png") embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) await ctx.send(file=file, embed=embed) @@ -194,61 +199,59 @@ class PfpModify(commands.Cog): return args[0] async with ctx.typing(): - - # Grabs image of avatar - image_bytes = await ctx.author.avatar_url_as(size=256).read() - - old = Image.open(BytesIO(image_bytes)) - old = old.convert("RGBA") - - # Grabs alpha channel since posterize can't be used with an RGBA image. - alpha = old.getchannel("A").getdata() - old = old.convert("RGB") - old = ImageOps.posterize(old, 6) - - data = old.getdata() - setted_data = set(data) - new_d = {} - - for x in setted_data: - new_d[x] = self.closest(x) - await asyncio.sleep(0) # Ensures discord doesn't break in the background. - new_data = [(*new_d[x], alpha[i]) if x in new_d else x for i, x in enumerate(data)] - - im = Image.new("RGBA", old.size) - im.putdata(new_data) - + egg = None if colours: send_message = ctx.send ctx.send = send # Assigns ctx.send to a fake send egg = await ctx.invoke(self.bot.get_command("eggdecorate"), *colours) if isinstance(egg, str): # When an error message occurs in eggdecorate. - return await send_message(egg) - - ratio = 64 / egg.height - egg = egg.resize((round(egg.width * ratio), round(egg.height * ratio))) - egg = egg.convert("RGBA") - im.alpha_composite(egg, (im.width - egg.width, (im.height - egg.height)//2)) # Right centre. + await send_message(egg) + return ctx.send = send_message # Reassigns ctx.send - else: - bunny = Image.open(Path("bot/resources/easter/chocolate_bunny.png")) - im.alpha_composite(bunny, (im.width - bunny.width, (im.height - bunny.height)//2)) # Right centre. - bufferedio = BytesIO() - im.save(bufferedio, format="PNG") - - bufferedio.seek(0) + image_bytes = await ctx.author.avatar_url_as(size=256).read() + file = await in_thread( + self._apply_effect, + image_bytes, + self.easterify_effect, + egg + ) - file = discord.File(bufferedio, filename="easterified_avatar.png") # Creates file to be used in embed embed = discord.Embed( name="Your Lovely Easterified Avatar", description="Here is your lovely avatar, all bright and colourful\nwith Easter pastel colours. Enjoy :D" ) - embed.set_image(url="attachment://easterified_avatar.png") + embed.set_image(url="attachment://modified_avatar.png") embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) await ctx.send(file=file, embed=embed) + async def send_pride_image( + self, + ctx: commands.Context, + image_bytes: bytes, + pixels: int, + flag: str, + option: str + ) -> None: + """Gets and sends the image in an embed. Used by the pride commands.""" + async with ctx.typing(): + file = await in_thread( + self._apply_effect, + image_bytes, + self.pridify_effect, + pixels, + flag + ) + + embed = discord.Embed( + name="Your Lovely Pride Avatar", + description=f"Here is your lovely avatar, surrounded by\n a beautiful {option} flag. Enjoy :D" + ) + embed.set_image(url="attachment://modified_avatar.png") + embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + await ctx.send(file=file, embed=embed) + @pfp_modify.group( aliases=["avatarpride", "pridepfp", "prideprofile"], root_aliases=("prideavatar", "avatarpride", "pridepfp", "prideprofile"), @@ -263,13 +266,15 @@ class PfpModify(commands.Cog): This has a maximum of 512px and defaults to a 64px border. The full image is 1024x1024. """ - option, pixels, flag = self.process_options(option, pixels) + option = option.lower() + pixels = max(0, min(512, pixels)) + flag = self.GENDER_OPTIONS.get(option) if flag is None: return await ctx.send("I don't have that flag!") async with ctx.typing(): image_bytes = await ctx.author.avatar_url.read() - await self.send_image(ctx, image_bytes, pixels, flag, option) + await self.send_pride_image(ctx, image_bytes, pixels, flag, option) @prideavatar.command() async def image(self, ctx: commands.Context, url: str, option: str = "lgbt", pixels: int = 64) -> None: @@ -281,22 +286,24 @@ class PfpModify(commands.Cog): This has a maximum of 512px and defaults to a 64px border. The full image is 1024x1024. """ - option, pixels, flag = self.process_options(option, pixels) + option = option.lower() + pixels = max(0, min(512, pixels)) + flag = self.GENDER_OPTIONS.get(option) if flag is None: return await ctx.send("I don't have that flag!") async with ctx.typing(): - async with aiohttp.ClientSession() as session: + async with self.bot.http_session as session: try: response = await session.get(url) - except aiohttp.client_exceptions.ClientConnectorError: + except client_exceptions.ClientConnectorError: return await ctx.send("Cannot connect to provided URL!") - except aiohttp.client_exceptions.InvalidURL: + except client_exceptions.InvalidURL: return await ctx.send("Invalid URL!") if response.status != 200: return await ctx.send("Bad response from provided URL!") image_bytes = await response.read() - await self.send_image(ctx, image_bytes, pixels, flag, option) + await self.send_pride_image(ctx, image_bytes, pixels, flag, option) @prideavatar.command() async def flags(self, ctx: commands.Context) -> None: @@ -308,7 +315,6 @@ class PfpModify(commands.Cog): description=options, colour=Colours.soft_red ) - await ctx.send(embed=embed) @pfp_modify.command( @@ -323,19 +329,18 @@ class PfpModify(commands.Cog): user = ctx.message.author async with ctx.typing(): - embed = discord.Embed(colour=0xFF0000) - embed.title = "Is this you or am I just really paranoid?" + image_bytes = await ctx.author.avatar_url.read() + file = await in_thread(self._apply_effect, image_bytes, spookifications.get_random_effect) + + embed = discord.Embed( + title="Is this you or am I just really paranoid?", + colour=0xFF0000 + ) embed.set_author(name=str(user.name), icon_url=user.avatar_url) + embed.set_image(url='attachment://modified_avatar.png') + embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) - image_bytes = await ctx.author.avatar_url.read() - im = Image.open(BytesIO(image_bytes)) - modified_im = spookifications.get_random_effect(im) - modified_im.save(str(ctx.message.id)+'.png') - f = discord.File(str(ctx.message.id)+'.png') - embed.set_image(url='attachment://'+str(ctx.message.id)+'.png') - - await ctx.send(file=f, embed=embed) - os.remove(str(ctx.message.id)+'.png') + await ctx.send(file=file, embed=embed) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From 326d50cdc039556e88e0b210a7124a4cc47a9275 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 20 Feb 2021 15:44:47 +0000 Subject: Split effects from cog --- bot/exts/evergreen/pfp_modify.py | 348 --------------------- .../evergreen/profile_pic_modification/__init__.py | 0 .../evergreen/profile_pic_modification/_effects.py | 122 ++++++++ .../profile_pic_modification/pfp_modify.py | 234 ++++++++++++++ 4 files changed, 356 insertions(+), 348 deletions(-) delete mode 100644 bot/exts/evergreen/pfp_modify.py create mode 100644 bot/exts/evergreen/profile_pic_modification/__init__.py create mode 100644 bot/exts/evergreen/profile_pic_modification/_effects.py create mode 100644 bot/exts/evergreen/profile_pic_modification/pfp_modify.py diff --git a/bot/exts/evergreen/pfp_modify.py b/bot/exts/evergreen/pfp_modify.py deleted file mode 100644 index 5fb3b89d..00000000 --- a/bot/exts/evergreen/pfp_modify.py +++ /dev/null @@ -1,348 +0,0 @@ -import asyncio -import json -import logging -import typing as t -from concurrent.futures import ThreadPoolExecutor -from io import BytesIO -from pathlib import Path - -import aiofiles -import discord -from PIL import Image, ImageDraw, ImageOps -from aiohttp import client_exceptions -from discord.ext import commands - -from bot.constants import Colours -from bot.utils.halloween import spookifications - -log = logging.getLogger(__name__) - -EASTER_COLOURS = [ - (255, 247, 0), (255, 255, 224), (0, 255, 127), (189, 252, 201), (255, 192, 203), - (255, 160, 122), (181, 115, 220), (221, 160, 221), (200, 162, 200), (238, 130, 238), - (135, 206, 235), (0, 204, 204), (64, 224, 208) -] # Pastel colours - Easter-like - -_EXECUTOR = ThreadPoolExecutor(10) - - -async def in_thread(func: t.Callable, *args) -> asyncio.Future: - """Allows non-async functions to work in async functions.""" - log.trace(f"Running {func.__name__} in an executor.") - loop = asyncio.get_event_loop() - return await loop.run_in_executor(_EXECUTOR, func, *args) - - -class PfpModify(commands.Cog): - """Various commands for users to change their own profile picture.""" - - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - self.bot.loop.create_task(self.init_cog()) - - async def init_cog(self) -> None: - """Initial load from resources asynchronously.""" - async with aiofiles.open('bot/resources/pride/gender_options.json') as f: - self.GENDER_OPTIONS = json.loads(await f.read()) - - @staticmethod - def closest(x: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: - """ - Finds the closest easter colour to a given pixel. - - Returns a merge between the original colour and the closest colour - """ - r1, g1, b1 = x - - def distance(point: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: - """Finds the difference between a pastel colour and the original pixel colour.""" - r2, g2, b2 = point - return ((r1 - r2)**2 + (g1 - g2)**2 + (b1 - b2)**2) - - closest_colours = sorted(EASTER_COLOURS, key=lambda point: distance(point)) - r2, g2, b2 = closest_colours[0] - r = (r1 + r2) // 2 - g = (g1 + g2) // 2 - b = (b1 + b2) // 2 - - return (r, g, b) - - @staticmethod - def crop_avatar_circle(avatar: Image) -> Image: - """This crops the avatar given into a circle.""" - mask = Image.new("L", avatar.size, 0) - draw = ImageDraw.Draw(mask) - draw.ellipse((0, 0) + avatar.size, fill=255) - avatar.putalpha(mask) - return avatar - - @staticmethod - def crop_ring(ring: Image, px: int) -> Image: - """This crops the given ring into a circle.""" - mask = Image.new("L", ring.size, 0) - draw = ImageDraw.Draw(mask) - draw.ellipse((0, 0) + ring.size, fill=255) - draw.ellipse((px, px, 1024-px, 1024-px), fill=0) - ring.putalpha(mask) - return ring - - @staticmethod - def _apply_effect(image_bytes: bytes, effect: t.Callable, *args) -> discord.File: - im = Image.open(BytesIO(image_bytes)) - im = im.convert("RGBA") - im = effect(im, *args) - - bufferedio = BytesIO() - im.save(bufferedio, format="PNG") - bufferedio.seek(0) - - return discord.File(bufferedio, filename="modified_avatar.png") - - @staticmethod - def pridify_effect( - image: Image, - pixels: int, - flag: str - ) -> Image: - """Applies the pride effect to the given image.""" - image = image.resize((1024, 1024)) - image = PfpModify.crop_avatar_circle(image) - - ring = Image.open(Path(f"bot/resources/pride/flags/{flag}.png")).resize((1024, 1024)) - ring = ring.convert("RGBA") - ring = PfpModify.crop_ring(ring, pixels) - - image.alpha_composite(ring, (0, 0)) - return image - - @staticmethod - def eight_bitify_effect(image: Image) -> Image: - """Applies the 8bit effect to the given image.""" - image = image.resize((32, 32), resample=Image.NEAREST).resize((1024, 1024), resample=Image.NEAREST) - return image.quantize() - - @staticmethod - def easterify_effect(image: Image, overlay_image: Image = None) -> Image: - """Applies the easter effect to the given image.""" - if overlay_image: - ratio = 64 / overlay_image.height - overlay_image = overlay_image.resize(( - round(overlay_image.width * ratio), - round(overlay_image.height * ratio) - )) - overlay_image = overlay_image.convert("RGBA") - else: - overlay_image = overlay_image = Image.open(Path("bot/resources/easter/chocolate_bunny.png")) - - alpha = image.getchannel("A").getdata() - image = image.convert("RGB") - image = ImageOps.posterize(image, 6) - - data = image.getdata() - setted_data = set(data) - new_d = {} - - for x in setted_data: - new_d[x] = PfpModify.closest(x) - new_data = [(*new_d[x], alpha[i]) if x in new_d else x for i, x in enumerate(data)] - - im = Image.new("RGBA", image.size) - im.putdata(new_data) - im.alpha_composite(overlay_image, (im.width - overlay_image.width, (im.height - overlay_image.height)//2)) - return im - - @commands.group() - async def pfp_modify(self, ctx: commands.Context) -> None: - """Groups all of the pfp modifing commands to allow a single concurrency limit.""" - if not ctx.invoked_subcommand: - await ctx.send_help(ctx.command) - - @pfp_modify.command(name="8bitify", root_aliases=("8bitify",)) - async def eightbit_command(self, ctx: commands.Context) -> None: - """Pixelates your avatar and changes the palette to an 8bit one.""" - async with ctx.typing(): - image_bytes = await ctx.author.avatar_url.read() - file = await in_thread( - self._apply_effect, - image_bytes, - self.eight_bitify_effect - ) - - embed = discord.Embed( - title="Your 8-bit avatar", - description='Here is your avatar. I think it looks all cool and "retro"' - ) - - embed.set_image(url="attachment://modified_avatar.png") - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) - - await ctx.send(file=file, embed=embed) - - @pfp_modify.command(pass_context=True, aliases=["easterify"], root_aliases=("easterify", "avatareasterify")) - async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: - """ - This "Easterifies" the user's avatar. - - Given colours will produce a personalised egg in the corner, similar to the egg_decorate command. - If colours are not given, a nice little chocolate bunny will sit in the corner. - Colours are split by spaces, unless you wrap the colour name in double quotes. - Discord colour names, HTML colour names, XKCD colour names and hex values are accepted. - """ - async def send(*args, **kwargs) -> str: - """ - This replaces the original ctx.send. - - When invoking the egg decorating command, the egg itself doesn't print to to the channel. - Returns the message content so that if any errors occur, the error message can be output. - """ - if args: - return args[0] - - async with ctx.typing(): - egg = None - if colours: - send_message = ctx.send - ctx.send = send # Assigns ctx.send to a fake send - egg = await ctx.invoke(self.bot.get_command("eggdecorate"), *colours) - if isinstance(egg, str): # When an error message occurs in eggdecorate. - await send_message(egg) - return - ctx.send = send_message # Reassigns ctx.send - - image_bytes = await ctx.author.avatar_url_as(size=256).read() - file = await in_thread( - self._apply_effect, - image_bytes, - self.easterify_effect, - egg - ) - - embed = discord.Embed( - name="Your Lovely Easterified Avatar", - description="Here is your lovely avatar, all bright and colourful\nwith Easter pastel colours. Enjoy :D" - ) - embed.set_image(url="attachment://modified_avatar.png") - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) - - await ctx.send(file=file, embed=embed) - - async def send_pride_image( - self, - ctx: commands.Context, - image_bytes: bytes, - pixels: int, - flag: str, - option: str - ) -> None: - """Gets and sends the image in an embed. Used by the pride commands.""" - async with ctx.typing(): - file = await in_thread( - self._apply_effect, - image_bytes, - self.pridify_effect, - pixels, - flag - ) - - embed = discord.Embed( - name="Your Lovely Pride Avatar", - description=f"Here is your lovely avatar, surrounded by\n a beautiful {option} flag. Enjoy :D" - ) - embed.set_image(url="attachment://modified_avatar.png") - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) - await ctx.send(file=file, embed=embed) - - @pfp_modify.group( - aliases=["avatarpride", "pridepfp", "prideprofile"], - root_aliases=("prideavatar", "avatarpride", "pridepfp", "prideprofile"), - invoke_without_command=True - ) - async def prideavatar(self, ctx: commands.Context, option: str = "lgbt", pixels: int = 64) -> None: - """ - This surrounds an avatar with a border of a specified LGBT flag. - - This defaults to the LGBT rainbow flag if none is given. - The amount of pixels can be given which determines the thickness of the flag border. - This has a maximum of 512px and defaults to a 64px border. - The full image is 1024x1024. - """ - option = option.lower() - pixels = max(0, min(512, pixels)) - flag = self.GENDER_OPTIONS.get(option) - if flag is None: - return await ctx.send("I don't have that flag!") - - async with ctx.typing(): - image_bytes = await ctx.author.avatar_url.read() - await self.send_pride_image(ctx, image_bytes, pixels, flag, option) - - @prideavatar.command() - async def image(self, ctx: commands.Context, url: str, option: str = "lgbt", pixels: int = 64) -> None: - """ - This surrounds the image specified by the URL with a border of a specified LGBT flag. - - This defaults to the LGBT rainbow flag if none is given. - The amount of pixels can be given which determines the thickness of the flag border. - This has a maximum of 512px and defaults to a 64px border. - The full image is 1024x1024. - """ - option = option.lower() - pixels = max(0, min(512, pixels)) - flag = self.GENDER_OPTIONS.get(option) - if flag is None: - return await ctx.send("I don't have that flag!") - - async with ctx.typing(): - async with self.bot.http_session as session: - try: - response = await session.get(url) - except client_exceptions.ClientConnectorError: - return await ctx.send("Cannot connect to provided URL!") - except client_exceptions.InvalidURL: - return await ctx.send("Invalid URL!") - if response.status != 200: - return await ctx.send("Bad response from provided URL!") - image_bytes = await response.read() - await self.send_pride_image(ctx, image_bytes, pixels, flag, option) - - @prideavatar.command() - async def flags(self, ctx: commands.Context) -> None: - """This lists the flags that can be used with the prideavatar command.""" - choices = sorted(set(self.GENDER_OPTIONS.values())) - options = "• " + "\n• ".join(choices) - embed = discord.Embed( - title="I have the following flags:", - description=options, - colour=Colours.soft_red - ) - await ctx.send(embed=embed) - - @pfp_modify.command( - name='savatar', - aliases=('spookyavatar', 'spookify'), - root_aliases=('spookyavatar', 'spookify'), - brief='Spookify an user\'s avatar.' - ) - async def spooky_avatar(self, ctx: commands.Context, user: discord.Member = None) -> None: - """A command to print the user's spookified avatar.""" - if user is None: - user = ctx.message.author - - async with ctx.typing(): - image_bytes = await ctx.author.avatar_url.read() - file = await in_thread(self._apply_effect, image_bytes, spookifications.get_random_effect) - - embed = discord.Embed( - title="Is this you or am I just really paranoid?", - colour=0xFF0000 - ) - embed.set_author(name=str(user.name), icon_url=user.avatar_url) - embed.set_image(url='attachment://modified_avatar.png') - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) - - await ctx.send(file=file, embed=embed) - - -def setup(bot: commands.Bot) -> None: - """Load the PfpModify cog.""" - bot.add_cog(PfpModify(bot)) diff --git a/bot/exts/evergreen/profile_pic_modification/__init__.py b/bot/exts/evergreen/profile_pic_modification/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py new file mode 100644 index 00000000..fbe5f706 --- /dev/null +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -0,0 +1,122 @@ +import typing as t +from io import BytesIO +from pathlib import Path + +import discord +from PIL import Image, ImageDraw, ImageOps + +EASTER_COLOURS = [ + (255, 247, 0), (255, 255, 224), (0, 255, 127), (189, 252, 201), (255, 192, 203), + (255, 160, 122), (181, 115, 220), (221, 160, 221), (200, 162, 200), (238, 130, 238), + (135, 206, 235), (0, 204, 204), (64, 224, 208) +] # Pastel colours - Easter-like + + +class PfpEffects(): + """Implements various image effects.""" + + @staticmethod + def closest(x: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: + """ + Finds the closest easter colour to a given pixel. + + Returns a merge between the original colour and the closest colour + """ + r1, g1, b1 = x + + def distance(point: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: + """Finds the difference between a pastel colour and the original pixel colour.""" + r2, g2, b2 = point + return ((r1 - r2)**2 + (g1 - g2)**2 + (b1 - b2)**2) + + closest_colours = sorted(EASTER_COLOURS, key=lambda point: distance(point)) + r2, g2, b2 = closest_colours[0] + r = (r1 + r2) // 2 + g = (g1 + g2) // 2 + b = (b1 + b2) // 2 + + return (r, g, b) + + @staticmethod + def crop_avatar_circle(avatar: Image) -> Image: + """This crops the avatar given into a circle.""" + mask = Image.new("L", avatar.size, 0) + draw = ImageDraw.Draw(mask) + draw.ellipse((0, 0) + avatar.size, fill=255) + avatar.putalpha(mask) + return avatar + + @staticmethod + def crop_ring(ring: Image, px: int) -> Image: + """This crops the given ring into a circle.""" + mask = Image.new("L", ring.size, 0) + draw = ImageDraw.Draw(mask) + draw.ellipse((0, 0) + ring.size, fill=255) + draw.ellipse((px, px, 1024-px, 1024-px), fill=0) + ring.putalpha(mask) + return ring + + @staticmethod + def _apply_effect(image_bytes: bytes, effect: t.Callable, *args) -> discord.File: + im = Image.open(BytesIO(image_bytes)) + im = im.convert("RGBA") + im = effect(im, *args) + + bufferedio = BytesIO() + im.save(bufferedio, format="PNG") + bufferedio.seek(0) + + return discord.File(bufferedio, filename="modified_avatar.png") + + @staticmethod + def pridify_effect( + image: Image, + pixels: int, + flag: str + ) -> Image: + """Applies the pride effect to the given image.""" + image = image.resize((1024, 1024)) + image = PfpEffects.crop_avatar_circle(image) + + ring = Image.open(Path(f"bot/resources/pride/flags/{flag}.png")).resize((1024, 1024)) + ring = ring.convert("RGBA") + ring = PfpEffects.crop_ring(ring, pixels) + + image.alpha_composite(ring, (0, 0)) + return image + + @staticmethod + def eight_bitify_effect(image: Image) -> Image: + """Applies the 8bit effect to the given image.""" + image = image.resize((32, 32), resample=Image.NEAREST).resize((1024, 1024), resample=Image.NEAREST) + return image.quantize() + + @staticmethod + def easterify_effect(image: Image, overlay_image: Image = None) -> Image: + """Applies the easter effect to the given image.""" + if overlay_image: + ratio = 64 / overlay_image.height + overlay_image = overlay_image.resize(( + round(overlay_image.width * ratio), + round(overlay_image.height * ratio) + )) + overlay_image = overlay_image.convert("RGBA") + else: + overlay_image = overlay_image = Image.open(Path("bot/resources/easter/chocolate_bunny.png")) + + alpha = image.getchannel("A").getdata() + image = image.convert("RGB") + image = ImageOps.posterize(image, 6) + + data = image.getdata() + setted_data = set(data) + new_d = {} + + for x in setted_data: + new_d[x] = PfpEffects.closest(x) + new_data = [(*new_d[x], alpha[i]) if x in new_d else x for i, x in enumerate(data)] + + im = Image.new("RGBA", image.size) + im.putdata(new_data) + im.alpha_composite(overlay_image, (im.width - overlay_image.width, (im.height - overlay_image.height)//2)) + return im diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py new file mode 100644 index 00000000..51742257 --- /dev/null +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -0,0 +1,234 @@ +import asyncio +import json +import logging +import typing as t +from concurrent.futures import ThreadPoolExecutor + +import aiofiles +import discord +from aiohttp import client_exceptions +from discord.ext import commands + +from bot.constants import Colours +from bot.exts.evergreen.profile_pic_modification._effects import PfpEffects +from bot.utils.halloween import spookifications + +log = logging.getLogger(__name__) + +_EXECUTOR = ThreadPoolExecutor(10) + + +async def in_thread(func: t.Callable, *args) -> asyncio.Future: + """Allows non-async functions to work in async functions.""" + log.trace(f"Running {func.__name__} in an executor.") + loop = asyncio.get_event_loop() + return await loop.run_in_executor(_EXECUTOR, func, *args) + + +class PfpModify(commands.Cog): + """Various commands for users to change their own profile picture.""" + + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + self.bot.loop.create_task(self.init_cog()) + + async def init_cog(self) -> None: + """Initial load from resources asynchronously.""" + async with aiofiles.open('bot/resources/pride/gender_options.json') as f: + self.GENDER_OPTIONS = json.loads(await f.read()) + + @commands.group() + async def pfp_modify(self, ctx: commands.Context) -> None: + """Groups all of the pfp modifing commands to allow a single concurrency limit.""" + if not ctx.invoked_subcommand: + await ctx.send_help(ctx.command) + + @pfp_modify.command(name="8bitify", root_aliases=("8bitify",)) + async def eightbit_command(self, ctx: commands.Context) -> None: + """Pixelates your avatar and changes the palette to an 8bit one.""" + async with ctx.typing(): + image_bytes = await ctx.author.avatar_url.read() + file = await in_thread( + PfpEffects._apply_effect, + image_bytes, + PfpEffects.eight_bitify_effect + ) + + embed = discord.Embed( + title="Your 8-bit avatar", + description='Here is your avatar. I think it looks all cool and "retro"' + ) + + embed.set_image(url="attachment://modified_avatar.png") + embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + + await ctx.send(file=file, embed=embed) + + @pfp_modify.command(pass_context=True, aliases=["easterify"], root_aliases=("easterify", "avatareasterify")) + async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: + """ + This "Easterifies" the user's avatar. + + Given colours will produce a personalised egg in the corner, similar to the egg_decorate command. + If colours are not given, a nice little chocolate bunny will sit in the corner. + Colours are split by spaces, unless you wrap the colour name in double quotes. + Discord colour names, HTML colour names, XKCD colour names and hex values are accepted. + """ + async def send(*args, **kwargs) -> str: + """ + This replaces the original ctx.send. + + When invoking the egg decorating command, the egg itself doesn't print to to the channel. + Returns the message content so that if any errors occur, the error message can be output. + """ + if args: + return args[0] + + async with ctx.typing(): + egg = None + if colours: + send_message = ctx.send + ctx.send = send # Assigns ctx.send to a fake send + egg = await ctx.invoke(self.bot.get_command("eggdecorate"), *colours) + if isinstance(egg, str): # When an error message occurs in eggdecorate. + await send_message(egg) + return + ctx.send = send_message # Reassigns ctx.send + + image_bytes = await ctx.author.avatar_url_as(size=256).read() + file = await in_thread( + PfpEffects._apply_effect, + image_bytes, + PfpEffects.easterify_effect, + egg + ) + + embed = discord.Embed( + name="Your Lovely Easterified Avatar", + description="Here is your lovely avatar, all bright and colourful\nwith Easter pastel colours. Enjoy :D" + ) + embed.set_image(url="attachment://modified_avatar.png") + embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + + await ctx.send(file=file, embed=embed) + + async def send_pride_image( + self, + ctx: commands.Context, + image_bytes: bytes, + pixels: int, + flag: str, + option: str + ) -> None: + """Gets and sends the image in an embed. Used by the pride commands.""" + async with ctx.typing(): + file = await in_thread( + PfpEffects._apply_effect, + image_bytes, + PfpEffects.pridify_effect, + pixels, + flag + ) + + embed = discord.Embed( + name="Your Lovely Pride Avatar", + description=f"Here is your lovely avatar, surrounded by\n a beautiful {option} flag. Enjoy :D" + ) + embed.set_image(url="attachment://modified_avatar.png") + embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + await ctx.send(file=file, embed=embed) + + @pfp_modify.group( + aliases=["avatarpride", "pridepfp", "prideprofile"], + root_aliases=("prideavatar", "avatarpride", "pridepfp", "prideprofile"), + invoke_without_command=True + ) + async def prideavatar(self, ctx: commands.Context, option: str = "lgbt", pixels: int = 64) -> None: + """ + This surrounds an avatar with a border of a specified LGBT flag. + + This defaults to the LGBT rainbow flag if none is given. + The amount of pixels can be given which determines the thickness of the flag border. + This has a maximum of 512px and defaults to a 64px border. + The full image is 1024x1024. + """ + option = option.lower() + pixels = max(0, min(512, pixels)) + flag = self.GENDER_OPTIONS.get(option) + if flag is None: + return await ctx.send("I don't have that flag!") + + async with ctx.typing(): + image_bytes = await ctx.author.avatar_url.read() + await self.send_pride_image(ctx, image_bytes, pixels, flag, option) + + @prideavatar.command() + async def image(self, ctx: commands.Context, url: str, option: str = "lgbt", pixels: int = 64) -> None: + """ + This surrounds the image specified by the URL with a border of a specified LGBT flag. + + This defaults to the LGBT rainbow flag if none is given. + The amount of pixels can be given which determines the thickness of the flag border. + This has a maximum of 512px and defaults to a 64px border. + The full image is 1024x1024. + """ + option = option.lower() + pixels = max(0, min(512, pixels)) + flag = self.GENDER_OPTIONS.get(option) + if flag is None: + return await ctx.send("I don't have that flag!") + + async with ctx.typing(): + async with self.bot.http_session as session: + try: + response = await session.get(url) + except client_exceptions.ClientConnectorError: + return await ctx.send("Cannot connect to provided URL!") + except client_exceptions.InvalidURL: + return await ctx.send("Invalid URL!") + if response.status != 200: + return await ctx.send("Bad response from provided URL!") + image_bytes = await response.read() + await self.send_pride_image(ctx, image_bytes, pixels, flag, option) + + @prideavatar.command() + async def flags(self, ctx: commands.Context) -> None: + """This lists the flags that can be used with the prideavatar command.""" + choices = sorted(set(self.GENDER_OPTIONS.values())) + options = "• " + "\n• ".join(choices) + embed = discord.Embed( + title="I have the following flags:", + description=options, + colour=Colours.soft_red + ) + await ctx.send(embed=embed) + + @pfp_modify.command( + name='spookyavatar', + aliases=('savatar', 'spookify'), + root_aliases=('spookyavatar', 'spookify', 'savatar'), + brief='Spookify an user\'s avatar.' + ) + async def spooky_avatar(self, ctx: commands.Context, user: discord.Member = None) -> None: + """A command to print the user's spookified avatar.""" + if user is None: + user = ctx.message.author + + async with ctx.typing(): + image_bytes = await ctx.author.avatar_url.read() + file = await in_thread(PfpEffects._apply_effect, image_bytes, spookifications.get_random_effect) + + embed = discord.Embed( + title="Is this you or am I just really paranoid?", + colour=0xFF0000 + ) + embed.set_author(name=str(user.name), icon_url=user.avatar_url) + embed.set_image(url='attachment://modified_avatar.png') + embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + + await ctx.send(file=file, embed=embed) + + +def setup(bot: commands.Bot) -> None: + """Load the PfpModify cog.""" + bot.add_cog(PfpModify(bot)) -- cgit v1.2.3 From e210354fc5d8e0970e1ea2e75bb28fa314fa72fa Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 20 Feb 2021 15:50:03 +0000 Subject: Hoist apply effect and remove private _. --- .../evergreen/profile_pic_modification/_effects.py | 25 +++++++++++----------- .../profile_pic_modification/pfp_modify.py | 8 +++---- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py index fbe5f706..a290ed3d 100644 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -15,6 +15,19 @@ EASTER_COLOURS = [ class PfpEffects(): """Implements various image effects.""" + @staticmethod + def apply_effect(image_bytes: bytes, effect: t.Callable, *args) -> discord.File: + """Applies the given effect to the image passed to it.""" + im = Image.open(BytesIO(image_bytes)) + im = im.convert("RGBA") + im = effect(im, *args) + + bufferedio = BytesIO() + im.save(bufferedio, format="PNG") + bufferedio.seek(0) + + return discord.File(bufferedio, filename="modified_avatar.png") + @staticmethod def closest(x: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: """ @@ -56,18 +69,6 @@ class PfpEffects(): ring.putalpha(mask) return ring - @staticmethod - def _apply_effect(image_bytes: bytes, effect: t.Callable, *args) -> discord.File: - im = Image.open(BytesIO(image_bytes)) - im = im.convert("RGBA") - im = effect(im, *args) - - bufferedio = BytesIO() - im.save(bufferedio, format="PNG") - bufferedio.seek(0) - - return discord.File(bufferedio, filename="modified_avatar.png") - @staticmethod def pridify_effect( image: Image, diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 51742257..a58f44d2 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -49,7 +49,7 @@ class PfpModify(commands.Cog): async with ctx.typing(): image_bytes = await ctx.author.avatar_url.read() file = await in_thread( - PfpEffects._apply_effect, + PfpEffects.apply_effect, image_bytes, PfpEffects.eight_bitify_effect ) @@ -97,7 +97,7 @@ class PfpModify(commands.Cog): image_bytes = await ctx.author.avatar_url_as(size=256).read() file = await in_thread( - PfpEffects._apply_effect, + PfpEffects.apply_effect, image_bytes, PfpEffects.easterify_effect, egg @@ -123,7 +123,7 @@ class PfpModify(commands.Cog): """Gets and sends the image in an embed. Used by the pride commands.""" async with ctx.typing(): file = await in_thread( - PfpEffects._apply_effect, + PfpEffects.apply_effect, image_bytes, PfpEffects.pridify_effect, pixels, @@ -216,7 +216,7 @@ class PfpModify(commands.Cog): async with ctx.typing(): image_bytes = await ctx.author.avatar_url.read() - file = await in_thread(PfpEffects._apply_effect, image_bytes, spookifications.get_random_effect) + file = await in_thread(PfpEffects.apply_effect, image_bytes, spookifications.get_random_effect) embed = discord.Embed( title="Is this you or am I just really paranoid?", -- cgit v1.2.3 From a30f4448bfe411cf418e4de938870d89680d68df Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 20 Feb 2021 15:53:42 +0000 Subject: Don't return discord.Message from ctx.send calls --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index a58f44d2..f3e8d426 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -156,7 +156,8 @@ class PfpModify(commands.Cog): pixels = max(0, min(512, pixels)) flag = self.GENDER_OPTIONS.get(option) if flag is None: - return await ctx.send("I don't have that flag!") + await ctx.send("I don't have that flag!") + return async with ctx.typing(): image_bytes = await ctx.author.avatar_url.read() @@ -176,7 +177,8 @@ class PfpModify(commands.Cog): pixels = max(0, min(512, pixels)) flag = self.GENDER_OPTIONS.get(option) if flag is None: - return await ctx.send("I don't have that flag!") + await ctx.send("I don't have that flag!") + return async with ctx.typing(): async with self.bot.http_session as session: -- cgit v1.2.3 From 7bc0ea474ed39dbddca3958bb90fc072f0d7aa1d Mon Sep 17 00:00:00 2001 From: Arez1337 <> Date: Sat, 20 Feb 2021 20:43:59 +0100 Subject: Removed the Question about the Nile and changed the IDs to matc the order again --- bot/resources/evergreen/trivia_quiz.json | 57 ++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index faa3bc3b..a4225eb1 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -2,36 +2,51 @@ "retro": [ { "id": 1, - "hints": ["It is not a mainline Mario Game, although the plumber is present.", "It is not a mainline Zelda Game, although Link is present."], + "hints": [ + "It is not a mainline Mario Game, although the plumber is present.", + "It is not a mainline Zelda Game, although Link is present." + ], "question": "What is the best selling game on the Nintendo GameCube?", "answer": "Super Smash Bros" }, { "id": 2, - "hints": ["It was released before the 90's.", "It was released after 1980."], + "hints": [ + "It was released before the 90's.", + "It was released after 1980." + ], "question": "What year was Tetris released?", "answer": "1984" }, { "id": 3, - "hints": ["The occupation was in construction", "He appeared as this kind of worker in 1981's Donkey Kong"], + "hints": [ + "The occupation was in construction", + "He appeared as this kind of worker in 1981's Donkey Kong" + ], "question": "What was Mario's original occupation?", "answer": "Carpenter" }, { "id": 4, - "hints": ["It was revealed in the Nintendo Character Guide in 1993.", "His last name has to do with eating Mario's enemies."], + "hints": [ + "It was revealed in the Nintendo Character Guide in 1993.", + "His last name has to do with eating Mario's enemies." + ], "question": "What is Yoshi's (from Mario Bros.) full name?", "answer": "Yoshisaur Munchakoopas" }, { "id": 5, - "hints": ["The game was released in 1990.", "It was released on the SNES."], + "hints": [ + "The game was released in 1990.", + "It was released on the SNES." + ], "question": "What was the first game Yoshi appeared in?", "answer": "Super Mario World" } ], - "general":[ + "general": [ { "id": 100, "question": "Name \"the land of a thousand lakes\"", @@ -114,7 +129,7 @@ "id": 113, "question": "What's the name of the tallest waterfall in the world.", "answer": "Angel Falls", - "info": "Angel Falls (Salto Ángel) in Venezuela is the highest waterfall in the world. The falls are 3230 feet in height, with an uninterrupted drop of 2647 feet. Angel Falls is located on a tributary of the Rio Caroni." + "info": "Angel Falls (Salto \u00c1ngel) in Venezuela is the highest waterfall in the world. The falls are 3230 feet in height, with an uninterrupted drop of 2647 feet. Angel Falls is located on a tributary of the Rio Caroni." }, { "id": 114, @@ -180,7 +195,7 @@ "id": 124, "question": "When did the Second World War end?", "answer": "1945", - "info": "World War 2 ended with the unconditional surrender of the Axis powers. On 8 May 1945, the Allies accepted Germany's surrender, about a week after Adolf Hitler had committed suicide. VE Day – Victory in Europe celebrates the end of the Second World War on 8 May 1945." + "info": "World War 2 ended with the unconditional surrender of the Axis powers. On 8 May 1945, the Allies accepted Germany's surrender, about a week after Adolf Hitler had committed suicide. VE Day \u2013 Victory in Europe celebrates the end of the Second World War on 8 May 1945." }, { "id": 125, @@ -190,72 +205,66 @@ }, { "id": 126, - "question": "What's the name of the largest river in the world?", - "answer": "Nile", - "info": "The Nile, which is about 6,650 km (4,130 mi) long, is an \"international\" river as its drainage basin covers eleven countries, namely, Tanzania, Uganda, Rwanda, Burundi, the Democratic Republic of the Congo, Kenya, Ethiopia, Eritrea, South Sudan, Republic of the Sudan and Egypt." - }, - { - "id": 127, "question": "Which is the smallest planet in the Solar System?", "answer": "Mercury", "info": "Mercury is the smallest planet in our solar system. It's just a little bigger than Earth's moon. It is the closest planet to the sun, but it's actually not the hottest. Venus is hotter." }, { - "id": 128, + "id": 127, "question": "What is the smallest country?", "answer": "Vatican City", "info": "With an area of 0.17 square miles (0.44 km2) and a population right around 1,000, Vatican City is the smallest country in the world, both in terms of size and population." }, { - "id": 129, + "id": 128, "question": "What's the name of the largest bird?", "answer": "Ostrich", "info": "The largest living bird, a member of the Struthioniformes, is the ostrich (Struthio camelus), from the plains of Africa and Arabia. A large male ostrich can reach a height of 2.8 metres (9.2 feet) and weigh over 156 kilograms (344 pounds)." }, { - "id": 130, + "id": 129, "question": "What does the acronym GPRS stand for?", "answer": "General Packet Radio Service", "info": "General Packet Radio Service (GPRS) is a packet-based mobile data service on the global system for mobile communications (GSM) of 3G and 2G cellular communication systems. It is a non-voice, high-speed and useful packet-switching technology intended for GSM networks." }, { - "id": 131, + "id": 130, "question": "In what country is the Ebro river located?", "answer": "Spain", "info": "The Ebro river is located in Spain. It is 930 kilometers long and it's the second longest river that ends on the Mediterranean Sea." }, { - "id": 132, + "id": 131, "question": "What year was the IBM PC model 5150 introduced into the market?", "answer": "1981", "info": "The IBM PC was introduced into the market in 1981. It used the Intel 8088, with a clock speed of 4.77 MHz, along with the MDA and CGA as a video card." }, { - "id": 133, + "id": 132, "question": "What's the world's largest urban area?", "answer": "Tokyo", "info": "Tokyo is the most populated city in the world, with a population of 37 million people. It is located in Japan." }, { - "id": 134, + "id": 133, "question": "How many planets are there in the Solar system?", "answer": "8", "info": "In the Solar system, there are 8 planets: Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus and Neptune. Pluto isn't considered a planet in the Solar System anymore." }, { - "id": 135, + "id": 134, "question": "What is the capital of Iraq?", "answer": "Baghdad", "info": "Baghdad is the capital of Iraq. It has a population of 7 million people." }, { - "id": 136, + "id": 135, "question": "The United Nations headquarters is located at which city?", "answer": "New York", "info": "The United Nations is headquartered in New York City in a complex designed by a board of architects led by Wallace Harrison and built by the architectural firm Harrison & Abramovitz. The complex has served as the official headquarters of the United Nations since its completion in 1951." }, { - "id": 137, + "id": 136, "question": "At what year did Christopher Columbus discover America?", "answer": "1492", "info": "The explorer Christopher Columbus made four trips across the Atlantic Ocean from Spain: in 1492, 1493, 1498 and 1502. He was determined to find a direct water route west from Europe to Asia, but he never did. Instead, he stumbled upon the Americas" -- cgit v1.2.3 From 6e7521c74e9c34159d8ba4873f09f5277f47908e Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 22 Feb 2021 18:35:10 +0000 Subject: Improve doc string. --- bot/exts/evergreen/profile_pic_modification/_effects.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py index a290ed3d..1179100c 100644 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -13,7 +13,11 @@ EASTER_COLOURS = [ class PfpEffects(): - """Implements various image effects.""" + """ + Implements various image effects. + + All of these methods are blocking, so should be ran in threads. + """ @staticmethod def apply_effect(image_bytes: bytes, effect: t.Callable, *args) -> discord.File: -- cgit v1.2.3 From 16476ab7b4d5efe36a9e101b382f7be24888dec5 Mon Sep 17 00:00:00 2001 From: MrKomodoDragon Date: Mon, 22 Feb 2021 11:13:01 -0800 Subject: Switch to itertools.cycle and add colors to constants.py --- bot/constants.py | 2 ++ bot/exts/evergreen/pythonfacts.py | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index bb538487..3aec6ba3 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -156,6 +156,8 @@ class Colours: soft_orange = 0xf9cb54 soft_red = 0xcd6d6d yellow = 0xf9f586 + python_blue = 0x4B8BBE + python_yellow = 0xFFD43B class Emojis: diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index 734782b8..8e6d300b 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -1,13 +1,16 @@ -import random +import itertools import discord from discord.ext import commands +from bot.constants import Colours with open('bot/resources/evergreen/python_facts.txt') as file: FACTS = list(file) + FACT_CYCLE = itertools.cycle(FACTS) -COLORS = [0x4B8BBE, 0xFFD43B, ] +COLORS = [Colours.python_blue, Colours.python_yellow] +COLOR_CYCLE = itertools.cycle(COLORS) class PythonFacts(commands.Cog): @@ -19,7 +22,7 @@ class PythonFacts(commands.Cog): @commands.command(name='pythonfact', aliases=['pyfact']) async def get_python_fact(self, ctx: commands.Context) -> None: """Sends a Random fun fact about Python.""" - embed = discord.Embed(title='Python Facts', description=random.choice(FACTS), colour=random.choice(COLORS)) + embed = discord.Embed(title='Python Facts', description=next(FACT_CYCLE), colour=next(COLOR_CYCLE)) await ctx.send(embed=embed) -- cgit v1.2.3 From aef25cb560eb525305c583a8ff2834b503cb91d4 Mon Sep 17 00:00:00 2001 From: MrKomodoDragon Date: Tue, 23 Feb 2021 09:03:05 -0800 Subject: Remove unused variables --- bot/exts/evergreen/pythonfacts.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index 8e6d300b..11b258f9 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -6,11 +6,9 @@ from discord.ext import commands from bot.constants import Colours with open('bot/resources/evergreen/python_facts.txt') as file: - FACTS = list(file) - FACT_CYCLE = itertools.cycle(FACTS) + FACTS = itertools.cycle(list(file)) -COLORS = [Colours.python_blue, Colours.python_yellow] -COLOR_CYCLE = itertools.cycle(COLORS) +COLORS = itertools.cycle([Colours.python_blue, Colours.python_yellow]) class PythonFacts(commands.Cog): @@ -22,7 +20,7 @@ class PythonFacts(commands.Cog): @commands.command(name='pythonfact', aliases=['pyfact']) async def get_python_fact(self, ctx: commands.Context) -> None: """Sends a Random fun fact about Python.""" - embed = discord.Embed(title='Python Facts', description=next(FACT_CYCLE), colour=next(COLOR_CYCLE)) + embed = discord.Embed(title='Python Facts', description=next(FACTS), colour=next(COLORS)) await ctx.send(embed=embed) -- cgit v1.2.3 From 1e240e7fbac71b0546ef090732c7417bab4e1563 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 2 Mar 2021 14:27:58 -0600 Subject: initial commit of earth_photos --- bot/exts/easter/earth_photos.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 bot/exts/easter/earth_photos.py diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py new file mode 100644 index 00000000..ceffeb48 --- /dev/null +++ b/bot/exts/easter/earth_photos.py @@ -0,0 +1,27 @@ +import asyncio +import logging +import random +from unsplash.api import Api +from unsplash.auth import Auth + +import discord +from discord.ext import commands + +log = logging.getLogger(__name__) + +class EarthPhotos(commands.Cog): + """This cog contains the command for earth photos.""" + + def init(self, bot: commands.Bot): + self.bot = bot + self.current_channel = None + + @commands.command(aliases=["earth"]) + async def earth_photos(self, ctx: commands.Context): + """ + Returns a random photo of earth. + """ + + + + -- cgit v1.2.3 From 99a9d7a1b7651caea4cf1587c89485497c061387 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 2 Mar 2021 15:22:39 -0600 Subject: Update earth_photos.py --- bot/exts/easter/earth_photos.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index ceffeb48..436b39a7 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -1,14 +1,24 @@ import asyncio import logging import random -from unsplash.api import Api -from unsplash.auth import Auth +from unsplash.api import Api as uApi +from unsplash.auth import Auth as uAuth import discord from discord.ext import commands +from bot.constants import Tokens + log = logging.getLogger(__name__) +UnClient_id = Tokens.UNSPLASH_API + +UnClient_secret = Tokens.UNSPLASH_SECRET + +redirect_uri = "urn:ietf:wg:oauth:2.0:oob" + +unsplash_auth = uAuth(client_id, + class EarthPhotos(commands.Cog): """This cog contains the command for earth photos.""" @@ -19,7 +29,7 @@ class EarthPhotos(commands.Cog): @commands.command(aliases=["earth"]) async def earth_photos(self, ctx: commands.Context): """ - Returns a random photo of earth. + Returns a random photo of earth, sourced from Unsplash. """ -- cgit v1.2.3 From fc8fae8d0c4cda598af5bf1a9d8de1049d4c9923 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 2 Mar 2021 17:57:31 -0600 Subject: Update Pipfile --- Pipfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Pipfile b/Pipfile index e7e01a31..a1d6fbf4 100644 --- a/Pipfile +++ b/Pipfile @@ -15,6 +15,7 @@ PyYAML = "~=5.3.1" "discord.py" = {extras = ["voice"], version = "~=1.5.1"} async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} emojis = "~=0.6.0" +python-unsplash = "~=1.1.0" [dev-packages] flake8 = "~=3.8" -- cgit v1.2.3 From 0260ad04af6e98c7fd804c58913412b6b936eb73 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 2 Mar 2021 21:39:01 -0600 Subject: finished function --- Pipfile | 1 + Pipfile.lock | 26 +++++++++++++++++--------- bot/constants.py | 1 + bot/exts/easter/earth_photos.py | 35 +++++++++++++++++++---------------- 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/Pipfile b/Pipfile index e7e01a31..1a2d464e 100644 --- a/Pipfile +++ b/Pipfile @@ -15,6 +15,7 @@ PyYAML = "~=5.3.1" "discord.py" = {extras = ["voice"], version = "~=1.5.1"} async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} emojis = "~=0.6.0" +requests = "~=2.25.1" [dev-packages] flake8 = "~=3.8" diff --git a/Pipfile.lock b/Pipfile.lock index ec801979..34cd08cf 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "b4aaaacbab13179145e36d7b86c736db512286f6cce8e513cc30c48d68fe3810" + "sha256": "53409f1c6726e95cf348740b1dc55124b77a1f327aea273dce041b5056270b2e" }, "pipfile-spec": 6, "requires": { @@ -238,11 +238,11 @@ }, "idna": { "hashes": [ - "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16", - "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1" + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" ], - "markers": "python_version >= '3.4'", - "version": "==3.1" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.10" }, "multidict": { "hashes": [ @@ -412,6 +412,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==3.5.3" }, + "requests": { + "hashes": [ + "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", + "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" + ], + "index": "pypi", + "version": "==2.25.1" + }, "sentry-sdk": { "hashes": [ "sha256:4ae8d1ced6c67f1c8ea51d82a16721c166c489b76876c9f2c202b8a50334b237", @@ -585,11 +593,11 @@ }, "identify": { "hashes": [ - "sha256:de7129142a5c86d75a52b96f394d94d96d497881d2aaf8eafe320cdbe8ac4bcc", - "sha256:e0dae57c0397629ce13c289f6ddde0204edf518f557bfdb1e56474aa143e77c3" + "sha256:9cdd81e5d2b6e76c3006d5226316dd947bd6324fbeebb881bec489202fa09d3a", + "sha256:b99aa309329c4fea679463eb35d169f3fbe13e66e9dd6162ad1856cbeb03dcbd" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.5.14" + "markers": "python_full_version >= '3.6.1'", + "version": "==2.0.0" }, "mccabe": { "hashes": [ diff --git a/bot/constants.py b/bot/constants.py index db34b55a..f8ea5743 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -271,6 +271,7 @@ class Tokens(NamedTuple): igdb_client_id = environ.get("IGDB_CLIENT_ID") igdb_client_secret = environ.get("IGDB_CLIENT_SECRET") github = environ.get("GITHUB_TOKEN") + unsplash_key = environ.get("UNSPLASH_KEY") class Wolfram(NamedTuple): diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 436b39a7..478502eb 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -1,8 +1,5 @@ -import asyncio import logging -import random -from unsplash.api import Api as uApi -from unsplash.auth import Auth as uAuth +import requests import discord from discord.ext import commands @@ -11,27 +8,33 @@ from bot.constants import Tokens log = logging.getLogger(__name__) -UnClient_id = Tokens.UNSPLASH_API +UnClient_id = Tokens.unsplash_key -UnClient_secret = Tokens.UNSPLASH_SECRET - -redirect_uri = "urn:ietf:wg:oauth:2.0:oob" - -unsplash_auth = uAuth(client_id, class EarthPhotos(commands.Cog): """This cog contains the command for earth photos.""" - + def init(self, bot: commands.Bot): self.bot = bot self.current_channel = None - + @commands.command(aliases=["earth"]) async def earth_photos(self, ctx: commands.Context): """ Returns a random photo of earth, sourced from Unsplash. """ - - - - + photorequest = requests.get("https://api.unsplash.com/photos/random?query=earth&client_id=" + UnClient_id) + photojson = photorequest.json() + photourls = photojson.get('urls') + urltosend = photourls.get('regular') + userjson = photojson.get('user') + userName = userjson.get('name') + embed = discord.Embed(title="Earth Photo", description="A photo of Earth from Unsplash.", color=0x66ff00) + embed.set_image(url=urltosend) + embed.set_footer(text="Image by " + userName + " on Unsplash.") + await ctx.send(embed=embed) + + +def setup(bot: commands.Bot) -> None: + """Cog load""" + bot.add_cog(EarthPhotos(bot)) -- cgit v1.2.3 From 51f16ed0a4e1ce43d7ce4cef1701a886d1b8492d Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 2 Mar 2021 21:58:27 -0600 Subject: Update earth_photos.py --- bot/exts/easter/earth_photos.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 478502eb..2ca43e42 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -1,9 +1,10 @@ -import logging -import requests - import discord from discord.ext import commands +import logging + +import requests + from bot.constants import Tokens log = logging.getLogger(__name__) @@ -15,6 +16,7 @@ class EarthPhotos(commands.Cog): """This cog contains the command for earth photos.""" def init(self, bot: commands.Bot): + """Init function.""" self.bot = bot self.current_channel = None @@ -28,13 +30,13 @@ class EarthPhotos(commands.Cog): photourls = photojson.get('urls') urltosend = photourls.get('regular') userjson = photojson.get('user') - userName = userjson.get('name') + username = userjson.get('name') embed = discord.Embed(title="Earth Photo", description="A photo of Earth from Unsplash.", color=0x66ff00) embed.set_image(url=urltosend) - embed.set_footer(text="Image by " + userName + " on Unsplash.") + embed.set_footer(text="Image by " + username + " on Unsplash.") await ctx.send(embed=embed) def setup(bot: commands.Bot) -> None: - """Cog load""" + """Cog load.""" bot.add_cog(EarthPhotos(bot)) -- cgit v1.2.3 From 286d40bb887246216da23857b4eeb8364fcbf9c7 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 2 Mar 2021 22:04:47 -0600 Subject: linting changes --- bot/exts/easter/earth_photos.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 2ca43e42..e660112f 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -1,8 +1,10 @@ import discord -from discord.ext import commands import logging +from discord.ext import commands + + import requests from bot.constants import Tokens @@ -22,9 +24,7 @@ class EarthPhotos(commands.Cog): @commands.command(aliases=["earth"]) async def earth_photos(self, ctx: commands.Context): - """ - Returns a random photo of earth, sourced from Unsplash. - """ + """Returns a random photo of earth, sourced from Unsplash.""" photorequest = requests.get("https://api.unsplash.com/photos/random?query=earth&client_id=" + UnClient_id) photojson = photorequest.json() photourls = photojson.get('urls') -- cgit v1.2.3 From 8220661e8ef65ef4bbe92613ed2d3f273d43d76f Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 2 Mar 2021 22:09:10 -0600 Subject: more linting fixes --- bot/exts/easter/earth_photos.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index e660112f..c9cfc9c8 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -1,11 +1,9 @@ -import discord - import logging -from discord.ext import commands - +import discord import requests +from discord.ext import commands from bot.constants import Tokens -- cgit v1.2.3 From da54e5181cbf2ab3406be4a8ab9628e346d3c328 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 2 Mar 2021 22:15:57 -0600 Subject: lint fixes --- bot/exts/easter/earth_photos.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index c9cfc9c8..909fcbd8 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -1,7 +1,6 @@ import logging import discord - import requests from discord.ext import commands @@ -15,13 +14,13 @@ UnClient_id = Tokens.unsplash_key class EarthPhotos(commands.Cog): """This cog contains the command for earth photos.""" - def init(self, bot: commands.Bot): + def init(self, bot: commands.Bot) -> None: """Init function.""" self.bot = bot self.current_channel = None @commands.command(aliases=["earth"]) - async def earth_photos(self, ctx: commands.Context): + async def earth_photos(self, ctx: commands.Context) -> None: """Returns a random photo of earth, sourced from Unsplash.""" photorequest = requests.get("https://api.unsplash.com/photos/random?query=earth&client_id=" + UnClient_id) photojson = photorequest.json() -- cgit v1.2.3 From 029f5f0d6b38f70000013ca52c859a3486cdf544 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 07:14:06 -0600 Subject: Update bot/exts/easter/earth_photos.py Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/easter/earth_photos.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 909fcbd8..b41b277c 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -15,7 +15,6 @@ class EarthPhotos(commands.Cog): """This cog contains the command for earth photos.""" def init(self, bot: commands.Bot) -> None: - """Init function.""" self.bot = bot self.current_channel = None -- cgit v1.2.3 From 883405e6f419ebfe4799086bf4e7516fd1588c4d Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 07:43:41 -0600 Subject: init to __init__ Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/easter/earth_photos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index b41b277c..db295285 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -14,7 +14,7 @@ UnClient_id = Tokens.unsplash_key class EarthPhotos(commands.Cog): """This cog contains the command for earth photos.""" - def init(self, bot: commands.Bot) -> None: + def __init__(self, bot: commands.Bot) -> None: self.bot = bot self.current_channel = None -- cgit v1.2.3 From 246e5bd7109ba906162989e42c77d68402359106 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 09:26:48 -0600 Subject: Fix docstrings Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/easter/earth_photos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index db295285..4c319eaa 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -34,5 +34,5 @@ class EarthPhotos(commands.Cog): def setup(bot: commands.Bot) -> None: - """Cog load.""" + """Load the Earth Photos cog.""" bot.add_cog(EarthPhotos(bot)) -- cgit v1.2.3 From 4a0401ddb322fba53e2025a63fdf8d0a52e12076 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 09:32:47 -0600 Subject: Update earth_photos.py --- bot/exts/easter/earth_photos.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 4c319eaa..52bbaf36 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -1,13 +1,9 @@ -import logging - +import aiohttp import discord -import requests from discord.ext import commands from bot.constants import Tokens -log = logging.getLogger(__name__) - UnClient_id = Tokens.unsplash_key @@ -21,16 +17,14 @@ class EarthPhotos(commands.Cog): @commands.command(aliases=["earth"]) async def earth_photos(self, ctx: commands.Context) -> None: """Returns a random photo of earth, sourced from Unsplash.""" - photorequest = requests.get("https://api.unsplash.com/photos/random?query=earth&client_id=" + UnClient_id) - photojson = photorequest.json() - photourls = photojson.get('urls') - urltosend = photourls.get('regular') - userjson = photojson.get('user') - username = userjson.get('name') - embed = discord.Embed(title="Earth Photo", description="A photo of Earth from Unsplash.", color=0x66ff00) - embed.set_image(url=urltosend) - embed.set_footer(text="Image by " + username + " on Unsplash.") - await ctx.send(embed=embed) + async with ctx.typing(): + async with aiohttp.ClientSession as session: + async with session.get( + 'https://api.unsplash.com/photos/random?query=earth&client_id=' + UnClientId + ) as r: + jsondata = await r.json() + await ctx.send("Still a placeholder") + def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From a2c68caf0e39e6947372d303dcc707f26f1a744f Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 09:57:07 -0600 Subject: Update earth_photos.py --- bot/exts/easter/earth_photos.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 52bbaf36..adae0dcd 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -20,10 +20,18 @@ class EarthPhotos(commands.Cog): async with ctx.typing(): async with aiohttp.ClientSession as session: async with session.get( - 'https://api.unsplash.com/photos/random?query=earth&client_id=' + UnClientId - ) as r: + 'https://api.unsplash.com/photos/random?query=earth&client_id=' + UnClientId) as r: jsondata = await r.json() - await ctx.send("Still a placeholder") + linksdata = jsondata.get("urls") + downloadlinksdata = jsondata.get("links") + async with session.get( + downloadlinksdata.get("download_location") + "?client_id=" + UnClient_id) as er: + pass + await ctx.send("Still a work in progress") + + + + -- cgit v1.2.3 From 01a1624b14cc5d60b2e76871361e14f9cbfd69ce Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 09:57:49 -0600 Subject: asyncio start --- bot/exts/easter/earth_photos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index adae0dcd..26beda79 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -27,7 +27,7 @@ class EarthPhotos(commands.Cog): async with session.get( downloadlinksdata.get("download_location") + "?client_id=" + UnClient_id) as er: pass - await ctx.send("Still a work in progress") + await ctx.send("Still a work in progress, coming soon.") -- cgit v1.2.3 From 3b0949c96926fa30aa650bfd9c9a43e4fd52521f Mon Sep 17 00:00:00 2001 From: Kronifer Date: Wed, 3 Mar 2021 16:39:05 +0000 Subject: linting --- Pipfile | 1 + Pipfile.lock | 76 ++++++++++++++++++++++++++++++++++++++--- bot/exts/easter/earth_photos.py | 14 ++++---- 3 files changed, 79 insertions(+), 12 deletions(-) diff --git a/Pipfile b/Pipfile index 1a2d464e..1d1df3d7 100644 --- a/Pipfile +++ b/Pipfile @@ -16,6 +16,7 @@ PyYAML = "~=5.3.1" async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} emojis = "~=0.6.0" requests = "~=2.25.1" +pre-commit = "*" [dev-packages] flake8 = "~=3.8" diff --git a/Pipfile.lock b/Pipfile.lock index 34cd08cf..b58db761 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "53409f1c6726e95cf348740b1dc55124b77a1f327aea273dce041b5056270b2e" + "sha256": "434b76f6a372bf3ddc418d7b6bdba4d8906cbebb76553ffebf0b15d572e83487" }, "pipfile-spec": 6, "requires": { @@ -50,6 +50,13 @@ ], "version": "==1.3.1" }, + "appdirs": { + "hashes": [ + "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", + "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128" + ], + "version": "==1.4.4" + }, "arrow": { "hashes": [ "sha256:e098abbd9af3665aea81bdd6c869e93af4feb078e98468dd351c383af187aac5", @@ -143,6 +150,14 @@ ], "version": "==1.14.5" }, + "cfgv": { + "hashes": [ + "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d", + "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1" + ], + "markers": "python_full_version >= '3.6.1'", + "version": "==3.2.0" + }, "chardet": { "hashes": [ "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", @@ -161,6 +176,13 @@ "index": "pypi", "version": "==1.5.1" }, + "distlib": { + "hashes": [ + "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb", + "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1" + ], + "version": "==0.3.1" + }, "emojis": { "hashes": [ "sha256:7da34c8a78ae262fd68cef9e2c78a3c1feb59784489eeea0f54ba1d4b7111c7c", @@ -176,6 +198,13 @@ ], "version": "==1.4.5" }, + "filelock": { + "hashes": [ + "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", + "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" + ], + "version": "==3.0.12" + }, "fuzzywuzzy": { "hashes": [ "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8", @@ -236,6 +265,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.1.0" }, + "identify": { + "hashes": [ + "sha256:2179e7359471ab55729f201b3fdf7dc2778e221f868410fedcb0987b791ba552", + "sha256:2a5fdf2f5319cc357eda2550bea713a404392495961022cf2462624ce62f0f46" + ], + "markers": "python_full_version >= '3.6.1'", + "version": "==2.1.0" + }, "idna": { "hashes": [ "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", @@ -267,6 +304,13 @@ "markers": "python_version >= '3.5'", "version": "==4.7.6" }, + "nodeenv": { + "hashes": [ + "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9", + "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c" + ], + "version": "==1.5.0" + }, "pillow": { "hashes": [ "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", @@ -301,6 +345,14 @@ "index": "pypi", "version": "==7.2.0" }, + "pre-commit": { + "hashes": [ + "sha256:16212d1fde2bed88159287da88ff03796863854b04dc9f838a55979325a3d20e", + "sha256:399baf78f13f4de82a29b649afd74bef2c4e28eb4f021661fc7f29246e8c7a3a" + ], + "index": "pypi", + "version": "==2.10.1" + }, "pycares": { "hashes": [ "sha256:050f00b39ed77ea8a4e555f09417d4b1a6b5baa24bb9531a3e15d003d2319b3f", @@ -451,6 +503,14 @@ "markers": "python_version >= '3.0'", "version": "==2.2" }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.10.2" + }, "urllib3": { "hashes": [ "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80", @@ -459,6 +519,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", "version": "==1.26.3" }, + "virtualenv": { + "hashes": [ + "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d", + "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==20.4.2" + }, "yarl": { "hashes": [ "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409", @@ -593,11 +661,11 @@ }, "identify": { "hashes": [ - "sha256:9cdd81e5d2b6e76c3006d5226316dd947bd6324fbeebb881bec489202fa09d3a", - "sha256:b99aa309329c4fea679463eb35d169f3fbe13e66e9dd6162ad1856cbeb03dcbd" + "sha256:2179e7359471ab55729f201b3fdf7dc2778e221f868410fedcb0987b791ba552", + "sha256:2a5fdf2f5319cc357eda2550bea713a404392495961022cf2462624ce62f0f46" ], "markers": "python_full_version >= '3.6.1'", - "version": "==2.0.0" + "version": "==2.1.0" }, "mccabe": { "hashes": [ diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 26beda79..8e90c9e7 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -20,19 +20,17 @@ class EarthPhotos(commands.Cog): async with ctx.typing(): async with aiohttp.ClientSession as session: async with session.get( - 'https://api.unsplash.com/photos/random?query=earth&client_id=' + UnClientId) as r: + 'https://api.unsplash.com/photos/random?query=earth&client_id=' + UnClient_id) as r: jsondata = await r.json() linksdata = jsondata.get("urls") + embedlink = linksdata.get("full") downloadlinksdata = jsondata.get("links") async with session.get( downloadlinksdata.get("download_location") + "?client_id=" + UnClient_id) as er: - pass - await ctx.send("Still a work in progress, coming soon.") - - - - - + er.status() + embed = discord.Embed(title="In progress") + embed.set_photo(url=embedlink) + await ctx.send(embed=embed) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From 443c623548caf8edb82d0b9cfe41afcf594f0161 Mon Sep 17 00:00:00 2001 From: Kronifer Date: Wed, 3 Mar 2021 16:48:19 +0000 Subject: final lint fix --- bot/exts/easter/earth_photos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 8e90c9e7..e3d5936e 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -20,13 +20,13 @@ class EarthPhotos(commands.Cog): async with ctx.typing(): async with aiohttp.ClientSession as session: async with session.get( - 'https://api.unsplash.com/photos/random?query=earth&client_id=' + UnClient_id) as r: + 'https://api.unsplash.com/photos/random?query=earth&client_id=' + UnClient_id) as r: jsondata = await r.json() linksdata = jsondata.get("urls") embedlink = linksdata.get("full") downloadlinksdata = jsondata.get("links") async with session.get( - downloadlinksdata.get("download_location") + "?client_id=" + UnClient_id) as er: + downloadlinksdata.get("download_location") + "?client_id=" + UnClient_id) as er: er.status() embed = discord.Embed(title="In progress") embed.set_photo(url=embedlink) -- cgit v1.2.3 From f0cd29587f29731286be8c29d1ee0ca20d37d42f Mon Sep 17 00:00:00 2001 From: Kronifer Date: Wed, 3 Mar 2021 17:07:20 +0000 Subject: possible final update --- bot/exts/easter/earth_photos.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index e3d5936e..0ac470ce 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -18,19 +18,27 @@ class EarthPhotos(commands.Cog): async def earth_photos(self, ctx: commands.Context) -> None: """Returns a random photo of earth, sourced from Unsplash.""" async with ctx.typing(): - async with aiohttp.ClientSession as session: + async with aiohttp.ClientSession() as session: async with session.get( 'https://api.unsplash.com/photos/random?query=earth&client_id=' + UnClient_id) as r: jsondata = await r.json() linksdata = jsondata.get("urls") embedlink = linksdata.get("full") downloadlinksdata = jsondata.get("links") + userdata = jsondata.get("user") + username = userdata.get("name") + userlinks = userdata.get("links") + profile = userlinks.get("html") async with session.get( downloadlinksdata.get("download_location") + "?client_id=" + UnClient_id) as er: - er.status() - embed = discord.Embed(title="In progress") - embed.set_photo(url=embedlink) - await ctx.send(embed=embed) + er.status + embed = discord.Embed( + title="Earth Photo", + description="A photo of earth from Unsplash.", + color=0x66ff00) + embed.set_image(url=embedlink) + embed.add_field(title="Author", value="Made by [" + username + "](" + profile + ") on Unsplash.") + await ctx.send(embed=embed) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From b2ee1b9e79766b8cfe7b45670f6c86e7166ee84f Mon Sep 17 00:00:00 2001 From: Kronifer Date: Wed, 3 Mar 2021 20:20:22 +0000 Subject: fstrings, colours, and self.bot --- Pipfile | 2 -- bot/constants.py | 1 + bot/exts/easter/earth_photos.py | 48 ++++++++++++++++++++--------------------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/Pipfile b/Pipfile index 1d1df3d7..e7e01a31 100644 --- a/Pipfile +++ b/Pipfile @@ -15,8 +15,6 @@ PyYAML = "~=5.3.1" "discord.py" = {extras = ["voice"], version = "~=1.5.1"} async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} emojis = "~=0.6.0" -requests = "~=2.25.1" -pre-commit = "*" [dev-packages] flake8 = "~=3.8" diff --git a/bot/constants.py b/bot/constants.py index f8ea5743..c570b17f 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -158,6 +158,7 @@ class Colours: soft_orange = 0xf9cb54 soft_red = 0xcd6d6d yellow = 0xf9f586 + grass_green = 0x66ff00 class Emojis: diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 0ac470ce..a49e666c 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -1,8 +1,8 @@ -import aiohttp import discord from discord.ext import commands from bot.constants import Tokens +from bot.constants import Colours UnClient_id = Tokens.unsplash_key @@ -10,35 +10,35 @@ UnClient_id = Tokens.unsplash_key class EarthPhotos(commands.Cog): """This cog contains the command for earth photos.""" - def __init__(self, bot: commands.Bot) -> None: + def __init__(self, bot: commands.Bot): self.bot = bot - self.current_channel = None @commands.command(aliases=["earth"]) async def earth_photos(self, ctx: commands.Context) -> None: """Returns a random photo of earth, sourced from Unsplash.""" async with ctx.typing(): - async with aiohttp.ClientSession() as session: - async with session.get( - 'https://api.unsplash.com/photos/random?query=earth&client_id=' + UnClient_id) as r: - jsondata = await r.json() - linksdata = jsondata.get("urls") - embedlink = linksdata.get("full") - downloadlinksdata = jsondata.get("links") - userdata = jsondata.get("user") - username = userdata.get("name") - userlinks = userdata.get("links") - profile = userlinks.get("html") - async with session.get( - downloadlinksdata.get("download_location") + "?client_id=" + UnClient_id) as er: - er.status - embed = discord.Embed( - title="Earth Photo", - description="A photo of earth from Unsplash.", - color=0x66ff00) - embed.set_image(url=embedlink) - embed.add_field(title="Author", value="Made by [" + username + "](" + profile + ") on Unsplash.") - await ctx.send(embed=embed) + async with self.bot.http_session.get( + 'https://api.unsplash.com/photos/random', + params={"query": "planet_earth", "client_id": UnClient_id}) as r: + jsondata = await r.json() + linksdata = jsondata.get("urls") + embedlink = linksdata.get("full") + downloadlinksdata = jsondata.get("links") + userdata = jsondata.get("user") + username = userdata.get("name") + userlinks = userdata.get("links") + profile = userlinks.get("html") + async with self.bot.http_session.get( + downloadlinksdata.get("download_location"), + params={"client_id": UnClient_id}) as er: + er.status + embed = discord.Embed( + title="Earth Photo", + description="A photo of earth from Unsplash.", + color=Colours.grass_green) + embed.set_image(url=embedlink) + embed.add_field(name="Author", value=f"Made by [{username}]({profile}) on Unsplash.") + await ctx.send(embed=embed) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From cee75db89dd622546a563638cb17847ce6fd9702 Mon Sep 17 00:00:00 2001 From: Kronifer Date: Wed, 3 Mar 2021 20:26:34 +0000 Subject: locked pipfile --- Pipfile.lock | 86 ++++-------------------------------------------------------- 1 file changed, 5 insertions(+), 81 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index b58db761..2ac9784b 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "434b76f6a372bf3ddc418d7b6bdba4d8906cbebb76553ffebf0b15d572e83487" + "sha256": "b4aaaacbab13179145e36d7b86c736db512286f6cce8e513cc30c48d68fe3810" }, "pipfile-spec": 6, "requires": { @@ -50,13 +50,6 @@ ], "version": "==1.3.1" }, - "appdirs": { - "hashes": [ - "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", - "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128" - ], - "version": "==1.4.4" - }, "arrow": { "hashes": [ "sha256:e098abbd9af3665aea81bdd6c869e93af4feb078e98468dd351c383af187aac5", @@ -150,14 +143,6 @@ ], "version": "==1.14.5" }, - "cfgv": { - "hashes": [ - "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d", - "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1" - ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.2.0" - }, "chardet": { "hashes": [ "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", @@ -176,13 +161,6 @@ "index": "pypi", "version": "==1.5.1" }, - "distlib": { - "hashes": [ - "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb", - "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1" - ], - "version": "==0.3.1" - }, "emojis": { "hashes": [ "sha256:7da34c8a78ae262fd68cef9e2c78a3c1feb59784489eeea0f54ba1d4b7111c7c", @@ -198,13 +176,6 @@ ], "version": "==1.4.5" }, - "filelock": { - "hashes": [ - "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", - "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" - ], - "version": "==3.0.12" - }, "fuzzywuzzy": { "hashes": [ "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8", @@ -265,21 +236,13 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.1.0" }, - "identify": { - "hashes": [ - "sha256:2179e7359471ab55729f201b3fdf7dc2778e221f868410fedcb0987b791ba552", - "sha256:2a5fdf2f5319cc357eda2550bea713a404392495961022cf2462624ce62f0f46" - ], - "markers": "python_full_version >= '3.6.1'", - "version": "==2.1.0" - }, "idna": { "hashes": [ - "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", - "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16", + "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.10" + "markers": "python_version >= '3.4'", + "version": "==3.1" }, "multidict": { "hashes": [ @@ -304,13 +267,6 @@ "markers": "python_version >= '3.5'", "version": "==4.7.6" }, - "nodeenv": { - "hashes": [ - "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9", - "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c" - ], - "version": "==1.5.0" - }, "pillow": { "hashes": [ "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", @@ -345,14 +301,6 @@ "index": "pypi", "version": "==7.2.0" }, - "pre-commit": { - "hashes": [ - "sha256:16212d1fde2bed88159287da88ff03796863854b04dc9f838a55979325a3d20e", - "sha256:399baf78f13f4de82a29b649afd74bef2c4e28eb4f021661fc7f29246e8c7a3a" - ], - "index": "pypi", - "version": "==2.10.1" - }, "pycares": { "hashes": [ "sha256:050f00b39ed77ea8a4e555f09417d4b1a6b5baa24bb9531a3e15d003d2319b3f", @@ -464,14 +412,6 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==3.5.3" }, - "requests": { - "hashes": [ - "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", - "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" - ], - "index": "pypi", - "version": "==2.25.1" - }, "sentry-sdk": { "hashes": [ "sha256:4ae8d1ced6c67f1c8ea51d82a16721c166c489b76876c9f2c202b8a50334b237", @@ -503,14 +443,6 @@ "markers": "python_version >= '3.0'", "version": "==2.2" }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" - }, "urllib3": { "hashes": [ "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80", @@ -519,14 +451,6 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", "version": "==1.26.3" }, - "virtualenv": { - "hashes": [ - "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d", - "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.4.2" - }, "yarl": { "hashes": [ "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409", -- cgit v1.2.3 From 4cf72b014efbd8141e30482984e68e65c08e312d Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 14:28:43 -0600 Subject: Fixed import --- bot/exts/easter/earth_photos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index a49e666c..b496cbea 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -1,8 +1,8 @@ import discord from discord.ext import commands -from bot.constants import Tokens from bot.constants import Colours +from bot.constants import Tokens UnClient_id = Tokens.unsplash_key -- cgit v1.2.3 From 6c46dca3525758f1c1841f1dba312bb45c28531e Mon Sep 17 00:00:00 2001 From: Kronifer Date: Wed, 3 Mar 2021 22:27:37 +0000 Subject: minor change to getting image --- bot/exts/easter/earth_photos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index a49e666c..66780f6a 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -19,10 +19,10 @@ class EarthPhotos(commands.Cog): async with ctx.typing(): async with self.bot.http_session.get( 'https://api.unsplash.com/photos/random', - params={"query": "planet_earth", "client_id": UnClient_id}) as r: + params={"query": "earth", "client_id": UnClient_id}) as r: jsondata = await r.json() linksdata = jsondata.get("urls") - embedlink = linksdata.get("full") + embedlink = linksdata.get("regular") downloadlinksdata = jsondata.get("links") userdata = jsondata.get("user") username = userdata.get("name") -- cgit v1.2.3 From 01c380754e7466654924238dcee2f2077dd752a7 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 19:03:40 -0600 Subject: Format change Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/easter/earth_photos.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index efc5081a..a544ab59 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -35,7 +35,8 @@ class EarthPhotos(commands.Cog): embed = discord.Embed( title="Earth Photo", description="A photo of earth from Unsplash.", - color=Colours.grass_green) + color=Colours.grass_green + ) embed.set_image(url=embedlink) embed.add_field(name="Author", value=f"Made by [{username}]({profile}) on Unsplash.") await ctx.send(embed=embed) -- cgit v1.2.3 From fa38d70fc16e34ff3aff58bfbc7533f586a8eb97 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 19:05:16 -0600 Subject: More formatting Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/easter/earth_photos.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index a544ab59..f0c57fc5 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -30,7 +30,8 @@ class EarthPhotos(commands.Cog): profile = userlinks.get("html") async with self.bot.http_session.get( downloadlinksdata.get("download_location"), - params={"client_id": UnClient_id}) as er: + params={"client_id": UnClient_id} + ) as er: er.status embed = discord.Embed( title="Earth Photo", -- cgit v1.2.3 From 825080e805787afc1f36a80b4fa3f73040ebdc75 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 19:05:37 -0600 Subject: Format change Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/easter/earth_photos.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index f0c57fc5..6bfc26b0 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -19,7 +19,8 @@ class EarthPhotos(commands.Cog): async with ctx.typing(): async with self.bot.http_session.get( 'https://api.unsplash.com/photos/random', - params={"query": "earth", "client_id": UnClient_id}) as r: + params={"query": "earth", "client_id": UnClient_id} + ) as r: jsondata = await r.json() linksdata = jsondata.get("urls") embedlink = linksdata.get("regular") -- cgit v1.2.3 From dd2f7041739b2337ce1261c492089a8ba4474bc6 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 19:24:28 -0600 Subject: Pipfile lock fix --- Pipfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 2ac9784b..e204b9df 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -588,8 +588,8 @@ "sha256:2179e7359471ab55729f201b3fdf7dc2778e221f868410fedcb0987b791ba552", "sha256:2a5fdf2f5319cc357eda2550bea713a404392495961022cf2462624ce62f0f46" ], - "markers": "python_full_version >= '3.6.1'", - "version": "==2.1.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.5.14" }, "mccabe": { "hashes": [ -- cgit v1.2.3 From 05e18c02cd076a70e3aac370e7d120a16c84aab7 Mon Sep 17 00:00:00 2001 From: Kronifer Date: Thu, 4 Mar 2021 01:31:14 +0000 Subject: Revert "Format change" This reverts commit 825080e805787afc1f36a80b4fa3f73040ebdc75. --- bot/exts/easter/earth_photos.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 6bfc26b0..f0c57fc5 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -19,8 +19,7 @@ class EarthPhotos(commands.Cog): async with ctx.typing(): async with self.bot.http_session.get( 'https://api.unsplash.com/photos/random', - params={"query": "earth", "client_id": UnClient_id} - ) as r: + params={"query": "earth", "client_id": UnClient_id}) as r: jsondata = await r.json() linksdata = jsondata.get("urls") embedlink = linksdata.get("regular") -- cgit v1.2.3 From 370aec2283ce251eedbdcebbf42af5a0a82a03b1 Mon Sep 17 00:00:00 2001 From: Kronifer Date: Thu, 4 Mar 2021 01:33:36 +0000 Subject: pipfile fix --- Pipfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index e204b9df..01451c37 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -588,8 +588,8 @@ "sha256:2179e7359471ab55729f201b3fdf7dc2778e221f868410fedcb0987b791ba552", "sha256:2a5fdf2f5319cc357eda2550bea713a404392495961022cf2462624ce62f0f46" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.5.14" + "markers": "python_full_version >= '3.6.1'", + "version": "==3.2.0" }, "mccabe": { "hashes": [ -- cgit v1.2.3 From 60712f67c3e0f77023a481e5cbf0ce9816a7368e Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 20:16:59 -0600 Subject: requested changes by @Shivansh-007 --- bot/exts/easter/earth_photos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index f0c57fc5..001f8f41 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -31,8 +31,8 @@ class EarthPhotos(commands.Cog): async with self.bot.http_session.get( downloadlinksdata.get("download_location"), params={"client_id": UnClient_id} - ) as er: - er.status + ) as _: + pass embed = discord.Embed( title="Earth Photo", description="A photo of earth from Unsplash.", -- cgit v1.2.3 From e1e9b6f02abed2f14ae839e2483d94123d1cc9ec Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 3 Mar 2021 20:19:10 -0600 Subject: fixed pipfile.lock because of a previous mistake --- Pipfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 01451c37..ec801979 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -585,11 +585,11 @@ }, "identify": { "hashes": [ - "sha256:2179e7359471ab55729f201b3fdf7dc2778e221f868410fedcb0987b791ba552", - "sha256:2a5fdf2f5319cc357eda2550bea713a404392495961022cf2462624ce62f0f46" + "sha256:de7129142a5c86d75a52b96f394d94d96d497881d2aaf8eafe320cdbe8ac4bcc", + "sha256:e0dae57c0397629ce13c289f6ddde0204edf518f557bfdb1e56474aa143e77c3" ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.2.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.5.14" }, "mccabe": { "hashes": [ -- cgit v1.2.3 From e222ece92b4d14f4a9bb148f28acf9770da44d1b Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 10:35:38 -0600 Subject: Changes requested by @ChrisLovering --- bot/exts/easter/earth_photos.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 001f8f41..3ab8e7d9 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -1,10 +1,12 @@ +import logging + import discord from discord.ext import commands from bot.constants import Colours from bot.constants import Tokens -UnClient_id = Tokens.unsplash_key +log = logging.getLogger(__name__) class EarthPhotos(commands.Cog): @@ -19,7 +21,7 @@ class EarthPhotos(commands.Cog): async with ctx.typing(): async with self.bot.http_session.get( 'https://api.unsplash.com/photos/random', - params={"query": "earth", "client_id": UnClient_id}) as r: + params={"query": "earth", "client_id": Tokens.unsplash_access_key}) as r: jsondata = await r.json() linksdata = jsondata.get("urls") embedlink = linksdata.get("regular") @@ -30,9 +32,10 @@ class EarthPhotos(commands.Cog): profile = userlinks.get("html") async with self.bot.http_session.get( downloadlinksdata.get("download_location"), - params={"client_id": UnClient_id} - ) as _: + params={"client_id": Tokens.unsplash_access_key} + ) as er: pass + embed = discord.Embed( title="Earth Photo", description="A photo of earth from Unsplash.", @@ -45,4 +48,7 @@ class EarthPhotos(commands.Cog): def setup(bot: commands.Bot) -> None: """Load the Earth Photos cog.""" + if not Tokens.unsplash_access_key: + log.warning("No Unsplash access key found. Cog not loading.") + return bot.add_cog(EarthPhotos(bot)) -- cgit v1.2.3 From 512a5b3f085510d5862589ecc21bd253cb219207 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 10:37:53 -0600 Subject: Changes requested by @ChrisLovering --- bot/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/constants.py b/bot/constants.py index c570b17f..721defc8 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -272,7 +272,7 @@ class Tokens(NamedTuple): igdb_client_id = environ.get("IGDB_CLIENT_ID") igdb_client_secret = environ.get("IGDB_CLIENT_SECRET") github = environ.get("GITHUB_TOKEN") - unsplash_key = environ.get("UNSPLASH_KEY") + unsplash_access_key = environ.get("UNSPLASH_KEY") class Wolfram(NamedTuple): -- cgit v1.2.3 From 8b05f80392174a1a8b47e872b955d59bdcd4472f Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 10:40:42 -0600 Subject: small lint fix --- bot/exts/easter/earth_photos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 3ab8e7d9..beb16772 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -33,7 +33,7 @@ class EarthPhotos(commands.Cog): async with self.bot.http_session.get( downloadlinksdata.get("download_location"), params={"client_id": Tokens.unsplash_access_key} - ) as er: + ) as _: pass embed = discord.Embed( -- cgit v1.2.3 From f22aa5bd3f706751556def22d5660cdc8d93fde1 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 10:56:06 -0600 Subject: Format fix Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/easter/earth_photos.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index beb16772..3abedd0c 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -21,7 +21,8 @@ class EarthPhotos(commands.Cog): async with ctx.typing(): async with self.bot.http_session.get( 'https://api.unsplash.com/photos/random', - params={"query": "earth", "client_id": Tokens.unsplash_access_key}) as r: + params={"query": "earth", "client_id": Tokens.unsplash_access_key} + ) as r: jsondata = await r.json() linksdata = jsondata.get("urls") embedlink = linksdata.get("regular") -- cgit v1.2.3 From 1e1f4035f14592d7beaf93cbd3ae8e3895de588c Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 10:56:36 -0600 Subject: String upgrade Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/easter/earth_photos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 3abedd0c..d3b21644 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -39,7 +39,7 @@ class EarthPhotos(commands.Cog): embed = discord.Embed( title="Earth Photo", - description="A photo of earth from Unsplash.", + description="A photo of Earth 🌎 from Unsplash.", color=Colours.grass_green ) embed.set_image(url=embedlink) -- cgit v1.2.3 From 3fa4049b0c16777c7a9349dc9058d89a4a44b5c7 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 10:58:25 -0600 Subject: Update earth_photos.py --- bot/exts/easter/earth_photos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index d3b21644..71fb1313 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -43,7 +43,7 @@ class EarthPhotos(commands.Cog): color=Colours.grass_green ) embed.set_image(url=embedlink) - embed.add_field(name="Author", value=f"Made by [{username}]({profile}) on Unsplash.") + embed.add_field(name="Author", value=f"Photo by [{username}]({profile}) on Unsplash.") await ctx.send(embed=embed) -- cgit v1.2.3 From c25381fffd3b42bd1345dd9a910218c70877d2d3 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 10:59:36 -0600 Subject: suggestion by @Shivansh-007 --- bot/exts/easter/earth_photos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 71fb1313..1cf2d11a 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -44,7 +44,7 @@ class EarthPhotos(commands.Cog): ) embed.set_image(url=embedlink) embed.add_field(name="Author", value=f"Photo by [{username}]({profile}) on Unsplash.") - await ctx.send(embed=embed) + await ctx.send(embed=embed) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From 68ff4f3a524e90099e6953bd601321623e789e66 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 11:04:05 -0600 Subject: clarification in some areas --- bot/exts/easter/earth_photos.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 1cf2d11a..c1704e12 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -31,6 +31,7 @@ class EarthPhotos(commands.Cog): username = userdata.get("name") userlinks = userdata.get("links") profile = userlinks.get("html") + # Extra request as per Unsplash Guidelines async with self.bot.http_session.get( downloadlinksdata.get("download_location"), params={"client_id": Tokens.unsplash_access_key} -- cgit v1.2.3 From 55e33789830c00fc970c74be142857f90678cda7 Mon Sep 17 00:00:00 2001 From: MrKomodoDragon Date: Thu, 4 Mar 2021 11:20:37 -0800 Subject: Add link to discussion for suggesting facts in Embed --- bot/exts/evergreen/pythonfacts.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index 11b258f9..fb96a19d 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -20,7 +20,12 @@ class PythonFacts(commands.Cog): @commands.command(name='pythonfact', aliases=['pyfact']) async def get_python_fact(self, ctx: commands.Context) -> None: """Sends a Random fun fact about Python.""" - embed = discord.Embed(title='Python Facts', description=next(FACTS), colour=next(COLORS)) + embed = discord.Embed(title='Python Facts', + description=next(FACTS), + colour=next(COLORS)) + embed.add_field(name='Suggestions', + value="Want to suggest more facts? " + "Suggest more facts [here!](https://github.com/python-discord/meta/discussions/93)") await ctx.send(embed=embed) -- cgit v1.2.3 From cce1b2cb25b572ac3e976b190d7eae417240ae76 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 13:26:42 -0600 Subject: API guideline fix for Unsplash --- bot/exts/easter/earth_photos.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index c1704e12..39bb627e 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -31,7 +31,6 @@ class EarthPhotos(commands.Cog): username = userdata.get("name") userlinks = userdata.get("links") profile = userlinks.get("html") - # Extra request as per Unsplash Guidelines async with self.bot.http_session.get( downloadlinksdata.get("download_location"), params={"client_id": Tokens.unsplash_access_key} @@ -44,7 +43,9 @@ class EarthPhotos(commands.Cog): color=Colours.grass_green ) embed.set_image(url=embedlink) - embed.add_field(name="Author", value=f"Photo by [{username}]({profile}) on Unsplash.") + embed.add_field(name="Author", + value=f"Photo by [{username}]({profile}?utm_source=sir_lancebot&utm_medium=referral)\ + on [Unsplash](https://unsplash.com?utm_source=sir_lancebot&utm_medium=referral).") await ctx.send(embed=embed) -- cgit v1.2.3 From 6b8a81e7a698eb362b4bea7bb73ef369d589cbfc Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 13:30:17 -0600 Subject: whitespace fix --- bot/exts/easter/earth_photos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 39bb627e..9bcca583 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -39,11 +39,11 @@ class EarthPhotos(commands.Cog): embed = discord.Embed( title="Earth Photo", - description="A photo of Earth 🌎 from Unsplash.", + description="A photo of Earth 🌎 from Unsplash.", color=Colours.grass_green ) embed.set_image(url=embedlink) - embed.add_field(name="Author", + embed.add_field(name="Author", value=f"Photo by [{username}]({profile}?utm_source=sir_lancebot&utm_medium=referral)\ on [Unsplash](https://unsplash.com?utm_source=sir_lancebot&utm_medium=referral).") await ctx.send(embed=embed) -- cgit v1.2.3 From 43d42c9fc254e79ecb8e98e42ff4ea9aa43c707a Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 13:44:31 -0600 Subject: linting fix --- bot/exts/easter/earth_photos.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 9bcca583..26f3d115 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -21,7 +21,7 @@ class EarthPhotos(commands.Cog): async with ctx.typing(): async with self.bot.http_session.get( 'https://api.unsplash.com/photos/random', - params={"query": "earth", "client_id": Tokens.unsplash_access_key} + params={"query": "planet_earth", "client_id": Tokens.unsplash_access_key} ) as r: jsondata = await r.json() linksdata = jsondata.get("urls") @@ -31,6 +31,8 @@ class EarthPhotos(commands.Cog): username = userdata.get("name") userlinks = userdata.get("links") profile = userlinks.get("html") + # Referral flags + rf = "?utm_source=sir_lancebot&utm_medium=referral" async with self.bot.http_session.get( downloadlinksdata.get("download_location"), params={"client_id": Tokens.unsplash_access_key} @@ -39,13 +41,13 @@ class EarthPhotos(commands.Cog): embed = discord.Embed( title="Earth Photo", - description="A photo of Earth 🌎 from Unsplash.", + description="A photo of Earth 🌎 from Unsplash.", color=Colours.grass_green ) embed.set_image(url=embedlink) embed.add_field(name="Author", - value=f"Photo by [{username}]({profile}?utm_source=sir_lancebot&utm_medium=referral)\ - on [Unsplash](https://unsplash.com?utm_source=sir_lancebot&utm_medium=referral).") + value=f"Photo by [{username}]({profile}" + rf + ") \ +on [Unsplash](https://unsplash.com?" + rf + ").") await ctx.send(embed=embed) -- cgit v1.2.3 From e6f2d51cfc35ae8bafc160fb3449911457442252 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 13:53:45 -0600 Subject: style guidelines --- bot/exts/easter/earth_photos.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 26f3d115..96282d24 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -45,9 +45,11 @@ class EarthPhotos(commands.Cog): color=Colours.grass_green ) embed.set_image(url=embedlink) - embed.add_field(name="Author", - value=f"Photo by [{username}]({profile}" + rf + ") \ -on [Unsplash](https://unsplash.com?" + rf + ").") + embed.add_field( + name="Author", + value=f"Photo by [{username}]({profile}{rf}) \ + on [Unsplash](https://unsplash.com{rf})." + ) await ctx.send(embed=embed) -- cgit v1.2.3 From 1ae9948c04257c27fb2e71e5ae03a05bcd1db60e Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 13:57:26 -0600 Subject: single space fix --- bot/exts/easter/earth_photos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 96282d24..12fe6f1c 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -41,7 +41,7 @@ class EarthPhotos(commands.Cog): embed = discord.Embed( title="Earth Photo", - description="A photo of Earth 🌎 from Unsplash.", + description="A photo of Earth 🌎 from Unsplash.", color=Colours.grass_green ) embed.set_image(url=embedlink) -- cgit v1.2.3 From 1669b50e026678fd8e9fe6f72677e1de5943dd66 Mon Sep 17 00:00:00 2001 From: MrKomodoDragon Date: Thu, 4 Mar 2021 12:18:25 -0800 Subject: Remove "Want to suggest a fact?" for consistency --- bot/exts/evergreen/pythonfacts.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index fb96a19d..457c2fd3 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -24,8 +24,7 @@ class PythonFacts(commands.Cog): description=next(FACTS), colour=next(COLORS)) embed.add_field(name='Suggestions', - value="Want to suggest more facts? " - "Suggest more facts [here!](https://github.com/python-discord/meta/discussions/93)") + value="Suggest more facts [here!](https://github.com/python-discord/meta/discussions/93)") await ctx.send(embed=embed) -- cgit v1.2.3 From 334885c1b98ac4dcd475baac358eb8981a65b9c5 Mon Sep 17 00:00:00 2001 From: Dillon Runke <44979306+Kronifer@users.noreply.github.com> Date: Thu, 4 Mar 2021 16:17:42 -0600 Subject: Update earth_photos.py --- bot/exts/easter/earth_photos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 12fe6f1c..60e34b15 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -32,7 +32,7 @@ class EarthPhotos(commands.Cog): userlinks = userdata.get("links") profile = userlinks.get("html") # Referral flags - rf = "?utm_source=sir_lancebot&utm_medium=referral" + rf = "?utm_source=Sir%20Lancebot&utm_medium=referral" async with self.bot.http_session.get( downloadlinksdata.get("download_location"), params={"client_id": Tokens.unsplash_access_key} -- cgit v1.2.3 From bd49374ca6efad79b97d891882b05b8568f10fb4 Mon Sep 17 00:00:00 2001 From: MrKomodoDragon <74436682+MrKomodoDragon@users.noreply.github.com> Date: Thu, 4 Mar 2021 14:39:47 -0800 Subject: Remove arrows (whoopsies)! --- bot/constants.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index f6e09ae7..b8e30a7c 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -158,12 +158,9 @@ class Colours: soft_orange = 0xf9cb54 soft_red = 0xcd6d6d yellow = 0xf9f586 -<<<<<<< master python_blue = 0x4B8BBE python_yellow = 0xFFD43B -======= grass_green = 0x66ff00 ->>>>>>> master class Emojis: -- cgit v1.2.3 From 722d9b200d50b3bfdf9d665eb4b5b023aae0223e Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 5 Mar 2021 17:15:37 +0000 Subject: Fix Pipfile merge conflicts --- Pipfile | 1 + Pipfile.lock | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Pipfile b/Pipfile index d4b74e85..79a5dfc8 100644 --- a/Pipfile +++ b/Pipfile @@ -14,6 +14,7 @@ sentry-sdk = "~=0.19" PyYAML = "~=5.3.1" "discord.py" = {extras = ["voice"], version = "~=1.5.1"} async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} +emojis = "~=0.6.0" aiofiles = "~=0.6" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index d5f313d1..9d0a988e 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "640043d117ada0863e85327e52288393fc6f08568f4bf1aeba53e0443fe7f8f0" + "sha256": "91281e9ed353fea748de3da19abd7bef402402b23fc78a1260dc8bf8bd2bd98c" }, "pipfile-spec": 6, "requires": { @@ -169,6 +169,14 @@ "index": "pypi", "version": "==1.5.1" }, + "emojis": { + "hashes": [ + "sha256:7da34c8a78ae262fd68cef9e2c78a3c1feb59784489eeea0f54ba1d4b7111c7c", + "sha256:bf605d1f1a27a81cd37fe82eb65781c904467f569295a541c33710b97e4225ec" + ], + "index": "pypi", + "version": "==0.6.0" + }, "fakeredis": { "hashes": [ "sha256:01cb47d2286825a171fb49c0e445b1fa9307087e07cbb3d027ea10dbff108b6a", @@ -585,11 +593,11 @@ }, "identify": { "hashes": [ - "sha256:70b638cf4743f33042bebb3b51e25261a0a10e80f978739f17e7fd4837664a66", - "sha256:9dfb63a2e871b807e3ba62f029813552a24b5289504f5b071dea9b041aee9fe4" + "sha256:2179e7359471ab55729f201b3fdf7dc2778e221f868410fedcb0987b791ba552", + "sha256:2a5fdf2f5319cc357eda2550bea713a404392495961022cf2462624ce62f0f46" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.5.13" + "markers": "python_full_version >= '3.6.1'", + "version": "==2.1.0" }, "mccabe": { "hashes": [ -- cgit v1.2.3 From 64576f56a69ec26406d7eaa11956d93db642fc3a Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 5 Mar 2021 22:05:56 +0000 Subject: Rename in_thread fuction for clarity. --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index f3e8d426..df5005c9 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -18,7 +18,7 @@ log = logging.getLogger(__name__) _EXECUTOR = ThreadPoolExecutor(10) -async def in_thread(func: t.Callable, *args) -> asyncio.Future: +async def in_executor(func: t.Callable, *args) -> asyncio.Future: """Allows non-async functions to work in async functions.""" log.trace(f"Running {func.__name__} in an executor.") loop = asyncio.get_event_loop() @@ -48,7 +48,7 @@ class PfpModify(commands.Cog): """Pixelates your avatar and changes the palette to an 8bit one.""" async with ctx.typing(): image_bytes = await ctx.author.avatar_url.read() - file = await in_thread( + file = await in_executor( PfpEffects.apply_effect, image_bytes, PfpEffects.eight_bitify_effect @@ -96,7 +96,7 @@ class PfpModify(commands.Cog): ctx.send = send_message # Reassigns ctx.send image_bytes = await ctx.author.avatar_url_as(size=256).read() - file = await in_thread( + file = await in_executor( PfpEffects.apply_effect, image_bytes, PfpEffects.easterify_effect, @@ -122,7 +122,7 @@ class PfpModify(commands.Cog): ) -> None: """Gets and sends the image in an embed. Used by the pride commands.""" async with ctx.typing(): - file = await in_thread( + file = await in_executor( PfpEffects.apply_effect, image_bytes, PfpEffects.pridify_effect, @@ -218,7 +218,7 @@ class PfpModify(commands.Cog): async with ctx.typing(): image_bytes = await ctx.author.avatar_url.read() - file = await in_thread(PfpEffects.apply_effect, image_bytes, spookifications.get_random_effect) + file = await in_executor(PfpEffects.apply_effect, image_bytes, spookifications.get_random_effect) embed = discord.Embed( title="Is this you or am I just really paranoid?", -- cgit v1.2.3 From 013ce6da618ee9fdb7ec99d0c3a538f0aa8c745d Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 6 Mar 2021 12:12:22 +0000 Subject: Improve readibility of code, and fix grammar issues. --- .../evergreen/profile_pic_modification/_effects.py | 24 +++++++++++++--------- .../profile_pic_modification/pfp_modify.py | 18 ++++++++-------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py index 1179100c..99010931 100644 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -12,7 +12,7 @@ EASTER_COLOURS = [ ] # Pastel colours - Easter-like -class PfpEffects(): +class PfpEffects: """ Implements various image effects. @@ -37,14 +37,14 @@ class PfpEffects(): """ Finds the closest easter colour to a given pixel. - Returns a merge between the original colour and the closest colour + Returns a merge between the original colour and the closest colour. """ r1, g1, b1 = x def distance(point: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: """Finds the difference between a pastel colour and the original pixel colour.""" r2, g2, b2 = point - return ((r1 - r2)**2 + (g1 - g2)**2 + (b1 - b2)**2) + return (r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2 closest_colours = sorted(EASTER_COLOURS, key=lambda point: distance(point)) r2, g2, b2 = closest_colours[0] @@ -52,7 +52,7 @@ class PfpEffects(): g = (g1 + g2) // 2 b = (b1 + b2) // 2 - return (r, g, b) + return r, g, b @staticmethod def crop_avatar_circle(avatar: Image) -> Image: @@ -114,14 +114,18 @@ class PfpEffects(): image = ImageOps.posterize(image, 6) data = image.getdata() - setted_data = set(data) - new_d = {} + data_set = set(data) + easterified_data_set = {} - for x in setted_data: - new_d[x] = PfpEffects.closest(x) - new_data = [(*new_d[x], alpha[i]) if x in new_d else x for i, x in enumerate(data)] + for x in data_set: + easterified_data_set[x] = PfpEffects.closest(x) + new_pixel_data = [ + (*easterified_data_set[x], alpha[i]) + if x in easterified_data_set else x + for i, x in enumerate(data) + ] im = Image.new("RGBA", image.size) - im.putdata(new_data) + im.putdata(new_pixel_data) im.alpha_composite(overlay_image, (im.width - overlay_image.width, (im.height - overlay_image.height)//2)) return im diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index df5005c9..1d5b7208 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -39,7 +39,7 @@ class PfpModify(commands.Cog): @commands.group() async def pfp_modify(self, ctx: commands.Context) -> None: - """Groups all of the pfp modifing commands to allow a single concurrency limit.""" + """Groups all of the pfp modifying commands to allow a single concurrency limit.""" if not ctx.invoked_subcommand: await ctx.send_help(ctx.command) @@ -56,11 +56,11 @@ class PfpModify(commands.Cog): embed = discord.Embed( title="Your 8-bit avatar", - description='Here is your avatar. I think it looks all cool and "retro"' + description="Here is your avatar. I think it looks all cool and 'retro'." ) embed.set_image(url="attachment://modified_avatar.png") - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) await ctx.send(file=file, embed=embed) @@ -104,11 +104,11 @@ class PfpModify(commands.Cog): ) embed = discord.Embed( - name="Your Lovely Easterified Avatar", + name="Your Lovely Easterified Avatar!", description="Here is your lovely avatar, all bright and colourful\nwith Easter pastel colours. Enjoy :D" ) embed.set_image(url="attachment://modified_avatar.png") - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) await ctx.send(file=file, embed=embed) @@ -131,11 +131,11 @@ class PfpModify(commands.Cog): ) embed = discord.Embed( - name="Your Lovely Pride Avatar", + name="Your Lovely Pride Avatar!", description=f"Here is your lovely avatar, surrounded by\n a beautiful {option} flag. Enjoy :D" ) embed.set_image(url="attachment://modified_avatar.png") - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) await ctx.send(file=file, embed=embed) @pfp_modify.group( @@ -222,11 +222,11 @@ class PfpModify(commands.Cog): embed = discord.Embed( title="Is this you or am I just really paranoid?", - colour=0xFF0000 + colour=Colours.soft_red ) embed.set_author(name=str(user.name), icon_url=user.avatar_url) embed.set_image(url='attachment://modified_avatar.png') - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) await ctx.send(file=file, embed=embed) -- cgit v1.2.3 From 67db0d13d35cda2fcaa7d3bb20e22b38bb264487 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 6 Mar 2021 12:13:22 +0000 Subject: Convert method to static as it doesn't use class attributes. --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 1d5b7208..45a41e67 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -112,8 +112,8 @@ class PfpModify(commands.Cog): await ctx.send(file=file, embed=embed) + @staticmethod async def send_pride_image( - self, ctx: commands.Context, image_bytes: bytes, pixels: int, -- cgit v1.2.3 From 50a344d5caa006df1cbb033da4101b6cfa71529c Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 6 Mar 2021 12:14:23 +0000 Subject: Fix return type of in_executor. Its return type varies, based on the Callable given. --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 45a41e67..bba688aa 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -18,7 +18,7 @@ log = logging.getLogger(__name__) _EXECUTOR = ThreadPoolExecutor(10) -async def in_executor(func: t.Callable, *args) -> asyncio.Future: +async def in_executor(func: t.Callable, *args) -> t.Any: """Allows non-async functions to work in async functions.""" log.trace(f"Running {func.__name__} in an executor.") loop = asyncio.get_event_loop() -- cgit v1.2.3 From ea44e8ed8f5804cf5d80ab2318db6359c1e22c17 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 6 Mar 2021 12:15:28 +0000 Subject: Don't return discord.Messages. --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index bba688aa..7d4326a4 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -185,11 +185,16 @@ class PfpModify(commands.Cog): try: response = await session.get(url) except client_exceptions.ClientConnectorError: - return await ctx.send("Cannot connect to provided URL!") + await ctx.send("Cannot connect to provided URL!") + return except client_exceptions.InvalidURL: - return await ctx.send("Invalid URL!") + await ctx.send("Invalid URL!") + return + if response.status != 200: - return await ctx.send("Bad response from provided URL!") + await ctx.send("Bad response from provided URL!") + return + image_bytes = await response.read() await self.send_pride_image(ctx, image_bytes, pixels, flag, option) -- cgit v1.2.3 From 49ce88636124b826437c17a3c39c60c0840c1256 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 6 Mar 2021 12:16:00 +0000 Subject: Remove superfluous str cast. --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 7d4326a4..11222563 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -229,7 +229,7 @@ class PfpModify(commands.Cog): title="Is this you or am I just really paranoid?", colour=Colours.soft_red ) - embed.set_author(name=str(user.name), icon_url=user.avatar_url) + embed.set_author(name=user.name, icon_url=user.avatar_url) embed.set_image(url='attachment://modified_avatar.png') embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) -- cgit v1.2.3 From 365a272ce235706006a36acd0eac09637205a1ae Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 6 Mar 2021 12:20:25 +0000 Subject: Split up complex line. --- bot/exts/evergreen/profile_pic_modification/_effects.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py index 99010931..dda58006 100644 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -127,5 +127,8 @@ class PfpEffects: im = Image.new("RGBA", image.size) im.putdata(new_pixel_data) - im.alpha_composite(overlay_image, (im.width - overlay_image.width, (im.height - overlay_image.height)//2)) + im.alpha_composite( + overlay_image, + (im.width - overlay_image.width, (im.height - overlay_image.height) // 2) + ) return im -- cgit v1.2.3 From bb8820c9df9a01f39e3d97f76ca25007dd9f4c9e Mon Sep 17 00:00:00 2001 From: kosayoda Date: Sun, 7 Mar 2021 01:35:49 +0800 Subject: Unify emoji count and emoji information to a cog. --- bot/exts/evergreen/emoji.py | 122 ++++++++++++++++++++++++++++++++++++++ bot/exts/evergreen/emoji_count.py | 97 ------------------------------ bot/exts/evergreen/emojis.py | 39 ------------ 3 files changed, 122 insertions(+), 136 deletions(-) create mode 100644 bot/exts/evergreen/emoji.py delete mode 100644 bot/exts/evergreen/emoji_count.py delete mode 100644 bot/exts/evergreen/emojis.py diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py new file mode 100644 index 00000000..a24d420c --- /dev/null +++ b/bot/exts/evergreen/emoji.py @@ -0,0 +1,122 @@ +import datetime +import logging +import random +import textwrap +from collections import defaultdict +from typing import List, Tuple + +from discord import Color, Embed, Emoji +from discord.ext import commands + +from bot.constants import Colours, ERROR_REPLIES +from bot.utils.pagination import LinePaginator +from bot.utils.time import time_since + +log = logging.getLogger(__name__) + + +class Emoji(commands.Cog): + """A collection of commands related to emojis in the server.""" + + def __init__(self, bot: commands.Bot): + self.bot = bot + + @staticmethod + def embed_builder(emoji: dict) -> Tuple[Embed, List[str]]: + """Generates an embed with the emoji names and count.""" + embed = Embed( + color=Colours.orange, + title="Emoji Count", + timestamp=datetime.datetime.utcnow() + ) + msg = [] + + if len(emoji) == 1: + for category_name, category_emojis in emoji.items(): + if len(category_emojis) == 1: + msg.append(f"There is **{len(category_emojis)}** emoji in **{category_name}** category") + else: + msg.append(f"There are **{len(category_emojis)}** emojis in **{category_name}** category") + embed.set_thumbnail(url=random.choice(category_emojis).url) + + else: + for category_name, category_emojis in emoji.items(): + emoji_choice = random.choice(category_emojis) + if len(category_emojis) > 1: + emoji_info = f"There are **{len(category_emojis)}** emojis in **{category_name}** category" + else: + emoji_info = f"There is **{len(category_emojis)}** emoji in **{category_name}** category" + if emoji_choice.animated: + msg.append(f' {emoji_info}') + else: + msg.append(f'<:{emoji_choice.name}:{emoji_choice.id}> {emoji_info}') + return embed, msg + + @staticmethod + def generate_invalid_embed(emojis: list) -> Tuple[Embed, List[str]]: + """Generates error embed.""" + embed = Embed( + color=Colours.soft_red, + title=random.choice(ERROR_REPLIES) + ) + msg = [] + + emoji_dict = defaultdict(list) + for emoji in emojis: + emoji_dict[emoji.name.split("_")[0]].append(emoji) + + error_comp = ', '.join(emoji_dict) + msg.append(f"These are the valid categories\n```{error_comp}```") + return embed, msg + + @commands.group(name="emoji", invoke_without_command=True) + async def emoji_group(self, ctx: commands.Context) -> None: + """A group of commands related to emojis.""" + await ctx.send_help(ctx.command) + + @emoji_group.command(name="count", aliases=("c",)) + async def count_command(self, ctx: commands.Context, *, category_query: str = None) -> None: + """Returns embed with emoji category and info given by the user.""" + emoji_dict = defaultdict(list) + + if not ctx.guild.emojis: + await ctx.send("No emojis found.") + return + log.trace(f"Emoji Category {'' if category_query else 'not '}provided by the user") + for emoji in ctx.guild.emojis: + emoji_category = emoji.name.split("_")[0] + + if category_query is not None and emoji_category not in category_query: + continue + + emoji_dict[emoji_category].append(emoji) + + if not emoji_dict: + log.trace("Invalid name provided by the user") + embed, msg = self.generate_invalid_embed(ctx.guild.emojis) + else: + embed, msg = self.embed_builder(emoji_dict) + await LinePaginator.paginate(lines=msg, ctx=ctx, embed=embed) + + @emoji_group.command(name="info", aliases=("i",)) + async def info_command(self, ctx: commands.Context, emoji: Emoji) -> None: + """Returns relevant information about a Discord Emoji.""" + emoji_information = Embed( + title=f'Information about "{emoji.name}"', + description=textwrap.dedent(f""" + Name: {emoji.name} + Created: {time_since(emoji.created_at, precision="hours")} + ID: {emoji.id} + [Emoji source image]({emoji.url}) + """), + color=Color.blurple() + ) + emoji_information.set_thumbnail( + url=emoji.url + ) + await ctx.send(embed=emoji_information) + + +def setup(bot: commands.Bot) -> None: + """Add the Emoji cog into the bot.""" + bot.add_cog(Emoji(bot)) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py deleted file mode 100644 index cc43e9ab..00000000 --- a/bot/exts/evergreen/emoji_count.py +++ /dev/null @@ -1,97 +0,0 @@ -import datetime -import logging -import random -from collections import defaultdict -from typing import List, Tuple - -import discord -from discord.ext import commands - -from bot.constants import Colours, ERROR_REPLIES -from bot.utils.pagination import LinePaginator - -log = logging.getLogger(__name__) - - -class EmojiCount(commands.Cog): - """Command that give random emoji based on category.""" - - def __init__(self, bot: commands.Bot): - self.bot = bot - - @staticmethod - def embed_builder(emoji: dict) -> Tuple[discord.Embed, List[str]]: - """Generates an embed with the emoji names and count.""" - embed = discord.Embed( - color=Colours.orange, - title="Emoji Count", - timestamp=datetime.datetime.utcnow() - ) - msg = [] - - if len(emoji) == 1: - for category_name, category_emojis in emoji.items(): - if len(category_emojis) == 1: - msg.append(f"There is **{len(category_emojis)}** emoji in **{category_name}** category") - else: - msg.append(f"There are **{len(category_emojis)}** emojis in **{category_name}** category") - embed.set_thumbnail(url=random.choice(category_emojis).url) - - else: - for category_name, category_emojis in emoji.items(): - emoji_choice = random.choice(category_emojis) - if len(category_emojis) > 1: - emoji_info = f"There are **{len(category_emojis)}** emojis in **{category_name}** category" - else: - emoji_info = f"There is **{len(category_emojis)}** emoji in **{category_name}** category" - if emoji_choice.animated: - msg.append(f' {emoji_info}') - else: - msg.append(f'<:{emoji_choice.name}:{emoji_choice.id}> {emoji_info}') - return embed, msg - - @staticmethod - def generate_invalid_embed(emojis: list) -> Tuple[discord.Embed, List[str]]: - """Generates error embed.""" - embed = discord.Embed( - color=Colours.soft_red, - title=random.choice(ERROR_REPLIES) - ) - msg = [] - - emoji_dict = defaultdict(list) - for emoji in emojis: - emoji_dict[emoji.name.split("_")[0]].append(emoji) - - error_comp = ', '.join(emoji_dict) - msg.append(f"These are the valid categories\n```{error_comp}```") - return embed, msg - - @commands.command(name="emojicount", aliases=["ec", "emojis"]) - async def emoji_count(self, ctx: commands.Context, *, category_query: str = None) -> None: - """Returns embed with emoji category and info given by the user.""" - emoji_dict = defaultdict(list) - - if not ctx.guild.emojis: - await ctx.send("No emojis found.") - return - log.trace(f"Emoji Category {'' if category_query else 'not '}provided by the user") - for emoji in ctx.guild.emojis: - emoji_category = emoji.name.split("_")[0] - - if category_query is not None and emoji_category not in category_query: - continue - - emoji_dict[emoji_category].append(emoji) - - if not emoji_dict: - log.trace("Invalid name provided by the user") - embed, msg = self.generate_invalid_embed(ctx.guild.emojis) - else: - embed, msg = self.embed_builder(emoji_dict) - await LinePaginator.paginate(lines=msg, ctx=ctx, embed=embed) - - -def setup(bot: commands.Bot) -> None: - """Emoji Count Cog load.""" - bot.add_cog(EmojiCount(bot)) diff --git a/bot/exts/evergreen/emojis.py b/bot/exts/evergreen/emojis.py deleted file mode 100644 index 9f825e6d..00000000 --- a/bot/exts/evergreen/emojis.py +++ /dev/null @@ -1,39 +0,0 @@ -import textwrap - -from discord import Color, Embed, Emoji -from discord.ext import commands - -from bot.utils.time import time_since - - -class Emojis(commands.Cog): - """Has commands related to emojis.""" - - @commands.group(name="emoji", invoke_without_command=True) - async def emojis_group(self, ctx: commands.Context, emoji: Emoji) -> None: - """A group of commands related to emojis.""" - # No parameters = same as invoking info subcommand - await ctx.invoke(self.info_command, emoji) - - @emojis_group.command(name="info") - async def info_command(self, ctx: commands.Context, emoji: Emoji) -> None: - """Returns relevant information about a Discord Emoji.""" - emoji_information = Embed( - title=f'Information about "{emoji.name}"', - description=textwrap.dedent(f""" - Name: {emoji.name} - Created: {time_since(emoji.created_at, precision="hours")} - ID: {emoji.id} - [Emoji source image]({emoji.url}) - """), - color=Color.blurple() - ) - emoji_information.set_thumbnail( - url=emoji.url - ) - await ctx.send(embed=emoji_information) - - -def setup(bot: commands.Bot) -> None: - """Add the Emojis cog into the bot.""" - bot.add_cog(Emojis()) -- cgit v1.2.3 From af22fe556a3b1ab8a98245cc8f96e1d81d0f4289 Mon Sep 17 00:00:00 2001 From: kosayoda Date: Sun, 7 Mar 2021 11:49:58 +0800 Subject: Improve emoji information embed visuals. --- bot/exts/evergreen/emoji.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index a24d420c..8e5279f3 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -1,9 +1,9 @@ -import datetime import logging import random import textwrap from collections import defaultdict -from typing import List, Tuple +from datetime import datetime +from typing import List, Optional, Tuple from discord import Color, Embed, Emoji from discord.ext import commands @@ -27,7 +27,7 @@ class Emoji(commands.Cog): embed = Embed( color=Colours.orange, title="Emoji Count", - timestamp=datetime.datetime.utcnow() + timestamp=datetime.utcnow() ) msg = [] @@ -54,7 +54,7 @@ class Emoji(commands.Cog): @staticmethod def generate_invalid_embed(emojis: list) -> Tuple[Embed, List[str]]: - """Generates error embed.""" + """Generates error embed for invalid emoji categories.""" embed = Embed( color=Colours.soft_red, title=random.choice(ERROR_REPLIES) @@ -70,9 +70,12 @@ class Emoji(commands.Cog): return embed, msg @commands.group(name="emoji", invoke_without_command=True) - async def emoji_group(self, ctx: commands.Context) -> None: + async def emoji_group(self, ctx: commands.Context, emoji: Optional[Emoji]) -> None: """A group of commands related to emojis.""" - await ctx.send_help(ctx.command) + if emoji is not None: + await ctx.invoke(self.info_command, emoji) + else: + await ctx.send_help(ctx.command) @emoji_group.command(name="count", aliases=("c",)) async def count_command(self, ctx: commands.Context, *, category_query: str = None) -> None: @@ -102,18 +105,17 @@ class Emoji(commands.Cog): async def info_command(self, ctx: commands.Context, emoji: Emoji) -> None: """Returns relevant information about a Discord Emoji.""" emoji_information = Embed( - title=f'Information about "{emoji.name}"', + title=f"Emoji Information: {emoji.name}", description=textwrap.dedent(f""" - Name: {emoji.name} - Created: {time_since(emoji.created_at, precision="hours")} - ID: {emoji.id} - [Emoji source image]({emoji.url}) + **Name:** {emoji.name} + **Created:** {time_since(emoji.created_at, precision="hours")} + **Date:** {datetime.strftime(emoji.created_at, "%d/%m/%Y")} + **ID:** {emoji.id} """), - color=Color.blurple() - ) - emoji_information.set_thumbnail( - url=emoji.url - ) + color=Color.blurple(), + url=str(emoji.url), + ).set_thumbnail(url=emoji.url) + await ctx.send(embed=emoji_information) -- cgit v1.2.3 From 5f7abe98e81b8184ea0813eaccffbe92ad5b43dc Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Mon, 8 Mar 2021 12:59:39 +0530 Subject: Add pride leader cog and resources. --- bot/exts/pride/pride_leader.py | 104 +++++++++++++++++++++++++++++++++++ bot/resources/pride/prideleader.json | 100 +++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 bot/exts/pride/pride_leader.py create mode 100644 bot/resources/pride/prideleader.json diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py new file mode 100644 index 00000000..631fca4b --- /dev/null +++ b/bot/exts/pride/pride_leader.py @@ -0,0 +1,104 @@ +import json +import logging +import random +from pathlib import Path +from typing import Optional + +import discord +from discord.ext import commands +from fuzzywuzzy import fuzz + +from bot.constants import Colours + +log = logging.getLogger(__name__) + +PRIDE_LEADERS_RESOURCE = Path("bot/resources/pride/prideleader.json") +MINIMUM_FUZZ_RATIO = 40 + + +class PrideLeader(commands.Cog): + """Gives information about Pride Leaders.""" + + def __init__(self, bot: commands.Bot): + self.bot = bot + + with PRIDE_LEADERS_RESOURCE.open(encoding="utf8") as data: + self.pride = json.load(data) + + def name_verifier(self, leader_name: str) -> Optional[str]: + """Verify leader name whether it is present in resources or not.""" + leader_name = leader_name.title() + if leader_name in self.pride: + return leader_name + log.trace(f"Got a Invalid pride leader: {leader_name}") + + def invalid_embed_generate(self, pride_leader: str) -> discord.Embed: + """Generates Invalid Embed.""" + embed = discord.Embed( + color=Colours.soft_red + ) + valid_names = [] + pride_leader = pride_leader.title() + for name in self.pride: + if fuzz.ratio(pride_leader, name) >= MINIMUM_FUZZ_RATIO: + valid_names.append(name) + + if not valid_names: + valid_names = ", ".join(self.pride) + error_msg = "Sorry your input didn't match any stored name, here is a list of available names:" + else: + valid_names = "\n".join(valid_names) + error_msg = "Did you mean?" + + embed.description = f"{error_msg}\n```{valid_names}```" + return embed + + def embed_builder(self, leader_name: str) -> discord.Embed: + """Generate an Embed with information about a pride leader.""" + embed = discord.Embed( + title=leader_name, + description=self.pride[leader_name]["About"], + color=Colours.blue + ) + embed.add_field( + name="Known for", + value=self.pride[leader_name]["Known for"], + inline=False + ) + embed.add_field( + name="D.O.B and Birth place", + value=self.pride[leader_name]["Born"], + inline=False + ) + embed.add_field( + name="Awards and honors", + value=self.pride[leader_name]["Awards"], + inline=False + ) + embed.set_thumbnail(url=self.pride[leader_name]["url"]) + return embed + + @commands.command(aliases=("pl", "prideleader")) + async def pride_leader(self, ctx: commands.Context, *, pride_leader_name: Optional[str]) -> None: + """ + Information about a Pride Leader. + + Returns information about the specified pride leader + and if there is no pride leader given, return a random pride leader. + """ + if not pride_leader_name: + leader = random.choice([name for name in self.pride]) + else: + leader = self.name_verifier(pride_leader_name) + if not leader: + embed = self.invalid_embed_generate(pride_leader_name) + await ctx.send(embed=embed) + return + + embed = self.embed_builder(leader) + await ctx.send(embed=embed) + + +def setup(bot: commands.Bot) -> None: + """Loads Pride leader cog.""" + bot.add_cog(PrideLeader(bot)) diff --git a/bot/resources/pride/prideleader.json b/bot/resources/pride/prideleader.json new file mode 100644 index 00000000..30e84bdc --- /dev/null +++ b/bot/resources/pride/prideleader.json @@ -0,0 +1,100 @@ +{ + "Tim Cook": { + "Known for": "CEO of Apple", + "About": "**Timothy Donald Cook** popularly known as Tim Cook. Despite being a notably private person, Tim Cook became the first CEO of a 500 fortune company, coming out as gay in 2014. He revealed his sexual orientation through an open letter published in Bloomberg BusinessWeek. He strongly believes that business leaders need to do their part to make the world a better place. An excerpt from his open letter is as follows: “Part of social progress is understanding that a person is not defined only by one's sexuality, race, or gender.", + "url": "https://image.cnbcfm.com/api/v1/image/105608434-1543945658496rts28qzc.jpg?v=1554921416&w=1400&h=950", + "Born": "In November 1, 1960 at Mobile, Alabama, U.S.", + "Awards": "• Financial Times Person of the Year (2014)\n• Ripple of Change Award (2015)\n• Fortune Magazine's: World's Greatest Leader. (2015)\n• Alabama Academy of Honor: Inductee. (2015)\n• Human Rights Campaign's Visibility Award (2015)\n• Honorary Doctor of Science from University of Glasgow in Glasgow, Scotland (2017)\n• Courage Against Hate award from Anti-Defamation League (2018)" + }, + "Alan Joyce": { + "Known for": "CEO of Qantas Airlines", + "About": "**Alan Joseph Joyce, AC** popularly known as Alan Joyce. Alan Joyce has been named as one of the world’s most influential business executives. He has always been very open about his orientation and has been in a committed gay relationship for over 20 years now. He supports same-sex marriage and believes that it is critical to make people recognize that they know LGBT people\n\nAlan likes to frequently talk about equality at the workplace and said, “It has become more important for leaders who are LGBT to be open about their sexuality. I am passionate about it. There should be more people like Apple’s Tim Cook and Paul Zahra, the former CEO of Australia’s David Jones [store chain].”", + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Alan_Joyce_%28cropped%29.jpg/220px-Alan_Joyce_%28cropped%29.jpg", + "Born": "On 30 June, 1966 at Tallaght, Dublin, Republic of Ireland", + "Awards": "• The Australian named Joyce the most influential business leader in 2011\n• Joyce is an Ambassador of the Australian Indigenous Education Foundation\n• Joyce named a Companion of the Order of Australia, Australia's highest civil honour, in the 2017 Queen's birthday honours list" + }, + "Peter Thiel": { + "Known for": "Co-Founder and Former CEO of paypal", + "About": "**Peter Andreas Thiel** popularly known as Peter Thiel. Peter Thiel served as the CEO of PayPal from its inception to its sale to eBay in October 2002. Thiel’s sexuality came out in 2007 when Gawker Media outed him in a blog post. He became the first openly gay speaker at Republican National Convention and delivered a speech on sexuality.\n\n“Of course every American has a unique identity,” he said. “I am proud to be gay. I am proud to be a Republican. But most of all I am proud to be an American.”", + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Peter_Thiel_2014_by_Heisenberg_Media.jpg/220px-Peter_Thiel_2014_by_Heisenberg_Media.jpg", + "Born": "On 11 October, 1967 at Frankfurt, West Germany", + "Awards": "• Thiel received a co-producer credit for Thank You for Smoking, a 2005 feature film based on Christopher Buckley's 1994 novel of the same name\n• In 2006, Thiel won the Herman Lay Award for Entrepreneurship\n• In 2007, he was honored as a Young Global leader by the World Economic Forum as one of the 250 most distinguished leaders age 40 and under\n• On 7 November 2009, Thiel was awarded an honorary degree from Universidad Francisco Marroquin\n• In 2012, Students For Liberty, an organization dedicated to spreading libertarian ideals on college campuses, awarded Thiel its “Alumnus of the Year” award\n• In February 2013, Thiel received a TechCrunch Crunchie Award for Venture Capitalist of the Year.\n• Insurance Executive of the Year: St Joseph’s University’s Academy of Risk Management and Insurance in Philadelphia" + }, + "Martine Rothblatt": { + "Known for": "CEO of United Therapeutics", + "About": "**Martine Aliana Rothblatt** popularly known as Martine Rothblatt. Martine co-founded Sirius XM Satellite Radio in 1990 and 1996 founded United Therapeutics, making her the highest-earning CEO in the biopharmaceutical industry. She came out as a transgender in 1994 and has been vocal about the trans community ever since.\n\nIn 2014 in an interview, Martine said, “I took a journey from male to female, so if I hide that, I’m, like just replicating the closet of my past with another closet of the future. That made no sense and that is why I am open.” She has authored a book called “The apartheid of Sex”.", + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/bd/Martine-Rothblatt2-5773.jpg/220px-Martine-Rothblatt2-5773.jpg", + "Born": "On October 10, 1954 at Chicago, Illinois, U.S.", + "Awards": "• In January 2018 Rothblatt was presented the UCLA Medal, the university's highest award, in recognition of her creation of Sirius XM satellite radio, advancing organ transplant technology, and having “expanded the way we understand fundamental concepts ranging from communication to gender to the nature of consciousness and mortality.”" + }, + "Peter Arvai": { + "Known for": "Co-Founder and CEO of Prezi", + "About": "Peter Arvai is the 11th most influential LGBT leader in the world. His goal has always been to create an open environment that fosters diversity. He has co-founded an NGO with Google and espell called ‘We Are Open’ to promote openness at the workplace. Peter regularly talks about his gay background in every on-boarding session at his company.\n\nWhen Prezi was featured on the cover of Forbes, Peter used the opportunity by coming out and sharing his story to start a conversation on this topic that most people seem to avoid. If you want to create a more inclusive workplace, you need to be willing to be vulnerable yourself, he says. “To spark honest discussions about inclusivity and openness, your personal experience of inclusion is a key resource and you need to create a safe environment so people find the courage to have uncomfortable conversations.”", + "url": "https://cached.imagescaler.hbpl.co.uk/resize/scaleWidth/880/cached.offlinehbpl.hbpl.co.uk/news/OTM/Peterarvai-20191218101617863.jpg", + "Born": "On October 26, 1979 at Karlskoga, Sweden", + "Awards": "• 2014: European Tech Startups Award for Best Startup Co-Founders.\n• 2014: European Web Entrepreneur of the Year.\n• 2015: Executive of the Year – Business Services: Bronze Stevie Winner.\n• 2016: Number 11 on the 2016 OUTstanding & Financial Times Leading LGBT Executives List of 100" + }, + "Inga Beale": { + "Known for": "CEO of Lloyd's of London", + "About": "Inga became the first female CEO of Lloyd’s of London in 2013 and in 2017 was named a Dame Commander of the Order of the British Empire for her contribution towards the British economy.\n\nShe came out first as a bisexual, in an interview in 2008 and since then has made efforts to bring diversity and inclusion at the forefront in her company. “It’s not about me. It’s about what you do for other people. For me, it’s so important because you need these role models.”\n\nSpeaking between meetings at the World Economic Forum in Davos, she says her position at the top of the LGBT table is important for its impact on others: “It’s about giving people confidence,” she says.", + "url": "https://cdn-res.keymedia.com/cms/images/us/018/0248_637072371134927741.jpeg", + "Born": "On May 15, 1963 at Great Britain", + "Awards": "• Trailblazer of the Year: The Insurance Industry Charitable Foundation (IICF)(2019)\n• Free Enterprise Award: Insurance Federation of New York (IFNY)(2018)\n• Market People - Outstanding Contribution Award: The London Market Forums(2018)\n• Outstanding Achievement Award: Insurance Day | Informa(2018)\n• Barclay's Lifetime Achievement Award: Variety Children's Charity - Catherine Awards(2017)\n• Insurance Woman of the Year Award: Association of Professional Insurance Women (APIW)(2017)\n• Dame Commander of the Order of the British Empire - DBE: HM The Queen(2017)\n• Insurance Personality of the Year: British Insurance Awards\n• Insurance Executive of the Year: St Joseph’s University’s Academy of Risk Management and Insurance in Philadelphia(2015)" + }, + "David Geffen": { + "Known for": "Founder of Dreamworks", + "About": "**David Lawrence Geffen** popularly known as David Geffen. Founder of film studio Dream Works as well as record labels Asylum Records, Geffen Records and DGC Records, David Geffen came out in 1992 at a fund raiser announcing, “As a Gay man, I have come a long way to be here tonight.” He was already among the strongest pillars of the gay rights movement by then.\n\n“If I am going to be a role model, I want to be one that I can be proud of,” Geffen said in an interview back in 1992.”\n\nGeffen contributed significantly towards society through the David Geffen Foundation that worked relentlessly towards healthcare, people affected by HIV/AIDS, civil liberties, issues of concern to the Jewish community, and arts. Interestingly, the song ‘Free man in Paris’ by Joni Mitchell is based on Geffen’s time in Paris during a trip they took together along with Canadian musician Robbie Robertson and his wife.", + "url": "https://i.insider.com/5b733a2be199f31d138b4bec?width=1100&format=jpeg&auto=webp", + "Born": "On February 21, 1943 at New York City, U.S.", + "Awards": "• Tony Award for Best Musical(1983)\n• Tony Award for Best Play(1988)\n• Daytime Emmy Award for Outstanding Children's Animated Program(1990)" + }, + "Joel Simkhai": { + "Known for": "Founder and former CEO of Grindr and Blendr", + "About": "Joel Simkhai founded Grindr, a dating app for men in the LGBTQ+ community in 2009. He says he launched the app with a “selfish desire’ to meet more gay men. Today, Grindr has over 4 million users and has become the world's largest social networking platform for men from the LGBTQ+ community to interact.\n\nIn an interview Joel shared, “ As a kid I was teased, teased for being feminine, I guess for being gay. So a lot of my early life was just in denial about it with myself and others. But by 16 and as I started getting older, I realized that I like guys”. “My story is the story of every gay man. We are seen as a minority in some ways and the services out there weren’t that great. I am an introvert so I don’t really do well at being who I am right away but online seemed like my comfort zone”. It all begins with meeting someone, he adds.", + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/44/Joel_Simkhai_2012_%28cropped%29.jpg/220px-Joel_Simkhai_2012_%28cropped%29.jpg", + "Born": "On 1976 at Tel Aviv, Israel", + "Awards": "• Simkhai’s company has been mentioned in pop culture icons like The Office, Saturday Night Live, Judge Judy and Top Gear and won a number of industry awards including “Best Mobile Dating App” in 2011 and 2012 and “Best New Technology” in 2012 from the iDate Awards and “Best Location Application” at the TechCrunch Awards in 2011." + }, + "Megan Smith": { + "Known for": "Former CTO of United States", + "About": "Megan Smith the former CTO of the United States has always been vocal about the need to push inclusion. Most central to her message, however, is the key insight that is most often lost: not only is inclusivity a part of technology’s future, it was also a seminal part of its past. Ada Lovelace, for example, an English woman born in 1812, was the first computer programmer; Katherine G. Johnson, an African-American woman featured in the Oscar-nominated film Hidden Figures, helped put NASA astronauts on the moon.\n\nIn 2003, in an interview Megan said, “When you are gay, you come out everyday because everyone assumes you are straight. But you have to be yourself.” Smith also hopes to open up the tech industry to more women and encourage girls to pursue a career in it.", + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f0/Megan_Smith_official_portrait.jpg/220px-Megan_Smith_official_portrait.jpg", + "Born": "On October 21, 1964 at Buffalo, New York, and Fort Erie, Ontario.", + "Awards": "• World Economic Forum Technology Pioneer 2001, 2002\n• Listed by Out magazine in 2012 and 2013, as one of the 50 most powerful LGBT people in the United States\n• Reuters Digital Vision Program Fellow at Stanford, 2003-2004\n• Top 25 Women on the Web, 2000\n• Upside Magazine 100 Digital Elite, 1999 and 2000\n• Advertising Age i.20, 1999\n• GLAAD Interactive Media Award for Internet Leadership, 1999\n• Charging Buffalo Award, 2015\n• Business Insider 23 Most Powerful LGBTQ+ People in Tech, 2019" + }, + "David Bohnett": { + "Known for": "Founder of Geocities", + "About": "**David C. Bohnett** popularly known as David Bohnett. A tech entrepreneur, with his LA-based venture firm Baroda Ventures, founded in 1998, David Bohnett is also the Founder of Geocities, which remains the first largest internet venture built on user-generated content, founded in 1998 and acquired by Yahoo in 1999. Geocities was cited #2 by TechRadar in its list of ‘20 websites that changed the world’ back in 2008.\n\nBohnett came out to his family post his graduation and worked extensively towards equal rights for gays and lesbians, and towards legalizing same-sex marriage. He founded the David Bohnett Foundation, dedicated to community-building and social activism addressing concerns across a broad spectrum of arts, educational, and civic programs. The first openly bisexual US congressperson Krysten Sinema and the first openly gay mayor of a major US city (Houston), Annise Parker, are both alumnis of the LGBT Leadership Fellows run by his foundation that trains LGBT leaders for local and state governments.", + "url": "https://upload.wikimedia.org/wikipedia/commons/c/cb/David_Bohnett.jpg", + "Born": "On April 2, 1956 at Chicago, Illinois", + "Awards": "• Number 16 on Time's Top 50 Cyber Elite (1998)\n• Upside magazine's Elite 100 (1998)\n• Newsweek's “100 People to Watch in the Next Millennium”\n• Ernst & Young Entrepreneur of the Year Award for Southern California (1999)\n• Los Angeles Gay and Lesbian Center's Rand Schrader Award (1999)\n• Los Angeles Business Journal's Technology Leader of the Year (2000)\n• ACLU Citizen Advocate Award (2002)\n• amfAR Award of Courage (2006)\n• Los Angeles City of Angels Award (2008)\n• GLSEN's Lifetime Achievement Award (2009)\n• Honorary doctorate of Humane Letters from Whittier College (2012)\n• American Jewish Committee Los Angeles' Ira E. Yellin Community Leadership Award (2014)\n• Brady Bear Award from the Brady Center to Prevent Gun Violence (2016)\n• Los Angeles Business Journal's LA 500: The Most Influential People in Los Angeles (2017)" + }, + "Jennifer Pritzker": { + "Known for": "Founder and CEO of Tawani Enterprises", + "About": "**Jennifer Natalya Pritzker** popularly known as Jennifer Pritzker. A retired Lieutenant Colonel of the US Army, and Founder and CEO of private wealth management firm Tawani Enterprises, Jennifer Natalya Pritzker, is an American investor, philanthropist, member of the Pritzker family, and the world’s first transgender billionaire. Having retired from the US Army in 2001, Jennifer was promoted to the honorary rank of Colonel in the Illinois Army National Guard.\n\nFormerly known as James Nicholas Pritzker, she legally changed her official name to Jennifer Natalya Pritzker in 2013, identifying herself as a woman for all business and personal undertakings, as per an announcement shared with employees of the Pritzker Military Library and Tawani Enterprises.\n\nPritzker in 1995 founded the Tawani Foundation aiming “to enhance the awareness and understanding of the importance of the Citizen Soldier; to preserve unique sites of significance to American and military history; to foster health and wellness projects for improved quality of life; and to honor the service of military personnel, past, present and future.”", + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/62/Jennifer_Pritzker.jpg/220px-Jennifer_Pritzker.jpg", + "Born": "On August 13, 1950 at Chicago, Illinois.", + "Awards": "• First transgender billionaire\n• Founder of Tawani Foundation and Pritzker Military Library" + }, + "Claudia Brind-Woody": { + "Known for": "VP and managing director of intellectual property IBM", + "About": "**Claudia Lavergne Brind-Woody** popularly known as Claudia Brind-Woody. Global Co-Chair for the LGBT Executive Task-force at IBM, Claudia Brind-Woody is a force of nature to reckon with. In 2019, she was named among the most powerful LGBTQ+ people in tech, in addition to being in Financial Times Top 50 Outstanding list in 2013, 2014 and 2015, The Guardian’s 100 most influential LGBT people of the year in 2012, and winning the Out & Equal Trailblazer award in 2011, among other accolades.\n\nShe came out as a lesbian in the early years of her career and strives to work towards equality at the workplace. In an interview back in 2016 she shared, “What the LGBT+ community wants is to just want it to be ordinary [to be LGBT+] so that you are just seen to be valued on merit and what you bring to the business without someone thinking twice about you being LGBT+....When our employees don't have to think twice about struggling for the same benefits, recognition, or are afraid of being safe, then productivity goes up.”", + "url": "https://image.insider.com/580e9350dd089551098b47ff?width=750&format=jpeg&auto=webp", + "Born": "On January 30, 1955 at Virginia, U.S.", + "Awards": "• Out & Equal Trailblazer Award (2011)\n• GO Magazine's 100 Women We Love (2011)\n• The Guardian's World Pride Power List Top 100 (2012)\n• The Financial Times' Top 50 Outstanding list (2013, 2014, 2015)\n• The Daily Telegraph's Top 50 list of LGBT executives (2015)\n• The Financial Times' Hall of Fame (2016)\n• Diva power list (2016)\n• Business Insider The 23 Most Influential LGBTQ+ People in Tech" + }, + "Laxmi Narayan Tripathi": { + "Known for": "Humans rights activist and founder, Astitva trust", + "About": "The first transgender individual to represent APAC in the UN task meeting in 2008, representative of APAC yet again at the 20th International AIDS Conference in Melbourne and recipient of the ‘Indian of the Year’ award in 2017, Lakshmi Narayan Tripathi is a transgender activist, and part of the team that led the charge to getting transgender recognized as a third gender in India by the Supreme Court in 2014.\n\nLakshmi was appointed as the President of the NGO DAI Welfare Society in 2002, the first registered and working organization for eunuchs in South Asia. By 2007 she founded her own organization, Astitiva that works towards the welfare, support and development of sexual minorities.\n\nWith the background of an abusive childhood and being bullied for being feminine, she stated in an interview, “I chose not to remember the prejudice,” adding, “Rather I think (about) the good things that have happened to me, and be a flamboyant rainbow.”", + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/bd/Laxmi_Narayan_Tripathi_at_JLF_Melbourne_presented_by_Melbourne_Writers_Festival%2C_Federation_Square%2C_Melbourne_2017.jpg/220px-Laxmi_Narayan_Tripathi_at_JLF_Melbourne_presented_by_Melbourne_Writers_Festival%2C_Federation_Square%2C_Melbourne_2017.jpg", + "Born": "On 13th Dec 1978 at Thane", + "Awards": "• Awarded 'Indian of the Year 2017" + }, + "Tim Gill": { + "Known for": "Founder of Quark", + "About": "Tim Gill founded Quark Inc in 1981 and sold his stakes in Quark in 1999 in order to focus more on his interests in LGBT+ activism and philanthropy. He founded the pro-LGBT Gill Foundation in 1994, and since its inception it has invested more than $357 Mn in programs and non-profits around the country, substantially contributing towards many victories for LGBT community.\n\nGill married Scott Miller in 2009 and continues to be the largest donor for LGBT initiatives in America.\n\n“The LGBTQ movement has no Martin Luther King. We never have. And we probably never will,” Gill said. “So it’s not going to be grandiose gestures and big speeches and things like that that secure us equal opportunity. It will be the hard work of thousands and thousands of people over many, many years.”", + "url": "https://gillfoundation.org/wp-content/uploads/2014/09/tim-gill-20151.jpg", + "Born": "On October 18, 1953 at Hobart, Indiana", + "Awards": "• Gill was awarded the NOGLSTP GLBT Engineer of the Year Award in 2007." + } +} -- cgit v1.2.3 From 9ff3d046b7c20ad0e5ba876fadb9347b667e6fc7 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Mon, 8 Mar 2021 13:57:51 +0530 Subject: Add link to wikipedia in error embed and information embed. --- bot/exts/pride/pride_leader.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index 631fca4b..9a2ee6e1 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -8,7 +8,7 @@ import discord from discord.ext import commands from fuzzywuzzy import fuzz -from bot.constants import Colours +from bot import constants log = logging.getLogger(__name__) @@ -35,7 +35,7 @@ class PrideLeader(commands.Cog): def invalid_embed_generate(self, pride_leader: str) -> discord.Embed: """Generates Invalid Embed.""" embed = discord.Embed( - color=Colours.soft_red + color=constants.Colours.soft_red ) valid_names = [] pride_leader = pride_leader.title() @@ -51,6 +51,13 @@ class PrideLeader(commands.Cog): error_msg = "Did you mean?" embed.description = f"{error_msg}\n```{valid_names}```" + embed.add_field( + name="You can get information about the Pride Leader on the Wikipedia command", + value=f"Do `{constants.Client.prefix}wiki {pride_leader}`" + f" in <#{constants.Channels.community_bot_commands}>", + inline=False + ) + return embed def embed_builder(self, leader_name: str) -> discord.Embed: @@ -58,7 +65,7 @@ class PrideLeader(commands.Cog): embed = discord.Embed( title=leader_name, description=self.pride[leader_name]["About"], - color=Colours.blue + color=constants.Colours.blue ) embed.add_field( name="Known for", @@ -75,6 +82,12 @@ class PrideLeader(commands.Cog): value=self.pride[leader_name]["Awards"], inline=False ) + embed.add_field( + name="For More Information", + value=f"Do `{constants.Client.prefix}wiki {leader_name}`" + f" in <#{constants.Channels.community_bot_commands}>", + inline=False + ) embed.set_thumbnail(url=self.pride[leader_name]["url"]) return embed -- cgit v1.2.3 From 5c46abf4b88b4d45f672082c829b1ec6cec12296 Mon Sep 17 00:00:00 2001 From: kosayoda Date: Mon, 8 Mar 2021 16:44:21 +0800 Subject: Rename cog to avoid name conflict with discordpy. --- bot/exts/evergreen/emoji.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index 8e5279f3..e07b1505 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -15,7 +15,7 @@ from bot.utils.time import time_since log = logging.getLogger(__name__) -class Emoji(commands.Cog): +class Emojis(commands.Cog): """A collection of commands related to emojis in the server.""" def __init__(self, bot: commands.Bot): @@ -120,5 +120,5 @@ class Emoji(commands.Cog): def setup(bot: commands.Bot) -> None: - """Add the Emoji cog into the bot.""" - bot.add_cog(Emoji(bot)) + """Add the Emojis cog into the bot.""" + bot.add_cog(Emojis(bot)) -- cgit v1.2.3 From cfd59aed903c2ed2d57f4fc804bd978e07090ae1 Mon Sep 17 00:00:00 2001 From: kosayoda Date: Mon, 8 Mar 2021 16:51:27 +0800 Subject: Improve punctuation and wording of .emoji count. --- bot/exts/evergreen/emoji.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index e07b1505..99f71218 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -34,18 +34,18 @@ class Emojis(commands.Cog): if len(emoji) == 1: for category_name, category_emojis in emoji.items(): if len(category_emojis) == 1: - msg.append(f"There is **{len(category_emojis)}** emoji in **{category_name}** category") + msg.append(f"There is **{len(category_emojis)}** emoji in the **{category_name}** category.") else: - msg.append(f"There are **{len(category_emojis)}** emojis in **{category_name}** category") + msg.append(f"There are **{len(category_emojis)}** emojis in the **{category_name}** category.") embed.set_thumbnail(url=random.choice(category_emojis).url) else: for category_name, category_emojis in emoji.items(): emoji_choice = random.choice(category_emojis) if len(category_emojis) > 1: - emoji_info = f"There are **{len(category_emojis)}** emojis in **{category_name}** category" + emoji_info = f"There are **{len(category_emojis)}** emojis in the **{category_name}** category." else: - emoji_info = f"There is **{len(category_emojis)}** emoji in **{category_name}** category" + emoji_info = f"There is **{len(category_emojis)}** emoji in the **{category_name}** category." if emoji_choice.animated: msg.append(f' {emoji_info}') else: @@ -66,7 +66,7 @@ class Emojis(commands.Cog): emoji_dict[emoji.name.split("_")[0]].append(emoji) error_comp = ', '.join(emoji_dict) - msg.append(f"These are the valid categories\n```{error_comp}```") + msg.append(f"These are the valid emoji categories:\n```{error_comp}```") return embed, msg @commands.group(name="emoji", invoke_without_command=True) -- cgit v1.2.3 From 6c89267f2ce07a3a55dff933f5d76aba2a695f48 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Mon, 8 Mar 2021 19:33:41 +0100 Subject: Properly rename countdown_task -> notification_task The old name was still in use in the cog_unload, making it fail --- bot/exts/christmas/advent_of_code/_cog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index 466edd48..2203bf00 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -36,7 +36,7 @@ class AdventOfCode(commands.Cog): self.about_aoc_filepath = Path("./bot/resources/advent_of_code/about.json") self.cached_about_aoc = self._build_about_embed() - self.countdown_task = None + self.notification_task = None self.status_task = None notification_coro = _helpers.new_puzzle_notification(self.bot) @@ -268,7 +268,7 @@ class AdventOfCode(commands.Cog): def cog_unload(self) -> None: """Cancel season-related tasks on cog unload.""" log.debug("Unloading the cog and canceling the background task.") - self.countdown_task.cancel() + self.notification_task.cancel() self.status_task.cancel() def _build_about_embed(self) -> discord.Embed: -- cgit v1.2.3 From e6e12c3d3b8a9233cdd92c380c720aa461ab4565 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Mon, 8 Mar 2021 19:39:09 +0100 Subject: Restrict the aoc leaderboard commands to december --- bot/exts/christmas/advent_of_code/_cog.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index 2203bf00..c86ce18a 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -173,6 +173,7 @@ class AdventOfCode(commands.Cog): else: await ctx.message.add_reaction(Emojis.envelope) + @in_month(Month.DECEMBER) @adventofcode_group.command( name="leaderboard", aliases=("board", "lb"), @@ -198,6 +199,7 @@ class AdventOfCode(commands.Cog): await ctx.send(content=f"{header}\n\n{table}", embed=info_embed) + @in_month(Month.DECEMBER) @adventofcode_group.command( name="global", aliases=("globalboard", "gb"), -- cgit v1.2.3 From 8b0c1cd3b6729558218bd85aeb660d1975877345 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Tue, 9 Mar 2021 10:21:47 +0100 Subject: Remove useless initialization in the AoC cog --- bot/exts/christmas/advent_of_code/_cog.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index c86ce18a..29902306 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -36,9 +36,6 @@ class AdventOfCode(commands.Cog): self.about_aoc_filepath = Path("./bot/resources/advent_of_code/about.json") self.cached_about_aoc = self._build_about_embed() - self.notification_task = None - self.status_task = None - notification_coro = _helpers.new_puzzle_notification(self.bot) self.notification_task = self.bot.loop.create_task(notification_coro) self.notification_task.set_name("Daily AoC Notification") -- cgit v1.2.3 From ccfb8a7deea064b3b1e02d233d033cec20891eba Mon Sep 17 00:00:00 2001 From: Dillon Runke <44979306+Kronifer@users.noreply.github.com> Date: Tue, 9 Mar 2021 16:54:57 -0600 Subject: small style fix --- bot/exts/easter/earth_photos.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 60e34b15..58d7de61 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -46,10 +46,9 @@ class EarthPhotos(commands.Cog): ) embed.set_image(url=embedlink) embed.add_field( - name="Author", - value=f"Photo by [{username}]({profile}{rf}) \ - on [Unsplash](https://unsplash.com{rf})." - ) + value=f"Photo by [{username}]({profile}{rf}) " + f"on [Unsplash](https://unsplash.com{rf})." + ) await ctx.send(embed=embed) -- cgit v1.2.3 From abc41bde93fb51eeac6d7a80286fde184a665ba0 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 9 Mar 2021 17:06:17 -0600 Subject: embed fix --- bot/exts/easter/earth_photos.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 58d7de61..89d37e11 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -46,9 +46,10 @@ class EarthPhotos(commands.Cog): ) embed.set_image(url=embedlink) embed.add_field( - value=f"Photo by [{username}]({profile}{rf}) " - f"on [Unsplash](https://unsplash.com{rf})." - ) + name="Author", + value=f"Photo by [{username}]({profile}{rf}) " + f"on [Unsplash](https://unsplash.com{rf})." + ) await ctx.send(embed=embed) -- cgit v1.2.3 From 3939fa7e065a0ccab7e4aa0ece866ec99c016139 Mon Sep 17 00:00:00 2001 From: Dillon Runke <44979306+Kronifer@users.noreply.github.com> Date: Wed, 10 Mar 2021 07:50:44 -0600 Subject: suggestion by @Xithrius Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/easter/earth_photos.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 89d37e11..bf658391 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -47,8 +47,10 @@ class EarthPhotos(commands.Cog): embed.set_image(url=embedlink) embed.add_field( name="Author", - value=f"Photo by [{username}]({profile}{rf}) " - f"on [Unsplash](https://unsplash.com{rf})." + value=( + f"Photo by [{username}]({profile}{rf}) " + f"on [Unsplash](https://unsplash.com{rf})." + ) ) await ctx.send(embed=embed) -- cgit v1.2.3 From 2758242609ba4f7cba7e7fbf6dcc5d0932ee3bb5 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 17:49:42 +0000 Subject: Alter pre-commit --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a66bf97c..2e91534a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,8 +18,8 @@ repos: hooks: - id: flake8 name: Flake8 - description: This hook runs flake8 within our project's pipenv environment. - entry: pipenv run flake8 + description: This hook runs flake8 within our project's poetry environment. + entry: poetry run flake8 language: system types: [python] require_serial: true -- cgit v1.2.3 From 15c6daeed2f89a72b68c22e6f5c97b3efacc9f81 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 17:50:05 +0000 Subject: Remove Pipfile and Pipfile.lock --- Pipfile | 37 ---- Pipfile.lock | 699 ----------------------------------------------------------- 2 files changed, 736 deletions(-) delete mode 100644 Pipfile delete mode 100644 Pipfile.lock diff --git a/Pipfile b/Pipfile deleted file mode 100644 index e7e01a31..00000000 --- a/Pipfile +++ /dev/null @@ -1,37 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -aiodns = "~=2.0" -arrow = "~=0.14" -beautifulsoup4 = "~=4.8" -fuzzywuzzy = "~=0.17" -pillow = "~=7.2" -pytz = "~=2019.2" -sentry-sdk = "~=0.19" -PyYAML = "~=5.3.1" -"discord.py" = {extras = ["voice"], version = "~=1.5.1"} -async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} -emojis = "~=0.6.0" - -[dev-packages] -flake8 = "~=3.8" -flake8-annotations = "~=2.3" -flake8-bugbear = "~=20.1" -flake8-docstrings = "~=1.5" -flake8-import-order = "~=0.18" -flake8-string-format = "~=0.3" -flake8-tidy-imports = "~=4.1" -flake8-todo = "~=0.7" -pep8-naming = "~=0.11" -pre-commit = "~=2.1" - -[requires] -python_version = "3.8" - -[scripts] -start = "python -m bot" -lint = "pre-commit run --all-files" -precommit = "pre-commit install" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index ec801979..00000000 --- a/Pipfile.lock +++ /dev/null @@ -1,699 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "b4aaaacbab13179145e36d7b86c736db512286f6cce8e513cc30c48d68fe3810" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.8" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "aiodns": { - "hashes": [ - "sha256:815fdef4607474295d68da46978a54481dd1e7be153c7d60f9e72773cd38d77d", - "sha256:aaa5ac584f40fe778013df0aa6544bf157799bd3f608364b451840ed2c8688de" - ], - "index": "pypi", - "version": "==2.0.0" - }, - "aiohttp": { - "hashes": [ - "sha256:1a4160579ffbc1b69e88cb6ca8bb0fbd4947dfcbf9fb1e2a4fc4c7a4a986c1fe", - "sha256:206c0ccfcea46e1bddc91162449c20c72f308aebdcef4977420ef329c8fcc599", - "sha256:2ad493de47a8f926386fa6d256832de3095ba285f325db917c7deae0b54a9fc8", - "sha256:319b490a5e2beaf06891f6711856ea10591cfe84fe9f3e71a721aa8f20a0872a", - "sha256:470e4c90da36b601676fe50c49a60d34eb8c6593780930b1aa4eea6f508dfa37", - "sha256:60f4caa3b7f7a477f66ccdd158e06901e1d235d572283906276e3803f6b098f5", - "sha256:66d64486172b032db19ea8522328b19cfb78a3e1e5b62ab6a0567f93f073dea0", - "sha256:687461cd974722110d1763b45c5db4d2cdee8d50f57b00c43c7590d1dd77fc5c", - "sha256:698cd7bc3c7d1b82bb728bae835724a486a8c376647aec336aa21a60113c3645", - "sha256:797456399ffeef73172945708810f3277f794965eb6ec9bd3a0c007c0476be98", - "sha256:a885432d3cabc1287bcf88ea94e1826d3aec57fd5da4a586afae4591b061d40d", - "sha256:c506853ba52e516b264b106321c424d03f3ddef2813246432fa9d1cefd361c81", - "sha256:fb83326d8295e8840e4ba774edf346e87eca78ba8a89c55d2690352842c15ba5" - ], - "markers": "python_full_version >= '3.5.3'", - "version": "==3.6.3" - }, - "aioredis": { - "hashes": [ - "sha256:15f8af30b044c771aee6787e5ec24694c048184c7b9e54c3b60c750a4b93273a", - "sha256:b61808d7e97b7cd5a92ed574937a079c9387fdadd22bfbfa7ad2fd319ecc26e3" - ], - "version": "==1.3.1" - }, - "arrow": { - "hashes": [ - "sha256:e098abbd9af3665aea81bdd6c869e93af4feb078e98468dd351c383af187aac5", - "sha256:ff08d10cda1d36c68657d6ad20d74fbea493d980f8b2d45344e00d6ed2bf6ed4" - ], - "index": "pypi", - "version": "==0.17.0" - }, - "async-rediscache": { - "extras": [ - "fakeredis" - ], - "hashes": [ - "sha256:6be8a657d724ccbcfb1946d29a80c3478c5f9ecd2f78a0a26d2f4013a622258f", - "sha256:c25e4fff73f64d20645254783c3224a4c49e083e3fab67c44f17af944c5e26af" - ], - "index": "pypi", - "version": "==0.1.4" - }, - "async-timeout": { - "hashes": [ - "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", - "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" - ], - "markers": "python_full_version >= '3.5.3'", - "version": "==3.0.1" - }, - "attrs": { - "hashes": [ - "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", - "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.3.0" - }, - "beautifulsoup4": { - "hashes": [ - "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35", - "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25", - "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666" - ], - "index": "pypi", - "version": "==4.9.3" - }, - "certifi": { - "hashes": [ - "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", - "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" - ], - "version": "==2020.12.5" - }, - "cffi": { - "hashes": [ - "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813", - "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06", - "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea", - "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee", - "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396", - "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73", - "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315", - "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1", - "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49", - "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892", - "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482", - "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058", - "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5", - "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53", - "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045", - "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3", - "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5", - "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e", - "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c", - "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369", - "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827", - "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053", - "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa", - "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4", - "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322", - "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132", - "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62", - "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa", - "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0", - "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396", - "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e", - "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991", - "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6", - "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1", - "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406", - "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d", - "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c" - ], - "version": "==1.14.5" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" - }, - "discord.py": { - "extras": [ - "voice" - ], - "hashes": [ - "sha256:2367359e31f6527f8a936751fc20b09d7495dd6a76b28c8fb13d4ca6c55b7563", - "sha256:def00dc50cf36d21346d71bc89f0cad8f18f9a3522978dc18c7796287d47de8b" - ], - "index": "pypi", - "version": "==1.5.1" - }, - "emojis": { - "hashes": [ - "sha256:7da34c8a78ae262fd68cef9e2c78a3c1feb59784489eeea0f54ba1d4b7111c7c", - "sha256:bf605d1f1a27a81cd37fe82eb65781c904467f569295a541c33710b97e4225ec" - ], - "index": "pypi", - "version": "==0.6.0" - }, - "fakeredis": { - "hashes": [ - "sha256:01cb47d2286825a171fb49c0e445b1fa9307087e07cbb3d027ea10dbff108b6a", - "sha256:2c6041cf0225889bc403f3949838b2c53470a95a9e2d4272422937786f5f8f73" - ], - "version": "==1.4.5" - }, - "fuzzywuzzy": { - "hashes": [ - "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8", - "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993" - ], - "index": "pypi", - "version": "==0.18.0" - }, - "hiredis": { - "hashes": [ - "sha256:06a039208f83744a702279b894c8cf24c14fd63c59cd917dcde168b79eef0680", - "sha256:0a909bf501459062aa1552be1461456518f367379fdc9fdb1f2ca5e4a1fdd7c0", - "sha256:18402d9e54fb278cb9a8c638df6f1550aca36a009d47ecf5aa263a38600f35b0", - "sha256:1e4cbbc3858ec7e680006e5ca590d89a5e083235988f26a004acf7244389ac01", - "sha256:23344e3c2177baf6975fbfa361ed92eb7d36d08f454636e5054b3faa7c2aff8a", - "sha256:289b31885b4996ce04cadfd5fc03d034dce8e2a8234479f7c9e23b9e245db06b", - "sha256:2c1c570ae7bf1bab304f29427e2475fe1856814312c4a1cf1cd0ee133f07a3c6", - "sha256:2c227c0ed371771ffda256034427320870e8ea2e4fd0c0a618c766e7c49aad73", - "sha256:3bb9b63d319402cead8bbd9dd55dca3b667d2997e9a0d8a1f9b6cc274db4baee", - "sha256:3ef2183de67b59930d2db8b8e8d4d58e00a50fcc5e92f4f678f6eed7a1c72d55", - "sha256:43b8ed3dbfd9171e44c554cb4acf4ee4505caa84c5e341858b50ea27dd2b6e12", - "sha256:47bcf3c5e6c1e87ceb86cdda2ee983fa0fe56a999e6185099b3c93a223f2fa9b", - "sha256:5263db1e2e1e8ae30500cdd75a979ff99dcc184201e6b4b820d0de74834d2323", - "sha256:5b1451727f02e7acbdf6aae4e06d75f66ee82966ff9114550381c3271a90f56c", - "sha256:6996883a8a6ff9117cbb3d6f5b0dcbbae6fb9e31e1a3e4e2f95e0214d9a1c655", - "sha256:6c96f64a54f030366657a54bb90b3093afc9c16c8e0dfa29fc0d6dbe169103a5", - "sha256:7332d5c3e35154cd234fd79573736ddcf7a0ade7a986db35b6196b9171493e75", - "sha256:7885b6f32c4a898e825bb7f56f36a02781ac4a951c63e4169f0afcf9c8c30dfb", - "sha256:7b0f63f10a166583ab744a58baad04e0f52cfea1ac27bfa1b0c21a48d1003c23", - "sha256:819f95d4eba3f9e484dd115ab7ab72845cf766b84286a00d4ecf76d33f1edca1", - "sha256:8968eeaa4d37a38f8ca1f9dbe53526b69628edc9c42229a5b2f56d98bb828c1f", - "sha256:89ebf69cb19a33d625db72d2ac589d26e936b8f7628531269accf4a3196e7872", - "sha256:8daecd778c1da45b8bd54fd41ffcd471a86beed3d8e57a43acf7a8d63bba4058", - "sha256:955ba8ea73cf3ed8bd2f963b4cb9f8f0dcb27becd2f4b3dd536fd24c45533454", - "sha256:964f18a59f5a64c0170f684c417f4fe3e695a536612e13074c4dd5d1c6d7c882", - "sha256:969843fbdfbf56cdb71da6f0bdf50f9985b8b8aeb630102945306cf10a9c6af2", - "sha256:996021ef33e0f50b97ff2d6b5f422a0fe5577de21a8873b58a779a5ddd1c3132", - "sha256:9e9c9078a7ce07e6fce366bd818be89365a35d2e4b163268f0ca9ba7e13bb2f6", - "sha256:a04901757cb0fb0f5602ac11dda48f5510f94372144d06c2563ba56c480b467c", - "sha256:a7bf1492429f18d205f3a818da3ff1f242f60aa59006e53dee00b4ef592a3363", - "sha256:aa0af2deb166a5e26e0d554b824605e660039b161e37ed4f01b8d04beec184f3", - "sha256:abfb15a6a7822f0fae681785cb38860e7a2cb1616a708d53df557b3d76c5bfd4", - "sha256:b253fe4df2afea4dfa6b1fa8c5fef212aff8bcaaeb4207e81eed05cb5e4a7919", - "sha256:b27f082f47d23cffc4cf1388b84fdc45c4ef6015f906cd7e0d988d9e35d36349", - "sha256:b33aea449e7f46738811fbc6f0b3177c6777a572207412bbbf6f525ffed001ae", - "sha256:b44f9421c4505c548435244d74037618f452844c5d3c67719d8a55e2613549da", - "sha256:bcc371151d1512201d0214c36c0c150b1dc64f19c2b1a8c9cb1d7c7c15ebd93f", - "sha256:c2851deeabd96d3f6283e9c6b26e0bfed4de2dc6fb15edf913e78b79fc5909ed", - "sha256:cdfd501c7ac5b198c15df800a3a34c38345f5182e5f80770caf362bccca65628", - "sha256:d2c0caffa47606d6d7c8af94ba42547bd2a441f06c74fd90a1ffe328524a6c64", - "sha256:dcb2db95e629962db5a355047fb8aefb012df6c8ae608930d391619dbd96fd86", - "sha256:e0eeb9c112fec2031927a1745788a181d0eecbacbed941fc5c4f7bc3f7b273bf", - "sha256:e154891263306200260d7f3051982774d7b9ef35af3509d5adbbe539afd2610c", - "sha256:e2e023a42dcbab8ed31f97c2bcdb980b7fbe0ada34037d87ba9d799664b58ded", - "sha256:e64be68255234bb489a574c4f2f8df7029c98c81ec4d160d6cd836e7f0679390", - "sha256:e82d6b930e02e80e5109b678c663a9ed210680ded81c1abaf54635d88d1da298" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.1.0" - }, - "idna": { - "hashes": [ - "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16", - "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1" - ], - "markers": "python_version >= '3.4'", - "version": "==3.1" - }, - "multidict": { - "hashes": [ - "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a", - "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000", - "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2", - "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507", - "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5", - "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7", - "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d", - "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463", - "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19", - "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3", - "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b", - "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c", - "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87", - "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7", - "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430", - "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255", - "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d" - ], - "markers": "python_version >= '3.5'", - "version": "==4.7.6" - }, - "pillow": { - "hashes": [ - "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", - "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", - "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", - "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", - "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", - "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", - "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", - "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", - "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", - "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", - "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", - "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", - "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", - "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", - "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", - "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", - "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", - "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", - "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", - "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", - "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", - "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", - "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", - "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117", - "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", - "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", - "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", - "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce" - ], - "index": "pypi", - "version": "==7.2.0" - }, - "pycares": { - "hashes": [ - "sha256:050f00b39ed77ea8a4e555f09417d4b1a6b5baa24bb9531a3e15d003d2319b3f", - "sha256:0a24d2e580a8eb567140d7b69f12cb7de90c836bd7b6488ec69394d308605ac3", - "sha256:0c5bd1f6f885a219d5e972788d6eef7b8043b55c3375a845e5399638436e0bba", - "sha256:11c628402cc8fc8ef461076d4e47f88afc1f8609989ebbff0dbffcd54c97239f", - "sha256:18dfd4fd300f570d6c4536c1d987b7b7673b2a9d14346592c5d6ed716df0d104", - "sha256:1917b82494907a4a342db420bc4dd5bac355a5fa3984c35ba9bf51422b020b48", - "sha256:1b90fa00a89564df059fb18e796458864cc4e00cb55e364dbf921997266b7c55", - "sha256:1d8d177c40567de78108a7835170f570ab04f09084bfd32df9919c0eaec47aa1", - "sha256:236286f81664658b32c141c8e79d20afc3d54f6e2e49dfc8b702026be7265855", - "sha256:2e4f74677542737fb5af4ea9a2e415ec5ab31aa67e7b8c3c969fdb15c069f679", - "sha256:48a7750f04e69e1f304f4332b755728067e7c4b1abe2760bba1cacd9ff7a847a", - "sha256:7d86e62b700b21401ffe7fd1bbfe91e08489416fecae99c6570ab023c6896022", - "sha256:7e2d7effd08d2e5a3cb95d98a7286ebab71ab2fbce84fa93cc2dd56caf7240dd", - "sha256:81edb016d9e43dde7473bc3999c29cdfee3a6b67308fed1ea21049f458e83ae0", - "sha256:96c90e11b4a4c7c0b8ff5aaaae969c5035493136586043ff301979aae0623941", - "sha256:9a0a1845f8cb2e62332bca0aaa9ad5494603ac43fb60d510a61d5b5b170d7216", - "sha256:a05bbfdfd41f8410a905a818f329afe7510cbd9ee65c60f8860a72b6c64ce5dc", - "sha256:a5089fd660f0b0d228b14cdaa110d0d311edfa5a63f800618dbf1321dcaef66b", - "sha256:c457a709e6f2befea7e2996c991eda6d79705dd075f6521593ba6ebc1485b811", - "sha256:c5cb72644b04e5e5abfb1e10a0e7eb75da6684ea0e60871652f348e412cf3b11", - "sha256:cce46dd4717debfd2aab79d6d7f0cbdf6b1e982dc4d9bebad81658d59ede07c2", - "sha256:cfdd1f90bcf373b00f4b2c55ea47868616fe2f779f792fc913fa82a3d64ffe43", - "sha256:d88a279cbc5af613f73e86e19b3f63850f7a2e2736e249c51995dedcc830b1bb", - "sha256:eba9a9227438da5e78fc8eee32f32eb35d9a50cf0a0bd937eb6275c7cc3015fe", - "sha256:eee7b6a5f5b5af050cb7d66ab28179287b416f06d15a8974ac831437fec51336", - "sha256:f41ac1c858687e53242828c9f59c2e7b0b95dbcd5bdd09c7e5d3c48b0f89a25a", - "sha256:f8deaefefc3a589058df1b177275f79233e8b0eeee6734cf4336d80164ecd022", - "sha256:fa78e919f3bd7d6d075db262aa41079b4c02da315c6043c6f43881e2ebcdd623", - "sha256:fadb97d2e02dabdc15a0091591a972a938850d79ddde23d385d813c1731983f0" - ], - "version": "==3.1.1" - }, - "pycparser": { - "hashes": [ - "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", - "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.20" - }, - "pynacl": { - "hashes": [ - "sha256:05c26f93964373fc0abe332676cb6735f0ecad27711035b9472751faa8521255", - "sha256:0c6100edd16fefd1557da078c7a31e7b7d7a52ce39fdca2bec29d4f7b6e7600c", - "sha256:0d0a8171a68edf51add1e73d2159c4bc19fc0718e79dec51166e940856c2f28e", - "sha256:1c780712b206317a746ace34c209b8c29dbfd841dfbc02aa27f2084dd3db77ae", - "sha256:2424c8b9f41aa65bbdbd7a64e73a7450ebb4aa9ddedc6a081e7afcc4c97f7621", - "sha256:2d23c04e8d709444220557ae48ed01f3f1086439f12dbf11976e849a4926db56", - "sha256:30f36a9c70450c7878053fa1344aca0145fd47d845270b43a7ee9192a051bf39", - "sha256:37aa336a317209f1bb099ad177fef0da45be36a2aa664507c5d72015f956c310", - "sha256:4943decfc5b905748f0756fdd99d4f9498d7064815c4cf3643820c9028b711d1", - "sha256:53126cd91356342dcae7e209f840212a58dcf1177ad52c1d938d428eebc9fee5", - "sha256:57ef38a65056e7800859e5ba9e6091053cd06e1038983016effaffe0efcd594a", - "sha256:5bd61e9b44c543016ce1f6aef48606280e45f892a928ca7068fba30021e9b786", - "sha256:6482d3017a0c0327a49dddc8bd1074cc730d45db2ccb09c3bac1f8f32d1eb61b", - "sha256:7d3ce02c0784b7cbcc771a2da6ea51f87e8716004512493a2b69016326301c3b", - "sha256:a14e499c0f5955dcc3991f785f3f8e2130ed504fa3a7f44009ff458ad6bdd17f", - "sha256:a39f54ccbcd2757d1d63b0ec00a00980c0b382c62865b61a505163943624ab20", - "sha256:aabb0c5232910a20eec8563503c153a8e78bbf5459490c49ab31f6adf3f3a415", - "sha256:bd4ecb473a96ad0f90c20acba4f0bf0df91a4e03a1f4dd6a4bdc9ca75aa3a715", - "sha256:bf459128feb543cfca16a95f8da31e2e65e4c5257d2f3dfa8c0c1031139c9c92", - "sha256:e2da3c13307eac601f3de04887624939aca8ee3c9488a0bb0eca4fb9401fc6b1", - "sha256:f67814c38162f4deb31f68d590771a29d5ae3b1bd64b75cf232308e5c74777e0" - ], - "version": "==1.3.0" - }, - "python-dateutil": { - "hashes": [ - "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", - "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.1" - }, - "pytz": { - "hashes": [ - "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", - "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be" - ], - "index": "pypi", - "version": "==2019.3" - }, - "pyyaml": { - "hashes": [ - "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", - "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", - "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", - "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e", - "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", - "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", - "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", - "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", - "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", - "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a", - "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", - "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", - "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" - ], - "index": "pypi", - "version": "==5.3.1" - }, - "redis": { - "hashes": [ - "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2", - "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==3.5.3" - }, - "sentry-sdk": { - "hashes": [ - "sha256:4ae8d1ced6c67f1c8ea51d82a16721c166c489b76876c9f2c202b8a50334b237", - "sha256:e75c8c58932bda8cd293ea8e4b242527129e1caaec91433d21b8b2f20fee030b" - ], - "index": "pypi", - "version": "==0.20.3" - }, - "six": { - "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.15.0" - }, - "sortedcontainers": { - "hashes": [ - "sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f", - "sha256:59cc937650cf60d677c16775597c89a960658a09cf7c1a668f86e1e4464b10a1" - ], - "version": "==2.3.0" - }, - "soupsieve": { - "hashes": [ - "sha256:407fa1e8eb3458d1b5614df51d9651a1180ea5fedf07feb46e45d7e25e6d6cdd", - "sha256:d3a5ea5b350423f47d07639f74475afedad48cf41c0ad7a82ca13a3928af34f6" - ], - "markers": "python_version >= '3.0'", - "version": "==2.2" - }, - "urllib3": { - "hashes": [ - "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80", - "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.26.3" - }, - "yarl": { - "hashes": [ - "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409", - "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593", - "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2", - "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8", - "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d", - "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692", - "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02", - "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a", - "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8", - "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6", - "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511", - "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e", - "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a", - "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb", - "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f", - "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317", - "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6" - ], - "markers": "python_version >= '3.5'", - "version": "==1.5.1" - } - }, - "develop": { - "appdirs": { - "hashes": [ - "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", - "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128" - ], - "version": "==1.4.4" - }, - "attrs": { - "hashes": [ - "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", - "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.3.0" - }, - "cfgv": { - "hashes": [ - "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d", - "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1" - ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.2.0" - }, - "distlib": { - "hashes": [ - "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb", - "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1" - ], - "version": "==0.3.1" - }, - "filelock": { - "hashes": [ - "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", - "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" - ], - "version": "==3.0.12" - }, - "flake8": { - "hashes": [ - "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839", - "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b" - ], - "index": "pypi", - "version": "==3.8.4" - }, - "flake8-annotations": { - "hashes": [ - "sha256:3a377140556aecf11fa9f3bb18c10db01f5ea56dc79a730e2ec9b4f1f49e2055", - "sha256:e17947a48a5b9f632fe0c72682fc797c385e451048e7dfb20139f448a074cb3e" - ], - "index": "pypi", - "version": "==2.5.0" - }, - "flake8-bugbear": { - "hashes": [ - "sha256:528020129fea2dea33a466b9d64ab650aa3e5f9ffc788b70ea4bc6cf18283538", - "sha256:f35b8135ece7a014bc0aee5b5d485334ac30a6da48494998cc1fabf7ec70d703" - ], - "index": "pypi", - "version": "==20.11.1" - }, - "flake8-docstrings": { - "hashes": [ - "sha256:3d5a31c7ec6b7367ea6506a87ec293b94a0a46c0bce2bb4975b7f1d09b6f3717", - "sha256:a256ba91bc52307bef1de59e2a009c3cf61c3d0952dbe035d6ff7208940c2edc" - ], - "index": "pypi", - "version": "==1.5.0" - }, - "flake8-import-order": { - "hashes": [ - "sha256:90a80e46886259b9c396b578d75c749801a41ee969a235e163cfe1be7afd2543", - "sha256:a28dc39545ea4606c1ac3c24e9d05c849c6e5444a50fb7e9cdd430fc94de6e92" - ], - "index": "pypi", - "version": "==0.18.1" - }, - "flake8-polyfill": { - "hashes": [ - "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9", - "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda" - ], - "version": "==1.0.2" - }, - "flake8-string-format": { - "hashes": [ - "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2", - "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af" - ], - "index": "pypi", - "version": "==0.3.0" - }, - "flake8-tidy-imports": { - "hashes": [ - "sha256:52e5f2f987d3d5597538d5941153409ebcab571635835b78f522c7bf03ca23bc", - "sha256:76e36fbbfdc8e3c5017f9a216c2855a298be85bc0631e66777f4e6a07a859dc4" - ], - "index": "pypi", - "version": "==4.2.1" - }, - "flake8-todo": { - "hashes": [ - "sha256:6e4c5491ff838c06fe5a771b0e95ee15fc005ca57196011011280fc834a85915" - ], - "index": "pypi", - "version": "==0.7" - }, - "identify": { - "hashes": [ - "sha256:de7129142a5c86d75a52b96f394d94d96d497881d2aaf8eafe320cdbe8ac4bcc", - "sha256:e0dae57c0397629ce13c289f6ddde0204edf518f557bfdb1e56474aa143e77c3" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.5.14" - }, - "mccabe": { - "hashes": [ - "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", - "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" - ], - "version": "==0.6.1" - }, - "nodeenv": { - "hashes": [ - "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9", - "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c" - ], - "version": "==1.5.0" - }, - "pep8-naming": { - "hashes": [ - "sha256:a1dd47dd243adfe8a83616e27cf03164960b507530f155db94e10b36a6cd6724", - "sha256:f43bfe3eea7e0d73e8b5d07d6407ab47f2476ccaeff6937c84275cd30b016738" - ], - "index": "pypi", - "version": "==0.11.1" - }, - "pre-commit": { - "hashes": [ - "sha256:16212d1fde2bed88159287da88ff03796863854b04dc9f838a55979325a3d20e", - "sha256:399baf78f13f4de82a29b649afd74bef2c4e28eb4f021661fc7f29246e8c7a3a" - ], - "index": "pypi", - "version": "==2.10.1" - }, - "pycodestyle": { - "hashes": [ - "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", - "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.6.0" - }, - "pydocstyle": { - "hashes": [ - "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325", - "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678" - ], - "markers": "python_version >= '3.5'", - "version": "==5.1.1" - }, - "pyflakes": { - "hashes": [ - "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", - "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.2.0" - }, - "pyyaml": { - "hashes": [ - "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", - "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", - "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", - "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e", - "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", - "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", - "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", - "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", - "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", - "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a", - "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", - "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", - "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" - ], - "index": "pypi", - "version": "==5.3.1" - }, - "six": { - "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.15.0" - }, - "snowballstemmer": { - "hashes": [ - "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2", - "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914" - ], - "version": "==2.1.0" - }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" - }, - "virtualenv": { - "hashes": [ - "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d", - "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.4.2" - } - } -} -- cgit v1.2.3 From 390f731e02023d2372d6731a2f275b9c3f30ba36 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 17:50:51 +0000 Subject: Add poetry.lock and pyproject.toml --- poetry.lock | 1219 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 43 ++ 2 files changed, 1262 insertions(+) create mode 100644 poetry.lock create mode 100644 pyproject.toml diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..5e2db721 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1219 @@ +[[package]] +name = "aiodns" +version = "2.0.0" +description = "Simple DNS resolver for asyncio" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycares = ">=3.0.0" + +[[package]] +name = "aiohttp" +version = "3.7.4.post0" +description = "Async http client/server framework (asyncio)" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +async-timeout = ">=3.0,<4.0" +attrs = ">=17.3.0" +chardet = ">=2.0,<5.0" +multidict = ">=4.5,<7.0" +typing-extensions = ">=3.6.5" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["aiodns", "brotlipy", "cchardet"] + +[[package]] +name = "aioredis" +version = "1.3.1" +description = "asyncio (PEP 3156) Redis support" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +async-timeout = "*" +hiredis = "*" + +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "arrow" +version = "0.17.0" +description = "Better dates & times for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +python-dateutil = ">=2.7.0" + +[[package]] +name = "async-rediscache" +version = "0.1.4" +description = "An easy to use asynchronous Redis cache" +category = "main" +optional = false +python-versions = "~=3.7" + +[package.dependencies] +aioredis = ">=1" +fakeredis = {version = ">=1.3.1", optional = true, markers = "extra == \"fakeredis\""} + +[package.extras] +fakeredis = ["fakeredis (>=1.3.1)"] + +[[package]] +name = "async-timeout" +version = "3.0.1" +description = "Timeout context manager for asyncio programs" +category = "main" +optional = false +python-versions = ">=3.5.3" + +[[package]] +name = "attrs" +version = "20.3.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] + +[[package]] +name = "beautifulsoup4" +version = "4.9.3" +description = "Screen-scraping library" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +soupsieve = {version = ">1.2", markers = "python_version >= \"3.0\""} + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "certifi" +version = "2020.12.5" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "cffi" +version = "1.14.5" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "cfgv" +version = "3.2.0" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6.1" + +[[package]] +name = "chardet" +version = "4.0.0" +description = "Universal encoding detector for Python 2 and 3" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "discord.py" +version = "1.6.0" +description = "A Python wrapper for the Discord API" +category = "main" +optional = false +python-versions = ">=3.5.3" + +[package.dependencies] +aiohttp = ">=3.6.0,<3.8.0" +PyNaCl = {version = ">=1.3.0,<1.5", optional = true, markers = "extra == \"voice\""} + +[package.extras] +docs = ["sphinx (==3.0.3)", "sphinxcontrib-trio (==1.1.2)", "sphinxcontrib-websupport"] +voice = ["PyNaCl (>=1.3.0,<1.5)"] + +[[package]] +name = "distlib" +version = "0.3.1" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "emojis" +version = "0.6.0" +description = "Emojis for Python" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "fakeredis" +version = "1.4.5" +description = "Fake implementation of redis API for testing purposes." +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +redis = "<3.6.0" +six = ">=1.12" +sortedcontainers = "*" + +[package.extras] +aioredis = ["aioredis"] +lua = ["lupa"] + +[[package]] +name = "filelock" +version = "3.0.12" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "flake8" +version = "3.8.4" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.dependencies] +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.6.0a1,<2.7.0" +pyflakes = ">=2.2.0,<2.3.0" + +[[package]] +name = "flake8-annotations" +version = "2.6.0" +description = "Flake8 Type Annotation Checks" +category = "dev" +optional = false +python-versions = ">=3.6.1,<4.0.0" + +[package.dependencies] +flake8 = ">=3.7,<3.9" + +[[package]] +name = "flake8-bugbear" +version = "20.11.1" +description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +attrs = ">=19.2.0" +flake8 = ">=3.0.0" + +[package.extras] +dev = ["coverage", "black", "hypothesis", "hypothesmith"] + +[[package]] +name = "flake8-docstrings" +version = "1.5.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-import-order" +version = "0.18.1" +description = "Flake8 and pylama plugin that checks the ordering of import statements." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +pycodestyle = "*" + +[[package]] +name = "flake8-polyfill" +version = "1.0.2" +description = "Polyfill package for Flake8 plugins" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +flake8 = "*" + +[[package]] +name = "flake8-string-format" +version = "0.3.0" +description = "string format checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +flake8 = "*" + +[[package]] +name = "flake8-tidy-imports" +version = "4.2.1" +description = "A flake8 plugin that helps you write tidier imports." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +flake8 = ">=3.0,<3.2.0 || >3.2.0,<4" + +[[package]] +name = "flake8-todo" +version = "0.7" +description = "TODO notes checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +pycodestyle = ">=2.0.0,<3.0.0" + +[[package]] +name = "fuzzywuzzy" +version = "0.18.0" +description = "Fuzzy string matching in python" +category = "main" +optional = false +python-versions = "*" + +[package.extras] +speedup = ["python-levenshtein (>=0.12)"] + +[[package]] +name = "hiredis" +version = "1.1.0" +description = "Python wrapper for hiredis" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "identify" +version = "2.1.1" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.6.1" + +[package.extras] +license = ["editdistance"] + +[[package]] +name = "idna" +version = "3.1" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.4" + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "mslex" +version = "0.3.0" +description = "shlex for windows" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "multidict" +version = "5.1.0" +description = "multidict implementation" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "nodeenv" +version = "1.5.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "pep8-naming" +version = "0.11.1" +description = "Check PEP-8 naming conventions, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +flake8-polyfill = ">=1.0.2,<2" + +[[package]] +name = "pillow" +version = "8.1.2" +description = "Python Imaging Library (Fork)" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "pre-commit" +version = "2.11.1" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.6.1" + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +toml = "*" +virtualenv = ">=20.0.8" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] + +[[package]] +name = "pycares" +version = "3.1.1" +description = "Python interface for c-ares" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +cffi = ">=1.5.0" + +[package.extras] +idna = ["idna (>=2.1)"] + +[[package]] +name = "pycodestyle" +version = "2.6.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pycparser" +version = "2.20" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pydocstyle" +version = "5.1.1" +description = "Python docstring style checker" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +snowballstemmer = "*" + +[[package]] +name = "pyflakes" +version = "2.2.0" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pynacl" +version = "1.4.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +cffi = ">=1.4.1" +six = "*" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"] + +[[package]] +name = "python-dateutil" +version = "2.8.1" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "0.15.0" +description = "Add .env support to your django/flask apps in development and deployments" +category = "dev" +optional = false +python-versions = "*" + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "pytz" +version = "2019.3" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pyyaml" +version = "5.3.1" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "redis" +version = "3.5.3" +description = "Python client for Redis key-value store" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +hiredis = ["hiredis (>=0.1.3)"] + +[[package]] +name = "sentry-sdk" +version = "0.20.3" +description = "Python client for Sentry (https://sentry.io)" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +certifi = "*" +urllib3 = ">=1.10.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +flask = ["flask (>=0.11)", "blinker (>=1.1)"] +pure_eval = ["pure-eval", "executing", "asttokens"] +pyspark = ["pyspark (>=2.4.4)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "six" +version = "1.15.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "snowballstemmer" +version = "2.1.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "sortedcontainers" +version = "2.3.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "soupsieve" +version = "2.2" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "taskipy" +version = "1.6.0" +description = "tasks runner for python projects" +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[package.dependencies] +mslex = ">=0.3.0,<0.4.0" +psutil = ">=5.7.2,<6.0.0" +toml = ">=0.10.0,<0.11.0" + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "typing-extensions" +version = "3.7.4.3" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "urllib3" +version = "1.26.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "virtualenv" +version = "20.4.2" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.dependencies] +appdirs = ">=1.4.3,<2" +distlib = ">=0.3.1,<1" +filelock = ">=3.0.0,<4" +six = ">=1.9.0,<2" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] + +[[package]] +name = "yarl" +version = "1.6.3" +description = "Yet another URL library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[metadata] +lock-version = "1.1" +python-versions = "^3.9" +content-hash = "0d7d777a2ba3c3fc2591868c7a0615e35a4714017e82b2e1c4b6f7959242094f" + +[metadata.files] +aiodns = [ + {file = "aiodns-2.0.0-py2.py3-none-any.whl", hash = "sha256:aaa5ac584f40fe778013df0aa6544bf157799bd3f608364b451840ed2c8688de"}, + {file = "aiodns-2.0.0.tar.gz", hash = "sha256:815fdef4607474295d68da46978a54481dd1e7be153c7d60f9e72773cd38d77d"}, +] +aiohttp = [ + {file = "aiohttp-3.7.4.post0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:3cf75f7cdc2397ed4442594b935a11ed5569961333d49b7539ea741be2cc79d5"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4b302b45040890cea949ad092479e01ba25911a15e648429c7c5aae9650c67a8"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:393f389841e8f2dfc86f774ad22f00923fdee66d238af89b70ea314c4aefd290"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:c6e9dcb4cb338d91a73f178d866d051efe7c62a7166653a91e7d9fb18274058f"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:5df68496d19f849921f05f14f31bd6ef53ad4b00245da3195048c69934521809"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-win32.whl", hash = "sha256:3d78619672183be860b96ed96f533046ec97ca067fd46ac1f6a09cd9b7484287"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:f705e12750171c0ab4ef2a3c76b9a4024a62c4103e3a55dd6f99265b9bc6fcfc"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:230a8f7e24298dea47659251abc0fd8b3c4e38a664c59d4b89cca7f6c09c9e87"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2e19413bf84934d651344783c9f5e22dee452e251cfd220ebadbed2d9931dbf0"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e4b2b334e68b18ac9817d828ba44d8fcb391f6acb398bcc5062b14b2cbeac970"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:d012ad7911653a906425d8473a1465caa9f8dea7fcf07b6d870397b774ea7c0f"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:40eced07f07a9e60e825554a31f923e8d3997cfc7fb31dbc1328c70826e04cde"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:209b4a8ee987eccc91e2bd3ac36adee0e53a5970b8ac52c273f7f8fd4872c94c"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:14762875b22d0055f05d12abc7f7d61d5fd4fe4642ce1a249abdf8c700bf1fd8"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-win32.whl", hash = "sha256:7615dab56bb07bff74bc865307aeb89a8bfd9941d2ef9d817b9436da3a0ea54f"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:d9e13b33afd39ddeb377eff2c1c4f00544e191e1d1dee5b6c51ddee8ea6f0cf5"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:547da6cacac20666422d4882cfcd51298d45f7ccb60a04ec27424d2f36ba3eaf"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:af9aa9ef5ba1fd5b8c948bb11f44891968ab30356d65fd0cc6707d989cd521df"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:64322071e046020e8797117b3658b9c2f80e3267daec409b350b6a7a05041213"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bb437315738aa441251214dad17428cafda9cdc9729499f1d6001748e1d432f4"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:e54962802d4b8b18b6207d4a927032826af39395a3bd9196a5af43fc4e60b009"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:a00bb73540af068ca7390e636c01cbc4f644961896fa9363154ff43fd37af2f5"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:79ebfc238612123a713a457d92afb4096e2148be17df6c50fb9bf7a81c2f8013"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-win32.whl", hash = "sha256:515dfef7f869a0feb2afee66b957cc7bbe9ad0cdee45aec7fdc623f4ecd4fb16"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-win_amd64.whl", hash = "sha256:114b281e4d68302a324dd33abb04778e8557d88947875cbf4e842c2c01a030c5"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:7b18b97cf8ee5452fa5f4e3af95d01d84d86d32c5e2bfa260cf041749d66360b"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:15492a6368d985b76a2a5fdd2166cddfea5d24e69eefed4630cbaae5c81d89bd"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bdb230b4943891321e06fc7def63c7aace16095be7d9cf3b1e01be2f10fba439"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:cffe3ab27871bc3ea47df5d8f7013945712c46a3cc5a95b6bee15887f1675c22"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:a5ca29ee66f8343ed336816c553e82d6cade48a3ad702b9ffa6125d187e2dedb"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:17c073de315745a1510393a96e680d20af8e67e324f70b42accbd4cb3315c9fb"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-win32.whl", hash = "sha256:932bb1ea39a54e9ea27fc9232163059a0b8855256f4052e776357ad9add6f1c9"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-win_amd64.whl", hash = "sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe"}, + {file = "aiohttp-3.7.4.post0.tar.gz", hash = "sha256:493d3299ebe5f5a7c66b9819eacdcfbbaaf1a8e84911ddffcdc48888497afecf"}, +] +aioredis = [ + {file = "aioredis-1.3.1-py3-none-any.whl", hash = "sha256:b61808d7e97b7cd5a92ed574937a079c9387fdadd22bfbfa7ad2fd319ecc26e3"}, + {file = "aioredis-1.3.1.tar.gz", hash = "sha256:15f8af30b044c771aee6787e5ec24694c048184c7b9e54c3b60c750a4b93273a"}, +] +appdirs = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] +arrow = [ + {file = "arrow-0.17.0-py2.py3-none-any.whl", hash = "sha256:e098abbd9af3665aea81bdd6c869e93af4feb078e98468dd351c383af187aac5"}, + {file = "arrow-0.17.0.tar.gz", hash = "sha256:ff08d10cda1d36c68657d6ad20d74fbea493d980f8b2d45344e00d6ed2bf6ed4"}, +] +async-rediscache = [ + {file = "async-rediscache-0.1.4.tar.gz", hash = "sha256:6be8a657d724ccbcfb1946d29a80c3478c5f9ecd2f78a0a26d2f4013a622258f"}, + {file = "async_rediscache-0.1.4-py3-none-any.whl", hash = "sha256:c25e4fff73f64d20645254783c3224a4c49e083e3fab67c44f17af944c5e26af"}, +] +async-timeout = [ + {file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"}, + {file = "async_timeout-3.0.1-py3-none-any.whl", hash = "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"}, +] +attrs = [ + {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, +] +beautifulsoup4 = [ + {file = "beautifulsoup4-4.9.3-py2-none-any.whl", hash = "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35"}, + {file = "beautifulsoup4-4.9.3-py3-none-any.whl", hash = "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"}, + {file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"}, +] +certifi = [ + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, +] +cffi = [ + {file = "cffi-1.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"}, + {file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"}, + {file = "cffi-1.14.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa"}, + {file = "cffi-1.14.5-cp27-cp27m-win32.whl", hash = "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3"}, + {file = "cffi-1.14.5-cp27-cp27m-win_amd64.whl", hash = "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5"}, + {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482"}, + {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6"}, + {file = "cffi-1.14.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045"}, + {file = "cffi-1.14.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa"}, + {file = "cffi-1.14.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406"}, + {file = "cffi-1.14.5-cp35-cp35m-win32.whl", hash = "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369"}, + {file = "cffi-1.14.5-cp35-cp35m-win_amd64.whl", hash = "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315"}, + {file = "cffi-1.14.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"}, + {file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"}, + {file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"}, + {file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"}, + {file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"}, + {file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"}, + {file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"}, + {file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"}, + {file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"}, + {file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"}, + {file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"}, + {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"}, + {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"}, +] +cfgv = [ + {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"}, + {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, +] +chardet = [ + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, +] +"discord.py" = [ + {file = "discord.py-1.6.0-py3-none-any.whl", hash = "sha256:3df148daf6fbcc7ab5b11042368a3cd5f7b730b62f09fb5d3cbceff59bcfbb12"}, + {file = "discord.py-1.6.0.tar.gz", hash = "sha256:ba8be99ff1b8c616f7b6dcb700460d0222b29d4c11048e74366954c465fdd05f"}, +] +distlib = [ + {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, + {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, +] +emojis = [ + {file = "emojis-0.6.0-py3-none-any.whl", hash = "sha256:7da34c8a78ae262fd68cef9e2c78a3c1feb59784489eeea0f54ba1d4b7111c7c"}, + {file = "emojis-0.6.0.tar.gz", hash = "sha256:bf605d1f1a27a81cd37fe82eb65781c904467f569295a541c33710b97e4225ec"}, +] +fakeredis = [ + {file = "fakeredis-1.4.5-py3-none-any.whl", hash = "sha256:2c6041cf0225889bc403f3949838b2c53470a95a9e2d4272422937786f5f8f73"}, + {file = "fakeredis-1.4.5.tar.gz", hash = "sha256:01cb47d2286825a171fb49c0e445b1fa9307087e07cbb3d027ea10dbff108b6a"}, +] +filelock = [ + {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, + {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, +] +flake8 = [ + {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"}, + {file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"}, +] +flake8-annotations = [ + {file = "flake8-annotations-2.6.0.tar.gz", hash = "sha256:bd0505616c0d85ebb45c6052d339c69f320d3f87fa079ab4e91a4f234a863d05"}, + {file = "flake8_annotations-2.6.0-py3-none-any.whl", hash = "sha256:8968ff12f296433028ad561c680ccc03a7cd62576d100c3f1475e058b3c11b43"}, +] +flake8-bugbear = [ + {file = "flake8-bugbear-20.11.1.tar.gz", hash = "sha256:528020129fea2dea33a466b9d64ab650aa3e5f9ffc788b70ea4bc6cf18283538"}, + {file = "flake8_bugbear-20.11.1-py36.py37.py38-none-any.whl", hash = "sha256:f35b8135ece7a014bc0aee5b5d485334ac30a6da48494998cc1fabf7ec70d703"}, +] +flake8-docstrings = [ + {file = "flake8-docstrings-1.5.0.tar.gz", hash = "sha256:3d5a31c7ec6b7367ea6506a87ec293b94a0a46c0bce2bb4975b7f1d09b6f3717"}, + {file = "flake8_docstrings-1.5.0-py2.py3-none-any.whl", hash = "sha256:a256ba91bc52307bef1de59e2a009c3cf61c3d0952dbe035d6ff7208940c2edc"}, +] +flake8-import-order = [ + {file = "flake8-import-order-0.18.1.tar.gz", hash = "sha256:a28dc39545ea4606c1ac3c24e9d05c849c6e5444a50fb7e9cdd430fc94de6e92"}, + {file = "flake8_import_order-0.18.1-py2.py3-none-any.whl", hash = "sha256:90a80e46886259b9c396b578d75c749801a41ee969a235e163cfe1be7afd2543"}, +] +flake8-polyfill = [ + {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"}, + {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"}, +] +flake8-string-format = [ + {file = "flake8-string-format-0.3.0.tar.gz", hash = "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2"}, + {file = "flake8_string_format-0.3.0-py2.py3-none-any.whl", hash = "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af"}, +] +flake8-tidy-imports = [ + {file = "flake8-tidy-imports-4.2.1.tar.gz", hash = "sha256:52e5f2f987d3d5597538d5941153409ebcab571635835b78f522c7bf03ca23bc"}, + {file = "flake8_tidy_imports-4.2.1-py3-none-any.whl", hash = "sha256:76e36fbbfdc8e3c5017f9a216c2855a298be85bc0631e66777f4e6a07a859dc4"}, +] +flake8-todo = [ + {file = "flake8-todo-0.7.tar.gz", hash = "sha256:6e4c5491ff838c06fe5a771b0e95ee15fc005ca57196011011280fc834a85915"}, +] +fuzzywuzzy = [ + {file = "fuzzywuzzy-0.18.0-py2.py3-none-any.whl", hash = "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993"}, + {file = "fuzzywuzzy-0.18.0.tar.gz", hash = "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8"}, +] +hiredis = [ + {file = "hiredis-1.1.0-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:289b31885b4996ce04cadfd5fc03d034dce8e2a8234479f7c9e23b9e245db06b"}, + {file = "hiredis-1.1.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:7b0f63f10a166583ab744a58baad04e0f52cfea1ac27bfa1b0c21a48d1003c23"}, + {file = "hiredis-1.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:6996883a8a6ff9117cbb3d6f5b0dcbbae6fb9e31e1a3e4e2f95e0214d9a1c655"}, + {file = "hiredis-1.1.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:b33aea449e7f46738811fbc6f0b3177c6777a572207412bbbf6f525ffed001ae"}, + {file = "hiredis-1.1.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8daecd778c1da45b8bd54fd41ffcd471a86beed3d8e57a43acf7a8d63bba4058"}, + {file = "hiredis-1.1.0-cp27-cp27m-win32.whl", hash = "sha256:e82d6b930e02e80e5109b678c663a9ed210680ded81c1abaf54635d88d1da298"}, + {file = "hiredis-1.1.0-cp27-cp27m-win_amd64.whl", hash = "sha256:d2c0caffa47606d6d7c8af94ba42547bd2a441f06c74fd90a1ffe328524a6c64"}, + {file = "hiredis-1.1.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:47bcf3c5e6c1e87ceb86cdda2ee983fa0fe56a999e6185099b3c93a223f2fa9b"}, + {file = "hiredis-1.1.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:dcb2db95e629962db5a355047fb8aefb012df6c8ae608930d391619dbd96fd86"}, + {file = "hiredis-1.1.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:7332d5c3e35154cd234fd79573736ddcf7a0ade7a986db35b6196b9171493e75"}, + {file = "hiredis-1.1.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6c96f64a54f030366657a54bb90b3093afc9c16c8e0dfa29fc0d6dbe169103a5"}, + {file = "hiredis-1.1.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:b44f9421c4505c548435244d74037618f452844c5d3c67719d8a55e2613549da"}, + {file = "hiredis-1.1.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:abfb15a6a7822f0fae681785cb38860e7a2cb1616a708d53df557b3d76c5bfd4"}, + {file = "hiredis-1.1.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:89ebf69cb19a33d625db72d2ac589d26e936b8f7628531269accf4a3196e7872"}, + {file = "hiredis-1.1.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:5b1451727f02e7acbdf6aae4e06d75f66ee82966ff9114550381c3271a90f56c"}, + {file = "hiredis-1.1.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:7885b6f32c4a898e825bb7f56f36a02781ac4a951c63e4169f0afcf9c8c30dfb"}, + {file = "hiredis-1.1.0-cp35-cp35m-win32.whl", hash = "sha256:a04901757cb0fb0f5602ac11dda48f5510f94372144d06c2563ba56c480b467c"}, + {file = "hiredis-1.1.0-cp35-cp35m-win_amd64.whl", hash = "sha256:3bb9b63d319402cead8bbd9dd55dca3b667d2997e9a0d8a1f9b6cc274db4baee"}, + {file = "hiredis-1.1.0-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:e0eeb9c112fec2031927a1745788a181d0eecbacbed941fc5c4f7bc3f7b273bf"}, + {file = "hiredis-1.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:18402d9e54fb278cb9a8c638df6f1550aca36a009d47ecf5aa263a38600f35b0"}, + {file = "hiredis-1.1.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:cdfd501c7ac5b198c15df800a3a34c38345f5182e5f80770caf362bccca65628"}, + {file = "hiredis-1.1.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:43b8ed3dbfd9171e44c554cb4acf4ee4505caa84c5e341858b50ea27dd2b6e12"}, + {file = "hiredis-1.1.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:c2851deeabd96d3f6283e9c6b26e0bfed4de2dc6fb15edf913e78b79fc5909ed"}, + {file = "hiredis-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:955ba8ea73cf3ed8bd2f963b4cb9f8f0dcb27becd2f4b3dd536fd24c45533454"}, + {file = "hiredis-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5263db1e2e1e8ae30500cdd75a979ff99dcc184201e6b4b820d0de74834d2323"}, + {file = "hiredis-1.1.0-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:e154891263306200260d7f3051982774d7b9ef35af3509d5adbbe539afd2610c"}, + {file = "hiredis-1.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:964f18a59f5a64c0170f684c417f4fe3e695a536612e13074c4dd5d1c6d7c882"}, + {file = "hiredis-1.1.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:23344e3c2177baf6975fbfa361ed92eb7d36d08f454636e5054b3faa7c2aff8a"}, + {file = "hiredis-1.1.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:b27f082f47d23cffc4cf1388b84fdc45c4ef6015f906cd7e0d988d9e35d36349"}, + {file = "hiredis-1.1.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:aa0af2deb166a5e26e0d554b824605e660039b161e37ed4f01b8d04beec184f3"}, + {file = "hiredis-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:819f95d4eba3f9e484dd115ab7ab72845cf766b84286a00d4ecf76d33f1edca1"}, + {file = "hiredis-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2c1c570ae7bf1bab304f29427e2475fe1856814312c4a1cf1cd0ee133f07a3c6"}, + {file = "hiredis-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e9c9078a7ce07e6fce366bd818be89365a35d2e4b163268f0ca9ba7e13bb2f6"}, + {file = "hiredis-1.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:2c227c0ed371771ffda256034427320870e8ea2e4fd0c0a618c766e7c49aad73"}, + {file = "hiredis-1.1.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:0a909bf501459062aa1552be1461456518f367379fdc9fdb1f2ca5e4a1fdd7c0"}, + {file = "hiredis-1.1.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e4cbbc3858ec7e680006e5ca590d89a5e083235988f26a004acf7244389ac01"}, + {file = "hiredis-1.1.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:a7bf1492429f18d205f3a818da3ff1f242f60aa59006e53dee00b4ef592a3363"}, + {file = "hiredis-1.1.0-cp38-cp38-win32.whl", hash = "sha256:bcc371151d1512201d0214c36c0c150b1dc64f19c2b1a8c9cb1d7c7c15ebd93f"}, + {file = "hiredis-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:e64be68255234bb489a574c4f2f8df7029c98c81ec4d160d6cd836e7f0679390"}, + {file = "hiredis-1.1.0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:8968eeaa4d37a38f8ca1f9dbe53526b69628edc9c42229a5b2f56d98bb828c1f"}, + {file = "hiredis-1.1.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:b253fe4df2afea4dfa6b1fa8c5fef212aff8bcaaeb4207e81eed05cb5e4a7919"}, + {file = "hiredis-1.1.0-pp27-pypy_73-win32.whl", hash = "sha256:969843fbdfbf56cdb71da6f0bdf50f9985b8b8aeb630102945306cf10a9c6af2"}, + {file = "hiredis-1.1.0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:e2e023a42dcbab8ed31f97c2bcdb980b7fbe0ada34037d87ba9d799664b58ded"}, + {file = "hiredis-1.1.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:06a039208f83744a702279b894c8cf24c14fd63c59cd917dcde168b79eef0680"}, + {file = "hiredis-1.1.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:3ef2183de67b59930d2db8b8e8d4d58e00a50fcc5e92f4f678f6eed7a1c72d55"}, + {file = "hiredis-1.1.0.tar.gz", hash = "sha256:996021ef33e0f50b97ff2d6b5f422a0fe5577de21a8873b58a779a5ddd1c3132"}, +] +identify = [ + {file = "identify-2.1.1-py2.py3-none-any.whl", hash = "sha256:220169a38a0c977c8fef377dc808d6a3330641b5211ec7356c7bbe73cda487c7"}, + {file = "identify-2.1.1.tar.gz", hash = "sha256:da3d757c94596c50865aae63db6ba4e2e5e3f666c3ea6a6da0cd09a8b2d34abc"}, +] +idna = [ + {file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"}, + {file = "idna-3.1.tar.gz", hash = "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mslex = [ + {file = "mslex-0.3.0-py2.py3-none-any.whl", hash = "sha256:380cb14abf8fabf40e56df5c8b21a6d533dc5cbdcfe42406bbf08dda8f42e42a"}, + {file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"}, +] +multidict = [ + {file = "multidict-5.1.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224"}, + {file = "multidict-5.1.0-cp36-cp36m-win32.whl", hash = "sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26"}, + {file = "multidict-5.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6"}, + {file = "multidict-5.1.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37"}, + {file = "multidict-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5"}, + {file = "multidict-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632"}, + {file = "multidict-5.1.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea"}, + {file = "multidict-5.1.0-cp38-cp38-win32.whl", hash = "sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656"}, + {file = "multidict-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3"}, + {file = "multidict-5.1.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda"}, + {file = "multidict-5.1.0-cp39-cp39-win32.whl", hash = "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"}, + {file = "multidict-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359"}, + {file = "multidict-5.1.0.tar.gz", hash = "sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5"}, +] +nodeenv = [ + {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, + {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"}, +] +pep8-naming = [ + {file = "pep8-naming-0.11.1.tar.gz", hash = "sha256:a1dd47dd243adfe8a83616e27cf03164960b507530f155db94e10b36a6cd6724"}, + {file = "pep8_naming-0.11.1-py2.py3-none-any.whl", hash = "sha256:f43bfe3eea7e0d73e8b5d07d6407ab47f2476ccaeff6937c84275cd30b016738"}, +] +pillow = [ + {file = "Pillow-8.1.2-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:5cf03b9534aca63b192856aa601c68d0764810857786ea5da652581f3a44c2b0"}, + {file = "Pillow-8.1.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f91b50ad88048d795c0ad004abbe1390aa1882073b1dca10bfd55d0b8cf18ec5"}, + {file = "Pillow-8.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5762ebb4436f46b566fc6351d67a9b5386b5e5de4e58fdaa18a1c83e0e20f1a8"}, + {file = "Pillow-8.1.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e2cd8ac157c1e5ae88b6dd790648ee5d2777e76f1e5c7d184eaddb2938594f34"}, + {file = "Pillow-8.1.2-cp36-cp36m-win32.whl", hash = "sha256:72027ebf682abc9bafd93b43edc44279f641e8996fb2945104471419113cfc71"}, + {file = "Pillow-8.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d1d6bca39bb6dd94fba23cdb3eeaea5e30c7717c5343004d900e2a63b132c341"}, + {file = "Pillow-8.1.2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:90882c6f084ef68b71bba190209a734bf90abb82ab5e8f64444c71d5974008c6"}, + {file = "Pillow-8.1.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:89e4c757a91b8c55d97c91fa09c69b3677c227b942fa749e9a66eef602f59c28"}, + {file = "Pillow-8.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8c4e32218c764bc27fe49b7328195579581aa419920edcc321c4cb877c65258d"}, + {file = "Pillow-8.1.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:a01da2c266d9868c4f91a9c6faf47a251f23b9a862dce81d2ff583135206f5be"}, + {file = "Pillow-8.1.2-cp37-cp37m-win32.whl", hash = "sha256:30d33a1a6400132e6f521640dd3f64578ac9bfb79a619416d7e8802b4ce1dd55"}, + {file = "Pillow-8.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:71b01ee69e7df527439d7752a2ce8fb89e19a32df484a308eca3e81f673d3a03"}, + {file = "Pillow-8.1.2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:5a2d957eb4aba9d48170b8fe6538ec1fbc2119ffe6373782c03d8acad3323f2e"}, + {file = "Pillow-8.1.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:87f42c976f91ca2fc21a3293e25bd3cd895918597db1b95b93cbd949f7d019ce"}, + {file = "Pillow-8.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:15306d71a1e96d7e271fd2a0737038b5a92ca2978d2e38b6ced7966583e3d5af"}, + {file = "Pillow-8.1.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:71f31ee4df3d5e0b366dd362007740106d3210fb6a56ec4b581a5324ba254f06"}, + {file = "Pillow-8.1.2-cp38-cp38-win32.whl", hash = "sha256:98afcac3205d31ab6a10c5006b0cf040d0026a68ec051edd3517b776c1d78b09"}, + {file = "Pillow-8.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:328240f7dddf77783e72d5ed79899a6b48bc6681f8d1f6001f55933cb4905060"}, + {file = "Pillow-8.1.2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:bead24c0ae3f1f6afcb915a057943ccf65fc755d11a1410a909c1fefb6c06ad1"}, + {file = "Pillow-8.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81b3716cc9744ffdf76b39afb6247eae754186838cedad0b0ac63b2571253fe6"}, + {file = "Pillow-8.1.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:63cd413ac52ee3f67057223d363f4f82ce966e64906aea046daf46695e3c8238"}, + {file = "Pillow-8.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:8565355a29655b28fdc2c666fd9a3890fe5edc6639d128814fafecfae2d70910"}, + {file = "Pillow-8.1.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1940fc4d361f9cc7e558d6f56ff38d7351b53052fd7911f4b60cd7bc091ea3b1"}, + {file = "Pillow-8.1.2-cp39-cp39-win32.whl", hash = "sha256:46c2bcf8e1e75d154e78417b3e3c64e96def738c2a25435e74909e127a8cba5e"}, + {file = "Pillow-8.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:aeab4cd016e11e7aa5cfc49dcff8e51561fa64818a0be86efa82c7038e9369d0"}, + {file = "Pillow-8.1.2-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:74cd9aa648ed6dd25e572453eb09b08817a1e3d9f8d1bd4d8403d99e42ea790b"}, + {file = "Pillow-8.1.2-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:e5739ae63636a52b706a0facec77b2b58e485637e1638202556156e424a02dc2"}, + {file = "Pillow-8.1.2-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:903293320efe2466c1ab3509a33d6b866dc850cfd0c5d9cc92632014cec185fb"}, + {file = "Pillow-8.1.2-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:5daba2b40782c1c5157a788ec4454067c6616f5a0c1b70e26ac326a880c2d328"}, + {file = "Pillow-8.1.2-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:1f93f2fe211f1ef75e6f589327f4d4f8545d5c8e826231b042b483d8383e8a7c"}, + {file = "Pillow-8.1.2-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:6efac40344d8f668b6c4533ae02a48d52fd852ef0654cc6f19f6ac146399c733"}, + {file = "Pillow-8.1.2-pp37-pypy37_pp73-win32.whl", hash = "sha256:f36c3ff63d6fc509ce599a2f5b0d0732189eed653420e7294c039d342c6e204a"}, + {file = "Pillow-8.1.2.tar.gz", hash = "sha256:b07c660e014852d98a00a91adfbe25033898a9d90a8f39beb2437d22a203fc44"}, +] +pre-commit = [ + {file = "pre_commit-2.11.1-py2.py3-none-any.whl", hash = "sha256:94c82f1bf5899d56edb1d926732f4e75a7df29a0c8c092559c77420c9d62428b"}, + {file = "pre_commit-2.11.1.tar.gz", hash = "sha256:de55c5c72ce80d79106e48beb1b54104d16495ce7f95b0c7b13d4784193a00af"}, +] +psutil = [ + {file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"}, + {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c"}, + {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df"}, + {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131"}, + {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60"}, + {file = "psutil-5.8.0-cp27-none-win32.whl", hash = "sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876"}, + {file = "psutil-5.8.0-cp27-none-win_amd64.whl", hash = "sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65"}, + {file = "psutil-5.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8"}, + {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6"}, + {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac"}, + {file = "psutil-5.8.0-cp36-cp36m-win32.whl", hash = "sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2"}, + {file = "psutil-5.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d"}, + {file = "psutil-5.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935"}, + {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d"}, + {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023"}, + {file = "psutil-5.8.0-cp37-cp37m-win32.whl", hash = "sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394"}, + {file = "psutil-5.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"}, + {file = "psutil-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef"}, + {file = "psutil-5.8.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28"}, + {file = "psutil-5.8.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b"}, + {file = "psutil-5.8.0-cp38-cp38-win32.whl", hash = "sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d"}, + {file = "psutil-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d"}, + {file = "psutil-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7"}, + {file = "psutil-5.8.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4"}, + {file = "psutil-5.8.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b"}, + {file = "psutil-5.8.0-cp39-cp39-win32.whl", hash = "sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0"}, + {file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"}, + {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"}, +] +pycares = [ + {file = "pycares-3.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:81edb016d9e43dde7473bc3999c29cdfee3a6b67308fed1ea21049f458e83ae0"}, + {file = "pycares-3.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1917b82494907a4a342db420bc4dd5bac355a5fa3984c35ba9bf51422b020b48"}, + {file = "pycares-3.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:a5089fd660f0b0d228b14cdaa110d0d311edfa5a63f800618dbf1321dcaef66b"}, + {file = "pycares-3.1.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:48a7750f04e69e1f304f4332b755728067e7c4b1abe2760bba1cacd9ff7a847a"}, + {file = "pycares-3.1.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:d88a279cbc5af613f73e86e19b3f63850f7a2e2736e249c51995dedcc830b1bb"}, + {file = "pycares-3.1.1-cp35-cp35m-win32.whl", hash = "sha256:96c90e11b4a4c7c0b8ff5aaaae969c5035493136586043ff301979aae0623941"}, + {file = "pycares-3.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:eee7b6a5f5b5af050cb7d66ab28179287b416f06d15a8974ac831437fec51336"}, + {file = "pycares-3.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:050f00b39ed77ea8a4e555f09417d4b1a6b5baa24bb9531a3e15d003d2319b3f"}, + {file = "pycares-3.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:2e4f74677542737fb5af4ea9a2e415ec5ab31aa67e7b8c3c969fdb15c069f679"}, + {file = "pycares-3.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:f8deaefefc3a589058df1b177275f79233e8b0eeee6734cf4336d80164ecd022"}, + {file = "pycares-3.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:c5cb72644b04e5e5abfb1e10a0e7eb75da6684ea0e60871652f348e412cf3b11"}, + {file = "pycares-3.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:c457a709e6f2befea7e2996c991eda6d79705dd075f6521593ba6ebc1485b811"}, + {file = "pycares-3.1.1-cp36-cp36m-win32.whl", hash = "sha256:1d8d177c40567de78108a7835170f570ab04f09084bfd32df9919c0eaec47aa1"}, + {file = "pycares-3.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f41ac1c858687e53242828c9f59c2e7b0b95dbcd5bdd09c7e5d3c48b0f89a25a"}, + {file = "pycares-3.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:9a0a1845f8cb2e62332bca0aaa9ad5494603ac43fb60d510a61d5b5b170d7216"}, + {file = "pycares-3.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:eba9a9227438da5e78fc8eee32f32eb35d9a50cf0a0bd937eb6275c7cc3015fe"}, + {file = "pycares-3.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0c5bd1f6f885a219d5e972788d6eef7b8043b55c3375a845e5399638436e0bba"}, + {file = "pycares-3.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:a05bbfdfd41f8410a905a818f329afe7510cbd9ee65c60f8860a72b6c64ce5dc"}, + {file = "pycares-3.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:11c628402cc8fc8ef461076d4e47f88afc1f8609989ebbff0dbffcd54c97239f"}, + {file = "pycares-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:fadb97d2e02dabdc15a0091591a972a938850d79ddde23d385d813c1731983f0"}, + {file = "pycares-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cce46dd4717debfd2aab79d6d7f0cbdf6b1e982dc4d9bebad81658d59ede07c2"}, + {file = "pycares-3.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0a24d2e580a8eb567140d7b69f12cb7de90c836bd7b6488ec69394d308605ac3"}, + {file = "pycares-3.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:fa78e919f3bd7d6d075db262aa41079b4c02da315c6043c6f43881e2ebcdd623"}, + {file = "pycares-3.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:236286f81664658b32c141c8e79d20afc3d54f6e2e49dfc8b702026be7265855"}, + {file = "pycares-3.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:7d86e62b700b21401ffe7fd1bbfe91e08489416fecae99c6570ab023c6896022"}, + {file = "pycares-3.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1b90fa00a89564df059fb18e796458864cc4e00cb55e364dbf921997266b7c55"}, + {file = "pycares-3.1.1-cp38-cp38-win32.whl", hash = "sha256:cfdd1f90bcf373b00f4b2c55ea47868616fe2f779f792fc913fa82a3d64ffe43"}, + {file = "pycares-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7e2d7effd08d2e5a3cb95d98a7286ebab71ab2fbce84fa93cc2dd56caf7240dd"}, + {file = "pycares-3.1.1.tar.gz", hash = "sha256:18dfd4fd300f570d6c4536c1d987b7b7673b2a9d14346592c5d6ed716df0d104"}, +] +pycodestyle = [ + {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, + {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, +] +pycparser = [ + {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, + {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, +] +pydocstyle = [ + {file = "pydocstyle-5.1.1-py3-none-any.whl", hash = "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678"}, + {file = "pydocstyle-5.1.1.tar.gz", hash = "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325"}, +] +pyflakes = [ + {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, + {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, +] +pynacl = [ + {file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"}, + {file = "PyNaCl-1.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514"}, + {file = "PyNaCl-1.4.0-cp27-cp27m-win32.whl", hash = "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574"}, + {file = "PyNaCl-1.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"}, + {file = "PyNaCl-1.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7"}, + {file = "PyNaCl-1.4.0-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122"}, + {file = "PyNaCl-1.4.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d"}, + {file = "PyNaCl-1.4.0-cp35-abi3-win32.whl", hash = "sha256:4e10569f8cbed81cb7526ae137049759d2a8d57726d52c1a000a3ce366779634"}, + {file = "PyNaCl-1.4.0-cp35-abi3-win_amd64.whl", hash = "sha256:c914f78da4953b33d4685e3cdc7ce63401247a21425c16a39760e282075ac4a6"}, + {file = "PyNaCl-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4"}, + {file = "PyNaCl-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25"}, + {file = "PyNaCl-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4"}, + {file = "PyNaCl-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6"}, + {file = "PyNaCl-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f"}, + {file = "PyNaCl-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f"}, + {file = "PyNaCl-1.4.0-cp38-cp38-win32.whl", hash = "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96"}, + {file = "PyNaCl-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420"}, + {file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, + {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, +] +python-dotenv = [ + {file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"}, + {file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"}, +] +pytz = [ + {file = "pytz-2019.3-py2.py3-none-any.whl", hash = "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d"}, + {file = "pytz-2019.3.tar.gz", hash = "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"}, +] +pyyaml = [ + {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, + {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, + {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, + {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, + {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, + {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, + {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, + {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, + {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, + {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, + {file = "PyYAML-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a"}, + {file = "PyYAML-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e"}, + {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, +] +redis = [ + {file = "redis-3.5.3-py2.py3-none-any.whl", hash = "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"}, + {file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"}, +] +sentry-sdk = [ + {file = "sentry-sdk-0.20.3.tar.gz", hash = "sha256:4ae8d1ced6c67f1c8ea51d82a16721c166c489b76876c9f2c202b8a50334b237"}, + {file = "sentry_sdk-0.20.3-py2.py3-none-any.whl", hash = "sha256:e75c8c58932bda8cd293ea8e4b242527129e1caaec91433d21b8b2f20fee030b"}, +] +six = [ + {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, + {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, +] +snowballstemmer = [ + {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, + {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, +] +sortedcontainers = [ + {file = "sortedcontainers-2.3.0-py2.py3-none-any.whl", hash = "sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f"}, + {file = "sortedcontainers-2.3.0.tar.gz", hash = "sha256:59cc937650cf60d677c16775597c89a960658a09cf7c1a668f86e1e4464b10a1"}, +] +soupsieve = [ + {file = "soupsieve-2.2-py3-none-any.whl", hash = "sha256:d3a5ea5b350423f47d07639f74475afedad48cf41c0ad7a82ca13a3928af34f6"}, + {file = "soupsieve-2.2.tar.gz", hash = "sha256:407fa1e8eb3458d1b5614df51d9651a1180ea5fedf07feb46e45d7e25e6d6cdd"}, +] +taskipy = [ + {file = "taskipy-1.6.0-py3-none-any.whl", hash = "sha256:33ee1d52b378cb4af3678fc459b75c3028f594c5e8e42ac0696cbe3e95d47394"}, + {file = "taskipy-1.6.0.tar.gz", hash = "sha256:ec4d1f2208ae24218950e3a2812e4e8b4397b1f65a6ad7e2b1240b702042fa3e"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +typing-extensions = [ + {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, + {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, + {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, +] +urllib3 = [ + {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"}, + {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"}, +] +virtualenv = [ + {file = "virtualenv-20.4.2-py2.py3-none-any.whl", hash = "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3"}, + {file = "virtualenv-20.4.2.tar.gz", hash = "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d"}, +] +yarl = [ + {file = "yarl-1.6.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366"}, + {file = "yarl-1.6.3-cp36-cp36m-win32.whl", hash = "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721"}, + {file = "yarl-1.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643"}, + {file = "yarl-1.6.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970"}, + {file = "yarl-1.6.3-cp37-cp37m-win32.whl", hash = "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e"}, + {file = "yarl-1.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50"}, + {file = "yarl-1.6.3-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2"}, + {file = "yarl-1.6.3-cp38-cp38-win32.whl", hash = "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896"}, + {file = "yarl-1.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a"}, + {file = "yarl-1.6.3-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4"}, + {file = "yarl-1.6.3-cp39-cp39-win32.whl", hash = "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424"}, + {file = "yarl-1.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6"}, + {file = "yarl-1.6.3.tar.gz", hash = "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..b342f7c1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,43 @@ +[tool.poetry] +name = "sir-lancebot" +version = "0.1.0" +description = "A Discord bot designed as a fun and beginner-friendly learning environment for writing bot features and learning open-source." +authors = ["Your Name "] +license = "MIT" + +[tool.poetry.dependencies] +python = "^3.9" +aiodns = "~=2.0" +arrow = "~=0.14" +beautifulsoup4 = "~=4.8" +fuzzywuzzy = "~=0.17" +pillow = "~=8.1" +pytz = "~=2019.2" +sentry-sdk = "~=0.19" +PyYAML = "~=5.3.1" +"discord.py" = {extras = ["voice"], version = "~=1.6.0"} +async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} +emojis = "~=0.6.0" + +[tool.poetry.dev-dependencies] +flake8 = "~=3.8" +flake8-annotations = "~=2.3" +flake8-bugbear = "~=20.1" +flake8-docstrings = "~=1.5" +flake8-import-order = "~=0.18" +flake8-string-format = "~=0.3" +flake8-tidy-imports = "~=4.1" +flake8-todo = "~=0.7" +pep8-naming = "~=0.11" +pre-commit = "~=2.1" +taskipy = "^1.6.0" +python-dotenv = "^0.15.0" + +[tool.taskipy.tasks] +start = "python -m bot" +lint = "pre-commit run --all-files" +precommit = "pre-commit install" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" -- cgit v1.2.3 From 03ef262f1a0957558ec07253dce8b103ac27964d Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 17:51:19 +0000 Subject: Update Dockerfile --- Dockerfile | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index 328984ad..641a180f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,33 +1,34 @@ -FROM python:3.8-slim +FROM python:3.9-slim # Set SHA build argument ARG git_sha="development" # Set pip to have cleaner logs and no saved cache ENV PIP_NO_CACHE_DIR=false \ - PIPENV_HIDE_EMOJIS=1 \ - PIPENV_IGNORE_VIRTUALENVS=1 \ - PIPENV_NOSPIN=1 \ - GIT_SHA=$git_sha + POETRY_HOME="/opt/poetry" # Install git to be able to dowload git dependencies in the Pipfile RUN apt-get -y update \ && apt-get install -y \ ffmpeg \ + gcc \ + build-essential \ && rm -rf /var/lib/apt/lists/* -# Install pipenv -RUN pip install -U pipenv +RUN pip install --user poetry +ENV PATH="${PATH}:/root/.local/bin" -# Copy the project files into working directory WORKDIR /bot -COPY . . -# Install project dependencies -RUN pipenv install --deploy --system +COPY pyproject.toml poetry.lock /bot/ + +RUN poetry install --no-dev --no-interaction --no-ansi + +ENV GIT_SHA=$git_sha + +COPY . . -ENTRYPOINT ["python"] -CMD ["-m", "bot"] +CMD ["poetry", "run", "python", "-m", "bot"] # Define docker persistent volumes VOLUME /bot/bot/log /bot/data -- cgit v1.2.3 From a40967bab9ae60bc78c96a978350cb7a79372ac6 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 17:51:57 +0000 Subject: Load environment variables from .env --- bot/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bot/__init__.py b/bot/__init__.py index bdb18666..ffd43d1f 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -1,3 +1,10 @@ +try: + from dotenv import load_dotenv + print("Found .env file, loading environment variables from it.") + load_dotenv(override=True) +except ModuleNotFoundError: + pass + import asyncio import logging import logging.handlers -- cgit v1.2.3 From e967928ee00f7bc21976c5d813499ddb2e8e85cf Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 17:52:38 +0000 Subject: Update CONTRIBUTNG.md with Poetry instructions --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7cf83db5..e21e1895 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ Note that contributions may be rejected on the basis of a contributor failing to * If PRing from your own fork, **ensure that "Allow edits from maintainers" is checked**. This gives permission for maintainers to commit changes directly to your fork, speeding up the review process. 5. **Adhere to the prevailing code style**, which we enforce using [`flake8`](http://flake8.pycqa.org/en/latest/index.html) and [`pre-commit`](https://pre-commit.com/). * Run `flake8` and `pre-commit` against your code [**before** you push it](https://soundcloud.com/lemonsaurusrex/lint-before-you-push). Your commit will be rejected by the build server if it fails to lint. - * [Git Hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) are a powerful git feature for executing custom scripts when certain important git actions occur. The pre-commit hook is the first hook executed during the commit process and can be used to check the code being committed & abort the commit if issues, such as linting failures, are detected. While git hooks can seem daunting to configure, the `pre-commit` framework abstracts this process away from you and is provided as a dev dependency for this project. Run `pipenv run precommit` when setting up the project and you'll never have to worry about committing code that fails linting. + * [Git Hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) are a powerful git feature for executing custom scripts when certain important git actions occur. The pre-commit hook is the first hook executed during the commit process and can be used to check the code being committed & abort the commit if issues, such as linting failures, are detected. While git hooks can seem daunting to configure, the `pre-commit` framework abstracts this process away from you and is provided as a dev dependency for this project. Run `poetry run task precommit` when setting up the project and you'll never have to worry about committing code that fails linting. 6. **Make great commits**. A well structured git log is key to a project's maintainability; it efficiently provides insight into when and *why* things were done for future maintainers of the project. * Commits should be as narrow in scope as possible. Commits that span hundreds of lines across multiple unrelated functions and/or files are very hard for maintainers to follow. After about a week they'll probably be hard for you to follow too. * Avoid making minor commits for fixing typos or linting errors. Since you've already set up a `pre-commit` hook to run the linting pipeline before a commit, you shouldn't be committing linting issues anyway. @@ -39,9 +39,9 @@ All projects evolve over time, and this contribution guide is no different. This ## Supplemental Information ### Developer Environment -Sir Lancebot utilizes [Pipenv](https://pipenv.readthedocs.io/en/latest/) for installation and dependency management. For users unfamiliar with the Pipenv workflow, Pipenv's documentation provides a [Basic Usage](https://pipenv.readthedocs.io/en/latest/basics/) tutorial, along with some of the more advanced workflows. A project-specific installation guide can be found in [Sir Lancebot's README](https://github.com/python-discord/sir-lancebot/blob/master/README.md). +Sir Lancebot utilizes [Poetry](https://python-poetry.org/docs/) for installation and dependency management. For users unfamiliar with the Poetry workflow, Poetry's documentation provides a [Basic Usage](https://python-poetry.org/docs/basic-usage/) tutorial, along with some of the more advanced workflows. A project-specific installation guide can be found in [Sir Lancebot's README](https://github.com/python-discord/sir-lancebot/blob/master/README.md). -When pulling down changes from GitHub, remember to sync your environment using `pipenv sync --dev` to ensure you're using the most up-to-date versions the project's dependencies. +When pulling down changes from GitHub, remember to sync your environment using `poetry update` to ensure you're using the most up-to-date versions the project's dependencies. ### Type Hinting [PEP 484](https://www.python.org/dev/peps/pep-0484/) formally specifies type hints for Python functions, added to the Python Standard Library in version 3.5. Type hints are recognized by most modern code editing tools and provide useful insight into both the input and output types of a function, preventing the user from having to go through the codebase to determine these types. -- cgit v1.2.3 From 24602966bcee772dcfb61ee7b725ab5432996138 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 17:52:54 +0000 Subject: Alter .dockerignore to allow poetry files --- .dockerignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.dockerignore b/.dockerignore index 159e4f4c..cb6f8f88 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,6 +3,6 @@ # Make exceptions for what's needed !bot -!Pipfile -!Pipfile.lock +!pyproject.toml +!poetry.lock !LICENSE -- cgit v1.2.3 From 07f5ca2c99b6a529ee3e4ca0d60d19a7170d4835 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 17:53:08 +0000 Subject: Alter pull request template --- .github/pull_request_template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e2739287..9b9e0e3a 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -25,6 +25,6 @@ - [ ] Join the [**Python Discord Community**](https://discord.gg/python)? -- [ ] If dependencies have been added or updated, run `pipenv lock`? -- [ ] **Lint your code** (`pipenv run lint`)? +- [ ] If dependencies have been added or updated, run `poetry lock`? +- [ ] **Lint your code** (`poetry run task lint`)? - [ ] Set the PR to **allow edits from contributors**? -- cgit v1.2.3 From 96d01477ba311b7fc29294bc1c502e816850e931 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 17:53:16 +0000 Subject: Alter linting workflow --- .github/workflows/lint.yaml | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index a5f45255..4f7d775a 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -16,13 +16,6 @@ jobs: PIP_NO_CACHE_DIR: false PIP_USER: 1 - # Hide the graphical elements from pipenv's output - PIPENV_HIDE_EMOJIS: 1 - PIPENV_NOSPIN: 1 - - # Make sure pipenv does not try reuse an environment it's running in - PIPENV_IGNORE_VIRTUALENVS: 1 - # Specify explicit paths for python dependencies and the pre-commit # environment so we know which directories to cache PYTHONUSERBASE: ${{ github.workspace }}/.cache/py-user-base @@ -54,14 +47,14 @@ jobs: path: ${{ env.PYTHONUSERBASE }} key: "python-0-${{ runner.os }}-${{ env.PYTHONUSERBASE }}-\ ${{ steps.python.outputs.python-version }}-\ - ${{ hashFiles('./Pipfile', './Pipfile.lock') }}" + ${{ hashFiles('./pyproject.toml', './poetry.lock') }}" # Install our dependencies if we did not restore a dependency cache - - name: Install dependencies using pipenv + - name: Install dependencies using poetry if: steps.python_cache.outputs.cache-hit != 'true' run: | - pip install pipenv - pipenv install --dev --deploy --system + pip install poetry + poetry install --no-interaction --no-ansi # This step caches our pre-commit environment. To make sure we # do create a new environment when our pre-commit setup changes, @@ -78,7 +71,7 @@ jobs: # action. As pre-commit does not support user installs, we set # PIP_USER=0 to not do a user install. - name: Run pre-commit hooks - run: export PIP_USER=0; SKIP=flake8 pre-commit run --all-files + run: export PIP_USER=0; SKIP=flake8 poetry run pre-commit run --all-files # Run flake8 and have it format the linting errors in the format of # the GitHub Workflow command to register error annotations. This @@ -89,7 +82,7 @@ jobs: # Format used: # ::error file={filename},line={line},col={col}::{message} - name: Run flake8 - run: "flake8 \ + run: "poetry run flake8 \ --format='::error file=%(path)s,line=%(row)d,col=%(col)d::[flake8] %(code)s: %(text)s'" # Prepare the Pull Request Payload artifact. If this fails, we -- cgit v1.2.3 From 6d2327eb092aa8ec2580192f587292474d4aef64 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 17:55:48 +0000 Subject: Bump lint workflow to py3.9 --- .github/workflows/lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 4f7d775a..3c82a0f2 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -32,7 +32,7 @@ jobs: id: python uses: actions/setup-python@v2 with: - python-version: '3.8' + python-version: '3.9' # This step caches our Python dependencies. To make sure we # only restore a cache when the dependencies, the python version, -- cgit v1.2.3 From aeb72c922c675f1eeb4c279874c8ef3561dde437 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 18:54:29 +0000 Subject: Disable poetry venv creation in Dockerfile and linting workflow --- .github/workflows/lint.yaml | 7 +++++-- Dockerfile | 10 +++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 3c82a0f2..3b5f2a5e 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -16,6 +16,9 @@ jobs: PIP_NO_CACHE_DIR: false PIP_USER: 1 + # Disable Poetry virtualenv creation + POETRY_VIRTUALENVS_CREATE: false + # Specify explicit paths for python dependencies and the pre-commit # environment so we know which directories to cache PYTHONUSERBASE: ${{ github.workspace }}/.cache/py-user-base @@ -71,7 +74,7 @@ jobs: # action. As pre-commit does not support user installs, we set # PIP_USER=0 to not do a user install. - name: Run pre-commit hooks - run: export PIP_USER=0; SKIP=flake8 poetry run pre-commit run --all-files + run: export PIP_USER=0; SKIP=flake8 pre-commit run --all-files # Run flake8 and have it format the linting errors in the format of # the GitHub Workflow command to register error annotations. This @@ -82,7 +85,7 @@ jobs: # Format used: # ::error file={filename},line={line},col={col}::{message} - name: Run flake8 - run: "poetry run flake8 \ + run: "flake8 \ --format='::error file=%(path)s,line=%(row)d,col=%(col)d::[flake8] %(code)s: %(text)s'" # Prepare the Pull Request Payload artifact. If this fails, we diff --git a/Dockerfile b/Dockerfile index 641a180f..f24f1eaa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,9 @@ FROM python:3.9-slim -# Set SHA build argument -ARG git_sha="development" - # Set pip to have cleaner logs and no saved cache ENV PIP_NO_CACHE_DIR=false \ - POETRY_HOME="/opt/poetry" + POETRY_HOME="/opt/poetry" \ + POETRY_VIRTUALENVS_CREATE=false # Install git to be able to dowload git dependencies in the Pipfile RUN apt-get -y update \ @@ -24,11 +22,13 @@ COPY pyproject.toml poetry.lock /bot/ RUN poetry install --no-dev --no-interaction --no-ansi +# Set SHA build argument +ARG git_sha="development" ENV GIT_SHA=$git_sha COPY . . -CMD ["poetry", "run", "python", "-m", "bot"] +CMD ["python", "-m", "bot"] # Define docker persistent volumes VOLUME /bot/bot/log /bot/data -- cgit v1.2.3 From 57cfa3bfc09ed16ffadd06efeb1fe35f2bac35ae Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 18:56:26 +0000 Subject: Annotate Dockerfile --- Dockerfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f24f1eaa..f5cd75b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,6 @@ FROM python:3.9-slim # Set pip to have cleaner logs and no saved cache ENV PIP_NO_CACHE_DIR=false \ - POETRY_HOME="/opt/poetry" \ POETRY_VIRTUALENVS_CREATE=false # Install git to be able to dowload git dependencies in the Pipfile @@ -13,21 +12,27 @@ RUN apt-get -y update \ build-essential \ && rm -rf /var/lib/apt/lists/* +# Install Poetry and add it to the path RUN pip install --user poetry ENV PATH="${PATH}:/root/.local/bin" WORKDIR /bot +# Copy dependencies and lockfile COPY pyproject.toml poetry.lock /bot/ +# Install dependencies and lockfile, excluding development +# dependencies, RUN poetry install --no-dev --no-interaction --no-ansi # Set SHA build argument ARG git_sha="development" ENV GIT_SHA=$git_sha +# Copy the rest of the project code COPY . . +# Start the bot CMD ["python", "-m", "bot"] # Define docker persistent volumes -- cgit v1.2.3 From 1cb0b203bb3af2f9377786aae672593783946314 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Wed, 10 Mar 2021 18:59:59 +0000 Subject: Change authors value in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b342f7c1..49ca5f0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "sir-lancebot" version = "0.1.0" description = "A Discord bot designed as a fun and beginner-friendly learning environment for writing bot features and learning open-source." -authors = ["Your Name "] +authors = ["Python Discord "] license = "MIT" [tool.poetry.dependencies] -- cgit v1.2.3 From 2ff509601fb97466ba6d980d226f6c0e481a1961 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 10 Mar 2021 19:31:37 +0000 Subject: Move colours to constants --- bot/constants.py | 16 ++++++++++++++++ bot/exts/evergreen/profile_pic_modification/_effects.py | 8 ++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index b8e30a7c..5c95d9c1 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -162,6 +162,22 @@ class Colours: python_yellow = 0xFFD43B grass_green = 0x66ff00 + easter_like_colours = [ + (255, 247, 0), + (255, 255, 224), + (0, 255, 127), + (189, 252, 201), + (255, 192, 203), + (255, 160, 122), + (181, 115, 220), + (221, 160, 221), + (200, 162, 200), + (238, 130, 238), + (135, 206, 235), + (0, 204, 204), + (64, 224, 208), + ] + class Emojis: star = "\u2B50" diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py index dda58006..b0d50f4b 100644 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -5,11 +5,7 @@ from pathlib import Path import discord from PIL import Image, ImageDraw, ImageOps -EASTER_COLOURS = [ - (255, 247, 0), (255, 255, 224), (0, 255, 127), (189, 252, 201), (255, 192, 203), - (255, 160, 122), (181, 115, 220), (221, 160, 221), (200, 162, 200), (238, 130, 238), - (135, 206, 235), (0, 204, 204), (64, 224, 208) -] # Pastel colours - Easter-like +from bot.constants import Colours class PfpEffects: @@ -46,7 +42,7 @@ class PfpEffects: r2, g2, b2 = point return (r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2 - closest_colours = sorted(EASTER_COLOURS, key=lambda point: distance(point)) + closest_colours = sorted(Colours.easter_like_colours, key=lambda point: distance(point)) r2, g2, b2 = closest_colours[0] r = (r1 + r2) // 2 g = (g1 + g2) // 2 -- cgit v1.2.3 From 3d54a88ee1d7fd5d9ece91ecea998e8da107ff22 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 10 Mar 2021 19:33:30 +0000 Subject: Improve docstring and split line for readibility --- bot/exts/evergreen/profile_pic_modification/_effects.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py index b0d50f4b..3f4e796d 100644 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -88,8 +88,14 @@ class PfpEffects: @staticmethod def eight_bitify_effect(image: Image) -> Image: - """Applies the 8bit effect to the given image.""" - image = image.resize((32, 32), resample=Image.NEAREST).resize((1024, 1024), resample=Image.NEAREST) + """ + Applies the 8bit effect to the given image. + + This is done by reducing the image to 32x32 and then back up to 1024x1024. + We then quantize the image before returning too. + """ + image = image.resize((32, 32), resample=Image.NEAREST) + image = image.resize((1024, 1024), resample=Image.NEAREST) return image.quantize() @staticmethod -- cgit v1.2.3 From 33141d9fa9d3ffe2120fc4cf2fcba824d047fe7e Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 10 Mar 2021 19:34:00 +0000 Subject: Remove double assignment of a variable --- bot/exts/evergreen/profile_pic_modification/_effects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py index 3f4e796d..ef0a3f37 100644 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -109,7 +109,7 @@ class PfpEffects: )) overlay_image = overlay_image.convert("RGBA") else: - overlay_image = overlay_image = Image.open(Path("bot/resources/easter/chocolate_bunny.png")) + overlay_image = Image.open(Path("bot/resources/easter/chocolate_bunny.png")) alpha = image.getchannel("A").getdata() image = image.convert("RGB") -- cgit v1.2.3 From 11bf3700c10c29b27ddd14c5282b239bb6f1f191 Mon Sep 17 00:00:00 2001 From: JagTheFriend Date: Tue, 9 Mar 2021 22:03:55 +0200 Subject: Removed requesting to bots. Added grammer. --- bot/exts/evergreen/tic_tac_toe.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index e1190502..6e21528e 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -10,8 +10,8 @@ from bot.constants import Emojis from bot.utils.pagination import LinePaginator CONFIRMATION_MESSAGE = ( - "{opponent}, {requester} wants to play Tic-Tac-Toe against you. React to this message with " - f"{Emojis.confirmation} to accept or with {Emojis.decline} to decline." + "{opponent}, {requester} wants to play Tic-Tac-Toe against you." + f"\nReact to this message with {Emojis.confirmation} to accept or with {Emojis.decline} to decline." ) @@ -253,7 +253,7 @@ class TicTacToe(Cog): @guild_only() @is_channel_free() @is_requester_free() - @group(name="tictactoe", aliases=("ttt",), invoke_without_command=True) + @group(name="tictactoe", aliases=("ttt", "tic"), invoke_without_command=True) async def tic_tac_toe(self, ctx: Context, opponent: t.Optional[discord.User]) -> None: """Tic Tac Toe game. Play against friends or AI. Use reactions to add your mark to field.""" if opponent == ctx.author: @@ -276,6 +276,10 @@ class TicTacToe(Cog): ) self.games.append(game) if opponent is not None: + if opponent.bot: # check whether the opponent is a bot or not + await ctx.send("You can't play Tic-Tac-Toe with bots!") + return + confirmed, msg = await game.get_confirmation() if not confirmed: -- cgit v1.2.3 From 1cd9c7a4f74834d799bfc706f5f28cb4147fa0ff Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Thu, 11 Mar 2021 12:24:49 -0500 Subject: change ctx.send_help to ctx.invoke(help_command) ; --- bot/exts/christmas/advent_of_code/_cog.py | 3 ++- bot/exts/evergreen/emoji.py | 3 ++- bot/exts/evergreen/game.py | 3 ++- bot/exts/evergreen/minesweeper.py | 3 ++- bot/exts/evergreen/movie.py | 3 ++- bot/exts/evergreen/snakes/_snakes_cog.py | 3 ++- bot/exts/evergreen/space.py | 3 ++- bot/exts/evergreen/status_codes.py | 3 ++- bot/exts/utils/extensions.py | 12 ++++++++---- bot/exts/valentines/be_my_valentine.py | 3 ++- 10 files changed, 26 insertions(+), 13 deletions(-) diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index 29902306..bf008ee9 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -51,7 +51,8 @@ class AdventOfCode(commands.Cog): async def adventofcode_group(self, ctx: commands.Context) -> None: """All of the Advent of Code commands.""" if not ctx.invoked_subcommand: - await ctx.send_help(ctx.command) + help_command = self.bot.get_command("help") + await ctx.invoke(help_command, ctx.command.name) @adventofcode_group.command( name="subscribe", diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index 99f71218..40bf83e8 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -75,7 +75,8 @@ class Emojis(commands.Cog): if emoji is not None: await ctx.invoke(self.info_command, emoji) else: - await ctx.send_help(ctx.command) + help_command = self.bot.get_command("help") + await ctx.invoke(help_command, ctx.command.name) @emoji_group.command(name="count", aliases=("c",)) async def count_command(self, ctx: commands.Context, *, category_query: str = None) -> None: diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index d37be0e2..d49a8858 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -234,7 +234,8 @@ class Games(Cog): """ # When user didn't specified genre, send help message if genre is None: - await ctx.send_help("games") + help_command = self.bot.get_command("help") + await ctx.invoke(help_command, ctx.command.name) return # Capitalize genre for check diff --git a/bot/exts/evergreen/minesweeper.py b/bot/exts/evergreen/minesweeper.py index 286ac7a5..06a75c2b 100644 --- a/bot/exts/evergreen/minesweeper.py +++ b/bot/exts/evergreen/minesweeper.py @@ -83,7 +83,8 @@ class Minesweeper(commands.Cog): @commands.group(name='minesweeper', aliases=('ms',), invoke_without_command=True) async def minesweeper_group(self, ctx: commands.Context) -> None: """Commands for Playing Minesweeper.""" - await ctx.send_help(ctx.command) + help_command = ctx.bot.get_command("help") + await ctx.invoke(help_command, ctx.command.name) @staticmethod def get_neighbours(x: int, y: int) -> typing.Generator[typing.Tuple[int, int], None, None]: diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index 340a5724..238ab038 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -73,7 +73,8 @@ class Movie(Cog): try: result = await self.get_movies_list(self.http_session, MovieGenres[genre].value, 1) except KeyError: - await ctx.send_help('movies') + help_command = self.bot.get_command("help") + await ctx.invoke(help_command, ctx.command.name) return # Check if "results" is in result. If not, throw error. diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index d5e4f206..819a98b2 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -440,7 +440,8 @@ class Snakes(Cog): @group(name='snakes', aliases=('snake',), invoke_without_command=True) async def snakes_group(self, ctx: Context) -> None: """Commands from our first code jam.""" - await ctx.send_help(ctx.command) + help_command = self.bot.get_command("help") + await ctx.invoke(help_command, ctx.command.name) @bot_has_permissions(manage_messages=True) @snakes_group.command(name='antidote') diff --git a/bot/exts/evergreen/space.py b/bot/exts/evergreen/space.py index bc8e3118..e388e13f 100644 --- a/bot/exts/evergreen/space.py +++ b/bot/exts/evergreen/space.py @@ -63,7 +63,8 @@ class Space(Cog): @group(name="space", invoke_without_command=True) async def space(self, ctx: Context) -> None: """Head command that contains commands about space.""" - await ctx.send_help("space") + help_command = self.bot.get_command("help") + await ctx.invoke(help_command, ctx.command.name) @space.command(name="apod") async def apod(self, ctx: Context, date: Optional[str] = None) -> None: diff --git a/bot/exts/evergreen/status_codes.py b/bot/exts/evergreen/status_codes.py index 874c87eb..d06724eb 100644 --- a/bot/exts/evergreen/status_codes.py +++ b/bot/exts/evergreen/status_codes.py @@ -17,7 +17,8 @@ class HTTPStatusCodes(commands.Cog): async def http_status_group(self, ctx: commands.Context) -> None: """Group containing dog and cat http status code commands.""" if not ctx.invoked_subcommand: - await ctx.send_help(ctx.command) + help_command = self.bot.get_command("help") + await ctx.invoke(help_command, ctx.command.name) @http_status_group.command(name='cat') async def http_cat(self, ctx: commands.Context, code: int) -> None: diff --git a/bot/exts/utils/extensions.py b/bot/exts/utils/extensions.py index bb22c353..f7ff2396 100644 --- a/bot/exts/utils/extensions.py +++ b/bot/exts/utils/extensions.py @@ -77,7 +77,8 @@ class Extensions(commands.Cog): @group(name="extensions", aliases=("ext", "exts", "c", "cogs"), invoke_without_command=True) async def extensions_group(self, ctx: Context) -> None: """Load, unload, reload, and list loaded extensions.""" - await ctx.send_help(ctx.command) + help_command = self.bot.get_command("help") + await ctx.invoke(help_command, ctx.command.name) @extensions_group.command(name="load", aliases=("l",)) async def load_command(self, ctx: Context, *extensions: Extension) -> None: @@ -87,7 +88,8 @@ class Extensions(commands.Cog): If '\*' or '\*\*' is given as the name, all unloaded extensions will be loaded. """ # noqa: W605 if not extensions: - await ctx.send_help(ctx.command) + help_command = self.bot.get_command("help") + await ctx.invoke(help_command, "extensions", ctx.command.name) return if "*" in extensions or "**" in extensions: @@ -104,7 +106,8 @@ class Extensions(commands.Cog): If '\*' or '\*\*' is given as the name, all loaded extensions will be unloaded. """ # noqa: W605 if not extensions: - await ctx.send_help(ctx.command) + help_command = self.bot.get_command("help") + await ctx.invoke(help_command, "extensions", ctx.command.name) return blacklisted = "\n".join(UNLOAD_BLACKLIST & set(extensions)) @@ -130,7 +133,8 @@ class Extensions(commands.Cog): If '\*\*' is given as the name, all extensions, including unloaded ones, will be reloaded. """ # noqa: W605 if not extensions: - await ctx.send_help(ctx.command) + help_command = self.bot.get_command("help") + await ctx.invoke(help_command, "extensions", ctx.command.name) return if "**" in extensions: diff --git a/bot/exts/valentines/be_my_valentine.py b/bot/exts/valentines/be_my_valentine.py index f3392bcb..59bd42d3 100644 --- a/bot/exts/valentines/be_my_valentine.py +++ b/bot/exts/valentines/be_my_valentine.py @@ -43,7 +43,8 @@ class BeMyValentine(commands.Cog): 2) use the command \".lovefest unsub\" to get rid of the lovefest role. """ if not ctx.invoked_subcommand: - await ctx.send_help(ctx.command) + help_command = self.bot.get_command("help") + await ctx.invoke(help_command, ctx.command.name) @lovefest_role.command(name="sub") async def add_role(self, ctx: commands.Context) -> None: -- cgit v1.2.3 From d90f735913b087104a24aa718b6473fa0b39384c Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 11 Mar 2021 17:55:20 +0000 Subject: Use a more specific filename for avatar modifiying commands --- .../evergreen/profile_pic_modification/_effects.py | 4 +- .../profile_pic_modification/pfp_modify.py | 44 ++++++++++++++++++---- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py index ef0a3f37..ae606dcb 100644 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -16,7 +16,7 @@ class PfpEffects: """ @staticmethod - def apply_effect(image_bytes: bytes, effect: t.Callable, *args) -> discord.File: + def apply_effect(image_bytes: bytes, effect: t.Callable, filename: str, *args) -> discord.File: """Applies the given effect to the image passed to it.""" im = Image.open(BytesIO(image_bytes)) im = im.convert("RGBA") @@ -26,7 +26,7 @@ class PfpEffects: im.save(bufferedio, format="PNG") bufferedio.seek(0) - return discord.File(bufferedio, filename="modified_avatar.png") + return discord.File(bufferedio, filename=filename) @staticmethod def closest(x: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 11222563..299ed107 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -17,6 +17,8 @@ log = logging.getLogger(__name__) _EXECUTOR = ThreadPoolExecutor(10) +FILENAME_STRING = "{effect}_{author}.png" + async def in_executor(func: t.Callable, *args) -> t.Any: """Allows non-async functions to work in async functions.""" @@ -48,10 +50,16 @@ class PfpModify(commands.Cog): """Pixelates your avatar and changes the palette to an 8bit one.""" async with ctx.typing(): image_bytes = await ctx.author.avatar_url.read() + file_name = FILENAME_STRING.format( + effect="eightbit_avatar", + author=ctx.author.display_name + ) + file = await in_executor( PfpEffects.apply_effect, image_bytes, - PfpEffects.eight_bitify_effect + PfpEffects.eight_bitify_effect, + file_name ) embed = discord.Embed( @@ -59,10 +67,10 @@ class PfpModify(commands.Cog): description="Here is your avatar. I think it looks all cool and 'retro'." ) - embed.set_image(url="attachment://modified_avatar.png") + embed.set_image(url=f"attachment://{file_name}") embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) - await ctx.send(file=file, embed=embed) + await ctx.send(embed=embed, file=file) @pfp_modify.command(pass_context=True, aliases=["easterify"], root_aliases=("easterify", "avatareasterify")) async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: @@ -96,10 +104,16 @@ class PfpModify(commands.Cog): ctx.send = send_message # Reassigns ctx.send image_bytes = await ctx.author.avatar_url_as(size=256).read() + file_name = FILENAME_STRING.format( + effect="easterified_avatar", + author=ctx.author.display_name + ) + file = await in_executor( PfpEffects.apply_effect, image_bytes, PfpEffects.easterify_effect, + file_name, egg ) @@ -107,7 +121,7 @@ class PfpModify(commands.Cog): name="Your Lovely Easterified Avatar!", description="Here is your lovely avatar, all bright and colourful\nwith Easter pastel colours. Enjoy :D" ) - embed.set_image(url="attachment://modified_avatar.png") + embed.set_image(url=f"attachment://{file_name}") embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) await ctx.send(file=file, embed=embed) @@ -122,10 +136,16 @@ class PfpModify(commands.Cog): ) -> None: """Gets and sends the image in an embed. Used by the pride commands.""" async with ctx.typing(): + file_name = FILENAME_STRING.format( + effect="pride_avatar", + author=ctx.author.display_name + ) + file = await in_executor( PfpEffects.apply_effect, image_bytes, PfpEffects.pridify_effect, + file_name, pixels, flag ) @@ -134,7 +154,7 @@ class PfpModify(commands.Cog): name="Your Lovely Pride Avatar!", description=f"Here is your lovely avatar, surrounded by\n a beautiful {option} flag. Enjoy :D" ) - embed.set_image(url="attachment://modified_avatar.png") + embed.set_image(url=f"attachment://{file_name}") embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) await ctx.send(file=file, embed=embed) @@ -223,14 +243,24 @@ class PfpModify(commands.Cog): async with ctx.typing(): image_bytes = await ctx.author.avatar_url.read() - file = await in_executor(PfpEffects.apply_effect, image_bytes, spookifications.get_random_effect) + + file_name = FILENAME_STRING.format( + effect="pride_avatar", + author=ctx.author.display_name + ) + file = await in_executor( + PfpEffects.apply_effect, + image_bytes, + spookifications.get_random_effect, + file_name + ) embed = discord.Embed( title="Is this you or am I just really paranoid?", colour=Colours.soft_red ) embed.set_author(name=user.name, icon_url=user.avatar_url) - embed.set_image(url='attachment://modified_avatar.png') + embed.set_image(url=f"attachment://{file_name}") embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) await ctx.send(file=file, embed=embed) -- cgit v1.2.3 From 856cffbbfa2411fa246a2ff988b1199e0dcb4030 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 11 Mar 2021 17:59:39 +0000 Subject: Defer errors in pride image to error handler --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 299ed107..68750fe2 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -8,6 +8,7 @@ import aiofiles import discord from aiohttp import client_exceptions from discord.ext import commands +from discord.ext.commands.errors import BadArgument from bot.constants import Colours from bot.exts.evergreen.profile_pic_modification._effects import PfpEffects @@ -205,11 +206,9 @@ class PfpModify(commands.Cog): try: response = await session.get(url) except client_exceptions.ClientConnectorError: - await ctx.send("Cannot connect to provided URL!") - return + raise BadArgument("Cannot connect to provided URL!") except client_exceptions.InvalidURL: - await ctx.send("Invalid URL!") - return + raise BadArgument("Invalid URL!") if response.status != 200: await ctx.send("Bad response from provided URL!") -- cgit v1.2.3 From 6c1b715bae96980309af3b38a5ff6cad6ce49f95 Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Thu, 11 Mar 2021 18:01:22 +0000 Subject: Update docstring to refer to groups rather than commands Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/group.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/group.py b/bot/group.py index 77092adf..a7bc59b7 100644 --- a/bot/group.py +++ b/bot/group.py @@ -6,7 +6,7 @@ class Group(commands.Group): A `discord.ext.commands.Group` subclass which supports root aliases. A `root_aliases` keyword argument is added, which is a sequence of alias names that will act as - top-level commands rather than being aliases of the command's group. It's stored as an attribute + top-level groups rather than being aliases of the command's group. It's stored as an attribute also named `root_aliases`. """ @@ -15,4 +15,4 @@ class Group(commands.Group): self.root_aliases = kwargs.get("root_aliases", []) if not isinstance(self.root_aliases, (list, tuple)): - raise TypeError("Root aliases of a command must be a list or a tuple of strings.") + raise TypeError("Root aliases of a group must be a list or a tuple of strings.") -- cgit v1.2.3 From 335702f65429c6f1ebc9a9a50ffc528bcfd38581 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 11 Mar 2021 18:06:22 +0000 Subject: Fix filename for spooky effect avatars --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 68750fe2..9b874900 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -244,7 +244,7 @@ class PfpModify(commands.Cog): image_bytes = await ctx.author.avatar_url.read() file_name = FILENAME_STRING.format( - effect="pride_avatar", + effect="spooky_avatar", author=ctx.author.display_name ) file = await in_executor( -- cgit v1.2.3 From cc72d484115a8acebf0fafe8bb332817168d3acd Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 12 Mar 2021 19:17:39 -0500 Subject: make utility invoke_help_command function --- bot/exts/christmas/advent_of_code/_cog.py | 4 ++-- bot/exts/evergreen/emoji.py | 4 ++-- bot/exts/evergreen/game.py | 4 ++-- bot/exts/evergreen/minesweeper.py | 4 ++-- bot/exts/evergreen/movie.py | 4 ++-- bot/exts/evergreen/snakes/_snakes_cog.py | 4 ++-- bot/exts/evergreen/space.py | 4 ++-- bot/exts/evergreen/status_codes.py | 4 ++-- bot/exts/utils/extensions.py | 14 +++++--------- bot/exts/valentines/be_my_valentine.py | 4 ++-- bot/utils/extensions.py | 12 ++++++++++++ 11 files changed, 35 insertions(+), 27 deletions(-) diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index bf008ee9..6ba5c2af 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -12,6 +12,7 @@ from bot.constants import ( ) from bot.exts.christmas.advent_of_code import _helpers from bot.utils.decorators import InChannelCheckFailure, in_month, whitelist_override, with_role +from bot.utils.extensions import invoke_help_command log = logging.getLogger(__name__) @@ -51,8 +52,7 @@ class AdventOfCode(commands.Cog): async def adventofcode_group(self, ctx: commands.Context) -> None: """All of the Advent of Code commands.""" if not ctx.invoked_subcommand: - help_command = self.bot.get_command("help") - await ctx.invoke(help_command, ctx.command.name) + await invoke_help_command(ctx, ctx.command.name) @adventofcode_group.command( name="subscribe", diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index 40bf83e8..042e539d 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -11,6 +11,7 @@ from discord.ext import commands from bot.constants import Colours, ERROR_REPLIES from bot.utils.pagination import LinePaginator from bot.utils.time import time_since +from bot.utils.extensions import invoke_help_command log = logging.getLogger(__name__) @@ -75,8 +76,7 @@ class Emojis(commands.Cog): if emoji is not None: await ctx.invoke(self.info_command, emoji) else: - help_command = self.bot.get_command("help") - await ctx.invoke(help_command, ctx.command.name) + await invoke_help_command(ctx, ctx.command.name) @emoji_group.command(name="count", aliases=("c",)) async def count_command(self, ctx: commands.Context, *, category_query: str = None) -> None: diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index d49a8858..ab4cae77 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -16,6 +16,7 @@ from bot.bot import Bot from bot.constants import STAFF_ROLES, Tokens from bot.utils.decorators import with_role from bot.utils.pagination import ImagePaginator, LinePaginator +from bot.utils.extensions import invoke_help_command # Base URL of IGDB API BASE_URL = "https://api.igdb.com/v4" @@ -234,8 +235,7 @@ class Games(Cog): """ # When user didn't specified genre, send help message if genre is None: - help_command = self.bot.get_command("help") - await ctx.invoke(help_command, ctx.command.name) + await invoke_help_command(ctx, ctx.command.name) return # Capitalize genre for check diff --git a/bot/exts/evergreen/minesweeper.py b/bot/exts/evergreen/minesweeper.py index 06a75c2b..cfe7150e 100644 --- a/bot/exts/evergreen/minesweeper.py +++ b/bot/exts/evergreen/minesweeper.py @@ -8,6 +8,7 @@ from discord.ext import commands from bot.constants import Client from bot.utils.exceptions import UserNotPlayingError +from bot.utils.extensions import invoke_help_command MESSAGE_MAPPING = { 0: ":stop_button:", @@ -83,8 +84,7 @@ class Minesweeper(commands.Cog): @commands.group(name='minesweeper', aliases=('ms',), invoke_without_command=True) async def minesweeper_group(self, ctx: commands.Context) -> None: """Commands for Playing Minesweeper.""" - help_command = ctx.bot.get_command("help") - await ctx.invoke(help_command, ctx.command.name) + await invoke_help_command(ctx, ctx.command.name) @staticmethod def get_neighbours(x: int, y: int) -> typing.Generator[typing.Tuple[int, int], None, None]: diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index 238ab038..9aa3aea7 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -10,6 +10,7 @@ from discord.ext.commands import Bot, Cog, Context, group from bot.constants import Tokens from bot.utils.pagination import ImagePaginator +from bot.utils.extensions import invoke_help_command # Define base URL of TMDB BASE_URL = "https://api.themoviedb.org/3/" @@ -73,8 +74,7 @@ class Movie(Cog): try: result = await self.get_movies_list(self.http_session, MovieGenres[genre].value, 1) except KeyError: - help_command = self.bot.get_command("help") - await ctx.invoke(help_command, ctx.command.name) + await invoke_help_command(ctx, ctx.command.name) return # Check if "results" is in result. If not, throw error. diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index 819a98b2..d0e60819 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -22,6 +22,7 @@ from bot.constants import ERROR_REPLIES, Tokens from bot.exts.evergreen.snakes import _utils as utils from bot.exts.evergreen.snakes._converter import Snake from bot.utils.decorators import locked +from bot.utils.extensions import invoke_help_command log = logging.getLogger(__name__) @@ -440,8 +441,7 @@ class Snakes(Cog): @group(name='snakes', aliases=('snake',), invoke_without_command=True) async def snakes_group(self, ctx: Context) -> None: """Commands from our first code jam.""" - help_command = self.bot.get_command("help") - await ctx.invoke(help_command, ctx.command.name) + await invoke_help_command(ctx, ctx.command.name) @bot_has_permissions(manage_messages=True) @snakes_group.command(name='antidote') diff --git a/bot/exts/evergreen/space.py b/bot/exts/evergreen/space.py index e388e13f..305d6721 100644 --- a/bot/exts/evergreen/space.py +++ b/bot/exts/evergreen/space.py @@ -10,6 +10,7 @@ from discord.ext.commands import BadArgument, Cog, Context, Converter, group from bot.bot import Bot from bot.constants import Tokens +from bot.utils.extensions import invoke_help_command logger = logging.getLogger(__name__) @@ -63,8 +64,7 @@ class Space(Cog): @group(name="space", invoke_without_command=True) async def space(self, ctx: Context) -> None: """Head command that contains commands about space.""" - help_command = self.bot.get_command("help") - await ctx.invoke(help_command, ctx.command.name) + await invoke_help_command(ctx, ctx.command.name) @space.command(name="apod") async def apod(self, ctx: Context, date: Optional[str] = None) -> None: diff --git a/bot/exts/evergreen/status_codes.py b/bot/exts/evergreen/status_codes.py index d06724eb..a037e117 100644 --- a/bot/exts/evergreen/status_codes.py +++ b/bot/exts/evergreen/status_codes.py @@ -2,6 +2,7 @@ from http import HTTPStatus import discord from discord.ext import commands +from bot.utils.extensions import invoke_help_command HTTP_DOG_URL = "https://httpstatusdogs.com/img/{code}.jpg" HTTP_CAT_URL = "https://http.cat/{code}.jpg" @@ -17,8 +18,7 @@ class HTTPStatusCodes(commands.Cog): async def http_status_group(self, ctx: commands.Context) -> None: """Group containing dog and cat http status code commands.""" if not ctx.invoked_subcommand: - help_command = self.bot.get_command("help") - await ctx.invoke(help_command, ctx.command.name) + await invoke_help_command(ctx, ctx.command.name) @http_status_group.command(name='cat') async def http_cat(self, ctx: commands.Context, code: int) -> None: diff --git a/bot/exts/utils/extensions.py b/bot/exts/utils/extensions.py index f7ff2396..9c126a7b 100644 --- a/bot/exts/utils/extensions.py +++ b/bot/exts/utils/extensions.py @@ -11,7 +11,7 @@ from bot import exts from bot.bot import Bot from bot.constants import Client, Emojis, MODERATION_ROLES, Roles from bot.utils.checks import with_role_check -from bot.utils.extensions import EXTENSIONS, unqualify +from bot.utils.extensions import EXTENSIONS, unqualify, invoke_help_command from bot.utils.pagination import LinePaginator log = logging.getLogger(__name__) @@ -77,8 +77,7 @@ class Extensions(commands.Cog): @group(name="extensions", aliases=("ext", "exts", "c", "cogs"), invoke_without_command=True) async def extensions_group(self, ctx: Context) -> None: """Load, unload, reload, and list loaded extensions.""" - help_command = self.bot.get_command("help") - await ctx.invoke(help_command, ctx.command.name) + await invoke_help_command(ctx, ctx.command.name) @extensions_group.command(name="load", aliases=("l",)) async def load_command(self, ctx: Context, *extensions: Extension) -> None: @@ -88,8 +87,7 @@ class Extensions(commands.Cog): If '\*' or '\*\*' is given as the name, all unloaded extensions will be loaded. """ # noqa: W605 if not extensions: - help_command = self.bot.get_command("help") - await ctx.invoke(help_command, "extensions", ctx.command.name) + await invoke_help_command(ctx, "extensions", ctx.command.name) return if "*" in extensions or "**" in extensions: @@ -106,8 +104,7 @@ class Extensions(commands.Cog): If '\*' or '\*\*' is given as the name, all loaded extensions will be unloaded. """ # noqa: W605 if not extensions: - help_command = self.bot.get_command("help") - await ctx.invoke(help_command, "extensions", ctx.command.name) + await invoke_help_command(ctx, "extensions", ctx.command.name) return blacklisted = "\n".join(UNLOAD_BLACKLIST & set(extensions)) @@ -133,8 +130,7 @@ class Extensions(commands.Cog): If '\*\*' is given as the name, all extensions, including unloaded ones, will be reloaded. """ # noqa: W605 if not extensions: - help_command = self.bot.get_command("help") - await ctx.invoke(help_command, "extensions", ctx.command.name) + await invoke_help_command(ctx, "extensions", ctx.command.name) return if "**" in extensions: diff --git a/bot/exts/valentines/be_my_valentine.py b/bot/exts/valentines/be_my_valentine.py index 59bd42d3..d5cc8644 100644 --- a/bot/exts/valentines/be_my_valentine.py +++ b/bot/exts/valentines/be_my_valentine.py @@ -10,6 +10,7 @@ from discord.ext.commands.cooldowns import BucketType from bot.constants import Channels, Colours, Lovefest, Month from bot.utils.decorators import in_month +from bot.utils.extensions import invoke_help_command log = logging.getLogger(__name__) @@ -43,8 +44,7 @@ class BeMyValentine(commands.Cog): 2) use the command \".lovefest unsub\" to get rid of the lovefest role. """ if not ctx.invoked_subcommand: - help_command = self.bot.get_command("help") - await ctx.invoke(help_command, ctx.command.name) + await invoke_help_command(ctx, ctx.command.name) @lovefest_role.command(name="sub") async def add_role(self, ctx: commands.Context) -> None: diff --git a/bot/utils/extensions.py b/bot/utils/extensions.py index 50350ea8..56f6dd00 100644 --- a/bot/utils/extensions.py +++ b/bot/utils/extensions.py @@ -4,6 +4,7 @@ import pkgutil from typing import Iterator, NoReturn from bot import exts +from discord.ext.commands import Context def unqualify(name: str) -> str: @@ -31,4 +32,15 @@ def walk_extensions() -> Iterator[str]: yield module.name +async def invoke_help_command(ctx: Context, *commands: str) -> None: + """Invoke the help command, and will use the default help command + if the help exten is not loaded. + """ + + if 'bot.exts.evergreen.help' in ctx.bot.extensions: + help_command = ctx.bot.get_command('help') + await ctx.invoke(help_command, *commands) + return + await ctx.send_help(''.join(commands)) + EXTENSIONS = frozenset(walk_extensions()) -- cgit v1.2.3 From ccf7d118eb2dea996b10bf0a4cfca977c452f636 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 12 Mar 2021 20:01:24 -0500 Subject: Make flake8 happy. --- bot/exts/evergreen/game.py | 2 +- bot/exts/evergreen/status_codes.py | 1 + bot/exts/utils/extensions.py | 2 +- bot/utils/extensions.py | 7 ++----- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index ab4cae77..a6bfecaa 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -15,8 +15,8 @@ from discord.ext.commands import Cog, Context, group from bot.bot import Bot from bot.constants import STAFF_ROLES, Tokens from bot.utils.decorators import with_role -from bot.utils.pagination import ImagePaginator, LinePaginator from bot.utils.extensions import invoke_help_command +from bot.utils.pagination import ImagePaginator, LinePaginator # Base URL of IGDB API BASE_URL = "https://api.igdb.com/v4" diff --git a/bot/exts/evergreen/status_codes.py b/bot/exts/evergreen/status_codes.py index a037e117..127f3424 100644 --- a/bot/exts/evergreen/status_codes.py +++ b/bot/exts/evergreen/status_codes.py @@ -2,6 +2,7 @@ from http import HTTPStatus import discord from discord.ext import commands + from bot.utils.extensions import invoke_help_command HTTP_DOG_URL = "https://httpstatusdogs.com/img/{code}.jpg" diff --git a/bot/exts/utils/extensions.py b/bot/exts/utils/extensions.py index 9c126a7b..4bd606b0 100644 --- a/bot/exts/utils/extensions.py +++ b/bot/exts/utils/extensions.py @@ -11,7 +11,7 @@ from bot import exts from bot.bot import Bot from bot.constants import Client, Emojis, MODERATION_ROLES, Roles from bot.utils.checks import with_role_check -from bot.utils.extensions import EXTENSIONS, unqualify, invoke_help_command +from bot.utils.extensions import EXTENSIONS, invoke_help_command, unqualify from bot.utils.pagination import LinePaginator log = logging.getLogger(__name__) diff --git a/bot/utils/extensions.py b/bot/utils/extensions.py index 56f6dd00..afc57383 100644 --- a/bot/utils/extensions.py +++ b/bot/utils/extensions.py @@ -1,10 +1,10 @@ +from discord.ext.commands import Context import importlib import inspect import pkgutil from typing import Iterator, NoReturn from bot import exts -from discord.ext.commands import Context def unqualify(name: str) -> str: @@ -33,10 +33,7 @@ def walk_extensions() -> Iterator[str]: async def invoke_help_command(ctx: Context, *commands: str) -> None: - """Invoke the help command, and will use the default help command - if the help exten is not loaded. - """ - + """Invoke the help command or default help command if help extensions is not loaded.""" if 'bot.exts.evergreen.help' in ctx.bot.extensions: help_command = ctx.bot.get_command('help') await ctx.invoke(help_command, *commands) -- cgit v1.2.3 From b395c05cc0885c427afab80e914d098dd9edc742 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 12 Mar 2021 20:06:56 -0500 Subject: Make flake8 happy again. --- bot/exts/evergreen/emoji.py | 2 +- bot/exts/evergreen/movie.py | 2 +- bot/utils/extensions.py | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index 042e539d..e55a11ad 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -9,9 +9,9 @@ from discord import Color, Embed, Emoji from discord.ext import commands from bot.constants import Colours, ERROR_REPLIES +from bot.utils.extensions import invoke_help_command from bot.utils.pagination import LinePaginator from bot.utils.time import time_since -from bot.utils.extensions import invoke_help_command log = logging.getLogger(__name__) diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index 9aa3aea7..63ce81da 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -9,8 +9,8 @@ from discord import Embed from discord.ext.commands import Bot, Cog, Context, group from bot.constants import Tokens -from bot.utils.pagination import ImagePaginator from bot.utils.extensions import invoke_help_command +from bot.utils.pagination import ImagePaginator # Define base URL of TMDB BASE_URL = "https://api.themoviedb.org/3/" diff --git a/bot/utils/extensions.py b/bot/utils/extensions.py index afc57383..ffa8a936 100644 --- a/bot/utils/extensions.py +++ b/bot/utils/extensions.py @@ -1,9 +1,10 @@ -from discord.ext.commands import Context import importlib import inspect import pkgutil from typing import Iterator, NoReturn +from discord.ext.commands import Context + from bot import exts -- cgit v1.2.3 From 88e7ab58ff6ad74a8323c828cc5411886bb57dbe Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Sat, 13 Mar 2021 14:57:54 +0000 Subject: Use user reference passed via command The command takes in user as argument but doesn't use it while converting the image into bytes. Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 9b874900..aa0f0404 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -241,11 +241,11 @@ class PfpModify(commands.Cog): user = ctx.message.author async with ctx.typing(): - image_bytes = await ctx.author.avatar_url.read() + image_bytes = await user.avatar_url.read() file_name = FILENAME_STRING.format( effect="spooky_avatar", - author=ctx.author.display_name + author=user.display_name ) file = await in_executor( PfpEffects.apply_effect, -- cgit v1.2.3 From 9ec1cf9b983a69c381507f82c317e0465eecdc35 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Sat, 13 Mar 2021 10:16:31 -0500 Subject: make invoke_help_command only take ctx --- bot/exts/christmas/advent_of_code/_cog.py | 2 +- bot/exts/evergreen/emoji.py | 2 +- bot/exts/evergreen/game.py | 2 +- bot/exts/evergreen/minesweeper.py | 2 +- bot/exts/evergreen/movie.py | 2 +- bot/exts/evergreen/snakes/_snakes_cog.py | 2 +- bot/exts/evergreen/space.py | 2 +- bot/exts/evergreen/status_codes.py | 2 +- bot/exts/utils/extensions.py | 8 ++++---- bot/exts/valentines/be_my_valentine.py | 2 +- bot/utils/extensions.py | 6 +++--- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index 6ba5c2af..dc3d7616 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -52,7 +52,7 @@ class AdventOfCode(commands.Cog): async def adventofcode_group(self, ctx: commands.Context) -> None: """All of the Advent of Code commands.""" if not ctx.invoked_subcommand: - await invoke_help_command(ctx, ctx.command.name) + await invoke_help_command(ctx) @adventofcode_group.command( name="subscribe", diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index e55a11ad..fa3044e3 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -76,7 +76,7 @@ class Emojis(commands.Cog): if emoji is not None: await ctx.invoke(self.info_command, emoji) else: - await invoke_help_command(ctx, ctx.command.name) + await invoke_help_command(ctx) @emoji_group.command(name="count", aliases=("c",)) async def count_command(self, ctx: commands.Context, *, category_query: str = None) -> None: diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index a6bfecaa..068d3f68 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -235,7 +235,7 @@ class Games(Cog): """ # When user didn't specified genre, send help message if genre is None: - await invoke_help_command(ctx, ctx.command.name) + await invoke_help_command(ctx) return # Capitalize genre for check diff --git a/bot/exts/evergreen/minesweeper.py b/bot/exts/evergreen/minesweeper.py index cfe7150e..3031debc 100644 --- a/bot/exts/evergreen/minesweeper.py +++ b/bot/exts/evergreen/minesweeper.py @@ -84,7 +84,7 @@ class Minesweeper(commands.Cog): @commands.group(name='minesweeper', aliases=('ms',), invoke_without_command=True) async def minesweeper_group(self, ctx: commands.Context) -> None: """Commands for Playing Minesweeper.""" - await invoke_help_command(ctx, ctx.command.name) + await invoke_help_command(ctx) @staticmethod def get_neighbours(x: int, y: int) -> typing.Generator[typing.Tuple[int, int], None, None]: diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index 63ce81da..b3bfe998 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -74,7 +74,7 @@ class Movie(Cog): try: result = await self.get_movies_list(self.http_session, MovieGenres[genre].value, 1) except KeyError: - await invoke_help_command(ctx, ctx.command.name) + await invoke_help_command(ctx) return # Check if "results" is in result. If not, throw error. diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index d0e60819..3732b559 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -441,7 +441,7 @@ class Snakes(Cog): @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, ctx.command.name) + await invoke_help_command(ctx) @bot_has_permissions(manage_messages=True) @snakes_group.command(name='antidote') diff --git a/bot/exts/evergreen/space.py b/bot/exts/evergreen/space.py index 305d6721..323ff659 100644 --- a/bot/exts/evergreen/space.py +++ b/bot/exts/evergreen/space.py @@ -64,7 +64,7 @@ class Space(Cog): @group(name="space", invoke_without_command=True) async def space(self, ctx: Context) -> None: """Head command that contains commands about space.""" - await invoke_help_command(ctx, ctx.command.name) + await invoke_help_command(ctx) @space.command(name="apod") async def apod(self, ctx: Context, date: Optional[str] = None) -> None: diff --git a/bot/exts/evergreen/status_codes.py b/bot/exts/evergreen/status_codes.py index 127f3424..7c00fe20 100644 --- a/bot/exts/evergreen/status_codes.py +++ b/bot/exts/evergreen/status_codes.py @@ -19,7 +19,7 @@ class HTTPStatusCodes(commands.Cog): async def http_status_group(self, ctx: commands.Context) -> None: """Group containing dog and cat http status code commands.""" if not ctx.invoked_subcommand: - await invoke_help_command(ctx, ctx.command.name) + await invoke_help_command(ctx) @http_status_group.command(name='cat') async def http_cat(self, ctx: commands.Context, code: int) -> None: diff --git a/bot/exts/utils/extensions.py b/bot/exts/utils/extensions.py index 4bd606b0..64e404d2 100644 --- a/bot/exts/utils/extensions.py +++ b/bot/exts/utils/extensions.py @@ -77,7 +77,7 @@ class Extensions(commands.Cog): @group(name="extensions", aliases=("ext", "exts", "c", "cogs"), invoke_without_command=True) async def extensions_group(self, ctx: Context) -> None: """Load, unload, reload, and list loaded extensions.""" - await invoke_help_command(ctx, ctx.command.name) + await invoke_help_command(ctx) @extensions_group.command(name="load", aliases=("l",)) async def load_command(self, ctx: Context, *extensions: Extension) -> None: @@ -87,7 +87,7 @@ class Extensions(commands.Cog): If '\*' or '\*\*' is given as the name, all unloaded extensions will be loaded. """ # noqa: W605 if not extensions: - await invoke_help_command(ctx, "extensions", ctx.command.name) + await invoke_help_command(ctx) return if "*" in extensions or "**" in extensions: @@ -104,7 +104,7 @@ class Extensions(commands.Cog): If '\*' or '\*\*' is given as the name, all loaded extensions will be unloaded. """ # noqa: W605 if not extensions: - await invoke_help_command(ctx, "extensions", ctx.command.name) + await invoke_help_command(ctx) return blacklisted = "\n".join(UNLOAD_BLACKLIST & set(extensions)) @@ -130,7 +130,7 @@ class Extensions(commands.Cog): If '\*\*' is given as the name, all extensions, including unloaded ones, will be reloaded. """ # noqa: W605 if not extensions: - await invoke_help_command(ctx, "extensions", ctx.command.name) + await invoke_help_command(ctx) return if "**" in extensions: diff --git a/bot/exts/valentines/be_my_valentine.py b/bot/exts/valentines/be_my_valentine.py index d5cc8644..09591cf8 100644 --- a/bot/exts/valentines/be_my_valentine.py +++ b/bot/exts/valentines/be_my_valentine.py @@ -44,7 +44,7 @@ class BeMyValentine(commands.Cog): 2) use the command \".lovefest unsub\" to get rid of the lovefest role. """ if not ctx.invoked_subcommand: - await invoke_help_command(ctx, ctx.command.name) + await invoke_help_command(ctx) @lovefest_role.command(name="sub") async def add_role(self, ctx: commands.Context) -> None: diff --git a/bot/utils/extensions.py b/bot/utils/extensions.py index ffa8a936..459588a1 100644 --- a/bot/utils/extensions.py +++ b/bot/utils/extensions.py @@ -33,12 +33,12 @@ def walk_extensions() -> Iterator[str]: yield module.name -async def invoke_help_command(ctx: Context, *commands: str) -> None: +async def invoke_help_command(ctx: Context) -> None: """Invoke the help command or default help command if help extensions is not loaded.""" if 'bot.exts.evergreen.help' in ctx.bot.extensions: help_command = ctx.bot.get_command('help') - await ctx.invoke(help_command, *commands) + await ctx.invoke(help_command, ctx.command.qualified_name) return - await ctx.send_help(''.join(commands)) + await ctx.send_help(ctx.command) EXTENSIONS = frozenset(walk_extensions()) -- cgit v1.2.3 From d8b30f2a1a99f1c301c4d1e675dc708b90d0e652 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 13 Mar 2021 15:54:15 +0000 Subject: Use new help command ext Since sir-lancebot#625 was merged, we now have bot.utils.extensions.invoke_help_command which sends a nicely formatted help embed. --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index aa0f0404..b13ba574 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -12,6 +12,7 @@ from discord.ext.commands.errors import BadArgument from bot.constants import Colours from bot.exts.evergreen.profile_pic_modification._effects import PfpEffects +from bot.utils.extensions import invoke_help_command from bot.utils.halloween import spookifications log = logging.getLogger(__name__) @@ -44,7 +45,7 @@ class PfpModify(commands.Cog): async def pfp_modify(self, ctx: commands.Context) -> None: """Groups all of the pfp modifying commands to allow a single concurrency limit.""" if not ctx.invoked_subcommand: - await ctx.send_help(ctx.command) + await invoke_help_command(ctx) @pfp_modify.command(name="8bitify", root_aliases=("8bitify",)) async def eightbit_command(self, ctx: commands.Context) -> None: -- cgit v1.2.3 From 87219f3a0c299d62a95341a90237715f1cff64d8 Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Sat, 13 Mar 2021 20:13:12 +0000 Subject: master => main --- .github/workflows/build.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/sentry_release.yaml | 4 ++-- CONTRIBUTING.md | 8 ++++---- README.md | 10 +++++----- bot/exts/christmas/advent_of_code/_helpers.py | 2 +- bot/exts/evergreen/source.py | 2 +- bot/resources/halloween/spooky_rating.json | 18 +++++++++--------- bot/utils/time.py | 2 +- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 9d12cd10..08721dfd 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -4,7 +4,7 @@ on: workflow_run: workflows: ["Lint"] branches: - - master + - main types: - completed diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index a5f45255..7f157da3 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -3,7 +3,7 @@ name: Lint on: push: branches: - - master + - main pull_request: diff --git a/.github/workflows/sentry_release.yaml b/.github/workflows/sentry_release.yaml index 0e02dd0c..3d15e01e 100644 --- a/.github/workflows/sentry_release.yaml +++ b/.github/workflows/sentry_release.yaml @@ -3,14 +3,14 @@ name: Create Sentry release on: push: branches: - - master + - main jobs: create_sentry_release: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@master + uses: actions/checkout@main - name: Create a Sentry.io release uses: tclindner/sentry-releases-action@v1.2.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7cf83db5..3a1803e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ Sir Lancebot is a community project for the Python Discord community over at https://discord.gg/python. We will be providing support for those of you who are new to Git, and this project is to be considered educational. -Our projects are open-source and are automatically deployed whenever commits are pushed to the `master` branch on each repository, so we've created a set of guidelines in order to keep everything clean and in working order. +Our projects are open-source and are automatically deployed whenever commits are pushed to the `main` branch on each repository, so we've created a set of guidelines in order to keep everything clean and in working order. Note that contributions may be rejected on the basis of a contributor failing to follow these guidelines. @@ -12,7 +12,7 @@ Note that contributions may be rejected on the basis of a contributor failing to 2. Your pull request must solve an issue created or approved by a staff member. These will be labeled with the `approved` label. Feel free to suggest issues of your own, which staff can review for approval. 3. **No force-pushes** or modifying the Git history in any way. 4. If you have direct access to the repository, **create a branch for your changes** and create a pull request for that branch. If not, create a branch on a fork of the repository and create a pull request from there. - * It's common practice for a repository to reject direct pushes to `master`, so make branching a habit! + * It's common practice for a repository to reject direct pushes to `main`, so make branching a habit! * If PRing from your own fork, **ensure that "Allow edits from maintainers" is checked**. This gives permission for maintainers to commit changes directly to your fork, speeding up the review process. 5. **Adhere to the prevailing code style**, which we enforce using [`flake8`](http://flake8.pycqa.org/en/latest/index.html) and [`pre-commit`](https://pre-commit.com/). * Run `flake8` and `pre-commit` against your code [**before** you push it](https://soundcloud.com/lemonsaurusrex/lint-before-you-push). Your commit will be rejected by the build server if it fails to lint. @@ -22,7 +22,7 @@ Note that contributions may be rejected on the basis of a contributor failing to * Avoid making minor commits for fixing typos or linting errors. Since you've already set up a `pre-commit` hook to run the linting pipeline before a commit, you shouldn't be committing linting issues anyway. * A more in-depth guide to writing great commit messages can be found in Chris Beam's [*How to Write a Git Commit Message*](https://chris.beams.io/posts/git-commit/) 7. **Avoid frequent pushes to the main repository**. This goes for PRs opened against your fork as well. Our test build pipelines are triggered every time a push to the repository (or PR) is made. Try to batch your commits until you've finished working for that session, or you've reached a point where collaborators need your commits to continue their own work. This also provides you the opportunity to amend commits for minor changes rather than having to commit them on their own because you've already pushed. - * This includes merging master into your branch. Try to leave merging from master for after your PR passes review; a maintainer will bring your PR up to date before merging. Exceptions to this include: resolving merge conflicts, needing something that was pushed to master for your branch, or something was pushed to master that could potentionally affect the functionality of what you're writing. + * This includes merging main into your branch. Try to leave merging from main for after your PR passes review; a maintainer will bring your PR up to date before merging. Exceptions to this include: resolving merge conflicts, needing something that was pushed to main for your branch, or something was pushed to main that could potentionally affect the functionality of what you're writing. 8. **Don't fight the framework**. Every framework has its flaws, but the frameworks we've picked out have been carefully chosen for their particular merits. If you can avoid it, please resist reimplementing swathes of framework logic - the work has already been done for you! 9. If someone is working on an issue or pull request, **do not open your own pull request for the same task**. Instead, collaborate with the author(s) of the existing pull request. Duplicate PRs opened without communicating with the other author(s) and/or PyDis staff will be closed. Communication is key, and there's no point in two separate implementations of the same thing. * One option is to fork the other contributor's repository and submit your changes to their branch with your own pull request. We suggest following these guidelines when interacting with their repository as well. @@ -39,7 +39,7 @@ All projects evolve over time, and this contribution guide is no different. This ## Supplemental Information ### Developer Environment -Sir Lancebot utilizes [Pipenv](https://pipenv.readthedocs.io/en/latest/) for installation and dependency management. For users unfamiliar with the Pipenv workflow, Pipenv's documentation provides a [Basic Usage](https://pipenv.readthedocs.io/en/latest/basics/) tutorial, along with some of the more advanced workflows. A project-specific installation guide can be found in [Sir Lancebot's README](https://github.com/python-discord/sir-lancebot/blob/master/README.md). +Sir Lancebot utilizes [Pipenv](https://pipenv.readthedocs.io/en/latest/) for installation and dependency management. For users unfamiliar with the Pipenv workflow, Pipenv's documentation provides a [Basic Usage](https://pipenv.readthedocs.io/en/latest/basics/) tutorial, along with some of the more advanced workflows. A project-specific installation guide can be found in [Sir Lancebot's README](https://github.com/python-discord/sir-lancebot/blob/main/README.md). When pulling down changes from GitHub, remember to sync your environment using `pipenv sync --dev` to ensure you're using the most up-to-date versions the project's dependencies. diff --git a/README.md b/README.md index 6b453879..11b46aac 100755 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ Before you start, please take some time to read through our [contributing guidel See [Sir Lancebot's Wiki](https://pythondiscord.com/pages/contributing/sir-lancebot/) for in-depth guides on getting started with the project! -[1]:https://github.com/python-discord/sir-lancebot/workflows/Lint/badge.svg?branch=master -[2]:https://github.com/python-discord/sir-lancebot/actions?query=workflow%3ALint+branch%3Amaster -[3]:https://github.com/python-discord/sir-lancebot/workflows/Build/badge.svg?branch=master -[4]:https://github.com/python-discord/sir-lancebot/actions?query=workflow%3ABuild+branch%3Amaster -[5]: https://raw.githubusercontent.com/python-discord/branding/master/logos/badge/badge_github.svg +[1]:https://github.com/python-discord/sir-lancebot/workflows/Lint/badge.svg?branch=main +[2]:https://github.com/python-discord/sir-lancebot/actions?query=workflow%3ALint+branch%3Amain +[3]:https://github.com/python-discord/sir-lancebot/workflows/Build/badge.svg?branch=main +[4]:https://github.com/python-discord/sir-lancebot/actions?query=workflow%3ABuild+branch%3Amain +[5]: https://raw.githubusercontent.com/python-discord/branding/main/logos/badge/badge_github.svg [6]: https://discord.gg/python diff --git a/bot/exts/christmas/advent_of_code/_helpers.py b/bot/exts/christmas/advent_of_code/_helpers.py index b7adc895..a16a4871 100644 --- a/bot/exts/christmas/advent_of_code/_helpers.py +++ b/bot/exts/christmas/advent_of_code/_helpers.py @@ -44,7 +44,7 @@ REQUIRED_CACHE_KEYS = ( AOC_EMBED_THUMBNAIL = ( "https://raw.githubusercontent.com/python-discord" - "/branding/master/seasonal/christmas/server_icons/festive_256.gif" + "/branding/main/seasonal/christmas/server_icons/festive_256.gif" ) # Create an easy constant for the EST timezone diff --git a/bot/exts/evergreen/source.py b/bot/exts/evergreen/source.py index cdfe54ec..45752bf9 100644 --- a/bot/exts/evergreen/source.py +++ b/bot/exts/evergreen/source.py @@ -76,7 +76,7 @@ class BotSource(commands.Cog): file_location = Path(filename).relative_to(Path.cwd()).as_posix() - url = f"{Source.github}/blob/master/{file_location}{lines_extension}" + url = f"{Source.github}/blob/main/{file_location}{lines_extension}" return url, file_location, first_line_no or None diff --git a/bot/resources/halloween/spooky_rating.json b/bot/resources/halloween/spooky_rating.json index 533e7107..8e3e66bb 100644 --- a/bot/resources/halloween/spooky_rating.json +++ b/bot/resources/halloween/spooky_rating.json @@ -2,46 +2,46 @@ "-1": { "title": "\uD83D\uDD6F You're not scarin' anyone \uD83D\uDD6F", "text": "No matter what you say or do, nobody even flinches when you try to scare them. Was your costume this year only a white sheet with holes for eyes? Or did you even bother with a costume at all? Either way, don't expect too many treats when going from door-to-door.", - "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/master/bot/resources/halloween/spookyrating/candle.jpeg" + "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/main/bot/resources/halloween/spookyrating/candle.jpeg" }, "5": { "title": "\uD83D\uDC76 Like taking candy from a baby \uD83D\uDC76", "text": "Your scaring will probably make a baby cry... but that's the limit on your frightening powers. Be careful not to get to the point where everyone's running away from you because they don't like you, not because they're scared of you.", - "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/master/bot/resources/halloween/spookyrating/baby.jpeg" + "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/main/bot/resources/halloween/spookyrating/baby.jpeg" }, "20": { "title": "\uD83C\uDFDA You're skills are forming... \uD83C\uDFDA", "text": "As you become the Devil's apprentice, you begin to make people jump every time you sneak up on them. A good start, but you have to learn not to wear the same costume every year until it doesn't fit you. People will notice you and your prowess will decrease.", - "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/master/bot/resources/halloween/spookyrating/tiger.jpeg" + "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/main/bot/resources/halloween/spookyrating/tiger.jpeg" }, "30": { "title": "\uD83D\uDC80 Picture Perfect... \uD83D\uDC80", "text": "You've nailed the costume this year! You look suuuper scary! Now make sure to play the part and act out your costume and you'll be sure to give a few people a massive fright!", - "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/master/bot/resources/halloween/spookyrating/costume.jpeg" + "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/main/bot/resources/halloween/spookyrating/costume.jpeg" }, "50": { "title": "\uD83D\uDC7B Uhm... are you human \uD83D\uDC7B", "text": "Uhm... you're too good to be human and now you're beginning to sound like a ghost. You're almost invisible when haunting and nobody truly knows where you are at any given time. But they will always scream at the sound of a ghost...", - "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/master/bot/resources/halloween/spookyrating/ghost.jpeg" + "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/main/bot/resources/halloween/spookyrating/ghost.jpeg" }, "65": { "title": "\uD83C\uDF83 That potion can't be real \uD83C\uDF83", "text": "You're carrying... some... unknown liquids and no one knows who they are but yourself. Be careful on who you use these powerful spells on, because no Mage has the power to do any irreversible enchantments because even you won't know what will happen to these mortals.", - "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/master/bot/resources/halloween/spookyrating/necromancer.jepg" + "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/main/bot/resources/halloween/spookyrating/necromancer.jepg" }, "80": { "title": "\uD83E\uDD21 The most sinister face \uD83E\uDD21", "text": "Who knew something intended to be playful could be so menacing... Especially other people seeing you in their nightmares, continuing to haunt them day by day, stuck in their head throughout the entire year. Make sure to pull a face they will never forget.", - "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/master/bot/resources/halloween/spookyrating/clown.jpeg" + "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/main/bot/resources/halloween/spookyrating/clown.jpeg" }, "95": { "title": "\uD83D\uDE08 The Devil's Accomplice \uD83D\uDE08", "text": "Imagine being allies with the most evil character with an aim to scare people to death. Force people to suffer as they proceed straight to hell to meet your boss and best friend. Not even you know the power He has...", - "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/master/bot/resources/halloween/spookyrating/jackolantern.jpg" + "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/main/bot/resources/halloween/spookyrating/jackolantern.jpg" }, "100": { "title":"\uD83D\uDC7F The Devil Himself \uD83D\uDC7F", "text": "You are the evillest creature in existence to scare anyone and everyone humanly possible. The reason your underlings are called mortals is that they die. With your help, they die a lot quicker. With all the evil power in the universe, you know what to do.", - "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/master/bot/resources/halloween/spookyrating/devil.jpeg" + "image": "https://raw.githubusercontent.com/python-discord/sir-lancebot/main/bot/resources/halloween/spookyrating/devil.jpeg" } } diff --git a/bot/utils/time.py b/bot/utils/time.py index 3c57e126..fbf2fd21 100644 --- a/bot/utils/time.py +++ b/bot/utils/time.py @@ -3,7 +3,7 @@ import datetime from dateutil.relativedelta import relativedelta -# All these functions are from https://github.com/python-discord/bot/blob/master/bot/utils/time.py +# All these functions are from https://github.com/python-discord/bot/blob/main/bot/utils/time.py def _stringify_time_unit(value: int, unit: str) -> str: """ Returns a string to represent a value and time unit, ensuring that it uses the right plural form of the unit. -- cgit v1.2.3 From 1c94d02b6d08c4b1df6e714d3571312b1376171f Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Sat, 13 Mar 2021 20:15:42 +0000 Subject: Dockerfile optimisations --- Dockerfile | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 328984ad..0db0b0ef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,10 @@ FROM python:3.8-slim -# Set SHA build argument -ARG git_sha="development" - # Set pip to have cleaner logs and no saved cache ENV PIP_NO_CACHE_DIR=false \ PIPENV_HIDE_EMOJIS=1 \ PIPENV_IGNORE_VIRTUALENVS=1 \ - PIPENV_NOSPIN=1 \ - GIT_SHA=$git_sha + PIPENV_NOSPIN=1 # Install git to be able to dowload git dependencies in the Pipfile RUN apt-get -y update \ @@ -21,11 +17,20 @@ RUN pip install -U pipenv # Copy the project files into working directory WORKDIR /bot -COPY . . + +# Copy dependency files +COPY Pipfile Pipfile.lock ./ # Install project dependencies RUN pipenv install --deploy --system +# Copy project code +COPY . . + +# Set Git SHA enviroment variable +ARG git_sha="development" +ENV GIT_SHA=$git_sha + ENTRYPOINT ["python"] CMD ["-m", "bot"] -- cgit v1.2.3 From 0e074b798dd8b47c86043f85b8fac71b76cb8d1b Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Sat, 13 Mar 2021 23:26:24 +0300 Subject: Removes Unused Roles & Channels Removes roles and channels from constants.py that are not used anywhere in the project. Signed-off-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> --- bot/constants.py | 25 ------------------------- bot/exts/christmas/advent_of_code/_cog.py | 2 +- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index b8e30a7c..416dd0e7 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -94,33 +94,18 @@ class Branding: class Channels(NamedTuple): - admins = 365960823622991872 advent_of_code = int(environ.get("AOC_CHANNEL_ID", 782715290437943306)) advent_of_code_commands = int(environ.get("AOC_COMMANDS_CHANNEL_ID", 607247579608121354)) - announcements = int(environ.get("CHANNEL_ANNOUNCEMENTS", 354619224620138496)) - big_brother_logs = 468507907357409333 bot = 267659945086812160 - checkpoint_test = 422077681434099723 organisation = 551789653284356126 - devalerts = 460181980097675264 devlog = int(environ.get("CHANNEL_DEVLOG", 622895325144940554)) dev_contrib = 635950537262759947 - dev_branding = 753252897059373066 - helpers = 385474242440986624 - message_log = 467752170159079424 - mod_alerts = 473092532147060736 - modlog = 282638479504965634 mod_meta = 775412552795947058 mod_tools = 775413915391098921 off_topic_0 = 291284109232308226 off_topic_1 = 463035241142026251 off_topic_2 = 463035268514185226 - python = 267624335836053506 - reddit = 458224812528238616 community_bot_commands = int(environ.get("CHANNEL_COMMUNITY_BOT_COMMANDS", 607247579608121354)) - staff_lounge = 464905259261755392 - verification = 352442727016693763 - python_discussion = 267624335836053506 hacktoberfest_2020 = 760857070781071431 voice_chat_0 = 412357430186344448 voice_chat_1 = 799647045886541885 @@ -248,20 +233,10 @@ if Client.month_override is not None: class Roles(NamedTuple): admin = int(environ.get("BOT_ADMIN_ROLE_ID", 267628507062992896)) - announcements = 463658397560995840 - champion = 430492892331769857 - contributor = 295488872404484098 - devops = 409416496733880320 - jammer = 423054537079783434 moderator = 267629731250176001 - muted = 277914926603829249 owner = 267627879762755584 - verified = 352427296948486144 helpers = int(environ.get("ROLE_HELPERS", 267630620367257601)) - rockstars = 458226413825294336 core_developers = 587606783669829632 - events_lead = 778361735739998228 - everyone_role = 267624335836053506 class Tokens(NamedTuple): diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index dc3d7616..8376987d 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -244,7 +244,7 @@ class AdventOfCode(commands.Cog): info_embed = _helpers.get_summary_embed(leaderboard) await ctx.send(f"```\n{table}\n```", embed=info_embed) - @with_role(Roles.admin, Roles.events_lead) + @with_role(Roles.admin) @adventofcode_group.command( name="refresh", aliases=("fetch",), -- cgit v1.2.3 From 11f12a4b1cb8da70d6e454ff359a529bd097ba90 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sat, 13 Mar 2021 14:39:28 -0800 Subject: Use .gitattributes to normalise line endings on check-in Remove the mixed line endings pre-commit hook because it is obsolete. Relying on git to handle line endings means contributors have more flexibility with which line endings they want to use on check-out. The settings in .gitattributes only impose which line endings will be used upon check-in (LF), which should not impact local development; git will still respect the core.eol and core.autocrlf settings. --- .gitattributes | 2 ++ .pre-commit-config.yaml | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..3f3c4fd6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto +*.png binary diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a66bf97c..6c94375c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,8 +6,6 @@ repos: - id: check-toml - id: check-yaml - id: end-of-file-fixer - - id: mixed-line-ending - args: [--fix=lf] - id: trailing-whitespace args: [--markdown-linebreak-ext=md] - repo: https://github.com/pre-commit/pygrep-hooks -- cgit v1.2.3 From 1c6bcfdb01ad4ab4d31047be05207c8a27cdfbd0 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sun, 14 Mar 2021 13:36:37 +0530 Subject: Apply requested grammar changes. --- bot/exts/pride/pride_leader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index 9a2ee6e1..d955a2cc 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -45,7 +45,7 @@ class PrideLeader(commands.Cog): if not valid_names: valid_names = ", ".join(self.pride) - error_msg = "Sorry your input didn't match any stored name, here is a list of available names:" + error_msg = "Sorry your input didn't match any stored names, here is a list of available names:" else: valid_names = "\n".join(valid_names) error_msg = "Did you mean?" @@ -113,5 +113,5 @@ class PrideLeader(commands.Cog): def setup(bot: commands.Bot) -> None: - """Loads Pride leader cog.""" + """Loads the Pride leader cog.""" bot.add_cog(PrideLeader(bot)) -- cgit v1.2.3 From 8c09ba1db566a6c1051479e353ad025392e7e5bb Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 14 Mar 2021 13:34:27 +0000 Subject: Remove aiofiles dep --- Pipfile | 1 - Pipfile.lock | 28 ++++++++-------------- .../profile_pic_modification/pfp_modify.py | 16 +++++-------- 3 files changed, 16 insertions(+), 29 deletions(-) diff --git a/Pipfile b/Pipfile index 79a5dfc8..e7e01a31 100644 --- a/Pipfile +++ b/Pipfile @@ -15,7 +15,6 @@ PyYAML = "~=5.3.1" "discord.py" = {extras = ["voice"], version = "~=1.5.1"} async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} emojis = "~=0.6.0" -aiofiles = "~=0.6" [dev-packages] flake8 = "~=3.8" diff --git a/Pipfile.lock b/Pipfile.lock index 9d0a988e..19bcb719 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "91281e9ed353fea748de3da19abd7bef402402b23fc78a1260dc8bf8bd2bd98c" + "sha256": "b4aaaacbab13179145e36d7b86c736db512286f6cce8e513cc30c48d68fe3810" }, "pipfile-spec": 6, "requires": { @@ -24,14 +24,6 @@ "index": "pypi", "version": "==2.0.0" }, - "aiofiles": { - "hashes": [ - "sha256:bd3019af67f83b739f8e4053c6c0512a7f545b9a8d91aaeab55e6e0f9d123c27", - "sha256:e0281b157d3d5d59d803e3f4557dcc9a3dff28a4dd4829a9ff478adae50ca092" - ], - "index": "pypi", - "version": "==0.6.0" - }, "aiohttp": { "hashes": [ "sha256:1a4160579ffbc1b69e88cb6ca8bb0fbd4947dfcbf9fb1e2a4fc4c7a4a986c1fe", @@ -531,11 +523,11 @@ }, "flake8-annotations": { "hashes": [ - "sha256:3a377140556aecf11fa9f3bb18c10db01f5ea56dc79a730e2ec9b4f1f49e2055", - "sha256:e17947a48a5b9f632fe0c72682fc797c385e451048e7dfb20139f448a074cb3e" + "sha256:8968ff12f296433028ad561c680ccc03a7cd62576d100c3f1475e058b3c11b43", + "sha256:bd0505616c0d85ebb45c6052d339c69f320d3f87fa079ab4e91a4f234a863d05" ], "index": "pypi", - "version": "==2.5.0" + "version": "==2.6.0" }, "flake8-bugbear": { "hashes": [ @@ -593,11 +585,11 @@ }, "identify": { "hashes": [ - "sha256:2179e7359471ab55729f201b3fdf7dc2778e221f868410fedcb0987b791ba552", - "sha256:2a5fdf2f5319cc357eda2550bea713a404392495961022cf2462624ce62f0f46" + "sha256:e3b7fd755b7ceee44fe22957005a92c2a085c863c2e65a6efdec35d0e06666db", + "sha256:fab0d3a3ab0d7d5f513985b0335ccccad9d61420c5216fb779237bf7edc3e5d1" ], "markers": "python_full_version >= '3.6.1'", - "version": "==2.1.0" + "version": "==2.1.2" }, "mccabe": { "hashes": [ @@ -623,11 +615,11 @@ }, "pre-commit": { "hashes": [ - "sha256:16212d1fde2bed88159287da88ff03796863854b04dc9f838a55979325a3d20e", - "sha256:399baf78f13f4de82a29b649afd74bef2c4e28eb4f021661fc7f29246e8c7a3a" + "sha256:94c82f1bf5899d56edb1d926732f4e75a7df29a0c8c092559c77420c9d62428b", + "sha256:de55c5c72ce80d79106e48beb1b54104d16495ce7f95b0c7b13d4784193a00af" ], "index": "pypi", - "version": "==2.10.1" + "version": "==2.11.1" }, "pycodestyle": { "hashes": [ diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index b13ba574..d41fcaad 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -4,7 +4,6 @@ import logging import typing as t from concurrent.futures import ThreadPoolExecutor -import aiofiles import discord from aiohttp import client_exceptions from discord.ext import commands @@ -21,6 +20,9 @@ _EXECUTOR = ThreadPoolExecutor(10) FILENAME_STRING = "{effect}_{author}.png" +with open('bot/resources/pride/gender_options.json') as f: + GENDER_OPTIONS = json.loads(f.read()) + async def in_executor(func: t.Callable, *args) -> t.Any: """Allows non-async functions to work in async functions.""" @@ -34,12 +36,6 @@ class PfpModify(commands.Cog): def __init__(self, bot: commands.Bot) -> None: self.bot = bot - self.bot.loop.create_task(self.init_cog()) - - async def init_cog(self) -> None: - """Initial load from resources asynchronously.""" - async with aiofiles.open('bot/resources/pride/gender_options.json') as f: - self.GENDER_OPTIONS = json.loads(await f.read()) @commands.group() async def pfp_modify(self, ctx: commands.Context) -> None: @@ -176,7 +172,7 @@ class PfpModify(commands.Cog): """ option = option.lower() pixels = max(0, min(512, pixels)) - flag = self.GENDER_OPTIONS.get(option) + flag = GENDER_OPTIONS.get(option) if flag is None: await ctx.send("I don't have that flag!") return @@ -197,7 +193,7 @@ class PfpModify(commands.Cog): """ option = option.lower() pixels = max(0, min(512, pixels)) - flag = self.GENDER_OPTIONS.get(option) + flag = GENDER_OPTIONS.get(option) if flag is None: await ctx.send("I don't have that flag!") return @@ -221,7 +217,7 @@ class PfpModify(commands.Cog): @prideavatar.command() async def flags(self, ctx: commands.Context) -> None: """This lists the flags that can be used with the prideavatar command.""" - choices = sorted(set(self.GENDER_OPTIONS.values())) + choices = sorted(set(GENDER_OPTIONS.values())) options = "• " + "\n• ".join(choices) embed = discord.Embed( title="I have the following flags:", -- cgit v1.2.3 From 4dbd106f3febeadcde162bcafa163ae4d07f1b12 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 14 Mar 2021 13:38:47 +0000 Subject: Remove unnecessary lambda in when getting closest colour --- bot/exts/evergreen/profile_pic_modification/_effects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py index ae606dcb..2ff92000 100644 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -42,7 +42,7 @@ class PfpEffects: r2, g2, b2 = point return (r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2 - closest_colours = sorted(Colours.easter_like_colours, key=lambda point: distance(point)) + closest_colours = sorted(Colours.easter_like_colours, key=distance) r2, g2, b2 = closest_colours[0] r = (r1 + r2) // 2 g = (g1 + g2) // 2 -- cgit v1.2.3 From 3eb23d5307bbf4abc03522e3ba9903e42131bb27 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 14 Mar 2021 13:39:26 +0000 Subject: Pull the function signature back onto one line for readibility --- bot/exts/evergreen/profile_pic_modification/_effects.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py index 2ff92000..9319a1b8 100644 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -70,11 +70,7 @@ class PfpEffects: return ring @staticmethod - def pridify_effect( - image: Image, - pixels: int, - flag: str - ) -> Image: + def pridify_effect(image: Image, pixels: int, flag: str) -> Image: """Applies the pride effect to the given image.""" image = image.resize((1024, 1024)) image = PfpEffects.crop_avatar_circle(image) -- cgit v1.2.3 From 63ec14c47ef34fe48a32ecf6b51718c277714b88 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 14 Mar 2021 13:50:37 +0000 Subject: Imrpoves docstrings within the profile pic modification functions --- bot/exts/evergreen/profile_pic_modification/_effects.py | 17 ++++++++++++----- .../evergreen/profile_pic_modification/pfp_modify.py | 9 +++++++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py index 9319a1b8..a1069db7 100644 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -10,9 +10,9 @@ from bot.constants import Colours class PfpEffects: """ - Implements various image effects. + Implements various image modifying effects, for the PfpModify cog. - All of these methods are blocking, so should be ran in threads. + All of these fuctions are slow, and blocking, so should be ran in executors. """ @staticmethod @@ -31,7 +31,7 @@ class PfpEffects: @staticmethod def closest(x: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: """ - Finds the closest easter colour to a given pixel. + Finds the closest "easter" colour to a given pixel. Returns a merge between the original colour and the closest colour. """ @@ -71,7 +71,7 @@ class PfpEffects: @staticmethod def pridify_effect(image: Image, pixels: int, flag: str) -> Image: - """Applies the pride effect to the given image.""" + """Applies the given pride effect to the given image.""" image = image.resize((1024, 1024)) image = PfpEffects.crop_avatar_circle(image) @@ -96,7 +96,14 @@ class PfpEffects: @staticmethod def easterify_effect(image: Image, overlay_image: Image = None) -> Image: - """Applies the easter effect to the given image.""" + """ + Applies the easter effect to the given image. + + This is done by getting the closest "easter" colour to each pixel and changing the colour + to the half-way RGBvalue. + + We also then add an overlay image on top in middle right, a chocolate bunny by default. + """ if overlay_image: ratio = 64 / overlay_image.height overlay_image = overlay_image.resize(( diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index d41fcaad..19f0bdf0 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -25,7 +25,12 @@ with open('bot/resources/pride/gender_options.json') as f: async def in_executor(func: t.Callable, *args) -> t.Any: - """Allows non-async functions to work in async functions.""" + """ + Runs the given synchronus function `func` in an executor. + + This is useful for running slow, blocking code within async + functions, so that they don't block the bot. + """ log.trace(f"Running {func.__name__} in an executor.") loop = asyncio.get_event_loop() return await loop.run_in_executor(_EXECUTOR, func, *args) @@ -233,7 +238,7 @@ class PfpModify(commands.Cog): brief='Spookify an user\'s avatar.' ) async def spooky_avatar(self, ctx: commands.Context, user: discord.Member = None) -> None: - """A command to print the user's spookified avatar.""" + """This "spookifies" the given user's avatar, with a random *spooky* effect.""" if user is None: user = ctx.message.author -- cgit v1.2.3 From c18039246b6a40dc78fee4bcae24d22dab0c79b9 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 14 Mar 2021 16:31:02 +0000 Subject: revert pip lock to origin/main --- Pipfile.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 19bcb719..ec801979 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -523,11 +523,11 @@ }, "flake8-annotations": { "hashes": [ - "sha256:8968ff12f296433028ad561c680ccc03a7cd62576d100c3f1475e058b3c11b43", - "sha256:bd0505616c0d85ebb45c6052d339c69f320d3f87fa079ab4e91a4f234a863d05" + "sha256:3a377140556aecf11fa9f3bb18c10db01f5ea56dc79a730e2ec9b4f1f49e2055", + "sha256:e17947a48a5b9f632fe0c72682fc797c385e451048e7dfb20139f448a074cb3e" ], "index": "pypi", - "version": "==2.6.0" + "version": "==2.5.0" }, "flake8-bugbear": { "hashes": [ @@ -585,11 +585,11 @@ }, "identify": { "hashes": [ - "sha256:e3b7fd755b7ceee44fe22957005a92c2a085c863c2e65a6efdec35d0e06666db", - "sha256:fab0d3a3ab0d7d5f513985b0335ccccad9d61420c5216fb779237bf7edc3e5d1" + "sha256:de7129142a5c86d75a52b96f394d94d96d497881d2aaf8eafe320cdbe8ac4bcc", + "sha256:e0dae57c0397629ce13c289f6ddde0204edf518f557bfdb1e56474aa143e77c3" ], - "markers": "python_full_version >= '3.6.1'", - "version": "==2.1.2" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.5.14" }, "mccabe": { "hashes": [ @@ -615,11 +615,11 @@ }, "pre-commit": { "hashes": [ - "sha256:94c82f1bf5899d56edb1d926732f4e75a7df29a0c8c092559c77420c9d62428b", - "sha256:de55c5c72ce80d79106e48beb1b54104d16495ce7f95b0c7b13d4784193a00af" + "sha256:16212d1fde2bed88159287da88ff03796863854b04dc9f838a55979325a3d20e", + "sha256:399baf78f13f4de82a29b649afd74bef2c4e28eb4f021661fc7f29246e8c7a3a" ], "index": "pypi", - "version": "==2.11.1" + "version": "==2.10.1" }, "pycodestyle": { "hashes": [ -- cgit v1.2.3 From c1df836a35ac942136a6fce7448bb5da03b7369b Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Sun, 14 Mar 2021 22:27:04 +0000 Subject: Separate deploy stage Separate the build and deploy stage and set the environment value for the deployment stage. --- .github/workflows/build.yaml | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 08721dfd..0a006eb9 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -11,7 +11,7 @@ on: jobs: build: if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' - name: Build, Push, & Deploy Container + name: Build & Push runs-on: ubuntu-latest steps: @@ -28,15 +28,6 @@ jobs: with: path: sir-lancebot - # Check out the private "kubernetes" repository in the `kubernetes` - # subdirectory using a GitHub Personal Access Token - - name: Checkout code - uses: actions/checkout@v2 - with: - repository: python-discord/kubernetes - token: ${{ secrets.REPO_TOKEN }} - path: kubernetes - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 @@ -64,6 +55,29 @@ jobs: build-args: | git_sha=${{ github.sha }} + deploy: + needs: build + name: Deploy + runs-on: ubuntu-latest + environment: production + + steps: + # Create a commit SHA-based tag for the container repositories + - name: Create SHA Container Tag + id: sha_tag + run: | + tag=$(cut -c 1-7 <<< $GITHUB_SHA) + echo "::set-output name=tag::$tag" + + # Check out the private "kubernetes" repository in the `kubernetes` + # subdirectory using a GitHub Personal Access Token + - name: Checkout code + uses: actions/checkout@v2 + with: + repository: python-discord/kubernetes + token: ${{ secrets.REPO_TOKEN }} + path: kubernetes + - name: Authenticate with Kubernetes uses: azure/k8s-set-context@v1 with: -- cgit v1.2.3 From 521aeb14632db8b3e848cad632059d053219aecd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Mar 2021 21:35:02 +0000 Subject: Bump pillow from 7.2.0 to 8.1.1 Bumps [pillow](https://github.com/python-pillow/Pillow) from 7.2.0 to 8.1.1. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst) - [Commits](https://github.com/python-pillow/Pillow/compare/7.2.0...8.1.1) Signed-off-by: dependabot[bot] --- Pipfile | 2 +- Pipfile.lock | 104 +++++++++++++++++++++++++---------------------------------- 2 files changed, 45 insertions(+), 61 deletions(-) diff --git a/Pipfile b/Pipfile index e7e01a31..2e922ec4 100644 --- a/Pipfile +++ b/Pipfile @@ -8,7 +8,7 @@ aiodns = "~=2.0" arrow = "~=0.14" beautifulsoup4 = "~=4.8" fuzzywuzzy = "~=0.17" -pillow = "~=7.2" +pillow = "~=8.1" pytz = "~=2019.2" sentry-sdk = "~=0.19" PyYAML = "~=5.3.1" diff --git a/Pipfile.lock b/Pipfile.lock index ec801979..d09588cf 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "b4aaaacbab13179145e36d7b86c736db512286f6cce8e513cc30c48d68fe3810" + "sha256": "cf7535796e0d4f4dd6a414d8db4781caaab4a624380d29abea17577209043742" }, "pipfile-spec": 6, "requires": { @@ -40,7 +40,6 @@ "sha256:c506853ba52e516b264b106321c424d03f3ddef2813246432fa9d1cefd361c81", "sha256:fb83326d8295e8840e4ba774edf346e87eca78ba8a89c55d2690352842c15ba5" ], - "markers": "python_full_version >= '3.5.3'", "version": "==3.6.3" }, "aioredis": { @@ -74,7 +73,6 @@ "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" ], - "markers": "python_full_version >= '3.5.3'", "version": "==3.0.1" }, "attrs": { @@ -82,7 +80,6 @@ "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==20.3.0" }, "beautifulsoup4": { @@ -233,7 +230,6 @@ "sha256:e64be68255234bb489a574c4f2f8df7029c98c81ec4d160d6cd836e7f0679390", "sha256:e82d6b930e02e80e5109b678c663a9ed210680ded81c1abaf54635d88d1da298" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.1.0" }, "idna": { @@ -241,7 +237,6 @@ "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16", "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1" ], - "markers": "python_version >= '3.4'", "version": "==3.1" }, "multidict": { @@ -264,42 +259,46 @@ "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255", "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d" ], - "markers": "python_version >= '3.5'", "version": "==4.7.6" }, "pillow": { "hashes": [ - "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", - "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", - "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", - "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", - "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", - "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", - "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", - "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", - "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", - "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", - "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", - "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", - "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", - "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", - "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", - "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", - "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", - "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", - "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", - "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", - "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", - "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", - "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", - "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117", - "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", - "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", - "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", - "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce" + "sha256:01bb0a34f1a6689b138c0089d670ae2e8f886d2666a9b2f2019031abdea673c4", + "sha256:07872f1d8421db5a3fe770f7480835e5e90fddb58f36c216d4a2ac0d594de474", + "sha256:1022f8f6dc3c5b0dcf928f1c49ba2ac73051f576af100d57776e2b65c1f76a8d", + "sha256:14415e9e28410232370615dbde0cf0a00e526f522f665460344a5b96973a3086", + "sha256:172acfaf00434a28dddfe592d83f2980e22e63c769ff4a448ddf7b7a38ffd165", + "sha256:1c5e3c36f02c815766ae9dd91899b1c5b4652f2a37b7a51609f3bd467c0f11fb", + "sha256:292f2aa1ae5c5c1451cb4b558addb88c257411d3fd71c6cf45562911baffc979", + "sha256:2a40d7d4b17db87f5b9a1efc0aff56000e1d0d5ece415090c102aafa0ccbe858", + "sha256:2f0d7034d5faae9a8d1019d152ede924f653df2ce77d3bba4ce62cd21b5f94ae", + "sha256:33fdbd4f5608c852d97264f9d2e3b54e9e9959083d008145175b86100b275e5b", + "sha256:3b13d89d97b551e02549d1f0edf22bed6acfd6fd2e888cd1e9a953bf215f0e81", + "sha256:3e759bcc03d6f39bc751e56d86bc87252b9a21c689a27c5ed753717a87d53a5b", + "sha256:3ec87bd1248b23a2e4e19e774367fbe30fddc73913edc5f9b37470624f55dc1f", + "sha256:436b0a2dd9fe3f7aa6a444af6bdf53c1eb8f5ced9ea3ef104daa83f0ea18e7bc", + "sha256:43b3c859912e8bf754b3c5142df624794b18eb7ae07cfeddc917e1a9406a3ef2", + "sha256:4fe74636ee71c57a7f65d7b21a9f127d842b4fb75511e5d256ace258826eb352", + "sha256:59445af66b59cc39530b4f810776928d75e95f41e945f0c32a3de4aceb93c15d", + "sha256:69da5b1d7102a61ce9b45deb2920a2012d52fd8f4201495ea9411d0071b0ec22", + "sha256:7094bbdecb95ebe53166e4c12cf5e28310c2b550b08c07c5dc15433898e2238e", + "sha256:8211cac9bf10461f9e33fe9a3af6c5131f3fdd0d10672afc2abb2c70cf95c5ca", + "sha256:8cf77e458bd996dc85455f10fe443c0c946f5b13253773439bcbec08aa1aebc2", + "sha256:924fc33cb4acaf6267b8ca3b8f1922620d57a28470d5e4f49672cea9a841eb08", + "sha256:99ce3333b40b7a4435e0a18baad468d44ab118a4b1da0af0a888893d03253f1d", + "sha256:a7d690b2c5f7e4a932374615fedceb1e305d2dd5363c1de15961725fe10e7d16", + "sha256:b9af590adc1e46898a1276527f3cfe2da8048ae43fbbf9b1bf9395f6c99d9b47", + "sha256:bb18422ad00c1fecc731d06592e99c3be2c634da19e26942ba2f13d805005cf2", + "sha256:c10af40ee2f1a99e1ae755ab1f773916e8bca3364029a042cd9161c400416bd8", + "sha256:c143c409e7bc1db784471fe9d0bf95f37c4458e879ad84cfae640cb74ee11a26", + "sha256:c448d2b335e21951416a30cd48d35588d122a912d5fe9e41900afacecc7d21a1", + "sha256:d30f30c044bdc0ab8f3924e1eeaac87e0ff8a27e87369c5cac4064b6ec78fd83", + "sha256:df534e64d4f3e84e8f1e1a37da3f541555d947c1c1c09b32178537f0f243f69d", + "sha256:f6fc18f9c9c7959bf58e6faf801d14fafb6d4717faaf6f79a68c8bb2a13dcf20", + "sha256:ff83dfeb04c98bb3e7948f876c17513a34e9a19fd92e292288649164924c1b39" ], "index": "pypi", - "version": "==7.2.0" + "version": "==8.1.1" }, "pycares": { "hashes": [ @@ -340,7 +339,6 @@ "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.20" }, "pynacl": { @@ -374,7 +372,6 @@ "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.8.1" }, "pytz": { @@ -409,7 +406,6 @@ "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2", "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==3.5.3" }, "sentry-sdk": { @@ -425,7 +421,6 @@ "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.15.0" }, "sortedcontainers": { @@ -445,11 +440,10 @@ }, "urllib3": { "hashes": [ - "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80", - "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73" + "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df", + "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.26.3" + "version": "==1.26.4" }, "yarl": { "hashes": [ @@ -471,7 +465,6 @@ "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317", "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6" ], - "markers": "python_version >= '3.5'", "version": "==1.5.1" } }, @@ -488,7 +481,6 @@ "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==20.3.0" }, "cfgv": { @@ -496,7 +488,6 @@ "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d", "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1" ], - "markers": "python_full_version >= '3.6.1'", "version": "==3.2.0" }, "distlib": { @@ -585,11 +576,10 @@ }, "identify": { "hashes": [ - "sha256:de7129142a5c86d75a52b96f394d94d96d497881d2aaf8eafe320cdbe8ac4bcc", - "sha256:e0dae57c0397629ce13c289f6ddde0204edf518f557bfdb1e56474aa143e77c3" + "sha256:46d1816c6a4fc2d1e8758f293a5dcc1ae6404ab344179d7c1e73637bf283beb1", + "sha256:ed4a05fb80e3cbd12e83c959f9ff7f729ba6b66ab8d6178850fd5cb4c1cf6c5d" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.5.14" + "version": "==2.1.3" }, "mccabe": { "hashes": [ @@ -626,7 +616,6 @@ "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.6.0" }, "pydocstyle": { @@ -634,7 +623,6 @@ "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325", "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678" ], - "markers": "python_version >= '3.5'", "version": "==5.1.1" }, "pyflakes": { @@ -642,7 +630,6 @@ "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.2.0" }, "pyyaml": { @@ -669,7 +656,6 @@ "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.15.0" }, "snowballstemmer": { @@ -684,16 +670,14 @@ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.10.2" }, "virtualenv": { "hashes": [ - "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d", - "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3" + "sha256:49ec4eb4c224c6f7dd81bb6d0a28a09ecae5894f4e593c89b0db0885f565a107", + "sha256:83f95875d382c7abafe06bd2a4cdd1b363e1bb77e02f155ebe8ac082a916b37c" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.4.2" + "version": "==20.4.3" } } } -- cgit v1.2.3 From 1dea5902487a93d1f9a6382d838928d0a9739627 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 19 Mar 2021 15:10:58 -0400 Subject: Make .github a group, and add github repo + user commands --- bot/exts/evergreen/githubinfo.py | 72 +++++++++++++++++++++++++++++++++++----- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 2e38e3ab..74371244 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -8,9 +8,12 @@ from discord.ext import commands from discord.ext.commands.cooldowns import BucketType from bot.constants import NEGATIVE_REPLIES +from bot.exts.utils.extensions import invoke_help_command log = logging.getLogger(__name__) +GITHUB_API_URL = "https://api.github.com" + class GithubInfo(commands.Cog): """Fetches info from GitHub.""" @@ -23,21 +26,27 @@ class GithubInfo(commands.Cog): async with self.bot.http_session.get(url) as r: return await r.json() - @commands.command(name='github', aliases=['gh']) - @commands.cooldown(1, 60, BucketType.user) - async def get_github_info(self, ctx: commands.Context, username: Optional[str]) -> None: + @commands.group(name='github', aliases=('gh',)) + @commands.cooldown(1, 10, BucketType.user) + async def github_group(self, ctx: commands.Context): + """Commands for finding info related to Github.""" + if ctx.invoked_subcommand is None: + await invoke_help_command(ctx) + + @github_group.command(name='user', aliases=('userinfo',)) + async def github_user_info(self, ctx: commands.Context, username: Optional[str]) -> None: """ Fetches a user's GitHub information. Username is optional and sends the help command if not specified. """ if username is None: - await ctx.invoke(self.bot.get_command('help'), 'github') + await invoke_help_command(ctx) ctx.command.reset_cooldown(ctx) return async with ctx.typing(): - user_data = await self.fetch_data(f"https://api.github.com/users/{username}") + user_data = await self.fetch_data(f"{GITHUB_API_URL}/users/{username}") # User_data will not have a message key if the user exists if user_data.get('message') is not None: @@ -74,24 +83,71 @@ class GithubInfo(commands.Cog): embed.add_field(name="Followers", value=f"[{user_data['followers']}]({user_data['html_url']}?tab=followers)") - embed.add_field(name="\u200b", value="\u200b") embed.add_field(name="Following", value=f"[{user_data['following']}]({user_data['html_url']}?tab=following)") embed.add_field(name="Public repos", value=f"[{user_data['public_repos']}]({user_data['html_url']}?tab=repositories)") - embed.add_field(name="\u200b", value="\u200b") if user_data['type'] == "User": embed.add_field(name="Gists", value=f"[{gists}](https://gist.github.com/{username})") embed.add_field(name=f"Organization{'s' if len(orgs)!=1 else ''}", value=orgs_to_add if orgs else "No organizations") - embed.add_field(name="\u200b", value="\u200b") embed.add_field(name="Website", value=blog) await ctx.send(embed=embed) + @github_group.command(name='repo', aliases=('repositories',)) + async def github_repo_info(self, ctx: commands.Context, repo: Optional[str]): + """ + Fetches a repositories's GitHub information. Repository should look like `user/reponame`. + + Repository is optional and sends the help command if not specified. + """ + if repo is None: + await invoke_help_command(ctx) + ctx.command.reset_cooldown() + return + async with ctx.typing(): + repo_data = await self.fetch_data(f"{GITHUB_API_URL}/repos/{repo}") + + # There won't be a message key if this repo exists + if repo_data.get('message') is not None: + await ctx.send(embed=discord.Embed(title=random.choice(NEGATIVE_REPLIES), + description=f"The requested repository was not found.", + colour=discord.Colour.red())) + return + + repo_owner = repo_data['owner'] + + parent = repo_data.get('parent') + + embed = discord.Embed(title=f"{repo_data['name']}", + description=repo_data["description"], + colour=0x7289da, + url=repo_data['html_url'], + timestamp=datetime.strptime(repo_data['pushed_at'], "%Y-%m-%dT%H:%M:%SZ") + ) + + # If it's a fork, then it will have a parent key + if parent: + embed.description += f"\nForked from [{parent['full_name']}]({parent['html_url']})" + + embed.set_author( + name=repo_owner["login"], + url=repo_owner["html_url"], + icon_url=repo_owner["avatar_url"] + ) + + repo_created_at = datetime.strptime(repo_data['created_at'], "%Y-%m-%dT%H:%M:%SZ").strftime("%d/%m/%Y") + + embed.set_footer(text=f"{repo_data['forks_count']} " + f"⑂ • {repo_data['stargazers_count']} ⭐ • Created At {repo_created_at} • " + " Last commit ") + + await ctx.send(embed=embed) + def setup(bot: commands.Bot) -> None: """Adding the cog to the bot.""" -- cgit v1.2.3 From b3c25c888b16ea707e974282c249eaf9799e184d Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 19 Mar 2021 15:19:05 -0400 Subject: Make alias repository, not repositories --- bot/exts/evergreen/githubinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 74371244..b683065e 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -98,7 +98,7 @@ class GithubInfo(commands.Cog): await ctx.send(embed=embed) - @github_group.command(name='repo', aliases=('repositories',)) + @github_group.command(name='repo', aliases=('repository',)) async def github_repo_info(self, ctx: commands.Context, repo: Optional[str]): """ Fetches a repositories's GitHub information. Repository should look like `user/reponame`. -- cgit v1.2.3 From 658b3b4de1112378e9694b95bcba3d4b8c5213f7 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 19 Mar 2021 15:23:24 -0400 Subject: Missed a \n --- bot/exts/evergreen/githubinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index b683065e..521b94e7 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -132,7 +132,7 @@ class GithubInfo(commands.Cog): # If it's a fork, then it will have a parent key if parent: - embed.description += f"\nForked from [{parent['full_name']}]({parent['html_url']})" + embed.description += f"\n\nForked from [{parent['full_name']}]({parent['html_url']})" embed.set_author( name=repo_owner["login"], -- cgit v1.2.3 From 01ce649b05999b029490bff00e07cd9f32e285e8 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 19 Mar 2021 15:27:53 -0400 Subject: Make flake8 happy --- bot/exts/evergreen/githubinfo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 521b94e7..2c7dd885 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -28,7 +28,7 @@ class GithubInfo(commands.Cog): @commands.group(name='github', aliases=('gh',)) @commands.cooldown(1, 10, BucketType.user) - async def github_group(self, ctx: commands.Context): + async def github_group(self, ctx: commands.Context) -> None: """Commands for finding info related to Github.""" if ctx.invoked_subcommand is None: await invoke_help_command(ctx) @@ -99,7 +99,7 @@ class GithubInfo(commands.Cog): await ctx.send(embed=embed) @github_group.command(name='repo', aliases=('repository',)) - async def github_repo_info(self, ctx: commands.Context, repo: Optional[str]): + async def github_repo_info(self, ctx: commands.Context, repo: Optional[str]) -> None: """ Fetches a repositories's GitHub information. Repository should look like `user/reponame`. @@ -115,7 +115,7 @@ class GithubInfo(commands.Cog): # There won't be a message key if this repo exists if repo_data.get('message') is not None: await ctx.send(embed=discord.Embed(title=random.choice(NEGATIVE_REPLIES), - description=f"The requested repository was not found.", + description="The requested repository was not found.", colour=discord.Colour.red())) return -- cgit v1.2.3 From 060c5403a7ea1866731ec2bb23d3e7ba4234fe8c Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 19 Mar 2021 21:07:35 -0400 Subject: Make better aliases and follow style guidelines --- bot/exts/evergreen/githubinfo.py | 41 ++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 2c7dd885..8dbcc681 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -98,37 +98,38 @@ class GithubInfo(commands.Cog): await ctx.send(embed=embed) - @github_group.command(name='repo', aliases=('repository',)) - async def github_repo_info(self, ctx: commands.Context, repo: Optional[str]) -> None: + @github_group.command(name='repository', aliases=('repo', 'git')) + async def github_repo_info(self, ctx: commands.Context, repo: str) -> None: """ Fetches a repositories's GitHub information. Repository should look like `user/reponame`. Repository is optional and sends the help command if not specified. """ - if repo is None: - await invoke_help_command(ctx) - ctx.command.reset_cooldown() - return async with ctx.typing(): repo_data = await self.fetch_data(f"{GITHUB_API_URL}/repos/{repo}") # There won't be a message key if this repo exists if repo_data.get('message') is not None: - await ctx.send(embed=discord.Embed(title=random.choice(NEGATIVE_REPLIES), - description="The requested repository was not found.", - colour=discord.Colour.red())) + embed = discord.Embed( + title=random.choice(NEGATIVE_REPLIES), + description="The requested repository was not found.", + colour=discord.Colour.red() + ) + + await ctx.send(embed=embed) return repo_owner = repo_data['owner'] parent = repo_data.get('parent') - embed = discord.Embed(title=f"{repo_data['name']}", - description=repo_data["description"], - colour=0x7289da, - url=repo_data['html_url'], - timestamp=datetime.strptime(repo_data['pushed_at'], "%Y-%m-%dT%H:%M:%SZ") - ) + embed = discord.Embed( + title=f"{repo_data['name']}", + description=repo_data["description"], + colour=0x7289da, + url=repo_data['html_url'], + timestamp=datetime.strptime(repo_data['pushed_at'], "%Y-%m-%dT%H:%M:%SZ") + ) # If it's a fork, then it will have a parent key if parent: @@ -142,9 +143,13 @@ class GithubInfo(commands.Cog): repo_created_at = datetime.strptime(repo_data['created_at'], "%Y-%m-%dT%H:%M:%SZ").strftime("%d/%m/%Y") - embed.set_footer(text=f"{repo_data['forks_count']} " - f"⑂ • {repo_data['stargazers_count']} ⭐ • Created At {repo_created_at} • " - " Last commit ") + embed.set_footer( + text=( + f"{repo_data['forks_count']} " + f"⑂ • {repo_data['stargazers_count']} ⭐ • Created At {repo_created_at} • " + " Last commit " + ) + ) await ctx.send(embed=embed) -- cgit v1.2.3 From ad3c78e7a1ed8f4c54c21ab45e85be6c18e5c63f Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 19 Mar 2021 21:25:11 -0400 Subject: Make git alias for the group instead --- bot/exts/evergreen/githubinfo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 8dbcc681..58162e42 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -26,7 +26,7 @@ class GithubInfo(commands.Cog): async with self.bot.http_session.get(url) as r: return await r.json() - @commands.group(name='github', aliases=('gh',)) + @commands.group(name='github', aliases=('gh', 'git')) @commands.cooldown(1, 10, BucketType.user) async def github_group(self, ctx: commands.Context) -> None: """Commands for finding info related to Github.""" @@ -98,7 +98,7 @@ class GithubInfo(commands.Cog): await ctx.send(embed=embed) - @github_group.command(name='repository', aliases=('repo', 'git')) + @github_group.command(name='repository', aliases=('repo')) async def github_repo_info(self, ctx: commands.Context, repo: str) -> None: """ Fetches a repositories's GitHub information. Repository should look like `user/reponame`. -- cgit v1.2.3 From 6387376cdb000b6e33dd1c67d9b7a24d1d7705b4 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 19 Mar 2021 21:26:59 -0400 Subject: Forgot a comma for the tuple --- bot/exts/evergreen/githubinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 58162e42..da2b37ac 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -98,7 +98,7 @@ class GithubInfo(commands.Cog): await ctx.send(embed=embed) - @github_group.command(name='repository', aliases=('repo')) + @github_group.command(name='repository', aliases=('repo',)) async def github_repo_info(self, ctx: commands.Context, repo: str) -> None: """ Fetches a repositories's GitHub information. Repository should look like `user/reponame`. -- cgit v1.2.3 From 9e01d6b85dd27d8ecb5f00a5cf20f332d534e584 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 20 Mar 2021 11:13:46 +0000 Subject: Only output override channels & bot commands channel on whitelist error Previously this would output all channels, and could result in an error. This change ensures only the main bot channel & and any overridden channels are shown in the embed. We do this before the categories block as the categories kwarg itself is an override, so we want to include those in any output. --- bot/utils/decorators.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bot/utils/decorators.py b/bot/utils/decorators.py index c12a15ff..f0e2ff99 100644 --- a/bot/utils/decorators.py +++ b/bot/utils/decorators.py @@ -11,7 +11,7 @@ from discord import Colour, Embed from discord.ext import commands from discord.ext.commands import CheckFailure, Command, Context -from bot.constants import ERROR_REPLIES, Month +from bot.constants import Channels, ERROR_REPLIES, Month, WHITELISTED_CHANNELS from bot.utils import human_months, resolve_current_month from bot.utils.checks import in_whitelist_check @@ -253,6 +253,12 @@ def whitelist_check(**default_kwargs: t.Container[int]) -> t.Callable[[Context], channels = set(kwargs.get("channels") or {}) categories = kwargs.get("categories") + # Only output override channels + community_bot_commands + if channels: + default_whitelist_channels = set(WHITELISTED_CHANNELS) + default_whitelist_channels.discard(Channels.community_bot_commands) + channels.difference_update(default_whitelist_channels) + # Add all whitelisted category channels if categories: for category_id in categories: -- cgit v1.2.3 From e0dbf3b9c01774fc4570ce29b6898c5a32f32a1f Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Sat, 20 Mar 2021 07:53:45 -0400 Subject: Update bot/exts/evergreen/githubinfo.py Co-authored-by: Shivansh-007 --- bot/exts/evergreen/githubinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index da2b37ac..cffdc0b2 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -101,7 +101,7 @@ class GithubInfo(commands.Cog): @github_group.command(name='repository', aliases=('repo',)) async def github_repo_info(self, ctx: commands.Context, repo: str) -> None: """ - Fetches a repositories's GitHub information. Repository should look like `user/reponame`. + Fetches a repositories' GitHub information. The repository should look like `user/reponame`. Repository is optional and sends the help command if not specified. """ -- cgit v1.2.3 From 087b58209b52f835eef6493d2269d98867f39b0c Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Sat, 20 Mar 2021 11:02:28 -0400 Subject: Follow style guildines and use better colours --- bot/exts/evergreen/githubinfo.py | 48 ++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index da2b37ac..ec2f9d98 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -1,13 +1,12 @@ import logging import random from datetime import datetime -from typing import Optional import discord from discord.ext import commands from discord.ext.commands.cooldowns import BucketType -from bot.constants import NEGATIVE_REPLIES +from bot.constants import NEGATIVE_REPLIES, Colours from bot.exts.utils.extensions import invoke_help_command log = logging.getLogger(__name__) @@ -34,25 +33,24 @@ class GithubInfo(commands.Cog): await invoke_help_command(ctx) @github_group.command(name='user', aliases=('userinfo',)) - async def github_user_info(self, ctx: commands.Context, username: Optional[str]) -> None: + async def github_user_info(self, ctx: commands.Context, username: str) -> None: """ Fetches a user's GitHub information. Username is optional and sends the help command if not specified. """ - if username is None: - await invoke_help_command(ctx) - ctx.command.reset_cooldown(ctx) - return - async with ctx.typing(): user_data = await self.fetch_data(f"{GITHUB_API_URL}/users/{username}") # User_data will not have a message key if the user exists if user_data.get('message') is not None: - await ctx.send(embed=discord.Embed(title=random.choice(NEGATIVE_REPLIES), - description=f"The profile for `{username}` was not found.", - colour=discord.Colour.red())) + embed = discord.Embed( + title=random.choice(NEGATIVE_REPLIES), + description=f"The profile for `{username}` was not found.", + colour=Colours.soft_red + ) + + await ctx.send() return org_data = await self.fetch_data(user_data['organizations_url']) @@ -72,7 +70,7 @@ class GithubInfo(commands.Cog): embed = discord.Embed( title=f"`{user_data['login']}`'s GitHub profile info", description=f"```{user_data['bio']}```\n" if user_data['bio'] is not None else "", - colour=0x7289da, + colour=discord.Colour.blurple(), url=user_data['html_url'], timestamp=datetime.strptime(user_data['created_at'], "%Y-%m-%dT%H:%M:%SZ") ) @@ -81,19 +79,27 @@ class GithubInfo(commands.Cog): if user_data['type'] == "User": - embed.add_field(name="Followers", - value=f"[{user_data['followers']}]({user_data['html_url']}?tab=followers)") - embed.add_field(name="Following", - value=f"[{user_data['following']}]({user_data['html_url']}?tab=following)") + embed.add_field( + name="Followers", + value=f"[{user_data['followers']}]({user_data['html_url']}?tab=followers)" + ) + embed.add_field( + name="Following", + value=f"[{user_data['following']}]({user_data['html_url']}?tab=following)" + ) - embed.add_field(name="Public repos", - value=f"[{user_data['public_repos']}]({user_data['html_url']}?tab=repositories)") + embed.add_field( + name="Public repos", + value=f"[{user_data['public_repos']}]({user_data['html_url']}?tab=repositories)" + ) if user_data['type'] == "User": embed.add_field(name="Gists", value=f"[{gists}](https://gist.github.com/{username})") - embed.add_field(name=f"Organization{'s' if len(orgs)!=1 else ''}", - value=orgs_to_add if orgs else "No organizations") + embed.add_field( + name=f"Organization{'s' if len(orgs)!=1 else ''}", + value=orgs_to_add if orgs else "No organizations" + ) embed.add_field(name="Website", value=blog) await ctx.send(embed=embed) @@ -113,7 +119,7 @@ class GithubInfo(commands.Cog): embed = discord.Embed( title=random.choice(NEGATIVE_REPLIES), description="The requested repository was not found.", - colour=discord.Colour.red() + colour=Colours.soft_red ) await ctx.send(embed=embed) -- cgit v1.2.3 From 7fb2134e40f7d9b47759797785d9e47ff059ff7c Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Sat, 20 Mar 2021 11:14:33 -0400 Subject: Make a new line for `created_at` Co-authored-by: Shivansh-007 --- bot/exts/evergreen/githubinfo.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index cffdc0b2..75db10d4 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -145,9 +145,10 @@ class GithubInfo(commands.Cog): embed.set_footer( text=( - f"{repo_data['forks_count']} " - f"⑂ • {repo_data['stargazers_count']} ⭐ • Created At {repo_created_at} • " - " Last commit " + f"{repo_data['forks_count']} ⑂ " + f"• {repo_data['stargazers_count']} ⭐ " + f"• Created At {repo_created_at} " + f"• Last commit " ) ) -- cgit v1.2.3 From d945ec3314500e579d8e79f086d53453886daf9a Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Sat, 20 Mar 2021 11:28:17 -0400 Subject: Fix trailing whitespace --- bot/exts/evergreen/githubinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 0b2c4c77..07175857 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -152,7 +152,7 @@ class GithubInfo(commands.Cog): embed.set_footer( text=( f"{repo_data['forks_count']} ⑂ " - f"• {repo_data['stargazers_count']} ⭐ " + f"• {repo_data['stargazers_count']} ⭐ " f"• Created At {repo_created_at} " f"• Last commit " ) -- cgit v1.2.3 From 5c8e3008ee5f37bbcab24552fd4c933c8271aa58 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Sat, 20 Mar 2021 11:37:56 -0400 Subject: Fix imports --- bot/exts/evergreen/githubinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 07175857..ebb265b5 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -6,7 +6,7 @@ import discord from discord.ext import commands from discord.ext.commands.cooldowns import BucketType -from bot.constants import NEGATIVE_REPLIES, Colours +from bot.constants import Colours, NEGATIVE_REPLIES from bot.exts.utils.extensions import invoke_help_command log = logging.getLogger(__name__) -- cgit v1.2.3 From 69569fe2c5dafb659b3c56e764d034281686492e Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Sat, 20 Mar 2021 11:41:12 -0400 Subject: Change --- bot/exts/evergreen/githubinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index ebb265b5..5559a464 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -132,7 +132,7 @@ class GithubInfo(commands.Cog): embed = discord.Embed( title=f"{repo_data['name']}", description=repo_data["description"], - colour=0x7289da, + colour=discord.Colour.blurple(), url=repo_data['html_url'], timestamp=datetime.strptime(repo_data['pushed_at'], "%Y-%m-%dT%H:%M:%SZ") ) -- cgit v1.2.3 From 85c6dfa00e01abea8b65654aed59a520af3c18a6 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Sat, 20 Mar 2021 12:15:57 -0400 Subject: Make last commit not a timestamp to improve readability --- bot/exts/evergreen/githubinfo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 5559a464..67d91589 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -133,8 +133,7 @@ class GithubInfo(commands.Cog): title=f"{repo_data['name']}", description=repo_data["description"], colour=discord.Colour.blurple(), - url=repo_data['html_url'], - timestamp=datetime.strptime(repo_data['pushed_at'], "%Y-%m-%dT%H:%M:%SZ") + url=repo_data['html_url'] ) # If it's a fork, then it will have a parent key @@ -148,13 +147,14 @@ class GithubInfo(commands.Cog): ) repo_created_at = datetime.strptime(repo_data['created_at'], "%Y-%m-%dT%H:%M:%SZ").strftime("%d/%m/%Y") + last_pushed = datetime.strptime(repo_data['pushed_at'], "%Y-%m-%dT%H:%M:%SZ").strftime("%d/%m/%Y at %H:%M") embed.set_footer( text=( f"{repo_data['forks_count']} ⑂ " f"• {repo_data['stargazers_count']} ⭐ " f"• Created At {repo_created_at} " - f"• Last commit " + f"• Last Commit {last_pushed}" ) ) -- cgit v1.2.3 From ed26d381e1959523b9a5da2866f7f1f917ee7bae Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Sun, 21 Mar 2021 08:36:09 -0400 Subject: Add the embed kwarg to ctx.send --- bot/exts/evergreen/githubinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 67d91589..1be2e385 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -50,7 +50,7 @@ class GithubInfo(commands.Cog): colour=Colours.soft_red ) - await ctx.send() + await ctx.send(embed=embed) return org_data = await self.fetch_data(user_data['organizations_url']) -- cgit v1.2.3 From eac2b1d79275e3bdc08fc2a8de7a28913b8ebd5b Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 21 Mar 2021 13:52:56 +0000 Subject: Don't close bot's http session when getting image --- .../profile_pic_modification/pfp_modify.py | 24 ++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 19f0bdf0..9242ff0c 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -204,20 +204,18 @@ class PfpModify(commands.Cog): return async with ctx.typing(): - async with self.bot.http_session as session: - try: - response = await session.get(url) - except client_exceptions.ClientConnectorError: - raise BadArgument("Cannot connect to provided URL!") - except client_exceptions.InvalidURL: - raise BadArgument("Invalid URL!") - - if response.status != 200: - await ctx.send("Bad response from provided URL!") - return + try: + async with self.bot.http_session.get(url) as response: + if response.status != 200: + await ctx.send("Bad response from provided URL!") + return + image_bytes = await response.read() + except client_exceptions.ClientConnectorError: + raise BadArgument("Cannot connect to provided URL!") + except client_exceptions.InvalidURL: + raise BadArgument("Invalid URL!") - image_bytes = await response.read() - await self.send_pride_image(ctx, image_bytes, pixels, flag, option) + await self.send_pride_image(ctx, image_bytes, pixels, flag, option) @prideavatar.command() async def flags(self, ctx: commands.Context) -> None: -- cgit v1.2.3 From 1e9a88f5b59cf7d1c4e24f48cc09aa98e5f6499b Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 21 Mar 2021 18:11:50 +0000 Subject: Replace list comp with a set update + sequence --- bot/utils/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/utils/decorators.py b/bot/utils/decorators.py index f0e2ff99..60066dc4 100644 --- a/bot/utils/decorators.py +++ b/bot/utils/decorators.py @@ -266,7 +266,7 @@ def whitelist_check(**default_kwargs: t.Container[int]) -> t.Callable[[Context], if category is None: continue - [channels.add(channel.id) for channel in category.text_channels] + channels.update(channel.id for channel in category.text_channels) if channels: channels_str = ', '.join(f"<#{c_id}>" for c_id in channels) -- cgit v1.2.3 From f6215bb61fed2e1247237752414e38f539d54015 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 23 Mar 2021 06:12:34 +0530 Subject: Feature: suggest command usage for misspell commands --- bot/exts/evergreen/error_handler.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index 28902503..d5069168 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -1,3 +1,4 @@ +import difflib import logging import math import random @@ -5,6 +6,7 @@ from typing import Iterable, Union from discord import Embed, Message from discord.ext import commands +from discord.ext.commands import errors from sentry_sdk import push_scope from bot.constants import Channels, Colours, ERROR_REPLIES, NEGATIVE_REPLIES @@ -14,6 +16,9 @@ from bot.utils.exceptions import UserNotPlayingError log = logging.getLogger(__name__) +QUESTION_MARK_ICON = "https://cdn.discordapp.com/emojis/512367613339369475.png" + + class CommandErrorHandler(commands.Cog): """A error handler for the PythonDiscord server.""" @@ -55,6 +60,7 @@ class CommandErrorHandler(commands.Cog): ) if isinstance(error, commands.CommandNotFound): + await self.send_command_suggestion(ctx, ctx.invoked_with) return if isinstance(error, (InChannelCheckFailure, InMonthCheckFailure)): @@ -128,6 +134,35 @@ class CommandErrorHandler(commands.Cog): log.exception(f"Unhandled command error: {str(error)}", exc_info=error) + async def send_command_suggestion(self, ctx: commands.Context, command_name: str) -> None: + """Sends user similar commands if any can be found.""" + raw_commands = [] + for cmd in self.bot.walk_commands(): + if not cmd.hidden: + raw_commands += (cmd.name, *cmd.aliases) + if similar_command_data := difflib.get_close_matches(command_name, raw_commands, 1): + similar_command_name = similar_command_data[0] + similar_command = self.bot.get_command(similar_command_name) + + if not similar_command: + return + + log_msg = "Cancelling attempt to suggest a command due to failed checks." + try: + if not await similar_command.can_run(ctx): + log.debug(log_msg) + return + except errors.CommandError as cmd_error: + log.debug(log_msg) + await self.on_command_error(ctx, cmd_error) + return + + misspelled_content = ctx.message.content + e = Embed() + e.set_author(name="Did you mean:", icon_url=QUESTION_MARK_ICON) + e.description = f"{misspelled_content.replace(command_name, similar_command_name, 1)}" + await ctx.send(embed=e, delete_after=10.0) + def setup(bot: commands.Bot) -> None: """Error handler Cog load.""" -- cgit v1.2.3 From 9473521a9e8457882e135e278996a3294e53cd36 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 23 Mar 2021 06:14:04 +0530 Subject: Fix imports --- bot/exts/evergreen/error_handler.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index d5069168..3d056f81 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -6,7 +6,6 @@ from typing import Iterable, Union from discord import Embed, Message from discord.ext import commands -from discord.ext.commands import errors from sentry_sdk import push_scope from bot.constants import Channels, Colours, ERROR_REPLIES, NEGATIVE_REPLIES @@ -152,7 +151,7 @@ class CommandErrorHandler(commands.Cog): if not await similar_command.can_run(ctx): log.debug(log_msg) return - except errors.CommandError as cmd_error: + except commands.errors.CommandError as cmd_error: log.debug(log_msg) await self.on_command_error(ctx, cmd_error) return -- cgit v1.2.3 From faccb5d2828d046d5e3a1e21c63f392529be4691 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Wed, 24 Mar 2021 09:24:32 +0100 Subject: Remove broken link from the April fool collection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Its’a braken --- bot/resources/easter/april_fools_vids.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bot/resources/easter/april_fools_vids.json b/bot/resources/easter/april_fools_vids.json index 5f0ba06e..d8c1efa1 100644 --- a/bot/resources/easter/april_fools_vids.json +++ b/bot/resources/easter/april_fools_vids.json @@ -16,10 +16,6 @@ "title": "Haptic Helpers: bringing you to your senses", "link": "https://youtu.be/3MA6_21nka8" }, - { - "title": "Introducing Google Gnome", - "link": "https://youtu.be/vNOllWX-2aE" - }, { "title": "Introducing Google Wind", "link": "https://youtu.be/QAwL0O5nXe0" -- cgit v1.2.3 From 8b72a57f05c85fbdd6f9735f8cf722f7d63df659 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 24 Mar 2021 13:31:13 -0400 Subject: Update docstrings because user and repo are now mandatory arguments --- bot/exts/evergreen/githubinfo.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 1be2e385..34e30d0b 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -34,11 +34,7 @@ class GithubInfo(commands.Cog): @github_group.command(name='user', aliases=('userinfo',)) async def github_user_info(self, ctx: commands.Context, username: str) -> None: - """ - Fetches a user's GitHub information. - - Username is optional and sends the help command if not specified. - """ + """Fetches a user's GitHub information.""" async with ctx.typing(): user_data = await self.fetch_data(f"{GITHUB_API_URL}/users/{username}") @@ -106,11 +102,7 @@ class GithubInfo(commands.Cog): @github_group.command(name='repository', aliases=('repo',)) async def github_repo_info(self, ctx: commands.Context, repo: str) -> None: - """ - Fetches a repositories' GitHub information. The repository should look like `user/reponame`. - - Repository is optional and sends the help command if not specified. - """ + """Fetches a repositories' GitHub information. The repository should look like `user/reponame`.""" async with ctx.typing(): repo_data = await self.fetch_data(f"{GITHUB_API_URL}/repos/{repo}") -- cgit v1.2.3 From e044bf3d716dc166704ba31def82bd34a68acb42 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 24 Mar 2021 13:34:47 -0400 Subject: Info -> information Co-authored-by: Shivansh-007 --- bot/exts/evergreen/githubinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 34e30d0b..712a516b 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -28,7 +28,7 @@ class GithubInfo(commands.Cog): @commands.group(name='github', aliases=('gh', 'git')) @commands.cooldown(1, 10, BucketType.user) async def github_group(self, ctx: commands.Context) -> None: - """Commands for finding info related to Github.""" + """Commands for finding information related to GitHub.""" if ctx.invoked_subcommand is None: await invoke_help_command(ctx) -- cgit v1.2.3 From ed5dc457c51aae388e5d4607639fb79be958ff27 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Thu, 25 Mar 2021 19:22:12 -0400 Subject: Quote reponame and username --- bot/exts/evergreen/githubinfo.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 34e30d0b..56195e8c 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -1,6 +1,7 @@ import logging import random from datetime import datetime +from urllib.parse import quote import discord from discord.ext import commands @@ -90,7 +91,7 @@ class GithubInfo(commands.Cog): ) if user_data['type'] == "User": - embed.add_field(name="Gists", value=f"[{gists}](https://gist.github.com/{username})") + embed.add_field(name="Gists", value=f"[{gists}](https://gist.github.com/{quote(username, safe='')})") embed.add_field( name=f"Organization{'s' if len(orgs)!=1 else ''}", @@ -103,8 +104,18 @@ class GithubInfo(commands.Cog): @github_group.command(name='repository', aliases=('repo',)) async def github_repo_info(self, ctx: commands.Context, repo: str) -> None: """Fetches a repositories' GitHub information. The repository should look like `user/reponame`.""" + if repo.count('/') != 1: + embed = discord.Embed( + title=random.choice(NEGATIVE_REPLIES), + description="The repository should look like `user/reponame`.", + colour=Colours.soft_red + ) + + await ctx.send(embed=embed) + return + async with ctx.typing(): - repo_data = await self.fetch_data(f"{GITHUB_API_URL}/repos/{repo}") + repo_data = await self.fetch_data(f"{GITHUB_API_URL}/repos/{quote(repo)}") # There won't be a message key if this repo exists if repo_data.get('message') is not None: -- cgit v1.2.3 From 8f79d6705dd8d62a51d34f12f27fa9643fc3b04c Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Thu, 25 Mar 2021 21:44:26 -0400 Subject: Make requested changes --- bot/exts/evergreen/githubinfo.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 94a3166b..1b64b0eb 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -40,7 +40,7 @@ class GithubInfo(commands.Cog): user_data = await self.fetch_data(f"{GITHUB_API_URL}/users/{username}") # User_data will not have a message key if the user exists - if user_data.get('message') is not None: + if "message" in user_data: embed = discord.Embed( title=random.choice(NEGATIVE_REPLIES), description=f"The profile for `{username}` was not found.", @@ -118,7 +118,7 @@ class GithubInfo(commands.Cog): repo_data = await self.fetch_data(f"{GITHUB_API_URL}/repos/{quote(repo)}") # There won't be a message key if this repo exists - if repo_data.get('message') is not None: + if "message" in repo_data: embed = discord.Embed( title=random.choice(NEGATIVE_REPLIES), description="The requested repository was not found.", @@ -128,20 +128,21 @@ class GithubInfo(commands.Cog): await ctx.send(embed=embed) return - repo_owner = repo_data['owner'] - - parent = repo_data.get('parent') - embed = discord.Embed( - title=f"{repo_data['name']}", + title=repo_data['name'], description=repo_data["description"], colour=discord.Colour.blurple(), url=repo_data['html_url'] ) # If it's a fork, then it will have a parent key - if parent: + try: + parent = repo_data["parent"] embed.description += f"\n\nForked from [{parent['full_name']}]({parent['html_url']})" + except KeyError: + log.debug("Repository is not a fork.") + + repo_owner = repo_data['owner'] embed.set_author( name=repo_owner["login"], -- cgit v1.2.3 From f6a28d5833160dccb6d308fd8e27c6afb9729643 Mon Sep 17 00:00:00 2001 From: Neil Shah <78612612+NeilShah2026@users.noreply.github.com> Date: Fri, 26 Mar 2021 09:50:03 -0400 Subject: Added 3 More Elements To The Json --- bot/resources/easter/april_fools_vids.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/bot/resources/easter/april_fools_vids.json b/bot/resources/easter/april_fools_vids.json index d8c1efa1..d029ff86 100644 --- a/bot/resources/easter/april_fools_vids.json +++ b/bot/resources/easter/april_fools_vids.json @@ -116,6 +116,18 @@ "title": "Introducing Gmail Motion", "link": "https://youtu.be/Bu927_ul_X0" } + { + "title": "Introducing GeForce GTX G-Assist", + "link": "https://youtu.be/smM-Wdk2RLQ" + } + { + "title": "The Hovering Mouse - Project McFly | Razer", + "link": "https://youtu.be/IlCx5gjAmqI" + } + { + "title": "Be the Machine | Project Venom v2", + "link": "https://youtu.be/j8UJE7DoyJ8" + } ] } -- cgit v1.2.3 From 877671523c18aad4bb5061a1a2379beb5c761e3c Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 26 Mar 2021 14:54:07 -0400 Subject: Make repository accept either user/reponame or user reponame --- bot/exts/evergreen/githubinfo.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 1b64b0eb..c8a6b3f7 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -102,12 +102,17 @@ class GithubInfo(commands.Cog): await ctx.send(embed=embed) @github_group.command(name='repository', aliases=('repo',)) - async def github_repo_info(self, ctx: commands.Context, repo: str) -> None: - """Fetches a repositories' GitHub information. The repository should look like `user/reponame`.""" + async def github_repo_info(self, ctx: commands.Context, *repo: str) -> None: + """ + Fetches a repositories' GitHub information. + + The repository should look like `user/reponame` or `user reponame`. + """ + repo = '/'.join(repo) if repo.count('/') != 1: embed = discord.Embed( title=random.choice(NEGATIVE_REPLIES), - description="The repository should look like `user/reponame`.", + description="The repository should look like `user/reponame` or `user reponame`.", colour=Colours.soft_red ) -- cgit v1.2.3 From dfe0e6001d4c1886e29b20eafacf4de1cc3a938b Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sat, 27 Mar 2021 18:17:26 +0100 Subject: Allow automatic linking of issues everywhere We have been limiting the channels where automatic linking issues can be used in (See #566). Since we haven't seen any potential issue with it, we can allow it everywhere to avoid updating it every two days or because of missing channels. --- bot/exts/evergreen/issues.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index bbcbf611..6ca0c3c9 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -180,12 +180,6 @@ class Issues(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - if not( - message.channel.category.id in WHITELISTED_CATEGORIES - or message.channel.id in WHITELISTED_CHANNELS_ON_MESSAGE - ): - return - message_repo_issue_map = re.findall(fr"({self.repo_regex})#(\d+)", message.content) links = [] -- cgit v1.2.3 From d03a41dfbeb3653fbc96ae4c6e3160fe581b1d16 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sat, 27 Mar 2021 18:33:36 +0100 Subject: Remove unused WHITELISTED_CHANNELS_ON_MESSAGE constant --- bot/exts/evergreen/issues.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 6ca0c3c9..3d23b869 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -7,7 +7,7 @@ from enum import Enum import discord from discord.ext import commands, tasks -from bot.constants import Categories, Channels, Colours, ERROR_REPLIES, Emojis, Tokens, WHITELISTED_CHANNELS +from bot.constants import Categories, Colours, ERROR_REPLIES, Emojis, Tokens, WHITELISTED_CHANNELS log = logging.getLogger(__name__) @@ -26,9 +26,6 @@ if GITHUB_TOKEN := Tokens.github: WHITELISTED_CATEGORIES = ( Categories.development, Categories.devprojects, Categories.media, Categories.staff ) -WHITELISTED_CHANNELS_ON_MESSAGE = ( - Channels.organisation, Channels.mod_meta, Channels.mod_tools, Channels.staff_voice -) CODE_BLOCK_RE = re.compile( r"^`([^`\n]+)`" # Inline codeblock -- cgit v1.2.3 From 73970e6ba6909bf7ab5023377585ee12cdc77b97 Mon Sep 17 00:00:00 2001 From: Xithrius Date: Sun, 28 Mar 2021 14:45:12 -0700 Subject: Added more topics. --- bot/resources/evergreen/py_topics.yaml | 2 +- bot/resources/evergreen/starter.yaml | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/bot/resources/evergreen/py_topics.yaml b/bot/resources/evergreen/py_topics.yaml index f3b2eaa3..7d5f7cb0 100644 --- a/bot/resources/evergreen/py_topics.yaml +++ b/bot/resources/evergreen/py_topics.yaml @@ -69,7 +69,7 @@ # game-development 660625198390837248: - - + - What is your favorite game mechanic? # microcontrollers 545603026732318730: diff --git a/bot/resources/evergreen/starter.yaml b/bot/resources/evergreen/starter.yaml index 949220f9..4fec6a10 100644 --- a/bot/resources/evergreen/starter.yaml +++ b/bot/resources/evergreen/starter.yaml @@ -31,3 +31,22 @@ - What is your favorite TV show? - What is your favorite media genre? - How many years have you spent coding? +- What book do you highly recommend everyone to read? +- What websites do you use daily to keep yourself up to date with the industry? +- What made you want to join this Discord server? +- How are you? +- What is the best advice you have ever gotten in regards to programming/software? +- What is the most satisfying thing you've done in your life? +- Who is your favorite music composer/producer/singer? +- What is your favorite song? +- What is your favorite video game? +- What are your hobbies other than programming? +- Who is your favorite Writer? +- What is your favorite movie? +- What is your favorite sport? +- What is your favorite fruit? +- What is your favorite juice? +- What is the best scenery you've ever seen? +- What artistic talents do you have? +- What is the tallest building you've entered? +- What is the oldest computer you've ever used? -- cgit v1.2.3 From ab126639380b337493606bc6c90acb5046e9f76e Mon Sep 17 00:00:00 2001 From: Boris Muratov <8bee278@gmail.com> Date: Mon, 29 Mar 2021 01:00:05 +0300 Subject: Add more game dev topics --- bot/resources/evergreen/py_topics.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bot/resources/evergreen/py_topics.yaml b/bot/resources/evergreen/py_topics.yaml index 7d5f7cb0..6b7e0206 100644 --- a/bot/resources/evergreen/py_topics.yaml +++ b/bot/resources/evergreen/py_topics.yaml @@ -70,6 +70,10 @@ # game-development 660625198390837248: - What is your favorite game mechanic? + - What is your favorite framework and why? + - What games do you know that were written in Python? + - What books or tutorials would you recommend for game-development beginners? + - What made you start developing games? # microcontrollers 545603026732318730: -- cgit v1.2.3 From bdfeef5d16ff64a7ca8a9a610d39a654f2e1fb99 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 28 Mar 2021 23:01:55 +0100 Subject: Rework bookmark command to allow other users to bookmark the same message. --- bot/exts/evergreen/bookmark.py | 116 +++++++++++++++++++++++++++++++++-------- 1 file changed, 95 insertions(+), 21 deletions(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 5fa05d2e..d26c4845 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -1,14 +1,18 @@ +import asyncio import logging import random import discord from discord.ext import commands -from bot.constants import Colours, ERROR_REPLIES, Emojis, Icons +from bot.constants import Colours, ERROR_REPLIES, Icons from bot.utils.converters import WrappedMessageConverter log = logging.getLogger(__name__) +# Number of seconds to wait for other users to bookmark the same message +TIMEOUT = 120 + class Bookmark(commands.Cog): """Creates personal bookmarks by relaying a message link to the user's DMs.""" @@ -16,6 +20,64 @@ class Bookmark(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot + @staticmethod + def build_bookmark_dm(target_message: WrappedMessageConverter, title: str) -> discord.Embed: + """Builds the embed to DM the bookmark requestor.""" + embed = discord.Embed( + title=title, + description=target_message.content, + colour=Colours.soft_green + ) + embed.add_field(name="Wanna give it a visit?", value=f"[Visit original message]({target_message.jump_url})") + embed.set_author(name=target_message.author, icon_url=target_message.author.avatar_url) + embed.set_thumbnail(url=Icons.bookmark) + + return embed + + @staticmethod + def build_error_embed(user: discord.Member) -> discord.Embed: + """Builds an error embed for when a bookmark requestor has DMs disabled.""" + return discord.Embed( + title=random.choice(ERROR_REPLIES), + description=f"{user.mention}, please enable your DMs to receive the bookmark", + colour=Colours.soft_red + ) + + async def action_bookmark( + self, + channel: discord.TextChannel, + user: discord.Member, + target_message: WrappedMessageConverter, + title: str + ) -> None: + """Sends the bookmark DM, or sends an error embed when a user bookmarks a message.""" + try: + embed = self.build_bookmark_dm(target_message, title) + await user.send(embed=embed) + except discord.Forbidden: + error_embed = self.build_error_embed(user) + await channel.send(embed=error_embed) + else: + log.info(f"{user} bookmarked {target_message.jump_url} with title '{title}'") + + @staticmethod + async def send_reaction_embed( + channel: discord.TextChannel, + target_message: WrappedMessageConverter + ) -> discord.Message: + """Sends an embed, with a reaction, so users can react to bookmark the message too.""" + message = await channel.send( + embed=discord.Embed( + description=( + f"React with 📌 to be sent your very own bookmark to [this message]({target_message.jump_url})." + ), + colour=Colours.soft_green + ) + ) + + await message.add_reaction("📌") + return message + @commands.command(name="bookmark", aliases=("bm", "pin")) async def bookmark( self, @@ -37,27 +99,39 @@ class Bookmark(commands.Cog): await ctx.send(embed=embed) return - embed = discord.Embed( - title=title, - colour=Colours.soft_green, - description=target_message.content - ) - embed.add_field(name="Wanna give it a visit?", value=f"[Visit original message]({target_message.jump_url})") - embed.set_author(name=target_message.author, icon_url=target_message.author.avatar_url) - embed.set_thumbnail(url=Icons.bookmark) - - try: - await ctx.author.send(embed=embed) - except discord.Forbidden: - error_embed = discord.Embed( - title=random.choice(ERROR_REPLIES), - description=f"{ctx.author.mention}, please enable your DMs to receive the bookmark", - colour=Colours.soft_red + def event_check(reaction_: discord.Reaction, user_: discord.Member) -> bool: + """Make sure that this reaction is what we want to operate on.""" + return ( + # Conditions for a successful pagination: + all(( + # Reaction is on this message + reaction_.message.id == reaction_message.id, + # User has not already bookmarked this message + user_.id not in bookmarked_users, + # Reaction is the 📌 emoji + str(reaction_.emoji) == "📌", + # Reaction was not made by the Bot + user_.id != self.bot.user.id + )) ) - await ctx.send(embed=error_embed) - else: - log.info(f"{ctx.author} bookmarked {target_message.jump_url} with title '{title}'") - await ctx.message.add_reaction(Emojis.envelope) + await self.action_bookmark(ctx.channel, ctx.author, target_message, title) + + # Keep track of who has already bookmarked, so users can't spam reactions and cause loads of DMs + bookmarked_users = [ctx.author.id] + reaction_message = await self.send_reaction_embed(ctx.channel, target_message) + + while True: + try: + _, user = await self.bot.wait_for("reaction_add", timeout=TIMEOUT, check=event_check) + except asyncio.TimeoutError: + log.debug("Timed out waiting for a reaction") + break # We're done, no reactions for the last 5 minutes + log.trace(f"{user} has sucessfully bookmarked from a reaction, attempting to DM them.") + await self.action_bookmark(ctx.channel, user, target_message, title) + bookmarked_users.append(user.id) + + # Delete the message now that we're done with it. + await reaction_message.delete() def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From 99a0fccf221ae9e365a76d9b8c6c330de6264c90 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 28 Mar 2021 23:30:32 +0100 Subject: Update phrasing of comments in bookmark command --- bot/exts/evergreen/bookmark.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index d26c4845..bfb1e700 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -125,12 +125,12 @@ class Bookmark(commands.Cog): _, user = await self.bot.wait_for("reaction_add", timeout=TIMEOUT, check=event_check) except asyncio.TimeoutError: log.debug("Timed out waiting for a reaction") - break # We're done, no reactions for the last 5 minutes + break # No reactions for the last `TIMEOUT` seconds log.trace(f"{user} has sucessfully bookmarked from a reaction, attempting to DM them.") await self.action_bookmark(ctx.channel, user, target_message, title) bookmarked_users.append(user.id) - # Delete the message now that we're done with it. + # Delete the message now that the bot isn't listening to it to save screen space await reaction_message.delete() -- cgit v1.2.3 From a308990d1432a1a29f54e74678630f374a5fac2c Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Mon, 29 Mar 2021 12:28:40 +0100 Subject: Fix spelling and grammar errors in bookmark command Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/evergreen/bookmark.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index bfb1e700..9246825a 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -22,7 +22,7 @@ class Bookmark(commands.Cog): @staticmethod def build_bookmark_dm(target_message: WrappedMessageConverter, title: str) -> discord.Embed: - """Builds the embed to DM the bookmark requestor.""" + """Builds the embed to DM the bookmark requester.""" embed = discord.Embed( title=title, description=target_message.content, @@ -36,10 +36,10 @@ class Bookmark(commands.Cog): @staticmethod def build_error_embed(user: discord.Member) -> discord.Embed: - """Builds an error embed for when a bookmark requestor has DMs disabled.""" + """Builds an error embed for when a bookmark requester has DMs disabled.""" return discord.Embed( title=random.choice(ERROR_REPLIES), - description=f"{user.mention}, please enable your DMs to receive the bookmark", + description=f"{user.mention}, please enable your DMs to receive the bookmark.", colour=Colours.soft_red ) @@ -126,7 +126,7 @@ class Bookmark(commands.Cog): except asyncio.TimeoutError: log.debug("Timed out waiting for a reaction") break # No reactions for the last `TIMEOUT` seconds - log.trace(f"{user} has sucessfully bookmarked from a reaction, attempting to DM them.") + log.trace(f"{user} has successfully bookmarked from a reaction, attempting to DM them.") await self.action_bookmark(ctx.channel, user, target_message, title) bookmarked_users.append(user.id) -- cgit v1.2.3 From 6e71554c1604cb372415a505e4db7ca0f80f30fd Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Mon, 29 Mar 2021 12:29:25 +0100 Subject: Split out an embed add field call for readability Co-authored-by: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> --- bot/exts/evergreen/bookmark.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 9246825a..c4c238c3 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -28,7 +28,10 @@ class Bookmark(commands.Cog): description=target_message.content, colour=Colours.soft_green ) - embed.add_field(name="Wanna give it a visit?", value=f"[Visit original message]({target_message.jump_url})") + embed.add_field( + name="Wanna give it a visit?", + value=f"[Visit original message]({target_message.jump_url})" + ) embed.set_author(name=target_message.author, icon_url=target_message.author.avatar_url) embed.set_thumbnail(url=Icons.bookmark) -- cgit v1.2.3 From 22e062ab679afd5b6aa1790617097ed45ffee1f5 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 29 Mar 2021 12:31:10 +0100 Subject: Make bookmark emoji a constant --- bot/exts/evergreen/bookmark.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index c4c238c3..59e2c9d0 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -12,6 +12,7 @@ log = logging.getLogger(__name__) # Number of seconds to wait for other users to bookmark the same message TIMEOUT = 120 +BOOKMARK_EMOJI = "📌" class Bookmark(commands.Cog): @@ -29,7 +30,7 @@ class Bookmark(commands.Cog): colour=Colours.soft_green ) embed.add_field( - name="Wanna give it a visit?", + name="Wanna give it a visit?", value=f"[Visit original message]({target_message.jump_url})" ) embed.set_author(name=target_message.author, icon_url=target_message.author.avatar_url) @@ -72,13 +73,14 @@ class Bookmark(commands.Cog): message = await channel.send( embed=discord.Embed( description=( - f"React with 📌 to be sent your very own bookmark to [this message]({target_message.jump_url})." + f"React with {BOOKMARK_EMOJI} to be sent your very own bookmark to " + f"[this message]({target_message.jump_url})." ), colour=Colours.soft_green ) ) - await message.add_reaction("📌") + await message.add_reaction(BOOKMARK_EMOJI) return message @commands.command(name="bookmark", aliases=("bm", "pin")) @@ -111,8 +113,8 @@ class Bookmark(commands.Cog): reaction_.message.id == reaction_message.id, # User has not already bookmarked this message user_.id not in bookmarked_users, - # Reaction is the 📌 emoji - str(reaction_.emoji) == "📌", + # Reaction is the `BOOKMARK_EMOJI` emoji + str(reaction_.emoji) == BOOKMARK_EMOJI, # Reaction was not made by the Bot user_.id != self.bot.user.id )) -- cgit v1.2.3 From 4bf63f11929a22ffbb9d8859b76ed5437b0a4f13 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Tue, 30 Mar 2021 16:51:29 +0200 Subject: Add missing commas to april_fools_vids.json #639 left the json file in a broken state, not letting the bot start. --- bot/resources/easter/april_fools_vids.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/resources/easter/april_fools_vids.json b/bot/resources/easter/april_fools_vids.json index d029ff86..b2cbd07b 100644 --- a/bot/resources/easter/april_fools_vids.json +++ b/bot/resources/easter/april_fools_vids.json @@ -115,15 +115,15 @@ { "title": "Introducing Gmail Motion", "link": "https://youtu.be/Bu927_ul_X0" - } + }, { "title": "Introducing GeForce GTX G-Assist", "link": "https://youtu.be/smM-Wdk2RLQ" - } + }, { "title": "The Hovering Mouse - Project McFly | Razer", "link": "https://youtu.be/IlCx5gjAmqI" - } + }, { "title": "Be the Machine | Project Venom v2", "link": "https://youtu.be/j8UJE7DoyJ8" -- cgit v1.2.3 From 47cde431c13d9e20b3adfb28c39c0c0a9b0863fc Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Tue, 30 Mar 2021 23:06:34 +0530 Subject: retry until status code is 200 and make sure search query isn't very long --- bot/exts/evergreen/stackoverflow.py | 46 +++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 51ae567c..0aebac2d 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -1,9 +1,15 @@ +import logging from html import unescape from urllib.parse import quote_plus -from discord import Colour, Embed +from discord import Embed +from discord.errors import HTTPException from discord.ext import commands +from bot.constants import Colours + +logger = logging.getLogger(__name__) + BASE_URL = "https://api.stackexchange.com/2.2/search/advanced?order=desc&sort=activity&site=stackoverflow&q={query}" SEARCH_URL = "https://stackoverflow.com/search?q={query}" @@ -18,14 +24,37 @@ class Stackoverflow(commands.Cog): @commands.cooldown(1, 15, commands.cooldowns.BucketType.user) async def stackoverflow(self, ctx: commands.Context, *, search_query: str) -> None: """Sends the top 5 results of a search query from stackoverflow.""" - async with self.bot.http_session.get(BASE_URL.format(query=quote_plus(search_query))) as response: - data = await response.json() + for _ in range(3): + async with self.bot.http_session.get(BASE_URL.format(query=quote_plus(search_query))) as response: + if response.status == 200: + data = await response.json() + break + else: + logger.error(f'Status code is not 200, it is {response.status}') + continue + if response.status != 200: # If the status is still not 200 after the 3 tries + err_embed = Embed( + title="Error in fetching results from Stackoverflow", + description=("Sorry, there was en error while trying to fetch data from the Stackoverflow website. " + "Please try again in some time. If this issue persists, please contact the mods or send a " + "message in #dev-contrib."), + color=Colours.soft_red + ) + await ctx.send(embed=err_embed) + return + elif not data['items']: + err_embed = Embed( + title=f"No search results found for {search_query!r}", + color=Colours.soft_red + ) + await ctx.send(embed=err_embed) + return top5 = data["items"][:5] embed = Embed(title=f"Search results for {search_query!r} - Stackoverflow", url=SEARCH_URL.format(query=quote_plus(search_query)), description=f"Here are the top {len(top5)} results:", - color=Colour.orange) + color=Colours.orange) for item in top5: embed.add_field( name=f"{unescape(item['title'])}", @@ -36,7 +65,14 @@ class Stackoverflow(commands.Cog): f"({item['link']})"), inline=False) embed.set_footer(text="View the original link for more results.") - await ctx.send(embed=embed) + try: + await ctx.send(embed=embed) + except HTTPException: + err_embed = Embed( + title="Your search query is too long, please try shortening your search query", + color=Colours.soft_red + ) + await ctx.send(embed=err_embed) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From c082ab627c4a4ecf5e497f85b1424f78c73c2120 Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Tue, 30 Mar 2021 21:22:06 +0100 Subject: Consistent comments in bookmark command Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- bot/exts/evergreen/bookmark.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 59e2c9d0..2088c610 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -130,7 +130,8 @@ class Bookmark(commands.Cog): _, user = await self.bot.wait_for("reaction_add", timeout=TIMEOUT, check=event_check) except asyncio.TimeoutError: log.debug("Timed out waiting for a reaction") - break # No reactions for the last `TIMEOUT` seconds + # No reactions for the last `TIMEOUT` seconds + break log.trace(f"{user} has successfully bookmarked from a reaction, attempting to DM them.") await self.action_bookmark(ctx.channel, user, target_message, title) bookmarked_users.append(user.id) -- cgit v1.2.3 From fd57de601752db923f558b726e17af665a69df55 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 30 Mar 2021 21:26:10 +0100 Subject: Remove unnecessary underscores from variables in the bm command --- bot/exts/evergreen/bookmark.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 2088c610..16919d8c 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -104,19 +104,19 @@ class Bookmark(commands.Cog): await ctx.send(embed=embed) return - def event_check(reaction_: discord.Reaction, user_: discord.Member) -> bool: + def event_check(reaction: discord.Reaction, user: discord.Member) -> bool: """Make sure that this reaction is what we want to operate on.""" return ( # Conditions for a successful pagination: all(( # Reaction is on this message - reaction_.message.id == reaction_message.id, + reaction.message.id == reaction_message.id, # User has not already bookmarked this message - user_.id not in bookmarked_users, + user.id not in bookmarked_users, # Reaction is the `BOOKMARK_EMOJI` emoji - str(reaction_.emoji) == BOOKMARK_EMOJI, + str(reaction.emoji) == BOOKMARK_EMOJI, # Reaction was not made by the Bot - user_.id != self.bot.user.id + user.id != self.bot.user.id )) ) await self.action_bookmark(ctx.channel, ctx.author, target_message, title) -- cgit v1.2.3 From c22f1975144f3a96f440a1048010d91086a254e7 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Wed, 31 Mar 2021 09:27:49 +0000 Subject: Update our policy documents --- CODE_OF_CONDUCT.md | 3 ++ CONTRIBUTING.md | 126 +---------------------------------------------------- SECURITY.md | 3 ++ 3 files changed, 8 insertions(+), 124 deletions(-) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 SECURITY.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..57ccd80e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Code of Conduct + +The Python Discord Code of Conduct can be found [on our website](https://pydis.com/coc). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3a1803e2..f20b5316 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,125 +1,3 @@ -# Contributing to Sir Lancebot +# Contributing Guidelines -Sir Lancebot is a community project for the Python Discord community over at https://discord.gg/python. We will be providing support for those of you who are new to Git, and this project is to be considered educational. - -Our projects are open-source and are automatically deployed whenever commits are pushed to the `main` branch on each repository, so we've created a set of guidelines in order to keep everything clean and in working order. - -Note that contributions may be rejected on the basis of a contributor failing to follow these guidelines. - -## Rules - -1. You must be a member of [our Discord community](https://discord.gg/python) in order to contribute to this project. -2. Your pull request must solve an issue created or approved by a staff member. These will be labeled with the `approved` label. Feel free to suggest issues of your own, which staff can review for approval. -3. **No force-pushes** or modifying the Git history in any way. -4. If you have direct access to the repository, **create a branch for your changes** and create a pull request for that branch. If not, create a branch on a fork of the repository and create a pull request from there. - * It's common practice for a repository to reject direct pushes to `main`, so make branching a habit! - * If PRing from your own fork, **ensure that "Allow edits from maintainers" is checked**. This gives permission for maintainers to commit changes directly to your fork, speeding up the review process. -5. **Adhere to the prevailing code style**, which we enforce using [`flake8`](http://flake8.pycqa.org/en/latest/index.html) and [`pre-commit`](https://pre-commit.com/). - * Run `flake8` and `pre-commit` against your code [**before** you push it](https://soundcloud.com/lemonsaurusrex/lint-before-you-push). Your commit will be rejected by the build server if it fails to lint. - * [Git Hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) are a powerful git feature for executing custom scripts when certain important git actions occur. The pre-commit hook is the first hook executed during the commit process and can be used to check the code being committed & abort the commit if issues, such as linting failures, are detected. While git hooks can seem daunting to configure, the `pre-commit` framework abstracts this process away from you and is provided as a dev dependency for this project. Run `pipenv run precommit` when setting up the project and you'll never have to worry about committing code that fails linting. -6. **Make great commits**. A well structured git log is key to a project's maintainability; it efficiently provides insight into when and *why* things were done for future maintainers of the project. - * Commits should be as narrow in scope as possible. Commits that span hundreds of lines across multiple unrelated functions and/or files are very hard for maintainers to follow. After about a week they'll probably be hard for you to follow too. - * Avoid making minor commits for fixing typos or linting errors. Since you've already set up a `pre-commit` hook to run the linting pipeline before a commit, you shouldn't be committing linting issues anyway. - * A more in-depth guide to writing great commit messages can be found in Chris Beam's [*How to Write a Git Commit Message*](https://chris.beams.io/posts/git-commit/) -7. **Avoid frequent pushes to the main repository**. This goes for PRs opened against your fork as well. Our test build pipelines are triggered every time a push to the repository (or PR) is made. Try to batch your commits until you've finished working for that session, or you've reached a point where collaborators need your commits to continue their own work. This also provides you the opportunity to amend commits for minor changes rather than having to commit them on their own because you've already pushed. - * This includes merging main into your branch. Try to leave merging from main for after your PR passes review; a maintainer will bring your PR up to date before merging. Exceptions to this include: resolving merge conflicts, needing something that was pushed to main for your branch, or something was pushed to main that could potentionally affect the functionality of what you're writing. -8. **Don't fight the framework**. Every framework has its flaws, but the frameworks we've picked out have been carefully chosen for their particular merits. If you can avoid it, please resist reimplementing swathes of framework logic - the work has already been done for you! -9. If someone is working on an issue or pull request, **do not open your own pull request for the same task**. Instead, collaborate with the author(s) of the existing pull request. Duplicate PRs opened without communicating with the other author(s) and/or PyDis staff will be closed. Communication is key, and there's no point in two separate implementations of the same thing. - * One option is to fork the other contributor's repository and submit your changes to their branch with your own pull request. We suggest following these guidelines when interacting with their repository as well. - * The author(s) of inactive PRs and claimed issues will be be pinged after a week of inactivity for an update. Continued inactivity may result in the issue being released back to the community and/or PR closure. -10. **Work as a team** and collaborate wherever possible. Keep things friendly and help each other out - these are shared projects and nobody likes to have their feet trodden on. -11. All static content, such as images or audio, **must be licensed for open public use**. - * Static content must be hosted by a service designed to do so. Failing to do so is known as "leeching" and is frowned upon, as it generates extra bandwidth costs to the host without providing benefit. It would be best if appropriately licensed content is added to the repository itself so it can be served by PyDis' infrastructure. - -Above all, the needs of our community should come before the wants of an individual. Work together, build solutions to problems and try to do so in a way that people can learn from easily. Abuse of our trust may result in the loss of your Contributor role. - -## Changes to this Arrangement - -All projects evolve over time, and this contribution guide is no different. This document is open to pull requests or changes by contributors. If you believe you have something valuable to add or change, please don't hesitate to do so in a PR. - -## Supplemental Information -### Developer Environment -Sir Lancebot utilizes [Pipenv](https://pipenv.readthedocs.io/en/latest/) for installation and dependency management. For users unfamiliar with the Pipenv workflow, Pipenv's documentation provides a [Basic Usage](https://pipenv.readthedocs.io/en/latest/basics/) tutorial, along with some of the more advanced workflows. A project-specific installation guide can be found in [Sir Lancebot's README](https://github.com/python-discord/sir-lancebot/blob/main/README.md). - -When pulling down changes from GitHub, remember to sync your environment using `pipenv sync --dev` to ensure you're using the most up-to-date versions the project's dependencies. - -### Type Hinting -[PEP 484](https://www.python.org/dev/peps/pep-0484/) formally specifies type hints for Python functions, added to the Python Standard Library in version 3.5. Type hints are recognized by most modern code editing tools and provide useful insight into both the input and output types of a function, preventing the user from having to go through the codebase to determine these types. - -For example: - -```py -import typing as t - - -def foo(input_1: int, input_2: t.Dict[str, str]) -> bool: - ... -``` - -Tells us that `foo` accepts an `int` and a `dict`, with `str` keys and values, and returns a `bool`. - -All function declarations should be type hinted in code contributed to the PyDis organization. - -For more information, see *[PEP 483](https://www.python.org/dev/peps/pep-0483/) - The Theory of Type Hints* and Python's documentation for the [`typing`](https://docs.python.org/3/library/typing.html) module. - -### AutoDoc Formatting Directives -Many documentation packages provide support for automatic documentation generation from the codebase's docstrings. These tools utilize special formatting directives to enable richer formatting in the generated documentation. - -For example: - -```py -import typing as t - - -def foo(bar: int, baz: t.Optional[t.Dict[str, str]] = None) -> bool: - """ - Does some things with some stuff. - - :param bar: Some input - :param baz: Optional, some dictionary with string keys and values - - :return: Some boolean - """ - ... -``` - -Since PyDis does not utilize automatic documentation generation, use of this syntax should not be used in code contributed to the organization. Should the purpose and type of the input variables not be easily discernable from the variable name and type annotation, a prose explanation can be used. Explicit references to variables, functions, classes, etc. should be wrapped with backticks (`` ` ``). - -For example, the above docstring would become: - -```py -import typing as t - - -def foo(bar: int, baz: t.Optional[t.Dict[str, str]] = None) -> bool: - """ - Does some things with some stuff. - - This function takes an index, `bar` and checks for its presence in the database `baz`, passed as a dictionary. Returns `False` if `baz` is not passed. - """ - ... -``` - -### Logging Levels -The project currently defines [`logging`](https://docs.python.org/3/library/logging.html) levels as follows, from lowest to highest severity: -* **TRACE:** These events should be used to provide a *verbose* trace of every step of a complex process. This is essentially the `logging` equivalent of sprinkling `print` statements throughout the code. - * **Note:** This is a PyDis-implemented logging level. -* **DEBUG:** These events should add context to what's happening in a development setup to make it easier to follow what's going while working on a project. This is in the same vein as **TRACE** logging but at a much lower level of verbosity. -* **INFO:** These events are normal and don't need direct attention but are worth keeping track of in production, like checking which cogs were loaded during a start-up. -* **WARNING:** These events are out of the ordinary and should be fixed, but have not caused a failure. - * **NOTE:** Events at this logging level and higher should be reserved for events that require the attention of the DevOps team. -* **ERROR:** These events have caused a failure in a specific part of the application and require urgent attention. -* **CRITICAL:** These events have caused the whole application to fail and require immediate intervention. - -Ensure that log messages are succinct. Should you want to pass additional useful information that would otherwise make the log message overly verbose the `logging` module accepts an `extra` kwarg, which can be used to pass a dictionary. This is used to populate the `__dict__` of the `LogRecord` created for the logging event with user-defined attributes that can be accessed by a log handler. Additional information and caveats may be found [in Python's `logging` documentation](https://docs.python.org/3/library/logging.html#logging.Logger.debug). - -### Work in Progress (WIP) PRs -Github [provides a PR feature](https://github.blog/2019-02-14-introducing-draft-pull-requests/) that allows the PR author to mark it as a WIP. This provides both a visual and functional indicator that the contents of the PR are in a draft state and not yet ready for formal review. - -This feature should be utilized in place of the traditional method of prepending `[WIP]` to the PR title. - -As stated earlier, **ensure that "Allow edits from maintainers" is checked**. This gives permission for maintainers to commit changes directly to your fork, speeding up the review process. - -## Footnotes - -This document was inspired by the [Glowstone contribution guidelines](https://github.com/GlowstoneMC/Glowstone/blob/dev/docs/CONTRIBUTING.md). +The Contributing Guidelines for Python Discord projects can be found [on our website](https://pydis.com/contributing.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..fa5a88a3 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +# Security Notice + +The Security Notice for Python Discord projects can be found [on our website](https://pydis.com/security.md). -- cgit v1.2.3 From ada498686700a2a4678b5233339577fd72f6b570 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Wed, 31 Mar 2021 20:17:51 +0530 Subject: Use a variable for the encoded search query. --- bot/exts/evergreen/stackoverflow.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 0aebac2d..721f31f4 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -24,8 +24,10 @@ class Stackoverflow(commands.Cog): @commands.cooldown(1, 15, commands.cooldowns.BucketType.user) async def stackoverflow(self, ctx: commands.Context, *, search_query: str) -> None: """Sends the top 5 results of a search query from stackoverflow.""" + encoded_search_query = quote_plus(search_query) + for _ in range(3): - async with self.bot.http_session.get(BASE_URL.format(query=quote_plus(search_query))) as response: + async with self.bot.http_session.get(BASE_URL.format(query=encoded_search_query)) as response: if response.status == 200: data = await response.json() break @@ -52,7 +54,7 @@ class Stackoverflow(commands.Cog): top5 = data["items"][:5] embed = Embed(title=f"Search results for {search_query!r} - Stackoverflow", - url=SEARCH_URL.format(query=quote_plus(search_query)), + url=SEARCH_URL.format(query=encoded_search_query), description=f"Here are the top {len(top5)} results:", color=Colours.orange) for item in top5: -- cgit v1.2.3 From e3b7b3bc5b56b805112558377e25e2537cc86083 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 2 Apr 2021 14:53:27 +0100 Subject: Don't allow users to run the issue command in DMs, given error feedback. --- bot/exts/evergreen/issues.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 1f22f287..0f0be33a 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -7,7 +7,16 @@ from enum import Enum import discord from discord.ext import commands, tasks -from bot.constants import Categories, Channels, Colours, ERROR_REPLIES, Emojis, Tokens, WHITELISTED_CHANNELS +from bot.constants import ( + Categories, + Channels, + Colours, + ERROR_REPLIES, + Emojis, + NEGATIVE_REPLIES, + Tokens, + WHITELISTED_CHANNELS +) log = logging.getLogger(__name__) @@ -148,11 +157,20 @@ class Issues(commands.Cog): user: str = "python-discord" ) -> None: """Command to retrieve issue(s) from a GitHub repository.""" - if not( + if not ctx.guild or not( ctx.channel.category.id in WHITELISTED_CATEGORIES or ctx.channel.id in WHITELISTED_CHANNELS ): - return + await ctx.send( + embed=discord.Embed( + title=random.choice(NEGATIVE_REPLIES), + description=( + "You can't run this command in this channel. " + f"Try again in {Channels.community_bot_commands}" + ), + colour=discord.Colour.red() + ) + ) result = await self.fetch_issues(set(numbers), repository, user) -- cgit v1.2.3 From 6a8a582c49c2a3d97a2a061eca157502acb0618d Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 2 Apr 2021 17:02:47 +0100 Subject: Send error embed when a user tries to retrieve an issue in DMs --- bot/exts/evergreen/issues.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index ca0e4494..4a73d20b 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -168,11 +168,12 @@ class Issues(commands.Cog): title=random.choice(NEGATIVE_REPLIES), description=( "You can't run this command in this channel. " - f"Try again in {Channels.community_bot_commands}" + f"Try again in <#{Channels.community_bot_commands}>" ), colour=discord.Colour.red() ) ) + return result = await self.fetch_issues(set(numbers), repository, user) @@ -198,12 +199,8 @@ class Issues(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - # Ignore messages from DMs - if not message.guild: - return - - # Ignore messages not in whitelisted categories / channels - if not ( + # Ignore messages not in whitelisted categories / channels, only when in guild. + if message.guild and not ( message.channel.category.id in WHITELISTED_CATEGORIES or message.channel.id in WHITELISTED_CHANNELS_ON_MESSAGE ): @@ -213,6 +210,18 @@ class Issues(commands.Cog): links = [] if message_repo_issue_map: + if not message.guild: + await message.channel.send( + embed=discord.Embed( + title=random.choice(NEGATIVE_REPLIES), + description=( + "You can't retreive issues from DMs. " + f"Try again in <#{Channels.community_bot_commands}>" + ), + colour=discord.Colour.red() + ) + ) + return for repo_issue in message_repo_issue_map: if not self.check_in_block(message, " ".join(repo_issue)): result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") -- cgit v1.2.3 From 4e151344832b2e17a188c943aa5eedc51bead3cc Mon Sep 17 00:00:00 2001 From: Sougata das Date: Fri, 2 Apr 2021 23:32:53 +0530 Subject: Update battleship.py --- bot/exts/evergreen/battleship.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/battleship.py b/bot/exts/evergreen/battleship.py index fa3fb35c..1681434f 100644 --- a/bot/exts/evergreen/battleship.py +++ b/bot/exts/evergreen/battleship.py @@ -227,7 +227,7 @@ class Game: if message.content.lower() == "surrender": self.surrender = True return True - self.match = re.match("([A-J]|[a-j]) ?((10)|[1-9])", message.content.strip()) + self.match = re.fullmatch("([A-J]|[a-j]) ?((10)|[1-9])", message.content.strip()) if not self.match: self.bot.loop.create_task(message.add_reaction(CROSS_EMOJI)) return bool(self.match) -- cgit v1.2.3 From 09eeac31df1fd9138d81d057829dd20cb8504ef9 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sat, 3 Apr 2021 11:18:38 +0530 Subject: Use constants for delete delay and remove redundant f-string. --- bot/constants.py | 5 +++++ bot/exts/evergreen/error_handler.py | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 416dd0e7..ba4345bc 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -20,6 +20,7 @@ __all__ = ( "Tokens", "Wolfram", "RedisConfig", + "RedirectOutput", "MODERATION_ROLES", "STAFF_ROLES", "WHITELISTED_CHANNELS", @@ -270,6 +271,10 @@ class Source: github_avatar_url = "https://avatars1.githubusercontent.com/u/9919" +class RedirectOutput: + delete_delay: int = 10 + + # Default role combinations MODERATION_ROLES = Roles.moderator, Roles.admin, Roles.owner STAFF_ROLES = Roles.helpers, Roles.moderator, Roles.admin, Roles.owner diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index 3d056f81..f74218b7 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -8,7 +8,7 @@ from discord import Embed, Message from discord.ext import commands from sentry_sdk import push_scope -from bot.constants import Channels, Colours, ERROR_REPLIES, NEGATIVE_REPLIES +from bot.constants import Channels, Colours, ERROR_REPLIES, NEGATIVE_REPLIES, RedirectOutput from bot.utils.decorators import InChannelCheckFailure, InMonthCheckFailure from bot.utils.exceptions import UserNotPlayingError @@ -159,8 +159,8 @@ class CommandErrorHandler(commands.Cog): misspelled_content = ctx.message.content e = Embed() e.set_author(name="Did you mean:", icon_url=QUESTION_MARK_ICON) - e.description = f"{misspelled_content.replace(command_name, similar_command_name, 1)}" - await ctx.send(embed=e, delete_after=10.0) + e.description = misspelled_content.replace(command_name, similar_command_name, 1) + await ctx.send(embed=e, delete_after=RedirectOutput.delete_delay) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From 50cd34e73d77bf71fe214250d5f20a2b4d98664d Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Mon, 5 Apr 2021 15:01:58 +0100 Subject: fix: use get_user in 8bitify to avoid events issues --- bot/exts/evergreen/8bitify.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/8bitify.py b/bot/exts/evergreen/8bitify.py index 54e68f80..ee2b4034 100644 --- a/bot/exts/evergreen/8bitify.py +++ b/bot/exts/evergreen/8bitify.py @@ -25,7 +25,8 @@ class EightBitify(commands.Cog): async def eightbit_command(self, ctx: commands.Context) -> None: """Pixelates your avatar and changes the palette to an 8bit one.""" async with ctx.typing(): - image_bytes = await ctx.author.avatar_url.read() + author = await self.bot.get_user(ctx.author.id) + image_bytes = await author.avatar_url.read() avatar = Image.open(BytesIO(image_bytes)) avatar = avatar.convert("RGBA").resize((1024, 1024)) -- cgit v1.2.3 From 9edeff2eea5df363446e98d2905283562849619b Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Mon, 5 Apr 2021 16:08:24 +0200 Subject: Issues: remove duplicates --- bot/exts/evergreen/issues.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 3d23b869..d7e29a90 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -181,6 +181,9 @@ class Issues(commands.Cog): links = [] if message_repo_issue_map: + # Remove duplicates + message_repo_issue_map = set(message_repo_issue_map) + for repo_issue in message_repo_issue_map: if not self.check_in_block(message, " ".join(repo_issue)): result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") -- cgit v1.2.3 From 0e2422f7caaec73fbee9708c507d9be23635dd78 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Mon, 5 Apr 2021 16:43:28 +0200 Subject: Issues: limit results to 5 --- bot/exts/evergreen/issues.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index d7e29a90..afaa7012 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -15,8 +15,6 @@ BAD_RESPONSE = { 404: "Issue/pull request not located! Please enter a valid number!", 403: "Rate limit has been hit! Please try again later!" } - -MAX_REQUESTS = 10 REQUEST_HEADERS = dict() REPOS_API = "https://api.github.com/orgs/{org}/repos" @@ -33,6 +31,9 @@ CODE_BLOCK_RE = re.compile( re.DOTALL | re.MULTILINE ) +# Maximum number of issues in one message +MAXIMUM_ISSUES = 5 + class FetchIssueErrors(Enum): """Errors returned in fetch issues.""" @@ -83,7 +84,7 @@ class Issues(commands.Cog): if not numbers: return FetchIssueErrors.value_error - if len(numbers) > MAX_REQUESTS: + if len(numbers) > MAXIMUM_ISSUES: return FetchIssueErrors.max_requests for number in numbers: @@ -162,7 +163,7 @@ class Issues(commands.Cog): embed = discord.Embed( title=random.choice(ERROR_REPLIES), color=Colours.soft_red, - description=f"Too many issues/PRs! (maximum of {MAX_REQUESTS})" + description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" ) await ctx.send(embed=embed) @@ -184,6 +185,15 @@ class Issues(commands.Cog): # Remove duplicates message_repo_issue_map = set(message_repo_issue_map) + if len(message_repo_issue_map) > MAXIMUM_ISSUES: + embed = discord.Embed( + title=random.choice(ERROR_REPLIES), + color=Colours.soft_red, + description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" + ) + await message.channel.send(embed=embed) + return + for repo_issue in message_repo_issue_map: if not self.check_in_block(message, " ".join(repo_issue)): result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") -- cgit v1.2.3 From 30756d4dcc2dcdba334cc3493d82eccdc9c6aded Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Mon, 5 Apr 2021 15:45:10 +0100 Subject: fix: use api fetch not cache get --- bot/exts/evergreen/8bitify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/8bitify.py b/bot/exts/evergreen/8bitify.py index ee2b4034..7eb4d313 100644 --- a/bot/exts/evergreen/8bitify.py +++ b/bot/exts/evergreen/8bitify.py @@ -25,7 +25,7 @@ class EightBitify(commands.Cog): async def eightbit_command(self, ctx: commands.Context) -> None: """Pixelates your avatar and changes the palette to an 8bit one.""" async with ctx.typing(): - author = await self.bot.get_user(ctx.author.id) + author = await self.bot.fetch_user(ctx.author.id) image_bytes = await author.avatar_url.read() avatar = Image.open(BytesIO(image_bytes)) avatar = avatar.convert("RGBA").resize((1024, 1024)) -- cgit v1.2.3 From 2d03c9956e17709d00daeda00b1e778214708d50 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Mon, 5 Apr 2021 18:56:28 +0300 Subject: Migrate GHCR_TOKEN -> GITHUB_TOKEN in Docker build action GITHUB_TOKEN has now enough permissions to be used for pushing to GHCR, so we don't need PAT anymore. --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 0a006eb9..baa046ce 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -36,7 +36,7 @@ jobs: with: registry: ghcr.io username: ${{ github.repository_owner }} - password: ${{ secrets.GHCR_TOKEN }} + password: ${{ secrets.GITHUB_TOKEN }} # Build and push the container to the GitHub Container # Repository. The container will be tagged as "latest" -- cgit v1.2.3 From 2ad7b496c742d275d717b2de410caebdbeaa50d4 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 5 Apr 2021 17:16:52 +0100 Subject: Fetch member before modifying their pfp We do this as the member cache may have an outdated version of their pfp, which can lead to errors if it's removed from the Discord CDN. Co-authored-by: vcokltfre --- bot/constants.py | 1 + .../profile_pic_modification/pfp_modify.py | 65 +++++++++++++++++----- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 3ca2cda9..853ea340 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -165,6 +165,7 @@ class Colours: class Emojis: + cross_mark = "\u274C" star = "\u2B50" christmas_tree = "\U0001F384" check = "\u2611" diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 9242ff0c..c4b74d04 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -9,7 +9,7 @@ from aiohttp import client_exceptions from discord.ext import commands from discord.ext.commands.errors import BadArgument -from bot.constants import Colours +from bot.constants import Client, Colours, Emojis from bot.exts.evergreen.profile_pic_modification._effects import PfpEffects from bot.utils.extensions import invoke_help_command from bot.utils.halloween import spookifications @@ -42,6 +42,24 @@ class PfpModify(commands.Cog): def __init__(self, bot: commands.Bot) -> None: self.bot = bot + async def _fetch_member(self, member_id: int) -> t.Optional[discord.Member]: + """ + Fetches a member and handles errors. + + This helper funciton is required as the member cache doesn't always have the most up to date + profile picture. THis can lead to errors if the image is delted from the Discord CDN. + """ + try: + member = await self.bot.get_guild(Client.guild).fetch_member(member_id) + except discord.errors.NotFound: + log.debug(f"Member {member_id} left the guild before we could get their pfp.") + return None + except discord.HTTPException: + log.exception(f"Exception while trying to retrieve member {member_id} from Discord.") + return None + + return member + @commands.group() async def pfp_modify(self, ctx: commands.Context) -> None: """Groups all of the pfp modifying commands to allow a single concurrency limit.""" @@ -52,10 +70,15 @@ class PfpModify(commands.Cog): async def eightbit_command(self, ctx: commands.Context) -> None: """Pixelates your avatar and changes the palette to an 8bit one.""" async with ctx.typing(): - image_bytes = await ctx.author.avatar_url.read() + member = await self._fetch_member(ctx.author.id) + if not member: + ctx.send(f"{Emojis.cross_mark} Could not get member info.") + return + + image_bytes = await member.avatar_url.read() file_name = FILENAME_STRING.format( effect="eightbit_avatar", - author=ctx.author.display_name + author=member.display_name ) file = await in_executor( @@ -71,7 +94,7 @@ class PfpModify(commands.Cog): ) embed.set_image(url=f"attachment://{file_name}") - embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) + embed.set_footer(text=f"Made by {member.display_name}.", icon_url=member.avatar_url) await ctx.send(embed=embed, file=file) @@ -96,6 +119,11 @@ class PfpModify(commands.Cog): return args[0] async with ctx.typing(): + member = await self._fetch_member(ctx.author.id) + if not member: + ctx.send(f"{Emojis.cross_mark} Could not get member info.") + return + egg = None if colours: send_message = ctx.send @@ -106,10 +134,10 @@ class PfpModify(commands.Cog): return ctx.send = send_message # Reassigns ctx.send - image_bytes = await ctx.author.avatar_url_as(size=256).read() + image_bytes = await member.avatar_url_as(size=256).read() file_name = FILENAME_STRING.format( effect="easterified_avatar", - author=ctx.author.display_name + author=member.display_name ) file = await in_executor( @@ -125,7 +153,7 @@ class PfpModify(commands.Cog): description="Here is your lovely avatar, all bright and colourful\nwith Easter pastel colours. Enjoy :D" ) embed.set_image(url=f"attachment://{file_name}") - embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) + embed.set_footer(text=f"Made by {member.display_name}.", icon_url=member.avatar_url) await ctx.send(file=file, embed=embed) @@ -183,7 +211,11 @@ class PfpModify(commands.Cog): return async with ctx.typing(): - image_bytes = await ctx.author.avatar_url.read() + member = await self._fetch_member(ctx.author.id) + if not member: + ctx.send(f"{Emojis.cross_mark} Could not get member info.") + return + image_bytes = await member.avatar_url.read() await self.send_pride_image(ctx, image_bytes, pixels, flag, option) @prideavatar.command() @@ -235,17 +267,22 @@ class PfpModify(commands.Cog): root_aliases=('spookyavatar', 'spookify', 'savatar'), brief='Spookify an user\'s avatar.' ) - async def spooky_avatar(self, ctx: commands.Context, user: discord.Member = None) -> None: + async def spooky_avatar(self, ctx: commands.Context, member: discord.Member = None) -> None: """This "spookifies" the given user's avatar, with a random *spooky* effect.""" - if user is None: - user = ctx.message.author + if member is None: + member = ctx.author + + member = await self._fetch_member(member.id) + if not member: + ctx.send(f"{Emojis.cross_mark} Could not get member info.") + return async with ctx.typing(): - image_bytes = await user.avatar_url.read() + image_bytes = await member.avatar_url.read() file_name = FILENAME_STRING.format( effect="spooky_avatar", - author=user.display_name + author=member.display_name ) file = await in_executor( PfpEffects.apply_effect, @@ -258,7 +295,7 @@ class PfpModify(commands.Cog): title="Is this you or am I just really paranoid?", colour=Colours.soft_red ) - embed.set_author(name=user.name, icon_url=user.avatar_url) + embed.set_author(name=member.name, icon_url=member.avatar_url) embed.set_image(url=f"attachment://{file_name}") embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) -- cgit v1.2.3 From 08b1348b33d2d865987f2677a68ba62645db2abe Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Tue, 6 Apr 2021 10:05:40 +0200 Subject: Rewrite issue cog - Switch to a dataclass based communication - Use GitHub headers when querying the repo_regex - Properly handle non-200 return codes - Use @whitelist_override --- bot/exts/evergreen/issues.py | 205 +++++++++++++++++++++++++------------------ 1 file changed, 119 insertions(+), 86 deletions(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index afaa7012..dce11678 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -2,12 +2,13 @@ import logging import random import re import typing as t -from enum import Enum +from dataclasses import dataclass import discord from discord.ext import commands, tasks from bot.constants import Categories, Colours, ERROR_REPLIES, Emojis, Tokens, WHITELISTED_CHANNELS +from bot.utils.decorators import whitelist_override log = logging.getLogger(__name__) @@ -17,7 +18,10 @@ BAD_RESPONSE = { } REQUEST_HEADERS = dict() -REPOS_API = "https://api.github.com/orgs/{org}/repos" +REPOSITORY_ENDPOINT = "https://api.github.com/orgs/{org}/repos" +ISSUE_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/issues/{number}" +PR_MERGE_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/pulls/{number}/merge" + if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" @@ -35,11 +39,23 @@ CODE_BLOCK_RE = re.compile( MAXIMUM_ISSUES = 5 -class FetchIssueErrors(Enum): - """Errors returned in fetch issues.""" +@dataclass +class FetchError: + """Dataclass representing an error while fetching an issue.""" + + return_code: int + message: str + - value_error = "Numbers not found." - max_requests = "Max requests hit." +@dataclass +class IssueState: + """Dataclass representing the state of an issue.""" + + repository: str + number: int + url: str + title: str + icon_url: str class Issues(commands.Cog): @@ -48,19 +64,27 @@ class Issues(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot self.repos = [] + self.repo_regex = None self.get_pydis_repos.start() @tasks.loop(minutes=30) async def get_pydis_repos(self) -> None: - """Get all python-discord repositories on github.""" - async with self.bot.http_session.get(REPOS_API.format(org="python-discord")) as resp: + """ + Get all python-discord repositories on github. + + This task will update a pipe-separated list of repositories in self.repo_regex. + """ + async with self.bot.http_session.get( + REPOSITORY_ENDPOINT.format(org="python-discord"), + headers=REQUEST_HEADERS + ) as resp: if resp.status == 200: data = await resp.json() for repo in data: self.repos.append(repo["full_name"].split("/")[1]) self.repo_regex = "|".join(self.repos) else: - log.debug(f"Failed to get latest Pydis repositories. Status code {resp.status}") + log.warning(f"Failed to get latest Pydis repositories. Status code {resp.status}") @staticmethod def check_in_block(message: discord.Message, repo_issue: str) -> bool: @@ -75,70 +99,85 @@ class Issues(commands.Cog): async def fetch_issues( self, - numbers: set, + number: int, repository: str, user: str - ) -> t.Union[FetchIssueErrors, str, list]: - """Retrieve issue(s) from a GitHub repository.""" - links = [] - if not numbers: - return FetchIssueErrors.value_error - - if len(numbers) > MAXIMUM_ISSUES: - return FetchIssueErrors.max_requests - - for number in numbers: - url = f"https://api.github.com/repos/{user}/{repository}/issues/{number}" - merge_url = f"https://api.github.com/repos/{user}/{repository}/pulls/{number}/merge" - log.trace(f"Querying GH issues API: {url}") - async with self.bot.http_session.get(url, headers=REQUEST_HEADERS) as r: - json_data = await r.json() - - if r.status in BAD_RESPONSE: - log.warning(f"Received response {r.status} from: {url}") - return f"[{str(r.status)}] #{number} {BAD_RESPONSE.get(r.status)}" - - # The initial API request is made to the issues API endpoint, which will return information - # if the issue or PR is present. However, the scope of information returned for PRs differs - # from issues: if the 'issues' key is present in the response then we can pull the data we - # need from the initial API call. - if "issues" in json_data.get("html_url"): + ) -> t.Union[IssueState, FetchError]: + """ + Retrieve an issue from a GitHub repository. + + returns IssueState on success, FetchError on failure. + """ + url = ISSUE_ENDPOINT.format(user=user, repository=repository, number=number) + merge_url = PR_MERGE_ENDPOINT.format(user=user, repository=repository, number=number) + log.trace(f"Querying GH issues API: {url}") + + async with self.bot.http_session.get(url, headers=REQUEST_HEADERS) as r: + json_data = await r.json() + + if r.status == 403: + if "X-RateLimit-Remaining" in r.headers and r.headers["X-RateLimit-Remaining"] == "0": + log.info(f"Ratelimit reached while fetching {url}") + return FetchError(403, "Ratelimit reached, please retry in a few minutes.") + return FetchError(403, "Cannot access issue.") + elif r.status in (404, 410): + return FetchError(r.status, "Issue not found.") + elif r.status != 200: + return FetchError(r.status, "Error while fetching issue.") + + # The initial API request is made to the issues API endpoint, which will return information + # if the issue or PR is present. However, the scope of information returned for PRs differs + # from issues: if the 'issues' key is present in the response then we can pull the data we + # need from the initial API call. + if "issues" in json_data.get("html_url"): + if json_data.get("state") == "open": + icon_url = Emojis.issue + else: + icon_url = Emojis.issue_closed + + # If the 'issues' key is not contained in the API response and there is no error code, then + # we know that a PR has been requested and a call to the pulls API endpoint is necessary + # to get the desired information for the PR. + else: + log.trace(f"PR provided, querying GH pulls API for additional information: {merge_url}") + async with self.bot.http_session.get(merge_url) as m: if json_data.get("state") == "open": - icon_url = Emojis.issue + icon_url = Emojis.pull_request + # When the status is 204 this means that the state of the PR is merged + elif m.status == 204: + icon_url = Emojis.merge else: - icon_url = Emojis.issue_closed - - # If the 'issues' key is not contained in the API response and there is no error code, then - # we know that a PR has been requested and a call to the pulls API endpoint is necessary - # to get the desired information for the PR. - else: - log.trace(f"PR provided, querying GH pulls API for additional information: {merge_url}") - async with self.bot.http_session.get(merge_url) as m: - if json_data.get("state") == "open": - icon_url = Emojis.pull_request - # When the status is 204 this means that the state of the PR is merged - elif m.status == 204: - icon_url = Emojis.merge - else: - icon_url = Emojis.pull_request_closed + icon_url = Emojis.pull_request_closed - issue_url = json_data.get("html_url") - links.append([icon_url, f"[{repository}] #{number} {json_data.get('title')}", issue_url]) + issue_url = json_data.get("html_url") - return links + return IssueState(repository, number, issue_url, json_data.get('title', ''), icon_url) @staticmethod - def get_embed(result: list, user: str = "python-discord", repository: str = "") -> discord.Embed: - """Get Response Embed.""" - description_list = ["{0} [{1}]({2})".format(*link) for link in result] + def format_embed( + results: t.List[t.Union[IssueState, FetchError]], + user: str, + repository: t.Optional[str] = None + ) -> discord.Embed: + """Take a list of IssueState or FetchError and format a Discord embed for them.""" + description_list = [] + + for result in results: + if isinstance(result, IssueState): + description_list.append(f"{result.icon_url} [{result.title}]({result.url})") + elif isinstance(result, FetchError): + description_list.append(f"[{result.return_code}] {result.message}") + resp = discord.Embed( colour=Colours.bright_green, description='\n'.join(description_list) ) - resp.set_author(name="GitHub", url=f"https://github.com/{user}/{repository}") + embed_url = f"https://github.com/{user}/{repository}" if repository else f"https://github.com/{user}" + resp.set_author(name="GitHub", url=embed_url) return resp + @whitelist_override(channels=WHITELISTED_CHANNELS, categories=WHITELISTED_CATEGORIES) @commands.command(aliases=("pr",)) async def issue( self, @@ -148,62 +187,56 @@ class Issues(commands.Cog): user: str = "python-discord" ) -> None: """Command to retrieve issue(s) from a GitHub repository.""" - if not( - ctx.channel.category.id in WHITELISTED_CATEGORIES - or ctx.channel.id in WHITELISTED_CHANNELS - ): - return - - result = await self.fetch_issues(set(numbers), repository, user) - - if result == FetchIssueErrors.value_error: - await ctx.invoke(self.bot.get_command('help'), 'issue') + # Remove duplicates + numbers = set(numbers) - elif result == FetchIssueErrors.max_requests: + if len(numbers) > MAXIMUM_ISSUES: embed = discord.Embed( title=random.choice(ERROR_REPLIES), color=Colours.soft_red, description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" ) await ctx.send(embed=embed) + await ctx.invoke(self.bot.get_command('help'), 'issue') - elif isinstance(result, list): - # Issue/PR format: emoji to show if open/closed/merged, number and the title as a singular link. - resp = self.get_embed(result, user, repository) - await ctx.send(embed=resp) - - elif isinstance(result, str): - await ctx.send(result) + results = [await self.fetch_issues(number, repository, user) for number in numbers] + await ctx.send(embed=self.format_embed(results)) @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - message_repo_issue_map = re.findall(fr"({self.repo_regex})#(\d+)", message.content) + if not self.repo_regex: + log.warning("repo_regex isn't ready, cannot look for issues.") + return + + # `issues` will hold a list of two element tuples `(repository, issue_number)` + issues = re.findall(fr"({self.repo_regex})#(\d+)", message.content) links = [] - if message_repo_issue_map: + if issues: + log.trace(f"Found {issues = }") # Remove duplicates - message_repo_issue_map = set(message_repo_issue_map) + issues = set(issues) - if len(message_repo_issue_map) > MAXIMUM_ISSUES: + if len(issues) > MAXIMUM_ISSUES: embed = discord.Embed( title=random.choice(ERROR_REPLIES), color=Colours.soft_red, description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" ) - await message.channel.send(embed=embed) + await message.channel.send(embed=embed).delete(delay=5) return - for repo_issue in message_repo_issue_map: + for repo_issue in issues: if not self.check_in_block(message, " ".join(repo_issue)): - result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") - if isinstance(result, list): - links.extend(result) + result = await self.fetch_issues(repo_issue[1], repo_issue[0], "python-discord") + if isinstance(result, IssueState): + links.append(result) if not links: return - resp = self.get_embed(links, "python-discord") + resp = self.format_embed(links, "python-discord") await message.channel.send(embed=resp) -- cgit v1.2.3 From f8e14d089e87cc2628a9087a60bf0c3cc6eefc39 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Tue, 6 Apr 2021 10:13:31 +0200 Subject: Issues: ignore bots --- bot/exts/evergreen/issues.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index dce11678..7e9defbe 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -209,6 +209,10 @@ class Issues(commands.Cog): log.warning("repo_regex isn't ready, cannot look for issues.") return + # Ignore bots + if message.author.bot: + return + # `issues` will hold a list of two element tuples `(repository, issue_number)` issues = re.findall(fr"({self.repo_regex})#(\d+)", message.content) links = [] -- cgit v1.2.3 From fd2ff1d06e31a9b0f412c2a81e373bf46fe192f7 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Thu, 8 Apr 2021 03:15:47 +0100 Subject: increase the number of repos per page we fetch from github --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 4a73d20b..58f9d7aa 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -28,7 +28,7 @@ BAD_RESPONSE = { MAX_REQUESTS = 10 REQUEST_HEADERS = dict() -REPOS_API = "https://api.github.com/orgs/{org}/repos" +REPOS_API = "https://api.github.com/orgs/{org}/repos?per_page=100" if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" -- cgit v1.2.3 From 291997462779b79ff088432d7333bee9b2ff2f2c Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 8 Apr 2021 09:07:01 +0200 Subject: Refactor issue cog Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/evergreen/issues.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 7255d450..bcaeee1f 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -115,7 +115,7 @@ class Issues(commands.Cog): """ Retrieve an issue from a GitHub repository. - returns IssueState on success, FetchError on failure. + Returns IssueState on success, FetchError on failure. """ url = ISSUE_ENDPOINT.format(user=user, repository=repository, number=number) merge_url = PR_MERGE_ENDPOINT.format(user=user, repository=repository, number=number) @@ -125,7 +125,7 @@ class Issues(commands.Cog): json_data = await r.json() if r.status == 403: - if "X-RateLimit-Remaining" in r.headers and r.headers["X-RateLimit-Remaining"] == "0": + if r.headers.get("X-RateLimit-Remaining") == "0": log.info(f"Ratelimit reached while fetching {url}") return FetchError(403, "Ratelimit reached, please retry in a few minutes.") return FetchError(403, "Cannot access issue.") @@ -138,7 +138,7 @@ class Issues(commands.Cog): # if the issue or PR is present. However, the scope of information returned for PRs differs # from issues: if the 'issues' key is present in the response then we can pull the data we # need from the initial API call. - if "issues" in json_data.get("html_url"): + if "issues" in json_data["html_url"]: if json_data.get("state") == "open": icon_url = Emojis.issue else: @@ -251,7 +251,7 @@ class Issues(commands.Cog): color=Colours.soft_red, description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" ) - await message.channel.send(embed=embed).delete(delay=5) + await message.channel.send(embed=embed, delete_after=5) return for repo_issue in issues: -- cgit v1.2.3 From cd0153d36f081f2a0a41b610eac86ff52211e5c5 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 8 Apr 2021 09:10:48 +0200 Subject: Issues: make use of invoke_help_command Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/evergreen/issues.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index bcaeee1f..d8b373d7 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -18,6 +18,7 @@ from bot.constants import ( WHITELISTED_CHANNELS ) from bot.utils.decorators import whitelist_override +from bot.utils.extensions import invoke_help_command log = logging.getLogger(__name__) @@ -206,7 +207,7 @@ class Issues(commands.Cog): description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" ) await ctx.send(embed=embed) - await ctx.invoke(self.bot.get_command('help'), 'issue') + await invoke_help_command(ctx) results = [await self.fetch_issues(number, repository, user) for number in numbers] await ctx.send(embed=self.format_embed(results, user, repository)) -- cgit v1.2.3 From 422f6d50b8eeafdf2118f1d0e96c420c0b1391ce Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Thu, 8 Apr 2021 10:04:14 +0100 Subject: Refactor pfp cog to remove unnecessary params and calls Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/evergreen/profile_pic_modification/_effects.py | 2 +- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py index a1069db7..e415d700 100644 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ b/bot/exts/evergreen/profile_pic_modification/_effects.py @@ -12,7 +12,7 @@ class PfpEffects: """ Implements various image modifying effects, for the PfpModify cog. - All of these fuctions are slow, and blocking, so should be ran in executors. + All of these fuctions are slow, and blocking, so they should be ran in executors. """ @staticmethod diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index c4b74d04..712631d1 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -21,7 +21,7 @@ _EXECUTOR = ThreadPoolExecutor(10) FILENAME_STRING = "{effect}_{author}.png" with open('bot/resources/pride/gender_options.json') as f: - GENDER_OPTIONS = json.loads(f.read()) + GENDER_OPTIONS = json.load(f) async def in_executor(func: t.Callable, *args) -> t.Any: @@ -98,7 +98,7 @@ class PfpModify(commands.Cog): await ctx.send(embed=embed, file=file) - @pfp_modify.command(pass_context=True, aliases=["easterify"], root_aliases=("easterify", "avatareasterify")) + @pfp_modify.command(aliases=["easterify"], root_aliases=("easterify", "avatareasterify")) async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: """ This "Easterifies" the user's avatar. -- cgit v1.2.3 From c9e4916cb1cad8d43148030660575f5084710d38 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 8 Apr 2021 10:05:53 +0100 Subject: Ensure to await ctx.send() calls --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 712631d1..68d68927 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -72,7 +72,7 @@ class PfpModify(commands.Cog): async with ctx.typing(): member = await self._fetch_member(ctx.author.id) if not member: - ctx.send(f"{Emojis.cross_mark} Could not get member info.") + await ctx.send(f"{Emojis.cross_mark} Could not get member info.") return image_bytes = await member.avatar_url.read() @@ -121,7 +121,7 @@ class PfpModify(commands.Cog): async with ctx.typing(): member = await self._fetch_member(ctx.author.id) if not member: - ctx.send(f"{Emojis.cross_mark} Could not get member info.") + await ctx.send(f"{Emojis.cross_mark} Could not get member info.") return egg = None @@ -213,7 +213,7 @@ class PfpModify(commands.Cog): async with ctx.typing(): member = await self._fetch_member(ctx.author.id) if not member: - ctx.send(f"{Emojis.cross_mark} Could not get member info.") + await ctx.send(f"{Emojis.cross_mark} Could not get member info.") return image_bytes = await member.avatar_url.read() await self.send_pride_image(ctx, image_bytes, pixels, flag, option) @@ -274,7 +274,7 @@ class PfpModify(commands.Cog): member = await self._fetch_member(member.id) if not member: - ctx.send(f"{Emojis.cross_mark} Could not get member info.") + await ctx.send(f"{Emojis.cross_mark} Could not get member info.") return async with ctx.typing(): -- cgit v1.2.3 From d067f4d4a63d96d463aa289e6291ddf1710a8af5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Apr 2021 09:09:53 +0000 Subject: Bump pyyaml from 5.3.1 to 5.4 Bumps [pyyaml](https://github.com/yaml/pyyaml) from 5.3.1 to 5.4. - [Release notes](https://github.com/yaml/pyyaml/releases) - [Changelog](https://github.com/yaml/pyyaml/blob/master/CHANGES) - [Commits](https://github.com/yaml/pyyaml/compare/5.3.1...5.4) Signed-off-by: dependabot[bot] --- Pipfile | 2 +- Pipfile.lock | 189 +++++++++++++++++++++++++++++++---------------------------- 2 files changed, 101 insertions(+), 90 deletions(-) diff --git a/Pipfile b/Pipfile index 2e922ec4..25e96c92 100644 --- a/Pipfile +++ b/Pipfile @@ -11,7 +11,7 @@ fuzzywuzzy = "~=0.17" pillow = "~=8.1" pytz = "~=2019.2" sentry-sdk = "~=0.19" -PyYAML = "~=5.3.1" +PyYAML = "~=5.4" "discord.py" = {extras = ["voice"], version = "~=1.5.1"} async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} emojis = "~=0.6.0" diff --git a/Pipfile.lock b/Pipfile.lock index d09588cf..64cae8cc 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "cf7535796e0d4f4dd6a414d8db4781caaab4a624380d29abea17577209043742" + "sha256": "427595155192c24bd1b3de5e8ffc242dc55f4390b9b90257179b5fa76457b611" }, "pipfile-spec": 6, "requires": { @@ -168,10 +168,10 @@ }, "fakeredis": { "hashes": [ - "sha256:01cb47d2286825a171fb49c0e445b1fa9307087e07cbb3d027ea10dbff108b6a", - "sha256:2c6041cf0225889bc403f3949838b2c53470a95a9e2d4272422937786f5f8f73" + "sha256:1ac0cef767c37f51718874a33afb5413e69d132988cb6a80c6e6dbeddf8c7623", + "sha256:e0416e4941cecd3089b0d901e60c8dc3c944f6384f5e29e2261c0d3c5fa99669" ], - "version": "==1.4.5" + "version": "==1.5.0" }, "fuzzywuzzy": { "hashes": [ @@ -183,54 +183,49 @@ }, "hiredis": { "hashes": [ - "sha256:06a039208f83744a702279b894c8cf24c14fd63c59cd917dcde168b79eef0680", - "sha256:0a909bf501459062aa1552be1461456518f367379fdc9fdb1f2ca5e4a1fdd7c0", - "sha256:18402d9e54fb278cb9a8c638df6f1550aca36a009d47ecf5aa263a38600f35b0", - "sha256:1e4cbbc3858ec7e680006e5ca590d89a5e083235988f26a004acf7244389ac01", - "sha256:23344e3c2177baf6975fbfa361ed92eb7d36d08f454636e5054b3faa7c2aff8a", - "sha256:289b31885b4996ce04cadfd5fc03d034dce8e2a8234479f7c9e23b9e245db06b", - "sha256:2c1c570ae7bf1bab304f29427e2475fe1856814312c4a1cf1cd0ee133f07a3c6", - "sha256:2c227c0ed371771ffda256034427320870e8ea2e4fd0c0a618c766e7c49aad73", - "sha256:3bb9b63d319402cead8bbd9dd55dca3b667d2997e9a0d8a1f9b6cc274db4baee", - "sha256:3ef2183de67b59930d2db8b8e8d4d58e00a50fcc5e92f4f678f6eed7a1c72d55", - "sha256:43b8ed3dbfd9171e44c554cb4acf4ee4505caa84c5e341858b50ea27dd2b6e12", - "sha256:47bcf3c5e6c1e87ceb86cdda2ee983fa0fe56a999e6185099b3c93a223f2fa9b", - "sha256:5263db1e2e1e8ae30500cdd75a979ff99dcc184201e6b4b820d0de74834d2323", - "sha256:5b1451727f02e7acbdf6aae4e06d75f66ee82966ff9114550381c3271a90f56c", - "sha256:6996883a8a6ff9117cbb3d6f5b0dcbbae6fb9e31e1a3e4e2f95e0214d9a1c655", - "sha256:6c96f64a54f030366657a54bb90b3093afc9c16c8e0dfa29fc0d6dbe169103a5", - "sha256:7332d5c3e35154cd234fd79573736ddcf7a0ade7a986db35b6196b9171493e75", - "sha256:7885b6f32c4a898e825bb7f56f36a02781ac4a951c63e4169f0afcf9c8c30dfb", - "sha256:7b0f63f10a166583ab744a58baad04e0f52cfea1ac27bfa1b0c21a48d1003c23", - "sha256:819f95d4eba3f9e484dd115ab7ab72845cf766b84286a00d4ecf76d33f1edca1", - "sha256:8968eeaa4d37a38f8ca1f9dbe53526b69628edc9c42229a5b2f56d98bb828c1f", - "sha256:89ebf69cb19a33d625db72d2ac589d26e936b8f7628531269accf4a3196e7872", - "sha256:8daecd778c1da45b8bd54fd41ffcd471a86beed3d8e57a43acf7a8d63bba4058", - "sha256:955ba8ea73cf3ed8bd2f963b4cb9f8f0dcb27becd2f4b3dd536fd24c45533454", - "sha256:964f18a59f5a64c0170f684c417f4fe3e695a536612e13074c4dd5d1c6d7c882", - "sha256:969843fbdfbf56cdb71da6f0bdf50f9985b8b8aeb630102945306cf10a9c6af2", - "sha256:996021ef33e0f50b97ff2d6b5f422a0fe5577de21a8873b58a779a5ddd1c3132", - "sha256:9e9c9078a7ce07e6fce366bd818be89365a35d2e4b163268f0ca9ba7e13bb2f6", - "sha256:a04901757cb0fb0f5602ac11dda48f5510f94372144d06c2563ba56c480b467c", - "sha256:a7bf1492429f18d205f3a818da3ff1f242f60aa59006e53dee00b4ef592a3363", - "sha256:aa0af2deb166a5e26e0d554b824605e660039b161e37ed4f01b8d04beec184f3", - "sha256:abfb15a6a7822f0fae681785cb38860e7a2cb1616a708d53df557b3d76c5bfd4", - "sha256:b253fe4df2afea4dfa6b1fa8c5fef212aff8bcaaeb4207e81eed05cb5e4a7919", - "sha256:b27f082f47d23cffc4cf1388b84fdc45c4ef6015f906cd7e0d988d9e35d36349", - "sha256:b33aea449e7f46738811fbc6f0b3177c6777a572207412bbbf6f525ffed001ae", - "sha256:b44f9421c4505c548435244d74037618f452844c5d3c67719d8a55e2613549da", - "sha256:bcc371151d1512201d0214c36c0c150b1dc64f19c2b1a8c9cb1d7c7c15ebd93f", - "sha256:c2851deeabd96d3f6283e9c6b26e0bfed4de2dc6fb15edf913e78b79fc5909ed", - "sha256:cdfd501c7ac5b198c15df800a3a34c38345f5182e5f80770caf362bccca65628", - "sha256:d2c0caffa47606d6d7c8af94ba42547bd2a441f06c74fd90a1ffe328524a6c64", - "sha256:dcb2db95e629962db5a355047fb8aefb012df6c8ae608930d391619dbd96fd86", - "sha256:e0eeb9c112fec2031927a1745788a181d0eecbacbed941fc5c4f7bc3f7b273bf", - "sha256:e154891263306200260d7f3051982774d7b9ef35af3509d5adbbe539afd2610c", - "sha256:e2e023a42dcbab8ed31f97c2bcdb980b7fbe0ada34037d87ba9d799664b58ded", - "sha256:e64be68255234bb489a574c4f2f8df7029c98c81ec4d160d6cd836e7f0679390", - "sha256:e82d6b930e02e80e5109b678c663a9ed210680ded81c1abaf54635d88d1da298" - ], - "version": "==1.1.0" + "sha256:04026461eae67fdefa1949b7332e488224eac9e8f2b5c58c98b54d29af22093e", + "sha256:04927a4c651a0e9ec11c68e4427d917e44ff101f761cd3b5bc76f86aaa431d27", + "sha256:07bbf9bdcb82239f319b1f09e8ef4bdfaec50ed7d7ea51a56438f39193271163", + "sha256:09004096e953d7ebd508cded79f6b21e05dff5d7361771f59269425108e703bc", + "sha256:0adea425b764a08270820531ec2218d0508f8ae15a448568109ffcae050fee26", + "sha256:0b39ec237459922c6544d071cdcf92cbb5bc6685a30e7c6d985d8a3e3a75326e", + "sha256:0d5109337e1db373a892fdcf78eb145ffb6bbd66bb51989ec36117b9f7f9b579", + "sha256:0f41827028901814c709e744060843c77e78a3aca1e0d6875d2562372fcb405a", + "sha256:11d119507bb54e81f375e638225a2c057dda748f2b1deef05c2b1a5d42686048", + "sha256:1233e303645f468e399ec906b6b48ab7cd8391aae2d08daadbb5cad6ace4bd87", + "sha256:139705ce59d94eef2ceae9fd2ad58710b02aee91e7fa0ccb485665ca0ecbec63", + "sha256:1f03d4dadd595f7a69a75709bc81902673fa31964c75f93af74feac2f134cc54", + "sha256:240ce6dc19835971f38caf94b5738092cb1e641f8150a9ef9251b7825506cb05", + "sha256:294a6697dfa41a8cba4c365dd3715abc54d29a86a40ec6405d677ca853307cfb", + "sha256:3d55e36715ff06cdc0ab62f9591607c4324297b6b6ce5b58cb9928b3defe30ea", + "sha256:3dddf681284fe16d047d3ad37415b2e9ccdc6c8986c8062dbe51ab9a358b50a5", + "sha256:3f5f7e3a4ab824e3de1e1700f05ad76ee465f5f11f5db61c4b297ec29e692b2e", + "sha256:508999bec4422e646b05c95c598b64bdbef1edf0d2b715450a078ba21b385bcc", + "sha256:5d2a48c80cf5a338d58aae3c16872f4d452345e18350143b3bf7216d33ba7b99", + "sha256:5dc7a94bb11096bc4bffd41a3c4f2b958257085c01522aa81140c68b8bf1630a", + "sha256:65d653df249a2f95673976e4e9dd7ce10de61cfc6e64fa7eeaa6891a9559c581", + "sha256:7492af15f71f75ee93d2a618ca53fea8be85e7b625e323315169977fae752426", + "sha256:7f0055f1809b911ab347a25d786deff5e10e9cf083c3c3fd2dd04e8612e8d9db", + "sha256:807b3096205c7cec861c8803a6738e33ed86c9aae76cac0e19454245a6bbbc0a", + "sha256:81d6d8e39695f2c37954d1011c0480ef7cf444d4e3ae24bc5e89ee5de360139a", + "sha256:87c7c10d186f1743a8fd6a971ab6525d60abd5d5d200f31e073cd5e94d7e7a9d", + "sha256:8b42c0dc927b8d7c0eb59f97e6e34408e53bc489f9f90e66e568f329bff3e443", + "sha256:a00514362df15af041cc06e97aebabf2895e0a7c42c83c21894be12b84402d79", + "sha256:a39efc3ade8c1fb27c097fd112baf09d7fd70b8cb10ef1de4da6efbe066d381d", + "sha256:a4ee8000454ad4486fb9f28b0cab7fa1cd796fc36d639882d0b34109b5b3aec9", + "sha256:a7928283143a401e72a4fad43ecc85b35c27ae699cf5d54d39e1e72d97460e1d", + "sha256:adf4dd19d8875ac147bf926c727215a0faf21490b22c053db464e0bf0deb0485", + "sha256:ae8427a5e9062ba66fc2c62fb19a72276cf12c780e8db2b0956ea909c48acff5", + "sha256:b4c8b0bc5841e578d5fb32a16e0c305359b987b850a06964bd5a62739d688048", + "sha256:b84f29971f0ad4adaee391c6364e6f780d5aae7e9226d41964b26b49376071d0", + "sha256:c39c46d9e44447181cd502a35aad2bb178dbf1b1f86cf4db639d7b9614f837c6", + "sha256:cb2126603091902767d96bcb74093bd8b14982f41809f85c9b96e519c7e1dc41", + "sha256:dcef843f8de4e2ff5e35e96ec2a4abbdf403bd0f732ead127bd27e51f38ac298", + "sha256:e3447d9e074abf0e3cd85aef8131e01ab93f9f0e86654db7ac8a3f73c63706ce", + "sha256:f52010e0a44e3d8530437e7da38d11fb822acfb0d5b12e9cd5ba655509937ca0", + "sha256:f8196f739092a78e4f6b1b2172679ed3343c39c61a3e9d722ce6fcf1dac2824a" + ], + "version": "==2.0.0" }, "idna": { "hashes": [ @@ -384,22 +379,30 @@ }, "pyyaml": { "hashes": [ - "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", - "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", - "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", - "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e", - "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", - "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", - "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", - "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", - "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", - "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a", - "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", - "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", - "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" + "sha256:02c78d77281d8f8d07a255e57abdbf43b02257f59f50cc6b636937d68efa5dd0", + "sha256:0dc9f2eb2e3c97640928dec63fd8dc1dd91e6b6ed236bd5ac00332b99b5c2ff9", + "sha256:124fd7c7bc1e95b1eafc60825f2daf67c73ce7b33f1194731240d24b0d1bf628", + "sha256:26fcb33776857f4072601502d93e1a619f166c9c00befb52826e7b774efaa9db", + "sha256:31ba07c54ef4a897758563e3a0fcc60077698df10180abe4b8165d9895c00ebf", + "sha256:3c49e39ac034fd64fd576d63bb4db53cda89b362768a67f07749d55f128ac18a", + "sha256:52bf0930903818e600ae6c2901f748bc4869c0c406056f679ab9614e5d21a166", + "sha256:5a3f345acff76cad4aa9cb171ee76c590f37394186325d53d1aa25318b0d4a09", + "sha256:5e7ac4e0e79a53451dc2814f6876c2fa6f71452de1498bbe29c0b54b69a986f4", + "sha256:7242790ab6c20316b8e7bb545be48d7ed36e26bbe279fd56f2c4a12510e60b4b", + "sha256:737bd70e454a284d456aa1fa71a0b429dd527bcbf52c5c33f7c8eee81ac16b89", + "sha256:8635d53223b1f561b081ff4adecb828fd484b8efffe542edcfdff471997f7c39", + "sha256:8b818b6c5a920cbe4203b5a6b14256f0e5244338244560da89b7b0f1313ea4b6", + "sha256:8bf38641b4713d77da19e91f8b5296b832e4db87338d6aeffe422d42f1ca896d", + "sha256:a36a48a51e5471513a5aea920cdad84cbd56d70a5057cca3499a637496ea379c", + "sha256:b2243dd033fd02c01212ad5c601dafb44fbb293065f430b0d3dbf03f3254d615", + "sha256:cc547d3ead3754712223abb7b403f0a184e4c3eae18c9bb7fd15adef1597cc4b", + "sha256:cc552b6434b90d9dbed6a4f13339625dc466fd82597119897e9489c953acbc22", + "sha256:f3790156c606299ff499ec44db422f66f05a7363b39eb9d5b064f17bd7d7c47b", + "sha256:f7a21e3d99aa3095ef0553e7ceba36fb693998fbb1226f1392ce33681047465f", + "sha256:fdc6b2cb4b19e431994f25a9160695cc59a4e861710cc6fc97161c5e845fc579" ], "index": "pypi", - "version": "==5.3.1" + "version": "==5.4" }, "redis": { "hashes": [ @@ -432,11 +435,11 @@ }, "soupsieve": { "hashes": [ - "sha256:407fa1e8eb3458d1b5614df51d9651a1180ea5fedf07feb46e45d7e25e6d6cdd", - "sha256:d3a5ea5b350423f47d07639f74475afedad48cf41c0ad7a82ca13a3928af34f6" + "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc", + "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b" ], "markers": "python_version >= '3.0'", - "version": "==2.2" + "version": "==2.2.1" }, "urllib3": { "hashes": [ @@ -576,10 +579,10 @@ }, "identify": { "hashes": [ - "sha256:46d1816c6a4fc2d1e8758f293a5dcc1ae6404ab344179d7c1e73637bf283beb1", - "sha256:ed4a05fb80e3cbd12e83c959f9ff7f729ba6b66ab8d6178850fd5cb4c1cf6c5d" + "sha256:43cb1965e84cdd247e875dec6d13332ef5be355ddc16776396d98089b9053d87", + "sha256:c7c0f590526008911ccc5ceee6ed7b085cbc92f7b6591d0ee5913a130ad64034" ], - "version": "==2.1.3" + "version": "==2.2.2" }, "mccabe": { "hashes": [ @@ -620,10 +623,10 @@ }, "pydocstyle": { "hashes": [ - "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325", - "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678" + "sha256:164befb520d851dbcf0e029681b91f4f599c62c5cd8933fd54b1bfbd50e89e1f", + "sha256:d4449cf16d7e6709f63192146706933c7a334af7c0f083904799ccb851c50f6d" ], - "version": "==5.1.1" + "version": "==6.0.0" }, "pyflakes": { "hashes": [ @@ -634,22 +637,30 @@ }, "pyyaml": { "hashes": [ - "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", - "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", - "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", - "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e", - "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", - "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", - "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", - "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", - "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", - "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a", - "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", - "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", - "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" + "sha256:02c78d77281d8f8d07a255e57abdbf43b02257f59f50cc6b636937d68efa5dd0", + "sha256:0dc9f2eb2e3c97640928dec63fd8dc1dd91e6b6ed236bd5ac00332b99b5c2ff9", + "sha256:124fd7c7bc1e95b1eafc60825f2daf67c73ce7b33f1194731240d24b0d1bf628", + "sha256:26fcb33776857f4072601502d93e1a619f166c9c00befb52826e7b774efaa9db", + "sha256:31ba07c54ef4a897758563e3a0fcc60077698df10180abe4b8165d9895c00ebf", + "sha256:3c49e39ac034fd64fd576d63bb4db53cda89b362768a67f07749d55f128ac18a", + "sha256:52bf0930903818e600ae6c2901f748bc4869c0c406056f679ab9614e5d21a166", + "sha256:5a3f345acff76cad4aa9cb171ee76c590f37394186325d53d1aa25318b0d4a09", + "sha256:5e7ac4e0e79a53451dc2814f6876c2fa6f71452de1498bbe29c0b54b69a986f4", + "sha256:7242790ab6c20316b8e7bb545be48d7ed36e26bbe279fd56f2c4a12510e60b4b", + "sha256:737bd70e454a284d456aa1fa71a0b429dd527bcbf52c5c33f7c8eee81ac16b89", + "sha256:8635d53223b1f561b081ff4adecb828fd484b8efffe542edcfdff471997f7c39", + "sha256:8b818b6c5a920cbe4203b5a6b14256f0e5244338244560da89b7b0f1313ea4b6", + "sha256:8bf38641b4713d77da19e91f8b5296b832e4db87338d6aeffe422d42f1ca896d", + "sha256:a36a48a51e5471513a5aea920cdad84cbd56d70a5057cca3499a637496ea379c", + "sha256:b2243dd033fd02c01212ad5c601dafb44fbb293065f430b0d3dbf03f3254d615", + "sha256:cc547d3ead3754712223abb7b403f0a184e4c3eae18c9bb7fd15adef1597cc4b", + "sha256:cc552b6434b90d9dbed6a4f13339625dc466fd82597119897e9489c953acbc22", + "sha256:f3790156c606299ff499ec44db422f66f05a7363b39eb9d5b064f17bd7d7c47b", + "sha256:f7a21e3d99aa3095ef0553e7ceba36fb693998fbb1226f1392ce33681047465f", + "sha256:fdc6b2cb4b19e431994f25a9160695cc59a4e861710cc6fc97161c5e845fc579" ], "index": "pypi", - "version": "==5.3.1" + "version": "==5.4" }, "six": { "hashes": [ -- cgit v1.2.3 From a52eeb5e253a31c447d28fe663f3a60c0c9efcdc Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 8 Apr 2021 11:35:43 +0200 Subject: Issues: add accept header --- bot/exts/evergreen/issues.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index d8b373d7..5ea8a6bf 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -26,7 +26,9 @@ BAD_RESPONSE = { 404: "Issue/pull request not located! Please enter a valid number!", 403: "Rate limit has been hit! Please try again later!" } -REQUEST_HEADERS = dict() +REQUEST_HEADERS = { + "Accept": "application/vnd.github.v3+json" +} REPOSITORY_ENDPOINT = "https://api.github.com/orgs/{org}/repos" ISSUE_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/issues/{number}" -- cgit v1.2.3 From 37a3b571467bb889526b7d21db17f7644453a85e Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 8 Apr 2021 11:39:45 +0200 Subject: Issues: icon_url -> emoji_url --- bot/exts/evergreen/issues.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 5ea8a6bf..9b04c8c1 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -67,7 +67,7 @@ class IssueState: number: int url: str title: str - icon_url: str + emoji_url: str class Issues(commands.Cog): @@ -176,7 +176,7 @@ class Issues(commands.Cog): for result in results: if isinstance(result, IssueState): - description_list.append(f"{result.icon_url} [{result.title}]({result.url})") + description_list.append(f"{result.emoji_url} [{result.title}]({result.url})") elif isinstance(result, FetchError): description_list.append(f"[{result.return_code}] {result.message}") -- cgit v1.2.3 From c272bdf0149d741efad477b77a7e123a42569f68 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 8 Apr 2021 11:41:23 +0200 Subject: Issues: add red cross emoji for failed issues Co-authored-by: Shivansh-007 --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 83cafcc8..5ad13628 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -178,7 +178,7 @@ class Issues(commands.Cog): if isinstance(result, IssueState): description_list.append(f"{result.emoji_url} [{result.title}]({result.url})") elif isinstance(result, FetchError): - description_list.append(f"[{result.return_code}] {result.message}") + description_list.append(f":x: [{result.return_code}] {result.message}") resp = discord.Embed( colour=Colours.bright_green, -- cgit v1.2.3 From 0ff42e4e2197822bd1e1a63e5ebd73d75588b1eb Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 8 Apr 2021 11:51:16 +0200 Subject: Issues: emoji_url -> emoji --- bot/exts/evergreen/issues.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 83cafcc8..10aa347d 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -67,7 +67,7 @@ class IssueState: number: int url: str title: str - emoji_url: str + emoji: str class Issues(commands.Cog): @@ -143,9 +143,9 @@ class Issues(commands.Cog): # need from the initial API call. if "issues" in json_data["html_url"]: if json_data.get("state") == "open": - icon_url = Emojis.issue + emoji = Emojis.issue else: - icon_url = Emojis.issue_closed + emoji = Emojis.issue_closed # If the 'issues' key is not contained in the API response and there is no error code, then # we know that a PR has been requested and a call to the pulls API endpoint is necessary @@ -154,16 +154,16 @@ class Issues(commands.Cog): log.trace(f"PR provided, querying GH pulls API for additional information: {merge_url}") async with self.bot.http_session.get(merge_url) as m: if json_data.get("state") == "open": - icon_url = Emojis.pull_request + emoji = Emojis.pull_request # When the status is 204 this means that the state of the PR is merged elif m.status == 204: - icon_url = Emojis.merge + emoji = Emojis.merge else: - icon_url = Emojis.pull_request_closed + emoji = Emojis.pull_request_closed issue_url = json_data.get("html_url") - return IssueState(repository, number, issue_url, json_data.get('title', ''), icon_url) + return IssueState(repository, number, issue_url, json_data.get('title', ''), emoji) @staticmethod def format_embed( @@ -176,7 +176,7 @@ class Issues(commands.Cog): for result in results: if isinstance(result, IssueState): - description_list.append(f"{result.emoji_url} [{result.title}]({result.url})") + description_list.append(f"{result.emoji} [{result.title}]({result.url})") elif isinstance(result, FetchError): description_list.append(f"[{result.return_code}] {result.message}") -- cgit v1.2.3 From c94a45a10bac63eff1ce066707fe2df0b1243daa Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Thu, 8 Apr 2021 12:37:14 -0400 Subject: Use PRDraft emoji when the pr is a draft pr for the .issue|.pr command --- bot/constants.py | 1 + bot/exts/evergreen/issues.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 416dd0e7..e59fa641 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -168,6 +168,7 @@ class Emojis: issue_closed = "<:IssueClosed:629695470570307614>" pull_request = "<:PROpen:629695470175780875>" pull_request_closed = "<:PRClosed:629695470519713818>" + pull_request_draft = "<:PRDraft:829755345425399848>" merge = "<:PRMerged:629695470570176522>" number_emojis = { diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 4a73d20b..e83f1a3e 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -100,7 +100,7 @@ class Issues(commands.Cog): for number in numbers: url = f"https://api.github.com/repos/{user}/{repository}/issues/{number}" - merge_url = f"https://api.github.com/repos/{user}/{repository}/pulls/{number}/merge" + pulls_url = f"https://api.github.com/repos/{user}/{repository}/pulls/{number}" log.trace(f"Querying GH issues API: {url}") async with self.bot.http_session.get(url, headers=REQUEST_HEADERS) as r: json_data = await r.json() @@ -123,12 +123,15 @@ class Issues(commands.Cog): # we know that a PR has been requested and a call to the pulls API endpoint is necessary # to get the desired information for the PR. else: - log.trace(f"PR provided, querying GH pulls API for additional information: {merge_url}") - async with self.bot.http_session.get(merge_url) as m: - if json_data.get("state") == "open": + log.trace(f"PR provided, querying GH pulls API for additional information: {pulls_url}") + async with self.bot.http_session.get(pulls_url) as p: + pull_data = await p.json() + if pull_data["draft"]: + icon_url = Emojis.pull_request_draft + elif pull_data["state"] == "open": icon_url = Emojis.pull_request # When the status is 204 this means that the state of the PR is merged - elif m.status == 204: + elif pull_data["merged_at"] is not None: icon_url = Emojis.merge else: icon_url = Emojis.pull_request_closed -- cgit v1.2.3 From 22ba746b3892857d1b2050eab7c1c72cd435ef48 Mon Sep 17 00:00:00 2001 From: onerandomusername <71233171+onerandomusername@users.noreply.github.com> Date: Thu, 8 Apr 2021 13:01:36 -0400 Subject: Remove topic that doesn't make sense in discord Removed the `Name one thing you like about a person to your right.` topic as it doesn't make sense in discord --- bot/resources/evergreen/starter.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/resources/evergreen/starter.yaml b/bot/resources/evergreen/starter.yaml index 4fec6a10..6b0de0ef 100644 --- a/bot/resources/evergreen/starter.yaml +++ b/bot/resources/evergreen/starter.yaml @@ -6,7 +6,6 @@ - "What is better: Milk, Dark or White chocolate?" - What is your favourite holiday? - If you could have any superpower, what would it be? -- Name one thing you like about a person to your right. - If you could be anyone else for one day, who would it be? - What Easter tradition do you enjoy most? - What is the best gift you've been given? -- cgit v1.2.3 From 6b6e2bd0ec58197a88ca9fdc8cc3eeccbf8ebab5 Mon Sep 17 00:00:00 2001 From: Shakya Majumdar Date: Thu, 8 Apr 2021 23:45:35 +0530 Subject: Add latex cog --- Pipfile | 1 + Pipfile.lock | 339 +++++++++++++++++++++++++++++++------------- bot/exts/evergreen/latex.py | 57 ++++++++ 3 files changed, 300 insertions(+), 97 deletions(-) create mode 100644 bot/exts/evergreen/latex.py diff --git a/Pipfile b/Pipfile index 25e96c92..f20f6845 100644 --- a/Pipfile +++ b/Pipfile @@ -15,6 +15,7 @@ PyYAML = "~=5.4" "discord.py" = {extras = ["voice"], version = "~=1.5.1"} async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} emojis = "~=0.6.0" +matplotlib = "~=3.4.1" [dev-packages] flake8 = "~=3.8" diff --git a/Pipfile.lock b/Pipfile.lock index 64cae8cc..d7fc6b27 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "427595155192c24bd1b3de5e8ffc242dc55f4390b9b90257179b5fa76457b611" + "sha256": "03b52d5b9fdfa6d037780d5aa2896c82fd5454a40bd69acf7e9b0e129557dbd5" }, "pipfile-spec": 6, "requires": { @@ -40,6 +40,7 @@ "sha256:c506853ba52e516b264b106321c424d03f3ddef2813246432fa9d1cefd361c81", "sha256:fb83326d8295e8840e4ba774edf346e87eca78ba8a89c55d2690352842c15ba5" ], + "markers": "python_full_version >= '3.5.3'", "version": "==3.6.3" }, "aioredis": { @@ -73,6 +74,7 @@ "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" ], + "markers": "python_full_version >= '3.5.3'", "version": "==3.0.1" }, "attrs": { @@ -80,6 +82,7 @@ "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==20.3.0" }, "beautifulsoup4": { @@ -147,6 +150,13 @@ ], "version": "==3.0.4" }, + "cycler": { + "hashes": [ + "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d", + "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8" + ], + "version": "==0.10.0" + }, "discord.py": { "extras": [ "voice" @@ -225,6 +235,7 @@ "sha256:f52010e0a44e3d8530437e7da38d11fb822acfb0d5b12e9cd5ba655509937ca0", "sha256:f8196f739092a78e4f6b1b2172679ed3343c39c61a3e9d722ce6fcf1dac2824a" ], + "markers": "python_version >= '3.6'", "version": "==2.0.0" }, "idna": { @@ -232,8 +243,72 @@ "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16", "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1" ], + "markers": "python_version >= '3.4'", "version": "==3.1" }, + "kiwisolver": { + "hashes": [ + "sha256:0cd53f403202159b44528498de18f9285b04482bab2a6fc3f5dd8dbb9352e30d", + "sha256:1e1bc12fb773a7b2ffdeb8380609f4f8064777877b2225dec3da711b421fda31", + "sha256:225e2e18f271e0ed8157d7f4518ffbf99b9450fca398d561eb5c4a87d0986dd9", + "sha256:232c9e11fd7ac3a470d65cd67e4359eee155ec57e822e5220322d7b2ac84fbf0", + "sha256:31dfd2ac56edc0ff9ac295193eeaea1c0c923c0355bf948fbd99ed6018010b72", + "sha256:33449715e0101e4d34f64990352bce4095c8bf13bed1b390773fc0a7295967b3", + "sha256:401a2e9afa8588589775fe34fc22d918ae839aaaf0c0e96441c0fdbce6d8ebe6", + "sha256:44a62e24d9b01ba94ae7a4a6c3fb215dc4af1dde817e7498d901e229aaf50e4e", + "sha256:50af681a36b2a1dee1d3c169ade9fdc59207d3c31e522519181e12f1b3ba7000", + "sha256:563c649cfdef27d081c84e72a03b48ea9408c16657500c312575ae9d9f7bc1c3", + "sha256:5989db3b3b34b76c09253deeaf7fbc2707616f130e166996606c284395da3f18", + "sha256:5a7a7dbff17e66fac9142ae2ecafb719393aaee6a3768c9de2fd425c63b53e21", + "sha256:5c3e6455341008a054cccee8c5d24481bcfe1acdbc9add30aa95798e95c65621", + "sha256:5f6ccd3dd0b9739edcf407514016108e2280769c73a85b9e59aa390046dbf08b", + "sha256:72c99e39d005b793fb7d3d4e660aed6b6281b502e8c1eaf8ee8346023c8e03bc", + "sha256:78751b33595f7f9511952e7e60ce858c6d64db2e062afb325985ddbd34b5c131", + "sha256:834ee27348c4aefc20b479335fd422a2c69db55f7d9ab61721ac8cd83eb78882", + "sha256:8be8d84b7d4f2ba4ffff3665bcd0211318aa632395a1a41553250484a871d454", + "sha256:950a199911a8d94683a6b10321f9345d5a3a8433ec58b217ace979e18f16e248", + "sha256:a357fd4f15ee49b4a98b44ec23a34a95f1e00292a139d6015c11f55774ef10de", + "sha256:a53d27d0c2a0ebd07e395e56a1fbdf75ffedc4a05943daf472af163413ce9598", + "sha256:acef3d59d47dd85ecf909c359d0fd2c81ed33bdff70216d3956b463e12c38a54", + "sha256:b38694dcdac990a743aa654037ff1188c7a9801ac3ccc548d3341014bc5ca278", + "sha256:b9edd0110a77fc321ab090aaa1cfcaba1d8499850a12848b81be2222eab648f6", + "sha256:c08e95114951dc2090c4a630c2385bef681cacf12636fb0241accdc6b303fd81", + "sha256:c5518d51a0735b1e6cee1fdce66359f8d2b59c3ca85dc2b0813a8aa86818a030", + "sha256:c8fd0f1ae9d92b42854b2979024d7597685ce4ada367172ed7c09edf2cef9cb8", + "sha256:ca3820eb7f7faf7f0aa88de0e54681bddcb46e485beb844fcecbcd1c8bd01689", + "sha256:cf8b574c7b9aa060c62116d4181f3a1a4e821b2ec5cbfe3775809474113748d4", + "sha256:d3155d828dec1d43283bd24d3d3e0d9c7c350cdfcc0bd06c0ad1209c1bbc36d0", + "sha256:f8d6f8db88049a699817fd9178782867bf22283e3813064302ac59f61d95be05", + "sha256:fd34fbbfbc40628200730bc1febe30631347103fc8d3d4fa012c21ab9c11eca9" + ], + "markers": "python_version >= '3.6'", + "version": "==1.3.1" + }, + "matplotlib": { + "hashes": [ + "sha256:1f83a32e4b6045191f9d34e4dc68c0a17c870b57ef9cca518e516da591246e79", + "sha256:2eee37340ca1b353e0a43a33da79d0cd4bcb087064a0c3c3d1329cdea8fbc6f3", + "sha256:53ceb12ef44f8982b45adc7a0889a7e2df1d758e8b360f460e435abe8a8cd658", + "sha256:574306171b84cd6854c83dc87bc353cacc0f60184149fb00c9ea871eca8c1ecb", + "sha256:7561fd541477d41f3aa09457c434dd1f7604f3bd26d7858d52018f5dfe1c06d1", + "sha256:7a54efd6fcad9cb3cd5ef2064b5a3eeb0b63c99f26c346bdcf66e7c98294d7cc", + "sha256:7f16660edf9a8bcc0f766f51c9e1b9d2dc6ceff6bf636d2dbd8eb925d5832dfd", + "sha256:81e6fe8b18ef5be67f40a1d4f07d5a4ed21d3878530193898449ddef7793952f", + "sha256:84a10e462120aa7d9eb6186b50917ed5a6286ee61157bfc17c5b47987d1a9068", + "sha256:84d4c4f650f356678a5d658a43ca21a41fca13f9b8b00169c0b76e6a6a948908", + "sha256:86dc94e44403fa0f2b1dd76c9794d66a34e821361962fe7c4e078746362e3b14", + "sha256:90dbc007f6389bcfd9ef4fe5d4c78c8d2efe4e0ebefd48b4f221cdfed5672be2", + "sha256:9f374961a3996c2d1b41ba3145462c3708a89759e604112073ed6c8bdf9f622f", + "sha256:a18cc1ab4a35b845cf33b7880c979f5c609fd26c2d6e74ddfacb73dcc60dd956", + "sha256:a97781453ac79409ddf455fccf344860719d95142f9c334f2a8f3fff049ffec3", + "sha256:a989022f89cda417f82dbf65e0a830832afd8af743d05d1414fb49549287ff04", + "sha256:ac2a30a09984c2719f112a574b6543ccb82d020fd1b23b4d55bf4759ba8dd8f5", + "sha256:be4430b33b25e127fc4ea239cc386389de420be4d63e71d5359c20b562951ce1", + "sha256:c45e7bf89ea33a2adaef34774df4e692c7436a18a48bcb0e47a53e698a39fa39" + ], + "index": "pypi", + "version": "==3.4.1" + }, "multidict": { "hashes": [ "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a", @@ -254,46 +329,77 @@ "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255", "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d" ], + "markers": "python_version >= '3.5'", "version": "==4.7.6" }, + "numpy": { + "hashes": [ + "sha256:2428b109306075d89d21135bdd6b785f132a1f5a3260c371cee1fae427e12727", + "sha256:377751954da04d4a6950191b20539066b4e19e3b559d4695399c5e8e3e683bf6", + "sha256:4703b9e937df83f5b6b7447ca5912b5f5f297aba45f91dbbbc63ff9278c7aa98", + "sha256:471c0571d0895c68da309dacee4e95a0811d0a9f9f532a48dc1bea5f3b7ad2b7", + "sha256:61d5b4cf73622e4d0c6b83408a16631b670fc045afd6540679aa35591a17fe6d", + "sha256:6c915ee7dba1071554e70a3664a839fbc033e1d6528199d4621eeaaa5487ccd2", + "sha256:6e51e417d9ae2e7848314994e6fc3832c9d426abce9328cf7571eefceb43e6c9", + "sha256:719656636c48be22c23641859ff2419b27b6bdf844b36a2447cb39caceb00935", + "sha256:780ae5284cb770ade51d4b4a7dce4faa554eb1d88a56d0e8b9f35fca9b0270ff", + "sha256:878922bf5ad7550aa044aa9301d417e2d3ae50f0f577de92051d739ac6096cee", + "sha256:924dc3f83de20437de95a73516f36e09918e9c9c18d5eac520062c49191025fb", + "sha256:97ce8b8ace7d3b9288d88177e66ee75480fb79b9cf745e91ecfe65d91a856042", + "sha256:9c0fab855ae790ca74b27e55240fe4f2a36a364a3f1ebcfd1fb5ac4088f1cec3", + "sha256:9cab23439eb1ebfed1aaec9cd42b7dc50fc96d5cd3147da348d9161f0501ada5", + "sha256:a8e6859913ec8eeef3dbe9aed3bf475347642d1cdd6217c30f28dee8903528e6", + "sha256:aa046527c04688af680217fffac61eec2350ef3f3d7320c07fd33f5c6e7b4d5f", + "sha256:abc81829c4039e7e4c30f7897938fa5d4916a09c2c7eb9b244b7a35ddc9656f4", + "sha256:bad70051de2c50b1a6259a6df1daaafe8c480ca98132da98976d8591c412e737", + "sha256:c73a7975d77f15f7f68dacfb2bca3d3f479f158313642e8ea9058eea06637931", + "sha256:d15007f857d6995db15195217afdbddfcd203dfaa0ba6878a2f580eaf810ecd6", + "sha256:d76061ae5cab49b83a8cf3feacefc2053fac672728802ac137dd8c4123397677", + "sha256:e8e4fbbb7e7634f263c5b0150a629342cc19b47c5eba8d1cd4363ab3455ab576", + "sha256:e9459f40244bb02b2f14f6af0cd0732791d72232bbb0dc4bab57ef88e75f6935", + "sha256:edb1f041a9146dcf02cd7df7187db46ab524b9af2515f392f337c7cbbf5b52cd" + ], + "markers": "python_version >= '3.7'", + "version": "==1.20.2" + }, "pillow": { "hashes": [ - "sha256:01bb0a34f1a6689b138c0089d670ae2e8f886d2666a9b2f2019031abdea673c4", - "sha256:07872f1d8421db5a3fe770f7480835e5e90fddb58f36c216d4a2ac0d594de474", - "sha256:1022f8f6dc3c5b0dcf928f1c49ba2ac73051f576af100d57776e2b65c1f76a8d", - "sha256:14415e9e28410232370615dbde0cf0a00e526f522f665460344a5b96973a3086", - "sha256:172acfaf00434a28dddfe592d83f2980e22e63c769ff4a448ddf7b7a38ffd165", - "sha256:1c5e3c36f02c815766ae9dd91899b1c5b4652f2a37b7a51609f3bd467c0f11fb", - "sha256:292f2aa1ae5c5c1451cb4b558addb88c257411d3fd71c6cf45562911baffc979", - "sha256:2a40d7d4b17db87f5b9a1efc0aff56000e1d0d5ece415090c102aafa0ccbe858", - "sha256:2f0d7034d5faae9a8d1019d152ede924f653df2ce77d3bba4ce62cd21b5f94ae", - "sha256:33fdbd4f5608c852d97264f9d2e3b54e9e9959083d008145175b86100b275e5b", - "sha256:3b13d89d97b551e02549d1f0edf22bed6acfd6fd2e888cd1e9a953bf215f0e81", - "sha256:3e759bcc03d6f39bc751e56d86bc87252b9a21c689a27c5ed753717a87d53a5b", - "sha256:3ec87bd1248b23a2e4e19e774367fbe30fddc73913edc5f9b37470624f55dc1f", - "sha256:436b0a2dd9fe3f7aa6a444af6bdf53c1eb8f5ced9ea3ef104daa83f0ea18e7bc", - "sha256:43b3c859912e8bf754b3c5142df624794b18eb7ae07cfeddc917e1a9406a3ef2", - "sha256:4fe74636ee71c57a7f65d7b21a9f127d842b4fb75511e5d256ace258826eb352", - "sha256:59445af66b59cc39530b4f810776928d75e95f41e945f0c32a3de4aceb93c15d", - "sha256:69da5b1d7102a61ce9b45deb2920a2012d52fd8f4201495ea9411d0071b0ec22", - "sha256:7094bbdecb95ebe53166e4c12cf5e28310c2b550b08c07c5dc15433898e2238e", - "sha256:8211cac9bf10461f9e33fe9a3af6c5131f3fdd0d10672afc2abb2c70cf95c5ca", - "sha256:8cf77e458bd996dc85455f10fe443c0c946f5b13253773439bcbec08aa1aebc2", - "sha256:924fc33cb4acaf6267b8ca3b8f1922620d57a28470d5e4f49672cea9a841eb08", - "sha256:99ce3333b40b7a4435e0a18baad468d44ab118a4b1da0af0a888893d03253f1d", - "sha256:a7d690b2c5f7e4a932374615fedceb1e305d2dd5363c1de15961725fe10e7d16", - "sha256:b9af590adc1e46898a1276527f3cfe2da8048ae43fbbf9b1bf9395f6c99d9b47", - "sha256:bb18422ad00c1fecc731d06592e99c3be2c634da19e26942ba2f13d805005cf2", - "sha256:c10af40ee2f1a99e1ae755ab1f773916e8bca3364029a042cd9161c400416bd8", - "sha256:c143c409e7bc1db784471fe9d0bf95f37c4458e879ad84cfae640cb74ee11a26", - "sha256:c448d2b335e21951416a30cd48d35588d122a912d5fe9e41900afacecc7d21a1", - "sha256:d30f30c044bdc0ab8f3924e1eeaac87e0ff8a27e87369c5cac4064b6ec78fd83", - "sha256:df534e64d4f3e84e8f1e1a37da3f541555d947c1c1c09b32178537f0f243f69d", - "sha256:f6fc18f9c9c7959bf58e6faf801d14fafb6d4717faaf6f79a68c8bb2a13dcf20", - "sha256:ff83dfeb04c98bb3e7948f876c17513a34e9a19fd92e292288649164924c1b39" + "sha256:01425106e4e8cee195a411f729cff2a7d61813b0b11737c12bd5991f5f14bcd5", + "sha256:031a6c88c77d08aab84fecc05c3cde8414cd6f8406f4d2b16fed1e97634cc8a4", + "sha256:083781abd261bdabf090ad07bb69f8f5599943ddb539d64497ed021b2a67e5a9", + "sha256:0d19d70ee7c2ba97631bae1e7d4725cdb2ecf238178096e8c82ee481e189168a", + "sha256:0e04d61f0064b545b989126197930807c86bcbd4534d39168f4aa5fda39bb8f9", + "sha256:12e5e7471f9b637762453da74e390e56cc43e486a88289995c1f4c1dc0bfe727", + "sha256:22fd0f42ad15dfdde6c581347eaa4adb9a6fc4b865f90b23378aa7914895e120", + "sha256:238c197fc275b475e87c1453b05b467d2d02c2915fdfdd4af126145ff2e4610c", + "sha256:3b570f84a6161cf8865c4e08adf629441f56e32f180f7aa4ccbd2e0a5a02cba2", + "sha256:463822e2f0d81459e113372a168f2ff59723e78528f91f0bd25680ac185cf797", + "sha256:4d98abdd6b1e3bf1a1cbb14c3895226816e666749ac040c4e2554231068c639b", + "sha256:5afe6b237a0b81bd54b53f835a153770802f164c5570bab5e005aad693dab87f", + "sha256:5b70110acb39f3aff6b74cf09bb4169b167e2660dabc304c1e25b6555fa781ef", + "sha256:5cbf3e3b1014dddc45496e8cf38b9f099c95a326275885199f427825c6522232", + "sha256:624b977355cde8b065f6d51b98497d6cd5fbdd4f36405f7a8790e3376125e2bb", + "sha256:63728564c1410d99e6d1ae8e3b810fe012bc440952168af0a2877e8ff5ab96b9", + "sha256:66cc56579fd91f517290ab02c51e3a80f581aba45fd924fcdee01fa06e635812", + "sha256:6c32cc3145928c4305d142ebec682419a6c0a8ce9e33db900027ddca1ec39178", + "sha256:8bb1e155a74e1bfbacd84555ea62fa21c58e0b4e7e6b20e4447b8d07990ac78b", + "sha256:95d5ef984eff897850f3a83883363da64aae1000e79cb3c321915468e8c6add5", + "sha256:a013cbe25d20c2e0c4e85a9daf438f85121a4d0344ddc76e33fd7e3965d9af4b", + "sha256:a787ab10d7bb5494e5f76536ac460741788f1fbce851068d73a87ca7c35fc3e1", + "sha256:a7d5e9fad90eff8f6f6106d3b98b553a88b6f976e51fce287192a5d2d5363713", + "sha256:aac00e4bc94d1b7813fe882c28990c1bc2f9d0e1aa765a5f2b516e8a6a16a9e4", + "sha256:b91c36492a4bbb1ee855b7d16fe51379e5f96b85692dc8210831fbb24c43e484", + "sha256:c03c07ed32c5324939b19e36ae5f75c660c81461e312a41aea30acdd46f93a7c", + "sha256:c5236606e8570542ed424849f7852a0ff0bce2c4c8d0ba05cc202a5a9c97dee9", + "sha256:c6b39294464b03457f9064e98c124e09008b35a62e3189d3513e5148611c9388", + "sha256:cb7a09e173903541fa888ba010c345893cd9fc1b5891aaf060f6ca77b6a3722d", + "sha256:d68cb92c408261f806b15923834203f024110a2e2872ecb0bd2a110f89d3c602", + "sha256:dc38f57d8f20f06dd7c3161c59ca2c86893632623f33a42d592f097b00f720a9", + "sha256:e98eca29a05913e82177b3ba3d198b1728e164869c613d76d0de4bde6768a50e", + "sha256:f217c3954ce5fd88303fc0c317af55d5e0204106d86dea17eb8205700d47dec2" ], "index": "pypi", - "version": "==8.1.1" + "version": "==8.2.0" }, "pycares": { "hashes": [ @@ -334,6 +440,7 @@ "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.20" }, "pynacl": { @@ -362,11 +469,20 @@ ], "version": "==1.3.0" }, + "pyparsing": { + "hashes": [ + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.4.7" + }, "python-dateutil": { "hashes": [ "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.8.1" }, "pytz": { @@ -379,36 +495,45 @@ }, "pyyaml": { "hashes": [ - "sha256:02c78d77281d8f8d07a255e57abdbf43b02257f59f50cc6b636937d68efa5dd0", - "sha256:0dc9f2eb2e3c97640928dec63fd8dc1dd91e6b6ed236bd5ac00332b99b5c2ff9", - "sha256:124fd7c7bc1e95b1eafc60825f2daf67c73ce7b33f1194731240d24b0d1bf628", - "sha256:26fcb33776857f4072601502d93e1a619f166c9c00befb52826e7b774efaa9db", - "sha256:31ba07c54ef4a897758563e3a0fcc60077698df10180abe4b8165d9895c00ebf", - "sha256:3c49e39ac034fd64fd576d63bb4db53cda89b362768a67f07749d55f128ac18a", - "sha256:52bf0930903818e600ae6c2901f748bc4869c0c406056f679ab9614e5d21a166", - "sha256:5a3f345acff76cad4aa9cb171ee76c590f37394186325d53d1aa25318b0d4a09", - "sha256:5e7ac4e0e79a53451dc2814f6876c2fa6f71452de1498bbe29c0b54b69a986f4", - "sha256:7242790ab6c20316b8e7bb545be48d7ed36e26bbe279fd56f2c4a12510e60b4b", - "sha256:737bd70e454a284d456aa1fa71a0b429dd527bcbf52c5c33f7c8eee81ac16b89", - "sha256:8635d53223b1f561b081ff4adecb828fd484b8efffe542edcfdff471997f7c39", - "sha256:8b818b6c5a920cbe4203b5a6b14256f0e5244338244560da89b7b0f1313ea4b6", - "sha256:8bf38641b4713d77da19e91f8b5296b832e4db87338d6aeffe422d42f1ca896d", - "sha256:a36a48a51e5471513a5aea920cdad84cbd56d70a5057cca3499a637496ea379c", - "sha256:b2243dd033fd02c01212ad5c601dafb44fbb293065f430b0d3dbf03f3254d615", - "sha256:cc547d3ead3754712223abb7b403f0a184e4c3eae18c9bb7fd15adef1597cc4b", - "sha256:cc552b6434b90d9dbed6a4f13339625dc466fd82597119897e9489c953acbc22", - "sha256:f3790156c606299ff499ec44db422f66f05a7363b39eb9d5b064f17bd7d7c47b", - "sha256:f7a21e3d99aa3095ef0553e7ceba36fb693998fbb1226f1392ce33681047465f", - "sha256:fdc6b2cb4b19e431994f25a9160695cc59a4e861710cc6fc97161c5e845fc579" + "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", + "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", + "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", + "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", + "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", + "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", + "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", + "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", + "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", + "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", + "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", + "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", + "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", + "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", + "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", + "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", + "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", + "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", + "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", + "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", + "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", + "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", + "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", + "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", + "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", + "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", + "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", + "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", + "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" ], "index": "pypi", - "version": "==5.4" + "version": "==5.4.1" }, "redis": { "hashes": [ "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2", "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==3.5.3" }, "sentry-sdk": { @@ -424,6 +549,7 @@ "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.15.0" }, "sortedcontainers": { @@ -446,6 +572,7 @@ "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df", "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", "version": "==1.26.4" }, "yarl": { @@ -468,6 +595,7 @@ "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317", "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6" ], + "markers": "python_version >= '3.5'", "version": "==1.5.1" } }, @@ -484,6 +612,7 @@ "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==20.3.0" }, "cfgv": { @@ -491,6 +620,7 @@ "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d", "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1" ], + "markers": "python_full_version >= '3.6.1'", "version": "==3.2.0" }, "distlib": { @@ -509,19 +639,19 @@ }, "flake8": { "hashes": [ - "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839", - "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b" + "sha256:12d05ab02614b6aee8df7c36b97d1a3b2372761222b19b58621355e82acddcff", + "sha256:78873e372b12b093da7b5e5ed302e8ad9e988b38b063b61ad937f26ca58fc5f0" ], "index": "pypi", - "version": "==3.8.4" + "version": "==3.9.0" }, "flake8-annotations": { "hashes": [ - "sha256:3a377140556aecf11fa9f3bb18c10db01f5ea56dc79a730e2ec9b4f1f49e2055", - "sha256:e17947a48a5b9f632fe0c72682fc797c385e451048e7dfb20139f448a074cb3e" + "sha256:0d6cd2e770b5095f09689c9d84cc054c51b929c41a68969ea1beb4b825cac515", + "sha256:d10c4638231f8a50c0a597c4efce42bd7b7d85df4f620a0ddaca526138936a4f" ], "index": "pypi", - "version": "==2.5.0" + "version": "==2.6.2" }, "flake8-bugbear": { "hashes": [ @@ -533,11 +663,11 @@ }, "flake8-docstrings": { "hashes": [ - "sha256:3d5a31c7ec6b7367ea6506a87ec293b94a0a46c0bce2bb4975b7f1d09b6f3717", - "sha256:a256ba91bc52307bef1de59e2a009c3cf61c3d0952dbe035d6ff7208940c2edc" + "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde", + "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b" ], "index": "pypi", - "version": "==1.5.0" + "version": "==1.6.0" }, "flake8-import-order": { "hashes": [ @@ -582,6 +712,7 @@ "sha256:43cb1965e84cdd247e875dec6d13332ef5be355ddc16776396d98089b9053d87", "sha256:c7c0f590526008911ccc5ceee6ed7b085cbc92f7b6591d0ee5913a130ad64034" ], + "markers": "python_full_version >= '3.6.1'", "version": "==2.2.2" }, "mccabe": { @@ -608,65 +739,77 @@ }, "pre-commit": { "hashes": [ - "sha256:16212d1fde2bed88159287da88ff03796863854b04dc9f838a55979325a3d20e", - "sha256:399baf78f13f4de82a29b649afd74bef2c4e28eb4f021661fc7f29246e8c7a3a" + "sha256:029d53cb83c241fe7d66eeee1e24db426f42c858f15a38d20bcefd8d8e05c9da", + "sha256:46b6ffbab37986c47d0a35e40906ae029376deed89a0eb2e446fb6e67b220427" ], "index": "pypi", - "version": "==2.10.1" + "version": "==2.12.0" }, "pycodestyle": { "hashes": [ - "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", - "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" + "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068", + "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef" ], - "version": "==2.6.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.7.0" }, "pydocstyle": { "hashes": [ "sha256:164befb520d851dbcf0e029681b91f4f599c62c5cd8933fd54b1bfbd50e89e1f", "sha256:d4449cf16d7e6709f63192146706933c7a334af7c0f083904799ccb851c50f6d" ], + "markers": "python_version >= '3.6'", "version": "==6.0.0" }, "pyflakes": { "hashes": [ - "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", - "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" + "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3", + "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db" ], - "version": "==2.2.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.3.1" }, "pyyaml": { "hashes": [ - "sha256:02c78d77281d8f8d07a255e57abdbf43b02257f59f50cc6b636937d68efa5dd0", - "sha256:0dc9f2eb2e3c97640928dec63fd8dc1dd91e6b6ed236bd5ac00332b99b5c2ff9", - "sha256:124fd7c7bc1e95b1eafc60825f2daf67c73ce7b33f1194731240d24b0d1bf628", - "sha256:26fcb33776857f4072601502d93e1a619f166c9c00befb52826e7b774efaa9db", - "sha256:31ba07c54ef4a897758563e3a0fcc60077698df10180abe4b8165d9895c00ebf", - "sha256:3c49e39ac034fd64fd576d63bb4db53cda89b362768a67f07749d55f128ac18a", - "sha256:52bf0930903818e600ae6c2901f748bc4869c0c406056f679ab9614e5d21a166", - "sha256:5a3f345acff76cad4aa9cb171ee76c590f37394186325d53d1aa25318b0d4a09", - "sha256:5e7ac4e0e79a53451dc2814f6876c2fa6f71452de1498bbe29c0b54b69a986f4", - "sha256:7242790ab6c20316b8e7bb545be48d7ed36e26bbe279fd56f2c4a12510e60b4b", - "sha256:737bd70e454a284d456aa1fa71a0b429dd527bcbf52c5c33f7c8eee81ac16b89", - "sha256:8635d53223b1f561b081ff4adecb828fd484b8efffe542edcfdff471997f7c39", - "sha256:8b818b6c5a920cbe4203b5a6b14256f0e5244338244560da89b7b0f1313ea4b6", - "sha256:8bf38641b4713d77da19e91f8b5296b832e4db87338d6aeffe422d42f1ca896d", - "sha256:a36a48a51e5471513a5aea920cdad84cbd56d70a5057cca3499a637496ea379c", - "sha256:b2243dd033fd02c01212ad5c601dafb44fbb293065f430b0d3dbf03f3254d615", - "sha256:cc547d3ead3754712223abb7b403f0a184e4c3eae18c9bb7fd15adef1597cc4b", - "sha256:cc552b6434b90d9dbed6a4f13339625dc466fd82597119897e9489c953acbc22", - "sha256:f3790156c606299ff499ec44db422f66f05a7363b39eb9d5b064f17bd7d7c47b", - "sha256:f7a21e3d99aa3095ef0553e7ceba36fb693998fbb1226f1392ce33681047465f", - "sha256:fdc6b2cb4b19e431994f25a9160695cc59a4e861710cc6fc97161c5e845fc579" + "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", + "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", + "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", + "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", + "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", + "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", + "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", + "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", + "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", + "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", + "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", + "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", + "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", + "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", + "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", + "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", + "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", + "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", + "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", + "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", + "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", + "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", + "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", + "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", + "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", + "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", + "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", + "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", + "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" ], "index": "pypi", - "version": "==5.4" + "version": "==5.4.1" }, "six": { "hashes": [ "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.15.0" }, "snowballstemmer": { @@ -681,6 +824,7 @@ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.10.2" }, "virtualenv": { @@ -688,6 +832,7 @@ "sha256:49ec4eb4c224c6f7dd81bb6d0a28a09ecae5894f4e593c89b0db0885f565a107", "sha256:83f95875d382c7abafe06bd2a4cdd1b363e1bb77e02f155ebe8ac082a916b37c" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==20.4.3" } } diff --git a/bot/exts/evergreen/latex.py b/bot/exts/evergreen/latex.py new file mode 100644 index 00000000..79b50ccd --- /dev/null +++ b/bot/exts/evergreen/latex.py @@ -0,0 +1,57 @@ +from io import BytesIO +from typing import Union + +import discord +import matplotlib.pyplot as plt +from discord.ext import commands + +# configure fonts and colors for matplotlib +plt.rcParams.update( + { + "font.size": 16, + "mathtext.fontset": "cm", # Computer Modern font set + "mathtext.rm": "serif", + "figure.facecolor": "38383F", # matches Discord's dark mode background color + "text.color": "white", + } +) + + +class Latex(commands.Cog): + """Renders latex.""" + + def __init__(self, bot: commands.Bot): + self.bot = bot + + @staticmethod + def _render(text: str) -> Union[BytesIO, str]: + """Return the rendered image if latex compiles without errors, otherwise return the error message.""" + text = text.replace(r"\\", "$\n$") # matplotlib uses \n for newlines, not \\ + fig = plt.figure() + + try: + fig.text(0, 1, text, horizontalalignment="left", verticalalignment="top") + + rendered_image = BytesIO() + plt.savefig(rendered_image, bbox_inches="tight", dpi=600) + rendered_image.seek(0) + return rendered_image + + except ValueError as e: + return str(e) + + @commands.command() + async def latex(self, ctx: commands.Context, *, text: str) -> None: + """Renders the text in latex and sends the image.""" + async with ctx.typing(): + image = self._render(text) + + if isinstance(image, BytesIO): + await ctx.send(file=discord.File(image, "latex.png")) + else: + await ctx.send(image) + + +def setup(bot: commands.Bot) -> None: + """Load the Latex Cog.""" + bot.add_cog(Latex(bot)) -- cgit v1.2.3 From a36a58d81b64412458a7b51fbfb4a37e88a060fe Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 8 Apr 2021 19:28:59 +0100 Subject: Add word boundaries to the issues regex --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 0d43326d..b653f7ae 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -226,7 +226,7 @@ class Issues(commands.Cog): return # `issues` will hold a list of two element tuples `(repository, issue_number)` - issues = re.findall(fr"({self.repo_regex})#(\d+)", message.content) + issues = re.findall(fr"\b({self.repo_regex})#(\d+)\b", message.content) links = [] if issues: -- cgit v1.2.3 From 3e09dd8500d91486a2a3c776cb213d61204f3c61 Mon Sep 17 00:00:00 2001 From: Shakya Majumdar Date: Fri, 9 Apr 2021 11:56:02 +0530 Subject: add markdown support --- bot/exts/evergreen/latex.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/latex.py b/bot/exts/evergreen/latex.py index 79b50ccd..1f9de163 100644 --- a/bot/exts/evergreen/latex.py +++ b/bot/exts/evergreen/latex.py @@ -1,3 +1,4 @@ +import re from io import BytesIO from typing import Union @@ -16,6 +17,16 @@ plt.rcParams.update( } ) +FORMATTED_CODE_REGEX = re.compile( + r"(?P(?P```)|``?)" # code delimiter: 1-3 backticks; (?P=block) only matches if it's a block + r"(?(block)(?:(?P[a-z]+)\n)?)" # if we're in a block, match optional language (only letters plus newline) + r"(?:[ \t]*\n)*" # any blank (empty or tabs/spaces only) lines before the code + r"(?P.*?)" # extract all code inside the markup + r"\s*" # any more whitespace before the end of the code markup + r"(?P=delim)", # match the exact same delimiter from the start again + re.DOTALL | re.IGNORECASE, # "." also matches newlines, case insensitive +) + class Latex(commands.Cog): """Renders latex.""" @@ -26,7 +37,6 @@ class Latex(commands.Cog): @staticmethod def _render(text: str) -> Union[BytesIO, str]: """Return the rendered image if latex compiles without errors, otherwise return the error message.""" - text = text.replace(r"\\", "$\n$") # matplotlib uses \n for newlines, not \\ fig = plt.figure() try: @@ -40,16 +50,26 @@ class Latex(commands.Cog): except ValueError as e: return str(e) + @staticmethod + def _prepare_input(text: str) -> str: + text = text.replace(r"\\", "$\n$") # matplotlib uses \n for newlines, not \\ + + if match := FORMATTED_CODE_REGEX.match(text): + return match.group("code") + else: + return text + @commands.command() async def latex(self, ctx: commands.Context, *, text: str) -> None: """Renders the text in latex and sends the image.""" + text = self._prepare_input(text) async with ctx.typing(): image = self._render(text) if isinstance(image, BytesIO): await ctx.send(file=discord.File(image, "latex.png")) else: - await ctx.send(image) + await ctx.send("```" + image + "```") def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From 0f2c076e6e64cf8f09fc8a89872767121acdfd83 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Fri, 9 Apr 2021 09:16:40 +0200 Subject: Issues: use soft_red instead of red Co-authored-by: Shivansh-007 --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index b653f7ae..3fb69127 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -239,7 +239,7 @@ class Issues(commands.Cog): "You can't retrieve issues from DMs. " f"Try again in <#{Channels.community_bot_commands}>" ), - colour=discord.Colour.red() + colour=Colours.soft_red ) ) return -- cgit v1.2.3 From e095a4f71f81547378af7c5cdd42fed4cbf8a5ac Mon Sep 17 00:00:00 2001 From: Shakya Majumdar Date: Fri, 9 Apr 2021 19:12:18 +0530 Subject: run _render in executor, raise BadArgument for invalid input --- bot/exts/evergreen/latex.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/bot/exts/evergreen/latex.py b/bot/exts/evergreen/latex.py index 1f9de163..b9652a75 100644 --- a/bot/exts/evergreen/latex.py +++ b/bot/exts/evergreen/latex.py @@ -1,6 +1,8 @@ +import asyncio import re +from concurrent.futures import ThreadPoolExecutor +from functools import partial from io import BytesIO -from typing import Union import discord import matplotlib.pyplot as plt @@ -31,12 +33,9 @@ FORMATTED_CODE_REGEX = re.compile( class Latex(commands.Cog): """Renders latex.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - @staticmethod - def _render(text: str) -> Union[BytesIO, str]: - """Return the rendered image if latex compiles without errors, otherwise return the error message.""" + def _render(text: str) -> BytesIO: + """Return the rendered image if latex compiles without errors, otherwise raise a BadArgument Exception.""" fig = plt.figure() try: @@ -48,7 +47,7 @@ class Latex(commands.Cog): return rendered_image except ValueError as e: - return str(e) + raise commands.BadArgument(str(e)) @staticmethod def _prepare_input(text: str) -> str: @@ -64,12 +63,13 @@ class Latex(commands.Cog): """Renders the text in latex and sends the image.""" text = self._prepare_input(text) async with ctx.typing(): - image = self._render(text) - if isinstance(image, BytesIO): - await ctx.send(file=discord.File(image, "latex.png")) - else: - await ctx.send("```" + image + "```") + with ThreadPoolExecutor() as pool: + image = await asyncio.get_running_loop().run_in_executor( + pool, partial(self._render, text) + ) + + await ctx.send(file=discord.File(image, "latex.png")) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From 29c77d6c36d693daeff33fc1ed3240c8927e5217 Mon Sep 17 00:00:00 2001 From: Shakya Majumdar Date: Fri, 9 Apr 2021 19:38:11 +0530 Subject: remove redundant use of functools.partial --- bot/exts/evergreen/latex.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/evergreen/latex.py b/bot/exts/evergreen/latex.py index b9652a75..a8ed56fb 100644 --- a/bot/exts/evergreen/latex.py +++ b/bot/exts/evergreen/latex.py @@ -1,7 +1,6 @@ import asyncio import re from concurrent.futures import ThreadPoolExecutor -from functools import partial from io import BytesIO import discord @@ -66,7 +65,7 @@ class Latex(commands.Cog): with ThreadPoolExecutor() as pool: image = await asyncio.get_running_loop().run_in_executor( - pool, partial(self._render, text) + pool, self._render, text ) await ctx.send(file=discord.File(image, "latex.png")) -- cgit v1.2.3 From 70fa9a8e36d586d6fbc1690a80a598e94506883b Mon Sep 17 00:00:00 2001 From: janine9vn Date: Fri, 9 Apr 2021 12:52:04 -0400 Subject: Cutover of rattlesnake to lancebot This is an initial cutover of the rattlesnake internal eval to Sir Lancebot. This commit by itself will not work. This is a simple drop in of rattlesnake code so there is context as to what has changed and why. --- bot/exts/internal_eval/__init__.py | 0 bot/exts/internal_eval/internal_eval.py | 152 ++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 bot/exts/internal_eval/__init__.py create mode 100644 bot/exts/internal_eval/internal_eval.py diff --git a/bot/exts/internal_eval/__init__.py b/bot/exts/internal_eval/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bot/exts/internal_eval/internal_eval.py b/bot/exts/internal_eval/internal_eval.py new file mode 100644 index 00000000..f6812942 --- /dev/null +++ b/bot/exts/internal_eval/internal_eval.py @@ -0,0 +1,152 @@ +import logging +import re +import textwrap +import typing + +import discord +from discord.ext import commands + +from rattlesnake.bot import Rattlesnake +from rattlesnake.constants import ADMIN_ROLES +from rattlesnake.utils import in_whitelist +from .helpers import EvalContext + +__all__ = ["InternalEval"] + +log = logging.getLogger("rattlesnake.exts.admin_tools.internal_eval") + +CODEBLOCK_REGEX = re.compile(r"(^```(py(thon)?)?\n)|(```$)") + + +class InternalEval(commands.Cog): + """Top secret code evaluation for admins and owners.""" + + def __init__(self, bot: Rattlesnake): + self.bot = bot + self.locals = {} + + @staticmethod + def shorten_output( + output: str, + max_length: int = 1900, + placeholder: str = "\n[output truncated]" + ) -> str: + """ + Shorten the `output` so it's shorter than `max_length`. + There are three tactics for this, tried in the following order: + - Shorten the output on a line-by-line basis + - Shorten the output on any whitespace character + - Shorten the output solely on character count + """ + max_length = max_length - len(placeholder) + + shortened_output = [] + char_count = 0 + for line in output.split("\n"): + if char_count + len(line) > max_length: + break + shortened_output.append(line) + char_count += len(line) + 1 # account for (possible) line ending + + if shortened_output: + shortened_output.append(placeholder) + return "\n".join(shortened_output) + + shortened_output = textwrap.shorten(output, width=max_length, placeholder=placeholder) + + if shortened_output.strip() == placeholder.strip(): + # `textwrap` was unable to find whitespace to shorten on, so it has + # reduced the output to just the placeholder. Let's shorten based on + # characters instead. + shortened_output = output[:max_length] + placeholder + + return shortened_output + + async def _upload_output(self, output: str) -> typing.Optional[str]: + """Upload `internal eval` output to our pastebin and return the url.""" + try: + async with self.bot.http_session.post( + "https://paste.pythondiscord.com/documents", data=output, raise_for_status=True + ) as resp: + data = await resp.json() + + if "key" in data: + return f"https://paste.pythondiscord.com/{data['key']}" + except Exception: + # 400 (Bad Request) means there are too many characters + log.exception("Failed to upload `internal eval` output to paste service!") + + async def _send_output(self, ctx: commands.Context, output: str) -> None: + """Send the `internal eval` output to the command invocation context.""" + upload_message = "" + if len(output) >= 1980: + # The output is too long, let's truncate it for in-channel output and + # upload the complete output to the paste service. + url = await self._upload_output(output) + + if url: + upload_message = f"\nFull output here: {url}" + else: + upload_message = "\n:warning: Failed to upload full output!" + + output = self.shorten_output(output) + + await ctx.send(f"```py\n{output}```{upload_message}") + + async def _eval(self, ctx: commands.Context, code: str) -> None: + """Evaluate the `code` in the current evaluation context.""" + if code.startswith("exit"): + self.locals = {} + await ctx.send("The evaluation context was reset.") + return + + context_vars = { + "message": ctx.message, + "author": ctx.message.author, + "channel": ctx.channel, + "guild": ctx.guild, + "ctx": ctx, + "self": self, + "bot": self.bot, + "discord": discord, + } + + eval_context = EvalContext(context_vars, self.locals) + + log.trace("Preparing the evaluation by parsing the AST of the code") + error = eval_context.prepare_eval(code) + + if error: + log.trace("The code can't be evaluated due to an error") + await ctx.send(f"```py\n{error}\n```") + return + + log.trace("Evaluate the AST we've generated for the evaluation") + new_locals = await eval_context.run_eval() + + log.trace("Updating locals with those set during evaluation") + self.locals.update(new_locals) + + log.trace("Sending the formatted output back to the context") + await self._send_output(ctx, eval_context.format_output()) + + @commands.group(name='internal', aliases=('int',)) + @in_whitelist(roles=ADMIN_ROLES) + async def internal_group(self, ctx: commands.Context) -> None: + """Internal commands. Top secret!""" + if not ctx.invoked_subcommand: + await ctx.send_help(ctx.command) + + @internal_group.command(name='eval', aliases=('e',)) + @in_whitelist(roles=ADMIN_ROLES) + async def eval(self, ctx: commands.Context, *, code: str) -> None: + """Run eval in a REPL-like format.""" + code = CODEBLOCK_REGEX.sub("", code.strip()) + await self._eval(ctx, code) + + @internal_group.command(name='reset', aliases=("clear", "exit", "r", "c")) + @in_whitelist(roles=ADMIN_ROLES) + async def reset(self, ctx: commands.Context) -> None: + """Run eval in a REPL-like format.""" + self.locals = {} + await ctx.send("The evaluation context was reset.") -- cgit v1.2.3 From 8ca0fd85045bf9fbb9b78a117f3a2e30ee1d1fa5 Mon Sep 17 00:00:00 2001 From: janine9vn Date: Fri, 9 Apr 2021 12:54:59 -0400 Subject: Add helpers for internal eval Cutting over the rattlesnake helpers specifically for internal_eval. I am mirroring the rattlesnake structure as much as I can initially to ensure basic functionality before migrating functions to fit more within Sir Lancebot's file structure. --- bot/exts/internal_eval/helpers.py | 243 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 bot/exts/internal_eval/helpers.py diff --git a/bot/exts/internal_eval/helpers.py b/bot/exts/internal_eval/helpers.py new file mode 100644 index 00000000..5c602e4d --- /dev/null +++ b/bot/exts/internal_eval/helpers.py @@ -0,0 +1,243 @@ +import ast +import collections +import contextlib +import functools +import inspect +import io +import logging +import sys +import traceback +import types +import typing + + +log = logging.getLogger("rattlesnake.exts.admin_tools.internal_eval") + +# A type alias to annotate the tuples returned from `sys.exc_info()` +ExcInfo = typing.Tuple[typing.Type[Exception], Exception, types.TracebackType] +Namespace = typing.Dict[str, typing.Any] + +# This will be used as an coroutine function wrapper for the code +# to be evaluated. The wrapper contains one `pass` statement which +# will be replaced with `ast` with the code that we want to have +# evaluated. +# The function redirects output and captures exceptions that were +# raised in the code we evaluate. The latter is used to provide a +# meaningful traceback to the end user. +EVAL_WRAPPER = """ +async def _eval_wrapper_function(): + try: + with contextlib.redirect_stdout(_eval_context.stdout): + pass + if '_value_last_expression' in locals(): + if inspect.isawaitable(_value_last_expression): + _value_last_expression = await _value_last_expression + _eval_context._value_last_expression = _value_last_expression + else: + _eval_context._value_last_expression = None + except Exception: + _eval_context.exc_info = sys.exc_info() + finally: + _eval_context.locals = locals() +_eval_context.function = _eval_wrapper_function +""" + + +def format_internal_eval_exception(exc_info: ExcInfo, code: str) -> str: + """Format an exception caught while evaluation code by inserting lines.""" + exc_type, exc_value, tb = exc_info + stack_summary = traceback.StackSummary.extract(traceback.walk_tb(tb)) + code = code.split("\n") + + output = ["Traceback (most recent call last):"] + for frame in stack_summary: + if frame.filename == "": + line = code[frame.lineno - 1].lstrip() + + if frame.name == "_eval_wrapper_function": + name = "" + else: + name = frame.name + else: + line = frame.line + name = frame.name + + output.append( + f' File "{frame.filename}", line {frame.lineno}, in {name}\n' + f" {line}" + ) + + output.extend(traceback.format_exception_only(exc_type, exc_value)) + return "\n".join(output) + + +class EvalContext: + """ + Represents the current `internal eval` context. + The context remembers names set during earlier runs of `internal eval`. To + clear the context, use the `?internal clear` command. + """ + + def __init__(self, context_vars: Namespace, local_vars: Namespace) -> None: + self._locals = dict(local_vars) + self.context_vars = dict(context_vars) + + self.stdout = io.StringIO() + self._value_last_expression = None + self.exc_info = None + self.code = "" + self.function = None + self.eval_tree = None + + @property + def dependencies(self) -> typing.Dict[str, typing.Any]: + """ + Return a mapping of the dependencies for the wrapper function. + By using a property descriptor, the mapping can't be accidentally + mutated during evaluation. This ensures the dependencies are always + available. + """ + return { + "print": functools.partial(print, file=self.stdout), + "contextlib": contextlib, + "inspect": inspect, + "sys": sys, + "_eval_context": self, + "_": self._value_last_expression, + } + + @property + def locals(self) -> typing.Dict[str, typing.Any]: + """Return a mapping of names->values needed for evaluation.""" + return {**collections.ChainMap(self.dependencies, self.context_vars, self._locals)} + + @locals.setter + def locals(self, locals_: typing.Dict[str, typing.Any]) -> None: + """Update the contextual mapping of names to values.""" + log.trace(f"Updating {self._locals} with {locals_}") + self._locals.update(locals_) + + def prepare_eval(self, code: str) -> typing.Optional[str]: + """Prepare an evaluation by processing the code and setting up the context.""" + self.code = code + + if not self.code: + log.debug("No code was attached to the evaluation command") + return "[No code detected]" + + try: + code_tree = ast.parse(code, filename="") + except SyntaxError: + log.debug("Got a SyntaxError while parsing the eval code") + return "".join(traceback.format_exception(*sys.exc_info(), limit=0)) + + log.trace("Parsing the AST to see if there's a trailing expression we need to capture") + code_tree = CaptureLastExpression(code_tree).capture() + + log.trace("Wrapping the AST in the AST of the wrapper coroutine") + eval_tree = WrapEvalCodeTree(code_tree).wrap() + + self.eval_tree = eval_tree + return None + + async def run_eval(self) -> Namespace: + """Run the evaluation and return the updated locals.""" + log.trace("Compiling the AST to bytecode using `exec` mode") + compiled_code = compile(self.eval_tree, filename="", mode="exec") + + log.trace("Executing the compiled code with the desired namespace environment") + exec(compiled_code, self.locals) # noqa: B102,S102 + + log.trace("Awaiting the created evaluation wrapper coroutine.") + await self.function() + + log.trace("Returning the updated captured locals.") + return self._locals + + def format_output(self) -> str: + """Format the output of the most recent evaluation.""" + output = [] + + log.trace(f"Getting output from stdout `{id(self.stdout)}`") + stdout_text = self.stdout.getvalue() + if stdout_text: + log.trace("Appending output captured from stdout/print") + output.append(stdout_text) + + if self._value_last_expression is not None: + log.trace("Appending the output of a captured trialing expression") + output.append(f"[Captured] {self._value_last_expression!r}") + + if self.exc_info: + log.trace("Appending exception information") + output.append(format_internal_eval_exception(self.exc_info, self.code)) + + log.trace(f"Generated output: {output!r}") + return "\n".join(output) or "[No output]" + + +class WrapEvalCodeTree(ast.NodeTransformer): + """Wraps the AST of eval code with the wrapper function.""" + + def __init__(self, eval_code_tree: ast.AST, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.eval_code_tree = eval_code_tree + + # To avoid mutable aliasing, parse the WRAPPER_FUNC for each wrapping + self.wrapper = ast.parse(EVAL_WRAPPER, filename="") + + def wrap(self) -> ast.AST: + """Wrap the tree of the code by the tree of the wrapper function.""" + new_tree = self.visit(self.wrapper) + return ast.fix_missing_locations(new_tree) + + def visit_Pass(self, node: ast.Pass) -> typing.List[ast.AST]: # noqa: N802 + """ + Replace the `_ast.Pass` node in the wrapper function by the eval AST. + This method works on the assumption that there's a single `pass` + statement in the wrapper function. + """ + return list(ast.iter_child_nodes(self.eval_code_tree)) + + +class CaptureLastExpression(ast.NodeTransformer): + """Captures the return value from a loose expression.""" + + def __init__(self, tree: ast.AST, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.tree = tree + self.last_node = list(ast.iter_child_nodes(tree))[-1] + + def visit_Expr(self, node: ast.Expr) -> typing.Union[ast.Expr, ast.Assign]: # noqa: N802 + """ + Replace the Expr node that is last child node of Module with an assignment. + We use an assignment to capture the value of the last node, if it's a loose + Expr node. Normally, the value of an Expr node is lost, meaning we don't get + the output of such a last "loose" expression. By assigning it a name, we can + retrieve it for our output. + """ + if node is not self.last_node: + return node + + log.trace("Found a trailing last expression in the evaluation code") + + log.trace("Creating assignment statement with trailing expression as the right-hand side") + right_hand_side = list(ast.iter_child_nodes(node))[0] + + assignment = ast.Assign( + targets=[ast.Name(id='_value_last_expression', ctx=ast.Store())], + value=right_hand_side, + lineno=node.lineno, + col_offset=0, + ) + ast.fix_missing_locations(assignment) + return assignment + + def capture(self) -> ast.AST: + """Capture the value of the last expression with an assignment.""" + if not isinstance(self.last_node, ast.Expr): + # We only have to replace a node if the very last node is an Expr node + return self.tree + + new_tree = self.visit(self.tree) + return ast.fix_missing_locations(new_tree) -- cgit v1.2.3 From edb6ffab317499689d7b8f5d0b884a8d10a01a76 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 9 Apr 2021 13:28:36 -0400 Subject: Update comment --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 7604c438..fa07b674 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -158,7 +158,7 @@ class Issues(commands.Cog): emoji = Emojis.pull_request_draft elif pull_data["state"] == "open": emoji = Emojis.pull_request - # When the status is 204 this means that the state of the PR is merged + # When 'merged_at' is not None, this means that the state of the PR is merged elif pull_data["merged_at"] is not None: emoji = Emojis.merge else: -- cgit v1.2.3 From 80a9a40b3c27376657486cf183d4c0c7d4e9880f Mon Sep 17 00:00:00 2001 From: janine9vn Date: Fri, 9 Apr 2021 13:41:45 -0400 Subject: Realigned to SirLancebot Structure The code for rattlesnakes's internal eval was aligned to Sir Lancebot's structure. It was mostly renaming rattlesnake to bot and changing how some of the imports were setup as. It also included changing the __init__.py to match the Sir Lancebot cog structure. Additionally, the whitelist check has been significantly simplified to only be a role check for the admin role. The rattlesnake implementation had a more robust `in_whitelist` decorator, so it may be worth investigating adding that in if we see fit. For now, it's a simple `with_role` decorator check. The name of the cog file itself was changed to include an underscore to sidestep what I think was a namespace collision that would prevent the setup function from properly running. --- bot/exts/internal_eval/__init__.py | 10 ++ bot/exts/internal_eval/_helpers.py | 243 +++++++++++++++++++++++++++++++ bot/exts/internal_eval/_internal_eval.py | 152 +++++++++++++++++++ 3 files changed, 405 insertions(+) create mode 100644 bot/exts/internal_eval/_helpers.py create mode 100644 bot/exts/internal_eval/_internal_eval.py diff --git a/bot/exts/internal_eval/__init__.py b/bot/exts/internal_eval/__init__.py index e69de29b..695fa74d 100644 --- a/bot/exts/internal_eval/__init__.py +++ b/bot/exts/internal_eval/__init__.py @@ -0,0 +1,10 @@ +from bot.bot import Bot + + +def setup(bot: Bot) -> None: + """Set up the Internal Eval extension.""" + # Import the Cog at runtime to prevent side effects like defining + # RedisCache instances too early. + from ._internal_eval import InternalEval + + bot.add_cog(InternalEval(bot)) diff --git a/bot/exts/internal_eval/_helpers.py b/bot/exts/internal_eval/_helpers.py new file mode 100644 index 00000000..5c602e4d --- /dev/null +++ b/bot/exts/internal_eval/_helpers.py @@ -0,0 +1,243 @@ +import ast +import collections +import contextlib +import functools +import inspect +import io +import logging +import sys +import traceback +import types +import typing + + +log = logging.getLogger("rattlesnake.exts.admin_tools.internal_eval") + +# A type alias to annotate the tuples returned from `sys.exc_info()` +ExcInfo = typing.Tuple[typing.Type[Exception], Exception, types.TracebackType] +Namespace = typing.Dict[str, typing.Any] + +# This will be used as an coroutine function wrapper for the code +# to be evaluated. The wrapper contains one `pass` statement which +# will be replaced with `ast` with the code that we want to have +# evaluated. +# The function redirects output and captures exceptions that were +# raised in the code we evaluate. The latter is used to provide a +# meaningful traceback to the end user. +EVAL_WRAPPER = """ +async def _eval_wrapper_function(): + try: + with contextlib.redirect_stdout(_eval_context.stdout): + pass + if '_value_last_expression' in locals(): + if inspect.isawaitable(_value_last_expression): + _value_last_expression = await _value_last_expression + _eval_context._value_last_expression = _value_last_expression + else: + _eval_context._value_last_expression = None + except Exception: + _eval_context.exc_info = sys.exc_info() + finally: + _eval_context.locals = locals() +_eval_context.function = _eval_wrapper_function +""" + + +def format_internal_eval_exception(exc_info: ExcInfo, code: str) -> str: + """Format an exception caught while evaluation code by inserting lines.""" + exc_type, exc_value, tb = exc_info + stack_summary = traceback.StackSummary.extract(traceback.walk_tb(tb)) + code = code.split("\n") + + output = ["Traceback (most recent call last):"] + for frame in stack_summary: + if frame.filename == "": + line = code[frame.lineno - 1].lstrip() + + if frame.name == "_eval_wrapper_function": + name = "" + else: + name = frame.name + else: + line = frame.line + name = frame.name + + output.append( + f' File "{frame.filename}", line {frame.lineno}, in {name}\n' + f" {line}" + ) + + output.extend(traceback.format_exception_only(exc_type, exc_value)) + return "\n".join(output) + + +class EvalContext: + """ + Represents the current `internal eval` context. + The context remembers names set during earlier runs of `internal eval`. To + clear the context, use the `?internal clear` command. + """ + + def __init__(self, context_vars: Namespace, local_vars: Namespace) -> None: + self._locals = dict(local_vars) + self.context_vars = dict(context_vars) + + self.stdout = io.StringIO() + self._value_last_expression = None + self.exc_info = None + self.code = "" + self.function = None + self.eval_tree = None + + @property + def dependencies(self) -> typing.Dict[str, typing.Any]: + """ + Return a mapping of the dependencies for the wrapper function. + By using a property descriptor, the mapping can't be accidentally + mutated during evaluation. This ensures the dependencies are always + available. + """ + return { + "print": functools.partial(print, file=self.stdout), + "contextlib": contextlib, + "inspect": inspect, + "sys": sys, + "_eval_context": self, + "_": self._value_last_expression, + } + + @property + def locals(self) -> typing.Dict[str, typing.Any]: + """Return a mapping of names->values needed for evaluation.""" + return {**collections.ChainMap(self.dependencies, self.context_vars, self._locals)} + + @locals.setter + def locals(self, locals_: typing.Dict[str, typing.Any]) -> None: + """Update the contextual mapping of names to values.""" + log.trace(f"Updating {self._locals} with {locals_}") + self._locals.update(locals_) + + def prepare_eval(self, code: str) -> typing.Optional[str]: + """Prepare an evaluation by processing the code and setting up the context.""" + self.code = code + + if not self.code: + log.debug("No code was attached to the evaluation command") + return "[No code detected]" + + try: + code_tree = ast.parse(code, filename="") + except SyntaxError: + log.debug("Got a SyntaxError while parsing the eval code") + return "".join(traceback.format_exception(*sys.exc_info(), limit=0)) + + log.trace("Parsing the AST to see if there's a trailing expression we need to capture") + code_tree = CaptureLastExpression(code_tree).capture() + + log.trace("Wrapping the AST in the AST of the wrapper coroutine") + eval_tree = WrapEvalCodeTree(code_tree).wrap() + + self.eval_tree = eval_tree + return None + + async def run_eval(self) -> Namespace: + """Run the evaluation and return the updated locals.""" + log.trace("Compiling the AST to bytecode using `exec` mode") + compiled_code = compile(self.eval_tree, filename="", mode="exec") + + log.trace("Executing the compiled code with the desired namespace environment") + exec(compiled_code, self.locals) # noqa: B102,S102 + + log.trace("Awaiting the created evaluation wrapper coroutine.") + await self.function() + + log.trace("Returning the updated captured locals.") + return self._locals + + def format_output(self) -> str: + """Format the output of the most recent evaluation.""" + output = [] + + log.trace(f"Getting output from stdout `{id(self.stdout)}`") + stdout_text = self.stdout.getvalue() + if stdout_text: + log.trace("Appending output captured from stdout/print") + output.append(stdout_text) + + if self._value_last_expression is not None: + log.trace("Appending the output of a captured trialing expression") + output.append(f"[Captured] {self._value_last_expression!r}") + + if self.exc_info: + log.trace("Appending exception information") + output.append(format_internal_eval_exception(self.exc_info, self.code)) + + log.trace(f"Generated output: {output!r}") + return "\n".join(output) or "[No output]" + + +class WrapEvalCodeTree(ast.NodeTransformer): + """Wraps the AST of eval code with the wrapper function.""" + + def __init__(self, eval_code_tree: ast.AST, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.eval_code_tree = eval_code_tree + + # To avoid mutable aliasing, parse the WRAPPER_FUNC for each wrapping + self.wrapper = ast.parse(EVAL_WRAPPER, filename="") + + def wrap(self) -> ast.AST: + """Wrap the tree of the code by the tree of the wrapper function.""" + new_tree = self.visit(self.wrapper) + return ast.fix_missing_locations(new_tree) + + def visit_Pass(self, node: ast.Pass) -> typing.List[ast.AST]: # noqa: N802 + """ + Replace the `_ast.Pass` node in the wrapper function by the eval AST. + This method works on the assumption that there's a single `pass` + statement in the wrapper function. + """ + return list(ast.iter_child_nodes(self.eval_code_tree)) + + +class CaptureLastExpression(ast.NodeTransformer): + """Captures the return value from a loose expression.""" + + def __init__(self, tree: ast.AST, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.tree = tree + self.last_node = list(ast.iter_child_nodes(tree))[-1] + + def visit_Expr(self, node: ast.Expr) -> typing.Union[ast.Expr, ast.Assign]: # noqa: N802 + """ + Replace the Expr node that is last child node of Module with an assignment. + We use an assignment to capture the value of the last node, if it's a loose + Expr node. Normally, the value of an Expr node is lost, meaning we don't get + the output of such a last "loose" expression. By assigning it a name, we can + retrieve it for our output. + """ + if node is not self.last_node: + return node + + log.trace("Found a trailing last expression in the evaluation code") + + log.trace("Creating assignment statement with trailing expression as the right-hand side") + right_hand_side = list(ast.iter_child_nodes(node))[0] + + assignment = ast.Assign( + targets=[ast.Name(id='_value_last_expression', ctx=ast.Store())], + value=right_hand_side, + lineno=node.lineno, + col_offset=0, + ) + ast.fix_missing_locations(assignment) + return assignment + + def capture(self) -> ast.AST: + """Capture the value of the last expression with an assignment.""" + if not isinstance(self.last_node, ast.Expr): + # We only have to replace a node if the very last node is an Expr node + return self.tree + + new_tree = self.visit(self.tree) + return ast.fix_missing_locations(new_tree) diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py new file mode 100644 index 00000000..f7a0946b --- /dev/null +++ b/bot/exts/internal_eval/_internal_eval.py @@ -0,0 +1,152 @@ +import logging +import re +import textwrap +import typing + +import discord +from discord.ext import commands + +from bot.bot import Bot +from bot.constants import Roles +from bot.utils.decorators import with_role +from ._helpers import EvalContext + +__all__ = ["InternalEval"] + +log = logging.getLogger("rattlesnake.exts.admin_tools.internal_eval") + +CODEBLOCK_REGEX = re.compile(r"(^```(py(thon)?)?\n)|(```$)") + + +class InternalEval(commands.Cog): + """Top secret code evaluation for admins and owners.""" + + def __init__(self, bot: Bot): + self.bot = bot + self.locals = {} + + @staticmethod + def shorten_output( + output: str, + max_length: int = 1900, + placeholder: str = "\n[output truncated]" + ) -> str: + """ + Shorten the `output` so it's shorter than `max_length`. + There are three tactics for this, tried in the following order: + - Shorten the output on a line-by-line basis + - Shorten the output on any whitespace character + - Shorten the output solely on character count + """ + max_length = max_length - len(placeholder) + + shortened_output = [] + char_count = 0 + for line in output.split("\n"): + if char_count + len(line) > max_length: + break + shortened_output.append(line) + char_count += len(line) + 1 # account for (possible) line ending + + if shortened_output: + shortened_output.append(placeholder) + return "\n".join(shortened_output) + + shortened_output = textwrap.shorten(output, width=max_length, placeholder=placeholder) + + if shortened_output.strip() == placeholder.strip(): + # `textwrap` was unable to find whitespace to shorten on, so it has + # reduced the output to just the placeholder. Let's shorten based on + # characters instead. + shortened_output = output[:max_length] + placeholder + + return shortened_output + + async def _upload_output(self, output: str) -> typing.Optional[str]: + """Upload `internal eval` output to our pastebin and return the url.""" + try: + async with self.bot.http_session.post( + "https://paste.pythondiscord.com/documents", data=output, raise_for_status=True + ) as resp: + data = await resp.json() + + if "key" in data: + return f"https://paste.pythondiscord.com/{data['key']}" + except Exception: + # 400 (Bad Request) means there are too many characters + log.exception("Failed to upload `internal eval` output to paste service!") + + async def _send_output(self, ctx: commands.Context, output: str) -> None: + """Send the `internal eval` output to the command invocation context.""" + upload_message = "" + if len(output) >= 1980: + # The output is too long, let's truncate it for in-channel output and + # upload the complete output to the paste service. + url = await self._upload_output(output) + + if url: + upload_message = f"\nFull output here: {url}" + else: + upload_message = "\n:warning: Failed to upload full output!" + + output = self.shorten_output(output) + + await ctx.send(f"```py\n{output}```{upload_message}") + + async def _eval(self, ctx: commands.Context, code: str) -> None: + """Evaluate the `code` in the current evaluation context.""" + if code.startswith("exit"): + self.locals = {} + await ctx.send("The evaluation context was reset.") + return + + context_vars = { + "message": ctx.message, + "author": ctx.message.author, + "channel": ctx.channel, + "guild": ctx.guild, + "ctx": ctx, + "self": self, + "bot": self.bot, + "discord": discord, + } + + eval_context = EvalContext(context_vars, self.locals) + + log.trace("Preparing the evaluation by parsing the AST of the code") + error = eval_context.prepare_eval(code) + + if error: + log.trace("The code can't be evaluated due to an error") + await ctx.send(f"```py\n{error}\n```") + return + + log.trace("Evaluate the AST we've generated for the evaluation") + new_locals = await eval_context.run_eval() + + log.trace("Updating locals with those set during evaluation") + self.locals.update(new_locals) + + log.trace("Sending the formatted output back to the context") + await self._send_output(ctx, eval_context.format_output()) + + @commands.group(name='internal', aliases=('int',)) + @with_role(Roles.admin) + async def internal_group(self, ctx: commands.Context) -> None: + """Internal commands. Top secret!""" + if not ctx.invoked_subcommand: + await ctx.send_help(ctx.command) + + @internal_group.command(name='eval', aliases=('e',)) + @with_role(Roles.admin) + async def eval(self, ctx: commands.Context, *, code: str) -> None: + """Run eval in a REPL-like format.""" + code = CODEBLOCK_REGEX.sub("", code.strip()) + await self._eval(ctx, code) + + @internal_group.command(name='reset', aliases=("clear", "exit", "r", "c")) + @with_role(Roles.admin) + async def reset(self, ctx: commands.Context) -> None: + """Run eval in a REPL-like format.""" + self.locals = {} + await ctx.send("The evaluation context was reset.") -- cgit v1.2.3 From 4675123f4e3e5a23623a2f2b062f754370c42673 Mon Sep 17 00:00:00 2001 From: Shakya Majumdar Date: Fri, 9 Apr 2021 23:12:54 +0530 Subject: exclude non error-causing lines from try-except --- bot/exts/evergreen/latex.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/bot/exts/evergreen/latex.py b/bot/exts/evergreen/latex.py index a8ed56fb..7cc3432f 100644 --- a/bot/exts/evergreen/latex.py +++ b/bot/exts/evergreen/latex.py @@ -36,18 +36,17 @@ class Latex(commands.Cog): def _render(text: str) -> BytesIO: """Return the rendered image if latex compiles without errors, otherwise raise a BadArgument Exception.""" fig = plt.figure() + rendered_image = BytesIO() + fig.text(0, 1, text, horizontalalignment="left", verticalalignment="top") try: - fig.text(0, 1, text, horizontalalignment="left", verticalalignment="top") - - rendered_image = BytesIO() plt.savefig(rendered_image, bbox_inches="tight", dpi=600) - rendered_image.seek(0) - return rendered_image - except ValueError as e: raise commands.BadArgument(str(e)) + rendered_image.seek(0) + return rendered_image + @staticmethod def _prepare_input(text: str) -> str: text = text.replace(r"\\", "$\n$") # matplotlib uses \n for newlines, not \\ -- cgit v1.2.3 From 0e9ea17010310c7d63d9b0fbba9e3a441cf2c1b5 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Sat, 10 Apr 2021 05:56:22 +0100 Subject: add timed command for timed execution of commands --- bot/exts/evergreen/timed.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 bot/exts/evergreen/timed.py diff --git a/bot/exts/evergreen/timed.py b/bot/exts/evergreen/timed.py new file mode 100644 index 00000000..0537141b --- /dev/null +++ b/bot/exts/evergreen/timed.py @@ -0,0 +1,36 @@ +from copy import copy +from time import perf_counter + +from discord import Message +from discord.ext import commands + + +class TimedCommands(commands.Cog): + """Time the command execution of a command.""" + + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + + @staticmethod + async def create_execution_context(ctx: commands.Context, command: str) -> commands.Context: + """Get a new execution context for a command.""" + msg: Message = copy(ctx.message) + msg._update({"content": f"{ctx.prefix}{command}"}) + + return await ctx.bot.get_context(msg) + + @commands.command(name="timed", aliases=["t"]) + async def timed(self, ctx: commands.Context, *, command: str) -> None: + """Time the command execution of a command.""" + new_ctx = await self.create_execution_context(ctx, command) + + t_start = perf_counter() + await new_ctx.command.invoke(new_ctx) + t_end = perf_counter() + + await ctx.send(f"Command execution for `{new_ctx.command}` finished in {(t_end - t_start):.4f} seconds.") + + +def setup(bot: commands.Bot) -> None: + """Cog load.""" + bot.add_cog(TimedCommands(bot)) -- cgit v1.2.3 From 4bdc3a934c5c9564829466a175f3a8c4545448ba Mon Sep 17 00:00:00 2001 From: Shakya Majumdar Date: Sat, 10 Apr 2021 10:56:39 +0530 Subject: change background color to match discord theme exactly --- bot/exts/evergreen/latex.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/latex.py b/bot/exts/evergreen/latex.py index 7cc3432f..d140e79d 100644 --- a/bot/exts/evergreen/latex.py +++ b/bot/exts/evergreen/latex.py @@ -13,19 +13,19 @@ plt.rcParams.update( "font.size": 16, "mathtext.fontset": "cm", # Computer Modern font set "mathtext.rm": "serif", - "figure.facecolor": "38383F", # matches Discord's dark mode background color + "figure.facecolor": "36393F", # matches Discord's dark mode background color "text.color": "white", } ) FORMATTED_CODE_REGEX = re.compile( - r"(?P(?P```)|``?)" # code delimiter: 1-3 backticks; (?P=block) only matches if it's a block - r"(?(block)(?:(?P[a-z]+)\n)?)" # if we're in a block, match optional language (only letters plus newline) - r"(?:[ \t]*\n)*" # any blank (empty or tabs/spaces only) lines before the code - r"(?P.*?)" # extract all code inside the markup - r"\s*" # any more whitespace before the end of the code markup - r"(?P=delim)", # match the exact same delimiter from the start again - re.DOTALL | re.IGNORECASE, # "." also matches newlines, case insensitive + r"(?P(?P```)|``?)" # code delimiter: 1-3 backticks; (?P=block) only matches if it's a block + r"(?(block)(?:(?P[a-z]+)\n)?)" # if we're in a block, match optional language (only letters plus newline) + r"(?:[ \t]*\n)*" # any blank (empty or tabs/spaces only) lines before the code + r"(?P.*?)" # extract all code inside the markup + r"\s*" # any more whitespace before the end of the code markup + r"(?P=delim)", # match the exact same delimiter from the start again + re.DOTALL | re.IGNORECASE, # "." also matches newlines, case insensitive ) -- cgit v1.2.3 From d7263238b68a3d55f47ec944cc9e85450e2800b6 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Sat, 10 Apr 2021 21:48:26 +0100 Subject: removed unused cog init and fixed timing timed command --- bot/exts/evergreen/timed.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/timed.py b/bot/exts/evergreen/timed.py index 0537141b..58da3dc8 100644 --- a/bot/exts/evergreen/timed.py +++ b/bot/exts/evergreen/timed.py @@ -8,9 +8,6 @@ from discord.ext import commands class TimedCommands(commands.Cog): """Time the command execution of a command.""" - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - @staticmethod async def create_execution_context(ctx: commands.Context, command: str) -> commands.Context: """Get a new execution context for a command.""" @@ -24,6 +21,9 @@ class TimedCommands(commands.Cog): """Time the command execution of a command.""" new_ctx = await self.create_execution_context(ctx, command) + if new_ctx.command and new_ctx.command.qualified_name == "timed": + return await ctx.send("You are not allowed to time the execution of the `timed` command.") + t_start = perf_counter() await new_ctx.command.invoke(new_ctx) t_end = perf_counter() -- cgit v1.2.3 From bf1cd68efaa7f0fe5037c69fbdc7cfffcf284a2d Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Sun, 11 Apr 2021 01:11:21 +0100 Subject: fix message updating and return types --- bot/exts/evergreen/timed.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/timed.py b/bot/exts/evergreen/timed.py index 58da3dc8..57575993 100644 --- a/bot/exts/evergreen/timed.py +++ b/bot/exts/evergreen/timed.py @@ -12,7 +12,7 @@ class TimedCommands(commands.Cog): async def create_execution_context(ctx: commands.Context, command: str) -> commands.Context: """Get a new execution context for a command.""" msg: Message = copy(ctx.message) - msg._update({"content": f"{ctx.prefix}{command}"}) + msg.content = f"{ctx.prefix}{command}" return await ctx.bot.get_context(msg) @@ -22,7 +22,8 @@ class TimedCommands(commands.Cog): new_ctx = await self.create_execution_context(ctx, command) if new_ctx.command and new_ctx.command.qualified_name == "timed": - return await ctx.send("You are not allowed to time the execution of the `timed` command.") + await ctx.send("You are not allowed to time the execution of the `timed` command.") + return t_start = perf_counter() await new_ctx.command.invoke(new_ctx) -- cgit v1.2.3 From 0ac739dc029664332f1416ba85424c3b396e5660 Mon Sep 17 00:00:00 2001 From: janine9vn Date: Sat, 10 Apr 2021 20:17:47 -0400 Subject: Change names These were missed in a previous commit. It's a simple name change from the original files to better align with Sir Lancebot. --- bot/exts/internal_eval/helpers.py | 243 -------------------------------- bot/exts/internal_eval/internal_eval.py | 152 -------------------- 2 files changed, 395 deletions(-) delete mode 100644 bot/exts/internal_eval/helpers.py delete mode 100644 bot/exts/internal_eval/internal_eval.py diff --git a/bot/exts/internal_eval/helpers.py b/bot/exts/internal_eval/helpers.py deleted file mode 100644 index 5c602e4d..00000000 --- a/bot/exts/internal_eval/helpers.py +++ /dev/null @@ -1,243 +0,0 @@ -import ast -import collections -import contextlib -import functools -import inspect -import io -import logging -import sys -import traceback -import types -import typing - - -log = logging.getLogger("rattlesnake.exts.admin_tools.internal_eval") - -# A type alias to annotate the tuples returned from `sys.exc_info()` -ExcInfo = typing.Tuple[typing.Type[Exception], Exception, types.TracebackType] -Namespace = typing.Dict[str, typing.Any] - -# This will be used as an coroutine function wrapper for the code -# to be evaluated. The wrapper contains one `pass` statement which -# will be replaced with `ast` with the code that we want to have -# evaluated. -# The function redirects output and captures exceptions that were -# raised in the code we evaluate. The latter is used to provide a -# meaningful traceback to the end user. -EVAL_WRAPPER = """ -async def _eval_wrapper_function(): - try: - with contextlib.redirect_stdout(_eval_context.stdout): - pass - if '_value_last_expression' in locals(): - if inspect.isawaitable(_value_last_expression): - _value_last_expression = await _value_last_expression - _eval_context._value_last_expression = _value_last_expression - else: - _eval_context._value_last_expression = None - except Exception: - _eval_context.exc_info = sys.exc_info() - finally: - _eval_context.locals = locals() -_eval_context.function = _eval_wrapper_function -""" - - -def format_internal_eval_exception(exc_info: ExcInfo, code: str) -> str: - """Format an exception caught while evaluation code by inserting lines.""" - exc_type, exc_value, tb = exc_info - stack_summary = traceback.StackSummary.extract(traceback.walk_tb(tb)) - code = code.split("\n") - - output = ["Traceback (most recent call last):"] - for frame in stack_summary: - if frame.filename == "": - line = code[frame.lineno - 1].lstrip() - - if frame.name == "_eval_wrapper_function": - name = "" - else: - name = frame.name - else: - line = frame.line - name = frame.name - - output.append( - f' File "{frame.filename}", line {frame.lineno}, in {name}\n' - f" {line}" - ) - - output.extend(traceback.format_exception_only(exc_type, exc_value)) - return "\n".join(output) - - -class EvalContext: - """ - Represents the current `internal eval` context. - The context remembers names set during earlier runs of `internal eval`. To - clear the context, use the `?internal clear` command. - """ - - def __init__(self, context_vars: Namespace, local_vars: Namespace) -> None: - self._locals = dict(local_vars) - self.context_vars = dict(context_vars) - - self.stdout = io.StringIO() - self._value_last_expression = None - self.exc_info = None - self.code = "" - self.function = None - self.eval_tree = None - - @property - def dependencies(self) -> typing.Dict[str, typing.Any]: - """ - Return a mapping of the dependencies for the wrapper function. - By using a property descriptor, the mapping can't be accidentally - mutated during evaluation. This ensures the dependencies are always - available. - """ - return { - "print": functools.partial(print, file=self.stdout), - "contextlib": contextlib, - "inspect": inspect, - "sys": sys, - "_eval_context": self, - "_": self._value_last_expression, - } - - @property - def locals(self) -> typing.Dict[str, typing.Any]: - """Return a mapping of names->values needed for evaluation.""" - return {**collections.ChainMap(self.dependencies, self.context_vars, self._locals)} - - @locals.setter - def locals(self, locals_: typing.Dict[str, typing.Any]) -> None: - """Update the contextual mapping of names to values.""" - log.trace(f"Updating {self._locals} with {locals_}") - self._locals.update(locals_) - - def prepare_eval(self, code: str) -> typing.Optional[str]: - """Prepare an evaluation by processing the code and setting up the context.""" - self.code = code - - if not self.code: - log.debug("No code was attached to the evaluation command") - return "[No code detected]" - - try: - code_tree = ast.parse(code, filename="") - except SyntaxError: - log.debug("Got a SyntaxError while parsing the eval code") - return "".join(traceback.format_exception(*sys.exc_info(), limit=0)) - - log.trace("Parsing the AST to see if there's a trailing expression we need to capture") - code_tree = CaptureLastExpression(code_tree).capture() - - log.trace("Wrapping the AST in the AST of the wrapper coroutine") - eval_tree = WrapEvalCodeTree(code_tree).wrap() - - self.eval_tree = eval_tree - return None - - async def run_eval(self) -> Namespace: - """Run the evaluation and return the updated locals.""" - log.trace("Compiling the AST to bytecode using `exec` mode") - compiled_code = compile(self.eval_tree, filename="", mode="exec") - - log.trace("Executing the compiled code with the desired namespace environment") - exec(compiled_code, self.locals) # noqa: B102,S102 - - log.trace("Awaiting the created evaluation wrapper coroutine.") - await self.function() - - log.trace("Returning the updated captured locals.") - return self._locals - - def format_output(self) -> str: - """Format the output of the most recent evaluation.""" - output = [] - - log.trace(f"Getting output from stdout `{id(self.stdout)}`") - stdout_text = self.stdout.getvalue() - if stdout_text: - log.trace("Appending output captured from stdout/print") - output.append(stdout_text) - - if self._value_last_expression is not None: - log.trace("Appending the output of a captured trialing expression") - output.append(f"[Captured] {self._value_last_expression!r}") - - if self.exc_info: - log.trace("Appending exception information") - output.append(format_internal_eval_exception(self.exc_info, self.code)) - - log.trace(f"Generated output: {output!r}") - return "\n".join(output) or "[No output]" - - -class WrapEvalCodeTree(ast.NodeTransformer): - """Wraps the AST of eval code with the wrapper function.""" - - def __init__(self, eval_code_tree: ast.AST, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - self.eval_code_tree = eval_code_tree - - # To avoid mutable aliasing, parse the WRAPPER_FUNC for each wrapping - self.wrapper = ast.parse(EVAL_WRAPPER, filename="") - - def wrap(self) -> ast.AST: - """Wrap the tree of the code by the tree of the wrapper function.""" - new_tree = self.visit(self.wrapper) - return ast.fix_missing_locations(new_tree) - - def visit_Pass(self, node: ast.Pass) -> typing.List[ast.AST]: # noqa: N802 - """ - Replace the `_ast.Pass` node in the wrapper function by the eval AST. - This method works on the assumption that there's a single `pass` - statement in the wrapper function. - """ - return list(ast.iter_child_nodes(self.eval_code_tree)) - - -class CaptureLastExpression(ast.NodeTransformer): - """Captures the return value from a loose expression.""" - - def __init__(self, tree: ast.AST, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - self.tree = tree - self.last_node = list(ast.iter_child_nodes(tree))[-1] - - def visit_Expr(self, node: ast.Expr) -> typing.Union[ast.Expr, ast.Assign]: # noqa: N802 - """ - Replace the Expr node that is last child node of Module with an assignment. - We use an assignment to capture the value of the last node, if it's a loose - Expr node. Normally, the value of an Expr node is lost, meaning we don't get - the output of such a last "loose" expression. By assigning it a name, we can - retrieve it for our output. - """ - if node is not self.last_node: - return node - - log.trace("Found a trailing last expression in the evaluation code") - - log.trace("Creating assignment statement with trailing expression as the right-hand side") - right_hand_side = list(ast.iter_child_nodes(node))[0] - - assignment = ast.Assign( - targets=[ast.Name(id='_value_last_expression', ctx=ast.Store())], - value=right_hand_side, - lineno=node.lineno, - col_offset=0, - ) - ast.fix_missing_locations(assignment) - return assignment - - def capture(self) -> ast.AST: - """Capture the value of the last expression with an assignment.""" - if not isinstance(self.last_node, ast.Expr): - # We only have to replace a node if the very last node is an Expr node - return self.tree - - new_tree = self.visit(self.tree) - return ast.fix_missing_locations(new_tree) diff --git a/bot/exts/internal_eval/internal_eval.py b/bot/exts/internal_eval/internal_eval.py deleted file mode 100644 index f6812942..00000000 --- a/bot/exts/internal_eval/internal_eval.py +++ /dev/null @@ -1,152 +0,0 @@ -import logging -import re -import textwrap -import typing - -import discord -from discord.ext import commands - -from rattlesnake.bot import Rattlesnake -from rattlesnake.constants import ADMIN_ROLES -from rattlesnake.utils import in_whitelist -from .helpers import EvalContext - -__all__ = ["InternalEval"] - -log = logging.getLogger("rattlesnake.exts.admin_tools.internal_eval") - -CODEBLOCK_REGEX = re.compile(r"(^```(py(thon)?)?\n)|(```$)") - - -class InternalEval(commands.Cog): - """Top secret code evaluation for admins and owners.""" - - def __init__(self, bot: Rattlesnake): - self.bot = bot - self.locals = {} - - @staticmethod - def shorten_output( - output: str, - max_length: int = 1900, - placeholder: str = "\n[output truncated]" - ) -> str: - """ - Shorten the `output` so it's shorter than `max_length`. - There are three tactics for this, tried in the following order: - - Shorten the output on a line-by-line basis - - Shorten the output on any whitespace character - - Shorten the output solely on character count - """ - max_length = max_length - len(placeholder) - - shortened_output = [] - char_count = 0 - for line in output.split("\n"): - if char_count + len(line) > max_length: - break - shortened_output.append(line) - char_count += len(line) + 1 # account for (possible) line ending - - if shortened_output: - shortened_output.append(placeholder) - return "\n".join(shortened_output) - - shortened_output = textwrap.shorten(output, width=max_length, placeholder=placeholder) - - if shortened_output.strip() == placeholder.strip(): - # `textwrap` was unable to find whitespace to shorten on, so it has - # reduced the output to just the placeholder. Let's shorten based on - # characters instead. - shortened_output = output[:max_length] + placeholder - - return shortened_output - - async def _upload_output(self, output: str) -> typing.Optional[str]: - """Upload `internal eval` output to our pastebin and return the url.""" - try: - async with self.bot.http_session.post( - "https://paste.pythondiscord.com/documents", data=output, raise_for_status=True - ) as resp: - data = await resp.json() - - if "key" in data: - return f"https://paste.pythondiscord.com/{data['key']}" - except Exception: - # 400 (Bad Request) means there are too many characters - log.exception("Failed to upload `internal eval` output to paste service!") - - async def _send_output(self, ctx: commands.Context, output: str) -> None: - """Send the `internal eval` output to the command invocation context.""" - upload_message = "" - if len(output) >= 1980: - # The output is too long, let's truncate it for in-channel output and - # upload the complete output to the paste service. - url = await self._upload_output(output) - - if url: - upload_message = f"\nFull output here: {url}" - else: - upload_message = "\n:warning: Failed to upload full output!" - - output = self.shorten_output(output) - - await ctx.send(f"```py\n{output}```{upload_message}") - - async def _eval(self, ctx: commands.Context, code: str) -> None: - """Evaluate the `code` in the current evaluation context.""" - if code.startswith("exit"): - self.locals = {} - await ctx.send("The evaluation context was reset.") - return - - context_vars = { - "message": ctx.message, - "author": ctx.message.author, - "channel": ctx.channel, - "guild": ctx.guild, - "ctx": ctx, - "self": self, - "bot": self.bot, - "discord": discord, - } - - eval_context = EvalContext(context_vars, self.locals) - - log.trace("Preparing the evaluation by parsing the AST of the code") - error = eval_context.prepare_eval(code) - - if error: - log.trace("The code can't be evaluated due to an error") - await ctx.send(f"```py\n{error}\n```") - return - - log.trace("Evaluate the AST we've generated for the evaluation") - new_locals = await eval_context.run_eval() - - log.trace("Updating locals with those set during evaluation") - self.locals.update(new_locals) - - log.trace("Sending the formatted output back to the context") - await self._send_output(ctx, eval_context.format_output()) - - @commands.group(name='internal', aliases=('int',)) - @in_whitelist(roles=ADMIN_ROLES) - async def internal_group(self, ctx: commands.Context) -> None: - """Internal commands. Top secret!""" - if not ctx.invoked_subcommand: - await ctx.send_help(ctx.command) - - @internal_group.command(name='eval', aliases=('e',)) - @in_whitelist(roles=ADMIN_ROLES) - async def eval(self, ctx: commands.Context, *, code: str) -> None: - """Run eval in a REPL-like format.""" - code = CODEBLOCK_REGEX.sub("", code.strip()) - await self._eval(ctx, code) - - @internal_group.command(name='reset', aliases=("clear", "exit", "r", "c")) - @in_whitelist(roles=ADMIN_ROLES) - async def reset(self, ctx: commands.Context) -> None: - """Run eval in a REPL-like format.""" - self.locals = {} - await ctx.send("The evaluation context was reset.") -- cgit v1.2.3 From 13be6262e6048790062be9dd7daf178fb8b8d0e5 Mon Sep 17 00:00:00 2001 From: janine9vn Date: Sat, 10 Apr 2021 20:23:45 -0400 Subject: Update codeblock regex The snekbox implementation of the codeblock regex was incorporated. This now correctly parses the `code` and ``code`` markdown discord allows. You can also use multiple code blocks with text interrupting it and it will process the different code blocks as one continuous code block. --- bot/exts/internal_eval/_internal_eval.py | 37 +++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index f7a0946b..45bfbdc3 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -17,6 +17,23 @@ log = logging.getLogger("rattlesnake.exts.admin_tools.internal_eval") CODEBLOCK_REGEX = re.compile(r"(^```(py(thon)?)?\n)|(```$)") +FORMATTED_CODE_REGEX = re.compile( + r"(?P(?P```)|``?)" # code delimiter: 1-3 backticks; (?P=block) only matches if it's a block + r"(?(block)(?:(?P[a-z]+)\n)?)" # if we're in a block, match optional language (only letters plus newline) + r"(?:[ \t]*\n)*" # any blank (empty or tabs/spaces only) lines before the code + r"(?P.*?)" # extract all code inside the markup + r"\s*" # any more whitespace before the end of the code markup + r"(?P=delim)", # match the exact same delimiter from the start again + re.DOTALL | re.IGNORECASE # "." also matches newlines, case insensitive +) + +RAW_CODE_REGEX = re.compile( + r"^(?:[ \t]*\n)*" # any blank (empty or tabs/spaces only) lines before the code + r"(?P.*?)" # extract all the rest as code + r"\s*$", # any trailing whitespace until the end of the string + re.DOTALL # "." also matches newlines +) + class InternalEval(commands.Cog): """Top secret code evaluation for admins and owners.""" @@ -141,7 +158,25 @@ class InternalEval(commands.Cog): @with_role(Roles.admin) async def eval(self, ctx: commands.Context, *, code: str) -> None: """Run eval in a REPL-like format.""" - code = CODEBLOCK_REGEX.sub("", code.strip()) + + if match := list(FORMATTED_CODE_REGEX.finditer(code)): + blocks = [block for block in match if block.group("block")] + + if len(blocks) > 1: + code = '\n'.join(block.group("code") for block in blocks) + info = "several code blocks" + else: + match = match[0] if len(blocks) == 0 else blocks[0] + code, block, lang, delim = match.group("code", "block", "lang", "delim") + if block: + info = (f"'{lang}' highlighted" if lang else "plain") + " code block" + else: + info = f"{delim}-enclosed inline code" + else: + code = RAW_CODE_REGEX.fullmatch(code).group("code") + info = "unformatted or badly formatted code" + + code = textwrap.dedent(code) await self._eval(ctx, code) @internal_group.command(name='reset', aliases=("clear", "exit", "r", "c")) -- cgit v1.2.3 From e946b50c42412a08c823a89e087bd8c67c20a85b Mon Sep 17 00:00:00 2001 From: janine9vn Date: Sat, 10 Apr 2021 20:49:41 -0400 Subject: Removed rogue variable I'm better than this I swear. I can lint before I commit. Don't tell lemon. --- bot/exts/internal_eval/_internal_eval.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index 45bfbdc3..6f29a661 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -164,17 +164,12 @@ class InternalEval(commands.Cog): if len(blocks) > 1: code = '\n'.join(block.group("code") for block in blocks) - info = "several code blocks" else: match = match[0] if len(blocks) == 0 else blocks[0] code, block, lang, delim = match.group("code", "block", "lang", "delim") - if block: - info = (f"'{lang}' highlighted" if lang else "plain") + " code block" - else: - info = f"{delim}-enclosed inline code" + else: code = RAW_CODE_REGEX.fullmatch(code).group("code") - info = "unformatted or badly formatted code" code = textwrap.dedent(code) await self._eval(ctx, code) -- cgit v1.2.3 From 341908df150b6129055c970c7d2a8d36d76a4bfe Mon Sep 17 00:00:00 2001 From: janine9vn Date: Sat, 10 Apr 2021 21:07:48 -0400 Subject: Blind Fixes at Linting Both my pre-commit and flake8 runs are telling me everything is fine and it's all passed. Github actions is saying otherwise but isn't saying *where*. So here I am with useless linting commits. --- bot/exts/internal_eval/_helpers.py | 4 ++++ bot/exts/internal_eval/_internal_eval.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/bot/exts/internal_eval/_helpers.py b/bot/exts/internal_eval/_helpers.py index 5c602e4d..a8ae5bef 100644 --- a/bot/exts/internal_eval/_helpers.py +++ b/bot/exts/internal_eval/_helpers.py @@ -74,6 +74,7 @@ def format_internal_eval_exception(exc_info: ExcInfo, code: str) -> str: class EvalContext: """ Represents the current `internal eval` context. + The context remembers names set during earlier runs of `internal eval`. To clear the context, use the `?internal clear` command. """ @@ -93,6 +94,7 @@ class EvalContext: def dependencies(self) -> typing.Dict[str, typing.Any]: """ Return a mapping of the dependencies for the wrapper function. + By using a property descriptor, the mapping can't be accidentally mutated during evaluation. This ensures the dependencies are always available. @@ -194,6 +196,7 @@ class WrapEvalCodeTree(ast.NodeTransformer): def visit_Pass(self, node: ast.Pass) -> typing.List[ast.AST]: # noqa: N802 """ Replace the `_ast.Pass` node in the wrapper function by the eval AST. + This method works on the assumption that there's a single `pass` statement in the wrapper function. """ @@ -211,6 +214,7 @@ class CaptureLastExpression(ast.NodeTransformer): def visit_Expr(self, node: ast.Expr) -> typing.Union[ast.Expr, ast.Assign]: # noqa: N802 """ Replace the Expr node that is last child node of Module with an assignment. + We use an assignment to capture the value of the last node, if it's a loose Expr node. Normally, the value of an Expr node is lost, meaning we don't get the output of such a last "loose" expression. By assigning it a name, we can diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index 6f29a661..ee438724 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -50,6 +50,7 @@ class InternalEval(commands.Cog): ) -> str: """ Shorten the `output` so it's shorter than `max_length`. + There are three tactics for this, tried in the following order: - Shorten the output on a line-by-line basis - Shorten the output on any whitespace character @@ -158,7 +159,6 @@ class InternalEval(commands.Cog): @with_role(Roles.admin) async def eval(self, ctx: commands.Context, *, code: str) -> None: """Run eval in a REPL-like format.""" - if match := list(FORMATTED_CODE_REGEX.finditer(code)): blocks = [block for block in match if block.group("block")] -- cgit v1.2.3 From b0892e5afbbb13167c7c295c8f8241a2146eddf7 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Sun, 11 Apr 2021 04:03:59 +0100 Subject: return and send message if command isnt found while timing --- bot/exts/evergreen/timed.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/timed.py b/bot/exts/evergreen/timed.py index 57575993..a0f29838 100644 --- a/bot/exts/evergreen/timed.py +++ b/bot/exts/evergreen/timed.py @@ -21,7 +21,11 @@ class TimedCommands(commands.Cog): """Time the command execution of a command.""" new_ctx = await self.create_execution_context(ctx, command) - if new_ctx.command and new_ctx.command.qualified_name == "timed": + if not new_ctx.command: + await ctx.send("The command you are trying to time doesn't exist. Use `.help` for a list of commands.") + return + + if new_ctx.command.qualified_name == "timed": await ctx.send("You are not allowed to time the execution of the `timed` command.") return -- cgit v1.2.3 From 7c7c6dd2318815bc14e1cb20d5dd5fbd416712a0 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Sun, 11 Apr 2021 04:19:58 +0100 Subject: fix: use ctx.prefix when suggesting the help command --- bot/exts/evergreen/timed.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/timed.py b/bot/exts/evergreen/timed.py index a0f29838..604204b0 100644 --- a/bot/exts/evergreen/timed.py +++ b/bot/exts/evergreen/timed.py @@ -22,7 +22,10 @@ class TimedCommands(commands.Cog): new_ctx = await self.create_execution_context(ctx, command) if not new_ctx.command: - await ctx.send("The command you are trying to time doesn't exist. Use `.help` for a list of commands.") + help_command = f"{ctx.prefix}help" + error = f"The command you are trying to time doesn't exist. Use `{help_command}` for a list of commands." + + await ctx.send(error) return if new_ctx.command.qualified_name == "timed": -- cgit v1.2.3 From e51dfab90242822aeab6bd42e69296c133efe042 Mon Sep 17 00:00:00 2001 From: Shakya Majumdar Date: Sun, 11 Apr 2021 10:22:15 +0530 Subject: add caching --- .gitignore | 2 +- bot/exts/evergreen/latex.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d3d2bb8d..ce122d29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # bot (project-specific) log/* data/* - +_latex_cache/* diff --git a/bot/exts/evergreen/latex.py b/bot/exts/evergreen/latex.py index d140e79d..0c29f958 100644 --- a/bot/exts/evergreen/latex.py +++ b/bot/exts/evergreen/latex.py @@ -1,4 +1,6 @@ import asyncio +import hashlib +import pathlib import re from concurrent.futures import ThreadPoolExecutor from io import BytesIO @@ -28,6 +30,10 @@ FORMATTED_CODE_REGEX = re.compile( re.DOTALL | re.IGNORECASE, # "." also matches newlines, case insensitive ) +CACHE_DIRECTORY = pathlib.Path("_latex_cache") +if not CACHE_DIRECTORY.exists(): + CACHE_DIRECTORY.mkdir() + class Latex(commands.Cog): """Renders latex.""" @@ -60,7 +66,12 @@ class Latex(commands.Cog): async def latex(self, ctx: commands.Context, *, text: str) -> None: """Renders the text in latex and sends the image.""" text = self._prepare_input(text) + query_hash = hashlib.md5(text.encode()).hexdigest() + image_path = CACHE_DIRECTORY.joinpath(f"{query_hash}.png") async with ctx.typing(): + if image_path.exists(): + await ctx.send(file=discord.File(image_path)) + return with ThreadPoolExecutor() as pool: image = await asyncio.get_running_loop().run_in_executor( @@ -69,6 +80,9 @@ class Latex(commands.Cog): await ctx.send(file=discord.File(image, "latex.png")) + with open(image_path, "wb") as f: + f.write(image.getbuffer()) + def setup(bot: commands.Bot) -> None: """Load the Latex Cog.""" -- cgit v1.2.3 From 51d9b5413ceb8319e0ce9847ee048f112b21fdff Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sun, 11 Apr 2021 16:48:25 +0200 Subject: Issues: add `type=public` This will make sure that even if a privileged token is passed we won't leak any private information. --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index fa07b674..42ab97c3 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -30,7 +30,7 @@ REQUEST_HEADERS = { "Accept": "application/vnd.github.v3+json" } -REPOSITORY_ENDPOINT = "https://api.github.com/orgs/{org}/repos?per_page=100" +REPOSITORY_ENDPOINT = "https://api.github.com/orgs/{org}/repos?per_page=100&type=public" ISSUE_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/issues/{number}" PR_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/pulls/{number}" -- cgit v1.2.3 From 87e459836b8d3b0d624ec97fe293f994ba9c8c22 Mon Sep 17 00:00:00 2001 From: Janine vN Date: Sun, 11 Apr 2021 11:18:28 -0400 Subject: Correct prefix usage in a doctstring Corrects the prefix for the a command in the docstring to use Lancebot's prefix. Co-authored-by: Matteo Bertucci --- bot/exts/internal_eval/_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/internal_eval/_helpers.py b/bot/exts/internal_eval/_helpers.py index a8ae5bef..8b991d98 100644 --- a/bot/exts/internal_eval/_helpers.py +++ b/bot/exts/internal_eval/_helpers.py @@ -76,7 +76,7 @@ class EvalContext: Represents the current `internal eval` context. The context remembers names set during earlier runs of `internal eval`. To - clear the context, use the `?internal clear` command. + clear the context, use the `.internal clear` command. """ def __init__(self, context_vars: Namespace, local_vars: Namespace) -> None: -- cgit v1.2.3 From 17cf2b391df6d02083386a450c0f4569ba421762 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 11 Apr 2021 16:21:44 +0100 Subject: Add env var for trashcan eomji override --- bot/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/constants.py b/bot/constants.py index 416dd0e7..1d35b3f1 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -153,7 +153,7 @@ class Emojis: christmas_tree = "\U0001F384" check = "\u2611" envelope = "\U0001F4E8" - trashcan = "<:trashcan:637136429717389331>" + trashcan = environ.get("TRASHCAN_EMOJI", "<:trashcan:637136429717389331>") ok_hand = ":ok_hand:" hand_raised = "\U0001f64b" -- cgit v1.2.3 From 321ff54da6c2347294053fb2357ad1b883db7eaf Mon Sep 17 00:00:00 2001 From: Shakya Majumdar Date: Sun, 11 Apr 2021 21:03:48 +0530 Subject: add max_concurrency, move file-saving to _render --- bot/exts/evergreen/latex.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/latex.py b/bot/exts/evergreen/latex.py index 0c29f958..442b6f7b 100644 --- a/bot/exts/evergreen/latex.py +++ b/bot/exts/evergreen/latex.py @@ -31,16 +31,16 @@ FORMATTED_CODE_REGEX = re.compile( ) CACHE_DIRECTORY = pathlib.Path("_latex_cache") -if not CACHE_DIRECTORY.exists(): - CACHE_DIRECTORY.mkdir() +CACHE_DIRECTORY.mkdir(exist_ok=True) class Latex(commands.Cog): """Renders latex.""" @staticmethod - def _render(text: str) -> BytesIO: - """Return the rendered image if latex compiles without errors, otherwise raise a BadArgument Exception.""" + def _render(text: str, filepath: pathlib.Path) -> BytesIO: + """Return the rendered image if latex compiles without errors, otherwise raise a BadArgument Exception. + Saves rendered image to cache.""" fig = plt.figure() rendered_image = BytesIO() fig.text(0, 1, text, horizontalalignment="left", verticalalignment="top") @@ -51,6 +51,10 @@ class Latex(commands.Cog): raise commands.BadArgument(str(e)) rendered_image.seek(0) + + with open(filepath, "wb") as f: + f.write(rendered_image.getbuffer()) + return rendered_image @staticmethod @@ -63,6 +67,7 @@ class Latex(commands.Cog): return text @commands.command() + @commands.max_concurrency(1, commands.BucketType.guild, wait=True) async def latex(self, ctx: commands.Context, *, text: str) -> None: """Renders the text in latex and sends the image.""" text = self._prepare_input(text) @@ -75,14 +80,11 @@ class Latex(commands.Cog): with ThreadPoolExecutor() as pool: image = await asyncio.get_running_loop().run_in_executor( - pool, self._render, text + pool, self._render, text, image_path ) await ctx.send(file=discord.File(image, "latex.png")) - with open(image_path, "wb") as f: - f.write(image.getbuffer()) - def setup(bot: commands.Bot) -> None: """Load the Latex Cog.""" -- cgit v1.2.3 From 7f6fa93dc43a1885c3ea0dfd695dec639822647c Mon Sep 17 00:00:00 2001 From: Shakya Majumdar Date: Sun, 11 Apr 2021 21:03:48 +0530 Subject: add max_concurrency, move file-saving to _render --- bot/exts/evergreen/latex.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/latex.py b/bot/exts/evergreen/latex.py index 0c29f958..c4a8597c 100644 --- a/bot/exts/evergreen/latex.py +++ b/bot/exts/evergreen/latex.py @@ -31,16 +31,19 @@ FORMATTED_CODE_REGEX = re.compile( ) CACHE_DIRECTORY = pathlib.Path("_latex_cache") -if not CACHE_DIRECTORY.exists(): - CACHE_DIRECTORY.mkdir() +CACHE_DIRECTORY.mkdir(exist_ok=True) class Latex(commands.Cog): """Renders latex.""" @staticmethod - def _render(text: str) -> BytesIO: - """Return the rendered image if latex compiles without errors, otherwise raise a BadArgument Exception.""" + def _render(text: str, filepath: pathlib.Path) -> BytesIO: + """ + Return the rendered image if latex compiles without errors, otherwise raise a BadArgument Exception. + + Saves rendered image to cache. + """ fig = plt.figure() rendered_image = BytesIO() fig.text(0, 1, text, horizontalalignment="left", verticalalignment="top") @@ -51,6 +54,10 @@ class Latex(commands.Cog): raise commands.BadArgument(str(e)) rendered_image.seek(0) + + with open(filepath, "wb") as f: + f.write(rendered_image.getbuffer()) + return rendered_image @staticmethod @@ -63,6 +70,7 @@ class Latex(commands.Cog): return text @commands.command() + @commands.max_concurrency(1, commands.BucketType.guild, wait=True) async def latex(self, ctx: commands.Context, *, text: str) -> None: """Renders the text in latex and sends the image.""" text = self._prepare_input(text) @@ -75,14 +83,11 @@ class Latex(commands.Cog): with ThreadPoolExecutor() as pool: image = await asyncio.get_running_loop().run_in_executor( - pool, self._render, text + pool, self._render, text, image_path ) await ctx.send(file=discord.File(image, "latex.png")) - with open(image_path, "wb") as f: - f.write(image.getbuffer()) - def setup(bot: commands.Bot) -> None: """Load the Latex Cog.""" -- cgit v1.2.3 From 2cc2a2e618ed019de00054c768613e3e6ba2470c Mon Sep 17 00:00:00 2001 From: Janine vN Date: Sun, 11 Apr 2021 11:36:56 -0400 Subject: Correct logger name Changed the initialization of the logging to pull dynamically so it can actually log correctly. --- bot/exts/internal_eval/_helpers.py | 2 +- bot/exts/internal_eval/_internal_eval.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/internal_eval/_helpers.py b/bot/exts/internal_eval/_helpers.py index 8b991d98..bd36520d 100644 --- a/bot/exts/internal_eval/_helpers.py +++ b/bot/exts/internal_eval/_helpers.py @@ -11,7 +11,7 @@ import types import typing -log = logging.getLogger("rattlesnake.exts.admin_tools.internal_eval") +log = logging.getLogger(__name__) # A type alias to annotate the tuples returned from `sys.exc_info()` ExcInfo = typing.Tuple[typing.Type[Exception], Exception, types.TracebackType] diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index ee438724..198c1312 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -13,7 +13,7 @@ from ._helpers import EvalContext __all__ = ["InternalEval"] -log = logging.getLogger("rattlesnake.exts.admin_tools.internal_eval") +log = logging.getLogger(__name__) CODEBLOCK_REGEX = re.compile(r"(^```(py(thon)?)?\n)|(```$)") -- cgit v1.2.3 From fa67eebb08ec9d71d94cdeaf757cc84f33053691 Mon Sep 17 00:00:00 2001 From: Janine vN Date: Sun, 11 Apr 2021 13:58:39 -0400 Subject: Remove unused codeblock regex With the regex sufficiently stolen from snekbox and confirmed to work, the original codeblock regex has been removed. --- bot/exts/internal_eval/_internal_eval.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index 198c1312..4746c6c9 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -15,8 +15,6 @@ __all__ = ["InternalEval"] log = logging.getLogger(__name__) -CODEBLOCK_REGEX = re.compile(r"(^```(py(thon)?)?\n)|(```$)") - FORMATTED_CODE_REGEX = re.compile( r"(?P(?P```)|``?)" # code delimiter: 1-3 backticks; (?P=block) only matches if it's a block r"(?(block)(?:(?P[a-z]+)\n)?)" # if we're in a block, match optional language (only letters plus newline) -- cgit v1.2.3 From b8e8be4dff862997b8ba30ce48092c1c529d34c3 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Sun, 11 Apr 2021 19:02:01 +0100 Subject: fix: put april fools video links in correct channel names --- bot/resources/easter/april_fools_vids.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bot/resources/easter/april_fools_vids.json b/bot/resources/easter/april_fools_vids.json index b2cbd07b..49a28bfa 100644 --- a/bot/resources/easter/april_fools_vids.json +++ b/bot/resources/easter/april_fools_vids.json @@ -115,11 +115,15 @@ { "title": "Introducing Gmail Motion", "link": "https://youtu.be/Bu927_ul_X0" - }, + } + ], + "nvidia": [ { "title": "Introducing GeForce GTX G-Assist", "link": "https://youtu.be/smM-Wdk2RLQ" - }, + } + ], + "razer": [ { "title": "The Hovering Mouse - Project McFly | Razer", "link": "https://youtu.be/IlCx5gjAmqI" @@ -129,5 +133,4 @@ "link": "https://youtu.be/j8UJE7DoyJ8" } ] - } -- cgit v1.2.3 From e4829ffe61d8eda83ff89e7b86de35fe930bb793 Mon Sep 17 00:00:00 2001 From: Janine vN Date: Sun, 11 Apr 2021 14:06:39 -0400 Subject: Update docstring for reset Updated the docstring for `reset` to provide accurate information as to what the command does. --- bot/exts/internal_eval/_internal_eval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index 4746c6c9..8e474b7d 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -175,6 +175,6 @@ class InternalEval(commands.Cog): @internal_group.command(name='reset', aliases=("clear", "exit", "r", "c")) @with_role(Roles.admin) async def reset(self, ctx: commands.Context) -> None: - """Run eval in a REPL-like format.""" + """Reset the context and locals of the eval session.""" self.locals = {} await ctx.send("The evaluation context was reset.") -- cgit v1.2.3 From 652e306064efb451660e7c08dc88aeafdcf7360e Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Sun, 11 Apr 2021 19:12:10 +0100 Subject: chore: add time as alias of timed Co-authored-by: Matteo Bertucci --- bot/exts/evergreen/timed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/timed.py b/bot/exts/evergreen/timed.py index 604204b0..635ccb32 100644 --- a/bot/exts/evergreen/timed.py +++ b/bot/exts/evergreen/timed.py @@ -16,7 +16,7 @@ class TimedCommands(commands.Cog): return await ctx.bot.get_context(msg) - @commands.command(name="timed", aliases=["t"]) + @commands.command(name="timed", aliases=["time", "t"]) async def timed(self, ctx: commands.Context, *, command: str) -> None: """Time the command execution of a command.""" new_ctx = await self.create_execution_context(ctx, command) -- cgit v1.2.3 From af97f53e2db9494dcf934d8b646b7d65c6713924 Mon Sep 17 00:00:00 2001 From: Janine vN Date: Sun, 11 Apr 2021 14:12:48 -0400 Subject: Change command help format `.int` with nothing else now uses the `invoke_help_command()` utility that formats the help command much more nicely than the default version --- bot/exts/internal_eval/_internal_eval.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index 8e474b7d..06626b69 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -9,6 +9,7 @@ from discord.ext import commands from bot.bot import Bot from bot.constants import Roles from bot.utils.decorators import with_role +from bot.utils.extensions import invoke_help_command from ._helpers import EvalContext __all__ = ["InternalEval"] @@ -151,7 +152,7 @@ class InternalEval(commands.Cog): async def internal_group(self, ctx: commands.Context) -> None: """Internal commands. Top secret!""" if not ctx.invoked_subcommand: - await ctx.send_help(ctx.command) + await invoke_help_command(ctx) @internal_group.command(name='eval', aliases=('e',)) @with_role(Roles.admin) -- cgit v1.2.3 From 652f428347d2108d6c70df28c8c8130545ab9029 Mon Sep 17 00:00:00 2001 From: Janine vN Date: Sun, 11 Apr 2021 14:16:43 -0400 Subject: Ensure output will be robust for discord markdown Added in an extra `\n` at the end of the output. Sometimes discord won't properly format the codeblock in the triple ` is not on a newline. This changes ensures that it should. --- bot/exts/internal_eval/_internal_eval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index 06626b69..a62a7899 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -108,7 +108,7 @@ class InternalEval(commands.Cog): output = self.shorten_output(output) - await ctx.send(f"```py\n{output}```{upload_message}") + await ctx.send(f"```py\n{output}\n```{upload_message}") async def _eval(self, ctx: commands.Context, code: str) -> None: """Evaluate the `code` in the current evaluation context.""" -- cgit v1.2.3 From e187a3c67bcc93e8a688bac1a49541914f8b4871 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Mon, 12 Apr 2021 17:00:55 +0200 Subject: Allow automatic linking of issues outside of the python-discord organisation Actions python-discord/organisation#345 --- bot/exts/evergreen/issues.py | 75 ++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 41 deletions(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 42ab97c3..6797559c 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -5,7 +5,7 @@ import typing as t from dataclasses import dataclass import discord -from discord.ext import commands, tasks +from discord.ext import commands from bot.constants import ( Categories, @@ -50,6 +50,21 @@ CODE_BLOCK_RE = re.compile( # Maximum number of issues in one message MAXIMUM_ISSUES = 5 +# Regex used when looking for automatic linking in messages +AUTOMATIC_REGEX = re.compile(r"((?P.+?)\/)?(?P.+?)#(?P.+?)") + + +@dataclass +class FoundIssue: + """Dataclass representing an issue found by the regex.""" + + organisation: t.Optional[str] + repository: str + number: str + + def __hash__(self) -> int: + return hash(self.organisation) + hash(self.repository) + hash(self.number) + @dataclass class FetchError: @@ -76,38 +91,11 @@ class Issues(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot self.repos = [] - self.repo_regex = None - self.get_pydis_repos.start() - - @tasks.loop(minutes=30) - async def get_pydis_repos(self) -> None: - """ - Get all python-discord repositories on github. - - This task will update a pipe-separated list of repositories in self.repo_regex. - """ - async with self.bot.http_session.get( - REPOSITORY_ENDPOINT.format(org="python-discord"), - headers=REQUEST_HEADERS - ) as resp: - if resp.status == 200: - data = await resp.json() - for repo in data: - self.repos.append(repo["full_name"].split("/")[1]) - self.repo_regex = "|".join(self.repos) - else: - log.warning(f"Failed to get latest Pydis repositories. Status code {resp.status}") @staticmethod - def check_in_block(message: discord.Message, repo_issue: str) -> bool: - """Check whether the # is in codeblocks.""" - block = re.findall(CODE_BLOCK_RE, message.content) - - if not block: - return False - elif "#".join(repo_issue.split(" ")) in "".join([*block[0]]): - return True - return False + def remove_codeblocks(message: str) -> str: + """Remove any codeblock in a message.""" + return re.sub(CODE_BLOCK_RE, "", message) async def fetch_issues( self, @@ -219,17 +207,19 @@ class Issues(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: - """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - if not self.repo_regex: - log.warning("repo_regex isn't ready, cannot look for issues.") - return + """ + Automatic issue linking. + Listener to retrieve issue(s) from a GitHub repository using automatic linking if matching /#. + """ # Ignore bots if message.author.bot: return - # `issues` will hold a list of two element tuples `(repository, issue_number)` - issues = re.findall(fr"\b({self.repo_regex})#(\d+)\b", message.content) + issues = [ + FoundIssue(*match.group("org", "repo", "number")) + for match in AUTOMATIC_REGEX.finditer(self.remove_codeblocks(message.content)) + ] links = [] if issues: @@ -261,10 +251,13 @@ class Issues(commands.Cog): return for repo_issue in issues: - if not self.check_in_block(message, " ".join(repo_issue)): - result = await self.fetch_issues(repo_issue[1], repo_issue[0], "python-discord") - if isinstance(result, IssueState): - links.append(result) + result = await self.fetch_issues( + int(repo_issue.number), + repo_issue.repository, + repo_issue.organisation or "python-discord" + ) + if isinstance(result, IssueState): + links.append(result) if not links: return -- cgit v1.2.3 From 52dbe86fb8ff7d659e3ea7f7e49f1442e004e34d Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Mon, 12 Apr 2021 17:07:39 +0200 Subject: Issues: change hashing of FoundIssue to use a tuple --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 6797559c..bb6273bb 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -63,7 +63,7 @@ class FoundIssue: number: str def __hash__(self) -> int: - return hash(self.organisation) + hash(self.repository) + hash(self.number) + return hash((self.organisation, self.repository, self.number)) @dataclass -- cgit v1.2.3 From 375c247ad0098db12b40fc2c1ad36e417c95f9e9 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 12 Apr 2021 16:59:54 +0100 Subject: Remove inappropriate riddle --- bot/resources/easter/easter_riddle.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/bot/resources/easter/easter_riddle.json b/bot/resources/easter/easter_riddle.json index e93f6dad..f7eb63d8 100644 --- a/bot/resources/easter/easter_riddle.json +++ b/bot/resources/easter/easter_riddle.json @@ -63,14 +63,6 @@ ], "correct_answer": "A chocolate one" }, - { - "question": "Where does the Easter Bunny get his eggs?", - "riddles": [ - "Not a bush or tree", - "Emoji for a body part" - ], - "correct_answer": "Eggplants" - }, { "question": "Why did the Easter Bunny have to fire the duck?", "riddles": [ -- cgit v1.2.3 From 83245fa2d55e4430d2fff6a9aa6225ac1ee8de08 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Mon, 12 Apr 2021 19:47:41 +0100 Subject: feat: rewrite portion of fool command to allow all videos --- bot/exts/easter/april_fools_vids.py | 10 +- bot/resources/easter/april_fools_vids.json | 266 ++++++++++++++--------------- 2 files changed, 135 insertions(+), 141 deletions(-) diff --git a/bot/exts/easter/april_fools_vids.py b/bot/exts/easter/april_fools_vids.py index efe7e677..97cb407c 100644 --- a/bot/exts/easter/april_fools_vids.py +++ b/bot/exts/easter/april_fools_vids.py @@ -14,7 +14,6 @@ class AprilFoolVideos(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot self.yt_vids = self.load_json() - self.youtubers = ['google'] # will add more in future @staticmethod def load_json() -> dict: @@ -27,10 +26,11 @@ class AprilFoolVideos(commands.Cog): @commands.command(name='fool') async def april_fools(self, ctx: commands.Context) -> None: """Get a random April Fools' video from Youtube.""" - random_youtuber = random.choice(self.youtubers) - category = self.yt_vids[random_youtuber] - random_vid = random.choice(category) - await ctx.send(f"Check out this April Fools' video by {random_youtuber}.\n\n{random_vid['link']}") + video = random.choice(self.yt_vids) + + channel, url = video["channel"], video["url"] + + await ctx.send(f"Check out this April Fools' video by {channel}.\n\n{url}") def setup(bot: commands.Bot) -> None: diff --git a/bot/resources/easter/april_fools_vids.json b/bot/resources/easter/april_fools_vids.json index 49a28bfa..e1e8c70a 100644 --- a/bot/resources/easter/april_fools_vids.json +++ b/bot/resources/easter/april_fools_vids.json @@ -1,136 +1,130 @@ -{ - "google": [ - { - "title": "Introducing Bad Joke Detector", - "link": "https://youtu.be/OYcv406J_J4" - }, - { - "title": "Introducing Google Cloud Hummus API - Find your Hummus!", - "link": "https://youtu.be/0_5X6N6DHyk" - }, - { - "title": "Introducing Google Play for Pets", - "link": "https://youtu.be/UmJ2NBHXTqo" - }, - { - "title": "Haptic Helpers: bringing you to your senses", - "link": "https://youtu.be/3MA6_21nka8" - }, - { - "title": "Introducing Google Wind", - "link": "https://youtu.be/QAwL0O5nXe0" - }, - { - "title": "Experience YouTube in #SnoopaVision", - "link": "https://youtu.be/DPEJB-FCItk" - }, - { - "title": "Introducing the self-driving bicycle in the Netherlands", - "link": "https://youtu.be/LSZPNwZex9s" - }, - { - "title": "Android Developer Story: The Guardian goes galactic with Android and Google Play", - "link": "https://youtu.be/dFrgNiweQDk" - }, - { - "title": "Introducing new delivery technology from Google Express", - "link": "https://youtu.be/F0F6SnbqUcE" - }, - { - "title": "Google Cardboard Plastic", - "link": "https://youtu.be/VkOuShXpoKc" - }, - { - "title": "Google Photos: Search your photos by emoji", - "link": "https://youtu.be/HQtGFBbwKEk" - }, - { - "title": "Introducing Google Actual Cloud Platform", - "link": "https://youtu.be/Cp10_PygJ4o" - }, - { - "title": "Introducing Dial-Up mode", - "link": "https://youtu.be/XTTtkisylQw" - }, - { - "title": "Smartbox by Inbox: the mailbox of tomorrow, today", - "link": "https://youtu.be/hydLZJXG3Tk" - }, - { - "title": "Introducing Coffee to the Home", - "link": "https://youtu.be/U2JBFlW--UU" - }, - { - "title": "Chrome for Android and iOS: Emojify the Web", - "link": "https://youtu.be/G3NXNnoGr3Y" - }, - { - "title": "Google Maps: Pokémon Challenge", - "link": "https://youtu.be/4YMD6xELI_k" - }, - { - "title": "Introducing Google Fiber to the Pole", - "link": "https://youtu.be/qcgWRpQP6ds" - }, - { - "title": "Introducing Gmail Blue", - "link": "https://youtu.be/Zr4JwPb99qU" - }, - { - "title": "Introducing Google Nose", - "link": "https://youtu.be/VFbYadm_mrw" - }, - { - "title": "Explore Treasure Mode with Google Maps", - "link": "https://youtu.be/_qFFHC0eIUc" - }, - { - "title": "YouTube's ready to select a winner", - "link": "https://youtu.be/H542nLTTbu0" - }, - { - "title": "A word about Gmail Tap", - "link": "https://youtu.be/Je7Xq9tdCJc" - }, - { - "title": "Introducing the Google Fiber Bar", - "link": "https://youtu.be/re0VRK6ouwI" - }, - { - "title": "Introducing Gmail Tap", - "link": "https://youtu.be/1KhZKNZO8mQ" - }, - { - "title": "Chrome Multitask Mode", - "link": "https://youtu.be/UiLSiqyDf4Y" - }, - { - "title": "Google Maps 8-bit for NES", - "link": "https://youtu.be/rznYifPHxDg" - }, - { - "title": "Being a Google Autocompleter", - "link": "https://youtu.be/blB_X38YSxQ" - }, - { - "title": "Introducing Gmail Motion", - "link": "https://youtu.be/Bu927_ul_X0" - } - ], - "nvidia": [ - { - "title": "Introducing GeForce GTX G-Assist", - "link": "https://youtu.be/smM-Wdk2RLQ" - } - ], - "razer": [ - { - "title": "The Hovering Mouse - Project McFly | Razer", - "link": "https://youtu.be/IlCx5gjAmqI" - }, - { - "title": "Be the Machine | Project Venom v2", - "link": "https://youtu.be/j8UJE7DoyJ8" - } - ] -} +[ + { + "url": "https://youtu.be/OYcv406J_J4", + "channel": "google" + }, + { + "url": "https://youtu.be/0_5X6N6DHyk", + "channel": "google" + }, + { + "url": "https://youtu.be/UmJ2NBHXTqo", + "channel": "google" + }, + { + "url": "https://youtu.be/3MA6_21nka8", + "channel": "google" + }, + { + "url": "https://youtu.be/QAwL0O5nXe0", + "channel": "google" + }, + { + "url": "https://youtu.be/DPEJB-FCItk", + "channel": "google" + }, + { + "url": "https://youtu.be/LSZPNwZex9s", + "channel": "google" + }, + { + "url": "https://youtu.be/dFrgNiweQDk", + "channel": "google" + }, + { + "url": "https://youtu.be/F0F6SnbqUcE", + "channel": "google" + }, + { + "url": "https://youtu.be/VkOuShXpoKc", + "channel": "google" + }, + { + "url": "https://youtu.be/HQtGFBbwKEk", + "channel": "google" + }, + { + "url": "https://youtu.be/Cp10_PygJ4o", + "channel": "google" + }, + { + "url": "https://youtu.be/XTTtkisylQw", + "channel": "google" + }, + { + "url": "https://youtu.be/hydLZJXG3Tk", + "channel": "google" + }, + { + "url": "https://youtu.be/U2JBFlW--UU", + "channel": "google" + }, + { + "url": "https://youtu.be/G3NXNnoGr3Y", + "channel": "google" + }, + { + "url": "https://youtu.be/4YMD6xELI_k", + "channel": "google" + }, + { + "url": "https://youtu.be/qcgWRpQP6ds", + "channel": "google" + }, + { + "url": "https://youtu.be/Zr4JwPb99qU", + "channel": "google" + }, + { + "url": "https://youtu.be/VFbYadm_mrw", + "channel": "google" + }, + { + "url": "https://youtu.be/_qFFHC0eIUc", + "channel": "google" + }, + { + "url": "https://youtu.be/H542nLTTbu0", + "channel": "google" + }, + { + "url": "https://youtu.be/Je7Xq9tdCJc", + "channel": "google" + }, + { + "url": "https://youtu.be/re0VRK6ouwI", + "channel": "google" + }, + { + "url": "https://youtu.be/1KhZKNZO8mQ", + "channel": "google" + }, + { + "url": "https://youtu.be/UiLSiqyDf4Y", + "channel": "google" + }, + { + "url": "https://youtu.be/rznYifPHxDg", + "channel": "google" + }, + { + "url": "https://youtu.be/blB_X38YSxQ", + "channel": "google" + }, + { + "url": "https://youtu.be/Bu927_ul_X0", + "channel": "google" + }, + { + "url": "https://youtu.be/smM-Wdk2RLQ", + "channel": "nvidia" + }, + { + "url": "https://youtu.be/IlCx5gjAmqI", + "channel": "razer" + }, + { + "url": "https://youtu.be/j8UJE7DoyJ8", + "channel": "razer" + } +] -- cgit v1.2.3 From 607e83bf78728f990377cf3f8d0c3b080b07476b Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Mon, 12 Apr 2021 19:54:19 +0100 Subject: chore: remove unnecessary utility function and simplify code --- bot/exts/easter/april_fools_vids.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/bot/exts/easter/april_fools_vids.py b/bot/exts/easter/april_fools_vids.py index 97cb407c..c7a3c014 100644 --- a/bot/exts/easter/april_fools_vids.py +++ b/bot/exts/easter/april_fools_vids.py @@ -1,32 +1,22 @@ import logging import random from json import load -from pathlib import Path from discord.ext import commands log = logging.getLogger(__name__) +with open("bot/resources/easter/april_fools_vids.json", encoding="utf-8") as f: + ALL_VIDS = load(f) + class AprilFoolVideos(commands.Cog): """A cog for April Fools' that gets a random April Fools' video from Youtube.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - self.yt_vids = self.load_json() - - @staticmethod - def load_json() -> dict: - """A function to load JSON data.""" - p = Path('bot/resources/easter/april_fools_vids.json') - with p.open(encoding="utf-8") as json_file: - all_vids = load(json_file) - return all_vids - @commands.command(name='fool') async def april_fools(self, ctx: commands.Context) -> None: """Get a random April Fools' video from Youtube.""" - video = random.choice(self.yt_vids) + video = random.choice(ALL_VIDS) channel, url = video["channel"], video["url"] -- cgit v1.2.3 From dfc732267d65292881e0cc2f74ea7818f9e15da2 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 12 Apr 2021 21:39:36 +0100 Subject: Change to pillow-simd --- Pipfile | 2 +- Pipfile.lock | 215 ++++++++++++++++++++++++++++++----------------------------- 2 files changed, 111 insertions(+), 106 deletions(-) diff --git a/Pipfile b/Pipfile index 25e96c92..4f37b7f0 100644 --- a/Pipfile +++ b/Pipfile @@ -8,13 +8,13 @@ aiodns = "~=2.0" arrow = "~=0.14" beautifulsoup4 = "~=4.8" fuzzywuzzy = "~=0.17" -pillow = "~=8.1" pytz = "~=2019.2" sentry-sdk = "~=0.19" PyYAML = "~=5.4" "discord.py" = {extras = ["voice"], version = "~=1.5.1"} async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} emojis = "~=0.6.0" +pillow-simd = "~=7.0" [dev-packages] flake8 = "~=3.8" diff --git a/Pipfile.lock b/Pipfile.lock index 64cae8cc..875c93c5 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "427595155192c24bd1b3de5e8ffc242dc55f4390b9b90257179b5fa76457b611" + "sha256": "3ef6251d323e7b19a7810ad060fb74aa830e6f52f77a50a03a18e78fdeb29fd9" }, "pipfile-spec": 6, "requires": { @@ -40,6 +40,7 @@ "sha256:c506853ba52e516b264b106321c424d03f3ddef2813246432fa9d1cefd361c81", "sha256:fb83326d8295e8840e4ba774edf346e87eca78ba8a89c55d2690352842c15ba5" ], + "markers": "python_full_version >= '3.5.3'", "version": "==3.6.3" }, "aioredis": { @@ -73,6 +74,7 @@ "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" ], + "markers": "python_full_version >= '3.5.3'", "version": "==3.0.1" }, "attrs": { @@ -80,6 +82,7 @@ "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==20.3.0" }, "beautifulsoup4": { @@ -225,6 +228,7 @@ "sha256:f52010e0a44e3d8530437e7da38d11fb822acfb0d5b12e9cd5ba655509937ca0", "sha256:f8196f739092a78e4f6b1b2172679ed3343c39c61a3e9d722ce6fcf1dac2824a" ], + "markers": "python_version >= '3.6'", "version": "==2.0.0" }, "idna": { @@ -232,6 +236,7 @@ "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16", "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1" ], + "markers": "python_version >= '3.4'", "version": "==3.1" }, "multidict": { @@ -254,46 +259,15 @@ "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255", "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d" ], + "markers": "python_version >= '3.5'", "version": "==4.7.6" }, - "pillow": { - "hashes": [ - "sha256:01bb0a34f1a6689b138c0089d670ae2e8f886d2666a9b2f2019031abdea673c4", - "sha256:07872f1d8421db5a3fe770f7480835e5e90fddb58f36c216d4a2ac0d594de474", - "sha256:1022f8f6dc3c5b0dcf928f1c49ba2ac73051f576af100d57776e2b65c1f76a8d", - "sha256:14415e9e28410232370615dbde0cf0a00e526f522f665460344a5b96973a3086", - "sha256:172acfaf00434a28dddfe592d83f2980e22e63c769ff4a448ddf7b7a38ffd165", - "sha256:1c5e3c36f02c815766ae9dd91899b1c5b4652f2a37b7a51609f3bd467c0f11fb", - "sha256:292f2aa1ae5c5c1451cb4b558addb88c257411d3fd71c6cf45562911baffc979", - "sha256:2a40d7d4b17db87f5b9a1efc0aff56000e1d0d5ece415090c102aafa0ccbe858", - "sha256:2f0d7034d5faae9a8d1019d152ede924f653df2ce77d3bba4ce62cd21b5f94ae", - "sha256:33fdbd4f5608c852d97264f9d2e3b54e9e9959083d008145175b86100b275e5b", - "sha256:3b13d89d97b551e02549d1f0edf22bed6acfd6fd2e888cd1e9a953bf215f0e81", - "sha256:3e759bcc03d6f39bc751e56d86bc87252b9a21c689a27c5ed753717a87d53a5b", - "sha256:3ec87bd1248b23a2e4e19e774367fbe30fddc73913edc5f9b37470624f55dc1f", - "sha256:436b0a2dd9fe3f7aa6a444af6bdf53c1eb8f5ced9ea3ef104daa83f0ea18e7bc", - "sha256:43b3c859912e8bf754b3c5142df624794b18eb7ae07cfeddc917e1a9406a3ef2", - "sha256:4fe74636ee71c57a7f65d7b21a9f127d842b4fb75511e5d256ace258826eb352", - "sha256:59445af66b59cc39530b4f810776928d75e95f41e945f0c32a3de4aceb93c15d", - "sha256:69da5b1d7102a61ce9b45deb2920a2012d52fd8f4201495ea9411d0071b0ec22", - "sha256:7094bbdecb95ebe53166e4c12cf5e28310c2b550b08c07c5dc15433898e2238e", - "sha256:8211cac9bf10461f9e33fe9a3af6c5131f3fdd0d10672afc2abb2c70cf95c5ca", - "sha256:8cf77e458bd996dc85455f10fe443c0c946f5b13253773439bcbec08aa1aebc2", - "sha256:924fc33cb4acaf6267b8ca3b8f1922620d57a28470d5e4f49672cea9a841eb08", - "sha256:99ce3333b40b7a4435e0a18baad468d44ab118a4b1da0af0a888893d03253f1d", - "sha256:a7d690b2c5f7e4a932374615fedceb1e305d2dd5363c1de15961725fe10e7d16", - "sha256:b9af590adc1e46898a1276527f3cfe2da8048ae43fbbf9b1bf9395f6c99d9b47", - "sha256:bb18422ad00c1fecc731d06592e99c3be2c634da19e26942ba2f13d805005cf2", - "sha256:c10af40ee2f1a99e1ae755ab1f773916e8bca3364029a042cd9161c400416bd8", - "sha256:c143c409e7bc1db784471fe9d0bf95f37c4458e879ad84cfae640cb74ee11a26", - "sha256:c448d2b335e21951416a30cd48d35588d122a912d5fe9e41900afacecc7d21a1", - "sha256:d30f30c044bdc0ab8f3924e1eeaac87e0ff8a27e87369c5cac4064b6ec78fd83", - "sha256:df534e64d4f3e84e8f1e1a37da3f541555d947c1c1c09b32178537f0f243f69d", - "sha256:f6fc18f9c9c7959bf58e6faf801d14fafb6d4717faaf6f79a68c8bb2a13dcf20", - "sha256:ff83dfeb04c98bb3e7948f876c17513a34e9a19fd92e292288649164924c1b39" + "pillow-simd": { + "hashes": [ + "sha256:c27907af0e7ede1ceed281719e722e7dbf3e1dbfe561373978654a6b64896cb7" ], "index": "pypi", - "version": "==8.1.1" + "version": "==7.0.0.post3" }, "pycares": { "hashes": [ @@ -334,6 +308,7 @@ "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.20" }, "pynacl": { @@ -367,6 +342,7 @@ "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.8.1" }, "pytz": { @@ -379,36 +355,45 @@ }, "pyyaml": { "hashes": [ - "sha256:02c78d77281d8f8d07a255e57abdbf43b02257f59f50cc6b636937d68efa5dd0", - "sha256:0dc9f2eb2e3c97640928dec63fd8dc1dd91e6b6ed236bd5ac00332b99b5c2ff9", - "sha256:124fd7c7bc1e95b1eafc60825f2daf67c73ce7b33f1194731240d24b0d1bf628", - "sha256:26fcb33776857f4072601502d93e1a619f166c9c00befb52826e7b774efaa9db", - "sha256:31ba07c54ef4a897758563e3a0fcc60077698df10180abe4b8165d9895c00ebf", - "sha256:3c49e39ac034fd64fd576d63bb4db53cda89b362768a67f07749d55f128ac18a", - "sha256:52bf0930903818e600ae6c2901f748bc4869c0c406056f679ab9614e5d21a166", - "sha256:5a3f345acff76cad4aa9cb171ee76c590f37394186325d53d1aa25318b0d4a09", - "sha256:5e7ac4e0e79a53451dc2814f6876c2fa6f71452de1498bbe29c0b54b69a986f4", - "sha256:7242790ab6c20316b8e7bb545be48d7ed36e26bbe279fd56f2c4a12510e60b4b", - "sha256:737bd70e454a284d456aa1fa71a0b429dd527bcbf52c5c33f7c8eee81ac16b89", - "sha256:8635d53223b1f561b081ff4adecb828fd484b8efffe542edcfdff471997f7c39", - "sha256:8b818b6c5a920cbe4203b5a6b14256f0e5244338244560da89b7b0f1313ea4b6", - "sha256:8bf38641b4713d77da19e91f8b5296b832e4db87338d6aeffe422d42f1ca896d", - "sha256:a36a48a51e5471513a5aea920cdad84cbd56d70a5057cca3499a637496ea379c", - "sha256:b2243dd033fd02c01212ad5c601dafb44fbb293065f430b0d3dbf03f3254d615", - "sha256:cc547d3ead3754712223abb7b403f0a184e4c3eae18c9bb7fd15adef1597cc4b", - "sha256:cc552b6434b90d9dbed6a4f13339625dc466fd82597119897e9489c953acbc22", - "sha256:f3790156c606299ff499ec44db422f66f05a7363b39eb9d5b064f17bd7d7c47b", - "sha256:f7a21e3d99aa3095ef0553e7ceba36fb693998fbb1226f1392ce33681047465f", - "sha256:fdc6b2cb4b19e431994f25a9160695cc59a4e861710cc6fc97161c5e845fc579" + "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", + "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", + "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", + "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", + "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", + "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", + "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", + "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", + "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", + "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", + "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", + "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", + "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", + "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", + "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", + "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", + "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", + "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", + "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", + "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", + "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", + "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", + "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", + "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", + "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", + "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", + "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", + "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", + "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" ], "index": "pypi", - "version": "==5.4" + "version": "==5.4.1" }, "redis": { "hashes": [ "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2", "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==3.5.3" }, "sentry-sdk": { @@ -424,6 +409,7 @@ "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.15.0" }, "sortedcontainers": { @@ -446,6 +432,7 @@ "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df", "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", "version": "==1.26.4" }, "yarl": { @@ -468,6 +455,7 @@ "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317", "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6" ], + "markers": "python_version >= '3.5'", "version": "==1.5.1" } }, @@ -484,6 +472,7 @@ "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==20.3.0" }, "cfgv": { @@ -491,6 +480,7 @@ "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d", "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1" ], + "markers": "python_full_version >= '3.6.1'", "version": "==3.2.0" }, "distlib": { @@ -509,19 +499,19 @@ }, "flake8": { "hashes": [ - "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839", - "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b" + "sha256:12d05ab02614b6aee8df7c36b97d1a3b2372761222b19b58621355e82acddcff", + "sha256:78873e372b12b093da7b5e5ed302e8ad9e988b38b063b61ad937f26ca58fc5f0" ], "index": "pypi", - "version": "==3.8.4" + "version": "==3.9.0" }, "flake8-annotations": { "hashes": [ - "sha256:3a377140556aecf11fa9f3bb18c10db01f5ea56dc79a730e2ec9b4f1f49e2055", - "sha256:e17947a48a5b9f632fe0c72682fc797c385e451048e7dfb20139f448a074cb3e" + "sha256:0d6cd2e770b5095f09689c9d84cc054c51b929c41a68969ea1beb4b825cac515", + "sha256:d10c4638231f8a50c0a597c4efce42bd7b7d85df4f620a0ddaca526138936a4f" ], "index": "pypi", - "version": "==2.5.0" + "version": "==2.6.2" }, "flake8-bugbear": { "hashes": [ @@ -533,11 +523,11 @@ }, "flake8-docstrings": { "hashes": [ - "sha256:3d5a31c7ec6b7367ea6506a87ec293b94a0a46c0bce2bb4975b7f1d09b6f3717", - "sha256:a256ba91bc52307bef1de59e2a009c3cf61c3d0952dbe035d6ff7208940c2edc" + "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde", + "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b" ], "index": "pypi", - "version": "==1.5.0" + "version": "==1.6.0" }, "flake8-import-order": { "hashes": [ @@ -579,10 +569,11 @@ }, "identify": { "hashes": [ - "sha256:43cb1965e84cdd247e875dec6d13332ef5be355ddc16776396d98089b9053d87", - "sha256:c7c0f590526008911ccc5ceee6ed7b085cbc92f7b6591d0ee5913a130ad64034" + "sha256:398cb92a7599da0b433c65301a1b62b9b1f4bb8248719b84736af6c0b22289d6", + "sha256:4537474817e0bbb8cea3e5b7504b7de6d44e3f169a90846cbc6adb0fc8294502" ], - "version": "==2.2.2" + "markers": "python_full_version >= '3.6.1'", + "version": "==2.2.3" }, "mccabe": { "hashes": [ @@ -593,10 +584,10 @@ }, "nodeenv": { "hashes": [ - "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9", - "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c" + "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b", + "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7" ], - "version": "==1.5.0" + "version": "==1.6.0" }, "pep8-naming": { "hashes": [ @@ -608,65 +599,77 @@ }, "pre-commit": { "hashes": [ - "sha256:16212d1fde2bed88159287da88ff03796863854b04dc9f838a55979325a3d20e", - "sha256:399baf78f13f4de82a29b649afd74bef2c4e28eb4f021661fc7f29246e8c7a3a" + "sha256:029d53cb83c241fe7d66eeee1e24db426f42c858f15a38d20bcefd8d8e05c9da", + "sha256:46b6ffbab37986c47d0a35e40906ae029376deed89a0eb2e446fb6e67b220427" ], "index": "pypi", - "version": "==2.10.1" + "version": "==2.12.0" }, "pycodestyle": { "hashes": [ - "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", - "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" + "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068", + "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef" ], - "version": "==2.6.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.7.0" }, "pydocstyle": { "hashes": [ "sha256:164befb520d851dbcf0e029681b91f4f599c62c5cd8933fd54b1bfbd50e89e1f", "sha256:d4449cf16d7e6709f63192146706933c7a334af7c0f083904799ccb851c50f6d" ], + "markers": "python_version >= '3.6'", "version": "==6.0.0" }, "pyflakes": { "hashes": [ - "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", - "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" + "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3", + "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db" ], - "version": "==2.2.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.3.1" }, "pyyaml": { "hashes": [ - "sha256:02c78d77281d8f8d07a255e57abdbf43b02257f59f50cc6b636937d68efa5dd0", - "sha256:0dc9f2eb2e3c97640928dec63fd8dc1dd91e6b6ed236bd5ac00332b99b5c2ff9", - "sha256:124fd7c7bc1e95b1eafc60825f2daf67c73ce7b33f1194731240d24b0d1bf628", - "sha256:26fcb33776857f4072601502d93e1a619f166c9c00befb52826e7b774efaa9db", - "sha256:31ba07c54ef4a897758563e3a0fcc60077698df10180abe4b8165d9895c00ebf", - "sha256:3c49e39ac034fd64fd576d63bb4db53cda89b362768a67f07749d55f128ac18a", - "sha256:52bf0930903818e600ae6c2901f748bc4869c0c406056f679ab9614e5d21a166", - "sha256:5a3f345acff76cad4aa9cb171ee76c590f37394186325d53d1aa25318b0d4a09", - "sha256:5e7ac4e0e79a53451dc2814f6876c2fa6f71452de1498bbe29c0b54b69a986f4", - "sha256:7242790ab6c20316b8e7bb545be48d7ed36e26bbe279fd56f2c4a12510e60b4b", - "sha256:737bd70e454a284d456aa1fa71a0b429dd527bcbf52c5c33f7c8eee81ac16b89", - "sha256:8635d53223b1f561b081ff4adecb828fd484b8efffe542edcfdff471997f7c39", - "sha256:8b818b6c5a920cbe4203b5a6b14256f0e5244338244560da89b7b0f1313ea4b6", - "sha256:8bf38641b4713d77da19e91f8b5296b832e4db87338d6aeffe422d42f1ca896d", - "sha256:a36a48a51e5471513a5aea920cdad84cbd56d70a5057cca3499a637496ea379c", - "sha256:b2243dd033fd02c01212ad5c601dafb44fbb293065f430b0d3dbf03f3254d615", - "sha256:cc547d3ead3754712223abb7b403f0a184e4c3eae18c9bb7fd15adef1597cc4b", - "sha256:cc552b6434b90d9dbed6a4f13339625dc466fd82597119897e9489c953acbc22", - "sha256:f3790156c606299ff499ec44db422f66f05a7363b39eb9d5b064f17bd7d7c47b", - "sha256:f7a21e3d99aa3095ef0553e7ceba36fb693998fbb1226f1392ce33681047465f", - "sha256:fdc6b2cb4b19e431994f25a9160695cc59a4e861710cc6fc97161c5e845fc579" + "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", + "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", + "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", + "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", + "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", + "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", + "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", + "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", + "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", + "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", + "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", + "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", + "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347", + "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", + "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541", + "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", + "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", + "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc", + "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", + "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa", + "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", + "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122", + "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", + "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", + "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", + "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc", + "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247", + "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", + "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" ], "index": "pypi", - "version": "==5.4" + "version": "==5.4.1" }, "six": { "hashes": [ "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.15.0" }, "snowballstemmer": { @@ -681,6 +684,7 @@ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.10.2" }, "virtualenv": { @@ -688,6 +692,7 @@ "sha256:49ec4eb4c224c6f7dd81bb6d0a28a09ecae5894f4e593c89b0db0885f565a107", "sha256:83f95875d382c7abafe06bd2a4cdd1b363e1bb77e02f155ebe8ac082a916b37c" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==20.4.3" } } -- cgit v1.2.3 From 442391dfbafa3b3848a030301702031f01320fdd Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 12 Apr 2021 21:40:05 +0100 Subject: Request the pfp as the right size from discord --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 68d68927..51999fb0 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -215,7 +215,7 @@ class PfpModify(commands.Cog): if not member: await ctx.send(f"{Emojis.cross_mark} Could not get member info.") return - image_bytes = await member.avatar_url.read() + image_bytes = await member.avatar_url_as(size=1024).read() await self.send_pride_image(ctx, image_bytes, pixels, flag, option) @prideavatar.command() -- cgit v1.2.3 From 86491dcd62163c217dd7d103bdb2ed985b69f631 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 12 Apr 2021 21:42:49 +0100 Subject: Relock after merging --- Pipfile.lock | 55 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 06033465..41390bc9 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "3ef6251d323e7b19a7810ad060fb74aa830e6f52f77a50a03a18e78fdeb29fd9" + "sha256": "2cddd3d7f43bb0df21fabd1c801c844c56db93049db460e2c5b2d9beb9165b9d" }, "pipfile-spec": 6, "requires": { @@ -332,13 +332,6 @@ "markers": "python_version >= '3.5'", "version": "==4.7.6" }, - "pillow-simd": { - "hashes": [ - "sha256:c27907af0e7ede1ceed281719e722e7dbf3e1dbfe561373978654a6b64896cb7" - ], - "index": "pypi", - "version": "==7.0.0.post3" - }, "numpy": { "hashes": [ "sha256:2428b109306075d89d21135bdd6b785f132a1f5a3260c371cee1fae427e12727", @@ -369,6 +362,52 @@ "markers": "python_version >= '3.7'", "version": "==1.20.2" }, + "pillow": { + "hashes": [ + "sha256:01425106e4e8cee195a411f729cff2a7d61813b0b11737c12bd5991f5f14bcd5", + "sha256:031a6c88c77d08aab84fecc05c3cde8414cd6f8406f4d2b16fed1e97634cc8a4", + "sha256:083781abd261bdabf090ad07bb69f8f5599943ddb539d64497ed021b2a67e5a9", + "sha256:0d19d70ee7c2ba97631bae1e7d4725cdb2ecf238178096e8c82ee481e189168a", + "sha256:0e04d61f0064b545b989126197930807c86bcbd4534d39168f4aa5fda39bb8f9", + "sha256:12e5e7471f9b637762453da74e390e56cc43e486a88289995c1f4c1dc0bfe727", + "sha256:22fd0f42ad15dfdde6c581347eaa4adb9a6fc4b865f90b23378aa7914895e120", + "sha256:238c197fc275b475e87c1453b05b467d2d02c2915fdfdd4af126145ff2e4610c", + "sha256:3b570f84a6161cf8865c4e08adf629441f56e32f180f7aa4ccbd2e0a5a02cba2", + "sha256:463822e2f0d81459e113372a168f2ff59723e78528f91f0bd25680ac185cf797", + "sha256:4d98abdd6b1e3bf1a1cbb14c3895226816e666749ac040c4e2554231068c639b", + "sha256:5afe6b237a0b81bd54b53f835a153770802f164c5570bab5e005aad693dab87f", + "sha256:5b70110acb39f3aff6b74cf09bb4169b167e2660dabc304c1e25b6555fa781ef", + "sha256:5cbf3e3b1014dddc45496e8cf38b9f099c95a326275885199f427825c6522232", + "sha256:624b977355cde8b065f6d51b98497d6cd5fbdd4f36405f7a8790e3376125e2bb", + "sha256:63728564c1410d99e6d1ae8e3b810fe012bc440952168af0a2877e8ff5ab96b9", + "sha256:66cc56579fd91f517290ab02c51e3a80f581aba45fd924fcdee01fa06e635812", + "sha256:6c32cc3145928c4305d142ebec682419a6c0a8ce9e33db900027ddca1ec39178", + "sha256:8bb1e155a74e1bfbacd84555ea62fa21c58e0b4e7e6b20e4447b8d07990ac78b", + "sha256:95d5ef984eff897850f3a83883363da64aae1000e79cb3c321915468e8c6add5", + "sha256:a013cbe25d20c2e0c4e85a9daf438f85121a4d0344ddc76e33fd7e3965d9af4b", + "sha256:a787ab10d7bb5494e5f76536ac460741788f1fbce851068d73a87ca7c35fc3e1", + "sha256:a7d5e9fad90eff8f6f6106d3b98b553a88b6f976e51fce287192a5d2d5363713", + "sha256:aac00e4bc94d1b7813fe882c28990c1bc2f9d0e1aa765a5f2b516e8a6a16a9e4", + "sha256:b91c36492a4bbb1ee855b7d16fe51379e5f96b85692dc8210831fbb24c43e484", + "sha256:c03c07ed32c5324939b19e36ae5f75c660c81461e312a41aea30acdd46f93a7c", + "sha256:c5236606e8570542ed424849f7852a0ff0bce2c4c8d0ba05cc202a5a9c97dee9", + "sha256:c6b39294464b03457f9064e98c124e09008b35a62e3189d3513e5148611c9388", + "sha256:cb7a09e173903541fa888ba010c345893cd9fc1b5891aaf060f6ca77b6a3722d", + "sha256:d68cb92c408261f806b15923834203f024110a2e2872ecb0bd2a110f89d3c602", + "sha256:dc38f57d8f20f06dd7c3161c59ca2c86893632623f33a42d592f097b00f720a9", + "sha256:e98eca29a05913e82177b3ba3d198b1728e164869c613d76d0de4bde6768a50e", + "sha256:f217c3954ce5fd88303fc0c317af55d5e0204106d86dea17eb8205700d47dec2" + ], + "markers": "python_version >= '3.6'", + "version": "==8.2.0" + }, + "pillow-simd": { + "hashes": [ + "sha256:c27907af0e7ede1ceed281719e722e7dbf3e1dbfe561373978654a6b64896cb7" + ], + "index": "pypi", + "version": "==7.0.0.post3" + }, "pycares": { "hashes": [ "sha256:050f00b39ed77ea8a4e555f09417d4b1a6b5baa24bb9531a3e15d003d2319b3f", -- cgit v1.2.3 From 624031d4a008ffeaeddd8edc195382347c3fe914 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 12 Apr 2021 22:19:43 +0100 Subject: Revert pillow-simd change --- Pipfile | 2 +- Pipfile.lock | 23 ++++++++--------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/Pipfile b/Pipfile index 4f9b2237..f20f6845 100644 --- a/Pipfile +++ b/Pipfile @@ -8,13 +8,13 @@ aiodns = "~=2.0" arrow = "~=0.14" beautifulsoup4 = "~=4.8" fuzzywuzzy = "~=0.17" +pillow = "~=8.1" pytz = "~=2019.2" sentry-sdk = "~=0.19" PyYAML = "~=5.4" "discord.py" = {extras = ["voice"], version = "~=1.5.1"} async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} emojis = "~=0.6.0" -pillow-simd = "~=7.0" matplotlib = "~=3.4.1" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 41390bc9..d7fc6b27 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "2cddd3d7f43bb0df21fabd1c801c844c56db93049db460e2c5b2d9beb9165b9d" + "sha256": "03b52d5b9fdfa6d037780d5aa2896c82fd5454a40bd69acf7e9b0e129557dbd5" }, "pipfile-spec": 6, "requires": { @@ -398,15 +398,8 @@ "sha256:e98eca29a05913e82177b3ba3d198b1728e164869c613d76d0de4bde6768a50e", "sha256:f217c3954ce5fd88303fc0c317af55d5e0204106d86dea17eb8205700d47dec2" ], - "markers": "python_version >= '3.6'", - "version": "==8.2.0" - }, - "pillow-simd": { - "hashes": [ - "sha256:c27907af0e7ede1ceed281719e722e7dbf3e1dbfe561373978654a6b64896cb7" - ], "index": "pypi", - "version": "==7.0.0.post3" + "version": "==8.2.0" }, "pycares": { "hashes": [ @@ -716,11 +709,11 @@ }, "identify": { "hashes": [ - "sha256:398cb92a7599da0b433c65301a1b62b9b1f4bb8248719b84736af6c0b22289d6", - "sha256:4537474817e0bbb8cea3e5b7504b7de6d44e3f169a90846cbc6adb0fc8294502" + "sha256:43cb1965e84cdd247e875dec6d13332ef5be355ddc16776396d98089b9053d87", + "sha256:c7c0f590526008911ccc5ceee6ed7b085cbc92f7b6591d0ee5913a130ad64034" ], "markers": "python_full_version >= '3.6.1'", - "version": "==2.2.3" + "version": "==2.2.2" }, "mccabe": { "hashes": [ @@ -731,10 +724,10 @@ }, "nodeenv": { "hashes": [ - "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b", - "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7" + "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9", + "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c" ], - "version": "==1.6.0" + "version": "==1.5.0" }, "pep8-naming": { "hashes": [ -- cgit v1.2.3 From b411a4522422879eee3e821f149f21636dfb56bf Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 12 Apr 2021 22:21:44 +0100 Subject: Silence matplotlib's logger --- bot/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/__init__.py b/bot/__init__.py index d0992912..71b7c8a3 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -60,6 +60,7 @@ if root.handlers: logging.getLogger("discord").setLevel(logging.ERROR) logging.getLogger("websockets").setLevel(logging.ERROR) logging.getLogger("PIL").setLevel(logging.ERROR) +logging.getLogger("matplotlib").setLevel(logging.ERROR) # Setup new logging configuration logging.basicConfig( -- cgit v1.2.3 From c45e26621f9ea4e6209a33541f5db996e3279ea0 Mon Sep 17 00:00:00 2001 From: Janine vN Date: Mon, 12 Apr 2021 18:20:28 -0400 Subject: Add constants for common string filenames Added a constant for the same filenames used in several locations. Because the now-a-constant string is used in several locations this will allow for it to be updated more easily down the line. --- bot/exts/internal_eval/_helpers.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bot/exts/internal_eval/_helpers.py b/bot/exts/internal_eval/_helpers.py index bd36520d..3a50b9f3 100644 --- a/bot/exts/internal_eval/_helpers.py +++ b/bot/exts/internal_eval/_helpers.py @@ -41,6 +41,8 @@ async def _eval_wrapper_function(): _eval_context.locals = locals() _eval_context.function = _eval_wrapper_function """ +INTERNAL_EVAL_FRAMENAME = "" +EVAL_WRAPPER_FUNCTION_FRAMENAME = "_eval_wrapper_function" def format_internal_eval_exception(exc_info: ExcInfo, code: str) -> str: @@ -51,11 +53,11 @@ def format_internal_eval_exception(exc_info: ExcInfo, code: str) -> str: output = ["Traceback (most recent call last):"] for frame in stack_summary: - if frame.filename == "": + if frame.filename == INTERNAL_EVAL_FRAMENAME: line = code[frame.lineno - 1].lstrip() - if frame.name == "_eval_wrapper_function": - name = "" + if frame.name == EVAL_WRAPPER_FUNCTION_FRAMENAME: + name = INTERNAL_EVAL_FRAMENAME else: name = frame.name else: @@ -128,7 +130,7 @@ class EvalContext: return "[No code detected]" try: - code_tree = ast.parse(code, filename="") + code_tree = ast.parse(code, filename=INTERNAL_EVAL_FRAMENAME) except SyntaxError: log.debug("Got a SyntaxError while parsing the eval code") return "".join(traceback.format_exception(*sys.exc_info(), limit=0)) @@ -145,7 +147,7 @@ class EvalContext: async def run_eval(self) -> Namespace: """Run the evaluation and return the updated locals.""" log.trace("Compiling the AST to bytecode using `exec` mode") - compiled_code = compile(self.eval_tree, filename="", mode="exec") + compiled_code = compile(self.eval_tree, filename=INTERNAL_EVAL_FRAMENAME, mode="exec") log.trace("Executing the compiled code with the desired namespace environment") exec(compiled_code, self.locals) # noqa: B102,S102 @@ -186,7 +188,7 @@ class WrapEvalCodeTree(ast.NodeTransformer): self.eval_code_tree = eval_code_tree # To avoid mutable aliasing, parse the WRAPPER_FUNC for each wrapping - self.wrapper = ast.parse(EVAL_WRAPPER, filename="") + self.wrapper = ast.parse(EVAL_WRAPPER, filename=INTERNAL_EVAL_FRAMENAME) def wrap(self) -> ast.AST: """Wrap the tree of the code by the tree of the wrapper function.""" -- cgit v1.2.3 From c9a3cdf1e71525c307ee3385a375724a861a7b74 Mon Sep 17 00:00:00 2001 From: Janine vN Date: Mon, 12 Apr 2021 18:37:01 -0400 Subject: Remove check for `exit` to reset context This commit removes the `exit` check if someone were to use this: `.int e exit` to clear the context. The check would prevent `.int e exit()` from restarting the bot container. With the `.int reset` and `.int exit` ability to clear the context the check for `exit` to clear the context isn't necessary. --- bot/exts/internal_eval/_internal_eval.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index a62a7899..757a2a1e 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -112,11 +112,6 @@ class InternalEval(commands.Cog): async def _eval(self, ctx: commands.Context, code: str) -> None: """Evaluate the `code` in the current evaluation context.""" - if code.startswith("exit"): - self.locals = {} - await ctx.send("The evaluation context was reset.") - return - context_vars = { "message": ctx.message, "author": ctx.message.author, -- cgit v1.2.3 From a6e3343b4ca1b2d7833953228c52b9a4bba73867 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Tue, 13 Apr 2021 18:11:42 +0530 Subject: use emojis and limit tags to 3 --- bot/constants.py | 5 +++++ bot/exts/evergreen/stackoverflow.py | 40 +++++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index bb538487..6c114180 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -201,6 +201,11 @@ class Emojis: status_dnd = "<:status_dnd:470326272082313216>" status_offline = "<:status_offline:470326266537705472>" + stackoverflow_upvote = environ.get('stack_upvote', "<:stack_upvote:831468823320330270>") + stackoverflow_tag = environ.get('stack_tag', "<:stack_tag:831468691647889419> ") + stackoverflow_views = environ.get('stack_views', "<:stack_eye:831455489040318464>") + stackoverflow_ans = environ.get('stack_ans', "<:stack_ans:831455489195638784>") + class Icons: questionmark = "https://cdn.discordapp.com/emojis/512367613339369475.png" diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 721f31f4..316c1ad7 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -6,12 +6,20 @@ from discord import Embed from discord.errors import HTTPException from discord.ext import commands -from bot.constants import Colours +from bot.constants import Colours, Emojis logger = logging.getLogger(__name__) BASE_URL = "https://api.stackexchange.com/2.2/search/advanced?order=desc&sort=activity&site=stackoverflow&q={query}" SEARCH_URL = "https://stackoverflow.com/search?q={query}" +ERR_EMBED = Embed( + title="Error in fetching results from Stackoverflow", + description=( + "Sorry, there was en error while trying to fetch data from the Stackoverflow website. Please try again in some " + "time. If this issue persists, please contact the mods or send a message in #dev-contrib." + ), + color=Colours.soft_red +) class Stackoverflow(commands.Cog): @@ -35,21 +43,14 @@ class Stackoverflow(commands.Cog): logger.error(f'Status code is not 200, it is {response.status}') continue if response.status != 200: # If the status is still not 200 after the 3 tries - err_embed = Embed( - title="Error in fetching results from Stackoverflow", - description=("Sorry, there was en error while trying to fetch data from the Stackoverflow website. " - "Please try again in some time. If this issue persists, please contact the mods or send a " - "message in #dev-contrib."), - color=Colours.soft_red - ) - await ctx.send(embed=err_embed) + await ctx.send(embed=ERR_EMBED) return elif not data['items']: - err_embed = Embed( + no_search_result = Embed( title=f"No search results found for {search_query!r}", color=Colours.soft_red ) - await ctx.send(embed=err_embed) + await ctx.send(embed=no_search_result) return top5 = data["items"][:5] @@ -60,23 +61,28 @@ class Stackoverflow(commands.Cog): for item in top5: embed.add_field( name=f"{unescape(item['title'])}", - value=(f"[{item['score']} upvote{'s' if item['score'] != 1 else ''} ┃ " - f"{item['view_count']} view{'s' if item['view_count'] != 1 else ''} ┃ " - f"{item['answer_count']} answer{'s' if item['answer_count'] != 1 else ''} ┃ " - f"Tags: {', '.join(item['tags'])}]" + value=(f"[{Emojis.stackoverflow_upvote} {item['score']} " + f"{Emojis.stackoverflow_views} {item['view_count']} " + f"{Emojis.stackoverflow_ans} {item['answer_count']} " + f"{Emojis.stackoverflow_tag} {', '.join(item['tags'][:3])}]" f"({item['link']})"), inline=False) embed.set_footer(text="View the original link for more results.") try: await ctx.send(embed=embed) except HTTPException: - err_embed = Embed( + search_query_too_long = Embed( title="Your search query is too long, please try shortening your search query", color=Colours.soft_red ) - await ctx.send(embed=err_embed) + await ctx.send(embed=search_query_too_long) def setup(bot: commands.Bot) -> None: """Loads Stackoverflow Cog.""" bot.add_cog(Stackoverflow(bot)) + +# View icon made by Gregor Cresnar (https://www.flaticon.com/authors/gregor-cresnar) from www.flaticon.com, and edited +# by me +# Answer icon made by Prosymbols (https://www.flaticon.com/authors/prosymbols) from www.flaticon.com, and edited by me +# Tag icon made by Freepik (https://www.flaticon.com/authors/freepik) from www.flaticon.com, and edited by me -- cgit v1.2.3 From 7a22098801065d8ffcc5ea38d854ebaff0965b52 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Tue, 13 Apr 2021 18:28:20 +0530 Subject: Update emojis --- bot/constants.py | 8 ++++---- bot/exts/evergreen/stackoverflow.py | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 6c114180..05eea474 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -201,10 +201,10 @@ class Emojis: status_dnd = "<:status_dnd:470326272082313216>" status_offline = "<:status_offline:470326266537705472>" - stackoverflow_upvote = environ.get('stack_upvote', "<:stack_upvote:831468823320330270>") - stackoverflow_tag = environ.get('stack_tag', "<:stack_tag:831468691647889419> ") - stackoverflow_views = environ.get('stack_views', "<:stack_eye:831455489040318464>") - stackoverflow_ans = environ.get('stack_ans', "<:stack_ans:831455489195638784>") + stackoverflow_upvote = environ.get('stack_upvote', "<:stack_upvote:*>") # TODO: Fill in numbers (ids) + stackoverflow_tag = environ.get('stack_tag', "<:stack_tag:*> ") + stackoverflow_views = environ.get('stack_views', "<:stack_eye:*>") + stackoverflow_ans = environ.get('stack_ans', "<:stack_ans:*>") class Icons: diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 316c1ad7..04126d9c 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -82,7 +82,5 @@ def setup(bot: commands.Bot) -> None: """Loads Stackoverflow Cog.""" bot.add_cog(Stackoverflow(bot)) -# View icon made by Gregor Cresnar (https://www.flaticon.com/authors/gregor-cresnar) from www.flaticon.com, and edited -# by me -# Answer icon made by Prosymbols (https://www.flaticon.com/authors/prosymbols) from www.flaticon.com, and edited by me +# Upvote and Comment icon taken from Reddit bot # Tag icon made by Freepik (https://www.flaticon.com/authors/freepik) from www.flaticon.com, and edited by me -- cgit v1.2.3 From ccdf1c9efc273f2945baed90008ab3fdd73a53a1 Mon Sep 17 00:00:00 2001 From: laundmo Date: Tue, 13 Apr 2021 15:54:03 +0200 Subject: Update issue matching regex fixes it being unable to get issue numbers larger than 9 limits it somewhat length-wise and character-wise to the actual github limits --- bot/exts/evergreen/issues.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index bb6273bb..4dd10d13 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -51,7 +51,8 @@ CODE_BLOCK_RE = re.compile( MAXIMUM_ISSUES = 5 # Regex used when looking for automatic linking in messages -AUTOMATIC_REGEX = re.compile(r"((?P.+?)\/)?(?P.+?)#(?P.+?)") +# regex101 of current regex https://regex101.com/r/V2ji8M/6 +AUTOMATIC_REGEX = re.compile(r"((?P[a-zA-Z0-9][a-zA-Z0-9\-]{1,39})\/)?(?P[\w\-\.]{1,100})#(?P[0-9]+)") @dataclass -- cgit v1.2.3 From a3ce39f9d2cb5a91743ce3e2a35535a65fa4034b Mon Sep 17 00:00:00 2001 From: laundmo Date: Tue, 13 Apr 2021 15:57:50 +0200 Subject: Linebreak to hopefully not run into linter issues editing this from the web version because im at work and this fixes the issue linking being basically unusable --- bot/exts/evergreen/issues.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 4dd10d13..a0316080 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -52,7 +52,9 @@ MAXIMUM_ISSUES = 5 # Regex used when looking for automatic linking in messages # regex101 of current regex https://regex101.com/r/V2ji8M/6 -AUTOMATIC_REGEX = re.compile(r"((?P[a-zA-Z0-9][a-zA-Z0-9\-]{1,39})\/)?(?P[\w\-\.]{1,100})#(?P[0-9]+)") +AUTOMATIC_REGEX = re.compile( + r"((?P[a-zA-Z0-9][a-zA-Z0-9\-]{1,39})\/)?(?P[\w\-\.]{1,100})#(?P[0-9]+)" +) @dataclass -- cgit v1.2.3 From 7d9cf84c7de1845bfdb5203ed8cf62b33b76cc3a Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Tue, 13 Apr 2021 15:45:07 +0100 Subject: feat: add ping command --- bot/exts/evergreen/ping.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 bot/exts/evergreen/ping.py diff --git a/bot/exts/evergreen/ping.py b/bot/exts/evergreen/ping.py new file mode 100644 index 00000000..022d6373 --- /dev/null +++ b/bot/exts/evergreen/ping.py @@ -0,0 +1,27 @@ +from discord import Embed +from discord.ext import commands + +from bot.constants import Colours + + +class Ping(commands.Cog): + """Ping the bot to see its latency and state.""" + + def __init__(self, bot: commands.Bot): + self.bot = bot + + @commands.command(name="ping") + async def ping(self, ctx: commands.Context) -> None: + """Ping the bot to see its latency and state.""" + embed = Embed( + title="Pong!", + colour=Colours.bright_green, + description=f"WS Latency: {round(self.bot.latency * 1000)}ms", + ) + + await ctx.send(embed=embed) + + +def setup(bot: commands.Bot) -> None: + """Cog load.""" + bot.add_cog(Ping(bot)) -- cgit v1.2.3 From a66058467005209b6f8b4f55dc9750ebf54c86f3 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Tue, 13 Apr 2021 15:51:11 +0100 Subject: chore: add ping_pong emoji --- bot/exts/evergreen/ping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/ping.py b/bot/exts/evergreen/ping.py index 022d6373..637f71e8 100644 --- a/bot/exts/evergreen/ping.py +++ b/bot/exts/evergreen/ping.py @@ -14,7 +14,7 @@ class Ping(commands.Cog): async def ping(self, ctx: commands.Context) -> None: """Ping the bot to see its latency and state.""" embed = Embed( - title="Pong!", + title=":ping_pong: Pong!", colour=Colours.bright_green, description=f"WS Latency: {round(self.bot.latency * 1000)}ms", ) -- cgit v1.2.3 From 897d892a8666b94d6bdf8a3cdee79cca63b59812 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Tue, 13 Apr 2021 16:00:22 +0100 Subject: chore: use discord terminology Co-authored-by: Joe Banks --- bot/exts/evergreen/ping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/ping.py b/bot/exts/evergreen/ping.py index 637f71e8..97f8b34d 100644 --- a/bot/exts/evergreen/ping.py +++ b/bot/exts/evergreen/ping.py @@ -16,7 +16,7 @@ class Ping(commands.Cog): embed = Embed( title=":ping_pong: Pong!", colour=Colours.bright_green, - description=f"WS Latency: {round(self.bot.latency * 1000)}ms", + description=f"Gateway Latency: {round(self.bot.latency * 1000)}ms", ) await ctx.send(embed=embed) -- cgit v1.2.3 From fbe8e0a5bf4ffb4443a54588b7f55f25306eee6f Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Mon, 12 Apr 2021 21:49:45 +0100 Subject: fix: display help for the correct command when an error occurs in timed --- bot/exts/evergreen/error_handler.py | 10 ++++++++-- bot/exts/evergreen/timed.py | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index 28902503..8db49748 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -46,6 +46,11 @@ class CommandErrorHandler(commands.Cog): logging.debug(f"Command {ctx.command} had its error already handled locally; ignoring.") return + parent_command = "" + if subctx := getattr(ctx, "subcontext", None): + parent_command = f"{ctx.command} " + ctx = subctx + error = getattr(error, 'original', error) logging.debug( f"Error Encountered: {type(error).__name__} - {str(error)}, " @@ -63,8 +68,9 @@ class CommandErrorHandler(commands.Cog): if isinstance(error, commands.UserInputError): self.revert_cooldown_counter(ctx.command, ctx.message) + usage = f"```{ctx.prefix}{parent_command}{ctx.command} {ctx.command.signature}```" embed = self.error_embed( - f"Your input was invalid: {error}\n\nUsage:\n```{ctx.prefix}{ctx.command} {ctx.command.signature}```" + f"Your input was invalid: {error}\n\nUsage:{usage}" ) await ctx.send(embed=embed) return @@ -95,7 +101,7 @@ class CommandErrorHandler(commands.Cog): self.revert_cooldown_counter(ctx.command, ctx.message) embed = self.error_embed( "The argument you provided was invalid: " - f"{error}\n\nUsage:\n```{ctx.prefix}{ctx.command} {ctx.command.signature}```" + f"{error}\n\nUsage:\n```{ctx.prefix}{parent_command}{ctx.command} {ctx.command.signature}```" ) await ctx.send(embed=embed) return diff --git a/bot/exts/evergreen/timed.py b/bot/exts/evergreen/timed.py index 635ccb32..5f177fd6 100644 --- a/bot/exts/evergreen/timed.py +++ b/bot/exts/evergreen/timed.py @@ -21,7 +21,9 @@ class TimedCommands(commands.Cog): """Time the command execution of a command.""" new_ctx = await self.create_execution_context(ctx, command) - if not new_ctx.command: + ctx.subcontext = new_ctx + + if not ctx.subcontext.command: help_command = f"{ctx.prefix}help" error = f"The command you are trying to time doesn't exist. Use `{help_command}` for a list of commands." -- cgit v1.2.3 From b2ec5813ddc4e7abc38c4143d79a0165fa591cd7 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 15 Apr 2021 19:16:42 +0530 Subject: Use custom help command util for sending command help. --- bot/exts/evergreen/reddit.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index 1a4f9add..7f4ce6a0 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -15,6 +15,7 @@ from discord.utils import escape_markdown, sleep_until from bot.bot import Bot from bot.constants import Channels, ERROR_REPLIES, Emojis, Reddit as RedditConfig, STAFF_ROLES from bot.utils.converters import Subreddit +from bot.utils.extensions import invoke_help_command from bot.utils.messages import sub_clyde from bot.utils.pagination import ImagePaginator, LinePaginator @@ -300,7 +301,7 @@ class Reddit(Cog): @group(name="reddit", invoke_without_command=True) async def reddit_group(self, ctx: Context) -> None: """View the top posts from various subreddits.""" - await ctx.send_help(ctx.command) + await invoke_help_command(ctx) @reddit_group.command(name="top") async def top_command(self, ctx: Context, subreddit: Subreddit = "r/Python") -> None: -- cgit v1.2.3 From 1670e5a48efb50324096df859e673d30528380e4 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 15 Apr 2021 20:39:50 +0530 Subject: Add reddit channel ID to constants. --- bot/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/constants.py b/bot/constants.py index dfb39364..f5baec35 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -111,6 +111,7 @@ class Channels(NamedTuple): voice_chat_0 = 412357430186344448 voice_chat_1 = 799647045886541885 staff_voice = 541638762007101470 + reddit = int(environ.get("CHANNEL_REDDIT", 458224812528238616)) class Categories(NamedTuple): -- cgit v1.2.3 From 541efecc44fffec87f7e9346619dcae0710e2a08 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 15 Apr 2021 20:42:14 +0530 Subject: Apply code review suggestions. --- bot/exts/evergreen/reddit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index 7f4ce6a0..916563ac 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -86,11 +86,11 @@ class Reddit(Cog): author = data["author"] content_type = Emojis.reddit_post_text - if data["is_video"] is True or {"youtube", "youtu.be"}.issubset(set(data["url"].split("."))): + if data["is_video"] or {"youtube", "youtu.be"}.issubset(set(data["url"].split("."))): # This means the content type in the post is a video. content_type = f"{Emojis.reddit_post_video}" - elif any(data["url"].endswith(pic_format) for pic_format in ("jpg", "png", "gif")): + elif data["url"].endswith(("jpg", "png", "gif")): # This means the content type in the post is an image. content_type = f"{Emojis.reddit_post_photo}" image_url = data["url"] -- cgit v1.2.3 From 2fb7233c931b5b7abc8e7441473effe5229db70a Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 16 Apr 2021 14:00:39 +0100 Subject: Remove ffmpg and discord.py voice extra --- Dockerfile | 6 ------ Pipfile | 2 +- Pipfile.lock | 50 +++++++++++--------------------------------------- 3 files changed, 12 insertions(+), 46 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0db0b0ef..8c4920a9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,12 +6,6 @@ ENV PIP_NO_CACHE_DIR=false \ PIPENV_IGNORE_VIRTUALENVS=1 \ PIPENV_NOSPIN=1 -# Install git to be able to dowload git dependencies in the Pipfile -RUN apt-get -y update \ - && apt-get install -y \ - ffmpeg \ - && rm -rf /var/lib/apt/lists/* - # Install pipenv RUN pip install -U pipenv diff --git a/Pipfile b/Pipfile index f20f6845..f6118f1a 100644 --- a/Pipfile +++ b/Pipfile @@ -12,7 +12,7 @@ pillow = "~=8.1" pytz = "~=2019.2" sentry-sdk = "~=0.19" PyYAML = "~=5.4" -"discord.py" = {extras = ["voice"], version = "~=1.5.1"} +"discord.py" = "~=1.5.1" async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} emojis = "~=0.6.0" matplotlib = "~=3.4.1" diff --git a/Pipfile.lock b/Pipfile.lock index d7fc6b27..915c3784 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "03b52d5b9fdfa6d037780d5aa2896c82fd5454a40bd69acf7e9b0e129557dbd5" + "sha256": "96cd9674aea76763df9582acd392eece6546876698fffaf9024e5a2daccb8f6f" }, "pipfile-spec": 6, "requires": { @@ -158,9 +158,7 @@ "version": "==0.10.0" }, "discord.py": { - "extras": [ - "voice" - ], + "extras": [], "hashes": [ "sha256:2367359e31f6527f8a936751fc20b09d7495dd6a76b28c8fb13d4ca6c55b7563", "sha256:def00dc50cf36d21346d71bc89f0cad8f18f9a3522978dc18c7796287d47de8b" @@ -443,32 +441,6 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.20" }, - "pynacl": { - "hashes": [ - "sha256:05c26f93964373fc0abe332676cb6735f0ecad27711035b9472751faa8521255", - "sha256:0c6100edd16fefd1557da078c7a31e7b7d7a52ce39fdca2bec29d4f7b6e7600c", - "sha256:0d0a8171a68edf51add1e73d2159c4bc19fc0718e79dec51166e940856c2f28e", - "sha256:1c780712b206317a746ace34c209b8c29dbfd841dfbc02aa27f2084dd3db77ae", - "sha256:2424c8b9f41aa65bbdbd7a64e73a7450ebb4aa9ddedc6a081e7afcc4c97f7621", - "sha256:2d23c04e8d709444220557ae48ed01f3f1086439f12dbf11976e849a4926db56", - "sha256:30f36a9c70450c7878053fa1344aca0145fd47d845270b43a7ee9192a051bf39", - "sha256:37aa336a317209f1bb099ad177fef0da45be36a2aa664507c5d72015f956c310", - "sha256:4943decfc5b905748f0756fdd99d4f9498d7064815c4cf3643820c9028b711d1", - "sha256:53126cd91356342dcae7e209f840212a58dcf1177ad52c1d938d428eebc9fee5", - "sha256:57ef38a65056e7800859e5ba9e6091053cd06e1038983016effaffe0efcd594a", - "sha256:5bd61e9b44c543016ce1f6aef48606280e45f892a928ca7068fba30021e9b786", - "sha256:6482d3017a0c0327a49dddc8bd1074cc730d45db2ccb09c3bac1f8f32d1eb61b", - "sha256:7d3ce02c0784b7cbcc771a2da6ea51f87e8716004512493a2b69016326301c3b", - "sha256:a14e499c0f5955dcc3991f785f3f8e2130ed504fa3a7f44009ff458ad6bdd17f", - "sha256:a39f54ccbcd2757d1d63b0ec00a00980c0b382c62865b61a505163943624ab20", - "sha256:aabb0c5232910a20eec8563503c153a8e78bbf5459490c49ab31f6adf3f3a415", - "sha256:bd4ecb473a96ad0f90c20acba4f0bf0df91a4e03a1f4dd6a4bdc9ca75aa3a715", - "sha256:bf459128feb543cfca16a95f8da31e2e65e4c5257d2f3dfa8c0c1031139c9c92", - "sha256:e2da3c13307eac601f3de04887624939aca8ee3c9488a0bb0eca4fb9401fc6b1", - "sha256:f67814c38162f4deb31f68d590771a29d5ae3b1bd64b75cf232308e5c74777e0" - ], - "version": "==1.3.0" - }, "pyparsing": { "hashes": [ "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", @@ -639,11 +611,11 @@ }, "flake8": { "hashes": [ - "sha256:12d05ab02614b6aee8df7c36b97d1a3b2372761222b19b58621355e82acddcff", - "sha256:78873e372b12b093da7b5e5ed302e8ad9e988b38b063b61ad937f26ca58fc5f0" + "sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378", + "sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a" ], "index": "pypi", - "version": "==3.9.0" + "version": "==3.9.1" }, "flake8-annotations": { "hashes": [ @@ -709,11 +681,11 @@ }, "identify": { "hashes": [ - "sha256:43cb1965e84cdd247e875dec6d13332ef5be355ddc16776396d98089b9053d87", - "sha256:c7c0f590526008911ccc5ceee6ed7b085cbc92f7b6591d0ee5913a130ad64034" + "sha256:398cb92a7599da0b433c65301a1b62b9b1f4bb8248719b84736af6c0b22289d6", + "sha256:4537474817e0bbb8cea3e5b7504b7de6d44e3f169a90846cbc6adb0fc8294502" ], "markers": "python_full_version >= '3.6.1'", - "version": "==2.2.2" + "version": "==2.2.3" }, "mccabe": { "hashes": [ @@ -724,10 +696,10 @@ }, "nodeenv": { "hashes": [ - "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9", - "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c" + "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b", + "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7" ], - "version": "==1.5.0" + "version": "==1.6.0" }, "pep8-naming": { "hashes": [ -- cgit v1.2.3 From e43d311ffdc8378cc0ad7095c765c76aeea145d5 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 16 Apr 2021 14:01:17 +0100 Subject: Remove un-used sound resources --- .../spookysounds/109710__tomlija__horror-gate.mp3 | Bin 118125 -> 0 bytes .../spookysounds/126113__klankbeeld__laugh.mp3 | Bin 112365 -> 0 bytes ...ugh-original-132802-nanakisan-evil-laugh-08.mp3 | Bin 137385 -> 0 bytes .../spookysounds/14570__oscillator__ghost-fx.mp3 | Bin 135405 -> 0 bytes .../spookysounds/168650__0xmusex0__doorcreak.mp3 | Bin 162421 -> 0 bytes ...71078__klankbeeld__horror-scream-woman-long.mp3 | Bin 131625 -> 0 bytes .../193812__geoneo0__four-voices-whispering-6.mp3 | Bin 163257 -> 0 bytes ...37282__devilfish101__frantic-violin-screech.mp3 | Bin 131566 -> 0 bytes .../249686__cylon8472__cthulhu-growl.mp3 | Bin 153226 -> 0 bytes .../spookysounds/35716__analogchill__scream.mp3 | Bin 114773 -> 0 bytes ...15__inspectorj__something-evil-approaches-a.mp3 | Bin 298717 -> 0 bytes .../60571__gabemiller74__breathofdeath.mp3 | Bin 177049 -> 0 bytes .../spookysounds/Female_Monster_Growls_.mp3 | Bin 148276 -> 0 bytes .../halloween/spookysounds/Male_Zombie_Roar_.mp3 | Bin 62171 -> 0 bytes .../spookysounds/Monster_Alien_Growl_Calm_.mp3 | Bin 133651 -> 0 bytes .../spookysounds/Monster_Alien_Grunt_Hiss_.mp3 | Bin 74718 -> 0 bytes bot/resources/halloween/spookysounds/sources.txt | 41 --------------------- 17 files changed, 41 deletions(-) delete mode 100644 bot/resources/halloween/spookysounds/109710__tomlija__horror-gate.mp3 delete mode 100644 bot/resources/halloween/spookysounds/126113__klankbeeld__laugh.mp3 delete mode 100644 bot/resources/halloween/spookysounds/133674__klankbeeld__horror-laugh-original-132802-nanakisan-evil-laugh-08.mp3 delete mode 100644 bot/resources/halloween/spookysounds/14570__oscillator__ghost-fx.mp3 delete mode 100644 bot/resources/halloween/spookysounds/168650__0xmusex0__doorcreak.mp3 delete mode 100644 bot/resources/halloween/spookysounds/171078__klankbeeld__horror-scream-woman-long.mp3 delete mode 100644 bot/resources/halloween/spookysounds/193812__geoneo0__four-voices-whispering-6.mp3 delete mode 100644 bot/resources/halloween/spookysounds/237282__devilfish101__frantic-violin-screech.mp3 delete mode 100644 bot/resources/halloween/spookysounds/249686__cylon8472__cthulhu-growl.mp3 delete mode 100644 bot/resources/halloween/spookysounds/35716__analogchill__scream.mp3 delete mode 100644 bot/resources/halloween/spookysounds/413315__inspectorj__something-evil-approaches-a.mp3 delete mode 100644 bot/resources/halloween/spookysounds/60571__gabemiller74__breathofdeath.mp3 delete mode 100644 bot/resources/halloween/spookysounds/Female_Monster_Growls_.mp3 delete mode 100644 bot/resources/halloween/spookysounds/Male_Zombie_Roar_.mp3 delete mode 100644 bot/resources/halloween/spookysounds/Monster_Alien_Growl_Calm_.mp3 delete mode 100644 bot/resources/halloween/spookysounds/Monster_Alien_Grunt_Hiss_.mp3 delete mode 100644 bot/resources/halloween/spookysounds/sources.txt diff --git a/bot/resources/halloween/spookysounds/109710__tomlija__horror-gate.mp3 b/bot/resources/halloween/spookysounds/109710__tomlija__horror-gate.mp3 deleted file mode 100644 index 495f2bd1..00000000 Binary files a/bot/resources/halloween/spookysounds/109710__tomlija__horror-gate.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/126113__klankbeeld__laugh.mp3 b/bot/resources/halloween/spookysounds/126113__klankbeeld__laugh.mp3 deleted file mode 100644 index 538feabc..00000000 Binary files a/bot/resources/halloween/spookysounds/126113__klankbeeld__laugh.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/133674__klankbeeld__horror-laugh-original-132802-nanakisan-evil-laugh-08.mp3 b/bot/resources/halloween/spookysounds/133674__klankbeeld__horror-laugh-original-132802-nanakisan-evil-laugh-08.mp3 deleted file mode 100644 index 17f66698..00000000 Binary files a/bot/resources/halloween/spookysounds/133674__klankbeeld__horror-laugh-original-132802-nanakisan-evil-laugh-08.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/14570__oscillator__ghost-fx.mp3 b/bot/resources/halloween/spookysounds/14570__oscillator__ghost-fx.mp3 deleted file mode 100644 index 5670657c..00000000 Binary files a/bot/resources/halloween/spookysounds/14570__oscillator__ghost-fx.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/168650__0xmusex0__doorcreak.mp3 b/bot/resources/halloween/spookysounds/168650__0xmusex0__doorcreak.mp3 deleted file mode 100644 index 42f9e9fd..00000000 Binary files a/bot/resources/halloween/spookysounds/168650__0xmusex0__doorcreak.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/171078__klankbeeld__horror-scream-woman-long.mp3 b/bot/resources/halloween/spookysounds/171078__klankbeeld__horror-scream-woman-long.mp3 deleted file mode 100644 index 1cdb0f4d..00000000 Binary files a/bot/resources/halloween/spookysounds/171078__klankbeeld__horror-scream-woman-long.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/193812__geoneo0__four-voices-whispering-6.mp3 b/bot/resources/halloween/spookysounds/193812__geoneo0__four-voices-whispering-6.mp3 deleted file mode 100644 index 89150d57..00000000 Binary files a/bot/resources/halloween/spookysounds/193812__geoneo0__four-voices-whispering-6.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/237282__devilfish101__frantic-violin-screech.mp3 b/bot/resources/halloween/spookysounds/237282__devilfish101__frantic-violin-screech.mp3 deleted file mode 100644 index b5f85f8d..00000000 Binary files a/bot/resources/halloween/spookysounds/237282__devilfish101__frantic-violin-screech.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/249686__cylon8472__cthulhu-growl.mp3 b/bot/resources/halloween/spookysounds/249686__cylon8472__cthulhu-growl.mp3 deleted file mode 100644 index d141f68e..00000000 Binary files a/bot/resources/halloween/spookysounds/249686__cylon8472__cthulhu-growl.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/35716__analogchill__scream.mp3 b/bot/resources/halloween/spookysounds/35716__analogchill__scream.mp3 deleted file mode 100644 index a0614b53..00000000 Binary files a/bot/resources/halloween/spookysounds/35716__analogchill__scream.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/413315__inspectorj__something-evil-approaches-a.mp3 b/bot/resources/halloween/spookysounds/413315__inspectorj__something-evil-approaches-a.mp3 deleted file mode 100644 index 38374316..00000000 Binary files a/bot/resources/halloween/spookysounds/413315__inspectorj__something-evil-approaches-a.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/60571__gabemiller74__breathofdeath.mp3 b/bot/resources/halloween/spookysounds/60571__gabemiller74__breathofdeath.mp3 deleted file mode 100644 index f769d9d8..00000000 Binary files a/bot/resources/halloween/spookysounds/60571__gabemiller74__breathofdeath.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/Female_Monster_Growls_.mp3 b/bot/resources/halloween/spookysounds/Female_Monster_Growls_.mp3 deleted file mode 100644 index 8b04f0f5..00000000 Binary files a/bot/resources/halloween/spookysounds/Female_Monster_Growls_.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/Male_Zombie_Roar_.mp3 b/bot/resources/halloween/spookysounds/Male_Zombie_Roar_.mp3 deleted file mode 100644 index 964d685e..00000000 Binary files a/bot/resources/halloween/spookysounds/Male_Zombie_Roar_.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/Monster_Alien_Growl_Calm_.mp3 b/bot/resources/halloween/spookysounds/Monster_Alien_Growl_Calm_.mp3 deleted file mode 100644 index 9e643773..00000000 Binary files a/bot/resources/halloween/spookysounds/Monster_Alien_Growl_Calm_.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/Monster_Alien_Grunt_Hiss_.mp3 b/bot/resources/halloween/spookysounds/Monster_Alien_Grunt_Hiss_.mp3 deleted file mode 100644 index ad99cf76..00000000 Binary files a/bot/resources/halloween/spookysounds/Monster_Alien_Grunt_Hiss_.mp3 and /dev/null differ diff --git a/bot/resources/halloween/spookysounds/sources.txt b/bot/resources/halloween/spookysounds/sources.txt deleted file mode 100644 index 7df03c2e..00000000 --- a/bot/resources/halloween/spookysounds/sources.txt +++ /dev/null @@ -1,41 +0,0 @@ -Female_Monster_Growls_ -Male_Zombie_Roar_ -Monster_Alien_Growl_Calm_ -Monster_Alien_Grunt_Hiss_ -https://www.youtube.com/audiolibrary/soundeffects - -413315__inspectorj__something-evil-approaches-a -https://freesound.org/people/InspectorJ/sounds/413315/ - -133674__klankbeeld__horror-laugh-original-132802-nanakisan-evil-laugh-08 -https://freesound.org/people/klankbeeld/sounds/133674/ - -35716__analogchill__scream -https://freesound.org/people/analogchill/sounds/35716/ - -249686__cylon8472__cthulhu-growl -https://freesound.org/people/cylon8472/sounds/249686/ - -126113__klankbeeld__laugh -https://freesound.org/people/klankbeeld/sounds/126113/ - -14570__oscillator__ghost-fx -https://freesound.org/people/oscillator/sounds/14570/ - -60571__gabemiller74__breathofdeath -https://freesound.org/people/gabemiller74/sounds/60571/ - -168650__0xmusex0__doorcreak -https://freesound.org/people/0XMUSEX0/sounds/168650/ - -193812__geoneo0__four-voices-whispering-6 -https://freesound.org/people/geoneo0/sounds/193812/ - -109710__tomlija__horror-gate -https://freesound.org/people/Tomlija/sounds/109710/ - -171078__klankbeeld__horror-scream-woman-long -https://freesound.org/people/klankbeeld/sounds/171078/ - -237282__devilfish101__frantic-violin-screech -https://freesound.org/people/devilfish101/sounds/237282/ -- cgit v1.2.3 From 2d3e1cd56eaf4902ca49f0a47a6c933484f0f1b5 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 17 Apr 2021 12:24:17 +0100 Subject: Fix captitilization in docstring Co-authored-by: LXNN --- bot/exts/evergreen/profile_pic_modification/pfp_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 51999fb0..77c937de 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -47,7 +47,7 @@ class PfpModify(commands.Cog): Fetches a member and handles errors. This helper funciton is required as the member cache doesn't always have the most up to date - profile picture. THis can lead to errors if the image is delted from the Discord CDN. + profile picture. This can lead to errors if the image is delted from the Discord CDN. """ try: member = await self.bot.get_guild(Client.guild).fetch_member(member_id) -- cgit v1.2.3 From d115f6ea03a96fd22c3e38664040a7d7f6f99641 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Mon, 19 Apr 2021 23:27:02 +0530 Subject: add check for statistics --- bot/exts/evergreen/stackoverflow.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 04126d9c..0a1c339b 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -58,16 +58,21 @@ class Stackoverflow(commands.Cog): url=SEARCH_URL.format(query=encoded_search_query), description=f"Here are the top {len(top5)} results:", color=Colours.orange) - for item in top5: - embed.add_field( - name=f"{unescape(item['title'])}", - value=(f"[{Emojis.stackoverflow_upvote} {item['score']} " - f"{Emojis.stackoverflow_views} {item['view_count']} " - f"{Emojis.stackoverflow_ans} {item['answer_count']} " - f"{Emojis.stackoverflow_tag} {', '.join(item['tags'][:3])}]" - f"({item['link']})"), - inline=False) - embed.set_footer(text="View the original link for more results.") + try: + for item in top5: + embed.add_field( + name=f"{unescape(item['title'])}", + value=(f"[{Emojis.stackoverflow_upvote} {item['score']} " + f"{Emojis.stackoverflow_views} {item['view_count']} " + f"{Emojis.stackoverflow_ans} {item['answer_count']} " + f"{Emojis.stackoverflow_tag} {', '.join(item['tags'][:3])}]" + f"({item['link']})"), + inline=False) + embed.set_footer(text="View the original link for more results.") + except KeyError: + logger.error(f'Data not found for stack overflow question "{search_query}" ') + await ctx.send(embed=ERR_EMBED) + return try: await ctx.send(embed=embed) except HTTPException: -- cgit v1.2.3 From 1ed857d9093481387720ac1dc4041a64a2c9c593 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Mon, 19 Apr 2021 20:20:48 +0100 Subject: chore: switch commands.Bot typehints to bot.bot's Bot --- bot/exts/evergreen/8bitify.py | 6 ++++-- bot/exts/evergreen/battleship.py | 7 ++++--- bot/exts/evergreen/bookmark.py | 5 +++-- bot/exts/evergreen/cheatsheet.py | 5 +++-- bot/exts/evergreen/connect_four.py | 9 +++++---- bot/exts/evergreen/conversationstarters.py | 5 +++-- bot/exts/evergreen/emoji.py | 5 +++-- bot/exts/evergreen/error_handler.py | 5 +++-- bot/exts/evergreen/fun.py | 5 +++-- bot/exts/evergreen/githubinfo.py | 5 +++-- bot/exts/evergreen/issues.py | 5 +++-- bot/exts/evergreen/latex.py | 4 +++- bot/exts/evergreen/magic_8ball.py | 6 ++++-- bot/exts/evergreen/minesweeper.py | 5 +++-- bot/exts/evergreen/movie.py | 3 ++- bot/exts/evergreen/ping.py | 5 +++-- bot/exts/evergreen/pythonfacts.py | 5 +++-- bot/exts/evergreen/recommend_game.py | 6 ++++-- bot/exts/evergreen/reddit.py | 5 +++-- bot/exts/evergreen/source.py | 5 +++-- bot/exts/evergreen/speedrun.py | 6 ++++-- bot/exts/evergreen/status_codes.py | 5 +++-- bot/exts/evergreen/timed.py | 4 +++- bot/exts/evergreen/trivia_quiz.py | 5 +++-- bot/exts/evergreen/uptime.py | 5 +++-- bot/exts/evergreen/wolfram.py | 7 ++++--- bot/exts/evergreen/wonder_twins.py | 4 +++- 27 files changed, 88 insertions(+), 54 deletions(-) diff --git a/bot/exts/evergreen/8bitify.py b/bot/exts/evergreen/8bitify.py index 7eb4d313..621aa875 100644 --- a/bot/exts/evergreen/8bitify.py +++ b/bot/exts/evergreen/8bitify.py @@ -4,11 +4,13 @@ import discord from PIL import Image from discord.ext import commands +from bot.bot import Bot + class EightBitify(commands.Cog): """Make your avatar 8bit!""" - def __init__(self, bot: commands.Bot) -> None: + def __init__(self, bot: Bot) -> None: self.bot = bot @staticmethod @@ -50,6 +52,6 @@ class EightBitify(commands.Cog): await ctx.send(file=file, embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Cog load.""" bot.add_cog(EightBitify(bot)) diff --git a/bot/exts/evergreen/battleship.py b/bot/exts/evergreen/battleship.py index 1681434f..f255afb9 100644 --- a/bot/exts/evergreen/battleship.py +++ b/bot/exts/evergreen/battleship.py @@ -9,6 +9,7 @@ from functools import partial import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours log = logging.getLogger(__name__) @@ -95,7 +96,7 @@ class Game: def __init__( self, - bot: commands.Bot, + bot: Bot, channel: discord.TextChannel, player1: discord.Member, player2: discord.Member @@ -321,7 +322,7 @@ class Game: class Battleship(commands.Cog): """Play the classic game Battleship!""" - def __init__(self, bot: commands.Bot) -> None: + def __init__(self, bot: Bot) -> None: self.bot = bot self.games: typing.List[Game] = [] self.waiting: typing.List[discord.Member] = [] @@ -438,6 +439,6 @@ class Battleship(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Cog load.""" bot.add_cog(Battleship(bot)) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 5fa05d2e..329838b9 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -4,6 +4,7 @@ import random import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours, ERROR_REPLIES, Emojis, Icons from bot.utils.converters import WrappedMessageConverter @@ -13,7 +14,7 @@ log = logging.getLogger(__name__) class Bookmark(commands.Cog): """Creates personal bookmarks by relaying a message link to the user's DMs.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @commands.command(name="bookmark", aliases=("bm", "pin")) @@ -60,6 +61,6 @@ class Bookmark(commands.Cog): await ctx.message.add_reaction(Emojis.envelope) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the Bookmark cog.""" bot.add_cog(Bookmark(bot)) diff --git a/bot/exts/evergreen/cheatsheet.py b/bot/exts/evergreen/cheatsheet.py index 3fe709d5..57c6d0b0 100644 --- a/bot/exts/evergreen/cheatsheet.py +++ b/bot/exts/evergreen/cheatsheet.py @@ -8,6 +8,7 @@ from discord.ext import commands from discord.ext.commands import BucketType, Context from bot import constants +from bot.bot import Bot from bot.constants import Categories, Channels, Colours, ERROR_REPLIES from bot.utils.decorators import whitelist_override @@ -33,7 +34,7 @@ HEADERS = {'User-Agent': 'curl/7.68.0'} class CheatSheet(commands.Cog): """Commands that sends a result of a cht.sh search in code blocks.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @staticmethod @@ -102,6 +103,6 @@ class CheatSheet(commands.Cog): await ctx.send(content=description) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the CheatSheet cog.""" bot.add_cog(CheatSheet(bot)) diff --git a/bot/exts/evergreen/connect_four.py b/bot/exts/evergreen/connect_four.py index 7e3ec42b..fbb13780 100644 --- a/bot/exts/evergreen/connect_four.py +++ b/bot/exts/evergreen/connect_four.py @@ -8,6 +8,7 @@ import emojis from discord.ext import commands from discord.ext.commands import guild_only +from bot.bot import Bot from bot.constants import Emojis NUMBERS = list(Emojis.number_emojis.values()) @@ -22,7 +23,7 @@ class Game: def __init__( self, - bot: commands.Bot, + bot: Bot, channel: discord.TextChannel, player1: discord.Member, player2: typing.Optional[discord.Member], @@ -180,7 +181,7 @@ class Game: class AI: """The Computer Player for Single-Player games.""" - def __init__(self, bot: commands.Bot, game: Game) -> None: + def __init__(self, bot: Bot, game: Game) -> None: self.game = game self.mention = bot.user.mention @@ -255,7 +256,7 @@ class AI: class ConnectFour(commands.Cog): """Connect Four. The Classic Vertical Four-in-a-row Game!""" - def __init__(self, bot: commands.Bot) -> None: + def __init__(self, bot: Bot) -> None: self.bot = bot self.games: typing.List[Game] = [] self.waiting: typing.List[discord.Member] = [] @@ -445,6 +446,6 @@ class ConnectFour(commands.Cog): await self._play_game(ctx, None, board_size, str(emoji1), str(emoji2)) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load ConnectFour Cog.""" bot.add_cog(ConnectFour(bot)) diff --git a/bot/exts/evergreen/conversationstarters.py b/bot/exts/evergreen/conversationstarters.py index e7058961..aed76bcf 100644 --- a/bot/exts/evergreen/conversationstarters.py +++ b/bot/exts/evergreen/conversationstarters.py @@ -4,6 +4,7 @@ import yaml from discord import Color, Embed from discord.ext import commands +from bot.bot import Bot from bot.constants import WHITELISTED_CHANNELS from bot.utils.decorators import whitelist_override from bot.utils.randomization import RandomCycle @@ -34,7 +35,7 @@ TOPICS = { class ConvoStarters(commands.Cog): """Evergreen conversation topics.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @commands.command() @@ -66,6 +67,6 @@ class ConvoStarters(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Conversation starters Cog load.""" bot.add_cog(ConvoStarters(bot)) diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index fa3044e3..58d9be03 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -8,6 +8,7 @@ from typing import List, Optional, Tuple from discord import Color, Embed, Emoji from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours, ERROR_REPLIES from bot.utils.extensions import invoke_help_command from bot.utils.pagination import LinePaginator @@ -19,7 +20,7 @@ log = logging.getLogger(__name__) class Emojis(commands.Cog): """A collection of commands related to emojis in the server.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @staticmethod @@ -120,6 +121,6 @@ class Emojis(commands.Cog): await ctx.send(embed=emoji_information) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Add the Emojis cog into the bot.""" bot.add_cog(Emojis(bot)) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index 8db49748..053e3866 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -7,6 +7,7 @@ from discord import Embed, Message from discord.ext import commands from sentry_sdk import push_scope +from bot.bot import Bot from bot.constants import Channels, Colours, ERROR_REPLIES, NEGATIVE_REPLIES from bot.utils.decorators import InChannelCheckFailure, InMonthCheckFailure from bot.utils.exceptions import UserNotPlayingError @@ -17,7 +18,7 @@ log = logging.getLogger(__name__) class CommandErrorHandler(commands.Cog): """A error handler for the PythonDiscord server.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @staticmethod @@ -135,6 +136,6 @@ class CommandErrorHandler(commands.Cog): log.exception(f"Unhandled command error: {str(error)}", exc_info=error) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Error handler Cog load.""" bot.add_cog(CommandErrorHandler(bot)) diff --git a/bot/exts/evergreen/fun.py b/bot/exts/evergreen/fun.py index 101725da..cde37895 100644 --- a/bot/exts/evergreen/fun.py +++ b/bot/exts/evergreen/fun.py @@ -7,9 +7,10 @@ from typing import Callable, Iterable, Tuple, Union from discord import Embed, Message from discord.ext import commands -from discord.ext.commands import BadArgument, Bot, Cog, Context, MessageConverter, clean_content +from discord.ext.commands import BadArgument, Cog, Context, MessageConverter, clean_content from bot import utils +from bot.bot import Bot from bot.constants import Client, Colours, Emojis log = logging.getLogger(__name__) @@ -239,6 +240,6 @@ class Fun(Cog): return Embed.from_dict(embed_dict) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Fun Cog load.""" bot.add_cog(Fun(bot)) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index c8a6b3f7..da6eba5c 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -7,6 +7,7 @@ import discord from discord.ext import commands from discord.ext.commands.cooldowns import BucketType +from bot.bot import Bot from bot.constants import Colours, NEGATIVE_REPLIES from bot.exts.utils.extensions import invoke_help_command @@ -18,7 +19,7 @@ GITHUB_API_URL = "https://api.github.com" class GithubInfo(commands.Cog): """Fetches info from GitHub.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot async def fetch_data(self, url: str) -> dict: @@ -170,6 +171,6 @@ class GithubInfo(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Adding the cog to the bot.""" bot.add_cog(GithubInfo(bot)) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index a0316080..692e0b43 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -7,6 +7,7 @@ from dataclasses import dataclass import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import ( Categories, Channels, @@ -91,7 +92,7 @@ class IssueState: class Issues(commands.Cog): """Cog that allows users to retrieve issues from GitHub.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot self.repos = [] @@ -269,6 +270,6 @@ class Issues(commands.Cog): await message.channel.send(embed=resp) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Cog Retrieves Issues From Github.""" bot.add_cog(Issues(bot)) diff --git a/bot/exts/evergreen/latex.py b/bot/exts/evergreen/latex.py index c4a8597c..3a93907a 100644 --- a/bot/exts/evergreen/latex.py +++ b/bot/exts/evergreen/latex.py @@ -9,6 +9,8 @@ import discord import matplotlib.pyplot as plt from discord.ext import commands +from bot.bot import Bot + # configure fonts and colors for matplotlib plt.rcParams.update( { @@ -89,6 +91,6 @@ class Latex(commands.Cog): await ctx.send(file=discord.File(image, "latex.png")) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the Latex Cog.""" bot.add_cog(Latex(bot)) diff --git a/bot/exts/evergreen/magic_8ball.py b/bot/exts/evergreen/magic_8ball.py index f974e487..2dfe237a 100644 --- a/bot/exts/evergreen/magic_8ball.py +++ b/bot/exts/evergreen/magic_8ball.py @@ -5,13 +5,15 @@ from pathlib import Path from discord.ext import commands +from bot.bot import Bot + log = logging.getLogger(__name__) class Magic8ball(commands.Cog): """A Magic 8ball command to respond to a user's question.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot with open(Path("bot/resources/evergreen/magic8ball.json"), "r", encoding="utf8") as file: self.answers = json.load(file) @@ -26,6 +28,6 @@ class Magic8ball(commands.Cog): await ctx.send("Usage: .8ball (minimum length of 3 eg: `will I win?`)") -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Magic 8ball Cog load.""" bot.add_cog(Magic8ball(bot)) diff --git a/bot/exts/evergreen/minesweeper.py b/bot/exts/evergreen/minesweeper.py index 3031debc..7c1bac3b 100644 --- a/bot/exts/evergreen/minesweeper.py +++ b/bot/exts/evergreen/minesweeper.py @@ -6,6 +6,7 @@ from random import randint, random import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Client from bot.utils.exceptions import UserNotPlayingError from bot.utils.extensions import invoke_help_command @@ -78,7 +79,7 @@ GamesDict = typing.Dict[int, Game] class Minesweeper(commands.Cog): """Play a game of Minesweeper.""" - def __init__(self, bot: commands.Bot) -> None: + def __init__(self, bot: Bot) -> None: self.games: GamesDict = {} # Store the currently running games @commands.group(name='minesweeper', aliases=('ms',), invoke_without_command=True) @@ -292,6 +293,6 @@ class Minesweeper(commands.Cog): del self.games[ctx.author.id] -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the Minesweeper cog.""" bot.add_cog(Minesweeper(bot)) diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index b3bfe998..f4356f33 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -6,8 +6,9 @@ from urllib.parse import urlencode from aiohttp import ClientSession from discord import Embed -from discord.ext.commands import Bot, Cog, Context, group +from discord.ext.commands import Cog, Context, group +from bot.bot import Bot from bot.constants import Tokens from bot.utils.extensions import invoke_help_command from bot.utils.pagination import ImagePaginator diff --git a/bot/exts/evergreen/ping.py b/bot/exts/evergreen/ping.py index 97f8b34d..1332e3e6 100644 --- a/bot/exts/evergreen/ping.py +++ b/bot/exts/evergreen/ping.py @@ -1,13 +1,14 @@ from discord import Embed from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours class Ping(commands.Cog): """Ping the bot to see its latency and state.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @commands.command(name="ping") @@ -22,6 +23,6 @@ class Ping(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Cog load.""" bot.add_cog(Ping(bot)) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index 457c2fd3..bbc4eb17 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -3,6 +3,7 @@ import itertools import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours with open('bot/resources/evergreen/python_facts.txt') as file: @@ -14,7 +15,7 @@ COLORS = itertools.cycle([Colours.python_blue, Colours.python_yellow]) class PythonFacts(commands.Cog): """Sends a random fun fact about Python.""" - def __init__(self, bot: commands.Bot) -> None: + def __init__(self, bot: Bot) -> None: self.bot = bot @commands.command(name='pythonfact', aliases=['pyfact']) @@ -28,6 +29,6 @@ class PythonFacts(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load PythonFacts Cog.""" bot.add_cog(PythonFacts(bot)) diff --git a/bot/exts/evergreen/recommend_game.py b/bot/exts/evergreen/recommend_game.py index 5e262a5b..be329f44 100644 --- a/bot/exts/evergreen/recommend_game.py +++ b/bot/exts/evergreen/recommend_game.py @@ -6,6 +6,8 @@ from random import shuffle import discord from discord.ext import commands +from bot.bot import Bot + log = logging.getLogger(__name__) game_recs = [] @@ -20,7 +22,7 @@ shuffle(game_recs) class RecommendGame(commands.Cog): """Commands related to recommending games.""" - def __init__(self, bot: commands.Bot) -> None: + def __init__(self, bot: Bot) -> None: self.bot = bot self.index = 0 @@ -45,6 +47,6 @@ class RecommendGame(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Loads the RecommendGame cog.""" bot.add_cog(RecommendGame(bot)) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index 49127bea..ea77123e 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -5,6 +5,7 @@ import discord from discord.ext import commands from discord.ext.commands.cooldowns import BucketType +from bot.bot import Bot from bot.utils.pagination import ImagePaginator log = logging.getLogger(__name__) @@ -13,7 +14,7 @@ log = logging.getLogger(__name__) class Reddit(commands.Cog): """Fetches reddit posts.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot async def fetch(self, url: str) -> dict: @@ -123,6 +124,6 @@ class Reddit(commands.Cog): await ImagePaginator.paginate(pages, ctx, embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the Cog.""" bot.add_cog(Reddit(bot)) diff --git a/bot/exts/evergreen/source.py b/bot/exts/evergreen/source.py index 45752bf9..3e6031a5 100644 --- a/bot/exts/evergreen/source.py +++ b/bot/exts/evergreen/source.py @@ -5,6 +5,7 @@ from typing import Optional, Tuple, Union from discord import Embed from discord.ext import commands +from bot.bot import Bot from bot.constants import Source SourceType = Union[commands.Command, commands.Cog, str, commands.ExtensionNotLoaded] @@ -31,7 +32,7 @@ class SourceConverter(commands.Converter): class BotSource(commands.Cog): """Displays information about the bot's source code.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @commands.command(name="source", aliases=("src",)) @@ -104,6 +105,6 @@ class BotSource(commands.Cog): return embed -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the BotSource cog.""" bot.add_cog(BotSource(bot)) diff --git a/bot/exts/evergreen/speedrun.py b/bot/exts/evergreen/speedrun.py index 21aad5aa..27d944ca 100644 --- a/bot/exts/evergreen/speedrun.py +++ b/bot/exts/evergreen/speedrun.py @@ -5,6 +5,8 @@ from random import choice from discord.ext import commands +from bot.bot import Bot + log = logging.getLogger(__name__) with Path('bot/resources/evergreen/speedrun_links.json').open(encoding="utf8") as file: LINKS = json.load(file) @@ -13,7 +15,7 @@ with Path('bot/resources/evergreen/speedrun_links.json').open(encoding="utf8") a class Speedrun(commands.Cog): """Commands about the video game speedrunning community.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @commands.command(name="speedrun") @@ -22,6 +24,6 @@ class Speedrun(commands.Cog): await ctx.send(choice(LINKS)) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the Speedrun cog.""" bot.add_cog(Speedrun(bot)) diff --git a/bot/exts/evergreen/status_codes.py b/bot/exts/evergreen/status_codes.py index 7c00fe20..635eef3d 100644 --- a/bot/exts/evergreen/status_codes.py +++ b/bot/exts/evergreen/status_codes.py @@ -3,6 +3,7 @@ from http import HTTPStatus import discord from discord.ext import commands +from bot.bot import Bot from bot.utils.extensions import invoke_help_command HTTP_DOG_URL = "https://httpstatusdogs.com/img/{code}.jpg" @@ -12,7 +13,7 @@ HTTP_CAT_URL = "https://http.cat/{code}.jpg" class HTTPStatusCodes(commands.Cog): """Commands that give HTTP statuses described and visualized by cats and dogs.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @commands.group(name="http_status", aliases=("status", "httpstatus")) @@ -68,6 +69,6 @@ class HTTPStatusCodes(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the HTTPStatusCodes cog.""" bot.add_cog(HTTPStatusCodes(bot)) diff --git a/bot/exts/evergreen/timed.py b/bot/exts/evergreen/timed.py index 5f177fd6..35ca807c 100644 --- a/bot/exts/evergreen/timed.py +++ b/bot/exts/evergreen/timed.py @@ -4,6 +4,8 @@ from time import perf_counter from discord import Message from discord.ext import commands +from bot.bot import Bot + class TimedCommands(commands.Cog): """Time the command execution of a command.""" @@ -41,6 +43,6 @@ class TimedCommands(commands.Cog): await ctx.send(f"Command execution for `{new_ctx.command}` finished in {(t_end - t_start):.4f} seconds.") -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Cog load.""" bot.add_cog(TimedCommands(bot)) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index fe692c2a..080f5b6f 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -8,6 +8,7 @@ import discord from discord.ext import commands from fuzzywuzzy import fuzz +from bot.bot import Bot from bot.constants import Roles @@ -23,7 +24,7 @@ WRONG_ANS_RESPONSE = [ class TriviaQuiz(commands.Cog): """A cog for all quiz commands.""" - def __init__(self, bot: commands.Bot) -> None: + def __init__(self, bot: Bot) -> None: self.bot = bot self.questions = self.load_questions() self.game_status = {} # A variable to store the game status: either running or not running. @@ -299,6 +300,6 @@ class TriviaQuiz(commands.Cog): await channel.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the cog.""" bot.add_cog(TriviaQuiz(bot)) diff --git a/bot/exts/evergreen/uptime.py b/bot/exts/evergreen/uptime.py index a9ad9dfb..b8813bc7 100644 --- a/bot/exts/evergreen/uptime.py +++ b/bot/exts/evergreen/uptime.py @@ -5,6 +5,7 @@ from dateutil.relativedelta import relativedelta from discord.ext import commands from bot import start_time +from bot.bot import Bot log = logging.getLogger(__name__) @@ -12,7 +13,7 @@ log = logging.getLogger(__name__) class Uptime(commands.Cog): """A cog for posting the bot's uptime.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @commands.command(name="uptime") @@ -28,6 +29,6 @@ class Uptime(commands.Cog): await ctx.send(f"I started up {uptime_string}.") -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Uptime Cog load.""" bot.add_cog(Uptime(bot)) diff --git a/bot/exts/evergreen/wolfram.py b/bot/exts/evergreen/wolfram.py index 14ec1041..c4c69070 100644 --- a/bot/exts/evergreen/wolfram.py +++ b/bot/exts/evergreen/wolfram.py @@ -9,6 +9,7 @@ from discord import Embed from discord.ext import commands from discord.ext.commands import BucketType, Cog, Context, check, group +from bot.bot import Bot from bot.constants import Colours, STAFF_ROLES, Wolfram from bot.utils.pagination import ImagePaginator @@ -102,7 +103,7 @@ def custom_cooldown(*ignore: List[int]) -> Callable: return check(predicate) -async def get_pod_pages(ctx: Context, bot: commands.Bot, query: str) -> Optional[List[Tuple]]: +async def get_pod_pages(ctx: Context, bot: Bot, query: str) -> Optional[List[Tuple]]: """Get the Wolfram API pod pages for the provided query.""" async with ctx.channel.typing(): url_str = parse.urlencode({ @@ -162,7 +163,7 @@ async def get_pod_pages(ctx: Context, bot: commands.Bot, query: str) -> Optional class Wolfram(Cog): """Commands for interacting with the Wolfram|Alpha API.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @group(name="wolfram", aliases=("wolf", "wa"), invoke_without_command=True) @@ -283,6 +284,6 @@ class Wolfram(Cog): await send_embed(ctx, message, color) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the Wolfram cog.""" bot.add_cog(Wolfram(bot)) diff --git a/bot/exts/evergreen/wonder_twins.py b/bot/exts/evergreen/wonder_twins.py index afc5346e..80b9bb4d 100644 --- a/bot/exts/evergreen/wonder_twins.py +++ b/bot/exts/evergreen/wonder_twins.py @@ -2,7 +2,9 @@ import random from pathlib import Path import yaml -from discord.ext.commands import Bot, Cog, Context, command +from discord.ext.commands import Cog, Context, command + +from bot.bot import Bot class WonderTwins(Cog): -- cgit v1.2.3 From 713396a91859d914478e417ee6ab9e0a26ee6cf5 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Mon, 19 Apr 2021 20:56:53 +0100 Subject: chore(evergreen): format each cog load docstring the same way --- bot/exts/evergreen/8bitify.py | 2 +- bot/exts/evergreen/battleship.py | 2 +- bot/exts/evergreen/conversationstarters.py | 2 +- bot/exts/evergreen/emoji.py | 2 +- bot/exts/evergreen/error_handler.py | 2 +- bot/exts/evergreen/fun.py | 2 +- bot/exts/evergreen/game.py | 2 +- bot/exts/evergreen/githubinfo.py | 2 +- bot/exts/evergreen/issues.py | 2 +- bot/exts/evergreen/magic_8ball.py | 2 +- bot/exts/evergreen/movie.py | 2 +- bot/exts/evergreen/ping.py | 2 +- bot/exts/evergreen/pythonfacts.py | 2 +- bot/exts/evergreen/reddit.py | 2 +- bot/exts/evergreen/space.py | 2 +- bot/exts/evergreen/tic_tac_toe.py | 2 +- bot/exts/evergreen/timed.py | 2 +- bot/exts/evergreen/trivia_quiz.py | 2 +- bot/exts/evergreen/uptime.py | 2 +- bot/exts/evergreen/wikipedia.py | 2 +- bot/exts/evergreen/xkcd.py | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/bot/exts/evergreen/8bitify.py b/bot/exts/evergreen/8bitify.py index 621aa875..09a3eb5c 100644 --- a/bot/exts/evergreen/8bitify.py +++ b/bot/exts/evergreen/8bitify.py @@ -53,5 +53,5 @@ class EightBitify(commands.Cog): def setup(bot: Bot) -> None: - """Cog load.""" + """Cog the EightBitify load.""" bot.add_cog(EightBitify(bot)) diff --git a/bot/exts/evergreen/battleship.py b/bot/exts/evergreen/battleship.py index f255afb9..ca0e3881 100644 --- a/bot/exts/evergreen/battleship.py +++ b/bot/exts/evergreen/battleship.py @@ -440,5 +440,5 @@ class Battleship(commands.Cog): def setup(bot: Bot) -> None: - """Cog load.""" + """Load the Battleship cog""" bot.add_cog(Battleship(bot)) diff --git a/bot/exts/evergreen/conversationstarters.py b/bot/exts/evergreen/conversationstarters.py index aed76bcf..04194c01 100644 --- a/bot/exts/evergreen/conversationstarters.py +++ b/bot/exts/evergreen/conversationstarters.py @@ -68,5 +68,5 @@ class ConvoStarters(commands.Cog): def setup(bot: Bot) -> None: - """Conversation starters Cog load.""" + """Load the ConvoStarters cog.""" bot.add_cog(ConvoStarters(bot)) diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index 58d9be03..45b77e16 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -122,5 +122,5 @@ class Emojis(commands.Cog): def setup(bot: Bot) -> None: - """Add the Emojis cog into the bot.""" + """Load the Emojis cog.""" bot.add_cog(Emojis(bot)) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index 053e3866..64460822 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -137,5 +137,5 @@ class CommandErrorHandler(commands.Cog): def setup(bot: Bot) -> None: - """Error handler Cog load.""" + """Load the ErrorHandler cog.""" bot.add_cog(CommandErrorHandler(bot)) diff --git a/bot/exts/evergreen/fun.py b/bot/exts/evergreen/fun.py index cde37895..c7b0d7d9 100644 --- a/bot/exts/evergreen/fun.py +++ b/bot/exts/evergreen/fun.py @@ -241,5 +241,5 @@ class Fun(Cog): def setup(bot: Bot) -> None: - """Fun Cog load.""" + """Load the Fun cog.""" bot.add_cog(Fun(bot)) diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index 068d3f68..24872e76 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -471,7 +471,7 @@ class Games(Cog): def setup(bot: Bot) -> None: - """Add/Load Games cog.""" + """Load the Games cog.""" # Check does IGDB API key exist, if not, log warning and don't load cog if not Tokens.igdb_client_id: logger.warning("No IGDB client ID. Not loading Games cog.") diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index da6eba5c..65fcc7cf 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -172,5 +172,5 @@ class GithubInfo(commands.Cog): def setup(bot: Bot) -> None: - """Adding the cog to the bot.""" + """Load the GithubInfo cog.""" bot.add_cog(GithubInfo(bot)) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 692e0b43..d7ee99c0 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -271,5 +271,5 @@ class Issues(commands.Cog): def setup(bot: Bot) -> None: - """Cog Retrieves Issues From Github.""" + """Load the Issues cog.""" bot.add_cog(Issues(bot)) diff --git a/bot/exts/evergreen/magic_8ball.py b/bot/exts/evergreen/magic_8ball.py index 2dfe237a..61c68693 100644 --- a/bot/exts/evergreen/magic_8ball.py +++ b/bot/exts/evergreen/magic_8ball.py @@ -29,5 +29,5 @@ class Magic8ball(commands.Cog): def setup(bot: Bot) -> None: - """Magic 8ball Cog load.""" + """Load the Magic8Ball cog.""" bot.add_cog(Magic8ball(bot)) diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index f4356f33..0d535b03 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -199,5 +199,5 @@ class Movie(Cog): def setup(bot: Bot) -> None: - """Load Movie Cog.""" + """Load the Movie Cog.""" bot.add_cog(Movie(bot)) diff --git a/bot/exts/evergreen/ping.py b/bot/exts/evergreen/ping.py index 1332e3e6..71152d15 100644 --- a/bot/exts/evergreen/ping.py +++ b/bot/exts/evergreen/ping.py @@ -24,5 +24,5 @@ class Ping(commands.Cog): def setup(bot: Bot) -> None: - """Cog load.""" + """Load the Ping cog.""" bot.add_cog(Ping(bot)) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index bbc4eb17..1dd9b40d 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -30,5 +30,5 @@ class PythonFacts(commands.Cog): def setup(bot: Bot) -> None: - """Load PythonFacts Cog.""" + """Load the PythonFacts Cog.""" bot.add_cog(PythonFacts(bot)) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index ea77123e..ce22a864 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -125,5 +125,5 @@ class Reddit(commands.Cog): def setup(bot: Bot) -> None: - """Load the Cog.""" + """Load the Reddit cog.""" bot.add_cog(Reddit(bot)) diff --git a/bot/exts/evergreen/space.py b/bot/exts/evergreen/space.py index 323ff659..b3f0016b 100644 --- a/bot/exts/evergreen/space.py +++ b/bot/exts/evergreen/space.py @@ -242,7 +242,7 @@ class Space(Cog): def setup(bot: Bot) -> None: - """Load Space Cog.""" + """Load the Space cog.""" if not Tokens.nasa: logger.warning("Can't find NASA API key. Not loading Space Cog.") return diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 6e21528e..15cc1ef3 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -323,5 +323,5 @@ class TicTacToe(Cog): def setup(bot: Bot) -> None: - """Load TicTacToe Cog.""" + """Load the TicTacToe cog.""" bot.add_cog(TicTacToe(bot)) diff --git a/bot/exts/evergreen/timed.py b/bot/exts/evergreen/timed.py index 35ca807c..42a77346 100644 --- a/bot/exts/evergreen/timed.py +++ b/bot/exts/evergreen/timed.py @@ -44,5 +44,5 @@ class TimedCommands(commands.Cog): def setup(bot: Bot) -> None: - """Cog load.""" + """Load the Timed cog.""" bot.add_cog(TimedCommands(bot)) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 080f5b6f..f40375a6 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -301,5 +301,5 @@ class TriviaQuiz(commands.Cog): def setup(bot: Bot) -> None: - """Load the cog.""" + """Load the TriviaQuiz cog.""" bot.add_cog(TriviaQuiz(bot)) diff --git a/bot/exts/evergreen/uptime.py b/bot/exts/evergreen/uptime.py index b8813bc7..c22af2c9 100644 --- a/bot/exts/evergreen/uptime.py +++ b/bot/exts/evergreen/uptime.py @@ -30,5 +30,5 @@ class Uptime(commands.Cog): def setup(bot: Bot) -> None: - """Uptime Cog load.""" + """Load the Uptime cog.""" bot.add_cog(Uptime(bot)) diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index 068c4f43..e2172fc3 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -90,5 +90,5 @@ class WikipediaSearch(commands.Cog): def setup(bot: Bot) -> None: - """Wikipedia Cog load.""" + """Load the WikipediaSearch cog.""" bot.add_cog(WikipediaSearch(bot)) diff --git a/bot/exts/evergreen/xkcd.py b/bot/exts/evergreen/xkcd.py index 1ff98ca2..ba9e46e0 100644 --- a/bot/exts/evergreen/xkcd.py +++ b/bot/exts/evergreen/xkcd.py @@ -87,5 +87,5 @@ class XKCD(Cog): def setup(bot: Bot) -> None: - """Loading the XKCD cog.""" + """Load the XKCD cog.""" bot.add_cog(XKCD(bot)) -- cgit v1.2.3 From 227d0a7705e08b74deb9b95ec4f643b1d0a0fcdd Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Mon, 19 Apr 2021 21:09:10 +0100 Subject: chore(evergreen): make usage of . at sentence ends consistent --- bot/exts/evergreen/battleship.py | 6 +++--- bot/exts/evergreen/bookmark.py | 4 ++-- bot/exts/evergreen/connect_four.py | 2 +- bot/exts/evergreen/emoji.py | 2 +- bot/exts/evergreen/githubinfo.py | 2 +- bot/exts/evergreen/minesweeper.py | 10 +++++----- bot/exts/evergreen/wolfram.py | 12 ++++++------ 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/bot/exts/evergreen/battleship.py b/bot/exts/evergreen/battleship.py index ca0e3881..813f998e 100644 --- a/bot/exts/evergreen/battleship.py +++ b/bot/exts/evergreen/battleship.py @@ -238,7 +238,7 @@ class Game: square = None turn_message = await self.turn.user.send( "It's your turn! Type the square you want to fire at. Format it like this: A1\n" - "Type `surrender` to give up" + "Type `surrender` to give up." ) await self.next.user.send("Their turn", delete_after=3.0) while True: @@ -382,7 +382,7 @@ class Battleship(commands.Cog): return await ctx.send("You're already playing a game!") if ctx.author in self.waiting: - return await ctx.send("You've already sent out a request for a player 2") + return await ctx.send("You've already sent out a request for a player 2.") announcement = await ctx.send( "**Battleship**: A new game is about to start!\n" @@ -426,7 +426,7 @@ class Battleship(commands.Cog): self.games.remove(game) except Exception: # End the game in the event of an unforseen error so the players aren't stuck in a game - await ctx.send(f"{ctx.author.mention} {user.mention} An error occurred. Game failed") + await ctx.send(f"{ctx.author.mention} {user.mention} An error occurred. Game failed.") self.games.remove(game) raise diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 329838b9..ec25e7a7 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -29,7 +29,7 @@ class Bookmark(commands.Cog): # Prevent users from bookmarking a message in a channel they don't have access to permissions = ctx.author.permissions_in(target_message.channel) if not permissions.read_messages: - log.info(f"{ctx.author} tried to bookmark a message in #{target_message.channel} but has no permissions") + log.info(f"{ctx.author} tried to bookmark a message in #{target_message.channel} but has no permissions.") embed = discord.Embed( title=random.choice(ERROR_REPLIES), color=Colours.soft_red, @@ -52,7 +52,7 @@ class Bookmark(commands.Cog): except discord.Forbidden: error_embed = discord.Embed( title=random.choice(ERROR_REPLIES), - description=f"{ctx.author.mention}, please enable your DMs to receive the bookmark", + description=f"{ctx.author.mention}, please enable your DMs to receive the bookmark.", colour=Colours.soft_red ) await ctx.send(embed=error_embed) diff --git a/bot/exts/evergreen/connect_four.py b/bot/exts/evergreen/connect_four.py index fbb13780..df2a913a 100644 --- a/bot/exts/evergreen/connect_four.py +++ b/bot/exts/evergreen/connect_four.py @@ -355,7 +355,7 @@ class ConnectFour(commands.Cog): self.games.remove(game) except Exception: # End the game in the event of an unforeseen error so the players aren't stuck in a game - await ctx.send(f"{ctx.author.mention} {user.mention if user else ''} An error occurred. Game failed") + await ctx.send(f"{ctx.author.mention} {user.mention if user else ''} An error occurred. Game failed.") if game in self.games: self.games.remove(game) raise diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index 45b77e16..7ceb6c8d 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -87,7 +87,7 @@ class Emojis(commands.Cog): if not ctx.guild.emojis: await ctx.send("No emojis found.") return - log.trace(f"Emoji Category {'' if category_query else 'not '}provided by the user") + log.trace(f"Emoji Category {'' if category_query else 'not '}provided by the user.") for emoji in ctx.guild.emojis: emoji_category = emoji.name.split("_")[0] diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 65fcc7cf..fd100a7c 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -96,7 +96,7 @@ class GithubInfo(commands.Cog): embed.add_field( name=f"Organization{'s' if len(orgs)!=1 else ''}", - value=orgs_to_add if orgs else "No organizations" + value=orgs_to_add if orgs else "No organizations." ) embed.add_field(name="Website", value=blog) diff --git a/bot/exts/evergreen/minesweeper.py b/bot/exts/evergreen/minesweeper.py index 7c1bac3b..bba349fa 100644 --- a/bot/exts/evergreen/minesweeper.py +++ b/bot/exts/evergreen/minesweeper.py @@ -38,7 +38,7 @@ class CoordinateConverter(commands.Converter): async def convert(self, ctx: commands.Context, coordinate: str) -> typing.Tuple[int, int]: """Take in a coordinate string and turn it into an (x, y) tuple.""" if not 2 <= len(coordinate) <= 3: - raise commands.BadArgument('Invalid co-ordinate provided') + raise commands.BadArgument('Invalid co-ordinate provided.') coordinate = coordinate.lower() if coordinate[0].isalpha(): @@ -149,7 +149,7 @@ class Minesweeper(commands.Cog): f"Close the game with `{Client.prefix}ms end`\n" ) except discord.errors.Forbidden: - log.debug(f"{ctx.author.name} ({ctx.author.id}) has disabled DMs from server members") + log.debug(f"{ctx.author.name} ({ctx.author.id}) has disabled DMs from server members.") await ctx.send(f":x: {ctx.author.mention}, please enable DMs to play minesweeper.") return @@ -159,7 +159,7 @@ class Minesweeper(commands.Cog): dm_msg = await ctx.author.send(f"Here's your board!\n{self.format_for_discord(revealed_board)}") if ctx.guild: - await ctx.send(f"{ctx.author.mention} is playing Minesweeper") + await ctx.send(f"{ctx.author.mention} is playing Minesweeper.") chat_msg = await ctx.send(f"Here's their board!\n{self.format_for_discord(revealed_board)}") else: chat_msg = None @@ -248,7 +248,7 @@ class Minesweeper(commands.Cog): """ Reveal one square. - return is True if the game ended, breaking the loop in `reveal_command` and deleting the game + return is True if the game ended, breaking the loop in `reveal_command` and deleting the game. """ revealed[y][x] = board[y][x] if board[y][x] == "bomb": @@ -286,7 +286,7 @@ class Minesweeper(commands.Cog): game = self.games[ctx.author.id] game.revealed = game.board await self.update_boards(ctx) - new_msg = f":no_entry: Game canceled :no_entry:\n{game.dm_msg.content}" + new_msg = f":no_entry: Game canceled. :no_entry:\n{game.dm_msg.content}" await game.dm_msg.edit(content=new_msg) if game.activated_on_server: await game.chat_msg.edit(content=new_msg) diff --git a/bot/exts/evergreen/wolfram.py b/bot/exts/evergreen/wolfram.py index c4c69070..c57a8d7a 100644 --- a/bot/exts/evergreen/wolfram.py +++ b/bot/exts/evergreen/wolfram.py @@ -56,7 +56,7 @@ def custom_cooldown(*ignore: List[int]) -> Callable: """ Implement per-user and per-guild cooldowns for requests to the Wolfram API. - A list of roles may be provided to ignore the per-user cooldown + A list of roles may be provided to ignore the per-user cooldown. """ async def predicate(ctx: Context) -> bool: if ctx.invoked_with == 'help': @@ -189,11 +189,11 @@ class Wolfram(Cog): image_url = "attachment://image.png" if status == 501: - message = "Failed to get response" + message = "Failed to get response." footer = "" color = Colours.soft_red elif status == 400: - message = "No input found" + message = "No input found." footer = "" color = Colours.soft_red elif status == 403: @@ -269,12 +269,12 @@ class Wolfram(Cog): response_text = await response.text() if status == 501: - message = "Failed to get response" + message = "Failed to get response." color = Colours.soft_red elif status == 400: - message = "No input found" + message = "No input found." color = Colours.soft_red - elif response_text == "Error 1: Invalid appid": + elif response_text == "Error 1: Invalid appid.": message = "Wolfram API key is invalid or missing." color = Colours.soft_red else: -- cgit v1.2.3 From f919e0cdb9cff71c187f55ea4447ef4934c5b9ab Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Mon, 19 Apr 2021 21:17:10 +0100 Subject: chore(evergreen): remove unneeded cog constructors --- bot/exts/evergreen/bookmark.py | 3 --- bot/exts/evergreen/conversationstarters.py | 3 --- bot/exts/evergreen/emoji.py | 3 --- bot/exts/evergreen/error_handler.py | 3 --- bot/exts/evergreen/magic_8ball.py | 3 +-- bot/exts/evergreen/minesweeper.py | 2 +- bot/exts/evergreen/movie.py | 1 - bot/exts/evergreen/pythonfacts.py | 3 --- bot/exts/evergreen/source.py | 3 --- bot/exts/evergreen/space.py | 1 - bot/exts/evergreen/speedrun.py | 3 --- bot/exts/evergreen/tic_tac_toe.py | 3 +-- bot/exts/evergreen/uptime.py | 3 --- bot/exts/evergreen/wonder_twins.py | 4 +--- 14 files changed, 4 insertions(+), 34 deletions(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index ec25e7a7..6a272784 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -14,9 +14,6 @@ log = logging.getLogger(__name__) class Bookmark(commands.Cog): """Creates personal bookmarks by relaying a message link to the user's DMs.""" - def __init__(self, bot: Bot): - self.bot = bot - @commands.command(name="bookmark", aliases=("bm", "pin")) async def bookmark( self, diff --git a/bot/exts/evergreen/conversationstarters.py b/bot/exts/evergreen/conversationstarters.py index 04194c01..54fea0b3 100644 --- a/bot/exts/evergreen/conversationstarters.py +++ b/bot/exts/evergreen/conversationstarters.py @@ -35,9 +35,6 @@ TOPICS = { class ConvoStarters(commands.Cog): """Evergreen conversation topics.""" - def __init__(self, bot: Bot): - self.bot = bot - @commands.command() @whitelist_override(channels=ALL_ALLOWED_CHANNELS) async def topic(self, ctx: commands.Context) -> None: diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index 7ceb6c8d..8e540712 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -20,9 +20,6 @@ log = logging.getLogger(__name__) class Emojis(commands.Cog): """A collection of commands related to emojis in the server.""" - def __init__(self, bot: Bot): - self.bot = bot - @staticmethod def embed_builder(emoji: dict) -> Tuple[Embed, List[str]]: """Generates an embed with the emoji names and count.""" diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index 64460822..dabd0ab5 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -18,9 +18,6 @@ log = logging.getLogger(__name__) class CommandErrorHandler(commands.Cog): """A error handler for the PythonDiscord server.""" - def __init__(self, bot: Bot): - self.bot = bot - @staticmethod def revert_cooldown_counter(command: commands.Command, message: Message) -> None: """Undoes the last cooldown counter for user-error cases.""" diff --git a/bot/exts/evergreen/magic_8ball.py b/bot/exts/evergreen/magic_8ball.py index 61c68693..708aa6d2 100644 --- a/bot/exts/evergreen/magic_8ball.py +++ b/bot/exts/evergreen/magic_8ball.py @@ -13,8 +13,7 @@ log = logging.getLogger(__name__) class Magic8ball(commands.Cog): """A Magic 8ball command to respond to a user's question.""" - def __init__(self, bot: Bot): - self.bot = bot + def __init__(self, _bot: Bot): with open(Path("bot/resources/evergreen/magic8ball.json"), "r", encoding="utf8") as file: self.answers = json.load(file) diff --git a/bot/exts/evergreen/minesweeper.py b/bot/exts/evergreen/minesweeper.py index bba349fa..d0cc28c5 100644 --- a/bot/exts/evergreen/minesweeper.py +++ b/bot/exts/evergreen/minesweeper.py @@ -79,7 +79,7 @@ GamesDict = typing.Dict[int, Game] class Minesweeper(commands.Cog): """Play a game of Minesweeper.""" - def __init__(self, bot: Bot) -> None: + def __init__(self, _bot: Bot) -> None: self.games: GamesDict = {} # Store the currently running games @commands.group(name='minesweeper', aliases=('ms',), invoke_without_command=True) diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index 0d535b03..488e5142 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -51,7 +51,6 @@ class Movie(Cog): """Movie Cog contains movies command that grab random movies from TMDB.""" def __init__(self, bot: Bot): - self.bot = bot self.http_session: ClientSession = bot.http_session @group(name='movies', aliases=['movie'], invoke_without_command=True) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index 1dd9b40d..73234d55 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -15,9 +15,6 @@ COLORS = itertools.cycle([Colours.python_blue, Colours.python_yellow]) class PythonFacts(commands.Cog): """Sends a random fun fact about Python.""" - def __init__(self, bot: Bot) -> None: - self.bot = bot - @commands.command(name='pythonfact', aliases=['pyfact']) async def get_python_fact(self, ctx: commands.Context) -> None: """Sends a Random fun fact about Python.""" diff --git a/bot/exts/evergreen/source.py b/bot/exts/evergreen/source.py index 3e6031a5..14fd02f3 100644 --- a/bot/exts/evergreen/source.py +++ b/bot/exts/evergreen/source.py @@ -32,9 +32,6 @@ class SourceConverter(commands.Converter): class BotSource(commands.Cog): """Displays information about the bot's source code.""" - def __init__(self, bot: Bot): - self.bot = bot - @commands.command(name="source", aliases=("src",)) async def source_command(self, ctx: commands.Context, *, source_item: SourceConverter = None) -> None: """Display information and a GitHub link to the source code of a command, tag, or cog.""" diff --git a/bot/exts/evergreen/space.py b/bot/exts/evergreen/space.py index b3f0016b..ee9ad38c 100644 --- a/bot/exts/evergreen/space.py +++ b/bot/exts/evergreen/space.py @@ -39,7 +39,6 @@ class Space(Cog): """Space Cog contains commands, that show images, facts or other information about space.""" def __init__(self, bot: Bot): - self.bot = bot self.http_session = bot.http_session self.rovers = {} diff --git a/bot/exts/evergreen/speedrun.py b/bot/exts/evergreen/speedrun.py index 27d944ca..bf6f2117 100644 --- a/bot/exts/evergreen/speedrun.py +++ b/bot/exts/evergreen/speedrun.py @@ -15,9 +15,6 @@ with Path('bot/resources/evergreen/speedrun_links.json').open(encoding="utf8") a class Speedrun(commands.Cog): """Commands about the video game speedrunning community.""" - def __init__(self, bot: Bot): - self.bot = bot - @commands.command(name="speedrun") async def get_speedrun(self, ctx: commands.Context) -> None: """Sends a link to a video of a random speedrun.""" diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 15cc1ef3..1fef427a 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -246,8 +246,7 @@ def is_requester_free() -> t.Callable: class TicTacToe(Cog): """TicTacToe cog contains tic-tac-toe game commands.""" - def __init__(self, bot: Bot): - self.bot = bot + def __init__(self, _bot: Bot): self.games: t.List[Game] = [] @guild_only() diff --git a/bot/exts/evergreen/uptime.py b/bot/exts/evergreen/uptime.py index c22af2c9..d2509e6f 100644 --- a/bot/exts/evergreen/uptime.py +++ b/bot/exts/evergreen/uptime.py @@ -13,9 +13,6 @@ log = logging.getLogger(__name__) class Uptime(commands.Cog): """A cog for posting the bot's uptime.""" - def __init__(self, bot: Bot): - self.bot = bot - @commands.command(name="uptime") async def uptime(self, ctx: commands.Context) -> None: """Responds with the uptime of the bot.""" diff --git a/bot/exts/evergreen/wonder_twins.py b/bot/exts/evergreen/wonder_twins.py index 80b9bb4d..9fa2d7f8 100644 --- a/bot/exts/evergreen/wonder_twins.py +++ b/bot/exts/evergreen/wonder_twins.py @@ -10,9 +10,7 @@ from bot.bot import Bot class WonderTwins(Cog): """Cog for a Wonder Twins inspired command.""" - def __init__(self, bot: Bot): - self.bot = bot - + def __init__(self, _bot: Bot): with open(Path.cwd() / "bot" / "resources" / "evergreen" / "wonder_twins.yaml", "r", encoding="utf-8") as f: info = yaml.load(f, Loader=yaml.FullLoader) self.water_types = info["water_types"] -- cgit v1.2.3 From ae64ddd3e7d731a44a1684c93d67ea6334b120d2 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Mon, 19 Apr 2021 17:04:11 -0400 Subject: Clean Up the Halloween Season - Change commands.Bot type hints to bot.Bot - Remove the setting of Cog.bot if it isn't being used - Use Bot.http_session instead of creating new ones - Keep string quotes and Doc-Strings consistant - Remove redundant/outdated code Ignored spookyavatar.py as it is being moved in another PR. --- bot/exts/halloween/8ball.py | 17 ++--- bot/exts/halloween/candy_collection.py | 35 +++++---- bot/exts/halloween/hacktober-issue-finder.py | 73 +++++++++--------- bot/exts/halloween/hacktoberstats.py | 108 ++++++++++++--------------- bot/exts/halloween/halloween_facts.py | 15 ++-- bot/exts/halloween/halloweenify.py | 13 ++-- bot/exts/halloween/monsterbio.py | 10 +-- bot/exts/halloween/monstersurvey.py | 6 +- bot/exts/halloween/scarymovie.py | 74 +++++++++--------- bot/exts/halloween/spookygif.py | 23 +++--- bot/exts/halloween/spookynamerate.py | 21 +++--- bot/exts/halloween/spookyrating.py | 14 ++-- bot/exts/halloween/spookyreact.py | 29 +++---- bot/exts/halloween/timeleft.py | 8 +- 14 files changed, 216 insertions(+), 230 deletions(-) diff --git a/bot/exts/halloween/8ball.py b/bot/exts/halloween/8ball.py index 1df48fbf..59d4acc5 100644 --- a/bot/exts/halloween/8ball.py +++ b/bot/exts/halloween/8ball.py @@ -6,28 +6,27 @@ from pathlib import Path from discord.ext import commands +from bot.bot import Bot + log = logging.getLogger(__name__) -with open(Path("bot/resources/halloween/responses.json"), "r", encoding="utf8") as f: - responses = json.load(f) +with Path("bot/resources/halloween/responses.json").open("r", encoding="utf8") as f: + RESPONSES = json.load(f) class SpookyEightBall(commands.Cog): """Spooky Eightball answers.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - @commands.command(aliases=('spooky8ball',)) async def spookyeightball(self, ctx: commands.Context, *, question: str) -> None: """Responds with a random response to a question.""" - choice = random.choice(responses['responses']) + choice = random.choice(RESPONSES["responses"]) msg = await ctx.send(choice[0]) if len(choice) > 1: await asyncio.sleep(random.randint(2, 5)) await msg.edit(content=f"{choice[0]} \n{choice[1]}") -def setup(bot: commands.Bot) -> None: - """Spooky Eight Ball Cog Load.""" - bot.add_cog(SpookyEightBall(bot)) +def setup(bot: Bot) -> None: + """Load the Spooky Eight Ball Cog.""" + bot.add_cog(SpookyEightBall()) diff --git a/bot/exts/halloween/candy_collection.py b/bot/exts/halloween/candy_collection.py index 40e21f40..5441d8a5 100644 --- a/bot/exts/halloween/candy_collection.py +++ b/bot/exts/halloween/candy_collection.py @@ -6,6 +6,7 @@ import discord from async_rediscache import RedisCache from discord.ext import commands +from bot.bot import Bot from bot.constants import Channels, Month from bot.utils.decorators import in_month @@ -40,7 +41,7 @@ class CandyCollection(commands.Cog): candy_messages = RedisCache() skull_messages = RedisCache() - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @in_month(Month.OCTOBER) @@ -60,15 +61,15 @@ class CandyCollection(commands.Cog): # do random check for skull first as it has the lower chance if random.randint(1, ADD_SKULL_REACTION_CHANCE) == 1: await self.skull_messages.set(message.id, "skull") - return await message.add_reaction(EMOJIS['SKULL']) + await message.add_reaction(EMOJIS["SKULL"]) # check for the candy chance next - if random.randint(1, ADD_CANDY_REACTION_CHANCE) == 1: + elif random.randint(1, ADD_CANDY_REACTION_CHANCE) == 1: await self.candy_messages.set(message.id, "candy") - return await message.add_reaction(EMOJIS['CANDY']) + await message.add_reaction(EMOJIS["CANDY"]) @in_month(Month.OCTOBER) @commands.Cog.listener() - async def on_reaction_add(self, reaction: discord.Reaction, user: discord.Member) -> None: + async def on_reaction_add(self, reaction: discord.Reaction, user: Union[discord.User, discord.Member]) -> None: """Add/remove candies from a person if the reaction satisfies criteria.""" message = reaction.message # check to ensure the reactor is human @@ -81,7 +82,7 @@ class CandyCollection(commands.Cog): # if its not a candy or skull, and it is one of 10 most recent messages, # proceed to add a skull/candy with higher chance - if str(reaction.emoji) not in (EMOJIS['SKULL'], EMOJIS['CANDY']): + if str(reaction.emoji) not in (EMOJIS["SKULL"], EMOJIS["CANDY"]): recent_message_ids = map( lambda m: m.id, await self.hacktober_channel.history(limit=10).flatten() @@ -90,14 +91,14 @@ class CandyCollection(commands.Cog): await self.reacted_msg_chance(message) return - if await self.candy_messages.get(message.id) == "candy" and str(reaction.emoji) == EMOJIS['CANDY']: + if await self.candy_messages.get(message.id) == "candy" and str(reaction.emoji) == EMOJIS["CANDY"]: await self.candy_messages.delete(message.id) if await self.candy_records.contains(user.id): await self.candy_records.increment(user.id) else: await self.candy_records.set(user.id, 1) - elif await self.skull_messages.get(message.id) == "skull" and str(reaction.emoji) == EMOJIS['SKULL']: + elif await self.skull_messages.get(message.id) == "skull" and str(reaction.emoji) == EMOJIS["SKULL"]: await self.skull_messages.delete(message.id) if prev_record := await self.candy_records.get(user.id): @@ -124,11 +125,11 @@ class CandyCollection(commands.Cog): """ if random.randint(1, ADD_SKULL_EXISTING_REACTION_CHANCE) == 1: await self.skull_messages.set(message.id, "skull") - return await message.add_reaction(EMOJIS['SKULL']) + await message.add_reaction(EMOJIS['SKULL']) - if random.randint(1, ADD_CANDY_EXISTING_REACTION_CHANCE) == 1: + elif random.randint(1, ADD_CANDY_EXISTING_REACTION_CHANCE) == 1: await self.candy_messages.set(message.id, "candy") - return await message.add_reaction(EMOJIS['CANDY']) + await message.add_reaction(EMOJIS["CANDY"]) @property def hacktober_channel(self) -> discord.TextChannel: @@ -141,8 +142,10 @@ class CandyCollection(commands.Cog): ) -> None: """Send a spooky message.""" e = discord.Embed(colour=author.colour) - e.set_author(name="Ghosts and Ghouls and Jack o' lanterns at night; " - f"I took {candies} candies and quickly took flight.") + e.set_author( + name="Ghosts and Ghouls and Jack o' lanterns at night; " + f"I took {candies} candies and quickly took flight." + ) await channel.send(embed=e) @staticmethod @@ -173,7 +176,7 @@ class CandyCollection(commands.Cog): return '\n'.join( f"{EMOJIS['MEDALS'][index]} <@{record[0]}>: {record[1]}" for index, record in enumerate(top_five) - ) if top_five else 'No Candies' + ) if top_five else "No Candies" e = discord.Embed(colour=discord.Colour.blurple()) e.add_field( @@ -191,6 +194,6 @@ class CandyCollection(commands.Cog): await ctx.send(embed=e) -def setup(bot: commands.Bot) -> None: - """Candy Collection game Cog load.""" +def setup(bot: Bot) -> None: + """Load the Candy Collection Cog.""" bot.add_cog(CandyCollection(bot)) diff --git a/bot/exts/halloween/hacktober-issue-finder.py b/bot/exts/halloween/hacktober-issue-finder.py index 9deadde9..c88e2b6f 100644 --- a/bot/exts/halloween/hacktober-issue-finder.py +++ b/bot/exts/halloween/hacktober-issue-finder.py @@ -3,10 +3,10 @@ import logging import random from typing import Dict, Optional -import aiohttp import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Month, Tokens from bot.utils.decorators import in_month @@ -25,7 +25,7 @@ if GITHUB_TOKEN := Tokens.github: class HacktoberIssues(commands.Cog): """Find a random hacktober python issue on GitHub.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot self.cache_normal = None self.cache_timer_normal = datetime.datetime(1, 1, 1) @@ -41,7 +41,7 @@ class HacktoberIssues(commands.Cog): If the command is run with beginner (`.hacktoberissues beginner`): It will also narrow it down to the "first good issue" label. """ - with ctx.typing(): + async with ctx.typing(): issues = await self.get_issues(ctx, option) if issues is None: return @@ -59,40 +59,39 @@ class HacktoberIssues(commands.Cog): log.debug("using cache") return self.cache_normal - async with aiohttp.ClientSession() as session: + if option == "beginner": + url = URL + '+label:"good first issue"' + if self.cache_beginner is not None: + page = random.randint(1, min(1000, self.cache_beginner["total_count"]) // 100) + url += f"&page={page}" + else: + url = URL + if self.cache_normal is not None: + page = random.randint(1, min(1000, self.cache_normal["total_count"]) // 100) + url += f"&page={page}" + + log.debug(f"making api request to url: {url}") + async with self.bot.http_session.get(url, headers=REQUEST_HEADERS) as response: + if response.status != 200: + log.error(f"expected 200 status (got {response.status}) from the GitHub api.") + await ctx.send(f"ERROR: expected 200 status (got {response.status}) from the GitHub api.") + await ctx.send(await response.text()) + return None + data = await response.json() + + if len(data["items"]) == 0: + log.error(f"no issues returned from GitHub api. with url: {response.url}") + await ctx.send(f"ERROR: no issues returned from GitHub api. with url: {response.url}") + return None + if option == "beginner": - url = URL + '+label:"good first issue"' - if self.cache_beginner is not None: - page = random.randint(1, min(1000, self.cache_beginner["total_count"]) // 100) - url += f"&page={page}" + self.cache_beginner = data + self.cache_timer_beginner = ctx.message.created_at else: - url = URL - if self.cache_normal is not None: - page = random.randint(1, min(1000, self.cache_normal["total_count"]) // 100) - url += f"&page={page}" - - log.debug(f"making api request to url: {url}") - async with session.get(url, headers=REQUEST_HEADERS) as response: - if response.status != 200: - log.error(f"expected 200 status (got {response.status}) from the GitHub api.") - await ctx.send(f"ERROR: expected 200 status (got {response.status}) from the GitHub api.") - await ctx.send(await response.text()) - return None - data = await response.json() - - if len(data["items"]) == 0: - log.error(f"no issues returned from GitHub api. with url: {response.url}") - await ctx.send(f"ERROR: no issues returned from GitHub api. with url: {response.url}") - return None - - if option == "beginner": - self.cache_beginner = data - self.cache_timer_beginner = ctx.message.created_at - else: - self.cache_normal = data - self.cache_timer_normal = ctx.message.created_at - - return data + self.cache_normal = data + self.cache_timer_normal = ctx.message.created_at + + return data @staticmethod def format_embed(issue: Dict) -> discord.Embed: @@ -111,6 +110,6 @@ class HacktoberIssues(commands.Cog): return embed -def setup(bot: commands.Bot) -> None: - """Hacktober issue finder Cog Load.""" +def setup(bot: Bot) -> None: + """Load the HacktoberIssue finder.""" bot.add_cog(HacktoberIssues(bot)) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index d9fc0e8a..9695ba2a 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -5,11 +5,11 @@ from collections import Counter from datetime import datetime, timedelta from typing import List, Optional, Tuple, Union -import aiohttp import discord from async_rediscache import RedisCache from discord.ext import commands +from bot.bot import Bot from bot.constants import Channels, Month, NEGATIVE_REPLIES, Tokens, WHITELISTED_CHANNELS from bot.utils.decorators import in_month, whitelist_override @@ -39,7 +39,7 @@ class HacktoberStats(commands.Cog): # Stores mapping of user IDs and GitHub usernames linked_accounts = RedisCache() - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @in_month(Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER) @@ -83,15 +83,15 @@ class HacktoberStats(commands.Cog): if github_username: if await self.linked_accounts.contains(author_id): old_username = await self.linked_accounts.get(author_id) - logging.info(f"{author_id} has changed their github link from '{old_username}' to '{github_username}'") + log.info(f"{author_id} has changed their github link from '{old_username}' to '{github_username}'") await ctx.send(f"{author_mention}, your GitHub username has been updated to: '{github_username}'") else: - logging.info(f"{author_id} has added a github link to '{github_username}'") + log.info(f"{author_id} has added a github link to '{github_username}'") await ctx.send(f"{author_mention}, your GitHub username has been added") await self.linked_accounts.set(author_id, github_username) else: - logging.info(f"{author_id} tried to link a GitHub account but didn't provide a username") + log.info(f"{author_id} tried to link a GitHub account but didn't provide a username") await ctx.send(f"{author_mention}, a GitHub username is required to link your account") @in_month(Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER) @@ -157,7 +157,7 @@ class HacktoberStats(commands.Cog): stats_embed = discord.Embed( title=f"{github_username}'s Hacktoberfest", - color=discord.Color(0x9c4af7), + color=0x9c4af7, description=( f"{github_username} has made {n} valid " f"{self._contributionator(n)} in " @@ -188,8 +188,7 @@ class HacktoberStats(commands.Cog): logging.info(f"Hacktoberfest PR built for GitHub user '{github_username}'") return stats_embed - @staticmethod - async def get_october_prs(github_username: str) -> Optional[List[dict]]: + async def get_october_prs(self, github_username: str) -> Optional[List[dict]]: """ Query GitHub's API for PRs created during the month of October by github_username. @@ -212,7 +211,7 @@ class HacktoberStats(commands.Cog): Otherwise, return empty list. None will be returned when the GitHub user was not found. """ - logging.info(f"Fetching Hacktoberfest Stats for GitHub user: '{github_username}'") + log.info(f"Fetching Hacktoberfest Stats for GitHub user: '{github_username}'") base_url = "https://api.github.com/search/issues?q=" action_type = "pr" is_query = "public" @@ -228,24 +227,24 @@ class HacktoberStats(commands.Cog): f"+created:{date_range}" f"&per_page={per_page}" ) - logging.debug(f"GitHub query URL generated: {query_url}") + log.logProcesses.debug(f"GitHub query URL generated: {query_url}") - jsonresp = await HacktoberStats._fetch_url(query_url, REQUEST_HEADERS) - if "message" in jsonresp.keys(): + jsonresp = await self._fetch_url(query_url, REQUEST_HEADERS) + if "message" in jsonresp: # One of the parameters is invalid, short circuit for now api_message = jsonresp["errors"][0]["message"] # Ignore logging non-existent users or users we do not have permission to see if api_message == GITHUB_NONEXISTENT_USER_MESSAGE: - logging.debug(f"No GitHub user found named '{github_username}'") + log.debug(f"No GitHub user found named '{github_username}'") return else: - logging.error(f"GitHub API request for '{github_username}' failed with message: {api_message}") + 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: # Short circuit if there aren't any PRs - logging.info(f"No October PRs found for GitHub user: '{github_username}'") + log.info(f"No October PRs found for GitHub user: '{github_username}'") return [] logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") @@ -253,20 +252,20 @@ class HacktoberStats(commands.Cog): oct3 = datetime(int(CURRENT_YEAR), 10, 3, 23, 59, 59, tzinfo=None) hackto_topics = {} # cache whether each repo has the appropriate topic (bool values) for item in jsonresp["items"]: - shortname = HacktoberStats._get_shortname(item["repository_url"]) + shortname = self._get_shortname(item["repository_url"]) itemdict = { "repo_url": f"https://www.github.com/{shortname}", "repo_shortname": shortname, "created_at": datetime.strptime( - item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" + item["created_at"], "%Y-%m-%dT%H:%M:%SZ" ), "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 HacktoberStats._has_label(item, ["invalid", "spam"]): - if not await HacktoberStats._is_accepted(itemdict): + if self._has_label(item, ["invalid", "spam"]): + if not await self._is_accepted(itemdict): continue # PRs before oct 3 no need to check for topics @@ -277,21 +276,20 @@ class HacktoberStats(commands.Cog): continue # Checking PR's labels for "hacktoberfest-accepted" - if HacktoberStats._has_label(item, "hacktoberfest-accepted"): + if self._has_label(item, "hacktoberfest-accepted"): outlist.append(itemdict) continue # No need to query GitHub if repo topics are fetched before already - if shortname in hackto_topics.keys(): - if hackto_topics[shortname]: - outlist.append(itemdict) - continue + if hackto_topics.get(shortname): + outlist.append(itemdict) + continue # Fetch topics for the PR's repo topics_query_url = f"https://api.github.com/repos/{shortname}/topics" - logging.debug(f"Fetching repo topics for {shortname} with url: {topics_query_url}") - jsonresp2 = await HacktoberStats._fetch_url(topics_query_url, GITHUB_TOPICS_ACCEPT_HEADER) + log.debug(f"Fetching repo topics for {shortname} with url: {topics_query_url}") + jsonresp2 = await self._fetch_url(topics_query_url, GITHUB_TOPICS_ACCEPT_HEADER) if jsonresp2.get("names") is None: - logging.error(f"Error fetching topics for {shortname}: {jsonresp2['message']}") + log.error(f"Error fetching topics for {shortname}: {jsonresp2['message']}") continue # Assume the repo doesn't have the `hacktoberfest` topic if API request errored # PRs after oct 3 that doesn't have 'hacktoberfest-accepted' label @@ -301,12 +299,10 @@ class HacktoberStats(commands.Cog): outlist.append(itemdict) return outlist - @staticmethod - async def _fetch_url(url: str, headers: dict) -> dict: + async def _fetch_url(self, url: str, headers: dict) -> dict: """Retrieve API response from URL.""" - async with aiohttp.ClientSession() as session: - async with session.get(url, headers=headers) as resp: - jsonresp = await resp.json() + async with self.bot.http_session.get(url, headers=headers) as resp: + jsonresp = await resp.json() return jsonresp @staticmethod @@ -319,40 +315,36 @@ class HacktoberStats(commands.Cog): """ if not pr.get("labels"): # if PR has no labels return False - if (isinstance(labels, str)) and (any(label["name"].casefold() == labels for label in pr["labels"])): + if isinstance(labels, str) and any(label["name"].casefold() == labels for label in pr["labels"]): return True for item in labels: if any(label["name"].casefold() == item for label in pr["labels"]): return True return False - @staticmethod - async def _is_accepted(pr: dict) -> bool: + async def _is_accepted(self, pr: dict) -> bool: """Check if a PR is merged, approved, or labelled hacktoberfest-accepted.""" # checking for merge status - query_url = f"https://api.github.com/repos/{pr['repo_shortname']}/pulls/" - query_url += str(pr["number"]) - jsonresp = await HacktoberStats._fetch_url(query_url, REQUEST_HEADERS) - - if "message" in jsonresp.keys(): - logging.error( - f"Error fetching PR stats for #{pr['number']} in repo {pr['repo_shortname']}:\n" - f"{jsonresp['message']}" - ) + query_url = f"https://api.github.com/repos/{pr['repo_shortname']}/pulls/{pr['number']}" + jsonresp = await self._fetch_url(query_url, REQUEST_HEADERS) + + if message := jsonresp.get("message"): + log.error(f"Error fetching PR stats for #{pr['number']} in repo {pr['repo_shortname']}:\n{message}") return False - if ("merged" in jsonresp.keys()) and jsonresp["merged"]: + + if jsonresp.get("merged"): return True # checking for the label, using `jsonresp` which has the label information - if HacktoberStats._has_label(jsonresp, "hacktoberfest-accepted"): + if self._has_label(jsonresp, "hacktoberfest-accepted"): return True # checking approval query_url += "/reviews" - jsonresp2 = await HacktoberStats._fetch_url(query_url, REQUEST_HEADERS) + jsonresp2 = await self._fetch_url(query_url, REQUEST_HEADERS) if isinstance(jsonresp2, dict): # if API request is unsuccessful it will be a dict with the error in 'message' - logging.error( + log.error( f"Error fetching PR reviews for #{pr['number']} in repo {pr['repo_shortname']}:\n" f"{jsonresp2['message']}" ) @@ -363,9 +355,8 @@ class HacktoberStats(commands.Cog): # loop through reviews and check for approval for item in jsonresp2: - if "status" in item.keys(): - if item['status'] == "APPROVED": - return True + if item.get('status') == "APPROVED": + return True return False @staticmethod @@ -381,8 +372,7 @@ class HacktoberStats(commands.Cog): exp = r"https?:\/\/api.github.com\/repos\/([/\-\_\.\w]+)" return re.findall(exp, in_url)[0] - @staticmethod - async def _categorize_prs(prs: List[dict]) -> tuple: + async def _categorize_prs(self, prs: List[dict]) -> tuple: """ Categorize PRs into 'in_review' and 'accepted' and returns as a tuple. @@ -399,7 +389,7 @@ class HacktoberStats(commands.Cog): for pr in prs: if (pr['created_at'] + timedelta(REVIEW_DAYS)) > now: in_review.append(pr) - elif (pr['created_at'] <= oct3) or await HacktoberStats._is_accepted(pr): + elif (pr['created_at'] <= oct3) or await self._is_accepted(pr): accepted.append(pr) return in_review, accepted @@ -438,14 +428,14 @@ class HacktoberStats(commands.Cog): return "contributions" @staticmethod - def _author_mention_from_context(ctx: commands.Context) -> Tuple: + def _author_mention_from_context(ctx: commands.Context) -> Tuple[str, str]: """Return stringified Message author ID and mentionable string from commands.Context.""" - author_id = str(ctx.message.author.id) - author_mention = ctx.message.author.mention + author_id = str(ctx.author.id) + author_mention = ctx.author.mention return author_id, author_mention -def setup(bot: commands.Bot) -> None: - """Hacktoberstats Cog load.""" +def setup(bot: Bot) -> None: + """Load the Hacktober Stats Cog.""" bot.add_cog(HacktoberStats(bot)) diff --git a/bot/exts/halloween/halloween_facts.py b/bot/exts/halloween/halloween_facts.py index 7eb6d56f..139e0810 100644 --- a/bot/exts/halloween/halloween_facts.py +++ b/bot/exts/halloween/halloween_facts.py @@ -8,6 +8,8 @@ from typing import Tuple import discord from discord.ext import commands +from bot.bot import Bot + log = logging.getLogger(__name__) SPOOKY_EMOJIS = [ @@ -20,16 +22,15 @@ SPOOKY_EMOJIS = [ "\N{SKULL AND CROSSBONES}", "\N{SPIDER WEB}", ] -PUMPKIN_ORANGE = discord.Color(0xFF7518) +PUMPKIN_ORANGE = 0xFF7518 INTERVAL = timedelta(hours=6).total_seconds() class HalloweenFacts(commands.Cog): """A Cog for displaying interesting facts about Halloween.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - with open(Path("bot/resources/halloween/halloween_facts.json"), "r", encoding="utf8") as file: + def __init__(self): + with Path("bot/resources/halloween/halloween_facts.json").open("r", encoding="utf8") as file: self.halloween_facts = json.load(file) self.facts = list(enumerate(self.halloween_facts)) random.shuffle(self.facts) @@ -53,6 +54,6 @@ class HalloweenFacts(commands.Cog): return discord.Embed(title=title, description=fact, color=PUMPKIN_ORANGE) -def setup(bot: commands.Bot) -> None: - """Halloween facts Cog load.""" - bot.add_cog(HalloweenFacts(bot)) +def setup(bot: Bot) -> None: + """Load the Halloween Facts Cog.""" + bot.add_cog(HalloweenFacts()) diff --git a/bot/exts/halloween/halloweenify.py b/bot/exts/halloween/halloweenify.py index 596c6682..5a8f4ecc 100644 --- a/bot/exts/halloween/halloweenify.py +++ b/bot/exts/halloween/halloweenify.py @@ -6,7 +6,9 @@ from random import choice import discord from discord.errors import Forbidden from discord.ext import commands -from discord.ext.commands.cooldowns import BucketType +from discord.ext.commands import BucketType + +from bot.bot import Bot log = logging.getLogger(__name__) @@ -14,9 +16,6 @@ log = logging.getLogger(__name__) class Halloweenify(commands.Cog): """A cog to change a invokers nickname to a spooky one!""" - def __init__(self, bot: commands.Bot): - self.bot = bot - @commands.cooldown(1, 300, BucketType.user) @commands.command() async def halloweenify(self, ctx: commands.Context) -> None: @@ -61,6 +60,6 @@ class Halloweenify(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: - """Halloweenify Cog load.""" - bot.add_cog(Halloweenify(bot)) +def setup(bot: Bot) -> None: + """Load the Halloweenify Cog.""" + bot.add_cog(Halloweenify()) diff --git a/bot/exts/halloween/monsterbio.py b/bot/exts/halloween/monsterbio.py index 016a66d1..f484305d 100644 --- a/bot/exts/halloween/monsterbio.py +++ b/bot/exts/halloween/monsterbio.py @@ -6,6 +6,7 @@ from pathlib import Path import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours log = logging.getLogger(__name__) @@ -17,9 +18,6 @@ with open(Path("bot/resources/halloween/monster.json"), "r", encoding="utf8") as class MonsterBio(commands.Cog): """A cog that generates a spooky monster biography.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - def generate_name(self, seeded_random: random.Random) -> str: """Generates a name (for either monster species or monster name).""" n_candidate_strings = seeded_random.randint(2, len(TEXT_OPTIONS["monster_type"])) @@ -50,6 +48,6 @@ class MonsterBio(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: - """Monster bio Cog load.""" - bot.add_cog(MonsterBio(bot)) +def setup(bot: Bot) -> None: + """Load the Monster Bio Cog.""" + bot.add_cog(MonsterBio()) diff --git a/bot/exts/halloween/monstersurvey.py b/bot/exts/halloween/monstersurvey.py index 80196825..0610503d 100644 --- a/bot/exts/halloween/monstersurvey.py +++ b/bot/exts/halloween/monstersurvey.py @@ -23,9 +23,8 @@ class MonsterSurvey(Cog): Users may change their vote, but only their current vote will be counted. """ - def __init__(self, bot: Bot): + def __init__(self): """Initializes values for the bot to use within the voting commands.""" - self.bot = bot self.registry_location = os.path.join(os.getcwd(), 'bot', 'resources', 'halloween', 'monstersurvey.json') with open(self.registry_location, 'r', encoding="utf8") as jason: self.voter_registry = json.load(jason) @@ -201,4 +200,5 @@ class MonsterSurvey(Cog): def setup(bot: Bot) -> None: - """Monster survey Cog load.""" + """Load the Monster Survey Cog.""" + bot.add_cog(MonsterSurvey()) diff --git a/bot/exts/halloween/scarymovie.py b/bot/exts/halloween/scarymovie.py index 0807eca6..48c9f53d 100644 --- a/bot/exts/halloween/scarymovie.py +++ b/bot/exts/halloween/scarymovie.py @@ -2,24 +2,25 @@ import logging import random from os import environ -import aiohttp from discord import Embed from discord.ext import commands +from bot.bot import Bot + log = logging.getLogger(__name__) -TMDB_API_KEY = environ.get('TMDB_API_KEY') -TMDB_TOKEN = environ.get('TMDB_TOKEN') +TMDB_API_KEY = environ.get("TMDB_API_KEY") +TMDB_TOKEN = environ.get("TMDB_TOKEN") class ScaryMovie(commands.Cog): """Selects a random scary movie and embeds info into Discord chat.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot - @commands.command(name='scarymovie', alias=['smovie']) + @commands.command(name="scarymovie", alias=["smovie"]) async def random_movie(self, ctx: commands.Context) -> None: """Randomly select a scary movie and display information about it.""" async with ctx.typing(): @@ -28,36 +29,34 @@ class ScaryMovie(commands.Cog): await ctx.send(embed=movie_details) - @staticmethod - async def select_movie() -> dict: + async def select_movie(self) -> dict: """Selects a random movie and returns a JSON of movie details from TMDb.""" - url = 'https://api.themoviedb.org/4/discover/movie' + url = "https://api.themoviedb.org/4/discover/movie" params = { - 'with_genres': '27', - 'vote_count.gte': '5' + "with_genres": "27", + "vote_count.gte": "5" } headers = { - 'Authorization': 'Bearer ' + TMDB_TOKEN, - 'Content-Type': 'application/json;charset=utf-8' + "Authorization": "Bearer " + TMDB_TOKEN, + "Content-Type": "application/json;charset=utf-8" } # Get total page count of horror movies - async with aiohttp.ClientSession() as session: - response = await session.get(url=url, params=params, headers=headers) - total_pages = await response.json() - total_pages = total_pages.get('total_pages') - - # Get movie details from one random result on a random page - params['page'] = random.randint(1, total_pages) - response = await session.get(url=url, params=params, headers=headers) - response = await response.json() - selection_id = random.choice(response.get('results')).get('id') - - # Get full details and credits - selection = await session.get( - url='https://api.themoviedb.org/3/movie/' + str(selection_id), - params={'api_key': TMDB_API_KEY, 'append_to_response': 'credits'} - ) + async with self.bot.http_session.get(url=url, params=params, headers=headers) as response: + data = await response.json() + total_pages = data.get("total_pages") + + # Get movie details from one random result on a random page + params['page'] = random.randint(1, total_pages) + async with self.bot.http_session.get(url=url, params=params, headers=headers) as response: + data = await response.json() + selection_id = random.choice(data.get("results")).get("id") + + # Get full details and credits + async with self.bot.http_session.get( + url=f"https://api.themoviedb.org/3/movie/{selection_id}", + params={"api_key": TMDB_API_KEY, "append_to_response": "credits"} + ) as selection: return await selection.json() @@ -67,8 +66,8 @@ class ScaryMovie(commands.Cog): # Build the relevant URLs. movie_id = movie.get("id") poster_path = movie.get("poster_path") - tmdb_url = f'https://www.themoviedb.org/movie/{movie_id}' if movie_id else None - poster = f'https://image.tmdb.org/t/p/original{poster_path}' if poster_path else None + tmdb_url = f"https://www.themoviedb.org/movie/{movie_id}" if movie_id else None + poster = f"https://image.tmdb.org/t/p/original{poster_path}" if poster_path else None # Get cast names cast = [] @@ -82,10 +81,7 @@ class ScaryMovie(commands.Cog): # Determine the spookiness rating rating = '' - rating_count = movie.get('vote_average', 0) - - if rating_count: - rating_count /= 2 + rating_count = movie.get('vote_average', 0) / 2 for _ in range(int(rating_count)): rating += ':skull:' @@ -100,7 +96,7 @@ class ScaryMovie(commands.Cog): # Not all these attributes will always be present movie_attributes = { "Directed by": director, - "Starring": ', '.join(cast), + "Starring": ", ".join(cast), "Running time": runtime, "Release year": year, "Spookiness rating": rating, @@ -108,9 +104,9 @@ class ScaryMovie(commands.Cog): embed = Embed( colour=0x01d277, - title='**' + movie.get('title') + '**', + title=f"**{movie.get('title')}**", url=tmdb_url, - description=movie.get('overview') + description=movie.get("overview") ) if poster: @@ -127,6 +123,6 @@ class ScaryMovie(commands.Cog): return embed -def setup(bot: commands.Bot) -> None: - """Scary movie Cog load.""" +def setup(bot: Bot) -> None: + """Load the Scary Movie Cog.""" bot.add_cog(ScaryMovie(bot)) diff --git a/bot/exts/halloween/spookygif.py b/bot/exts/halloween/spookygif.py index f402437f..bfdf2128 100644 --- a/bot/exts/halloween/spookygif.py +++ b/bot/exts/halloween/spookygif.py @@ -1,9 +1,9 @@ import logging -import aiohttp import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Tokens log = logging.getLogger(__name__) @@ -12,27 +12,26 @@ log = logging.getLogger(__name__) class SpookyGif(commands.Cog): """A cog to fetch a random spooky gif from the web!""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @commands.command(name="spookygif", aliases=("sgif", "scarygif")) async def spookygif(self, ctx: commands.Context) -> None: """Fetches a random gif from the GIPHY API and responds with it.""" async with ctx.typing(): - async with aiohttp.ClientSession() as session: - params = {'api_key': Tokens.giphy, 'tag': 'halloween', 'rating': 'g'} - # Make a GET request to the Giphy API to get a random halloween gif. - async with session.get('http://api.giphy.com/v1/gifs/random', params=params) as resp: - data = await resp.json() - url = data['data']['image_url'] + params = {'api_key': Tokens.giphy, 'tag': 'halloween', 'rating': 'g'} + # Make a GET request to the Giphy API to get a random halloween gif. + async with self.bot.http_session.get('http://api.giphy.com/v1/gifs/random', params=params) as resp: + data = await resp.json() + url = data['data']['image_url'] - embed = discord.Embed(colour=0x9b59b6) - embed.title = "A spooooky gif!" - embed.set_image(url=url) + embed = discord.Embed(colour=0x9b59b6) + embed.title = "A spooooky gif!" + embed.set_image(url=url) await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Spooky GIF Cog load.""" bot.add_cog(SpookyGif(bot)) diff --git a/bot/exts/halloween/spookynamerate.py b/bot/exts/halloween/spookynamerate.py index e2950343..9191f5f6 100644 --- a/bot/exts/halloween/spookynamerate.py +++ b/bot/exts/halloween/spookynamerate.py @@ -12,8 +12,9 @@ from async_rediscache import RedisCache from discord import Embed, Reaction, TextChannel, User from discord.colour import Colour from discord.ext import tasks -from discord.ext.commands import Bot, Cog, Context, group +from discord.ext.commands import Cog, Context, group +from bot.bot import Bot from bot.constants import Channels, Client, Colours, Month from bot.utils.decorators import InMonthCheckFailure @@ -34,7 +35,7 @@ ADDED_MESSAGES = [ ] PING = "<@{id}>" -EMOJI_MESSAGE = "\n".join([f"- {emoji} {val}" for emoji, val in EMOJIS_VAL.items()]) +EMOJI_MESSAGE = "\n".join(f"- {emoji} {val}" for emoji, val in EMOJIS_VAL.items()) HELP_MESSAGE_DICT = { "title": "Spooky Name Rate", "description": f"Help for the `{Client.prefix}spookynamerate` command", @@ -137,14 +138,12 @@ class SpookyNameRate(Cog): async def add_name(self, ctx: Context, *, name: str) -> None: """Use this command to add/register your spookified name.""" if self.poll: - logger.info(f"{ctx.message.author} tried to add a name, but the poll had already started.") + logger.info(f"{ctx.author} tried to add a name, but the poll had already started.") await ctx.send("Sorry, the poll has started! You can try and participate in the next round though!") return - message = ctx.message - for data in (json.loads(user_data) for _, user_data in await self.messages.items()): - if data["author"] == message.author.id: + if data["author"] == ctx.author.id: await ctx.send( "But you have already added an entry! Type " f"`{self.bot.command_prefix}spookynamerate " @@ -156,14 +155,14 @@ class SpookyNameRate(Cog): await ctx.send("TOO LATE. Someone has already added this name.") return - msg = await (await self.get_channel()).send(f"{message.author.mention} added the name {name!r}!") + msg = await (await self.get_channel()).send(f"{ctx.author.mention} added the name {name!r}!") await self.messages.set( msg.id, json.dumps( { "name": name, - "author": message.author.id, + "author": ctx.author.id, "score": 0, } ), @@ -172,7 +171,7 @@ class SpookyNameRate(Cog): for emoji in EMOJIS_VAL: await msg.add_reaction(emoji) - logger.info(f"{message.author} added the name {name!r}") + logger.info(f"{ctx.author} added the name {name!r}") @spooky_name_rate.command(name="delete") async def delete_name(self, ctx: Context) -> None: @@ -185,7 +184,7 @@ class SpookyNameRate(Cog): if ctx.author.id == data["author"]: await self.messages.delete(message_id) - await ctx.send(f'Name deleted successfully ({data["name"]!r})!') + await ctx.send(f"Name deleted successfully ({data['name']!r})!") return await ctx.send( @@ -397,5 +396,5 @@ class SpookyNameRate(Cog): def setup(bot: Bot) -> None: - """Loads the SpookyNameRate Cog.""" + """Load the SpookyNameRate Cog.""" bot.add_cog(SpookyNameRate(bot)) diff --git a/bot/exts/halloween/spookyrating.py b/bot/exts/halloween/spookyrating.py index 6f069f8c..dc398e2e 100644 --- a/bot/exts/halloween/spookyrating.py +++ b/bot/exts/halloween/spookyrating.py @@ -7,20 +7,20 @@ from pathlib import Path import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours log = logging.getLogger(__name__) with Path("bot/resources/halloween/spooky_rating.json").open(encoding="utf8") as file: - SPOOKY_DATA = json.load(file) - SPOOKY_DATA = sorted((int(key), value) for key, value in SPOOKY_DATA.items()) + data = json.load(file) + SPOOKY_DATA = sorted((int(key), value) for key, value in data.items()) class SpookyRating(commands.Cog): """A cog for calculating one's spooky rating.""" - def __init__(self, bot: commands.Bot): - self.bot = bot + def __init__(self): self.local_random = random.Random() @commands.command() @@ -61,6 +61,6 @@ class SpookyRating(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: - """Spooky Rating Cog load.""" - bot.add_cog(SpookyRating(bot)) +def setup(bot: Bot) -> None: + """Load the Spooky Rating Cog.""" + bot.add_cog(SpookyRating()) diff --git a/bot/exts/halloween/spookyreact.py b/bot/exts/halloween/spookyreact.py index b335df75..dabc3c1f 100644 --- a/bot/exts/halloween/spookyreact.py +++ b/bot/exts/halloween/spookyreact.py @@ -2,8 +2,9 @@ import logging import re import discord -from discord.ext.commands import Bot, Cog +from discord.ext.commands import Cog +from bot.bot import Bot from bot.constants import Month from bot.utils.decorators import in_month @@ -28,20 +29,20 @@ class SpookyReact(Cog): @in_month(Month.OCTOBER) @Cog.listener() - async def on_message(self, ctx: discord.Message) -> None: + async def on_message(self, message: discord.Message) -> None: """Triggered when the bot sees a message in October.""" - for trigger in SPOOKY_TRIGGERS.keys(): - trigger_test = re.search(SPOOKY_TRIGGERS[trigger][0], ctx.content.lower()) + for name, trigger in SPOOKY_TRIGGERS.items(): + trigger_test = re.search(trigger[0], message.content.lower()) if trigger_test: # Check message for bot replies and/or command invocations # Short circuit if they're found, logging is handled in _short_circuit_check - if await self._short_circuit_check(ctx): + if await self._short_circuit_check(message): return else: - await ctx.add_reaction(SPOOKY_TRIGGERS[trigger][1]) - logging.info(f"Added '{trigger}' reaction to message ID: {ctx.id}") + await message.add_reaction(trigger[1]) + log.info(f"Added {name!r} reaction to message ID: {message.id}") - async def _short_circuit_check(self, ctx: discord.Message) -> bool: + async def _short_circuit_check(self, message: discord.Message) -> bool: """ Short-circuit helper check. @@ -50,20 +51,20 @@ class SpookyReact(Cog): * prefix is not None """ # Check for self reaction - if ctx.author == self.bot.user: - logging.debug(f"Ignoring reactions on self message. Message ID: {ctx.id}") + if message.author == self.bot.user: + log.debug(f"Ignoring reactions on self message. Message ID: {message.id}") return True # Check for command invocation # Because on_message doesn't give a full Context object, generate one first - tmp_ctx = await self.bot.get_context(ctx) - if tmp_ctx.prefix: - logging.debug(f"Ignoring reactions on command invocation. Message ID: {ctx.id}") + ctx = await self.bot.get_context(message) + if ctx.prefix: + log.debug(f"Ignoring reactions on command invocation. Message ID: {message.id}") return True return False def setup(bot: Bot) -> None: - """Spooky reaction Cog load.""" + """Load the Spooky Reaction Cog.""" bot.add_cog(SpookyReact(bot)) diff --git a/bot/exts/halloween/timeleft.py b/bot/exts/halloween/timeleft.py index 47adb09b..f4ab9284 100644 --- a/bot/exts/halloween/timeleft.py +++ b/bot/exts/halloween/timeleft.py @@ -4,13 +4,15 @@ from typing import Tuple from discord.ext import commands +from bot.bot import Bot + log = logging.getLogger(__name__) class TimeLeft(commands.Cog): """A Cog that tells you how long left until Hacktober is over!""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot def in_hacktober(self) -> bool: @@ -64,6 +66,6 @@ class TimeLeft(commands.Cog): ) -def setup(bot: commands.Bot) -> None: - """Cog load.""" +def setup(bot: Bot) -> None: + """Load the Time Left Cog.""" bot.add_cog(TimeLeft(bot)) -- cgit v1.2.3 From c1658ed8a37532bcdcbc6567e35a4ecb7578b237 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 20 Apr 2021 13:42:20 +0530 Subject: Fix Bug:Paginated embed image gets carried to next page --- bot/utils/pagination.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/utils/pagination.py b/bot/utils/pagination.py index a4d0cc56..917275c0 100644 --- a/bot/utils/pagination.py +++ b/bot/utils/pagination.py @@ -4,6 +4,7 @@ from typing import Iterable, List, Optional, Tuple from discord import Embed, Member, Reaction from discord.abc import User +from discord.embeds import EmptyEmbed from discord.ext.commands import Context, Paginator from bot.constants import Emojis @@ -417,9 +418,8 @@ class ImagePaginator(Paginator): await message.edit(embed=embed) embed.description = paginator.pages[current_page] - image = paginator.images[current_page] - if image: - embed.set_image(url=image) + image = paginator.images[current_page] or EmptyEmbed + embed.set_image(url=image) embed.set_footer(text=f"Page {current_page + 1}/{len(paginator.pages)}") log.debug(f"Got {reaction_type} page reaction - changing to page {current_page + 1}/{len(paginator.pages)}") -- cgit v1.2.3 From 8662541fc08037558f68e15dcc3fd72ef491dced Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Tue, 20 Apr 2021 17:01:02 +0100 Subject: chore: ctx.channel.send -> ctx.send --- bot/exts/evergreen/snakes/_snakes_cog.py | 22 +++++++++++----------- bot/exts/valentines/myvalenstate.py | 2 +- bot/exts/valentines/whoisvalentine.py | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index 3732b559..70093912 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -423,7 +423,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 @@ -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,7 +737,7 @@ 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') async def movie_command(self, ctx: Context) -> None: @@ -800,9 +800,9 @@ 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') @@ -828,7 +828,7 @@ 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',)) @@ -964,7 +964,7 @@ class Snakes(Cog): ) ) - await ctx.channel.send(embed=embed) + await ctx.send(embed=embed) @snakes_group.command(name='card') async def card_command(self, ctx: Context, *, name: Snake = None) -> None: @@ -1018,7 +1018,7 @@ class Snakes(Cog): color=SNAKE_COLOR, description=question ) - await ctx.channel.send(embed=embed) + await ctx.send(embed=embed) @snakes_group.command(name='snakify') async def snakify_command(self, ctx: Context, *, message: str = None) -> None: @@ -1059,7 +1059,7 @@ 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',)) async def video_command(self, ctx: Context, *, search: str = None) -> None: @@ -1095,7 +1095,7 @@ class Snakes(Cog): if len(data) > 0: num = random.randint(0, len(data) - 1) youtube_base_url = 'https://www.youtube.com/watch?v=' - await ctx.channel.send( + await ctx.send( content=f"{youtube_base_url}{data[num]['id']['videoId']}" ) else: @@ -1120,7 +1120,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/valentines/myvalenstate.py b/bot/exts/valentines/myvalenstate.py index 01801847..041af7af 100644 --- a/bot/exts/valentines/myvalenstate.py +++ b/bot/exts/valentines/myvalenstate.py @@ -78,7 +78,7 @@ class MyValenstate(commands.Cog): ) embed.add_field(name=embed_title, value=embed_text) embed.set_image(url=STATES[valenstate]["flag"]) - await ctx.channel.send(embed=embed) + await ctx.send(embed=embed) def setup(bot: commands.Bot) -> None: diff --git a/bot/exts/valentines/whoisvalentine.py b/bot/exts/valentines/whoisvalentine.py index 0ff9186c..ab6b1ca1 100644 --- a/bot/exts/valentines/whoisvalentine.py +++ b/bot/exts/valentines/whoisvalentine.py @@ -33,7 +33,7 @@ class ValentineFacts(commands.Cog): 'facial_reconstruction.jpg/1024px-Saint_Valentine_-_facial_reconstruction.jpg' ) - await ctx.channel.send(embed=embed) + await ctx.send(embed=embed) @commands.command() async def valentine_fact(self, ctx: commands.Context) -> None: @@ -44,7 +44,7 @@ class ValentineFacts(commands.Cog): color=Colours.pink ) - await ctx.channel.send(embed=embed) + await ctx.send(embed=embed) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From 9524620ddbb7d3e707258e1ba3b84b23b5e3b54a Mon Sep 17 00:00:00 2001 From: Dillon Runke <44979306+Kronifer@users.noreply.github.com> Date: Tue, 20 Apr 2021 11:12:17 -0500 Subject: Add Catify command (#694) Co-authored-by: Joe Banks Co-authored-by: hypergamer80 <79152594+hypergamer80@users.noreply.github.com> --- bot/constants.py | 5 +++ bot/exts/evergreen/catify.py | 78 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 bot/exts/evergreen/catify.py diff --git a/bot/constants.py b/bot/constants.py index a64882db..bcbdcba0 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -8,6 +8,7 @@ from typing import Dict, NamedTuple __all__ = ( "AdventOfCode", "Branding", + "Cats", "Channels", "Categories", "Client", @@ -93,6 +94,10 @@ class Branding: cycle_frequency = int(environ.get("CYCLE_FREQUENCY", 3)) # 0: never, 1: every day, 2: every other day, ... +class Cats: + cats = ["ᓚᘏᗢ", "ᘡᘏᗢ", "🐈", "ᓕᘏᗢ", "ᓇᘏᗢ", "ᓂᘏᗢ", "ᘣᘏᗢ", "ᕦᘏᗢ", "ᕂᘏᗢ"] + + class Channels(NamedTuple): advent_of_code = int(environ.get("AOC_CHANNEL_ID", 782715290437943306)) advent_of_code_commands = int(environ.get("AOC_COMMANDS_CHANNEL_ID", 607247579608121354)) diff --git a/bot/exts/evergreen/catify.py b/bot/exts/evergreen/catify.py new file mode 100644 index 00000000..a0121403 --- /dev/null +++ b/bot/exts/evergreen/catify.py @@ -0,0 +1,78 @@ +import random +from typing import Optional + +from discord import AllowedMentions, Embed +from discord.ext import commands + +from bot.constants import Cats, Colours, NEGATIVE_REPLIES + + +class Catify(commands.Cog): + """Cog for the catify command.""" + + def __init__(self, bot: commands.Bot): + self.bot = bot + + @commands.command(aliases=["ᓚᘏᗢify", "ᓚᘏᗢ"]) + async def catify(self, ctx: commands.Context, *, text: Optional[str]) -> None: + """ + Convert the provided text into a cat themed sentence by interspercing cats throughout text. + + If no text is given then the users nickname is edited. + """ + if not text: + display_name = ctx.author.display_name + + if len(display_name) > 26: + embed = Embed( + title=random.choice(NEGATIVE_REPLIES), + description="Your nickname is too long to be catified! Please change it to be under 26 characters.", + color=Colours.soft_red + ) + await ctx.send(embed=embed) + return + + else: + display_name += f" | {random.choice(Cats.cats)}" + await ctx.send(f"Your catified username is: `{display_name}`") + await ctx.author.edit(nick=display_name) + else: + if len(text) >= 1500: + embed = Embed( + title=random.choice(NEGATIVE_REPLIES), + description="Submitted text was too large! Please submit something under 1500 characters.", + color=Colours.soft_red + ) + await ctx.send(embed=embed) + return + + string_list = text.split() + for index, name in enumerate(string_list): + if "cat" in name: + if random.randint(0, 5) == 5: + string_list[index] = string_list[index].replace("cat", f"**{random.choice(Cats.cats)}**") + else: + string_list[index] = string_list[index].replace("cat", random.choice(Cats.cats)) + for element in Cats.cats: + if element in name: + string_list[index] = string_list[index].replace(element, "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)}**") + else: + string_list.insert(random.randint(0, len(string_list)), random.choice(Cats.cats)) + + text = " ".join(string_list) + await ctx.send( + f">>> {text}", + allowed_mentions=AllowedMentions.none() + ) + + +def setup(bot: commands.Bot) -> None: + """Loads the catify cog.""" + bot.add_cog(Catify(bot)) -- cgit v1.2.3 From 97048ce3634875e875f4a66e786b49d39bfdb17f Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Tue, 20 Apr 2021 12:22:41 -0400 Subject: Clean Up Christmas Season --- bot/exts/christmas/advent_of_code/_cog.py | 4 ++-- bot/exts/christmas/hanukkah_embed.py | 16 +++++++++------- bot/exts/pride/drag_queen_name.py | 9 +++++---- bot/exts/pride/pride_anthem.py | 14 ++++++++------ bot/exts/pride/pride_facts.py | 18 ++++++++---------- 5 files changed, 32 insertions(+), 29 deletions(-) diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index 8376987d..da1cf28d 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -124,7 +124,7 @@ class AdventOfCode(commands.Cog): @whitelist_override(channels=AOC_WHITELIST) async def about_aoc(self, ctx: commands.Context) -> None: """Respond with an explanation of all things Advent of Code.""" - await ctx.send("", embed=self.cached_about_aoc) + await ctx.send(embed=self.cached_about_aoc) @adventofcode_group.command(name="join", aliases=("j",), brief="Learn how to join the leaderboard (via DM)") @whitelist_override(channels=AOC_WHITELIST) @@ -135,7 +135,7 @@ class AdventOfCode(commands.Cog): await ctx.send(f"The Python Discord leaderboard for {current_year} is not yet available!") return - author = ctx.message.author + author = ctx.author log.info(f"{author.name} ({author.id}) has requested a PyDis AoC leaderboard code") if AocConfig.staff_leaderboard_id and any(r.id == Roles.helpers for r in author.roles): diff --git a/bot/exts/christmas/hanukkah_embed.py b/bot/exts/christmas/hanukkah_embed.py index 4f470a34..cd8a9192 100644 --- a/bot/exts/christmas/hanukkah_embed.py +++ b/bot/exts/christmas/hanukkah_embed.py @@ -5,6 +5,7 @@ from typing import List from discord import Embed from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours, Month from bot.utils.decorators import in_month @@ -14,7 +15,7 @@ log = logging.getLogger(__name__) class HanukkahEmbed(commands.Cog): """A cog that returns information about Hanukkah festival.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot self.url = ("https://www.hebcal.com/hebcal/?v=1&cfg=json&maj=on&min=on&mod=on&nx=on&" "year=now&month=x&ss=on&mf=on&c=on&geo=geoname&geonameid=3448439&m=50&s=on") @@ -60,17 +61,18 @@ class HanukkahEmbed(commands.Cog): if day in self.hanukkah_days and month in self.hanukkah_months and year in self.hanukkah_years: if int(day) == hanukkah_start_day: now = datetime.datetime.utcnow() - now = str(now) - hours = int(now[11:13]) + 4 # using only hours + hours = now.hour + 4 # using only hours hanukkah_start_hour = 18 if hours < hanukkah_start_hour: embed.description = (f"Hanukkah hasnt started yet, " f"it will start in about {hanukkah_start_hour-hours} hour/s.") - return await ctx.send(embed=embed) + await ctx.send(embed=embed) + return elif hours > hanukkah_start_hour: embed.description = (f'It is the starting day of Hanukkah ! ' f'Its been {hours-hanukkah_start_hour} hours hanukkah started !') - return await ctx.send(embed=embed) + await ctx.send(embed=embed) + return festival_day = self.hanukkah_days.index(day) number_suffixes = ['st', 'nd', 'rd', 'th'] suffix = '' @@ -108,6 +110,6 @@ class HanukkahEmbed(commands.Cog): self.hanukkah_years.append(date[0:4]) -def setup(bot: commands.Bot) -> None: - """Cog load.""" +def setup(bot: Bot) -> None: + """Load the Hanukkah Embed Cog.""" bot.add_cog(HanukkahEmbed(bot)) diff --git a/bot/exts/pride/drag_queen_name.py b/bot/exts/pride/drag_queen_name.py index fca9750f..32ead1bc 100644 --- a/bot/exts/pride/drag_queen_name.py +++ b/bot/exts/pride/drag_queen_name.py @@ -5,14 +5,15 @@ from pathlib import Path from discord.ext import commands +from bot.bot import Bot + log = logging.getLogger(__name__) class DragNames(commands.Cog): """Gives a random drag queen name!""" - def __init__(self, bot: commands.Bot): - self.bot = bot + def __init__(self): self.names = self.load_names() @staticmethod @@ -27,6 +28,6 @@ class DragNames(commands.Cog): await ctx.send(random.choice(self.names)) -def setup(bot: commands.Bot) -> None: - """Cog loader for drag queen name generator.""" +def setup(bot: Bot) -> None: + """Load the Drag Queen Cog.""" bot.add_cog(DragNames(bot)) diff --git a/bot/exts/pride/pride_anthem.py b/bot/exts/pride/pride_anthem.py index 33cb2a9d..a7f8d7ef 100644 --- a/bot/exts/pride/pride_anthem.py +++ b/bot/exts/pride/pride_anthem.py @@ -2,20 +2,22 @@ import json import logging import random from pathlib import Path +from typing import Optional from discord.ext import commands +from bot.bot import Bot + log = logging.getLogger(__name__) class PrideAnthem(commands.Cog): """Embed a random youtube video for a gay anthem!""" - def __init__(self, bot: commands.Bot): - self.bot = bot + def __init__(self): self.anthems = self.load_vids() - def get_video(self, genre: str = None) -> dict: + def get_video(self, genre: Optional[str] = None) -> dict: """ Picks a random anthem from the list. @@ -52,6 +54,6 @@ class PrideAnthem(commands.Cog): await ctx.send("I couldn't find a video, sorry!") -def setup(bot: commands.Bot) -> None: - """Cog loader for pride anthem.""" - bot.add_cog(PrideAnthem(bot)) +def setup(bot: Bot) -> None: + """Load the Pride Anthem Cog.""" + bot.add_cog(PrideAnthem()) diff --git a/bot/exts/pride/pride_facts.py b/bot/exts/pride/pride_facts.py index 5bd5d0ce..b2daaab7 100644 --- a/bot/exts/pride/pride_facts.py +++ b/bot/exts/pride/pride_facts.py @@ -15,8 +15,6 @@ from bot.utils.decorators import seasonal_task log = logging.getLogger(__name__) -Sendable = Union[commands.Context, discord.TextChannel] - class PrideFacts(commands.Cog): """Provides a new fact every day during the Pride season!""" @@ -44,7 +42,7 @@ class PrideFacts(commands.Cog): async def send_random_fact(self, ctx: commands.Context) -> None: """Provides a fact from any previous day, or today.""" now = datetime.utcnow() - previous_years_facts = (self.facts[x] for x in self.facts.keys() if int(x) < now.year) + previous_years_facts = (y for x, y in self.facts.items() if int(x) < now.year) current_year_facts = self.facts.get(str(now.year), [])[:now.day] previous_facts = current_year_facts + [x for y in previous_years_facts for x in y] try: @@ -52,7 +50,7 @@ class PrideFacts(commands.Cog): except IndexError: await ctx.send("No facts available") - async def send_select_fact(self, target: Sendable, _date: Union[str, datetime]) -> None: + async def send_select_fact(self, target: discord.abc.Messageable, _date: Union[str, datetime]) -> None: """Provides the fact for the specified day, if the day is today, or is in the past.""" now = datetime.utcnow() if isinstance(_date, str): @@ -76,7 +74,7 @@ class PrideFacts(commands.Cog): await target.send("The fact for the selected day is not yet available.") @commands.command(name="pridefact", aliases=["pridefacts"]) - async def pridefact(self, ctx: commands.Context) -> None: + async def pridefact(self, ctx: commands.Context, option: str = None) -> None: """ Sends a message with a pride fact of the day. @@ -85,15 +83,15 @@ class PrideFacts(commands.Cog): If a date is given as an argument, and the date is in the past, the fact from that day will be provided. """ - message_body = ctx.message.content[len(ctx.invoked_with) + 2:] - if message_body == "": + if not option: await self.send_select_fact(ctx, datetime.utcnow()) - elif message_body.lower().startswith("rand"): + elif option.lower().startswith("rand"): await self.send_random_fact(ctx) else: - await self.send_select_fact(ctx, message_body) + await self.send_select_fact(ctx, option) - def make_embed(self, fact: str) -> discord.Embed: + @staticmethod + def make_embed(fact: str) -> discord.Embed: """Makes a nice embed for the fact to be sent.""" return discord.Embed( colour=Colours.pink, -- cgit v1.2.3 From dc7923a78a4020a4020c42a392ff38bf3f5c35f3 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Tue, 20 Apr 2021 12:30:27 -0400 Subject: chore: Fix UnboundLocalError and discord.ForbiddenErrors in the catify command --- bot/exts/evergreen/catify.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/bot/exts/evergreen/catify.py b/bot/exts/evergreen/catify.py index a0121403..c409ce6c 100644 --- a/bot/exts/evergreen/catify.py +++ b/bot/exts/evergreen/catify.py @@ -1,7 +1,8 @@ import random +from contextlib import suppress from typing import Optional -from discord import AllowedMentions, Embed +from discord import AllowedMentions, Embed, Forbidden from discord.ext import commands from bot.constants import Cats, Colours, NEGATIVE_REPLIES @@ -34,8 +35,11 @@ class Catify(commands.Cog): else: display_name += f" | {random.choice(Cats.cats)}" + await ctx.send(f"Your catified username is: `{display_name}`") - await ctx.author.edit(nick=display_name) + + with suppress(Forbidden): + await ctx.author.edit(nick=display_name) else: if len(text) >= 1500: embed = Embed( @@ -50,27 +54,27 @@ class Catify(commands.Cog): for index, name in enumerate(string_list): if "cat" in name: if random.randint(0, 5) == 5: - string_list[index] = string_list[index].replace("cat", f"**{random.choice(Cats.cats)}**") + string_list[index] = name.replace("cat", f"**{random.choice(Cats.cats)}**") else: - string_list[index] = string_list[index].replace("cat", random.choice(Cats.cats)) + string_list[index] = name.replace("cat", random.choice(Cats.cats)) for element in Cats.cats: if element in name: - string_list[index] = string_list[index].replace(element, "cat") + string_list[index] = name.replace(element, "cat") - string_len = len(string_list) // 3 or len(string_list) + 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)}**") - else: - string_list.insert(random.randint(0, len(string_list)), random.choice(Cats.cats)) + 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)}**") + else: + string_list.insert(random.randint(0, len(string_list)), random.choice(Cats.cats)) - text = " ".join(string_list) - await ctx.send( - f">>> {text}", - allowed_mentions=AllowedMentions.none() - ) + text = " ".join(string_list) + await ctx.send( + f">>> {text}", + allowed_mentions=AllowedMentions.none() + ) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From 3477069a3a2e0b5e4030602afa4a4c0e7411a7e1 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Tue, 20 Apr 2021 12:59:46 -0400 Subject: chore: lower the input to fine more cats --- bot/exts/evergreen/catify.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/catify.py b/bot/exts/evergreen/catify.py index c409ce6c..262c75bd 100644 --- a/bot/exts/evergreen/catify.py +++ b/bot/exts/evergreen/catify.py @@ -52,11 +52,12 @@ class Catify(commands.Cog): string_list = text.split() for index, name in enumerate(string_list): - if "cat" in name: + name = name.lower() + if "cat" in text: if random.randint(0, 5) == 5: - string_list[index] = name.replace("cat", f"**{random.choice(Cats.cats)}**") + string_list[index] = text.replace("cat", f"**{random.choice(Cats.cats)}**") else: - string_list[index] = name.replace("cat", random.choice(Cats.cats)) + string_list[index] = text.replace("cat", random.choice(Cats.cats)) for element in Cats.cats: if element in name: string_list[index] = name.replace(element, "cat") -- cgit v1.2.3 From 5f889e4d3f0712c0005dbbc7c3ee820cc786ec30 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Tue, 20 Apr 2021 13:05:03 -0400 Subject: fix: Use name.replace not text.replace --- bot/exts/evergreen/catify.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/catify.py b/bot/exts/evergreen/catify.py index 262c75bd..88c63202 100644 --- a/bot/exts/evergreen/catify.py +++ b/bot/exts/evergreen/catify.py @@ -53,11 +53,11 @@ class Catify(commands.Cog): string_list = text.split() for index, name in enumerate(string_list): name = name.lower() - if "cat" in text: + if "cat" in name: if random.randint(0, 5) == 5: - string_list[index] = text.replace("cat", f"**{random.choice(Cats.cats)}**") + string_list[index] = name.replace("cat", f"**{random.choice(Cats.cats)}**") else: - string_list[index] = text.replace("cat", random.choice(Cats.cats)) + 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") -- cgit v1.2.3 From daf7dc79753d0d4482f58ddcad0ee2a7f7a244c1 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Tue, 20 Apr 2021 16:13:57 -0400 Subject: chore: use 'nickname' and 'display name' in the right places and use allowed_mentions --- bot/exts/evergreen/catify.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/catify.py b/bot/exts/evergreen/catify.py index 88c63202..ae8d54b6 100644 --- a/bot/exts/evergreen/catify.py +++ b/bot/exts/evergreen/catify.py @@ -27,7 +27,10 @@ class Catify(commands.Cog): if len(display_name) > 26: embed = Embed( title=random.choice(NEGATIVE_REPLIES), - description="Your nickname is too long to be catified! Please change it to be under 26 characters.", + description=( + "Your display name is too long to be catified! " + "Please change it to be under 26 characters." + ), color=Colours.soft_red ) await ctx.send(embed=embed) @@ -36,7 +39,7 @@ class Catify(commands.Cog): else: display_name += f" | {random.choice(Cats.cats)}" - await ctx.send(f"Your catified username is: `{display_name}`") + await ctx.send(f"Your catified nickname is: `{display_name}`", allowed_mentions=AllowedMentions.none()) with suppress(Forbidden): await ctx.author.edit(nick=display_name) -- cgit v1.2.3 From 4cc5b4432ff6955b405099bf42e89d942c518698 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela Date: Thu, 22 Apr 2021 02:06:47 +0300 Subject: Rewords PR Template This is a rewording of the comments in the PR template, to hopefully clarify intentions. It also removes unneeded fields. Signed-off-by: Hassan Abouelela --- .github/pull_request_template.md | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e2739287..2bb274de 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,30 +1,20 @@ ## Relevant Issues - - + - -## Description - - - -## Reasoning - + -## Screenshots - - - - -## Additional Details - - +## Description + ## Did you: - [ ] Join the [**Python Discord Community**](https://discord.gg/python)? -- [ ] If dependencies have been added or updated, run `pipenv lock`? -- [ ] **Lint your code** (`pipenv run lint`)? -- [ ] Set the PR to **allow edits from contributors**? +- [ ] Read all the comments in this tempelate. +- [ ] Ensure there is an issue open, or link relevant discord discussions. +- [ ] Read the [contributing guidelines](https://pythondiscord.com/pages/contributing/contributing-guidelines/). -- cgit v1.2.3 From c1defcff38fd274be2466df7fbbc99661bc44ae8 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela Date: Thu, 22 Apr 2021 02:19:32 +0300 Subject: Updates Punctuation In PR Template --- .github/pull_request_template.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2bb274de..f5fef80f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -15,6 +15,6 @@ Issues can be skipped with explicit core dev approval, but you have to link the - [ ] Join the [**Python Discord Community**](https://discord.gg/python)? -- [ ] Read all the comments in this tempelate. -- [ ] Ensure there is an issue open, or link relevant discord discussions. -- [ ] Read the [contributing guidelines](https://pythondiscord.com/pages/contributing/contributing-guidelines/). +- [ ] Read all the comments in this tempelate? +- [ ] Ensure there is an issue open, or link relevant discord discussions? +- [ ] Read the [contributing guidelines](https://pythondiscord.com/pages/contributing/contributing-guidelines/)? -- cgit v1.2.3 From 29084d1576e1435a5a4a32071a79b6af28acd362 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 22 Apr 2021 20:25:38 +0100 Subject: Fix errors when a subreddit has <5 posts. If a subreddit has <2 posts, the posts[1] check would fail with an IndexError. If the subreddit had less that 5 posts, then the k=5 check would also error. These changes harden the command for these edge cases. --- bot/exts/evergreen/reddit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index 49127bea..4fdb6fca 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -54,7 +54,7 @@ class Reddit(commands.Cog): if not posts: return await ctx.send('No posts available!') - if posts[1]["data"]["over_18"] is True: + if posts[0]["data"]["over_18"] is True: return await ctx.send( "You cannot access this Subreddit as it is ment for those who " "are 18 years or older." @@ -63,7 +63,7 @@ class Reddit(commands.Cog): embed_titles = "" # Chooses k unique random elements from a population sequence or set. - random_posts = random.sample(posts, k=5) + random_posts = random.sample(posts, k=min(len(posts), 5)) # ----------------------------------------------------------- # This code below is bound of change when the emojis are added. -- cgit v1.2.3 From 9b9ab546090dc2513ac00f6b476b8b494eb5428a Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 22 Apr 2021 21:02:31 +0100 Subject: Escape invalid filename chars before saving. This would cause an issue with the embed not embedding the image due to a filename mismatch. This has been implemented by escaping all invalid filename characters before saving the file. --- .../profile_pic_modification/pfp_modify.py | 40 +++++++++++++--------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py index 77c937de..f6b1d394 100644 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py @@ -1,7 +1,9 @@ import asyncio import json import logging +import string import typing as t +import unicodedata from concurrent.futures import ThreadPoolExecutor import discord @@ -36,6 +38,23 @@ async def in_executor(func: t.Callable, *args) -> t.Any: return await loop.run_in_executor(_EXECUTOR, func, *args) +def file_safe_name(effect: str, display_name: str) -> str: + """Returns a file safe filename based on the given effect and display name.""" + valid_filename_chars = f"-_.() {string.ascii_letters}{string.digits}" + + file_name = FILENAME_STRING.format(effect=effect, author=display_name) + + # Replace spaces + file_name = file_name.replace(" ", "_") + + # Normalize unicode characters + cleaned_filename = unicodedata.normalize('NFKD', file_name).encode('ASCII', 'ignore').decode() + + # Remove invalid filename characters + cleaned_filename = ''.join(c for c in cleaned_filename if c in valid_filename_chars) + return cleaned_filename + + class PfpModify(commands.Cog): """Various commands for users to change their own profile picture.""" @@ -76,10 +95,7 @@ class PfpModify(commands.Cog): return image_bytes = await member.avatar_url.read() - file_name = FILENAME_STRING.format( - effect="eightbit_avatar", - author=member.display_name - ) + file_name = file_safe_name("eightbit_avatar", member.display_name) file = await in_executor( PfpEffects.apply_effect, @@ -135,10 +151,7 @@ class PfpModify(commands.Cog): ctx.send = send_message # Reassigns ctx.send image_bytes = await member.avatar_url_as(size=256).read() - file_name = FILENAME_STRING.format( - effect="easterified_avatar", - author=member.display_name - ) + file_name = file_safe_name("easterified_avatar", member.display_name) file = await in_executor( PfpEffects.apply_effect, @@ -167,10 +180,7 @@ class PfpModify(commands.Cog): ) -> None: """Gets and sends the image in an embed. Used by the pride commands.""" async with ctx.typing(): - file_name = FILENAME_STRING.format( - effect="pride_avatar", - author=ctx.author.display_name - ) + file_name = file_safe_name("pride_avatar", ctx.author.display_name) file = await in_executor( PfpEffects.apply_effect, @@ -280,10 +290,8 @@ class PfpModify(commands.Cog): async with ctx.typing(): image_bytes = await member.avatar_url.read() - file_name = FILENAME_STRING.format( - effect="spooky_avatar", - author=member.display_name - ) + file_name = file_safe_name("spooky_avatar", member.display_name) + file = await in_executor( PfpEffects.apply_effect, image_bytes, -- cgit v1.2.3 From c837894af55deafff478bf8aa3b115d4589b6e07 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 22 Apr 2021 21:59:29 +0100 Subject: Only use WrappedMessageConverter in the actual command signature --- bot/exts/evergreen/bookmark.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 16919d8c..f0b828f8 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -22,7 +22,7 @@ class Bookmark(commands.Cog): self.bot = bot @staticmethod - def build_bookmark_dm(target_message: WrappedMessageConverter, title: str) -> discord.Embed: + def build_bookmark_dm(target_message: discord.Message, title: str) -> discord.Embed: """Builds the embed to DM the bookmark requester.""" embed = discord.Embed( title=title, @@ -51,7 +51,7 @@ class Bookmark(commands.Cog): self, channel: discord.TextChannel, user: discord.Member, - target_message: WrappedMessageConverter, + target_message: discord.Message, title: str ) -> None: """Sends the bookmark DM, or sends an error embed when a user bookmarks a message.""" @@ -67,7 +67,7 @@ class Bookmark(commands.Cog): @staticmethod async def send_reaction_embed( channel: discord.TextChannel, - target_message: WrappedMessageConverter + target_message: discord.Message ) -> discord.Message: """Sends an embed, with a reaction, so users can react to bookmark the message too.""" message = await channel.send( -- cgit v1.2.3 From e858a3682ffe36675570508802f633046b39ebd8 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela Date: Fri, 23 Apr 2021 02:55:03 +0300 Subject: Adds Link Suppressing Helper Adds a helper to find and escape links in a message. Signed-off-by: Hassan Abouelela --- bot/utils/helpers.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 bot/utils/helpers.py diff --git a/bot/utils/helpers.py b/bot/utils/helpers.py new file mode 100644 index 00000000..74c2ccd0 --- /dev/null +++ b/bot/utils/helpers.py @@ -0,0 +1,8 @@ +import re + + +def suppress_links(message: str) -> str: + """Accepts a message that may contain links, suppresses them, and returns them.""" + for link in set(re.findall(r"https?://[^\s]+", message, re.IGNORECASE)): + message = message.replace(link, f"<{link}>") + return message -- cgit v1.2.3 From 9e03064e9c116b0dd2bcc65b149b7ac9ee389ff3 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela Date: Fri, 23 Apr 2021 02:59:30 +0300 Subject: Suppresses Links In Commands Suppresses links in certain commands that can echo back user input. Signed-off-by: Hassan Abouelela --- bot/exts/easter/egg_decorating.py | 4 +++- bot/exts/evergreen/catify.py | 3 ++- bot/exts/evergreen/fun.py | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bot/exts/easter/egg_decorating.py b/bot/exts/easter/egg_decorating.py index b18e6636..a847388d 100644 --- a/bot/exts/easter/egg_decorating.py +++ b/bot/exts/easter/egg_decorating.py @@ -10,6 +10,8 @@ import discord from PIL import Image from discord.ext import commands +from bot.utils import helpers + log = logging.getLogger(__name__) with open(Path("bot/resources/evergreen/html_colours.json"), encoding="utf8") as f: @@ -65,7 +67,7 @@ class EggDecorating(commands.Cog): if value: colours[idx] = discord.Colour(value) else: - invalid.append(colour) + invalid.append(helpers.suppress_links(colour)) if len(invalid) > 1: return await ctx.send(f"Sorry, I don't know these colours: {' '.join(invalid)}") diff --git a/bot/exts/evergreen/catify.py b/bot/exts/evergreen/catify.py index ae8d54b6..d8a7442d 100644 --- a/bot/exts/evergreen/catify.py +++ b/bot/exts/evergreen/catify.py @@ -6,6 +6,7 @@ from discord import AllowedMentions, Embed, Forbidden from discord.ext import commands from bot.constants import Cats, Colours, NEGATIVE_REPLIES +from bot.utils import helpers class Catify(commands.Cog): @@ -74,7 +75,7 @@ class Catify(commands.Cog): else: string_list.insert(random.randint(0, len(string_list)), random.choice(Cats.cats)) - text = " ".join(string_list) + text = helpers.suppress_links(" ".join(string_list)) await ctx.send( f">>> {text}", allowed_mentions=AllowedMentions.none() diff --git a/bot/exts/evergreen/fun.py b/bot/exts/evergreen/fun.py index 101725da..7152d0cb 100644 --- a/bot/exts/evergreen/fun.py +++ b/bot/exts/evergreen/fun.py @@ -11,6 +11,7 @@ from discord.ext.commands import BadArgument, Bot, Cog, Context, MessageConverte from bot import utils from bot.constants import Client, Colours, Emojis +from bot.utils import helpers log = logging.getLogger(__name__) @@ -83,6 +84,7 @@ class Fun(Cog): if embed is not None: embed = Fun._convert_embed(conversion_func, embed) converted_text = conversion_func(text) + converted_text = helpers.suppress_links(converted_text) # Don't put >>> if only embed present if converted_text: converted_text = f">>> {converted_text.lstrip('> ')}" @@ -101,6 +103,7 @@ class Fun(Cog): if embed is not None: embed = Fun._convert_embed(conversion_func, embed) converted_text = conversion_func(text) + converted_text = helpers.suppress_links(converted_text) # Don't put >>> if only embed present if converted_text: converted_text = f">>> {converted_text.lstrip('> ')}" -- cgit v1.2.3 From 525078bf7f88de781eeeba5eb32cdac641374f1d Mon Sep 17 00:00:00 2001 From: Hassan Abouelela Date: Fri, 23 Apr 2021 11:47:27 +0300 Subject: Reword Issues Comment --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f5fef80f..fade00e1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,6 @@ ## Relevant Issues -- cgit v1.2.3 From d77986e528b977170c30efb6afc41e53425ad6df Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Fri, 23 Apr 2021 12:28:37 +0100 Subject: Fix spelling of a user facing message in reddit cog Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- bot/exts/evergreen/reddit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index 4fdb6fca..2be511c8 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -56,7 +56,7 @@ class Reddit(commands.Cog): if posts[0]["data"]["over_18"] is True: return await ctx.send( - "You cannot access this Subreddit as it is ment for those who " + "You cannot access this Subreddit as it is meant for those who " "are 18 years or older." ) -- cgit v1.2.3 From e666fb7b80a9f590257d1426c7604ff180b35e6e Mon Sep 17 00:00:00 2001 From: Salil Chincholikar Date: Tue, 27 Apr 2021 14:39:58 +0530 Subject: Reworded/fixed grammatical error --- bot/utils/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/utils/exceptions.py b/bot/utils/exceptions.py index 2b1c1b31..5e4d6330 100644 --- a/bot/utils/exceptions.py +++ b/bot/utils/exceptions.py @@ -1,4 +1,4 @@ class UserNotPlayingError(Exception): - """Will raised when user try to use game commands when not playing.""" + """Will be raised when the users try to use game commands when they are not playing.""" pass -- cgit v1.2.3 From 4e0210922ef8d25eeb18f080c9baab022d70d0d2 Mon Sep 17 00:00:00 2001 From: Salil Chincholikar <31334826+chincholikarsalil@users.noreply.github.com> Date: Tue, 27 Apr 2021 19:12:28 +0530 Subject: Updated bot/utils/exceptions.py Co-authored-by: Joe Banks --- bot/utils/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/utils/exceptions.py b/bot/utils/exceptions.py index 5e4d6330..9e080759 100644 --- a/bot/utils/exceptions.py +++ b/bot/utils/exceptions.py @@ -1,4 +1,4 @@ class UserNotPlayingError(Exception): - """Will be raised when the users try to use game commands when they are not playing.""" + """Raised when users try to use game commands when they are not playing.""" pass -- cgit v1.2.3 From 8e6e27c2400e0d630d3e8da93189e7d3fc0d39e7 Mon Sep 17 00:00:00 2001 From: Shivansh Date: Fri, 30 Apr 2021 07:19:52 +0530 Subject: Give more information in function doc-strings --- bot/exts/pride/pride_leader.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index d955a2cc..5ecfd2ea 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -33,7 +33,17 @@ class PrideLeader(commands.Cog): log.trace(f"Got a Invalid pride leader: {leader_name}") def invalid_embed_generate(self, pride_leader: str) -> discord.Embed: - """Generates Invalid Embed.""" + """ + Generates Invalid Embed. + + The invalid embed contains a list of closely matched names of the invalid pride + leader the user gave. If no closely matched names are found it would list all + the available pride leader names. + + Wikipedia is a useful place to learn about pride leaders and we don't have all + the pride leaders, so the bot would add a field containing the wikipedia + command to execute. + """ embed = discord.Embed( color=constants.Colours.soft_red ) -- cgit v1.2.3 From f6d8a3f39d1a480e64ac322291cbf7820c00d3b0 Mon Sep 17 00:00:00 2001 From: Shivansh Date: Fri, 30 Apr 2021 07:22:27 +0530 Subject: Remove redundant code The entire `name_verifier` function can be replaced with just a dict.get --- bot/exts/pride/pride_leader.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index 5ecfd2ea..83b3ac0e 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -25,13 +25,6 @@ class PrideLeader(commands.Cog): with PRIDE_LEADERS_RESOURCE.open(encoding="utf8") as data: self.pride = json.load(data) - def name_verifier(self, leader_name: str) -> Optional[str]: - """Verify leader name whether it is present in resources or not.""" - leader_name = leader_name.title() - if leader_name in self.pride: - return leader_name - log.trace(f"Got a Invalid pride leader: {leader_name}") - def invalid_embed_generate(self, pride_leader: str) -> discord.Embed: """ Generates Invalid Embed. @@ -112,8 +105,10 @@ class PrideLeader(commands.Cog): if not pride_leader_name: leader = random.choice([name for name in self.pride]) else: - leader = self.name_verifier(pride_leader_name) + leader = self.pride.get(pride_leader_name.title()) if not leader: + log.trace(f"Got a Invalid pride leader: {pride_leader_name}") + embed = self.invalid_embed_generate(pride_leader_name) await ctx.send(embed=embed) return -- cgit v1.2.3 From dade71a609d298e8bfb16fef8c8ecbd2faffe51d Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 30 Apr 2021 10:00:51 +0100 Subject: Rename pfp_modify cog and group to avatar_mod for clarity Added in aliases for ease of use too. --- bot/exts/evergreen/avatar_modification/__init__.py | 0 bot/exts/evergreen/avatar_modification/_effects.py | 139 +++++++++ .../evergreen/avatar_modification/avatar_modify.py | 315 +++++++++++++++++++++ .../evergreen/profile_pic_modification/__init__.py | 0 .../evergreen/profile_pic_modification/_effects.py | 139 --------- .../profile_pic_modification/pfp_modify.py | 315 --------------------- 6 files changed, 454 insertions(+), 454 deletions(-) create mode 100644 bot/exts/evergreen/avatar_modification/__init__.py create mode 100644 bot/exts/evergreen/avatar_modification/_effects.py create mode 100644 bot/exts/evergreen/avatar_modification/avatar_modify.py delete mode 100644 bot/exts/evergreen/profile_pic_modification/__init__.py delete mode 100644 bot/exts/evergreen/profile_pic_modification/_effects.py delete mode 100644 bot/exts/evergreen/profile_pic_modification/pfp_modify.py diff --git a/bot/exts/evergreen/avatar_modification/__init__.py b/bot/exts/evergreen/avatar_modification/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bot/exts/evergreen/avatar_modification/_effects.py b/bot/exts/evergreen/avatar_modification/_effects.py new file mode 100644 index 00000000..e415d700 --- /dev/null +++ b/bot/exts/evergreen/avatar_modification/_effects.py @@ -0,0 +1,139 @@ +import typing as t +from io import BytesIO +from pathlib import Path + +import discord +from PIL import Image, ImageDraw, ImageOps + +from bot.constants import Colours + + +class PfpEffects: + """ + Implements various image modifying effects, for the PfpModify cog. + + All of these fuctions are slow, and blocking, so they should be ran in executors. + """ + + @staticmethod + def apply_effect(image_bytes: bytes, effect: t.Callable, filename: str, *args) -> discord.File: + """Applies the given effect to the image passed to it.""" + im = Image.open(BytesIO(image_bytes)) + im = im.convert("RGBA") + im = effect(im, *args) + + bufferedio = BytesIO() + im.save(bufferedio, format="PNG") + bufferedio.seek(0) + + return discord.File(bufferedio, filename=filename) + + @staticmethod + def closest(x: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: + """ + Finds the closest "easter" colour to a given pixel. + + Returns a merge between the original colour and the closest colour. + """ + r1, g1, b1 = x + + def distance(point: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: + """Finds the difference between a pastel colour and the original pixel colour.""" + r2, g2, b2 = point + return (r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2 + + closest_colours = sorted(Colours.easter_like_colours, key=distance) + r2, g2, b2 = closest_colours[0] + r = (r1 + r2) // 2 + g = (g1 + g2) // 2 + b = (b1 + b2) // 2 + + return r, g, b + + @staticmethod + def crop_avatar_circle(avatar: Image) -> Image: + """This crops the avatar given into a circle.""" + mask = Image.new("L", avatar.size, 0) + draw = ImageDraw.Draw(mask) + draw.ellipse((0, 0) + avatar.size, fill=255) + avatar.putalpha(mask) + return avatar + + @staticmethod + def crop_ring(ring: Image, px: int) -> Image: + """This crops the given ring into a circle.""" + mask = Image.new("L", ring.size, 0) + draw = ImageDraw.Draw(mask) + draw.ellipse((0, 0) + ring.size, fill=255) + draw.ellipse((px, px, 1024-px, 1024-px), fill=0) + ring.putalpha(mask) + return ring + + @staticmethod + def pridify_effect(image: Image, pixels: int, flag: str) -> Image: + """Applies the given pride effect to the given image.""" + image = image.resize((1024, 1024)) + image = PfpEffects.crop_avatar_circle(image) + + ring = Image.open(Path(f"bot/resources/pride/flags/{flag}.png")).resize((1024, 1024)) + ring = ring.convert("RGBA") + ring = PfpEffects.crop_ring(ring, pixels) + + image.alpha_composite(ring, (0, 0)) + return image + + @staticmethod + def eight_bitify_effect(image: Image) -> Image: + """ + Applies the 8bit effect to the given image. + + This is done by reducing the image to 32x32 and then back up to 1024x1024. + We then quantize the image before returning too. + """ + image = image.resize((32, 32), resample=Image.NEAREST) + image = image.resize((1024, 1024), resample=Image.NEAREST) + return image.quantize() + + @staticmethod + def easterify_effect(image: Image, overlay_image: Image = None) -> Image: + """ + Applies the easter effect to the given image. + + This is done by getting the closest "easter" colour to each pixel and changing the colour + to the half-way RGBvalue. + + We also then add an overlay image on top in middle right, a chocolate bunny by default. + """ + if overlay_image: + ratio = 64 / overlay_image.height + overlay_image = overlay_image.resize(( + round(overlay_image.width * ratio), + round(overlay_image.height * ratio) + )) + overlay_image = overlay_image.convert("RGBA") + else: + overlay_image = Image.open(Path("bot/resources/easter/chocolate_bunny.png")) + + alpha = image.getchannel("A").getdata() + image = image.convert("RGB") + image = ImageOps.posterize(image, 6) + + data = image.getdata() + data_set = set(data) + easterified_data_set = {} + + for x in data_set: + easterified_data_set[x] = PfpEffects.closest(x) + new_pixel_data = [ + (*easterified_data_set[x], alpha[i]) + if x in easterified_data_set else x + for i, x in enumerate(data) + ] + + im = Image.new("RGBA", image.size) + im.putdata(new_pixel_data) + im.alpha_composite( + overlay_image, + (im.width - overlay_image.width, (im.height - overlay_image.height) // 2) + ) + return im diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py new file mode 100644 index 00000000..e1078fa0 --- /dev/null +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -0,0 +1,315 @@ +import asyncio +import json +import logging +import string +import typing as t +import unicodedata +from concurrent.futures import ThreadPoolExecutor + +import discord +from aiohttp import client_exceptions +from discord.ext import commands +from discord.ext.commands.errors import BadArgument + +from bot.constants import Client, Colours, Emojis +from bot.exts.evergreen.avatar_modification._effects import PfpEffects +from bot.utils.extensions import invoke_help_command +from bot.utils.halloween import spookifications + +log = logging.getLogger(__name__) + +_EXECUTOR = ThreadPoolExecutor(10) + +FILENAME_STRING = "{effect}_{author}.png" + +with open('bot/resources/pride/gender_options.json') as f: + GENDER_OPTIONS = json.load(f) + + +async def in_executor(func: t.Callable, *args) -> t.Any: + """ + Runs the given synchronus function `func` in an executor. + + This is useful for running slow, blocking code within async + functions, so that they don't block the bot. + """ + log.trace(f"Running {func.__name__} in an executor.") + loop = asyncio.get_event_loop() + return await loop.run_in_executor(_EXECUTOR, func, *args) + + +def file_safe_name(effect: str, display_name: str) -> str: + """Returns a file safe filename based on the given effect and display name.""" + valid_filename_chars = f"-_.() {string.ascii_letters}{string.digits}" + + file_name = FILENAME_STRING.format(effect=effect, author=display_name) + + # Replace spaces + file_name = file_name.replace(" ", "_") + + # Normalize unicode characters + cleaned_filename = unicodedata.normalize('NFKD', file_name).encode('ASCII', 'ignore').decode() + + # Remove invalid filename characters + cleaned_filename = ''.join(c for c in cleaned_filename if c in valid_filename_chars) + return cleaned_filename + + +class AvatarModify(commands.Cog): + """Various commands for users to apply affects to their own avatars.""" + + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + + async def _fetch_member(self, member_id: int) -> t.Optional[discord.Member]: + """ + Fetches a member and handles errors. + + This helper funciton is required as the member cache doesn't always have the most up to date + profile picture. This can lead to errors if the image is delted from the Discord CDN. + """ + try: + member = await self.bot.get_guild(Client.guild).fetch_member(member_id) + except discord.errors.NotFound: + log.debug(f"Member {member_id} left the guild before we could get their pfp.") + return None + except discord.HTTPException: + log.exception(f"Exception while trying to retrieve member {member_id} from Discord.") + return None + + return member + + @commands.group(aliases=('avatar_mod', 'pfp_mod')) + async def avatar_modify(self, ctx: commands.Context) -> None: + """Groups all of the pfp modifying commands to allow a single concurrency limit.""" + if not ctx.invoked_subcommand: + await invoke_help_command(ctx) + + @avatar_modify.command(name="8bitify", root_aliases=("8bitify",)) + async def eightbit_command(self, ctx: commands.Context) -> None: + """Pixelates your avatar and changes the palette to an 8bit one.""" + async with ctx.typing(): + member = await self._fetch_member(ctx.author.id) + if not member: + await ctx.send(f"{Emojis.cross_mark} Could not get member info.") + return + + image_bytes = await member.avatar_url.read() + file_name = file_safe_name("eightbit_avatar", member.display_name) + + file = await in_executor( + PfpEffects.apply_effect, + image_bytes, + PfpEffects.eight_bitify_effect, + file_name + ) + + embed = discord.Embed( + title="Your 8-bit avatar", + description="Here is your avatar. I think it looks all cool and 'retro'." + ) + + embed.set_image(url=f"attachment://{file_name}") + embed.set_footer(text=f"Made by {member.display_name}.", icon_url=member.avatar_url) + + await ctx.send(embed=embed, file=file) + + @avatar_modify.command(aliases=["easterify"], root_aliases=("easterify", "avatareasterify")) + async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: + """ + This "Easterifies" the user's avatar. + + Given colours will produce a personalised egg in the corner, similar to the egg_decorate command. + If colours are not given, a nice little chocolate bunny will sit in the corner. + Colours are split by spaces, unless you wrap the colour name in double quotes. + Discord colour names, HTML colour names, XKCD colour names and hex values are accepted. + """ + async def send(*args, **kwargs) -> str: + """ + This replaces the original ctx.send. + + When invoking the egg decorating command, the egg itself doesn't print to to the channel. + Returns the message content so that if any errors occur, the error message can be output. + """ + if args: + return args[0] + + async with ctx.typing(): + member = await self._fetch_member(ctx.author.id) + if not member: + await ctx.send(f"{Emojis.cross_mark} Could not get member info.") + return + + egg = None + if colours: + send_message = ctx.send + ctx.send = send # Assigns ctx.send to a fake send + egg = await ctx.invoke(self.bot.get_command("eggdecorate"), *colours) + if isinstance(egg, str): # When an error message occurs in eggdecorate. + await send_message(egg) + return + ctx.send = send_message # Reassigns ctx.send + + image_bytes = await member.avatar_url_as(size=256).read() + file_name = file_safe_name("easterified_avatar", member.display_name) + + file = await in_executor( + PfpEffects.apply_effect, + image_bytes, + PfpEffects.easterify_effect, + file_name, + egg + ) + + embed = discord.Embed( + name="Your Lovely Easterified Avatar!", + description="Here is your lovely avatar, all bright and colourful\nwith Easter pastel colours. Enjoy :D" + ) + embed.set_image(url=f"attachment://{file_name}") + embed.set_footer(text=f"Made by {member.display_name}.", icon_url=member.avatar_url) + + await ctx.send(file=file, embed=embed) + + @staticmethod + async def send_pride_image( + ctx: commands.Context, + image_bytes: bytes, + pixels: int, + flag: str, + option: str + ) -> None: + """Gets and sends the image in an embed. Used by the pride commands.""" + async with ctx.typing(): + file_name = file_safe_name("pride_avatar", ctx.author.display_name) + + file = await in_executor( + PfpEffects.apply_effect, + image_bytes, + PfpEffects.pridify_effect, + file_name, + pixels, + flag + ) + + embed = discord.Embed( + name="Your Lovely Pride Avatar!", + description=f"Here is your lovely avatar, surrounded by\n a beautiful {option} flag. Enjoy :D" + ) + embed.set_image(url=f"attachment://{file_name}") + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) + await ctx.send(file=file, embed=embed) + + @avatar_modify.group( + aliases=["avatarpride", "pridepfp", "prideprofile"], + root_aliases=("prideavatar", "avatarpride", "pridepfp", "prideprofile"), + invoke_without_command=True + ) + async def prideavatar(self, ctx: commands.Context, option: str = "lgbt", pixels: int = 64) -> None: + """ + This surrounds an avatar with a border of a specified LGBT flag. + + This defaults to the LGBT rainbow flag if none is given. + The amount of pixels can be given which determines the thickness of the flag border. + This has a maximum of 512px and defaults to a 64px border. + The full image is 1024x1024. + """ + option = option.lower() + pixels = max(0, min(512, pixels)) + flag = GENDER_OPTIONS.get(option) + if flag is None: + await ctx.send("I don't have that flag!") + return + + async with ctx.typing(): + member = await self._fetch_member(ctx.author.id) + if not member: + await ctx.send(f"{Emojis.cross_mark} Could not get member info.") + return + image_bytes = await member.avatar_url_as(size=1024).read() + await self.send_pride_image(ctx, image_bytes, pixels, flag, option) + + @prideavatar.command() + async def image(self, ctx: commands.Context, url: str, option: str = "lgbt", pixels: int = 64) -> None: + """ + This surrounds the image specified by the URL with a border of a specified LGBT flag. + + This defaults to the LGBT rainbow flag if none is given. + The amount of pixels can be given which determines the thickness of the flag border. + This has a maximum of 512px and defaults to a 64px border. + The full image is 1024x1024. + """ + option = option.lower() + pixels = max(0, min(512, pixels)) + flag = GENDER_OPTIONS.get(option) + if flag is None: + await ctx.send("I don't have that flag!") + return + + async with ctx.typing(): + try: + async with self.bot.http_session.get(url) as response: + if response.status != 200: + await ctx.send("Bad response from provided URL!") + return + image_bytes = await response.read() + except client_exceptions.ClientConnectorError: + raise BadArgument("Cannot connect to provided URL!") + except client_exceptions.InvalidURL: + raise BadArgument("Invalid URL!") + + await self.send_pride_image(ctx, image_bytes, pixels, flag, option) + + @prideavatar.command() + async def flags(self, ctx: commands.Context) -> None: + """This lists the flags that can be used with the prideavatar command.""" + choices = sorted(set(GENDER_OPTIONS.values())) + options = "• " + "\n• ".join(choices) + embed = discord.Embed( + title="I have the following flags:", + description=options, + colour=Colours.soft_red + ) + await ctx.send(embed=embed) + + @avatar_modify.command( + name='spookyavatar', + aliases=('savatar', 'spookify'), + root_aliases=('spookyavatar', 'spookify', 'savatar'), + brief='Spookify an user\'s avatar.' + ) + async def spooky_avatar(self, ctx: commands.Context, member: discord.Member = None) -> None: + """This "spookifies" the given user's avatar, with a random *spooky* effect.""" + if member is None: + member = ctx.author + + member = await self._fetch_member(member.id) + if not member: + await ctx.send(f"{Emojis.cross_mark} Could not get member info.") + return + + async with ctx.typing(): + image_bytes = await member.avatar_url.read() + + file_name = file_safe_name("spooky_avatar", member.display_name) + + file = await in_executor( + PfpEffects.apply_effect, + image_bytes, + spookifications.get_random_effect, + file_name + ) + + embed = discord.Embed( + title="Is this you or am I just really paranoid?", + colour=Colours.soft_red + ) + embed.set_author(name=member.name, icon_url=member.avatar_url) + embed.set_image(url=f"attachment://{file_name}") + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) + + await ctx.send(file=file, embed=embed) + + +def setup(bot: commands.Bot) -> None: + """Load the PfpModify cog.""" + bot.add_cog(AvatarModify(bot)) diff --git a/bot/exts/evergreen/profile_pic_modification/__init__.py b/bot/exts/evergreen/profile_pic_modification/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/bot/exts/evergreen/profile_pic_modification/_effects.py b/bot/exts/evergreen/profile_pic_modification/_effects.py deleted file mode 100644 index e415d700..00000000 --- a/bot/exts/evergreen/profile_pic_modification/_effects.py +++ /dev/null @@ -1,139 +0,0 @@ -import typing as t -from io import BytesIO -from pathlib import Path - -import discord -from PIL import Image, ImageDraw, ImageOps - -from bot.constants import Colours - - -class PfpEffects: - """ - Implements various image modifying effects, for the PfpModify cog. - - All of these fuctions are slow, and blocking, so they should be ran in executors. - """ - - @staticmethod - def apply_effect(image_bytes: bytes, effect: t.Callable, filename: str, *args) -> discord.File: - """Applies the given effect to the image passed to it.""" - im = Image.open(BytesIO(image_bytes)) - im = im.convert("RGBA") - im = effect(im, *args) - - bufferedio = BytesIO() - im.save(bufferedio, format="PNG") - bufferedio.seek(0) - - return discord.File(bufferedio, filename=filename) - - @staticmethod - def closest(x: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: - """ - Finds the closest "easter" colour to a given pixel. - - Returns a merge between the original colour and the closest colour. - """ - r1, g1, b1 = x - - def distance(point: t.Tuple[int, int, int]) -> t.Tuple[int, int, int]: - """Finds the difference between a pastel colour and the original pixel colour.""" - r2, g2, b2 = point - return (r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2 - - closest_colours = sorted(Colours.easter_like_colours, key=distance) - r2, g2, b2 = closest_colours[0] - r = (r1 + r2) // 2 - g = (g1 + g2) // 2 - b = (b1 + b2) // 2 - - return r, g, b - - @staticmethod - def crop_avatar_circle(avatar: Image) -> Image: - """This crops the avatar given into a circle.""" - mask = Image.new("L", avatar.size, 0) - draw = ImageDraw.Draw(mask) - draw.ellipse((0, 0) + avatar.size, fill=255) - avatar.putalpha(mask) - return avatar - - @staticmethod - def crop_ring(ring: Image, px: int) -> Image: - """This crops the given ring into a circle.""" - mask = Image.new("L", ring.size, 0) - draw = ImageDraw.Draw(mask) - draw.ellipse((0, 0) + ring.size, fill=255) - draw.ellipse((px, px, 1024-px, 1024-px), fill=0) - ring.putalpha(mask) - return ring - - @staticmethod - def pridify_effect(image: Image, pixels: int, flag: str) -> Image: - """Applies the given pride effect to the given image.""" - image = image.resize((1024, 1024)) - image = PfpEffects.crop_avatar_circle(image) - - ring = Image.open(Path(f"bot/resources/pride/flags/{flag}.png")).resize((1024, 1024)) - ring = ring.convert("RGBA") - ring = PfpEffects.crop_ring(ring, pixels) - - image.alpha_composite(ring, (0, 0)) - return image - - @staticmethod - def eight_bitify_effect(image: Image) -> Image: - """ - Applies the 8bit effect to the given image. - - This is done by reducing the image to 32x32 and then back up to 1024x1024. - We then quantize the image before returning too. - """ - image = image.resize((32, 32), resample=Image.NEAREST) - image = image.resize((1024, 1024), resample=Image.NEAREST) - return image.quantize() - - @staticmethod - def easterify_effect(image: Image, overlay_image: Image = None) -> Image: - """ - Applies the easter effect to the given image. - - This is done by getting the closest "easter" colour to each pixel and changing the colour - to the half-way RGBvalue. - - We also then add an overlay image on top in middle right, a chocolate bunny by default. - """ - if overlay_image: - ratio = 64 / overlay_image.height - overlay_image = overlay_image.resize(( - round(overlay_image.width * ratio), - round(overlay_image.height * ratio) - )) - overlay_image = overlay_image.convert("RGBA") - else: - overlay_image = Image.open(Path("bot/resources/easter/chocolate_bunny.png")) - - alpha = image.getchannel("A").getdata() - image = image.convert("RGB") - image = ImageOps.posterize(image, 6) - - data = image.getdata() - data_set = set(data) - easterified_data_set = {} - - for x in data_set: - easterified_data_set[x] = PfpEffects.closest(x) - new_pixel_data = [ - (*easterified_data_set[x], alpha[i]) - if x in easterified_data_set else x - for i, x in enumerate(data) - ] - - im = Image.new("RGBA", image.size) - im.putdata(new_pixel_data) - im.alpha_composite( - overlay_image, - (im.width - overlay_image.width, (im.height - overlay_image.height) // 2) - ) - return im diff --git a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py b/bot/exts/evergreen/profile_pic_modification/pfp_modify.py deleted file mode 100644 index f6b1d394..00000000 --- a/bot/exts/evergreen/profile_pic_modification/pfp_modify.py +++ /dev/null @@ -1,315 +0,0 @@ -import asyncio -import json -import logging -import string -import typing as t -import unicodedata -from concurrent.futures import ThreadPoolExecutor - -import discord -from aiohttp import client_exceptions -from discord.ext import commands -from discord.ext.commands.errors import BadArgument - -from bot.constants import Client, Colours, Emojis -from bot.exts.evergreen.profile_pic_modification._effects import PfpEffects -from bot.utils.extensions import invoke_help_command -from bot.utils.halloween import spookifications - -log = logging.getLogger(__name__) - -_EXECUTOR = ThreadPoolExecutor(10) - -FILENAME_STRING = "{effect}_{author}.png" - -with open('bot/resources/pride/gender_options.json') as f: - GENDER_OPTIONS = json.load(f) - - -async def in_executor(func: t.Callable, *args) -> t.Any: - """ - Runs the given synchronus function `func` in an executor. - - This is useful for running slow, blocking code within async - functions, so that they don't block the bot. - """ - log.trace(f"Running {func.__name__} in an executor.") - loop = asyncio.get_event_loop() - return await loop.run_in_executor(_EXECUTOR, func, *args) - - -def file_safe_name(effect: str, display_name: str) -> str: - """Returns a file safe filename based on the given effect and display name.""" - valid_filename_chars = f"-_.() {string.ascii_letters}{string.digits}" - - file_name = FILENAME_STRING.format(effect=effect, author=display_name) - - # Replace spaces - file_name = file_name.replace(" ", "_") - - # Normalize unicode characters - cleaned_filename = unicodedata.normalize('NFKD', file_name).encode('ASCII', 'ignore').decode() - - # Remove invalid filename characters - cleaned_filename = ''.join(c for c in cleaned_filename if c in valid_filename_chars) - return cleaned_filename - - -class PfpModify(commands.Cog): - """Various commands for users to change their own profile picture.""" - - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - - async def _fetch_member(self, member_id: int) -> t.Optional[discord.Member]: - """ - Fetches a member and handles errors. - - This helper funciton is required as the member cache doesn't always have the most up to date - profile picture. This can lead to errors if the image is delted from the Discord CDN. - """ - try: - member = await self.bot.get_guild(Client.guild).fetch_member(member_id) - except discord.errors.NotFound: - log.debug(f"Member {member_id} left the guild before we could get their pfp.") - return None - except discord.HTTPException: - log.exception(f"Exception while trying to retrieve member {member_id} from Discord.") - return None - - return member - - @commands.group() - async def pfp_modify(self, ctx: commands.Context) -> None: - """Groups all of the pfp modifying commands to allow a single concurrency limit.""" - if not ctx.invoked_subcommand: - await invoke_help_command(ctx) - - @pfp_modify.command(name="8bitify", root_aliases=("8bitify",)) - async def eightbit_command(self, ctx: commands.Context) -> None: - """Pixelates your avatar and changes the palette to an 8bit one.""" - async with ctx.typing(): - member = await self._fetch_member(ctx.author.id) - if not member: - await ctx.send(f"{Emojis.cross_mark} Could not get member info.") - return - - image_bytes = await member.avatar_url.read() - file_name = file_safe_name("eightbit_avatar", member.display_name) - - file = await in_executor( - PfpEffects.apply_effect, - image_bytes, - PfpEffects.eight_bitify_effect, - file_name - ) - - embed = discord.Embed( - title="Your 8-bit avatar", - description="Here is your avatar. I think it looks all cool and 'retro'." - ) - - embed.set_image(url=f"attachment://{file_name}") - embed.set_footer(text=f"Made by {member.display_name}.", icon_url=member.avatar_url) - - await ctx.send(embed=embed, file=file) - - @pfp_modify.command(aliases=["easterify"], root_aliases=("easterify", "avatareasterify")) - async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: - """ - This "Easterifies" the user's avatar. - - Given colours will produce a personalised egg in the corner, similar to the egg_decorate command. - If colours are not given, a nice little chocolate bunny will sit in the corner. - Colours are split by spaces, unless you wrap the colour name in double quotes. - Discord colour names, HTML colour names, XKCD colour names and hex values are accepted. - """ - async def send(*args, **kwargs) -> str: - """ - This replaces the original ctx.send. - - When invoking the egg decorating command, the egg itself doesn't print to to the channel. - Returns the message content so that if any errors occur, the error message can be output. - """ - if args: - return args[0] - - async with ctx.typing(): - member = await self._fetch_member(ctx.author.id) - if not member: - await ctx.send(f"{Emojis.cross_mark} Could not get member info.") - return - - egg = None - if colours: - send_message = ctx.send - ctx.send = send # Assigns ctx.send to a fake send - egg = await ctx.invoke(self.bot.get_command("eggdecorate"), *colours) - if isinstance(egg, str): # When an error message occurs in eggdecorate. - await send_message(egg) - return - ctx.send = send_message # Reassigns ctx.send - - image_bytes = await member.avatar_url_as(size=256).read() - file_name = file_safe_name("easterified_avatar", member.display_name) - - file = await in_executor( - PfpEffects.apply_effect, - image_bytes, - PfpEffects.easterify_effect, - file_name, - egg - ) - - embed = discord.Embed( - name="Your Lovely Easterified Avatar!", - description="Here is your lovely avatar, all bright and colourful\nwith Easter pastel colours. Enjoy :D" - ) - embed.set_image(url=f"attachment://{file_name}") - embed.set_footer(text=f"Made by {member.display_name}.", icon_url=member.avatar_url) - - await ctx.send(file=file, embed=embed) - - @staticmethod - async def send_pride_image( - ctx: commands.Context, - image_bytes: bytes, - pixels: int, - flag: str, - option: str - ) -> None: - """Gets and sends the image in an embed. Used by the pride commands.""" - async with ctx.typing(): - file_name = file_safe_name("pride_avatar", ctx.author.display_name) - - file = await in_executor( - PfpEffects.apply_effect, - image_bytes, - PfpEffects.pridify_effect, - file_name, - pixels, - flag - ) - - embed = discord.Embed( - name="Your Lovely Pride Avatar!", - description=f"Here is your lovely avatar, surrounded by\n a beautiful {option} flag. Enjoy :D" - ) - embed.set_image(url=f"attachment://{file_name}") - embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) - await ctx.send(file=file, embed=embed) - - @pfp_modify.group( - aliases=["avatarpride", "pridepfp", "prideprofile"], - root_aliases=("prideavatar", "avatarpride", "pridepfp", "prideprofile"), - invoke_without_command=True - ) - async def prideavatar(self, ctx: commands.Context, option: str = "lgbt", pixels: int = 64) -> None: - """ - This surrounds an avatar with a border of a specified LGBT flag. - - This defaults to the LGBT rainbow flag if none is given. - The amount of pixels can be given which determines the thickness of the flag border. - This has a maximum of 512px and defaults to a 64px border. - The full image is 1024x1024. - """ - option = option.lower() - pixels = max(0, min(512, pixels)) - flag = GENDER_OPTIONS.get(option) - if flag is None: - await ctx.send("I don't have that flag!") - return - - async with ctx.typing(): - member = await self._fetch_member(ctx.author.id) - if not member: - await ctx.send(f"{Emojis.cross_mark} Could not get member info.") - return - image_bytes = await member.avatar_url_as(size=1024).read() - await self.send_pride_image(ctx, image_bytes, pixels, flag, option) - - @prideavatar.command() - async def image(self, ctx: commands.Context, url: str, option: str = "lgbt", pixels: int = 64) -> None: - """ - This surrounds the image specified by the URL with a border of a specified LGBT flag. - - This defaults to the LGBT rainbow flag if none is given. - The amount of pixels can be given which determines the thickness of the flag border. - This has a maximum of 512px and defaults to a 64px border. - The full image is 1024x1024. - """ - option = option.lower() - pixels = max(0, min(512, pixels)) - flag = GENDER_OPTIONS.get(option) - if flag is None: - await ctx.send("I don't have that flag!") - return - - async with ctx.typing(): - try: - async with self.bot.http_session.get(url) as response: - if response.status != 200: - await ctx.send("Bad response from provided URL!") - return - image_bytes = await response.read() - except client_exceptions.ClientConnectorError: - raise BadArgument("Cannot connect to provided URL!") - except client_exceptions.InvalidURL: - raise BadArgument("Invalid URL!") - - await self.send_pride_image(ctx, image_bytes, pixels, flag, option) - - @prideavatar.command() - async def flags(self, ctx: commands.Context) -> None: - """This lists the flags that can be used with the prideavatar command.""" - choices = sorted(set(GENDER_OPTIONS.values())) - options = "• " + "\n• ".join(choices) - embed = discord.Embed( - title="I have the following flags:", - description=options, - colour=Colours.soft_red - ) - await ctx.send(embed=embed) - - @pfp_modify.command( - name='spookyavatar', - aliases=('savatar', 'spookify'), - root_aliases=('spookyavatar', 'spookify', 'savatar'), - brief='Spookify an user\'s avatar.' - ) - async def spooky_avatar(self, ctx: commands.Context, member: discord.Member = None) -> None: - """This "spookifies" the given user's avatar, with a random *spooky* effect.""" - if member is None: - member = ctx.author - - member = await self._fetch_member(member.id) - if not member: - await ctx.send(f"{Emojis.cross_mark} Could not get member info.") - return - - async with ctx.typing(): - image_bytes = await member.avatar_url.read() - - file_name = file_safe_name("spooky_avatar", member.display_name) - - file = await in_executor( - PfpEffects.apply_effect, - image_bytes, - spookifications.get_random_effect, - file_name - ) - - embed = discord.Embed( - title="Is this you or am I just really paranoid?", - colour=Colours.soft_red - ) - embed.set_author(name=member.name, icon_url=member.avatar_url) - embed.set_image(url=f"attachment://{file_name}") - embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) - - await ctx.send(file=file, embed=embed) - - -def setup(bot: commands.Bot) -> None: - """Load the PfpModify cog.""" - bot.add_cog(PfpModify(bot)) -- cgit v1.2.3 From 1e1e8aec0d943cecda46fa22300bab3cae108263 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 30 Apr 2021 10:04:07 +0100 Subject: Consistant use of double quotes and tuples in avatar_mod cog --- .../evergreen/avatar_modification/avatar_modify.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index e1078fa0..095de306 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -22,7 +22,7 @@ _EXECUTOR = ThreadPoolExecutor(10) FILENAME_STRING = "{effect}_{author}.png" -with open('bot/resources/pride/gender_options.json') as f: +with open("bot/resources/pride/gender_options.json") as f: GENDER_OPTIONS = json.load(f) @@ -48,10 +48,10 @@ def file_safe_name(effect: str, display_name: str) -> str: file_name = file_name.replace(" ", "_") # Normalize unicode characters - cleaned_filename = unicodedata.normalize('NFKD', file_name).encode('ASCII', 'ignore').decode() + cleaned_filename = unicodedata.normalize("NFKD", file_name).encode("ASCII", "ignore").decode() # Remove invalid filename characters - cleaned_filename = ''.join(c for c in cleaned_filename if c in valid_filename_chars) + cleaned_filename = "".join(c for c in cleaned_filename if c in valid_filename_chars) return cleaned_filename @@ -79,7 +79,7 @@ class AvatarModify(commands.Cog): return member - @commands.group(aliases=('avatar_mod', 'pfp_mod')) + @commands.group(aliases=("avatar_mod", "pfp_mod")) async def avatar_modify(self, ctx: commands.Context) -> None: """Groups all of the pfp modifying commands to allow a single concurrency limit.""" if not ctx.invoked_subcommand: @@ -114,7 +114,7 @@ class AvatarModify(commands.Cog): await ctx.send(embed=embed, file=file) - @avatar_modify.command(aliases=["easterify"], root_aliases=("easterify", "avatareasterify")) + @avatar_modify.command(aliases=("easterify",), root_aliases=("easterify", "avatareasterify")) async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: """ This "Easterifies" the user's avatar. @@ -200,7 +200,7 @@ class AvatarModify(commands.Cog): await ctx.send(file=file, embed=embed) @avatar_modify.group( - aliases=["avatarpride", "pridepfp", "prideprofile"], + aliases=("avatarpride", "pridepfp", "prideprofile"), root_aliases=("prideavatar", "avatarpride", "pridepfp", "prideprofile"), invoke_without_command=True ) @@ -272,12 +272,11 @@ class AvatarModify(commands.Cog): await ctx.send(embed=embed) @avatar_modify.command( - name='spookyavatar', - aliases=('savatar', 'spookify'), - root_aliases=('spookyavatar', 'spookify', 'savatar'), - brief='Spookify an user\'s avatar.' + aliases=("savatar", "spookify"), + root_aliases=("spookyavatar", "spookify", "savatar"), + brief="Spookify an user's avatar." ) - async def spooky_avatar(self, ctx: commands.Context, member: discord.Member = None) -> None: + async def spookyavatar(self, ctx: commands.Context, member: discord.Member = None) -> None: """This "spookifies" the given user's avatar, with a random *spooky* effect.""" if member is None: member = ctx.author -- cgit v1.2.3 From 2bbbef35ebbb01e7671250f58c4a8e7543942b08 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 30 Apr 2021 10:09:37 +0100 Subject: Add non-underscore aliases for lazy people like me :) --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 095de306..a111c480 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -79,7 +79,7 @@ class AvatarModify(commands.Cog): return member - @commands.group(aliases=("avatar_mod", "pfp_mod")) + @commands.group(aliases=("avatar_mod", "pfp_mod", "avatarmod", "pfpmod")) async def avatar_modify(self, ctx: commands.Context) -> None: """Groups all of the pfp modifying commands to allow a single concurrency limit.""" if not ctx.invoked_subcommand: -- cgit v1.2.3 From 477a4d7e095c2cb5ca51339b3e75b287338e3e92 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Fri, 30 Apr 2021 17:32:53 +0100 Subject: fix: remove () from list of safe filename chars --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index a111c480..0baee8b2 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -40,7 +40,7 @@ async def in_executor(func: t.Callable, *args) -> t.Any: def file_safe_name(effect: str, display_name: str) -> str: """Returns a file safe filename based on the given effect and display name.""" - valid_filename_chars = f"-_.() {string.ascii_letters}{string.digits}" + valid_filename_chars = f"-_. {string.ascii_letters}{string.digits}" file_name = FILENAME_STRING.format(effect=effect, author=display_name) -- cgit v1.2.3 From cf110bc4c8b0713291174ac7cfb3730d0ff226a7 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 30 Apr 2021 14:08:14 -0400 Subject: feat: Add the .mosaic command --- bot/exts/evergreen/avatar_modification/_effects.py | 158 ++++++++++++++++++++- .../evergreen/avatar_modification/avatar_modify.py | 50 ++++++- 2 files changed, 202 insertions(+), 6 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/_effects.py b/bot/exts/evergreen/avatar_modification/_effects.py index e415d700..d2370b4b 100644 --- a/bot/exts/evergreen/avatar_modification/_effects.py +++ b/bot/exts/evergreen/avatar_modification/_effects.py @@ -1,3 +1,5 @@ +import math +import random import typing as t from io import BytesIO from pathlib import Path @@ -51,7 +53,7 @@ class PfpEffects: return r, g, b @staticmethod - def crop_avatar_circle(avatar: Image) -> Image: + def crop_avatar_circle(avatar: Image.Image) -> Image.Image: """This crops the avatar given into a circle.""" mask = Image.new("L", avatar.size, 0) draw = ImageDraw.Draw(mask) @@ -60,7 +62,7 @@ class PfpEffects: return avatar @staticmethod - def crop_ring(ring: Image, px: int) -> Image: + def crop_ring(ring: Image.Image, px: int) -> Image.Image: """This crops the given ring into a circle.""" mask = Image.new("L", ring.size, 0) draw = ImageDraw.Draw(mask) @@ -70,7 +72,7 @@ class PfpEffects: return ring @staticmethod - def pridify_effect(image: Image, pixels: int, flag: str) -> Image: + def pridify_effect(image: Image.Image, pixels: int, flag: str) -> Image.Image: """Applies the given pride effect to the given image.""" image = image.resize((1024, 1024)) image = PfpEffects.crop_avatar_circle(image) @@ -83,7 +85,7 @@ class PfpEffects: return image @staticmethod - def eight_bitify_effect(image: Image) -> Image: + def eight_bitify_effect(image: Image.Image) -> Image.Image: """ Applies the 8bit effect to the given image. @@ -95,7 +97,7 @@ class PfpEffects: return image.quantize() @staticmethod - def easterify_effect(image: Image, overlay_image: Image = None) -> Image: + def easterify_effect(image: Image.Image, overlay_image: t.Optional[Image.Image] = None) -> Image.Image: """ Applies the easter effect to the given image. @@ -137,3 +139,149 @@ class PfpEffects: (im.width - overlay_image.width, (im.height - overlay_image.height) // 2) ) return im + + @staticmethod + def split_image(img: Image.Image, squares: int) -> list: + """ + Split an image into a selection of squares, specified by the squares argument. + + Explanation: + + 1. It gets the width and the height of the Image passed to the function. + + 2. It gets the root of a number of squares (number of squares) passed, which is called xy. Reason: if let's say + 25 squares (number of squares) were passed, that is the total squares (split pieces) that the image is supposed + to be split into. As it is known, a 2D shape has a height and a width, and in this case the program thinks of it + as rows and columns. Rows multiplied by columns is equal to the passed squares (number of squares). To get rows + and columns, since in this case, each square (split piece) is identical, rows are equal to columns and the + program treats the image as a square-shaped, it gets the root out of the squares (number of squares) passed. + + 3. Now width and height are both of the original Image, Discord PFP, so when it comes to forming the squares, + the program divides the original image's height and width by the xy. In a case of 25 squares (number of squares) + passed, xy would be 5, so if an image was 250x300, x_frac would be 50 and y_frac - 60. Note: + x_frac stands for a fracture of width. The reason it's called that is because it is shorter to use x for width + in mind and then it's just half of the word fracture, same applies to y_frac, just height instead of width. + x_frac and y_frac are width and height of a single square (split piece). + + 4. With left, top, right, bottom, = 0, 0, x_frac, y_frac, the program sets these variables to create the initial + square (split piece). Explanation: all of these 4 variables start at the top left corner of the Image, by adding + value to right and bottom, it's creating the initial square (split piece). + + 5. In the for loop, it keeps adding those squares (split pieces) in a row and once (index + 1) % xy == 0 is + True, it adds to top and bottom to lower them and reset right and left to recreate the initial space between + them, forming a square (split piece), it also adds the newly created square (split piece) into the new_imgs list + where it stores them. The program keeps repeating this process till all 25 squares get added to the list. + + 6. It returns new_imgs, a list of squares (split pieces). + """ + width, heigth = img.size + + xy = math.sqrt(squares) + + x_frac = width // xy + y_frac = heigth // xy + + left, top, right, bottom, = 0, 0, x_frac, y_frac + + new_imgs = [] + + for index in range(squares): + new_img = img.crop((left, top, right, bottom)) + new_imgs.append(new_img) + + if (index + 1) % xy == 0: + top += y_frac + bottom += y_frac + left = 0 + right = x_frac + else: + left += x_frac + right += x_frac + + return new_imgs + + @staticmethod + def join_images(images: t.List[Image.Image]) -> Image.Image: + """ + Stitches all the image squares into a new image. + + Explanation: + + 1. Shuffles the passed images to randomize the pieces. + + 2. The program gets a single square (split piece) out of the list and defines single_width as the square's width + and single_height as the square's height. + + 3. It gets the root of type integer of the number of images (split pieces) in the list and calls it multiplier. + Program then proceeds to calculate total height and width of the new image that it's creating using the same + multiplier. + + 4. The program then defines new_image as the image that it's creating, using the previously obtained total_width + and total_height. + + 5. Now it defines width_multiplier as well as height with values of 0. These will be used to correctly position + squares (split pieces) onto the new_image canvas. + + 6. Similar to how in the split_image function, the program gets the root of number of images in the list. + In split_image function, it was the passed squares (number of squares) instead of a number of imgs in the + list that it got the square of here. + + 7. In the for loop, as it iterates, the program multiplies single_width by width_multiplier to correctly + position a square (split piece) width wise. It then proceeds to paste the newly positioned square (split piece) + onto the new_image. The program increases the width_multiplier by 1 every iteration so the image wouldn't get + pasted in the same spot and the positioning would move accordingly. It makes sure to increase the + width_multiplier before the check, which checks if the end of a row has been reached, - + (index + 1) % pieces == 0, so after it, if it was True, width_multiplier would have been reset to 0 (start of + the row). If the check returns True, the height gets increased by a single square's (split piece) height to + lower the positioning height wise and, as mentioned, the width_multiplier gets reset to 0 and width will + then be calculated from the start of the new row. The for loop finishes once all the squares (split pieces) were + positioned accordingly. + + 8. Finally, it returns the new_image, the randomized squares (split pieces) stitched back into the format of the + original image - user's PFP. + """ + random.shuffle(images) + single_img = images[0] + + single_wdith = single_img.size[0] + single_height = single_img.size[1] + + multiplier = int(math.sqrt(len(images))) + + total_width = multiplier * single_wdith + total_height = multiplier * single_height + + new_image = Image.new('RGBA', (total_width, total_height), (250, 250, 250)) + + width_multiplier = 0 + height = 0 + + squares = math.sqrt(len(images)) + + for index, image in enumerate(images): + width = single_wdith * width_multiplier + + new_image.paste(image, (width, height)) + + width_multiplier += 1 + + if (index + 1) % squares == 0: + width_multiplier = 0 + height += single_height + + return new_image + + @staticmethod + def mosaic_effect(img_bytes: bytes, squares: int, file_name: str) -> discord.File: + """Seperate function run from an executor which turns an image into a mosaic.""" + avatar = Image.open(BytesIO(img_bytes)) + avatar = avatar.convert('RGBA').resize((1024, 1024)) + + img_squares = PfpEffects.split_image(avatar, squares) + new_img = PfpEffects.join_images(img_squares) + + bufferedio = BytesIO() + new_img.save(bufferedio, format='PNG') + bufferedio.seek(0) + + return discord.File(bufferedio, filename=file_name) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 0baee8b2..afff125f 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -1,6 +1,7 @@ import asyncio import json import logging +import math import string import typing as t import unicodedata @@ -22,11 +23,15 @@ _EXECUTOR = ThreadPoolExecutor(10) FILENAME_STRING = "{effect}_{author}.png" +MAX_SQUARES = 10_000 + +T = t.TypeVar("T") + with open("bot/resources/pride/gender_options.json") as f: GENDER_OPTIONS = json.load(f) -async def in_executor(func: t.Callable, *args) -> t.Any: +async def in_executor(func: t.Callable[..., T], *args) -> T: """ Runs the given synchronus function `func` in an executor. @@ -308,6 +313,49 @@ class AvatarModify(commands.Cog): await ctx.send(file=file, embed=embed) + @avatar_modify.command(name='mosaic', root_aliases=('mosaic',)) + async def mosaic_command(self, ctx: commands.Context, squares: int = 16) -> None: + """Splits your avatar into x squares, randomizes them and stitches them back into a new image!""" + async with ctx.typing(): + if squares < 1: + raise commands.BadArgument("Squares must be a positive number") + + if not math.sqrt(squares).is_integer(): + raise commands.BadArgument("Squares must be a perfect square") + + if squares > MAX_SQUARES: + raise commands.BadArgument(f"Number of squares cannot be higher than {MAX_SQUARES:,}.") + + file_name = file_safe_name("mosaic_avatar", ctx.author.display_name) + img_bytes = await ctx.author.avatar_url.read() + + file = await in_executor( + PfpEffects.mosaic_effect, + img_bytes, + squares, + file_name + ) + + if squares == 1: + title = 'Hooh... that was a lot of work' + description = 'I present to you... Yourself!' + elif squares == MAX_SQUARES: + title = 'Testing the limits I see...' + description = 'What a masterpiece. :star:' + else: + title = 'Your mosaic avatar' + description = 'Here is your avatar. I think it looks a bit *puzzling*' + + embed = discord.Embed( + title=title, + description=description + ) + + embed.set_image(url=f'attachment://{file_name}') + embed.set_footer(text=f'Made by {ctx.author.display_name}', icon_url=ctx.author.avatar_url) + + await ctx.send(file=file, embed=embed) + def setup(bot: commands.Bot) -> None: """Load the PfpModify cog.""" -- cgit v1.2.3 From 10fc58ddfe591115e286284cc5fe2143b8be5cdd Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 30 Apr 2021 14:30:52 -0400 Subject: fix: tempelate -> template --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index fade00e1..ba418166 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -15,6 +15,6 @@ Issues can be skipped with explicit core dev approval, but you have to link the - [ ] Join the [**Python Discord Community**](https://discord.gg/python)? -- [ ] Read all the comments in this tempelate? +- [ ] Read all the comments in this template? - [ ] Ensure there is an issue open, or link relevant discord discussions? - [ ] Read the [contributing guidelines](https://pythondiscord.com/pages/contributing/contributing-guidelines/)? -- cgit v1.2.3 From a1aa1a8ff0e9970cbb492282d10b6cb73aa17b28 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Mon, 3 May 2021 12:28:47 -0400 Subject: fix: Don't pass bot to __init__ --- bot/exts/pride/drag_queen_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/pride/drag_queen_name.py b/bot/exts/pride/drag_queen_name.py index 32ead1bc..9839f089 100644 --- a/bot/exts/pride/drag_queen_name.py +++ b/bot/exts/pride/drag_queen_name.py @@ -30,4 +30,4 @@ class DragNames(commands.Cog): def setup(bot: Bot) -> None: """Load the Drag Queen Cog.""" - bot.add_cog(DragNames(bot)) + bot.add_cog(DragNames()) -- cgit v1.2.3 From 5838d291ff56bac0aaf7a0cfc669c065ddda50b1 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Mon, 3 May 2021 13:18:25 -0400 Subject: chore: Clean Up the Valentines Season - Change commands.Bot type hints to bot.Bot - Remove the setting of Cog.bot if it isn't being used - Change ctx.author -> ctx.message.author --- bot/exts/valentines/be_my_valentine.py | 11 ++++++----- bot/exts/valentines/lovecalculator.py | 9 ++++----- bot/exts/valentines/movie_generator.py | 4 +++- bot/exts/valentines/myvalenstate.py | 7 ++++--- bot/exts/valentines/pickuplines.py | 8 +++----- bot/exts/valentines/savethedate.py | 8 +++----- bot/exts/valentines/valentine_zodiac.py | 8 ++++---- bot/exts/valentines/whoisvalentine.py | 8 +++----- 8 files changed, 30 insertions(+), 33 deletions(-) diff --git a/bot/exts/valentines/be_my_valentine.py b/bot/exts/valentines/be_my_valentine.py index 09591cf8..051f09b8 100644 --- a/bot/exts/valentines/be_my_valentine.py +++ b/bot/exts/valentines/be_my_valentine.py @@ -8,6 +8,7 @@ import discord from discord.ext import commands from discord.ext.commands.cooldowns import BucketType +from bot.bot import Bot from bot.constants import Channels, Colours, Lovefest, Month from bot.utils.decorators import in_month from bot.utils.extensions import invoke_help_command @@ -50,7 +51,7 @@ class BeMyValentine(commands.Cog): async def add_role(self, ctx: commands.Context) -> None: """Adds the lovefest role.""" user = ctx.author - role = discord.utils.get(ctx.guild.roles, id=Lovefest.role_id) + role = ctx.guild.get_role(Lovefest.role_id) if Lovefest.role_id not in [role.id for role in ctx.message.author.roles]: await user.add_roles(role) await ctx.send("The Lovefest role has been added !") @@ -61,12 +62,12 @@ class BeMyValentine(commands.Cog): async def remove_role(self, ctx: commands.Context) -> None: """Removes the lovefest role.""" user = ctx.author - role = discord.utils.get(ctx.guild.roles, id=Lovefest.role_id) - if Lovefest.role_id not in [role.id for role in ctx.message.author.roles]: + role = ctx.guild.get_role(Lovefest.role_id) + if role not in ctx.author.roles: await ctx.send("You dont have the lovefest role.") else: await user.remove_roles(role) - await ctx.send("The lovefest role has been successfully removed !") + await ctx.send("The lovefest role has been successfully removed!") @commands.cooldown(1, 1800, BucketType.user) @commands.group(name='bemyvalentine', invoke_without_command=True) @@ -196,6 +197,6 @@ class BeMyValentine(commands.Cog): return valentine_compliment -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Be my Valentine Cog load.""" bot.add_cog(BeMyValentine(bot)) diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index 966acc82..c2a5da26 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -11,6 +11,8 @@ from discord import Member from discord.ext import commands from discord.ext.commands import BadArgument, Cog, clean_content +from bot.bot import Bot + log = logging.getLogger(__name__) with Path("bot/resources/valentines/love_matches.json").open(encoding="utf8") as file: @@ -21,9 +23,6 @@ with Path("bot/resources/valentines/love_matches.json").open(encoding="utf8") as class LoveCalculator(Cog): """A cog for calculating the love between two people.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - @commands.command(aliases=('love_calculator', 'love_calc')) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) async def love(self, ctx: commands.Context, who: Union[Member, str], whom: Union[Member, str] = None) -> None: @@ -93,6 +92,6 @@ class LoveCalculator(Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Love calculator Cog load.""" - bot.add_cog(LoveCalculator(bot)) + bot.add_cog(LoveCalculator()) diff --git a/bot/exts/valentines/movie_generator.py b/bot/exts/valentines/movie_generator.py index 4df9e0d5..461255ff 100644 --- a/bot/exts/valentines/movie_generator.py +++ b/bot/exts/valentines/movie_generator.py @@ -6,6 +6,8 @@ from urllib import parse import discord from discord.ext import commands +from bot.bot import Bot + TMDB_API_KEY = environ.get("TMDB_API_KEY") log = logging.getLogger(__name__) @@ -59,6 +61,6 @@ class RomanceMovieFinder(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Romance movie Cog load.""" bot.add_cog(RomanceMovieFinder(bot)) diff --git a/bot/exts/valentines/myvalenstate.py b/bot/exts/valentines/myvalenstate.py index 041af7af..19e6b57f 100644 --- a/bot/exts/valentines/myvalenstate.py +++ b/bot/exts/valentines/myvalenstate.py @@ -7,6 +7,7 @@ from random import choice import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours log = logging.getLogger(__name__) @@ -46,7 +47,7 @@ class MyValenstate(commands.Cog): """Find the vacation spot(s) with the most matching characters to the invoking user.""" eq_chars = collections.defaultdict(int) if name is None: - author = ctx.message.author.name.lower().replace(' ', '') + author = ctx.author.name.lower().replace(' ', '') else: author = name.lower().replace(' ', '') @@ -60,7 +61,7 @@ class MyValenstate(commands.Cog): embed_title = "But there are more!" if len(matches) > 1: - leftovers = f"{', '.join(matches[:len(matches)-2])}, and {matches[len(matches)-1]}" + leftovers = f"{', '.join(matches[:-2])}, and {matches[-1]}" embed_text = f"You have {len(matches)} more matches, these being {leftovers}." elif len(matches) == 1: embed_title = "But there's another one!" @@ -81,6 +82,6 @@ class MyValenstate(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Valenstate Cog load.""" bot.add_cog(MyValenstate(bot)) diff --git a/bot/exts/valentines/pickuplines.py b/bot/exts/valentines/pickuplines.py index 74c7e68b..bb322016 100644 --- a/bot/exts/valentines/pickuplines.py +++ b/bot/exts/valentines/pickuplines.py @@ -6,6 +6,7 @@ from pathlib import Path import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours log = logging.getLogger(__name__) @@ -17,9 +18,6 @@ with open(Path("bot/resources/valentines/pickup_lines.json"), "r", encoding="utf class PickupLine(commands.Cog): """A cog that gives random cheesy pickup lines.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - @commands.command() async def pickupline(self, ctx: commands.Context) -> None: """ @@ -39,6 +37,6 @@ class PickupLine(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Pickup lines Cog load.""" - bot.add_cog(PickupLine(bot)) + bot.add_cog(PickupLine()) diff --git a/bot/exts/valentines/savethedate.py b/bot/exts/valentines/savethedate.py index ac38d279..bda5d8c6 100644 --- a/bot/exts/valentines/savethedate.py +++ b/bot/exts/valentines/savethedate.py @@ -6,6 +6,7 @@ from pathlib import Path import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours log = logging.getLogger(__name__) @@ -19,9 +20,6 @@ with open(Path("bot/resources/valentines/date_ideas.json"), "r", encoding="utf8" class SaveTheDate(commands.Cog): """A cog that gives random suggestion for a Valentine's date.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - @commands.command() async def savethedate(self, ctx: commands.Context) -> None: """Gives you ideas for what to do on a date with your valentine.""" @@ -36,6 +34,6 @@ class SaveTheDate(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Save the date Cog Load.""" - bot.add_cog(SaveTheDate(bot)) + bot.add_cog(SaveTheDate()) diff --git a/bot/exts/valentines/valentine_zodiac.py b/bot/exts/valentines/valentine_zodiac.py index 2696999f..237fe5db 100644 --- a/bot/exts/valentines/valentine_zodiac.py +++ b/bot/exts/valentines/valentine_zodiac.py @@ -9,6 +9,7 @@ from typing import Tuple, Union import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours log = logging.getLogger(__name__) @@ -20,8 +21,7 @@ HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_hea class ValentineZodiac(commands.Cog): """A Cog that returns a counter compatible zodiac sign to the given user's zodiac sign.""" - def __init__(self, bot: commands.Bot): - self.bot = bot + def __init__(self): self.zodiacs, self.zodiac_fact = self.load_comp_json() @staticmethod @@ -141,6 +141,6 @@ class ValentineZodiac(commands.Cog): log.trace("Embed from date successfully sent.") -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Valentine zodiac Cog load.""" - bot.add_cog(ValentineZodiac(bot)) + bot.add_cog(ValentineZodiac()) diff --git a/bot/exts/valentines/whoisvalentine.py b/bot/exts/valentines/whoisvalentine.py index ab6b1ca1..73cdcf52 100644 --- a/bot/exts/valentines/whoisvalentine.py +++ b/bot/exts/valentines/whoisvalentine.py @@ -6,6 +6,7 @@ from random import choice import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours log = logging.getLogger(__name__) @@ -17,9 +18,6 @@ with open(Path("bot/resources/valentines/valentine_facts.json"), "r", encoding=" class ValentineFacts(commands.Cog): """A Cog for displaying facts about Saint Valentine.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - @commands.command(aliases=('whoisvalentine', 'saint_valentine')) async def who_is_valentine(self, ctx: commands.Context) -> None: """Displays info about Saint Valentine.""" @@ -47,6 +45,6 @@ class ValentineFacts(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Who is Valentine Cog load.""" - bot.add_cog(ValentineFacts(bot)) + bot.add_cog(ValentineFacts()) -- cgit v1.2.3 From e9f33306b5344c5d7e0877b0bf63ff9c914e6f85 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Mon, 3 May 2021 13:36:26 -0400 Subject: Clean Up the Easter Season - Keep Consistent setup docstrings - Type hint to bot.Bot instead of commands.Bot - Don't set Cog.bot if it isn't used --- bot/exts/easter/april_fools_vids.py | 8 +++++--- bot/exts/easter/bunny_name_generator.py | 9 ++++----- bot/exts/easter/earth_photos.py | 5 +++-- bot/exts/easter/easter_riddle.py | 5 +++-- bot/exts/easter/egg_decorating.py | 8 +++----- bot/exts/easter/egg_facts.py | 2 +- bot/exts/easter/egghead_quiz.py | 10 +++++----- bot/exts/easter/save_the_planet.py | 10 ++++------ bot/exts/easter/traditions.py | 11 +++++------ 9 files changed, 33 insertions(+), 35 deletions(-) diff --git a/bot/exts/easter/april_fools_vids.py b/bot/exts/easter/april_fools_vids.py index c7a3c014..84aa2913 100644 --- a/bot/exts/easter/april_fools_vids.py +++ b/bot/exts/easter/april_fools_vids.py @@ -4,6 +4,8 @@ from json import load from discord.ext import commands +from bot.bot import Bot + log = logging.getLogger(__name__) with open("bot/resources/easter/april_fools_vids.json", encoding="utf-8") as f: @@ -23,6 +25,6 @@ class AprilFoolVideos(commands.Cog): await ctx.send(f"Check out this April Fools' video by {channel}.\n\n{url}") -def setup(bot: commands.Bot) -> None: - """April Fools' Cog load.""" - bot.add_cog(AprilFoolVideos(bot)) +def setup(bot: Bot) -> None: + """Load the April Fools' Cog.""" + bot.add_cog(AprilFoolVideos()) diff --git a/bot/exts/easter/bunny_name_generator.py b/bot/exts/easter/bunny_name_generator.py index 3ecf9be9..6d9e2a57 100644 --- a/bot/exts/easter/bunny_name_generator.py +++ b/bot/exts/easter/bunny_name_generator.py @@ -7,6 +7,8 @@ from typing import List, Union from discord.ext import commands +from bot.bot import Bot + log = logging.getLogger(__name__) with Path("bot/resources/easter/bunny_names.json").open("r", encoding="utf8") as f: @@ -16,9 +18,6 @@ with Path("bot/resources/easter/bunny_names.json").open("r", encoding="utf8") as class BunnyNameGenerator(commands.Cog): """Generate a random bunny name, or bunnify your Discord username!""" - def __init__(self, bot: commands.Bot): - self.bot = bot - def find_separators(self, displayname: str) -> Union[List[str], None]: """Check if Discord name contains spaces so we can bunnify an individual word in the name.""" new_name = re.split(r'[_.\s]', displayname) @@ -87,6 +86,6 @@ class BunnyNameGenerator(commands.Cog): await ctx.send(bunnified_name) -def setup(bot: commands.Bot) -> None: - """Bunny Name Generator Cog load.""" +def setup(bot: Bot) -> None: + """Load the Bunny Name Generator Cog.""" bot.add_cog(BunnyNameGenerator(bot)) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index bf658391..d7e7ccc3 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -3,6 +3,7 @@ import logging import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours from bot.constants import Tokens @@ -12,7 +13,7 @@ log = logging.getLogger(__name__) class EarthPhotos(commands.Cog): """This cog contains the command for earth photos.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @commands.command(aliases=["earth"]) @@ -55,7 +56,7 @@ class EarthPhotos(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the Earth Photos cog.""" if not Tokens.unsplash_access_key: log.warning("No Unsplash access key found. Cog not loading.") diff --git a/bot/exts/easter/easter_riddle.py b/bot/exts/easter/easter_riddle.py index a93b3745..da66edf5 100644 --- a/bot/exts/easter/easter_riddle.py +++ b/bot/exts/easter/easter_riddle.py @@ -7,6 +7,7 @@ from pathlib import Path import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours, NEGATIVE_REPLIES log = logging.getLogger(__name__) @@ -20,7 +21,7 @@ TIMELIMIT = 10 class EasterRiddle(commands.Cog): """This cog contains the command for the Easter quiz!""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot self.winners = set() self.correct = "" @@ -106,6 +107,6 @@ class EasterRiddle(commands.Cog): self.winners.add(message.author.mention) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Easter Riddle Cog load.""" bot.add_cog(EasterRiddle(bot)) diff --git a/bot/exts/easter/egg_decorating.py b/bot/exts/easter/egg_decorating.py index a847388d..b8a8c6a7 100644 --- a/bot/exts/easter/egg_decorating.py +++ b/bot/exts/easter/egg_decorating.py @@ -10,6 +10,7 @@ import discord from PIL import Image from discord.ext import commands +from bot.bot import Bot from bot.utils import helpers log = logging.getLogger(__name__) @@ -33,9 +34,6 @@ IRREPLACEABLE = [ class EggDecorating(commands.Cog): """Decorate some easter eggs!""" - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - @staticmethod def replace_invalid(colour: str) -> Union[int, None]: """Attempts to match with HTML or XKCD colour names, returning the int value.""" @@ -115,6 +113,6 @@ class EggDecorating(commands.Cog): return new_im -def setup(bot: commands.bot) -> None: - """Egg decorating Cog load.""" +def setup(bot: Bot) -> None: + """Load the Egg decorating Cog.""" bot.add_cog(EggDecorating(bot)) diff --git a/bot/exts/easter/egg_facts.py b/bot/exts/easter/egg_facts.py index 761e9059..78a5e592 100644 --- a/bot/exts/easter/egg_facts.py +++ b/bot/exts/easter/egg_facts.py @@ -57,5 +57,5 @@ class EasterFacts(commands.Cog): def setup(bot: Bot) -> None: - """Easter Egg facts cog load.""" + """Load the Easter Egg facts Cog.""" bot.add_cog(EasterFacts(bot)) diff --git a/bot/exts/easter/egghead_quiz.py b/bot/exts/easter/egghead_quiz.py index 0498d9db..e950bc2e 100644 --- a/bot/exts/easter/egghead_quiz.py +++ b/bot/exts/easter/egghead_quiz.py @@ -8,6 +8,7 @@ from typing import Union import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours log = logging.getLogger(__name__) @@ -31,8 +32,7 @@ TIMELIMIT = 30 class EggheadQuiz(commands.Cog): """This cog contains the command for the Easter quiz!""" - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot + def __init__(self) -> None: self.quiz_messages = {} @commands.command(aliases=["eggheadquiz", "easterquiz"]) @@ -114,6 +114,6 @@ class EggheadQuiz(commands.Cog): return await reaction.message.remove_reaction(reaction, user) -def setup(bot: commands.Bot) -> None: - """Egghead Quiz Cog load.""" - bot.add_cog(EggheadQuiz(bot)) +def setup(bot: Bot) -> None: + """Load the Egghead Quiz Cog.""" + bot.add_cog(EggheadQuiz()) diff --git a/bot/exts/easter/save_the_planet.py b/bot/exts/easter/save_the_planet.py index 8f644259..db9d3498 100644 --- a/bot/exts/easter/save_the_planet.py +++ b/bot/exts/easter/save_the_planet.py @@ -4,6 +4,7 @@ from pathlib import Path from discord import Embed from discord.ext import commands +from bot.bot import Bot from bot.utils.randomization import RandomCycle @@ -14,9 +15,6 @@ with Path("bot/resources/easter/save_the_planet.json").open('r', encoding='utf8' class SaveThePlanet(commands.Cog): """A cog that teaches users how they can help our planet.""" - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - @commands.command(aliases=('savetheearth', 'saveplanet', 'saveearth')) async def savetheplanet(self, ctx: commands.Context) -> None: """Responds with a random tip on how to be eco-friendly and help our planet.""" @@ -24,6 +22,6 @@ class SaveThePlanet(commands.Cog): await ctx.send(embed=return_embed) -def setup(bot: commands.Bot) -> None: - """Save the Planet Cog load.""" - bot.add_cog(SaveThePlanet(bot)) +def setup(bot: Bot) -> None: + """Load the Save the Planet Cog.""" + bot.add_cog(SaveThePlanet()) diff --git a/bot/exts/easter/traditions.py b/bot/exts/easter/traditions.py index 85b4adfb..19e69b98 100644 --- a/bot/exts/easter/traditions.py +++ b/bot/exts/easter/traditions.py @@ -5,6 +5,8 @@ from pathlib import Path from discord.ext import commands +from bot.bot import Bot + log = logging.getLogger(__name__) with open(Path("bot/resources/easter/traditions.json"), "r", encoding="utf8") as f: @@ -14,9 +16,6 @@ with open(Path("bot/resources/easter/traditions.json"), "r", encoding="utf8") as class Traditions(commands.Cog): """A cog which allows users to get a random easter tradition or custom from a random country.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - @commands.command(aliases=('eastercustoms',)) async def easter_tradition(self, ctx: commands.Context) -> None: """Responds with a random tradition or custom.""" @@ -25,6 +24,6 @@ class Traditions(commands.Cog): await ctx.send(f"{random_country}:\n{traditions[random_country]}") -def setup(bot: commands.Bot) -> None: - """Traditions Cog load.""" - bot.add_cog(Traditions(bot)) +def setup(bot: Bot) -> None: + """Load the Traditions Cog.""" + bot.add_cog(Traditions()) -- cgit v1.2.3 From 5189259ea3332bffa61863452f7f07bec0f4b533 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Mon, 3 May 2021 14:06:05 -0400 Subject: chore: End the sentence with a period --- bot/exts/evergreen/battleship.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/battleship.py b/bot/exts/evergreen/battleship.py index 813f998e..78fb0937 100644 --- a/bot/exts/evergreen/battleship.py +++ b/bot/exts/evergreen/battleship.py @@ -440,5 +440,5 @@ class Battleship(commands.Cog): def setup(bot: Bot) -> None: - """Load the Battleship cog""" + """Load the Battleship Cog.""" bot.add_cog(Battleship(bot)) -- cgit v1.2.3 From a7279c624b093ffe826edadbc999103083710953 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Mon, 3 May 2021 19:23:52 -0400 Subject: chore: Don't return a Message object when the return annotation is None --- bot/exts/easter/easter_riddle.py | 3 ++- bot/exts/easter/egg_decorating.py | 9 ++++++--- bot/exts/evergreen/battleship.py | 12 ++++++++---- bot/exts/evergreen/reddit.py | 9 ++++++--- bot/exts/evergreen/snakes/_snakes_cog.py | 6 ++++-- bot/exts/evergreen/trivia_quiz.py | 3 ++- bot/utils/pagination.py | 10 ++++++---- 7 files changed, 34 insertions(+), 18 deletions(-) diff --git a/bot/exts/easter/easter_riddle.py b/bot/exts/easter/easter_riddle.py index da66edf5..9a253a6a 100644 --- a/bot/exts/easter/easter_riddle.py +++ b/bot/exts/easter/easter_riddle.py @@ -35,7 +35,8 @@ class EasterRiddle(commands.Cog): The duration of the hint interval can be configured by changing the TIMELIMIT constant in this file. """ if self.current_channel: - return await ctx.send(f"A riddle is already being solved in {self.current_channel.mention}!") + await ctx.send(f"A riddle is already being solved in {self.current_channel.mention}!") + return # Don't let users start in a DM if not ctx.guild: diff --git a/bot/exts/easter/egg_decorating.py b/bot/exts/easter/egg_decorating.py index b8a8c6a7..1432fa31 100644 --- a/bot/exts/easter/egg_decorating.py +++ b/bot/exts/easter/egg_decorating.py @@ -54,7 +54,8 @@ class EggDecorating(commands.Cog): Discord colour names, HTML colour names, XKCD colour names and hex values are accepted. """ if len(colours) < 2: - return await ctx.send("You must include at least 2 colours!") + await ctx.send("You must include at least 2 colours!") + return invalid = [] colours = list(colours) @@ -68,9 +69,11 @@ class EggDecorating(commands.Cog): invalid.append(helpers.suppress_links(colour)) if len(invalid) > 1: - return await ctx.send(f"Sorry, I don't know these colours: {' '.join(invalid)}") + await ctx.send(f"Sorry, I don't know these colours: {' '.join(invalid)}") + return elif len(invalid) == 1: - return await ctx.send(f"Sorry, I don't know the colour {invalid[0]}!") + await ctx.send(f"Sorry, I don't know the colour {invalid[0]}!") + return async with ctx.typing(): # Expand list to 8 colours diff --git a/bot/exts/evergreen/battleship.py b/bot/exts/evergreen/battleship.py index 78fb0937..d4584ae8 100644 --- a/bot/exts/evergreen/battleship.py +++ b/bot/exts/evergreen/battleship.py @@ -379,10 +379,12 @@ class Battleship(commands.Cog): Make sure you have your DMs open so that the bot can message you. """ if self.already_playing(ctx.author): - return await ctx.send("You're already playing a game!") + await ctx.send("You're already playing a game!") + return if ctx.author in self.waiting: - return await ctx.send("You've already sent out a request for a player 2.") + await ctx.send("You've already sent out a request for a player 2.") + return announcement = await ctx.send( "**Battleship**: A new game is about to start!\n" @@ -402,12 +404,14 @@ class Battleship(commands.Cog): except asyncio.TimeoutError: self.waiting.remove(ctx.author) await announcement.delete() - return await ctx.send(f"{ctx.author.mention} Seems like there's no one here to play...") + await ctx.send(f"{ctx.author.mention} Seems like there's no one here to play...") + return if str(reaction.emoji) == CROSS_EMOJI: self.waiting.remove(ctx.author) await announcement.delete() - return await ctx.send(f"{ctx.author.mention} Game cancelled.") + await ctx.send(f"{ctx.author.mention} Game cancelled.") + return await announcement.delete() self.waiting.remove(ctx.author) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index 518ffeb7..51a360b3 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -51,15 +51,18 @@ class Reddit(commands.Cog): try: posts = data["data"]["children"] except KeyError: - return await ctx.send('Subreddit not found!') + await ctx.send('Subreddit not found!') + return if not posts: - return await ctx.send('No posts available!') + await ctx.send('No posts available!') + return if posts[0]["data"]["over_18"] is True: - return await ctx.send( + await ctx.send( "You cannot access this Subreddit as it is meant for those who " "are 18 years or older." ) + return embed_titles = "" diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index 70093912..c8633ce7 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -643,7 +643,8 @@ class Snakes(Cog): data = await self._get_snek(name) if data.get('error'): - return await ctx.send('Could not fetch data from Wikipedia.') + await ctx.send('Could not fetch data from Wikipedia.') + return description = data["info"] @@ -900,7 +901,8 @@ class Snakes(Cog): color=SNAKE_COLOR ) - return await ctx.send(embed=embed) + await ctx.send(embed=embed) + return @snakes_group.command(name='sal') @locked() diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index f40375a6..bfd7d357 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -62,10 +62,11 @@ class TriviaQuiz(commands.Cog): # Stop game if running. if self.game_status[ctx.channel.id] is True: - return await ctx.send( + await ctx.send( f"Game is already running..." f"do `{self.bot.command_prefix}quiz stop`" ) + return # Send embed showing available categories if inputted category is invalid. if category is None: diff --git a/bot/utils/pagination.py b/bot/utils/pagination.py index a4d0cc56..a97dd023 100644 --- a/bot/utils/pagination.py +++ b/bot/utils/pagination.py @@ -79,7 +79,7 @@ class LinePaginator(Paginator): prefix: str = "", suffix: str = "", max_lines: Optional[int] = None, max_size: int = 500, empty: bool = True, restrict_to_user: User = None, timeout: int = 300, footer_text: str = None, url: str = None, - exception_on_empty_embed: bool = False): + exception_on_empty_embed: bool = False) -> None: """ Use a paginator and set of reactions to provide pagination over a set of lines. @@ -157,7 +157,8 @@ class LinePaginator(Paginator): log.trace(f"Setting embed url to '{url}'") log.debug("There's less than two pages, so we won't paginate - sending single page on its own") - return await ctx.send(embed=embed) + await ctx.send(embed=embed) + return else: if footer_text: embed.set_footer(text=f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})") @@ -302,7 +303,7 @@ class ImagePaginator(Paginator): @classmethod async def paginate(cls, pages: List[Tuple[str, str]], ctx: Context, embed: Embed, prefix: str = "", suffix: str = "", timeout: int = 300, - exception_on_empty_embed: bool = False): + exception_on_empty_embed: bool = False) -> None: """ Use a paginator and set of reactions to provide pagination over a set of title/image pairs. @@ -352,7 +353,8 @@ class ImagePaginator(Paginator): embed.set_image(url=image) if len(paginator.pages) <= 1: - return await ctx.send(embed=embed) + await ctx.send(embed=embed) + return embed.set_footer(text=f"Page {current_page + 1}/{len(paginator.pages)}") message = await ctx.send(embed=embed) -- cgit v1.2.3 From 8769624eac7e86e634fcef39c8fb81c4abc2f48e Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Tue, 4 May 2021 11:30:19 -0400 Subject: chore: Don't make content an empty string Co-authored-by: Matteo Bertucci --- bot/exts/evergreen/snakes/_snakes_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index c8633ce7..e75f958c 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -829,7 +829,7 @@ class Snakes(Cog): ) ) - quiz = await ctx.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',)) -- cgit v1.2.3 From a6cc40ff3b323dff112d7f8c339e124f3a6d9980 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Tue, 4 May 2021 12:57:03 -0400 Subject: chore: Prefer double quotes over single quotes --- bot/bot.py | 2 +- bot/exts/christmas/advent_of_code/_helpers.py | 8 +- bot/exts/christmas/hanukkah_embed.py | 28 ++--- bot/exts/easter/april_fools_vids.py | 2 +- bot/exts/easter/bunny_name_generator.py | 16 +-- bot/exts/easter/earth_photos.py | 2 +- bot/exts/easter/egg_facts.py | 2 +- bot/exts/easter/egghead_quiz.py | 12 +- bot/exts/easter/save_the_planet.py | 4 +- bot/exts/easter/traditions.py | 2 +- bot/exts/evergreen/cheatsheet.py | 4 +- bot/exts/evergreen/connect_four.py | 6 +- bot/exts/evergreen/conversationstarters.py | 12 +- bot/exts/evergreen/emoji.py | 6 +- bot/exts/evergreen/error_handler.py | 4 +- bot/exts/evergreen/game.py | 4 +- bot/exts/evergreen/githubinfo.py | 42 +++---- bot/exts/evergreen/help.py | 74 ++++++------- bot/exts/evergreen/issues.py | 4 +- bot/exts/evergreen/minesweeper.py | 6 +- bot/exts/evergreen/movie.py | 20 ++-- bot/exts/evergreen/pythonfacts.py | 8 +- bot/exts/evergreen/recommend_game.py | 10 +- bot/exts/evergreen/reddit.py | 20 ++-- bot/exts/evergreen/snakes/_converter.py | 12 +- bot/exts/evergreen/snakes/_snakes_cog.py | 152 +++++++++++++------------- bot/exts/evergreen/snakes/_utils.py | 34 +++--- bot/exts/evergreen/source.py | 2 +- bot/exts/evergreen/speedrun.py | 2 +- bot/exts/evergreen/status_codes.py | 16 +-- bot/exts/evergreen/tic_tac_toe.py | 2 +- bot/exts/evergreen/trivia_quiz.py | 2 +- bot/exts/evergreen/wikipedia.py | 10 +- bot/exts/evergreen/wolfram.py | 4 +- bot/exts/evergreen/xkcd.py | 4 +- bot/exts/halloween/8ball.py | 2 +- bot/exts/halloween/candy_collection.py | 18 +-- bot/exts/halloween/hacktober-issue-finder.py | 2 +- bot/exts/halloween/hacktoberstats.py | 8 +- bot/exts/halloween/halloweenify.py | 4 +- bot/exts/halloween/monsterbio.py | 2 +- bot/exts/halloween/monstersurvey.py | 84 +++++++------- bot/exts/halloween/scarymovie.py | 22 ++-- bot/exts/halloween/spookygif.py | 6 +- bot/exts/halloween/spookynamerate.py | 2 +- bot/exts/halloween/spookyrating.py | 10 +- bot/exts/halloween/spookyreact.py | 14 +-- bot/exts/internal_eval/_internal_eval.py | 8 +- bot/exts/valentines/be_my_valentine.py | 34 +++--- bot/exts/valentines/myvalenstate.py | 10 +- bot/exts/valentines/pickuplines.py | 8 +- bot/exts/valentines/savethedate.py | 2 +- bot/exts/valentines/valentine_zodiac.py | 32 +++--- bot/exts/valentines/whoisvalentine.py | 12 +- bot/utils/__init__.py | 14 +-- bot/utils/checks.py | 4 +- bot/utils/decorators.py | 2 +- bot/utils/extensions.py | 4 +- bot/utils/halloween/spookifications.py | 10 +- bot/utils/pagination.py | 8 +- 60 files changed, 430 insertions(+), 430 deletions(-) diff --git a/bot/bot.py b/bot/bot.py index 7e495940..b8de97aa 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -101,7 +101,7 @@ class Bot(commands.Bot): all_channels_ids = [channel.id for channel in self.get_all_channels()] for name, channel_id in vars(constants.Channels).items(): - if name.startswith('_'): + if name.startswith("_"): continue if channel_id not in all_channels_ids: log.error(f'Channel "{name}" with ID {channel_id} missing') diff --git a/bot/exts/christmas/advent_of_code/_helpers.py b/bot/exts/christmas/advent_of_code/_helpers.py index a16a4871..f4a258c0 100644 --- a/bot/exts/christmas/advent_of_code/_helpers.py +++ b/bot/exts/christmas/advent_of_code/_helpers.py @@ -108,7 +108,7 @@ def _parse_raw_leaderboard_data(raw_leaderboard_data: dict) -> dict: # star view. We need that per star view to compute rank scores per star. for member in raw_leaderboard_data.values(): name = member["name"] if member["name"] else f"Anonymous #{member['id']}" - member_id = member['id'] + member_id = member["id"] leaderboard[member_id] = {"name": name, "score": 0, "star_1": 0, "star_2": 0} # Iterate over all days for this participant @@ -119,7 +119,7 @@ def _parse_raw_leaderboard_data(raw_leaderboard_data: dict) -> dict: leaderboard[member_id][f"star_{star}"] += 1 # Record completion datetime for this participant for this day/star - completion_time = datetime.datetime.fromtimestamp(int(data['get_star_ts'])) + completion_time = datetime.datetime.fromtimestamp(int(data["get_star_ts"])) star_results[(day, star)].append( StarResult(member_id=member_id, completion_time=completion_time) ) @@ -133,7 +133,7 @@ def _parse_raw_leaderboard_data(raw_leaderboard_data: dict) -> dict: if day in AdventOfCode.ignored_days: continue - sorted_result = sorted(results, key=operator.attrgetter('completion_time')) + sorted_result = sorted(results, key=operator.attrgetter("completion_time")) for rank, star_result in enumerate(sorted_result): leaderboard[star_result.member_id]["score"] += max_score - rank @@ -307,7 +307,7 @@ async def fetch_leaderboard(invalidate_cache: bool = False) -> dict: def get_summary_embed(leaderboard: dict) -> discord.Embed: """Get an embed with the current summary stats of the leaderboard.""" - leaderboard_url = leaderboard['full_leaderboard_url'] + leaderboard_url = leaderboard["full_leaderboard_url"] refresh_minutes = AdventOfCode.leaderboard_cache_expiry_seconds // 60 aoc_embed = discord.Embed( diff --git a/bot/exts/christmas/hanukkah_embed.py b/bot/exts/christmas/hanukkah_embed.py index cd8a9192..32002f76 100644 --- a/bot/exts/christmas/hanukkah_embed.py +++ b/bot/exts/christmas/hanukkah_embed.py @@ -28,15 +28,15 @@ class HanukkahEmbed(commands.Cog): hanukkah_dates = [] async with self.bot.http_session.get(self.url) as response: json_data = await response.json() - festivals = json_data['items'] + festivals = json_data["items"] for festival in festivals: - if festival['title'].startswith('Chanukah'): - date = festival['date'] + if festival["title"].startswith("Chanukah"): + date = festival["date"] hanukkah_dates.append(date) return hanukkah_dates @in_month(Month.DECEMBER) - @commands.command(name='hanukkah', aliases=['chanukah']) + @commands.command(name="hanukkah", aliases=["chanukah"]) async def hanukkah_festival(self, ctx: commands.Context) -> None: """Tells you about the Hanukkah Festivaltime of festival, festival day, etc).""" hanukkah_dates = await self.get_hanukkah_dates() @@ -56,7 +56,7 @@ class HanukkahEmbed(commands.Cog): month = str(today.month) year = str(today.year) embed = Embed() - embed.title = 'Hanukkah' + embed.title = "Hanukkah" embed.colour = Colours.blue if day in self.hanukkah_days and month in self.hanukkah_months and year in self.hanukkah_years: if int(day) == hanukkah_start_day: @@ -69,13 +69,13 @@ class HanukkahEmbed(commands.Cog): await ctx.send(embed=embed) return elif hours > hanukkah_start_hour: - embed.description = (f'It is the starting day of Hanukkah ! ' - f'Its been {hours-hanukkah_start_hour} hours hanukkah started !') + embed.description = (f"It is the starting day of Hanukkah ! " + f"Its been {hours-hanukkah_start_hour} hours hanukkah started !") await ctx.send(embed=embed) return festival_day = self.hanukkah_days.index(day) - number_suffixes = ['st', 'nd', 'rd', 'th'] - suffix = '' + number_suffixes = ["st", "nd", "rd", "th"] + suffix = "" if int(festival_day) == 1: suffix = number_suffixes[0] if int(festival_day) == 2: @@ -84,19 +84,19 @@ class HanukkahEmbed(commands.Cog): suffix = number_suffixes[2] if int(festival_day) > 3: suffix = number_suffixes[3] - message = '' + message = "" for _ in range(1, festival_day + 1): - message += ':menorah:' - embed.description = f'It is the {festival_day}{suffix} day of Hanukkah ! \n {message}' + message += ":menorah:" + embed.description = f"It is the {festival_day}{suffix} day of Hanukkah ! \n {message}" await ctx.send(embed=embed) else: if today < hanukkah_start: - festival_starting_month = hanukkah_start.strftime('%B') + festival_starting_month = hanukkah_start.strftime("%B") embed.description = (f"Hanukkah has not started yet. " f"Hanukkah will start at sundown on {hanukkah_start_day}th " f"of {festival_starting_month}.") else: - festival_end_month = hanukkah_end.strftime('%B') + festival_end_month = hanukkah_end.strftime("%B") embed.description = (f"Looks like you missed Hanukkah !" f"Hanukkah ended on {hanukkah_end_day}th of {festival_end_month}.") diff --git a/bot/exts/easter/april_fools_vids.py b/bot/exts/easter/april_fools_vids.py index 84aa2913..3ce1f72a 100644 --- a/bot/exts/easter/april_fools_vids.py +++ b/bot/exts/easter/april_fools_vids.py @@ -15,7 +15,7 @@ with open("bot/resources/easter/april_fools_vids.json", encoding="utf-8") as f: class AprilFoolVideos(commands.Cog): """A cog for April Fools' that gets a random April Fools' video from Youtube.""" - @commands.command(name='fool') + @commands.command(name="fool") async def april_fools(self, ctx: commands.Context) -> None: """Get a random April Fools' video from Youtube.""" video = random.choice(ALL_VIDS) diff --git a/bot/exts/easter/bunny_name_generator.py b/bot/exts/easter/bunny_name_generator.py index 6d9e2a57..23f85226 100644 --- a/bot/exts/easter/bunny_name_generator.py +++ b/bot/exts/easter/bunny_name_generator.py @@ -20,7 +20,7 @@ class BunnyNameGenerator(commands.Cog): def find_separators(self, displayname: str) -> Union[List[str], None]: """Check if Discord name contains spaces so we can bunnify an individual word in the name.""" - new_name = re.split(r'[_.\s]', displayname) + new_name = re.split(r"[_.\s]", displayname) if displayname not in new_name: return new_name @@ -33,11 +33,11 @@ class BunnyNameGenerator(commands.Cog): Only the most recently matched pattern will apply the changes. """ expressions = [ - (r'a.+y', 'patchy'), - (r'e.+y', 'ears'), - (r'i.+y', 'ditsy'), - (r'o.+y', 'oofy'), - (r'u.+y', 'uffy'), + ("a.+y", "patchy"), + ("e.+y", "ears"), + ("i.+y", "ditsy"), + ("o.+y", "oofy"), + ("u.+y", "uffy"), ] for exp, vowel_sub in expressions: @@ -47,7 +47,7 @@ class BunnyNameGenerator(commands.Cog): def append_name(self, displayname: str) -> str: """Adds a suffix to the end of the Discord name.""" - extensions = ['foot', 'ear', 'nose', 'tail'] + extensions = ["foot", "ear", "nose", "tail"] suffix = random.choice(extensions) appended_name = displayname + suffix @@ -74,7 +74,7 @@ class BunnyNameGenerator(commands.Cog): unmatched_name = self.append_name(username) if spaces_in_name is not None: - replacements = ['Cotton', 'Fluff', 'Floof' 'Bounce', 'Snuffle', 'Nibble', 'Cuddle', 'Velvetpaw', 'Carrot'] + replacements = ["Cotton", "Fluff", "Floof" "Bounce", "Snuffle", "Nibble", "Cuddle", "Velvetpaw", "Carrot"] word_to_replace = random.choice(spaces_in_name) substitute = random.choice(replacements) bunnified_name = username.replace(word_to_replace, substitute) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index d7e7ccc3..0e82e99a 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -21,7 +21,7 @@ class EarthPhotos(commands.Cog): """Returns a random photo of earth, sourced from Unsplash.""" async with ctx.typing(): async with self.bot.http_session.get( - 'https://api.unsplash.com/photos/random', + "https://api.unsplash.com/photos/random", params={"query": "planet_earth", "client_id": Tokens.unsplash_access_key} ) as r: jsondata = await r.json() diff --git a/bot/exts/easter/egg_facts.py b/bot/exts/easter/egg_facts.py index 78a5e592..8c93ca7b 100644 --- a/bot/exts/easter/egg_facts.py +++ b/bot/exts/easter/egg_facts.py @@ -41,7 +41,7 @@ class EasterFacts(commands.Cog): channel = self.bot.get_channel(Channels.community_bot_commands) await channel.send(embed=self.make_embed()) - @commands.command(name='eggfact', aliases=['fact']) + @commands.command(name="eggfact", aliases=["fact"]) async def easter_facts(self, ctx: commands.Context) -> None: """Get easter egg facts.""" embed = self.make_embed() diff --git a/bot/exts/easter/egghead_quiz.py b/bot/exts/easter/egghead_quiz.py index e950bc2e..59c1f6f8 100644 --- a/bot/exts/easter/egghead_quiz.py +++ b/bot/exts/easter/egghead_quiz.py @@ -18,12 +18,12 @@ with open(Path("bot/resources/easter/egghead_questions.json"), "r", encoding="ut EMOJIS = [ - '\U0001f1e6', '\U0001f1e7', '\U0001f1e8', '\U0001f1e9', '\U0001f1ea', - '\U0001f1eb', '\U0001f1ec', '\U0001f1ed', '\U0001f1ee', '\U0001f1ef', - '\U0001f1f0', '\U0001f1f1', '\U0001f1f2', '\U0001f1f3', '\U0001f1f4', - '\U0001f1f5', '\U0001f1f6', '\U0001f1f7', '\U0001f1f8', '\U0001f1f9', - '\U0001f1fa', '\U0001f1fb', '\U0001f1fc', '\U0001f1fd', '\U0001f1fe', - '\U0001f1ff' + "\U0001f1e6", "\U0001f1e7", "\U0001f1e8", "\U0001f1e9", "\U0001f1ea", + "\U0001f1eb", "\U0001f1ec", "\U0001f1ed", "\U0001f1ee", "\U0001f1ef", + "\U0001f1f0", "\U0001f1f1", "\U0001f1f2", "\U0001f1f3", "\U0001f1f4", + "\U0001f1f5", "\U0001f1f6", "\U0001f1f7", "\U0001f1f8", "\U0001f1f9", + "\U0001f1fa", "\U0001f1fb", "\U0001f1fc", "\U0001f1fd", "\U0001f1fe", + "\U0001f1ff" ] # Regional Indicators A-Z (used for voting) TIMELIMIT = 30 diff --git a/bot/exts/easter/save_the_planet.py b/bot/exts/easter/save_the_planet.py index db9d3498..444bb030 100644 --- a/bot/exts/easter/save_the_planet.py +++ b/bot/exts/easter/save_the_planet.py @@ -8,14 +8,14 @@ from bot.bot import Bot from bot.utils.randomization import RandomCycle -with Path("bot/resources/easter/save_the_planet.json").open('r', encoding='utf8') as f: +with Path("bot/resources/easter/save_the_planet.json").open("r", encoding="utf8") as f: EMBED_DATA = RandomCycle(json.load(f)) class SaveThePlanet(commands.Cog): """A cog that teaches users how they can help our planet.""" - @commands.command(aliases=('savetheearth', 'saveplanet', 'saveearth')) + @commands.command(aliases=("savetheearth", "saveplanet", "saveearth")) async def savetheplanet(self, ctx: commands.Context) -> None: """Responds with a random tip on how to be eco-friendly and help our planet.""" return_embed = Embed.from_dict(next(EMBED_DATA)) diff --git a/bot/exts/easter/traditions.py b/bot/exts/easter/traditions.py index 19e69b98..cb70f2d0 100644 --- a/bot/exts/easter/traditions.py +++ b/bot/exts/easter/traditions.py @@ -16,7 +16,7 @@ with open(Path("bot/resources/easter/traditions.json"), "r", encoding="utf8") as class Traditions(commands.Cog): """A cog which allows users to get a random easter tradition or custom from a random country.""" - @commands.command(aliases=('eastercustoms',)) + @commands.command(aliases=("eastercustoms",)) async def easter_tradition(self, ctx: commands.Context) -> None: """Responds with a random tradition or custom.""" random_country = random.choice(list(traditions)) diff --git a/bot/exts/evergreen/cheatsheet.py b/bot/exts/evergreen/cheatsheet.py index 57c6d0b0..86fae167 100644 --- a/bot/exts/evergreen/cheatsheet.py +++ b/bot/exts/evergreen/cheatsheet.py @@ -24,11 +24,11 @@ Unknown cheat sheet. Please try to reformulate your query. If the problem persists send a message in <#{Channels.dev_contrib}> """ -URL = 'https://cheat.sh/python/{search}' +URL = "https://cheat.sh/python/{search}" ESCAPE_TT = str.maketrans({"`": "\\`"}) ANSI_RE = re.compile(r"\x1b\[.*?m") # We need to pass headers as curl otherwise it would default to aiohttp which would return raw html. -HEADERS = {'User-Agent': 'curl/7.68.0'} +HEADERS = {"User-Agent": "curl/7.68.0"} class CheatSheet(commands.Cog): diff --git a/bot/exts/evergreen/connect_four.py b/bot/exts/evergreen/connect_four.py index df2a913a..929a15d8 100644 --- a/bot/exts/evergreen/connect_four.py +++ b/bot/exts/evergreen/connect_four.py @@ -55,8 +55,8 @@ class Game: async def print_grid(self) -> None: """Formats and outputs the Connect Four grid to the channel.""" title = ( - f'Connect 4: {self.player1.display_name}' - f' VS {self.bot.user.display_name if isinstance(self.player2, AI) else self.player2.display_name}' + f"Connect 4: {self.player1.display_name}" + f" VS {self.bot.user.display_name if isinstance(self.player2, AI) else self.player2.display_name}" ) rows = [" ".join(self.tokens[s] for s in row) for row in self.grid] @@ -67,7 +67,7 @@ class Game: if self.message: await self.message.edit(embed=embed) else: - self.message = await self.channel.send(content='Loading...') + self.message = await self.channel.send(content="Loading...") for emoji in self.unicode_numbers: await self.message.add_reaction(emoji) await self.message.add_reaction(CROSS_EMOJI) diff --git a/bot/exts/evergreen/conversationstarters.py b/bot/exts/evergreen/conversationstarters.py index 54fea0b3..4fe8c47c 100644 --- a/bot/exts/evergreen/conversationstarters.py +++ b/bot/exts/evergreen/conversationstarters.py @@ -9,7 +9,7 @@ from bot.constants import WHITELISTED_CHANNELS from bot.utils.decorators import whitelist_override from bot.utils.randomization import RandomCycle -SUGGESTION_FORM = 'https://forms.gle/zw6kkJqv8U43Nfjg9' +SUGGESTION_FORM = "https://forms.gle/zw6kkJqv8U43Nfjg9" with Path("bot/resources/evergreen/starter.yaml").open("r", encoding="utf8") as f: STARTERS = yaml.load(f, Loader=yaml.FullLoader) @@ -25,9 +25,9 @@ with Path("bot/resources/evergreen/py_topics.yaml").open("r", encoding="utf8") a ALL_ALLOWED_CHANNELS = list(PY_TOPICS.keys()) + list(WHITELISTED_CHANNELS) # Putting all topics into one dictionary and shuffling lists to reduce same-topic repetitions. -ALL_TOPICS = {'default': STARTERS, **PY_TOPICS} +ALL_TOPICS = {"default": STARTERS, **PY_TOPICS} TOPICS = { - channel: RandomCycle(topics or ['No topics found for this channel.']) + channel: RandomCycle(topics or ["No topics found for this channel."]) for channel, topics in ALL_TOPICS.items() } @@ -46,7 +46,7 @@ class ConvoStarters(commands.Cog): Otherwise, a random conversation topic will be received by the user. """ # No matter what, the form will be shown. - embed = Embed(description=f'Suggest more topics [here]({SUGGESTION_FORM})!', color=Color.blurple()) + embed = Embed(description=f"Suggest more topics [here]({SUGGESTION_FORM})!", color=Color.blurple()) try: # Fetching topics. @@ -54,11 +54,11 @@ class ConvoStarters(commands.Cog): # If the channel isn't Python-related. except KeyError: - embed.title = f'**{next(TOPICS["default"])}**' + embed.title = f"**{next(TOPICS['default'])}**" # If the channel ID doesn't have any topics. else: - embed.title = f'**{next(channel_topics)}**' + embed.title = f"**{next(channel_topics)}**" finally: await ctx.send(embed=embed) diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index 8e540712..e7452a15 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -46,9 +46,9 @@ class Emojis(commands.Cog): else: emoji_info = f"There is **{len(category_emojis)}** emoji in the **{category_name}** category." if emoji_choice.animated: - msg.append(f' {emoji_info}') + msg.append(f" {emoji_info}") else: - msg.append(f'<:{emoji_choice.name}:{emoji_choice.id}> {emoji_info}') + msg.append(f"<:{emoji_choice.name}:{emoji_choice.id}> {emoji_info}") return embed, msg @staticmethod @@ -64,7 +64,7 @@ class Emojis(commands.Cog): for emoji in emojis: emoji_dict[emoji.name.split("_")[0]].append(emoji) - error_comp = ', '.join(emoji_dict) + error_comp = ", ".join(emoji_dict) msg.append(f"These are the valid emoji categories:\n```{error_comp}```") return embed, msg diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index dabd0ab5..5cd8d28d 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -40,7 +40,7 @@ class CommandErrorHandler(commands.Cog): @commands.Cog.listener() async def on_command_error(self, ctx: commands.Context, error: commands.CommandError) -> None: """Activates when a command opens an error.""" - if getattr(error, 'handled', False): + if getattr(error, "handled", False): logging.debug(f"Command {ctx.command} had its error already handled locally; ignoring.") return @@ -49,7 +49,7 @@ class CommandErrorHandler(commands.Cog): parent_command = f"{ctx.command} " ctx = subctx - error = getattr(error, 'original', error) + error = getattr(error, "original", error) logging.debug( f"Error Encountered: {type(error).__name__} - {str(error)}, " f"Command: {ctx.command}, " diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index 24872e76..7abbadcd 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -176,7 +176,7 @@ class Games(Cog): "Invalid OAuth credentials. Unloading Games cog. " f"OAuth response message: {result['message']}" ) - self.bot.remove_cog('Games') + self.bot.remove_cog("Games") return @@ -260,7 +260,7 @@ class Games(Cog): display_possibilities = "`, `".join(p[1] for p in possibilities) await ctx.send( f"Invalid genre `{genre}`. " - f"{f'Maybe you meant `{display_possibilities}`?' if display_possibilities else ''}" + f"Maybe you meant `{display_possibilities}`?" if display_possibilities else '' ) return elif len(possibilities) == 1: diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index fd100a7c..24479c79 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -27,14 +27,14 @@ class GithubInfo(commands.Cog): async with self.bot.http_session.get(url) as r: return await r.json() - @commands.group(name='github', aliases=('gh', 'git')) + @commands.group(name="github", aliases=("gh", "git")) @commands.cooldown(1, 10, BucketType.user) async def github_group(self, ctx: commands.Context) -> None: """Commands for finding information related to GitHub.""" if ctx.invoked_subcommand is None: await invoke_help_command(ctx) - @github_group.command(name='user', aliases=('userinfo',)) + @github_group.command(name="user", aliases=("userinfo",)) async def github_user_info(self, ctx: commands.Context, username: str) -> None: """Fetches a user's GitHub information.""" async with ctx.typing(): @@ -51,31 +51,31 @@ class GithubInfo(commands.Cog): await ctx.send(embed=embed) return - org_data = await self.fetch_data(user_data['organizations_url']) + org_data = await self.fetch_data(user_data["organizations_url"]) orgs = [f"[{org['login']}](https://github.com/{org['login']})" for org in org_data] - orgs_to_add = ' | '.join(orgs) + orgs_to_add = " | ".join(orgs) - gists = user_data['public_gists'] + gists = user_data["public_gists"] # Forming blog link - if user_data['blog'].startswith("http"): # Blog link is complete - blog = user_data['blog'] - elif user_data['blog']: # Blog exists but the link is not complete + if user_data["blog"].startswith("http"): # Blog link is complete + blog = user_data["blog"] + elif user_data["blog"]: # Blog exists but the link is not complete blog = f"https://{user_data['blog']}" else: blog = "No website link available" embed = discord.Embed( title=f"`{user_data['login']}`'s GitHub profile info", - description=f"```{user_data['bio']}```\n" if user_data['bio'] is not None else "", + description=f"```{user_data['bio']}```\n" if user_data["bio"] is not None else "", colour=discord.Colour.blurple(), - url=user_data['html_url'], - timestamp=datetime.strptime(user_data['created_at'], "%Y-%m-%dT%H:%M:%SZ") + url=user_data["html_url"], + timestamp=datetime.strptime(user_data["created_at"], "%Y-%m-%dT%H:%M:%SZ") ) - embed.set_thumbnail(url=user_data['avatar_url']) + embed.set_thumbnail(url=user_data["avatar_url"]) embed.set_footer(text="Account created at") - if user_data['type'] == "User": + if user_data["type"] == "User": embed.add_field( name="Followers", @@ -91,7 +91,7 @@ class GithubInfo(commands.Cog): value=f"[{user_data['public_repos']}]({user_data['html_url']}?tab=repositories)" ) - if user_data['type'] == "User": + if user_data["type"] == "User": embed.add_field(name="Gists", value=f"[{gists}](https://gist.github.com/{quote(username, safe='')})") embed.add_field( @@ -109,8 +109,8 @@ class GithubInfo(commands.Cog): The repository should look like `user/reponame` or `user reponame`. """ - repo = '/'.join(repo) - if repo.count('/') != 1: + repo = "/".join(repo) + if repo.count("/") != 1: embed = discord.Embed( title=random.choice(NEGATIVE_REPLIES), description="The repository should look like `user/reponame` or `user reponame`.", @@ -135,10 +135,10 @@ class GithubInfo(commands.Cog): return embed = discord.Embed( - title=repo_data['name'], + title=repo_data["name"], description=repo_data["description"], colour=discord.Colour.blurple(), - url=repo_data['html_url'] + url=repo_data["html_url"] ) # If it's a fork, then it will have a parent key @@ -148,7 +148,7 @@ class GithubInfo(commands.Cog): except KeyError: log.debug("Repository is not a fork.") - repo_owner = repo_data['owner'] + repo_owner = repo_data["owner"] embed.set_author( name=repo_owner["login"], @@ -156,8 +156,8 @@ class GithubInfo(commands.Cog): icon_url=repo_owner["avatar_url"] ) - repo_created_at = datetime.strptime(repo_data['created_at'], "%Y-%m-%dT%H:%M:%SZ").strftime("%d/%m/%Y") - last_pushed = datetime.strptime(repo_data['pushed_at'], "%Y-%m-%dT%H:%M:%SZ").strftime("%d/%m/%Y at %H:%M") + repo_created_at = datetime.strptime(repo_data["created_at"], "%Y-%m-%dT%H:%M:%SZ").strftime("%d/%m/%Y") + last_pushed = datetime.strptime(repo_data["pushed_at"], "%Y-%m-%dT%H:%M:%SZ").strftime("%d/%m/%Y at %H:%M") embed.set_footer( text=( diff --git a/bot/exts/evergreen/help.py b/bot/exts/evergreen/help.py index f557e42e..bfaf25f1 100644 --- a/bot/exts/evergreen/help.py +++ b/bot/exts/evergreen/help.py @@ -22,14 +22,14 @@ from bot.utils.pagination import ( DELETE_EMOJI = Emojis.trashcan REACTIONS = { - FIRST_EMOJI: 'first', - LEFT_EMOJI: 'back', - RIGHT_EMOJI: 'next', - LAST_EMOJI: 'end', - DELETE_EMOJI: 'stop', + FIRST_EMOJI: "first", + LEFT_EMOJI: "back", + RIGHT_EMOJI: "next", + LAST_EMOJI: "end", + DELETE_EMOJI: "stop", } -Cog = namedtuple('Cog', ['name', 'description', 'commands']) +Cog = namedtuple("Cog", ["name", "description", "commands"]) log = logging.getLogger(__name__) @@ -87,7 +87,7 @@ class HelpSession: # set the query details for the session if command: - query_str = ' '.join(command) + query_str = " ".join(command) self.query = self._get_query(query_str) self.description = self.query.description or self.query.help else: @@ -191,7 +191,7 @@ class HelpSession: self.reset_timeout() # Run relevant action method - action = getattr(self, f'do_{REACTIONS[emoji]}', None) + action = getattr(self, f"do_{REACTIONS[emoji]}", None) if action: await action() @@ -234,11 +234,11 @@ class HelpSession: if cmd.cog: try: if cmd.cog.category: - return f'**{cmd.cog.category}**' + return f"**{cmd.cog.category}**" except AttributeError: pass - return f'**{cmd.cog_name}**' + return f"**{cmd.cog_name}**" else: return "**\u200bNo Category:**" @@ -262,47 +262,47 @@ class HelpSession: # if default is not an empty string or None if show_default: - results.append(f'[{name}={param.default}]') + results.append(f"[{name}={param.default}]") else: - results.append(f'[{name}]') + results.append(f"[{name}]") # if variable length argument elif param.kind == param.VAR_POSITIONAL: - results.append(f'[{name}...]') + results.append(f"[{name}...]") # if required else: - results.append(f'<{name}>') + results.append(f"<{name}>") return f"{cmd.name} {' '.join(results)}" async def build_pages(self) -> None: """Builds the list of content pages to be paginated through in the help message, as a list of str.""" # Use LinePaginator to restrict embed line height - paginator = LinePaginator(prefix='', suffix='', max_lines=self._max_lines) + paginator = LinePaginator(prefix="", suffix="", max_lines=self._max_lines) prefix = constants.Client.prefix # show signature if query is a command if isinstance(self.query, commands.Command): signature = self._get_command_params(self.query) - parent = self.query.full_parent_name + ' ' if self.query.parent else '' - paginator.add_line(f'**```{prefix}{parent}{signature}```**') + parent = self.query.full_parent_name + " " if self.query.parent else "" + paginator.add_line(f"**```{prefix}{parent}{signature}```**") aliases = [f"`{alias}`" if not parent else f"`{parent} {alias}`" for alias in self.query.aliases] aliases += [f"`{alias}`" for alias in getattr(self.query, "root_aliases", ())] aliases = ", ".join(sorted(aliases)) if aliases: - paginator.add_line(f'**Can also use:** {aliases}\n') + paginator.add_line(f"**Can also use:** {aliases}\n") if not await self.query.can_run(self._ctx): - paginator.add_line('***You cannot run this command.***\n') + paginator.add_line("***You cannot run this command.***\n") if isinstance(self.query, Cog): - paginator.add_line(f'**{self.query.name}**') + paginator.add_line(f"**{self.query.name}**") if self.description: - paginator.add_line(f'*{self.description}*') + paginator.add_line(f"*{self.description}*") # list all children commands of the queried object if isinstance(self.query, (commands.GroupMixin, Cog)): @@ -319,13 +319,13 @@ class HelpSession: return if isinstance(self.query, Cog): - grouped = (('**Commands:**', self.query.commands),) + grouped = (("**Commands:**", self.query.commands),) elif isinstance(self.query, commands.Command): - grouped = (('**Subcommands:**', self.query.commands),) + grouped = (("**Subcommands:**", self.query.commands),) # don't show prefix for subcommands - prefix = '' + prefix = "" # otherwise sort and organise all commands into categories else: @@ -347,7 +347,7 @@ class HelpSession: continue # see if the user can run the command - strikeout = '' + strikeout = "" # Patch to make the !help command work outside of #bot-commands again # This probably needs a proper rewrite, but this will make it work in @@ -361,16 +361,16 @@ class HelpSession: # skip if we don't show commands they can't run if self._only_can_run: continue - strikeout = '~~' + strikeout = "~~" signature = self._get_command_params(command) info = f"{strikeout}**`{prefix}{signature}`**{strikeout}" # handle if the command has no docstring if command.short_doc: - cat_cmds.append(f'{info}\n*{command.short_doc}*') + cat_cmds.append(f"{info}\n*{command.short_doc}*") else: - cat_cmds.append(f'{info}\n*No details provided.*') + cat_cmds.append(f"{info}\n*No details provided.*") # state var for if the category should be added next print_cat = 1 @@ -379,7 +379,7 @@ class HelpSession: for details in cat_cmds: # keep details together, paginating early if it won't fit - lines_adding = len(details.split('\n')) + print_cat + lines_adding = len(details.split("\n")) + print_cat if paginator._linecount + lines_adding > self._max_lines: paginator._linecount = 0 new_page = True @@ -390,7 +390,7 @@ class HelpSession: if print_cat: if new_page: - paginator.add_line('') + paginator.add_line("") paginator.add_line(category) print_cat = 0 @@ -412,7 +412,7 @@ class HelpSession: page_count = len(self._pages) if page_count > 1: - embed.set_footer(text=f'Page {self._current_page+1} / {page_count}') + embed.set_footer(text=f"Page {self._current_page+1} / {page_count}") return embed @@ -496,7 +496,7 @@ class HelpSession: class Help(DiscordCog): """Custom Embed Pagination Help feature.""" - @commands.command('help') + @commands.command("help") async def new_help(self, ctx: Context, *commands) -> None: """Shows Command Help.""" try: @@ -507,8 +507,8 @@ class Help(DiscordCog): embed.title = str(error) if error.possible_matches: - matches = '\n'.join(error.possible_matches.keys()) - embed.description = f'**Did you mean:**\n`{matches}`' + matches = "\n".join(error.possible_matches.keys()) + embed.description = f"**Did you mean:**\n`{matches}`" await ctx.send(embed=embed) @@ -519,7 +519,7 @@ def unload(bot: Bot) -> None: This is run if the cog raises an exception on load, or if the extension is unloaded. """ - bot.remove_command('help') + bot.remove_command("help") bot.add_command(bot._old_help) @@ -534,8 +534,8 @@ def setup(bot: Bot) -> None: If an exception is raised during the loading of the cog, `unload` will be called in order to reinstate the original help command. """ - bot._old_help = bot.get_command('help') - bot.remove_command('help') + bot._old_help = bot.get_command("help") + bot.remove_command("help") try: bot.add_cog(Help()) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index d7ee99c0..5bbc57c6 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -158,7 +158,7 @@ class Issues(commands.Cog): issue_url = json_data.get("html_url") - return IssueState(repository, number, issue_url, json_data.get('title', ''), emoji) + return IssueState(repository, number, issue_url, json_data.get("title", ""), emoji) @staticmethod def format_embed( @@ -177,7 +177,7 @@ class Issues(commands.Cog): resp = discord.Embed( colour=Colours.bright_green, - description='\n'.join(description_list) + description="\n".join(description_list) ) embed_url = f"https://github.com/{user}/{repository}" if repository else f"https://github.com/{user}" diff --git a/bot/exts/evergreen/minesweeper.py b/bot/exts/evergreen/minesweeper.py index d0cc28c5..f2c5e656 100644 --- a/bot/exts/evergreen/minesweeper.py +++ b/bot/exts/evergreen/minesweeper.py @@ -38,7 +38,7 @@ class CoordinateConverter(commands.Converter): async def convert(self, ctx: commands.Context, coordinate: str) -> typing.Tuple[int, int]: """Take in a coordinate string and turn it into an (x, y) tuple.""" if not 2 <= len(coordinate) <= 3: - raise commands.BadArgument('Invalid co-ordinate provided.') + raise commands.BadArgument("Invalid co-ordinate provided.") coordinate = coordinate.lower() if coordinate[0].isalpha(): @@ -51,7 +51,7 @@ class CoordinateConverter(commands.Converter): if not digit.isdigit(): raise commands.BadArgument - x = ord(letter) - ord('a') + x = ord(letter) - ord("a") y = int(digit) - 1 if (not 0 <= x <= 9) or (not 0 <= y <= 9): @@ -82,7 +82,7 @@ class Minesweeper(commands.Cog): def __init__(self, _bot: Bot) -> None: self.games: GamesDict = {} # Store the currently running games - @commands.group(name='minesweeper', aliases=('ms',), invoke_without_command=True) + @commands.group(name="minesweeper", aliases=("ms",), invoke_without_command=True) async def minesweeper_group(self, ctx: commands.Context) -> None: """Commands for Playing Minesweeper.""" await invoke_help_command(ctx) diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index 488e5142..e67f8d04 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -53,7 +53,7 @@ class Movie(Cog): def __init__(self, bot: Bot): self.http_session: ClientSession = bot.http_session - @group(name='movies', aliases=['movie'], invoke_without_command=True) + @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. @@ -89,7 +89,7 @@ class Movie(Cog): # Get movies list from TMDB, check if results key in result. When not, raise error. movies = await self.get_movies_list(self.http_session, MovieGenres[genre].value, page) - if 'results' not in movies.keys(): + if "results" not in movies.keys(): err_msg = f"There is problem while making TMDB API request. Response Code: {result['status_code']}, " \ f"{result['status_message']}." await ctx.send(err_msg) @@ -101,7 +101,7 @@ class Movie(Cog): await ImagePaginator.paginate(pages, ctx, embed) - @movies.command(name='genres', aliases=['genre', 'g']) + @movies.command(name="genres", aliases=["genre", "g"]) async def genres(self, ctx: Context) -> None: """Show all currently available genres for .movies command.""" await ctx.send(f"Current available genres: {', '.join('`' + genre.name + '`' for genre in MovieGenres)}") @@ -130,7 +130,7 @@ class Movie(Cog): pages = [] for i in range(amount): - movie_id = movies['results'][i]['id'] + movie_id = movies["results"][i]["id"] movie = await self.get_movie(client, movie_id) page, img = await self.create_page(movie) @@ -151,7 +151,7 @@ class Movie(Cog): # Add title + tagline (if not empty) text += f"**{movie['title']}**\n" - if movie['tagline']: + if movie["tagline"]: text += f"{movie['tagline']}\n\n" else: text += "\n" @@ -162,8 +162,8 @@ class Movie(Cog): text += "__**Production Information**__\n" - companies = movie['production_companies'] - countries = movie['production_countries'] + companies = movie["production_companies"] + countries = movie["production_countries"] text += f"**Made by:** {', '.join(company['name'] for company in companies)}\n" text += f"**Made in:** {', '.join(country['name'] for country in countries)}\n\n" @@ -173,8 +173,8 @@ class Movie(Cog): 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) + if movie["runtime"] is not None: + duration = divmod(movie["runtime"], 60) else: duration = ("?", "?") @@ -182,7 +182,7 @@ class Movie(Cog): text += f"**Revenue:** ${revenue}\n" text += f"**Duration:** {f'{duration[0]} hour(s) {duration[1]} minute(s)'}\n\n" - text += movie['overview'] + text += movie["overview"] img = f"http://image.tmdb.org/t/p/w200{movie['poster_path']}" diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index 73234d55..2dc4996f 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -6,7 +6,7 @@ from discord.ext import commands from bot.bot import Bot from bot.constants import Colours -with open('bot/resources/evergreen/python_facts.txt') as file: +with open("bot/resources/evergreen/python_facts.txt") as file: FACTS = itertools.cycle(list(file)) COLORS = itertools.cycle([Colours.python_blue, Colours.python_yellow]) @@ -15,13 +15,13 @@ COLORS = itertools.cycle([Colours.python_blue, Colours.python_yellow]) class PythonFacts(commands.Cog): """Sends a random fun fact about Python.""" - @commands.command(name='pythonfact', aliases=['pyfact']) + @commands.command(name="pythonfact", aliases=["pyfact"]) async def get_python_fact(self, ctx: commands.Context) -> None: """Sends a Random fun fact about Python.""" - embed = discord.Embed(title='Python Facts', + embed = discord.Embed(title="Python Facts", description=next(FACTS), colour=next(COLORS)) - embed.add_field(name='Suggestions', + embed.add_field(name="Suggestions", value="Suggest more facts [here!](https://github.com/python-discord/meta/discussions/93)") await ctx.send(embed=embed) diff --git a/bot/exts/evergreen/recommend_game.py b/bot/exts/evergreen/recommend_game.py index be329f44..340a42d3 100644 --- a/bot/exts/evergreen/recommend_game.py +++ b/bot/exts/evergreen/recommend_game.py @@ -13,7 +13,7 @@ game_recs = [] # Populate the list `game_recs` with resource files for rec_path in Path("bot/resources/evergreen/game_recs").glob("*.json"): - with rec_path.open(encoding='utf8') as file: + with rec_path.open(encoding="utf8") as file: data = json.load(file) game_recs.append(data) shuffle(game_recs) @@ -26,7 +26,7 @@ class RecommendGame(commands.Cog): self.bot = bot self.index = 0 - @commands.command(name="recommendgame", aliases=['gamerec']) + @commands.command(name="recommendgame", aliases=["gamerec"]) async def recommend_game(self, ctx: commands.Context) -> None: """Sends an Embed of a random game recommendation.""" if self.index >= len(game_recs): @@ -35,14 +35,14 @@ class RecommendGame(commands.Cog): game = game_recs[self.index] self.index += 1 - author = self.bot.get_user(int(game['author'])) + author = self.bot.get_user(int(game["author"])) # Creating and formatting Embed embed = discord.Embed(color=discord.Colour.blue()) if author is not None: embed.set_author(name=author.name, icon_url=author.avatar_url) - embed.set_image(url=game['image']) - embed.add_field(name='Recommendation: ' + game['title'] + '\n' + game['link'], value=game['description']) + embed.set_image(url=game["image"]) + embed.add_field(name="Recommendation: " + game["title"] + "\n" + game["link"], value=game["description"]) await ctx.send(embed=embed) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index 51a360b3..82af6ce9 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -21,18 +21,18 @@ class Reddit(commands.Cog): """Send a get request to the reddit API and get json response.""" session = self.bot.http_session params = { - 'limit': 50 + "limit": 50 } headers = { - 'User-Agent': 'Iceman' + "User-Agent": "Iceman" } async with session.get(url=url, params=params, headers=headers) as response: return await response.json() - @commands.command(name='reddit') + @commands.command(name="reddit") @commands.cooldown(1, 10, BucketType.user) - async def get_reddit(self, ctx: commands.Context, subreddit: str = 'python', sort: str = "hot") -> None: + async def get_reddit(self, ctx: commands.Context, subreddit: str = "python", sort: str = "hot") -> None: """ Fetch reddit posts by using this command. @@ -46,15 +46,15 @@ class Reddit(commands.Cog): await ctx.send(f"Invalid sorting: {sort}\nUsing default sorting: `Hot`") sort = "hot" - data = await self.fetch(f'https://www.reddit.com/r/{subreddit}/{sort}/.json') + data = await self.fetch(f"https://www.reddit.com/r/{subreddit}/{sort}/.json") try: posts = data["data"]["children"] except KeyError: - await ctx.send('Subreddit not found!') + await ctx.send("Subreddit not found!") return if not posts: - await ctx.send('No posts available!') + await ctx.send("No posts available!") return if posts[0]["data"]["over_18"] is True: @@ -106,12 +106,12 @@ class Reddit(commands.Cog): post_stats = f"{image_emoji} " image_url = post_url - votes = f'{upvote_emoji}{post["data"]["ups"]}' - comments = f'{comment_emoji}\u2002{ post["data"]["num_comments"]}' + votes = f"{upvote_emoji}{post['data']['ups']}" + comments = f"{comment_emoji}\u2002{ post['data']['num_comments']}" post_stats += ( f"\u2002{votes}\u2003" f"{comments}" - f'\u2003{user_emoji}\u2002{post["data"]["author"]}\n' + f"\u2003{user_emoji}\u2002{post['data']['author']}\n" ) embed_titles += f"{post_stats}\n" page_text = f"**[{post_title}]({post_url})**\n{post_stats}\n{post['data']['selftext'][0:200]}" 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 c8633ce7..d95970da 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -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 @@ -311,12 +311,12 @@ class Snakes(Cog): async with aiohttp.ClientSession() as session: params = { - 'format': 'json', - 'action': 'query', - 'list': 'search', - 'srsearch': name, - 'utf8': '', - 'srlimit': '1', + "format": "json", + "action": "query", + "list": "search", + "srsearch": name, + "utf8": "", + "srlimit": "1", } json = await self._fetch(session, URL, params=params) @@ -331,13 +331,13 @@ class Snakes(Cog): return None params = { - 'format': 'json', - 'action': 'query', - 'prop': 'extracts|images|info', - 'exlimit': 'max', - 'explaintext': '', - 'inprop': 'url', - 'pageids': pageid + "format": "json", + "action": "query", + "prop": "extracts|images|info", + "exlimit": "max", + "explaintext": "", + "inprop": "url", + "pageids": pageid } json = await self._fetch(session, URL, params=params) @@ -353,32 +353,32 @@ class Snakes(Cog): snake_info["error"] = True if snake_info["images"]: - i_url = 'https://commons.wikimedia.org/wiki/Special:FilePath/' + 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' + "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(':') + file, sep, filename = image["title"].partition(":") filename = filename.replace(" ", "%20") # Wikipedia returns good data! - if not filename.startswith('Map'): + if not filename.startswith("Map"): if any(ban in filename for ban in banned): pass else: @@ -392,7 +392,7 @@ class Snakes(Cog): snake_info["thumb_list"] = thumb_list snake_info["name"] = name - match = self.wiki_brief.match(snake_info['extract']) + match = self.wiki_brief.match(snake_info["extract"]) info = match.group(1) if match else None if info: @@ -438,13 +438,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 +586,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 +621,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 +642,8 @@ class Snakes(Cog): else: data = await self._get_snek(name) - if data.get('error'): - 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"] @@ -662,19 +662,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: """ @@ -694,11 +694,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 @@ -709,7 +709,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. @@ -740,7 +740,7 @@ class Snakes(Cog): 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. @@ -806,7 +806,7 @@ class Snakes(Cog): 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: """ @@ -832,7 +832,7 @@ class Snakes(Cog): 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. @@ -856,7 +856,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. @@ -904,7 +904,7 @@ class Snakes(Cog): 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: """ @@ -923,7 +923,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 = [ @@ -968,7 +968,7 @@ class Snakes(Cog): 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. @@ -978,7 +978,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): @@ -992,7 +992,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) @@ -1003,10 +1003,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. @@ -1022,7 +1022,7 @@ class Snakes(Cog): ) 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? @@ -1063,7 +1063,7 @@ class Snakes(Cog): 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. @@ -1074,13 +1074,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={ @@ -1096,14 +1096,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=' + 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. 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) diff --git a/bot/exts/evergreen/source.py b/bot/exts/evergreen/source.py index 14fd02f3..2f25e4cb 100644 --- a/bot/exts/evergreen/source.py +++ b/bot/exts/evergreen/source.py @@ -83,7 +83,7 @@ class BotSource(commands.Cog): url, location, first_line = self.get_source_link(source_object) if isinstance(source_object, commands.Command): - if source_object.cog_name == 'Help': + if source_object.cog_name == "Help": title = "Help Command" description = source_object.__doc__.splitlines()[1] else: diff --git a/bot/exts/evergreen/speedrun.py b/bot/exts/evergreen/speedrun.py index bf6f2117..110d5c13 100644 --- a/bot/exts/evergreen/speedrun.py +++ b/bot/exts/evergreen/speedrun.py @@ -8,7 +8,7 @@ from discord.ext import commands from bot.bot import Bot log = logging.getLogger(__name__) -with Path('bot/resources/evergreen/speedrun_links.json').open(encoding="utf8") as file: +with Path("bot/resources/evergreen/speedrun_links.json").open(encoding="utf8") as file: LINKS = json.load(file) diff --git a/bot/exts/evergreen/status_codes.py b/bot/exts/evergreen/status_codes.py index 635eef3d..a866692e 100644 --- a/bot/exts/evergreen/status_codes.py +++ b/bot/exts/evergreen/status_codes.py @@ -22,10 +22,10 @@ class HTTPStatusCodes(commands.Cog): if not ctx.invoked_subcommand: await invoke_help_command(ctx) - @http_status_group.command(name='cat') + @http_status_group.command(name="cat") async def http_cat(self, ctx: commands.Context, code: int) -> None: """Sends an embed with an image of a cat, portraying the status code.""" - embed = discord.Embed(title=f'**Status: {code}**') + embed = discord.Embed(title=f"**Status: {code}**") url = HTTP_CAT_URL.format(code=code) try: @@ -37,18 +37,18 @@ class HTTPStatusCodes(commands.Cog): raise NotImplementedError except ValueError: - embed.set_footer(text='Inputted status code does not exist.') + embed.set_footer(text="Inputted status code does not exist.") except NotImplementedError: - embed.set_footer(text='Inputted status code is not implemented by http.cat yet.') + embed.set_footer(text="Inputted status code is not implemented by http.cat yet.") finally: await ctx.send(embed=embed) - @http_status_group.command(name='dog') + @http_status_group.command(name="dog") async def http_dog(self, ctx: commands.Context, code: int) -> None: """Sends an embed with an image of a dog, portraying the status code.""" - embed = discord.Embed(title=f'**Status: {code}**') + embed = discord.Embed(title=f"**Status: {code}**") url = HTTP_DOG_URL.format(code=code) try: @@ -60,10 +60,10 @@ class HTTPStatusCodes(commands.Cog): raise NotImplementedError except ValueError: - embed.set_footer(text='Inputted status code does not exist.') + embed.set_footer(text="Inputted status code does not exist.") except NotImplementedError: - embed.set_footer(text='Inputted status code is not implemented by httpstatusdogs.com yet.') + embed.set_footer(text="Inputted status code is not implemented by httpstatusdogs.com yet.") finally: await ctx.send(embed=embed) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 1fef427a..7b387c0a 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -58,7 +58,7 @@ class Player: ) try: - react, _ = await self.ctx.bot.wait_for('reaction_add', timeout=30.0, check=check_for_move) + react, _ = await self.ctx.bot.wait_for("reaction_add", timeout=30.0, check=check_for_move) except asyncio.TimeoutError: return True, None else: diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index bfd7d357..1953253b 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -129,7 +129,7 @@ class TriviaQuiz(commands.Cog): ) try: - msg = await self.bot.wait_for('message', check=check, timeout=10) + msg = await self.bot.wait_for("message", check=check, timeout=10) except asyncio.TimeoutError: # In case of TimeoutError and the game has been stopped, then do nothing. if self.game_status[ctx.channel.id] is False: diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index e2172fc3..fa21b916 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -20,7 +20,7 @@ WIKI_THUMBNAIL = ( "https://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg" "/330px-Wikipedia-logo-v2.svg.png" ) -WIKI_SNIPPET_REGEX = r'(|<[^>]*>)' +WIKI_SNIPPET_REGEX = r"(|<[^>]*>)" WIKI_SEARCH_RESULT = ( "**[{name}]({url})**\n" "{description}\n" @@ -39,18 +39,18 @@ class WikipediaSearch(commands.Cog): async with self.bot.http_session.get(url=url) as resp: if resp.status == 200: raw_data = await resp.json() - number_of_results = raw_data['query']['searchinfo']['totalhits'] + number_of_results = raw_data["query"]["searchinfo"]["totalhits"] if number_of_results: - results = raw_data['query']['search'] + results = raw_data["query"]["search"] lines = [] for article in results: line = WIKI_SEARCH_RESULT.format( - name=article['title'], + name=article["title"], description=unescape( re.sub( - WIKI_SNIPPET_REGEX, '', article['snippet'] + WIKI_SNIPPET_REGEX, "", article["snippet"] ) ), url=f"https://en.wikipedia.org/?curid={article['pageid']}" diff --git a/bot/exts/evergreen/wolfram.py b/bot/exts/evergreen/wolfram.py index c57a8d7a..3cc12c03 100644 --- a/bot/exts/evergreen/wolfram.py +++ b/bot/exts/evergreen/wolfram.py @@ -59,7 +59,7 @@ def custom_cooldown(*ignore: List[int]) -> Callable: A list of roles may be provided to ignore the per-user cooldown. """ async def predicate(ctx: Context) -> bool: - if ctx.invoked_with == 'help': + if ctx.invoked_with == "help": # if the invoked command is help we don't want to increase the ratelimits since it's not actually # invoking the command/making a request, so instead just check if the user/guild are on cooldown. guild_cooldown = not guildcd.get_bucket(ctx.message).get_tokens() == 0 # if guild is on cooldown @@ -118,7 +118,7 @@ async def get_pod_pages(ctx: Context, bot: Bot, query: str) -> Optional[List[Tup request_url = QUERY.format(request="query", data=url_str) async with bot.http_session.get(request_url) as response: - json = await response.json(content_type='text/plain') + json = await response.json(content_type="text/plain") result = json["queryresult"] diff --git a/bot/exts/evergreen/xkcd.py b/bot/exts/evergreen/xkcd.py index ba9e46e0..c98830bc 100644 --- a/bot/exts/evergreen/xkcd.py +++ b/bot/exts/evergreen/xkcd.py @@ -53,7 +53,7 @@ class XKCD(Cog): await ctx.send(embed=embed) return - comic = randint(1, self.latest_comic_info['num']) if comic is None else comic.group(0) + comic = randint(1, self.latest_comic_info["num"]) if comic is None else comic.group(0) if comic == "latest": info = self.latest_comic_info @@ -69,7 +69,7 @@ class XKCD(Cog): return embed.title = f"XKCD comic #{info['num']}" - embed.description = info['alt'] + embed.description = info["alt"] embed.url = f"{BASE_URL}/{info['num']}" if info["img"][-3:] in ("jpg", "png", "gif"): diff --git a/bot/exts/halloween/8ball.py b/bot/exts/halloween/8ball.py index 59d4acc5..d6c5a299 100644 --- a/bot/exts/halloween/8ball.py +++ b/bot/exts/halloween/8ball.py @@ -17,7 +17,7 @@ with Path("bot/resources/halloween/responses.json").open("r", encoding="utf8") a class SpookyEightBall(commands.Cog): """Spooky Eightball answers.""" - @commands.command(aliases=('spooky8ball',)) + @commands.command(aliases=("spooky8ball",)) async def spookyeightball(self, ctx: commands.Context, *, question: str) -> None: """Responds with a random response to a question.""" choice = random.choice(RESPONSES["responses"]) diff --git a/bot/exts/halloween/candy_collection.py b/bot/exts/halloween/candy_collection.py index 5441d8a5..14efa1fb 100644 --- a/bot/exts/halloween/candy_collection.py +++ b/bot/exts/halloween/candy_collection.py @@ -22,11 +22,11 @@ EMOJIS = dict( CANDY="\N{CANDY}", SKULL="\N{SKULL}", MEDALS=( - '\N{FIRST PLACE MEDAL}', - '\N{SECOND PLACE MEDAL}', - '\N{THIRD PLACE MEDAL}', - '\N{SPORTS MEDAL}', - '\N{SPORTS MEDAL}', + "\N{FIRST PLACE MEDAL}", + "\N{SECOND PLACE MEDAL}", + "\N{THIRD PLACE MEDAL}", + "\N{SPORTS MEDAL}", + "\N{SPORTS MEDAL}", ), ) @@ -106,7 +106,7 @@ class CandyCollection(commands.Cog): await self.candy_records.decrement(user.id, lost) if lost == prev_record: - await CandyCollection.send_spook_msg(user, message.channel, 'all of your') + await CandyCollection.send_spook_msg(user, message.channel, "all of your") else: await CandyCollection.send_spook_msg(user, message.channel, lost) else: @@ -125,7 +125,7 @@ class CandyCollection(commands.Cog): """ if random.randint(1, ADD_SKULL_EXISTING_REACTION_CHANCE) == 1: await self.skull_messages.set(message.id, "skull") - await message.add_reaction(EMOJIS['SKULL']) + await message.add_reaction(EMOJIS["SKULL"]) elif random.randint(1, ADD_CANDY_EXISTING_REACTION_CHANCE) == 1: await self.candy_messages.set(message.id, "candy") @@ -173,7 +173,7 @@ class CandyCollection(commands.Cog): ) top_five = top_sorted[:5] - return '\n'.join( + return "\n".join( f"{EMOJIS['MEDALS'][index]} <@{record[0]}>: {record[1]}" for index, record in enumerate(top_five) ) if top_five else "No Candies" @@ -185,7 +185,7 @@ class CandyCollection(commands.Cog): inline=False ) e.add_field( - name='\u200b', + name="\u200b", value="Candies will randomly appear on messages sent. " "\nHit the candy when it appears as fast as possible to get the candy! " "\nBut beware the ghosts...", diff --git a/bot/exts/halloween/hacktober-issue-finder.py b/bot/exts/halloween/hacktober-issue-finder.py index c88e2b6f..baee9612 100644 --- a/bot/exts/halloween/hacktober-issue-finder.py +++ b/bot/exts/halloween/hacktober-issue-finder.py @@ -102,7 +102,7 @@ class HacktoberIssues(commands.Cog): labels = [label["name"] for label in issue["labels"]] embed = discord.Embed(title=title) - embed.description = body[:500] + '...' if len(body) > 500 else body + embed.description = body[:500] + "..." if len(body) > 500 else body embed.add_field(name="labels", value="\n".join(labels)) embed.url = issue_url embed.set_footer(text=issue_url) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 9695ba2a..25da9ad5 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -138,7 +138,7 @@ class HacktoberStats(commands.Cog): if prs: stats_embed = await self.build_embed(github_username, prs) - await ctx.send('Here are some stats!', embed=stats_embed) + await ctx.send("Here are some stats!", embed=stats_embed) else: await ctx.send(f"No valid Hacktoberfest PRs found for '{github_username}'") @@ -355,7 +355,7 @@ class HacktoberStats(commands.Cog): # loop through reviews and check for approval for item in jsonresp2: - if item.get('status') == "APPROVED": + if item.get("status") == "APPROVED": return True return False @@ -387,9 +387,9 @@ class HacktoberStats(commands.Cog): in_review = [] accepted = [] for pr in prs: - if (pr['created_at'] + timedelta(REVIEW_DAYS)) > now: + if (pr["created_at"] + timedelta(REVIEW_DAYS)) > now: in_review.append(pr) - elif (pr['created_at'] <= oct3) or await self._is_accepted(pr): + elif (pr["created_at"] <= oct3) or await self._is_accepted(pr): accepted.append(pr) return in_review, accepted diff --git a/bot/exts/halloween/halloweenify.py b/bot/exts/halloween/halloweenify.py index 5a8f4ecc..47b20a2a 100644 --- a/bot/exts/halloween/halloweenify.py +++ b/bot/exts/halloween/halloweenify.py @@ -34,8 +34,8 @@ class Halloweenify(commands.Cog): embed.colour = discord.Colour.dark_orange() embed.title = "Not spooky enough?" embed.description = ( - f"**{ctx.author.display_name}** wasn\'t spooky enough for you? That\'s understandable, " - f"{ctx.author.display_name} isn\'t scary at all! " + f"**{ctx.author.display_name}** wasn't spooky enough for you? That's understandable, " + f"{ctx.author.display_name} isn't scary at all! " "Let me think of something better. Hmm... I got it!\n\n " ) embed.set_image(url=image) diff --git a/bot/exts/halloween/monsterbio.py b/bot/exts/halloween/monsterbio.py index f484305d..dbafa43f 100644 --- a/bot/exts/halloween/monsterbio.py +++ b/bot/exts/halloween/monsterbio.py @@ -37,7 +37,7 @@ class MonsterBio(commands.Cog): continue options = seeded_random.sample(TEXT_OPTIONS[key], value) - words[key] = ' '.join(options) + words[key] = " ".join(options) embed = discord.Embed( title=f"{name}'s Biography", diff --git a/bot/exts/halloween/monstersurvey.py b/bot/exts/halloween/monstersurvey.py index 0610503d..486e8937 100644 --- a/bot/exts/halloween/monstersurvey.py +++ b/bot/exts/halloween/monstersurvey.py @@ -9,8 +9,8 @@ from discord.ext.commands import Bot, Cog, Context log = logging.getLogger(__name__) EMOJIS = { - 'SUCCESS': u'\u2705', - 'ERROR': u'\u274C' + "SUCCESS": u"\u2705", + "ERROR": u"\u274C" } @@ -25,63 +25,63 @@ class MonsterSurvey(Cog): def __init__(self): """Initializes values for the bot to use within the voting commands.""" - self.registry_location = os.path.join(os.getcwd(), 'bot', 'resources', 'halloween', 'monstersurvey.json') - with open(self.registry_location, 'r', encoding="utf8") as jason: + self.registry_location = os.path.join(os.getcwd(), "bot", "resources", "halloween", "monstersurvey.json") + with open(self.registry_location, "r", encoding="utf8") as jason: self.voter_registry = json.load(jason) def json_write(self) -> None: """Write voting results to a local JSON file.""" log.info("Saved Monster Survey Results") - with open(self.registry_location, 'w', encoding="utf8") as jason: + with open(self.registry_location, "w", encoding="utf8") as jason: json.dump(self.voter_registry, jason, indent=2) def cast_vote(self, id: int, monster: str) -> None: """ - Cast a user's vote for the specified monster. + Cast a user"s vote for the specified monster. If the user has already voted, their existing vote is removed. """ vr = self.voter_registry for m in vr.keys(): - if id not in vr[m]['votes'] and m == monster: - vr[m]['votes'].append(id) + if id not in vr[m]["votes"] and m == monster: + vr[m]["votes"].append(id) else: - if id in vr[m]['votes'] and m != monster: - vr[m]['votes'].remove(id) + if id in vr[m]["votes"] and m != monster: + vr[m]["votes"].remove(id) def get_name_by_leaderboard_index(self, n: int) -> str: """Return the monster at the specified leaderboard index.""" n = n - 1 vr = self.voter_registry - top = sorted(vr, key=lambda k: len(vr[k]['votes']), reverse=True) + top = sorted(vr, key=lambda k: len(vr[k]["votes"]), reverse=True) name = top[n] if n >= 0 else None return name @commands.group( - name='monster', - aliases=('mon',) + name="monster", + aliases=("mon",) ) async def monster_group(self, ctx: Context) -> None: """The base voting command. If nothing is called, then it will return an embed.""" if ctx.invoked_subcommand is None: async with ctx.typing(): default_embed = Embed( - title='Monster Voting', + title="Monster Voting", color=0xFF6800, - description='Vote for your favorite monster!' + description="Vote for your favorite monster!" ) default_embed.add_field( - name='.monster show monster_name(optional)', - value='Show a specific monster. If none is listed, it will give you an error with valid choices.', + name=".monster show monster_name(optional)", + value="Show a specific monster. If none is listed, it will give you an error with valid choices.", inline=False) default_embed.add_field( - name='.monster vote monster_name', - value='Vote for a specific monster. You get one vote, but can change it at any time.', + name=".monster vote monster_name", + value="Vote for a specific monster. You get one vote, but can change it at any time.", inline=False ) default_embed.add_field( - name='.monster leaderboard', - value='Which monster has the most votes? This command will tell you.', + name=".monster leaderboard", + value="Which monster has the most votes? This command will tell you.", inline=False ) default_embed.set_footer(text=f"Monsters choices are: {', '.join(self.voter_registry.keys())}") @@ -89,7 +89,7 @@ class MonsterSurvey(Cog): await ctx.send(embed=default_embed) @monster_group.command( - name='vote' + name="vote" ) async def monster_vote(self, ctx: Context, name: str = None) -> None: """ @@ -110,37 +110,37 @@ class MonsterSurvey(Cog): name = name.lower() vote_embed = Embed( - name='Monster Voting', + name="Monster Voting", color=0xFF6800 ) m = self.voter_registry.get(name) if m is None: - vote_embed.description = f'You cannot vote for {name} because it\'s not in the running.' + vote_embed.description = f"You cannot vote for {name} because it's not in the running." vote_embed.add_field( - name='Use `.monster show {monster_name}` for more information on a specific monster', - value='or use `.monster vote {monster}` to cast your vote for said monster.', + name="Use `.monster show {monster_name}` for more information on a specific monster", + value="or use `.monster vote {monster}` to cast your vote for said monster.", inline=False ) vote_embed.add_field( - name='You may vote for or show the following monsters:', - value=f"{', '.join(self.voter_registry.keys())}" + name="You may vote for or show the following monsters:", + value=", ".join(self.voter_registry.keys()) ) else: self.cast_vote(ctx.author.id, name) vote_embed.add_field( - name='Vote successful!', - value=f'You have successfully voted for {m["full_name"]}!', + name="Vote successful!", + value=f"You have successfully voted for {m['full_name']}!", inline=False ) - vote_embed.set_thumbnail(url=m['image']) + vote_embed.set_thumbnail(url=m["image"]) vote_embed.set_footer(text="Please note that any previous votes have been removed.") self.json_write() await ctx.send(embed=vote_embed) @monster_group.command( - name='show' + name="show" ) async def monster_show(self, ctx: Context, name: str = None) -> None: """Shows the named monster. If one is not named, it sends the default voting embed instead.""" @@ -158,31 +158,31 @@ class MonsterSurvey(Cog): m = self.voter_registry.get(name) if not m: - await ctx.send('That monster does not exist.') + await ctx.send("That monster does not exist.") await ctx.invoke(self.monster_vote) return - embed = Embed(title=m['full_name'], color=0xFF6800) - embed.add_field(name='Summary', value=m['summary']) - embed.set_image(url=m['image']) - embed.set_footer(text=f'To vote for this monster, type .monster vote {name}') + embed = Embed(title=m["full_name"], color=0xFF6800) + embed.add_field(name="Summary", value=m["summary"]) + embed.set_image(url=m["image"]) + embed.set_footer(text=f"To vote for this monster, type .monster vote {name}") await ctx.send(embed=embed) @monster_group.command( - name='leaderboard', - aliases=('lb',) + name="leaderboard", + aliases=("lb",) ) async def monster_leaderboard(self, ctx: Context) -> None: """Shows the current standings.""" async with ctx.typing(): vr = self.voter_registry - top = sorted(vr, key=lambda k: len(vr[k]['votes']), reverse=True) - total_votes = sum(len(m['votes']) for m in self.voter_registry.values()) + top = sorted(vr, key=lambda k: len(vr[k]["votes"]), reverse=True) + total_votes = sum(len(m["votes"]) for m in self.voter_registry.values()) embed = Embed(title="Monster Survey Leader Board", color=0xFF6800) for rank, m in enumerate(top): - votes = len(vr[m]['votes']) + votes = len(vr[m]["votes"]) percentage = ((votes / total_votes) * 100) if total_votes > 0 else 0 embed.add_field(name=f"{rank+1}. {vr[m]['full_name']}", value=( diff --git a/bot/exts/halloween/scarymovie.py b/bot/exts/halloween/scarymovie.py index 48c9f53d..f4cf41db 100644 --- a/bot/exts/halloween/scarymovie.py +++ b/bot/exts/halloween/scarymovie.py @@ -47,7 +47,7 @@ class ScaryMovie(commands.Cog): total_pages = data.get("total_pages") # Get movie details from one random result on a random page - params['page'] = random.randint(1, total_pages) + params["page"] = random.randint(1, total_pages) async with self.bot.http_session.get(url=url, params=params, headers=headers) as response: data = await response.json() selection_id = random.choice(data.get("results")).get("id") @@ -71,26 +71,26 @@ class ScaryMovie(commands.Cog): # Get cast names cast = [] - for actor in movie.get('credits', {}).get('cast', [])[:3]: - cast.append(actor.get('name')) + for actor in movie.get("credits", {}).get("cast", [])[:3]: + cast.append(actor.get("name")) # Get director name - director = movie.get('credits', {}).get('crew', []) + director = movie.get("credits", {}).get("crew", []) if director: - director = director[0].get('name') + director = director[0].get("name") # Determine the spookiness rating - rating = '' - rating_count = movie.get('vote_average', 0) / 2 + rating = "" + rating_count = movie.get("vote_average", 0) / 2 for _ in range(int(rating_count)): - rating += ':skull:' + rating += ":skull:" if (rating_count % 1) >= .5: - rating += ':bat:' + rating += ":bat:" # Try to get year of release and runtime - year = movie.get('release_date', [])[:4] - runtime = movie.get('runtime') + year = movie.get("release_date", [])[:4] + runtime = movie.get("runtime") runtime = f"{runtime} minutes" if runtime else None # Not all these attributes will always be present diff --git a/bot/exts/halloween/spookygif.py b/bot/exts/halloween/spookygif.py index bfdf2128..ffb91b1b 100644 --- a/bot/exts/halloween/spookygif.py +++ b/bot/exts/halloween/spookygif.py @@ -19,11 +19,11 @@ class SpookyGif(commands.Cog): async def spookygif(self, ctx: commands.Context) -> None: """Fetches a random gif from the GIPHY API and responds with it.""" async with ctx.typing(): - params = {'api_key': Tokens.giphy, 'tag': 'halloween', 'rating': 'g'} + params = {"api_key": Tokens.giphy, "tag": "halloween", "rating": "g"} # Make a GET request to the Giphy API to get a random halloween gif. - async with self.bot.http_session.get('http://api.giphy.com/v1/gifs/random', params=params) as resp: + async with self.bot.http_session.get("http://api.giphy.com/v1/gifs/random", params=params) as resp: data = await resp.json() - url = data['data']['image_url'] + url = data["data"]["image_url"] embed = discord.Embed(colour=0x9b59b6) embed.title = "A spooooky gif!" diff --git a/bot/exts/halloween/spookynamerate.py b/bot/exts/halloween/spookynamerate.py index 9191f5f6..87172922 100644 --- a/bot/exts/halloween/spookynamerate.py +++ b/bot/exts/halloween/spookynamerate.py @@ -81,7 +81,7 @@ class SpookyNameRate(Cog): # The data cache stores small information such as the current name that is going on and whether it is the first time # the bot is running data = RedisCache() - debug = getenv('SPOOKYNAMERATE_DEBUG', False) # Enable if you do not want to limit the commands to October or if + debug = getenv("SPOOKYNAMERATE_DEBUG", False) # Enable if you do not want to limit the commands to October or if # you do not want to wait till 12 UTC. Note: if debug is enabled and you run `.cogs reload spookynamerate`, it # will automatically start the scoring and announcing the result (without waiting for 12, so do not expect it to.). # Also, it won't wait for the two hours (when the poll closes). diff --git a/bot/exts/halloween/spookyrating.py b/bot/exts/halloween/spookyrating.py index dc398e2e..6c79fbed 100644 --- a/bot/exts/halloween/spookyrating.py +++ b/bot/exts/halloween/spookyrating.py @@ -46,16 +46,16 @@ class SpookyRating(commands.Cog): _, data = SPOOKY_DATA[index] embed = discord.Embed( - title=data['title'], - description=f'{who} scored {spooky_percent}%!', + title=data["title"], + description=f"{who} scored {spooky_percent}%!", color=Colours.orange ) embed.add_field( - name='A whisper from Satan', - value=data['text'] + name="A whisper from Satan", + value=data["text"] ) embed.set_thumbnail( - url=data['image'] + url=data["image"] ) await ctx.send(embed=embed) diff --git a/bot/exts/halloween/spookyreact.py b/bot/exts/halloween/spookyreact.py index dabc3c1f..25e783f4 100644 --- a/bot/exts/halloween/spookyreact.py +++ b/bot/exts/halloween/spookyreact.py @@ -11,13 +11,13 @@ from bot.utils.decorators import in_month log = logging.getLogger(__name__) SPOOKY_TRIGGERS = { - 'spooky': (r"\bspo{2,}ky\b", "\U0001F47B"), - 'skeleton': (r"\bskeleton\b", "\U0001F480"), - 'doot': (r"\bdo{2,}t\b", "\U0001F480"), - 'pumpkin': (r"\bpumpkin\b", "\U0001F383"), - 'halloween': (r"\bhalloween\b", "\U0001F383"), - 'jack-o-lantern': (r"\bjack-o-lantern\b", "\U0001F383"), - 'danger': (r"\bdanger\b", "\U00002620") + "spooky": (r"\bspo{2,}ky\b", "\U0001F47B"), + "skeleton": (r"\bskeleton\b", "\U0001F480"), + "doot": (r"\bdo{2,}t\b", "\U0001F480"), + "pumpkin": (r"\bpumpkin\b", "\U0001F383"), + "halloween": (r"\bhalloween\b", "\U0001F383"), + "jack-o-lantern": (r"\bjack-o-lantern\b", "\U0001F383"), + "danger": (r"\bdanger\b", "\U00002620") } diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index 757a2a1e..4fe3bc09 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -142,14 +142,14 @@ class InternalEval(commands.Cog): log.trace("Sending the formatted output back to the context") await self._send_output(ctx, eval_context.format_output()) - @commands.group(name='internal', aliases=('int',)) + @commands.group(name="internal", aliases=("int",)) @with_role(Roles.admin) async def internal_group(self, ctx: commands.Context) -> None: """Internal commands. Top secret!""" if not ctx.invoked_subcommand: await invoke_help_command(ctx) - @internal_group.command(name='eval', aliases=('e',)) + @internal_group.command(name="eval", aliases=("e",)) @with_role(Roles.admin) async def eval(self, ctx: commands.Context, *, code: str) -> None: """Run eval in a REPL-like format.""" @@ -157,7 +157,7 @@ class InternalEval(commands.Cog): blocks = [block for block in match if block.group("block")] if len(blocks) > 1: - code = '\n'.join(block.group("code") for block in blocks) + code = "\n".join(block.group("code") for block in blocks) else: match = match[0] if len(blocks) == 0 else blocks[0] code, block, lang, delim = match.group("code", "block", "lang", "delim") @@ -168,7 +168,7 @@ class InternalEval(commands.Cog): code = textwrap.dedent(code) await self._eval(ctx, code) - @internal_group.command(name='reset', aliases=("clear", "exit", "r", "c")) + @internal_group.command(name="reset", aliases=("clear", "exit", "r", "c")) @with_role(Roles.admin) async def reset(self, ctx: commands.Context) -> None: """Reset the context and locals of the eval session.""" diff --git a/bot/exts/valentines/be_my_valentine.py b/bot/exts/valentines/be_my_valentine.py index 051f09b8..e94efda0 100644 --- a/bot/exts/valentines/be_my_valentine.py +++ b/bot/exts/valentines/be_my_valentine.py @@ -70,7 +70,7 @@ class BeMyValentine(commands.Cog): await ctx.send("The lovefest role has been successfully removed!") @commands.cooldown(1, 1800, BucketType.user) - @commands.group(name='bemyvalentine', invoke_without_command=True) + @commands.group(name="bemyvalentine", invoke_without_command=True) async def send_valentine( self, ctx: commands.Context, user: discord.Member, *, valentine_type: str = None ) -> None: @@ -102,14 +102,14 @@ class BeMyValentine(commands.Cog): valentine, title = self.valentine_check(valentine_type) embed = discord.Embed( - title=f'{emoji_1} {title} {user.display_name} {emoji_2}', - description=f'{valentine} \n **{emoji_2}From {ctx.author}{emoji_1}**', + title=f"{emoji_1} {title} {user.display_name} {emoji_2}", + description=f"{valentine} \n **{emoji_2}From {ctx.author}{emoji_1}**", color=Colours.pink ) await channel.send(user.mention, embed=embed) @commands.cooldown(1, 1800, BucketType.user) - @send_valentine.command(name='secret') + @send_valentine.command(name="secret") async def anonymous( self, ctx: commands.Context, user: discord.Member, *, valentine_type: str = None ) -> None: @@ -137,8 +137,8 @@ class BeMyValentine(commands.Cog): valentine, title = self.valentine_check(valentine_type) embed = discord.Embed( - title=f'{emoji_1}{title} {user.display_name}{emoji_2}', - description=f'{valentine} \n **{emoji_2}From anonymous{emoji_1}**', + title=f"{emoji_1}{title} {user.display_name}{emoji_2}", + description=f"{valentine} \n **{emoji_2}From anonymous{emoji_1}**", color=Colours.pink ) await ctx.message.delete() @@ -154,18 +154,18 @@ class BeMyValentine(commands.Cog): if valentine_type is None: valentine, title = self.random_valentine() - elif valentine_type.lower() in ['p', 'poem']: + elif valentine_type.lower() in ["p", "poem"]: valentine = self.valentine_poem() - title = 'A poem dedicated to' + title = "A poem dedicated to" - elif valentine_type.lower() in ['c', 'compliment']: + elif valentine_type.lower() in ["c", "compliment"]: valentine = self.valentine_compliment() - title = 'A compliment for' + title = "A compliment for" else: # in this case, the user decides to type his own valentine. valentine = valentine_type - title = 'A message for' + title = "A message for" return valentine, title @staticmethod @@ -177,23 +177,23 @@ class BeMyValentine(commands.Cog): def random_valentine(self) -> Tuple[str, str]: """Grabs a random poem or a compliment (any message).""" - valentine_poem = random.choice(self.valentines['valentine_poems']) - valentine_compliment = random.choice(self.valentines['valentine_compliments']) + valentine_poem = random.choice(self.valentines["valentine_poems"]) + valentine_compliment = random.choice(self.valentines["valentine_compliments"]) random_valentine = random.choice([valentine_compliment, valentine_poem]) if random_valentine == valentine_poem: - title = 'A poem dedicated to' + title = "A poem dedicated to" else: - title = 'A compliment for ' + title = "A compliment for " return random_valentine, title def valentine_poem(self) -> str: """Grabs a random poem.""" - valentine_poem = random.choice(self.valentines['valentine_poems']) + valentine_poem = random.choice(self.valentines["valentine_poems"]) return valentine_poem def valentine_compliment(self) -> str: """Grabs a random compliment.""" - valentine_compliment = random.choice(self.valentines['valentine_compliments']) + valentine_compliment = random.choice(self.valentines["valentine_compliments"]) return valentine_compliment diff --git a/bot/exts/valentines/myvalenstate.py b/bot/exts/valentines/myvalenstate.py index 19e6b57f..7a0f8318 100644 --- a/bot/exts/valentines/myvalenstate.py +++ b/bot/exts/valentines/myvalenstate.py @@ -47,12 +47,12 @@ class MyValenstate(commands.Cog): """Find the vacation spot(s) with the most matching characters to the invoking user.""" eq_chars = collections.defaultdict(int) if name is None: - author = ctx.author.name.lower().replace(' ', '') + author = ctx.author.name.lower().replace(" ", "") else: - author = name.lower().replace(' ', '') + author = name.lower().replace(" ", "") for state in STATES.keys(): - lower_state = state.lower().replace(' ', '') + lower_state = state.lower().replace(" ", "") eq_chars[state] = self.levenshtein(author, lower_state) matches = [x for x, y in eq_chars.items() if y == min(eq_chars.values())] @@ -73,8 +73,8 @@ class MyValenstate(commands.Cog): " you better" embed = discord.Embed( - title=f'Your Valenstate is {valenstate} \u2764', - description=f'{STATES[valenstate]["text"]}', + title=f"Your Valenstate is {valenstate} \u2764", + description=f"{STATES[valenstate]['text']}", colour=Colours.pink ) embed.add_field(name=embed_title, value=embed_text) diff --git a/bot/exts/valentines/pickuplines.py b/bot/exts/valentines/pickuplines.py index bb322016..216ee13b 100644 --- a/bot/exts/valentines/pickuplines.py +++ b/bot/exts/valentines/pickuplines.py @@ -25,14 +25,14 @@ class PickupLine(commands.Cog): Note that most of them are very cheesy. """ - random_line = random.choice(pickup_lines['lines']) + random_line = random.choice(pickup_lines["lines"]) embed = discord.Embed( - title=':cheese: Your pickup line :cheese:', - description=random_line['line'], + title=":cheese: Your pickup line :cheese:", + description=random_line["line"], color=Colours.pink ) embed.set_thumbnail( - url=random_line.get('image', pickup_lines['placeholder']) + url=random_line.get("image", pickup_lines["placeholder"]) ) await ctx.send(embed=embed) diff --git a/bot/exts/valentines/savethedate.py b/bot/exts/valentines/savethedate.py index bda5d8c6..ed2d2c5f 100644 --- a/bot/exts/valentines/savethedate.py +++ b/bot/exts/valentines/savethedate.py @@ -23,7 +23,7 @@ class SaveTheDate(commands.Cog): @commands.command() async def savethedate(self, ctx: commands.Context) -> None: """Gives you ideas for what to do on a date with your valentine.""" - random_date = random.choice(VALENTINES_DATES['ideas']) + random_date = random.choice(VALENTINES_DATES["ideas"]) emoji_1 = random.choice(HEART_EMOJIS) emoji_2 = random.choice(HEART_EMOJIS) embed = discord.Embed( diff --git a/bot/exts/valentines/valentine_zodiac.py b/bot/exts/valentines/valentine_zodiac.py index 237fe5db..72fd93fe 100644 --- a/bot/exts/valentines/valentine_zodiac.py +++ b/bot/exts/valentines/valentine_zodiac.py @@ -14,7 +14,7 @@ from bot.constants import Colours log = logging.getLogger(__name__) -LETTER_EMOJI = ':love_letter:' +LETTER_EMOJI = ":love_letter:" HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_heart:", ":two_hearts:"] @@ -32,8 +32,8 @@ class ValentineZodiac(commands.Cog): with explanation_file.open(encoding="utf8") as json_data: zodiac_fact = json.load(json_data) for zodiac_data in zodiac_fact.values(): - zodiac_data['start_at'] = datetime.fromisoformat(zodiac_data['start_at']) - zodiac_data['end_at'] = datetime.fromisoformat(zodiac_data['end_at']) + zodiac_data["start_at"] = datetime.fromisoformat(zodiac_data["start_at"]) + zodiac_data["end_at"] = datetime.fromisoformat(zodiac_data["end_at"]) with compatibility_file.open(encoding="utf8") as json_data: zodiacs = json.load(json_data) @@ -62,10 +62,10 @@ class ValentineZodiac(commands.Cog): log.trace("Making zodiac embed.") embed.title = f"__{zodiac}__" embed.description = self.zodiac_fact[zodiac]["About"] - embed.add_field(name='__Motto__', value=self.zodiac_fact[zodiac]["Motto"], inline=False) - embed.add_field(name='__Strengths__', value=self.zodiac_fact[zodiac]["Strengths"], inline=False) - embed.add_field(name='__Weaknesses__', value=self.zodiac_fact[zodiac]["Weaknesses"], inline=False) - embed.add_field(name='__Full form__', value=self.zodiac_fact[zodiac]["full_form"], inline=False) + embed.add_field(name="__Motto__", value=self.zodiac_fact[zodiac]["Motto"], inline=False) + embed.add_field(name="__Strengths__", value=self.zodiac_fact[zodiac]["Strengths"], inline=False) + embed.add_field(name="__Weaknesses__", value=self.zodiac_fact[zodiac]["Weaknesses"], inline=False) + embed.add_field(name="__Full form__", value=self.zodiac_fact[zodiac]["full_form"], inline=False) embed.set_thumbnail(url=self.zodiac_fact[zodiac]["url"]) else: embed = self.generate_invalidname_embed(zodiac) @@ -79,7 +79,7 @@ class ValentineZodiac(commands.Cog): log.trace("Zodiac name sent.") return zodiac_name - @commands.group(name='zodiac', invoke_without_command=True) + @commands.group(name="zodiac", invoke_without_command=True) async def zodiac(self, ctx: commands.Context, zodiac_sign: str) -> None: """Provides information about zodiac sign by taking zodiac sign name as input.""" final_embed = self.zodiac_build_embed(zodiac_sign) @@ -93,9 +93,9 @@ class ValentineZodiac(commands.Cog): month = month.capitalize() try: month = list(calendar.month_abbr).index(month[:3]) - log.trace('Valid month name entered by user') + log.trace("Valid month name entered by user") except ValueError: - log.info('Invalid month name entered by user') + log.info("Invalid month name entered by user") await ctx.send(f"Sorry, but `{month}` is not a valid month name.") return if (month == 1 and 1 <= date <= 19) or (month == 12 and 22 <= date <= 31): @@ -109,14 +109,14 @@ class ValentineZodiac(commands.Cog): final_embed = discord.Embed() final_embed.color = Colours.soft_red final_embed.description = f"Zodiac sign could not be found because.\n```{e}```" - log.info(f'Error in "zodiac date" command:\n{e}.') + log.info(f"Error in 'zodiac date' command:\n{e}.") else: final_embed = self.zodiac_build_embed(zodiac_sign_based_on_date) await ctx.send(embed=final_embed) log.trace("Embed from date successfully sent.") - @zodiac.command(name="partnerzodiac", aliases=['partner']) + @zodiac.command(name="partnerzodiac", aliases=["partner"]) async def partner_zodiac(self, ctx: commands.Context, zodiac_sign: str) -> None: """Provides a random counter compatible zodiac sign to the given user's zodiac sign.""" embed = discord.Embed() @@ -128,12 +128,12 @@ class ValentineZodiac(commands.Cog): emoji2 = random.choice(HEART_EMOJIS) embed.title = "Zodiac Compatibility" embed.description = ( - f'{zodiac_sign.capitalize()}{emoji1}{compatible_zodiac["Zodiac"]}\n' - f'{emoji2}Compatibility meter : {compatible_zodiac["compatibility_score"]}{emoji2}' + f"{zodiac_sign.capitalize()}{emoji1}{compatible_zodiac['Zodiac']}\n" + f"{emoji2}Compatibility meter : {compatible_zodiac['compatibility_score']}{emoji2}" ) embed.add_field( - name=f'A letter from Dr.Zodiac {LETTER_EMOJI}', - value=compatible_zodiac['description'] + name=f"A letter from Dr.Zodiac {LETTER_EMOJI}", + value=compatible_zodiac["description"] ) else: embed = self.generate_invalidname_embed(zodiac_sign) diff --git a/bot/exts/valentines/whoisvalentine.py b/bot/exts/valentines/whoisvalentine.py index 73cdcf52..3789fad5 100644 --- a/bot/exts/valentines/whoisvalentine.py +++ b/bot/exts/valentines/whoisvalentine.py @@ -18,17 +18,17 @@ with open(Path("bot/resources/valentines/valentine_facts.json"), "r", encoding=" class ValentineFacts(commands.Cog): """A Cog for displaying facts about Saint Valentine.""" - @commands.command(aliases=('whoisvalentine', 'saint_valentine')) + @commands.command(aliases=("whoisvalentine", "saint_valentine")) async def who_is_valentine(self, ctx: commands.Context) -> None: """Displays info about Saint Valentine.""" embed = discord.Embed( title="Who is Saint Valentine?", - description=FACTS['whois'], + description=FACTS["whois"], color=Colours.pink ) embed.set_thumbnail( - url='https://upload.wikimedia.org/wikipedia/commons/thumb/f/f1/Saint_Valentine_-_' - 'facial_reconstruction.jpg/1024px-Saint_Valentine_-_facial_reconstruction.jpg' + url="https://upload.wikimedia.org/wikipedia/commons/thumb/f/f1/Saint_Valentine_-_" + "facial_reconstruction.jpg/1024px-Saint_Valentine_-_facial_reconstruction.jpg" ) await ctx.send(embed=embed) @@ -37,8 +37,8 @@ class ValentineFacts(commands.Cog): async def valentine_fact(self, ctx: commands.Context) -> None: """Shows a random fact about Valentine's Day.""" embed = discord.Embed( - title=choice(FACTS['titles']), - description=choice(FACTS['text']), + title=choice(FACTS["titles"]), + description=choice(FACTS["text"]), color=Colours.pink ) diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py index 35ef0a7b..2fac2086 100644 --- a/bot/utils/__init__.py +++ b/bot/utils/__init__.py @@ -43,12 +43,12 @@ async def disambiguate( or if the user makes an invalid choice. """ if len(entries) == 0: - raise BadArgument('No matches found.') + raise BadArgument("No matches found.") if len(entries) == 1: return entries[0] - choices = (f'{index}: {entry}' for index, entry in enumerate(entries, start=1)) + choices = (f"{index}: {entry}" for index, entry in enumerate(entries, start=1)) def check(message: discord.Message) -> bool: return (message.content.isdigit() @@ -59,7 +59,7 @@ async def disambiguate( if embed is None: embed = discord.Embed() - coro1 = ctx.bot.wait_for('message', check=check, timeout=timeout) + coro1 = ctx.bot.wait_for("message", check=check, timeout=timeout) coro2 = LinePaginator.paginate(choices, ctx, embed=embed, max_lines=entries_per_page, empty=empty, max_size=6000, timeout=9000) @@ -74,7 +74,7 @@ async def disambiguate( if result is None: for coro in pending: coro.cancel() - raise BadArgument('Canceled.') + raise BadArgument("Canceled.") # Pagination was not initiated, only one page if result.author == ctx.bot.user: @@ -85,7 +85,7 @@ async def disambiguate( for coro in pending: coro.cancel() except asyncio.TimeoutError: - raise BadArgument('Timed out.') + raise BadArgument("Timed out.") # Guaranteed to not error because of isdigit() in check index = int(result.content) @@ -93,7 +93,7 @@ async def disambiguate( try: return entries[index - 1] except IndexError: - raise BadArgument('Invalid choice.') + raise BadArgument("Invalid choice.") def replace_many( @@ -139,7 +139,7 @@ def replace_many( return replacement # Clean punctuation from word so string methods work - cleaned_word = word.translate(str.maketrans('', '', string.punctuation)) + cleaned_word = word.translate(str.maketrans("", "", string.punctuation)) if cleaned_word.isupper(): return replacement.upper() elif cleaned_word[0].isupper(): diff --git a/bot/utils/checks.py b/bot/utils/checks.py index 9dd4dde0..3783dd38 100644 --- a/bot/utils/checks.py +++ b/bot/utils/checks.py @@ -154,8 +154,8 @@ def cooldown_with_role_bypass(rate: int, per: float, type: BucketType = BucketTy # # If the `before_invoke` detail is ever a problem then I can quickly just swap over. if not isinstance(command, Command): - raise TypeError('Decorator `cooldown_with_role_bypass` must be applied after the command decorator. ' - 'This means it has to be above the command decorator in the code.') + raise TypeError("Decorator `cooldown_with_role_bypass` must be applied after the command decorator. " + "This means it has to be above the command decorator in the code.") command._before_invoke = predicate diff --git a/bot/utils/decorators.py b/bot/utils/decorators.py index 60066dc4..c0783144 100644 --- a/bot/utils/decorators.py +++ b/bot/utils/decorators.py @@ -269,7 +269,7 @@ def whitelist_check(**default_kwargs: t.Container[int]) -> t.Callable[[Context], channels.update(channel.id for channel in category.text_channels) if channels: - channels_str = ', '.join(f"<#{c_id}>" for c_id in channels) + channels_str = ", ".join(f"<#{c_id}>" for c_id in channels) message = f"Sorry, but you may only use this command within {channels_str}." else: message = "Sorry, but you may not use this command." diff --git a/bot/utils/extensions.py b/bot/utils/extensions.py index 459588a1..cd491c4b 100644 --- a/bot/utils/extensions.py +++ b/bot/utils/extensions.py @@ -35,8 +35,8 @@ def walk_extensions() -> Iterator[str]: async def invoke_help_command(ctx: Context) -> None: """Invoke the help command or default help command if help extensions is not loaded.""" - if 'bot.exts.evergreen.help' in ctx.bot.extensions: - help_command = ctx.bot.get_command('help') + if "bot.exts.evergreen.help" in ctx.bot.extensions: + help_command = ctx.bot.get_command("help") await ctx.invoke(help_command, ctx.command.qualified_name) return await ctx.send_help(ctx.command) diff --git a/bot/utils/halloween/spookifications.py b/bot/utils/halloween/spookifications.py index 11f69850..f69dd6fd 100644 --- a/bot/utils/halloween/spookifications.py +++ b/bot/utils/halloween/spookifications.py @@ -13,16 +13,16 @@ def inversion(im: Image) -> Image: Returns an inverted image when supplied with an Image object. """ - im = im.convert('RGB') + im = im.convert("RGB") inv = ImageOps.invert(im) return inv def pentagram(im: Image) -> Image: """Adds pentagram to the image.""" - im = im.convert('RGB') + im = im.convert("RGB") wt, ht = im.size - penta = Image.open('bot/resources/halloween/bloody-pentagram.png') + penta = Image.open("bot/resources/halloween/bloody-pentagram.png") penta = penta.resize((wt, ht)) im.paste(penta, (0, 0), penta) return im @@ -35,9 +35,9 @@ def bat(im: Image) -> Image: The bat silhoutte is of a size at least one-fifths that of the original image and may be rotated up to 90 degrees anti-clockwise. """ - im = im.convert('RGB') + im = im.convert("RGB") wt, ht = im.size - bat = Image.open('bot/resources/halloween/bat-clipart.png') + bat = Image.open("bot/resources/halloween/bat-clipart.png") bat_size = randint(wt//10, wt//7) rot = randint(0, 90) bat = bat.resize((bat_size, bat_size)) diff --git a/bot/utils/pagination.py b/bot/utils/pagination.py index a97dd023..a073a00b 100644 --- a/bot/utils/pagination.py +++ b/bot/utils/pagination.py @@ -26,7 +26,7 @@ class EmptyPaginatorEmbed(Exception): class LinePaginator(Paginator): """A class that aids in paginating code blocks for Discord messages.""" - def __init__(self, prefix: str = '```', suffix: str = '```', max_size: int = 2000, max_lines: int = None): + def __init__(self, prefix: str = "```", suffix: str = "```", max_size: int = 2000, max_lines: int = None): """ Overrides the Paginator.__init__ from inside discord.ext.commands. @@ -44,7 +44,7 @@ class LinePaginator(Paginator): self._count = len(prefix) + 1 # prefix + newline self._pages = [] - def add_line(self, line: str = '', *, empty: bool = False) -> None: + def add_line(self, line: str = "", *, empty: bool = False) -> None: """ Adds a line to the current page. @@ -56,7 +56,7 @@ class LinePaginator(Paginator): If `empty` is True, an empty line will be placed after the a given `line`. """ if len(line) > self.max_size - len(self.prefix) - 2: - raise RuntimeError('Line exceeds maximum page size %s' % (self.max_size - len(self.prefix) - 2)) + raise RuntimeError("Line exceeds maximum page size %s" % (self.max_size - len(self.prefix) - 2)) if self.max_lines is not None: if self._linecount >= self.max_lines: @@ -71,7 +71,7 @@ class LinePaginator(Paginator): self._current_page.append(line) if empty: - self._current_page.append('') + self._current_page.append("") self._count += 1 @classmethod -- cgit v1.2.3 From c051abf49ce5bb82d127c8ddfad7ece431bb028f Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Tue, 4 May 2021 13:15:55 -0400 Subject: chore: Apply suggested changes --- bot/exts/evergreen/magic_8ball.py | 6 +++--- bot/exts/evergreen/minesweeper.py | 4 ++-- bot/exts/evergreen/tic_tac_toe.py | 4 ++-- bot/exts/evergreen/wonder_twins.py | 4 ++-- bot/exts/halloween/hacktoberstats.py | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bot/exts/evergreen/magic_8ball.py b/bot/exts/evergreen/magic_8ball.py index 708aa6d2..7c9b929d 100644 --- a/bot/exts/evergreen/magic_8ball.py +++ b/bot/exts/evergreen/magic_8ball.py @@ -13,7 +13,7 @@ log = logging.getLogger(__name__) class Magic8ball(commands.Cog): """A Magic 8ball command to respond to a user's question.""" - def __init__(self, _bot: Bot): + def __init__(self): with open(Path("bot/resources/evergreen/magic8ball.json"), "r", encoding="utf8") as file: self.answers = json.load(file) @@ -28,5 +28,5 @@ class Magic8ball(commands.Cog): def setup(bot: Bot) -> None: - """Load the Magic8Ball cog.""" - bot.add_cog(Magic8ball(bot)) + """Load the Magic8Ball Cog.""" + bot.add_cog(Magic8ball()) diff --git a/bot/exts/evergreen/minesweeper.py b/bot/exts/evergreen/minesweeper.py index f2c5e656..bd28d365 100644 --- a/bot/exts/evergreen/minesweeper.py +++ b/bot/exts/evergreen/minesweeper.py @@ -79,7 +79,7 @@ GamesDict = typing.Dict[int, Game] class Minesweeper(commands.Cog): """Play a game of Minesweeper.""" - def __init__(self, _bot: Bot) -> None: + def __init__(self) -> None: self.games: GamesDict = {} # Store the currently running games @commands.group(name="minesweeper", aliases=("ms",), invoke_without_command=True) @@ -295,4 +295,4 @@ class Minesweeper(commands.Cog): def setup(bot: Bot) -> None: """Load the Minesweeper cog.""" - bot.add_cog(Minesweeper(bot)) + bot.add_cog(Minesweeper()) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 7b387c0a..bd5e0102 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -246,7 +246,7 @@ def is_requester_free() -> t.Callable: class TicTacToe(Cog): """TicTacToe cog contains tic-tac-toe game commands.""" - def __init__(self, _bot: Bot): + def __init__(self): self.games: t.List[Game] = [] @guild_only() @@ -323,4 +323,4 @@ class TicTacToe(Cog): def setup(bot: Bot) -> None: """Load the TicTacToe cog.""" - bot.add_cog(TicTacToe(bot)) + bot.add_cog(TicTacToe()) diff --git a/bot/exts/evergreen/wonder_twins.py b/bot/exts/evergreen/wonder_twins.py index 9fa2d7f8..437d69f1 100644 --- a/bot/exts/evergreen/wonder_twins.py +++ b/bot/exts/evergreen/wonder_twins.py @@ -10,7 +10,7 @@ from bot.bot import Bot class WonderTwins(Cog): """Cog for a Wonder Twins inspired command.""" - def __init__(self, _bot: Bot): + def __init__(self): with open(Path.cwd() / "bot" / "resources" / "evergreen" / "wonder_twins.yaml", "r", encoding="utf-8") as f: info = yaml.load(f, Loader=yaml.FullLoader) self.water_types = info["water_types"] @@ -46,4 +46,4 @@ class WonderTwins(Cog): def setup(bot: Bot) -> None: """Load the WonderTwins cog.""" - bot.add_cog(WonderTwins(bot)) + bot.add_cog(WonderTwins()) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 25da9ad5..33e9ca31 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -10,7 +10,7 @@ from async_rediscache import RedisCache from discord.ext import commands from bot.bot import Bot -from bot.constants import Channels, Month, NEGATIVE_REPLIES, Tokens, WHITELISTED_CHANNELS +from bot.constants import Channels, Colours, Month, NEGATIVE_REPLIES, Tokens, WHITELISTED_CHANNELS from bot.utils.decorators import in_month, whitelist_override log = logging.getLogger(__name__) @@ -157,7 +157,7 @@ class HacktoberStats(commands.Cog): stats_embed = discord.Embed( title=f"{github_username}'s Hacktoberfest", - color=0x9c4af7, + color=Colours.purple, description=( f"{github_username} has made {n} valid " f"{self._contributionator(n)} in " @@ -227,7 +227,7 @@ class HacktoberStats(commands.Cog): f"+created:{date_range}" f"&per_page={per_page}" ) - log.logProcesses.debug(f"GitHub query URL generated: {query_url}") + log.debug(f"GitHub query URL generated: {query_url}") jsonresp = await self._fetch_url(query_url, REQUEST_HEADERS) if "message" in jsonresp: -- cgit v1.2.3 From dd20f59b9fb12288f286d479b5719697ab1a30f4 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Tue, 4 May 2021 15:04:18 -0400 Subject: Apply suggested changes to hanukkah_embed.py --- bot/exts/christmas/hanukkah_embed.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/exts/christmas/hanukkah_embed.py b/bot/exts/christmas/hanukkah_embed.py index 32002f76..214044b8 100644 --- a/bot/exts/christmas/hanukkah_embed.py +++ b/bot/exts/christmas/hanukkah_embed.py @@ -65,24 +65,24 @@ class HanukkahEmbed(commands.Cog): hanukkah_start_hour = 18 if hours < hanukkah_start_hour: embed.description = (f"Hanukkah hasnt started yet, " - f"it will start in about {hanukkah_start_hour-hours} hour/s.") + f"it will start in about {hanukkah_start_hour - hours} hour/s.") await ctx.send(embed=embed) return elif hours > hanukkah_start_hour: embed.description = (f"It is the starting day of Hanukkah ! " - f"Its been {hours-hanukkah_start_hour} hours hanukkah started !") + f"Its been {hours - hanukkah_start_hour} hours hanukkah started !") await ctx.send(embed=embed) return festival_day = self.hanukkah_days.index(day) number_suffixes = ["st", "nd", "rd", "th"] suffix = "" - if int(festival_day) == 1: + if festival_day == 1: suffix = number_suffixes[0] - if int(festival_day) == 2: + if festival_day == 2: suffix = number_suffixes[1] - if int(festival_day) == 3: + if festival_day == 3: suffix = number_suffixes[2] - if int(festival_day) > 3: + if festival_day > 3: suffix = number_suffixes[3] message = "" for _ in range(1, festival_day + 1): -- cgit v1.2.3 From 6d31315fa72ca9e6ccd7553e78548fed6e5e4829 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Tue, 4 May 2021 15:44:31 -0400 Subject: fix: Pass bot only when __init__ is defined --- bot/exts/easter/bunny_name_generator.py | 2 +- bot/exts/easter/egg_decorating.py | 2 +- bot/exts/evergreen/avatar_modification/avatar_modify.py | 5 +++-- bot/exts/evergreen/bookmark.py | 2 +- bot/exts/evergreen/catify.py | 8 +++----- bot/exts/evergreen/conversationstarters.py | 2 +- bot/exts/evergreen/emoji.py | 2 +- bot/exts/evergreen/error_handler.py | 2 +- bot/exts/evergreen/latex.py | 2 +- bot/exts/evergreen/pythonfacts.py | 2 +- bot/exts/evergreen/snakes/__init__.py | 5 ++--- bot/exts/evergreen/source.py | 2 +- bot/exts/evergreen/speedrun.py | 2 +- bot/exts/evergreen/timed.py | 2 +- bot/exts/evergreen/uptime.py | 2 +- bot/exts/halloween/timeleft.py | 5 +---- bot/exts/valentines/be_my_valentine.py | 4 ++-- bot/exts/valentines/lovecalculator.py | 2 +- bot/exts/valentines/movie_generator.py | 4 ++-- bot/exts/valentines/myvalenstate.py | 7 ++----- bot/exts/valentines/pickuplines.py | 2 +- bot/exts/valentines/savethedate.py | 2 +- bot/exts/valentines/valentine_zodiac.py | 2 +- bot/exts/valentines/whoisvalentine.py | 2 +- 24 files changed, 32 insertions(+), 40 deletions(-) diff --git a/bot/exts/easter/bunny_name_generator.py b/bot/exts/easter/bunny_name_generator.py index 23f85226..19a0b0f6 100644 --- a/bot/exts/easter/bunny_name_generator.py +++ b/bot/exts/easter/bunny_name_generator.py @@ -88,4 +88,4 @@ class BunnyNameGenerator(commands.Cog): def setup(bot: Bot) -> None: """Load the Bunny Name Generator Cog.""" - bot.add_cog(BunnyNameGenerator(bot)) + bot.add_cog(BunnyNameGenerator()) diff --git a/bot/exts/easter/egg_decorating.py b/bot/exts/easter/egg_decorating.py index 1432fa31..3744989d 100644 --- a/bot/exts/easter/egg_decorating.py +++ b/bot/exts/easter/egg_decorating.py @@ -118,4 +118,4 @@ class EggDecorating(commands.Cog): def setup(bot: Bot) -> None: """Load the Egg decorating Cog.""" - bot.add_cog(EggDecorating(bot)) + bot.add_cog(EggDecorating()) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 0baee8b2..2304e459 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -11,6 +11,7 @@ from aiohttp import client_exceptions from discord.ext import commands from discord.ext.commands.errors import BadArgument +from bot.bot import Bot from bot.constants import Client, Colours, Emojis from bot.exts.evergreen.avatar_modification._effects import PfpEffects from bot.utils.extensions import invoke_help_command @@ -58,7 +59,7 @@ def file_safe_name(effect: str, display_name: str) -> str: class AvatarModify(commands.Cog): """Various commands for users to apply affects to their own avatars.""" - def __init__(self, bot: commands.Bot) -> None: + def __init__(self, bot: Bot) -> None: self.bot = bot async def _fetch_member(self, member_id: int) -> t.Optional[discord.Member]: @@ -309,6 +310,6 @@ class AvatarModify(commands.Cog): await ctx.send(file=file, embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the PfpModify cog.""" bot.add_cog(AvatarModify(bot)) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 6a272784..f0fad0ea 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -60,4 +60,4 @@ class Bookmark(commands.Cog): def setup(bot: Bot) -> None: """Load the Bookmark cog.""" - bot.add_cog(Bookmark(bot)) + bot.add_cog(Bookmark()) diff --git a/bot/exts/evergreen/catify.py b/bot/exts/evergreen/catify.py index d8a7442d..b4ae4a25 100644 --- a/bot/exts/evergreen/catify.py +++ b/bot/exts/evergreen/catify.py @@ -5,6 +5,7 @@ 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.utils import helpers @@ -12,9 +13,6 @@ from bot.utils import helpers class Catify(commands.Cog): """Cog for the catify command.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - @commands.command(aliases=["ᓚᘏᗢify", "ᓚᘏᗢ"]) async def catify(self, ctx: commands.Context, *, text: Optional[str]) -> None: """ @@ -82,6 +80,6 @@ class Catify(commands.Cog): ) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Loads the catify cog.""" - bot.add_cog(Catify(bot)) + bot.add_cog(Catify()) diff --git a/bot/exts/evergreen/conversationstarters.py b/bot/exts/evergreen/conversationstarters.py index 4fe8c47c..fdc4467a 100644 --- a/bot/exts/evergreen/conversationstarters.py +++ b/bot/exts/evergreen/conversationstarters.py @@ -66,4 +66,4 @@ class ConvoStarters(commands.Cog): def setup(bot: Bot) -> None: """Load the ConvoStarters cog.""" - bot.add_cog(ConvoStarters(bot)) + bot.add_cog(ConvoStarters()) diff --git a/bot/exts/evergreen/emoji.py b/bot/exts/evergreen/emoji.py index e7452a15..11615214 100644 --- a/bot/exts/evergreen/emoji.py +++ b/bot/exts/evergreen/emoji.py @@ -120,4 +120,4 @@ class Emojis(commands.Cog): def setup(bot: Bot) -> None: """Load the Emojis cog.""" - bot.add_cog(Emojis(bot)) + bot.add_cog(Emojis()) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index 5cd8d28d..62529f52 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -135,4 +135,4 @@ class CommandErrorHandler(commands.Cog): def setup(bot: Bot) -> None: """Load the ErrorHandler cog.""" - bot.add_cog(CommandErrorHandler(bot)) + bot.add_cog(CommandErrorHandler()) diff --git a/bot/exts/evergreen/latex.py b/bot/exts/evergreen/latex.py index 3a93907a..54d25267 100644 --- a/bot/exts/evergreen/latex.py +++ b/bot/exts/evergreen/latex.py @@ -93,4 +93,4 @@ class Latex(commands.Cog): def setup(bot: Bot) -> None: """Load the Latex Cog.""" - bot.add_cog(Latex(bot)) + bot.add_cog(Latex()) diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index 2dc4996f..c0086c20 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -28,4 +28,4 @@ class PythonFacts(commands.Cog): def setup(bot: Bot) -> None: """Load the PythonFacts Cog.""" - bot.add_cog(PythonFacts(bot)) + bot.add_cog(PythonFacts()) diff --git a/bot/exts/evergreen/snakes/__init__.py b/bot/exts/evergreen/snakes/__init__.py index bc42f0c2..049bc964 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: +def setup(bot: Bot) -> None: """Snakes Cog load.""" bot.add_cog(Snakes(bot)) diff --git a/bot/exts/evergreen/source.py b/bot/exts/evergreen/source.py index 2f25e4cb..685b3111 100644 --- a/bot/exts/evergreen/source.py +++ b/bot/exts/evergreen/source.py @@ -104,4 +104,4 @@ class BotSource(commands.Cog): def setup(bot: Bot) -> None: """Load the BotSource cog.""" - bot.add_cog(BotSource(bot)) + bot.add_cog(BotSource()) diff --git a/bot/exts/evergreen/speedrun.py b/bot/exts/evergreen/speedrun.py index 110d5c13..d9820287 100644 --- a/bot/exts/evergreen/speedrun.py +++ b/bot/exts/evergreen/speedrun.py @@ -23,4 +23,4 @@ class Speedrun(commands.Cog): def setup(bot: Bot) -> None: """Load the Speedrun cog.""" - bot.add_cog(Speedrun(bot)) + bot.add_cog(Speedrun()) diff --git a/bot/exts/evergreen/timed.py b/bot/exts/evergreen/timed.py index 42a77346..491231cc 100644 --- a/bot/exts/evergreen/timed.py +++ b/bot/exts/evergreen/timed.py @@ -45,4 +45,4 @@ class TimedCommands(commands.Cog): def setup(bot: Bot) -> None: """Load the Timed cog.""" - bot.add_cog(TimedCommands(bot)) + bot.add_cog(TimedCommands()) diff --git a/bot/exts/evergreen/uptime.py b/bot/exts/evergreen/uptime.py index d2509e6f..b390e7f7 100644 --- a/bot/exts/evergreen/uptime.py +++ b/bot/exts/evergreen/uptime.py @@ -28,4 +28,4 @@ class Uptime(commands.Cog): def setup(bot: Bot) -> None: """Load the Uptime cog.""" - bot.add_cog(Uptime(bot)) + bot.add_cog(Uptime()) diff --git a/bot/exts/halloween/timeleft.py b/bot/exts/halloween/timeleft.py index f4ab9284..e80025dc 100644 --- a/bot/exts/halloween/timeleft.py +++ b/bot/exts/halloween/timeleft.py @@ -12,9 +12,6 @@ log = logging.getLogger(__name__) class TimeLeft(commands.Cog): """A Cog that tells you how long left until Hacktober is over!""" - def __init__(self, bot: Bot): - self.bot = bot - def in_hacktober(self) -> bool: """Return True if the current time is within Hacktoberfest.""" _, end, start = self.load_date() @@ -68,4 +65,4 @@ class TimeLeft(commands.Cog): def setup(bot: Bot) -> None: """Load the Time Left Cog.""" - bot.add_cog(TimeLeft(bot)) + bot.add_cog(TimeLeft()) diff --git a/bot/exts/valentines/be_my_valentine.py b/bot/exts/valentines/be_my_valentine.py index e94efda0..cf6099d2 100644 --- a/bot/exts/valentines/be_my_valentine.py +++ b/bot/exts/valentines/be_my_valentine.py @@ -21,7 +21,7 @@ HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_hea class BeMyValentine(commands.Cog): """A cog that sends Valentines to other users!""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot self.valentines = self.load_json() @@ -198,5 +198,5 @@ class BeMyValentine(commands.Cog): def setup(bot: Bot) -> None: - """Be my Valentine Cog load.""" + """Load the Be my Valentine Cog.""" bot.add_cog(BeMyValentine(bot)) diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index c2a5da26..e55dc128 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -93,5 +93,5 @@ class LoveCalculator(Cog): def setup(bot: Bot) -> None: - """Love calculator Cog load.""" + """Load the Love calculator Cog.""" bot.add_cog(LoveCalculator()) diff --git a/bot/exts/valentines/movie_generator.py b/bot/exts/valentines/movie_generator.py index 461255ff..4508c3b2 100644 --- a/bot/exts/valentines/movie_generator.py +++ b/bot/exts/valentines/movie_generator.py @@ -16,7 +16,7 @@ log = logging.getLogger(__name__) class RomanceMovieFinder(commands.Cog): """A Cog that returns a random romance movie suggestion to a user.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @commands.command(name="romancemovie") @@ -62,5 +62,5 @@ class RomanceMovieFinder(commands.Cog): def setup(bot: Bot) -> None: - """Romance movie Cog load.""" + """Load the Romance movie Cog.""" bot.add_cog(RomanceMovieFinder(bot)) diff --git a/bot/exts/valentines/myvalenstate.py b/bot/exts/valentines/myvalenstate.py index 7a0f8318..1c67984b 100644 --- a/bot/exts/valentines/myvalenstate.py +++ b/bot/exts/valentines/myvalenstate.py @@ -19,9 +19,6 @@ with open(Path("bot/resources/valentines/valenstates.json"), "r", encoding="utf8 class MyValenstate(commands.Cog): """A Cog to find your most likely Valentine's vacation destination.""" - def __init__(self, bot: commands.Bot): - self.bot = bot - def levenshtein(self, source: str, goal: str) -> int: """Calculates the Levenshtein Distance between source and goal.""" if len(source) < len(goal): @@ -83,5 +80,5 @@ class MyValenstate(commands.Cog): def setup(bot: Bot) -> None: - """Valenstate Cog load.""" - bot.add_cog(MyValenstate(bot)) + """Load the Valenstate Cog.""" + bot.add_cog(MyValenstate()) diff --git a/bot/exts/valentines/pickuplines.py b/bot/exts/valentines/pickuplines.py index 216ee13b..909169e6 100644 --- a/bot/exts/valentines/pickuplines.py +++ b/bot/exts/valentines/pickuplines.py @@ -38,5 +38,5 @@ class PickupLine(commands.Cog): def setup(bot: Bot) -> None: - """Pickup lines Cog load.""" + """Load the Pickup lines Cog.""" bot.add_cog(PickupLine()) diff --git a/bot/exts/valentines/savethedate.py b/bot/exts/valentines/savethedate.py index ed2d2c5f..cc16f5c9 100644 --- a/bot/exts/valentines/savethedate.py +++ b/bot/exts/valentines/savethedate.py @@ -35,5 +35,5 @@ class SaveTheDate(commands.Cog): def setup(bot: Bot) -> None: - """Save the date Cog Load.""" + """Load the Save the date Cog.""" bot.add_cog(SaveTheDate()) diff --git a/bot/exts/valentines/valentine_zodiac.py b/bot/exts/valentines/valentine_zodiac.py index 72fd93fe..a444a355 100644 --- a/bot/exts/valentines/valentine_zodiac.py +++ b/bot/exts/valentines/valentine_zodiac.py @@ -142,5 +142,5 @@ class ValentineZodiac(commands.Cog): def setup(bot: Bot) -> None: - """Valentine zodiac Cog load.""" + """Load the Valentine zodiac Cog.""" bot.add_cog(ValentineZodiac()) diff --git a/bot/exts/valentines/whoisvalentine.py b/bot/exts/valentines/whoisvalentine.py index 3789fad5..3f23201f 100644 --- a/bot/exts/valentines/whoisvalentine.py +++ b/bot/exts/valentines/whoisvalentine.py @@ -46,5 +46,5 @@ class ValentineFacts(commands.Cog): def setup(bot: Bot) -> None: - """Who is Valentine Cog load.""" + """Load the Who is Valentine Cog.""" bot.add_cog(ValentineFacts()) -- cgit v1.2.3 From d73eaf3d4d0724444161d0232533b2daaf7baedd Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Tue, 4 May 2021 16:03:25 -0400 Subject: chore: Change back to the original string --- bot/exts/evergreen/game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index 7abbadcd..f3d4e9a2 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -260,7 +260,7 @@ class Games(Cog): display_possibilities = "`, `".join(p[1] for p in possibilities) await ctx.send( f"Invalid genre `{genre}`. " - f"Maybe you meant `{display_possibilities}`?" if display_possibilities else '' + f"{f'Maybe you meant `{display_possibilities}`?' if display_possibilities else ''}" ) return elif len(possibilities) == 1: -- cgit v1.2.3 From f7c9fecf5dbab7320ff91eecd322c5521034b628 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Tue, 4 May 2021 16:15:57 -0400 Subject: chore: Replace the remaining double quotes with double quotes --- bot/__init__.py | 2 +- bot/exts/evergreen/reddit.py | 2 +- bot/exts/halloween/halloweenify.py | 4 ++-- bot/exts/valentines/lovecalculator.py | 12 ++++++------ bot/utils/pagination.py | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/bot/__init__.py b/bot/__init__.py index 71b7c8a3..669f9f5d 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -64,7 +64,7 @@ logging.getLogger("matplotlib").setLevel(logging.ERROR) # Setup new logging configuration logging.basicConfig( - format='%(asctime)s - %(name)s %(levelname)s: %(message)s', + format="%(asctime)s - %(name)s %(levelname)s: %(message)s", datefmt="%D %H:%M:%S", level=logging.TRACE if Client.debug else logging.DEBUG, handlers=[console_handler, file_handler], diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index 82af6ce9..f2b95fe2 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -84,7 +84,7 @@ class Reddit(commands.Cog): for i, post in enumerate(random_posts, start=1): post_title = post["data"]["title"][0:50] - post_url = post['data']['url'] + post_url = post["data"]["url"] if post_title == "": post_title = "No Title." elif post_title == post_url: diff --git a/bot/exts/halloween/halloweenify.py b/bot/exts/halloween/halloweenify.py index 47b20a2a..df55b55d 100644 --- a/bot/exts/halloween/halloweenify.py +++ b/bot/exts/halloween/halloweenify.py @@ -26,8 +26,8 @@ class Halloweenify(commands.Cog): # Choose a random character from our list we loaded above and set apart the nickname and image url. character = choice(data["characters"]) - nickname = ''.join([nickname for nickname in character]) - image = ''.join([character[nickname] for nickname in character]) + nickname = "".join([nickname for nickname in character]) + image = "".join([character[nickname] for nickname in character]) # Build up a Embed embed = discord.Embed() diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index e55dc128..9096d994 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -23,7 +23,7 @@ with Path("bot/resources/valentines/love_matches.json").open(encoding="utf8") as class LoveCalculator(Cog): """A cog for calculating the love between two people.""" - @commands.command(aliases=('love_calculator', 'love_calc')) + @commands.command(aliases=("love_calculator", "love_calc")) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) async def love(self, ctx: commands.Context, who: Union[Member, str], whom: Union[Member, str] = None) -> None: """ @@ -61,7 +61,7 @@ class LoveCalculator(Cog): # Make sure user didn't provide something silly such as 10 spaces if not (who and whom): - raise BadArgument('Arguments be non-empty strings.') + raise BadArgument("Arguments be non-empty strings.") # Hash inputs to guarantee consistent results (hashing algorithm choice arbitrary) # @@ -78,15 +78,15 @@ class LoveCalculator(Cog): # We only need the dict, so we can ditch the first element _, data = LOVE_DATA[index] - status = random.choice(data['titles']) + status = random.choice(data["titles"]) embed = discord.Embed( title=status, - description=f'{who} \N{HEAVY BLACK HEART} {whom} scored {love_percent}%!\n\u200b', + description=f"{who} \N{HEAVY BLACK HEART} {whom} scored {love_percent}%!\n\u200b", color=discord.Color.dark_magenta() ) embed.add_field( - name='A letter from Dr. Love:', - value=data['text'] + name="A letter from Dr. Love:", + value=data["text"] ) await ctx.send(embed=embed) diff --git a/bot/utils/pagination.py b/bot/utils/pagination.py index a073a00b..e0dc25b4 100644 --- a/bot/utils/pagination.py +++ b/bot/utils/pagination.py @@ -283,7 +283,7 @@ class ImagePaginator(Paginator): self.images = [] self._pages = [] - def add_line(self, line: str = '', *, empty: bool = False) -> None: + def add_line(self, line: str = "", *, empty: bool = False) -> None: """ Adds a line to each page, usually just 1 line in this context. -- cgit v1.2.3 From ccb7cd7bc4ab43e4d777093a14c921a64c884f6e Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 5 May 2021 11:28:01 -0400 Subject: chore: Add a 5 second cooldown per user to .catify --- bot/exts/evergreen/catify.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/evergreen/catify.py b/bot/exts/evergreen/catify.py index d8a7442d..a175602f 100644 --- a/bot/exts/evergreen/catify.py +++ b/bot/exts/evergreen/catify.py @@ -16,6 +16,7 @@ class Catify(commands.Cog): self.bot = bot @commands.command(aliases=["ᓚᘏᗢify", "ᓚᘏᗢ"]) + @commands.cooldown(1, 5, commands.BucketType.user) async def catify(self, ctx: commands.Context, *, text: Optional[str]) -> None: """ Convert the provided text into a cat themed sentence by interspercing cats throughout text. -- cgit v1.2.3 From 6ae760663b7055f9b74089591b4f46ac75c6f8e5 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 5 May 2021 12:10:18 -0400 Subject: chore: Don't have defaults for typing.Optional[...] in commands --- bot/exts/evergreen/game.py | 2 +- bot/exts/evergreen/space.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index f3d4e9a2..4da33259 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -225,7 +225,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) -> None: + async def games(self, ctx: Context, amount: Optional[int] = 5, *, genre: Optional[str]) -> None: """ Get random game(s) by genre from IGDB. Use .games genres command to get all available genres. diff --git a/bot/exts/evergreen/space.py b/bot/exts/evergreen/space.py index ee9ad38c..7d83dbfb 100644 --- a/bot/exts/evergreen/space.py +++ b/bot/exts/evergreen/space.py @@ -66,7 +66,7 @@ class Space(Cog): await invoke_help_command(ctx) @space.command(name="apod") - async def apod(self, ctx: Context, date: Optional[str] = None) -> None: + async def apod(self, ctx: Context, date: Optional[str]) -> None: """ Get Astronomy Picture of Day from NASA API. Date is optional parameter, what formatting is YYYY-MM-DD. @@ -99,7 +99,7 @@ class Space(Cog): ) @space.command(name="nasa") - async def nasa(self, ctx: Context, *, search_term: Optional[str] = None) -> None: + async def nasa(self, ctx: Context, *, search_term: Optional[str]) -> None: """Get random NASA information/facts + image. Support `search_term` parameter for more specific search.""" params = { "media_type": "image" @@ -124,7 +124,7 @@ class Space(Cog): ) @space.command(name="epic") - async def epic(self, ctx: Context, date: Optional[str] = None) -> None: + async def epic(self, ctx: Context, date: Optional[str]) -> None: """Get one of latest random image of earth from NASA EPIC API. Support date parameter, format is YYYY-MM-DD.""" if date: try: @@ -160,8 +160,8 @@ class Space(Cog): async def mars( self, ctx: Context, - date: Optional[DateConverter] = None, - rover: Optional[str] = "curiosity" + date: Optional[DateConverter], + rover: str = "curiosity" ) -> None: """ Get random Mars image by date. Support both SOL (martian solar day) and earth date and rovers. -- cgit v1.2.3 From 7a18b365f3ffe43fe3de6bb342d98b0414e0283c Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 5 May 2021 13:23:59 -0400 Subject: chore: Don't use "is not None" Co-authored-by: Anand Krishna <40204976+anand2312@users.noreply.github.com> --- bot/exts/evergreen/githubinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 24479c79..fe126aa2 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -67,7 +67,7 @@ class GithubInfo(commands.Cog): embed = discord.Embed( title=f"`{user_data['login']}`'s GitHub profile info", - description=f"```{user_data['bio']}```\n" if user_data["bio"] is not None else "", + description=f"```{user_data['bio']}```\n" if user_data["bio"] else "", colour=discord.Colour.blurple(), url=user_data["html_url"], timestamp=datetime.strptime(user_data["created_at"], "%Y-%m-%dT%H:%M:%SZ") -- cgit v1.2.3 From 888f83170bce384315a0a5c47b87b7ffbfb5d8ea Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 5 May 2021 13:46:08 -0400 Subject: chore: Add all of the converters into bot/utils/converters.py --- bot/exts/evergreen/minesweeper.py | 28 +------------- bot/exts/evergreen/source.py | 23 +----------- bot/exts/evergreen/space.py | 19 ++-------- bot/utils/converters.py | 77 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 80 insertions(+), 67 deletions(-) diff --git a/bot/exts/evergreen/minesweeper.py b/bot/exts/evergreen/minesweeper.py index bd28d365..7a31dfde 100644 --- a/bot/exts/evergreen/minesweeper.py +++ b/bot/exts/evergreen/minesweeper.py @@ -8,6 +8,7 @@ from discord.ext import commands from bot.bot import Bot from bot.constants import Client +from bot.utils.converters import CoordinateConverter from bot.utils.exceptions import UserNotPlayingError from bot.utils.extensions import invoke_help_command @@ -32,33 +33,6 @@ MESSAGE_MAPPING = { log = logging.getLogger(__name__) -class CoordinateConverter(commands.Converter): - """Converter for Coordinates.""" - - async def convert(self, ctx: commands.Context, coordinate: str) -> typing.Tuple[int, int]: - """Take in a coordinate string and turn it into an (x, y) tuple.""" - if not 2 <= len(coordinate) <= 3: - raise commands.BadArgument("Invalid co-ordinate provided.") - - coordinate = coordinate.lower() - if coordinate[0].isalpha(): - digit = coordinate[1:] - letter = coordinate[0] - else: - digit = coordinate[:-1] - letter = coordinate[-1] - - if not digit.isdigit(): - raise commands.BadArgument - - x = ord(letter) - ord("a") - y = int(digit) - 1 - - if (not 0 <= x <= 9) or (not 0 <= y <= 9): - raise commands.BadArgument - return x, y - - GameBoard = typing.List[typing.List[typing.Union[str, int]]] diff --git a/bot/exts/evergreen/source.py b/bot/exts/evergreen/source.py index 685b3111..8fb72143 100644 --- a/bot/exts/evergreen/source.py +++ b/bot/exts/evergreen/source.py @@ -1,32 +1,13 @@ import inspect from pathlib import Path -from typing import Optional, Tuple, Union +from typing import Optional, Tuple from discord import Embed from discord.ext import commands from bot.bot import Bot from bot.constants import Source - -SourceType = Union[commands.Command, commands.Cog, str, commands.ExtensionNotLoaded] - - -class SourceConverter(commands.Converter): - """Convert an argument into a help command, tag, command, or cog.""" - - async def convert(self, ctx: commands.Context, argument: str) -> SourceType: - """Convert argument into source object.""" - cog = ctx.bot.get_cog(argument) - if cog: - return cog - - cmd = ctx.bot.get_command(argument) - if cmd: - return cmd - - raise commands.BadArgument( - f"Unable to convert `{argument}` to valid command or Cog." - ) +from bot.utils.converters import SourceConverter, SourceType class BotSource(commands.Cog): diff --git a/bot/exts/evergreen/space.py b/bot/exts/evergreen/space.py index 7d83dbfb..77b63946 100644 --- a/bot/exts/evergreen/space.py +++ b/bot/exts/evergreen/space.py @@ -1,15 +1,16 @@ import logging import random from datetime import date, datetime -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Optional from urllib.parse import urlencode from discord import Embed from discord.ext import tasks -from discord.ext.commands import BadArgument, Cog, Context, Converter, group +from discord.ext.commands import Cog, Context, group from bot.bot import Bot from bot.constants import Tokens +from bot.utils.converters import DateConverter from bot.utils.extensions import invoke_help_command logger = logging.getLogger(__name__) @@ -21,20 +22,6 @@ NASA_EPIC_BASE_URL = "https://epic.gsfc.nasa.gov" APOD_MIN_DATE = date(1995, 6, 16) -class DateConverter(Converter): - """Parse SOL or earth date (in format YYYY-MM-DD) into `int` or `datetime`. When invalid input, raise error.""" - - async def convert(self, ctx: Context, argument: str) -> Union[int, datetime]: - """Parse date (SOL or earth) into `datetime` or `int`. When invalid value, raise error.""" - if argument.isdigit(): - return int(argument) - try: - date = datetime.strptime(argument, "%Y-%m-%d") - except ValueError: - raise BadArgument(f"Can't convert `{argument}` to `datetime` in format `YYYY-MM-DD` or `int` in SOL.") - return date - - class Space(Cog): """Space Cog contains commands, that show images, facts or other information about space.""" diff --git a/bot/utils/converters.py b/bot/utils/converters.py index 228714c9..98607087 100644 --- a/bot/utils/converters.py +++ b/bot/utils/converters.py @@ -1,11 +1,15 @@ +from datetime import datetime +from typing import Tuple, Union + import discord -from discord.ext.commands.converter import MessageConverter +from discord.ext import commands -class WrappedMessageConverter(MessageConverter): +class WrappedMessageConverter(commands.MessageConverter): """A converter that handles embed-suppressed links like .""" - async def convert(self, ctx: discord.ext.commands.Context, argument: str) -> discord.Message: + @staticmethod + async def convert(ctx: commands.Context, argument: str) -> discord.Message: """Wrap the commands.MessageConverter to handle <> delimited message links.""" # It's possible to wrap a message in [<>] as well, and it's supported because its easy if argument.startswith("[") and argument.endswith("]"): @@ -14,3 +18,70 @@ class WrappedMessageConverter(MessageConverter): argument = argument[1:-1] return await super().convert(ctx, argument) + + +class CoordinateConverter(commands.Converter): + """Converter for Coordinates.""" + + @staticmethod + async def convert(ctx: commands.Context, coordinate: str) -> Tuple[int, int]: + """Take in a coordinate string and turn it into an (x, y) tuple.""" + if len(coordinate) not in (2, 3): + raise commands.BadArgument("Invalid co-ordinate provided.") + + coordinate = coordinate.lower() + if coordinate[0].isalpha(): + digit = coordinate[1:] + letter = coordinate[0] + else: + digit = coordinate[:-1] + letter = coordinate[-1] + + if not digit.isdigit(): + raise commands.BadArgument + + x = ord(letter) - ord("a") + y = int(digit) - 1 + + if (not 0 <= x <= 9) or (not 0 <= y <= 9): + raise commands.BadArgument + return x, y + + +SourceType = Union[commands.Command, commands.Cog] + + +class SourceConverter(commands.Converter): + """Convert an argument into a command or cog.""" + + @staticmethod + async def convert(ctx: commands.Context, argument: str) -> SourceType: + """Convert argument into source object.""" + cog = ctx.bot.get_cog(argument) + if cog: + return cog + + cmd = ctx.bot.get_command(argument) + if cmd: + return cmd + + raise commands.BadArgument( + f"Unable to convert `{argument}` to valid command or Cog." + ) + + +class DateConverter(commands.Converter): + """Parse SOL or earth date (in format YYYY-MM-DD) into `int` or `datetime`. When invalid input, raise error.""" + + @staticmethod + async def convert(ctx: commands.Context, argument: str) -> Union[int, datetime]: + """Parse date (SOL or earth) into `datetime` or `int`. When invalid value, raise error.""" + if argument.isdigit(): + return int(argument) + try: + date = datetime.strptime(argument, "%Y-%m-%d") + except ValueError: + raise commands.BadArgument( + f"Can't convert `{argument}` to `datetime` in format `YYYY-MM-DD` or `int` in SOL." + ) + return date -- cgit v1.2.3 From 780aaeb5c272f7a9fd9fe9e8cdc1c041beb90a25 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 5 May 2021 14:20:40 -0400 Subject: chore: Apply anand's suggested changes --- bot/exts/evergreen/error_handler.py | 2 +- bot/exts/evergreen/githubinfo.py | 3 +- bot/exts/evergreen/movie.py | 10 +- bot/exts/evergreen/reddit.py | 3 +- bot/exts/evergreen/snakes/__init__.py | 2 +- bot/exts/evergreen/snakes/_snakes_cog.py | 175 +++++++++++++++---------------- 6 files changed, 96 insertions(+), 99 deletions(-) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index 62529f52..faaf1386 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -39,7 +39,7 @@ class CommandErrorHandler(commands.Cog): @commands.Cog.listener() async def on_command_error(self, ctx: commands.Context, error: commands.CommandError) -> None: - """Activates when a command opens an error.""" + """Activates when a command raises an error.""" if getattr(error, "handled", False): logging.debug(f"Command {ctx.command} had its error already handled locally; ignoring.") return diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index fe126aa2..27e607e5 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -5,7 +5,6 @@ from urllib.parse import quote import discord from discord.ext import commands -from discord.ext.commands.cooldowns import BucketType from bot.bot import Bot from bot.constants import Colours, NEGATIVE_REPLIES @@ -28,7 +27,7 @@ class GithubInfo(commands.Cog): return await r.json() @commands.group(name="github", aliases=("gh", "git")) - @commands.cooldown(1, 10, BucketType.user) + @commands.cooldown(1, 10, commands.BucketType.user) async def github_group(self, ctx: commands.Context) -> None: """Commands for finding information related to GitHub.""" if ctx.invoked_subcommand is None: diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index e67f8d04..fa284417 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -72,13 +72,13 @@ class Movie(Cog): # Capitalize genre for getting data from Enum, get random page, send help when genre don't exist. genre = genre.capitalize() try: - result = await self.get_movies_list(self.http_session, MovieGenres[genre].value, 1) + result = await self.get_movies_data(self.http_session, MovieGenres[genre].value, 1) except KeyError: await invoke_help_command(ctx) return # Check if "results" is in result. If not, throw error. - if "results" not in result.keys(): + if "results" not in result: err_msg = f"There is problem while making TMDB API request. Response Code: {result['status_code']}, " \ f"{result['status_message']}." await ctx.send(err_msg) @@ -88,8 +88,8 @@ class Movie(Cog): page = random.randint(1, result["total_pages"]) # Get movies list from TMDB, check if results key in result. When not, raise error. - movies = await self.get_movies_list(self.http_session, MovieGenres[genre].value, page) - if "results" not in movies.keys(): + movies = await self.get_movies_data(self.http_session, MovieGenres[genre].value, page) + if "results" not in movies: err_msg = f"There is problem while making TMDB API request. Response Code: {result['status_code']}, " \ f"{result['status_message']}." await ctx.send(err_msg) @@ -106,7 +106,7 @@ class Movie(Cog): """Show all currently available genres for .movies command.""" await ctx.send(f"Current available genres: {', '.join('`' + genre.name + '`' for genre in MovieGenres)}") - async def get_movies_list(self, client: ClientSession, genre_id: str, page: int) -> Dict[str, Any]: + async def get_movies_data(self, client: ClientSession, genre_id: str, page: int) -> List[Dict[str, Any]]: """Return JSON of TMDB discover request.""" # Define params of request params = { diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index f2b95fe2..bda155c3 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -3,7 +3,6 @@ import random import discord from discord.ext import commands -from discord.ext.commands.cooldowns import BucketType from bot.bot import Bot from bot.utils.pagination import ImagePaginator @@ -31,7 +30,7 @@ class Reddit(commands.Cog): return await response.json() @commands.command(name="reddit") - @commands.cooldown(1, 10, BucketType.user) + @commands.cooldown(1, 10, commands.BucketType.user) async def get_reddit(self, ctx: commands.Context, subreddit: str = "python", sort: str = "hot") -> None: """ Fetch reddit posts by using this command. diff --git a/bot/exts/evergreen/snakes/__init__.py b/bot/exts/evergreen/snakes/__init__.py index 049bc964..7740429b 100644 --- a/bot/exts/evergreen/snakes/__init__.py +++ b/bot/exts/evergreen/snakes/__init__.py @@ -7,5 +7,5 @@ log = logging.getLogger(__name__) def setup(bot: Bot) -> None: - """Snakes Cog load.""" + """Load the Snakes Cog.""" bot.add_cog(Snakes(bot)) diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index 353bcd66..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 @@ -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 -- cgit v1.2.3 From 353264e53f54ba32d6f71fc027bf81aca871fdf7 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 5 May 2021 14:25:59 -0400 Subject: chore: Improve .space epic's docstring --- bot/exts/evergreen/space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/space.py b/bot/exts/evergreen/space.py index 77b63946..6c991d26 100644 --- a/bot/exts/evergreen/space.py +++ b/bot/exts/evergreen/space.py @@ -112,7 +112,7 @@ class Space(Cog): @space.command(name="epic") async def epic(self, ctx: Context, date: Optional[str]) -> None: - """Get one of latest random image of earth from NASA EPIC API. Support date parameter, format is YYYY-MM-DD.""" + """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() -- cgit v1.2.3 From 19e2efaf5572a0edb4595bead6877aaae078adae Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 5 May 2021 14:46:29 -0400 Subject: chore: Apply suggested changes --- bot/exts/halloween/hacktober-issue-finder.py | 12 +++++++----- bot/exts/halloween/hacktoberstats.py | 3 +-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/bot/exts/halloween/hacktober-issue-finder.py b/bot/exts/halloween/hacktober-issue-finder.py index baee9612..20a06770 100644 --- a/bot/exts/halloween/hacktober-issue-finder.py +++ b/bot/exts/halloween/hacktober-issue-finder.py @@ -73,15 +73,17 @@ class HacktoberIssues(commands.Cog): log.debug(f"making api request to url: {url}") async with self.bot.http_session.get(url, headers=REQUEST_HEADERS) as response: if response.status != 200: - log.error(f"expected 200 status (got {response.status}) from the GitHub api.") - await ctx.send(f"ERROR: expected 200 status (got {response.status}) from the GitHub api.") - await ctx.send(await response.text()) + log.error(f"expected 200 status (got {response.status}) by the GitHub api.") + await ctx.send( + f"ERROR: expected 200 status (got {response.status}) by the GitHub api.\n" + f"{await response.text()}" + ) return None data = await response.json() if len(data["items"]) == 0: - log.error(f"no issues returned from GitHub api. with url: {response.url}") - await ctx.send(f"ERROR: no issues returned from GitHub api. with url: {response.url}") + log.error(f"no issues returned by GitHub API, with url: {response.url}") + await ctx.send(f"ERROR: no issues returned by GitHub API, with url: {response.url}") return None if option == "beginner": diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 33e9ca31..b74e680b 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -302,8 +302,7 @@ class HacktoberStats(commands.Cog): async def _fetch_url(self, url: str, headers: dict) -> dict: """Retrieve API response from URL.""" async with self.bot.http_session.get(url, headers=headers) as resp: - jsonresp = await resp.json() - return jsonresp + return await resp.json() @staticmethod def _has_label(pr: dict, labels: Union[List[str], str]) -> bool: -- cgit v1.2.3 From e22c03395b83d81b4c5de90378bc09fae9dce4e1 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 5 May 2021 15:33:08 -0400 Subject: chore: Use pathlib more often and apply other changes --- bot/exts/halloween/halloween_facts.py | 1 - bot/exts/halloween/monstersurvey.py | 16 ++++++++-------- bot/exts/pride/drag_queen_name.py | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/bot/exts/halloween/halloween_facts.py b/bot/exts/halloween/halloween_facts.py index 139e0810..3a89b5aa 100644 --- a/bot/exts/halloween/halloween_facts.py +++ b/bot/exts/halloween/halloween_facts.py @@ -33,7 +33,6 @@ class HalloweenFacts(commands.Cog): with Path("bot/resources/halloween/halloween_facts.json").open("r", encoding="utf8") as file: self.halloween_facts = json.load(file) self.facts = list(enumerate(self.halloween_facts)) - random.shuffle(self.facts) def random_fact(self) -> Tuple[int, str]: """Return a random fact from the loaded facts.""" diff --git a/bot/exts/halloween/monstersurvey.py b/bot/exts/halloween/monstersurvey.py index 486e8937..38e3850e 100644 --- a/bot/exts/halloween/monstersurvey.py +++ b/bot/exts/halloween/monstersurvey.py @@ -1,6 +1,6 @@ import json import logging -import os +import pathlib from discord import Embed from discord.ext import commands @@ -25,24 +25,24 @@ class MonsterSurvey(Cog): def __init__(self): """Initializes values for the bot to use within the voting commands.""" - self.registry_location = os.path.join(os.getcwd(), "bot", "resources", "halloween", "monstersurvey.json") - with open(self.registry_location, "r", encoding="utf8") as jason: - self.voter_registry = json.load(jason) + self.registry_path = pathlib.Path("bot", "resources", "halloween", "monstersurvey.json") + with self.registry_path.open(encoding="utf8") as data: + self.voter_registry = json.load(data) def json_write(self) -> None: """Write voting results to a local JSON file.""" log.info("Saved Monster Survey Results") - with open(self.registry_location, "w", encoding="utf8") as jason: - json.dump(self.voter_registry, jason, indent=2) + with self.registry_path.open("w", encoding="utf8") as data: + json.dump(self.voter_registry, data, indent=2) def cast_vote(self, id: int, monster: str) -> None: """ - Cast a user"s vote for the specified monster. + Cast a user's vote for the specified monster. If the user has already voted, their existing vote is removed. """ vr = self.voter_registry - for m in vr.keys(): + for m in vr: if id not in vr[m]["votes"] and m == monster: vr[m]["votes"].append(id) else: diff --git a/bot/exts/pride/drag_queen_name.py b/bot/exts/pride/drag_queen_name.py index 9839f089..d9424001 100644 --- a/bot/exts/pride/drag_queen_name.py +++ b/bot/exts/pride/drag_queen_name.py @@ -19,7 +19,7 @@ class DragNames(commands.Cog): @staticmethod def load_names() -> list: """Loads a list of drag queen names.""" - with open(Path("bot/resources/pride/drag_queen_names.json"), "r", encoding="utf8") as f: + with Path("bot/resources/pride/drag_queen_names.json").open(encoding="utf8") as f: return json.load(f) @commands.command(name="dragname", aliases=["dragqueenname", "queenme"]) @@ -29,5 +29,5 @@ class DragNames(commands.Cog): def setup(bot: Bot) -> None: - """Load the Drag Queen Cog.""" + """Load the Drag Names Cog.""" bot.add_cog(DragNames()) -- cgit v1.2.3 From b486ed47478adf2916291497243e760026e686bd Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 5 May 2021 15:36:39 -0400 Subject: chore: Remove useless call of `dict.keys()` Co-authored-by: Anand Krishna <40204976+anand2312@users.noreply.github.com> --- bot/exts/halloween/monstersurvey.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/halloween/monstersurvey.py b/bot/exts/halloween/monstersurvey.py index 486e8937..42a316e6 100644 --- a/bot/exts/halloween/monstersurvey.py +++ b/bot/exts/halloween/monstersurvey.py @@ -84,7 +84,7 @@ class MonsterSurvey(Cog): value="Which monster has the most votes? This command will tell you.", inline=False ) - default_embed.set_footer(text=f"Monsters choices are: {', '.join(self.voter_registry.keys())}") + default_embed.set_footer(text=f"Monsters choices are: {', '.join(self.voter_registry)}") await ctx.send(embed=default_embed) -- cgit v1.2.3 From 544a05b00758583f4594569e032c9e661406a72f Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 5 May 2021 16:50:59 -0400 Subject: chore: Fetch the member and use Colours.blue in the embed for the .mosaic command --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index afff125f..bd151d30 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -317,6 +317,11 @@ class AvatarModify(commands.Cog): async def mosaic_command(self, ctx: commands.Context, squares: int = 16) -> None: """Splits your avatar into x squares, randomizes them and stitches them back into a new image!""" async with ctx.typing(): + member = await self._fetch_member(ctx.author.id) + if not member: + await ctx.send(f"{Emojis.cross_mark} Could not get member info.") + return + if squares < 1: raise commands.BadArgument("Squares must be a positive number") @@ -327,7 +332,8 @@ class AvatarModify(commands.Cog): raise commands.BadArgument(f"Number of squares cannot be higher than {MAX_SQUARES:,}.") file_name = file_safe_name("mosaic_avatar", ctx.author.display_name) - img_bytes = await ctx.author.avatar_url.read() + + img_bytes = await member.avatar_url.read() file = await in_executor( PfpEffects.mosaic_effect, @@ -348,7 +354,8 @@ class AvatarModify(commands.Cog): embed = discord.Embed( title=title, - description=description + description=description, + colour=Colours.blue ) embed.set_image(url=f'attachment://{file_name}') -- cgit v1.2.3 From 390c05383e42ba60197f5fb888cd93e867c00038 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 5 May 2021 17:17:08 -0400 Subject: chore: Prefer double quotes over single quotes --- .../evergreen/avatar_modification/avatar_modify.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index bd151d30..ca048134 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -313,7 +313,7 @@ class AvatarModify(commands.Cog): await ctx.send(file=file, embed=embed) - @avatar_modify.command(name='mosaic', root_aliases=('mosaic',)) + @avatar_modify.command(name="mosaic", root_aliases=("mosaic",)) async def mosaic_command(self, ctx: commands.Context, squares: int = 16) -> None: """Splits your avatar into x squares, randomizes them and stitches them back into a new image!""" async with ctx.typing(): @@ -343,14 +343,14 @@ class AvatarModify(commands.Cog): ) if squares == 1: - title = 'Hooh... that was a lot of work' - description = 'I present to you... Yourself!' + title = "Hooh... that was a lot of work" + description = "I present to you... Yourself!" elif squares == MAX_SQUARES: - title = 'Testing the limits I see...' - description = 'What a masterpiece. :star:' + title = "Testing the limits I see..." + description = "What a masterpiece. :star:" else: - title = 'Your mosaic avatar' - description = 'Here is your avatar. I think it looks a bit *puzzling*' + title = "Your mosaic avatar" + description = "Here is your avatar. I think it looks a bit *puzzling*" embed = discord.Embed( title=title, @@ -358,8 +358,8 @@ class AvatarModify(commands.Cog): colour=Colours.blue ) - embed.set_image(url=f'attachment://{file_name}') - embed.set_footer(text=f'Made by {ctx.author.display_name}', icon_url=ctx.author.avatar_url) + embed.set_image(url=f"attachment://{file_name}") + embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) await ctx.send(file=file, embed=embed) -- cgit v1.2.3 From f6175b5cf842c7052089c05f387770fc9d91b6d0 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Wed, 5 May 2021 17:19:32 -0400 Subject: chore: Use the name AvatarModify instead of PfpModify --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index ca048134..221bd809 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -365,5 +365,5 @@ class AvatarModify(commands.Cog): def setup(bot: commands.Bot) -> None: - """Load the PfpModify cog.""" + """Load the AvatarModify cog.""" bot.add_cog(AvatarModify(bot)) -- cgit v1.2.3 From df65a5091100a59424bedc14144f37c43954d0b3 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 6 May 2021 14:45:29 +0100 Subject: Use bot.bot.Bot for type hint, over commands.Bot --- bot/exts/evergreen/bookmark.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index f0b828f8..5f550a4a 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -5,6 +5,7 @@ import random import discord from discord.ext import commands +from bot.bot import Bot from bot.constants import Colours, ERROR_REPLIES, Icons from bot.utils.converters import WrappedMessageConverter @@ -18,7 +19,7 @@ BOOKMARK_EMOJI = "📌" class Bookmark(commands.Cog): """Creates personal bookmarks by relaying a message link to the user's DMs.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @staticmethod @@ -140,6 +141,6 @@ class Bookmark(commands.Cog): await reaction_message.delete() -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Load the Bookmark cog.""" bot.add_cog(Bookmark(bot)) -- cgit v1.2.3 From 16b43597050b8dad922c8d93aa07c061e0512c1f Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Thu, 6 May 2021 13:05:37 -0400 Subject: chore: Check if the number of squares first is bigger than the max first Co-authored-by: Anand Krishna <40204976+anand2312@users.noreply.github.com> --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 221bd809..dffe43ce 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -322,14 +322,11 @@ class AvatarModify(commands.Cog): await ctx.send(f"{Emojis.cross_mark} Could not get member info.") return - if squares < 1: - raise commands.BadArgument("Squares must be a positive number") + if 1 <= squares <= MAX_SQUARES: + raise commands.BadArgument(f"Squares must be a positive number less than or equal to {MAX_SQUARES:,}.") if not math.sqrt(squares).is_integer(): - raise commands.BadArgument("Squares must be a perfect square") - - if squares > MAX_SQUARES: - raise commands.BadArgument(f"Number of squares cannot be higher than {MAX_SQUARES:,}.") + raise commands.BadArgument("The number of squares must be a perfect square.") file_name = file_safe_name("mosaic_avatar", ctx.author.display_name) -- cgit v1.2.3 From 4c3312a642f1876427d17a41eeb9c912f9cd4b06 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Fri, 7 May 2021 10:41:30 -0300 Subject: Add 30 math questions and 30 science questions to trivia_quiz.json --- bot/resources/evergreen/trivia_quiz.json | 331 +++++++++++++++++++++++++++++++ 1 file changed, 331 insertions(+) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index a4225eb1..b20b953f 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -44,6 +44,24 @@ ], "question": "What was the first game Yoshi appeared in?", "answer": "Super Mario World" + }, + { + "id": 6, + "hints": [ + "They were used alternatively to playing cards.", + "They generally have handdrawn nature images on them." + ], + "question": "What did Nintendo make before video games and toys?", + "answer": "Hanafuda, Hanafuda cards" + }, + { + "id": 7, + "hints": [ + "Before being Nintendo's main competitor in home gaming, they were successful in arcades.", + "Their first console was called the Master System." + ], + "question": "Who was Nintendo's biggest competitor in 1990?", + "answer": "Sega" } ], "general": [ @@ -269,5 +287,318 @@ "answer": "1492", "info": "The explorer Christopher Columbus made four trips across the Atlantic Ocean from Spain: in 1492, 1493, 1498 and 1502. He was determined to find a direct water route west from Europe to Asia, but he never did. Instead, he stumbled upon the Americas" } + ], + "math": [ + { + "id": 201, + "question": "What is the highest power of a biquadratic polynomial?", + "answer": "4, four" + }, + { + "id": 202, + "question": "What is the formula for surface area of a sphere?", + "answer": "4pir^2, 4πr^2" + }, + { + "id": 203, + "question": "Which theorem states that hypotenuse2 = base2 + height2?", + "answer": "Pythagorean's, Pythagorean's theorem" + }, + { + "id": 204, + "question": "Which trigonometric function is defined as hypotenuse/opposite?", + "answer": "cosecant, cosec, csc" + }, + { + "id": 205, + "question": "Does the harmonic series converge or diverge?", + "answer": "diverge" + }, + { + "id": 206, + "question": "How many quadrants are there in a cartesian plane?", + "answer": "4, four" + }, + { + "id": 207, + "question": "What is the (0,0) coordinate in a cartesian plane termed as?", + "answer": "origin" + }, + { + "id": 208, + "question": "What's the following formula that finds the area of a triangle called?", + "img_url": "https://wikimedia.org/api/rest_v1/media/math/render/png/d22b8566e8187542966e8d166e72e93746a1a6fc", + "answer": "Heron's formula" + }, + { + "id": 209, + "dynamic_id": 201, + "question": "Solve the following system of linear equations (format your answer like this & ):\n{}x + {}y = {},\n{}x + {}y = {}", + "answer": "{} & {}" + }, + { + "id": 210, + "dynamic_id": 202, + "question": "What's {} + {} mod {} congruent to?", + "answer": "{}" + }, + { + "id": 211, + "question": "What is the bottom number on a fraction called?", + "answer": "denominator" + }, + { + "id": 212, + "dynamic_id": 203, + "question": "How many vertices are on a {}gonal prism?", + "answer": "{}" + }, + { + "id": 213, + "question": "What is the term used to describe two triangles that have equal corresponding sides and angle measures?", + "answer": "congruent" + }, + { + "id": 214, + "question": "⅓πr2h is the volume of which 3 dimensional figure?", + "answer": "cone" + }, + { + "id": 215, + "dynamic_id": 204, + "question": "Find the square root of -{}.", + "answer": "{}i" + }, + { + "id": 216, + "question": "In set builder notation, {p/q | q ≠ 0, p & q ∈ Z} represents what?", + "answer": "Rational Numbers" + }, + { + "id": 217, + "question": "What is the natural log of -1 (use i for imaginary number)?", + "answer": "pi*i, pii, πi" + }, + { + "id": 218, + "question": "When is the *inaugural* World Maths Day (format your answer in MM/DD)?", + "answer": "03/13" + }, + { + "id": 219, + "question": "As the Fibonacci sequence extends to infinity, what's the ratio of each number `n` and its preceding number `n-1` approaching?", + "answer": "Golden Ratio" + }, + { + "id": 220, + "question": "0, 1, 1, 2, 3, 5, 8, 13, 21, 34 are numbers of which sequence?", + "answer": "Fibonacci" + }, + { + "id": 221, + "question": "Prime numbers only have __ factors.", + "answer": "2, two" + }, + { + "id": 222, + "question": "In probability, the ________ ______ of an experiment or random trial is the set of all possible outcomes of it.", + "answer": "sample space" + }, + { + "id": 223, + "question": "In statistics, what does this formula represent?", + "img_url": "https://www.statisticshowto.com/wp-content/uploads/2013/11/sample-standard-deviation.jpg", + "answer": "sample standard deviation, standard deviation of a sample" + }, + { + "id": 224, + "question": "\"Hexakosioihexekontahexaphobia\" is the fear of which number?", + "answer": "666" + }, + { + "id": 225, + "question": "Prove or disprove the Riemann Hypothesis", + "answer": "" + }, + { + "id": 226, + "dynamic_id": 205, + "question": "BASE TWO QUESTION: Calculate {} {} {}", + "answer": "{}" + }, + { + "id": 227, + "question": "What is the only number in the entire number system which can be spelled with the same number of letters as itself?", + "answer": "4, four" + + }, + { + "id": 228, + "question": "1/100th of a second is also termed as what?", + "answer": "jiffy" + }, + { + "id": 229, + "question": "What is this triangle called?", + "img_url": "https://cdn.askpython.com/wp-content/uploads/2020/07/Pascals-triangle.png", + "answer": "Pascal's triangle, Pascal" + }, + { + "id": 230, + "question": "6a^2 is the surface area of which 3 dimensional figure?", + "answer": "cube" + } + ], + "science": [ + { + "id": 301, + "question": "The three main components of a normal atom are: protons, neutrons, and…", + "answer": "electrons" + }, + { + "id": 302, + "question": "As of 2021, how many elements are there in the Periodic Table?", + "answer": "118" + }, + { + "id": 303, + "question": "What is the universal force discovered by Newton that causes objects with mass to attract each other called?", + "answer": "gravity" + }, + { + "id": 304, + "question": "What do you call an organism composed of only one cell?", + "answer": "unicellular, single-celled" + }, + { + "id": 305, + "question": "The Heisenberg’s Uncertainty Principle states that the position and ________ of a quantum object can’t be both exactly measured at the same time.", + "answer": "velocity, momentum" + }, + { + "id": 306, + "question": "A ____________ reaction is the one wherein an atom or a set of atoms is/are replaced by another atom or a set of atoms", + "answer": "displacement, exchange" + }, + { + "id": 307, + "question": "What is the process by which green plants and certain other organisms transform light energy into chemical energy?", + "answer": "photosynthesis" + }, + { + "id": 308, + "question": "DYNAMIC QUESTION, TO BE DETERMINED", + "answer": "DYNAMIC QUESTION, TO BE DETERMINED" + }, + { + "id": 309, + "question": "DYNAMIC QUESTION, TO BE DETERMINED", + "answer": "DYNAMIC QUESTION, TO BE DETERMINED" + }, + { + "id": 310, + "question": "DYNAMIC QUESTION, TO BE DETERMINED", + "answer": "DYNAMIC QUESTION, TO BE DETERMINED" + }, + { + "id": 311, + "question": "How does one call the direct phase transition from gas to solid?", + "answer": "deposition" + }, + { + "id": 312, + "question": "What is the intermolecular force caused by temporary and induced dipoles?", + "answer": "LDF, London dispersion, London dispersion force" + }, + { + "id": 313, + "question": "What is the force that causes objects to float in fluids called?", + "answer": "buoyancy" + }, + { + "id": 314, + "question": "About how many neurons are in the human brain? (A. 1 billion, B. 10 billion, C. 100 billion, D. 300 billion)", + "answer": "C, 100 billion, 100 bil" + }, + { + "id": 315, + "question": "What is the name of our galaxy group in which the Milky Way resides?", + "answer": "Local Group" + }, + { + "id": 316, + "question": "Which cell organelle is nicknamed \"the powerhouse of the cell\"?", + "answer": "mitochondria" + }, + { + "id": 317, + "question": "Which vascular tissue transports water and minerals from the roots to the rest of a plant?", + "answer": "xylem" + }, + { + "id": 318, + "question": "Who discovered the theories of relativity?", + "answer": "Albert Einstein, Einstein" + }, + { + "id": 319, + "question": "In particle physics, the hypothetical isolated elementary particle with only one magnetic pole is termed as...", + "answer": "magnetic monopole" + }, + { + "id": 320, + "question": "How does one describe a chemical reaction wherein heat is released?", + "answer": "exothermic" + }, + { + "id": 321, + "question": "What range of frequency are the average human ears capable of hearing (A. 10Hz-10kHz, B. 20Hz-20kHz, C. 20Hz-2000Hz, D. 10kHz-20kHz)?", + "answer": "B, 20Hz-20kHz" + }, + { + "id": 322, + "question": "What is the process used to separate substances with different polarity in a mixture, using a stationary and mobile phase?", + "answer": "chromatography" + }, + { + "id": 323, + "question": "Which law states that the current through a conductor between two points is directly proportional to the voltage across the two points?", + "answer": "Ohm's law" + }, + { + "id": 324, + "question": "The type of rock that is formed by the accumulation or deposition of mineral or organic particles at the Earth's surface, followed by cementation, is called...", + "answer": "sedimentary, sedimentary rock" + }, + { + "id": 325, + "question": "Is the Richter scale (common earthquake scale) linear or logarithmic?", + "answer": "logarithmic" + }, + { + "id": 326, + "question": "What type of image is formed by a convex mirror?", + "answer": "virtual image, virtual" + }, + { + "id": 327, + "question": "How does one call the branch of physics that deals with the study of mechanical waves in gases, liquids, and solids including topics such as vibration, sound, ultrasound and infrasound", + "answer": "acoustics" + }, + { + "id": 328, + "question": "Which law states that the global entropy in a closed system can only increase?", + "answer": "second law of thermodynamics" + }, + { + "id": 329, + "question": "Which particle is emitted during the beta decay of a radioactive element?", + "answer": "an electron, electron" + }, + { + "id": 330, + "question": "When DNA is unzipped, two strands are formed. What are they called (separate both answers by the word \"and\")?", + "answer": "leading and lagging, leading strand and lagging strand" + } ] } -- cgit v1.2.3 From 113a20a62fdd55776052e3e1968a52aae2c07771 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Fri, 7 May 2021 11:03:16 -0300 Subject: Implement the math and science questions, added a dynamic_id checker --- bot/exts/evergreen/trivia_quiz.py | 241 ++++++++++++++++++++++++++++---------- 1 file changed, 182 insertions(+), 59 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index fe692c2a..1139e3e1 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -25,34 +25,58 @@ class TriviaQuiz(commands.Cog): def __init__(self, bot: commands.Bot) -> None: self.bot = bot - self.questions = self.load_questions() + self.game_status = {} # A variable to store the game status: either running or not running. self.game_owners = {} # A variable to store the person's ID who started the quiz game in a channel. - self.question_limit = 4 + + self.questions = self.load_questions() + self.question_limit = 7 + self.player_scores = {} # A variable to store all player's scores for a bot session. self.game_player_scores = {} # A variable to store temporary game player's scores. + self.categories = { - "general": "Test your general knowledge" - # "retro": "Questions related to retro gaming." + "general": "Test your general knowledge.", + "retro": "Questions related to retro gaming.", + "math": "General questions about mathematics ranging from grade 8 to grade 12.", + "science": "Put your understanding of science to the test!", } + @commands.command() + async def test(self, ctx: commands.Context) -> None: + embed = discord.Embed( + title="yeet", + description="yeet", + ) + + print("yeet") + embed.set_image(url="") + + await ctx.send(embed=embed) + @staticmethod def load_questions() -> dict: """Load the questions from the JSON file.""" p = Path("bot", "resources", "evergreen", "trivia_quiz.json") + with p.open(encoding="utf8") as json_data: questions = json.load(json_data) return questions @commands.group(name="quiz", aliases=["trivia"], invoke_without_command=True) - async def quiz_game(self, ctx: commands.Context, category: str = None) -> None: + async def quiz_game(self, ctx: commands.Context, category: Optional[str], questions: Optional[int]) -> None: """ Start a quiz! Questions for the quiz can be selected from the following categories: - - general : Test your general knowledge. (default) + - general: Test your general knowledge. (default) + - retro: Questions related to retro gaming. + - math: General questions about mathematics ranging from grade 8 to grade 12. + - science: Put your understanding of science to the test! + (More to come!) """ + if ctx.channel.id not in self.game_status: self.game_status[ctx.channel.id] = False @@ -61,11 +85,13 @@ class TriviaQuiz(commands.Cog): # Stop game if running. if self.game_status[ctx.channel.id] is True: - return await ctx.send( - f"Game is already running..." + await ctx.send( + "Game is already running..." f"do `{self.bot.command_prefix}quiz stop`" ) + return + # Send embed showing available categories if inputted category is invalid. if category is None: category = random.choice(list(self.categories)) @@ -76,6 +102,30 @@ class TriviaQuiz(commands.Cog): await ctx.send(embed=embed) return + topic = self.questions[category] + topic_length = len(topic) + + if questions: + if questions > topic_length: + await ctx.send( + embed=self.make_error_embed( + f"This category only has {topic_length} questions. " + "Please input a lower value!" + ) + ) + return + + elif questions < 1: + await ctx.send( + embed=self.make_error_embed( + "Please do at least one question." + ) + ) + return + + else: + self.question_limit = questions - 1 + # Start game if not running. if self.game_status[ctx.channel.id] is False: self.game_owners[ctx.channel.id] = ctx.author @@ -85,16 +135,17 @@ class TriviaQuiz(commands.Cog): await ctx.send(embed=start_embed) # send an embed with the rules await asyncio.sleep(1) - topic = self.questions[category] - done_question = [] hint_no = 0 - answer = None + answers = None + while self.game_status[ctx.channel.id]: # Exit quiz if number of questions for a round are already sent. if len(done_question) > self.question_limit and hint_no == 0: await ctx.send("The round has ended.") - await self.declare_winner(ctx.channel, self.game_player_scores[ctx.channel.id]) + await self.declare_winner( + ctx.channel, self.game_player_scores[ctx.channel.id] + ) self.game_status[ctx.channel.id] = False del self.game_owners[ctx.channel.id] @@ -111,23 +162,39 @@ class TriviaQuiz(commands.Cog): done_question.append(question_dict["id"]) break - q = question_dict["question"] - answer = question_dict["answer"] + if "dynamic_id" not in question_dict: + q = question_dict["question"] + answers = question_dict["answer"].split(", ") + else: + q, answers = DYNAMIC_QUESTIONS_FORMAT_FUNCS[ + question_dict["dynamic_id"] + ]( + question_dict["question"], + question_dict["answer"], + ) + + answers = [answers] + + embed = discord.Embed( + colour=discord.Colour.gold(), + title=f"Question #{len(done_question)}", + description=q, + ) + + if "img_url" in question_dict: + embed.set_image(url=question_dict["img_url"]) - embed = discord.Embed(colour=discord.Colour.gold()) - embed.title = f"Question #{len(done_question)}" - embed.description = q await ctx.send(embed=embed) # Send question embed. # A function to check whether user input is the correct answer(close to the right answer) def check(m: discord.Message) -> bool: - return ( - m.channel == ctx.channel - and fuzz.ratio(answer.lower(), m.content.lower()) > 85 + return m.channel == ctx.channel and any( + fuzz.ratio(answer.lower(), m.content.lower()) > VARIATION_TOLERANCE + for answer in answers ) try: - msg = await self.bot.wait_for('message', check=check, timeout=10) + msg = await self.bot.wait_for("message", check=check, timeout=10) except asyncio.TimeoutError: # In case of TimeoutError and the game has been stopped, then do nothing. if self.game_status[ctx.channel.id] is False: @@ -136,9 +203,14 @@ class TriviaQuiz(commands.Cog): # if number of hints sent or time alerts sent is less than 2, then send one. if hint_no < 2: hint_no += 1 + if "hints" in question_dict: hints = question_dict["hints"] - await ctx.send(f"**Hint #{hint_no+1}\n**{hints[hint_no]}") + + await ctx.send( + f"**Hint #{hint_no}\n**" + f"{hints[hint_no - 1]}" + ) else: await ctx.send(f"{30 - hint_no * 10}s left!") @@ -151,19 +223,22 @@ class TriviaQuiz(commands.Cog): response = random.choice(WRONG_ANS_RESPONSE) await ctx.send(response) - await self.send_answer(ctx.channel, question_dict) + await self.send_answer(ctx.channel, answers, question_dict) await asyncio.sleep(1) hint_no = 0 # init hint_no = 0 so that 2 hints/time alerts can be sent for the new question. - await self.send_score(ctx.channel, self.game_player_scores[ctx.channel.id]) + await self.send_score( + ctx.channel, self.game_player_scores[ctx.channel.id] + ) + await asyncio.sleep(2) else: if self.game_status[ctx.channel.id] is False: break # Reduce points by 25 for every hint/time alert that has been sent. - points = 100 - 25*hint_no + points = 100 - 25 * hint_no if msg.author in self.game_player_scores[ctx.channel.id]: self.game_player_scores[ctx.channel.id][msg.author] += points else: @@ -177,24 +252,49 @@ class TriviaQuiz(commands.Cog): hint_no = 0 - await ctx.send(f"{msg.author.mention} got the correct answer :tada: {points} points!") - await self.send_answer(ctx.channel, question_dict) - await self.send_score(ctx.channel, self.game_player_scores[ctx.channel.id]) + await ctx.send( + f"{msg.author.mention} got the correct answer :tada: {points} points!" + ) + + await self.send_answer(ctx.channel, answers, question_dict) + await self.send_score( + ctx.channel, self.game_player_scores[ctx.channel.id] + ) + await asyncio.sleep(2) - @staticmethod - def make_start_embed(category: str) -> discord.Embed: + def make_start_embed(self, category: str) -> discord.Embed: """Generate a starting/introduction embed for the quiz.""" - start_embed = discord.Embed(colour=discord.Colour.red()) - start_embed.title = "Quiz game Starting!!" - start_embed.description = "Each game consists of 5 questions.\n" - start_embed.description += "**Rules :**\nNo cheating and have fun!" - start_embed.description += f"\n **Category** : {category}" + start_embed = discord.Embed( + colour=discord.Colour.red(), + title="Quiz game starting!", + description=( + f"Each game consists of {self.question_limit + 1} questions.\n" + "**Rules: **No cheating and have fun!\n" + f"**Category**: {category}" + ), + ) + start_embed.set_footer( - text="Points for each question reduces by 25 after 10s or after a hint. Total time is 30s per question" + text=( + "Points for each question reduces by 25 after 10s or after a hint." + "Total time is 30s per question" + ) ) + return start_embed + @staticmethod + def make_error_embed(desc: str) -> discord.Embed: + """Generate an error embed with the given description.""" + error_embed = discord.Embed( + colour=discord.Colour.red(), + title=random.choice(NEGATIVE_REPLIES), + description=desc, + ) + + return error_embed + @quiz_game.command(name="stop") async def stop_quiz(self, ctx: commands.Context) -> None: """ @@ -204,18 +304,23 @@ class TriviaQuiz(commands.Cog): """ if self.game_status[ctx.channel.id] is True: # Check if the author is the game starter or a moderator. - if ( - ctx.author == self.game_owners[ctx.channel.id] - or any(Roles.moderator == role.id for role in ctx.author.roles) + if ctx.author == self.game_owners[ctx.channel.id] or any( + Roles.moderator == role.id for role in ctx.author.roles ): + await ctx.send("Quiz stopped.") - await self.declare_winner(ctx.channel, self.game_player_scores[ctx.channel.id]) + await self.declare_winner( + ctx.channel, self.game_player_scores[ctx.channel.id] + ) self.game_status[ctx.channel.id] = False del self.game_owners[ctx.channel.id] self.game_player_scores[ctx.channel.id] = {} + else: - await ctx.send(f"{ctx.author.mention}, you are not authorised to stop this game :ghost:!") + await ctx.send( + f"{ctx.author.mention}, you are not authorised to stop this game :ghost:!" + ) else: await ctx.send("No quiz running.") @@ -231,13 +336,15 @@ class TriviaQuiz(commands.Cog): await channel.send("No one has made it onto the leaderboard yet.") return - embed = discord.Embed(colour=discord.Colour.blue()) - embed.title = "Score Board" - embed.description = "" + embed = discord.Embed( + colour=discord.Colour.blue(), + title="Score Board", + description="", + ) sorted_dict = sorted(player_data.items(), key=lambda a: a[1], reverse=True) for item in sorted_dict: - embed.description += f"{item[0]} : {item[1]}\n" + embed.description += f"{item[0]}: {item[1]}\n" await channel.send(embed=embed) @@ -250,7 +357,6 @@ class TriviaQuiz(commands.Cog): # Check if more than 1 player has highest points. if no_of_winners > 1: - word = "You guys" winners = [] points_copy = list(player_data.values()).copy() @@ -261,36 +367,53 @@ class TriviaQuiz(commands.Cog): winners_mention = " ".join(winner.mention for winner in winners) else: - word = "You" author_index = list(player_data.values()).index(highest_points) winner = list(player_data.keys())[author_index] winners_mention = winner.mention await channel.send( f"Congratulations {winners_mention} :tada: " - f"{word} have won this quiz game with a grand total of {highest_points} points!" + f"You have won this quiz game with a grand total of {highest_points} points!" ) def category_embed(self) -> discord.Embed: """Build an embed showing all available trivia categories.""" - embed = discord.Embed(colour=discord.Colour.blue()) - embed.title = "The available question categories are:" - embed.set_footer(text="If a category is not chosen, a random one will be selected.") - embed.description = "" + embed = discord.Embed( + colour=discord.Colour.blue(), + title="The available question categories are:", + description="", + ) + + embed.set_footer( + text="If a category is not chosen, a random one will be selected." + ) for cat, description in self.categories.items(): - embed.description += f"**- {cat.capitalize()}**\n{description.capitalize()}\n" + embed.description += ( + f"**- {cat.capitalize()}**\n" + f"{description.capitalize()}\n" + ) return embed @staticmethod - async def send_answer(channel: discord.TextChannel, question_dict: dict) -> None: + async def send_answer( + channel: discord.TextChannel, + answers: List[str], + question_dict: dict, + ) -> None: + """Send the correct answer of a question to the game channel.""" - answer = question_dict["answer"] - info = question_dict["info"] - embed = discord.Embed(color=discord.Colour.red()) - embed.title = f"The correct answer is **{answer}**\n" - embed.description = "" + if "info" in question_dict: + info = question_dict["info"] + else: + info = "" + + embed = discord.Embed( + color=discord.Colour.red(), + title=f"The correct answer is/are **{', '.join(answers)}**\n", + description="", + ) if info != "": embed.description += f"**Information**\n{info}\n\n" -- cgit v1.2.3 From 9e6004f26d7d95537cef6e60c8ee7bf78e705a82 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Fri, 7 May 2021 11:19:31 -0300 Subject: fix NameError, add some forgotten imports --- bot/exts/evergreen/trivia_quiz.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 1139e3e1..2158ac9c 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -9,11 +9,12 @@ from discord.ext import commands from fuzzywuzzy import fuzz from bot.constants import Roles +from bot.constants import NEGATIVE_REPLIES +from typing import Optional, List, Tuple logger = logging.getLogger(__name__) - WRONG_ANS_RESPONSE = [ "No one answered correctly!", "Better luck next time" -- cgit v1.2.3 From 40d5a00f1b609b23a6cd77a3b7f1e4814ed7df82 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Fri, 7 May 2021 11:01:24 -0400 Subject: chore: Get the next perfect square If the amount of squares is not a perfect square, get the next highest perfect square --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index dffe43ce..6418eaee 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -325,8 +325,10 @@ class AvatarModify(commands.Cog): if 1 <= squares <= MAX_SQUARES: raise commands.BadArgument(f"Squares must be a positive number less than or equal to {MAX_SQUARES:,}.") - if not math.sqrt(squares).is_integer(): - raise commands.BadArgument("The number of squares must be a perfect square.") + sqrt = math.sqrt(squares) + + if not sqrt.is_integer(): + squares = math.ceil(sqrt) ** 2 # Get the next perfect square file_name = file_safe_name("mosaic_avatar", ctx.author.display_name) -- cgit v1.2.3 From 14fc010db6a8ac06eb356617146e9deaa540177b Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Fri, 7 May 2021 11:34:35 -0400 Subject: fix: Add a missing 'not' --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 6418eaee..2afc3b74 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -322,7 +322,7 @@ class AvatarModify(commands.Cog): await ctx.send(f"{Emojis.cross_mark} Could not get member info.") return - if 1 <= squares <= MAX_SQUARES: + if not 1 <= squares <= MAX_SQUARES: raise commands.BadArgument(f"Squares must be a positive number less than or equal to {MAX_SQUARES:,}.") sqrt = math.sqrt(squares) -- cgit v1.2.3 From 39350fb471d9dff954cbecceebf3113b28dad6d9 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 7 May 2021 17:34:25 +0100 Subject: Switch from fetch_member to fetch_user This is because discord.py would populate the user portion of the fetched member with cached information which would cause the avatar url not to be updated as we had intended with that function --- .../evergreen/avatar_modification/avatar_modify.py | 68 +++++++++++----------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 2afc3b74..a84983e0 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -12,7 +12,7 @@ from aiohttp import client_exceptions from discord.ext import commands from discord.ext.commands.errors import BadArgument -from bot.constants import Client, Colours, Emojis +from bot.constants import Colours, Emojis from bot.exts.evergreen.avatar_modification._effects import PfpEffects from bot.utils.extensions import invoke_help_command from bot.utils.halloween import spookifications @@ -66,23 +66,25 @@ class AvatarModify(commands.Cog): def __init__(self, bot: commands.Bot) -> None: self.bot = bot - async def _fetch_member(self, member_id: int) -> t.Optional[discord.Member]: + async def _fetch_user(self, user_id: int) -> t.Optional[discord.Member]: """ - Fetches a member and handles errors. + Fetches a user and handles errors. - This helper funciton is required as the member cache doesn't always have the most up to date + This helper function is required as the member cache doesn't always have the most up to date profile picture. This can lead to errors if the image is delted from the Discord CDN. + fetch_member can't be used due to the avatar url being part of the user object, and + some weird caching that D.py does """ try: - member = await self.bot.get_guild(Client.guild).fetch_member(member_id) + user = await self.bot.fetch_user(user_id) except discord.errors.NotFound: - log.debug(f"Member {member_id} left the guild before we could get their pfp.") + log.debug(f"User {user_id} could not be found.") return None except discord.HTTPException: - log.exception(f"Exception while trying to retrieve member {member_id} from Discord.") + log.exception(f"Exception while trying to retrieve user {user_id} from Discord.") return None - return member + return user @commands.group(aliases=("avatar_mod", "pfp_mod", "avatarmod", "pfpmod")) async def avatar_modify(self, ctx: commands.Context) -> None: @@ -94,13 +96,13 @@ class AvatarModify(commands.Cog): async def eightbit_command(self, ctx: commands.Context) -> None: """Pixelates your avatar and changes the palette to an 8bit one.""" async with ctx.typing(): - member = await self._fetch_member(ctx.author.id) - if not member: - await ctx.send(f"{Emojis.cross_mark} Could not get member info.") + user = await self._fetch_user(ctx.author.id) + if not user: + await ctx.send(f"{Emojis.cross_mark} Could not get user info.") return - image_bytes = await member.avatar_url.read() - file_name = file_safe_name("eightbit_avatar", member.display_name) + image_bytes = await user.avatar_url.read() + file_name = file_safe_name("eightbit_avatar", ctx.author.display_name) file = await in_executor( PfpEffects.apply_effect, @@ -115,7 +117,7 @@ class AvatarModify(commands.Cog): ) embed.set_image(url=f"attachment://{file_name}") - embed.set_footer(text=f"Made by {member.display_name}.", icon_url=member.avatar_url) + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) await ctx.send(embed=embed, file=file) @@ -140,9 +142,9 @@ class AvatarModify(commands.Cog): return args[0] async with ctx.typing(): - member = await self._fetch_member(ctx.author.id) - if not member: - await ctx.send(f"{Emojis.cross_mark} Could not get member info.") + user = await self._fetch_user(ctx.author.id) + if not user: + await ctx.send(f"{Emojis.cross_mark} Could not get user info.") return egg = None @@ -155,8 +157,8 @@ class AvatarModify(commands.Cog): return ctx.send = send_message # Reassigns ctx.send - image_bytes = await member.avatar_url_as(size=256).read() - file_name = file_safe_name("easterified_avatar", member.display_name) + image_bytes = await user.avatar_url_as(size=256).read() + file_name = file_safe_name("easterified_avatar", ctx.author.display_name) file = await in_executor( PfpEffects.apply_effect, @@ -171,7 +173,7 @@ class AvatarModify(commands.Cog): description="Here is your lovely avatar, all bright and colourful\nwith Easter pastel colours. Enjoy :D" ) embed.set_image(url=f"attachment://{file_name}") - embed.set_footer(text=f"Made by {member.display_name}.", icon_url=member.avatar_url) + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) await ctx.send(file=file, embed=embed) @@ -226,11 +228,11 @@ class AvatarModify(commands.Cog): return async with ctx.typing(): - member = await self._fetch_member(ctx.author.id) - if not member: - await ctx.send(f"{Emojis.cross_mark} Could not get member info.") + user = await self._fetch_user(ctx.author.id) + if not user: + await ctx.send(f"{Emojis.cross_mark} Could not get user info.") return - image_bytes = await member.avatar_url_as(size=1024).read() + image_bytes = await user.avatar_url.read() await self.send_pride_image(ctx, image_bytes, pixels, flag, option) @prideavatar.command() @@ -286,13 +288,13 @@ class AvatarModify(commands.Cog): if member is None: member = ctx.author - member = await self._fetch_member(member.id) - if not member: - await ctx.send(f"{Emojis.cross_mark} Could not get member info.") + user = await self._fetch_user(member.id) + if not user: + await ctx.send(f"{Emojis.cross_mark} Could not get user info.") return async with ctx.typing(): - image_bytes = await member.avatar_url.read() + image_bytes = await user.avatar_url.read() file_name = file_safe_name("spooky_avatar", member.display_name) @@ -317,9 +319,9 @@ class AvatarModify(commands.Cog): async def mosaic_command(self, ctx: commands.Context, squares: int = 16) -> None: """Splits your avatar into x squares, randomizes them and stitches them back into a new image!""" async with ctx.typing(): - member = await self._fetch_member(ctx.author.id) - if not member: - await ctx.send(f"{Emojis.cross_mark} Could not get member info.") + user = await self._fetch_user(ctx.author.id) + if not user: + await ctx.send(f"{Emojis.cross_mark} Could not get user info.") return if not 1 <= squares <= MAX_SQUARES: @@ -332,7 +334,7 @@ class AvatarModify(commands.Cog): file_name = file_safe_name("mosaic_avatar", ctx.author.display_name) - img_bytes = await member.avatar_url.read() + img_bytes = await user.avatar_url.read() file = await in_executor( PfpEffects.mosaic_effect, @@ -358,7 +360,7 @@ class AvatarModify(commands.Cog): ) embed.set_image(url=f"attachment://{file_name}") - embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=user.avatar_url) await ctx.send(file=file, embed=embed) -- cgit v1.2.3 From 7725a022675c6bddad4354283fbad8e56a364eb6 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 7 May 2021 17:35:54 +0100 Subject: Download avatars as size 1024 to avoid very large avatars affecting infra --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index a84983e0..1eceaf03 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -101,7 +101,7 @@ class AvatarModify(commands.Cog): await ctx.send(f"{Emojis.cross_mark} Could not get user info.") return - image_bytes = await user.avatar_url.read() + image_bytes = await user.avatar_url_as(size=1024).read() file_name = file_safe_name("eightbit_avatar", ctx.author.display_name) file = await in_executor( @@ -232,7 +232,7 @@ class AvatarModify(commands.Cog): if not user: await ctx.send(f"{Emojis.cross_mark} Could not get user info.") return - image_bytes = await user.avatar_url.read() + image_bytes = await user.avatar_url_as(size=1024).read() await self.send_pride_image(ctx, image_bytes, pixels, flag, option) @prideavatar.command() @@ -294,7 +294,7 @@ class AvatarModify(commands.Cog): return async with ctx.typing(): - image_bytes = await user.avatar_url.read() + image_bytes = await user.avatar_url_as(size=1024).read() file_name = file_safe_name("spooky_avatar", member.display_name) @@ -334,7 +334,7 @@ class AvatarModify(commands.Cog): file_name = file_safe_name("mosaic_avatar", ctx.author.display_name) - img_bytes = await user.avatar_url.read() + img_bytes = await user.avatar_url_as(size=1024).read() file = await in_executor( PfpEffects.mosaic_effect, -- cgit v1.2.3 From 94a9550f3729d7c266c3437554b36d8b52fc84fb Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 7 May 2021 17:36:56 +0100 Subject: Add the number of squares that were used by .mosaic This is useful for when we round up the number of squares used, users can still see how many were actually used. --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 1eceaf03..b604b3e4 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -351,7 +351,7 @@ class AvatarModify(commands.Cog): description = "What a masterpiece. :star:" else: title = "Your mosaic avatar" - description = "Here is your avatar. I think it looks a bit *puzzling*" + description = f"Here is your avatar. I think it looks a bit *puzzling*\nMade with {squares} squares" embed = discord.Embed( title=title, -- cgit v1.2.3 From 28f0c10eaf8880995d4eccfe3df92e3b707d95d5 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 7 May 2021 17:43:56 +0100 Subject: Correct grammer in the new mosaic command output --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index b604b3e4..9cc2e6b4 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -351,7 +351,7 @@ class AvatarModify(commands.Cog): description = "What a masterpiece. :star:" else: title = "Your mosaic avatar" - description = f"Here is your avatar. I think it looks a bit *puzzling*\nMade with {squares} squares" + description = f"Here is your avatar. I think it looks a bit *puzzling*\nMade with {squares} squares." embed = discord.Embed( title=title, -- cgit v1.2.3 From e929d6b07ee5676d6f73fe7fc9d86db8214f6d22 Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Fri, 7 May 2021 17:51:40 +0100 Subject: Update return type hint to reflect new behaviour. Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 9cc2e6b4..693d15c7 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -66,7 +66,7 @@ class AvatarModify(commands.Cog): def __init__(self, bot: commands.Bot) -> None: self.bot = bot - async def _fetch_user(self, user_id: int) -> t.Optional[discord.Member]: + async def _fetch_user(self, user_id: int) -> t.Optional[discord.User]: """ Fetches a user and handles errors. -- cgit v1.2.3 From 0913bf2ba9a66bb7d6dc7c729d689b98dfdcb520 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Fri, 7 May 2021 14:06:34 -0300 Subject: Add dynamically generated science questions --- bot/resources/evergreen/trivia_quiz.json | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index b20b953f..ce33e8c9 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -435,7 +435,7 @@ { "id": 228, "question": "1/100th of a second is also termed as what?", - "answer": "jiffy" + "answer": "a jiffy, jiffy" }, { "id": 229, @@ -487,18 +487,22 @@ }, { "id": 308, - "question": "DYNAMIC QUESTION, TO BE DETERMINED", - "answer": "DYNAMIC QUESTION, TO BE DETERMINED" + "dynamic_id": 301, + "question": "What is the {} planet of our Solar System?", + "answer": "{}" }, { "id": 309, - "question": "DYNAMIC QUESTION, TO BE DETERMINED", - "answer": "DYNAMIC QUESTION, TO BE DETERMINED" + "dynamic_id": 302, + "question": "In the biological taxonomic hierarchy, what is placed directly above {}?", + "answer": "{}" }, { "id": 310, - "question": "DYNAMIC QUESTION, TO BE DETERMINED", - "answer": "DYNAMIC QUESTION, TO BE DETERMINED" + "dynamic_id": 303, + "question": "How does one describe the unit {} in SI base units?\n**IMPORTANT:** use * for multiplication, ^ for exponentiation, and place your base units in this order: m - kg - s - A", + "img_url": "https://i.imgur.com/NRzU6tf.png", + "answer": "{}" }, { "id": 311, @@ -533,7 +537,7 @@ { "id": 317, "question": "Which vascular tissue transports water and minerals from the roots to the rest of a plant?", - "answer": "xylem" + "answer": "the xylem, xylem" }, { "id": 318, @@ -593,7 +597,7 @@ { "id": 329, "question": "Which particle is emitted during the beta decay of a radioactive element?", - "answer": "an electron, electron" + "answer": "an electron, the electron, electron" }, { "id": 330, -- cgit v1.2.3 From c940d96a9d11ddfaaf4b599f429d559b2da60d1b Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Fri, 7 May 2021 14:08:39 -0300 Subject: Add dynamic question formatters and its constants --- bot/exts/evergreen/trivia_quiz.py | 164 +++++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 2158ac9c..d57ddc09 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -15,11 +15,171 @@ from typing import Optional, List, Tuple logger = logging.getLogger(__name__) +VARIATION_TOLERANCE = 83 + WRONG_ANS_RESPONSE = [ "No one answered correctly!", - "Better luck next time" + "Better luck next time...", +] + +N_PREFIX_STARTS_AT = 5 +N_PREFIXES = [ + "penta", "hexa", "hepta", "octa", "nona", + "deca", "hendeca", "dodeca", "trideca", "tetradeca", +] + +PLANETS = [ + ("1st", "Mercury"), + ("2nd", "Venus"), + ("3rd", "Earth"), + ("4th", "Mars"), + ("5th", "Jupiter"), + ("6th", "Saturn"), + ("7th", "Uranus"), + ("8th", "Neptune"), +] + +TAXONOMIC_HIERARCHY = [ + "species", "genus", "family", "order", + "class", "phylum", "kingdom", "domain", ] +UNITS_TO_BASE_UNITS = { + "hertz": ("(unit of frequency)", "s^-1"), + "newton": ("(unit of force)", "m*kg*s^-2"), + "pascal": ("(unit of pressure & stress)", "m^-1*kg*s^-2"), + "joule": ("(unit of energy & quantity of heat)", "m^2*kg*s^-2"), + "watt": ("(unit of power)", "m^2*kg*s^-3"), + "coulomb": ("(unit of electric charge & quantity of electricity)", "s*A"), + "volt": ("(unit of voltage & electromotive force)", "m^2*kg*s^-3*A^-1"), + "farad": ("(unit of capacitance)", "m^-2*kg^-1*s^4*A^2"), + "ohm": ("(unit of electric resistance)", "m^2*kg*s^-3*A^-2"), + "weber": ("(unit of magnetic flux)", "m^2*kg*s^-2*A^-1"), + "tesla": ("(unit of magnetic flux density)", "kg*s^-2*A^-1"), +} + + +def linear_system(q_format: str, a_format: str) -> Tuple[str, str]: + x, y = random.randint(2, 5), random.randint(2, 5) + answer = a_format.format(x, y) + + nums = [i for i in range(1, 6)] + coeffs = [nums.pop(random.randint(0, len(nums) - 1)) for _ in range(4)] + + question = q_format.format( + coeffs[0], + coeffs[1], + ( + coeffs[0] * x + coeffs[1] * y + ), + coeffs[2], + coeffs[3], + ( + coeffs[2] * x + coeffs[3] * y + ), + ) + + return question, answer + + +def mod_arith(q_format: str, a_format: str) -> Tuple[str, str]: + quotient, m, b = random.randint(30, 40), random.randint(10, 20), random.randint(200, 350) + ans = random.randint(0, 9) # max 9 because min mod 10 + a = quotient * m + ans - b + + question = q_format.format(a, b, m) + answer = a_format.format(ans) + + return question, answer + + +def ngonal_prism(q_format: str, a_format: str) -> Tuple[str, str]: + n = random.randint(0, len(N_PREFIXES) - 1) + + question = q_format.format(N_PREFIXES[n]) + answer = a_format.format((n + N_PREFIX_STARTS_AT) * 2) + + return question, answer + + +def imag_sqrt(q_format: str, a_format: str) -> Tuple[str, str]: + ans_coeff = random.randint(3, 10) + + question = q_format.format(ans_coeff ** 2) + answer = a_format.format(ans_coeff) + + return question, answer + + +def binary_calc(q_format: str, a_format: str) -> Tuple[str, str]: + a = random.randint(15, 20) + b = random.randint(10, a) + oper = random.choice( + ( + ("+", lambda x, y: x + y), + ("-", lambda x, y: x - y), + ("*", lambda x, y: x * y), + ) + ) + + if oper[0] == "*": + a -= 5 + b -= 5 + + question = q_format.format( + bin(a)[2:], + oper[0], + bin(b)[2:], + ) + answer = a_format.format( + bin(oper[1](a, b))[2:] + ) + + return question, answer + + +def solar_system(q_format: str, a_format: str) -> Tuple[str, str]: + planet = random.choice(PLANETS) + + question = q_format.format(planet[0]) + answer = a_format.format(planet[1]) + + return question, answer + + +def taxonomic_rank(q_format: str, a_format: str) -> Tuple[str, str]: + level = random.randint(0, len(TAXONOMIC_HIERARCHY) - 2) + + question = q_format.format(TAXONOMIC_HIERARCHY[level]) + answer = a_format.format(TAXONOMIC_HIERARCHY[level + 1]) + + return question, answer + + +def base_units_convert(q_format: str, a_format: str) -> Tuple[str, str]: + unit = random.choice(list(UNITS_TO_BASE_UNITS.keys())) + + question = q_format.format( + unit + " " + UNITS_TO_BASE_UNITS[unit][0] + ) + answer = a_format.format( + UNITS_TO_BASE_UNITS[unit][1] + ) + + return question, answer + + +DYNAMIC_QUESTIONS_FORMAT_FUNCS = { + 201: linear_system, + 202: mod_arith, + 203: ngonal_prism, + 204: imag_sqrt, + 205: binary_calc, + 301: solar_system, + 302: taxonomic_rank, + 303: base_units_convert, +} + class TriviaQuiz(commands.Cog): """A cog for all quiz commands.""" @@ -412,7 +572,7 @@ class TriviaQuiz(commands.Cog): embed = discord.Embed( color=discord.Colour.red(), - title=f"The correct answer is/are **{', '.join(answers)}**\n", + title=f"The correct answer is/are **`{', '.join(answers)}`**\n", description="", ) -- cgit v1.2.3 From b80588d0c74f78f3d78b0580ba52d0ec8d23aeec Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Fri, 7 May 2021 14:27:10 -0300 Subject: Fix lint failures: adding docstrings, removing whitespace --- bot/exts/evergreen/trivia_quiz.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index d57ddc09..7bbb8a15 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -8,10 +8,10 @@ import discord from discord.ext import commands from fuzzywuzzy import fuzz -from bot.constants import Roles -from bot.constants import NEGATIVE_REPLIES +from typing import List, Optional, Tuple -from typing import Optional, List, Tuple +from bot.constants import NEGATIVE_REPLIES +from bot.constants import Roles logger = logging.getLogger(__name__) @@ -60,6 +60,7 @@ UNITS_TO_BASE_UNITS = { def linear_system(q_format: str, a_format: str) -> Tuple[str, str]: + """Generates a system of linear equations with two unknowns.""" x, y = random.randint(2, 5), random.randint(2, 5) answer = a_format.format(x, y) @@ -83,6 +84,7 @@ def linear_system(q_format: str, a_format: str) -> Tuple[str, str]: def mod_arith(q_format: str, a_format: str) -> Tuple[str, str]: + """Generate a basic modular arithmetic question.""" quotient, m, b = random.randint(30, 40), random.randint(10, 20), random.randint(200, 350) ans = random.randint(0, 9) # max 9 because min mod 10 a = quotient * m + ans - b @@ -94,6 +96,7 @@ def mod_arith(q_format: str, a_format: str) -> Tuple[str, str]: def ngonal_prism(q_format: str, a_format: str) -> Tuple[str, str]: + """Generate a question regarding vertices on n-gonal prisms.""" n = random.randint(0, len(N_PREFIXES) - 1) question = q_format.format(N_PREFIXES[n]) @@ -103,6 +106,7 @@ def ngonal_prism(q_format: str, a_format: str) -> Tuple[str, str]: def imag_sqrt(q_format: str, a_format: str) -> Tuple[str, str]: + """Generates a negative square root question.""" ans_coeff = random.randint(3, 10) question = q_format.format(ans_coeff ** 2) @@ -112,6 +116,7 @@ def imag_sqrt(q_format: str, a_format: str) -> Tuple[str, str]: def binary_calc(q_format: str, a_format: str) -> Tuple[str, str]: + """Generates a binary calculation question.""" a = random.randint(15, 20) b = random.randint(10, a) oper = random.choice( @@ -139,6 +144,7 @@ def binary_calc(q_format: str, a_format: str) -> Tuple[str, str]: def solar_system(q_format: str, a_format: str) -> Tuple[str, str]: + """Generates a question on the planets of the Solar System.""" planet = random.choice(PLANETS) question = q_format.format(planet[0]) @@ -148,6 +154,7 @@ def solar_system(q_format: str, a_format: str) -> Tuple[str, str]: def taxonomic_rank(q_format: str, a_format: str) -> Tuple[str, str]: + """Generates a question on taxonomic classification.""" level = random.randint(0, len(TAXONOMIC_HIERARCHY) - 2) question = q_format.format(TAXONOMIC_HIERARCHY[level]) @@ -157,6 +164,7 @@ def taxonomic_rank(q_format: str, a_format: str) -> Tuple[str, str]: def base_units_convert(q_format: str, a_format: str) -> Tuple[str, str]: + """Generates a SI base units conversion question.""" unit = random.choice(list(UNITS_TO_BASE_UNITS.keys())) question = q_format.format( @@ -203,18 +211,6 @@ class TriviaQuiz(commands.Cog): "science": "Put your understanding of science to the test!", } - @commands.command() - async def test(self, ctx: commands.Context) -> None: - embed = discord.Embed( - title="yeet", - description="yeet", - ) - - print("yeet") - embed.set_image(url="") - - await ctx.send(embed=embed) - @staticmethod def load_questions() -> dict: """Load the questions from the JSON file.""" @@ -237,7 +233,6 @@ class TriviaQuiz(commands.Cog): (More to come!) """ - if ctx.channel.id not in self.game_status: self.game_status[ctx.channel.id] = False @@ -563,7 +558,6 @@ class TriviaQuiz(commands.Cog): answers: List[str], question_dict: dict, ) -> None: - """Send the correct answer of a question to the game channel.""" if "info" in question_dict: info = question_dict["info"] -- cgit v1.2.3 From c742604f310097339e130688232862f27052cc3b Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Fri, 7 May 2021 14:28:31 -0300 Subject: Fix wrong import order lint failure --- bot/exts/evergreen/trivia_quiz.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 7bbb8a15..d024f856 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -3,13 +3,12 @@ import json import logging import random from pathlib import Path +from typing import List, Optional, Tuple import discord from discord.ext import commands from fuzzywuzzy import fuzz -from typing import List, Optional, Tuple - from bot.constants import NEGATIVE_REPLIES from bot.constants import Roles -- cgit v1.2.3 From d5f01153aac59cfbc043d894ebf6e1173c1efb48 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Fri, 7 May 2021 13:41:23 -0400 Subject: chore: Reformat the code to follow the style guild --- bot/__init__.py | 2 +- bot/exts/christmas/advent_of_code/_cog.py | 18 ++++++++++------ bot/exts/christmas/hanukkah_embed.py | 34 ++++++++++++++++++++----------- bot/exts/evergreen/cheatsheet.py | 20 ++++++++++-------- bot/exts/evergreen/connect_four.py | 6 ++++-- bot/exts/evergreen/game.py | 15 +++++++------- bot/exts/evergreen/pythonfacts.py | 14 ++++++++----- bot/exts/evergreen/snakes/_snakes_cog.py | 26 ++++++++++++++++------- bot/exts/evergreen/snakes/_utils.py | 7 +++++-- 9 files changed, 92 insertions(+), 50 deletions(-) diff --git a/bot/__init__.py b/bot/__init__.py index 669f9f5d..e5ed9d92 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -69,7 +69,7 @@ logging.basicConfig( level=logging.TRACE if Client.debug else logging.DEBUG, handlers=[console_handler, file_handler], ) -logging.getLogger().info('Logging initialization complete') +logging.getLogger().info("Logging initialization complete") # On Windows, the selector event loop is required for aiodns. diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index da1cf28d..863d2a21 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -72,11 +72,15 @@ class AdventOfCode(commands.Cog): if role not in ctx.author.roles: await ctx.author.add_roles(role) - await ctx.send("Okay! You have been __subscribed__ to notifications about new Advent of Code tasks. " - f"You can run `{unsubscribe_command}` to disable them again for you.") + await ctx.send( + "Okay! You have been __subscribed__ to notifications about new Advent of Code tasks. " + f"You can run `{unsubscribe_command}` to disable them again for you." + ) else: - await ctx.send("Hey, you already are receiving notifications about new Advent of Code tasks. " - f"If you don't want them any more, run `{unsubscribe_command}` instead.") + await ctx.send( + "Hey, you already are receiving notifications about new Advent of Code tasks. " + f"If you don't want them any more, run `{unsubscribe_command}` instead." + ) @in_month(Month.DECEMBER) @adventofcode_group.command(name="unsubscribe", aliases=("unsub",), brief="Notifications for new days") @@ -110,8 +114,10 @@ class AdventOfCode(commands.Cog): else: delta_str = f"{delta.days} days" - await ctx.send(f"The Advent of Code event is not currently running. " - f"The next event will start in {delta_str}.") + await ctx.send( + f"The Advent of Code event is not currently running. " + f"The next event will start in {delta_str}." + ) return tomorrow, time_left = _helpers.time_left_to_est_midnight() diff --git a/bot/exts/christmas/hanukkah_embed.py b/bot/exts/christmas/hanukkah_embed.py index 214044b8..af5cfccf 100644 --- a/bot/exts/christmas/hanukkah_embed.py +++ b/bot/exts/christmas/hanukkah_embed.py @@ -17,8 +17,10 @@ class HanukkahEmbed(commands.Cog): def __init__(self, bot: Bot): self.bot = bot - self.url = ("https://www.hebcal.com/hebcal/?v=1&cfg=json&maj=on&min=on&mod=on&nx=on&" - "year=now&month=x&ss=on&mf=on&c=on&geo=geoname&geonameid=3448439&m=50&s=on") + self.url = ( + "https://www.hebcal.com/hebcal/?v=1&cfg=json&maj=on&min=on&mod=on&nx=on&" + "year=now&month=x&ss=on&mf=on&c=on&geo=geoname&geonameid=3448439&m=50&s=on" + ) self.hanukkah_days = [] self.hanukkah_months = [] self.hanukkah_years = [] @@ -64,13 +66,17 @@ class HanukkahEmbed(commands.Cog): hours = now.hour + 4 # using only hours hanukkah_start_hour = 18 if hours < hanukkah_start_hour: - embed.description = (f"Hanukkah hasnt started yet, " - f"it will start in about {hanukkah_start_hour - hours} hour/s.") + embed.description = ( + f"Hanukkah hasnt started yet, " + f"it will start in about {hanukkah_start_hour - hours} hour/s." + ) await ctx.send(embed=embed) return elif hours > hanukkah_start_hour: - embed.description = (f"It is the starting day of Hanukkah ! " - f"Its been {hours - hanukkah_start_hour} hours hanukkah started !") + embed.description = ( + f"It is the starting day of Hanukkah ! " + f"Its been {hours - hanukkah_start_hour} hours hanukkah started !" + ) await ctx.send(embed=embed) return festival_day = self.hanukkah_days.index(day) @@ -87,18 +93,22 @@ class HanukkahEmbed(commands.Cog): message = "" for _ in range(1, festival_day + 1): message += ":menorah:" - embed.description = f"It is the {festival_day}{suffix} day of Hanukkah ! \n {message}" + embed.description = f"It is the {festival_day}{suffix} day of Hanukkah!\n{message}" await ctx.send(embed=embed) else: if today < hanukkah_start: festival_starting_month = hanukkah_start.strftime("%B") - embed.description = (f"Hanukkah has not started yet. " - f"Hanukkah will start at sundown on {hanukkah_start_day}th " - f"of {festival_starting_month}.") + embed.description = ( + f"Hanukkah has not started yet. " + f"Hanukkah will start at sundown on {hanukkah_start_day}th " + f"of {festival_starting_month}." + ) else: festival_end_month = hanukkah_end.strftime("%B") - embed.description = (f"Looks like you missed Hanukkah !" - f"Hanukkah ended on {hanukkah_end_day}th of {festival_end_month}.") + embed.description = ( + f"Looks like you missed Hanukkah !" + f"Hanukkah ended on {hanukkah_end_day}th of {festival_end_month}." + ) await ctx.send(embed=embed) diff --git a/bot/exts/evergreen/cheatsheet.py b/bot/exts/evergreen/cheatsheet.py index 86fae167..7ee34b1d 100644 --- a/bot/exts/evergreen/cheatsheet.py +++ b/bot/exts/evergreen/cheatsheet.py @@ -12,7 +12,7 @@ from bot.bot import Bot from bot.constants import Categories, Channels, Colours, ERROR_REPLIES from bot.utils.decorators import whitelist_override -ERROR_MESSAGE = f""" +ERROR_MESSAGE = f"""\ Unknown cheat sheet. Please try to reformulate your query. **Examples**: @@ -61,14 +61,18 @@ class CheatSheet(commands.Cog): body_space = min(1986 - len(url), 1000) if len(body_text) > body_space: - description = (f"**Result Of cht.sh**\n" - f"```python\n{body_text[:body_space]}\n" - f"... (truncated - too many lines)```\n" - f"Full results: {url} ") + description = ( + f"**Result Of cht.sh**\n" + f"```python\n{body_text[:body_space]}\n" + f"... (truncated - too many lines)```\n" + f"Full results: {url} " + ) else: - description = (f"**Result Of cht.sh**\n" - f"```python\n{body_text}```\n" - f"{url}") + description = ( + f"**Result Of cht.sh**\n" + f"```python\n{body_text}```\n" + f"{url}" + ) return False, description @commands.command( diff --git a/bot/exts/evergreen/connect_four.py b/bot/exts/evergreen/connect_four.py index 929a15d8..dc365a70 100644 --- a/bot/exts/evergreen/connect_four.py +++ b/bot/exts/evergreen/connect_four.py @@ -277,8 +277,10 @@ class ConnectFour(commands.Cog): return False if not self.min_board_size <= board_size <= self.max_board_size: - await ctx.send(f"{board_size} is not a valid board size. A valid board size is " - f"between `{self.min_board_size}` and `{self.max_board_size}`.") + await ctx.send( + f"{board_size} is not a valid board size. A valid board size is " + f"between `{self.min_board_size}` and `{self.max_board_size}`." + ) return False return True diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index 4da33259..6f01d81c 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -335,13 +335,14 @@ class Games(Cog): return await ctx.send("Successfully refreshed genres.") - async def get_games_list(self, - amount: int, - genre: Optional[str] = None, - sort: Optional[str] = None, - additional_body: str = "", - offset: int = 0 - ) -> List[Dict[str, Any]]: + async def get_games_list( + self, + amount: int, + genre: Optional[str] = None, + sort: Optional[str] = None, + additional_body: str = "", + offset: int = 0 + ) -> List[Dict[str, Any]]: """ Get list of games from IGDB API by parameters that is provided. diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index c0086c20..e162c9bd 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -18,11 +18,15 @@ class PythonFacts(commands.Cog): @commands.command(name="pythonfact", aliases=["pyfact"]) async def get_python_fact(self, ctx: commands.Context) -> None: """Sends a Random fun fact about Python.""" - embed = discord.Embed(title="Python Facts", - description=next(FACTS), - colour=next(COLORS)) - embed.add_field(name="Suggestions", - value="Suggest more facts [here!](https://github.com/python-discord/meta/discussions/93)") + embed = discord.Embed( + title="Python Facts", + description=next(FACTS), + colour=next(COLORS) + ) + embed.add_field( + name="Suggestions", + value="Suggest more facts [here!](https://github.com/python-discord/meta/discussions/93)" + ) await ctx.send(embed=embed) diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index 62795aef..6278c883 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -497,9 +497,11 @@ class Snakes(Cog): for i in range(0, 10): page_guess_list.append(f"{HOLE_EMOJI} {HOLE_EMOJI} {HOLE_EMOJI} {HOLE_EMOJI}") page_result_list.append(f"{CROSS_EMOJI} {CROSS_EMOJI} {CROSS_EMOJI} {CROSS_EMOJI}") - board.append(f"`{i+1:02d}` " - f"{page_guess_list[i]} - " - f"{page_result_list[i]}") + board.append( + f"`{i+1:02d}` " + f"{page_guess_list[i]} - " + f"{page_result_list[i]}" + ) board.append(EMPTY_UNICODE) antidote_embed.add_field(name="10 guesses remaining", value="\n".join(board)) board_id = await ctx.send(embed=antidote_embed) # Display board @@ -667,8 +669,14 @@ class Snakes(Cog): ) 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) + + _iter = ( + url + for url in data["image_list"] + if url.endswith(self.valid_image_extensions) + ) + image = next(_iter, emoji) + embed.set_image(url=image) await ctx.send(embed=embed) @@ -693,8 +701,12 @@ class Snakes(Cog): data = await self._get_snek(snake) - image = next((url for url in data["image_list"] - if url.endswith(self.valid_image_extensions)), None) + _iter = ( + url + for url in data["image_list"] + if url.endswith(self.valid_image_extensions) + ) + image = next(_iter, None) embed = Embed( title="Which of the following is the snake in the image?", diff --git a/bot/exts/evergreen/snakes/_utils.py b/bot/exts/evergreen/snakes/_utils.py index d58ee279..998c13a9 100644 --- a/bot/exts/evergreen/snakes/_utils.py +++ b/bot/exts/evergreen/snakes/_utils.py @@ -191,8 +191,11 @@ class PerlinNoiseFactory(object): def get_plain_noise(self, *point) -> float: """Get plain noise for a single point, without taking into account either octaves or tiling.""" if len(point) != self.dimension: - raise ValueError("Expected {0} values, got {1}".format( - self.dimension, len(point))) + raise ValueError( + "Expected {0} values, got {1}".format( + self.dimension, len(point) + ) + ) # Build a list of the (min, max) bounds in each dimension grid_coords = [] -- cgit v1.2.3 From 0017b76e914e4963a8556ac768e2b4a176111636 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Fri, 7 May 2021 13:45:57 -0400 Subject: chore: Apply anand's suggested changes --- bot/exts/pride/pride_facts.py | 2 +- bot/exts/valentines/be_my_valentine.py | 5 ++--- bot/exts/valentines/lovecalculator.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/bot/exts/pride/pride_facts.py b/bot/exts/pride/pride_facts.py index b2daaab7..6d06cf64 100644 --- a/bot/exts/pride/pride_facts.py +++ b/bot/exts/pride/pride_facts.py @@ -101,5 +101,5 @@ class PrideFacts(commands.Cog): def setup(bot: Bot) -> None: - """Cog loader for pride facts.""" + """Load the Pride Facts Cog.""" bot.add_cog(PrideFacts(bot)) diff --git a/bot/exts/valentines/be_my_valentine.py b/bot/exts/valentines/be_my_valentine.py index cf6099d2..1ce92203 100644 --- a/bot/exts/valentines/be_my_valentine.py +++ b/bot/exts/valentines/be_my_valentine.py @@ -6,7 +6,6 @@ from typing import Tuple import discord from discord.ext import commands -from discord.ext.commands.cooldowns import BucketType from bot.bot import Bot from bot.constants import Channels, Colours, Lovefest, Month @@ -69,7 +68,7 @@ class BeMyValentine(commands.Cog): await user.remove_roles(role) await ctx.send("The lovefest role has been successfully removed!") - @commands.cooldown(1, 1800, BucketType.user) + @commands.cooldown(1, 1800, commands.BucketType.user) @commands.group(name="bemyvalentine", invoke_without_command=True) async def send_valentine( self, ctx: commands.Context, user: discord.Member, *, valentine_type: str = None @@ -108,7 +107,7 @@ class BeMyValentine(commands.Cog): ) await channel.send(user.mention, embed=embed) - @commands.cooldown(1, 1800, BucketType.user) + @commands.cooldown(1, 1800, commands.BucketType.user) @send_valentine.command(name="secret") async def anonymous( self, ctx: commands.Context, user: discord.Member, *, valentine_type: str = None diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index 9096d994..8a4c71eb 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -61,7 +61,7 @@ class LoveCalculator(Cog): # Make sure user didn't provide something silly such as 10 spaces if not (who and whom): - raise BadArgument("Arguments be non-empty strings.") + raise BadArgument("Arguments must be non-empty strings.") # Hash inputs to guarantee consistent results (hashing algorithm choice arbitrary) # -- cgit v1.2.3 From ae3ecb3ac46d90ebab1cb556fdc357848f45abb5 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 7 May 2021 13:47:43 -0400 Subject: chore: Simplify the if statement Co-authored-by: Anand Krishna <40204976+anand2312@users.noreply.github.com> --- bot/exts/valentines/be_my_valentine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/valentines/be_my_valentine.py b/bot/exts/valentines/be_my_valentine.py index 1ce92203..b9cf6738 100644 --- a/bot/exts/valentines/be_my_valentine.py +++ b/bot/exts/valentines/be_my_valentine.py @@ -51,7 +51,7 @@ class BeMyValentine(commands.Cog): """Adds the lovefest role.""" user = ctx.author role = ctx.guild.get_role(Lovefest.role_id) - if Lovefest.role_id not in [role.id for role in ctx.message.author.roles]: + if role not in ctx.author.roles: await user.add_roles(role) await ctx.send("The Lovefest role has been added !") else: -- cgit v1.2.3 From 92dd10e62dbea4cbed9d12c239ca9f666f69f90a Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Fri, 7 May 2021 17:51:25 -0300 Subject: fixed weird 8-space indents on lines 556-558 --- bot/exts/evergreen/trivia_quiz.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index d024f856..5553898d 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -553,9 +553,9 @@ class TriviaQuiz(commands.Cog): @staticmethod async def send_answer( - channel: discord.TextChannel, - answers: List[str], - question_dict: dict, + channel: discord.TextChannel, + answers: List[str], + question_dict: dict, ) -> None: """Send the correct answer of a question to the game channel.""" if "info" in question_dict: -- cgit v1.2.3 From 1854cd45dc4b79bed492a95bd00bcadf8ff116af Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 14:00:08 -0400 Subject: use random.sample for more concise expression on line 66 Co-authored-by: Anand Krishna <40204976+anand2312@users.noreply.github.com> --- bot/exts/evergreen/trivia_quiz.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 5553898d..620a4377 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -63,8 +63,7 @@ def linear_system(q_format: str, a_format: str) -> Tuple[str, str]: x, y = random.randint(2, 5), random.randint(2, 5) answer = a_format.format(x, y) - nums = [i for i in range(1, 6)] - coeffs = [nums.pop(random.randint(0, len(nums) - 1)) for _ in range(4)] + coeffs = random.sample(range(1, 6), 4) question = q_format.format( coeffs[0], -- cgit v1.2.3 From 961e3f788ce4d99782418ec9bc4d33998af42124 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 16:31:32 -0400 Subject: use dict.get for retrieving a default value instead of a condition Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/evergreen/trivia_quiz.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 620a4377..6f39d9f2 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -557,10 +557,7 @@ class TriviaQuiz(commands.Cog): question_dict: dict, ) -> None: """Send the correct answer of a question to the game channel.""" - if "info" in question_dict: - info = question_dict["info"] - else: - info = "" + info = question_dict.get("info", "") embed = discord.Embed( color=discord.Colour.red(), -- cgit v1.2.3 From c57a8557f0f52ab35c9ef79c6894bad04f73c8d2 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 17:36:27 -0300 Subject: use `operator` module instead of lambda functions --- bot/exts/evergreen/trivia_quiz.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 6f39d9f2..10237be9 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -2,6 +2,7 @@ import asyncio import json import logging import random +import operator from pathlib import Path from typing import List, Optional, Tuple @@ -119,9 +120,9 @@ def binary_calc(q_format: str, a_format: str) -> Tuple[str, str]: b = random.randint(10, a) oper = random.choice( ( - ("+", lambda x, y: x + y), - ("-", lambda x, y: x - y), - ("*", lambda x, y: x * y), + ("+", operator.add), + ("-", operator.sub), + ("*", operator.mul), ) ) -- cgit v1.2.3 From bfc7c4335a4e7156f877607b62491b7aaf2faa58 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 17:37:42 -0300 Subject: lint failure fix, imported in alphabetical order --- bot/exts/evergreen/trivia_quiz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 10237be9..7a498ebd 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -1,8 +1,8 @@ import asyncio import json import logging -import random import operator +import random from pathlib import Path from typing import List, Optional, Tuple -- cgit v1.2.3 From 66480996e0a227506c01260e8899ea4256db6eb9 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 17:41:34 -0300 Subject: use binary formatting instead of `bin()` in function --- bot/resources/evergreen/trivia_quiz.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index ce33e8c9..79e79d00 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -423,8 +423,8 @@ { "id": 226, "dynamic_id": 205, - "question": "BASE TWO QUESTION: Calculate {} {} {}", - "answer": "{}" + "question": "BASE TWO QUESTION: Calculate {:b} {} {:b}", + "answer": "{:b}" }, { "id": 227, -- cgit v1.2.3 From 8b0eb4aa07fbc9c20f7748888d0db00f2264b774 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 17:43:33 -0300 Subject: delete `bin()`s --- bot/exts/evergreen/trivia_quiz.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 7a498ebd..03aadd19 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -131,12 +131,12 @@ def binary_calc(q_format: str, a_format: str) -> Tuple[str, str]: b -= 5 question = q_format.format( - bin(a)[2:], + a, oper[0], - bin(b)[2:], + b, ) answer = a_format.format( - bin(oper[1](a, b))[2:] + oper[1](a, b) ) return question, answer -- cgit v1.2.3 From f8e5ec95028f8a73cf0e9d96356696a047001c41 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 16:45:30 -0400 Subject: add space at the end of string on line 435 Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/evergreen/trivia_quiz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 03aadd19..1e1c973c 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -432,7 +432,7 @@ class TriviaQuiz(commands.Cog): start_embed.set_footer( text=( - "Points for each question reduces by 25 after 10s or after a hint." + "Points for each question reduces by 25 after 10s or after a hint. " "Total time is 30s per question" ) ) -- cgit v1.2.3 From 9d5ea922a5a9d045b15610a66ebc07cd5b4424b1 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 16:47:30 -0400 Subject: remove redundant `description` kwarg pass Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/evergreen/trivia_quiz.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 1e1c973c..d3def4f2 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -563,7 +563,6 @@ class TriviaQuiz(commands.Cog): embed = discord.Embed( color=discord.Colour.red(), title=f"The correct answer is/are **`{', '.join(answers)}`**\n", - description="", ) if info != "": -- cgit v1.2.3 From af709306d716d2d1c19c288cec08a3fa74a20a5c Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 16:48:47 -0400 Subject: use walrus so dict lookup is only performed once Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/evergreen/trivia_quiz.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index d3def4f2..88c44971 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -336,8 +336,8 @@ class TriviaQuiz(commands.Cog): description=q, ) - if "img_url" in question_dict: - embed.set_image(url=question_dict["img_url"]) + if img_url := question_dict.get("image_url"): + embed.set_image(url=img_url) await ctx.send(embed=embed) # Send question embed. -- cgit v1.2.3 From 17aee2141770085bc66c04d948249de954359f6a Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 17:53:01 -0300 Subject: changed error embed color to `bot.constants.Colours.soft_red` --- bot/exts/evergreen/trivia_quiz.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 88c44971..4496082e 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -10,6 +10,7 @@ import discord from discord.ext import commands from fuzzywuzzy import fuzz +from bot.constants import Colours from bot.constants import NEGATIVE_REPLIES from bot.constants import Roles @@ -443,7 +444,7 @@ class TriviaQuiz(commands.Cog): def make_error_embed(desc: str) -> discord.Embed: """Generate an error embed with the given description.""" error_embed = discord.Embed( - colour=discord.Colour.red(), + colour=Colours.soft_red, title=random.choice(NEGATIVE_REPLIES), description=desc, ) -- cgit v1.2.3 From 84f2b8783a28e2d90d601cbab33c6c3a439040bf Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 18:05:01 -0300 Subject: add a space at end of string on line 245 --- bot/exts/evergreen/trivia_quiz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 4496082e..2bd8f7d4 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -242,7 +242,7 @@ class TriviaQuiz(commands.Cog): # Stop game if running. if self.game_status[ctx.channel.id] is True: await ctx.send( - "Game is already running..." + "Game is already running... " f"do `{self.bot.command_prefix}quiz stop`" ) -- cgit v1.2.3 From fb556c6407bab3ef078bc23620074ba206fb7ad8 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela Date: Mon, 10 May 2021 00:27:25 +0300 Subject: Fixes Expected Token Revoke Status Code --- bot/exts/evergreen/reddit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index 916563ac..e57fa2c0 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -174,7 +174,7 @@ class Reddit(Cog): } ) - if response.status == 204 and response.content_type == "application/json": + if response.status in [200, 204] and response.content_type == "application/json": self.access_token = None else: log.warning(f"Unable to revoke access token: status {response.status}.") -- cgit v1.2.3 From 21282d531208a613083362a2e5b22aebb8c53573 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 18:35:58 -0300 Subject: reverted all undesired black formatting changes --- bot/exts/evergreen/trivia_quiz.py | 47 +++++++++++---------------------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 2bd8f7d4..79cf1318 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -224,13 +224,11 @@ class TriviaQuiz(commands.Cog): async def quiz_game(self, ctx: commands.Context, category: Optional[str], questions: Optional[int]) -> None: """ Start a quiz! - Questions for the quiz can be selected from the following categories: - general: Test your general knowledge. (default) - retro: Questions related to retro gaming. - math: General questions about mathematics ranging from grade 8 to grade 12. - science: Put your understanding of science to the test! - (More to come!) """ if ctx.channel.id not in self.game_status: @@ -299,9 +297,7 @@ class TriviaQuiz(commands.Cog): # Exit quiz if number of questions for a round are already sent. if len(done_question) > self.question_limit and hint_no == 0: await ctx.send("The round has ended.") - await self.declare_winner( - ctx.channel, self.game_player_scores[ctx.channel.id] - ) + await self.declare_winner(ctx.channel, self.game_player_scores[ctx.channel.id]) self.game_status[ctx.channel.id] = False del self.game_owners[ctx.channel.id] @@ -319,12 +315,12 @@ class TriviaQuiz(commands.Cog): break if "dynamic_id" not in question_dict: - q = question_dict["question"] + question = question_dict["question"] answers = question_dict["answer"].split(", ") else: - q, answers = DYNAMIC_QUESTIONS_FORMAT_FUNCS[ - question_dict["dynamic_id"] - ]( + format_func = DYNAMIC_QUESTIONS_FORMAT_FUNCS[question_dict["dynamic_id"]] + + question, answers = format_func( question_dict["question"], question_dict["answer"], ) @@ -334,7 +330,7 @@ class TriviaQuiz(commands.Cog): embed = discord.Embed( colour=discord.Colour.gold(), title=f"Question #{len(done_question)}", - description=q, + description=question, ) if img_url := question_dict.get("image_url"): @@ -363,10 +359,7 @@ class TriviaQuiz(commands.Cog): if "hints" in question_dict: hints = question_dict["hints"] - await ctx.send( - f"**Hint #{hint_no}\n**" - f"{hints[hint_no - 1]}" - ) + await ctx.send(f"**Hint #{hint_no}\n**{hints[hint_no - 1]}") else: await ctx.send(f"{30 - hint_no * 10}s left!") @@ -384,10 +377,7 @@ class TriviaQuiz(commands.Cog): hint_no = 0 # init hint_no = 0 so that 2 hints/time alerts can be sent for the new question. - await self.send_score( - ctx.channel, self.game_player_scores[ctx.channel.id] - ) - + await self.send_score(ctx.channel, self.game_player_scores[ctx.channel.id]) await asyncio.sleep(2) else: if self.game_status[ctx.channel.id] is False: @@ -408,14 +398,10 @@ class TriviaQuiz(commands.Cog): hint_no = 0 - await ctx.send( - f"{msg.author.mention} got the correct answer :tada: {points} points!" - ) + await ctx.send(f"{msg.author.mention} got the correct answer :tada: {points} points!") await self.send_answer(ctx.channel, answers, question_dict) - await self.send_score( - ctx.channel, self.game_player_scores[ctx.channel.id] - ) + await self.send_score(ctx.channel, self.game_player_scores[ctx.channel.id]) await asyncio.sleep(2) @@ -455,7 +441,6 @@ class TriviaQuiz(commands.Cog): async def stop_quiz(self, ctx: commands.Context) -> None: """ Stop a quiz game if its running in the channel. - Note: Only mods or the owner of the quiz can stop it. """ if self.game_status[ctx.channel.id] is True: @@ -465,18 +450,14 @@ class TriviaQuiz(commands.Cog): ): await ctx.send("Quiz stopped.") - await self.declare_winner( - ctx.channel, self.game_player_scores[ctx.channel.id] - ) + await self.declare_winner(ctx.channel, self.game_player_scores[ctx.channel.id]) self.game_status[ctx.channel.id] = False del self.game_owners[ctx.channel.id] self.game_player_scores[ctx.channel.id] = {} else: - await ctx.send( - f"{ctx.author.mention}, you are not authorised to stop this game :ghost:!" - ) + await ctx.send(f"{ctx.author.mention}, you are not authorised to stop this game :ghost:!") else: await ctx.send("No quiz running.") @@ -540,9 +521,7 @@ class TriviaQuiz(commands.Cog): description="", ) - embed.set_footer( - text="If a category is not chosen, a random one will be selected." - ) + embed.set_footer(text="If a category is not chosen, a random one will be selected.") for cat, description in self.categories.items(): embed.description += ( -- cgit v1.2.3 From 675d2a03ececa926b5e3f7c129afb3f51b2a8213 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 18:40:49 -0300 Subject: fix lint failure; add a newline at line 444 --- bot/exts/evergreen/trivia_quiz.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 79cf1318..f20a21f5 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -441,6 +441,7 @@ class TriviaQuiz(commands.Cog): async def stop_quiz(self, ctx: commands.Context) -> None: """ Stop a quiz game if its running in the channel. + Note: Only mods or the owner of the quiz can stop it. """ if self.game_status[ctx.channel.id] is True: -- cgit v1.2.3 From 2cd8c03c9d9daf410624589f8a94dfe4348c063c Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 18:42:43 -0300 Subject: fix another linting fail regarding newlines in docstrings --- bot/exts/evergreen/trivia_quiz.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index f20a21f5..60dc2d0c 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -224,11 +224,13 @@ class TriviaQuiz(commands.Cog): async def quiz_game(self, ctx: commands.Context, category: Optional[str], questions: Optional[int]) -> None: """ Start a quiz! + Questions for the quiz can be selected from the following categories: - general: Test your general knowledge. (default) - retro: Questions related to retro gaming. - math: General questions about mathematics ranging from grade 8 to grade 12. - science: Put your understanding of science to the test! + (More to come!) """ if ctx.channel.id not in self.game_status: -- cgit v1.2.3 From 9c1dc83992cddca8aaa59d38afc55b1b8df34143 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 18:46:22 -0300 Subject: docstring tweaks; consistency and imperative mood --- bot/exts/evergreen/trivia_quiz.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 60dc2d0c..9d6e695f 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -61,7 +61,7 @@ UNITS_TO_BASE_UNITS = { def linear_system(q_format: str, a_format: str) -> Tuple[str, str]: - """Generates a system of linear equations with two unknowns.""" + """Generate a system of linear equations with two unknowns.""" x, y = random.randint(2, 5), random.randint(2, 5) answer = a_format.format(x, y) @@ -106,7 +106,7 @@ def ngonal_prism(q_format: str, a_format: str) -> Tuple[str, str]: def imag_sqrt(q_format: str, a_format: str) -> Tuple[str, str]: - """Generates a negative square root question.""" + """Generate a negative square root question.""" ans_coeff = random.randint(3, 10) question = q_format.format(ans_coeff ** 2) @@ -116,7 +116,7 @@ def imag_sqrt(q_format: str, a_format: str) -> Tuple[str, str]: def binary_calc(q_format: str, a_format: str) -> Tuple[str, str]: - """Generates a binary calculation question.""" + """Generate a binary calculation question.""" a = random.randint(15, 20) b = random.randint(10, a) oper = random.choice( @@ -144,7 +144,7 @@ def binary_calc(q_format: str, a_format: str) -> Tuple[str, str]: def solar_system(q_format: str, a_format: str) -> Tuple[str, str]: - """Generates a question on the planets of the Solar System.""" + """Generate a question on the planets of the Solar System.""" planet = random.choice(PLANETS) question = q_format.format(planet[0]) @@ -154,7 +154,7 @@ def solar_system(q_format: str, a_format: str) -> Tuple[str, str]: def taxonomic_rank(q_format: str, a_format: str) -> Tuple[str, str]: - """Generates a question on taxonomic classification.""" + """Generate a question on taxonomic classification.""" level = random.randint(0, len(TAXONOMIC_HIERARCHY) - 2) question = q_format.format(TAXONOMIC_HIERARCHY[level]) @@ -164,7 +164,7 @@ def taxonomic_rank(q_format: str, a_format: str) -> Tuple[str, str]: def base_units_convert(q_format: str, a_format: str) -> Tuple[str, str]: - """Generates a SI base units conversion question.""" + """Generate a SI base units conversion question.""" unit = random.choice(list(UNITS_TO_BASE_UNITS.keys())) question = q_format.format( @@ -556,5 +556,5 @@ class TriviaQuiz(commands.Cog): def setup(bot: commands.Bot) -> None: - """Load the cog.""" + """Load the TriviaQuiz cog.""" bot.add_cog(TriviaQuiz(bot)) -- cgit v1.2.3 From 4503a3307b989b251826f36e9370bda7471640b3 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Sun, 9 May 2021 17:49:53 -0400 Subject: fix: Use str.isdecimal instead of str.isdigit --- bot/utils/__init__.py | 4 ++-- bot/utils/converters.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py index 2fac2086..09a4dfc3 100644 --- a/bot/utils/__init__.py +++ b/bot/utils/__init__.py @@ -51,7 +51,7 @@ async def disambiguate( choices = (f"{index}: {entry}" for index, entry in enumerate(entries, start=1)) def check(message: discord.Message) -> bool: - return (message.content.isdigit() + return (message.content.isdecimal() and message.author == ctx.author and message.channel == ctx.channel) @@ -87,7 +87,7 @@ async def disambiguate( except asyncio.TimeoutError: raise BadArgument("Timed out.") - # Guaranteed to not error because of isdigit() in check + # Guaranteed to not error because of isdecimal() in check index = int(result.content) try: diff --git a/bot/utils/converters.py b/bot/utils/converters.py index 98607087..72b64848 100644 --- a/bot/utils/converters.py +++ b/bot/utils/converters.py @@ -37,7 +37,7 @@ class CoordinateConverter(commands.Converter): digit = coordinate[:-1] letter = coordinate[-1] - if not digit.isdigit(): + if not digit.isdecimal(): raise commands.BadArgument x = ord(letter) - ord("a") @@ -76,7 +76,7 @@ class DateConverter(commands.Converter): @staticmethod async def convert(ctx: commands.Context, argument: str) -> Union[int, datetime]: """Parse date (SOL or earth) into `datetime` or `int`. When invalid value, raise error.""" - if argument.isdigit(): + if argument.isdecimal(): return int(argument) try: date = datetime.strptime(argument, "%Y-%m-%d") -- cgit v1.2.3 From cb49ea232cd9d4d3c54637b386b89dd0f1b21a90 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 20:05:31 -0300 Subject: did some changes per the new reviews and suggestions implemented all suggested changes from vcokltfre and almost all from decorator-factory --- bot/exts/evergreen/trivia_quiz.py | 49 ++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 9d6e695f..f2dfe6a9 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -16,6 +16,8 @@ from bot.constants import Roles logger = logging.getLogger(__name__) +QAndA = Tuple[str, str] + VARIATION_TOLERANCE = 83 WRONG_ANS_RESPONSE = [ @@ -60,7 +62,7 @@ UNITS_TO_BASE_UNITS = { } -def linear_system(q_format: str, a_format: str) -> Tuple[str, str]: +def linear_system(q_format: str, a_format: str) -> QAndA: """Generate a system of linear equations with two unknowns.""" x, y = random.randint(2, 5), random.randint(2, 5) answer = a_format.format(x, y) @@ -83,7 +85,7 @@ def linear_system(q_format: str, a_format: str) -> Tuple[str, str]: return question, answer -def mod_arith(q_format: str, a_format: str) -> Tuple[str, str]: +def mod_arith(q_format: str, a_format: str) -> QAndA: """Generate a basic modular arithmetic question.""" quotient, m, b = random.randint(30, 40), random.randint(10, 20), random.randint(200, 350) ans = random.randint(0, 9) # max 9 because min mod 10 @@ -95,7 +97,7 @@ def mod_arith(q_format: str, a_format: str) -> Tuple[str, str]: return question, answer -def ngonal_prism(q_format: str, a_format: str) -> Tuple[str, str]: +def ngonal_prism(q_format: str, a_format: str) -> QAndA: """Generate a question regarding vertices on n-gonal prisms.""" n = random.randint(0, len(N_PREFIXES) - 1) @@ -105,7 +107,7 @@ def ngonal_prism(q_format: str, a_format: str) -> Tuple[str, str]: return question, answer -def imag_sqrt(q_format: str, a_format: str) -> Tuple[str, str]: +def imag_sqrt(q_format: str, a_format: str) -> QAndA: """Generate a negative square root question.""" ans_coeff = random.randint(3, 10) @@ -115,7 +117,7 @@ def imag_sqrt(q_format: str, a_format: str) -> Tuple[str, str]: return question, answer -def binary_calc(q_format: str, a_format: str) -> Tuple[str, str]: +def binary_calc(q_format: str, a_format: str) -> QAndA: """Generate a binary calculation question.""" a = random.randint(15, 20) b = random.randint(10, a) @@ -136,14 +138,12 @@ def binary_calc(q_format: str, a_format: str) -> Tuple[str, str]: oper[0], b, ) - answer = a_format.format( - oper[1](a, b) - ) + answer = a_format.format(oper[1](a, b)) return question, answer -def solar_system(q_format: str, a_format: str) -> Tuple[str, str]: +def solar_system(q_format: str, a_format: str) -> QAndA: """Generate a question on the planets of the Solar System.""" planet = random.choice(PLANETS) @@ -153,7 +153,7 @@ def solar_system(q_format: str, a_format: str) -> Tuple[str, str]: return question, answer -def taxonomic_rank(q_format: str, a_format: str) -> Tuple[str, str]: +def taxonomic_rank(q_format: str, a_format: str) -> QAndA: """Generate a question on taxonomic classification.""" level = random.randint(0, len(TAXONOMIC_HIERARCHY) - 2) @@ -163,7 +163,7 @@ def taxonomic_rank(q_format: str, a_format: str) -> Tuple[str, str]: return question, answer -def base_units_convert(q_format: str, a_format: str) -> Tuple[str, str]: +def base_units_convert(q_format: str, a_format: str) -> QAndA: """Generate a SI base units conversion question.""" unit = random.choice(list(UNITS_TO_BASE_UNITS.keys())) @@ -216,9 +216,7 @@ class TriviaQuiz(commands.Cog): """Load the questions from the JSON file.""" p = Path("bot", "resources", "evergreen", "trivia_quiz.json") - with p.open(encoding="utf8") as json_data: - questions = json.load(json_data) - return questions + return json.loads(p.read_text()) @commands.group(name="quiz", aliases=["trivia"], invoke_without_command=True) async def quiz_game(self, ctx: commands.Context, category: Optional[str], questions: Optional[int]) -> None: @@ -274,7 +272,7 @@ class TriviaQuiz(commands.Cog): elif questions < 1: await ctx.send( embed=self.make_error_embed( - "Please do at least one question." + "You must choose to complete at least one question." ) ) return @@ -283,7 +281,7 @@ class TriviaQuiz(commands.Cog): self.question_limit = questions - 1 # Start game if not running. - if self.game_status[ctx.channel.id] is False: + if not self.game_status[ctx.channel.id]: self.game_owners[ctx.channel.id] = ctx.author self.game_status[ctx.channel.id] = True start_embed = self.make_start_embed(category) @@ -338,23 +336,21 @@ class TriviaQuiz(commands.Cog): if img_url := question_dict.get("image_url"): embed.set_image(url=img_url) - await ctx.send(embed=embed) # Send question embed. + await ctx.send(embed=embed) - # A function to check whether user input is the correct answer(close to the right answer) - def check(m: discord.Message) -> bool: + def contains_correct_answer(m: discord.Message) -> bool: return m.channel == ctx.channel and any( fuzz.ratio(answer.lower(), m.content.lower()) > VARIATION_TOLERANCE for answer in answers ) try: - msg = await self.bot.wait_for("message", check=check, timeout=10) + msg = await self.bot.wait_for("message", check=contains_correct_answer, timeout=10) except asyncio.TimeoutError: # In case of TimeoutError and the game has been stopped, then do nothing. - if self.game_status[ctx.channel.id] is False: + if not self.game_status[ctx.channel.id]: break - # if number of hints sent or time alerts sent is less than 2, then send one. if hint_no < 2: hint_no += 1 @@ -385,7 +381,6 @@ class TriviaQuiz(commands.Cog): if self.game_status[ctx.channel.id] is False: break - # Reduce points by 25 for every hint/time alert that has been sent. points = 100 - 25 * hint_no if msg.author in self.game_player_scores[ctx.channel.id]: self.game_player_scores[ctx.channel.id][msg.author] += points @@ -471,13 +466,13 @@ class TriviaQuiz(commands.Cog): @staticmethod async def send_score(channel: discord.TextChannel, player_data: dict) -> None: - """A function which sends the score.""" + """Send the score.""" if len(player_data) == 0: await channel.send("No one has made it onto the leaderboard yet.") return embed = discord.Embed( - colour=discord.Colour.blue(), + colour=Colours.blue, title="Score Board", description="", ) @@ -519,7 +514,7 @@ class TriviaQuiz(commands.Cog): def category_embed(self) -> discord.Embed: """Build an embed showing all available trivia categories.""" embed = discord.Embed( - colour=discord.Colour.blue(), + colour=Colours.blue, title="The available question categories are:", description="", ) @@ -544,7 +539,7 @@ class TriviaQuiz(commands.Cog): info = question_dict.get("info", "") embed = discord.Embed( - color=discord.Colour.red(), + color=Colours.bright_green, title=f"The correct answer is/are **`{', '.join(answers)}`**\n", ) -- cgit v1.2.3 From 1f0231d725deb44b3d1ab603448680228b1942a2 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 20:13:53 -0300 Subject: add gold colour --- bot/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/constants.py b/bot/constants.py index 6cbfa8db..884cf3a8 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -151,6 +151,7 @@ class Colours: python_blue = 0x4B8BBE python_yellow = 0xFFD43B grass_green = 0x66ff00 + gold = 0xE6C200 easter_like_colours = [ (255, 247, 0), -- cgit v1.2.3 From a3cf147787058832740316df03a358e3f1039bc9 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 20:15:45 -0300 Subject: use gold colour from constants.py for consistency --- bot/exts/evergreen/trivia_quiz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index f2dfe6a9..31d9dbce 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -328,7 +328,7 @@ class TriviaQuiz(commands.Cog): answers = [answers] embed = discord.Embed( - colour=discord.Colour.gold(), + colour=Colours.gold, title=f"Question #{len(done_question)}", description=question, ) -- cgit v1.2.3 From 5320a38afe1810ccb445f61e74a7facf5b0e9e5a Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 20:45:13 -0300 Subject: minor tweaks as suggested by vcokltfre see their five change requests for details --- bot/exts/evergreen/trivia_quiz.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 31d9dbce..51e24226 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -88,7 +88,7 @@ def linear_system(q_format: str, a_format: str) -> QAndA: def mod_arith(q_format: str, a_format: str) -> QAndA: """Generate a basic modular arithmetic question.""" quotient, m, b = random.randint(30, 40), random.randint(10, 20), random.randint(200, 350) - ans = random.randint(0, 9) # max 9 because min mod 10 + ans = random.randint(0, 9) # max remainder is 9, since the minimum modulus is 10 a = quotient * m + ans - b question = q_format.format(a, b, m) @@ -133,11 +133,7 @@ def binary_calc(q_format: str, a_format: str) -> QAndA: a -= 5 b -= 5 - question = q_format.format( - a, - oper[0], - b, - ) + question = q_format.format(a, oper[0], b) answer = a_format.format(oper[1](a, b)) return question, answer @@ -165,7 +161,7 @@ def taxonomic_rank(q_format: str, a_format: str) -> QAndA: def base_units_convert(q_format: str, a_format: str) -> QAndA: """Generate a SI base units conversion question.""" - unit = random.choice(list(UNITS_TO_BASE_UNITS.keys())) + unit = random.choice(list(UNITS_TO_BASE_UNITS)) question = q_format.format( unit + " " + UNITS_TO_BASE_UNITS[unit][0] @@ -405,7 +401,7 @@ class TriviaQuiz(commands.Cog): def make_start_embed(self, category: str) -> discord.Embed: """Generate a starting/introduction embed for the quiz.""" start_embed = discord.Embed( - colour=discord.Colour.red(), + colour=Colours.blue, title="Quiz game starting!", description=( f"Each game consists of {self.question_limit + 1} questions.\n" @@ -538,9 +534,11 @@ class TriviaQuiz(commands.Cog): """Send the correct answer of a question to the game channel.""" info = question_dict.get("info", "") + word = "is" if len(answers) == 1 else "are" + embed = discord.Embed( color=Colours.bright_green, - title=f"The correct answer is/are **`{', '.join(answers)}`**\n", + title=f"The correct answer {word} **`{', '.join(answers)}`**\n", ) if info != "": -- cgit v1.2.3 From 7bbf7b18a5066b17475bcd2b8c1dc87417692f75 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 20:57:03 -0300 Subject: fixed no description embed error --- bot/exts/evergreen/trivia_quiz.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 51e24226..23aac5fa 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -539,6 +539,7 @@ class TriviaQuiz(commands.Cog): embed = discord.Embed( color=Colours.bright_green, title=f"The correct answer {word} **`{', '.join(answers)}`**\n", + description="", ) if info != "": -- cgit v1.2.3 From 2e58fca5d444e8eb8adf7eba1223daff2f5fc14c Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 21:09:23 -0300 Subject: reduce default no of questions to 7, add actual content to "Remaining questions: " --- bot/exts/evergreen/trivia_quiz.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 23aac5fa..8d1a27ba 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -195,7 +195,7 @@ class TriviaQuiz(commands.Cog): self.game_owners = {} # A variable to store the person's ID who started the quiz game in a channel. self.questions = self.load_questions() - self.question_limit = 7 + self.question_limit = 6 self.player_scores = {} # A variable to store all player's scores for a bot session. self.game_player_scores = {} # A variable to store temporary game player's scores. @@ -366,7 +366,7 @@ class TriviaQuiz(commands.Cog): response = random.choice(WRONG_ANS_RESPONSE) await ctx.send(response) - await self.send_answer(ctx.channel, answers, question_dict) + await self.send_answer(ctx.channel, answers, question_dict, len(done_question)) await asyncio.sleep(1) hint_no = 0 # init hint_no = 0 so that 2 hints/time alerts can be sent for the new question. @@ -393,7 +393,7 @@ class TriviaQuiz(commands.Cog): await ctx.send(f"{msg.author.mention} got the correct answer :tada: {points} points!") - await self.send_answer(ctx.channel, answers, question_dict) + await self.send_answer(ctx.channel, answers, question_dict, len(done_question)) await self.send_score(ctx.channel, self.game_player_scores[ctx.channel.id]) await asyncio.sleep(2) @@ -530,6 +530,7 @@ class TriviaQuiz(commands.Cog): channel: discord.TextChannel, answers: List[str], question_dict: dict, + q_left: int, ) -> None: """Send the correct answer of a question to the game channel.""" info = question_dict.get("info", "") @@ -545,7 +546,7 @@ class TriviaQuiz(commands.Cog): if info != "": embed.description += f"**Information**\n{info}\n\n" - embed.description += "Let's move to the next question.\nRemaining questions: " + embed.description += f"Let's move to the next question.\nRemaining questions: {q_left}" await channel.send(embed=embed) -- cgit v1.2.3 From 119f08411eeb9ec205b4678959feb270ff76548c Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 21:23:01 -0300 Subject: fix bug where remaining questions go up instead of down --- bot/exts/evergreen/trivia_quiz.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 8d1a27ba..5ec37b25 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -366,7 +366,7 @@ class TriviaQuiz(commands.Cog): response = random.choice(WRONG_ANS_RESPONSE) await ctx.send(response) - await self.send_answer(ctx.channel, answers, question_dict, len(done_question)) + await self.send_answer(ctx.channel, answers, question_dict, self.question_limit - len(done_question)) await asyncio.sleep(1) hint_no = 0 # init hint_no = 0 so that 2 hints/time alerts can be sent for the new question. @@ -393,7 +393,7 @@ class TriviaQuiz(commands.Cog): await ctx.send(f"{msg.author.mention} got the correct answer :tada: {points} points!") - await self.send_answer(ctx.channel, answers, question_dict, len(done_question)) + await self.send_answer(ctx.channel, answers, question_dict, self.question_limit - len(done_question)) await self.send_score(ctx.channel, self.game_player_scores[ctx.channel.id]) await asyncio.sleep(2) -- cgit v1.2.3 From f22cebc139e03cd965e5b1d470795126d02250fd Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 21:25:40 -0300 Subject: increase readability, fix lint failure --- bot/exts/evergreen/trivia_quiz.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 5ec37b25..43d786bc 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -366,7 +366,13 @@ class TriviaQuiz(commands.Cog): response = random.choice(WRONG_ANS_RESPONSE) await ctx.send(response) - await self.send_answer(ctx.channel, answers, question_dict, self.question_limit - len(done_question)) + + await self.send_answer( + ctx.channel, + answers, + question_dict, + self.question_limit - len(done_question) + ) await asyncio.sleep(1) hint_no = 0 # init hint_no = 0 so that 2 hints/time alerts can be sent for the new question. @@ -393,7 +399,12 @@ class TriviaQuiz(commands.Cog): await ctx.send(f"{msg.author.mention} got the correct answer :tada: {points} points!") - await self.send_answer(ctx.channel, answers, question_dict, self.question_limit - len(done_question)) + await self.send_answer( + ctx.channel, + answers, + question_dict, + self.question_limit - len(done_question) + ) await self.send_score(ctx.channel, self.game_player_scores[ctx.channel.id]) await asyncio.sleep(2) -- cgit v1.2.3 From d2e1d52c762b73e240dabdbee1004e73315a5b4e Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 21:31:10 -0300 Subject: forgot to increment by one, fixed --- bot/exts/evergreen/trivia_quiz.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 43d786bc..bd3b136e 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -371,7 +371,7 @@ class TriviaQuiz(commands.Cog): ctx.channel, answers, question_dict, - self.question_limit - len(done_question) + self.question_limit - len(done_question) + 1 ) await asyncio.sleep(1) @@ -403,7 +403,7 @@ class TriviaQuiz(commands.Cog): ctx.channel, answers, question_dict, - self.question_limit - len(done_question) + self.question_limit - len(done_question) + 1 ) await self.send_score(ctx.channel, self.game_player_scores[ctx.channel.id]) -- cgit v1.2.3 From 1012c8b7421b30cf80ff26798308f9ee1586e0f7 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Sun, 9 May 2021 21:50:33 -0300 Subject: final minor refinery as requested by vcokltfre --- bot/exts/evergreen/trivia_quiz.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index bd3b136e..56929fe7 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -370,8 +370,9 @@ class TriviaQuiz(commands.Cog): await self.send_answer( ctx.channel, answers, + False, question_dict, - self.question_limit - len(done_question) + 1 + self.question_limit - len(done_question) + 1, ) await asyncio.sleep(1) @@ -402,8 +403,9 @@ class TriviaQuiz(commands.Cog): await self.send_answer( ctx.channel, answers, + True, question_dict, - self.question_limit - len(done_question) + 1 + self.question_limit - len(done_question) + 1, ) await self.send_score(ctx.channel, self.game_player_scores[ctx.channel.id]) @@ -540,6 +542,7 @@ class TriviaQuiz(commands.Cog): async def send_answer( channel: discord.TextChannel, answers: List[str], + answer_is_correct: bool, question_dict: dict, q_left: int, ) -> None: @@ -550,14 +553,20 @@ class TriviaQuiz(commands.Cog): embed = discord.Embed( color=Colours.bright_green, - title=f"The correct answer {word} **`{', '.join(answers)}`**\n", + title=( + "You got it! " if answer_is_correct else "" + f"The correct answer {word} **`{', '.join(answers)}`**\n" + ), description="", ) if info != "": embed.description += f"**Information**\n{info}\n\n" - embed.description += f"Let's move to the next question.\nRemaining questions: {q_left}" + embed.description += ( + "Let's move to the next question." if q_left > 0 else "" + f"\nRemaining questions: {q_left}" + ) await channel.send(embed=embed) -- cgit v1.2.3 From d7e3937d019025578b09fed0bfc59bb0e174ed84 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Mon, 10 May 2021 08:43:40 -0400 Subject: Update bot/exts/evergreen/trivia_quiz.py Co-authored-by: Shivansh-007 --- bot/exts/evergreen/trivia_quiz.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 56929fe7..bd319910 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -72,9 +72,7 @@ def linear_system(q_format: str, a_format: str) -> QAndA: question = q_format.format( coeffs[0], coeffs[1], - ( - coeffs[0] * x + coeffs[1] * y - ), + coeffs[0] * x + coeffs[1] * y, coeffs[2], coeffs[3], ( -- cgit v1.2.3 From f8ea21fd364adb09f26b092aa4ef0cbbeeca0430 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Mon, 10 May 2021 08:44:16 -0400 Subject: remove redundant parentheses Co-authored-by: Shivansh-007 --- bot/exts/evergreen/trivia_quiz.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index bd319910..045fedbf 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -75,9 +75,7 @@ def linear_system(q_format: str, a_format: str) -> QAndA: coeffs[0] * x + coeffs[1] * y, coeffs[2], coeffs[3], - ( - coeffs[2] * x + coeffs[3] * y - ), + coeffs[2] * x + coeffs[3] * y, ) return question, answer -- cgit v1.2.3 From 5f9242749db72912abc7c52a8e1da90ca09c6848 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Mon, 10 May 2021 09:52:59 -0300 Subject: apply some changes suggested by Shivansh-007 made send_score docstring more precise, added a comment for better understanding, and changed an error embed description --- bot/exts/evergreen/trivia_quiz.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 045fedbf..2620dea1 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -125,6 +125,7 @@ def binary_calc(q_format: str, a_format: str) -> QAndA: ) ) + # if the operator is multiplication, lower the values of the two operands to make it easier if oper[0] == "*": a -= 5 b -= 5 @@ -264,7 +265,8 @@ class TriviaQuiz(commands.Cog): elif questions < 1: await ctx.send( embed=self.make_error_embed( - "You must choose to complete at least one question." + "You must choose to complete at least one question. " + "(or enter 0 for the default value of 7 questions)" ) ) return @@ -471,7 +473,7 @@ class TriviaQuiz(commands.Cog): @staticmethod async def send_score(channel: discord.TextChannel, player_data: dict) -> None: - """Send the score.""" + """Send the current scores of players in the game channel.""" if len(player_data) == 0: await channel.send("No one has made it onto the leaderboard yet.") return -- cgit v1.2.3 From 4ba9c941fc8ceb50cf61cfcb5ae15afc6216c7b2 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Mon, 10 May 2021 09:28:43 -0400 Subject: chore: Apply more suggested changes --- bot/exts/easter/bunny_name_generator.py | 1 + bot/exts/evergreen/help.py | 12 +++++++++--- bot/exts/evergreen/snakes/_utils.py | 4 +--- bot/exts/valentines/be_my_valentine.py | 18 ++++++------------ 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/bot/exts/easter/bunny_name_generator.py b/bot/exts/easter/bunny_name_generator.py index 19a0b0f6..b6523ff6 100644 --- a/bot/exts/easter/bunny_name_generator.py +++ b/bot/exts/easter/bunny_name_generator.py @@ -23,6 +23,7 @@ class BunnyNameGenerator(commands.Cog): new_name = re.split(r"[_.\s]", displayname) if displayname not in new_name: return new_name + return None def find_vowels(self, displayname: str) -> str: """ diff --git a/bot/exts/evergreen/help.py b/bot/exts/evergreen/help.py index bfaf25f1..7e3fdad4 100644 --- a/bot/exts/evergreen/help.py +++ b/bot/exts/evergreen/help.py @@ -2,9 +2,8 @@ import asyncio import itertools import logging -from collections import namedtuple from contextlib import suppress -from typing import Union +from typing import List, NamedTuple, Union from discord import Colour, Embed, HTTPException, Message, Reaction, User from discord.ext import commands @@ -29,7 +28,14 @@ REACTIONS = { DELETE_EMOJI: "stop", } -Cog = namedtuple("Cog", ["name", "description", "commands"]) + +class Cog(NamedTuple): + """Show information about a Cog's name, description and commands.""" + + name: str + description: str + commands: List[Command] + log = logging.getLogger(__name__) diff --git a/bot/exts/evergreen/snakes/_utils.py b/bot/exts/evergreen/snakes/_utils.py index 998c13a9..9b38ffa2 100644 --- a/bot/exts/evergreen/snakes/_utils.py +++ b/bot/exts/evergreen/snakes/_utils.py @@ -192,9 +192,7 @@ class PerlinNoiseFactory(object): """Get plain noise for a single point, without taking into account either octaves or tiling.""" if len(point) != self.dimension: raise ValueError( - "Expected {0} values, got {1}".format( - self.dimension, len(point) - ) + f"Expected {self.dimension} values, got {len(point)}" ) # Build a list of the (min, max) bounds in each dimension diff --git a/bot/exts/valentines/be_my_valentine.py b/bot/exts/valentines/be_my_valentine.py index b9cf6738..d1eea388 100644 --- a/bot/exts/valentines/be_my_valentine.py +++ b/bot/exts/valentines/be_my_valentine.py @@ -151,21 +151,17 @@ class BeMyValentine(commands.Cog): def valentine_check(self, valentine_type: str) -> Tuple[str, str]: """Return the appropriate Valentine type & title based on the invoking user's input.""" if valentine_type is None: - valentine, title = self.random_valentine() + return self.random_valentine() elif valentine_type.lower() in ["p", "poem"]: - valentine = self.valentine_poem() - title = "A poem dedicated to" + return self.valentine_poem(), "A poem dedicated to" elif valentine_type.lower() in ["c", "compliment"]: - valentine = self.valentine_compliment() - title = "A compliment for" + return self.valentine_compliment(), "A compliment for" else: # in this case, the user decides to type his own valentine. - valentine = valentine_type - title = "A message for" - return valentine, title + return valentine_type, "A message for" @staticmethod def random_emoji() -> Tuple[str, str]: @@ -187,13 +183,11 @@ class BeMyValentine(commands.Cog): def valentine_poem(self) -> str: """Grabs a random poem.""" - valentine_poem = random.choice(self.valentines["valentine_poems"]) - return valentine_poem + return random.choice(self.valentines["valentine_poems"]) def valentine_compliment(self) -> str: """Grabs a random compliment.""" - valentine_compliment = random.choice(self.valentines["valentine_compliments"]) - return valentine_compliment + return random.choice(self.valentines["valentine_compliments"]) def setup(bot: Bot) -> None: -- cgit v1.2.3 From 52aa6dcd499fc3e757a226c1192755888a6d88ef Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Mon, 10 May 2021 09:30:46 -0400 Subject: chore: ctx.message.author -> ctx.author --- bot/exts/easter/bunny_name_generator.py | 2 +- bot/exts/evergreen/snakes/_snakes_cog.py | 6 +++--- bot/exts/halloween/monsterbio.py | 2 +- bot/exts/internal_eval/_internal_eval.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/exts/easter/bunny_name_generator.py b/bot/exts/easter/bunny_name_generator.py index b6523ff6..5e3b014d 100644 --- a/bot/exts/easter/bunny_name_generator.py +++ b/bot/exts/easter/bunny_name_generator.py @@ -62,7 +62,7 @@ class BunnyNameGenerator(commands.Cog): @commands.command() async def bunnifyme(self, ctx: commands.Context) -> None: """Gets your Discord username and bunnifies it.""" - username = ctx.message.author.display_name + username = ctx.author.display_name # If name contains spaces or other separators, get the individual words to randomly bunnify spaces_in_name = self.find_separators(username) diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index 6278c883..b844960a 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -746,7 +746,7 @@ class Snakes(Cog): my_snake_embed = Embed(description=":tada: Congrats! You hatched: **{0}**".format(snake_name)) my_snake_embed.set_thumbnail(url=snake_image) my_snake_embed.set_footer( - text=" Owner: {0}#{1}".format(ctx.message.author.name, ctx.message.author.discriminator) + text=" Owner: {0}#{1}".format(ctx.author.name, ctx.author.discriminator) ) await ctx.send(embed=my_snake_embed) @@ -1046,14 +1046,14 @@ class Snakes(Cog): """ with ctx.typing(): embed = Embed() - user = ctx.message.author + user = ctx.author if not message: # Get a random message from the users history messages = [] async for message in ctx.channel.history(limit=500).filter( - lambda msg: msg.author == ctx.message.author # Message was sent by author. + lambda msg: msg.author == ctx.author # Message was sent by author. ): messages.append(message.content) diff --git a/bot/exts/halloween/monsterbio.py b/bot/exts/halloween/monsterbio.py index dbafa43f..1aaba7bb 100644 --- a/bot/exts/halloween/monsterbio.py +++ b/bot/exts/halloween/monsterbio.py @@ -26,7 +26,7 @@ class MonsterBio(commands.Cog): @commands.command(brief="Sends your monster bio!") async def monsterbio(self, ctx: commands.Context) -> None: """Sends a description of a monster.""" - seeded_random = random.Random(ctx.message.author.id) # Seed a local Random instance rather than the system one + seeded_random = random.Random(ctx.author.id) # Seed a local Random instance rather than the system one name = self.generate_name(seeded_random) species = self.generate_name(seeded_random) diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index 4fe3bc09..56bf5add 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -114,7 +114,7 @@ class InternalEval(commands.Cog): """Evaluate the `code` in the current evaluation context.""" context_vars = { "message": ctx.message, - "author": ctx.message.author, + "author": ctx.author, "channel": ctx.channel, "guild": ctx.guild, "ctx": ctx, -- cgit v1.2.3 From b17a60a83edc98405e75ae84de23bbd1c7f4bf6c Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Mon, 10 May 2021 09:35:48 -0400 Subject: chore: Use ctx instead of ctx.channel --- bot/exts/easter/egghead_quiz.py | 2 +- bot/exts/evergreen/error_handler.py | 5 +---- bot/exts/evergreen/snakes/_snakes_cog.py | 2 +- bot/exts/evergreen/wolfram.py | 6 +++--- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/bot/exts/easter/egghead_quiz.py b/bot/exts/easter/egghead_quiz.py index 59c1f6f8..b6b1593d 100644 --- a/bot/exts/easter/egghead_quiz.py +++ b/bot/exts/easter/egghead_quiz.py @@ -64,7 +64,7 @@ class EggheadQuiz(commands.Cog): del self.quiz_messages[msg.id] - msg = await ctx.channel.fetch_message(msg.id) # Refreshes message + msg = await ctx.fetch_message(msg.id) # Refreshes message total_no = sum([len(await r.users().flatten()) for r in msg.reactions]) - len(valid_emojis) # - bot's reactions diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index faaf1386..de8e53d0 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -125,10 +125,7 @@ class CommandErrorHandler(commands.Cog): scope.set_extra("full_message", ctx.message.content) if ctx.guild is not None: - scope.set_extra( - "jump_to", - f"https://discordapp.com/channels/{ctx.guild.id}/{ctx.channel.id}/{ctx.message.id}" - ) + scope.set_extra("jump_to", ctx.message.jump_url) log.exception(f"Unhandled command error: {str(error)}", exc_info=error) diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index b844960a..c50b23c5 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -1052,7 +1052,7 @@ class Snakes(Cog): # Get a random message from the users history messages = [] - async for message in ctx.channel.history(limit=500).filter( + async for message in ctx.history(limit=500).filter( lambda msg: msg.author == ctx.author # Message was sent by author. ): messages.append(message.content) diff --git a/bot/exts/evergreen/wolfram.py b/bot/exts/evergreen/wolfram.py index 3cc12c03..ca7d8454 100644 --- a/bot/exts/evergreen/wolfram.py +++ b/bot/exts/evergreen/wolfram.py @@ -105,7 +105,7 @@ def custom_cooldown(*ignore: List[int]) -> Callable: async def get_pod_pages(ctx: Context, bot: Bot, query: str) -> Optional[List[Tuple]]: """Get the Wolfram API pod pages for the provided query.""" - async with ctx.channel.typing(): + async with ctx.typing(): url_str = parse.urlencode({ "input": query, "appid": APPID, @@ -180,7 +180,7 @@ class Wolfram(Cog): query = QUERY.format(request="simple", data=url_str) # Give feedback that the bot is working. - async with ctx.channel.typing(): + async with ctx.typing(): async with self.bot.http_session.get(query) as response: status = response.status image_bytes = await response.read() @@ -263,7 +263,7 @@ class Wolfram(Cog): query = QUERY.format(request="result", data=url_str) # Give feedback that the bot is working. - async with ctx.channel.typing(): + async with ctx.typing(): async with self.bot.http_session.get(query) as response: status = response.status response_text = await response.text() -- cgit v1.2.3 From 6040e351403c5a708e77a26d1f8167f767988c56 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Mon, 10 May 2021 12:06:19 -0300 Subject: make vartol stricter when it's a generated question also fixed image not displaying bug --- bot/exts/evergreen/trivia_quiz.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 2620dea1..c00d91ae 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -4,7 +4,7 @@ import logging import operator import random from pathlib import Path -from typing import List, Optional, Tuple +from typing import Callable, List, Optional, Tuple import discord from discord.ext import commands @@ -18,7 +18,8 @@ logger = logging.getLogger(__name__) QAndA = Tuple[str, str] -VARIATION_TOLERANCE = 83 +STANDARD_VARIATION_TOLERANCE = 83 +DYNAMICALLY_GEN_VARIATION_TOLERANCE = 95 WRONG_ANS_RESPONSE = [ "No one answered correctly!", @@ -215,13 +216,11 @@ class TriviaQuiz(commands.Cog): async def quiz_game(self, ctx: commands.Context, category: Optional[str], questions: Optional[int]) -> None: """ Start a quiz! - Questions for the quiz can be selected from the following categories: - general: Test your general knowledge. (default) - retro: Questions related to retro gaming. - math: General questions about mathematics ranging from grade 8 to grade 12. - science: Put your understanding of science to the test! - (More to come!) """ if ctx.channel.id not in self.game_status: @@ -311,6 +310,8 @@ class TriviaQuiz(commands.Cog): if "dynamic_id" not in question_dict: question = question_dict["question"] answers = question_dict["answer"].split(", ") + + var_tol = STANDARD_VARIATION_TOLERANCE else: format_func = DYNAMIC_QUESTIONS_FORMAT_FUNCS[question_dict["dynamic_id"]] @@ -318,28 +319,32 @@ class TriviaQuiz(commands.Cog): question_dict["question"], question_dict["answer"], ) - answers = [answers] + var_tol = DYNAMICALLY_GEN_VARIATION_TOLERANCE + embed = discord.Embed( colour=Colours.gold, title=f"Question #{len(done_question)}", description=question, ) - if img_url := question_dict.get("image_url"): + if img_url := question_dict.get("img_url"): embed.set_image(url=img_url) await ctx.send(embed=embed) - def contains_correct_answer(m: discord.Message) -> bool: - return m.channel == ctx.channel and any( - fuzz.ratio(answer.lower(), m.content.lower()) > VARIATION_TOLERANCE - for answer in answers - ) + def check_func(variation_tolerance: int) -> Callable[[discord.Message], bool]: + def contains_correct_answer(m: discord.Message) -> bool: + return m.channel == ctx.channel and any( + fuzz.ratio(answer.lower(), m.content.lower()) > variation_tolerance + for answer in answers + ) + + return contains_correct_answer try: - msg = await self.bot.wait_for("message", check=contains_correct_answer, timeout=10) + msg = await self.bot.wait_for("message", check=check_func(var_tol), timeout=10) except asyncio.TimeoutError: # In case of TimeoutError and the game has been stopped, then do nothing. if not self.game_status[ctx.channel.id]: @@ -445,7 +450,6 @@ class TriviaQuiz(commands.Cog): async def stop_quiz(self, ctx: commands.Context) -> None: """ Stop a quiz game if its running in the channel. - Note: Only mods or the owner of the quiz can stop it. """ if self.game_status[ctx.channel.id] is True: @@ -552,7 +556,7 @@ class TriviaQuiz(commands.Cog): embed = discord.Embed( color=Colours.bright_green, title=( - "You got it! " if answer_is_correct else "" + ("You got it! " if answer_is_correct else "") + f"The correct answer {word} **`{', '.join(answers)}`**\n" ), description="", @@ -562,7 +566,7 @@ class TriviaQuiz(commands.Cog): embed.description += f"**Information**\n{info}\n\n" embed.description += ( - "Let's move to the next question." if q_left > 0 else "" + ("Let's move to the next question." if q_left > 0 else "") + f"\nRemaining questions: {q_left}" ) await channel.send(embed=embed) -- cgit v1.2.3 From aab59b138e877fab349314b4b326ede914ea56c3 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Mon, 10 May 2021 12:10:54 -0300 Subject: lint failure fixes --- bot/exts/evergreen/trivia_quiz.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index c00d91ae..94dc9f06 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -216,11 +216,13 @@ class TriviaQuiz(commands.Cog): async def quiz_game(self, ctx: commands.Context, category: Optional[str], questions: Optional[int]) -> None: """ Start a quiz! + Questions for the quiz can be selected from the following categories: - general: Test your general knowledge. (default) - retro: Questions related to retro gaming. - math: General questions about mathematics ranging from grade 8 to grade 12. - science: Put your understanding of science to the test! + (More to come!) """ if ctx.channel.id not in self.game_status: @@ -450,6 +452,7 @@ class TriviaQuiz(commands.Cog): async def stop_quiz(self, ctx: commands.Context) -> None: """ Stop a quiz game if its running in the channel. + Note: Only mods or the owner of the quiz can stop it. """ if self.game_status[ctx.channel.id] is True: @@ -556,8 +559,8 @@ class TriviaQuiz(commands.Cog): embed = discord.Embed( color=Colours.bright_green, title=( - ("You got it! " if answer_is_correct else "") + - f"The correct answer {word} **`{', '.join(answers)}`**\n" + ("You got it! " if answer_is_correct else "") + + f"The correct answer {word} **`{', '.join(answers)}`**\n" ), description="", ) @@ -566,8 +569,8 @@ class TriviaQuiz(commands.Cog): embed.description += f"**Information**\n{info}\n\n" embed.description += ( - ("Let's move to the next question." if q_left > 0 else "") + - f"\nRemaining questions: {q_left}" + ("Let's move to the next question." if q_left > 0 else "") + + f"\nRemaining questions: {q_left}" ) await channel.send(embed=embed) -- cgit v1.2.3 From 8c9298495a86da7c7b67b0a5d7557819ce0057e7 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 10 May 2021 20:52:13 +0100 Subject: Remove redundant comments in bookmark cog --- bot/exts/evergreen/bookmark.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 5f550a4a..7a97a40d 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -24,7 +24,7 @@ class Bookmark(commands.Cog): @staticmethod def build_bookmark_dm(target_message: discord.Message, title: str) -> discord.Embed: - """Builds the embed to DM the bookmark requester.""" + """Build the embed to DM the bookmark requester.""" embed = discord.Embed( title=title, description=target_message.content, @@ -131,13 +131,11 @@ class Bookmark(commands.Cog): _, user = await self.bot.wait_for("reaction_add", timeout=TIMEOUT, check=event_check) except asyncio.TimeoutError: log.debug("Timed out waiting for a reaction") - # No reactions for the last `TIMEOUT` seconds break log.trace(f"{user} has successfully bookmarked from a reaction, attempting to DM them.") await self.action_bookmark(ctx.channel, user, target_message, title) bookmarked_users.append(user.id) - # Delete the message now that the bot isn't listening to it to save screen space await reaction_message.delete() -- cgit v1.2.3 From 3f0388556c886d9d8e26b7a0373e3a6d2d9ba5ad Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Tue, 11 May 2021 09:05:09 -0400 Subject: fix original copy paste error in math question Co-authored-by: Matteo Bertucci --- bot/resources/evergreen/trivia_quiz.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index 79e79d00..1e85542f 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -301,7 +301,7 @@ }, { "id": 203, - "question": "Which theorem states that hypotenuse2 = base2 + height2?", + "question": "Which theorem states that hypotenuse^2 = base^2 + height^2?", "answer": "Pythagorean's, Pythagorean's theorem" }, { -- cgit v1.2.3 From fa0f365acebfdef1130330dc2835945e9da3a69b Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Tue, 11 May 2021 10:08:40 -0300 Subject: add one more correct answer to science (q328) --- bot/resources/evergreen/trivia_quiz.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index 1e85542f..774c235a 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -592,7 +592,7 @@ { "id": 328, "question": "Which law states that the global entropy in a closed system can only increase?", - "answer": "second law of thermodynamics" + "answer": "second law, second law of thermodynamics" }, { "id": 329, -- cgit v1.2.3 From d2754ce38232955e17322264e78ca90b0a3d7cb7 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Tue, 11 May 2021 10:57:36 -0300 Subject: made comment more precise as described by decorator-factory --- bot/exts/evergreen/trivia_quiz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 94dc9f06..94d9e374 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -381,7 +381,7 @@ class TriviaQuiz(commands.Cog): ) await asyncio.sleep(1) - hint_no = 0 # init hint_no = 0 so that 2 hints/time alerts can be sent for the new question. + hint_no = 0 # Reset the hint counter so that on the next round, it's in the initial state await self.send_score(ctx.channel, self.game_player_scores[ctx.channel.id]) await asyncio.sleep(2) -- cgit v1.2.3 From 5241fc893d1d378f6a4dcae32925f672ce8f87a7 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Tue, 11 May 2021 15:29:13 -0300 Subject: change weird google docs unicode to standard ones --- bot/resources/evergreen/trivia_quiz.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index 774c235a..e2054f43 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -301,7 +301,7 @@ }, { "id": 203, - "question": "Which theorem states that hypotenuse^2 = base^2 + height^2?", + "question": "Which theorem states that hypotenuse2 = base2 + height2?", "answer": "Pythagorean's, Pythagorean's theorem" }, { @@ -360,7 +360,7 @@ }, { "id": 214, - "question": "⅓πr2h is the volume of which 3 dimensional figure?", + "question": "⅓πr^2h is the volume of which 3 dimensional figure?", "answer": "cone" }, { @@ -452,7 +452,7 @@ "science": [ { "id": 301, - "question": "The three main components of a normal atom are: protons, neutrons, and…", + "question": "The three main components of a normal atom are: protons, neutrons, and...", "answer": "electrons" }, { @@ -472,7 +472,7 @@ }, { "id": 305, - "question": "The Heisenberg’s Uncertainty Principle states that the position and ________ of a quantum object can’t be both exactly measured at the same time.", + "question": "The Heisenberg's Uncertainty Principle states that the position and ________ of a quantum object can't be both exactly measured at the same time.", "answer": "velocity, momentum" }, { @@ -500,9 +500,9 @@ { "id": 310, "dynamic_id": 303, - "question": "How does one describe the unit {} in SI base units?\n**IMPORTANT:** use * for multiplication, ^ for exponentiation, and place your base units in this order: m - kg - s - A", + "question": "How does one describe the unit {} in SI base units?\n**IMPORTANT:** enclose answer in backticks, use \\* for multiplication, ^ for exponentiation, and place your base units in this order: m - kg - s - A", "img_url": "https://i.imgur.com/NRzU6tf.png", - "answer": "{}" + "answer": "`{}`" }, { "id": 311, -- cgit v1.2.3 From 91db03e5fde06efb41341d595d75424b9ff759b2 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Tue, 11 May 2021 15:30:34 -0300 Subject: fix copy paste error --- bot/resources/evergreen/trivia_quiz.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index e2054f43..1ba5ea7b 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -301,7 +301,7 @@ }, { "id": 203, - "question": "Which theorem states that hypotenuse2 = base2 + height2?", + "question": "Which theorem states that hypotenuse^2 = base^2 + height2^?", "answer": "Pythagorean's, Pythagorean's theorem" }, { -- cgit v1.2.3 From febe3e7d5fea74ed939b903306e552177b2c61c2 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Tue, 11 May 2021 15:32:30 -0300 Subject: fix unicode corruption errors by encoding to utf-8 --- bot/exts/evergreen/trivia_quiz.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 94d9e374..7fdd3f5f 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -10,9 +10,7 @@ import discord from discord.ext import commands from fuzzywuzzy import fuzz -from bot.constants import Colours -from bot.constants import NEGATIVE_REPLIES -from bot.constants import Roles +from bot.constants import Colours, NEGATIVE_REPLIES, Roles logger = logging.getLogger(__name__) @@ -210,7 +208,7 @@ class TriviaQuiz(commands.Cog): """Load the questions from the JSON file.""" p = Path("bot", "resources", "evergreen", "trivia_quiz.json") - return json.loads(p.read_text()) + return json.loads(p.read_text(encoding="utf-8")) @commands.group(name="quiz", aliases=["trivia"], invoke_without_command=True) async def quiz_game(self, ctx: commands.Context, category: Optional[str], questions: Optional[int]) -> None: -- cgit v1.2.3 From 8ee99da88cb5d2b0fc9b1e3dcf24f72bca5679a4 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Wed, 12 May 2021 08:38:53 -0400 Subject: fix ANOTHER copy paste error 3rd copy paste error- smh Co-authored-by: Shivansh-007 --- bot/resources/evergreen/trivia_quiz.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index 1ba5ea7b..acf7375a 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -301,7 +301,7 @@ }, { "id": 203, - "question": "Which theorem states that hypotenuse^2 = base^2 + height2^?", + "question": "Which theorem states that hypotenuse^2 = base^2 + height^2?", "answer": "Pythagorean's, Pythagorean's theorem" }, { -- cgit v1.2.3 From 4448f0d04987ac2628f74e43f6262a5856509f9d Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Wed, 12 May 2021 09:41:05 -0300 Subject: add one more possible answer to math q208 --- bot/resources/evergreen/trivia_quiz.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index acf7375a..3a907de1 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -328,7 +328,7 @@ "id": 208, "question": "What's the following formula that finds the area of a triangle called?", "img_url": "https://wikimedia.org/api/rest_v1/media/math/render/png/d22b8566e8187542966e8d166e72e93746a1a6fc", - "answer": "Heron's formula" + "answer": "Heron's formula, Heron" }, { "id": 209, -- cgit v1.2.3 From 358e5641d16ac27ca68c73f52cbccf12299818d6 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Wed, 12 May 2021 10:56:18 -0300 Subject: fix inconsistency of hexadecimal letter cases in Colours class --- bot/constants.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 9b45b89a..28ec477d 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -140,19 +140,19 @@ class Client(NamedTuple): class Colours: - blue = 0x0279fd - bright_green = 0x01d277 - dark_green = 0x1f8b4c - orange = 0xe67e22 - pink = 0xcf84e0 - purple = 0xb734eb - soft_green = 0x68c290 - soft_orange = 0xf9cb54 - soft_red = 0xcd6d6d - yellow = 0xf9f586 + blue = 0x0279FD + bright_green = 0x01D277 + dark_green = 0x1F8B4C + orange = 0xE67E22 + pink = 0xCF84E0 + purple = 0xB734EB + soft_green = 0x68C290 + soft_orange = 0xF9CB54 + soft_red = 0xCD6D6D + yellow = 0xF9F586 python_blue = 0x4B8BBE python_yellow = 0xFFD43B - grass_green = 0x66ff00 + grass_green = 0x66FF00 gold = 0xE6C200 easter_like_colours = [ @@ -180,7 +180,7 @@ class Emojis: envelope = "\U0001F4E8" trashcan = environ.get("TRASHCAN_EMOJI", "<:trashcan:637136429717389331>") ok_hand = ":ok_hand:" - hand_raised = "\U0001f64b" + hand_raised = "\U0001F64B" dice_1 = "<:dice_1:755891608859443290>" dice_2 = "<:dice_2:755891608741740635>" -- cgit v1.2.3 From 4430760c56f71046040369b3b1f248d8344bf968 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Wed, 12 May 2021 11:45:06 -0300 Subject: 0 as an input to `questions` would now also raise an error before it would go to the default of 7 --- bot/exts/evergreen/trivia_quiz.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 7fdd3f5f..c2f891e2 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -16,6 +16,7 @@ logger = logging.getLogger(__name__) QAndA = Tuple[str, str] +DEFAULT_QUESTION_LIMIT = 6 STANDARD_VARIATION_TOLERANCE = 83 DYNAMICALLY_GEN_VARIATION_TOLERANCE = 95 @@ -191,7 +192,7 @@ class TriviaQuiz(commands.Cog): self.game_owners = {} # A variable to store the person's ID who started the quiz game in a channel. self.questions = self.load_questions() - self.question_limit = 6 + self.question_limit = 0 self.player_scores = {} # A variable to store all player's scores for a bot session. self.game_player_scores = {} # A variable to store temporary game player's scores. @@ -251,7 +252,9 @@ class TriviaQuiz(commands.Cog): topic = self.questions[category] topic_length = len(topic) - if questions: + if questions is None: + self.question_limit = DEFAULT_QUESTION_LIMIT + else: if questions > topic_length: await ctx.send( embed=self.make_error_embed( @@ -265,7 +268,7 @@ class TriviaQuiz(commands.Cog): await ctx.send( embed=self.make_error_embed( "You must choose to complete at least one question. " - "(or enter 0 for the default value of 7 questions)" + "(or enter nothing for the default value of 7 questions)" ) ) return -- cgit v1.2.3 From cb3b7a394f5fe60f75279d7ac9e4cdb47b267a20 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Wed, 12 May 2021 11:53:04 -0300 Subject: make the error embed change accordingly if DQL constant is modified --- bot/exts/evergreen/trivia_quiz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index c2f891e2..0b242947 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -268,7 +268,7 @@ class TriviaQuiz(commands.Cog): await ctx.send( embed=self.make_error_embed( "You must choose to complete at least one question. " - "(or enter nothing for the default value of 7 questions)" + f"(or enter nothing for the default value of {DEFAULT_QUESTION_LIMIT + 1} questions)" ) ) return -- cgit v1.2.3 From 8f4911ad75d0860c8710e977a3d6928378fd703e Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Wed, 12 May 2021 14:23:24 -0300 Subject: quality-of-life refinery --- bot/exts/evergreen/trivia_quiz.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 0b242947..cc1bb0bb 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -423,7 +423,7 @@ class TriviaQuiz(commands.Cog): colour=Colours.blue, title="Quiz game starting!", description=( - f"Each game consists of {self.question_limit + 1} questions.\n" + f"This game consists of {self.question_limit + 1} questions.\n" "**Rules: **No cheating and have fun!\n" f"**Category**: {category}" ), @@ -555,13 +555,13 @@ class TriviaQuiz(commands.Cog): """Send the correct answer of a question to the game channel.""" info = question_dict.get("info", "") - word = "is" if len(answers) == 1 else "are" + plurality = " is" if len(answers) == 1 else "s are" embed = discord.Embed( color=Colours.bright_green, title=( ("You got it! " if answer_is_correct else "") - + f"The correct answer {word} **`{', '.join(answers)}`**\n" + + f"The correct answer{word} **`{', '.join(answers)}`**\n" ), description="", ) -- cgit v1.2.3 From 7df1d38400a275fb6c2d079f27ffaedad5bd4453 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Wed, 12 May 2021 14:24:41 -0300 Subject: fix 4th copy paste error --- bot/exts/evergreen/trivia_quiz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index cc1bb0bb..e1a06aa5 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -561,7 +561,7 @@ class TriviaQuiz(commands.Cog): color=Colours.bright_green, title=( ("You got it! " if answer_is_correct else "") - + f"The correct answer{word} **`{', '.join(answers)}`**\n" + + f"The correct answer{plurality} **`{', '.join(answers)}`**\n" ), description="", ) -- cgit v1.2.3 From d821b4e2bd908e38cf3476dc5adb43211ccbcd3a Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Wed, 12 May 2021 19:26:41 +0100 Subject: feat: merge ping and uptime cogs (#722) --- bot/exts/evergreen/ping.py | 21 +++++++++++++++++++-- bot/exts/evergreen/uptime.py | 33 --------------------------------- 2 files changed, 19 insertions(+), 35 deletions(-) delete mode 100644 bot/exts/evergreen/uptime.py diff --git a/bot/exts/evergreen/ping.py b/bot/exts/evergreen/ping.py index 97f8b34d..07c13524 100644 --- a/bot/exts/evergreen/ping.py +++ b/bot/exts/evergreen/ping.py @@ -1,11 +1,14 @@ +import arrow +from dateutil.relativedelta import relativedelta from discord import Embed from discord.ext import commands +from bot import start_time from bot.constants import Colours class Ping(commands.Cog): - """Ping the bot to see its latency and state.""" + """Get info about the bot's ping and uptime.""" def __init__(self, bot: commands.Bot): self.bot = bot @@ -21,7 +24,21 @@ class Ping(commands.Cog): await ctx.send(embed=embed) + # Originally made in 70d2170a0a6594561d59c7d080c4280f1ebcd70b by lemon & gdude2002 + @commands.command(name="uptime") + async def uptime(self, ctx: commands.Context) -> None: + """Get the current uptime of the bot.""" + difference = relativedelta(start_time - arrow.utcnow()) + uptime_string = start_time.shift( + seconds=-difference.seconds, + minutes=-difference.minutes, + hours=-difference.hours, + days=-difference.days + ).humanize() + + await ctx.send(f"I started up {uptime_string}.") + def setup(bot: commands.Bot) -> None: - """Cog load.""" + """Load the Ping cog.""" bot.add_cog(Ping(bot)) diff --git a/bot/exts/evergreen/uptime.py b/bot/exts/evergreen/uptime.py deleted file mode 100644 index a9ad9dfb..00000000 --- a/bot/exts/evergreen/uptime.py +++ /dev/null @@ -1,33 +0,0 @@ -import logging - -import arrow -from dateutil.relativedelta import relativedelta -from discord.ext import commands - -from bot import start_time - -log = logging.getLogger(__name__) - - -class Uptime(commands.Cog): - """A cog for posting the bot's uptime.""" - - def __init__(self, bot: commands.Bot): - self.bot = bot - - @commands.command(name="uptime") - async def uptime(self, ctx: commands.Context) -> None: - """Responds with the uptime of the bot.""" - difference = relativedelta(start_time - arrow.utcnow()) - uptime_string = start_time.shift( - seconds=-difference.seconds, - minutes=-difference.minutes, - hours=-difference.hours, - days=-difference.days - ).humanize() - await ctx.send(f"I started up {uptime_string}.") - - -def setup(bot: commands.Bot) -> None: - """Uptime Cog load.""" - bot.add_cog(Uptime(bot)) -- cgit v1.2.3 From b125a1ab1cdb8355d6c4c5225f1b048ca760e957 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Thu, 13 May 2021 08:49:41 -0400 Subject: change the unsolvable math question to a linalg question --- bot/resources/evergreen/trivia_quiz.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index 3a907de1..94082849 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -417,8 +417,8 @@ }, { "id": 225, - "question": "Prove or disprove the Riemann Hypothesis", - "answer": "" + "question": "A matrix multiplied by its inverse matrix equals...", + "answer": "the identity matrix, identity matrix" }, { "id": 226, -- cgit v1.2.3 From 2aa1916d5c8e4832f26f6da4094238e9a0021d1c Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Thu, 13 May 2021 13:34:06 -0400 Subject: chore: Use pathlib.Path.read_text & write_text over open --- bot/exts/christmas/advent_of_code/_cog.py | 3 +-- bot/exts/easter/april_fools_vids.py | 6 +++--- bot/exts/easter/bunny_name_generator.py | 3 +-- bot/exts/easter/easter_riddle.py | 5 ++--- bot/exts/easter/egg_decorating.py | 6 ++---- bot/exts/easter/egg_facts.py | 5 ++--- bot/exts/easter/egghead_quiz.py | 5 ++--- bot/exts/easter/save_the_planet.py | 4 +--- bot/exts/easter/traditions.py | 3 +-- bot/exts/evergreen/avatar_modification/avatar_modify.py | 4 ++-- bot/exts/evergreen/fun.py | 3 +-- bot/exts/evergreen/magic_8ball.py | 3 +-- bot/exts/evergreen/recommend_game.py | 3 +-- bot/exts/evergreen/snakes/_converter.py | 7 ++----- bot/exts/evergreen/snakes/_utils.py | 5 ++--- bot/exts/evergreen/speedrun.py | 4 ++-- bot/exts/evergreen/trivia_quiz.py | 4 +--- bot/exts/halloween/8ball.py | 3 +-- bot/exts/halloween/halloween_facts.py | 3 +-- bot/exts/halloween/halloweenify.py | 5 ++--- bot/exts/halloween/monsterbio.py | 5 +++-- bot/exts/halloween/monstersurvey.py | 6 ++---- bot/exts/halloween/spookynamerate.py | 3 +-- bot/exts/halloween/spookyrating.py | 6 +++--- bot/exts/pride/drag_queen_name.py | 3 +-- bot/exts/pride/pride_anthem.py | 4 +--- bot/exts/pride/pride_facts.py | 3 +-- bot/exts/valentines/be_my_valentine.py | 6 ++---- bot/exts/valentines/lovecalculator.py | 5 ++--- bot/exts/valentines/myvalenstate.py | 3 +-- bot/exts/valentines/pickuplines.py | 9 ++++----- bot/exts/valentines/savethedate.py | 5 ++--- bot/exts/valentines/valentine_zodiac.py | 16 ++++++++-------- bot/exts/valentines/whoisvalentine.py | 3 +-- 34 files changed, 63 insertions(+), 98 deletions(-) diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index 863d2a21..ce6a4cb6 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -279,8 +279,7 @@ class AdventOfCode(commands.Cog): def _build_about_embed(self) -> discord.Embed: """Build and return the informational "About AoC" embed from the resources file.""" - with self.about_aoc_filepath.open("r", encoding="utf8") as f: - embed_fields = json.load(f) + embed_fields = json.loads(self.about_aoc_filepath.read_text("utf8")) about_embed = discord.Embed( title=self._base_url, diff --git a/bot/exts/easter/april_fools_vids.py b/bot/exts/easter/april_fools_vids.py index 3ce1f72a..5ef40704 100644 --- a/bot/exts/easter/april_fools_vids.py +++ b/bot/exts/easter/april_fools_vids.py @@ -1,6 +1,7 @@ import logging import random -from json import load +from json import loads +from pathlib import Path from discord.ext import commands @@ -8,8 +9,7 @@ from bot.bot import Bot log = logging.getLogger(__name__) -with open("bot/resources/easter/april_fools_vids.json", encoding="utf-8") as f: - ALL_VIDS = load(f) +ALL_VIDS = loads(Path("bot/resources/easter/april_fools_vids.json").read_text("utf-8")) class AprilFoolVideos(commands.Cog): diff --git a/bot/exts/easter/bunny_name_generator.py b/bot/exts/easter/bunny_name_generator.py index 5e3b014d..adde8704 100644 --- a/bot/exts/easter/bunny_name_generator.py +++ b/bot/exts/easter/bunny_name_generator.py @@ -11,8 +11,7 @@ from bot.bot import Bot log = logging.getLogger(__name__) -with Path("bot/resources/easter/bunny_names.json").open("r", encoding="utf8") as f: - BUNNY_NAMES = json.load(f) +BUNNY_NAMES = json.loads(Path("bot/resources/easter/bunny_names.json").read_text("utf8")) class BunnyNameGenerator(commands.Cog): diff --git a/bot/exts/easter/easter_riddle.py b/bot/exts/easter/easter_riddle.py index 9a253a6a..01b956f1 100644 --- a/bot/exts/easter/easter_riddle.py +++ b/bot/exts/easter/easter_riddle.py @@ -1,7 +1,7 @@ import asyncio import logging import random -from json import load +from json import loads from pathlib import Path import discord @@ -12,8 +12,7 @@ from bot.constants import Colours, NEGATIVE_REPLIES log = logging.getLogger(__name__) -with Path("bot/resources/easter/easter_riddle.json").open("r", encoding="utf8") as f: - RIDDLE_QUESTIONS = load(f) +RIDDLE_QUESTIONS = loads(Path("bot/resources/easter/easter_riddle.json").read_text("utf8")) TIMELIMIT = 10 diff --git a/bot/exts/easter/egg_decorating.py b/bot/exts/easter/egg_decorating.py index 3744989d..7448f702 100644 --- a/bot/exts/easter/egg_decorating.py +++ b/bot/exts/easter/egg_decorating.py @@ -15,11 +15,9 @@ from bot.utils import helpers log = logging.getLogger(__name__) -with open(Path("bot/resources/evergreen/html_colours.json"), encoding="utf8") as f: - HTML_COLOURS = json.load(f) +HTML_COLOURS = json.loads(Path("bot/resources/evergreen/html_colours.json").read_text("utf8")) -with open(Path("bot/resources/evergreen/xkcd_colours.json"), encoding="utf8") as f: - XKCD_COLOURS = json.load(f) +XKCD_COLOURS = json.loads(Path("bot/resources/evergreen/xkcd_colours.json").read_text("utf8")) COLOURS = [ (255, 0, 0, 255), (255, 128, 0, 255), (255, 255, 0, 255), (0, 255, 0, 255), diff --git a/bot/exts/easter/egg_facts.py b/bot/exts/easter/egg_facts.py index 8c93ca7b..c1c43f80 100644 --- a/bot/exts/easter/egg_facts.py +++ b/bot/exts/easter/egg_facts.py @@ -1,6 +1,6 @@ import logging import random -from json import load +from json import loads from pathlib import Path import discord @@ -30,8 +30,7 @@ class EasterFacts(commands.Cog): def load_json() -> dict: """Load a list of easter egg facts from the resource JSON file.""" p = Path("bot/resources/easter/easter_egg_facts.json") - with p.open(encoding="utf8") as f: - return load(f) + return loads(p.read_text("utf8")) @seasonal_task(Month.APRIL) async def send_egg_fact_daily(self) -> None: diff --git a/bot/exts/easter/egghead_quiz.py b/bot/exts/easter/egghead_quiz.py index b6b1593d..4b67310f 100644 --- a/bot/exts/easter/egghead_quiz.py +++ b/bot/exts/easter/egghead_quiz.py @@ -1,7 +1,7 @@ import asyncio import logging import random -from json import load +from json import loads from pathlib import Path from typing import Union @@ -13,8 +13,7 @@ from bot.constants import Colours log = logging.getLogger(__name__) -with open(Path("bot/resources/easter/egghead_questions.json"), "r", encoding="utf8") as f: - EGGHEAD_QUESTIONS = load(f) +EGGHEAD_QUESTIONS = loads(Path("bot/resources/easter/egghead_questions.json").read_text("utf8")) EMOJIS = [ diff --git a/bot/exts/easter/save_the_planet.py b/bot/exts/easter/save_the_planet.py index 444bb030..1bd515f2 100644 --- a/bot/exts/easter/save_the_planet.py +++ b/bot/exts/easter/save_the_planet.py @@ -7,9 +7,7 @@ from discord.ext import commands from bot.bot import Bot from bot.utils.randomization import RandomCycle - -with Path("bot/resources/easter/save_the_planet.json").open("r", encoding="utf8") as f: - EMBED_DATA = RandomCycle(json.load(f)) +EMBED_DATA = RandomCycle(json.loads(Path("bot/resources/easter/save_the_planet.json").read_text("utf8"))) class SaveThePlanet(commands.Cog): diff --git a/bot/exts/easter/traditions.py b/bot/exts/easter/traditions.py index cb70f2d0..93404f3e 100644 --- a/bot/exts/easter/traditions.py +++ b/bot/exts/easter/traditions.py @@ -9,8 +9,7 @@ from bot.bot import Bot log = logging.getLogger(__name__) -with open(Path("bot/resources/easter/traditions.json"), "r", encoding="utf8") as f: - traditions = json.load(f) +traditions = json.loads(Path("bot/resources/easter/traditions.json").read_text("utf8")) class Traditions(commands.Cog): diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 5685b2c1..199b6dcb 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -6,6 +6,7 @@ import string import typing as t import unicodedata from concurrent.futures import ThreadPoolExecutor +from pathlib import Path import discord from aiohttp import client_exceptions @@ -27,8 +28,7 @@ MAX_SQUARES = 10_000 T = t.TypeVar("T") -with open("bot/resources/pride/gender_options.json") as f: - GENDER_OPTIONS = json.load(f) +GENDER_OPTIONS = json.loads(Path("bot/resources/pride/gender_options.json").read_text("utf8")) async def in_executor(func: t.Callable[..., T], *args) -> T: diff --git a/bot/exts/evergreen/fun.py b/bot/exts/evergreen/fun.py index ed51358c..3b266e1b 100644 --- a/bot/exts/evergreen/fun.py +++ b/bot/exts/evergreen/fun.py @@ -56,8 +56,7 @@ class Fun(Cog): def __init__(self, bot: Bot) -> None: self.bot = bot - with Path("bot/resources/evergreen/caesar_info.json").open("r", encoding="UTF-8") as f: - self._caesar_cipher_embed = json.load(f) + self._caesar_cipher_embed = json.loads(Path("bot/resources/evergreen/caesar_info.json").read_text("UTF-8")) @staticmethod def _get_random_die() -> str: diff --git a/bot/exts/evergreen/magic_8ball.py b/bot/exts/evergreen/magic_8ball.py index 7c9b929d..4b3ed2a4 100644 --- a/bot/exts/evergreen/magic_8ball.py +++ b/bot/exts/evergreen/magic_8ball.py @@ -14,8 +14,7 @@ class Magic8ball(commands.Cog): """A Magic 8ball command to respond to a user's question.""" def __init__(self): - with open(Path("bot/resources/evergreen/magic8ball.json"), "r", encoding="utf8") as file: - self.answers = json.load(file) + self.answers = json.loads(Path("bot/resources/evergreen/magic8ball.json").read_text("utf8")) @commands.command(name="8ball") async def output_answer(self, ctx: commands.Context, *, question: str) -> None: diff --git a/bot/exts/evergreen/recommend_game.py b/bot/exts/evergreen/recommend_game.py index 340a42d3..56596020 100644 --- a/bot/exts/evergreen/recommend_game.py +++ b/bot/exts/evergreen/recommend_game.py @@ -13,8 +13,7 @@ game_recs = [] # Populate the list `game_recs` with resource files for rec_path in Path("bot/resources/evergreen/game_recs").glob("*.json"): - with rec_path.open(encoding="utf8") as file: - data = json.load(file) + data = json.loads(rec_path.read_text("utf8")) game_recs.append(data) shuffle(game_recs) diff --git a/bot/exts/evergreen/snakes/_converter.py b/bot/exts/evergreen/snakes/_converter.py index 0ca10d6c..26bde611 100644 --- a/bot/exts/evergreen/snakes/_converter.py +++ b/bot/exts/evergreen/snakes/_converter.py @@ -63,13 +63,10 @@ class Snake(Converter): """Build list of snakes from the static snake resources.""" # Get all the snakes if cls.snakes is None: - with (SNAKE_RESOURCES / "snake_names.json").open(encoding="utf8") as snakefile: - cls.snakes = json.load(snakefile) - + cls.snakes = json.loads((SNAKE_RESOURCES / "snake_names.json").read_text("utf8")) # Get the special cases if cls.special_cases is None: - with (SNAKE_RESOURCES / "special_snakes.json").open(encoding="utf8") as snakefile: - special_cases = json.load(snakefile) + special_cases = json.loads((SNAKE_RESOURCES / "special_snakes.json").read_text("utf8")) cls.special_cases = {snake["name"].lower(): snake for snake in special_cases} @classmethod diff --git a/bot/exts/evergreen/snakes/_utils.py b/bot/exts/evergreen/snakes/_utils.py index 9b38ffa2..8b39f217 100644 --- a/bot/exts/evergreen/snakes/_utils.py +++ b/bot/exts/evergreen/snakes/_utils.py @@ -114,8 +114,7 @@ ANGLE_RANGE = math.pi * 2 def get_resource(file: str) -> List[dict]: """Load Snake resources JSON.""" - with (SNAKE_RESOURCES / f"{file}.json").open(encoding="utf-8") as snakefile: - return json.load(snakefile) + return json.loads((SNAKE_RESOURCES / f"{file}.json").read_text("utf-8")) def smoothstep(t: float) -> float: @@ -560,7 +559,7 @@ class SnakeAndLaddersGame: 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")) + board_img = Image.open(SNAKE_RESOURCES / "snakes_and_ladders" / "board.jpg") player_row_size = math.ceil(MAX_PLAYERS / 2) for i, player in enumerate(self.players): diff --git a/bot/exts/evergreen/speedrun.py b/bot/exts/evergreen/speedrun.py index d9820287..774eff81 100644 --- a/bot/exts/evergreen/speedrun.py +++ b/bot/exts/evergreen/speedrun.py @@ -8,8 +8,8 @@ from discord.ext import commands from bot.bot import Bot log = logging.getLogger(__name__) -with Path("bot/resources/evergreen/speedrun_links.json").open(encoding="utf8") as file: - LINKS = json.load(file) + +LINKS = json.loads(Path("bot/resources/evergreen/speedrun_links.json").read_text("utf8")) class Speedrun(commands.Cog): diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 1953253b..9db201ef 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -41,9 +41,7 @@ class TriviaQuiz(commands.Cog): def load_questions() -> dict: """Load the questions from the JSON file.""" p = Path("bot", "resources", "evergreen", "trivia_quiz.json") - with p.open(encoding="utf8") as json_data: - questions = json.load(json_data) - return questions + return json.loads(p.read_text("utf8")) @commands.group(name="quiz", aliases=["trivia"], invoke_without_command=True) async def quiz_game(self, ctx: commands.Context, category: str = None) -> None: diff --git a/bot/exts/halloween/8ball.py b/bot/exts/halloween/8ball.py index d6c5a299..a2431190 100644 --- a/bot/exts/halloween/8ball.py +++ b/bot/exts/halloween/8ball.py @@ -10,8 +10,7 @@ from bot.bot import Bot log = logging.getLogger(__name__) -with Path("bot/resources/halloween/responses.json").open("r", encoding="utf8") as f: - RESPONSES = json.load(f) +RESPONSES = json.loads(Path("bot/resources/halloween/responses.json").read_text("utf8")) class SpookyEightBall(commands.Cog): diff --git a/bot/exts/halloween/halloween_facts.py b/bot/exts/halloween/halloween_facts.py index 3a89b5aa..98cc2db0 100644 --- a/bot/exts/halloween/halloween_facts.py +++ b/bot/exts/halloween/halloween_facts.py @@ -30,8 +30,7 @@ class HalloweenFacts(commands.Cog): """A Cog for displaying interesting facts about Halloween.""" def __init__(self): - with Path("bot/resources/halloween/halloween_facts.json").open("r", encoding="utf8") as file: - self.halloween_facts = json.load(file) + self.halloween_facts = json.loads(Path("bot/resources/halloween/halloween_facts.json").read_text("utf8")) self.facts = list(enumerate(self.halloween_facts)) def random_fact(self) -> Tuple[int, str]: diff --git a/bot/exts/halloween/halloweenify.py b/bot/exts/halloween/halloweenify.py index df55b55d..e839950a 100644 --- a/bot/exts/halloween/halloweenify.py +++ b/bot/exts/halloween/halloweenify.py @@ -1,5 +1,5 @@ import logging -from json import load +from json import loads from pathlib import Path from random import choice @@ -21,8 +21,7 @@ class Halloweenify(commands.Cog): async def halloweenify(self, ctx: commands.Context) -> None: """Change your nickname into a much spookier one!""" async with ctx.typing(): - with open(Path("bot/resources/halloween/halloweenify.json"), "r", encoding="utf8") as f: - data = load(f) + data = loads(Path("bot/resources/halloween/halloweenify.json").read_text("utf8")) # Choose a random character from our list we loaded above and set apart the nickname and image url. character = choice(data["characters"]) diff --git a/bot/exts/halloween/monsterbio.py b/bot/exts/halloween/monsterbio.py index 1aaba7bb..69e898cb 100644 --- a/bot/exts/halloween/monsterbio.py +++ b/bot/exts/halloween/monsterbio.py @@ -11,8 +11,9 @@ from bot.constants import Colours log = logging.getLogger(__name__) -with open(Path("bot/resources/halloween/monster.json"), "r", encoding="utf8") as f: - TEXT_OPTIONS = json.load(f) # Data for a mad-lib style generation of text +TEXT_OPTIONS = json.loads( + Path("bot/resources/halloween/monster.json").read_text("utf8") +) # Data for a mad-lib style generation of text class MonsterBio(commands.Cog): diff --git a/bot/exts/halloween/monstersurvey.py b/bot/exts/halloween/monstersurvey.py index 231454ea..240a97db 100644 --- a/bot/exts/halloween/monstersurvey.py +++ b/bot/exts/halloween/monstersurvey.py @@ -26,14 +26,12 @@ class MonsterSurvey(Cog): def __init__(self): """Initializes values for the bot to use within the voting commands.""" self.registry_path = pathlib.Path("bot", "resources", "halloween", "monstersurvey.json") - with self.registry_path.open(encoding="utf8") as data: - self.voter_registry = json.load(data) + self.voter_registry = json.loads(self.registry_path.read_text("utf8")) def json_write(self) -> None: """Write voting results to a local JSON file.""" log.info("Saved Monster Survey Results") - with self.registry_path.open("w", encoding="utf8") as data: - json.dump(self.voter_registry, data, indent=2) + self.registry_path.write_text(json.dumps(self.voter_registry, indent=2)) def cast_vote(self, id: int, monster: str) -> None: """ diff --git a/bot/exts/halloween/spookynamerate.py b/bot/exts/halloween/spookynamerate.py index 87172922..63040289 100644 --- a/bot/exts/halloween/spookynamerate.py +++ b/bot/exts/halloween/spookynamerate.py @@ -371,8 +371,7 @@ class SpookyNameRate(Cog): @staticmethod def load_json(file: Path) -> Dict[str, str]: """Loads a JSON file and returns its contents.""" - with file.open("r", encoding="utf-8") as f: - return json.load(f) + return json.loads(file.read_text("utf8")) @staticmethod def in_allowed_month() -> bool: diff --git a/bot/exts/halloween/spookyrating.py b/bot/exts/halloween/spookyrating.py index 6c79fbed..105d2164 100644 --- a/bot/exts/halloween/spookyrating.py +++ b/bot/exts/halloween/spookyrating.py @@ -3,6 +3,7 @@ import json import logging import random from pathlib import Path +from typing import Dict import discord from discord.ext import commands @@ -12,9 +13,8 @@ from bot.constants import Colours log = logging.getLogger(__name__) -with Path("bot/resources/halloween/spooky_rating.json").open(encoding="utf8") as file: - data = json.load(file) - SPOOKY_DATA = sorted((int(key), value) for key, value in data.items()) +data: Dict[str, Dict[str, str]] = json.loads(Path("bot/resources/halloween/spooky_rating.json").read_text("utf8")) +SPOOKY_DATA = sorted((int(key), value) for key, value in data.items()) class SpookyRating(commands.Cog): diff --git a/bot/exts/pride/drag_queen_name.py b/bot/exts/pride/drag_queen_name.py index d9424001..6bf43913 100644 --- a/bot/exts/pride/drag_queen_name.py +++ b/bot/exts/pride/drag_queen_name.py @@ -19,8 +19,7 @@ class DragNames(commands.Cog): @staticmethod def load_names() -> list: """Loads a list of drag queen names.""" - with Path("bot/resources/pride/drag_queen_names.json").open(encoding="utf8") as f: - return json.load(f) + return json.loads(Path("bot/resources/pride/drag_queen_names.json").read_text("utf8")) @commands.command(name="dragname", aliases=["dragqueenname", "queenme"]) async def dragname(self, ctx: commands.Context) -> None: diff --git a/bot/exts/pride/pride_anthem.py b/bot/exts/pride/pride_anthem.py index a7f8d7ef..21b7e468 100644 --- a/bot/exts/pride/pride_anthem.py +++ b/bot/exts/pride/pride_anthem.py @@ -36,9 +36,7 @@ class PrideAnthem(commands.Cog): @staticmethod def load_vids() -> list: """Loads a list of videos from the resources folder as dictionaries.""" - with open(Path("bot/resources/pride/anthems.json"), "r", encoding="utf8") as f: - anthems = json.load(f) - return anthems + return json.loads(Path("bot/resources/pride/anthems.json").read_text("utf8")) @commands.command(name="prideanthem", aliases=["anthem", "pridesong"]) async def prideanthem(self, ctx: commands.Context, genre: str = None) -> None: diff --git a/bot/exts/pride/pride_facts.py b/bot/exts/pride/pride_facts.py index 6d06cf64..47e69a03 100644 --- a/bot/exts/pride/pride_facts.py +++ b/bot/exts/pride/pride_facts.py @@ -28,8 +28,7 @@ class PrideFacts(commands.Cog): @staticmethod def load_facts() -> dict: """Loads a dictionary of years mapping to lists of facts.""" - with open(Path("bot/resources/pride/facts.json"), "r", encoding="utf8") as f: - return json.load(f) + return json.loads(Path("bot/resources/pride/facts.json").read_text("utf8")) @seasonal_task(Month.JUNE) async def send_pride_fact_daily(self) -> None: diff --git a/bot/exts/valentines/be_my_valentine.py b/bot/exts/valentines/be_my_valentine.py index d1eea388..8b522a72 100644 --- a/bot/exts/valentines/be_my_valentine.py +++ b/bot/exts/valentines/be_my_valentine.py @@ -1,6 +1,6 @@ import logging import random -from json import load +from json import loads from pathlib import Path from typing import Tuple @@ -28,9 +28,7 @@ class BeMyValentine(commands.Cog): def load_json() -> dict: """Load Valentines messages from the static resources.""" p = Path("bot/resources/valentines/bemyvalentine_valentines.json") - with p.open(encoding="utf8") as json_data: - valentines = load(json_data) - return valentines + return loads(p.read_text("utf8")) @in_month(Month.FEBRUARY) @commands.group(name="lovefest") diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index 8a4c71eb..b10b7bca 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -15,9 +15,8 @@ from bot.bot import Bot log = logging.getLogger(__name__) -with Path("bot/resources/valentines/love_matches.json").open(encoding="utf8") as file: - LOVE_DATA = json.load(file) - LOVE_DATA = sorted((int(key), value) for key, value in LOVE_DATA.items()) +LOVE_DATA = json.loads(Path("bot/resources/valentines/love_matches.json").read_text("utf8")) +LOVE_DATA = sorted((int(key), value) for key, value in LOVE_DATA.items()) class LoveCalculator(Cog): diff --git a/bot/exts/valentines/myvalenstate.py b/bot/exts/valentines/myvalenstate.py index 1c67984b..d2409dcc 100644 --- a/bot/exts/valentines/myvalenstate.py +++ b/bot/exts/valentines/myvalenstate.py @@ -12,8 +12,7 @@ from bot.constants import Colours log = logging.getLogger(__name__) -with open(Path("bot/resources/valentines/valenstates.json"), "r", encoding="utf8") as file: - STATES = json.load(file) +STATES = json.loads(Path("bot/resources/valentines/valenstates.json").read_text("utf8")) class MyValenstate(commands.Cog): diff --git a/bot/exts/valentines/pickuplines.py b/bot/exts/valentines/pickuplines.py index 909169e6..00741a72 100644 --- a/bot/exts/valentines/pickuplines.py +++ b/bot/exts/valentines/pickuplines.py @@ -1,6 +1,6 @@ import logging import random -from json import load +from json import loads from pathlib import Path import discord @@ -11,8 +11,7 @@ from bot.constants import Colours log = logging.getLogger(__name__) -with open(Path("bot/resources/valentines/pickup_lines.json"), "r", encoding="utf8") as f: - pickup_lines = load(f) +PICKUP_LINES = loads(Path("bot/resources/valentines/pickup_lines.json").read_text("utf8")) class PickupLine(commands.Cog): @@ -25,14 +24,14 @@ class PickupLine(commands.Cog): Note that most of them are very cheesy. """ - random_line = random.choice(pickup_lines["lines"]) + random_line = random.choice(PICKUP_LINES["lines"]) embed = discord.Embed( title=":cheese: Your pickup line :cheese:", description=random_line["line"], color=Colours.pink ) embed.set_thumbnail( - url=random_line.get("image", pickup_lines["placeholder"]) + url=random_line.get("image", PICKUP_LINES["placeholder"]) ) await ctx.send(embed=embed) diff --git a/bot/exts/valentines/savethedate.py b/bot/exts/valentines/savethedate.py index cc16f5c9..ffe559d6 100644 --- a/bot/exts/valentines/savethedate.py +++ b/bot/exts/valentines/savethedate.py @@ -1,6 +1,6 @@ import logging import random -from json import load +from json import loads from pathlib import Path import discord @@ -13,8 +13,7 @@ log = logging.getLogger(__name__) HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_heart:", ":two_hearts:"] -with open(Path("bot/resources/valentines/date_ideas.json"), "r", encoding="utf8") as f: - VALENTINES_DATES = load(f) +VALENTINES_DATES = loads(Path("bot/resources/valentines/date_ideas.json").read_text("utf8")) class SaveTheDate(commands.Cog): diff --git a/bot/exts/valentines/valentine_zodiac.py b/bot/exts/valentines/valentine_zodiac.py index a444a355..45d1edd5 100644 --- a/bot/exts/valentines/valentine_zodiac.py +++ b/bot/exts/valentines/valentine_zodiac.py @@ -29,14 +29,14 @@ class ValentineZodiac(commands.Cog): """Load zodiac compatibility from static JSON resource.""" explanation_file = Path("bot/resources/valentines/zodiac_explanation.json") compatibility_file = Path("bot/resources/valentines/zodiac_compatibility.json") - with explanation_file.open(encoding="utf8") as json_data: - zodiac_fact = json.load(json_data) - for zodiac_data in zodiac_fact.values(): - zodiac_data["start_at"] = datetime.fromisoformat(zodiac_data["start_at"]) - zodiac_data["end_at"] = datetime.fromisoformat(zodiac_data["end_at"]) - - with compatibility_file.open(encoding="utf8") as json_data: - zodiacs = json.load(json_data) + + zodiac_fact = json.loads(explanation_file.read_text("utf8")) + + for zodiac_data in zodiac_fact.values(): + zodiac_data["start_at"] = datetime.fromisoformat(zodiac_data["start_at"]) + zodiac_data["end_at"] = datetime.fromisoformat(zodiac_data["end_at"]) + + zodiacs = json.loads(compatibility_file.read_text("utf8")) return zodiacs, zodiac_fact diff --git a/bot/exts/valentines/whoisvalentine.py b/bot/exts/valentines/whoisvalentine.py index 3f23201f..211b1f27 100644 --- a/bot/exts/valentines/whoisvalentine.py +++ b/bot/exts/valentines/whoisvalentine.py @@ -11,8 +11,7 @@ from bot.constants import Colours log = logging.getLogger(__name__) -with open(Path("bot/resources/valentines/valentine_facts.json"), "r", encoding="utf8") as file: - FACTS = json.load(file) +FACTS = json.loads(Path("bot/resources/valentines/valentine_facts.json").read_text("utf8")) class ValentineFacts(commands.Cog): -- cgit v1.2.3 From bbfe606f36abacb9d722574d104a31fbfb4300c7 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Thu, 13 May 2021 23:45:33 +0530 Subject: change mods to staff --- bot/exts/evergreen/stackoverflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 04126d9c..d77c131c 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -16,7 +16,7 @@ ERR_EMBED = Embed( title="Error in fetching results from Stackoverflow", description=( "Sorry, there was en error while trying to fetch data from the Stackoverflow website. Please try again in some " - "time. If this issue persists, please contact the mods or send a message in #dev-contrib." + "time. If this issue persists, please contact the staff or send a message in #dev-contrib." ), color=Colours.soft_red ) -- cgit v1.2.3 From b952356c4227dc719baeda09f98a1ae721e8ac36 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Thu, 13 May 2021 23:46:43 +0530 Subject: change docstring --- bot/exts/evergreen/stackoverflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index d77c131c..b85a0c97 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -23,7 +23,7 @@ ERR_EMBED = Embed( class Stackoverflow(commands.Cog): - """A cog which returns the top 5 results of a query from stackoverflow.""" + """Contains command to interact with stackoverflow from discord.""" def __init__(self, bot: commands.Bot): self.bot = bot -- cgit v1.2.3 From 62345be36b0ebf8f867e0fb6597ebd5e29488d1c Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Thu, 13 May 2021 15:20:09 -0300 Subject: implement decorator-factory's changes except for the major refactoring, which would probably be in a separate pr --- bot/exts/evergreen/trivia_quiz.py | 55 ++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index e1a06aa5..7aab8b87 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -3,8 +3,9 @@ import json import logging import operator import random +from dataclasses import dataclass from pathlib import Path -from typing import Callable, List, Optional, Tuple +from typing import Callable, List, Optional import discord from discord.ext import commands @@ -14,8 +15,6 @@ from bot.constants import Colours, NEGATIVE_REPLIES, Roles logger = logging.getLogger(__name__) -QAndA = Tuple[str, str] - DEFAULT_QUESTION_LIMIT = 6 STANDARD_VARIATION_TOLERANCE = 83 DYNAMICALLY_GEN_VARIATION_TOLERANCE = 95 @@ -62,7 +61,13 @@ UNITS_TO_BASE_UNITS = { } -def linear_system(q_format: str, a_format: str) -> QAndA: +@dataclass(frozen=True) +class QuizEntry: + question: str + answer: str + + +def linear_system(q_format: str, a_format: str) -> QuizEntry: """Generate a system of linear equations with two unknowns.""" x, y = random.randint(2, 5), random.randint(2, 5) answer = a_format.format(x, y) @@ -78,10 +83,10 @@ def linear_system(q_format: str, a_format: str) -> QAndA: coeffs[2] * x + coeffs[3] * y, ) - return question, answer + return QuizEntry(question, answer) -def mod_arith(q_format: str, a_format: str) -> QAndA: +def mod_arith(q_format: str, a_format: str) -> QuizEntry: """Generate a basic modular arithmetic question.""" quotient, m, b = random.randint(30, 40), random.randint(10, 20), random.randint(200, 350) ans = random.randint(0, 9) # max remainder is 9, since the minimum modulus is 10 @@ -90,30 +95,30 @@ def mod_arith(q_format: str, a_format: str) -> QAndA: question = q_format.format(a, b, m) answer = a_format.format(ans) - return question, answer + return QuizEntry(question, answer) -def ngonal_prism(q_format: str, a_format: str) -> QAndA: +def ngonal_prism(q_format: str, a_format: str) -> QuizEntry: """Generate a question regarding vertices on n-gonal prisms.""" n = random.randint(0, len(N_PREFIXES) - 1) question = q_format.format(N_PREFIXES[n]) answer = a_format.format((n + N_PREFIX_STARTS_AT) * 2) - return question, answer + return QuizEntry(question, answer) -def imag_sqrt(q_format: str, a_format: str) -> QAndA: +def imag_sqrt(q_format: str, a_format: str) -> QuizEntry: """Generate a negative square root question.""" ans_coeff = random.randint(3, 10) question = q_format.format(ans_coeff ** 2) answer = a_format.format(ans_coeff) - return question, answer + return QuizEntry(question, answer) -def binary_calc(q_format: str, a_format: str) -> QAndA: +def binary_calc(q_format: str, a_format: str) -> QuizEntry: """Generate a binary calculation question.""" a = random.randint(15, 20) b = random.randint(10, a) @@ -133,30 +138,30 @@ def binary_calc(q_format: str, a_format: str) -> QAndA: question = q_format.format(a, oper[0], b) answer = a_format.format(oper[1](a, b)) - return question, answer + return QuizEntry(question, answer) -def solar_system(q_format: str, a_format: str) -> QAndA: +def solar_system(q_format: str, a_format: str) -> QuizEntry: """Generate a question on the planets of the Solar System.""" planet = random.choice(PLANETS) question = q_format.format(planet[0]) answer = a_format.format(planet[1]) - return question, answer + return QuizEntry(question, answer) -def taxonomic_rank(q_format: str, a_format: str) -> QAndA: +def taxonomic_rank(q_format: str, a_format: str) -> QuizEntry: """Generate a question on taxonomic classification.""" level = random.randint(0, len(TAXONOMIC_HIERARCHY) - 2) question = q_format.format(TAXONOMIC_HIERARCHY[level]) answer = a_format.format(TAXONOMIC_HIERARCHY[level + 1]) - return question, answer + return QuizEntry(question, answer) -def base_units_convert(q_format: str, a_format: str) -> QAndA: +def base_units_convert(q_format: str, a_format: str) -> QuizEntry: """Generate a SI base units conversion question.""" unit = random.choice(list(UNITS_TO_BASE_UNITS)) @@ -167,7 +172,7 @@ def base_units_convert(q_format: str, a_format: str) -> QAndA: UNITS_TO_BASE_UNITS[unit][1] ) - return question, answer + return QuizEntry(question, answer) DYNAMIC_QUESTIONS_FORMAT_FUNCS = { @@ -231,7 +236,7 @@ class TriviaQuiz(commands.Cog): self.game_player_scores[ctx.channel.id] = {} # Stop game if running. - if self.game_status[ctx.channel.id] is True: + if self.game_status[ctx.channel.id]: await ctx.send( "Game is already running... " f"do `{self.bot.command_prefix}quiz stop`" @@ -318,10 +323,12 @@ class TriviaQuiz(commands.Cog): else: format_func = DYNAMIC_QUESTIONS_FORMAT_FUNCS[question_dict["dynamic_id"]] - question, answers = format_func( + quiz_entry = format_func( question_dict["question"], question_dict["answer"], ) + + question, answers = quiz_entry.question, quiz_entry.answer answers = [answers] var_tol = DYNAMICALLY_GEN_VARIATION_TOLERANCE @@ -492,7 +499,7 @@ class TriviaQuiz(commands.Cog): description="", ) - sorted_dict = sorted(player_data.items(), key=lambda a: a[1], reverse=True) + sorted_dict = sorted(player_data.items(), key=operator.itemgetter(1), reverse=True) for item in sorted_dict: embed.description += f"{item[0]}: {item[1]}\n" @@ -553,7 +560,7 @@ class TriviaQuiz(commands.Cog): q_left: int, ) -> None: """Send the correct answer of a question to the game channel.""" - info = question_dict.get("info", "") + info = question_dict.get("info") plurality = " is" if len(answers) == 1 else "s are" @@ -566,7 +573,7 @@ class TriviaQuiz(commands.Cog): description="", ) - if info != "": + if info is not None: embed.description += f"**Information**\n{info}\n\n" embed.description += ( -- cgit v1.2.3 From d6066de336708df66e3d071115622a5db54c22b3 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Thu, 13 May 2021 23:47:07 +0530 Subject: change code style --- bot/exts/evergreen/stackoverflow.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index b85a0c97..46577cdd 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -54,18 +54,22 @@ class Stackoverflow(commands.Cog): return top5 = data["items"][:5] - embed = Embed(title=f"Search results for {search_query!r} - Stackoverflow", - url=SEARCH_URL.format(query=encoded_search_query), - description=f"Here are the top {len(top5)} results:", - color=Colours.orange) + embed = Embed( + title=f"Search results for {search_query!r} - Stackoverflow", + url=SEARCH_URL.format(query=encoded_search_query), + description=f"Here are the top {len(top5)} results:", + color=Colours.orange + ) for item in top5: embed.add_field( name=f"{unescape(item['title'])}", - value=(f"[{Emojis.stackoverflow_upvote} {item['score']} " - f"{Emojis.stackoverflow_views} {item['view_count']} " - f"{Emojis.stackoverflow_ans} {item['answer_count']} " - f"{Emojis.stackoverflow_tag} {', '.join(item['tags'][:3])}]" - f"({item['link']})"), + value=( + f"[{Emojis.stackoverflow_upvote} {item['score']} " + f"{Emojis.stackoverflow_views} {item['view_count']} " + f"{Emojis.stackoverflow_ans} {item['answer_count']} " + f"{Emojis.stackoverflow_tag} {', '.join(item['tags'][:3])}]" + f"({item['link']})" + ), inline=False) embed.set_footer(text="View the original link for more results.") try: -- cgit v1.2.3 From 4c7091708d92e8066045de25076959d94e4682e5 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Thu, 13 May 2021 23:51:41 +0530 Subject: remove redundant quote --- bot/exts/evergreen/stackoverflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 46577cdd..6a65bd0b 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -62,7 +62,7 @@ class Stackoverflow(commands.Cog): ) for item in top5: embed.add_field( - name=f"{unescape(item['title'])}", + name=unescape(item['title']), value=( f"[{Emojis.stackoverflow_upvote} {item['score']} " f"{Emojis.stackoverflow_views} {item['view_count']} " -- cgit v1.2.3 From b090b15b4df08b7908cca6016a65802fc779cf3c Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Thu, 13 May 2021 15:23:15 -0300 Subject: add a docstring to the dataclass --- bot/exts/evergreen/trivia_quiz.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 7aab8b87..b5a18516 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -63,6 +63,7 @@ UNITS_TO_BASE_UNITS = { @dataclass(frozen=True) class QuizEntry: + """Dataclass for a quiz entry (a question and a string containing answers separated by commas)""" question: str answer: str -- cgit v1.2.3 From 56896ebc00965de25d69c3781ae1c12235a3b166 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Thu, 13 May 2021 23:53:38 +0530 Subject: change typehint to bot.Bot --- bot/exts/evergreen/stackoverflow.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 6a65bd0b..bda66f95 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -6,6 +6,7 @@ from discord import Embed from discord.errors import HTTPException from discord.ext import commands +from bot import bot from bot.constants import Colours, Emojis logger = logging.getLogger(__name__) @@ -25,7 +26,7 @@ ERR_EMBED = Embed( class Stackoverflow(commands.Cog): """Contains command to interact with stackoverflow from discord.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: bot.Bot): self.bot = bot @commands.command(aliases=["so"]) @@ -82,7 +83,7 @@ class Stackoverflow(commands.Cog): await ctx.send(embed=search_query_too_long) -def setup(bot: commands.Bot) -> None: +def setup(bot: bot.Bot) -> None: """Loads Stackoverflow Cog.""" bot.add_cog(Stackoverflow(bot)) -- cgit v1.2.3 From 5c7818455ddf6411c86aea87c5da79f2b9eb3aa2 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Thu, 13 May 2021 23:54:08 +0530 Subject: squish imports --- bot/exts/evergreen/stackoverflow.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index bda66f95..e97e0318 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -2,8 +2,7 @@ import logging from html import unescape from urllib.parse import quote_plus -from discord import Embed -from discord.errors import HTTPException +from discord import Embed, HTTPException from discord.ext import commands from bot import bot -- cgit v1.2.3 From cf513ebd52cb337f760ae34e33380d1f09285c73 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Thu, 13 May 2021 15:24:40 -0300 Subject: fix the QuizEntry dataclass --- bot/exts/evergreen/trivia_quiz.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index b5a18516..4d18eaef 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -63,7 +63,8 @@ UNITS_TO_BASE_UNITS = { @dataclass(frozen=True) class QuizEntry: - """Dataclass for a quiz entry (a question and a string containing answers separated by commas)""" + """Dataclass for a quiz entry (a question and a string containing answers separated by commas).""" + question: str answer: str -- cgit v1.2.3 From aa3a38649e465c1299f3adfc5a5e4aa6fb8102b8 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Thu, 13 May 2021 15:27:08 -0300 Subject: delete trailing whitespace after docstring smh i have to stop making these errors lmao, definitely going to see that precommit thing after this --- bot/exts/evergreen/trivia_quiz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 4d18eaef..6d421668 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -64,7 +64,7 @@ UNITS_TO_BASE_UNITS = { @dataclass(frozen=True) class QuizEntry: """Dataclass for a quiz entry (a question and a string containing answers separated by commas).""" - + question: str answer: str -- cgit v1.2.3 From 42002ea00706732f5a67c9bbad194c104b046f25 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Thu, 13 May 2021 17:54:49 -0400 Subject: chore: Break up the HelpSession.build_pages method --- bot/exts/evergreen/help.py | 172 +++++++++++++++++++++++---------------------- 1 file changed, 87 insertions(+), 85 deletions(-) diff --git a/bot/exts/evergreen/help.py b/bot/exts/evergreen/help.py index 7e3fdad4..3c9ba4d2 100644 --- a/bot/exts/evergreen/help.py +++ b/bot/exts/evergreen/help.py @@ -287,22 +287,9 @@ class HelpSession: # Use LinePaginator to restrict embed line height paginator = LinePaginator(prefix="", suffix="", max_lines=self._max_lines) - prefix = constants.Client.prefix - # show signature if query is a command if isinstance(self.query, commands.Command): - signature = self._get_command_params(self.query) - parent = self.query.full_parent_name + " " if self.query.parent else "" - paginator.add_line(f"**```{prefix}{parent}{signature}```**") - - aliases = [f"`{alias}`" if not parent else f"`{parent} {alias}`" for alias in self.query.aliases] - aliases += [f"`{alias}`" for alias in getattr(self.query, "root_aliases", ())] - aliases = ", ".join(sorted(aliases)) - if aliases: - paginator.add_line(f"**Can also use:** {aliases}\n") - - if not await self.query.can_run(self._ctx): - paginator.add_line("***You cannot run this command.***\n") + await self._add_command_signature(paginator) if isinstance(self.query, Cog): paginator.add_line(f"**{self.query.name}**") @@ -312,97 +299,112 @@ class HelpSession: # list all children commands of the queried object if isinstance(self.query, (commands.GroupMixin, Cog)): + await self._list_child_commands(paginator) - # remove hidden commands if session is not wanting hiddens - if not self._show_hidden: - filtered = [c for c in self.query.commands if not c.hidden] - else: - filtered = self.query.commands - - # if after filter there are no commands, finish up - if not filtered: - self._pages = paginator.pages - return - - if isinstance(self.query, Cog): - grouped = (("**Commands:**", self.query.commands),) - - elif isinstance(self.query, commands.Command): - grouped = (("**Subcommands:**", self.query.commands),) - - # don't show prefix for subcommands - prefix = "" + self._pages = paginator.pages - # otherwise sort and organise all commands into categories - else: - cat_sort = sorted(filtered, key=self._category_key) - grouped = itertools.groupby(cat_sort, key=self._category_key) + async def _add_command_signature(self, paginator: LinePaginator) -> None: + prefix = constants.Client.prefix - for category, cmds in grouped: - cmds = sorted(cmds, key=lambda c: c.name) + signature = self._get_command_params(self.query) + parent = self.query.full_parent_name + " " if self.query.parent else "" + paginator.add_line(f"**```{prefix}{parent}{signature}```**") + aliases = [f"`{alias}`" if not parent else f"`{parent} {alias}`" for alias in self.query.aliases] + aliases += [f"`{alias}`" for alias in getattr(self.query, "root_aliases", ())] + aliases = ", ".join(sorted(aliases)) + if aliases: + paginator.add_line(f"**Can also use:** {aliases}\n") + if not await self.query.can_run(self._ctx): + paginator.add_line("***You cannot run this command.***\n") + + async def _list_child_commands(self, paginator: LinePaginator) -> None: + # remove hidden commands if session is not wanting hiddens + if not self._show_hidden: + filtered = [c for c in self.query.commands if not c.hidden] + else: + filtered = self.query.commands - if len(cmds) == 0: - continue + # if after filter there are no commands, finish up + if not filtered: + self._pages = paginator.pages + return - cat_cmds = [] + if isinstance(self.query, Cog): + grouped = (("**Commands:**", self.query.commands),) - for command in cmds: + elif isinstance(self.query, commands.Command): + grouped = (("**Subcommands:**", self.query.commands),) - # skip if hidden and hide if session is set to - if command.hidden and not self._show_hidden: - continue + # otherwise sort and organise all commands into categories + else: + cat_sort = sorted(filtered, key=self._category_key) + grouped = itertools.groupby(cat_sort, key=self._category_key) - # see if the user can run the command - strikeout = "" + for category, cmds in grouped: + await self._format_command_category(paginator, category, list(cmds)) - # Patch to make the !help command work outside of #bot-commands again - # This probably needs a proper rewrite, but this will make it work in - # the mean time. - try: - can_run = await command.can_run(self._ctx) - except CheckFailure: - can_run = False + async def _format_command_category(self, paginator: LinePaginator, category: str, cmds: List[Command]) -> None: + cmds = sorted(cmds, key=lambda c: c.name) + cat_cmds = [] + for command in cmds: + cat_cmds += await self._format_command(command) - if not can_run: - # skip if we don't show commands they can't run - if self._only_can_run: - continue - strikeout = "~~" + # state var for if the category should be added next + print_cat = 1 + new_page = True - signature = self._get_command_params(command) - info = f"{strikeout}**`{prefix}{signature}`**{strikeout}" + for details in cat_cmds: - # handle if the command has no docstring - if command.short_doc: - cat_cmds.append(f"{info}\n*{command.short_doc}*") - else: - cat_cmds.append(f"{info}\n*No details provided.*") + # keep details together, paginating early if it won"t fit + lines_adding = len(details.split("\n")) + print_cat + if paginator._linecount + lines_adding > self._max_lines: + paginator._linecount = 0 + new_page = True + paginator.close_page() - # state var for if the category should be added next + # new page so print category title again print_cat = 1 - new_page = True - for details in cat_cmds: + if print_cat: + if new_page: + paginator.add_line("") + paginator.add_line(category) + print_cat = 0 - # keep details together, paginating early if it won't fit - lines_adding = len(details.split("\n")) + print_cat - if paginator._linecount + lines_adding > self._max_lines: - paginator._linecount = 0 - new_page = True - paginator.close_page() + paginator.add_line(details) - # new page so print category title again - print_cat = 1 + async def _format_command(self, command: Command) -> List[str]: + # skip if hidden and hide if session is set to + if command.hidden and not self._show_hidden: + return [] - if print_cat: - if new_page: - paginator.add_line("") - paginator.add_line(category) - print_cat = 0 + # Patch to make the !help command work outside of #bot-commands again + # This probably needs a proper rewrite, but this will make it work in + # the mean time. + try: + can_run = await command.can_run(self._ctx) + except CheckFailure: + can_run = False + + # see if the user can run the command + strikeout = "" + if not can_run: + # skip if we don't show commands they can't run + if self._only_can_run: + return [] + strikeout = "~~" - paginator.add_line(details) + if isinstance(self.query, commands.Command): + prefix = "" + else: + prefix = constants.Client.prefix - self._pages = paginator.pages + signature = self._get_command_params(command) + info = f"{strikeout}**`{prefix}{signature}`**{strikeout}" + + # handle if the command has no docstring + short_doc = command.short_doc or "No details provided" + return [f"{info}\n*{short_doc}*"] def embed_page(self, page_number: int = 0) -> Embed: """Returns an Embed with the requested page formatted within.""" -- cgit v1.2.3 From f8c98972e2341fe621bc9b569a4b2fe0d1c5f391 Mon Sep 17 00:00:00 2001 From: Ice Wolfy Date: Fri, 14 May 2021 07:16:41 -0500 Subject: Added randomness to cat or dog when not specified --- bot/exts/evergreen/status_codes.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/bot/exts/evergreen/status_codes.py b/bot/exts/evergreen/status_codes.py index 7c00fe20..63b75a55 100644 --- a/bot/exts/evergreen/status_codes.py +++ b/bot/exts/evergreen/status_codes.py @@ -1,25 +1,35 @@ from http import HTTPStatus +from random import randint import discord from discord.ext import commands -from bot.utils.extensions import invoke_help_command - HTTP_DOG_URL = "https://httpstatusdogs.com/img/{code}.jpg" HTTP_CAT_URL = "https://http.cat/{code}.jpg" class HTTPStatusCodes(commands.Cog): - """Commands that give HTTP statuses described and visualized by cats and dogs.""" + """ + Commands that give HTTP statuses described and visualized by cats and dogs. + + Give a cat or dog visualization randomly if the option is not filled. + """ def __init__(self, bot: commands.Bot): self.bot = bot - @commands.group(name="http_status", aliases=("status", "httpstatus")) - async def http_status_group(self, ctx: commands.Context) -> None: + @commands.group(name="http_status", aliases=("status", "httpstatus"), invoke_without_command=True) + async def http_status_group(self, ctx: commands.Context, code: int) -> None: """Group containing dog and cat http status code commands.""" if not ctx.invoked_subcommand: - await invoke_help_command(ctx) + random = randint(0, 1) + if random == 0: + subcmd = self.bot.get_command("http_status cat") + else: + subcmd = self.bot.get_command("http_status dog") + + if await commands.Group.can_run(subcmd, ctx): + await subcmd(ctx, code) @http_status_group.command(name='cat') async def http_cat(self, ctx: commands.Context, code: int) -> None: -- cgit v1.2.3 From c4ee8a3bd9eeee61e3a2312f6c8a42f64858c8a6 Mon Sep 17 00:00:00 2001 From: Ice Wolfy Date: Fri, 14 May 2021 07:29:35 -0500 Subject: Added randomness to cat or dog when not specified --- bot/exts/evergreen/status_codes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/exts/evergreen/status_codes.py b/bot/exts/evergreen/status_codes.py index 63b75a55..0893cf99 100644 --- a/bot/exts/evergreen/status_codes.py +++ b/bot/exts/evergreen/status_codes.py @@ -11,7 +11,6 @@ HTTP_CAT_URL = "https://http.cat/{code}.jpg" class HTTPStatusCodes(commands.Cog): """ Commands that give HTTP statuses described and visualized by cats and dogs. - Give a cat or dog visualization randomly if the option is not filled. """ -- cgit v1.2.3 From 92c33d625068b9b39564dbf4fb9f6c1102711955 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Fri, 14 May 2021 12:34:30 -0400 Subject: chore: Refactor more code to follow our style guide --- bot/exts/evergreen/avatar_modification/_effects.py | 6 +- bot/exts/evergreen/connect_four.py | 70 +++++++++++----------- bot/exts/evergreen/game.py | 6 +- bot/exts/evergreen/issues.py | 16 ++--- bot/exts/evergreen/minesweeper.py | 12 ++-- bot/exts/evergreen/movie.py | 6 +- bot/exts/evergreen/snakes/_snakes_cog.py | 10 +++- bot/exts/evergreen/snakes/_utils.py | 16 ++--- bot/exts/evergreen/wolfram.py | 16 +++-- bot/exts/halloween/candy_collection.py | 8 ++- bot/exts/halloween/monstersurvey.py | 23 +++---- bot/exts/valentines/movie_generator.py | 6 +- bot/utils/__init__.py | 11 +++- bot/utils/checks.py | 30 ++++++---- 14 files changed, 136 insertions(+), 100 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/_effects.py b/bot/exts/evergreen/avatar_modification/_effects.py index d2370b4b..9339ecc4 100644 --- a/bot/exts/evergreen/avatar_modification/_effects.py +++ b/bot/exts/evergreen/avatar_modification/_effects.py @@ -251,7 +251,7 @@ class PfpEffects: total_width = multiplier * single_wdith total_height = multiplier * single_height - new_image = Image.new('RGBA', (total_width, total_height), (250, 250, 250)) + new_image = Image.new("RGBA", (total_width, total_height), (250, 250, 250)) width_multiplier = 0 height = 0 @@ -275,13 +275,13 @@ class PfpEffects: def mosaic_effect(img_bytes: bytes, squares: int, file_name: str) -> discord.File: """Seperate function run from an executor which turns an image into a mosaic.""" avatar = Image.open(BytesIO(img_bytes)) - avatar = avatar.convert('RGBA').resize((1024, 1024)) + avatar = avatar.convert("RGBA").resize((1024, 1024)) img_squares = PfpEffects.split_image(avatar, squares) new_img = PfpEffects.join_images(img_squares) bufferedio = BytesIO() - new_img.save(bufferedio, format='PNG') + new_img.save(bufferedio, format="PNG") bufferedio.seek(0) return discord.File(bufferedio, filename=file_name) diff --git a/bot/exts/evergreen/connect_four.py b/bot/exts/evergreen/connect_four.py index dc365a70..7a39d442 100644 --- a/bot/exts/evergreen/connect_four.py +++ b/bot/exts/evergreen/connect_four.py @@ -22,13 +22,13 @@ class Game: """A Connect 4 Game.""" def __init__( - self, - bot: Bot, - channel: discord.TextChannel, - player1: discord.Member, - player2: typing.Optional[discord.Member], - tokens: typing.List[str], - size: int = 7 + self, + bot: Bot, + channel: discord.TextChannel, + player1: discord.Member, + player2: typing.Optional[discord.Member], + tokens: typing.List[str], + size: int = 7 ) -> None: self.bot = bot @@ -286,20 +286,20 @@ class ConnectFour(commands.Cog): return True def get_player( - self, - ctx: commands.Context, - announcement: discord.Message, - reaction: discord.Reaction, - user: discord.Member + self, + ctx: commands.Context, + announcement: discord.Message, + reaction: discord.Reaction, + user: discord.Member ) -> bool: """Predicate checking the criteria for the announcement message.""" if self.already_playing(ctx.author): # If they've joined a game since requesting a player 2 return True # Is dealt with later on if ( - user.id not in (ctx.me.id, ctx.author.id) - and str(reaction.emoji) == Emojis.hand_raised - and reaction.message.id == announcement.id + user.id not in (ctx.me.id, ctx.author.id) + and str(reaction.emoji) == Emojis.hand_raised + and reaction.message.id == announcement.id ): if self.already_playing(user): self.bot.loop.create_task(ctx.send(f"{user.mention} You're already playing a game!")) @@ -316,9 +316,9 @@ class ConnectFour(commands.Cog): return True if ( - user.id == ctx.author.id - and str(reaction.emoji) == CROSS_EMOJI - and reaction.message.id == announcement.id + user.id == ctx.author.id + and str(reaction.emoji) == CROSS_EMOJI + and reaction.message.id == announcement.id ): return True return False @@ -329,7 +329,7 @@ class ConnectFour(commands.Cog): @staticmethod def check_emojis( - e1: EMOJI_CHECK, e2: EMOJI_CHECK + e1: EMOJI_CHECK, e2: EMOJI_CHECK ) -> typing.Tuple[bool, typing.Optional[str]]: """Validate the emojis, the user put.""" if isinstance(e1, str) and emojis.count(e1) != 1: @@ -339,12 +339,12 @@ class ConnectFour(commands.Cog): return True, None async def _play_game( - self, - ctx: commands.Context, - user: typing.Optional[discord.Member], - board_size: int, - emoji1: str, - emoji2: str + self, + ctx: commands.Context, + user: typing.Optional[discord.Member], + board_size: int, + emoji1: str, + emoji2: str ) -> None: """Helper for playing a game of connect four.""" self.tokens = [":white_circle:", str(emoji1), str(emoji2)] @@ -369,11 +369,11 @@ class ConnectFour(commands.Cog): case_insensitive=True ) async def connect_four( - self, - ctx: commands.Context, - board_size: int = 7, - emoji1: EMOJI_CHECK = "\U0001f535", - emoji2: EMOJI_CHECK = "\U0001f534" + self, + ctx: commands.Context, + board_size: int = 7, + emoji1: EMOJI_CHECK = "\U0001f535", + emoji2: EMOJI_CHECK = "\U0001f534" ) -> None: """ Play the classic game of Connect Four with someone! @@ -430,11 +430,11 @@ class ConnectFour(commands.Cog): @guild_only() @connect_four.command(aliases=["bot", "computer", "cpu"]) async def ai( - self, - ctx: commands.Context, - board_size: int = 7, - emoji1: EMOJI_CHECK = "\U0001f535", - emoji2: EMOJI_CHECK = "\U0001f534" + self, + ctx: commands.Context, + board_size: int = 7, + emoji1: EMOJI_CHECK = "\U0001f535", + emoji2: EMOJI_CHECK = "\U0001f534" ) -> None: """Play Connect Four against a computer player.""" check, emoji = self.check_emojis(emoji1, emoji2) diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index 6f01d81c..43f64be7 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -374,8 +374,10 @@ class Games(Cog): release_date = dt.utcfromtimestamp(data["first_release_date"]).date() if "first_release_date" in data else "?" # Create Age Ratings value - rating = ", ".join(f"{AgeRatingCategories(age['category']).name} {AgeRatings(age['rating']).name}" - for age in data["age_ratings"]) if "age_ratings" in data else "?" + rating = ", ".join( + f"{AgeRatingCategories(age['category']).name} {AgeRatings(age['rating']).name}" + for age in data["age_ratings"] + ) if "age_ratings" in data else "?" companies = [c["company"]["name"] for c in data["involved_companies"]] if "involved_companies" in data else "?" diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 5bbc57c6..b67aa4a6 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -162,9 +162,9 @@ class Issues(commands.Cog): @staticmethod def format_embed( - results: t.List[t.Union[IssueState, FetchError]], - user: str, - repository: t.Optional[str] = None + results: t.List[t.Union[IssueState, FetchError]], + user: str, + repository: t.Optional[str] = None ) -> discord.Embed: """Take a list of IssueState or FetchError and format a Discord embed for them.""" description_list = [] @@ -187,11 +187,11 @@ class Issues(commands.Cog): @whitelist_override(channels=WHITELISTED_CHANNELS, categories=WHITELISTED_CATEGORIES) @commands.command(aliases=("pr",)) async def issue( - self, - ctx: commands.Context, - numbers: commands.Greedy[int], - repository: str = "sir-lancebot", - user: str = "python-discord" + self, + ctx: commands.Context, + numbers: commands.Greedy[int], + repository: str = "sir-lancebot", + user: str = "python-discord" ) -> None: """Command to retrieve issue(s) from a GitHub repository.""" # Remove duplicates diff --git a/bot/exts/evergreen/minesweeper.py b/bot/exts/evergreen/minesweeper.py index 7a31dfde..932358f9 100644 --- a/bot/exts/evergreen/minesweeper.py +++ b/bot/exts/evergreen/minesweeper.py @@ -212,12 +212,12 @@ class Minesweeper(commands.Cog): return True async def reveal_one( - self, - ctx: commands.Context, - revealed: GameBoard, - board: GameBoard, - x: int, - y: int + self, + ctx: commands.Context, + revealed: GameBoard, + board: GameBoard, + x: int, + y: int ) -> bool: """ Reveal one square. diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index fa284417..ff23df4c 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -79,8 +79,10 @@ class Movie(Cog): # Check if "results" is in result. If not, throw error. if "results" not in result: - err_msg = f"There is problem while making TMDB API request. Response Code: {result['status_code']}, " \ - f"{result['status_message']}." + err_msg = ( + f"There is problem while making TMDB API request. Response Code: {result['status_code']}, " + f"{result['status_message']}." + ) await ctx.send(err_msg) logger.warning(err_msg) diff --git a/bot/exts/evergreen/snakes/_snakes_cog.py b/bot/exts/evergreen/snakes/_snakes_cog.py index c50b23c5..07d3c363 100644 --- a/bot/exts/evergreen/snakes/_snakes_cog.py +++ b/bot/exts/evergreen/snakes/_snakes_cog.py @@ -579,9 +579,13 @@ class Snakes(Cog): antidote_embed = Embed(color=SNAKE_COLOR, title="Antidote") antidote_embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar_url) antidote_embed.set_image(url="https://media.giphy.com/media/ceeN6U57leAhi/giphy.gif") - antidote_embed.add_field(name=EMPTY_UNICODE, - value=f"Sorry you didnt make the antidote in time.\n" - f"The formula was {' '.join(antidote_answer)}") + antidote_embed.add_field( + name=EMPTY_UNICODE, + value=( + f"Sorry you didnt make the antidote in time.\n" + f"The formula was {' '.join(antidote_answer)}" + ) + ) await board_id.edit(embed=antidote_embed) log.debug("Ending pagination and removing all reactions...") diff --git a/bot/exts/evergreen/snakes/_utils.py b/bot/exts/evergreen/snakes/_utils.py index 8b39f217..0a5894b7 100644 --- a/bot/exts/evergreen/snakes/_utils.py +++ b/bot/exts/evergreen/snakes/_utils.py @@ -17,38 +17,38 @@ from bot.constants import Roles SNAKE_RESOURCES = Path("bot/resources/snakes").absolute() -h1 = r'''``` +h1 = r"""``` ---- ------ /--------\ |--------| |--------| \------/ - ----```''' -h2 = r'''``` + ----```""" +h2 = r"""``` ---- ------ /---\-/--\ |-----\--| |--------| \------/ - ----```''' -h3 = r'''``` + ----```""" +h3 = r"""``` ---- ------ /---\-/--\ |-----\--| |-----/--| \----\-/ - ----```''' -h4 = r'''``` + ----```""" +h4 = r"""``` ----- ----- \ /--| /---\ |--\ -\---| |--\--/-- / \------- / - ------```''' + ------```""" stages = [h1, h2, h3, h4] snakes = { "Baby Python": "https://i.imgur.com/SYOcmSa.png", diff --git a/bot/exts/evergreen/wolfram.py b/bot/exts/evergreen/wolfram.py index ca7d8454..d23afd6f 100644 --- a/bot/exts/evergreen/wolfram.py +++ b/bot/exts/evergreen/wolfram.py @@ -40,9 +40,11 @@ async def send_embed( """Generate & send a response embed with Wolfram as the author.""" embed = Embed(colour=colour) embed.description = message_txt - embed.set_author(name="Wolfram Alpha", - icon_url=WOLF_IMAGE, - url="https://www.wolframalpha.com/") + embed.set_author( + name="Wolfram Alpha", + icon_url=WOLF_IMAGE, + url="https://www.wolframalpha.com/" + ) if footer: embed.set_footer(text=footer) @@ -222,9 +224,11 @@ class Wolfram(Cog): return embed = Embed() - embed.set_author(name="Wolfram Alpha", - icon_url=WOLF_IMAGE, - url="https://www.wolframalpha.com/") + embed.set_author( + name="Wolfram Alpha", + icon_url=WOLF_IMAGE, + url="https://www.wolframalpha.com/" + ) embed.colour = Colours.soft_orange await ImagePaginator.paginate(pages, ctx, embed) diff --git a/bot/exts/halloween/candy_collection.py b/bot/exts/halloween/candy_collection.py index 14efa1fb..4afd5913 100644 --- a/bot/exts/halloween/candy_collection.py +++ b/bot/exts/halloween/candy_collection.py @@ -155,8 +155,12 @@ class CandyCollection(commands.Cog): ) -> None: """An alternative spooky message sent when user has no candies in the collection.""" embed = discord.Embed(color=author.color) - embed.set_author(name="Ghosts and Ghouls and Jack o' lanterns at night; " - "I tried to take your candies but you had none to begin with!") + embed.set_author( + name=( + "Ghosts and Ghouls and Jack o' lanterns at night; " + "I tried to take your candies but you had none to begin with!" + ) + ) await channel.send(embed=embed) @in_month(Month.OCTOBER) diff --git a/bot/exts/halloween/monstersurvey.py b/bot/exts/halloween/monstersurvey.py index 240a97db..96cda11e 100644 --- a/bot/exts/halloween/monstersurvey.py +++ b/bot/exts/halloween/monstersurvey.py @@ -71,7 +71,8 @@ class MonsterSurvey(Cog): default_embed.add_field( name=".monster show monster_name(optional)", value="Show a specific monster. If none is listed, it will give you an error with valid choices.", - inline=False) + inline=False + ) default_embed.add_field( name=".monster vote monster_name", value="Vote for a specific monster. You get one vote, but can change it at any time.", @@ -182,15 +183,17 @@ class MonsterSurvey(Cog): for rank, m in enumerate(top): votes = len(vr[m]["votes"]) percentage = ((votes / total_votes) * 100) if total_votes > 0 else 0 - embed.add_field(name=f"{rank+1}. {vr[m]['full_name']}", - value=( - f"{votes} votes. {percentage:.1f}% of total votes.\n" - f"Vote for this monster by typing " - f"'.monster vote {m}'\n" - f"Get more information on this monster by typing " - f"'.monster show {m}'" - ), - inline=False) + embed.add_field( + name=f"{rank+1}. {vr[m]['full_name']}", + value=( + f"{votes} votes. {percentage:.1f}% of total votes.\n" + f"Vote for this monster by typing " + f"'.monster vote {m}'\n" + f"Get more information on this monster by typing " + f"'.monster show {m}'" + ), + inline=False + ) embed.set_footer(text="You can also vote by their rank number. '.monster vote {number}' ") diff --git a/bot/exts/valentines/movie_generator.py b/bot/exts/valentines/movie_generator.py index 4508c3b2..0fc5edb4 100644 --- a/bot/exts/valentines/movie_generator.py +++ b/bot/exts/valentines/movie_generator.py @@ -54,8 +54,10 @@ class RomanceMovieFinder(commands.Cog): embed.set_thumbnail(url="https://i.imgur.com/LtFtC8H.png") await ctx.send(embed=embed) except KeyError: - warning_message = "A KeyError was raised while fetching information on the movie. The API service" \ - " could be unavailable or the API key could be set incorrectly." + warning_message = ( + "A KeyError was raised while fetching information on the movie. The API service" + " could be unavailable or the API key could be set incorrectly." + ) embed = discord.Embed(title=warning_message) log.warning(warning_message) await ctx.send(embed=embed) diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py index 09a4dfc3..a1985019 100644 --- a/bot/utils/__init__.py +++ b/bot/utils/__init__.py @@ -3,7 +3,7 @@ import contextlib import re import string from datetime import datetime -from typing import Iterable, List +from typing import Iterable, List, Optional import discord from discord.ext.commands import BadArgument, Context @@ -31,8 +31,13 @@ def resolve_current_month() -> Month: async def disambiguate( - ctx: Context, entries: List[str], *, timeout: float = 30, - entries_per_page: int = 20, empty: bool = False, embed: discord.Embed = None + ctx: Context, + entries: List[str], + *, + timeout: float = 30, + entries_per_page: int = 20, + empty: bool = False, + embed: Optional[discord.Embed] = None ) -> str: """ Has the user choose between multiple entries in case one could not be chosen automatically. diff --git a/bot/utils/checks.py b/bot/utils/checks.py index 3783dd38..c06b6870 100644 --- a/bot/utils/checks.py +++ b/bot/utils/checks.py @@ -92,8 +92,10 @@ def in_whitelist_check( def with_role_check(ctx: Context, *role_ids: int) -> bool: """Returns True if the user has any one of the roles in role_ids.""" if not ctx.guild: # Return False in a DM - log.trace(f"{ctx.author} tried to use the '{ctx.command.name}'command from a DM. " - "This command is restricted by the with_role decorator. Rejecting request.") + log.trace( + f"{ctx.author} tried to use the '{ctx.command.name}'command from a DM. " + "This command is restricted by the with_role decorator. Rejecting request." + ) return False for role in ctx.author.roles: @@ -101,22 +103,28 @@ def with_role_check(ctx: Context, *role_ids: int) -> bool: log.trace(f"{ctx.author} has the '{role.name}' role, and passes the check.") return True - log.trace(f"{ctx.author} does not have the required role to use " - f"the '{ctx.command.name}' command, so the request is rejected.") + log.trace( + f"{ctx.author} does not have the required role to use " + f"the '{ctx.command.name}' command, so the request is rejected." + ) return False def without_role_check(ctx: Context, *role_ids: int) -> bool: """Returns True if the user does not have any of the roles in role_ids.""" if not ctx.guild: # Return False in a DM - log.trace(f"{ctx.author} tried to use the '{ctx.command.name}' command from a DM. " - "This command is restricted by the without_role decorator. Rejecting request.") + log.trace( + f"{ctx.author} tried to use the '{ctx.command.name}' command from a DM. " + "This command is restricted by the without_role decorator. Rejecting request." + ) return False author_roles = [role.id for role in ctx.author.roles] check = all(role not in author_roles for role in role_ids) - log.trace(f"{ctx.author} tried to call the '{ctx.command.name}' command. " - f"The result of the without_role check was {check}.") + log.trace( + f"{ctx.author} tried to call the '{ctx.command.name}' command. " + f"The result of the without_role check was {check}." + ) return check @@ -154,8 +162,10 @@ def cooldown_with_role_bypass(rate: int, per: float, type: BucketType = BucketTy # # If the `before_invoke` detail is ever a problem then I can quickly just swap over. if not isinstance(command, Command): - raise TypeError("Decorator `cooldown_with_role_bypass` must be applied after the command decorator. " - "This means it has to be above the command decorator in the code.") + raise TypeError( + "Decorator `cooldown_with_role_bypass` must be applied after the command decorator. " + "This means it has to be above the command decorator in the code." + ) command._before_invoke = predicate -- cgit v1.2.3 From d70e6ce0ed8e35b731f0e709b690879fe9c70506 Mon Sep 17 00:00:00 2001 From: Ice Wolfy Date: Fri, 14 May 2021 13:01:20 -0500 Subject: Fixes linting issue and improves randomness selection. --- bot/exts/evergreen/status_codes.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/bot/exts/evergreen/status_codes.py b/bot/exts/evergreen/status_codes.py index 0893cf99..1fb2231c 100644 --- a/bot/exts/evergreen/status_codes.py +++ b/bot/exts/evergreen/status_codes.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from random import randint +from random import choice import discord from discord.ext import commands @@ -11,6 +11,7 @@ HTTP_CAT_URL = "https://http.cat/{code}.jpg" class HTTPStatusCodes(commands.Cog): """ Commands that give HTTP statuses described and visualized by cats and dogs. + Give a cat or dog visualization randomly if the option is not filled. """ @@ -19,16 +20,15 @@ class HTTPStatusCodes(commands.Cog): @commands.group(name="http_status", aliases=("status", "httpstatus"), invoke_without_command=True) async def http_status_group(self, ctx: commands.Context, code: int) -> None: - """Group containing dog and cat http status code commands.""" - if not ctx.invoked_subcommand: - random = randint(0, 1) - if random == 0: - subcmd = self.bot.get_command("http_status cat") - else: - subcmd = self.bot.get_command("http_status dog") - - if await commands.Group.can_run(subcmd, ctx): - await subcmd(ctx, code) + """Randomize cat/dog picture output when a sub-command is not specified.""" + subcmd = choice((self.http_cat, self.http_dog)) + # if random == 0: + # subcmd = self.bot.get_command("http_status cat") + # else: + # subcmd = self.bot.get_command("http_status dog") + + if await subcmd.can_run(ctx): + await subcmd(ctx, code) @http_status_group.command(name='cat') async def http_cat(self, ctx: commands.Context, code: int) -> None: -- cgit v1.2.3 From 5a6d25aea9a1cae58d1b147bc95c027972aad2a0 Mon Sep 17 00:00:00 2001 From: Icebluewolf <44532864+Icebluewolf@users.noreply.github.com> Date: Fri, 14 May 2021 13:50:30 -0500 Subject: Remove comments and update docstrings Co-authored-by: Joe Banks --- bot/exts/evergreen/status_codes.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/status_codes.py b/bot/exts/evergreen/status_codes.py index 1fb2231c..0fe9ba47 100644 --- a/bot/exts/evergreen/status_codes.py +++ b/bot/exts/evergreen/status_codes.py @@ -10,9 +10,9 @@ HTTP_CAT_URL = "https://http.cat/{code}.jpg" class HTTPStatusCodes(commands.Cog): """ - Commands that give HTTP statuses described and visualized by cats and dogs. + Fetch an image depicting HTTP status codes as a dog or a cat. - Give a cat or dog visualization randomly if the option is not filled. + If neither animal is selected a cat or dog is chosen randomly for the given status code. """ def __init__(self, bot: commands.Bot): @@ -20,12 +20,8 @@ class HTTPStatusCodes(commands.Cog): @commands.group(name="http_status", aliases=("status", "httpstatus"), invoke_without_command=True) async def http_status_group(self, ctx: commands.Context, code: int) -> None: - """Randomize cat/dog picture output when a sub-command is not specified.""" + """Choose a cat or dog randomly for the given status code.""" subcmd = choice((self.http_cat, self.http_dog)) - # if random == 0: - # subcmd = self.bot.get_command("http_status cat") - # else: - # subcmd = self.bot.get_command("http_status dog") if await subcmd.can_run(ctx): await subcmd(ctx, code) -- cgit v1.2.3 From c6544d0fdc71be42a816c33971b19485137afcc8 Mon Sep 17 00:00:00 2001 From: Shivansh Date: Sat, 15 May 2021 09:26:43 +0530 Subject: (tic-tac-toe): Update x and o emoji --- bot/constants.py | 3 +++ bot/exts/evergreen/tic_tac_toe.py | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 549d01b6..d50dcc6e 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -214,6 +214,9 @@ class Emojis: x = "\U0001f1fd" o = "\U0001f1f4" + x_square = "<:x_square:632278427260682281>" + o_square = "<:o_square:632278452413661214>" + status_online = "<:status_online:470326272351010816>" status_idle = "<:status_idle:470326266625785866>" status_dnd = "<:status_dnd:470326272082313216>" diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 6e21528e..5d0f15b4 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -79,7 +79,7 @@ class AI: """Get move from AI. AI use Minimax strategy.""" possible_moves = [i for i, emoji in board.items() if emoji in list(Emojis.number_emojis.values())] - for symbol in (Emojis.o, Emojis.x): + for symbol in (Emojis.o_square, Emojis.x_square): for move in possible_moves: board_copy = board.copy() board_copy[move] = symbol @@ -266,12 +266,12 @@ class TicTacToe(Cog): return if opponent is None: game = Game( - [Player(ctx.author, ctx, Emojis.x), AI(Emojis.o)], + [Player(ctx.author, ctx, Emojis.x_square), AI(Emojis.o_square)], ctx ) else: game = Game( - [Player(ctx.author, ctx, Emojis.x), Player(opponent, ctx, Emojis.o)], + [Player(ctx.author, ctx, Emojis.x_square), Player(opponent, ctx, Emojis.o_square)], ctx ) self.games.append(game) -- cgit v1.2.3 From 8f3401890f035f3530a52b452dd6519d0b89ddcb Mon Sep 17 00:00:00 2001 From: Shivansh Date: Sat, 15 May 2021 09:28:07 +0530 Subject: (tic-tac-toe): Use embeds for showing previous game boards --- bot/exts/evergreen/tic_tac_toe.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 5d0f15b4..6c33ebc4 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -318,8 +318,12 @@ class TicTacToe(Cog): await ctx.send("Game don't exist.") return game = self.games[game_id - 1] - await ctx.send(f"{game.winner} :trophy: vs {game.loser}") - await ctx.send(game.format_board()) + + embed = discord.Embed( + title=f"Match #{game_id} Game Board", + description=f"{game.winner} :trophy: vs {game.loser}\n\n{game.format_board()}" + ) + await ctx.send(embed=embed) def setup(bot: Bot) -> None: -- cgit v1.2.3 From 4683f9482a33103d8da611be7f5d4a2bb402e373 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 15 May 2021 12:20:39 +0100 Subject: Add concurrency behaviour to github actions This grouping means any new actions on either a PR or against main, will cancel any running actions. We do not care about these old actions, as they are out of date, so cancelling them will mean the actions we do care about get done faster. --- .github/workflows/build.yaml | 4 ++++ .github/workflows/lint.yaml | 3 +++ .github/workflows/sentry_release.yaml | 4 ++++ .github/workflows/status_embed.yaml | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index baa046ce..dac711f8 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -8,6 +8,10 @@ on: types: - completed +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + jobs: build: if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 7f157da3..3a9b6dbd 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -6,6 +6,9 @@ on: - main pull_request: +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true jobs: lint: diff --git a/.github/workflows/sentry_release.yaml b/.github/workflows/sentry_release.yaml index 3d15e01e..e7bc0429 100644 --- a/.github/workflows/sentry_release.yaml +++ b/.github/workflows/sentry_release.yaml @@ -5,6 +5,10 @@ on: branches: - main +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + jobs: create_sentry_release: runs-on: ubuntu-latest diff --git a/.github/workflows/status_embed.yaml b/.github/workflows/status_embed.yaml index 28caa8c2..23677488 100644 --- a/.github/workflows/status_embed.yaml +++ b/.github/workflows/status_embed.yaml @@ -8,6 +8,10 @@ on: types: - completed +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + jobs: status_embed: # We send the embed in the following situations: -- cgit v1.2.3 From 95e3bcbee2a68b1379b35c74872df70977cae592 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Sat, 15 May 2021 11:43:11 -0400 Subject: chore: Remove redundant f-strings --- bot/exts/christmas/advent_of_code/_cog.py | 2 +- bot/exts/christmas/hanukkah_embed.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index ce6a4cb6..ead84544 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -115,7 +115,7 @@ class AdventOfCode(commands.Cog): delta_str = f"{delta.days} days" await ctx.send( - f"The Advent of Code event is not currently running. " + "The Advent of Code event is not currently running. " f"The next event will start in {delta_str}." ) return diff --git a/bot/exts/christmas/hanukkah_embed.py b/bot/exts/christmas/hanukkah_embed.py index af5cfccf..77ef4684 100644 --- a/bot/exts/christmas/hanukkah_embed.py +++ b/bot/exts/christmas/hanukkah_embed.py @@ -67,14 +67,14 @@ class HanukkahEmbed(commands.Cog): hanukkah_start_hour = 18 if hours < hanukkah_start_hour: embed.description = ( - f"Hanukkah hasnt started yet, " + "Hanukkah hasnt started yet, " f"it will start in about {hanukkah_start_hour - hours} hour/s." ) await ctx.send(embed=embed) return elif hours > hanukkah_start_hour: embed.description = ( - f"It is the starting day of Hanukkah ! " + "It is the starting day of Hanukkah ! " f"Its been {hours - hanukkah_start_hour} hours hanukkah started !" ) await ctx.send(embed=embed) -- cgit v1.2.3 From 1c07a8c874cdb421b456daec5c4398a0414b3d17 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 15 May 2021 16:46:41 +0100 Subject: Namespace each concurrency group This stops different actions, from the same commit, running in parallel from canceling each other. --- .github/workflows/build.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/sentry_release.yaml | 2 +- .github/workflows/status_embed.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index dac711f8..e857a6cf 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -9,7 +9,7 @@ on: - completed concurrency: - group: ${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 3a9b6dbd..9e9cbbbf 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -7,7 +7,7 @@ on: pull_request: concurrency: - group: ${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: diff --git a/.github/workflows/sentry_release.yaml b/.github/workflows/sentry_release.yaml index e7bc0429..c1073386 100644 --- a/.github/workflows/sentry_release.yaml +++ b/.github/workflows/sentry_release.yaml @@ -6,7 +6,7 @@ on: - main concurrency: - group: ${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: diff --git a/.github/workflows/status_embed.yaml b/.github/workflows/status_embed.yaml index 23677488..737efe00 100644 --- a/.github/workflows/status_embed.yaml +++ b/.github/workflows/status_embed.yaml @@ -9,7 +9,7 @@ on: - completed concurrency: - group: ${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: -- cgit v1.2.3 From c09ab5c5b8a2d682c320229271e75f21d7b33256 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Sat, 15 May 2021 11:57:14 -0400 Subject: chore: Reformat bot/utils/__init__.py --- bot/utils/__init__.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py index a1985019..bef12d25 100644 --- a/bot/utils/__init__.py +++ b/bot/utils/__init__.py @@ -56,17 +56,21 @@ async def disambiguate( choices = (f"{index}: {entry}" for index, entry in enumerate(entries, start=1)) def check(message: discord.Message) -> bool: - return (message.content.isdecimal() - and message.author == ctx.author - and message.channel == ctx.channel) + return ( + message.content.isdecimal() + and message.author == ctx.author + and message.channel == ctx.channel + ) try: if embed is None: embed = discord.Embed() coro1 = ctx.bot.wait_for("message", check=check, timeout=timeout) - coro2 = LinePaginator.paginate(choices, ctx, embed=embed, max_lines=entries_per_page, - empty=empty, max_size=6000, timeout=9000) + coro2 = LinePaginator.paginate( + choices, ctx, embed=embed, max_lines=entries_per_page, + empty=empty, max_size=6000, timeout=9000 + ) # wait_for timeout will go to except instead of the wait_for thing as I expected futures = [asyncio.ensure_future(coro1), asyncio.ensure_future(coro2)] @@ -102,7 +106,7 @@ async def disambiguate( def replace_many( - sentence: str, replacements: dict, *, ignore_case: bool = False, match_case: bool = False + sentence: str, replacements: dict, *, ignore_case: bool = False, match_case: bool = False ) -> str: """ Replaces multiple substrings in a string given a mapping of strings. -- cgit v1.2.3 From e1361d03fac450365d4bdc8d10d8bcf2c99b479f Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Sat, 15 May 2021 12:32:10 -0400 Subject: chore: Update myvalenstate.py Removes a redundant f string, and when is one leftover match, get the first item of the match list instead of having [''] --- bot/exts/valentines/myvalenstate.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bot/exts/valentines/myvalenstate.py b/bot/exts/valentines/myvalenstate.py index d2409dcc..c699de94 100644 --- a/bot/exts/valentines/myvalenstate.py +++ b/bot/exts/valentines/myvalenstate.py @@ -61,8 +61,7 @@ class MyValenstate(commands.Cog): embed_text = f"You have {len(matches)} more matches, these being {leftovers}." elif len(matches) == 1: embed_title = "But there's another one!" - leftovers = str(matches) - embed_text = f"You have another match, this being {leftovers}." + embed_text = f"You have another match, this being {matches[0]}." else: embed_title = "You have a true match!" embed_text = "This state is your true Valenstate! There are no states that would suit" \ @@ -70,7 +69,7 @@ class MyValenstate(commands.Cog): embed = discord.Embed( title=f"Your Valenstate is {valenstate} \u2764", - description=f"{STATES[valenstate]['text']}", + description=STATES[valenstate]['text'], colour=Colours.pink ) embed.add_field(name=embed_title, value=embed_text) -- cgit v1.2.3 From 55d472a97efea392b4eb2b198ffa6f5f5026072d Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Sat, 15 May 2021 12:53:00 -0400 Subject: chore: Make all aliases in commands tuples --- bot/exts/christmas/hanukkah_embed.py | 2 +- bot/exts/easter/earth_photos.py | 2 +- bot/exts/easter/easter_riddle.py | 2 +- bot/exts/easter/egg_decorating.py | 2 +- bot/exts/easter/egg_facts.py | 2 +- bot/exts/easter/egghead_quiz.py | 2 +- bot/exts/evergreen/battleship.py | 2 +- bot/exts/evergreen/catify.py | 2 +- bot/exts/evergreen/connect_four.py | 4 ++-- bot/exts/evergreen/game.py | 12 ++++++------ bot/exts/evergreen/movie.py | 4 ++-- bot/exts/evergreen/pythonfacts.py | 2 +- bot/exts/evergreen/recommend_game.py | 2 +- bot/exts/evergreen/space.py | 2 +- bot/exts/evergreen/timed.py | 2 +- bot/exts/evergreen/trivia_quiz.py | 2 +- bot/exts/evergreen/wikipedia.py | 2 +- bot/exts/evergreen/wonder_twins.py | 2 +- bot/exts/halloween/spookynamerate.py | 4 ++-- bot/exts/pride/drag_queen_name.py | 2 +- bot/exts/pride/pride_anthem.py | 2 +- bot/exts/pride/pride_facts.py | 2 +- bot/exts/valentines/valentine_zodiac.py | 2 +- 23 files changed, 31 insertions(+), 31 deletions(-) diff --git a/bot/exts/christmas/hanukkah_embed.py b/bot/exts/christmas/hanukkah_embed.py index 77ef4684..7a06b558 100644 --- a/bot/exts/christmas/hanukkah_embed.py +++ b/bot/exts/christmas/hanukkah_embed.py @@ -38,7 +38,7 @@ class HanukkahEmbed(commands.Cog): return hanukkah_dates @in_month(Month.DECEMBER) - @commands.command(name="hanukkah", aliases=["chanukah"]) + @commands.command(name="hanukkah", aliases=("chanukah",)) async def hanukkah_festival(self, ctx: commands.Context) -> None: """Tells you about the Hanukkah Festivaltime of festival, festival day, etc).""" hanukkah_dates = await self.get_hanukkah_dates() diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 0e82e99a..9df4f9ca 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -16,7 +16,7 @@ class EarthPhotos(commands.Cog): def __init__(self, bot: Bot): self.bot = bot - @commands.command(aliases=["earth"]) + @commands.command(aliases=("earth",)) async def earth_photos(self, ctx: commands.Context) -> None: """Returns a random photo of earth, sourced from Unsplash.""" async with ctx.typing(): diff --git a/bot/exts/easter/easter_riddle.py b/bot/exts/easter/easter_riddle.py index 01b956f1..a87f93d1 100644 --- a/bot/exts/easter/easter_riddle.py +++ b/bot/exts/easter/easter_riddle.py @@ -26,7 +26,7 @@ class EasterRiddle(commands.Cog): self.correct = "" self.current_channel = None - @commands.command(aliases=["riddlemethis", "riddleme"]) + @commands.command(aliases=("riddlemethis", "riddleme")) async def riddle(self, ctx: commands.Context) -> None: """ Gives a random riddle, then provides 2 hints at certain intervals before revealing the answer. diff --git a/bot/exts/easter/egg_decorating.py b/bot/exts/easter/egg_decorating.py index 7448f702..d4b27b20 100644 --- a/bot/exts/easter/egg_decorating.py +++ b/bot/exts/easter/egg_decorating.py @@ -41,7 +41,7 @@ class EggDecorating(commands.Cog): return int(XKCD_COLOURS[colour], 16) return None - @commands.command(aliases=["decorateegg"]) + @commands.command(aliases=("decorateegg",)) async def eggdecorate( self, ctx: commands.Context, *colours: Union[discord.Colour, str] ) -> Union[Image.Image, discord.Message]: diff --git a/bot/exts/easter/egg_facts.py b/bot/exts/easter/egg_facts.py index c1c43f80..d60032f0 100644 --- a/bot/exts/easter/egg_facts.py +++ b/bot/exts/easter/egg_facts.py @@ -40,7 +40,7 @@ class EasterFacts(commands.Cog): channel = self.bot.get_channel(Channels.community_bot_commands) await channel.send(embed=self.make_embed()) - @commands.command(name="eggfact", aliases=["fact"]) + @commands.command(name="eggfact", aliases=("fact",)) async def easter_facts(self, ctx: commands.Context) -> None: """Get easter egg facts.""" embed = self.make_embed() diff --git a/bot/exts/easter/egghead_quiz.py b/bot/exts/easter/egghead_quiz.py index 4b67310f..7c4960cd 100644 --- a/bot/exts/easter/egghead_quiz.py +++ b/bot/exts/easter/egghead_quiz.py @@ -34,7 +34,7 @@ class EggheadQuiz(commands.Cog): def __init__(self) -> None: self.quiz_messages = {} - @commands.command(aliases=["eggheadquiz", "easterquiz"]) + @commands.command(aliases=("eggheadquiz", "easterquiz")) async def eggquiz(self, ctx: commands.Context) -> None: """ Gives a random quiz question, waits 30 seconds and then outputs the answer. diff --git a/bot/exts/evergreen/battleship.py b/bot/exts/evergreen/battleship.py index d4584ae8..27c50bdb 100644 --- a/bot/exts/evergreen/battleship.py +++ b/bot/exts/evergreen/battleship.py @@ -434,7 +434,7 @@ class Battleship(commands.Cog): self.games.remove(game) raise - @battleship.command(name="ships", aliases=["boats"]) + @battleship.command(name="ships", aliases=("boats",)) async def battleship_ships(self, ctx: commands.Context) -> None: """Lists the ships that are found on the battleship grid.""" embed = discord.Embed(colour=Colours.blue) diff --git a/bot/exts/evergreen/catify.py b/bot/exts/evergreen/catify.py index 3182f69e..32dfae09 100644 --- a/bot/exts/evergreen/catify.py +++ b/bot/exts/evergreen/catify.py @@ -13,7 +13,7 @@ from bot.utils import helpers class Catify(commands.Cog): """Cog for the catify command.""" - @commands.command(aliases=["ᓚᘏᗢify", "ᓚᘏᗢ"]) + @commands.command(aliases=("ᓚᘏᗢify", "ᓚᘏᗢ")) @commands.cooldown(1, 5, commands.BucketType.user) async def catify(self, ctx: commands.Context, *, text: Optional[str]) -> None: """ diff --git a/bot/exts/evergreen/connect_four.py b/bot/exts/evergreen/connect_four.py index 7a39d442..5c82ffee 100644 --- a/bot/exts/evergreen/connect_four.py +++ b/bot/exts/evergreen/connect_four.py @@ -365,7 +365,7 @@ class ConnectFour(commands.Cog): @guild_only() @commands.group( invoke_without_command=True, - aliases=["4inarow", "connect4", "connectfour", "c4"], + aliases=("4inarow", "connect4", "connectfour", "c4"), case_insensitive=True ) async def connect_four( @@ -428,7 +428,7 @@ class ConnectFour(commands.Cog): await self._play_game(ctx, user, board_size, str(emoji1), str(emoji2)) @guild_only() - @connect_four.command(aliases=["bot", "computer", "cpu"]) + @connect_four.command(aliases=("bot", "computer", "cpu")) async def ai( self, ctx: commands.Context, diff --git a/bot/exts/evergreen/game.py b/bot/exts/evergreen/game.py index 43f64be7..32fe9263 100644 --- a/bot/exts/evergreen/game.py +++ b/bot/exts/evergreen/game.py @@ -224,7 +224,7 @@ class Games(Cog): else: self.genres[genre_name] = genre - @group(name="games", aliases=["game"], invoke_without_command=True) + @group(name="games", aliases=("game",), invoke_without_command=True) async def games(self, ctx: Context, amount: Optional[int] = 5, *, genre: Optional[str]) -> None: """ Get random game(s) by genre from IGDB. Use .games genres command to get all available genres. @@ -277,7 +277,7 @@ class Games(Cog): await ImagePaginator.paginate(pages, ctx, Embed(title=f"Random {genre.title()} Games")) - @games.command(name="top", aliases=["t"]) + @games.command(name="top", aliases=("t",)) async def top(self, ctx: Context, amount: int = 10) -> None: """ Get current Top games in IGDB. @@ -294,19 +294,19 @@ class Games(Cog): pages = [await self.create_page(game) for game in games] await ImagePaginator.paginate(pages, ctx, Embed(title=f"Top {amount} Games")) - @games.command(name="genres", aliases=["genre", "g"]) + @games.command(name="genres", aliases=("genre", "g")) async def genres(self, ctx: Context) -> None: """Get all available genres.""" await ctx.send(f"Currently available genres: {', '.join(f'`{genre}`' for genre in self.genres)}") - @games.command(name="search", aliases=["s"]) + @games.command(name="search", aliases=("s",)) async def search(self, ctx: Context, *, search_term: str) -> None: """Find games by name.""" lines = await self.search_games(search_term) await LinePaginator.paginate(lines, ctx, Embed(title=f"Game Search Results: {search_term}"), empty=False) - @games.command(name="company", aliases=["companies"]) + @games.command(name="company", aliases=("companies",)) async def company(self, ctx: Context, amount: int = 5) -> None: """ Get random Game Companies companies from IGDB API. @@ -325,7 +325,7 @@ class Games(Cog): await ImagePaginator.paginate(pages, ctx, Embed(title="Random Game Companies")) @with_role(*STAFF_ROLES) - @games.command(name="refresh", aliases=["r"]) + @games.command(name="refresh", aliases=("r",)) async def refresh_genres_command(self, ctx: Context) -> None: """Refresh .games command genres.""" try: diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index ff23df4c..10638aea 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -53,7 +53,7 @@ class Movie(Cog): def __init__(self, bot: Bot): self.http_session: ClientSession = bot.http_session - @group(name="movies", aliases=["movie"], invoke_without_command=True) + @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. @@ -103,7 +103,7 @@ class Movie(Cog): await ImagePaginator.paginate(pages, ctx, embed) - @movies.command(name="genres", aliases=["genre", "g"]) + @movies.command(name="genres", aliases=("genre", "g")) async def genres(self, ctx: Context) -> None: """Show all currently available genres for .movies command.""" await ctx.send(f"Current available genres: {', '.join('`' + genre.name + '`' for genre in MovieGenres)}") diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index e162c9bd..606545ab 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -15,7 +15,7 @@ COLORS = itertools.cycle([Colours.python_blue, Colours.python_yellow]) class PythonFacts(commands.Cog): """Sends a random fun fact about Python.""" - @commands.command(name="pythonfact", aliases=["pyfact"]) + @commands.command(name="pythonfact", aliases=("pyfact",)) async def get_python_fact(self, ctx: commands.Context) -> None: """Sends a Random fun fact about Python.""" embed = discord.Embed( diff --git a/bot/exts/evergreen/recommend_game.py b/bot/exts/evergreen/recommend_game.py index 56596020..45108969 100644 --- a/bot/exts/evergreen/recommend_game.py +++ b/bot/exts/evergreen/recommend_game.py @@ -25,7 +25,7 @@ class RecommendGame(commands.Cog): self.bot = bot self.index = 0 - @commands.command(name="recommendgame", aliases=["gamerec"]) + @commands.command(name="recommendgame", aliases=("gamerec",)) async def recommend_game(self, ctx: commands.Context) -> None: """Sends an Embed of a random game recommendation.""" if self.index >= len(game_recs): diff --git a/bot/exts/evergreen/space.py b/bot/exts/evergreen/space.py index 6c991d26..5e87c6d5 100644 --- a/bot/exts/evergreen/space.py +++ b/bot/exts/evergreen/space.py @@ -193,7 +193,7 @@ class Space(Cog): ) ) - @mars.command(name="dates", aliases=["d", "date", "rover", "rovers", "r"]) + @mars.command(name="dates", aliases=("d", "date", "rover", "rovers", "r")) async def dates(self, ctx: Context) -> None: """Get current available rovers photo date ranges.""" await ctx.send("\n".join( diff --git a/bot/exts/evergreen/timed.py b/bot/exts/evergreen/timed.py index 491231cc..2ea6b419 100644 --- a/bot/exts/evergreen/timed.py +++ b/bot/exts/evergreen/timed.py @@ -18,7 +18,7 @@ class TimedCommands(commands.Cog): return await ctx.bot.get_context(msg) - @commands.command(name="timed", aliases=["time", "t"]) + @commands.command(name="timed", aliases=("time", "t")) async def timed(self, ctx: commands.Context, *, command: str) -> None: """Time the command execution of a command.""" new_ctx = await self.create_execution_context(ctx, command) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 9db201ef..352d5ae8 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -43,7 +43,7 @@ class TriviaQuiz(commands.Cog): p = Path("bot", "resources", "evergreen", "trivia_quiz.json") return json.loads(p.read_text("utf8")) - @commands.group(name="quiz", aliases=["trivia"], invoke_without_command=True) + @commands.group(name="quiz", aliases=("trivia",), invoke_without_command=True) async def quiz_game(self, ctx: commands.Context, category: str = None) -> None: """ Start a quiz! diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index fa21b916..83937438 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -72,7 +72,7 @@ class WikipediaSearch(commands.Cog): return @commands.cooldown(1, 10, commands.BucketType.user) - @commands.command(name="wikipedia", aliases=["wiki"]) + @commands.command(name="wikipedia", aliases=("wiki",)) async def wikipedia_search_command(self, ctx: commands.Context, *, search: str) -> None: """Sends paginated top 10 results of Wikipedia search..""" contents = await self.wiki_request(ctx.channel, search) diff --git a/bot/exts/evergreen/wonder_twins.py b/bot/exts/evergreen/wonder_twins.py index 437d69f1..40edf785 100644 --- a/bot/exts/evergreen/wonder_twins.py +++ b/bot/exts/evergreen/wonder_twins.py @@ -38,7 +38,7 @@ class WonderTwins(Cog): object_name = self.append_onto(adjective, object_name) return f"{object_name} of {water_type}" - @command(name="formof", aliases=["wondertwins", "wondertwin", "fo"]) + @command(name="formof", aliases=("wondertwins", "wondertwin", "fo")) async def form_of(self, ctx: Context) -> None: """Command to send a Wonder Twins inspired phrase to the user invoking the command.""" await ctx.send(f"Form of {self.format_phrase()}!") diff --git a/bot/exts/halloween/spookynamerate.py b/bot/exts/halloween/spookynamerate.py index 63040289..c9b42ef5 100644 --- a/bot/exts/halloween/spookynamerate.py +++ b/bot/exts/halloween/spookynamerate.py @@ -117,7 +117,7 @@ class SpookyNameRate(Cog): """Get help on the Spooky Name Rate game.""" await ctx.send(embed=Embed.from_dict(HELP_MESSAGE_DICT)) - @spooky_name_rate.command(name="list", aliases=["all", "entries"]) + @spooky_name_rate.command(name="list", aliases=("all", "entries")) async def list_entries(self, ctx: Context) -> None: """Send all the entries up till now in a single embed.""" await ctx.send(embed=await self.get_responses_list(final=False)) @@ -134,7 +134,7 @@ class SpookyNameRate(Cog): "add an entry." ) - @spooky_name_rate.command(name="add", aliases=["register"]) + @spooky_name_rate.command(name="add", aliases=("register",)) async def add_name(self, ctx: Context, *, name: str) -> None: """Use this command to add/register your spookified name.""" if self.poll: diff --git a/bot/exts/pride/drag_queen_name.py b/bot/exts/pride/drag_queen_name.py index 6bf43913..81eeaff5 100644 --- a/bot/exts/pride/drag_queen_name.py +++ b/bot/exts/pride/drag_queen_name.py @@ -21,7 +21,7 @@ class DragNames(commands.Cog): """Loads a list of drag queen names.""" return json.loads(Path("bot/resources/pride/drag_queen_names.json").read_text("utf8")) - @commands.command(name="dragname", aliases=["dragqueenname", "queenme"]) + @commands.command(name="dragname", aliases=("dragqueenname", "queenme")) async def dragname(self, ctx: commands.Context) -> None: """Sends a message with a drag queen name.""" await ctx.send(random.choice(self.names)) diff --git a/bot/exts/pride/pride_anthem.py b/bot/exts/pride/pride_anthem.py index 21b7e468..ce4b06af 100644 --- a/bot/exts/pride/pride_anthem.py +++ b/bot/exts/pride/pride_anthem.py @@ -38,7 +38,7 @@ class PrideAnthem(commands.Cog): """Loads a list of videos from the resources folder as dictionaries.""" return json.loads(Path("bot/resources/pride/anthems.json").read_text("utf8")) - @commands.command(name="prideanthem", aliases=["anthem", "pridesong"]) + @commands.command(name="prideanthem", aliases=("anthem", "pridesong")) async def prideanthem(self, ctx: commands.Context, genre: str = None) -> None: """ Sends a message with a video of a random pride anthem. diff --git a/bot/exts/pride/pride_facts.py b/bot/exts/pride/pride_facts.py index 47e69a03..5bea1d32 100644 --- a/bot/exts/pride/pride_facts.py +++ b/bot/exts/pride/pride_facts.py @@ -72,7 +72,7 @@ class PrideFacts(commands.Cog): else: await target.send("The fact for the selected day is not yet available.") - @commands.command(name="pridefact", aliases=["pridefacts"]) + @commands.command(name="pridefact", aliases=("pridefacts",)) async def pridefact(self, ctx: commands.Context, option: str = None) -> None: """ Sends a message with a pride fact of the day. diff --git a/bot/exts/valentines/valentine_zodiac.py b/bot/exts/valentines/valentine_zodiac.py index 45d1edd5..d862ee63 100644 --- a/bot/exts/valentines/valentine_zodiac.py +++ b/bot/exts/valentines/valentine_zodiac.py @@ -116,7 +116,7 @@ class ValentineZodiac(commands.Cog): await ctx.send(embed=final_embed) log.trace("Embed from date successfully sent.") - @zodiac.command(name="partnerzodiac", aliases=["partner"]) + @zodiac.command(name="partnerzodiac", aliases=("partner",)) async def partner_zodiac(self, ctx: commands.Context, zodiac_sign: str) -> None: """Provides a random counter compatible zodiac sign to the given user's zodiac sign.""" embed = discord.Embed() -- cgit v1.2.3 From 28eb6d83dd8f1d1b41b4d45f09409cb6391ce52f Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Sat, 15 May 2021 12:54:54 -0400 Subject: fix: Remove the useless return Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- bot/exts/evergreen/reddit.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index b5af83e7..e57fa2c0 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -99,7 +99,6 @@ class Reddit(Cog): f"{content_type}\u2003{Emojis.reddit_upvote}{ups}\u2003{Emojis.reddit_comments}" f"\u2002{comments}\u2003{Emojis.reddit_users}{author}\n\n" ) - return if paginate: post_page += f"**[{title}]({link})**\n\n" -- cgit v1.2.3 From f8a179df350eb3abb7f3595aaff3dbca312f540c Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Sat, 15 May 2021 13:31:34 -0400 Subject: chore: Make things that are only used once constants --- bot/exts/christmas/hanukkah_embed.py | 21 ++++++++++----------- bot/exts/easter/earth_photos.py | 4 +++- bot/exts/easter/egg_facts.py | 12 +++--------- bot/exts/evergreen/cheatsheet.py | 2 +- bot/exts/evergreen/magic_8ball.py | 7 +++---- bot/exts/evergreen/pythonfacts.py | 3 ++- bot/exts/halloween/halloween_facts.py | 9 ++++----- bot/exts/halloween/halloweenify.py | 10 +++++----- bot/exts/halloween/spookygif.py | 7 ++++--- bot/exts/pride/drag_queen_name.py | 12 +++--------- bot/exts/pride/pride_anthem.py | 12 +++--------- bot/exts/pride/pride_facts.py | 13 ++++--------- 12 files changed, 45 insertions(+), 67 deletions(-) diff --git a/bot/exts/christmas/hanukkah_embed.py b/bot/exts/christmas/hanukkah_embed.py index 7a06b558..ca3146cc 100644 --- a/bot/exts/christmas/hanukkah_embed.py +++ b/bot/exts/christmas/hanukkah_embed.py @@ -11,16 +11,17 @@ from bot.utils.decorators import in_month log = logging.getLogger(__name__) +HEBCAL_URL = ( + "https://www.hebcal.com/hebcal/?v=1&cfg=json&maj=on&min=on&mod=on&nx=on&" + "year=now&month=x&ss=on&mf=on&c=on&geo=geoname&geonameid=3448439&m=50&s=on" +) + class HanukkahEmbed(commands.Cog): """A cog that returns information about Hanukkah festival.""" def __init__(self, bot: Bot): self.bot = bot - self.url = ( - "https://www.hebcal.com/hebcal/?v=1&cfg=json&maj=on&min=on&mod=on&nx=on&" - "year=now&month=x&ss=on&mf=on&c=on&geo=geoname&geonameid=3448439&m=50&s=on" - ) self.hanukkah_days = [] self.hanukkah_months = [] self.hanukkah_years = [] @@ -28,7 +29,7 @@ class HanukkahEmbed(commands.Cog): async def get_hanukkah_dates(self) -> List[str]: """Gets the dates for hanukkah festival.""" hanukkah_dates = [] - async with self.bot.http_session.get(self.url) as response: + async with self.bot.http_session.get(HEBCAL_URL) as response: json_data = await response.json() festivals = json_data["items"] for festival in festivals: @@ -57,9 +58,7 @@ class HanukkahEmbed(commands.Cog): day = str(today.day) month = str(today.month) year = str(today.year) - embed = Embed() - embed.title = "Hanukkah" - embed.colour = Colours.blue + embed = Embed(title="Hanukkah", colour=Colours.blue) if day in self.hanukkah_days and month in self.hanukkah_months and year in self.hanukkah_years: if int(day) == hanukkah_start_day: now = datetime.datetime.utcnow() @@ -74,8 +73,8 @@ class HanukkahEmbed(commands.Cog): return elif hours > hanukkah_start_hour: embed.description = ( - "It is the starting day of Hanukkah ! " - f"Its been {hours - hanukkah_start_hour} hours hanukkah started !" + "It is the starting day of Hanukkah! " + f"Its been {hours - hanukkah_start_hour} hours hanukkah started!" ) await ctx.send(embed=embed) return @@ -106,7 +105,7 @@ class HanukkahEmbed(commands.Cog): else: festival_end_month = hanukkah_end.strftime("%B") embed.description = ( - f"Looks like you missed Hanukkah !" + f"Looks like you missed Hanukkah!" f"Hanukkah ended on {hanukkah_end_day}th of {festival_end_month}." ) diff --git a/bot/exts/easter/earth_photos.py b/bot/exts/easter/earth_photos.py index 9df4f9ca..f65790af 100644 --- a/bot/exts/easter/earth_photos.py +++ b/bot/exts/easter/earth_photos.py @@ -9,6 +9,8 @@ from bot.constants import Tokens log = logging.getLogger(__name__) +API_URL = "https://api.unsplash.com/photos/random" + class EarthPhotos(commands.Cog): """This cog contains the command for earth photos.""" @@ -21,7 +23,7 @@ class EarthPhotos(commands.Cog): """Returns a random photo of earth, sourced from Unsplash.""" async with ctx.typing(): async with self.bot.http_session.get( - "https://api.unsplash.com/photos/random", + API_URL, params={"query": "planet_earth", "client_id": Tokens.unsplash_access_key} ) as r: jsondata = await r.json() diff --git a/bot/exts/easter/egg_facts.py b/bot/exts/easter/egg_facts.py index d60032f0..ee383171 100644 --- a/bot/exts/easter/egg_facts.py +++ b/bot/exts/easter/egg_facts.py @@ -12,6 +12,8 @@ from bot.utils.decorators import seasonal_task log = logging.getLogger(__name__) +EGG_FACTS = loads(Path("bot/resources/easter/easter_egg_facts.json").read_text("utf8")) + class EasterFacts(commands.Cog): """ @@ -22,16 +24,8 @@ class EasterFacts(commands.Cog): def __init__(self, bot: Bot): self.bot = bot - self.facts = self.load_json() - self.daily_fact_task = self.bot.loop.create_task(self.send_egg_fact_daily()) - @staticmethod - def load_json() -> dict: - """Load a list of easter egg facts from the resource JSON file.""" - p = Path("bot/resources/easter/easter_egg_facts.json") - return loads(p.read_text("utf8")) - @seasonal_task(Month.APRIL) async def send_egg_fact_daily(self) -> None: """A background task that sends an easter egg fact in the event channel everyday.""" @@ -51,7 +45,7 @@ class EasterFacts(commands.Cog): return discord.Embed( colour=Colours.soft_red, title="Easter Egg Fact", - description=random.choice(self.facts) + description=random.choice(EGG_FACTS) ) diff --git a/bot/exts/evergreen/cheatsheet.py b/bot/exts/evergreen/cheatsheet.py index 7ee34b1d..ae7793c9 100644 --- a/bot/exts/evergreen/cheatsheet.py +++ b/bot/exts/evergreen/cheatsheet.py @@ -12,7 +12,7 @@ from bot.bot import Bot from bot.constants import Categories, Channels, Colours, ERROR_REPLIES from bot.utils.decorators import whitelist_override -ERROR_MESSAGE = f"""\ +ERROR_MESSAGE = f""" Unknown cheat sheet. Please try to reformulate your query. **Examples**: diff --git a/bot/exts/evergreen/magic_8ball.py b/bot/exts/evergreen/magic_8ball.py index 4b3ed2a4..28ddcea0 100644 --- a/bot/exts/evergreen/magic_8ball.py +++ b/bot/exts/evergreen/magic_8ball.py @@ -9,18 +9,17 @@ from bot.bot import Bot log = logging.getLogger(__name__) +ANSWERS = json.loads(Path("bot/resources/evergreen/magic8ball.json").read_text("utf8")) + class Magic8ball(commands.Cog): """A Magic 8ball command to respond to a user's question.""" - def __init__(self): - self.answers = json.loads(Path("bot/resources/evergreen/magic8ball.json").read_text("utf8")) - @commands.command(name="8ball") async def output_answer(self, ctx: commands.Context, *, question: str) -> None: """Return a Magic 8ball answer from answers list.""" if len(question.split()) >= 3: - answer = random.choice(self.answers) + answer = random.choice(ANSWERS) await ctx.send(answer) else: await ctx.send("Usage: .8ball (minimum length of 3 eg: `will I win?`)") diff --git a/bot/exts/evergreen/pythonfacts.py b/bot/exts/evergreen/pythonfacts.py index 606545ab..80a8da5d 100644 --- a/bot/exts/evergreen/pythonfacts.py +++ b/bot/exts/evergreen/pythonfacts.py @@ -10,6 +10,7 @@ with open("bot/resources/evergreen/python_facts.txt") as file: FACTS = itertools.cycle(list(file)) COLORS = itertools.cycle([Colours.python_blue, Colours.python_yellow]) +PYFACTS_DISCUSSION = "https://github.com/python-discord/meta/discussions/93" class PythonFacts(commands.Cog): @@ -25,7 +26,7 @@ class PythonFacts(commands.Cog): ) embed.add_field( name="Suggestions", - value="Suggest more facts [here!](https://github.com/python-discord/meta/discussions/93)" + value=f"Suggest more facts [here!]({PYFACTS_DISCUSSION})" ) await ctx.send(embed=embed) diff --git a/bot/exts/halloween/halloween_facts.py b/bot/exts/halloween/halloween_facts.py index 98cc2db0..5ad8cc57 100644 --- a/bot/exts/halloween/halloween_facts.py +++ b/bot/exts/halloween/halloween_facts.py @@ -25,17 +25,16 @@ SPOOKY_EMOJIS = [ PUMPKIN_ORANGE = 0xFF7518 INTERVAL = timedelta(hours=6).total_seconds() +FACTS = json.loads(Path("bot/resources/halloween/halloween_facts.json").read_text("utf8")) +FACTS = list(enumerate(FACTS)) + class HalloweenFacts(commands.Cog): """A Cog for displaying interesting facts about Halloween.""" - def __init__(self): - self.halloween_facts = json.loads(Path("bot/resources/halloween/halloween_facts.json").read_text("utf8")) - self.facts = list(enumerate(self.halloween_facts)) - def random_fact(self) -> Tuple[int, str]: """Return a random fact from the loaded facts.""" - return random.choice(self.facts) + return random.choice(FACTS) @commands.command(name="spookyfact", aliases=("halloweenfact",), brief="Get the most recent Halloween fact") async def get_random_fact(self, ctx: commands.Context) -> None: diff --git a/bot/exts/halloween/halloweenify.py b/bot/exts/halloween/halloweenify.py index e839950a..83cfbaa7 100644 --- a/bot/exts/halloween/halloweenify.py +++ b/bot/exts/halloween/halloweenify.py @@ -12,6 +12,8 @@ from bot.bot import Bot log = logging.getLogger(__name__) +HALLOWEENIFY_DATA = loads(Path("bot/resources/halloween/halloweenify.json").read_text("utf8")) + class Halloweenify(commands.Cog): """A cog to change a invokers nickname to a spooky one!""" @@ -21,12 +23,10 @@ class Halloweenify(commands.Cog): async def halloweenify(self, ctx: commands.Context) -> None: """Change your nickname into a much spookier one!""" async with ctx.typing(): - data = loads(Path("bot/resources/halloween/halloweenify.json").read_text("utf8")) - # Choose a random character from our list we loaded above and set apart the nickname and image url. - character = choice(data["characters"]) - nickname = "".join([nickname for nickname in character]) - image = "".join([character[nickname] for nickname in character]) + character = choice(HALLOWEENIFY_DATA["characters"]) + nickname = "".join(nickname for nickname in character) + image = "".join(character[nickname] for nickname in character) # Build up a Embed embed = discord.Embed() diff --git a/bot/exts/halloween/spookygif.py b/bot/exts/halloween/spookygif.py index ffb91b1b..a2146a84 100644 --- a/bot/exts/halloween/spookygif.py +++ b/bot/exts/halloween/spookygif.py @@ -8,6 +8,8 @@ from bot.constants import Tokens log = logging.getLogger(__name__) +API_URL = "http://api.giphy.com/v1/gifs/random" + class SpookyGif(commands.Cog): """A cog to fetch a random spooky gif from the web!""" @@ -21,12 +23,11 @@ class SpookyGif(commands.Cog): async with ctx.typing(): params = {"api_key": Tokens.giphy, "tag": "halloween", "rating": "g"} # Make a GET request to the Giphy API to get a random halloween gif. - async with self.bot.http_session.get("http://api.giphy.com/v1/gifs/random", params=params) as resp: + async with self.bot.http_session.get(API_URL, params=params) as resp: data = await resp.json() url = data["data"]["image_url"] - embed = discord.Embed(colour=0x9b59b6) - embed.title = "A spooooky gif!" + embed = discord.Embed(title="A spooooky gif!", colour=0x9b59b6) embed.set_image(url=url) await ctx.send(embed=embed) diff --git a/bot/exts/pride/drag_queen_name.py b/bot/exts/pride/drag_queen_name.py index 81eeaff5..15ca6576 100644 --- a/bot/exts/pride/drag_queen_name.py +++ b/bot/exts/pride/drag_queen_name.py @@ -9,22 +9,16 @@ from bot.bot import Bot log = logging.getLogger(__name__) +NAMES = json.loads(Path("bot/resources/pride/drag_queen_names.json").read_text("utf8")) + class DragNames(commands.Cog): """Gives a random drag queen name!""" - def __init__(self): - self.names = self.load_names() - - @staticmethod - def load_names() -> list: - """Loads a list of drag queen names.""" - return json.loads(Path("bot/resources/pride/drag_queen_names.json").read_text("utf8")) - @commands.command(name="dragname", aliases=("dragqueenname", "queenme")) async def dragname(self, ctx: commands.Context) -> None: """Sends a message with a drag queen name.""" - await ctx.send(random.choice(self.names)) + await ctx.send(random.choice(NAMES)) def setup(bot: Bot) -> None: diff --git a/bot/exts/pride/pride_anthem.py b/bot/exts/pride/pride_anthem.py index ce4b06af..4650595a 100644 --- a/bot/exts/pride/pride_anthem.py +++ b/bot/exts/pride/pride_anthem.py @@ -10,13 +10,12 @@ from bot.bot import Bot log = logging.getLogger(__name__) +VIDEOS = json.loads(Path("bot/resources/pride/anthems.json").read_text("utf8")) + class PrideAnthem(commands.Cog): """Embed a random youtube video for a gay anthem!""" - def __init__(self): - self.anthems = self.load_vids() - def get_video(self, genre: Optional[str] = None) -> dict: """ Picks a random anthem from the list. @@ -27,17 +26,12 @@ class PrideAnthem(commands.Cog): if not genre: return random.choice(self.anthems) else: - songs = [song for song in self.anthems if genre.casefold() in song["genre"]] + songs = [song for song in VIDEOS if genre.casefold() in song["genre"]] try: return random.choice(songs) except IndexError: log.info("No videos for that genre.") - @staticmethod - def load_vids() -> list: - """Loads a list of videos from the resources folder as dictionaries.""" - return json.loads(Path("bot/resources/pride/anthems.json").read_text("utf8")) - @commands.command(name="prideanthem", aliases=("anthem", "pridesong")) async def prideanthem(self, ctx: commands.Context, genre: str = None) -> None: """ diff --git a/bot/exts/pride/pride_facts.py b/bot/exts/pride/pride_facts.py index 5bea1d32..631e2e8b 100644 --- a/bot/exts/pride/pride_facts.py +++ b/bot/exts/pride/pride_facts.py @@ -15,21 +15,16 @@ from bot.utils.decorators import seasonal_task log = logging.getLogger(__name__) +FACTS = json.loads(Path("bot/resources/pride/facts.json").read_text("utf8")) + class PrideFacts(commands.Cog): """Provides a new fact every day during the Pride season!""" def __init__(self, bot: Bot): self.bot = bot - self.facts = self.load_facts() - self.daily_fact_task = self.bot.loop.create_task(self.send_pride_fact_daily()) - @staticmethod - def load_facts() -> dict: - """Loads a dictionary of years mapping to lists of facts.""" - return json.loads(Path("bot/resources/pride/facts.json").read_text("utf8")) - @seasonal_task(Month.JUNE) async def send_pride_fact_daily(self) -> None: """Background task to post the daily pride fact every day.""" @@ -41,8 +36,8 @@ class PrideFacts(commands.Cog): async def send_random_fact(self, ctx: commands.Context) -> None: """Provides a fact from any previous day, or today.""" now = datetime.utcnow() - previous_years_facts = (y for x, y in self.facts.items() if int(x) < now.year) - current_year_facts = self.facts.get(str(now.year), [])[:now.day] + previous_years_facts = (y for x, y in FACTS.items() if int(x) < now.year) + current_year_facts = FACTS.get(str(now.year), [])[:now.day] previous_facts = current_year_facts + [x for y in previous_years_facts for x in y] try: await ctx.send(embed=self.make_embed(random.choice(previous_facts))) -- cgit v1.2.3 From bf999d03b02e0664ffe43c31cf9208c0251a8370 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Sat, 15 May 2021 13:44:14 -0400 Subject: chore: Apply suggested changes to hanukkah_embed.py --- bot/exts/christmas/hanukkah_embed.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/bot/exts/christmas/hanukkah_embed.py b/bot/exts/christmas/hanukkah_embed.py index ca3146cc..119f2446 100644 --- a/bot/exts/christmas/hanukkah_embed.py +++ b/bot/exts/christmas/hanukkah_embed.py @@ -80,18 +80,8 @@ class HanukkahEmbed(commands.Cog): return festival_day = self.hanukkah_days.index(day) number_suffixes = ["st", "nd", "rd", "th"] - suffix = "" - if festival_day == 1: - suffix = number_suffixes[0] - if festival_day == 2: - suffix = number_suffixes[1] - if festival_day == 3: - suffix = number_suffixes[2] - if festival_day > 3: - suffix = number_suffixes[3] - message = "" - for _ in range(1, festival_day + 1): - message += ":menorah:" + suffix = number_suffixes[festival_day - 1 if festival_day <= 3 else 3] + message = ":menorah:" * festival_day embed.description = f"It is the {festival_day}{suffix} day of Hanukkah!\n{message}" await ctx.send(embed=embed) else: -- cgit v1.2.3 From 85733251d46151dc76378fad253c1d45e49eb9a0 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sat, 15 May 2021 20:07:10 +0200 Subject: Latex: temporarily disable loading of the cog As we are currently working on restricting resources on the latex cog, this commit will prevent it from loading. It will be reverted once solved. --- bot/exts/evergreen/latex.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bot/exts/evergreen/latex.py b/bot/exts/evergreen/latex.py index c4a8597c..8f42da90 100644 --- a/bot/exts/evergreen/latex.py +++ b/bot/exts/evergreen/latex.py @@ -91,4 +91,9 @@ class Latex(commands.Cog): def setup(bot: commands.Bot) -> None: """Load the Latex Cog.""" + # As we have resource issues on this cog, + # we have it currently disabled while we fix it. + import logging + logging.info("Latex cog is currently disabled. It won't be loaded.") + return bot.add_cog(Latex(bot)) -- cgit v1.2.3 From 01e22725c627b677224ccf8409f71c9b7bc68c81 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Sat, 15 May 2021 15:34:08 -0400 Subject: chore: Make more things use constants --- bot/exts/halloween/spookygif.py | 4 ++-- bot/exts/halloween/spookynamerate.py | 22 +++++++--------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/bot/exts/halloween/spookygif.py b/bot/exts/halloween/spookygif.py index a2146a84..9511d407 100644 --- a/bot/exts/halloween/spookygif.py +++ b/bot/exts/halloween/spookygif.py @@ -4,7 +4,7 @@ import discord from discord.ext import commands from bot.bot import Bot -from bot.constants import Tokens +from bot.constants import Colours, Tokens log = logging.getLogger(__name__) @@ -27,7 +27,7 @@ class SpookyGif(commands.Cog): data = await resp.json() url = data["data"]["image_url"] - embed = discord.Embed(title="A spooooky gif!", colour=0x9b59b6) + embed = discord.Embed(title="A spooooky gif!", colour=Colours.purple) embed.set_image(url=url) await ctx.send(embed=embed) diff --git a/bot/exts/halloween/spookynamerate.py b/bot/exts/halloween/spookynamerate.py index c9b42ef5..3d6d95fa 100644 --- a/bot/exts/halloween/spookynamerate.py +++ b/bot/exts/halloween/spookynamerate.py @@ -6,7 +6,7 @@ from datetime import datetime, timedelta from logging import getLogger from os import getenv from pathlib import Path -from typing import Dict, Union +from typing import Union from async_rediscache import RedisCache from discord import Embed, Reaction, TextChannel, User @@ -65,6 +65,11 @@ HELP_MESSAGE_DICT = { ], } +# The names are from https://www.mockaroo.com/ +NAMES = json.loads(Path("bot/resources/halloween/spookynamerate_names.json").read_text("utf8")) +FIRST_NAMES = NAMES["first_names"] +LAST_NAMES = NAMES["last_names"] + class SpookyNameRate(Cog): """ @@ -88,14 +93,6 @@ class SpookyNameRate(Cog): def __init__(self, bot: Bot) -> None: self.bot = bot - - names_data = self.load_json( - Path("bot", "resources", "halloween", "spookynamerate_names.json") - ) - self.first_names = names_data["first_names"] - self.last_names = names_data["last_names"] - # the names are from https://www.mockaroo.com/ - self.name = None self.bot.loop.create_task(self.load_vars()) @@ -302,7 +299,7 @@ class SpookyNameRate(Cog): await self.messages.clear() # reset the messages # send the next name - self.name = f"{random.choice(self.first_names)} {random.choice(self.last_names)}" + self.name = f"{random.choice(FIRST_NAMES)} {random.choice(LAST_NAMES)}" await self.data.set("name", self.name) await channel.send( @@ -368,11 +365,6 @@ class SpookyNameRate(Cog): logger.warning("Bot is unable to get the #seasonalbot-commands channel. Please check the channel ID.") return channel - @staticmethod - def load_json(file: Path) -> Dict[str, str]: - """Loads a JSON file and returns its contents.""" - return json.loads(file.read_text("utf8")) - @staticmethod def in_allowed_month() -> bool: """Returns whether running in the limited month.""" -- cgit v1.2.3 From 369134e791fd9a3e52a4948725c249d03128fdd6 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Sat, 15 May 2021 15:40:09 -0400 Subject: chore: Apply suggestions from code review Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- bot/exts/evergreen/recommend_game.py | 2 +- bot/exts/valentines/myvalenstate.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/recommend_game.py b/bot/exts/evergreen/recommend_game.py index 45108969..35d60128 100644 --- a/bot/exts/evergreen/recommend_game.py +++ b/bot/exts/evergreen/recommend_game.py @@ -41,7 +41,7 @@ class RecommendGame(commands.Cog): if author is not None: embed.set_author(name=author.name, icon_url=author.avatar_url) embed.set_image(url=game["image"]) - embed.add_field(name="Recommendation: " + game["title"] + "\n" + game["link"], value=game["description"]) + embed.add_field(name=f"Recommendation: {game['title']}\n{game['link']}", value=game["description"]) await ctx.send(embed=embed) diff --git a/bot/exts/valentines/myvalenstate.py b/bot/exts/valentines/myvalenstate.py index c699de94..52a61011 100644 --- a/bot/exts/valentines/myvalenstate.py +++ b/bot/exts/valentines/myvalenstate.py @@ -69,7 +69,7 @@ class MyValenstate(commands.Cog): embed = discord.Embed( title=f"Your Valenstate is {valenstate} \u2764", - description=STATES[valenstate]['text'], + description=STATES[valenstate]["text"], colour=Colours.pink ) embed.add_field(name=embed_title, value=embed_text) -- cgit v1.2.3 From 8ac335afc7466d92888c40db46e0c6d454743ff6 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Sat, 15 May 2021 17:32:09 -0400 Subject: chore: Add Iceman's suggested changes --- bot/exts/easter/bunny_name_generator.py | 9 ++++++--- bot/exts/easter/easter_riddle.py | 2 +- bot/exts/easter/egg_facts.py | 3 ++- bot/exts/evergreen/avatar_modification/_effects.py | 4 ++-- bot/exts/evergreen/avatar_modification/avatar_modify.py | 4 ++-- bot/exts/evergreen/battleship.py | 8 ++++---- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/bot/exts/easter/bunny_name_generator.py b/bot/exts/easter/bunny_name_generator.py index adde8704..3e97373f 100644 --- a/bot/exts/easter/bunny_name_generator.py +++ b/bot/exts/easter/bunny_name_generator.py @@ -17,14 +17,16 @@ BUNNY_NAMES = json.loads(Path("bot/resources/easter/bunny_names.json").read_text class BunnyNameGenerator(commands.Cog): """Generate a random bunny name, or bunnify your Discord username!""" - def find_separators(self, displayname: str) -> Union[List[str], None]: + @staticmethod + def find_separators(displayname: str) -> Union[List[str], None]: """Check if Discord name contains spaces so we can bunnify an individual word in the name.""" new_name = re.split(r"[_.\s]", displayname) if displayname not in new_name: return new_name return None - def find_vowels(self, displayname: str) -> str: + @staticmethod + def find_vowels(displayname: str) -> str: """ Finds vowels in the user's display name. @@ -45,7 +47,8 @@ class BunnyNameGenerator(commands.Cog): if new_name != displayname: return new_name - def append_name(self, displayname: str) -> str: + @staticmethod + def append_name(displayname: str) -> str: """Adds a suffix to the end of the Discord name.""" extensions = ["foot", "ear", "nose", "tail"] suffix = random.choice(extensions) diff --git a/bot/exts/easter/easter_riddle.py b/bot/exts/easter/easter_riddle.py index a87f93d1..88b3be2f 100644 --- a/bot/exts/easter/easter_riddle.py +++ b/bot/exts/easter/easter_riddle.py @@ -48,7 +48,7 @@ class EasterRiddle(commands.Cog): ) return - self.current_channel = ctx.message.channel + self.current_channel = ctx.channel random_question = random.choice(RIDDLE_QUESTIONS) question = random_question["question"] diff --git a/bot/exts/easter/egg_facts.py b/bot/exts/easter/egg_facts.py index ee383171..486e735f 100644 --- a/bot/exts/easter/egg_facts.py +++ b/bot/exts/easter/egg_facts.py @@ -40,7 +40,8 @@ class EasterFacts(commands.Cog): embed = self.make_embed() await ctx.send(embed=embed) - def make_embed(self) -> discord.Embed: + @staticmethod + def make_embed() -> discord.Embed: """Makes a nice embed for the message to be sent.""" return discord.Embed( colour=Colours.soft_red, diff --git a/bot/exts/evergreen/avatar_modification/_effects.py b/bot/exts/evergreen/avatar_modification/_effects.py index 9339ecc4..cd798fc2 100644 --- a/bot/exts/evergreen/avatar_modification/_effects.py +++ b/bot/exts/evergreen/avatar_modification/_effects.py @@ -14,7 +14,7 @@ class PfpEffects: """ Implements various image modifying effects, for the PfpModify cog. - All of these fuctions are slow, and blocking, so they should be ran in executors. + All of these functions are slow, and blocking, so they should be ran in executors. """ @staticmethod @@ -102,7 +102,7 @@ class PfpEffects: Applies the easter effect to the given image. This is done by getting the closest "easter" colour to each pixel and changing the colour - to the half-way RGBvalue. + to the half-way RGB value. We also then add an overlay image on top in middle right, a chocolate bunny by default. """ diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 199b6dcb..17f34ed4 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -33,7 +33,7 @@ GENDER_OPTIONS = json.loads(Path("bot/resources/pride/gender_options.json").read async def in_executor(func: t.Callable[..., T], *args) -> T: """ - Runs the given synchronus function `func` in an executor. + Runs the given synchronous function `func` in an executor. This is useful for running slow, blocking code within async functions, so that they don't block the bot. @@ -71,7 +71,7 @@ class AvatarModify(commands.Cog): Fetches a user and handles errors. This helper function is required as the member cache doesn't always have the most up to date - profile picture. This can lead to errors if the image is delted from the Discord CDN. + profile picture. This can lead to errors if the image is deleted from the Discord CDN. fetch_member can't be used due to the avatar url being part of the user object, and some weird caching that D.py does """ diff --git a/bot/exts/evergreen/battleship.py b/bot/exts/evergreen/battleship.py index 27c50bdb..c2f2079c 100644 --- a/bot/exts/evergreen/battleship.py +++ b/bot/exts/evergreen/battleship.py @@ -31,8 +31,8 @@ EmojiSet = typing.Dict[typing.Tuple[bool, bool], str] class Player: """Each player in the game - their messages for the boards and their current grid.""" - user: discord.Member - board: discord.Message + user: typing.Optional[discord.Member] + board: typing.Optional[discord.Message] opponent_board: discord.Message grid: Grid @@ -417,9 +417,9 @@ class Battleship(commands.Cog): self.waiting.remove(ctx.author) if self.already_playing(ctx.author): return + game = Game(self.bot, ctx.channel, ctx.author, user) + self.games.append(game) try: - game = Game(self.bot, ctx.channel, ctx.author, user) - self.games.append(game) await game.start_game() self.games.remove(game) except discord.Forbidden: -- cgit v1.2.3 From 0285da6cae32bb5986111477bbd4d61e723005f3 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Sat, 15 May 2021 17:34:11 -0400 Subject: chore: Apply Iceman's suggestions Co-authored-by: Rohan Reddy Alleti --- bot/exts/easter/egg_decorating.py | 2 +- bot/exts/evergreen/avatar_modification/_effects.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/easter/egg_decorating.py b/bot/exts/easter/egg_decorating.py index d4b27b20..fd7620d4 100644 --- a/bot/exts/easter/egg_decorating.py +++ b/bot/exts/easter/egg_decorating.py @@ -44,7 +44,7 @@ class EggDecorating(commands.Cog): @commands.command(aliases=("decorateegg",)) async def eggdecorate( self, ctx: commands.Context, *colours: Union[discord.Colour, str] - ) -> Union[Image.Image, discord.Message]: + ) -> Union[Image.Image, None]: """ Picks a random egg design and decorates it using the given colours. diff --git a/bot/exts/evergreen/avatar_modification/_effects.py b/bot/exts/evergreen/avatar_modification/_effects.py index cd798fc2..b53b26f3 100644 --- a/bot/exts/evergreen/avatar_modification/_effects.py +++ b/bot/exts/evergreen/avatar_modification/_effects.py @@ -273,7 +273,7 @@ class PfpEffects: @staticmethod def mosaic_effect(img_bytes: bytes, squares: int, file_name: str) -> discord.File: - """Seperate function run from an executor which turns an image into a mosaic.""" + """Separate function run from an executor which turns an image into a mosaic.""" avatar = Image.open(BytesIO(img_bytes)) avatar = avatar.convert("RGBA").resize((1024, 1024)) -- cgit v1.2.3 From ce8a9d85cd5a27c38c61f17f5656106ddd9c7a0f Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Sat, 15 May 2021 23:21:09 +0100 Subject: feat: allow replying to messages to bookmark --- bot/exts/evergreen/bookmark.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 29915627..1d172a45 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -1,6 +1,7 @@ import asyncio import logging import random +import typing as t import discord from discord.ext import commands @@ -88,11 +89,16 @@ class Bookmark(commands.Cog): async def bookmark( self, ctx: commands.Context, - target_message: WrappedMessageConverter, + target_message: t.Optional[WrappedMessageConverter], *, title: str = "Bookmark" ) -> None: """Send the author a link to `target_message` via DMs.""" + if not target_message: + if not ctx.message.reference: + raise commands.UserInputError("You must either provide a message to bookmark, or reply to one.") + target_message = await ctx.channel.fetch_message(ctx.message.reference.message_id) + # Prevent users from bookmarking a message in a channel they don't have access to permissions = ctx.author.permissions_in(target_message.channel) if not permissions.read_messages: -- cgit v1.2.3 From 8c8df556a982d06218465f98ec9ea9ccca01d6b0 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Sat, 15 May 2021 23:32:22 +0100 Subject: chore: add 'valid' wording --- bot/exts/evergreen/bookmark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 1d172a45..1f65f07c 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -96,7 +96,7 @@ class Bookmark(commands.Cog): """Send the author a link to `target_message` via DMs.""" if not target_message: if not ctx.message.reference: - raise commands.UserInputError("You must either provide a message to bookmark, or reply to one.") + raise commands.UserInputError("You must either provide a valid message to bookmark, or reply to one.") target_message = await ctx.channel.fetch_message(ctx.message.reference.message_id) # Prevent users from bookmarking a message in a channel they don't have access to -- cgit v1.2.3 From 6885c85f7c02fec618265881f00f92738b64eabc Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Sun, 16 May 2021 03:19:26 +0100 Subject: chore: use ctx.fetch_message Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/evergreen/bookmark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 1f65f07c..c84446c5 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -97,7 +97,7 @@ class Bookmark(commands.Cog): if not target_message: if not ctx.message.reference: raise commands.UserInputError("You must either provide a valid message to bookmark, or reply to one.") - target_message = await ctx.channel.fetch_message(ctx.message.reference.message_id) + target_message = await ctx.fetch_message(ctx.message.reference.message_id) # Prevent users from bookmarking a message in a channel they don't have access to permissions = ctx.author.permissions_in(target_message.channel) -- cgit v1.2.3 From d65dfb150113d0c8e208ac5d29517d31d74874c2 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 16 May 2021 15:41:39 +0100 Subject: Revert bump to d.py --- poetry.lock | 212 ++++++++++++++++++--------------------------------------- pyproject.toml | 2 +- 2 files changed, 68 insertions(+), 146 deletions(-) diff --git a/poetry.lock b/poetry.lock index f70d0328..78a30370 100644 --- a/poetry.lock +++ b/poetry.lock @@ -11,19 +11,18 @@ pycares = ">=3.0.0" [[package]] name = "aiohttp" -version = "3.7.4.post0" +version = "3.6.3" description = "Async http client/server framework (asyncio)" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.5.3" [package.dependencies] async-timeout = ">=3.0,<4.0" attrs = ">=17.3.0" -chardet = ">=2.0,<5.0" -multidict = ">=4.5,<7.0" -typing-extensions = ">=3.6.5" -yarl = ">=1.0,<2.0" +chardet = ">=2.0,<4.0" +multidict = ">=4.5,<5.0" +yarl = ">=1.0,<1.6.0" [package.extras] speedups = ["aiodns", "brotlipy", "cchardet"] @@ -140,11 +139,11 @@ python-versions = ">=3.6.1" [[package]] name = "chardet" -version = "4.0.0" +version = "3.0.4" description = "Universal encoding detector for Python 2 and 3" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "*" [[package]] name = "cycler" @@ -159,18 +158,18 @@ six = "*" [[package]] name = "discord.py" -version = "1.7.2" +version = "1.5.1" description = "A Python wrapper for the Discord API" category = "main" optional = false python-versions = ">=3.5.3" [package.dependencies] -aiohttp = ">=3.6.0,<3.8.0" +aiohttp = ">=3.6.0,<3.7.0" [package.extras] -docs = ["sphinx (==3.0.3)", "sphinxcontrib-trio (==1.1.2)", "sphinxcontrib-websupport"] -voice = ["PyNaCl (>=1.3.0,<1.5)"] +docs = ["sphinx (==1.8.5)", "sphinxcontrib-trio (==1.1.1)", "sphinxcontrib-websupport"] +voice = ["PyNaCl (==1.3.0)"] [[package]] name = "distlib" @@ -399,11 +398,11 @@ python-versions = ">=3.5" [[package]] name = "multidict" -version = "5.1.0" +version = "4.7.6" description = "multidict implementation" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.5" [[package]] name = "nodeenv" @@ -654,14 +653,6 @@ category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -[[package]] -name = "typing-extensions" -version = "3.10.0.0" -description = "Backported and Experimental Type Hints for Python 3.5+" -category = "main" -optional = false -python-versions = "*" - [[package]] name = "urllib3" version = "1.26.4" @@ -695,11 +686,11 @@ testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", [[package]] name = "yarl" -version = "1.6.3" +version = "1.5.1" description = "Yet another URL library" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.5" [package.dependencies] idna = ">=2.0" @@ -708,7 +699,7 @@ multidict = ">=4.0" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "d0003b8cc4caac9d6eb0c14e4c4085191907d7fa0803888eddae4259446eada7" +content-hash = "fc7ffecf728af848ad4fcc1d5b14ded3360b8473ca5da77b5d39e256d36bd0fd" [metadata.files] aiodns = [ @@ -716,43 +707,19 @@ aiodns = [ {file = "aiodns-2.0.0.tar.gz", hash = "sha256:815fdef4607474295d68da46978a54481dd1e7be153c7d60f9e72773cd38d77d"}, ] aiohttp = [ - {file = "aiohttp-3.7.4.post0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:3cf75f7cdc2397ed4442594b935a11ed5569961333d49b7539ea741be2cc79d5"}, - {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4b302b45040890cea949ad092479e01ba25911a15e648429c7c5aae9650c67a8"}, - {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95"}, - {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:393f389841e8f2dfc86f774ad22f00923fdee66d238af89b70ea314c4aefd290"}, - {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:c6e9dcb4cb338d91a73f178d866d051efe7c62a7166653a91e7d9fb18274058f"}, - {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:5df68496d19f849921f05f14f31bd6ef53ad4b00245da3195048c69934521809"}, - {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe"}, - {file = "aiohttp-3.7.4.post0-cp36-cp36m-win32.whl", hash = "sha256:3d78619672183be860b96ed96f533046ec97ca067fd46ac1f6a09cd9b7484287"}, - {file = "aiohttp-3.7.4.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:f705e12750171c0ab4ef2a3c76b9a4024a62c4103e3a55dd6f99265b9bc6fcfc"}, - {file = "aiohttp-3.7.4.post0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:230a8f7e24298dea47659251abc0fd8b3c4e38a664c59d4b89cca7f6c09c9e87"}, - {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2e19413bf84934d651344783c9f5e22dee452e251cfd220ebadbed2d9931dbf0"}, - {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e4b2b334e68b18ac9817d828ba44d8fcb391f6acb398bcc5062b14b2cbeac970"}, - {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:d012ad7911653a906425d8473a1465caa9f8dea7fcf07b6d870397b774ea7c0f"}, - {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:40eced07f07a9e60e825554a31f923e8d3997cfc7fb31dbc1328c70826e04cde"}, - {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:209b4a8ee987eccc91e2bd3ac36adee0e53a5970b8ac52c273f7f8fd4872c94c"}, - {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:14762875b22d0055f05d12abc7f7d61d5fd4fe4642ce1a249abdf8c700bf1fd8"}, - {file = "aiohttp-3.7.4.post0-cp37-cp37m-win32.whl", hash = "sha256:7615dab56bb07bff74bc865307aeb89a8bfd9941d2ef9d817b9436da3a0ea54f"}, - {file = "aiohttp-3.7.4.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:d9e13b33afd39ddeb377eff2c1c4f00544e191e1d1dee5b6c51ddee8ea6f0cf5"}, - {file = "aiohttp-3.7.4.post0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:547da6cacac20666422d4882cfcd51298d45f7ccb60a04ec27424d2f36ba3eaf"}, - {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:af9aa9ef5ba1fd5b8c948bb11f44891968ab30356d65fd0cc6707d989cd521df"}, - {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:64322071e046020e8797117b3658b9c2f80e3267daec409b350b6a7a05041213"}, - {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bb437315738aa441251214dad17428cafda9cdc9729499f1d6001748e1d432f4"}, - {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:e54962802d4b8b18b6207d4a927032826af39395a3bd9196a5af43fc4e60b009"}, - {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:a00bb73540af068ca7390e636c01cbc4f644961896fa9363154ff43fd37af2f5"}, - {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:79ebfc238612123a713a457d92afb4096e2148be17df6c50fb9bf7a81c2f8013"}, - {file = "aiohttp-3.7.4.post0-cp38-cp38-win32.whl", hash = "sha256:515dfef7f869a0feb2afee66b957cc7bbe9ad0cdee45aec7fdc623f4ecd4fb16"}, - {file = "aiohttp-3.7.4.post0-cp38-cp38-win_amd64.whl", hash = "sha256:114b281e4d68302a324dd33abb04778e8557d88947875cbf4e842c2c01a030c5"}, - {file = "aiohttp-3.7.4.post0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:7b18b97cf8ee5452fa5f4e3af95d01d84d86d32c5e2bfa260cf041749d66360b"}, - {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:15492a6368d985b76a2a5fdd2166cddfea5d24e69eefed4630cbaae5c81d89bd"}, - {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bdb230b4943891321e06fc7def63c7aace16095be7d9cf3b1e01be2f10fba439"}, - {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:cffe3ab27871bc3ea47df5d8f7013945712c46a3cc5a95b6bee15887f1675c22"}, - {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a"}, - {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:a5ca29ee66f8343ed336816c553e82d6cade48a3ad702b9ffa6125d187e2dedb"}, - {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:17c073de315745a1510393a96e680d20af8e67e324f70b42accbd4cb3315c9fb"}, - {file = "aiohttp-3.7.4.post0-cp39-cp39-win32.whl", hash = "sha256:932bb1ea39a54e9ea27fc9232163059a0b8855256f4052e776357ad9add6f1c9"}, - {file = "aiohttp-3.7.4.post0-cp39-cp39-win_amd64.whl", hash = "sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe"}, - {file = "aiohttp-3.7.4.post0.tar.gz", hash = "sha256:493d3299ebe5f5a7c66b9819eacdcfbbaaf1a8e84911ddffcdc48888497afecf"}, + {file = "aiohttp-3.6.3-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:1a4160579ffbc1b69e88cb6ca8bb0fbd4947dfcbf9fb1e2a4fc4c7a4a986c1fe"}, + {file = "aiohttp-3.6.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:fb83326d8295e8840e4ba774edf346e87eca78ba8a89c55d2690352842c15ba5"}, + {file = "aiohttp-3.6.3-cp35-cp35m-win32.whl", hash = "sha256:470e4c90da36b601676fe50c49a60d34eb8c6593780930b1aa4eea6f508dfa37"}, + {file = "aiohttp-3.6.3-cp35-cp35m-win_amd64.whl", hash = "sha256:a885432d3cabc1287bcf88ea94e1826d3aec57fd5da4a586afae4591b061d40d"}, + {file = "aiohttp-3.6.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:c506853ba52e516b264b106321c424d03f3ddef2813246432fa9d1cefd361c81"}, + {file = "aiohttp-3.6.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:797456399ffeef73172945708810f3277f794965eb6ec9bd3a0c007c0476be98"}, + {file = "aiohttp-3.6.3-cp36-cp36m-win32.whl", hash = "sha256:60f4caa3b7f7a477f66ccdd158e06901e1d235d572283906276e3803f6b098f5"}, + {file = "aiohttp-3.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2ad493de47a8f926386fa6d256832de3095ba285f325db917c7deae0b54a9fc8"}, + {file = "aiohttp-3.6.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:319b490a5e2beaf06891f6711856ea10591cfe84fe9f3e71a721aa8f20a0872a"}, + {file = "aiohttp-3.6.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:66d64486172b032db19ea8522328b19cfb78a3e1e5b62ab6a0567f93f073dea0"}, + {file = "aiohttp-3.6.3-cp37-cp37m-win32.whl", hash = "sha256:206c0ccfcea46e1bddc91162449c20c72f308aebdcef4977420ef329c8fcc599"}, + {file = "aiohttp-3.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:687461cd974722110d1763b45c5db4d2cdee8d50f57b00c43c7590d1dd77fc5c"}, + {file = "aiohttp-3.6.3.tar.gz", hash = "sha256:698cd7bc3c7d1b82bb728bae835724a486a8c376647aec336aa21a60113c3645"}, ] aioredis = [ {file = "aioredis-1.3.1-py3-none-any.whl", hash = "sha256:b61808d7e97b7cd5a92ed574937a079c9387fdadd22bfbfa7ad2fd319ecc26e3"}, @@ -831,16 +798,16 @@ cfgv = [ {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, ] chardet = [ - {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, - {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, + {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, + {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, ] cycler = [ {file = "cycler-0.10.0-py2.py3-none-any.whl", hash = "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d"}, {file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"}, ] "discord.py" = [ - {file = "discord.py-1.7.2-py3-none-any.whl", hash = "sha256:f179db299c949a8cf0a12c1b1b94d0da9a18e088857154d93ae5ab1d807ec61d"}, - {file = "discord.py-1.7.2.tar.gz", hash = "sha256:114e76cd27362fb919abf7f001a2dbdc77c9a67cff74ed6a89aecd6582ee298e"}, + {file = "discord.py-1.5.1-py3-none-any.whl", hash = "sha256:2367359e31f6527f8a936751fc20b09d7495dd6a76b28c8fb13d4ca6c55b7563"}, + {file = "discord.py-1.5.1.tar.gz", hash = "sha256:def00dc50cf36d21346d71bc89f0cad8f18f9a3522978dc18c7796287d47de8b"}, ] distlib = [ {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, @@ -1012,43 +979,23 @@ mslex = [ {file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"}, ] multidict = [ - {file = "multidict-5.1.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f"}, - {file = "multidict-5.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf"}, - {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281"}, - {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d"}, - {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d"}, - {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da"}, - {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224"}, - {file = "multidict-5.1.0-cp36-cp36m-win32.whl", hash = "sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26"}, - {file = "multidict-5.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6"}, - {file = "multidict-5.1.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76"}, - {file = "multidict-5.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a"}, - {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f"}, - {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348"}, - {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93"}, - {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9"}, - {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37"}, - {file = "multidict-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5"}, - {file = "multidict-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632"}, - {file = "multidict-5.1.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952"}, - {file = "multidict-5.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79"}, - {file = "multidict-5.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456"}, - {file = "multidict-5.1.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7"}, - {file = "multidict-5.1.0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635"}, - {file = "multidict-5.1.0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a"}, - {file = "multidict-5.1.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea"}, - {file = "multidict-5.1.0-cp38-cp38-win32.whl", hash = "sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656"}, - {file = "multidict-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3"}, - {file = "multidict-5.1.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93"}, - {file = "multidict-5.1.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647"}, - {file = "multidict-5.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d"}, - {file = "multidict-5.1.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8"}, - {file = "multidict-5.1.0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1"}, - {file = "multidict-5.1.0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841"}, - {file = "multidict-5.1.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda"}, - {file = "multidict-5.1.0-cp39-cp39-win32.whl", hash = "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"}, - {file = "multidict-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359"}, - {file = "multidict-5.1.0.tar.gz", hash = "sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5"}, + {file = "multidict-4.7.6-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000"}, + {file = "multidict-4.7.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a"}, + {file = "multidict-4.7.6-cp35-cp35m-win32.whl", hash = "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5"}, + {file = "multidict-4.7.6-cp35-cp35m-win_amd64.whl", hash = "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3"}, + {file = "multidict-4.7.6-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87"}, + {file = "multidict-4.7.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2"}, + {file = "multidict-4.7.6-cp36-cp36m-win32.whl", hash = "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7"}, + {file = "multidict-4.7.6-cp36-cp36m-win_amd64.whl", hash = "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463"}, + {file = "multidict-4.7.6-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d"}, + {file = "multidict-4.7.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255"}, + {file = "multidict-4.7.6-cp37-cp37m-win32.whl", hash = "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507"}, + {file = "multidict-4.7.6-cp37-cp37m-win_amd64.whl", hash = "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c"}, + {file = "multidict-4.7.6-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b"}, + {file = "multidict-4.7.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7"}, + {file = "multidict-4.7.6-cp38-cp38-win32.whl", hash = "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d"}, + {file = "multidict-4.7.6-cp38-cp38-win_amd64.whl", hash = "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19"}, + {file = "multidict-4.7.6.tar.gz", hash = "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430"}, ] nodeenv = [ {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"}, @@ -1283,11 +1230,6 @@ toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -typing-extensions = [ - {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, - {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, - {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, -] urllib3 = [ {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, @@ -1297,41 +1239,21 @@ virtualenv = [ {file = "virtualenv-20.4.6.tar.gz", hash = "sha256:72cf267afc04bf9c86ec932329b7e94db6a0331ae9847576daaa7ca3c86b29a4"}, ] yarl = [ - {file = "yarl-1.6.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434"}, - {file = "yarl-1.6.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478"}, - {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6"}, - {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e"}, - {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406"}, - {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76"}, - {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366"}, - {file = "yarl-1.6.3-cp36-cp36m-win32.whl", hash = "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721"}, - {file = "yarl-1.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643"}, - {file = "yarl-1.6.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e"}, - {file = "yarl-1.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3"}, - {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8"}, - {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a"}, - {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c"}, - {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f"}, - {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970"}, - {file = "yarl-1.6.3-cp37-cp37m-win32.whl", hash = "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e"}, - {file = "yarl-1.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50"}, - {file = "yarl-1.6.3-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2"}, - {file = "yarl-1.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec"}, - {file = "yarl-1.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71"}, - {file = "yarl-1.6.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc"}, - {file = "yarl-1.6.3-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959"}, - {file = "yarl-1.6.3-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2"}, - {file = "yarl-1.6.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2"}, - {file = "yarl-1.6.3-cp38-cp38-win32.whl", hash = "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896"}, - {file = "yarl-1.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a"}, - {file = "yarl-1.6.3-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e"}, - {file = "yarl-1.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724"}, - {file = "yarl-1.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c"}, - {file = "yarl-1.6.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25"}, - {file = "yarl-1.6.3-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96"}, - {file = "yarl-1.6.3-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0"}, - {file = "yarl-1.6.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4"}, - {file = "yarl-1.6.3-cp39-cp39-win32.whl", hash = "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424"}, - {file = "yarl-1.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6"}, - {file = "yarl-1.6.3.tar.gz", hash = "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10"}, + {file = "yarl-1.5.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb"}, + {file = "yarl-1.5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593"}, + {file = "yarl-1.5.1-cp35-cp35m-win32.whl", hash = "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409"}, + {file = "yarl-1.5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317"}, + {file = "yarl-1.5.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511"}, + {file = "yarl-1.5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e"}, + {file = "yarl-1.5.1-cp36-cp36m-win32.whl", hash = "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f"}, + {file = "yarl-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2"}, + {file = "yarl-1.5.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a"}, + {file = "yarl-1.5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8"}, + {file = "yarl-1.5.1-cp37-cp37m-win32.whl", hash = "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8"}, + {file = "yarl-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d"}, + {file = "yarl-1.5.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02"}, + {file = "yarl-1.5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a"}, + {file = "yarl-1.5.1-cp38-cp38-win32.whl", hash = "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6"}, + {file = "yarl-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692"}, + {file = "yarl-1.5.1.tar.gz", hash = "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6"}, ] diff --git a/pyproject.toml b/pyproject.toml index 2528511e..8a79ea4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ pillow = "~=8.1" pytz = "~=2019.2" sentry-sdk = "~=0.19" PyYAML = "~=5.4" -"discord.py" = "~=1.7.2" +"discord.py" = "~=1.5.1" async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} emojis = "~=0.6.0" matplotlib = "~=3.4.1" -- cgit v1.2.3 From 6230b7836593e125577261d90ec8e8910fa0f739 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 16 May 2021 15:54:13 +0100 Subject: Bump d.py to 1.7.2 d.py 1.5.1 depending on multidict, which needs gcc installed. Bumping d.py to 1.7.2 removes the need fo rbuild tools in the image. d.py 1.7 introduced the line sep are on the paginator, so this needs to be added to our linepaginator subclass. --- bot/utils/pagination.py | 19 ++++- poetry.lock | 212 +++++++++++++++++++++++++++++++++--------------- pyproject.toml | 2 +- 3 files changed, 161 insertions(+), 72 deletions(-) diff --git a/bot/utils/pagination.py b/bot/utils/pagination.py index 742281d7..a0a362cb 100644 --- a/bot/utils/pagination.py +++ b/bot/utils/pagination.py @@ -27,7 +27,14 @@ class EmptyPaginatorEmbed(Exception): class LinePaginator(Paginator): """A class that aids in paginating code blocks for Discord messages.""" - def __init__(self, prefix: str = "```", suffix: str = "```", max_size: int = 2000, max_lines: int = None): + def __init__( + self, + prefix: str = '```', + suffix: str = '```', + max_size: int = 2000, + max_lines: int = None, + linesep: str = "\n" + ): """ Overrides the Paginator.__init__ from inside discord.ext.commands. @@ -36,9 +43,13 @@ class LinePaginator(Paginator): `max_size` and `max_lines` denote the maximum amount of codepoints and lines allowed per page. """ - self.prefix = prefix - self.suffix = suffix - self.max_size = max_size - len(suffix) + super().__init__( + prefix, + suffix, + max_size - len(suffix), + linesep + ) + self.max_lines = max_lines self._current_page = [prefix] self._linecount = 0 diff --git a/poetry.lock b/poetry.lock index 78a30370..f70d0328 100644 --- a/poetry.lock +++ b/poetry.lock @@ -11,18 +11,19 @@ pycares = ">=3.0.0" [[package]] name = "aiohttp" -version = "3.6.3" +version = "3.7.4.post0" description = "Async http client/server framework (asyncio)" category = "main" optional = false -python-versions = ">=3.5.3" +python-versions = ">=3.6" [package.dependencies] async-timeout = ">=3.0,<4.0" attrs = ">=17.3.0" -chardet = ">=2.0,<4.0" -multidict = ">=4.5,<5.0" -yarl = ">=1.0,<1.6.0" +chardet = ">=2.0,<5.0" +multidict = ">=4.5,<7.0" +typing-extensions = ">=3.6.5" +yarl = ">=1.0,<2.0" [package.extras] speedups = ["aiodns", "brotlipy", "cchardet"] @@ -139,11 +140,11 @@ python-versions = ">=3.6.1" [[package]] name = "chardet" -version = "3.0.4" +version = "4.0.0" description = "Universal encoding detector for Python 2 and 3" category = "main" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "cycler" @@ -158,18 +159,18 @@ six = "*" [[package]] name = "discord.py" -version = "1.5.1" +version = "1.7.2" description = "A Python wrapper for the Discord API" category = "main" optional = false python-versions = ">=3.5.3" [package.dependencies] -aiohttp = ">=3.6.0,<3.7.0" +aiohttp = ">=3.6.0,<3.8.0" [package.extras] -docs = ["sphinx (==1.8.5)", "sphinxcontrib-trio (==1.1.1)", "sphinxcontrib-websupport"] -voice = ["PyNaCl (==1.3.0)"] +docs = ["sphinx (==3.0.3)", "sphinxcontrib-trio (==1.1.2)", "sphinxcontrib-websupport"] +voice = ["PyNaCl (>=1.3.0,<1.5)"] [[package]] name = "distlib" @@ -398,11 +399,11 @@ python-versions = ">=3.5" [[package]] name = "multidict" -version = "4.7.6" +version = "5.1.0" description = "multidict implementation" category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [[package]] name = "nodeenv" @@ -653,6 +654,14 @@ category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "typing-extensions" +version = "3.10.0.0" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "urllib3" version = "1.26.4" @@ -686,11 +695,11 @@ testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", [[package]] name = "yarl" -version = "1.5.1" +version = "1.6.3" description = "Yet another URL library" category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] idna = ">=2.0" @@ -699,7 +708,7 @@ multidict = ">=4.0" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "fc7ffecf728af848ad4fcc1d5b14ded3360b8473ca5da77b5d39e256d36bd0fd" +content-hash = "d0003b8cc4caac9d6eb0c14e4c4085191907d7fa0803888eddae4259446eada7" [metadata.files] aiodns = [ @@ -707,19 +716,43 @@ aiodns = [ {file = "aiodns-2.0.0.tar.gz", hash = "sha256:815fdef4607474295d68da46978a54481dd1e7be153c7d60f9e72773cd38d77d"}, ] aiohttp = [ - {file = "aiohttp-3.6.3-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:1a4160579ffbc1b69e88cb6ca8bb0fbd4947dfcbf9fb1e2a4fc4c7a4a986c1fe"}, - {file = "aiohttp-3.6.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:fb83326d8295e8840e4ba774edf346e87eca78ba8a89c55d2690352842c15ba5"}, - {file = "aiohttp-3.6.3-cp35-cp35m-win32.whl", hash = "sha256:470e4c90da36b601676fe50c49a60d34eb8c6593780930b1aa4eea6f508dfa37"}, - {file = "aiohttp-3.6.3-cp35-cp35m-win_amd64.whl", hash = "sha256:a885432d3cabc1287bcf88ea94e1826d3aec57fd5da4a586afae4591b061d40d"}, - {file = "aiohttp-3.6.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:c506853ba52e516b264b106321c424d03f3ddef2813246432fa9d1cefd361c81"}, - {file = "aiohttp-3.6.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:797456399ffeef73172945708810f3277f794965eb6ec9bd3a0c007c0476be98"}, - {file = "aiohttp-3.6.3-cp36-cp36m-win32.whl", hash = "sha256:60f4caa3b7f7a477f66ccdd158e06901e1d235d572283906276e3803f6b098f5"}, - {file = "aiohttp-3.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2ad493de47a8f926386fa6d256832de3095ba285f325db917c7deae0b54a9fc8"}, - {file = "aiohttp-3.6.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:319b490a5e2beaf06891f6711856ea10591cfe84fe9f3e71a721aa8f20a0872a"}, - {file = "aiohttp-3.6.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:66d64486172b032db19ea8522328b19cfb78a3e1e5b62ab6a0567f93f073dea0"}, - {file = "aiohttp-3.6.3-cp37-cp37m-win32.whl", hash = "sha256:206c0ccfcea46e1bddc91162449c20c72f308aebdcef4977420ef329c8fcc599"}, - {file = "aiohttp-3.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:687461cd974722110d1763b45c5db4d2cdee8d50f57b00c43c7590d1dd77fc5c"}, - {file = "aiohttp-3.6.3.tar.gz", hash = "sha256:698cd7bc3c7d1b82bb728bae835724a486a8c376647aec336aa21a60113c3645"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:3cf75f7cdc2397ed4442594b935a11ed5569961333d49b7539ea741be2cc79d5"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4b302b45040890cea949ad092479e01ba25911a15e648429c7c5aae9650c67a8"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:393f389841e8f2dfc86f774ad22f00923fdee66d238af89b70ea314c4aefd290"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:c6e9dcb4cb338d91a73f178d866d051efe7c62a7166653a91e7d9fb18274058f"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:5df68496d19f849921f05f14f31bd6ef53ad4b00245da3195048c69934521809"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-win32.whl", hash = "sha256:3d78619672183be860b96ed96f533046ec97ca067fd46ac1f6a09cd9b7484287"}, + {file = "aiohttp-3.7.4.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:f705e12750171c0ab4ef2a3c76b9a4024a62c4103e3a55dd6f99265b9bc6fcfc"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:230a8f7e24298dea47659251abc0fd8b3c4e38a664c59d4b89cca7f6c09c9e87"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2e19413bf84934d651344783c9f5e22dee452e251cfd220ebadbed2d9931dbf0"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e4b2b334e68b18ac9817d828ba44d8fcb391f6acb398bcc5062b14b2cbeac970"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:d012ad7911653a906425d8473a1465caa9f8dea7fcf07b6d870397b774ea7c0f"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:40eced07f07a9e60e825554a31f923e8d3997cfc7fb31dbc1328c70826e04cde"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:209b4a8ee987eccc91e2bd3ac36adee0e53a5970b8ac52c273f7f8fd4872c94c"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:14762875b22d0055f05d12abc7f7d61d5fd4fe4642ce1a249abdf8c700bf1fd8"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-win32.whl", hash = "sha256:7615dab56bb07bff74bc865307aeb89a8bfd9941d2ef9d817b9436da3a0ea54f"}, + {file = "aiohttp-3.7.4.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:d9e13b33afd39ddeb377eff2c1c4f00544e191e1d1dee5b6c51ddee8ea6f0cf5"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:547da6cacac20666422d4882cfcd51298d45f7ccb60a04ec27424d2f36ba3eaf"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:af9aa9ef5ba1fd5b8c948bb11f44891968ab30356d65fd0cc6707d989cd521df"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:64322071e046020e8797117b3658b9c2f80e3267daec409b350b6a7a05041213"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bb437315738aa441251214dad17428cafda9cdc9729499f1d6001748e1d432f4"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:e54962802d4b8b18b6207d4a927032826af39395a3bd9196a5af43fc4e60b009"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:a00bb73540af068ca7390e636c01cbc4f644961896fa9363154ff43fd37af2f5"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:79ebfc238612123a713a457d92afb4096e2148be17df6c50fb9bf7a81c2f8013"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-win32.whl", hash = "sha256:515dfef7f869a0feb2afee66b957cc7bbe9ad0cdee45aec7fdc623f4ecd4fb16"}, + {file = "aiohttp-3.7.4.post0-cp38-cp38-win_amd64.whl", hash = "sha256:114b281e4d68302a324dd33abb04778e8557d88947875cbf4e842c2c01a030c5"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:7b18b97cf8ee5452fa5f4e3af95d01d84d86d32c5e2bfa260cf041749d66360b"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:15492a6368d985b76a2a5fdd2166cddfea5d24e69eefed4630cbaae5c81d89bd"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bdb230b4943891321e06fc7def63c7aace16095be7d9cf3b1e01be2f10fba439"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:cffe3ab27871bc3ea47df5d8f7013945712c46a3cc5a95b6bee15887f1675c22"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:a5ca29ee66f8343ed336816c553e82d6cade48a3ad702b9ffa6125d187e2dedb"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:17c073de315745a1510393a96e680d20af8e67e324f70b42accbd4cb3315c9fb"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-win32.whl", hash = "sha256:932bb1ea39a54e9ea27fc9232163059a0b8855256f4052e776357ad9add6f1c9"}, + {file = "aiohttp-3.7.4.post0-cp39-cp39-win_amd64.whl", hash = "sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe"}, + {file = "aiohttp-3.7.4.post0.tar.gz", hash = "sha256:493d3299ebe5f5a7c66b9819eacdcfbbaaf1a8e84911ddffcdc48888497afecf"}, ] aioredis = [ {file = "aioredis-1.3.1-py3-none-any.whl", hash = "sha256:b61808d7e97b7cd5a92ed574937a079c9387fdadd22bfbfa7ad2fd319ecc26e3"}, @@ -798,16 +831,16 @@ cfgv = [ {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, ] chardet = [ - {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, - {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] cycler = [ {file = "cycler-0.10.0-py2.py3-none-any.whl", hash = "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d"}, {file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"}, ] "discord.py" = [ - {file = "discord.py-1.5.1-py3-none-any.whl", hash = "sha256:2367359e31f6527f8a936751fc20b09d7495dd6a76b28c8fb13d4ca6c55b7563"}, - {file = "discord.py-1.5.1.tar.gz", hash = "sha256:def00dc50cf36d21346d71bc89f0cad8f18f9a3522978dc18c7796287d47de8b"}, + {file = "discord.py-1.7.2-py3-none-any.whl", hash = "sha256:f179db299c949a8cf0a12c1b1b94d0da9a18e088857154d93ae5ab1d807ec61d"}, + {file = "discord.py-1.7.2.tar.gz", hash = "sha256:114e76cd27362fb919abf7f001a2dbdc77c9a67cff74ed6a89aecd6582ee298e"}, ] distlib = [ {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, @@ -979,23 +1012,43 @@ mslex = [ {file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"}, ] multidict = [ - {file = "multidict-4.7.6-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000"}, - {file = "multidict-4.7.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a"}, - {file = "multidict-4.7.6-cp35-cp35m-win32.whl", hash = "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5"}, - {file = "multidict-4.7.6-cp35-cp35m-win_amd64.whl", hash = "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3"}, - {file = "multidict-4.7.6-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87"}, - {file = "multidict-4.7.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2"}, - {file = "multidict-4.7.6-cp36-cp36m-win32.whl", hash = "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7"}, - {file = "multidict-4.7.6-cp36-cp36m-win_amd64.whl", hash = "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463"}, - {file = "multidict-4.7.6-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d"}, - {file = "multidict-4.7.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255"}, - {file = "multidict-4.7.6-cp37-cp37m-win32.whl", hash = "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507"}, - {file = "multidict-4.7.6-cp37-cp37m-win_amd64.whl", hash = "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c"}, - {file = "multidict-4.7.6-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b"}, - {file = "multidict-4.7.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7"}, - {file = "multidict-4.7.6-cp38-cp38-win32.whl", hash = "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d"}, - {file = "multidict-4.7.6-cp38-cp38-win_amd64.whl", hash = "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19"}, - {file = "multidict-4.7.6.tar.gz", hash = "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430"}, + {file = "multidict-5.1.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224"}, + {file = "multidict-5.1.0-cp36-cp36m-win32.whl", hash = "sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26"}, + {file = "multidict-5.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6"}, + {file = "multidict-5.1.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37"}, + {file = "multidict-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5"}, + {file = "multidict-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632"}, + {file = "multidict-5.1.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea"}, + {file = "multidict-5.1.0-cp38-cp38-win32.whl", hash = "sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656"}, + {file = "multidict-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3"}, + {file = "multidict-5.1.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda"}, + {file = "multidict-5.1.0-cp39-cp39-win32.whl", hash = "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"}, + {file = "multidict-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359"}, + {file = "multidict-5.1.0.tar.gz", hash = "sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5"}, ] nodeenv = [ {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"}, @@ -1230,6 +1283,11 @@ toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] +typing-extensions = [ + {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, + {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, + {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, +] urllib3 = [ {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, @@ -1239,21 +1297,41 @@ virtualenv = [ {file = "virtualenv-20.4.6.tar.gz", hash = "sha256:72cf267afc04bf9c86ec932329b7e94db6a0331ae9847576daaa7ca3c86b29a4"}, ] yarl = [ - {file = "yarl-1.5.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb"}, - {file = "yarl-1.5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593"}, - {file = "yarl-1.5.1-cp35-cp35m-win32.whl", hash = "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409"}, - {file = "yarl-1.5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317"}, - {file = "yarl-1.5.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511"}, - {file = "yarl-1.5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e"}, - {file = "yarl-1.5.1-cp36-cp36m-win32.whl", hash = "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f"}, - {file = "yarl-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2"}, - {file = "yarl-1.5.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a"}, - {file = "yarl-1.5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8"}, - {file = "yarl-1.5.1-cp37-cp37m-win32.whl", hash = "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8"}, - {file = "yarl-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d"}, - {file = "yarl-1.5.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02"}, - {file = "yarl-1.5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a"}, - {file = "yarl-1.5.1-cp38-cp38-win32.whl", hash = "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6"}, - {file = "yarl-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692"}, - {file = "yarl-1.5.1.tar.gz", hash = "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6"}, + {file = "yarl-1.6.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366"}, + {file = "yarl-1.6.3-cp36-cp36m-win32.whl", hash = "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721"}, + {file = "yarl-1.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643"}, + {file = "yarl-1.6.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970"}, + {file = "yarl-1.6.3-cp37-cp37m-win32.whl", hash = "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e"}, + {file = "yarl-1.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50"}, + {file = "yarl-1.6.3-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2"}, + {file = "yarl-1.6.3-cp38-cp38-win32.whl", hash = "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896"}, + {file = "yarl-1.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a"}, + {file = "yarl-1.6.3-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4"}, + {file = "yarl-1.6.3-cp39-cp39-win32.whl", hash = "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424"}, + {file = "yarl-1.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6"}, + {file = "yarl-1.6.3.tar.gz", hash = "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10"}, ] diff --git a/pyproject.toml b/pyproject.toml index 8a79ea4c..2528511e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ pillow = "~=8.1" pytz = "~=2019.2" sentry-sdk = "~=0.19" PyYAML = "~=5.4" -"discord.py" = "~=1.5.1" +"discord.py" = "~=1.7.2" async-rediscache = {extras = ["fakeredis"], version = "~=0.1.4"} emojis = "~=0.6.0" matplotlib = "~=3.4.1" -- cgit v1.2.3 From c77180bfdebd83f92929eb204c968c2e693598f7 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 16 May 2021 16:02:01 +0100 Subject: Update CODEOWNERS with poetry files --- .github/CODEOWNERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 16e89359..f41ef83b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,4 +8,5 @@ Dockerfile @Akarys42 @Den4200 docker-compose.yml @Akarys42 @Den4200 # Tools -Pipfile* @Akarys42 +poetry.lock @Akarys42 +pyproject.toml @Akarys42 -- cgit v1.2.3 From ca9232b11acf012b8525871ec02428cae7f2c205 Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Sun, 16 May 2021 16:53:09 +0100 Subject: Type hint optional parameter Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/utils/pagination.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/utils/pagination.py b/bot/utils/pagination.py index a0a362cb..d9c0862a 100644 --- a/bot/utils/pagination.py +++ b/bot/utils/pagination.py @@ -32,7 +32,7 @@ class LinePaginator(Paginator): prefix: str = '```', suffix: str = '```', max_size: int = 2000, - max_lines: int = None, + max_lines: Optional[int] = None, linesep: str = "\n" ): """ -- cgit v1.2.3 From 90dcb6c2bc5005ebc1d890b6a194a9b53521de0a Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Sun, 16 May 2021 19:02:03 +0100 Subject: fix: use resolved reference --- bot/exts/evergreen/bookmark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index c84446c5..85c9b46f 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -97,7 +97,7 @@ class Bookmark(commands.Cog): if not target_message: if not ctx.message.reference: raise commands.UserInputError("You must either provide a valid message to bookmark, or reply to one.") - target_message = await ctx.fetch_message(ctx.message.reference.message_id) + target_message = ctx.message.reference.resolved # Prevent users from bookmarking a message in a channel they don't have access to permissions = ctx.author.permissions_in(target_message.channel) -- cgit v1.2.3 From aeb3ce4af9613325d23d590f3123192f9b991c2b Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Sun, 16 May 2021 17:01:16 -0400 Subject: fix: Send the help for the right command in .src help --- bot/exts/evergreen/source.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/source.py b/bot/exts/evergreen/source.py index 8fb72143..fc209bc3 100644 --- a/bot/exts/evergreen/source.py +++ b/bot/exts/evergreen/source.py @@ -33,7 +33,8 @@ class BotSource(commands.Cog): Raise BadArgument if `source_item` is a dynamically-created object (e.g. via internal eval). """ if isinstance(source_item, commands.Command): - src = source_item.callback.__code__ + callback = inspect.unwrap(source_item.callback) + src = callback.__code__ filename = src.co_filename else: src = type(source_item) @@ -64,12 +65,8 @@ class BotSource(commands.Cog): url, location, first_line = self.get_source_link(source_object) if isinstance(source_object, commands.Command): - if source_object.cog_name == "Help": - title = "Help Command" - description = source_object.__doc__.splitlines()[1] - else: - description = source_object.short_doc - title = f"Command: {source_object.qualified_name}" + description = source_object.short_doc + title = f"Command: {source_object.qualified_name}" else: title = f"Cog: {source_object.qualified_name}" description = source_object.description.splitlines()[0] -- cgit v1.2.3 From 00a4c148ff8754272921527410d35b0c3c27e44c Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Mon, 17 May 2021 11:17:35 -0400 Subject: fix: Handle a KeyError when using .quiz stop --- bot/exts/evergreen/trivia_quiz.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 419126dc..a8d10afd 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -465,22 +465,24 @@ class TriviaQuiz(commands.Cog): Note: Only mods or the owner of the quiz can stop it. """ - if self.game_status[ctx.channel.id] is True: - # Check if the author is the game starter or a moderator. - if ctx.author == self.game_owners[ctx.channel.id] or any( - Roles.moderator == role.id for role in ctx.author.roles - ): - - await ctx.send("Quiz stopped.") - await self.declare_winner(ctx.channel, self.game_player_scores[ctx.channel.id]) - - self.game_status[ctx.channel.id] = False - del self.game_owners[ctx.channel.id] - self.game_player_scores[ctx.channel.id] = {} + try: + 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( + Roles.moderator == role.id for role in ctx.author.roles + ): + self.game_status[ctx.channel.id] = False + del self.game_owners[ctx.channel.id] + self.game_player_scores[ctx.channel.id] = {} + + await ctx.send("Quiz stopped.") + await self.declare_winner(ctx.channel, self.game_player_scores[ctx.channel.id]) + else: + await ctx.send(f"{ctx.author.mention}, you are not authorised to stop this game :ghost:!") else: - await ctx.send(f"{ctx.author.mention}, you are not authorised to stop this game :ghost:!") - else: + await ctx.send("No quiz running.") + except KeyError: await ctx.send("No quiz running.") @quiz_game.command(name="leaderboard") -- cgit v1.2.3 From 1b21117a300d9191dec9f286e3bbd1c8e336e340 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 17 May 2021 22:35:58 +0100 Subject: Change AoC helpers to use arrow over pytz This is the only place in the codebase that uses pytz, so we can remove the dependancy by chaging this to use arrow. I chose to use America/Chicago arbitrarily, for no other reason that being the first tz google returned that was in EST. --- bot/exts/christmas/advent_of_code/_helpers.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/bot/exts/christmas/advent_of_code/_helpers.py b/bot/exts/christmas/advent_of_code/_helpers.py index f4a258c0..99688400 100644 --- a/bot/exts/christmas/advent_of_code/_helpers.py +++ b/bot/exts/christmas/advent_of_code/_helpers.py @@ -9,8 +9,8 @@ import typing from typing import Tuple import aiohttp +import arrow import discord -import pytz from bot.bot import Bot from bot.constants import AdventOfCode, Channels, Colours @@ -48,7 +48,7 @@ AOC_EMBED_THUMBNAIL = ( ) # Create an easy constant for the EST timezone -EST = pytz.timezone("EST") +EST = "America/Chicago" # Step size for the challenge countdown status COUNTDOWN_STEP = 60 * 5 @@ -395,13 +395,13 @@ def is_in_advent() -> bool: something for the next Advent of Code challenge should run. As the puzzle published on the 25th is the last puzzle, this check excludes that date. """ - return datetime.datetime.now(EST).day in range(1, 25) and datetime.datetime.now(EST).month == 12 + return arrow.now(EST).day in range(1, 25) and arrow.now(EST).month == 12 def time_left_to_est_midnight() -> Tuple[datetime.datetime, datetime.timedelta]: """Calculate the amount of time left until midnight EST/UTC-5.""" # Change all time properties back to 00:00 - todays_midnight = datetime.datetime.now(EST).replace( + todays_midnight = arrow.now(EST).replace( microsecond=0, second=0, minute=0, @@ -412,7 +412,7 @@ def time_left_to_est_midnight() -> Tuple[datetime.datetime, datetime.timedelta]: tomorrow = todays_midnight + datetime.timedelta(days=1) # Calculate the timedelta between the current time and midnight - return tomorrow, tomorrow - datetime.datetime.now(EST) + return tomorrow, tomorrow - arrow.now(EST) async def wait_for_advent_of_code(*, hours_before: int = 1) -> None: @@ -430,9 +430,9 @@ async def wait_for_advent_of_code(*, hours_before: int = 1) -> None: if we're already past the Advent of Code edition the bot is currently configured for. """ - start = datetime.datetime(AdventOfCode.year, 12, 1, 0, 0, 0, tzinfo=EST) + start = arrow.get(datetime.datetime(AdventOfCode.year, 12, 1), EST) target = start - datetime.timedelta(hours=hours_before) - now = datetime.datetime.now(EST) + now = arrow.now(EST) # If we've already reached or passed to target, we # simply return immediately. @@ -474,10 +474,10 @@ async def countdown_status(bot: Bot) -> None: # sleeping for the entire year, it will only wait in the currently # configured year. This means that the task will only start hibernating once # we start preparing the next event by changing environment variables. - last_challenge = datetime.datetime(AdventOfCode.year, 12, 25, 0, 0, 0, tzinfo=EST) + last_challenge = arrow.get(datetime.datetime(AdventOfCode.year, 12, 25), EST) end = last_challenge + datetime.timedelta(hours=1) - while datetime.datetime.now(EST) < end: + while arrow.now(EST) < end: _, time_left = time_left_to_est_midnight() aligned_seconds = int(math.ceil(time_left.seconds / COUNTDOWN_STEP)) * COUNTDOWN_STEP @@ -534,8 +534,8 @@ async def new_puzzle_notification(bot: Bot) -> None: # The last event day is 25 December, so we only have to schedule # a reminder if the current day is before 25 December. - end = datetime.datetime(AdventOfCode.year, 12, 25, tzinfo=EST) - while datetime.datetime.now(EST) < end: + end = arrow.get(datetime.datetime(AdventOfCode.year, 12, 25), EST) + while arrow.now(EST) < end: log.trace("Started puzzle notification loop.") tomorrow, time_left = time_left_to_est_midnight() -- cgit v1.2.3 From 36738b64ffadaf5860df20d911c0cc7e922dd397 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 17 May 2021 22:37:25 +0100 Subject: Remove bs4 and pytz update arrow to 1.1.0 Both bs4 and pytz are no longer used. Updated arrow to 1.1.0 while I'm here as the breaking changes don't impact us. --- poetry.lock | 88 ++++++++++++++++++++-------------------------------------- pyproject.toml | 4 +-- 2 files changed, 31 insertions(+), 61 deletions(-) diff --git a/poetry.lock b/poetry.lock index f70d0328..b6581b1b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -50,11 +50,11 @@ python-versions = "*" [[package]] name = "arrow" -version = "0.17.0" +version = "1.1.0" description = "Better dates & times for Python" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" [package.dependencies] python-dateutil = ">=2.7.0" @@ -96,21 +96,6 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] -[[package]] -name = "beautifulsoup4" -version = "4.9.3" -description = "Screen-scraping library" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -soupsieve = {version = ">1.2", markers = "python_version >= \"3.0\""} - -[package.extras] -html5lib = ["html5lib"] -lxml = ["lxml"] - [[package]] name = "certifi" version = "2020.12.5" @@ -146,6 +131,14 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + [[package]] name = "cycler" version = "0.10.0" @@ -499,7 +492,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pydocstyle" -version = "6.0.0" +version = "6.1.1" description = "Python docstring style checker" category = "dev" optional = false @@ -508,6 +501,9 @@ python-versions = ">=3.6" [package.dependencies] snowballstemmer = "*" +[package.extras] +toml = ["toml"] + [[package]] name = "pyflakes" version = "2.3.1" @@ -546,14 +542,6 @@ python-versions = "*" [package.extras] cli = ["click (>=5.0)"] -[[package]] -name = "pytz" -version = "2019.3" -description = "World timezone definitions, modern and historical" -category = "main" -optional = false -python-versions = "*" - [[package]] name = "pyyaml" version = "5.4.1" @@ -619,29 +607,22 @@ python-versions = "*" [[package]] name = "sortedcontainers" -version = "2.3.0" +version = "2.4.0" description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" category = "main" optional = false python-versions = "*" -[[package]] -name = "soupsieve" -version = "2.2.1" -description = "A modern CSS selector implementation for Beautiful Soup." -category = "main" -optional = false -python-versions = ">=3.6" - [[package]] name = "taskipy" -version = "1.7.0" +version = "1.8.1" description = "tasks runner for python projects" category = "dev" optional = false python-versions = ">=3.6,<4.0" [package.dependencies] +colorama = ">=0.4.4,<0.5.0" mslex = ">=0.3.0,<0.4.0" psutil = ">=5.7.2,<6.0.0" toml = ">=0.10.0,<0.11.0" @@ -708,7 +689,7 @@ multidict = ">=4.0" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "d0003b8cc4caac9d6eb0c14e4c4085191907d7fa0803888eddae4259446eada7" +content-hash = "14c54d898cad74073a3f8f83283be3ea3c32fbb8558149284c1475677b99bd59" [metadata.files] aiodns = [ @@ -763,8 +744,8 @@ appdirs = [ {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] arrow = [ - {file = "arrow-0.17.0-py2.py3-none-any.whl", hash = "sha256:e098abbd9af3665aea81bdd6c869e93af4feb078e98468dd351c383af187aac5"}, - {file = "arrow-0.17.0.tar.gz", hash = "sha256:ff08d10cda1d36c68657d6ad20d74fbea493d980f8b2d45344e00d6ed2bf6ed4"}, + {file = "arrow-1.1.0-py3-none-any.whl", hash = "sha256:8cbe6a629b1c54ae11b52d6d9e70890089241958f63bc59467e277e34b7a5378"}, + {file = "arrow-1.1.0.tar.gz", hash = "sha256:b8fe13abf3517abab315e09350c903902d1447bd311afbc17547ba1cb3ff5bd8"}, ] async-rediscache = [ {file = "async-rediscache-0.1.4.tar.gz", hash = "sha256:6be8a657d724ccbcfb1946d29a80c3478c5f9ecd2f78a0a26d2f4013a622258f"}, @@ -778,11 +759,6 @@ attrs = [ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] -beautifulsoup4 = [ - {file = "beautifulsoup4-4.9.3-py2-none-any.whl", hash = "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35"}, - {file = "beautifulsoup4-4.9.3-py3-none-any.whl", hash = "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"}, - {file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"}, -] certifi = [ {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, @@ -834,6 +810,10 @@ chardet = [ {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] cycler = [ {file = "cycler-0.10.0-py2.py3-none-any.whl", hash = "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d"}, {file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"}, @@ -1197,8 +1177,8 @@ pycparser = [ {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, ] pydocstyle = [ - {file = "pydocstyle-6.0.0-py3-none-any.whl", hash = "sha256:d4449cf16d7e6709f63192146706933c7a334af7c0f083904799ccb851c50f6d"}, - {file = "pydocstyle-6.0.0.tar.gz", hash = "sha256:164befb520d851dbcf0e029681b91f4f599c62c5cd8933fd54b1bfbd50e89e1f"}, + {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, + {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, ] pyflakes = [ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, @@ -1216,10 +1196,6 @@ python-dotenv = [ {file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"}, {file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"}, ] -pytz = [ - {file = "pytz-2019.3-py2.py3-none-any.whl", hash = "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d"}, - {file = "pytz-2019.3.tar.gz", hash = "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"}, -] pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, @@ -1268,16 +1244,12 @@ snowballstemmer = [ {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] sortedcontainers = [ - {file = "sortedcontainers-2.3.0-py2.py3-none-any.whl", hash = "sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f"}, - {file = "sortedcontainers-2.3.0.tar.gz", hash = "sha256:59cc937650cf60d677c16775597c89a960658a09cf7c1a668f86e1e4464b10a1"}, -] -soupsieve = [ - {file = "soupsieve-2.2.1-py3-none-any.whl", hash = "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b"}, - {file = "soupsieve-2.2.1.tar.gz", hash = "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc"}, + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] taskipy = [ - {file = "taskipy-1.7.0-py3-none-any.whl", hash = "sha256:9e284c10898e9dee01a3e72220b94b192b1daa0f560271503a6df1da53d03844"}, - {file = "taskipy-1.7.0.tar.gz", hash = "sha256:960e480b1004971e76454ecd1a0484e640744a30073a1069894a311467f85ed8"}, + {file = "taskipy-1.8.1-py3-none-any.whl", hash = "sha256:2b98f499966e40175d1f1306a64587f49dfa41b90d0d86c8f28b067cc58d0a56"}, + {file = "taskipy-1.8.1.tar.gz", hash = "sha256:7a2404125817e45d80e13fa663cae35da6e8ba590230094e815633653e25f98f"}, ] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, diff --git a/pyproject.toml b/pyproject.toml index 2528511e..de7fb2eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,11 +8,9 @@ license = "MIT" [tool.poetry.dependencies] python = "^3.9" aiodns = "~=2.0" -arrow = "~=0.14" -beautifulsoup4 = "~=4.8" +arrow = "~=1.1.0" fuzzywuzzy = "~=0.17" pillow = "~=8.1" -pytz = "~=2019.2" sentry-sdk = "~=0.19" PyYAML = "~=5.4" "discord.py" = "~=1.7.2" -- cgit v1.2.3 From 38e7ca7e8530fc9175b79724812892eb82907228 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 17 May 2021 23:58:47 +0100 Subject: Update references using EST const to use arrow --- bot/exts/christmas/advent_of_code/_cog.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/christmas/advent_of_code/_cog.py b/bot/exts/christmas/advent_of_code/_cog.py index ead84544..3d61753b 100644 --- a/bot/exts/christmas/advent_of_code/_cog.py +++ b/bot/exts/christmas/advent_of_code/_cog.py @@ -3,6 +3,7 @@ import logging from datetime import datetime, timedelta from pathlib import Path +import arrow import discord from discord.ext import commands @@ -100,11 +101,11 @@ class AdventOfCode(commands.Cog): async def aoc_countdown(self, ctx: commands.Context) -> None: """Return time left until next day.""" if not _helpers.is_in_advent(): - datetime_now = datetime.now(_helpers.EST) + datetime_now = arrow.now(_helpers.EST) # Calculate the delta to this & next year's December 1st to see which one is closest and not in the past - this_year = datetime(datetime_now.year, 12, 1, tzinfo=_helpers.EST) - next_year = datetime(datetime_now.year + 1, 12, 1, tzinfo=_helpers.EST) + this_year = arrow.get(datetime(datetime_now.year, 12, 1), _helpers.EST) + next_year = arrow.get(datetime(datetime_now.year + 1, 12, 1), _helpers.EST) deltas = (dec_first - datetime_now for dec_first in (this_year, next_year)) delta = min(delta for delta in deltas if delta >= timedelta()) # timedelta() gives 0 duration delta -- cgit v1.2.3 From d15cc6585e6be014b1ecd72df65bb26f1fc0d3bb Mon Sep 17 00:00:00 2001 From: Shivansh Date: Tue, 18 May 2021 10:56:01 +0530 Subject: Change bot typehint to bot.bot.Bot --- bot/exts/pride/pride_leader.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index 83b3ac0e..0e1ddb20 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -8,6 +8,7 @@ import discord from discord.ext import commands from fuzzywuzzy import fuzz +from bot import bot from bot import constants log = logging.getLogger(__name__) @@ -19,7 +20,7 @@ MINIMUM_FUZZ_RATIO = 40 class PrideLeader(commands.Cog): """Gives information about Pride Leaders.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: bot.Bot): self.bot = bot with PRIDE_LEADERS_RESOURCE.open(encoding="utf8") as data: @@ -117,6 +118,6 @@ class PrideLeader(commands.Cog): await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: +def setup(bot: bot.Bot) -> None: """Loads the Pride leader cog.""" bot.add_cog(PrideLeader(bot)) -- cgit v1.2.3 From dd01caf0393386998f9c121ccb1c108057c03f77 Mon Sep 17 00:00:00 2001 From: Shivansh Date: Tue, 18 May 2021 11:01:59 +0530 Subject: Apply code review suggestions * Use read_text rather a with open * Remove Redundant list comprehension --- bot/exts/pride/pride_leader.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index 0e1ddb20..95ca74e4 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -23,8 +23,7 @@ class PrideLeader(commands.Cog): def __init__(self, bot: bot.Bot): self.bot = bot - with PRIDE_LEADERS_RESOURCE.open(encoding="utf8") as data: - self.pride = json.load(data) + self.pride = json.loads(PRIDE_LEADERS_RESOURCE.read_text("utf8")) def invalid_embed_generate(self, pride_leader: str) -> discord.Embed: """ @@ -104,7 +103,7 @@ class PrideLeader(commands.Cog): and if there is no pride leader given, return a random pride leader. """ if not pride_leader_name: - leader = random.choice([name for name in self.pride]) + leader = random.choice(list(self.pride)) else: leader = self.pride.get(pride_leader_name.title()) if not leader: -- cgit v1.2.3 From 12fb4ecd736c8aad74eef65691f363f5e52449ab Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 18 May 2021 11:03:58 +0530 Subject: Make `setup` docstring same as other cogs Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/pride/pride_leader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index 95ca74e4..521f5985 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -118,5 +118,5 @@ class PrideLeader(commands.Cog): def setup(bot: bot.Bot) -> None: - """Loads the Pride leader cog.""" + """Load the Pride Leader Cog.""" bot.add_cog(PrideLeader(bot)) -- cgit v1.2.3 From b55df6ce4987159970115aeccab1c01fc1c03c8e Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 18 May 2021 09:53:46 +0100 Subject: Use an actual EST timezone Timezones are hard :( --- bot/exts/christmas/advent_of_code/_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/christmas/advent_of_code/_helpers.py b/bot/exts/christmas/advent_of_code/_helpers.py index 99688400..96de90c4 100644 --- a/bot/exts/christmas/advent_of_code/_helpers.py +++ b/bot/exts/christmas/advent_of_code/_helpers.py @@ -48,7 +48,7 @@ AOC_EMBED_THUMBNAIL = ( ) # Create an easy constant for the EST timezone -EST = "America/Chicago" +EST = "America/New_York" # Step size for the challenge countdown status COUNTDOWN_STEP = 60 * 5 -- cgit v1.2.3 From 7ad20d865e499942d13b4589101b65827e135de3 Mon Sep 17 00:00:00 2001 From: Icebluewolf <44532864+Icebluewolf@users.noreply.github.com> Date: Wed, 19 May 2021 17:21:12 -0500 Subject: remove unnecessary check --- bot/exts/evergreen/status_codes.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bot/exts/evergreen/status_codes.py b/bot/exts/evergreen/status_codes.py index dabf004d..089bdb4a 100644 --- a/bot/exts/evergreen/status_codes.py +++ b/bot/exts/evergreen/status_codes.py @@ -24,9 +24,7 @@ class HTTPStatusCodes(commands.Cog): async def http_status_group(self, ctx: commands.Context, code: int) -> None: """Choose a cat or dog randomly for the given status code.""" subcmd = choice((self.http_cat, self.http_dog)) - - if await subcmd.can_run(ctx): - await subcmd(ctx, code) + await subcmd(ctx, code) @http_status_group.command(name="cat") async def http_cat(self, ctx: commands.Context, code: int) -> None: -- cgit v1.2.3 From 8e03537ba2c9fea98cab7645a985fc80a867da9f Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 20 May 2021 18:32:10 +0530 Subject: Remove trailing space --- bot/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/constants.py b/bot/constants.py index 24dd42a2..28103022 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -309,7 +309,7 @@ class Source: class RedirectOutput: delete_delay: int = 10 - + class Reddit: subreddits = ["r/Python"] -- cgit v1.2.3 From 03a2c3c737795863f70572d080ff5eed15137cce Mon Sep 17 00:00:00 2001 From: Shivansh Date: Thu, 20 May 2021 18:47:27 +0530 Subject: (hotfix): Add __init__ to error handler cog --- bot/exts/evergreen/error_handler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index cd53f932..5873fb83 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -22,6 +22,9 @@ QUESTION_MARK_ICON = "https://cdn.discordapp.com/emojis/512367613339369475.png" class CommandErrorHandler(commands.Cog): """A error handler for the PythonDiscord server.""" + def __init__(self, bot: Bot) -> None: + self.bot = bot + @staticmethod def revert_cooldown_counter(command: commands.Command, message: Message) -> None: """Undoes the last cooldown counter for user-error cases.""" @@ -166,4 +169,4 @@ class CommandErrorHandler(commands.Cog): def setup(bot: Bot) -> None: """Load the ErrorHandler cog.""" - bot.add_cog(CommandErrorHandler()) + bot.add_cog(CommandErrorHandler(bot)) -- cgit v1.2.3 From 8b5f4117cceb809c412948976566bcd2ee4a2bfc Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 25 May 2021 19:25:25 +0100 Subject: Forward some status dog codes to status cat instead These status dog codes aren't server-friendly, so we use status cat instead. --- bot/exts/evergreen/status_codes.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bot/exts/evergreen/status_codes.py b/bot/exts/evergreen/status_codes.py index 089bdb4a..181c71ce 100644 --- a/bot/exts/evergreen/status_codes.py +++ b/bot/exts/evergreen/status_codes.py @@ -52,6 +52,11 @@ class HTTPStatusCodes(commands.Cog): @http_status_group.command(name="dog") async def http_dog(self, ctx: commands.Context, code: int) -> None: """Sends an embed with an image of a dog, portraying the status code.""" + # These codes aren't server-friendly. + if code in (304, 422): + await self.http_cat(ctx, code) + return + embed = discord.Embed(title=f"**Status: {code}**") url = HTTP_DOG_URL.format(code=code) -- cgit v1.2.3 From 6dc4dcd52d7dd697a6c0ad8a7cc941eeb1d102b7 Mon Sep 17 00:00:00 2001 From: Soham Banerjee <63705023+soham4abc@users.noreply.github.com> Date: Sat, 29 May 2021 09:54:10 +0530 Subject: Added "centisecond" in trivia_quiz.json (#751) * added centisecond in json Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- bot/resources/evergreen/trivia_quiz.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index 94082849..fee1b6d7 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -435,7 +435,7 @@ { "id": 228, "question": "1/100th of a second is also termed as what?", - "answer": "a jiffy, jiffy" + "answer": "a jiffy, jiffy, centisecond" }, { "id": 229, -- cgit v1.2.3 From 8e8de36ca18f2c830adb30aef970467c740b0b49 Mon Sep 17 00:00:00 2001 From: prashant Date: Mon, 31 May 2021 19:22:27 +0530 Subject: adeed rps command --- bot/exts/evergreen/rps.py | 98 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 bot/exts/evergreen/rps.py diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py new file mode 100644 index 00000000..3bb69871 --- /dev/null +++ b/bot/exts/evergreen/rps.py @@ -0,0 +1,98 @@ +import random +from bot.bot import Bot +import discord +from discord.ext import commands +from discord.ext.commands import guild_only + +choices = ['rock', 'paper', 'scissor'] +short_choices = ['r', 'p', 's'] +""" +Instead of putting bunch of conditions to check winner, +We can just manage this dictionary +""" +winner = { + 'r': { + 'r': 0, + 'p': -1, + 's': 1, + }, + 'p': { + 'r': 1, + 'p': 0, + 's': -1, + }, + 's': { + 'r': -1, + 'p': 1, + 's': 0, + } +} + + +class Game: + """A Rock Paper Scissors Game.""" + def __init__( + self, + channel: discord.TextChannel, + ) -> None: + self.channel = channel + + @staticmethod + def get_winner(action_one, action_two): + return winner[action_one][action_two] + + @staticmethod + def make_move() -> str: + """Return move""" + return random.choice(choices) + + async def game_start(self, player, action) -> None: + if not action: + return await self.channel.send("Please make a move.") + action = action.lower() + if action not in choices and action not in short_choices: + return await self.channel.send(f"Invalid move. Please make from options: {' '.join(choices)}") + bot_move = self.make_move() + player_result = self.get_winner(action[0], bot_move[0]) + if player_result == 0: + message_string = f"{player.mention} You and Sir Lancebot played {bot_move.upper()}, It's a tie." + return await self.channel.send(message_string) + elif player_result == 1: + return await self.channel.send(f"Sir Lancebot played {bot_move.upper()}! {player.mention} Won!") + else: + return await self.channel.send(f"Sir Lancebot played {bot_move.upper()}! {player.mention} Lost!") + + +class RPS(commands.Cog): + """Rock Paper Scissor. The Classic Game!""" + """ + def __init__(self, bot: Bot) -> None: + self.bot = bot + """ + async def _play_game( + self, + ctx: commands.Context, + move: str + ) -> None: + """Helper for playing RPS.""" + game = Game(ctx.channel) + await game.game_start(ctx.author, move) + + @guild_only() + @commands.group( + invoke_without_command=True, + case_insensitive=True + ) + async def rps( + self, + ctx: commands.Context, + arg + ) -> None: + """ + Play the classic game of Rock Paper Scisorr with your own sir lancebot! + """ + await self._play_game(ctx, arg) + + +def setup(bot: Bot) -> None: + bot.add_cog(RPS(bot)) -- cgit v1.2.3 From ae0bb30e9d335621e87f4ce232e74af48f66e7b2 Mon Sep 17 00:00:00 2001 From: prashant Date: Mon, 31 May 2021 19:23:09 +0530 Subject: minor changes --- bot/exts/evergreen/rps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index 3bb69871..df77f032 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -51,7 +51,7 @@ class Game: return await self.channel.send("Please make a move.") action = action.lower() if action not in choices and action not in short_choices: - return await self.channel.send(f"Invalid move. Please make from options: {' '.join(choices)}") + return await self.channel.send(f"Invalid move. Please make move from options: {' '.join(choices)}") bot_move = self.make_move() player_result = self.get_winner(action[0], bot_move[0]) if player_result == 0: -- cgit v1.2.3 From fdd60f0679b2935e7590cfbfd22c05dbc894ae68 Mon Sep 17 00:00:00 2001 From: prashant Date: Tue, 1 Jun 2021 11:04:26 +0530 Subject: made changes requested in #758 and resolved flake8 errors. --- bot/exts/evergreen/rps.py | 93 ++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 54 deletions(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index df77f032..e7c1a182 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -1,16 +1,18 @@ -import random -from bot.bot import Bot -import discord +from random import choice + +from discord import Member, TextChannel from discord.ext import commands from discord.ext.commands import guild_only -choices = ['rock', 'paper', 'scissor'] -short_choices = ['r', 'p', 's'] -""" -Instead of putting bunch of conditions to check winner, -We can just manage this dictionary -""" -winner = { +from bot.bot import Bot + + +CHOICES = ['rock', 'paper', 'scissor'] +SHORT_CHOICES = ['r', 'p', 's'] + +# Instead of putting bunch of conditions to check winner, +# we can just manage this dictionary +WINNER_DICT = { 'r': { 'r': 0, 'p': -1, @@ -29,70 +31,53 @@ winner = { } -class Game: - """A Rock Paper Scissors Game.""" - def __init__( - self, - channel: discord.TextChannel, - ) -> None: - self.channel = channel +class RPS(commands.Cog): + """Rock Paper Scissor. The Classic Game!""" @staticmethod - def get_winner(action_one, action_two): - return winner[action_one][action_two] + def get_winner(action_one: str, action_two: str) -> int: + """Returns result of match from (-1, 0, 1) as (lost, tied, won).""" + return WINNER_DICT[action_one][action_two] @staticmethod def make_move() -> str: - """Return move""" - return random.choice(choices) + """Returns random move for bot from CHOICES.""" + return choice(CHOICES) - async def game_start(self, player, action) -> None: + async def game_start(self, player: Member, channel: TextChannel, action: str) -> None: + """ + Check action of player, draw a move and return result. + + After checking if action of player is valid, make a random move. + And based on the move, compare moves of both player and bot and send approprite result. + """ if not action: - return await self.channel.send("Please make a move.") + await channel.send("Please make a move.") + return action = action.lower() - if action not in choices and action not in short_choices: - return await self.channel.send(f"Invalid move. Please make move from options: {' '.join(choices)}") + if action not in CHOICES and action not in SHORT_CHOICES: + await channel.send(f"Invalid move. Please make move from options: {' '.join(CHOICES)}") + return bot_move = self.make_move() player_result = self.get_winner(action[0], bot_move[0]) if player_result == 0: message_string = f"{player.mention} You and Sir Lancebot played {bot_move.upper()}, It's a tie." - return await self.channel.send(message_string) + await channel.send(message_string) elif player_result == 1: - return await self.channel.send(f"Sir Lancebot played {bot_move.upper()}! {player.mention} Won!") + await channel.send(f"Sir Lancebot played {bot_move.upper()}! {player.mention} Won!") else: - return await self.channel.send(f"Sir Lancebot played {bot_move.upper()}! {player.mention} Lost!") - - -class RPS(commands.Cog): - """Rock Paper Scissor. The Classic Game!""" - """ - def __init__(self, bot: Bot) -> None: - self.bot = bot - """ - async def _play_game( - self, - ctx: commands.Context, - move: str - ) -> None: - """Helper for playing RPS.""" - game = Game(ctx.channel) - await game.game_start(ctx.author, move) + await channel.send(f"Sir Lancebot played {bot_move.upper()}! {player.mention} Lost!") @guild_only() - @commands.group( + @commands.command( invoke_without_command=True, case_insensitive=True ) - async def rps( - self, - ctx: commands.Context, - arg - ) -> None: - """ - Play the classic game of Rock Paper Scisorr with your own sir lancebot! - """ - await self._play_game(ctx, arg) + async def rps(self, ctx: commands.Context, move: str) -> None: + """Play the classic game of Rock Paper Scissor with your own sir-lancebot!""" + await self.game_start(ctx.author, ctx.channel, move) def setup(bot: Bot) -> None: + """Load RPS Cog.""" bot.add_cog(RPS(bot)) -- cgit v1.2.3 From c5b4a94a2eee3efe5627d52cb656be9eb09b4ed6 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 1 Jun 2021 10:32:32 -0500 Subject: command finished --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 442df2f0..c4f5915e 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -121,12 +121,12 @@ class AvatarModify(commands.Cog): await ctx.send(embed=embed, file=file) - @avatar_modify.command(aliases=("flip", "mirror")) + @avatar_modify.command(name="reverse", root_aliases=("reverse",)) async def reverse(self, ctx: commands.Context, *, text: t.Optional[str]) -> None: """Either flips your profile picture or the submitted text.""" if not text: async with ctx.typing(): - user = self._fetch_user(ctx.author.id) + user = await self._fetch_user(ctx.author.id) image_bytes = await user.avatar_url_as(size=1024).read() file_name = file_safe_name("reverse_avatar", ctx.author.display_name) @@ -143,10 +143,11 @@ class AvatarModify(commands.Cog): ) embed.set_image(url=f"attachment://{file_name}") - embed.set_footer(f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) await ctx.send(embed=embed, file=file) - + else: + await ctx.send(f"> {text[::-1]}") @avatar_modify.command(aliases=("easterify",), root_aliases=("easterify", "avatareasterify")) async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: -- cgit v1.2.3 From ef3198074d5e1bf76fa3295993e4f1031a651f0e Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 1 Jun 2021 10:38:48 -0500 Subject: Blocked mentions. --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index c4f5915e..6789f44a 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -147,7 +147,7 @@ class AvatarModify(commands.Cog): await ctx.send(embed=embed, file=file) else: - await ctx.send(f"> {text[::-1]}") + await ctx.send(f"> {text[::-1]}", allowed_mentions=discord.AllowedMentions.none()) @avatar_modify.command(aliases=("easterify",), root_aliases=("easterify", "avatareasterify")) async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: -- cgit v1.2.3 From a25b9a2fea3ba48dd1ad036018169f6261894d39 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 1 Jun 2021 11:02:34 -0500 Subject: changes asked for by chris --- bot/exts/evergreen/avatar_modification/_effects.py | 3 +-- bot/exts/evergreen/avatar_modification/avatar_modify.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/_effects.py b/bot/exts/evergreen/avatar_modification/_effects.py index 7811d148..c5e5cb86 100644 --- a/bot/exts/evergreen/avatar_modification/_effects.py +++ b/bot/exts/evergreen/avatar_modification/_effects.py @@ -97,13 +97,12 @@ class PfpEffects: return image.quantize() @staticmethod - def reverse_effect(image: Image.Image) -> Image.Image: + def flip_effect(image: Image.Image) -> Image.Image: """ Flips the image horizontally. This is done by just using ImageOps.flip(). """ - image = image.resize((1024, 1024)) image = ImageOps.mirror(image) return image diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 6789f44a..4ed7f79d 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -124,7 +124,7 @@ class AvatarModify(commands.Cog): @avatar_modify.command(name="reverse", root_aliases=("reverse",)) async def reverse(self, ctx: commands.Context, *, text: t.Optional[str]) -> None: """Either flips your profile picture or the submitted text.""" - if not text: + if text is None: async with ctx.typing(): user = await self._fetch_user(ctx.author.id) image_bytes = await user.avatar_url_as(size=1024).read() @@ -133,7 +133,7 @@ class AvatarModify(commands.Cog): file = await in_executor( PfpEffects.apply_effect, image_bytes, - PfpEffects.reverse_effect, + PfpEffects.flip_effect, file_name ) -- cgit v1.2.3 From fe529ddb89f34f8274b7c979a9508966b14c8d7f Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 1 Jun 2021 11:20:10 -0500 Subject: checked if user returns None --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 4ed7f79d..98f5f132 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -127,6 +127,9 @@ class AvatarModify(commands.Cog): if text is None: async with ctx.typing(): user = await self._fetch_user(ctx.author.id) + if not user: + await ctx.send(f"{Emojis.cross_mark} Could not get user info.") + return image_bytes = await user.avatar_url_as(size=1024).read() file_name = file_safe_name("reverse_avatar", ctx.author.display_name) -- cgit v1.2.3 From 2c63f7fd419c506568e705093e9478b9cf29f4a8 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 1 Jun 2021 11:30:09 -0500 Subject: Small docstring change. Co-authored-by: ChrisJL --- bot/exts/evergreen/avatar_modification/_effects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/_effects.py b/bot/exts/evergreen/avatar_modification/_effects.py index c5e5cb86..46d1a2ab 100644 --- a/bot/exts/evergreen/avatar_modification/_effects.py +++ b/bot/exts/evergreen/avatar_modification/_effects.py @@ -101,7 +101,7 @@ class PfpEffects: """ Flips the image horizontally. - This is done by just using ImageOps.flip(). + This is done by just using ImageOps.mirror(). """ image = ImageOps.mirror(image) -- cgit v1.2.3 From fcd635fe6209ce8021e6129fb1df0dd610fb451e Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Tue, 1 Jun 2021 14:09:16 -0500 Subject: started cmd group --- .../evergreen/avatar_modification/avatar_modify.py | 58 ++++++++++++---------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 98f5f132..526ad3d0 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -121,36 +121,44 @@ class AvatarModify(commands.Cog): await ctx.send(embed=embed, file=file) - @avatar_modify.command(name="reverse", root_aliases=("reverse",)) - async def reverse(self, ctx: commands.Context, *, text: t.Optional[str]) -> None: - """Either flips your profile picture or the submitted text.""" - if text is None: - async with ctx.typing(): - user = await self._fetch_user(ctx.author.id) - if not user: - await ctx.send(f"{Emojis.cross_mark} Could not get user info.") - return - image_bytes = await user.avatar_url_as(size=1024).read() - file_name = file_safe_name("reverse_avatar", ctx.author.display_name) + @avatar_modify.group(name="reverse") + async def reverse(self, ctx: commands.Context) -> None: + if not ctx.invoked_subcommand: + await invoke_help_command(ctx) - file = await in_executor( - PfpEffects.apply_effect, - image_bytes, - PfpEffects.flip_effect, - file_name - ) + @reverse.command(name="text") + async def text(self, ctx: commands.Context, *, text: str) -> None: + """Sends the given text backwards.""" + await ctx.send(f"> {text[::-1]}", allowed_mentions=discord.AllowedMentions.none()) - embed = discord.Embed( - title="Your reversed avatar", - description="Here is your avatar. I think it's a spitting image of you." - ) + @reverse.command(name="image") + async def image(self, ctx: commands.Context) -> None: + """Sends a reversed version of the users profile picture.""" + async with ctx.typing(): + user = self._fetch_user(ctx.author.id) + if not user: + await ctx.send(f"{Emojis.cross_mark} Could not get user info.") + return + + image_bytes = await user.avatar_url_as(size=1024).read() + filename = file_safe_name("reverse_avatar", ctx.author.display_name) - embed.set_image(url=f"attachment://{file_name}") - embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) + file = await in_executor( + PfpEffects.apply_effect, + image_bytes, + PfpEffects.flip_effect, + filename + ) + + embed = discord.Embed( + title="Your reversed avatar.", + description="Here is your reversed avatar. I think it is a spitting image of you." + ) + + embed.set_image(f"attachment://{filename}") + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) await ctx.send(embed=embed, file=file) - else: - await ctx.send(f"> {text[::-1]}", allowed_mentions=discord.AllowedMentions.none()) @avatar_modify.command(aliases=("easterify",), root_aliases=("easterify", "avatareasterify")) async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: -- cgit v1.2.3 From 0442ea631198fd42014436e5c64c3a696b9b78c9 Mon Sep 17 00:00:00 2001 From: prashant Date: Wed, 2 Jun 2021 16:56:26 +0530 Subject: added logical spacing and removed redundant code --- bot/exts/evergreen/rps.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index e7c1a182..d07f9c60 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -54,12 +54,16 @@ class RPS(commands.Cog): if not action: await channel.send("Please make a move.") return + action = action.lower() + if action not in CHOICES and action not in SHORT_CHOICES: await channel.send(f"Invalid move. Please make move from options: {' '.join(CHOICES)}") return + bot_move = self.make_move() player_result = self.get_winner(action[0], bot_move[0]) + if player_result == 0: message_string = f"{player.mention} You and Sir Lancebot played {bot_move.upper()}, It's a tie." await channel.send(message_string) @@ -69,10 +73,7 @@ class RPS(commands.Cog): await channel.send(f"Sir Lancebot played {bot_move.upper()}! {player.mention} Lost!") @guild_only() - @commands.command( - invoke_without_command=True, - case_insensitive=True - ) + @commands.command(case_insensitive=True) async def rps(self, ctx: commands.Context, move: str) -> None: """Play the classic game of Rock Paper Scissor with your own sir-lancebot!""" await self.game_start(ctx.author, ctx.channel, move) -- cgit v1.2.3 From 5be1c59450c363b5f4483d3e46a82780ea229632 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Wed, 2 Jun 2021 09:51:00 -0500 Subject: Finished extra functionality --- .../evergreen/avatar_modification/avatar_modify.py | 50 +++++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 526ad3d0..940ff69e 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -9,6 +9,7 @@ from concurrent.futures import ThreadPoolExecutor from pathlib import Path import discord +from PIL import UnidentifiedImageError from aiohttp import client_exceptions from discord.ext import commands @@ -121,8 +122,9 @@ class AvatarModify(commands.Cog): await ctx.send(embed=embed, file=file) - @avatar_modify.group(name="reverse") + @avatar_modify.group(name="reverse", root_aliases=['reverse']) async def reverse(self, ctx: commands.Context) -> None: + """Group for the reverse commands.""" if not ctx.invoked_subcommand: await invoke_help_command(ctx) @@ -132,14 +134,48 @@ class AvatarModify(commands.Cog): await ctx.send(f"> {text[::-1]}", allowed_mentions=discord.AllowedMentions.none()) @reverse.command(name="image") - async def image(self, ctx: commands.Context) -> None: - """Sends a reversed version of the users profile picture.""" + async def photo(self, ctx: commands.Context) -> None: + """ + Sends a reversed version of the users profile picture. + + If an image is attached, the given image will be flipped. + """ async with ctx.typing(): - user = self._fetch_user(ctx.author.id) + user = await self._fetch_user(ctx.author.id) if not user: await ctx.send(f"{Emojis.cross_mark} Could not get user info.") return - + if ctx.message.attachments: + url = str(ctx.message.attachments[0]) + async with self.bot.http_session.get(url) as r: + image_bytes = await r.read() + filename = file_safe_name("reverse_image", ctx.author.display_name) + + try: + file = await in_executor( + PfpEffects.apply_effect, + image_bytes, + PfpEffects.flip_effect, + filename + ) + except UnidentifiedImageError: + raise commands.BadArgument( + "The attachment given is not an image. Please ensure you send an image." + ) + return + + embed = discord.Embed( + title="Your reversed image.", + description="Here is your reversed image. I think it looks somewhat flipped." + ) + + embed.set_image(url=f"attachment://{filename}") + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) + + await ctx.send(embed=embed, file=file) + + return + image_bytes = await user.avatar_url_as(size=1024).read() filename = file_safe_name("reverse_avatar", ctx.author.display_name) @@ -151,11 +187,11 @@ class AvatarModify(commands.Cog): ) embed = discord.Embed( - title="Your reversed avatar.", + title="Your reversed avatar.", description="Here is your reversed avatar. I think it is a spitting image of you." ) - embed.set_image(f"attachment://{filename}") + embed.set_image(url=f"attachment://{filename}") embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) await ctx.send(embed=embed, file=file) -- cgit v1.2.3 From f3bdc2d33b434101108d3bec1910055e3dead3e6 Mon Sep 17 00:00:00 2001 From: Shivansh Date: Thu, 3 Jun 2021 15:37:22 +0530 Subject: Make self.pride a constant --- bot/exts/pride/pride_leader.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index 521f5985..1ff2206a 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -13,7 +13,7 @@ from bot import constants log = logging.getLogger(__name__) -PRIDE_LEADERS_RESOURCE = Path("bot/resources/pride/prideleader.json") +PRIDE_RESOURCE = json.loads(Path("bot/resources/pride/prideleader.json").read_text("utf8")) MINIMUM_FUZZ_RATIO = 40 @@ -23,8 +23,6 @@ class PrideLeader(commands.Cog): def __init__(self, bot: bot.Bot): self.bot = bot - self.pride = json.loads(PRIDE_LEADERS_RESOURCE.read_text("utf8")) - def invalid_embed_generate(self, pride_leader: str) -> discord.Embed: """ Generates Invalid Embed. @@ -42,12 +40,12 @@ class PrideLeader(commands.Cog): ) valid_names = [] pride_leader = pride_leader.title() - for name in self.pride: + for name in PRIDE_RESOURCE: if fuzz.ratio(pride_leader, name) >= MINIMUM_FUZZ_RATIO: valid_names.append(name) if not valid_names: - valid_names = ", ".join(self.pride) + valid_names = ", ".join(PRIDE_RESOURCE) error_msg = "Sorry your input didn't match any stored names, here is a list of available names:" else: valid_names = "\n".join(valid_names) @@ -67,22 +65,22 @@ class PrideLeader(commands.Cog): """Generate an Embed with information about a pride leader.""" embed = discord.Embed( title=leader_name, - description=self.pride[leader_name]["About"], + description=PRIDE_RESOURCE[leader_name]["About"], color=constants.Colours.blue ) embed.add_field( name="Known for", - value=self.pride[leader_name]["Known for"], + value=PRIDE_RESOURCE[leader_name]["Known for"], inline=False ) embed.add_field( name="D.O.B and Birth place", - value=self.pride[leader_name]["Born"], + value=PRIDE_RESOURCE[leader_name]["Born"], inline=False ) embed.add_field( name="Awards and honors", - value=self.pride[leader_name]["Awards"], + value=PRIDE_RESOURCE[leader_name]["Awards"], inline=False ) embed.add_field( @@ -91,7 +89,7 @@ class PrideLeader(commands.Cog): f" in <#{constants.Channels.community_bot_commands}>", inline=False ) - embed.set_thumbnail(url=self.pride[leader_name]["url"]) + embed.set_thumbnail(url=PRIDE_RESOURCE[leader_name]["url"]) return embed @commands.command(aliases=("pl", "prideleader")) @@ -103,9 +101,9 @@ class PrideLeader(commands.Cog): and if there is no pride leader given, return a random pride leader. """ if not pride_leader_name: - leader = random.choice(list(self.pride)) + leader = random.choice(list(PRIDE_RESOURCE)) else: - leader = self.pride.get(pride_leader_name.title()) + leader = PRIDE_RESOURCE.get(pride_leader_name.title()) if not leader: log.trace(f"Got a Invalid pride leader: {pride_leader_name}") -- cgit v1.2.3 From 0cd444aea566c24d70c1bb0228ba23fb68b4e5e5 Mon Sep 17 00:00:00 2001 From: Shivansh Date: Thu, 3 Jun 2021 15:38:20 +0530 Subject: Pass pride leader name into embed generator --- bot/exts/pride/pride_leader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index 1ff2206a..7977212a 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -101,7 +101,7 @@ class PrideLeader(commands.Cog): and if there is no pride leader given, return a random pride leader. """ if not pride_leader_name: - leader = random.choice(list(PRIDE_RESOURCE)) + pride_leader_name = random.choice(list(PRIDE_RESOURCE)) else: leader = PRIDE_RESOURCE.get(pride_leader_name.title()) if not leader: @@ -111,7 +111,7 @@ class PrideLeader(commands.Cog): await ctx.send(embed=embed) return - embed = self.embed_builder(leader) + embed = self.embed_builder(pride_leader_name) await ctx.send(embed=embed) -- cgit v1.2.3 From b4ec17e97d8ab63683b2afbd6e52305a1ecc51d6 Mon Sep 17 00:00:00 2001 From: Shivansh Date: Thu, 3 Jun 2021 19:56:20 +0530 Subject: Add message to contribute instead of wiki link in error embed --- bot/exts/pride/pride_leader.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index 7977212a..681cc0f6 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -52,12 +52,7 @@ class PrideLeader(commands.Cog): error_msg = "Did you mean?" embed.description = f"{error_msg}\n```{valid_names}```" - embed.add_field( - name="You can get information about the Pride Leader on the Wikipedia command", - value=f"Do `{constants.Client.prefix}wiki {pride_leader}`" - f" in <#{constants.Channels.community_bot_commands}>", - inline=False - ) + embed.set_footer(text="To add more pride leaders, feel free to open a pull request!") return embed -- cgit v1.2.3 From 1a118437102c4c562ca413267e85a6488c525544 Mon Sep 17 00:00:00 2001 From: Shivansh Date: Fri, 4 Jun 2021 07:28:05 +0530 Subject: Pass information dict into embed builder --- bot/exts/pride/pride_leader.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index 681cc0f6..fefc8bf0 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -56,35 +56,37 @@ class PrideLeader(commands.Cog): return embed - def embed_builder(self, leader_name: str) -> discord.Embed: + def embed_builder(self, pride_leader: dict) -> discord.Embed: """Generate an Embed with information about a pride leader.""" + name = [name for name, info in PRIDE_RESOURCE.items() if info == pride_leader][0] + embed = discord.Embed( - title=leader_name, - description=PRIDE_RESOURCE[leader_name]["About"], + title=name, + description=pride_leader["About"], color=constants.Colours.blue ) embed.add_field( name="Known for", - value=PRIDE_RESOURCE[leader_name]["Known for"], + value=pride_leader["Known for"], inline=False ) embed.add_field( name="D.O.B and Birth place", - value=PRIDE_RESOURCE[leader_name]["Born"], + value=pride_leader["Born"], inline=False ) embed.add_field( name="Awards and honors", - value=PRIDE_RESOURCE[leader_name]["Awards"], + value=pride_leader["Awards"], inline=False ) embed.add_field( name="For More Information", - value=f"Do `{constants.Client.prefix}wiki {leader_name}`" + value=f"Do `{constants.Client.prefix}wiki {name}`" f" in <#{constants.Channels.community_bot_commands}>", inline=False ) - embed.set_thumbnail(url=PRIDE_RESOURCE[leader_name]["url"]) + embed.set_thumbnail(url=pride_leader["url"]) return embed @commands.command(aliases=("pl", "prideleader")) @@ -96,7 +98,7 @@ class PrideLeader(commands.Cog): and if there is no pride leader given, return a random pride leader. """ if not pride_leader_name: - pride_leader_name = random.choice(list(PRIDE_RESOURCE)) + leader = random.choice(list(PRIDE_RESOURCE.values())) else: leader = PRIDE_RESOURCE.get(pride_leader_name.title()) if not leader: @@ -106,7 +108,7 @@ class PrideLeader(commands.Cog): await ctx.send(embed=embed) return - embed = self.embed_builder(pride_leader_name) + embed = self.embed_builder(leader) await ctx.send(embed=embed) -- cgit v1.2.3 From 34853575c7318707f0708a06e4a64f1495dfb3c4 Mon Sep 17 00:00:00 2001 From: Shivansh Date: Fri, 4 Jun 2021 07:40:55 +0530 Subject: Fix lint issue --- bot/exts/pride/pride_leader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index fefc8bf0..c3426ad1 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -58,7 +58,7 @@ class PrideLeader(commands.Cog): def embed_builder(self, pride_leader: dict) -> discord.Embed: """Generate an Embed with information about a pride leader.""" - name = [name for name, info in PRIDE_RESOURCE.items() if info == pride_leader][0] + name = [name for name, info in PRIDE_RESOURCE.items() if info == pride_leader][0] embed = discord.Embed( title=name, -- cgit v1.2.3 From 9be669267b3261478049c3f478861f9a09f9a820 Mon Sep 17 00:00:00 2001 From: prashant Date: Fri, 4 Jun 2021 16:56:38 +0530 Subject: removed guild_only, now user can DM this command too! --- bot/exts/evergreen/rps.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index d07f9c60..8d844234 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -2,7 +2,6 @@ from random import choice from discord import Member, TextChannel from discord.ext import commands -from discord.ext.commands import guild_only from bot.bot import Bot @@ -72,7 +71,6 @@ class RPS(commands.Cog): else: await channel.send(f"Sir Lancebot played {bot_move.upper()}! {player.mention} Lost!") - @guild_only() @commands.command(case_insensitive=True) async def rps(self, ctx: commands.Context, move: str) -> None: """Play the classic game of Rock Paper Scissor with your own sir-lancebot!""" -- cgit v1.2.3 From 36c574797f9ae4a385cedfc0071d6aeaf31b717f Mon Sep 17 00:00:00 2001 From: Prashant <37273899+OculusMode@users.noreply.github.com> Date: Fri, 4 Jun 2021 18:20:52 +0530 Subject: Update bot/exts/evergreen/rps.py Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- bot/exts/evergreen/rps.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index 8d844234..e089ba85 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -9,8 +9,7 @@ from bot.bot import Bot CHOICES = ['rock', 'paper', 'scissor'] SHORT_CHOICES = ['r', 'p', 's'] -# Instead of putting bunch of conditions to check winner, -# we can just manage this dictionary +# Using a dictionary instead of conditions to check for the winner. WINNER_DICT = { 'r': { 'r': 0, -- cgit v1.2.3 From 2c04b89fb14317c86f497ba4ccbda8f86d3cd19d Mon Sep 17 00:00:00 2001 From: prashant Date: Fri, 4 Jun 2021 19:05:28 +0530 Subject: made changes given by Xith --- bot/exts/evergreen/rps.py | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index e089ba85..c34118f2 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -5,7 +5,6 @@ from discord.ext import commands from bot.bot import Bot - CHOICES = ['rock', 'paper', 'scissor'] SHORT_CHOICES = ['r', 'p', 's'] @@ -32,16 +31,6 @@ WINNER_DICT = { class RPS(commands.Cog): """Rock Paper Scissor. The Classic Game!""" - @staticmethod - def get_winner(action_one: str, action_two: str) -> int: - """Returns result of match from (-1, 0, 1) as (lost, tied, won).""" - return WINNER_DICT[action_one][action_two] - - @staticmethod - def make_move() -> str: - """Returns random move for bot from CHOICES.""" - return choice(CHOICES) - async def game_start(self, player: Member, channel: TextChannel, action: str) -> None: """ Check action of player, draw a move and return result. @@ -59,8 +48,9 @@ class RPS(commands.Cog): await channel.send(f"Invalid move. Please make move from options: {' '.join(CHOICES)}") return - bot_move = self.make_move() - player_result = self.get_winner(action[0], bot_move[0]) + bot_move = choice(CHOICES) + # value of player_result will be from (-1, 0, 1) as (lost, tied, won). + player_result = WINNER_DICT[action[0]][bot_move[0]] if player_result == 0: message_string = f"{player.mention} You and Sir Lancebot played {bot_move.upper()}, It's a tie." @@ -73,7 +63,29 @@ class RPS(commands.Cog): @commands.command(case_insensitive=True) async def rps(self, ctx: commands.Context, move: str) -> None: """Play the classic game of Rock Paper Scissor with your own sir-lancebot!""" - await self.game_start(ctx.author, ctx.channel, move) + channel = ctx.channel + if not move: + await channel.send("Please make a move.") + return + + move = move.lower() + player_mention = ctx.author.mention + + if move not in CHOICES and move not in SHORT_CHOICES: + await channel.send(f"Invalid move. Please make move from options: {', '.join(CHOICES).upper()}.") + return + + bot_move = choice(CHOICES) + # value of player_result will be from (-1, 0, 1) as (lost, tied, won). + player_result = WINNER_DICT[move[0]][bot_move[0]] + + if player_result == 0: + message_string = f"{player_mention} You and Sir Lancebot played {bot_move.upper()}, It's a tie." + await channel.send(message_string) + elif player_result == 1: + await channel.send(f"Sir Lancebot played {bot_move.upper()}! {player_mention} Won!") + else: + await channel.send(f"Sir Lancebot played {bot_move.upper()}! {player_mention} Lost!") def setup(bot: Bot) -> None: -- cgit v1.2.3 From 2e62c7c508c21e66bcf16012a7da3b8c964cc0ef Mon Sep 17 00:00:00 2001 From: prashant Date: Fri, 4 Jun 2021 19:06:39 +0530 Subject: cleaned up dead code --- bot/exts/evergreen/rps.py | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index c34118f2..13185470 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -1,6 +1,5 @@ from random import choice -from discord import Member, TextChannel from discord.ext import commands from bot.bot import Bot @@ -31,35 +30,6 @@ WINNER_DICT = { class RPS(commands.Cog): """Rock Paper Scissor. The Classic Game!""" - async def game_start(self, player: Member, channel: TextChannel, action: str) -> None: - """ - Check action of player, draw a move and return result. - - After checking if action of player is valid, make a random move. - And based on the move, compare moves of both player and bot and send approprite result. - """ - if not action: - await channel.send("Please make a move.") - return - - action = action.lower() - - if action not in CHOICES and action not in SHORT_CHOICES: - await channel.send(f"Invalid move. Please make move from options: {' '.join(CHOICES)}") - return - - bot_move = choice(CHOICES) - # value of player_result will be from (-1, 0, 1) as (lost, tied, won). - player_result = WINNER_DICT[action[0]][bot_move[0]] - - if player_result == 0: - message_string = f"{player.mention} You and Sir Lancebot played {bot_move.upper()}, It's a tie." - await channel.send(message_string) - elif player_result == 1: - await channel.send(f"Sir Lancebot played {bot_move.upper()}! {player.mention} Won!") - else: - await channel.send(f"Sir Lancebot played {bot_move.upper()}! {player.mention} Lost!") - @commands.command(case_insensitive=True) async def rps(self, ctx: commands.Context, move: str) -> None: """Play the classic game of Rock Paper Scissor with your own sir-lancebot!""" -- cgit v1.2.3 From 46ae4cc61471d4426492eb8f890d0e888b211f9f Mon Sep 17 00:00:00 2001 From: Prashant <37273899+OculusMode@users.noreply.github.com> Date: Fri, 4 Jun 2021 22:51:59 +0530 Subject: removed upper case on printing result and made minor grammar changes. Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- bot/exts/evergreen/rps.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index 13185470..49561e4b 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -50,12 +50,12 @@ class RPS(commands.Cog): player_result = WINNER_DICT[move[0]][bot_move[0]] if player_result == 0: - message_string = f"{player_mention} You and Sir Lancebot played {bot_move.upper()}, It's a tie." + message_string = f"{player_mention} You and Sir Lancebot played {bot_move}, it's a tie." await channel.send(message_string) elif player_result == 1: - await channel.send(f"Sir Lancebot played {bot_move.upper()}! {player_mention} Won!") + await channel.send(f"Sir Lancebot played {bot_move}! {player_mention} won!") else: - await channel.send(f"Sir Lancebot played {bot_move.upper()}! {player_mention} Lost!") + await channel.send(f"Sir Lancebot played {bot_move}! {player_mention} lost!") def setup(bot: Bot) -> None: -- cgit v1.2.3 From c883adaa3716d4ed2c421d45e9f935f79eee5944 Mon Sep 17 00:00:00 2001 From: prashant Date: Fri, 4 Jun 2021 23:01:31 +0530 Subject: changed contribution link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 11b46aac..dd8301dc 100755 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ We know it can be difficult to get into the whole open source thing at first. To This later evolved into a bot designed as a fun and beginner-friendly learning environment for writing bot features and learning open-source. ## Getting started -Before you start, please take some time to read through our [contributing guidelines](CONTRIBUTING.md). +Before you start, please take some time to read through our [contributing guidelines](https://pythondiscord.com/pages/guides/pydis-guides/contributing/contributing-guidelines/). See [Sir Lancebot's Wiki](https://pythondiscord.com/pages/contributing/sir-lancebot/) for in-depth guides on getting started with the project! -- cgit v1.2.3 From 46c947a6434a8f94722ce85141b39a084fde693e Mon Sep 17 00:00:00 2001 From: prashant Date: Fri, 4 Jun 2021 23:23:15 +0530 Subject: -removed dead code -used ctx.send instead of channel.send --- bot/exts/evergreen/rps.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index 49561e4b..2634be21 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -33,16 +33,11 @@ class RPS(commands.Cog): @commands.command(case_insensitive=True) async def rps(self, ctx: commands.Context, move: str) -> None: """Play the classic game of Rock Paper Scissor with your own sir-lancebot!""" - channel = ctx.channel - if not move: - await channel.send("Please make a move.") - return - move = move.lower() player_mention = ctx.author.mention if move not in CHOICES and move not in SHORT_CHOICES: - await channel.send(f"Invalid move. Please make move from options: {', '.join(CHOICES).upper()}.") + await ctx.send(f"Invalid move. Please make move from options: {', '.join(CHOICES).upper()}.") return bot_move = choice(CHOICES) @@ -51,11 +46,11 @@ class RPS(commands.Cog): if player_result == 0: message_string = f"{player_mention} You and Sir Lancebot played {bot_move}, it's a tie." - await channel.send(message_string) + await ctx.send(message_string) elif player_result == 1: - await channel.send(f"Sir Lancebot played {bot_move}! {player_mention} won!") + await ctx.send(f"Sir Lancebot played {bot_move}! {player_mention} won!") else: - await channel.send(f"Sir Lancebot played {bot_move}! {player_mention} lost!") + await ctx.send(f"Sir Lancebot played {bot_move}! {player_mention} lost!") def setup(bot: Bot) -> None: -- cgit v1.2.3 From 0eb22e68e8d66b58eeb5bc53fc963cbe3050b03b Mon Sep 17 00:00:00 2001 From: Prashant <37273899+OculusMode@users.noreply.github.com> Date: Fri, 4 Jun 2021 23:36:58 +0530 Subject: resolved minor grammar issue. Co-authored-by: Anand Krishna <40204976+anand2312@users.noreply.github.com> --- bot/exts/evergreen/rps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index 2634be21..852ae33e 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -54,5 +54,5 @@ class RPS(commands.Cog): def setup(bot: Bot) -> None: - """Load RPS Cog.""" + """Load the RPS Cog.""" bot.add_cog(RPS(bot)) -- cgit v1.2.3 From 0ed7df8e9a05b999855e52f4b64a097e6de22ce3 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 4 Jun 2021 19:35:47 +0100 Subject: Install Poetry globally in Docker The previous way would error out when building due to not being able to find a file on the overwritten path. https://paste.pythondiscord.com/alixoqaxuc.py --- Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2bad6450..01a7f3b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,9 +4,8 @@ FROM python:3.9-slim ENV PIP_NO_CACHE_DIR=false \ POETRY_VIRTUALENVS_CREATE=false -# Install Poetry and add it to the path -RUN pip install --user poetry -ENV PATH="${PATH}:/root/.local/bin" +# Install Poetry +RUN pip install --upgrade poetry WORKDIR /bot -- cgit v1.2.3 From 132af40b9a4d17252dfb77f38e41eee0d0e90980 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Fri, 4 Jun 2021 14:18:57 -0500 Subject: Small renaming Co-authored-by: ChrisJL --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 940ff69e..952f2350 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -129,7 +129,7 @@ class AvatarModify(commands.Cog): await invoke_help_command(ctx) @reverse.command(name="text") - async def text(self, ctx: commands.Context, *, text: str) -> None: + async def reverse_text(self, ctx: commands.Context, *, text: str) -> None: """Sends the given text backwards.""" await ctx.send(f"> {text[::-1]}", allowed_mentions=discord.AllowedMentions.none()) -- cgit v1.2.3 From c866096a06ac35c751c1b1ec66de5e525f1ec5e4 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Fri, 4 Jun 2021 14:19:17 -0500 Subject: Another rename Co-authored-by: ChrisJL --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 952f2350..154fe5de 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -134,7 +134,7 @@ class AvatarModify(commands.Cog): await ctx.send(f"> {text[::-1]}", allowed_mentions=discord.AllowedMentions.none()) @reverse.command(name="image") - async def photo(self, ctx: commands.Context) -> None: + async def reverse_photo(self, ctx: commands.Context) -> None: """ Sends a reversed version of the users profile picture. -- cgit v1.2.3 From 9ecaf7cbad9e34da467fa2612b81ff407ec227df Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Fri, 4 Jun 2021 14:19:44 -0500 Subject: Removed the file upload Co-authored-by: ChrisJL --- .../evergreen/avatar_modification/avatar_modify.py | 30 ---------------------- 1 file changed, 30 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 154fe5de..9356a9e7 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -145,36 +145,6 @@ class AvatarModify(commands.Cog): if not user: await ctx.send(f"{Emojis.cross_mark} Could not get user info.") return - if ctx.message.attachments: - url = str(ctx.message.attachments[0]) - async with self.bot.http_session.get(url) as r: - image_bytes = await r.read() - filename = file_safe_name("reverse_image", ctx.author.display_name) - - try: - file = await in_executor( - PfpEffects.apply_effect, - image_bytes, - PfpEffects.flip_effect, - filename - ) - except UnidentifiedImageError: - raise commands.BadArgument( - "The attachment given is not an image. Please ensure you send an image." - ) - return - - embed = discord.Embed( - title="Your reversed image.", - description="Here is your reversed image. I think it looks somewhat flipped." - ) - - embed.set_image(url=f"attachment://{filename}") - embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) - - await ctx.send(embed=embed, file=file) - - return image_bytes = await user.avatar_url_as(size=1024).read() filename = file_safe_name("reverse_avatar", ctx.author.display_name) -- cgit v1.2.3 From 712867d2193afe52ac63964c0c4e08962040be95 Mon Sep 17 00:00:00 2001 From: prashant Date: Sat, 5 Jun 2021 00:55:39 +0530 Subject: made changes suggested by ToxicKidz --- bot/exts/evergreen/rps.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index 2634be21..dcb7dce1 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -37,8 +37,7 @@ class RPS(commands.Cog): player_mention = ctx.author.mention if move not in CHOICES and move not in SHORT_CHOICES: - await ctx.send(f"Invalid move. Please make move from options: {', '.join(CHOICES).upper()}.") - return + raise commands.BadArgument(f"Invalid move. Please make move from options: {', '.join(CHOICES).upper()}.") bot_move = choice(CHOICES) # value of player_result will be from (-1, 0, 1) as (lost, tied, won). -- cgit v1.2.3 From 880f02e77e9a34e46aaa12591bc6e7201833d5c2 Mon Sep 17 00:00:00 2001 From: prashant Date: Sat, 5 Jun 2021 01:07:22 +0530 Subject: changed single quotes to double to follow style guide --- bot/exts/evergreen/rps.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index b96ae80b..a5cacd90 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -4,25 +4,25 @@ from discord.ext import commands from bot.bot import Bot -CHOICES = ['rock', 'paper', 'scissor'] -SHORT_CHOICES = ['r', 'p', 's'] +CHOICES = ["rock", "paper", "scissors"] +SHORT_CHOICES = ["r", "p", "s"] # Using a dictionary instead of conditions to check for the winner. WINNER_DICT = { - 'r': { - 'r': 0, - 'p': -1, - 's': 1, + "r": { + "r": 0, + "p": -1, + "s": 1, }, - 'p': { - 'r': 1, - 'p': 0, - 's': -1, + "p": { + "r": 1, + "p": 0, + "s": -1, }, - 's': { - 'r': -1, - 'p': 1, - 's': 0, + "s": { + "r": -1, + "p": 1, + "s": 0, } } -- cgit v1.2.3 From 4e525d921dcc2bfcc19ca96fdb6de9c90ffdf0c7 Mon Sep 17 00:00:00 2001 From: prashant Date: Sat, 5 Jun 2021 10:43:06 +0530 Subject: made minor typo changes --- bot/exts/evergreen/rps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/rps.py b/bot/exts/evergreen/rps.py index a5cacd90..c6bbff46 100644 --- a/bot/exts/evergreen/rps.py +++ b/bot/exts/evergreen/rps.py @@ -28,11 +28,11 @@ WINNER_DICT = { class RPS(commands.Cog): - """Rock Paper Scissor. The Classic Game!""" + """Rock Paper Scissors. The Classic Game!""" @commands.command(case_insensitive=True) async def rps(self, ctx: commands.Context, move: str) -> None: - """Play the classic game of Rock Paper Scissor with your own sir-lancebot!""" + """Play the classic game of Rock Paper Scissors with your own sir-lancebot!""" move = move.lower() player_mention = ctx.author.mention -- cgit v1.2.3 From 9bdd47a141806db517f0f12474ec5688cf3bfb82 Mon Sep 17 00:00:00 2001 From: ToxicKidz Date: Sat, 5 Jun 2021 11:12:32 -0400 Subject: chore: Use constants instead of instance variables that don't exist This caused attribute errors in the prideanthem and pridefact commands. --- bot/exts/pride/pride_anthem.py | 2 +- bot/exts/pride/pride_facts.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/pride/pride_anthem.py b/bot/exts/pride/pride_anthem.py index 4650595a..05286b3d 100644 --- a/bot/exts/pride/pride_anthem.py +++ b/bot/exts/pride/pride_anthem.py @@ -24,7 +24,7 @@ class PrideAnthem(commands.Cog): If none can be found, it will log this as well as provide that information to the user. """ if not genre: - return random.choice(self.anthems) + return random.choice(VIDEOS) else: songs = [song for song in VIDEOS if genre.casefold() in song["genre"]] try: diff --git a/bot/exts/pride/pride_facts.py b/bot/exts/pride/pride_facts.py index 631e2e8b..63e33dda 100644 --- a/bot/exts/pride/pride_facts.py +++ b/bot/exts/pride/pride_facts.py @@ -57,7 +57,7 @@ class PrideFacts(commands.Cog): date = _date if date.year < now.year or (date.year == now.year and date.day <= now.day): try: - await target.send(embed=self.make_embed(self.facts[str(date.year)][date.day - 1])) + await target.send(embed=self.make_embed(FACTS[str(date.year)][date.day - 1])) except KeyError: await target.send(f"The year {date.year} is not yet supported") return -- cgit v1.2.3 From ac50736bf1d537751dade4e6d5d1c585aaaa1c74 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 6 Jun 2021 22:35:12 +0100 Subject: Change to unless-stopped restart policy Since we use the same port for redis on all out projects, having this always restart causes conflicts for people starting up docker and wanting to use redis for anyother project. --- docker-compose.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index a18534a5..cef49213 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,17 @@ version: "3.7" + +x-restart-policy: &restart_policy + restart: unless-stopped + services: sir-lancebot: + << : *restart_policy build: context: . dockerfile: Dockerfile container_name: sir-lancebot init: true - restart: always depends_on: - redis @@ -20,6 +24,7 @@ services: - .:/bot redis: + << : *restart_policy image: redis:latest ports: - "127.0.0.1:6379:6379" -- cgit v1.2.3 From 19b647f1f2613fe5f1cce006d9bf2f20ec8765c1 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Mon, 7 Jun 2021 09:19:05 -0500 Subject: Removed import --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 9356a9e7..b482f42e 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -9,7 +9,6 @@ from concurrent.futures import ThreadPoolExecutor from pathlib import Path import discord -from PIL import UnidentifiedImageError from aiohttp import client_exceptions from discord.ext import commands -- cgit v1.2.3 From 9e89102f7061e0645563de7333fd162e431e53ef Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Mon, 7 Jun 2021 20:52:23 +0530 Subject: Use reddit emojis for upvote and answers --- bot/constants.py | 4 ++-- bot/exts/evergreen/stackoverflow.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index aa26be83..1c62e78b 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -222,10 +222,10 @@ class Emojis: status_offline = "<:status_offline:470326266537705472>" - stackoverflow_upvote = environ.get('stack_upvote', "<:stack_upvote:*>") # TODO: Fill in numbers (ids) + # stackoverflow_upvote = environ.get('stack_upvote', "<:stack_upvote:*>") # TODO: Fill in numbers (ids) stackoverflow_tag = environ.get('stack_tag', "<:stack_tag:*> ") stackoverflow_views = environ.get('stack_views', "<:stack_eye:*>") - stackoverflow_ans = environ.get('stack_ans', "<:sta + # stackoverflow_ans = environ.get('stack_ans', "<:sta") # Reddit emojis reddit = "<:reddit:676030265734332427>" diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index e97e0318..45dcb62a 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -64,9 +64,9 @@ class Stackoverflow(commands.Cog): embed.add_field( name=unescape(item['title']), value=( - f"[{Emojis.stackoverflow_upvote} {item['score']} " + f"[{Emojis.reddit_upvote} {item['score']} " f"{Emojis.stackoverflow_views} {item['view_count']} " - f"{Emojis.stackoverflow_ans} {item['answer_count']} " + f"{Emojis.reddit_comments} {item['answer_count']} " f"{Emojis.stackoverflow_tag} {', '.join(item['tags'][:3])}]" f"({item['link']})" ), -- cgit v1.2.3 From cd55b4796296c5675c17e6cc20a1e168380b8b08 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Mon, 7 Jun 2021 20:54:04 +0530 Subject: stop spamming stackoverflow api in case status code is not 200 --- bot/exts/evergreen/stackoverflow.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 45dcb62a..8607cac7 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -34,14 +34,11 @@ class Stackoverflow(commands.Cog): """Sends the top 5 results of a search query from stackoverflow.""" encoded_search_query = quote_plus(search_query) - for _ in range(3): - async with self.bot.http_session.get(BASE_URL.format(query=encoded_search_query)) as response: - if response.status == 200: - data = await response.json() - break - else: - logger.error(f'Status code is not 200, it is {response.status}') - continue + async with self.bot.http_session.get(BASE_URL.format(query=encoded_search_query)) as response: + if response.status == 200: + data = await response.json() + else: + logger.error(f'Status code is not 200, it is {response.status}') if response.status != 200: # If the status is still not 200 after the 3 tries await ctx.send(embed=ERR_EMBED) return -- cgit v1.2.3 From 5296e7a7aec536de86a752a6f424a582d32bade5 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Mon, 7 Jun 2021 20:55:21 +0530 Subject: use normal string instead of raw string --- bot/exts/evergreen/stackoverflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 8607cac7..8dd4a060 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -44,7 +44,7 @@ class Stackoverflow(commands.Cog): return elif not data['items']: no_search_result = Embed( - title=f"No search results found for {search_query!r}", + title=f"No search results found for {search_query}", color=Colours.soft_red ) await ctx.send(embed=no_search_result) -- cgit v1.2.3 From 14f7c354aabc316c25c1b6cedeb7fcbd6c916098 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Mon, 7 Jun 2021 20:56:05 +0530 Subject: Remove user's query from embed title --- bot/exts/evergreen/stackoverflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 8dd4a060..22963ad9 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -52,7 +52,7 @@ class Stackoverflow(commands.Cog): top5 = data["items"][:5] embed = Embed( - title=f"Search results for {search_query!r} - Stackoverflow", + title="Search results - Stackoverflow", url=SEARCH_URL.format(query=encoded_search_query), description=f"Here are the top {len(top5)} results:", color=Colours.orange -- cgit v1.2.3 From 46d55f402a4f66372f12cff2c98f0b834bc83079 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Mon, 7 Jun 2021 20:56:31 +0530 Subject: remove attribution comments --- bot/exts/evergreen/stackoverflow.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 22963ad9..62179150 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -82,6 +82,3 @@ class Stackoverflow(commands.Cog): def setup(bot: bot.Bot) -> None: """Loads Stackoverflow Cog.""" bot.add_cog(Stackoverflow(bot)) - -# Upvote and Comment icon taken from Reddit bot -# Tag icon made by Freepik (https://www.flaticon.com/authors/freepik) from www.flaticon.com, and edited by me -- cgit v1.2.3 From 70be524b713c8e6b2ac945c209ea915241faa83e Mon Sep 17 00:00:00 2001 From: shantanusingh1069 Date: Tue, 8 Jun 2021 13:25:44 +0530 Subject: embed displays draw condition on show command --- bot/exts/evergreen/tic_tac_toe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 48e8e142..37b53304 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -320,7 +320,7 @@ class TicTacToe(Cog): embed = discord.Embed( title=f"Match #{game_id} Game Board", - description=f"{game.winner} :trophy: vs {game.loser}\n\n{game.format_board()}" + description=f"{game.winner} vs {game.loser} (draw)\n\n{game.format_board()}" if game.draw else f"{game.winner} :trophy: vs {game.loser}\n\n{game.format_board()}" ) await ctx.send(embed=embed) -- cgit v1.2.3 From 3e795bb83f1aad33d49b62f9e7edc586424f0e29 Mon Sep 17 00:00:00 2001 From: Shantanu Singh <39329252+CasualCoder99@users.noreply.github.com> Date: Tue, 8 Jun 2021 14:17:34 +0530 Subject: resolved linting error with if-else --- bot/exts/evergreen/tic_tac_toe.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 37b53304..b50e2b28 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -320,7 +320,10 @@ class TicTacToe(Cog): embed = discord.Embed( title=f"Match #{game_id} Game Board", - description=f"{game.winner} vs {game.loser} (draw)\n\n{game.format_board()}" if game.draw else f"{game.winner} :trophy: vs {game.loser}\n\n{game.format_board()}" + if game.draw: + description = f"{game.winner} vs {game.loser} (draw)\n\n{game.format_board()}" + else: + description = f"{game.winner} :trophy: vs {game.loser}\n\n{game.format_board()}" ) await ctx.send(embed=embed) -- cgit v1.2.3 From 37390bde74953384145441bbd0d889d7da710755 Mon Sep 17 00:00:00 2001 From: Shantanu Singh <39329252+CasualCoder99@users.noreply.github.com> Date: Tue, 8 Jun 2021 14:47:33 +0530 Subject: resolved syntax error --- bot/exts/evergreen/tic_tac_toe.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index b50e2b28..dccbad4c 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -318,13 +318,15 @@ class TicTacToe(Cog): return game = self.games[game_id - 1] + if game.draw: + description = f"{game.winner} vs {game.loser} (draw)\n\n{game.format_board()}" + else: + description = f"{game.winner} :trophy: vs {game.loser}\n\n{game.format_board()}" + embed = discord.Embed( title=f"Match #{game_id} Game Board", - if game.draw: - description = f"{game.winner} vs {game.loser} (draw)\n\n{game.format_board()}" - else: - description = f"{game.winner} :trophy: vs {game.loser}\n\n{game.format_board()}" - ) + description=description, +) await ctx.send(embed=embed) -- cgit v1.2.3 From 29cf2b8ff43ddd6a8ba1b28a2b9a3b8d27b097f2 Mon Sep 17 00:00:00 2001 From: Shantanu Singh <39329252+CasualCoder99@users.noreply.github.com> Date: Tue, 8 Jun 2021 14:55:38 +0530 Subject: resolved indentation error --- bot/exts/evergreen/tic_tac_toe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index dccbad4c..8bd1bf53 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -326,7 +326,7 @@ class TicTacToe(Cog): embed = discord.Embed( title=f"Match #{game_id} Game Board", description=description, -) + ) await ctx.send(embed=embed) -- cgit v1.2.3 From bc35c934168510f83618ed0f43fa45db139a8bcb Mon Sep 17 00:00:00 2001 From: Shantanu Singh <39329252+CasualCoder99@users.noreply.github.com> Date: Wed, 9 Jun 2021 12:53:36 +0530 Subject: display player names in drawn match --- bot/exts/evergreen/tic_tac_toe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 8bd1bf53..164e056d 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -319,7 +319,7 @@ class TicTacToe(Cog): game = self.games[game_id - 1] if game.draw: - description = f"{game.winner} vs {game.loser} (draw)\n\n{game.format_board()}" + description = f"{game.players[0]} vs {game.players[1]} (draw)\n\n{game.format_board()}" else: description = f"{game.winner} :trophy: vs {game.loser}\n\n{game.format_board()}" -- cgit v1.2.3 From e0ec9b4811e5dcb60b4815c0376356697b6f73f3 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Wed, 9 Jun 2021 22:15:23 +0530 Subject: Remove unecessary if --- bot/exts/evergreen/stackoverflow.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 62179150..30b0a825 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -39,10 +39,9 @@ class Stackoverflow(commands.Cog): data = await response.json() else: logger.error(f'Status code is not 200, it is {response.status}') - if response.status != 200: # If the status is still not 200 after the 3 tries - await ctx.send(embed=ERR_EMBED) - return - elif not data['items']: + await ctx.send(embed=ERR_EMBED) + return + if not data['items']: no_search_result = Embed( title=f"No search results found for {search_query}", color=Colours.soft_red -- cgit v1.2.3 From 4374931d78dc1e3c8a6a361c6f78da4a9237a20d Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Wed, 9 Jun 2021 22:16:12 +0530 Subject: Remove commented emojis --- bot/constants.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index fa5dc2bc..25545c04 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -225,10 +225,8 @@ class Emojis: status_offline = "<:status_offline:470326266537705472>" - # stackoverflow_upvote = environ.get('stack_upvote', "<:stack_upvote:*>") # TODO: Fill in numbers (ids) stackoverflow_tag = environ.get('stack_tag', "<:stack_tag:*> ") stackoverflow_views = environ.get('stack_views', "<:stack_eye:*>") - # stackoverflow_ans = environ.get('stack_ans', "<:sta") # Reddit emojis reddit = "<:reddit:676030265734332427>" -- cgit v1.2.3 From 4cf74068aeb6b4c198e6bea438c320cf2978e863 Mon Sep 17 00:00:00 2001 From: Vivaan Parashar Date: Wed, 9 Jun 2021 22:16:53 +0530 Subject: Change docstring --- bot/exts/evergreen/stackoverflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 30b0a825..a53cecf7 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -79,5 +79,5 @@ class Stackoverflow(commands.Cog): def setup(bot: bot.Bot) -> None: - """Loads Stackoverflow Cog.""" + """Load the Stackoverflow Cog.""" bot.add_cog(Stackoverflow(bot)) -- cgit v1.2.3 From 32a4b6ac1c52381520de9dbf36be7f2f2be95bec Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 9 Jun 2021 22:24:18 +0100 Subject: Update Github emoji to new icons Github updated their icons, so we should update ours too. Co-authored-by: GDWR --- bot/constants.py | 14 ++++++++------ bot/exts/evergreen/issues.py | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index f7fe216b..798cb950 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -190,12 +190,14 @@ class Emojis: dice_5 = "<:dice_5:755891608091885627>" dice_6 = "<:dice_6:755891607680843838>" - issue = "<:IssueOpen:629695470327037963>" - issue_closed = "<:IssueClosed:629695470570307614>" - pull_request = "<:PROpen:629695470175780875>" - pull_request_closed = "<:PRClosed:629695470519713818>" - pull_request_draft = "<:PRDraft:829755345425399848>" - merge = "<:PRMerged:629695470570176522>" + # These icons are from Github's repo https://github.com/primer/octicons/ + issue_opened = "<:IssueOpen:852294683731165184>" + issue_closed = "<:IssueClosed:852294683723694140>" + issue_draft = "<:IssueDraft:852294683592884284>" # Not currently used by Github, but here for future. + pull_request_open = "<:PROpen:629695470175780875>" + pull_request_closed = "<:PRClosed:852294683194294304>" + pull_request_draft = "<:PRDraft:852294683861712916>" + pull_request_merge = "<:PRMerged:629695470570176522>" number_emojis = { 1: "\u0031\ufe0f\u20e3", diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index b67aa4a6..da10047e 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -135,7 +135,7 @@ class Issues(commands.Cog): # need from the initial API call. if "issues" in json_data["html_url"]: if json_data.get("state") == "open": - emoji = Emojis.issue + emoji = Emojis.issue_opened else: emoji = Emojis.issue_closed @@ -149,10 +149,10 @@ class Issues(commands.Cog): if pull_data["draft"]: emoji = Emojis.pull_request_draft elif pull_data["state"] == "open": - emoji = Emojis.pull_request + emoji = Emojis.pull_request_open # When 'merged_at' is not None, this means that the state of the PR is merged elif pull_data["merged_at"] is not None: - emoji = Emojis.merge + emoji = Emojis.pull_request_merge else: emoji = Emojis.pull_request_closed -- cgit v1.2.3 From 07a8f19be4f1178cca26330d68c778717d267415 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 9 Jun 2021 23:03:55 +0100 Subject: Update tense of icon constants This is so the name of the constants describe the state in the correct tense Co-authored-by: Numerlor --- bot/constants.py | 4 ++-- bot/exts/evergreen/issues.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 798cb950..2c18f6e7 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -191,13 +191,13 @@ class Emojis: dice_6 = "<:dice_6:755891607680843838>" # These icons are from Github's repo https://github.com/primer/octicons/ - issue_opened = "<:IssueOpen:852294683731165184>" + issue_open = "<:IssueOpen:852294683731165184>" issue_closed = "<:IssueClosed:852294683723694140>" issue_draft = "<:IssueDraft:852294683592884284>" # Not currently used by Github, but here for future. pull_request_open = "<:PROpen:629695470175780875>" pull_request_closed = "<:PRClosed:852294683194294304>" pull_request_draft = "<:PRDraft:852294683861712916>" - pull_request_merge = "<:PRMerged:629695470570176522>" + pull_request_merged = "<:PRMerged:629695470570176522>" number_emojis = { 1: "\u0031\ufe0f\u20e3", diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index da10047e..00810de8 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -135,7 +135,7 @@ class Issues(commands.Cog): # need from the initial API call. if "issues" in json_data["html_url"]: if json_data.get("state") == "open": - emoji = Emojis.issue_opened + emoji = Emojis.issue_open else: emoji = Emojis.issue_closed @@ -152,7 +152,7 @@ class Issues(commands.Cog): emoji = Emojis.pull_request_open # When 'merged_at' is not None, this means that the state of the PR is merged elif pull_data["merged_at"] is not None: - emoji = Emojis.pull_request_merge + emoji = Emojis.pull_request_merged else: emoji = Emojis.pull_request_closed -- cgit v1.2.3 From dce6930c8be76c1f236247f41ca55ed07f590147 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 10 Jun 2021 18:25:54 +0100 Subject: Add transparent border to new Github octicons --- bot/constants.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 2c18f6e7..7bd10b5f 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -191,13 +191,13 @@ class Emojis: dice_6 = "<:dice_6:755891607680843838>" # These icons are from Github's repo https://github.com/primer/octicons/ - issue_open = "<:IssueOpen:852294683731165184>" - issue_closed = "<:IssueClosed:852294683723694140>" - issue_draft = "<:IssueDraft:852294683592884284>" # Not currently used by Github, but here for future. - pull_request_open = "<:PROpen:629695470175780875>" - pull_request_closed = "<:PRClosed:852294683194294304>" - pull_request_draft = "<:PRDraft:852294683861712916>" - pull_request_merged = "<:PRMerged:629695470570176522>" + issue_open = "<:IssueOpen:852596024777506817>" + issue_closed = "<:IssueClosed:852596024739758081>" + issue_draft = "<:IssueDraft:852596025147523102>" # Not currently used by Github, but here for future. + pull_request_open = "<:PROpen:852596471505223781>" + pull_request_closed = "<:PRClosed:852596024732286976>" + pull_request_draft = "<:PRDraft:852596025045680218>" + pull_request_merged = "<:PRMerged:852596100301193227>" number_emojis = { 1: "\u0031\ufe0f\u20e3", -- cgit v1.2.3 From 1756871fedbd3053283cb136521c248a5ec9bd73 Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Thu, 24 Jun 2021 17:47:53 +0200 Subject: Fix .scarymovie command(halloween) Modify the incorrect API endpoint and set HTTP parameters accordingly. --- bot/exts/halloween/scarymovie.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/halloween/scarymovie.py b/bot/exts/halloween/scarymovie.py index f4cf41db..8be2cbaa 100644 --- a/bot/exts/halloween/scarymovie.py +++ b/bot/exts/halloween/scarymovie.py @@ -31,13 +31,14 @@ class ScaryMovie(commands.Cog): async def select_movie(self) -> dict: """Selects a random movie and returns a JSON of movie details from TMDb.""" - url = "https://api.themoviedb.org/4/discover/movie" + url = "https://api.themoviedb.org/3/discover/movie" params = { + "api_key": TMDB_API_KEY, "with_genres": "27", - "vote_count.gte": "5" + "vote_count.gte": "5", + "include_adult": "false" } headers = { - "Authorization": "Bearer " + TMDB_TOKEN, "Content-Type": "application/json;charset=utf-8" } -- cgit v1.2.3 From ba70f0d11fd53aafd7c21345bace8bdd7c7d8194 Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Thu, 24 Jun 2021 17:54:57 +0200 Subject: Remove unused variable (TMDB_TOKEN) --- bot/exts/halloween/scarymovie.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/exts/halloween/scarymovie.py b/bot/exts/halloween/scarymovie.py index 8be2cbaa..72f021cb 100644 --- a/bot/exts/halloween/scarymovie.py +++ b/bot/exts/halloween/scarymovie.py @@ -11,7 +11,6 @@ log = logging.getLogger(__name__) TMDB_API_KEY = environ.get("TMDB_API_KEY") -TMDB_TOKEN = environ.get("TMDB_TOKEN") class ScaryMovie(commands.Cog): -- cgit v1.2.3 From 58baaa3e40dafc28064bd234f7b68c064632e8d4 Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Fri, 25 Jun 2021 16:57:31 +0200 Subject: Update scarymovie.py to use the constants module --- bot/exts/halloween/scarymovie.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bot/exts/halloween/scarymovie.py b/bot/exts/halloween/scarymovie.py index 72f021cb..4ba6d57a 100644 --- a/bot/exts/halloween/scarymovie.py +++ b/bot/exts/halloween/scarymovie.py @@ -1,16 +1,15 @@ import logging import random -from os import environ from discord import Embed from discord.ext import commands from bot.bot import Bot - +from bot.constants import Tokens log = logging.getLogger(__name__) -TMDB_API_KEY = environ.get("TMDB_API_KEY") +TMDB_API_KEY = Tokens.tmdb class ScaryMovie(commands.Cog): -- cgit v1.2.3 From c040e726c2fedf7bc78f027f9400bed59ce5081f Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Fri, 25 Jun 2021 17:37:59 +0200 Subject: Update bot/exts/halloween/scarymovie.py Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/halloween/scarymovie.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/exts/halloween/scarymovie.py b/bot/exts/halloween/scarymovie.py index 4ba6d57a..c7526569 100644 --- a/bot/exts/halloween/scarymovie.py +++ b/bot/exts/halloween/scarymovie.py @@ -9,7 +9,6 @@ from bot.constants import Tokens log = logging.getLogger(__name__) -TMDB_API_KEY = Tokens.tmdb class ScaryMovie(commands.Cog): -- cgit v1.2.3 From 89a8d465f7f67dd5e05ad99b99c4e54ab2120c7b Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Fri, 25 Jun 2021 17:38:07 +0200 Subject: Update bot/exts/halloween/scarymovie.py Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/halloween/scarymovie.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/halloween/scarymovie.py b/bot/exts/halloween/scarymovie.py index c7526569..99c4758a 100644 --- a/bot/exts/halloween/scarymovie.py +++ b/bot/exts/halloween/scarymovie.py @@ -30,7 +30,7 @@ class ScaryMovie(commands.Cog): """Selects a random movie and returns a JSON of movie details from TMDb.""" url = "https://api.themoviedb.org/3/discover/movie" params = { - "api_key": TMDB_API_KEY, + "api_key": Tokens.tmbd, "with_genres": "27", "vote_count.gte": "5", "include_adult": "false" -- cgit v1.2.3 From a654a782ff5b45d88a0decf155e7fc27d45011bf Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Fri, 25 Jun 2021 18:25:12 +0200 Subject: Modify scarymovie to use the constants module everywhere in the code --- bot/exts/halloween/scarymovie.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bot/exts/halloween/scarymovie.py b/bot/exts/halloween/scarymovie.py index 99c4758a..33659fd8 100644 --- a/bot/exts/halloween/scarymovie.py +++ b/bot/exts/halloween/scarymovie.py @@ -9,8 +9,6 @@ from bot.constants import Tokens log = logging.getLogger(__name__) - - class ScaryMovie(commands.Cog): """Selects a random scary movie and embeds info into Discord chat.""" @@ -30,7 +28,7 @@ class ScaryMovie(commands.Cog): """Selects a random movie and returns a JSON of movie details from TMDb.""" url = "https://api.themoviedb.org/3/discover/movie" params = { - "api_key": Tokens.tmbd, + "api_key": Tokens.tmdb, "with_genres": "27", "vote_count.gte": "5", "include_adult": "false" @@ -53,7 +51,7 @@ class ScaryMovie(commands.Cog): # Get full details and credits async with self.bot.http_session.get( url=f"https://api.themoviedb.org/3/movie/{selection_id}", - params={"api_key": TMDB_API_KEY, "append_to_response": "credits"} + params={"api_key": Tokens.tmdb, "append_to_response": "credits"} ) as selection: return await selection.json() -- cgit v1.2.3 From ce355662afd55a5adf95335bbb50058b3eeb7891 Mon Sep 17 00:00:00 2001 From: wookie184 Date: Tue, 6 Jul 2021 17:11:41 +0100 Subject: Allow commands in code jam team channels --- bot/constants.py | 1 + bot/utils/checks.py | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/bot/constants.py b/bot/constants.py index 7bd10b5f..3b3d7711 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -127,6 +127,7 @@ class Categories(NamedTuple): media = 799054581991997460 staff = 364918151625965579 +codejam_categories_name = "Code Jam" # Name of the codejam team categories class Client(NamedTuple): name = "Sir Lancebot" diff --git a/bot/utils/checks.py b/bot/utils/checks.py index c06b6870..438ec750 100644 --- a/bot/utils/checks.py +++ b/bot/utils/checks.py @@ -75,6 +75,11 @@ def in_whitelist_check( log.trace(f"{ctx.author} may use the `{ctx.command.name}` command as they are in a whitelisted category.") return True + category = getattr(ctx.channel, "category", None) + if category and category.name == constants.codejam_categories_name: + log.trace(f"{ctx.author} may use the `{ctx.command.name}` command as they are in a codejam team channel.") + return True + # Only check the roles whitelist if we have one and ensure the author's roles attribute returns # an iterable to prevent breakage in DM channels (for if we ever decide to enable commands there). if roles and any(r.id in roles for r in getattr(ctx.author, "roles", ())): -- cgit v1.2.3 From 5ebe5ee24a4a1a0d867700927381a3601057ab8e Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Tue, 6 Jul 2021 16:40:15 -0400 Subject: Add "cs" and "python" categories to the `.quiz` command * add 30 questions each under the categories "cs" and "python" add the two categories into the code and modify the starting phase Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/evergreen/trivia_quiz.py | 29 +-- bot/resources/evergreen/trivia_quiz.json | 318 ++++++++++++++++++++++++++++++- 2 files changed, 326 insertions(+), 21 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index a8d10afd..28924aed 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -17,8 +17,8 @@ from bot.constants import Colours, NEGATIVE_REPLIES, Roles logger = logging.getLogger(__name__) DEFAULT_QUESTION_LIMIT = 6 -STANDARD_VARIATION_TOLERANCE = 83 -DYNAMICALLY_GEN_VARIATION_TOLERANCE = 95 +STANDARD_VARIATION_TOLERANCE = 88 +DYNAMICALLY_GEN_VARIATION_TOLERANCE = 97 WRONG_ANS_RESPONSE = [ "No one answered correctly!", @@ -210,6 +210,8 @@ class TriviaQuiz(commands.Cog): "retro": "Questions related to retro gaming.", "math": "General questions about mathematics ranging from grade 8 to grade 12.", "science": "Put your understanding of science to the test!", + "cs": "A large variety of computer science questions.", + "python": "Trivia on our amazing language, Python!", } @staticmethod @@ -225,10 +227,12 @@ class TriviaQuiz(commands.Cog): Start a quiz! Questions for the quiz can be selected from the following categories: - - general: Test your general knowledge. (default) + - general: Test your general knowledge. - retro: Questions related to retro gaming. - math: General questions about mathematics ranging from grade 8 to grade 12. - science: Put your understanding of science to the test! + - cs: A large variety of computer science questions. + - python: Trivia on our amazing language, Python! (More to come!) """ @@ -290,7 +294,7 @@ class TriviaQuiz(commands.Cog): start_embed = self.make_start_embed(category) await ctx.send(embed=start_embed) # send an embed with the rules - await asyncio.sleep(1) + await asyncio.sleep(5) done_question = [] hint_no = 0 @@ -430,21 +434,18 @@ class TriviaQuiz(commands.Cog): """Generate a starting/introduction embed for the quiz.""" start_embed = discord.Embed( colour=Colours.blue, - title="Quiz game starting!", + title="A quiz game is starting!", description=( - f"This game consists of {self.question_limit + 1} questions.\n" - "**Rules: **No cheating and have fun!\n" + f"This game consists of {self.question_limit + 1} questions.\n\n" + "**Rules: **\n" + "1. Only enclose your answer in backticks when the question tells you to.\n" + "2. If the question specifies an answer format, follow it or else it won't be accepted.\n" + "3. You have 30s per question. Points for each question reduces by 25 after 10s or after a hint.\n" + "4. No cheating and have fun!\n\n" f"**Category**: {category}" ), ) - start_embed.set_footer( - text=( - "Points for each question reduces by 25 after 10s or after a hint. " - "Total time is 30s per question" - ) - ) - return start_embed @staticmethod diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index fee1b6d7..8008838c 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -371,8 +371,8 @@ }, { "id": 216, - "question": "In set builder notation, {p/q | q ≠ 0, p & q ∈ Z} represents what?", - "answer": "Rational Numbers" + "question": "In set builder notation, what does {p/q | q ≠ 0, p & q ∈ Z} represent?", + "answer": "Rationals, Rational Numbers" }, { "id": 217, @@ -401,7 +401,7 @@ }, { "id": 222, - "question": "In probability, the ________ ______ of an experiment or random trial is the set of all possible outcomes of it.", + "question": "In probability, the \\_\\_\\_\\_\\_\\_ \\_\\_\\_\\_\\_ of an experiment or random trial is the set of all possible outcomes of it.", "answer": "sample space" }, { @@ -472,12 +472,12 @@ }, { "id": 305, - "question": "The Heisenberg's Uncertainty Principle states that the position and ________ of a quantum object can't be both exactly measured at the same time.", + "question": "The Heisenberg's Uncertainty Principle states that the position and \\_\\_\\_\\_\\_\\_\\_\\_ of a quantum object can't be both exactly measured at the same time.", "answer": "velocity, momentum" }, { "id": 306, - "question": "A ____________ reaction is the one wherein an atom or a set of atoms is/are replaced by another atom or a set of atoms", + "question": "A \\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_ reaction is the one wherein an atom or a set of atoms is/are replaced by another atom or a set of atoms", "answer": "displacement, exchange" }, { @@ -521,7 +521,7 @@ }, { "id": 314, - "question": "About how many neurons are in the human brain? (A. 1 billion, B. 10 billion, C. 100 billion, D. 300 billion)", + "question": "About how many neurons are in the human brain?\n(A. 1 billion, B. 10 billion, C. 100 billion, D. 300 billion)", "answer": "C, 100 billion, 100 bil" }, { @@ -556,7 +556,7 @@ }, { "id": 321, - "question": "What range of frequency are the average human ears capable of hearing (A. 10Hz-10kHz, B. 20Hz-20kHz, C. 20Hz-2000Hz, D. 10kHz-20kHz)?", + "question": "What range of frequency are the average human ears capable of hearing?\n(A. 10Hz-10kHz, B. 20Hz-20kHz, C. 20Hz-2000Hz, D. 10kHz-20kHz)", "answer": "B, 20Hz-20kHz" }, { @@ -604,5 +604,309 @@ "question": "When DNA is unzipped, two strands are formed. What are they called (separate both answers by the word \"and\")?", "answer": "leading and lagging, leading strand and lagging strand" } + ], + "cs": [ + { + "id": 401, + "question": "What does HTML stand for?", + "answer": "HyperText Markup Language" + }, + { + "id": 402, + "question": "What does ASCII stand for?", + "answer": "American Standard Code for Information Interchange" + }, + { + "id": 403, + "question": "What does SASS stand for?", + "answer": "Syntactically Awesome Stylesheets, Syntactically Awesome Style Sheets" + }, + { + "id": 404, + "question": "In neural networks, \\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_ is an algorithm for supervised learning using gradient descent.", + "answer": "backpropagation" + }, + { + "id": 405, + "question": "What is computing capable of performing exaFLOPS called?", + "answer": "exascale computing, exascale" + }, + { + "id": 406, + "question": "In quantum computing, what is the full name of \"qubit\"?", + "answer": "quantum binary digit" + }, + { + "id": 407, + "question": "Given that January 1, 1970 is the starting epoch of time_t in c time, and that time_t is stored as a signed 32-bit integer, when will unix time roll over (year)?", + "answer": "2038" + }, + { + "id": 408, + "question": "What are the components of digital devices that make up logic gates called?", + "answer": "transistors" + }, + { + "id": 409, + "question": "How many possible public IPv6 addresses are there (answer in 2^n)?", + "answer": "2^128" + }, + { + "id": 410, + "question": "A hypothetical point in time at which technological growth becomes uncontrollable and irreversible, resulting in unforeseeable changes to human civilization is termed as...?", + "answer": "technological singularity, singularity" + }, + { + "id": 411, + "question": "In cryptography, the practice of establishing a shared secret between two parties using public keys and private keys is called...?", + "answer": "key exchange" + }, + { + "id": 412, + "question": "How many bits are in a TCP checksum header?", + "answer": "16, sixteen" + }, + { + "id": 413, + "question": "What is the most popular protocol (as of 2021) that handles communication between email servers?", + "answer": "SMTP, Simple Mail Transfer Protocol" + }, + { + "id": 414, + "question": "Which port does SMTP use to communicate between email servers? (assuming its plaintext)", + "answer": "25" + }, + { + "id": 415, + "question": "Which DNS record contains mail servers of a given domain?", + "answer": "MX, mail exchange" + }, + { + "id": 416, + "question": "Which newline sequence does HTTP use?", + "answer": "carriage return line feed, CRLF, \\r\\n" + }, + { + "id": 417, + "question": "What does one call the optimization technique used in CPU design that attempts to guess the outcome of a conditional operation and prepare for the most likely result?", + "answer": "branch prediction" + }, + { + "id": 418, + "question": "Name a universal logic gate.", + "answer": "NAND, NOR" + }, + { + "id": 419, + "question": "What is the mathematical formalism which functional programming was built on?", + "answer": "lambda calculus" + }, + { + "id": 420, + "question": "Why is a DDoS attack different from a DoS attack?\n(A. because the victim's server was indefinitely disrupted from the amount of traffic, B. because it also attacks the victim's confidentiality, C. because the attack had political purposes behind it, D. because the traffic flooding the victim originated from many different sources)", + "answer": "D" + }, + { + "id": 421, + "question": "What is a HTTP/1.1 feature that was superseded by HTTP/2 multiplexing and is unsupported in most browsers nowadays?", + "answer": "pipelining" + }, + { + "id": 422, + "question": "Which of these languages is the oldest?\n(Tcl, Smalltalk 80, Haskell, Standard ML, Java)", + "answer": "Smalltalk 80" + }, + { + "id": 423, + "question": "What is the name for unicode codepoints that do not fit into 16 bits?", + "answer": "surrogates" + }, + { + "id": 424, + "question": "Under what locale does making a string lowercase behave differently?", + "answer": "Turkish" + }, + { + "id": 425, + "question": "What does the \"a\" represent in a HSLA color value?", + "answer": "transparency, translucency, alpha value, alpha channel, alpha" + }, + { + "id": 426, + "question": "What is the section of a GIF that is limited to 256 colors called?", + "answer": "image block" + }, + { + "id": 427, + "question": "What is an interpreter capable of interpreting itself called?", + "answer": "metainterpreter" + }, + { + "id": 428, + "question": "Due to what data storage medium did old programming languages, such as cobol, ignore all characters past the 72nd column?", + "answer": "punch cards" + }, + { + "id": 429, + "question": "Which of these sorting algorithms is not stable?\n(Counting sort, quick sort, insertion sort, tim sort, bubble sort)", + "answer": "quick, quick sort" + }, + { + "id": 430, + "question": "Which of these languages is the youngest?\n(Lisp, Python, Java, Haskell, Prolog, Ruby, Perl)", + "answer": "Java" + } + ], + "python": [ + { + "id": 501, + "question": "Is everything an instance of the `object` class (y/n)?", + "answer": "y, yes" + }, + { + "id": 502, + "question": "Name the only non-dunder method of the builtin slice object.", + "answer": "indices" + }, + { + "id": 503, + "question": "What exception, other than `StopIteration`, can you raise from a `__getitem__` dunder to indicate to an iterator that it should stop?", + "answer": "IndexError" + }, + { + "id": 504, + "question": "What type does the `&` operator return when given 2 `dict_keys` objects?", + "answer": "set" + }, + { + "id": 505, + "question": "Can you pickle a running `list_iterator` (y/n)?", + "answer": "y, yes" + }, + { + "id": 506, + "question": "What attribute of a closure contains the value closed over?", + "answer": "cell_contents" + }, + { + "id": 507, + "question": "What name does a lambda function have?", + "answer": "" + }, + { + "id": 508, + "question": "Which file contains all special site builtins, such as help or credits?", + "answer": "_sitebuiltins" + }, + { + "id": 509, + "question": "Which module when imported opens up a web browser tab that points to the classic 353 XKCD comic mentioning Python?", + "answer": "antigravity" + }, + { + "id": 510, + "question": "Which attribute is the documentation string of a function/method/class stored in (answer should be enclosed in backticks!)?", + "answer": "`__doc__`" + }, + { + "id": 511, + "question": "What is the official name of this operator `:=`, introduced in 3.8?", + "answer": "assignment-expression operator" + }, + { + "id": 512, + "question": "When was Python first released?", + "answer": "1991" + }, + { + "id": 513, + "question": "Where does the name Python come from?", + "answer": "Monty Python, Monty Python's Flying Circus" + }, + { + "id": 514, + "question": "How is infinity represented in Python?", + "answer": "float(\"infinity\"), float('infinity'), float(\"inf\"), float('inf')" + }, + { + "id": 515, + "question": "Which of these characters is valid python outside of string literals in some context?\n(`@`, `$`, `?`)", + "answer": "@" + }, + { + "id": 516, + "question": "Which standard library module is designed for making simple parsers for languages like shell, as well as safe quoting of strings for use in a shell?", + "answer": "shlex" + }, + { + "id": 517, + "question": "Which one of these protocols/abstract base classes does the builtin `range` object NOT implement?\n(`Sequence`, `Iterable`, `Generator`)", + "answer": "Generator" + }, + { + "id": 518, + "question": "What decorator is used to allow a protocol to be checked at runtime?", + "answer": "runtime_checkable, typing.runtime_checkable" + }, + { + "id": 519, + "question": "Does `numbers.Rational` include the builtin object float (y/n)", + "answer": "n, no" + }, + { + "id": 520, + "question": "What is a package that doesn't have a `__init__` file called?", + "answer":"namespace package" + }, + { + "id": 521, + "question": "What file extension is used by the site module to determine what to do at every start?", + "answer": ".pth" + }, + { + "id": 522, + "question": "What is the garbage collection strategy used by cpython to collect everything but reference cycles?", + "answer": "reference counting, refcounting" + }, + { + "id": 523, + "question": "What dunder method is used by the tuple constructor to optimize converting an iterator to a tuple (answer should be enclosed in backticks!)?", + "answer": "`__length_hint__`" + }, + { + "id": 524, + "question": "Which protocol is used to pass self to methods when accessed on classes?", + "answer": "Descriptor" + }, + { + "id": 525, + "question": "Which year was Python 3 released?", + "answer": "2008" + }, + { + "id": 526, + "question": "Which of these is not a generator method?\n(`next`, `send`, `throw`, `close`)", + "answer": "next" + }, + { + "id": 527, + "question": "Is the `__aiter__` method async (y/n)?", + "answer": "n, no" + }, + { + "id": 528, + "question": "How does one call a class who defines the behavior of their instance classes?", + "answer": "a metaclass, metaclass" + }, + { + "id": 529, + "question": "Which of these is a subclass of `Exception`?\n(`NotImplemented`, `asyncio.CancelledError`, `StopIteration`)", + "answer": "StopIteration" + }, + { + "id": 530, + "question": "What type is the attribute of a frame object that contains the current local variables?", + "answer": "dict" + } ] } -- cgit v1.2.3 From 198d22cba0a641f96bd8d1e95a28205cf7df40de Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 6 Jul 2021 21:51:24 +0100 Subject: Remove hacktoberfest channel overrides The hacktoberfest channel has now been removed, so these overrides can also be removed. The commands may still be useful in future, and can be used in lancebot's playground. --- bot/constants.py | 1 - bot/exts/halloween/hacktoberstats.py | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 3b3d7711..ff901c8e 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -113,7 +113,6 @@ class Channels(NamedTuple): off_topic_1 = 463035241142026251 off_topic_2 = 463035268514185226 community_bot_commands = int(environ.get("CHANNEL_COMMUNITY_BOT_COMMANDS", 607247579608121354)) - hacktoberfest_2020 = 760857070781071431 voice_chat_0 = 412357430186344448 voice_chat_1 = 799647045886541885 staff_voice = 541638762007101470 diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index b74e680b..50d3aaf6 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -10,15 +10,14 @@ from async_rediscache import RedisCache from discord.ext import commands from bot.bot import Bot -from bot.constants import Channels, Colours, Month, NEGATIVE_REPLIES, Tokens, WHITELISTED_CHANNELS -from bot.utils.decorators import in_month, whitelist_override +from bot.constants import Colours, Month, NEGATIVE_REPLIES, Tokens +from bot.utils.decorators import in_month log = logging.getLogger(__name__) CURRENT_YEAR = datetime.now().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 -HACKTOBER_WHITELIST = WHITELISTED_CHANNELS + (Channels.hacktoberfest_2020,) REQUEST_HEADERS = {"User-Agent": "Python Discord Hacktoberbot"} # using repo topics API during preview period requires an accept header @@ -44,7 +43,6 @@ class HacktoberStats(commands.Cog): @in_month(Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER) @commands.group(name="hacktoberstats", aliases=("hackstats",), invoke_without_command=True) - @whitelist_override(channels=HACKTOBER_WHITELIST) async def hacktoberstats_group(self, ctx: commands.Context, github_username: str = None) -> None: """ Display an embed for a user's Hacktoberfest contributions. @@ -72,7 +70,6 @@ class HacktoberStats(commands.Cog): @in_month(Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER) @hacktoberstats_group.command(name="link") - @whitelist_override(channels=HACKTOBER_WHITELIST) async def link_user(self, ctx: commands.Context, github_username: str = None) -> None: """ Link the invoking user's Github github_username to their Discord ID. @@ -96,7 +93,6 @@ class HacktoberStats(commands.Cog): @in_month(Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER) @hacktoberstats_group.command(name="unlink") - @whitelist_override(channels=HACKTOBER_WHITELIST) async def unlink_user(self, ctx: commands.Context) -> None: """Remove the invoking user's account link from the log.""" author_id, author_mention = self._author_mention_from_context(ctx) -- cgit v1.2.3 From c98861589cf6b6a65d8cdfac2ecc9c8dbc11bce6 Mon Sep 17 00:00:00 2001 From: Cam Date: Wed, 7 Jul 2021 15:52:08 -0400 Subject: boilerplate --- bot/exts/evergreen/duck_game.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 bot/exts/evergreen/duck_game.py diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py new file mode 100644 index 00000000..561d8a10 --- /dev/null +++ b/bot/exts/evergreen/duck_game.py @@ -0,0 +1,16 @@ + +from discord.ext import commands + +from bot.bot import Bot + + +class DuckGamesDirector(commands.Cog): + """A cog for running Duck Duck Duck Goose games.""" + + def __init__(self, bot: Bot) -> None: + self.bot = bot + + +def setup(bot: Bot) -> None: + """Load the DuckGamesDirector cog.""" + bot.add_cog(DuckGamesDirector(bot)) -- cgit v1.2.3 From 3194691b3fae8416ee5681d6d4fbcee38cac243d Mon Sep 17 00:00:00 2001 From: Cam Date: Wed, 7 Jul 2021 20:49:35 -0400 Subject: Define a game class to generate a board and find solutions --- bot/exts/evergreen/duck_game.py | 64 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 561d8a10..b455cc34 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -1,8 +1,72 @@ +import random +from itertools import product from discord.ext import commands from bot.bot import Bot +DECK = list(product(*[(0, 1, 2)]*4)) + + +class DuckGame: + """A class for a single game.""" + + def __init__(self, + rows: int = 4, + columns: int = 3, + minimum_solutions: int = 1, + ) -> None: + """ + Take samples from the deck to generate a board. + + Args: + rows (int, optional): Rows in the game board. Defaults to 4. + columns (int, optional): Columns in the game board. Defaults to 3. + minimum_solutions (int, optional): Minimum acceptable number of solutions in the board. Defaults to 1. + """ + self._solutions = None + size = rows * columns + self.board = random.sample(DECK, size) + while len(self.solutions) < minimum_solutions: + self.board = random.sample(DECK, size) + + @property + def board(self) -> list[tuple[int]]: + """Accesses board property.""" + return self._board + + @board.setter + def board(self, val: list[tuple[int]]) -> None: + """Erases calculated solutions if the board changes.""" + self._solution = None + self._board = val + + @property + def solutions(self) -> None: + """Calculate valid solutions and cache to avoid redoing work.""" + if self._solutions is None: + self._solutions = set() + for idx_a, card_a in enumerate(self.board): + for idx_b, card_b in enumerate(self.board[idx_a+1:], start=idx_a+1): + """ + Two points determine a line, and there are exactly 3 points per line in {0,1,2}^4. + The completion of a line will only be a duplicate point if the other two points are the same, + 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) + ) + try: + idx_c = self.board.index(completion) + except ValueError: + continue + + # Indices within the solution are sorted to detect duplicate solutions modulo order. + solution = tuple(sorted((idx_a, idx_b, idx_c))) + self._solutions.add(solution) + + return self._solutions + class DuckGamesDirector(commands.Cog): """A cog for running Duck Duck Duck Goose games.""" -- cgit v1.2.3 From 33aa3303c45b16bc7307d4acf7f77ba933d77036 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 01:49:06 -0400 Subject: Add command to start game --- bot/exts/evergreen/duck_game.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index b455cc34..e3e5db67 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -1,12 +1,16 @@ +import asyncio import random from itertools import product +import discord from discord.ext import commands from bot.bot import Bot DECK = list(product(*[(0, 1, 2)]*4)) +GAME_DURATION = 180 + class DuckGame: """A class for a single game.""" @@ -73,6 +77,40 @@ class DuckGamesDirector(commands.Cog): def __init__(self, bot: Bot) -> None: self.bot = bot + self.current_games = {} + + @commands.command(name='duckduckduckgoose', aliases=['dddg', 'duckgoose']) + @commands.cooldown(rate=1, per=2, type=commands.BucketType.channel) + async def start_game(self, ctx: commands.Context) -> None: + """ + Start a game. + + The bot will post an embed with the board and will listen to the following comments for valid solutions. + Claimed answers and the final scores will be added to this embed. + """ + # One game at a time per channel + if ctx.channel.id in self.current_games: + return + + game = DuckGame() + self.current_games[ctx.channel.id] = game + + game.embed_msg = await self.send_board_embed(ctx, game) + await asyncio.sleep(GAME_DURATION) + + """Checking for the channel ID in the currently running games is not sufficient. + The game could have been ended by a player, and a new game already started in the same channel. + """ + if game.running: + try: + del self.current_games[ctx.channel.id] + await self.end_game(game, end_message="Time's up!") + except KeyError: + pass + + async def send_board_embed(self, ctx: commands.Context, game: DuckGame) -> discord.Message: + """Create and send the initial game embed. This will be edited as the game goes on.""" + pass def setup(bot: Bot) -> None: -- cgit v1.2.3 From 27ebd35e1b8cf74e239608164b63999cf67ce682 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 01:54:36 -0400 Subject: Add initial embed and image manipulation functions - Add the all_cards.png file Fix my mistakes from last commit - add missing attributes to DuckGame - call divmod correctly - call assemble_board_image correctly - assemble_board_image now returns the image - I'm sorry it was late at night --- bot/exts/evergreen/duck_game.py | 59 ++++++++++++++++++++++++++++++++-- bot/resources/evergreen/all_cards.png | Bin 0 -> 155466 bytes 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 bot/resources/evergreen/all_cards.png diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index e3e5db67..96241ea1 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -1,16 +1,54 @@ import asyncio import random +from collections import defaultdict +from io import BytesIO from itertools import product +from pathlib import Path import discord +from PIL import Image, ImageDraw from discord.ext import commands from bot.bot import Bot +from bot.constants import Colours DECK = list(product(*[(0, 1, 2)]*4)) GAME_DURATION = 180 +p = Path("bot", "resources", "evergreen", "all_cards.png") +ALL_CARDS = Image.open(p) +CARD_WIDTH = 155 +CARD_HEIGHT = 97 + + +def assemble_board_image(board: list[tuple[int]], rows: int, columns: int) -> Image: + """Cut and paste images representing the given cards into an image representing the board.""" + new_im = Image.new("RGBA", (CARD_WIDTH*columns, CARD_HEIGHT*rows)) + draw = ImageDraw.Draw(new_im) + for idx, card in enumerate(board): + card_image = get_card_image(card) + row, col = divmod(idx, columns) + top, left = row * CARD_HEIGHT, col * CARD_WIDTH + new_im.paste(card_image, (left, top)) + draw.text((left+7, top+4), str(idx)) # magic numbers are buffers for the card labels + return new_im + + +def get_card_image(card: tuple[int]) -> Image: + """Slice the image containing all the cards to get just this card.""" + row, col = divmod(as_trinary(card), 9) # all_cards.png should have 9x9 cards + x1 = col * CARD_WIDTH + x2 = x1 + CARD_WIDTH + y1 = row * CARD_HEIGHT + y2 = y1 + CARD_HEIGHT + return ALL_CARDS.crop((x1, y1, x2, y2)) + + +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) + class DuckGame: """A class for a single game.""" @@ -28,8 +66,13 @@ class DuckGame: columns (int, optional): Columns in the game board. Defaults to 3. minimum_solutions (int, optional): Minimum acceptable number of solutions in the board. Defaults to 1. """ - self._solutions = None + self.rows = rows + self.columns = columns size = rows * columns + + self._solutions = None + self.scores = defaultdict(int) + self.board = random.sample(DECK, size) while len(self.solutions) < minimum_solutions: self.board = random.sample(DECK, size) @@ -93,6 +136,7 @@ class DuckGamesDirector(commands.Cog): return game = DuckGame() + game.running = True self.current_games[ctx.channel.id] = game game.embed_msg = await self.send_board_embed(ctx, game) @@ -110,7 +154,18 @@ class DuckGamesDirector(commands.Cog): async def send_board_embed(self, ctx: commands.Context, game: DuckGame) -> discord.Message: """Create and send the initial game embed. This will be edited as the game goes on.""" - pass + image = assemble_board_image(game.board, game.rows, game.columns) + with BytesIO() as image_stream: + image.save(image_stream, format="png") + image_stream.seek(0) + file = discord.File(fp=image_stream, filename="board.png") + embed = discord.Embed( + title="Duck Duck Duck Goose!", + color=Colours.bright_green, + footer="" + ) + embed.set_image(url="attachment://board.png") + return await ctx.send(embed=embed, file=file) def setup(bot: Bot) -> None: diff --git a/bot/resources/evergreen/all_cards.png b/bot/resources/evergreen/all_cards.png new file mode 100644 index 00000000..10ed2eb8 Binary files /dev/null and b/bot/resources/evergreen/all_cards.png differ -- cgit v1.2.3 From 4afbaf1106ecdb7fe9f6ff7c883b2fee180e9ff6 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 13:51:14 -0400 Subject: Add listener to process answers --- bot/exts/evergreen/duck_game.py | 66 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 96241ea1..a37b7277 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -1,5 +1,6 @@ import asyncio import random +import re from collections import defaultdict from io import BytesIO from itertools import product @@ -10,17 +11,25 @@ from PIL import Image, ImageDraw from discord.ext import commands from bot.bot import Bot -from bot.constants import Colours +from bot.constants import Colours, Emojis DECK = list(product(*[(0, 1, 2)]*4)) GAME_DURATION = 180 +CORRECT_SOLN = 1 +INCORRECT_SOLN = -1 +CORRECT_GOOSE = 2 +INCORRECT_GOOSE = -1 p = Path("bot", "resources", "evergreen", "all_cards.png") ALL_CARDS = Image.open(p) CARD_WIDTH = 155 CARD_HEIGHT = 97 +EMOJI_WRONG = Emojis.x + +ANSWER_REGEX = re.compile(r'^\D*(\d+)\D+(\d+)\D+(\d+)\D*$') + def assemble_board_image(board: list[tuple[int]], rows: int, columns: int) -> Image: """Cut and paste images representing the given cards into an image representing the board.""" @@ -71,6 +80,7 @@ class DuckGame: size = rows * columns self._solutions = None + self.claimed_answers = {} self.scores = defaultdict(int) self.board = random.sample(DECK, size) @@ -152,6 +162,52 @@ class DuckGamesDirector(commands.Cog): except KeyError: pass + @commands.Cog.listener() + async def on_message(self, msg: discord.Message) -> None: + """Listen for messages and process them as answers if appropriate.""" + if msg.author.bot: + return + + channel = msg.channel + if channel.id not in self.current_games: + return + + game = self.current_games[channel.id] + 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: + del self.current_games[channel.id] + game.scores[msg.author] += CORRECT_GOOSE + await self.end_game(game, end_message=f"{msg.author.display_name} GOOSED!") + except KeyError: + pass + else: + await msg.add_reaction(EMOJI_WRONG) + game.scores[msg.author] += INCORRECT_GOOSE + return + + # Valid answers contain 3 numbers. + if not (match := re.match(ANSWER_REGEX, msg.content)): + return + answer = tuple(sorted(int(m) for m in match.groups())) + + # Be forgiving for answers that use indices not on the board. + if not all(0 <= n < len(game.board) for n in answer): + return + + # Also be forgiving for answers that have already been claimed (and avoid penalizing for racing conditions). + if answer in game.claimed_answers: + return + + if answer in game.solutions: + game.claimed_answers[answer] = msg.author + game.scores[msg.author] += CORRECT_SOLN + await self.display_claimed_answer(game, msg.author, answer) + else: + await msg.add_reaction(EMOJI_WRONG) + game.scores[msg.author] += INCORRECT_SOLN + async def send_board_embed(self, ctx: commands.Context, game: DuckGame) -> discord.Message: """Create and send the initial game embed. This will be edited as the game goes on.""" image = assemble_board_image(game.board, game.rows, game.columns) @@ -167,6 +223,14 @@ class DuckGamesDirector(commands.Cog): embed.set_image(url="attachment://board.png") return await ctx.send(embed=embed, file=file) + async def display_claimed_answer(self, game: DuckGame, author: discord.Member, answer: tuple[int]) -> None: + """Add a claimed answer to the game embed.""" + pass + + async def end_game(self, game: DuckGame, end_message: str) -> None: + """Edit the game embed to reflect the end of the game and mark the game as not running.""" + pass + def setup(bot: Bot) -> None: """Load the DuckGamesDirector cog.""" -- cgit v1.2.3 From cd55eb39a44f767670dd92159e3c3ae163ac60cd Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 14:39:49 -0400 Subject: Add functionality to display correct answers and their author --- bot/exts/evergreen/duck_game.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index a37b7277..004ed01b 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -5,6 +5,7 @@ from collections import defaultdict from io import BytesIO from itertools import product from pathlib import Path +from urllib.parse import urlparse import discord from PIL import Image, ImageDraw @@ -59,6 +60,13 @@ def as_trinary(card: tuple[int]) -> int: return int(''.join(str(x) for x in card), base=3) +async def edit_embed_with_image(msg: discord.Message, embed: discord.Embed) -> discord.Message: + """Edit an embed without the attached image going wonky.""" + attach_name = urlparse(embed.image.url).path.split("/")[-1] + embed.set_image(url=f"attachment://{attach_name}") + return await msg.edit(embed=embed) + + class DuckGame: """A class for a single game.""" @@ -82,6 +90,7 @@ class DuckGame: self._solutions = None self.claimed_answers = {} self.scores = defaultdict(int) + self.editing_embed = asyncio.Lock() self.board = random.sample(DECK, size) while len(self.solutions) < minimum_solutions: @@ -225,7 +234,13 @@ class DuckGamesDirector(commands.Cog): async def display_claimed_answer(self, game: DuckGame, author: discord.Member, answer: tuple[int]) -> None: """Add a claimed answer to the game embed.""" - pass + async with game.editing_embed: + game_embed, = game.embed_msg.embeds + old_footer = game_embed.footer.text + if old_footer == discord.Embed.Empty: + old_footer = "" + game_embed.set_footer(text=f"{old_footer}\n{str(answer):12s} - {author.display_name}") + await edit_embed_with_image(game.embed_msg, game_embed) async def end_game(self, game: DuckGame, end_message: str) -> None: """Edit the game embed to reflect the end of the game and mark the game as not running.""" -- cgit v1.2.3 From 2ec637b38e6594d762e647474c0e9cc89f32c1b1 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 14:42:50 -0400 Subject: Fix card labels not appearing --- bot/exts/evergreen/duck_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 004ed01b..1f41e500 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -41,7 +41,7 @@ def assemble_board_image(board: list[tuple[int]], rows: int, columns: int) -> Im row, col = divmod(idx, columns) top, left = row * CARD_HEIGHT, col * CARD_WIDTH new_im.paste(card_image, (left, top)) - draw.text((left+7, top+4), str(idx)) # magic numbers are buffers for the card labels + draw.text((left+7, top+4), str(idx), (0, 0, 0)) # magic numbers are buffers for the card labels return new_im -- cgit v1.2.3 From c87cf2c2189609a314bd38de9c9a153c880fa99f Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 16:03:20 -0400 Subject: Add function to end the game --- bot/exts/evergreen/duck_game.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 1f41e500..55a57bb6 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -244,7 +244,33 @@ class DuckGamesDirector(commands.Cog): async def end_game(self, game: DuckGame, end_message: str) -> None: """Edit the game embed to reflect the end of the game and mark the game as not running.""" - pass + game.running = False + + scores = sorted( + game.scores.items(), + key=lambda item: item[1] + ) + scoreboard = "\n".join(f"{member.display_name}: {score}" for member, score in scores) + + missed = [ans for ans in game.solutions if ans not in game.claimed_answers] + if missed: + missed_text = "Flights everyone missed:\n" + "\n".join(f"{ans}" for ans in missed) + else: + missed_text = "All the flights were found!" + + game_embed, = game.embed_msg.embeds + old_footer = game_embed.footer.text + if old_footer == discord.Embed.Empty: + old_footer = "" + + embed_as_dict = game_embed.to_dict() # Cannot set embed color after initialization + embed_as_dict["color"] = Colours.soft_red + game_embed = discord.Embed.from_dict(embed_as_dict) + + game_embed.set_footer( + text=f"{old_footer.rstrip()}\n\n{end_message} Here are the scores:\n{scoreboard}\n\n{missed_text}" + ) + await edit_embed_with_image(game.embed_msg, game_embed) def setup(bot: Bot) -> None: -- cgit v1.2.3 From 0b659a6e398c690b8f6f97e62751c921efacb5d9 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 16:56:15 -0400 Subject: Define minimum solutions distribution --- bot/exts/evergreen/duck_game.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 55a57bb6..62c8075c 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -22,6 +22,12 @@ INCORRECT_SOLN = -1 CORRECT_GOOSE = 2 INCORRECT_GOOSE = -1 +""" Distribution of minimum acceptable solutions at board generation. + This is for gameplay reasons, to shift the number of solutions per board up, + while still making the end of the game unpredictable. +""" +SOLN_DISTR = 0, 0.05, 0.1, 0.1, 0.15, 0.2, 0.2, 0.15, .05 + p = Path("bot", "resources", "evergreen", "all_cards.png") ALL_CARDS = Image.open(p) CARD_WIDTH = 155 @@ -104,7 +110,7 @@ class DuckGame: @board.setter def board(self, val: list[tuple[int]]) -> None: """Erases calculated solutions if the board changes.""" - self._solution = None + self._solutions = None self._board = val @property @@ -154,7 +160,8 @@ class DuckGamesDirector(commands.Cog): if ctx.channel.id in self.current_games: return - game = DuckGame() + minimum_solutions, = random.choices(range(len(SOLN_DISTR)), weights=SOLN_DISTR) + game = DuckGame(minimum_solutions=minimum_solutions) game.running = True self.current_games[ctx.channel.id] = game -- cgit v1.2.3 From 1094ea17548b9ae9877e9100442c3883b50bedd5 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 17:48:30 -0400 Subject: Beautify multiline tuple comprehension --- bot/exts/evergreen/duck_game.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 62c8075c..ef13dc76 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -125,9 +125,10 @@ class DuckGame: The completion of a line will only be a duplicate point if the other two points are the same, 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) - ) + 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) + ) try: idx_c = self.board.index(completion) except ValueError: -- cgit v1.2.3 From 2ee7962a75e75503926d9f1f8ae7ace29de1c12b Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 17:49:05 -0400 Subject: Marginally improve documentation --- bot/exts/evergreen/duck_game.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index ef13dc76..99afc648 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -14,6 +14,7 @@ from discord.ext import commands from bot.bot import Bot from bot.constants import Colours, Emojis + DECK = list(product(*[(0, 1, 2)]*4)) GAME_DURATION = 180 @@ -25,6 +26,7 @@ INCORRECT_GOOSE = -1 """ Distribution of minimum acceptable solutions at board generation. This is for gameplay reasons, to shift the number of solutions per board up, while still making the end of the game unpredictable. + Note: this is *not* the same as the distribution of number of solutions. """ SOLN_DISTR = 0, 0.05, 0.1, 0.1, 0.15, 0.2, 0.2, 0.15, .05 @@ -151,12 +153,7 @@ class DuckGamesDirector(commands.Cog): @commands.command(name='duckduckduckgoose', aliases=['dddg', 'duckgoose']) @commands.cooldown(rate=1, per=2, type=commands.BucketType.channel) async def start_game(self, ctx: commands.Context) -> None: - """ - Start a game. - - The bot will post an embed with the board and will listen to the following comments for valid solutions. - Claimed answers and the final scores will be added to this embed. - """ + """Generate a board, send the game embed, and end the game after a time limit.""" # One game at a time per channel if ctx.channel.id in self.current_games: return -- cgit v1.2.3 From fa9bc560739fb180d6f207701256a366302d11c0 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 18:06:58 -0400 Subject: Refactor edit_embed_with_image into static method --- bot/exts/evergreen/duck_game.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 99afc648..c7110ba8 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -68,13 +68,6 @@ def as_trinary(card: tuple[int]) -> int: return int(''.join(str(x) for x in card), base=3) -async def edit_embed_with_image(msg: discord.Message, embed: discord.Embed) -> discord.Message: - """Edit an embed without the attached image going wonky.""" - attach_name = urlparse(embed.image.url).path.split("/")[-1] - embed.set_image(url=f"attachment://{attach_name}") - return await msg.edit(embed=embed) - - class DuckGame: """A class for a single game.""" @@ -245,7 +238,7 @@ class DuckGamesDirector(commands.Cog): if old_footer == discord.Embed.Empty: old_footer = "" game_embed.set_footer(text=f"{old_footer}\n{str(answer):12s} - {author.display_name}") - await edit_embed_with_image(game.embed_msg, game_embed) + await self.edit_embed_with_image(game.embed_msg, game_embed) async def end_game(self, game: DuckGame, end_message: str) -> None: """Edit the game embed to reflect the end of the game and mark the game as not running.""" @@ -275,7 +268,14 @@ class DuckGamesDirector(commands.Cog): game_embed.set_footer( text=f"{old_footer.rstrip()}\n\n{end_message} Here are the scores:\n{scoreboard}\n\n{missed_text}" ) - await edit_embed_with_image(game.embed_msg, game_embed) + await self.edit_embed_with_image(game.embed_msg, game_embed) + + @staticmethod + async def edit_embed_with_image(msg: discord.Message, embed: discord.Embed) -> discord.Message: + """Edit an embed without the attached image going wonky.""" + attach_name = urlparse(embed.image.url).path.split("/")[-1] + embed.set_image(url=f"attachment://{attach_name}") + return await msg.edit(embed=embed) def setup(bot: Bot) -> None: -- cgit v1.2.3 From 8b1244e77aa0084a095cc42b179519e2504239f1 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 19:00:49 -0400 Subject: Add help command --- bot/exts/evergreen/duck_game.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index c7110ba8..160e3cc4 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -143,7 +143,7 @@ class DuckGamesDirector(commands.Cog): self.bot = bot self.current_games = {} - @commands.command(name='duckduckduckgoose', aliases=['dddg', 'duckgoose']) + @commands.group(name='duckduckduckgoose', aliases=['dddg', 'duckgoose'], invoke_without_command=True) @commands.cooldown(rate=1, per=2, type=commands.BucketType.channel) async def start_game(self, ctx: commands.Context) -> None: """Generate a board, send the game embed, and end the game after a time limit.""" @@ -270,6 +270,28 @@ class DuckGamesDirector(commands.Cog): ) await self.edit_embed_with_image(game.embed_msg, game_embed) + @start_game.command(name="help") + async def show_rules(self, ctx: commands.Context) -> None: + """Explain the rules of the game.""" + await self.send_help_embed(ctx) + + @staticmethod + async def send_help_embed(ctx: commands.Context) -> discord.Message: + """Send rules embed.""" + embed = discord.Embed( + title="Compete against other players to find valid flights!", + color=discord.Color.dark_purple(), + ) + embed.description = ( + "**Each card has 4 features**\nColor, Number, Hat, and Accessory\n" + "\n**A valid flight**\n3 cards where each feature is either all the same or all different\n" + '\n**Call "GOOSE"**\nif you think there are no more flights\n' + "\n**1** for each valid flight\n" + '**2** for a correct "GOOSE" call\n' + "**-1** for any wrong answer" + ) + await ctx.send(embed=embed) + @staticmethod async def edit_embed_with_image(msg: discord.Message, embed: discord.Embed) -> discord.Message: """Edit an embed without the attached image going wonky.""" -- cgit v1.2.3 From f07854c0429abf4f774b80191d4092f755d73813 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 20:31:10 -0400 Subject: Change the "incorrect" emoji --- bot/exts/evergreen/duck_game.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 160e3cc4..5c83efa7 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -12,7 +12,7 @@ from PIL import Image, ImageDraw from discord.ext import commands from bot.bot import Bot -from bot.constants import Colours, Emojis +from bot.constants import Colours DECK = list(product(*[(0, 1, 2)]*4)) @@ -35,7 +35,7 @@ ALL_CARDS = Image.open(p) CARD_WIDTH = 155 CARD_HEIGHT = 97 -EMOJI_WRONG = Emojis.x +EMOJI_WRONG = "\u274C" ANSWER_REGEX = re.compile(r'^\D*(\d+)\D+(\d+)\D+(\d+)\D*$') -- cgit v1.2.3 From b5c6bd4831c9bd7dd6156bd8b3242580965b5c90 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 20:31:47 -0400 Subject: Change the "game over" embed color --- bot/exts/evergreen/duck_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 5c83efa7..0c75c5c3 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -262,7 +262,7 @@ class DuckGamesDirector(commands.Cog): old_footer = "" embed_as_dict = game_embed.to_dict() # Cannot set embed color after initialization - embed_as_dict["color"] = Colours.soft_red + embed_as_dict["color"] = discord.Color.red().value game_embed = discord.Embed.from_dict(embed_as_dict) game_embed.set_footer( -- cgit v1.2.3 From ad8ab58d121b3b11c718cbb6cc41dcf6d1491227 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 20:32:45 -0400 Subject: Add stop command for mods --- bot/exts/evergreen/duck_game.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 0c75c5c3..1661e855 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -12,7 +12,7 @@ from PIL import Image, ImageDraw from discord.ext import commands from bot.bot import Bot -from bot.constants import Colours +from bot.constants import Colours, MODERATION_ROLES DECK = list(product(*[(0, 1, 2)]*4)) @@ -275,6 +275,17 @@ class DuckGamesDirector(commands.Cog): """Explain the rules of the game.""" await self.send_help_embed(ctx) + @start_game.command(name="stop") + async def stop_game(self, ctx: commands.Context) -> None: + """Stop a currently running game. Only available to mods.""" + if not any(role in ctx.author.roles for role in MODERATION_ROLES): + return + try: + game = self.current_games.pop(ctx.channel.id) + except KeyError: + return + await self.end_game(game, end_message="Game canceled.") + @staticmethod async def send_help_embed(ctx: commands.Context) -> discord.Message: """Send rules embed.""" -- cgit v1.2.3 From 152fded782df5d666c94c5d58c80a9919d326890 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 20:49:20 -0400 Subject: Minor improvements to documentation --- bot/exts/evergreen/duck_game.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 1661e855..bc33102f 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -18,6 +18,8 @@ from bot.constants import Colours, MODERATION_ROLES DECK = list(product(*[(0, 1, 2)]*4)) GAME_DURATION = 180 + +# Scoring CORRECT_SOLN = 1 INCORRECT_SOLN = -1 CORRECT_GOOSE = 2 @@ -49,7 +51,7 @@ def assemble_board_image(board: list[tuple[int]], rows: int, columns: int) -> Im row, col = divmod(idx, columns) top, left = row * CARD_HEIGHT, col * CARD_WIDTH new_im.paste(card_image, (left, top)) - draw.text((left+7, top+4), str(idx), (0, 0, 0)) # magic numbers are buffers for the card labels + draw.text((left+7, top+4), str(idx), (0, 0, 0)) # magic numbers are buffers for the card labels return new_im @@ -147,7 +149,6 @@ class DuckGamesDirector(commands.Cog): @commands.cooldown(rate=1, per=2, type=commands.BucketType.channel) async def start_game(self, ctx: commands.Context) -> None: """Generate a board, send the game embed, and end the game after a time limit.""" - # One game at a time per channel if ctx.channel.id in self.current_games: return -- cgit v1.2.3 From 0fe4255cc7450445d7aa426d6450ed92aa9e7516 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 21:30:12 -0400 Subject: Reversed scoreboard so that winners are on top --- bot/exts/evergreen/duck_game.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index bc33102f..13e6165f 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -247,7 +247,8 @@ class DuckGamesDirector(commands.Cog): scores = sorted( game.scores.items(), - key=lambda item: item[1] + key=lambda item: item[1], + reverse=True, ) scoreboard = "\n".join(f"{member.display_name}: {score}" for member, score in scores) -- cgit v1.2.3 From 342b645a124317b20c5e9feb3c42e43468167b8b Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 8 Jul 2021 21:30:39 -0400 Subject: Tweaked minimum solution distribution --- bot/exts/evergreen/duck_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 13e6165f..fb54fcfd 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -30,7 +30,7 @@ INCORRECT_GOOSE = -1 while still making the end of the game unpredictable. Note: this is *not* the same as the distribution of number of solutions. """ -SOLN_DISTR = 0, 0.05, 0.1, 0.1, 0.15, 0.2, 0.2, 0.15, .05 +SOLN_DISTR = 0, 0.05, 0.05, 0.1, 0.15, 0.25, 0.2, 0.15, .05 p = Path("bot", "resources", "evergreen", "all_cards.png") ALL_CARDS = Image.open(p) -- cgit v1.2.3 From 79db5a00dfe7639020174bd543fb10d1e789dace Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Fri, 9 Jul 2021 13:36:08 -0400 Subject: Improve the look of labels --- bot/exts/evergreen/duck_game.py | 16 ++++++++++++---- bot/resources/evergreen/LuckiestGuy-Regular.ttf | Bin 0 -> 58292 bytes 2 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 bot/resources/evergreen/LuckiestGuy-Regular.ttf diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index fb54fcfd..7fb23ff6 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -8,7 +8,7 @@ from pathlib import Path from urllib.parse import urlparse import discord -from PIL import Image, ImageDraw +from PIL import Image, ImageDraw, ImageFont from discord.ext import commands from bot.bot import Bot @@ -32,8 +32,11 @@ INCORRECT_GOOSE = -1 """ SOLN_DISTR = 0, 0.05, 0.05, 0.1, 0.15, 0.25, 0.2, 0.15, .05 -p = Path("bot", "resources", "evergreen", "all_cards.png") -ALL_CARDS = Image.open(p) +image_path = Path("bot", "resources", "evergreen", "all_cards.png") +font_path = Path("bot", "resources", "evergreen", "LuckiestGuy-Regular.ttf") + +ALL_CARDS = Image.open(image_path) +LABEL_FONT = ImageFont.truetype(str(font_path), size=16) CARD_WIDTH = 155 CARD_HEIGHT = 97 @@ -51,7 +54,12 @@ def assemble_board_image(board: list[tuple[int]], rows: int, columns: int) -> Im row, col = divmod(idx, columns) top, left = row * CARD_HEIGHT, col * CARD_WIDTH new_im.paste(card_image, (left, top)) - draw.text((left+7, top+4), str(idx), (0, 0, 0)) # magic numbers are buffers for the card labels + draw.text( + xy=(left+5, top+5), # magic numbers are buffers for the card labels + text=str(idx), + fill=(0, 0, 0), + font=LABEL_FONT, + ) return new_im diff --git a/bot/resources/evergreen/LuckiestGuy-Regular.ttf b/bot/resources/evergreen/LuckiestGuy-Regular.ttf new file mode 100644 index 00000000..8c79c875 Binary files /dev/null and b/bot/resources/evergreen/LuckiestGuy-Regular.ttf differ -- cgit v1.2.3 From e258596a35c8ec747dc4785fadff622351bfd67e Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Fri, 9 Jul 2021 14:31:08 -0400 Subject: Add tip to help embed --- bot/exts/evergreen/duck_game.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 7fb23ff6..1286995f 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -311,6 +311,9 @@ class DuckGamesDirector(commands.Cog): '**2** for a correct "GOOSE" call\n' "**-1** for any wrong answer" ) + embed.set_footer( + text="Tip: using Discord's compact message display mode can help keep the board on the screen" + ) await ctx.send(embed=embed) @staticmethod -- cgit v1.2.3 From 3cd3517fd93bbcf2d13b423d21b7da592a933e32 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Wed, 21 Jul 2021 09:53:45 +0100 Subject: Remove command that parrots images input from users --- .../evergreen/avatar_modification/avatar_modify.py | 32 ---------------------- 1 file changed, 32 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 17f34ed4..8ab12365 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -9,7 +9,6 @@ from concurrent.futures import ThreadPoolExecutor from pathlib import Path import discord -from aiohttp import client_exceptions from discord.ext import commands from bot.bot import Bot @@ -235,37 +234,6 @@ class AvatarModify(commands.Cog): image_bytes = await user.avatar_url_as(size=1024).read() await self.send_pride_image(ctx, image_bytes, pixels, flag, option) - @prideavatar.command() - async def image(self, ctx: commands.Context, url: str, option: str = "lgbt", pixels: int = 64) -> None: - """ - This surrounds the image specified by the URL with a border of a specified LGBT flag. - - This defaults to the LGBT rainbow flag if none is given. - The amount of pixels can be given which determines the thickness of the flag border. - This has a maximum of 512px and defaults to a 64px border. - The full image is 1024x1024. - """ - option = option.lower() - pixels = max(0, min(512, pixels)) - flag = GENDER_OPTIONS.get(option) - if flag is None: - await ctx.send("I don't have that flag!") - return - - async with ctx.typing(): - try: - async with self.bot.http_session.get(url) as response: - if response.status != 200: - await ctx.send("Bad response from provided URL!") - return - image_bytes = await response.read() - except client_exceptions.ClientConnectorError: - raise commands.BadArgument("Cannot connect to provided URL!") - except client_exceptions.InvalidURL: - raise commands.BadArgument("Invalid URL!") - - await self.send_pride_image(ctx, image_bytes, pixels, flag, option) - @prideavatar.command() async def flags(self, ctx: commands.Context) -> None: """This lists the flags that can be used with the prideavatar command.""" -- cgit v1.2.3 From 59ae0e8d18d63f64d77970f487cc7acb0c44fd7d Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Wed, 21 Jul 2021 09:55:28 +0100 Subject: Remove command that can run on users other than the author --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 8ab12365..bd324f67 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -251,12 +251,9 @@ class AvatarModify(commands.Cog): root_aliases=("spookyavatar", "spookify", "savatar"), brief="Spookify an user's avatar." ) - async def spookyavatar(self, ctx: commands.Context, member: discord.Member = None) -> None: - """This "spookifies" the given user's avatar, with a random *spooky* effect.""" - if member is None: - member = ctx.author - - user = await self._fetch_user(member.id) + async def spookyavatar(self, ctx: commands.Context) -> None: + """This "spookifies" the user's avatar, with a random *spooky* effect.""" + user = await self._fetch_user(ctx.author.id) if not user: await ctx.send(f"{Emojis.cross_mark} Could not get user info.") return @@ -264,7 +261,7 @@ class AvatarModify(commands.Cog): async with ctx.typing(): image_bytes = await user.avatar_url_as(size=1024).read() - file_name = file_safe_name("spooky_avatar", member.display_name) + file_name = file_safe_name("spooky_avatar", ctx.author.display_name) file = await in_executor( PfpEffects.apply_effect, @@ -277,7 +274,6 @@ class AvatarModify(commands.Cog): title="Is this you or am I just really paranoid?", colour=Colours.soft_red ) - embed.set_author(name=member.name, icon_url=member.avatar_url) embed.set_image(url=f"attachment://{file_name}") embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=ctx.author.avatar_url) -- cgit v1.2.3 From f7db3f59cb78d8d87cb4dd9a8ac246172bb3fa08 Mon Sep 17 00:00:00 2001 From: aru Date: Thu, 29 Jul 2021 23:31:10 -0400 Subject: make user affirm their pr follows guidelines The contributing guidelines also contain a part which requires contributions to follow the open source license. This requires the user to agree that they do follow said guidelines. --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ba418166..403438d1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -17,4 +17,4 @@ Issues can be skipped with explicit core dev approval, but you have to link the - [ ] Join the [**Python Discord Community**](https://discord.gg/python)? - [ ] Read all the comments in this template? - [ ] Ensure there is an issue open, or link relevant discord discussions? -- [ ] Read the [contributing guidelines](https://pythondiscord.com/pages/contributing/contributing-guidelines/)? +- [ ] Read and agree to the [contributing guidelines](https://pythondiscord.com/pages/contributing/contributing-guidelines/)? -- cgit v1.2.3 From c9f8efc6cca4556b04adca2894cdd1b61c87d421 Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Mon, 2 Aug 2021 19:13:03 +0200 Subject: Handle Wikipedia API errors and warnings in the Wikipedia Cog - Add an additional check to handle errors and warnings coming from the side of the Wikipedia API, as the response code sent by the Wikipedia API remains 200 even if there is an error. --- bot/exts/evergreen/wikipedia.py | 55 ++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index 83937438..8af22737 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -39,31 +39,42 @@ class WikipediaSearch(commands.Cog): async with self.bot.http_session.get(url=url) as resp: if resp.status == 200: raw_data = await resp.json() - number_of_results = raw_data["query"]["searchinfo"]["totalhits"] - - if number_of_results: - results = raw_data["query"]["search"] - lines = [] - - for article in results: - line = WIKI_SEARCH_RESULT.format( - name=article["title"], - description=unescape( - re.sub( - WIKI_SNIPPET_REGEX, "", article["snippet"] - ) - ), - url=f"https://en.wikipedia.org/?curid={article['pageid']}" - ) - lines.append(line) - - return lines + if raw_data.get("query", None) is None: + if raw_data.get("errors", None) is not None: + log.info("There was an error regarding the Wikipedia API query.") + else: + log.info("There was an issue when trying to communicate with the Wikipedia API") - else: await channel.send( - "Sorry, we could not find a wikipedia article using that search term." - ) + "There was an issue processing your Wikipedia request, please try again later.") return + else: + + number_of_results = raw_data["query"]["searchinfo"]["totalhits"] + + if number_of_results: + results = raw_data["query"]["search"] + lines = [] + + for article in results: + line = WIKI_SEARCH_RESULT.format( + name=article["title"], + description=unescape( + re.sub( + WIKI_SNIPPET_REGEX, "", article["snippet"] + ) + ), + url=f"https://en.wikipedia.org/?curid={article['pageid']}" + ) + lines.append(line) + + return lines + + else: + await channel.send( + "Sorry, we could not find a wikipedia article using that search term." + ) + return else: log.info(f"Unexpected response `{resp.status}` while searching wikipedia for `{search}`") await channel.send( -- cgit v1.2.3 From f36cdb23cf9a8a5470180d85d336577658d849c3 Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Mon, 2 Aug 2021 21:58:21 +0200 Subject: Refactor wiki_request, add External API related error,-handler - Refactor wiki_request in the Wikipedia Cog, remove unnecessary else statements. - Add ExternalAPIError, and a handler for this Exception into error_handler --- bot/exts/evergreen/error_handler.py | 7 +++- bot/exts/evergreen/wikipedia.py | 67 +++++++++++++++++-------------------- bot/utils/exceptions.py | 10 ++++++ 3 files changed, 47 insertions(+), 37 deletions(-) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index 5873fb83..1662af50 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -11,7 +11,7 @@ from sentry_sdk import push_scope from bot.bot import Bot from bot.constants import Channels, Colours, ERROR_REPLIES, NEGATIVE_REPLIES, RedirectOutput from bot.utils.decorators import InChannelCheckFailure, InMonthCheckFailure -from bot.utils.exceptions import UserNotPlayingError +from bot.utils.exceptions import ExternalAPIError, UserNotPlayingError log = logging.getLogger(__name__) @@ -120,6 +120,11 @@ class CommandErrorHandler(commands.Cog): await ctx.send("Game not found.") return + if isinstance(error, ExternalAPIError): + await ctx.send(embed=self.error_embed(f"There was an error when communicating with the {error.api}", + NEGATIVE_REPLIES)) + return + with push_scope() as scope: scope.user = { "id": ctx.author.id, diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index 8af22737..d53424fd 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -9,6 +9,7 @@ from discord.ext import commands from bot.bot import Bot from bot.utils import LinePaginator +from bot.utils.exceptions import ExternalAPIError log = logging.getLogger(__name__) @@ -37,48 +38,42 @@ class WikipediaSearch(commands.Cog): """Search wikipedia search string and return formatted first 10 pages found.""" url = SEARCH_API.format(number_of_results=10, string=search) async with self.bot.http_session.get(url=url) as resp: - if resp.status == 200: - raw_data = await resp.json() - if raw_data.get("query", None) is None: - if raw_data.get("errors", None) is not None: - log.info("There was an error regarding the Wikipedia API query.") - else: - log.info("There was an issue when trying to communicate with the Wikipedia API") - - await channel.send( - "There was an issue processing your Wikipedia request, please try again later.") - return - else: + if resp.status != 200: + log.info(f"Unexpected response `{resp.status}` while searching wikipedia for `{search}`") + raise ExternalAPIError("Wikipedia API") - number_of_results = raw_data["query"]["searchinfo"]["totalhits"] - - if number_of_results: - results = raw_data["query"]["search"] - lines = [] - - for article in results: - line = WIKI_SEARCH_RESULT.format( - name=article["title"], - description=unescape( - re.sub( - WIKI_SNIPPET_REGEX, "", article["snippet"] - ) - ), - url=f"https://en.wikipedia.org/?curid={article['pageid']}" + raw_data = await resp.json() + if raw_data.get("query", None) is None: + if raw_data.get("errors", None) is not None: + log.info("There was an error regarding the Wikipedia API query.") + else: + log.info("There was an issue when trying to communicate with the Wikipedia API, " + "please try again later.") + raise ExternalAPIError("Wikipedia API") + + number_of_results = raw_data["query"]["searchinfo"]["totalhits"] + + if number_of_results: + results = raw_data["query"]["search"] + lines = [] + + for article in results: + line = WIKI_SEARCH_RESULT.format( + name=article["title"], + description=unescape( + re.sub( + WIKI_SNIPPET_REGEX, "", article["snippet"] ) - lines.append(line) + ), + url=f"https://en.wikipedia.org/?curid={article['pageid']}" + ) + lines.append(line) - return lines + return lines - else: - await channel.send( - "Sorry, we could not find a wikipedia article using that search term." - ) - return else: - log.info(f"Unexpected response `{resp.status}` while searching wikipedia for `{search}`") await channel.send( - "Whoops, the Wikipedia API is having some issues right now. Try again later." + "Sorry, we could not find a wikipedia article using that search term." ) return diff --git a/bot/utils/exceptions.py b/bot/utils/exceptions.py index 9e080759..5ccd6139 100644 --- a/bot/utils/exceptions.py +++ b/bot/utils/exceptions.py @@ -1,4 +1,14 @@ + class UserNotPlayingError(Exception): """Raised when users try to use game commands when they are not playing.""" pass + + +class ExternalAPIError(Exception): + """Raised when an external API(eg. Wikipedia) returns an error.""" + + def __init__(self, api: str): + super().__init__() + self.api = api + pass -- cgit v1.2.3 From 38add14229548d423d643d828063039010d00f80 Mon Sep 17 00:00:00 2001 From: Xithrius Date: Tue, 3 Aug 2021 14:31:59 -0700 Subject: Added emoji IDs to constants.py --- bot/constants.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 0a3b874a..bb602361 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -227,8 +227,8 @@ class Emojis: status_offline = "<:status_offline:470326266537705472>" - stackoverflow_tag = environ.get('stack_tag', "<:stack_tag:*> ") - stackoverflow_views = environ.get('stack_views', "<:stack_eye:*>") + stackoverflow_tag = "<:stack_tag:870926975307501570>" + stackoverflow_views = "<:stack_eye:870926992692879371>" # Reddit emojis reddit = "<:reddit:676030265734332427>" -- cgit v1.2.3 From f72bb8a032e8d94212e1e8c6072332d260c9262d Mon Sep 17 00:00:00 2001 From: wookie184 Date: Fri, 6 Aug 2021 15:34:15 +0100 Subject: Pinned aioredis to 1.3 and relocked dependencies aioredis pin needed as fakeredis doesn't support aioredis 2.0.0, but poetry update was not respecting that requirement --- poetry.lock | 350 ++++++++++++++++++++++++++++++++------------------------- pyproject.toml | 1 + 2 files changed, 196 insertions(+), 155 deletions(-) diff --git a/poetry.lock b/poetry.lock index b6581b1b..861dca0b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -40,17 +40,9 @@ python-versions = "*" async-timeout = "*" hiredis = "*" -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "arrow" -version = "1.1.0" +version = "1.1.1" description = "Better dates & times for Python" category = "main" optional = false @@ -96,9 +88,21 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] +[[package]] +name = "backports.entry-points-selectable" +version = "1.1.0" +description = "Compatibility shim providing selectable entry points for older implementations" +category = "dev" +optional = false +python-versions = ">=2.7" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-flake8", "pytest-cov", "pytest-black (>=0.3.7)", "pytest-mypy", "pytest-checkdocs (>=2.4)", "pytest-enabler (>=1.0.1)"] + [[package]] name = "certifi" -version = "2020.12.5" +version = "2021.5.30" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -106,7 +110,7 @@ python-versions = "*" [[package]] name = "cffi" -version = "1.14.5" +version = "1.14.6" description = "Foreign Function Interface for Python calling C code." category = "main" optional = false @@ -117,7 +121,7 @@ pycparser = "*" [[package]] name = "cfgv" -version = "3.2.0" +version = "3.3.0" description = "Validate configuration and produce human readable error messages." category = "dev" optional = false @@ -152,7 +156,7 @@ six = "*" [[package]] name = "discord.py" -version = "1.7.2" +version = "1.7.3" description = "A Python wrapper for the Discord API" category = "main" optional = false @@ -167,7 +171,7 @@ voice = ["PyNaCl (>=1.3.0,<1.5)"] [[package]] name = "distlib" -version = "0.3.1" +version = "0.3.2" description = "Distribution utilities" category = "dev" optional = false @@ -183,7 +187,7 @@ python-versions = "*" [[package]] name = "fakeredis" -version = "1.5.0" +version = "1.5.2" description = "Fake implementation of redis API for testing purposes." category = "main" optional = false @@ -195,7 +199,7 @@ six = ">=1.12" sortedcontainers = "*" [package.extras] -aioredis = ["aioredis"] +aioredis = ["aioredis (<2)"] lua = ["lupa"] [[package]] @@ -333,7 +337,7 @@ python-versions = ">=3.6" [[package]] name = "identify" -version = "2.2.4" +version = "2.2.12" description = "File identification library for Python" category = "dev" optional = false @@ -344,11 +348,11 @@ license = ["editdistance-s"] [[package]] name = "idna" -version = "3.1" +version = "3.2" description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false -python-versions = ">=3.4" +python-versions = ">=3.5" [[package]] name = "kiwisolver" @@ -408,7 +412,7 @@ python-versions = "*" [[package]] name = "numpy" -version = "1.20.3" +version = "1.21.1" description = "NumPy is the fundamental package for array computing with Python." category = "main" optional = false @@ -416,26 +420,39 @@ python-versions = ">=3.7" [[package]] name = "pep8-naming" -version = "0.11.1" +version = "0.12.0" description = "Check PEP-8 naming conventions, plugin for flake8" category = "dev" optional = false python-versions = "*" [package.dependencies] +flake8 = ">=3.9.1" flake8-polyfill = ">=1.0.2,<2" [[package]] name = "pillow" -version = "8.2.0" +version = "8.3.1" description = "Python Imaging Library (Fork)" category = "main" optional = false python-versions = ">=3.6" +[[package]] +name = "platformdirs" +version = "2.2.0" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + [[package]] name = "pre-commit" -version = "2.12.1" +version = "2.13.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false @@ -522,7 +539,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "python-dateutil" -version = "2.8.1" +version = "2.8.2" description = "Extensions to the standard Python datetime module" category = "main" optional = false @@ -645,34 +662,35 @@ python-versions = "*" [[package]] name = "urllib3" -version = "1.26.4" +version = "1.26.6" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] +brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -brotli = ["brotlipy (>=0.6.0)"] [[package]] name = "virtualenv" -version = "20.4.6" +version = "20.7.0" description = "Virtual Python Environment builder" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] -appdirs = ">=1.4.3,<2" +"backports.entry-points-selectable" = ">=1.0.4" distlib = ">=0.3.1,<1" filelock = ">=3.0.0,<4" +platformdirs = ">=2,<3" six = ">=1.9.0,<2" [package.extras] docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] [[package]] name = "yarl" @@ -689,7 +707,7 @@ multidict = ">=4.0" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "14c54d898cad74073a3f8f83283be3ea3c32fbb8558149284c1475677b99bd59" +content-hash = "a73732d7a2ea8348dd9e38b87677eeab0ece557ba5f56a24a0bed88c07f4d78d" [metadata.files] aiodns = [ @@ -739,13 +757,9 @@ aioredis = [ {file = "aioredis-1.3.1-py3-none-any.whl", hash = "sha256:b61808d7e97b7cd5a92ed574937a079c9387fdadd22bfbfa7ad2fd319ecc26e3"}, {file = "aioredis-1.3.1.tar.gz", hash = "sha256:15f8af30b044c771aee6787e5ec24694c048184c7b9e54c3b60c750a4b93273a"}, ] -appdirs = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] arrow = [ - {file = "arrow-1.1.0-py3-none-any.whl", hash = "sha256:8cbe6a629b1c54ae11b52d6d9e70890089241958f63bc59467e277e34b7a5378"}, - {file = "arrow-1.1.0.tar.gz", hash = "sha256:b8fe13abf3517abab315e09350c903902d1447bd311afbc17547ba1cb3ff5bd8"}, + {file = "arrow-1.1.1-py3-none-any.whl", hash = "sha256:77a60a4db5766d900a2085ce9074c5c7b8e2c99afeaa98ad627637ff6f292510"}, + {file = "arrow-1.1.1.tar.gz", hash = "sha256:dee7602f6c60e3ec510095b5e301441bc56288cb8f51def14dcb3079f623823a"}, ] async-rediscache = [ {file = "async-rediscache-0.1.4.tar.gz", hash = "sha256:6be8a657d724ccbcfb1946d29a80c3478c5f9ecd2f78a0a26d2f4013a622258f"}, @@ -759,52 +773,64 @@ attrs = [ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] +"backports.entry-points-selectable" = [ + {file = "backports.entry_points_selectable-1.1.0-py2.py3-none-any.whl", hash = "sha256:a6d9a871cde5e15b4c4a53e3d43ba890cc6861ec1332c9c2428c92f977192acc"}, + {file = "backports.entry_points_selectable-1.1.0.tar.gz", hash = "sha256:988468260ec1c196dab6ae1149260e2f5472c9110334e5d51adcb77867361f6a"}, +] certifi = [ - {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, - {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, + {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, + {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, ] cffi = [ - {file = "cffi-1.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"}, - {file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"}, - {file = "cffi-1.14.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa"}, - {file = "cffi-1.14.5-cp27-cp27m-win32.whl", hash = "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3"}, - {file = "cffi-1.14.5-cp27-cp27m-win_amd64.whl", hash = "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5"}, - {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482"}, - {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6"}, - {file = "cffi-1.14.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045"}, - {file = "cffi-1.14.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa"}, - {file = "cffi-1.14.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406"}, - {file = "cffi-1.14.5-cp35-cp35m-win32.whl", hash = "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369"}, - {file = "cffi-1.14.5-cp35-cp35m-win_amd64.whl", hash = "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315"}, - {file = "cffi-1.14.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892"}, - {file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"}, - {file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"}, - {file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"}, - {file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"}, - {file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"}, - {file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"}, - {file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"}, - {file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"}, - {file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"}, - {file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"}, - {file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"}, - {file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"}, - {file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"}, - {file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"}, - {file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"}, - {file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"}, - {file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"}, - {file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"}, - {file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"}, - {file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"}, - {file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"}, - {file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"}, - {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"}, - {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"}, + {file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"}, + {file = "cffi-1.14.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99"}, + {file = "cffi-1.14.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819"}, + {file = "cffi-1.14.6-cp27-cp27m-win32.whl", hash = "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20"}, + {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"}, + {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"}, + {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"}, + {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"}, + {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"}, + {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"}, + {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"}, + {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"}, + {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb"}, + {file = "cffi-1.14.6-cp36-cp36m-win32.whl", hash = "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a"}, + {file = "cffi-1.14.6-cp36-cp36m-win_amd64.whl", hash = "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e"}, + {file = "cffi-1.14.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762"}, + {file = "cffi-1.14.6-cp37-cp37m-win32.whl", hash = "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771"}, + {file = "cffi-1.14.6-cp37-cp37m-win_amd64.whl", hash = "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a"}, + {file = "cffi-1.14.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc"}, + {file = "cffi-1.14.6-cp38-cp38-win32.whl", hash = "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548"}, + {file = "cffi-1.14.6-cp38-cp38-win_amd64.whl", hash = "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156"}, + {file = "cffi-1.14.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87"}, + {file = "cffi-1.14.6-cp39-cp39-win32.whl", hash = "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728"}, + {file = "cffi-1.14.6-cp39-cp39-win_amd64.whl", hash = "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2"}, + {file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"}, ] cfgv = [ - {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"}, - {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, + {file = "cfgv-3.3.0-py2.py3-none-any.whl", hash = "sha256:b449c9c6118fe8cca7fa5e00b9ec60ba08145d281d52164230a69211c5d597a1"}, + {file = "cfgv-3.3.0.tar.gz", hash = "sha256:9e600479b3b99e8af981ecdfc80a0296104ee610cab48a5ae4ffd0b668650eb1"}, ] chardet = [ {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, @@ -819,20 +845,20 @@ cycler = [ {file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"}, ] "discord.py" = [ - {file = "discord.py-1.7.2-py3-none-any.whl", hash = "sha256:f179db299c949a8cf0a12c1b1b94d0da9a18e088857154d93ae5ab1d807ec61d"}, - {file = "discord.py-1.7.2.tar.gz", hash = "sha256:114e76cd27362fb919abf7f001a2dbdc77c9a67cff74ed6a89aecd6582ee298e"}, + {file = "discord.py-1.7.3-py3-none-any.whl", hash = "sha256:c6f64db136de0e18e090f6752ea68bdd4ab0a61b82dfe7acecefa22d6477bb0c"}, + {file = "discord.py-1.7.3.tar.gz", hash = "sha256:462cd0fe307aef8b29cbfa8dd613e548ae4b2cb581d46da9ac0d46fb6ea19408"}, ] distlib = [ - {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, - {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, + {file = "distlib-0.3.2-py2.py3-none-any.whl", hash = "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c"}, + {file = "distlib-0.3.2.zip", hash = "sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736"}, ] emojis = [ {file = "emojis-0.6.0-py3-none-any.whl", hash = "sha256:7da34c8a78ae262fd68cef9e2c78a3c1feb59784489eeea0f54ba1d4b7111c7c"}, {file = "emojis-0.6.0.tar.gz", hash = "sha256:bf605d1f1a27a81cd37fe82eb65781c904467f569295a541c33710b97e4225ec"}, ] fakeredis = [ - {file = "fakeredis-1.5.0-py3-none-any.whl", hash = "sha256:e0416e4941cecd3089b0d901e60c8dc3c944f6384f5e29e2261c0d3c5fa99669"}, - {file = "fakeredis-1.5.0.tar.gz", hash = "sha256:1ac0cef767c37f51718874a33afb5413e69d132988cb6a80c6e6dbeddf8c7623"}, + {file = "fakeredis-1.5.2-py3-none-any.whl", hash = "sha256:f1ffdb134538e6d7c909ddfb4fc5edeb4a73d0ea07245bc69b8135fbc4144b04"}, + {file = "fakeredis-1.5.2.tar.gz", hash = "sha256:18fc1808d2ce72169d3f11acdb524a00ef96bd29970c6d34cfeb2edb3fc0c020"}, ] filelock = [ {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, @@ -921,12 +947,12 @@ hiredis = [ {file = "hiredis-2.0.0.tar.gz", hash = "sha256:81d6d8e39695f2c37954d1011c0480ef7cf444d4e3ae24bc5e89ee5de360139a"}, ] identify = [ - {file = "identify-2.2.4-py2.py3-none-any.whl", hash = "sha256:ad9f3fa0c2316618dc4d840f627d474ab6de106392a4f00221820200f490f5a8"}, - {file = "identify-2.2.4.tar.gz", hash = "sha256:9bcc312d4e2fa96c7abebcdfb1119563b511b5e3985ac52f60d9116277865b2e"}, + {file = "identify-2.2.12-py2.py3-none-any.whl", hash = "sha256:a510cbe155f39665625c8a4c4b4f9360cbce539f51f23f47836ab7dd852db541"}, + {file = "identify-2.2.12.tar.gz", hash = "sha256:242332b3bdd45a8af1752d5d5a3afb12bee26f8e67c4be06e394f82d05ef1a4d"}, ] idna = [ - {file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"}, - {file = "idna-3.1.tar.gz", hash = "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1"}, + {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"}, + {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"}, ] kiwisolver = [ {file = "kiwisolver-1.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fd34fbbfbc40628200730bc1febe30631347103fc8d3d4fa012c21ab9c11eca9"}, @@ -1035,73 +1061,87 @@ nodeenv = [ {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"}, ] numpy = [ - {file = "numpy-1.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:70eb5808127284c4e5c9e836208e09d685a7978b6a216db85960b1a112eeace8"}, - {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6ca2b85a5997dabc38301a22ee43c82adcb53ff660b89ee88dded6b33687e1d8"}, - {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c5bf0e132acf7557fc9bb8ded8b53bbbbea8892f3c9a1738205878ca9434206a"}, - {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db250fd3e90117e0312b611574cd1b3f78bec046783195075cbd7ba9c3d73f16"}, - {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:637d827248f447e63585ca3f4a7d2dfaa882e094df6cfa177cc9cf9cd6cdf6d2"}, - {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8b7bb4b9280da3b2856cb1fc425932f46fba609819ee1c62256f61799e6a51d2"}, - {file = "numpy-1.20.3-cp37-cp37m-win32.whl", hash = "sha256:67d44acb72c31a97a3d5d33d103ab06d8ac20770e1c5ad81bdb3f0c086a56cf6"}, - {file = "numpy-1.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:43909c8bb289c382170e0282158a38cf306a8ad2ff6dfadc447e90f9961bef43"}, - {file = "numpy-1.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f1452578d0516283c87608a5a5548b0cdde15b99650efdfd85182102ef7a7c17"}, - {file = "numpy-1.20.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6e51534e78d14b4a009a062641f465cfaba4fdcb046c3ac0b1f61dd97c861b1b"}, - {file = "numpy-1.20.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e515c9a93aebe27166ec9593411c58494fa98e5fcc219e47260d9ab8a1cc7f9f"}, - {file = "numpy-1.20.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1c09247ccea742525bdb5f4b5ceeacb34f95731647fe55774aa36557dbb5fa4"}, - {file = "numpy-1.20.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:66fbc6fed94a13b9801fb70b96ff30605ab0a123e775a5e7a26938b717c5d71a"}, - {file = "numpy-1.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ea9cff01e75a956dbee133fa8e5b68f2f92175233de2f88de3a682dd94deda65"}, - {file = "numpy-1.20.3-cp38-cp38-win32.whl", hash = "sha256:f39a995e47cb8649673cfa0579fbdd1cdd33ea497d1728a6cb194d6252268e48"}, - {file = "numpy-1.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:1676b0a292dd3c99e49305a16d7a9f42a4ab60ec522eac0d3dd20cdf362ac010"}, - {file = "numpy-1.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:830b044f4e64a76ba71448fce6e604c0fc47a0e54d8f6467be23749ac2cbd2fb"}, - {file = "numpy-1.20.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:55b745fca0a5ab738647d0e4db099bd0a23279c32b31a783ad2ccea729e632df"}, - {file = "numpy-1.20.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5d050e1e4bc9ddb8656d7b4f414557720ddcca23a5b88dd7cff65e847864c400"}, - {file = "numpy-1.20.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9c65473ebc342715cb2d7926ff1e202c26376c0dcaaee85a1fd4b8d8c1d3b2f"}, - {file = "numpy-1.20.3-cp39-cp39-win32.whl", hash = "sha256:16f221035e8bd19b9dc9a57159e38d2dd060b48e93e1d843c49cb370b0f415fd"}, - {file = "numpy-1.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:6690080810f77485667bfbff4f69d717c3be25e5b11bb2073e76bb3f578d99b4"}, - {file = "numpy-1.20.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e465afc3b96dbc80cf4a5273e5e2b1e3451286361b4af70ce1adb2984d392f9"}, - {file = "numpy-1.20.3.zip", hash = "sha256:e55185e51b18d788e49fe8305fd73ef4470596b33fc2c1ceb304566b99c71a69"}, + {file = "numpy-1.21.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38e8648f9449a549a7dfe8d8755a5979b45b3538520d1e735637ef28e8c2dc50"}, + {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fd7d7409fa643a91d0a05c7554dd68aa9c9bb16e186f6ccfe40d6e003156e33a"}, + {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a75b4498b1e93d8b700282dc8e655b8bd559c0904b3910b144646dbbbc03e062"}, + {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1412aa0aec3e00bc23fbb8664d76552b4efde98fb71f60737c83efbac24112f1"}, + {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e46ceaff65609b5399163de5893d8f2a82d3c77d5e56d976c8b5fb01faa6b671"}, + {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c6a2324085dd52f96498419ba95b5777e40b6bcbc20088fddb9e8cbb58885e8e"}, + {file = "numpy-1.21.1-cp37-cp37m-win32.whl", hash = "sha256:73101b2a1fef16602696d133db402a7e7586654682244344b8329cdcbbb82172"}, + {file = "numpy-1.21.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7a708a79c9a9d26904d1cca8d383bf869edf6f8e7650d85dbc77b041e8c5a0f8"}, + {file = "numpy-1.21.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95b995d0c413f5d0428b3f880e8fe1660ff9396dcd1f9eedbc311f37b5652e16"}, + {file = "numpy-1.21.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:635e6bd31c9fb3d475c8f44a089569070d10a9ef18ed13738b03049280281267"}, + {file = "numpy-1.21.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a3d5fb89bfe21be2ef47c0614b9c9c707b7362386c9a3ff1feae63e0267ccb6"}, + {file = "numpy-1.21.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a326af80e86d0e9ce92bcc1e65c8ff88297de4fa14ee936cb2293d414c9ec63"}, + {file = "numpy-1.21.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:791492091744b0fe390a6ce85cc1bf5149968ac7d5f0477288f78c89b385d9af"}, + {file = "numpy-1.21.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0318c465786c1f63ac05d7c4dbcecd4d2d7e13f0959b01b534ea1e92202235c5"}, + {file = "numpy-1.21.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a513bd9c1551894ee3d31369f9b07460ef223694098cf27d399513415855b68"}, + {file = "numpy-1.21.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:91c6f5fc58df1e0a3cc0c3a717bb3308ff850abdaa6d2d802573ee2b11f674a8"}, + {file = "numpy-1.21.1-cp38-cp38-win32.whl", hash = "sha256:978010b68e17150db8765355d1ccdd450f9fc916824e8c4e35ee620590e234cd"}, + {file = "numpy-1.21.1-cp38-cp38-win_amd64.whl", hash = "sha256:9749a40a5b22333467f02fe11edc98f022133ee1bfa8ab99bda5e5437b831214"}, + {file = "numpy-1.21.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d7a4aeac3b94af92a9373d6e77b37691b86411f9745190d2c351f410ab3a791f"}, + {file = "numpy-1.21.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9e7912a56108aba9b31df688a4c4f5cb0d9d3787386b87d504762b6754fbb1b"}, + {file = "numpy-1.21.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:25b40b98ebdd272bc3020935427a4530b7d60dfbe1ab9381a39147834e985eac"}, + {file = "numpy-1.21.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a92c5aea763d14ba9d6475803fc7904bda7decc2a0a68153f587ad82941fec1"}, + {file = "numpy-1.21.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05a0f648eb28bae4bcb204e6fd14603de2908de982e761a2fc78efe0f19e96e1"}, + {file = "numpy-1.21.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01f28075a92eede918b965e86e8f0ba7b7797a95aa8d35e1cc8821f5fc3ad6a"}, + {file = "numpy-1.21.1-cp39-cp39-win32.whl", hash = "sha256:88c0b89ad1cc24a5efbb99ff9ab5db0f9a86e9cc50240177a571fbe9c2860ac2"}, + {file = "numpy-1.21.1-cp39-cp39-win_amd64.whl", hash = "sha256:01721eefe70544d548425a07c80be8377096a54118070b8a62476866d5208e33"}, + {file = "numpy-1.21.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2d4d1de6e6fb3d28781c73fbde702ac97f03d79e4ffd6598b880b2d95d62ead4"}, + {file = "numpy-1.21.1.zip", hash = "sha256:dff4af63638afcc57a3dfb9e4b26d434a7a602d225b42d746ea7fe2edf1342fd"}, ] pep8-naming = [ - {file = "pep8-naming-0.11.1.tar.gz", hash = "sha256:a1dd47dd243adfe8a83616e27cf03164960b507530f155db94e10b36a6cd6724"}, - {file = "pep8_naming-0.11.1-py2.py3-none-any.whl", hash = "sha256:f43bfe3eea7e0d73e8b5d07d6407ab47f2476ccaeff6937c84275cd30b016738"}, + {file = "pep8-naming-0.12.0.tar.gz", hash = "sha256:1f9a3ecb2f3fd83240fd40afdd70acc89695c49c333413e49788f93b61827e12"}, + {file = "pep8_naming-0.12.0-py2.py3-none-any.whl", hash = "sha256:2321ac2b7bf55383dd19a6a9c8ae2ebf05679699927a3af33e60dd7d337099d3"}, ] pillow = [ - {file = "Pillow-8.2.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:dc38f57d8f20f06dd7c3161c59ca2c86893632623f33a42d592f097b00f720a9"}, - {file = "Pillow-8.2.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a013cbe25d20c2e0c4e85a9daf438f85121a4d0344ddc76e33fd7e3965d9af4b"}, - {file = "Pillow-8.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8bb1e155a74e1bfbacd84555ea62fa21c58e0b4e7e6b20e4447b8d07990ac78b"}, - {file = "Pillow-8.2.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c5236606e8570542ed424849f7852a0ff0bce2c4c8d0ba05cc202a5a9c97dee9"}, - {file = "Pillow-8.2.0-cp36-cp36m-win32.whl", hash = "sha256:12e5e7471f9b637762453da74e390e56cc43e486a88289995c1f4c1dc0bfe727"}, - {file = "Pillow-8.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5afe6b237a0b81bd54b53f835a153770802f164c5570bab5e005aad693dab87f"}, - {file = "Pillow-8.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:cb7a09e173903541fa888ba010c345893cd9fc1b5891aaf060f6ca77b6a3722d"}, - {file = "Pillow-8.2.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0d19d70ee7c2ba97631bae1e7d4725cdb2ecf238178096e8c82ee481e189168a"}, - {file = "Pillow-8.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:083781abd261bdabf090ad07bb69f8f5599943ddb539d64497ed021b2a67e5a9"}, - {file = "Pillow-8.2.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c6b39294464b03457f9064e98c124e09008b35a62e3189d3513e5148611c9388"}, - {file = "Pillow-8.2.0-cp37-cp37m-win32.whl", hash = "sha256:01425106e4e8cee195a411f729cff2a7d61813b0b11737c12bd5991f5f14bcd5"}, - {file = "Pillow-8.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3b570f84a6161cf8865c4e08adf629441f56e32f180f7aa4ccbd2e0a5a02cba2"}, - {file = "Pillow-8.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:031a6c88c77d08aab84fecc05c3cde8414cd6f8406f4d2b16fed1e97634cc8a4"}, - {file = "Pillow-8.2.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:66cc56579fd91f517290ab02c51e3a80f581aba45fd924fcdee01fa06e635812"}, - {file = "Pillow-8.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c32cc3145928c4305d142ebec682419a6c0a8ce9e33db900027ddca1ec39178"}, - {file = "Pillow-8.2.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:624b977355cde8b065f6d51b98497d6cd5fbdd4f36405f7a8790e3376125e2bb"}, - {file = "Pillow-8.2.0-cp38-cp38-win32.whl", hash = "sha256:5cbf3e3b1014dddc45496e8cf38b9f099c95a326275885199f427825c6522232"}, - {file = "Pillow-8.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:463822e2f0d81459e113372a168f2ff59723e78528f91f0bd25680ac185cf797"}, - {file = "Pillow-8.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:95d5ef984eff897850f3a83883363da64aae1000e79cb3c321915468e8c6add5"}, - {file = "Pillow-8.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b91c36492a4bbb1ee855b7d16fe51379e5f96b85692dc8210831fbb24c43e484"}, - {file = "Pillow-8.2.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d68cb92c408261f806b15923834203f024110a2e2872ecb0bd2a110f89d3c602"}, - {file = "Pillow-8.2.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f217c3954ce5fd88303fc0c317af55d5e0204106d86dea17eb8205700d47dec2"}, - {file = "Pillow-8.2.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5b70110acb39f3aff6b74cf09bb4169b167e2660dabc304c1e25b6555fa781ef"}, - {file = "Pillow-8.2.0-cp39-cp39-win32.whl", hash = "sha256:a7d5e9fad90eff8f6f6106d3b98b553a88b6f976e51fce287192a5d2d5363713"}, - {file = "Pillow-8.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:238c197fc275b475e87c1453b05b467d2d02c2915fdfdd4af126145ff2e4610c"}, - {file = "Pillow-8.2.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0e04d61f0064b545b989126197930807c86bcbd4534d39168f4aa5fda39bb8f9"}, - {file = "Pillow-8.2.0-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:63728564c1410d99e6d1ae8e3b810fe012bc440952168af0a2877e8ff5ab96b9"}, - {file = "Pillow-8.2.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:c03c07ed32c5324939b19e36ae5f75c660c81461e312a41aea30acdd46f93a7c"}, - {file = "Pillow-8.2.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:4d98abdd6b1e3bf1a1cbb14c3895226816e666749ac040c4e2554231068c639b"}, - {file = "Pillow-8.2.0-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:aac00e4bc94d1b7813fe882c28990c1bc2f9d0e1aa765a5f2b516e8a6a16a9e4"}, - {file = "Pillow-8.2.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:22fd0f42ad15dfdde6c581347eaa4adb9a6fc4b865f90b23378aa7914895e120"}, - {file = "Pillow-8.2.0-pp37-pypy37_pp73-win32.whl", hash = "sha256:e98eca29a05913e82177b3ba3d198b1728e164869c613d76d0de4bde6768a50e"}, - {file = "Pillow-8.2.0.tar.gz", hash = "sha256:a787ab10d7bb5494e5f76536ac460741788f1fbce851068d73a87ca7c35fc3e1"}, + {file = "Pillow-8.3.1-1-cp36-cp36m-win_amd64.whl", hash = "sha256:fd7eef578f5b2200d066db1b50c4aa66410786201669fb76d5238b007918fb24"}, + {file = "Pillow-8.3.1-1-cp37-cp37m-win_amd64.whl", hash = "sha256:75e09042a3b39e0ea61ce37e941221313d51a9c26b8e54e12b3ececccb71718a"}, + {file = "Pillow-8.3.1-1-cp38-cp38-win_amd64.whl", hash = "sha256:c0e0550a404c69aab1e04ae89cca3e2a042b56ab043f7f729d984bf73ed2a093"}, + {file = "Pillow-8.3.1-1-cp39-cp39-win_amd64.whl", hash = "sha256:479ab11cbd69612acefa8286481f65c5dece2002ffaa4f9db62682379ca3bb77"}, + {file = "Pillow-8.3.1-1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:f156d6ecfc747ee111c167f8faf5f4953761b5e66e91a4e6767e548d0f80129c"}, + {file = "Pillow-8.3.1-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:196560dba4da7a72c5e7085fccc5938ab4075fd37fe8b5468869724109812edd"}, + {file = "Pillow-8.3.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c9569049d04aaacd690573a0398dbd8e0bf0255684fee512b413c2142ab723"}, + {file = "Pillow-8.3.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c088a000dfdd88c184cc7271bfac8c5b82d9efa8637cd2b68183771e3cf56f04"}, + {file = "Pillow-8.3.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fc214a6b75d2e0ea7745488da7da3c381f41790812988c7a92345978414fad37"}, + {file = "Pillow-8.3.1-cp36-cp36m-win32.whl", hash = "sha256:a17ca41f45cf78c2216ebfab03add7cc350c305c38ff34ef4eef66b7d76c5229"}, + {file = "Pillow-8.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:67b3666b544b953a2777cb3f5a922e991be73ab32635666ee72e05876b8a92de"}, + {file = "Pillow-8.3.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:ff04c373477723430dce2e9d024c708a047d44cf17166bf16e604b379bf0ca14"}, + {file = "Pillow-8.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9364c81b252d8348e9cc0cb63e856b8f7c1b340caba6ee7a7a65c968312f7dab"}, + {file = "Pillow-8.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a2f381932dca2cf775811a008aa3027671ace723b7a38838045b1aee8669fdcf"}, + {file = "Pillow-8.3.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d0da39795049a9afcaadec532e7b669b5ebbb2a9134576ebcc15dd5bdae33cc0"}, + {file = "Pillow-8.3.1-cp37-cp37m-win32.whl", hash = "sha256:2b6dfa068a8b6137da34a4936f5a816aba0ecc967af2feeb32c4393ddd671cba"}, + {file = "Pillow-8.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a4eef1ff2d62676deabf076f963eda4da34b51bc0517c70239fafed1d5b51500"}, + {file = "Pillow-8.3.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:660a87085925c61a0dcc80efb967512ac34dbb256ff7dd2b9b4ee8dbdab58cf4"}, + {file = "Pillow-8.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:15a2808e269a1cf2131930183dcc0419bc77bb73eb54285dde2706ac9939fa8e"}, + {file = "Pillow-8.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:969cc558cca859cadf24f890fc009e1bce7d7d0386ba7c0478641a60199adf79"}, + {file = "Pillow-8.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2ee77c14a0299d0541d26f3d8500bb57e081233e3fa915fa35abd02c51fa7fae"}, + {file = "Pillow-8.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c11003197f908878164f0e6da15fce22373ac3fc320cda8c9d16e6bba105b844"}, + {file = "Pillow-8.3.1-cp38-cp38-win32.whl", hash = "sha256:3f08bd8d785204149b5b33e3b5f0ebbfe2190ea58d1a051c578e29e39bfd2367"}, + {file = "Pillow-8.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:70af7d222df0ff81a2da601fab42decb009dc721545ed78549cb96e3a1c5f0c8"}, + {file = "Pillow-8.3.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:37730f6e68bdc6a3f02d2079c34c532330d206429f3cee651aab6b66839a9f0e"}, + {file = "Pillow-8.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4bc3c7ef940eeb200ca65bd83005eb3aae8083d47e8fcbf5f0943baa50726856"}, + {file = "Pillow-8.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c35d09db702f4185ba22bb33ef1751ad49c266534339a5cebeb5159d364f6f82"}, + {file = "Pillow-8.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b2efa07f69dc395d95bb9ef3299f4ca29bcb2157dc615bae0b42c3c20668ffc"}, + {file = "Pillow-8.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cc866706d56bd3a7dbf8bac8660c6f6462f2f2b8a49add2ba617bc0c54473d83"}, + {file = "Pillow-8.3.1-cp39-cp39-win32.whl", hash = "sha256:9a211b663cf2314edbdb4cf897beeb5c9ee3810d1d53f0e423f06d6ebbf9cd5d"}, + {file = "Pillow-8.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:c2a5ff58751670292b406b9f06e07ed1446a4b13ffced6b6cab75b857485cbc8"}, + {file = "Pillow-8.3.1-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c379425c2707078dfb6bfad2430728831d399dc95a7deeb92015eb4c92345eaf"}, + {file = "Pillow-8.3.1-pp36-pypy36_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:114f816e4f73f9ec06997b2fde81a92cbf0777c9e8f462005550eed6bae57e63"}, + {file = "Pillow-8.3.1-pp36-pypy36_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8960a8a9f4598974e4c2aeb1bff9bdd5db03ee65fd1fce8adf3223721aa2a636"}, + {file = "Pillow-8.3.1-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:147bd9e71fb9dcf08357b4d530b5167941e222a6fd21f869c7911bac40b9994d"}, + {file = "Pillow-8.3.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1fd5066cd343b5db88c048d971994e56b296868766e461b82fa4e22498f34d77"}, + {file = "Pillow-8.3.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f4ebde71785f8bceb39dcd1e7f06bcc5d5c3cf48b9f69ab52636309387b097c8"}, + {file = "Pillow-8.3.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1c03e24be975e2afe70dfc5da6f187eea0b49a68bb2b69db0f30a61b7031cee4"}, + {file = "Pillow-8.3.1.tar.gz", hash = "sha256:2cac53839bfc5cece8fdbe7f084d5e3ee61e1303cccc86511d351adcb9e2c792"}, +] +platformdirs = [ + {file = "platformdirs-2.2.0-py3-none-any.whl", hash = "sha256:4666d822218db6a262bdfdc9c39d21f23b4cfdb08af331a81e92751daf6c866c"}, + {file = "platformdirs-2.2.0.tar.gz", hash = "sha256:632daad3ab546bd8e6af0537d09805cec458dce201bccfe23012df73332e181e"}, ] pre-commit = [ - {file = "pre_commit-2.12.1-py2.py3-none-any.whl", hash = "sha256:70c5ec1f30406250b706eda35e868b87e3e4ba099af8787e3e8b4b01e84f4712"}, - {file = "pre_commit-2.12.1.tar.gz", hash = "sha256:900d3c7e1bf4cf0374bb2893c24c23304952181405b4d88c9c40b72bda1bb8a9"}, + {file = "pre_commit-2.13.0-py2.py3-none-any.whl", hash = "sha256:b679d0fddd5b9d6d98783ae5f10fd0c4c59954f375b70a58cbe1ce9bcf9809a4"}, + {file = "pre_commit-2.13.0.tar.gz", hash = "sha256:764972c60693dc668ba8e86eb29654ec3144501310f7198742a767bec385a378"}, ] psutil = [ {file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"}, @@ -1189,8 +1229,8 @@ pyparsing = [ {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] python-dateutil = [ - {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, - {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, ] python-dotenv = [ {file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"}, @@ -1261,12 +1301,12 @@ typing-extensions = [ {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, ] urllib3 = [ - {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, - {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, + {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, + {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, ] virtualenv = [ - {file = "virtualenv-20.4.6-py2.py3-none-any.whl", hash = "sha256:307a555cf21e1550885c82120eccaf5acedf42978fd362d32ba8410f9593f543"}, - {file = "virtualenv-20.4.6.tar.gz", hash = "sha256:72cf267afc04bf9c86ec932329b7e94db6a0331ae9847576daaa7ca3c86b29a4"}, + {file = "virtualenv-20.7.0-py2.py3-none-any.whl", hash = "sha256:fdfdaaf0979ac03ae7f76d5224a05b58165f3c804f8aa633f3dd6f22fbd435d5"}, + {file = "virtualenv-20.7.0.tar.gz", hash = "sha256:97066a978431ec096d163e72771df5357c5c898ffdd587048f45e0aecc228094"}, ] yarl = [ {file = "yarl-1.6.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434"}, diff --git a/pyproject.toml b/pyproject.toml index de7fb2eb..d244d2b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ license = "MIT" [tool.poetry.dependencies] python = "^3.9" aiodns = "~=2.0" +aioredis = "~1.3" arrow = "~=1.1.0" fuzzywuzzy = "~=0.17" pillow = "~=8.1" -- cgit v1.2.3 From 8e653f4f6fc64ad78809e5ec1943ee38865bf221 Mon Sep 17 00:00:00 2001 From: wookie184 Date: Fri, 6 Aug 2021 16:34:19 +0100 Subject: Replace fuzzywuzzy with rapidfuzz --- bot/exts/evergreen/help.py | 4 +- bot/exts/evergreen/snakes/_converter.py | 2 +- bot/exts/evergreen/trivia_quiz.py | 2 +- bot/exts/pride/pride_leader.py | 2 +- poetry.lock | 88 +++++++++++++++++++++++++++------ pyproject.toml | 2 +- 6 files changed, 78 insertions(+), 22 deletions(-) diff --git a/bot/exts/evergreen/help.py b/bot/exts/evergreen/help.py index 3c9ba4d2..bfb5db17 100644 --- a/bot/exts/evergreen/help.py +++ b/bot/exts/evergreen/help.py @@ -8,7 +8,7 @@ from typing import List, NamedTuple, Union from discord import Colour, Embed, HTTPException, Message, Reaction, User from discord.ext import commands from discord.ext.commands import CheckFailure, Cog as DiscordCog, Command, Context -from fuzzywuzzy import fuzz, process +from rapidfuzz import process from bot import constants from bot.bot import Bot @@ -159,7 +159,7 @@ class HelpSession: # Combine command and cog names choices = list(self._bot.all_commands) + list(self._bot.cogs) - result = process.extractBests(query, choices, scorer=fuzz.ratio, score_cutoff=90) + result = process.extract(query, choices, score_cutoff=90) raise HelpQueryNotFound(f'Query "{query}" not found.', dict(result)) diff --git a/bot/exts/evergreen/snakes/_converter.py b/bot/exts/evergreen/snakes/_converter.py index 26bde611..c8d1909b 100644 --- a/bot/exts/evergreen/snakes/_converter.py +++ b/bot/exts/evergreen/snakes/_converter.py @@ -5,7 +5,7 @@ from typing import Iterable, List import discord from discord.ext.commands import Context, Converter -from fuzzywuzzy import fuzz +from rapidfuzz import fuzz from bot.exts.evergreen.snakes._utils import SNAKE_RESOURCES from bot.utils import disambiguate diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 28924aed..bc25cbf7 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -9,7 +9,7 @@ from typing import Callable, List, Optional import discord from discord.ext import commands -from fuzzywuzzy import fuzz +from rapidfuzz import fuzz from bot.bot import Bot from bot.constants import Colours, NEGATIVE_REPLIES, Roles diff --git a/bot/exts/pride/pride_leader.py b/bot/exts/pride/pride_leader.py index c3426ad1..8e88183b 100644 --- a/bot/exts/pride/pride_leader.py +++ b/bot/exts/pride/pride_leader.py @@ -6,7 +6,7 @@ from typing import Optional import discord from discord.ext import commands -from fuzzywuzzy import fuzz +from rapidfuzz import fuzz from bot import bot from bot import constants diff --git a/poetry.lock b/poetry.lock index 861dca0b..64709d7a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -316,17 +316,6 @@ python-versions = "*" [package.dependencies] pycodestyle = ">=2.0.0,<3.0.0" -[[package]] -name = "fuzzywuzzy" -version = "0.18.0" -description = "Fuzzy string matching in python" -category = "main" -optional = false -python-versions = "*" - -[package.extras] -speedup = ["python-levenshtein (>=0.12)"] - [[package]] name = "hiredis" version = "2.0.0" @@ -567,6 +556,14 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +[[package]] +name = "rapidfuzz" +version = "1.4.1" +description = "rapid fuzzy string matching" +category = "main" +optional = false +python-versions = ">=3.5" + [[package]] name = "redis" version = "3.5.3" @@ -707,7 +704,7 @@ multidict = ">=4.0" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "a73732d7a2ea8348dd9e38b87677eeab0ece557ba5f56a24a0bed88c07f4d78d" +content-hash = "a62da963535ba0b679739b026c6d86f6b2c1993b50e81c06d7d89f63507b9aa1" [metadata.files] aiodns = [ @@ -899,10 +896,6 @@ flake8-tidy-imports = [ flake8-todo = [ {file = "flake8-todo-0.7.tar.gz", hash = "sha256:6e4c5491ff838c06fe5a771b0e95ee15fc005ca57196011011280fc834a85915"}, ] -fuzzywuzzy = [ - {file = "fuzzywuzzy-0.18.0-py2.py3-none-any.whl", hash = "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993"}, - {file = "fuzzywuzzy-0.18.0.tar.gz", hash = "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8"}, -] hiredis = [ {file = "hiredis-2.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b4c8b0bc5841e578d5fb32a16e0c305359b987b850a06964bd5a62739d688048"}, {file = "hiredis-2.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0adea425b764a08270820531ec2218d0508f8ae15a448568109ffcae050fee26"}, @@ -1267,6 +1260,69 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] +rapidfuzz = [ + {file = "rapidfuzz-1.4.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:72878878d6744883605b5453c382361716887e9e552f677922f76d93d622d8cb"}, + {file = "rapidfuzz-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:56a67a5b3f783e9af73940f6945366408b3a2060fc6ab18466e5a2894fd85617"}, + {file = "rapidfuzz-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f5d396b64f8ae3a793633911a1fb5d634ac25bf8f13d440139fa729131be42d8"}, + {file = "rapidfuzz-1.4.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:4990698233e7eda7face7c09f5874a09760c7524686045cbb10317e3a7f3225f"}, + {file = "rapidfuzz-1.4.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a87e212855b18a951e79ec71d71dbd856d98cd2019d0c2bd46ec30688a8aa68a"}, + {file = "rapidfuzz-1.4.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1897d2ef03f5b51bc19bdb2d0398ae968766750fa319843733f0a8f12ddde986"}, + {file = "rapidfuzz-1.4.1-cp35-cp35m-manylinux2014_ppc64le.whl", hash = "sha256:e1fc4fd219057f5f1fa40bb9bc5e880f8ef45bf19350d4f5f15ca2ce7f61c99b"}, + {file = "rapidfuzz-1.4.1-cp35-cp35m-manylinux2014_s390x.whl", hash = "sha256:21300c4d048798985c271a8bf1ed1611902ebd4479fcacda1a3eaaebbad2f744"}, + {file = "rapidfuzz-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:d2659967c6ac74211a87a1109e79253e4bc179641057c64800ef4e2dc0534fdb"}, + {file = "rapidfuzz-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:26ac4bfe564c516e053fc055f1543d2b2433338806738c7582e1f75ed0485f7e"}, + {file = "rapidfuzz-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3b485c98ad1ce3c04556f65aaab5d6d6d72121cde656d43505169c71ae956476"}, + {file = "rapidfuzz-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:59db06356eaf22c83f44b0dded964736cbb137291cdf2cf7b4974c0983b94932"}, + {file = "rapidfuzz-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fef95249af9a535854b617a68788c38cd96308d97ee14d44bc598cc73e986167"}, + {file = "rapidfuzz-1.4.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:7d8c186e8270e103d339b26ef498581cf3178470ccf238dfd5fd0e47d80e4c7d"}, + {file = "rapidfuzz-1.4.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:9246b9c5c8992a83a08ac7813c8bbff2e674ad0b681f9b3fb1ec7641eff6c21f"}, + {file = "rapidfuzz-1.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f58c17f7a82b1bcc2ce304942cae14287223e6b6eead7071241273da7d9b9770"}, + {file = "rapidfuzz-1.4.1-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:ed708620b23a09ac52eaaec0761943c1bbc9a62d19ecd2feb4da8c3f79ef9d37"}, + {file = "rapidfuzz-1.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:bdec9ae5fd8a8d4d8813b4aac3505c027b922b4033a32a7aab66a9b2f03a7b47"}, + {file = "rapidfuzz-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:fc668fd706ad1162ce14f26ca2957b4690d47770d23609756536c918a855ced0"}, + {file = "rapidfuzz-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f9f35df5dd9b02669ff6b1d4a386607ff56982c86a7e57d95eb08c6afbab4ddd"}, + {file = "rapidfuzz-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8427310ea29ce2968e1c6f6779ae5a458b3a4984f9150fc4d16f92b96456f848"}, + {file = "rapidfuzz-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1430dc745476e3798742ad835f61f6e6bf5d3e9a22cf9cd0288b28b7440a9872"}, + {file = "rapidfuzz-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1d20311da611c8f4638a09e2bc5e04b327bae010cb265ef9628d9c13c6d5da7b"}, + {file = "rapidfuzz-1.4.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7881965e428cf6fe248d6e702e6d5857da02278ab9b21313bee717c080e443e"}, + {file = "rapidfuzz-1.4.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f76c965f15861ec4d39e904bd65b84a39121334439ac17bfb8b900d1e6779a93"}, + {file = "rapidfuzz-1.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:61167f989415e701ac379de247e6b0a21ea62afc86c54d8a79f485b4f0173c02"}, + {file = "rapidfuzz-1.4.1-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:645cfb9456229f0bd5752b3eda69f221d825fbb8cbb8855433516bc185111506"}, + {file = "rapidfuzz-1.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:c28be57c9bc47b3d7f484340fab1bec8ed4393dee1090892c2774a4584435eb8"}, + {file = "rapidfuzz-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:3c94b6d3513c693f253ff762112cc4580d3bd377e4abacb96af31a3d606fbe14"}, + {file = "rapidfuzz-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:506d50a066451502ee2f8bf016bc3ba3e3b04eede7a4059d7956248e2dd96179"}, + {file = "rapidfuzz-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:80b375098658bb3db14215a975d354f6573d3943ac2ae0c4627c7760d57ce075"}, + {file = "rapidfuzz-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ba8f7cbd8fdbd3ae115f4484888f3cb94bc2ac7cbd4eb1ca95a3d4f874261ff8"}, + {file = "rapidfuzz-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5fa8570720b0fdfc52f24f5663d66c52ea88ba19cb8b1ff6a39a8bc0b925b33b"}, + {file = "rapidfuzz-1.4.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:f35c8a4c690447fd335bfd77df4da42dfea37cfa06a8ecbf22543d86dc720e12"}, + {file = "rapidfuzz-1.4.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:27f9eef48e212d73e78f0f5ceedc62180b68f6a25fa0752d2ccfaedc3a840bec"}, + {file = "rapidfuzz-1.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:31e99216e2a04aec4f281d472b28a683921f1f669a429cf605d11526623eaeed"}, + {file = "rapidfuzz-1.4.1-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:f22bf7ba6eddd59764457f74c637ab5c3ed976c5fcfaf827e1d320cc0478e12b"}, + {file = "rapidfuzz-1.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:c43ddb354abd00e56f024ce80affb3023fa23206239bb81916d5877cba7f2d1e"}, + {file = "rapidfuzz-1.4.1-cp38-cp38-win32.whl", hash = "sha256:62c1f4ac20c8019ce8d481fb27235306ef3912a8d0b9a60b17905699f43ff072"}, + {file = "rapidfuzz-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:2963f356c70b710dc6337b012ec976ce2fc2b81c2a9918a686838fead6eb4e1d"}, + {file = "rapidfuzz-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c07f301fd549b266410654850c6918318d7dcde8201350e9ac0819f0542cf147"}, + {file = "rapidfuzz-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa4c8b6fc7e93e3a3fb9be9566f1fe7ef920735eadcee248a0d70f3ca8941341"}, + {file = "rapidfuzz-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c200bd813bbd3b146ba0fd284a9ad314bbad9d95ed542813273bdb9d0ee4e796"}, + {file = "rapidfuzz-1.4.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2cccc84e1f0c6217747c09cafe93164e57d3644e18a334845a2dfbdd2073cd2c"}, + {file = "rapidfuzz-1.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f2033e3d61d1e498f618123b54dc7436d50510b0d18fd678d867720e8d7b2f23"}, + {file = "rapidfuzz-1.4.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:26b7f48b3ddd9d97cf8482a88f0f6cba47ac13ff16e63386ea7ce06178174770"}, + {file = "rapidfuzz-1.4.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:bf18614f87fe3bfff783f0a3d0fad0eb59c92391e52555976e55570a651d2330"}, + {file = "rapidfuzz-1.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8cb5c2502ff06028a1468bdf61323b53cc3a37f54b5d62d62c5371795b81086a"}, + {file = "rapidfuzz-1.4.1-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:f37f80c1541d6e0a30547261900086b8c0bac519ebc12c9cd6b61a9a43a7e195"}, + {file = "rapidfuzz-1.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:c13cd1e840aa93639ac1d131fbfa740a609fd20dfc2a462d5cd7bce747a2398d"}, + {file = "rapidfuzz-1.4.1-cp39-cp39-win32.whl", hash = "sha256:0ec346f271e96c485716c091c8b0b78ba52da33f7c6ebb52a349d64094566c2d"}, + {file = "rapidfuzz-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:5208ce1b1989a10e6fc5b5ef5d0bb7d1ffe5408838f3106abde241aff4dab08c"}, + {file = "rapidfuzz-1.4.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4fa195ea9ca35bacfa2a4319c6d4ab03aa6a283ad2089b70d2dfa0f6a7d9c1bc"}, + {file = "rapidfuzz-1.4.1-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:6e336cfd8103b0b38e107e01502e9d6bf7c7f04e49b970fb11a4bf6c7a932b94"}, + {file = "rapidfuzz-1.4.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:c798c5b87efe8a7e63f408e07ff3bc03ba8b94f4498a89b48eaab3a9f439d52c"}, + {file = "rapidfuzz-1.4.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:bb16a10b40f5bd3c645f7748fbd36f49699a03f550c010a2c665905cc8937de8"}, + {file = "rapidfuzz-1.4.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2278001924031d9d75f821bff2c5fef565c8376f252562e04d8eec8857475c36"}, + {file = "rapidfuzz-1.4.1-pp37-pypy37_pp73-manylinux1_x86_64.whl", hash = "sha256:a89d11f3b5da35fdf3e839186203b9367d56e2be792e8dccb098f47634ec6eb9"}, + {file = "rapidfuzz-1.4.1-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:f8c79cd11b4778d387366a59aa747f5268433f9d68be37b00d16f4fb08fdf850"}, + {file = "rapidfuzz-1.4.1-pp37-pypy37_pp73-win32.whl", hash = "sha256:4364db793ed4b439f9dd28a335bee14e2a828283d3b93c2d2686cc645eeafdd5"}, + {file = "rapidfuzz-1.4.1.tar.gz", hash = "sha256:de20550178376d21bfe1b34a7dc42ab107bb282ef82069cf6dfe2805a0029e26"}, +] redis = [ {file = "redis-3.5.3-py2.py3-none-any.whl", hash = "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"}, {file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"}, diff --git a/pyproject.toml b/pyproject.toml index d244d2b0..293d4e12 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,8 +9,8 @@ license = "MIT" python = "^3.9" aiodns = "~=2.0" aioredis = "~1.3" +rapidfuzz = "~=1.4" arrow = "~=1.1.0" -fuzzywuzzy = "~=0.17" pillow = "~=8.1" sentry-sdk = "~=0.19" PyYAML = "~=5.4" -- cgit v1.2.3 From fac37b213aad8ce57ff1ccd407669c1ff22c36ba Mon Sep 17 00:00:00 2001 From: wookie184 Date: Fri, 6 Aug 2021 16:34:37 +0100 Subject: Modify code to comply with new linter error N818 --- bot/exts/christmas/advent_of_code/_helpers.py | 8 ++++---- bot/utils/pagination.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bot/exts/christmas/advent_of_code/_helpers.py b/bot/exts/christmas/advent_of_code/_helpers.py index 96de90c4..e26a17ca 100644 --- a/bot/exts/christmas/advent_of_code/_helpers.py +++ b/bot/exts/christmas/advent_of_code/_helpers.py @@ -67,7 +67,7 @@ class UnexpectedResponseStatus(aiohttp.ClientError): """Raised when an unexpected redirect was detected.""" -class FetchingLeaderboardFailed(Exception): +class FetchingLeaderboardFailedError(Exception): """Raised when one or more leaderboards could not be fetched at all.""" @@ -210,7 +210,7 @@ async def _fetch_leaderboard_data() -> typing.Dict[str, typing.Any]: except UnexpectedRedirect: if cookies["session"] == AdventOfCode.fallback_session: log.error("It seems like the fallback cookie has expired!") - raise FetchingLeaderboardFailed from None + raise FetchingLeaderboardFailedError from None # If we're here, it means that the original session did not # work. Let's fall back to the fallback session. @@ -218,7 +218,7 @@ async def _fetch_leaderboard_data() -> typing.Dict[str, typing.Any]: continue except aiohttp.ClientError: # Don't retry, something unexpected is wrong and it may not be the session. - raise FetchingLeaderboardFailed from None + raise FetchingLeaderboardFailedError from None else: # Get the participants and store their current count. board_participants = raw_data["members"] @@ -227,7 +227,7 @@ async def _fetch_leaderboard_data() -> typing.Dict[str, typing.Any]: break else: log.error(f"reached 'unreachable' state while fetching board `{leaderboard.id}`.") - raise FetchingLeaderboardFailed + raise FetchingLeaderboardFailedError log.info(f"Fetched leaderboard information for {len(participants)} participants") return participants diff --git a/bot/utils/pagination.py b/bot/utils/pagination.py index d9c0862a..b1062c09 100644 --- a/bot/utils/pagination.py +++ b/bot/utils/pagination.py @@ -20,7 +20,7 @@ PAGINATION_EMOJI = (FIRST_EMOJI, LEFT_EMOJI, RIGHT_EMOJI, LAST_EMOJI, DELETE_EMO log = logging.getLogger(__name__) -class EmptyPaginatorEmbed(Exception): +class EmptyPaginatorEmbedError(Exception): """Base Exception class for an empty paginator embed.""" @@ -141,7 +141,7 @@ class LinePaginator(Paginator): if not lines: if exception_on_empty_embed: log.exception("Pagination asked for empty lines iterable") - raise EmptyPaginatorEmbed("No lines to paginate") + raise EmptyPaginatorEmbedError("No lines to paginate") log.debug("No lines to add to paginator, adding '(nothing to display)' message") lines.append("(nothing to display)") @@ -349,7 +349,7 @@ class ImagePaginator(Paginator): if not pages: if exception_on_empty_embed: log.exception("Pagination asked for empty image list") - raise EmptyPaginatorEmbed("No images to paginate") + raise EmptyPaginatorEmbedError("No images to paginate") log.debug("No images to add to paginator, adding '(no images to display)' message") pages.append(("(no images to display)", "")) -- cgit v1.2.3 From 9ffd7c89fcf0f9b498b7816407fe67dfc8a80dcb Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Sat, 7 Aug 2021 20:03:58 +0200 Subject: Update the movie cog to use aiohttp get params --- bot/exts/evergreen/movie.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index 10638aea..82fd50bc 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -2,7 +2,6 @@ import logging import random from enum import Enum from typing import Any, Dict, List, Tuple -from urllib.parse import urlencode from aiohttp import ClientSession from discord import Embed @@ -121,10 +120,10 @@ class Movie(Cog): "with_genres": genre_id } - url = BASE_URL + "discover/movie?" + urlencode(params) + url = BASE_URL + "discover/movie" # Make discover request to TMDB, return result - async with client.get(url) as resp: + async with client.get(url, params=params) as resp: return await resp.json() async def get_pages(self, client: ClientSession, movies: Dict[str, Any], amount: int) -> List[Tuple[str, str]]: @@ -142,9 +141,9 @@ class Movie(Cog): async def get_movie(self, client: ClientSession, movie: int) -> Dict: """Get Movie by movie ID from TMDB. Return result dictionary.""" - url = BASE_URL + f"movie/{movie}?" + urlencode(MOVIE_PARAMS) + url = BASE_URL + f"movie/{movie}" - async with client.get(url) as resp: + async with client.get(url, params=MOVIE_PARAMS) as resp: return await resp.json() async def create_page(self, movie: Dict[str, Any]) -> Tuple[str, str]: -- cgit v1.2.3 From 8532b4099baefb7000cb467fafc94688c1c04db9 Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Sat, 7 Aug 2021 20:54:48 +0200 Subject: Update the Wikipedia Cog to use aiohttp get params --- bot/exts/evergreen/wikipedia.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index 83937438..28bbf0de 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -13,9 +13,18 @@ from bot.utils import LinePaginator log = logging.getLogger(__name__) SEARCH_API = ( - "https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info&inprop=url&utf8=&" - "format=json&origin=*&srlimit={number_of_results}&srsearch={string}" + "https://en.wikipedia.org/w/api.php" ) +WIKI_PARAMS = { + "action": "query", + "list": "search", + "prop": "info", + "inprop": "url", + "utf8": "", + "format": "json", + "origin": "*", + +} WIKI_THUMBNAIL = ( "https://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg" "/330px-Wikipedia-logo-v2.svg.png" @@ -35,8 +44,8 @@ class WikipediaSearch(commands.Cog): async def wiki_request(self, channel: TextChannel, search: str) -> Optional[List[str]]: """Search wikipedia search string and return formatted first 10 pages found.""" - url = SEARCH_API.format(number_of_results=10, string=search) - async with self.bot.http_session.get(url=url) as resp: + params = dict(WIKI_PARAMS, srlimit=10, srsearch=search) + async with self.bot.http_session.get(url=SEARCH_API, params=params) as resp: if resp.status == 200: raw_data = await resp.json() number_of_results = raw_data["query"]["searchinfo"]["totalhits"] -- cgit v1.2.3 From fd33adf092014367790717a656e40c535c5cd33e Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Sat, 7 Aug 2021 21:14:20 +0200 Subject: Use quote_plus instead of quote in the Githubinfo Cog --- bot/exts/evergreen/githubinfo.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 27e607e5..b0eff896 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -1,7 +1,7 @@ import logging import random from datetime import datetime -from urllib.parse import quote +from urllib.parse import quote_plus import discord from discord.ext import commands @@ -37,7 +37,7 @@ class GithubInfo(commands.Cog): async def github_user_info(self, ctx: commands.Context, username: str) -> None: """Fetches a user's GitHub information.""" async with ctx.typing(): - user_data = await self.fetch_data(f"{GITHUB_API_URL}/users/{username}") + user_data = await self.fetch_data(f"{GITHUB_API_URL}/users/{quote_plus(username)}") # User_data will not have a message key if the user exists if "message" in user_data: @@ -91,7 +91,8 @@ class GithubInfo(commands.Cog): ) if user_data["type"] == "User": - embed.add_field(name="Gists", value=f"[{gists}](https://gist.github.com/{quote(username, safe='')})") + embed.add_field(name="Gists", value=f"[{gists}](https://gist.github.com/{quote_plus(username, safe='')}" + f")") embed.add_field( name=f"Organization{'s' if len(orgs)!=1 else ''}", @@ -120,7 +121,7 @@ class GithubInfo(commands.Cog): return async with ctx.typing(): - repo_data = await self.fetch_data(f"{GITHUB_API_URL}/repos/{quote(repo)}") + repo_data = await self.fetch_data(f"{GITHUB_API_URL}/repos/{quote_plus(repo)}") # There won't be a message key if this repo exists if "message" in repo_data: -- cgit v1.2.3 From 7adb1819668cd95e09c38bb98374b322fad0848d Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Sat, 7 Aug 2021 22:19:29 +0200 Subject: Update Hacktoberstats' get_october_prs function to use aiohttps get params --- bot/exts/halloween/hacktoberstats.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 50d3aaf6..b5d8591d 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -4,6 +4,7 @@ import re from collections import Counter from datetime import datetime, timedelta from typing import List, Optional, Tuple, Union +from urllib.parse import quote_plus import discord from async_rediscache import RedisCache @@ -208,24 +209,24 @@ class HacktoberStats(commands.Cog): None will be returned when the GitHub user was not found. """ log.info(f"Fetching Hacktoberfest Stats for GitHub user: '{github_username}'") - base_url = "https://api.github.com/search/issues?q=" + base_url = "https://api.github.com/search/issues" action_type = "pr" is_query = "public" not_query = "draft" date_range = f"{CURRENT_YEAR}-09-30T10:00Z..{CURRENT_YEAR}-11-01T12:00Z" per_page = "300" - query_url = ( - f"{base_url}" + query_params = ( f"+type:{action_type}" f"+is:{is_query}" - f"+author:{github_username}" + f"+author:{quote_plus(github_username)}" f"+-is:{not_query}" f"+created:{date_range}" f"&per_page={per_page}" ) - log.debug(f"GitHub query URL generated: {query_url}") - jsonresp = await self._fetch_url(query_url, REQUEST_HEADERS) + log.debug(f"GitHub query parameters generated: {query_params}") + + jsonresp = await self._fetch_url(base_url, REQUEST_HEADERS, dict(q=query_params)) if "message" in jsonresp: # One of the parameters is invalid, short circuit for now api_message = jsonresp["errors"][0]["message"] @@ -295,9 +296,9 @@ class HacktoberStats(commands.Cog): outlist.append(itemdict) return outlist - async def _fetch_url(self, url: str, headers: dict) -> dict: + async def _fetch_url(self, url: str, headers: dict, params: dict) -> dict: """Retrieve API response from URL.""" - async with self.bot.http_session.get(url, headers=headers) as resp: + async with self.bot.http_session.get(url, headers=headers, params=params) as resp: return await resp.json() @staticmethod -- cgit v1.2.3 From 22abc00742a610443b2cffe3ae609056a98da713 Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Sat, 7 Aug 2021 23:02:18 +0200 Subject: Improve code consistency in Movie, Wikipedia and Hactoberstats Cogs --- bot/exts/evergreen/movie.py | 2 ++ bot/exts/evergreen/wikipedia.py | 2 +- bot/exts/halloween/hacktoberstats.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index 82fd50bc..c6af4bcd 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -141,6 +141,8 @@ class Movie(Cog): async def get_movie(self, client: ClientSession, movie: int) -> Dict: """Get Movie by movie ID from TMDB. Return result dictionary.""" + if not isinstance(movie, int): + raise ValueError("Error while fetching movie from TMDB, movie argument must be integer. ") url = BASE_URL + f"movie/{movie}" async with client.get(url, params=MOVIE_PARAMS) as resp: diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index 28bbf0de..7b96cb7b 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -44,7 +44,7 @@ class WikipediaSearch(commands.Cog): async def wiki_request(self, channel: TextChannel, search: str) -> Optional[List[str]]: """Search wikipedia search string and return formatted first 10 pages found.""" - params = dict(WIKI_PARAMS, srlimit=10, srsearch=search) + params = WIKI_PARAMS | {"srlimit": 10, "srsearch": search} async with self.bot.http_session.get(url=SEARCH_API, params=params) as resp: if resp.status == 200: raw_data = await resp.json() diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index b5d8591d..24106a5e 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -226,7 +226,7 @@ class HacktoberStats(commands.Cog): log.debug(f"GitHub query parameters generated: {query_params}") - jsonresp = await self._fetch_url(base_url, REQUEST_HEADERS, dict(q=query_params)) + jsonresp = await self._fetch_url(base_url, REQUEST_HEADERS, {"q": query_params}) if "message" in jsonresp: # One of the parameters is invalid, short circuit for now api_message = jsonresp["errors"][0]["message"] -- cgit v1.2.3 From 45f6ff5d97d79638418f17617edac36fac0bdf9a Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Sat, 7 Aug 2021 23:27:36 +0200 Subject: Improve code indentation in the Githubinfo Cog --- bot/exts/evergreen/githubinfo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index b0eff896..7bc1288d 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -91,8 +91,8 @@ class GithubInfo(commands.Cog): ) if user_data["type"] == "User": - embed.add_field(name="Gists", value=f"[{gists}](https://gist.github.com/{quote_plus(username, safe='')}" - f")") + embed.add_field(name="Gists", value=f"[{gists}](https://gist.github.com/" + f"{quote_plus(username, safe='')}"f")") embed.add_field( name=f"Organization{'s' if len(orgs)!=1 else ''}", -- cgit v1.2.3 From e888b345deae3025704771e53494e694ce146fe2 Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Sat, 7 Aug 2021 23:50:25 +0200 Subject: Fine tune indenting in GithubInfo cog --- bot/exts/evergreen/githubinfo.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 7bc1288d..012cc274 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -91,8 +91,10 @@ class GithubInfo(commands.Cog): ) if user_data["type"] == "User": - embed.add_field(name="Gists", value=f"[{gists}](https://gist.github.com/" - f"{quote_plus(username, safe='')}"f")") + embed.add_field( + name="Gists", + value=f"[{gists}](https://gist.github.com/"f"{quote_plus(username, safe='')}"f")" + ) embed.add_field( name=f"Organization{'s' if len(orgs)!=1 else ''}", -- cgit v1.2.3 From a412cfe18ecb7845dafae8cedfdd6558a1b4dae4 Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Sun, 8 Aug 2021 00:45:08 +0200 Subject: Update Stackoverflow, Wolfram, movie_generator cogs to use aiohttp get params --- bot/exts/evergreen/githubinfo.py | 2 +- bot/exts/evergreen/stackoverflow.py | 13 +++++++++---- bot/exts/evergreen/wolfram.py | 34 +++++++++++++++++----------------- bot/exts/valentines/movie_generator.py | 5 ++--- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 012cc274..15f6154c 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -93,7 +93,7 @@ class GithubInfo(commands.Cog): if user_data["type"] == "User": embed.add_field( name="Gists", - value=f"[{gists}](https://gist.github.com/"f"{quote_plus(username, safe='')}"f")" + value=f"[{gists}](https://gist.github.com/{quote_plus(username, safe='')})" ) embed.add_field( diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index a53cecf7..14d20e79 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -10,7 +10,12 @@ from bot.constants import Colours, Emojis logger = logging.getLogger(__name__) -BASE_URL = "https://api.stackexchange.com/2.2/search/advanced?order=desc&sort=activity&site=stackoverflow&q={query}" +BASE_URL = "https://api.stackexchange.com/2.2/search/advanced" +STACKOW_PARAMS = { + "order": "desc", + "sort": "activity", + "site": "stackoverflow" +} SEARCH_URL = "https://stackoverflow.com/search?q={query}" ERR_EMBED = Embed( title="Error in fetching results from Stackoverflow", @@ -32,9 +37,8 @@ class Stackoverflow(commands.Cog): @commands.cooldown(1, 15, commands.cooldowns.BucketType.user) async def stackoverflow(self, ctx: commands.Context, *, search_query: str) -> None: """Sends the top 5 results of a search query from stackoverflow.""" - encoded_search_query = quote_plus(search_query) - - async with self.bot.http_session.get(BASE_URL.format(query=encoded_search_query)) as response: + params = STACKOW_PARAMS | {"q": search_query} + async with self.bot.http_session.get(url=BASE_URL, params=params) as response: if response.status == 200: data = await response.json() else: @@ -50,6 +54,7 @@ class Stackoverflow(commands.Cog): return top5 = data["items"][:5] + encoded_search_query = quote_plus(search_query) embed = Embed( title="Search results - Stackoverflow", url=SEARCH_URL.format(query=encoded_search_query), diff --git a/bot/exts/evergreen/wolfram.py b/bot/exts/evergreen/wolfram.py index d23afd6f..26674d37 100644 --- a/bot/exts/evergreen/wolfram.py +++ b/bot/exts/evergreen/wolfram.py @@ -1,7 +1,7 @@ import logging from io import BytesIO from typing import Callable, List, Optional, Tuple -from urllib import parse +from urllib.parse import urlencode import arrow import discord @@ -17,7 +17,7 @@ log = logging.getLogger(__name__) APPID = Wolfram.key DEFAULT_OUTPUT_FORMAT = "JSON" -QUERY = "http://api.wolframalpha.com/v2/{request}?{data}" +QUERY = "http://api.wolframalpha.com/v2/{request}" WOLF_IMAGE = "https://www.symbols.com/gi.php?type=1&id=2886&i=1" MAX_PODS = 20 @@ -108,7 +108,7 @@ def custom_cooldown(*ignore: List[int]) -> Callable: async def get_pod_pages(ctx: Context, bot: Bot, query: str) -> Optional[List[Tuple]]: """Get the Wolfram API pod pages for the provided query.""" async with ctx.typing(): - url_str = parse.urlencode({ + params = { "input": query, "appid": APPID, "output": DEFAULT_OUTPUT_FORMAT, @@ -116,27 +116,27 @@ async def get_pod_pages(ctx: Context, bot: Bot, query: str) -> Optional[List[Tup "location": "the moon", "latlong": "0.0,0.0", "ip": "1.1.1.1" - }) - request_url = QUERY.format(request="query", data=url_str) + } + request_url = QUERY.format(request="query") - async with bot.http_session.get(request_url) as response: + async with bot.http_session.get(url=request_url, params=params) as response: json = await response.json(content_type="text/plain") result = json["queryresult"] - + log_full_url = f"{request_url}?{urlencode(params)}" if result["error"]: # API key not set up correctly if result["error"]["msg"] == "Invalid appid": message = "Wolfram API key is invalid or missing." log.warning( "API key seems to be missing, or invalid when " - f"processing a wolfram request: {url_str}, Response: {json}" + f"processing a wolfram request: {log_full_url}, Response: {json}" ) await send_embed(ctx, message) return message = "Something went wrong internally with your request, please notify staff!" - log.warning(f"Something went wrong getting a response from wolfram: {url_str}, Response: {json}") + log.warning(f"Something went wrong getting a response from wolfram: {log_full_url}, Response: {json}") await send_embed(ctx, message) return @@ -172,18 +172,18 @@ class Wolfram(Cog): @custom_cooldown(*STAFF_ROLES) async def wolfram_command(self, ctx: Context, *, query: str) -> None: """Requests all answers on a single image, sends an image of all related pods.""" - url_str = parse.urlencode({ + params = { "i": query, "appid": APPID, "location": "the moon", "latlong": "0.0,0.0", "ip": "1.1.1.1" - }) - query = QUERY.format(request="simple", data=url_str) + } + request_url = QUERY.format(request="simple") # Give feedback that the bot is working. async with ctx.typing(): - async with self.bot.http_session.get(query) as response: + async with self.bot.http_session.get(url=request_url, params=params) as response: status = response.status image_bytes = await response.read() @@ -257,18 +257,18 @@ class Wolfram(Cog): @custom_cooldown(*STAFF_ROLES) async def wolfram_short_command(self, ctx: Context, *, query: str) -> None: """Requests an answer to a simple question.""" - url_str = parse.urlencode({ + params = { "i": query, "appid": APPID, "location": "the moon", "latlong": "0.0,0.0", "ip": "1.1.1.1" - }) - query = QUERY.format(request="result", data=url_str) + } + request_url = QUERY.format(request="result") # Give feedback that the bot is working. async with ctx.typing(): - async with self.bot.http_session.get(query) as response: + async with self.bot.http_session.get(url=request_url, params=params) as response: status = response.status response_text = await response.text() diff --git a/bot/exts/valentines/movie_generator.py b/bot/exts/valentines/movie_generator.py index 0fc5edb4..d2dc8213 100644 --- a/bot/exts/valentines/movie_generator.py +++ b/bot/exts/valentines/movie_generator.py @@ -1,7 +1,6 @@ import logging import random from os import environ -from urllib import parse import discord from discord.ext import commands @@ -35,8 +34,8 @@ class RomanceMovieFinder(commands.Cog): "with_genres": "10749" } # The api request url - request_url = "https://api.themoviedb.org/3/discover/movie?" + parse.urlencode(params) - async with self.bot.http_session.get(request_url) as resp: + request_url = "https://api.themoviedb.org/3/discover/movie" + async with self.bot.http_session.get(request_url, params=params) as resp: # Trying to load the json file returned from the api try: data = await resp.json() -- cgit v1.2.3 From 70d7fc09e875412ae295ee8c4d746c467e73abca Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Sun, 8 Aug 2021 01:15:12 +0200 Subject: Upgrade code transparency in the StackOverflow Cog --- bot/exts/evergreen/stackoverflow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/stackoverflow.py b/bot/exts/evergreen/stackoverflow.py index 14d20e79..40f149c9 100644 --- a/bot/exts/evergreen/stackoverflow.py +++ b/bot/exts/evergreen/stackoverflow.py @@ -11,7 +11,7 @@ from bot.constants import Colours, Emojis logger = logging.getLogger(__name__) BASE_URL = "https://api.stackexchange.com/2.2/search/advanced" -STACKOW_PARAMS = { +SO_PARAMS = { "order": "desc", "sort": "activity", "site": "stackoverflow" @@ -37,7 +37,7 @@ class Stackoverflow(commands.Cog): @commands.cooldown(1, 15, commands.cooldowns.BucketType.user) async def stackoverflow(self, ctx: commands.Context, *, search_query: str) -> None: """Sends the top 5 results of a search query from stackoverflow.""" - params = STACKOW_PARAMS | {"q": search_query} + params = SO_PARAMS | {"q": search_query} async with self.bot.http_session.get(url=BASE_URL, params=params) as response: if response.status == 200: data = await response.json() -- cgit v1.2.3 From ad6db1d554e25420314901291b7e2aa6e7bc4ca6 Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Sun, 8 Aug 2021 02:44:49 +0200 Subject: Update GithubInfo cog encode mechaninsm Update Githubinfo to use quote instead of quote_plus when fetching repository info. --- bot/exts/evergreen/githubinfo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 15f6154c..d29f3aa9 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -1,7 +1,7 @@ import logging import random from datetime import datetime -from urllib.parse import quote_plus +from urllib.parse import quote, quote_plus import discord from discord.ext import commands @@ -123,7 +123,7 @@ class GithubInfo(commands.Cog): return async with ctx.typing(): - repo_data = await self.fetch_data(f"{GITHUB_API_URL}/repos/{quote_plus(repo)}") + repo_data = await self.fetch_data(f"{GITHUB_API_URL}/repos/{quote(repo)}") # There won't be a message key if this repo exists if "message" in repo_data: -- cgit v1.2.3 From ff0d3a833bbbe517f50124c5157223ff95876931 Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Sun, 8 Aug 2021 15:51:44 +0200 Subject: Improve code consitency in Wikipedia Cog,Exceptions, and Error handler --- bot/exts/evergreen/error_handler.py | 11 ++++++++--- bot/exts/evergreen/wikipedia.py | 15 ++++++--------- bot/utils/exceptions.py | 9 ++++++--- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index 1662af50..8fcbb8f2 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -113,7 +113,10 @@ class CommandErrorHandler(commands.Cog): return if isinstance(error, commands.CheckFailure): - await ctx.send(embed=self.error_embed("You are not authorized to use this command.", NEGATIVE_REPLIES)) + await ctx.send( + embed=self.error_embed( + "You are not authorized to use this command.", + NEGATIVE_REPLIES)) return if isinstance(error, UserNotPlayingError): @@ -121,8 +124,10 @@ class CommandErrorHandler(commands.Cog): return if isinstance(error, ExternalAPIError): - await ctx.send(embed=self.error_embed(f"There was an error when communicating with the {error.api}", - NEGATIVE_REPLIES)) + await ctx.send( + embed=self.error_embed( + f"There was an error when communicating with the {error.api}", + NEGATIVE_REPLIES)) return with push_scope() as scope: diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index 879146a6..63462433 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -47,9 +47,9 @@ class WikipediaSearch(commands.Cog): """Search wikipedia search string and return formatted first 10 pages found.""" params = WIKI_PARAMS | {"srlimit": 10, "srsearch": search} async with self.bot.http_session.get(url=SEARCH_API, params=params) as resp: - if resp.status != 200: + if (status := resp.status) != 200: log.info(f"Unexpected response `{resp.status}` while searching wikipedia for `{search}`") - raise ExternalAPIError("Wikipedia API") + raise ExternalAPIError("Wikipedia API", status) raw_data = await resp.json() number_of_results = raw_data["query"]["searchinfo"]["totalhits"] @@ -72,13 +72,10 @@ class WikipediaSearch(commands.Cog): return lines raw_data = await resp.json() - if raw_data.get("query", None) is None: - if raw_data.get("errors", None) is not None: - log.info("There was an error regarding the Wikipedia API query.") - else: - log.info("There was an issue when trying to communicate with the Wikipedia API, " - "please try again later.") - raise ExternalAPIError("Wikipedia API") + if not raw_data.get("query", None): + if error := raw_data.get("errors"): + log.error(f"There was an error while communicating with the Wikipedia API: {error}") + raise ExternalAPIError("Wikipedia API", status, error) number_of_results = raw_data["query"]["searchinfo"]["totalhits"] diff --git a/bot/utils/exceptions.py b/bot/utils/exceptions.py index 5ccd6139..73c8e3e1 100644 --- a/bot/utils/exceptions.py +++ b/bot/utils/exceptions.py @@ -1,3 +1,5 @@ +from typing import Optional + class UserNotPlayingError(Exception): """Raised when users try to use game commands when they are not playing.""" @@ -6,9 +8,10 @@ class UserNotPlayingError(Exception): class ExternalAPIError(Exception): - """Raised when an external API(eg. Wikipedia) returns an error.""" + """Raised when an external API(eg. Wikipedia) returns an error response.""" - def __init__(self, api: str): + def __init__(self, api: str, status_code: int, error_msg: Optional[str] = None): super().__init__() self.api = api - pass + self.status_code = status_code + self.error_msg = error_msg -- cgit v1.2.3 From 12a4139e265a932ecd7caf7ed8151c5cdb08e3dd Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Sun, 8 Aug 2021 18:34:28 +0200 Subject: Remove duplicated code, improve consitency in Wikipedia Cog, and error_handler --- bot/exts/evergreen/error_handler.py | 9 ++++----- bot/exts/evergreen/wikipedia.py | 24 ++---------------------- 2 files changed, 6 insertions(+), 27 deletions(-) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index 8fcbb8f2..7a916606 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -113,10 +113,7 @@ class CommandErrorHandler(commands.Cog): return if isinstance(error, commands.CheckFailure): - await ctx.send( - embed=self.error_embed( - "You are not authorized to use this command.", - NEGATIVE_REPLIES)) + await ctx.send(embed=self.error_embed("You are not authorized to use this command.", NEGATIVE_REPLIES)) return if isinstance(error, UserNotPlayingError): @@ -127,7 +124,9 @@ class CommandErrorHandler(commands.Cog): await ctx.send( embed=self.error_embed( f"There was an error when communicating with the {error.api}", - NEGATIVE_REPLIES)) + NEGATIVE_REPLIES + ) + ) return with push_scope() as scope: diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index 63462433..35ab27f1 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -48,31 +48,11 @@ class WikipediaSearch(commands.Cog): params = WIKI_PARAMS | {"srlimit": 10, "srsearch": search} async with self.bot.http_session.get(url=SEARCH_API, params=params) as resp: if (status := resp.status) != 200: - log.info(f"Unexpected response `{resp.status}` while searching wikipedia for `{search}`") + log.info(f"Unexpected response `{status}` while searching wikipedia for `{search}`") raise ExternalAPIError("Wikipedia API", status) - raw_data = await resp.json() - number_of_results = raw_data["query"]["searchinfo"]["totalhits"] - - if number_of_results: - results = raw_data["query"]["search"] - lines = [] - - for article in results: - line = WIKI_SEARCH_RESULT.format( - name=article["title"], - description=unescape( - re.sub( - WIKI_SNIPPET_REGEX, "", article["snippet"] - ) - ), - url=f"https://en.wikipedia.org/?curid={article['pageid']}" - ) - lines.append(line) - - return lines raw_data = await resp.json() - if not raw_data.get("query", None): + if not raw_data.get("query"): if error := raw_data.get("errors"): log.error(f"There was an error while communicating with the Wikipedia API: {error}") raise ExternalAPIError("Wikipedia API", status, error) -- cgit v1.2.3 From dcd3bc3f70988e819de6b0e0f70a600029e1ebfb Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 10 Aug 2021 14:13:31 +0100 Subject: Add February in_month check to love calculator command This ensure the valentines themed command can only be ran during the valentines season. --- bot/exts/valentines/lovecalculator.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index b10b7bca..41ba25ae 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -12,6 +12,8 @@ from discord.ext import commands from discord.ext.commands import BadArgument, Cog, clean_content from bot.bot import Bot +from bot.constants import Month +from bot.utils.decorators import in_month log = logging.getLogger(__name__) @@ -22,6 +24,7 @@ LOVE_DATA = sorted((int(key), value) for key, value in LOVE_DATA.items()) class LoveCalculator(Cog): """A cog for calculating the love between two people.""" + @in_month(Month.FEBRUARY) @commands.command(aliases=("love_calculator", "love_calc")) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) async def love(self, ctx: commands.Context, who: Union[Member, str], whom: Union[Member, str] = None) -> None: -- cgit v1.2.3 From cd904229b7150d2db26fda9aba5a51abf3f95c58 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 10 Aug 2021 14:15:07 +0100 Subject: Require both members used in love calculator have opt-ed into lovefest This ensure that only uses that have opted into the love fest event can actually be used as part of this command. --- bot/exts/valentines/lovecalculator.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index 41ba25ae..ba5e0b92 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -4,7 +4,7 @@ import json import logging import random from pathlib import Path -from typing import Coroutine, Union +from typing import Coroutine, Optional, Union import discord from discord import Member @@ -12,7 +12,7 @@ from discord.ext import commands from discord.ext.commands import BadArgument, Cog, clean_content from bot.bot import Bot -from bot.constants import Month +from bot.constants import Lovefest, Month from bot.utils.decorators import in_month log = logging.getLogger(__name__) @@ -27,7 +27,7 @@ class LoveCalculator(Cog): @in_month(Month.FEBRUARY) @commands.command(aliases=("love_calculator", "love_calc")) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) - async def love(self, ctx: commands.Context, who: Union[Member, str], whom: Union[Member, str] = None) -> None: + async def love(self, ctx: commands.Context, who: Member, whom: Optional[Member] = None) -> None: """ Tells you how much the two love each other. @@ -49,6 +49,12 @@ class LoveCalculator(Cog): if whom is None: whom = ctx.author + if not all(( + Lovefest.role_id in [role.id for role in who.roles], + Lovefest.role_id in [role.id for role in whom.roles] + )): + raise BadArgument("Both members must have the love fest role!") + def normalize(arg: Union[Member, str]) -> Coroutine: if isinstance(arg, Member): # If we are given a member, return name#discrim without any extra changes -- cgit v1.2.3 From c334c20c03d4f3961c9b2628660efaae405e5d9c Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 10 Aug 2021 14:15:51 +0100 Subject: Sort the members before calculating a love score This ensures the same result for same input, regardless of order --- bot/exts/valentines/lovecalculator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index ba5e0b92..d06d883d 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -65,7 +65,8 @@ class LoveCalculator(Cog): # This has to be done manually to be applied to usernames return clean_content(escape_markdown=True).convert(ctx, arg) - who, whom = [await normalize(arg) for arg in (who, whom)] + # Sort to ensure same result for same input, regardless of order + who, whom = sorted([await normalize(arg) for arg in (who, whom)]) # Make sure user didn't provide something silly such as 10 spaces if not (who and whom): -- cgit v1.2.3 From 66d531583beef60d27bcb3afdd4e127531b7cb75 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 10 Aug 2021 14:23:35 +0100 Subject: Update love calculator docstring to reflect new behaviour Since we update how the command behaves, by not allow arbitrary stings, and being symmetrical, we should reflect this in the docstring. --- bot/exts/valentines/lovecalculator.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index d06d883d..d80f2adf 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -31,20 +31,18 @@ class LoveCalculator(Cog): """ Tells you how much the two love each other. - This command accepts users or arbitrary strings as arguments. - Users are converted from: + This command requires at least one member as input, if two are given love will be calculated between + those two users, if only one is given, the second member is asusmed to be the invoker. + Members are converted from: - User ID - Mention - name#discrim - name - nickname - Any two arguments will always yield the same result, though the order of arguments matters: - Running .love joseph erlang will always yield the same result. - Running .love erlang joseph won't yield the same result as .love joseph erlang - - If you want to use multiple words for one argument, you must include quotes. - .love "Zes Vappa" "morning coffee" + Any two arguments will always yield the same result, regardless of the order of arguments: + Running .love @joe#6000 @chrisjl#2655 will always yield the same result. + Running .love @chrisjl#2655 @joe#6000 will yield the same result as before. """ if whom is None: whom = ctx.author -- cgit v1.2.3 From 0a2e18423a8042f6ab7e96e3d0775402a5cddac8 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 10 Aug 2021 14:24:57 +0100 Subject: Remove unneeded checks in love calculator command Now that we don't allow arbitrary strings, we can simplify the input validation/modification we do. --- bot/exts/valentines/lovecalculator.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index d80f2adf..46bcc6fd 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -4,7 +4,7 @@ import json import logging import random from pathlib import Path -from typing import Coroutine, Optional, Union +from typing import Coroutine, Optional import discord from discord import Member @@ -53,23 +53,13 @@ class LoveCalculator(Cog): )): raise BadArgument("Both members must have the love fest role!") - def normalize(arg: Union[Member, str]) -> Coroutine: - if isinstance(arg, Member): - # If we are given a member, return name#discrim without any extra changes - arg = str(arg) - else: - # Otherwise normalise case and remove any leading/trailing whitespace - arg = arg.strip().title() + def normalize(arg: Member) -> Coroutine: # This has to be done manually to be applied to usernames - return clean_content(escape_markdown=True).convert(ctx, arg) + return clean_content(escape_markdown=True).convert(ctx, str(arg)) # Sort to ensure same result for same input, regardless of order who, whom = sorted([await normalize(arg) for arg in (who, whom)]) - # Make sure user didn't provide something silly such as 10 spaces - if not (who and whom): - raise BadArgument("Arguments must be non-empty strings.") - # Hash inputs to guarantee consistent results (hashing algorithm choice arbitrary) # # hashlib is used over the builtin hash() to guarantee same result over multiple runtimes -- cgit v1.2.3 From 298f8fc85bf65b9fd52f524b0d61eb99ef7e4687 Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Tue, 10 Aug 2021 15:17:30 +0100 Subject: Update love calculator error output to be consistent with other role check fails Co-authored-by: Matteo Bertucci --- bot/exts/valentines/lovecalculator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index 46bcc6fd..d9b60cfa 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -51,7 +51,7 @@ class LoveCalculator(Cog): Lovefest.role_id in [role.id for role in who.roles], Lovefest.role_id in [role.id for role in whom.roles] )): - raise BadArgument("Both members must have the love fest role!") + raise BadArgument("Both members must have the lovefest role!") def normalize(arg: Member) -> Coroutine: # This has to be done manually to be applied to usernames -- cgit v1.2.3 From 871445727937791645e892e9fe33c6e7f47c2e04 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Thu, 12 Aug 2021 18:41:32 +0100 Subject: Give lovefest unsub info in lovecalc footer --- bot/exts/valentines/lovecalculator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index d9b60cfa..b59e60ad 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -12,7 +12,7 @@ from discord.ext import commands from discord.ext.commands import BadArgument, Cog, clean_content from bot.bot import Bot -from bot.constants import Lovefest, Month +from bot.constants import Client, Lovefest, Month from bot.utils.decorators import in_month log = logging.getLogger(__name__) @@ -85,6 +85,7 @@ class LoveCalculator(Cog): name="A letter from Dr. Love:", value=data["text"] ) + embed.set_footer(text=f"You can unsubscribe from lovefest by using {Client.prefix}lovefest unsub") await ctx.send(embed=embed) -- cgit v1.2.3 From 446c86f54ae92ee0cde9a52f2e3bf09362e0add4 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Thu, 12 Aug 2021 18:44:25 +0100 Subject: Don't role check if running lovecalc on yourself This skips the role check if the author is running the love calc command on themselves. We still want to check the the user running against has the role, along with if the author is running it against 2 other people. i have also included info on how to get the lovefest role in the error embed. --- bot/exts/valentines/lovecalculator.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/bot/exts/valentines/lovecalculator.py b/bot/exts/valentines/lovecalculator.py index b59e60ad..1cb10e64 100644 --- a/bot/exts/valentines/lovecalculator.py +++ b/bot/exts/valentines/lovecalculator.py @@ -12,7 +12,7 @@ from discord.ext import commands from discord.ext.commands import BadArgument, Cog, clean_content from bot.bot import Bot -from bot.constants import Client, Lovefest, Month +from bot.constants import Channels, Client, Lovefest, Month from bot.utils.decorators import in_month log = logging.getLogger(__name__) @@ -44,15 +44,19 @@ class LoveCalculator(Cog): Running .love @joe#6000 @chrisjl#2655 will always yield the same result. Running .love @chrisjl#2655 @joe#6000 will yield the same result as before. """ + if ( + Lovefest.role_id not in [role.id for role in who.roles] + or (whom is not None and Lovefest.role_id not in [role.id for role in whom.roles]) + ): + raise BadArgument( + "This command can only be ran against members with the lovefest role! " + "This role be can assigned by running " + f"`{Client.prefix}lovefest sub` in <#{Channels.community_bot_commands}>." + ) + if whom is None: whom = ctx.author - if not all(( - Lovefest.role_id in [role.id for role in who.roles], - Lovefest.role_id in [role.id for role in whom.roles] - )): - raise BadArgument("Both members must have the lovefest role!") - def normalize(arg: Member) -> Coroutine: # This has to be done manually to be applied to usernames return clean_content(escape_markdown=True).convert(ctx, str(arg)) -- cgit v1.2.3 From 5ea96aabd779f7445b6db5236018b2b28b11a5dc Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Mon, 16 Aug 2021 10:39:29 -0500 Subject: Merged .reverse into one command and made reversing the users profile picture the default behavior --- .../evergreen/avatar_modification/avatar_modify.py | 66 +++++++++------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index b482f42e..ccbefbd7 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -121,49 +121,39 @@ class AvatarModify(commands.Cog): await ctx.send(embed=embed, file=file) - @avatar_modify.group(name="reverse", root_aliases=['reverse']) - async def reverse(self, ctx: commands.Context) -> None: - """Group for the reverse commands.""" - if not ctx.invoked_subcommand: - await invoke_help_command(ctx) - - @reverse.command(name="text") - async def reverse_text(self, ctx: commands.Context, *, text: str) -> None: - """Sends the given text backwards.""" - await ctx.send(f"> {text[::-1]}", allowed_mentions=discord.AllowedMentions.none()) - - @reverse.command(name="image") - async def reverse_photo(self, ctx: commands.Context) -> None: - """ - Sends a reversed version of the users profile picture. - - If an image is attached, the given image will be flipped. - """ - async with ctx.typing(): - user = await self._fetch_user(ctx.author.id) - if not user: - await ctx.send(f"{Emojis.cross_mark} Could not get user info.") - return + @avatar_modify.command(name="reverse", root_aliases=("reverse",)) + async def reverse(self, ctx: commands.Context, *, text: t.Optional[str]): + """Reverses the sent text. + + If no text is provided, the user's profile picture will be reversed.""" + if not text: + async with ctx.typing(): + user = await self._fetch_user(ctx.author.id) + if not user: + await ctx.send(f"{Emojis.cross_mark} Could not get user info.") + return - image_bytes = await user.avatar_url_as(size=1024).read() - filename = file_safe_name("reverse_avatar", ctx.author.display_name) + image_bytes = await user.avatar_url_as(size=1024).read() + filename = file_safe_name("reverse_avatar", ctx.author.display_name) - file = await in_executor( - PfpEffects.apply_effect, - image_bytes, - PfpEffects.flip_effect, - filename - ) + file = await in_executor( + PfpEffects.apply_effect, + image_bytes, + PfpEffects.flip_effect, + filename + ) - embed = discord.Embed( - title="Your reversed avatar.", - description="Here is your reversed avatar. I think it is a spitting image of you." - ) + embed = discord.Embed( + title="Your reversed avatar.", + description="Here is your reversed avatar. I think it is a spitting image of you." + ) - embed.set_image(url=f"attachment://{filename}") - embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) + embed.set_image(url=f"attachment://{filename}") + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) - await ctx.send(embed=embed, file=file) + await ctx.send(embed=embed, file=file) + else: + await ctx.send(f"> {text[::-1]}", allowed_mentions=discord.AllowedMentions.none()) @avatar_modify.command(aliases=("easterify",), root_aliases=("easterify", "avatareasterify")) async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: -- cgit v1.2.3 From 6190278321112cf131485a4791e4711f27bd7952 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Mon, 16 Aug 2021 10:41:48 -0500 Subject: lint fix because precommit didn't run --- bot/exts/evergreen/avatar_modification/avatar_modify.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index ccbefbd7..5decfce4 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -122,10 +122,12 @@ class AvatarModify(commands.Cog): await ctx.send(embed=embed, file=file) @avatar_modify.command(name="reverse", root_aliases=("reverse",)) - async def reverse(self, ctx: commands.Context, *, text: t.Optional[str]): - """Reverses the sent text. - - If no text is provided, the user's profile picture will be reversed.""" + async def reverse(self, ctx: commands.Context, *, text: t.Optional[str]) -> None: + """ + Reverses the sent text. + + If no text is provided, the user's profile picture will be reversed. + """ if not text: async with ctx.typing(): user = await self._fetch_user(ctx.author.id) -- cgit v1.2.3 From c18676eb72530dd8747fdd8d6572d4c6c748b6b3 Mon Sep 17 00:00:00 2001 From: Axis <77634274+blankRiot96@users.noreply.github.com> Date: Fri, 4 Jun 2021 22:21:03 +0530 Subject: Added more topics to `py_topics.yaml`. --- bot/resources/evergreen/py_topics.yaml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/bot/resources/evergreen/py_topics.yaml b/bot/resources/evergreen/py_topics.yaml index 6b7e0206..a3fb2ccc 100644 --- a/bot/resources/evergreen/py_topics.yaml +++ b/bot/resources/evergreen/py_topics.yaml @@ -23,6 +23,19 @@ - When you were first learning, what is a resource you wish you had? - What is something you know now, that you wish you knew when starting out? - What is something simple that you still error on today? + - What do you plan on eventually achieving with Python? + - Is Python your first programming language? If not, what is it? + - What's your favourite aspect of Python development? (Backend, frontend, game dev, machine learning, ai, etc.) + - In what ways has Python Discord helped you with Python? + - Are you currently using Python professionally, for education, or as a hobby? + - What is your process when you decide to start a project in Python? + - Have you ever been unable to finish a Python project? What is it and why? + - How often do you program in Python? + - How would you learn a new library if needed to do so? + - Have you ever worked with a microcontroller or anything physical with Python before? + - How good would you say you are at Python so far? Beginner, intermediate, or advanced? + - Have you ever tried making your own programming language? + - Has a recently discovered Python module changed your general use of Python? # algos-and-data-structs 650401909852864553: @@ -52,7 +65,7 @@ - What unique features does your bot contain, if any? - What commands/features are you proud of making? - What feature would you be the most interested in making? - - What feature would you like to see added to the library? what feature in the library do you think is redundant? + - What feature would you like to see added to the library? What feature in the library do you think is redundant? - Do you think there's a way in which Discord could handle bots better? - What's one feature you wish more developers had in their bots? -- cgit v1.2.3 From 12b562ef9a6fd2cf3015156eb738ee765f82d070 Mon Sep 17 00:00:00 2001 From: Kronifer <44979306+Kronifer@users.noreply.github.com> Date: Mon, 16 Aug 2021 16:39:52 -0500 Subject: Removed a massive it block --- .../evergreen/avatar_modification/avatar_modify.py | 47 +++++++++++----------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 5decfce4..185cdb38 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -128,34 +128,35 @@ class AvatarModify(commands.Cog): If no text is provided, the user's profile picture will be reversed. """ - if not text: - async with ctx.typing(): - user = await self._fetch_user(ctx.author.id) - if not user: - await ctx.send(f"{Emojis.cross_mark} Could not get user info.") - return + if text: + await ctx.send(f"> {text[::-1]}", allowed_mentions=discord.AllowedMentions.none()) + return + + async with ctx.typing(): + user = await self._fetch_user(ctx.author.id) + if not user: + await ctx.send(f"{Emojis.cross_mark} Could not get user info.") + return - image_bytes = await user.avatar_url_as(size=1024).read() - filename = file_safe_name("reverse_avatar", ctx.author.display_name) + image_bytes = await user.avatar_url_as(size=1024).read() + filename = file_safe_name("reverse_avatar", ctx.author.display_name) - file = await in_executor( - PfpEffects.apply_effect, - image_bytes, - PfpEffects.flip_effect, - filename - ) + file = await in_executor( + PfpEffects.apply_effect, + image_bytes, + PfpEffects.flip_effect, + filename + ) - embed = discord.Embed( - title="Your reversed avatar.", - description="Here is your reversed avatar. I think it is a spitting image of you." - ) + embed = discord.Embed( + title="Your reversed avatar.", + description="Here is your reversed avatar. I think it is a spitting image of you." + ) - embed.set_image(url=f"attachment://{filename}") - embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) + embed.set_image(url=f"attachment://{filename}") + embed.set_footer(text=f"Made by {ctx.author.display_name}.", icon_url=user.avatar_url) - await ctx.send(embed=embed, file=file) - else: - await ctx.send(f"> {text[::-1]}", allowed_mentions=discord.AllowedMentions.none()) + await ctx.send(embed=embed, file=file) @avatar_modify.command(aliases=("easterify",), root_aliases=("easterify", "avatareasterify")) async def avatareasterify(self, ctx: commands.Context, *colours: t.Union[discord.Colour, str]) -> None: -- cgit v1.2.3 From ce80a2fb34c14e2ece0b4ffc6bda85030764ef9c Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Tue, 17 Aug 2021 16:25:14 +0200 Subject: Improve code consistency and spacing in the Wikipedia Cog --- bot/exts/evergreen/error_handler.py | 4 ++-- bot/exts/evergreen/wikipedia.py | 27 +++++++++++++-------------- bot/utils/exceptions.py | 2 +- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/bot/exts/evergreen/error_handler.py b/bot/exts/evergreen/error_handler.py index 7a916606..a280c725 100644 --- a/bot/exts/evergreen/error_handler.py +++ b/bot/exts/evergreen/error_handler.py @@ -11,7 +11,7 @@ from sentry_sdk import push_scope from bot.bot import Bot from bot.constants import Channels, Colours, ERROR_REPLIES, NEGATIVE_REPLIES, RedirectOutput from bot.utils.decorators import InChannelCheckFailure, InMonthCheckFailure -from bot.utils.exceptions import ExternalAPIError, UserNotPlayingError +from bot.utils.exceptions import APIError, UserNotPlayingError log = logging.getLogger(__name__) @@ -120,7 +120,7 @@ class CommandErrorHandler(commands.Cog): await ctx.send("Game not found.") return - if isinstance(error, ExternalAPIError): + if isinstance(error, APIError): await ctx.send( embed=self.error_embed( f"There was an error when communicating with the {error.api}", diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index 35ab27f1..cabce1ad 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -2,14 +2,14 @@ import logging import re from datetime import datetime from html import unescape -from typing import List, Optional +from typing import List from discord import Color, Embed, TextChannel from discord.ext import commands from bot.bot import Bot from bot.utils import LinePaginator -from bot.utils.exceptions import ExternalAPIError +from bot.utils.exceptions import APIError log = logging.getLogger(__name__) @@ -43,25 +43,25 @@ class WikipediaSearch(commands.Cog): def __init__(self, bot: Bot): self.bot = bot - async def wiki_request(self, channel: TextChannel, search: str) -> Optional[List[str]]: + async def wiki_request(self, channel: TextChannel, search: str) -> List[str]: """Search wikipedia search string and return formatted first 10 pages found.""" params = WIKI_PARAMS | {"srlimit": 10, "srsearch": search} async with self.bot.http_session.get(url=SEARCH_API, params=params) as resp: if (status := resp.status) != 200: log.info(f"Unexpected response `{status}` while searching wikipedia for `{search}`") - raise ExternalAPIError("Wikipedia API", status) + raise APIError("Wikipedia API", status) raw_data = await resp.json() + if not raw_data.get("query"): if error := raw_data.get("errors"): log.error(f"There was an error while communicating with the Wikipedia API: {error}") - raise ExternalAPIError("Wikipedia API", status, error) + raise APIError("Wikipedia API", status, error) number_of_results = raw_data["query"]["searchinfo"]["totalhits"] - + lines = [] if number_of_results: results = raw_data["query"]["search"] - lines = [] for article in results: line = WIKI_SEARCH_RESULT.format( @@ -75,13 +75,7 @@ class WikipediaSearch(commands.Cog): ) lines.append(line) - return lines - - else: - await channel.send( - "Sorry, we could not find a wikipedia article using that search term." - ) - return + return lines @commands.cooldown(1, 10, commands.BucketType.user) @commands.command(name="wikipedia", aliases=("wiki",)) @@ -99,6 +93,11 @@ class WikipediaSearch(commands.Cog): await LinePaginator.paginate( contents, ctx, embed ) + else: + await ctx.send( + "Sorry, we could not find a wikipedia article using that search term." + ) + return def setup(bot: Bot) -> None: diff --git a/bot/utils/exceptions.py b/bot/utils/exceptions.py index 73c8e3e1..b4175695 100644 --- a/bot/utils/exceptions.py +++ b/bot/utils/exceptions.py @@ -7,7 +7,7 @@ class UserNotPlayingError(Exception): pass -class ExternalAPIError(Exception): +class APIError(Exception): """Raised when an external API(eg. Wikipedia) returns an error response.""" def __init__(self, api: str, status_code: int, error_msg: Optional[str] = None): -- cgit v1.2.3 From ce64585d828c741d092f6e14e03d38112b23b983 Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Tue, 17 Aug 2021 17:17:07 +0200 Subject: Improve code transparency in the Wikipedia Cog Co-authored-by: Rohan Reddy Alleti --- bot/exts/evergreen/wikipedia.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index cabce1ad..1b75f802 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -97,7 +97,6 @@ class WikipediaSearch(commands.Cog): await ctx.send( "Sorry, we could not find a wikipedia article using that search term." ) - return def setup(bot: Bot) -> None: -- cgit v1.2.3 From d0b580454ba707220d955bf6e2f5799220e57eba Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 19 Aug 2021 15:42:11 -0400 Subject: Fix indent style --- bot/exts/evergreen/duck_game.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 1286995f..e9d3fa39 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -81,11 +81,12 @@ def as_trinary(card: tuple[int]) -> int: class DuckGame: """A class for a single game.""" - def __init__(self, - rows: int = 4, - columns: int = 3, - minimum_solutions: int = 1, - ) -> None: + def __init__( + self, + rows: int = 4, + columns: int = 3, + minimum_solutions: int = 1, + ) -> None: """ Take samples from the deck to generate a board. -- cgit v1.2.3 From 970e0cbbe36e4731106b085fa0e7fdbd62928e2e Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 19 Aug 2021 15:57:39 -0400 Subject: Rename file constants to all-caps --- bot/exts/evergreen/duck_game.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index e9d3fa39..cc764f88 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -32,11 +32,11 @@ INCORRECT_GOOSE = -1 """ SOLN_DISTR = 0, 0.05, 0.05, 0.1, 0.15, 0.25, 0.2, 0.15, .05 -image_path = Path("bot", "resources", "evergreen", "all_cards.png") -font_path = Path("bot", "resources", "evergreen", "LuckiestGuy-Regular.ttf") +IMAGE_PATH = Path("bot", "resources", "evergreen", "all_cards.png") +FONT_PATH = Path("bot", "resources", "evergreen", "LuckiestGuy-Regular.ttf") -ALL_CARDS = Image.open(image_path) -LABEL_FONT = ImageFont.truetype(str(font_path), size=16) +ALL_CARDS = Image.open(IMAGE_PATH) +LABEL_FONT = ImageFont.truetype(str(FONT_PATH), size=16) CARD_WIDTH = 155 CARD_HEIGHT = 97 -- cgit v1.2.3 From 27129f29924a09141e0515a6e74de1393a7f6fff Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 19 Aug 2021 16:02:10 -0400 Subject: Fix block comment style --- bot/exts/evergreen/duck_game.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index cc764f88..c8920483 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -25,11 +25,11 @@ INCORRECT_SOLN = -1 CORRECT_GOOSE = 2 INCORRECT_GOOSE = -1 -""" Distribution of minimum acceptable solutions at board generation. - This is for gameplay reasons, to shift the number of solutions per board up, - while still making the end of the game unpredictable. - Note: this is *not* the same as the distribution of number of solutions. -""" +# Distribution of minimum acceptable solutions at board generation. +# This is for gameplay reasons, to shift the number of solutions per board up, +# while still making the end of the game unpredictable. +# Note: this is *not* the same as the distribution of number of solutions. + SOLN_DISTR = 0, 0.05, 0.05, 0.1, 0.15, 0.25, 0.2, 0.15, .05 IMAGE_PATH = Path("bot", "resources", "evergreen", "all_cards.png") @@ -126,11 +126,9 @@ class DuckGame: self._solutions = set() for idx_a, card_a in enumerate(self.board): for idx_b, card_b in enumerate(self.board[idx_a+1:], start=idx_a+1): - """ - Two points determine a line, and there are exactly 3 points per line in {0,1,2}^4. - The completion of a line will only be a duplicate point if the other two points are the same, - which is prevented by the triangle iteration. - """ + # Two points determine a line, and there are exactly 3 points per line in {0,1,2}^4. + # The completion of a line will only be a duplicate point if the other two points are the same, + # 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) -- cgit v1.2.3 From e2cd37b05318190a3ee8f248c75acab029f26e6f Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 19 Aug 2021 17:00:41 -0400 Subject: Clarify comment about master image file --- bot/exts/evergreen/duck_game.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index c8920483..8eb5d858 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -65,7 +65,9 @@ def assemble_board_image(board: list[tuple[int]], rows: int, columns: int) -> Im def get_card_image(card: tuple[int]) -> Image: """Slice the image containing all the cards to get just this card.""" - row, col = divmod(as_trinary(card), 9) # all_cards.png should have 9x9 cards + # The master card image file should have 9x9 cards, + # arranged such that their features can be interpreted as ordered trinary. + row, col = divmod(as_trinary(card), 9) x1 = col * CARD_WIDTH x2 = x1 + CARD_WIDTH y1 = row * CARD_HEIGHT -- cgit v1.2.3 From 9368d4c6f7befb956e049c7253ddbf2bddd0b3c5 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 19 Aug 2021 18:07:31 -0400 Subject: Improve help message --- bot/exts/evergreen/duck_game.py | 34 ++++++++++++++++++++++-------- bot/resources/evergreen/ducks_help_ex.png | Bin 0 -> 343921 bytes 2 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 bot/resources/evergreen/ducks_help_ex.png diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 8eb5d858..04ae03a0 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -34,6 +34,7 @@ SOLN_DISTR = 0, 0.05, 0.05, 0.1, 0.15, 0.25, 0.2, 0.15, .05 IMAGE_PATH = Path("bot", "resources", "evergreen", "all_cards.png") FONT_PATH = Path("bot", "resources", "evergreen", "LuckiestGuy-Regular.ttf") +HELP_IMAGE_PATH = Path("bot", "resources", "evergreen", "ducks_help_ex.png") ALL_CARDS = Image.open(IMAGE_PATH) LABEL_FONT = ImageFont.truetype(str(FONT_PATH), size=16) @@ -44,6 +45,26 @@ EMOJI_WRONG = "\u274C" ANSWER_REGEX = re.compile(r'^\D*(\d+)\D+(\d+)\D+(\d+)\D*$') +HELP_TEXT = """ +**Each card has 4 features** +Color, Number, Hat, and Accessory + +**A valid flight** +3 cards where each feature is either all the same or all different + +**Call "GOOSE"** +if you think there are no more flights + +**+1** for each valid flight +**+2** for a correct "GOOSE" call +**-1** for any wrong answer + +The first flight below is invalid: the first card has swords while the other two have no accessory.\ + It would be valid if the first card was empty-handed, or one of the other two had paintbrushes. + +The second flight is valid because there are no 2:1 splits; each feature is either all the same or all different. +""" + def assemble_board_image(board: list[tuple[int]], rows: int, columns: int) -> Image: """Cut and paste images representing the given cards into an image representing the board.""" @@ -304,18 +325,13 @@ class DuckGamesDirector(commands.Cog): title="Compete against other players to find valid flights!", color=discord.Color.dark_purple(), ) - embed.description = ( - "**Each card has 4 features**\nColor, Number, Hat, and Accessory\n" - "\n**A valid flight**\n3 cards where each feature is either all the same or all different\n" - '\n**Call "GOOSE"**\nif you think there are no more flights\n' - "\n**1** for each valid flight\n" - '**2** for a correct "GOOSE" call\n' - "**-1** for any wrong answer" - ) + embed.description = HELP_TEXT + file = discord.File(HELP_IMAGE_PATH, filename="help.png") + embed.set_image(url="attachment://help.png") embed.set_footer( text="Tip: using Discord's compact message display mode can help keep the board on the screen" ) - await ctx.send(embed=embed) + await ctx.send(file=file, embed=embed) @staticmethod async def edit_embed_with_image(msg: discord.Message, embed: discord.Embed) -> discord.Message: diff --git a/bot/resources/evergreen/ducks_help_ex.png b/bot/resources/evergreen/ducks_help_ex.png new file mode 100644 index 00000000..01d9c243 Binary files /dev/null and b/bot/resources/evergreen/ducks_help_ex.png differ -- cgit v1.2.3 From 261c4f873761fb1f75563fa370245110a81d519d Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 19 Aug 2021 18:08:40 -0400 Subject: Add more aliases --- bot/exts/evergreen/duck_game.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 04ae03a0..ffd1b882 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -175,7 +175,11 @@ class DuckGamesDirector(commands.Cog): self.bot = bot self.current_games = {} - @commands.group(name='duckduckduckgoose', aliases=['dddg', 'duckgoose'], invoke_without_command=True) + @commands.group( + name='duckduckduckgoose', + aliases=['dddg', 'ddg', 'duckduckgoose', 'duckgoose'], + invoke_without_command=True + ) @commands.cooldown(rate=1, per=2, type=commands.BucketType.channel) async def start_game(self, ctx: commands.Context) -> None: """Generate a board, send the game embed, and end the game after a time limit.""" -- cgit v1.2.3 From 601400a73ea09019af65d78ec45d1dcfe4229bec Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Fri, 20 Aug 2021 21:24:54 +0200 Subject: Adjust code transparency in the Wikipedia Cog Co-authored-by: Bluenix --- bot/exts/evergreen/wikipedia.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index 1b75f802..9397568d 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -58,9 +58,8 @@ class WikipediaSearch(commands.Cog): log.error(f"There was an error while communicating with the Wikipedia API: {error}") raise APIError("Wikipedia API", status, error) - number_of_results = raw_data["query"]["searchinfo"]["totalhits"] lines = [] - if number_of_results: + if raw_data["query"]["searchinfo"]["totalhits"]: results = raw_data["query"]["search"] for article in results: -- cgit v1.2.3 From 4f5432a97bfafab237efa6efa7bb6daebc83577d Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Fri, 20 Aug 2021 21:25:17 +0200 Subject: Adjust spacing in the Wikipedia Cog Co-authored-by: Bluenix --- bot/utils/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/utils/exceptions.py b/bot/utils/exceptions.py index b4175695..bf0e5813 100644 --- a/bot/utils/exceptions.py +++ b/bot/utils/exceptions.py @@ -8,7 +8,7 @@ class UserNotPlayingError(Exception): class APIError(Exception): - """Raised when an external API(eg. Wikipedia) returns an error response.""" + """Raised when an external API (eg. Wikipedia) returns an error response.""" def __init__(self, api: str, status_code: int, error_msg: Optional[str] = None): super().__init__() -- cgit v1.2.3 From 1d06e4f074876e5793af4fb7937a6aed615ad4ea Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Fri, 20 Aug 2021 21:40:36 +0200 Subject: Remove unnecessary variables from the Wikipedia cog --- bot/exts/evergreen/wikipedia.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index 9397568d..01c13005 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -47,16 +47,16 @@ class WikipediaSearch(commands.Cog): """Search wikipedia search string and return formatted first 10 pages found.""" params = WIKI_PARAMS | {"srlimit": 10, "srsearch": search} async with self.bot.http_session.get(url=SEARCH_API, params=params) as resp: - if (status := resp.status) != 200: - log.info(f"Unexpected response `{status}` while searching wikipedia for `{search}`") - raise APIError("Wikipedia API", status) + if resp.status != 200: + log.info(f"Unexpected response `{resp.status}` while searching wikipedia for `{search}`") + raise APIError("Wikipedia API", resp.status) raw_data = await resp.json() if not raw_data.get("query"): if error := raw_data.get("errors"): log.error(f"There was an error while communicating with the Wikipedia API: {error}") - raise APIError("Wikipedia API", status, error) + raise APIError("Wikipedia API", resp.status, error) lines = [] if raw_data["query"]["searchinfo"]["totalhits"]: -- cgit v1.2.3 From 0c466387b881d0eb648c6b923dff16e538442f2c Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Fri, 20 Aug 2021 22:21:27 +0200 Subject: Improve overall code transparency in the Wikipedia Cog Co-authored-by: Bluenix --- bot/exts/evergreen/wikipedia.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bot/exts/evergreen/wikipedia.py b/bot/exts/evergreen/wikipedia.py index 01c13005..27e68397 100644 --- a/bot/exts/evergreen/wikipedia.py +++ b/bot/exts/evergreen/wikipedia.py @@ -60,9 +60,7 @@ class WikipediaSearch(commands.Cog): lines = [] if raw_data["query"]["searchinfo"]["totalhits"]: - results = raw_data["query"]["search"] - - for article in results: + for article in raw_data["query"]["search"]: line = WIKI_SEARCH_RESULT.format( name=article["title"], description=unescape( -- cgit v1.2.3 From 83eb6a6ea0bb8429504a0deddf3f6f2a2202547e Mon Sep 17 00:00:00 2001 From: wookie184 Date: Sat, 21 Aug 2021 12:47:42 +0100 Subject: Resize avatar to 1024x1024 before applying effects --- bot/exts/evergreen/avatar_modification/_effects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/avatar_modification/_effects.py b/bot/exts/evergreen/avatar_modification/_effects.py index 46d1a2ab..55eed6ad 100644 --- a/bot/exts/evergreen/avatar_modification/_effects.py +++ b/bot/exts/evergreen/avatar_modification/_effects.py @@ -22,6 +22,7 @@ class PfpEffects: """Applies the given effect to the image passed to it.""" im = Image.open(BytesIO(image_bytes)) im = im.convert("RGBA") + im = im.resize((1024, 1024)) im = effect(im, *args) bufferedio = BytesIO() @@ -74,7 +75,6 @@ class PfpEffects: @staticmethod def pridify_effect(image: Image.Image, pixels: int, flag: str) -> Image.Image: """Applies the given pride effect to the given image.""" - image = image.resize((1024, 1024)) image = PfpEffects.crop_avatar_circle(image) ring = Image.open(Path(f"bot/resources/pride/flags/{flag}.png")).resize((1024, 1024)) -- cgit v1.2.3 From df3c882db4cd2f970682087093e81a770b4f484a Mon Sep 17 00:00:00 2001 From: wookie184 Date: Sat, 21 Aug 2021 15:42:40 +0100 Subject: Refactor mosaic command to use apply_effect --- bot/exts/evergreen/avatar_modification/_effects.py | 18 ++++++++---------- .../evergreen/avatar_modification/avatar_modify.py | 5 +++-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/bot/exts/evergreen/avatar_modification/_effects.py b/bot/exts/evergreen/avatar_modification/_effects.py index 55eed6ad..92244207 100644 --- a/bot/exts/evergreen/avatar_modification/_effects.py +++ b/bot/exts/evergreen/avatar_modification/_effects.py @@ -283,16 +283,14 @@ class PfpEffects: return new_image @staticmethod - def mosaic_effect(img_bytes: bytes, squares: int, file_name: str) -> discord.File: - """Separate function run from an executor which turns an image into a mosaic.""" - avatar = Image.open(BytesIO(img_bytes)) - avatar = avatar.convert("RGBA").resize((1024, 1024)) + def mosaic_effect(image: Image.Image, squares: int) -> Image.Image: + """ + Applies a mosaic effect to the given image. - img_squares = PfpEffects.split_image(avatar, squares) + The "squares" argument specifies the number of squares to split + the image into. This should be a square number. + """ + img_squares = PfpEffects.split_image(image, squares) new_img = PfpEffects.join_images(img_squares) - bufferedio = BytesIO() - new_img.save(bufferedio, format="PNG") - bufferedio.seek(0) - - return discord.File(bufferedio, filename=file_name) + return new_img diff --git a/bot/exts/evergreen/avatar_modification/avatar_modify.py b/bot/exts/evergreen/avatar_modification/avatar_modify.py index 765c316e..7b4ae9c7 100644 --- a/bot/exts/evergreen/avatar_modification/avatar_modify.py +++ b/bot/exts/evergreen/avatar_modification/avatar_modify.py @@ -338,10 +338,11 @@ class AvatarModify(commands.Cog): img_bytes = await user.avatar_url_as(size=1024).read() file = await in_executor( - PfpEffects.mosaic_effect, + PfpEffects.apply_effect, img_bytes, + PfpEffects.mosaic_effect, + file_name, squares, - file_name ) if squares == 1: -- cgit v1.2.3 From 5f20826574a801e8a31c777ed916944c615e0f4c Mon Sep 17 00:00:00 2001 From: Izan Date: Mon, 23 Aug 2021 21:31:46 +0100 Subject: Initial commit (add coinflip command in coinflip.py) --- bot/exts/evergreen/coinflip.py | 54 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 bot/exts/evergreen/coinflip.py diff --git a/bot/exts/evergreen/coinflip.py b/bot/exts/evergreen/coinflip.py new file mode 100644 index 00000000..dc9a960b --- /dev/null +++ b/bot/exts/evergreen/coinflip.py @@ -0,0 +1,54 @@ +import random +from typing import Optional, Tuple + +from discord.ext import commands + +from bot.bot import Bot + + +LEMON_HYPERPLEASED = "<:lemon_hyperpleased:754441879822663811>" +LEMON_PENSIVE = "<:lemon_pensive:754441880246419486>" + + +class CoinSide(commands.Converter): + HEADS: Tuple[str] = ("h", "head", "heads") + TAILS: Tuple[str] = ("t", "tail", "tails") + + async def convert(self, ctx: commands.Context, side: str) -> str: + if side in CoinSide.HEADS: + return "heads" + + elif side in CoinSide.TAILS: + return "tails" + + else: + raise commands.BadArgument(f"{side!r} is not a valid coin side.") + + +class CoinFlip(commands.Cog): + """Cog for the CoinFlip command.""" + + @commands.command(name="coinflip", aliases=("flip", "coin", "cf")) + async def coinflip_command(self, ctx: commands.Context, side: Optional[CoinSide]) -> None: + """ + Flips a coin. + + If `coin_side` is provided will state whether you guessed the side correctly. + """ + flipped_side = random.choice(["heads", "tails"]) + + if not side: + await ctx.send(f"{ctx.author.mention} Flipped **{flipped_side}**!") + return + + message = f"{ctx.author.mention} Flipped **{flipped_side}**. " + if side == flipped_side: + message += f"You guessed correctly! {LEMON_HYPERPLEASED}" + else: + message += f"You guessed incorrectly. {LEMON_PENSIVE}" + await ctx.send(message) + + +def setup(bot: Bot) -> None: + """Loads the coinflip cog.""" + bot.add_cog(CoinFlip()) -- cgit v1.2.3 From e58eb7c348fb470d2c9731c87f7365b0e025146f Mon Sep 17 00:00:00 2001 From: Izan Date: Mon, 23 Aug 2021 21:38:14 +0100 Subject: Add missing docstrings --- bot/exts/evergreen/coinflip.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bot/exts/evergreen/coinflip.py b/bot/exts/evergreen/coinflip.py index dc9a960b..eba4bb45 100644 --- a/bot/exts/evergreen/coinflip.py +++ b/bot/exts/evergreen/coinflip.py @@ -11,10 +11,13 @@ LEMON_PENSIVE = "<:lemon_pensive:754441880246419486>" class CoinSide(commands.Converter): + """Class used to convert the `side` parameter of coinflip command.""" + HEADS: Tuple[str] = ("h", "head", "heads") TAILS: Tuple[str] = ("t", "tail", "tails") async def convert(self, ctx: commands.Context, side: str) -> str: + """Converts the provided `side` into the corresponding string.""" if side in CoinSide.HEADS: return "heads" -- cgit v1.2.3 From 934ad673e4f62df390e8017d944c9182d4ee1211 Mon Sep 17 00:00:00 2001 From: Objectivitix <79152594+Objectivitix@users.noreply.github.com> Date: Mon, 23 Aug 2021 19:23:58 -0300 Subject: Whitelist `.bm` command in occupied help channels --- bot/exts/evergreen/bookmark.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/bookmark.py b/bot/exts/evergreen/bookmark.py index 85c9b46f..f93371a6 100644 --- a/bot/exts/evergreen/bookmark.py +++ b/bot/exts/evergreen/bookmark.py @@ -7,14 +7,16 @@ import discord from discord.ext import commands from bot.bot import Bot -from bot.constants import Colours, ERROR_REPLIES, Icons +from bot.constants import Categories, Colours, ERROR_REPLIES, Icons, WHITELISTED_CHANNELS from bot.utils.converters import WrappedMessageConverter +from bot.utils.decorators import whitelist_override log = logging.getLogger(__name__) # Number of seconds to wait for other users to bookmark the same message TIMEOUT = 120 BOOKMARK_EMOJI = "📌" +WHITELISTED_CATEGORIES = (Categories.help_in_use,) class Bookmark(commands.Cog): @@ -85,6 +87,7 @@ class Bookmark(commands.Cog): await message.add_reaction(BOOKMARK_EMOJI) return message + @whitelist_override(channels=WHITELISTED_CHANNELS, categories=WHITELISTED_CATEGORIES) @commands.command(name="bookmark", aliases=("bm", "pin")) async def bookmark( self, -- cgit v1.2.3 From 58c08750e755fa29a306fb38e475249d728e3b34 Mon Sep 17 00:00:00 2001 From: Izan Date: Tue, 24 Aug 2021 11:54:40 +0100 Subject: Update message to start with lowercase --- bot/exts/evergreen/coinflip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/coinflip.py b/bot/exts/evergreen/coinflip.py index eba4bb45..7ec5b5b9 100644 --- a/bot/exts/evergreen/coinflip.py +++ b/bot/exts/evergreen/coinflip.py @@ -41,10 +41,10 @@ class CoinFlip(commands.Cog): flipped_side = random.choice(["heads", "tails"]) if not side: - await ctx.send(f"{ctx.author.mention} Flipped **{flipped_side}**!") + await ctx.send(f"{ctx.author.mention} flipped **{flipped_side}**!") return - message = f"{ctx.author.mention} Flipped **{flipped_side}**. " + message = f"{ctx.author.mention} flipped **{flipped_side}**. " if side == flipped_side: message += f"You guessed correctly! {LEMON_HYPERPLEASED}" else: -- cgit v1.2.3 From d2e9bfb79010a6b3eb9fe5656334b7a703d4123c Mon Sep 17 00:00:00 2001 From: Izan Date: Tue, 24 Aug 2021 11:57:11 +0100 Subject: Move lemojis into constants.py --- bot/constants.py | 3 +++ bot/exts/evergreen/coinflip.py | 9 +++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index bb602361..b5097601 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -239,6 +239,9 @@ class Emojis: reddit_comments = "<:reddit_comments:755845255001014384>" reddit_users = "<:reddit_users:755845303822974997>" + lemon_hyperpleased = "<:lemon_hyperpleased:754441879822663811>" + lemon_pensive = "<:lemon_pensive:754441880246419486>" + class Icons: questionmark = "https://cdn.discordapp.com/emojis/512367613339369475.png" diff --git a/bot/exts/evergreen/coinflip.py b/bot/exts/evergreen/coinflip.py index 7ec5b5b9..2f9bf38b 100644 --- a/bot/exts/evergreen/coinflip.py +++ b/bot/exts/evergreen/coinflip.py @@ -4,10 +4,7 @@ from typing import Optional, Tuple from discord.ext import commands from bot.bot import Bot - - -LEMON_HYPERPLEASED = "<:lemon_hyperpleased:754441879822663811>" -LEMON_PENSIVE = "<:lemon_pensive:754441880246419486>" +from bot.constants import Emojis class CoinSide(commands.Converter): @@ -46,9 +43,9 @@ class CoinFlip(commands.Cog): message = f"{ctx.author.mention} flipped **{flipped_side}**. " if side == flipped_side: - message += f"You guessed correctly! {LEMON_HYPERPLEASED}" + message += f"You guessed correctly! {Emojis.lemon_hyperpleased}" else: - message += f"You guessed incorrectly. {LEMON_PENSIVE}" + message += f"You guessed incorrectly. {Emojis.lemon_pensive}" await ctx.send(message) -- cgit v1.2.3 From 812372b20062b6ce913f88a8c2e3d852c1758afa Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Wed, 25 Aug 2021 15:00:30 -0400 Subject: Use with_role decorator for stop command --- bot/exts/evergreen/duck_game.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index ffd1b882..3f122d84 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -12,7 +12,8 @@ from PIL import Image, ImageDraw, ImageFont from discord.ext import commands from bot.bot import Bot -from bot.constants import Colours, MODERATION_ROLES +from bot.constants import Colours, Roles +from bot.utils.decorators import with_role DECK = list(product(*[(0, 1, 2)]*4)) @@ -312,10 +313,9 @@ class DuckGamesDirector(commands.Cog): await self.send_help_embed(ctx) @start_game.command(name="stop") + @with_role(Roles.moderator) async def stop_game(self, ctx: commands.Context) -> None: """Stop a currently running game. Only available to mods.""" - if not any(role in ctx.author.roles for role in MODERATION_ROLES): - return try: game = self.current_games.pop(ctx.channel.id) except KeyError: -- cgit v1.2.3 From 4da145177397206c7acfbb6dad23bdb3666ab2ac Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Wed, 25 Aug 2021 15:14:41 -0400 Subject: Add an error message for trying to stop a nonexistent game --- bot/exts/evergreen/duck_game.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 3f122d84..10ff49aa 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -319,6 +319,7 @@ class DuckGamesDirector(commands.Cog): try: game = self.current_games.pop(ctx.channel.id) except KeyError: + await ctx.send("No game currently running in this channel") return await self.end_game(game, end_message="Game canceled.") -- cgit v1.2.3 From fceb50507bf34340615addca41bf5b920d951e15 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Wed, 25 Aug 2021 15:19:49 -0400 Subject: Add an error message for trying to start a game while one is already running --- bot/exts/evergreen/duck_game.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 10ff49aa..16fb534d 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -185,6 +185,7 @@ class DuckGamesDirector(commands.Cog): async def start_game(self, ctx: commands.Context) -> None: """Generate a board, send the game embed, and end the game after a time limit.""" if ctx.channel.id in self.current_games: + await ctx.send("There's already a game running!") return minimum_solutions, = random.choices(range(len(SOLN_DISTR)), weights=SOLN_DISTR) -- cgit v1.2.3 From 3d15c39e04f6d85938450fa212b7b8f36fe21ff2 Mon Sep 17 00:00:00 2001 From: Izan Date: Wed, 25 Aug 2021 21:30:02 +0100 Subject: Fix `CoinSide` error being suppressed Now explicitly passes `None` as default value rather than using `typing.Optional`. --- bot/exts/evergreen/coinflip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/coinflip.py b/bot/exts/evergreen/coinflip.py index 2f9bf38b..c0aa5645 100644 --- a/bot/exts/evergreen/coinflip.py +++ b/bot/exts/evergreen/coinflip.py @@ -1,5 +1,5 @@ import random -from typing import Optional, Tuple +from typing import Tuple from discord.ext import commands @@ -29,7 +29,7 @@ class CoinFlip(commands.Cog): """Cog for the CoinFlip command.""" @commands.command(name="coinflip", aliases=("flip", "coin", "cf")) - async def coinflip_command(self, ctx: commands.Context, side: Optional[CoinSide]) -> None: + async def coinflip_command(self, ctx: commands.Context, side: CoinSide = None) -> None: """ Flips a coin. -- cgit v1.2.3 From c241e2ac3b179de2f6819e28202efc5456ba3298 Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Wed, 25 Aug 2021 17:30:30 -0400 Subject: Send final scores as a new message --- bot/exts/evergreen/duck_game.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 16fb534d..f5f8470b 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -202,7 +202,7 @@ class DuckGamesDirector(commands.Cog): if game.running: try: del self.current_games[ctx.channel.id] - await self.end_game(game, end_message="Time's up!") + await self.end_game(ctx.channel, game, end_message="Time's up!") except KeyError: pass @@ -223,7 +223,7 @@ class DuckGamesDirector(commands.Cog): try: del self.current_games[channel.id] game.scores[msg.author] += CORRECT_GOOSE - await self.end_game(game, end_message=f"{msg.author.display_name} GOOSED!") + await self.end_game(channel, game, end_message=f"{msg.author.display_name} GOOSED!") except KeyError: pass else: @@ -277,16 +277,23 @@ class DuckGamesDirector(commands.Cog): game_embed.set_footer(text=f"{old_footer}\n{str(answer):12s} - {author.display_name}") await self.edit_embed_with_image(game.embed_msg, game_embed) - async def end_game(self, game: DuckGame, end_message: str) -> None: + async def end_game(self, channel: discord.TextChannel, game: DuckGame, end_message: str) -> None: """Edit the game embed to reflect the end of the game and mark the game as not running.""" game.running = False + scoreboard_embed = discord.Embed( + title=end_message, + color=discord.Color.dark_purple(), + ) scores = sorted( game.scores.items(), key=lambda item: item[1], reverse=True, ) - scoreboard = "\n".join(f"{member.display_name}: {score}" for member, score in scores) + scoreboard = "Final scores:\n\n" + scoreboard += "\n".join(f"{member.display_name}: {score}" for member, score in scores) + scoreboard_embed.description = scoreboard + await channel.send(embed=scoreboard_embed) missed = [ans for ans in game.solutions if ans not in game.claimed_answers] if missed: @@ -298,13 +305,11 @@ class DuckGamesDirector(commands.Cog): old_footer = game_embed.footer.text if old_footer == discord.Embed.Empty: old_footer = "" - embed_as_dict = game_embed.to_dict() # Cannot set embed color after initialization embed_as_dict["color"] = discord.Color.red().value game_embed = discord.Embed.from_dict(embed_as_dict) - game_embed.set_footer( - text=f"{old_footer.rstrip()}\n\n{end_message} Here are the scores:\n{scoreboard}\n\n{missed_text}" + text=f"{old_footer.rstrip()}\n\n{missed_text}" ) await self.edit_embed_with_image(game.embed_msg, game_embed) @@ -322,7 +327,7 @@ class DuckGamesDirector(commands.Cog): except KeyError: await ctx.send("No game currently running in this channel") return - await self.end_game(game, end_message="Game canceled.") + await self.end_game(ctx.channel, game, end_message="Game canceled.") @staticmethod async def send_help_embed(ctx: commands.Context) -> discord.Message: -- cgit v1.2.3 From 0df47ccbfc3b399376de1f82e0ee09e96a98f76c Mon Sep 17 00:00:00 2001 From: wookie184 Date: Thu, 26 Aug 2021 13:54:17 +0100 Subject: Make bot debug constant default to true --- bot/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/constants.py b/bot/constants.py index bb602361..6323af80 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -134,7 +134,7 @@ class Client(NamedTuple): prefix = environ.get("PREFIX", ".") token = environ.get("BOT_TOKEN") sentry_dsn = environ.get("BOT_SENTRY_DSN") - debug = environ.get("BOT_DEBUG", "").lower() == "true" + debug = environ.get("BOT_DEBUG", "true").lower() == "true" github_bot_repo = "https://github.com/python-discord/sir-lancebot" # Override seasonal locks: 1 (January) to 12 (December) month_override = int(environ["MONTH_OVERRIDE"]) if "MONTH_OVERRIDE" in environ else None -- cgit v1.2.3 From 48d43ea492a5f4bbbf67dd32dc6e5bff4f5053d2 Mon Sep 17 00:00:00 2001 From: wookie184 Date: Thu, 26 Aug 2021 13:57:30 +0100 Subject: Limit internal eval commands to owner if bot in debug mode --- bot/exts/internal_eval/_internal_eval.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/exts/internal_eval/_internal_eval.py b/bot/exts/internal_eval/_internal_eval.py index 56bf5add..b7749144 100644 --- a/bot/exts/internal_eval/_internal_eval.py +++ b/bot/exts/internal_eval/_internal_eval.py @@ -7,7 +7,7 @@ import discord from discord.ext import commands from bot.bot import Bot -from bot.constants import Roles +from bot.constants import Client, Roles from bot.utils.decorators import with_role from bot.utils.extensions import invoke_help_command from ._helpers import EvalContext @@ -41,6 +41,9 @@ class InternalEval(commands.Cog): self.bot = bot self.locals = {} + if Client.debug: + self.internal_group.add_check(commands.is_owner().predicate) + @staticmethod def shorten_output( output: str, -- cgit v1.2.3 From 12177525653abfdb36492fa707f75cf7eba714b4 Mon Sep 17 00:00:00 2001 From: camcaswell <38672443+camcaswell@users.noreply.github.com> Date: Thu, 26 Aug 2021 15:27:02 -0400 Subject: Change block comment style Co-authored-by: wookie184 --- bot/exts/evergreen/duck_game.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index f5f8470b..d6358837 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -196,9 +196,8 @@ class DuckGamesDirector(commands.Cog): game.embed_msg = await self.send_board_embed(ctx, game) await asyncio.sleep(GAME_DURATION) - """Checking for the channel ID in the currently running games is not sufficient. - The game could have been ended by a player, and a new game already started in the same channel. - """ + # Checking for the channel ID in the currently running games is not sufficient. + # The game could have been ended by a player, and a new game already started in the same channel. if game.running: try: del self.current_games[ctx.channel.id] -- cgit v1.2.3 From a6a0b3377eb800ab3c986d784b785501d5a5e60d Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 26 Aug 2021 15:30:54 -0400 Subject: Use MODERATION_ROLES for stop command --- bot/exts/evergreen/duck_game.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index d6358837..b83d5951 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -12,7 +12,7 @@ from PIL import Image, ImageDraw, ImageFont from discord.ext import commands from bot.bot import Bot -from bot.constants import Colours, Roles +from bot.constants import Colours, MODERATION_ROLES from bot.utils.decorators import with_role @@ -318,7 +318,7 @@ class DuckGamesDirector(commands.Cog): await self.send_help_embed(ctx) @start_game.command(name="stop") - @with_role(Roles.moderator) + @with_role(*MODERATION_ROLES) async def stop_game(self, ctx: commands.Context) -> None: """Stop a currently running game. Only available to mods.""" try: -- cgit v1.2.3 From aec70eef7284fcac3770910148dd832f9ad5788d Mon Sep 17 00:00:00 2001 From: Cam Caswell Date: Thu, 26 Aug 2021 15:41:16 -0400 Subject: Make some return type annotations accurate --- bot/exts/evergreen/duck_game.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index b83d5951..51e7a98a 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -341,14 +341,14 @@ class DuckGamesDirector(commands.Cog): embed.set_footer( text="Tip: using Discord's compact message display mode can help keep the board on the screen" ) - await ctx.send(file=file, embed=embed) + return await ctx.send(file=file, embed=embed) @staticmethod - async def edit_embed_with_image(msg: discord.Message, embed: discord.Embed) -> discord.Message: + async def edit_embed_with_image(msg: discord.Message, embed: discord.Embed) -> None: """Edit an embed without the attached image going wonky.""" attach_name = urlparse(embed.image.url).path.split("/")[-1] embed.set_image(url=f"attachment://{attach_name}") - return await msg.edit(embed=embed) + await msg.edit(embed=embed) def setup(bot: Bot) -> None: -- cgit v1.2.3 From cc5364421c69e8ad533483591cb37b063b7ea5a6 Mon Sep 17 00:00:00 2001 From: wookie184 Date: Fri, 27 Aug 2021 13:56:26 +0100 Subject: Escape markdown properly in post body to fix rendering on android --- bot/exts/evergreen/reddit.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/reddit.py b/bot/exts/evergreen/reddit.py index e57fa2c0..4df170c6 100644 --- a/bot/exts/evergreen/reddit.py +++ b/bot/exts/evergreen/reddit.py @@ -79,7 +79,8 @@ class Reddit(Cog): text = data["selftext"] if text: - first_page += textwrap.shorten(text, width=100, placeholder="...").replace("*", "") + "\n" + text = escape_markdown(text).replace("[", "⦋").replace("]", "⦌") + first_page += textwrap.shorten(text, width=100, placeholder="...") + "\n" ups = data["ups"] comments = data["num_comments"] -- cgit v1.2.3 From 6a92d64d9f113a02178cf05fd34c4169446c1842 Mon Sep 17 00:00:00 2001 From: Izan Date: Wed, 25 Aug 2021 21:37:57 +0100 Subject: General improvements Fix typo in docstring, change logic for creating message to be smarter, change CoinSide converter logic to be smarter --- bot/exts/evergreen/coinflip.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/bot/exts/evergreen/coinflip.py b/bot/exts/evergreen/coinflip.py index c0aa5645..3a2ece03 100644 --- a/bot/exts/evergreen/coinflip.py +++ b/bot/exts/evergreen/coinflip.py @@ -18,11 +18,10 @@ class CoinSide(commands.Converter): if side in CoinSide.HEADS: return "heads" - elif side in CoinSide.TAILS: + if side in CoinSide.TAILS: return "tails" - else: - raise commands.BadArgument(f"{side!r} is not a valid coin side.") + raise commands.BadArgument(f"{side!r} is not a valid coin side.") class CoinFlip(commands.Cog): @@ -33,16 +32,14 @@ class CoinFlip(commands.Cog): """ Flips a coin. - If `coin_side` is provided will state whether you guessed the side correctly. + If `side` is provided will state whether you guessed the side correctly. """ flipped_side = random.choice(["heads", "tails"]) - if not side: - await ctx.send(f"{ctx.author.mention} flipped **{flipped_side}**!") - return - message = f"{ctx.author.mention} flipped **{flipped_side}**. " - if side == flipped_side: + if not side: + await ctx.send(message) + elif side == flipped_side: message += f"You guessed correctly! {Emojis.lemon_hyperpleased}" else: message += f"You guessed incorrectly. {Emojis.lemon_pensive}" -- cgit v1.2.3 From cdaf8add882470efbe8fd63453e7457d9078b3d8 Mon Sep 17 00:00:00 2001 From: Izan Date: Fri, 27 Aug 2021 22:29:12 +0100 Subject: Fix message getting sent twice Message no longer gets sent twice when no side was provided to the `.coinflip` command --- bot/exts/evergreen/coinflip.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/coinflip.py b/bot/exts/evergreen/coinflip.py index 3a2ece03..bfd04843 100644 --- a/bot/exts/evergreen/coinflip.py +++ b/bot/exts/evergreen/coinflip.py @@ -39,7 +39,9 @@ class CoinFlip(commands.Cog): message = f"{ctx.author.mention} flipped **{flipped_side}**. " if not side: await ctx.send(message) - elif side == flipped_side: + return + + if side == flipped_side: message += f"You guessed correctly! {Emojis.lemon_hyperpleased}" else: message += f"You guessed incorrectly. {Emojis.lemon_pensive}" -- cgit v1.2.3 From d1743b3fb47ef20712c540fcb764b62a7f8df875 Mon Sep 17 00:00:00 2001 From: Izan Date: Fri, 27 Aug 2021 22:50:11 +0100 Subject: Implement requested changes to CoinSide converter Converter is now case-insensitive and uses `self.CONSTANT` instead of `CoinSide.CONSTANT` --- bot/exts/evergreen/coinflip.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/coinflip.py b/bot/exts/evergreen/coinflip.py index bfd04843..d1762463 100644 --- a/bot/exts/evergreen/coinflip.py +++ b/bot/exts/evergreen/coinflip.py @@ -15,10 +15,11 @@ class CoinSide(commands.Converter): async def convert(self, ctx: commands.Context, side: str) -> str: """Converts the provided `side` into the corresponding string.""" - if side in CoinSide.HEADS: + side = side.lower() + if side in self.HEADS: return "heads" - if side in CoinSide.TAILS: + if side in self.TAILS: return "tails" raise commands.BadArgument(f"{side!r} is not a valid coin side.") -- cgit v1.2.3 From 0da256ca8986700e9c288f26b9db9c8e0f842465 Mon Sep 17 00:00:00 2001 From: brad90four <42116429+brad90four@users.noreply.github.com> Date: Sat, 28 Aug 2021 08:37:48 -0400 Subject: Add a Real Python search command (#805) Co-authored-by: Vthechamp22 Co-authored-by: Bluenix --- bot/exts/evergreen/realpython.py | 76 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 bot/exts/evergreen/realpython.py diff --git a/bot/exts/evergreen/realpython.py b/bot/exts/evergreen/realpython.py new file mode 100644 index 00000000..e722dd4b --- /dev/null +++ b/bot/exts/evergreen/realpython.py @@ -0,0 +1,76 @@ +import logging +from html import unescape +from urllib.parse import quote_plus + +from discord import Embed +from discord.ext import commands + +from bot import bot +from bot.constants import Colours + +logger = logging.getLogger(__name__) + + +API_ROOT = "https://realpython.com/search/api/v1/" +ARTICLE_URL = "https://realpython.com{article_url}" +SEARCH_URL = "https://realpython.com/search?q={user_search}" + + +ERROR_EMBED = Embed( + title="Error while searching Real Python", + description="There was an error while trying to reach Real Python. Please try again shortly.", + color=Colours.soft_red, +) + + +class RealPython(commands.Cog): + """User initiated command to search for a Real Python article.""" + + def __init__(self, bot: bot.Bot): + self.bot = bot + + @commands.command(aliases=["rp"]) + @commands.cooldown(1, 10, commands.cooldowns.BucketType.user) + async def realpython(self, ctx: commands.Context, *, user_search: str) -> None: + """Send 5 articles that match the user's search terms.""" + params = {"q": user_search, "limit": 5} + async with self.bot.http_session.get(url=API_ROOT, params=params) as response: + if response.status != 200: + logger.error( + f"Unexpected status code {response.status} from Real Python" + ) + await ctx.send(embed=ERROR_EMBED) + return + + data = await response.json() + + articles = data["results"] + + if len(articles) == 0: + no_articles = Embed( + title=f"No articles found for '{user_search}'", color=Colours.soft_red + ) + await ctx.send(embed=no_articles) + return + + article_embed = Embed( + title="Search results - Real Python", + url=SEARCH_URL.format(user_search=quote_plus(user_search)), + description="Here are the top 5 results:", + color=Colours.orange, + ) + + for article in articles: + article_embed.add_field( + name=unescape(article["title"]), + value=ARTICLE_URL.format(article_url=article["url"]), + inline=False, + ) + article_embed.set_footer(text="Click the links to go to the articles.") + + await ctx.send(embed=article_embed) + + +def setup(bot: bot.Bot) -> None: + """Load the Real Python Cog.""" + bot.add_cog(RealPython(bot)) -- cgit v1.2.3 From 6c63e3cb62525c21f613c3deb9d0c072450acca1 Mon Sep 17 00:00:00 2001 From: Krishna Ramasimha <82328083+KittyBorgX@users.noreply.github.com> Date: Mon, 30 Aug 2021 14:17:53 +0530 Subject: Add baby anaconda to snake hatch (#822) Add baby anaconda to snake hatch, using the anaconda logo --- bot/exts/evergreen/snakes/_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/snakes/_utils.py b/bot/exts/evergreen/snakes/_utils.py index 0a5894b7..f996d7f8 100644 --- a/bot/exts/evergreen/snakes/_utils.py +++ b/bot/exts/evergreen/snakes/_utils.py @@ -55,7 +55,8 @@ snakes = { "Baby Rattle Snake": "https://i.imgur.com/i5jYA8f.png", "Baby Dragon Snake": "https://i.imgur.com/SuMKM4m.png", "Baby Garden Snake": "https://i.imgur.com/5vYx3ah.png", - "Baby Cobra": "https://i.imgur.com/jk14ryt.png" + "Baby Cobra": "https://i.imgur.com/jk14ryt.png", + "Baby Anaconda": "https://i.imgur.com/EpdrnNr.png", } BOARD_TILE_SIZE = 56 # the size of each board tile -- cgit v1.2.3 From 397605b14ecb0262d3dfa904a1661eb2514a1d5e Mon Sep 17 00:00:00 2001 From: brad90four <42116429+brad90four@users.noreply.github.com> Date: Mon, 30 Aug 2021 12:13:29 -0400 Subject: Only send articles to user Added the "kind": "article" parameter to the API request. This should only return articles to the user instead of courses, lessons or quizzes. Also updated the "Here are the top x results" message to handle a variable amount of articles. [Ticket: python-discord#828] --- bot/exts/evergreen/realpython.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/realpython.py b/bot/exts/evergreen/realpython.py index e722dd4b..6d148ad6 100644 --- a/bot/exts/evergreen/realpython.py +++ b/bot/exts/evergreen/realpython.py @@ -33,7 +33,7 @@ class RealPython(commands.Cog): @commands.cooldown(1, 10, commands.cooldowns.BucketType.user) async def realpython(self, ctx: commands.Context, *, user_search: str) -> None: """Send 5 articles that match the user's search terms.""" - params = {"q": user_search, "limit": 5} + params = {"q": user_search, "limit": 5, "kind": "article"} async with self.bot.http_session.get(url=API_ROOT, params=params) as response: if response.status != 200: logger.error( @@ -56,7 +56,7 @@ class RealPython(commands.Cog): article_embed = Embed( title="Search results - Real Python", url=SEARCH_URL.format(user_search=quote_plus(user_search)), - description="Here are the top 5 results:", + description=f"Here are the top {max(5, len(articles))} results:", color=Colours.orange, ) -- cgit v1.2.3 From 990dcf80b0813a85865450c9ad038d3ed620ec08 Mon Sep 17 00:00:00 2001 From: brad90four <42116429+brad90four@users.noreply.github.com> Date: Mon, 30 Aug 2021 12:37:26 -0400 Subject: Correct article length check with future proofing Co-authored-by: wookie184 --- bot/exts/evergreen/realpython.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/realpython.py b/bot/exts/evergreen/realpython.py index 6d148ad6..6ef750a9 100644 --- a/bot/exts/evergreen/realpython.py +++ b/bot/exts/evergreen/realpython.py @@ -56,7 +56,7 @@ class RealPython(commands.Cog): article_embed = Embed( title="Search results - Real Python", url=SEARCH_URL.format(user_search=quote_plus(user_search)), - description=f"Here are the top {max(5, len(articles))} results:", + description=f"Here are the top {len(articles)} results:", color=Colours.orange, ) -- cgit v1.2.3 From 0f63eea5090574123359e84b1ada0efd767cd0c8 Mon Sep 17 00:00:00 2001 From: brad90four <42116429+brad90four@users.noreply.github.com> Date: Mon, 30 Aug 2021 14:48:30 -0400 Subject: Handle single article result --- bot/exts/evergreen/realpython.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/realpython.py b/bot/exts/evergreen/realpython.py index 6ef750a9..5d9e5c5c 100644 --- a/bot/exts/evergreen/realpython.py +++ b/bot/exts/evergreen/realpython.py @@ -53,10 +53,15 @@ class RealPython(commands.Cog): await ctx.send(embed=no_articles) return + if len(articles) == 1: + article_description = "Here is the result:" + else: + article_description = f"Here are the top {len(articles)} results:" + article_embed = Embed( title="Search results - Real Python", url=SEARCH_URL.format(user_search=quote_plus(user_search)), - description=f"Here are the top {len(articles)} results:", + description=article_description, color=Colours.orange, ) -- cgit v1.2.3