From 0e0f5ca437176cec90db7203fb49004b78de4acc Mon Sep 17 00:00:00 2001 From: Suhail Date: Thu, 14 Mar 2019 21:37:35 +0000 Subject: Spooky Rating Added the cog containing the spookyrating command and added the JSON file containing all the ratings --- bot/resources/halloween/spooky_rating.json | 47 ++++++++++++++++++ bot/seasons/halloween/spookyrating.py | 77 ++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 bot/resources/halloween/spooky_rating.json create mode 100644 bot/seasons/halloween/spookyrating.py (limited to 'bot') diff --git a/bot/resources/halloween/spooky_rating.json b/bot/resources/halloween/spooky_rating.json new file mode 100644 index 00000000..2c459926 --- /dev/null +++ b/bot/resources/halloween/spooky_rating.json @@ -0,0 +1,47 @@ +{ + "0": { + "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://www.thoughtco.com/thmb/qv6FUFM6V_p7YUAk9lT6fZG8s3M=/768x0/filters:no_upscale():max_bytes(150000):strip_icc()/smoke-trailing-from-extinguished-white-candle-565954737-584daec23df78c491e6282a5.jpg" + }, + "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://static3.babygagaimages.com/wordpress/wp-content/uploads/2017/01/78ibcx9ire.jpg" + }, + "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://www.kimballstock.com/pix/TGR/04/TGR-04-AC0011-01P.JPG" + }, + "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://inst-2.cdn.shockers.de/hs_cdn/out/pictures//master/product/1/3d-skelett-kostuem-adult--skelett-overall-fuer-halloween--skeleton-jumpsuit-costume--15555.jpg" + }, + "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://d2v9y0dukr6mq2.cloudfront.net/video/thumbnail/B3mqp6o/videoblocks-shadow-silhouette-of-a-dark-halloween-ghost-dancing-in-fog-behind-the-glass_sfmu3or_g_thumbnail-full01.png" + }, + "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://gswiki.play.net/images/thumb/7/7e/Necromancer.jpg/450px-Necromancer.jpg" + }, + "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://www.indiewire.com/wp-content/uploads/2018/07/pennywise-bill-skarsgard-it-movie.jpg?w=780" + }, + "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://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX6978186.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://images.drawinghub.com/13941_501/how-to-draw-a-devil-face_5afe20070a9c10.00090710_10491_3_4.png" + } +} \ No newline at end of file diff --git a/bot/seasons/halloween/spookyrating.py b/bot/seasons/halloween/spookyrating.py new file mode 100644 index 00000000..92f71b9e --- /dev/null +++ b/bot/seasons/halloween/spookyrating.py @@ -0,0 +1,77 @@ +import bisect +import json +import logging +import random +from pathlib import Path + +import discord +from discord.ext import commands + +from bot.constants import Colours + +log = logging.getLogger(__name__) + +with Path('bot', 'resources', 'halloween', 'spooky_rating.json').open() as file: + SPOOKY_DATA = json.load(file) + SPOOKY_DATA = sorted((int(key), value) for key, value in SPOOKY_DATA.items()) + + +class SpookyRating: + """ + A cog for calculating one's spooky rating + """ + + def __init__(self, bot): + self.bot = bot + + @commands.command() + @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) + async def spookyrating(self, ctx, who: discord.Member = None): + """ + Calculates the spooky rating of someone. + + This command accepts an optional user as an argument, defaulting to the command executor. + Users are converted from: + - User ID + - Mention + - name#discrim + - name + - nickname + + Any user will always yield the same result, no matter who calls the command + """ + + if who is None: + who = ctx.author + + # This ensures that the same result over multiple runtimes + random.seed(who.id) + + spooky_percent = random.randint(1, 100) + + # We need the -1 due to how bisect returns the point + # see the documentation for further detail + # https://docs.python.org/3/library/bisect.html#bisect.bisect + index = bisect.bisect(SPOOKY_DATA, (spooky_percent,)) - 1 + + _, data = SPOOKY_DATA[index] + + embed = discord.Embed( + title=data['title'], + description=f'{who} scored {spooky_percent}%!', + color=Colours.orange + ) + embed.add_field( + name='A whisper from Satan', + value=data['text'] + ) + embed.set_thumbnail( + url=data['image'] + ) + + await ctx.send(embed=embed) + + +def setup(bot): + bot.add_cog(SpookyRating(bot)) + log.info("SpookyRating cog loaded") -- cgit v1.2.3 From 92e7e111f686c7078e5c6a3720514379d69ce02a Mon Sep 17 00:00:00 2001 From: Suhail Date: Thu, 14 Mar 2019 22:19:53 +0000 Subject: Time Left Adds the cog containing the command --- bot/seasons/halloween/timeleft.py | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 bot/seasons/halloween/timeleft.py (limited to 'bot') diff --git a/bot/seasons/halloween/timeleft.py b/bot/seasons/halloween/timeleft.py new file mode 100644 index 00000000..b28f94f0 --- /dev/null +++ b/bot/seasons/halloween/timeleft.py @@ -0,0 +1,53 @@ +import datetime +import logging + +from discord.ext import commands + +log = logging.getLogger(__name__) + + +class TimeLeft: + """ + A Cog that tells you how long left until Hacktober is over! + """ + + def __init__(self, bot): + self.bot = bot + + @staticmethod + def in_october(): + return datetime.datetime.now().month == 10 + + @staticmethod + def load_date(): + now = datetime.datetime.now() + year = now.year + if now.month > 10: + year += 1 + end = datetime.datetime(year, 10, 31, 11, 59, 59) + return now, end + + @commands.command() + async def timeleft(self, ctx): + """ + Calculates the time left until the end of Hacktober + + Whilst in October, displays the days, hours and minutes left. + Only displays the days left whilst in a different month + """ + + now, end = self.load_date() + diff = end - now + days, seconds = diff.days, diff.seconds + if self.in_october(): + minutes = seconds // 60 + hours, minutes = divmod(minutes, 60) + await ctx.send(f"There is currently only {days} days, {hours} hours and {minutes}" + "minutes left until the end of Hacktober.") + else: + await ctx.send(f"It is not currently Hacktober. However, the next one will finish in {days} days.") + + +def setup(bot): + bot.add_cog(TimeLeft(bot)) + log.info("TimeLeft cog loaded") -- cgit v1.2.3 From f0541bdb67ff8fb9c4b8028c23d6870d011847ed Mon Sep 17 00:00:00 2001 From: sco1 Date: Mon, 18 Mar 2019 21:37:11 -0700 Subject: Docstring pass for top-level bot functions --- bot/bot.py | 14 +++---- bot/decorators.py | 10 ++++- bot/pagination.py | 75 ++++++++++++++++++++----------------- bot/seasons/season.py | 102 ++++++++++++++++++++------------------------------ 4 files changed, 95 insertions(+), 106 deletions(-) (limited to 'bot') diff --git a/bot/bot.py b/bot/bot.py index 3cc57c3f..2885379c 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -16,6 +16,8 @@ __all__ = ('SeasonalBot',) class SeasonalBot(Bot): + """Base bot instance.""" + def __init__(self, **kwargs): super().__init__(**kwargs) self.http_session = ClientSession( @@ -26,9 +28,7 @@ class SeasonalBot(Bot): ) def load_extensions(self, exts: List[str]): - """ - Unload all current cogs, then load in the ones passed into `cogs` - """ + """Unload all current cogs, then load in the ones passed into `cogs`.""" # Unload all cogs extensions = list(self.extensions.keys()) @@ -46,9 +46,8 @@ class SeasonalBot(Bot): log.error(f'Failed to load extension {cog}: {repr(e)} {format_exc()}') async def send_log(self, title: str, details: str = None, *, icon: str = None): - """ - Send an embed message to the devlog channel - """ + """Send an embed message to the devlog channel.""" + devlog = self.get_channel(constants.Channels.devlog) if not devlog: @@ -64,7 +63,8 @@ class SeasonalBot(Bot): await devlog.send(embed=embed) async def on_command_error(self, context, exception): - # Don't punish the user for getting the arguments wrong + """Check command errors for UserInputError and reset the cooldown if thrown.""" + if isinstance(exception, commands.UserInputError): context.command.reset_cooldown(context) else: diff --git a/bot/decorators.py b/bot/decorators.py index f5ffadf4..15f7fed2 100644 --- a/bot/decorators.py +++ b/bot/decorators.py @@ -14,6 +14,8 @@ log = logging.getLogger(__name__) def with_role(*role_ids: int): + """Check to see whether the invoking user has any of the roles specified in role_ids.""" + async def predicate(ctx: Context): if not ctx.guild: # Return False in a DM log.debug(f"{ctx.author} tried to use the '{ctx.command.name}'command from a DM. " @@ -32,6 +34,8 @@ def with_role(*role_ids: int): def without_role(*role_ids: int): + """Check whether the invoking user does not have all of the roles specified in role_ids.""" + async def predicate(ctx: Context): if not ctx.guild: # Return False in a DM log.debug(f"{ctx.author} tried to use the '{ctx.command.name}' command from a DM. " @@ -47,6 +51,8 @@ def without_role(*role_ids: int): def in_channel(channel_id): + """Check that the command invocation is in the channel specified by channel_id.""" + async def predicate(ctx: Context): check = ctx.channel.id == channel_id log.debug(f"{ctx.author} tried to call the '{ctx.command.name}' command. " @@ -58,8 +64,8 @@ def in_channel(channel_id): def locked(): """ Allows the user to only run one instance of the decorated command at a time. - Subsequent calls to the command from the same author are - ignored until the command has completed invocation. + + Subsequent calls to the command from the same author are ignored until the command has completed invocation. This decorator has to go before (below) the `command` decorator. """ diff --git a/bot/pagination.py b/bot/pagination.py index 0ad5b81f..3916809d 100644 --- a/bot/pagination.py +++ b/bot/pagination.py @@ -18,6 +18,8 @@ log = logging.getLogger(__name__) class EmptyPaginatorEmbed(Exception): + """Base Exception class for an empty paginator embed.""" + pass @@ -37,14 +39,13 @@ class LinePaginator(Paginator): The maximum amount of lines allowed in a page. """ - def __init__(self, prefix='```', suffix='```', - max_size=2000, max_lines=None): + def __init__(self, prefix='```', suffix='```', max_size=2000, max_lines=None): """ - This function overrides the Paginator.__init__ - from inside discord.ext.commands. - It overrides in order to allow us to configure - the maximum number of lines per page. + Overrides the Paginator.__init__ from inside discord.ext.commands. + + Allows for configuration of the maximum number of lines per page. """ + self.prefix = prefix self.suffix = suffix self.max_size = max_size - len(suffix) @@ -55,15 +56,13 @@ class LinePaginator(Paginator): self._pages = [] def add_line(self, line='', *, empty=False): - """Adds a line to the current page. + """ + Adds a line to the current page. - If the line exceeds the :attr:`max_size` then an exception - is raised. + If the line exceeds the :attr:`max_size` then an exception is raised. - This function overrides the Paginator.add_line - from inside discord.ext.commands. - It overrides in order to allow us to configure - the maximum number of lines per page. + Overrides the Paginator.add_line from inside discord.ext.commands in order to allow + configuration of the maximum number of lines per page. Parameters ----------- @@ -77,6 +76,7 @@ class LinePaginator(Paginator): RuntimeError The line was too big for the current :attr:`max_size`. """ + 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)) @@ -98,21 +98,26 @@ class LinePaginator(Paginator): @classmethod async def paginate(cls, lines: Iterable[str], ctx: Context, embed: Embed, - 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): + 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): """ - Use a paginator and set of reactions to provide pagination over a set of lines. The reactions are used to - switch page, or to finish with pagination. - When used, this will send a message using `ctx.send()` and apply a set of reactions to it. These reactions may - be used to change page, or to remove pagination from the message. Pagination will also be removed automatically - if no reaction is added for five minutes (300 seconds). + Use a paginator and set of reactions to provide pagination over a set of lines. + + The reactions are used to switch page, or to finish with pagination. + + When used, this will send a message using `ctx.send()` and apply a set of reactions to it. + These reactions may be used to change page, or to remove pagination from the message. + Pagination will also be removed automatically if no reaction is added for five minutes (300 seconds). + >>> embed = Embed() >>> embed.set_author(name="Some Operation", url=url, icon_url=icon) >>> await LinePaginator.paginate( ... (line for line in lines), ... ctx, embed ... ) + :param lines: The lines to be paginated :param ctx: Current context object :param embed: A pre-configured embed to be used as a template for each page @@ -129,9 +134,7 @@ class LinePaginator(Paginator): """ def event_check(reaction_: Reaction, user_: Member): - """ - Make sure that this reaction is what we want to operate on - """ + """Make sure that this reaction is what we want to operate on.""" no_restrictions = ( # Pagination is not restricted @@ -301,6 +304,7 @@ class LinePaginator(Paginator): class ImagePaginator(Paginator): """ Helper class that paginates images for embeds in messages. + Close resemblance to LinePaginator, except focuses on images over text. Refer to ImagePaginator.paginate for documentation on how to use. @@ -314,7 +318,8 @@ class ImagePaginator(Paginator): def add_line(self, line: str = '', *, empty: bool = False) -> None: """ - Adds a line to each page, usually just 1 line in this context + Adds a line to each page, usually just 1 line in this context. + :param line: str to be page content / title :param empty: if there should be new lines between entries """ @@ -328,7 +333,8 @@ class ImagePaginator(Paginator): def add_image(self, image: str = None) -> None: """ - Adds an image to a page + Adds an image to a page. + :param image: image url to be appended """ @@ -339,16 +345,14 @@ class ImagePaginator(Paginator): prefix: str = "", suffix: str = "", timeout: int = 300, exception_on_empty_embed: bool = False): """ - Use a paginator and set of reactions to provide - pagination over a set of title/image pairs.The reactions are - used to switch page, or to finish with pagination. + Use a paginator and set of reactions to provide pagination over a set of title/image pairs. - When used, this will send a message using `ctx.send()` and - apply a set of reactions to it. These reactions may - be used to change page, or to remove pagination from the message. + The reactions are used to switch page, or to finish with pagination. - Note: Pagination will be removed automatically - if no reaction is added for five minutes (300 seconds). + When used, this will send a message using `ctx.send()` and apply a set of reactions to it. + These reactions may be used to change page, or to remove pagination from the message. + + Note: Pagination will be removed automatically if no reaction is added for five minutes (300 seconds). >>> embed = Embed() >>> embed.set_author(name="Some Operation", url=url, icon_url=icon) @@ -366,7 +370,8 @@ class ImagePaginator(Paginator): def check_event(reaction_: Reaction, member: Member) -> bool: """ - Checks each reaction added, if it matches our conditions pass the wait_for + Checks each reaction added, if it matches our conditions pass the wait_for. + :param reaction_: reaction added :param member: reaction added by member """ diff --git a/bot/seasons/season.py b/bot/seasons/season.py index ae12057f..9dac51e2 100644 --- a/bot/seasons/season.py +++ b/bot/seasons/season.py @@ -19,9 +19,7 @@ log = logging.getLogger(__name__) def get_seasons() -> List[str]: - """ - Returns all the Season objects located in bot/seasons/ - """ + """Returns all the Season objects located in /bot/seasons/.""" seasons = [] @@ -32,9 +30,7 @@ def get_seasons() -> List[str]: def get_season_class(season_name: str) -> Type["SeasonBase"]: - """ - Get's the season class of the season module. - """ + """Gets the season class of the season module.""" season_lib = importlib.import_module(f"bot.seasons.{season_name}") class_name = season_name.replace("_", " ").title().replace(" ", "") @@ -42,9 +38,7 @@ def get_season_class(season_name: str) -> Type["SeasonBase"]: def get_season(season_name: str = None, date: datetime.datetime = None) -> "SeasonBase": - """ - Returns a Season object based on either a string or a date. - """ + """Returns a Season object based on either a string or a date.""" # If either both or neither are set, raise an error. if not bool(season_name) ^ bool(date): @@ -78,9 +72,7 @@ def get_season(season_name: str = None, date: datetime.datetime = None) -> "Seas class SeasonBase: - """ - Base class for Seasonal classes. - """ + """Base class for Seasonal classes.""" name: Optional[str] = "evergreen" bot_name: str = "SeasonalBot" @@ -96,9 +88,7 @@ class SeasonBase: @staticmethod def current_year() -> int: - """ - Returns the current year. - """ + """Returns the current year.""" return datetime.date.today().year @@ -107,8 +97,7 @@ class SeasonBase: """ Returns the start date using current year and start_date attribute. - If no start_date was defined, returns the minimum datetime to ensure - it's always below checked dates. + If no start_date was defined, returns the minimum datetime to ensure it's always below checked dates. """ if not cls.start_date: @@ -120,8 +109,7 @@ class SeasonBase: """ Returns the start date using current year and end_date attribute. - If no end_date was defined, returns the minimum datetime to ensure - it's always above checked dates. + If no end_date was defined, returns the minimum datetime to ensure it's always above checked dates. """ if not cls.end_date: @@ -130,37 +118,36 @@ class SeasonBase: @classmethod def is_between_dates(cls, date: datetime.datetime) -> bool: - """ - Determines if the given date falls between the season's date range. - """ + """Determines if the given date falls between the season's date range.""" return cls.start() <= date <= cls.end() @property def name_clean(self) -> str: + """Return the Season's name with underscores replaced by whitespace.""" + return self.name.replace("_", " ").title() @property def greeting(self) -> str: """ - Provides a default greeting based on the season name if one wasn't - defined in the season class. + Provides a default greeting based on the season name if one wasn't defined in the season class. - It's recommended to define one in most cases by overwriting this as a - normal attribute in the inhertiting class. + It's recommended to define one in most cases by overwriting this as a normal attribute in the + inheriting class. """ return f"New Season, {self.name_clean}!" async def get_icon(self, avatar: bool = False) -> bytes: """ - Retrieves the icon image from the branding repository, using the - defined icon attribute for the season. If `avatar` is True, uses - optional bot-only avatar icon if present. + Retrieve the season's icon from the branding repository using the Season's icon attribute. + + If `avatar` is True, uses optional bot-only avatar icon if present. - The icon attribute must provide the url path, starting from the master - branch base url, including the starting slash: - `https://raw.githubusercontent.com/python-discord/branding/master` + The icon attribute must provide the url path, starting from the master branch base url, + including the starting slash. + e.g. `/logos/logo_seasonal/valentines/loved_up.png` """ base_url = "https://raw.githubusercontent.com/python-discord/branding/master" @@ -175,8 +162,9 @@ class SeasonBase: async def apply_username(self, *, debug: bool = False) -> Union[bool, None]: """ - Applies the username for the current season. Only changes nickname if - `bool` is False, otherwise only changes the nickname. + Applies the username for the current season. + + Only changes nickname if `bool` is False, otherwise only changes the nickname. Returns True if it successfully changed the username. Returns False if it failed to change the username, falling back to nick. @@ -216,7 +204,9 @@ class SeasonBase: async def apply_avatar(self) -> bool: """ - Applies the avatar for the current season. Returns if it was successful. + Applies the avatar for the current season. + + Returns True if successful. """ # track old avatar hash for later comparison @@ -238,7 +228,9 @@ class SeasonBase: async def apply_server_icon(self) -> bool: """ - Applies the server icon for the current season. Returns if it was successful. + Applies the server icon for the current season. + + Returns True if was successful. """ guild = bot.get_guild(Client.guild) @@ -265,8 +257,7 @@ class SeasonBase: """ Announces a change in season in the announcement channel. - It will skip the announcement if the current active season is the - "evergreen" default season. + It will skip the announcement if the current active season is the "evergreen" default season. """ # don't actually announce if reverting to normal season @@ -353,9 +344,7 @@ class SeasonBase: class SeasonManager: - """ - A cog for managing seasons. - """ + """A cog for managing seasons.""" def __init__(self, bot): self.bot = bot @@ -375,6 +364,8 @@ class SeasonManager: self.sleep_time = (midnight - datetime.datetime.now()).seconds + 60 async def load_seasons(self): + """Asynchronous timer loop to check for a new season every midnight.""" + await self.bot.wait_until_ready() await self.season.load() @@ -390,9 +381,7 @@ class SeasonManager: @with_role(Roles.moderator, Roles.admin, Roles.owner) @commands.command(name="season") async def change_season(self, ctx, new_season: str): - """ - Changes the currently active season on the bot. - """ + """Changes the currently active season on the bot.""" self.season = get_season(season_name=new_season) await self.season.load() @@ -401,9 +390,7 @@ class SeasonManager: @with_role(Roles.moderator, Roles.admin, Roles.owner) @commands.command(name="seasons") async def show_seasons(self, ctx): - """ - Shows the available seasons and their dates. - """ + """Shows the available seasons and their dates.""" # sort by start order, followed by lower duration def season_key(season_class: Type[SeasonBase]): @@ -447,17 +434,13 @@ class SeasonManager: @with_role(Roles.moderator, Roles.admin, Roles.owner) @commands.group() async def refresh(self, ctx): - """ - Refreshes certain seasonal elements without reloading seasons. - """ + """Refreshes certain seasonal elements without reloading seasons.""" if not ctx.invoked_subcommand: await ctx.invoke(bot.get_command("help"), "refresh") @refresh.command(name="avatar") async def refresh_avatar(self, ctx): - """ - Re-applies the bot avatar for the currently loaded season. - """ + """Re-applies the bot avatar for the currently loaded season.""" # attempt the change is_changed = await self.season.apply_avatar() @@ -481,9 +464,7 @@ class SeasonManager: @refresh.command(name="icon") async def refresh_server_icon(self, ctx): - """ - Re-applies the server icon for the currently loaded season. - """ + """Re-applies the server icon for the currently loaded season.""" # attempt the change is_changed = await self.season.apply_server_icon() @@ -507,9 +488,7 @@ class SeasonManager: @refresh.command(name="username", aliases=("name",)) async def refresh_username(self, ctx): - """ - Re-applies the bot username for the currently loaded season. - """ + """Re-applies the bot username for the currently loaded season.""" old_username = str(bot.user) old_display_name = ctx.guild.me.display_name @@ -549,9 +528,8 @@ class SeasonManager: @with_role(Roles.moderator, Roles.admin, Roles.owner) @commands.command() async def announce(self, ctx): - """ - Announces the currently loaded season. - """ + """Announces the currently loaded season.""" + await self.season.announce_season() def __unload(self): -- cgit v1.2.3 From 7e1da4fb50fcf10071657bc4c120593930529c9d Mon Sep 17 00:00:00 2001 From: sco1 Date: Tue, 19 Mar 2019 13:28:48 -0400 Subject: Docstring pass for Halloween cogs --- bot/seasons/halloween/__init__.py | 2 + bot/seasons/halloween/candy_collection.py | 48 +++++++++++------------ bot/seasons/halloween/hacktoberstats.py | 63 ++++++++++++++++++------------- bot/seasons/halloween/halloween_facts.py | 17 ++++++--- bot/seasons/halloween/halloweenify.py | 11 +++--- bot/seasons/halloween/monstersurvey.py | 26 +++++++++---- bot/seasons/halloween/scarymovie.py | 19 ++++------ bot/seasons/halloween/spookyavatar.py | 17 ++++----- bot/seasons/halloween/spookygif.py | 10 ++--- bot/seasons/halloween/spookyreact.py | 11 +++--- bot/seasons/halloween/spookysound.py | 16 ++++---- bot/utils/halloween/spookifications.py | 17 ++++++--- 12 files changed, 138 insertions(+), 119 deletions(-) (limited to 'bot') diff --git a/bot/seasons/halloween/__init__.py b/bot/seasons/halloween/__init__.py index 4b371f14..74c962ed 100644 --- a/bot/seasons/halloween/__init__.py +++ b/bot/seasons/halloween/__init__.py @@ -3,6 +3,8 @@ from bot.seasons import SeasonBase class Halloween(SeasonBase): + """Halloween Seasonal event attributes.""" + name = "halloween" bot_name = "Spookybot" greeting = "Happy Halloween!" diff --git a/bot/seasons/halloween/candy_collection.py b/bot/seasons/halloween/candy_collection.py index 80f30a1b..1bfbc00a 100644 --- a/bot/seasons/halloween/candy_collection.py +++ b/bot/seasons/halloween/candy_collection.py @@ -21,6 +21,8 @@ ADD_SKULL_EXISTING_REACTION_CHANCE = 20 # 5% class CandyCollection: + """Candy collection game Cog.""" + def __init__(self, bot): self.bot = bot with open(json_location) as candy: @@ -32,9 +34,7 @@ class CandyCollection: self.get_candyinfo[userid] = userinfo async def on_message(self, message): - """ - Randomly adds candy or skull to certain messages - """ + """Randomly adds candy or skull reaction to non-bot messages in the Event channel.""" # make sure its a human message if message.author.bot: @@ -55,9 +55,7 @@ class CandyCollection: return await message.add_reaction('\N{CANDY}') async def on_reaction_add(self, reaction, user): - """ - Add/remove candies from a person if the reaction satisfies criteria - """ + """Add/remove candies from a person if the reaction satisfies criteria.""" message = reaction.message # check to ensure the reactor is human @@ -105,8 +103,10 @@ class CandyCollection: async def reacted_msg_chance(self, message): """ - Randomly add a skull or candy to a message if there is a reaction there already - (higher probability) + Randomly add a skull or candy reaction to a message if there is a reaction there already. + + This event has a higher probability of occurring than a reaction add to a message without an + existing reaction. """ if random.randint(1, ADD_SKULL_EXISTING_REACTION_CHANCE) == 1: @@ -120,7 +120,8 @@ class CandyCollection: return await message.add_reaction('\N{CANDY}') async def ten_recent_msg(self): - """Get the last 10 messages sent in the channel""" + """Get the last 10 messages sent in the channel.""" + ten_recent = [] recent_msg = max(message.id for message in self.bot._connection._messages @@ -137,9 +138,7 @@ class CandyCollection: return ten_recent async def get_message(self, msg_id): - """ - Get the message from it's ID. - """ + """Get the message from its ID.""" try: o = discord.Object(id=msg_id + 1) @@ -156,15 +155,12 @@ class CandyCollection: return None async def hacktober_channel(self): - """ - Get #hacktoberbot channel from it's id - """ + """Get #hacktoberbot channel from its ID.""" + return self.bot.get_channel(id=Hacktoberfest.channel_id) async def remove_reactions(self, reaction): - """ - Remove all candy/skull reactions - """ + """Remove all candy/skull reactions.""" try: async for user in reaction.users(): @@ -174,26 +170,22 @@ class CandyCollection: pass async def send_spook_msg(self, author, channel, candies): - """ - Send a spooky message - """ + """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.") await channel.send(embed=e) def save_to_json(self): - """ - Save json to the file. - """ + """Save JSON to a local file.""" + with open(json_location, 'w') as outfile: json.dump(self.candy_json, outfile) @commands.command() async def candy(self, ctx): - """ - Get the candy leaderboard and save to json when this is called - """ + """Get the candy leaderboard and save to JSON.""" # use run_in_executor to prevent blocking thing = functools.partial(self.save_to_json) @@ -230,5 +222,7 @@ class CandyCollection: def setup(bot): + """Candy Collection game Cog load.""" + bot.add_cog(CandyCollection(bot)) log.debug("CandyCollection cog loaded") diff --git a/bot/seasons/halloween/hacktoberstats.py b/bot/seasons/halloween/hacktoberstats.py index 41cf10ee..3e2a261d 100644 --- a/bot/seasons/halloween/hacktoberstats.py +++ b/bot/seasons/halloween/hacktoberstats.py @@ -14,6 +14,8 @@ log = logging.getLogger(__name__) class HacktoberStats: + """Hacktoberfest statistics Cog.""" + def __init__(self, bot): self.bot = bot self.link_json = Path("bot", "resources", "github_links.json") @@ -26,11 +28,13 @@ class HacktoberStats: ) async def hacktoberstats_group(self, ctx: commands.Context, github_username: str = None): """ - If invoked without a subcommand or github_username, get the invoking user's stats if - they've linked their Discord name to GitHub using .stats link + Display an embed for a user's Hacktoberfest contributions. - If invoked with a github_username, get that user's contributions + If invoked without a subcommand or github_username, get the invoking user's stats if they've + linked their Discord name to GitHub using .stats link. If invoked with a github_username, + get that user's contributions """ + if not github_username: author_id, author_mention = HacktoberStats._author_mention_from_context(ctx) @@ -51,7 +55,7 @@ class HacktoberStats: @hacktoberstats_group.command(name="link") async def link_user(self, ctx: commands.Context, github_username: str = None): """ - Link the invoking user's Github github_username to their Discord ID + Link the invoking user's Github github_username to their Discord ID. Linked users are stored as a nested dict: { @@ -61,6 +65,7 @@ class HacktoberStats: } } """ + author_id, author_mention = HacktoberStats._author_mention_from_context(ctx) if github_username: if str(author_id) in self.linked_accounts.keys(): @@ -83,9 +88,8 @@ class HacktoberStats: @hacktoberstats_group.command(name="unlink") async def unlink_user(self, ctx: commands.Context): - """ - Remove the invoking user's account link from the log - """ + """Remove the invoking user's account link from the log.""" + author_id, author_mention = HacktoberStats._author_mention_from_context(ctx) stored_user = self.linked_accounts.pop(author_id, None) @@ -100,7 +104,7 @@ class HacktoberStats: def load_linked_users(self) -> typing.Dict: """ - Load list of linked users from local JSON file + Load list of linked users from local JSON file. Linked users are stored as a nested dict: { @@ -110,6 +114,7 @@ class HacktoberStats: } } """ + if self.link_json.exists(): logging.info(f"Loading linked GitHub accounts from '{self.link_json}'") with open(self.link_json, 'r') as fID: @@ -123,7 +128,7 @@ class HacktoberStats: def save_linked_users(self): """ - Save list of linked users to local JSON file + Save list of linked users to local JSON file. Linked users are stored as a nested dict: { @@ -133,6 +138,7 @@ class HacktoberStats: } } """ + logging.info(f"Saving linked_accounts to '{self.link_json}'") with open(self.link_json, 'w') as fID: json.dump(self.linked_accounts, fID, default=str) @@ -140,16 +146,15 @@ class HacktoberStats: async def get_stats(self, ctx: commands.Context, github_username: str): """ - Query GitHub's API for PRs created by a GitHub user during the month of October that - do not have an 'invalid' tag + Query GitHub's API for PRs created by a GitHub user during the month of October. - For example: - !getstats heavysaturn + PRs with the 'invalid' tag are ignored If a valid github_username is provided, an embed is generated and posted to the channel Otherwise, post a helpful error message """ + async with ctx.typing(): prs = await self.get_october_prs(github_username) @@ -160,9 +165,8 @@ class HacktoberStats: await ctx.send(f"No October GitHub contributions found for '{github_username}'") def build_embed(self, github_username: str, prs: typing.List[dict]) -> discord.Embed: - """ - Return a stats embed built from github_username's PRs - """ + """Return a stats embed built from github_username's PRs.""" + logging.info(f"Building Hacktoberfest embed for GitHub user: '{github_username}'") pr_stats = self._summarize_prs(prs) @@ -202,8 +206,9 @@ class HacktoberStats: @staticmethod async def get_october_prs(github_username: str) -> typing.List[dict]: """ - Query GitHub's API for PRs created during the month of October by github_username - that do not have an 'invalid' tag + Query GitHub's API for PRs created during the month of October by github_username. + + PRs with an 'invalid' tag are ignored If PRs are found, return a list of dicts with basic PR information @@ -216,6 +221,7 @@ class HacktoberStats: Otherwise, return None """ + logging.info(f"Generating Hacktoberfest PR query for GitHub user: '{github_username}'") base_url = "https://api.github.com/search/issues?q=" not_label = "invalid" @@ -265,20 +271,21 @@ class HacktoberStats: @staticmethod def _get_shortname(in_url: str) -> str: """ - Extract shortname from https://api.github.com/repos/* URL + Extract shortname from https://api.github.com/repos/* URL. e.g. "https://api.github.com/repos/python-discord/seasonalbot" | V "python-discord/seasonalbot" """ + exp = r"https?:\/\/api.github.com\/repos\/([/\-\_\.\w]+)" return re.findall(exp, in_url)[0] @staticmethod def _summarize_prs(prs: typing.List[dict]) -> typing.Dict: """ - Generate statistics from an input list of PR dictionaries, as output by get_october_prs + Generate statistics from an input list of PR dictionaries, as output by get_october_prs. Return a dictionary containing: { @@ -286,13 +293,14 @@ class HacktoberStats: "top5": [(repo_shortname, ncontributions), ...] } """ + contributed_repos = [pr["repo_shortname"] for pr in prs] return {"n_prs": len(prs), "top5": Counter(contributed_repos).most_common(5)} @staticmethod def _build_top5str(stats: typing.List[tuple]) -> str: """ - Build a string from the Top 5 contributions that is compatible with a discord.Embed field + Build a string from the Top 5 contributions that is compatible with a discord.Embed field. Top 5 contributions should be a list of tuples, as output in the stats dictionary by _summarize_prs @@ -301,6 +309,7 @@ class HacktoberStats: n contribution(s) to [shortname](url) ... """ + baseURL = "https://www.github.com/" contributionstrs = [] for repo in stats['top5']: @@ -311,9 +320,8 @@ class HacktoberStats: @staticmethod def _contributionator(n: int) -> str: - """ - Return "contribution" or "contributions" based on the value of n - """ + """Return "contribution" or "contributions" based on the value of n.""" + if n == 1: return "contribution" else: @@ -321,9 +329,8 @@ class HacktoberStats: @staticmethod def _author_mention_from_context(ctx: commands.Context) -> typing.Tuple: - """ - Return stringified Message author ID and mentionable string from commands.Context - """ + """Return stringified Message author ID and mentionable string from commands.Context.""" + author_id = str(ctx.message.author.id) author_mention = ctx.message.author.mention @@ -331,5 +338,7 @@ class HacktoberStats: def setup(bot): + """Hacktoberstats Cog load.""" + bot.add_cog(HacktoberStats(bot)) log.debug("HacktoberStats cog loaded") diff --git a/bot/seasons/halloween/halloween_facts.py b/bot/seasons/halloween/halloween_facts.py index 098ee432..21c70e76 100644 --- a/bot/seasons/halloween/halloween_facts.py +++ b/bot/seasons/halloween/halloween_facts.py @@ -26,6 +26,7 @@ INTERVAL = timedelta(hours=6).total_seconds() class HalloweenFacts: + """A Cog for displaying interesting facts about Halloween.""" def __init__(self, bot): self.bot = bot @@ -36,31 +37,35 @@ class HalloweenFacts: random.shuffle(self.facts) async def on_ready(self): + """Get event Channel object and initialize fact task loop.""" + self.channel = self.bot.get_channel(Hacktoberfest.channel_id) self.bot.loop.create_task(self._fact_publisher_task()) def random_fact(self): + """Return a random fact from the loaded facts.""" + return random.choice(self.facts) @commands.command(name="spookyfact", aliases=("halloweenfact",), brief="Get the most recent Halloween fact") async def get_random_fact(self, ctx): - """ - Reply with the most recent Halloween fact. - """ + """Reply with the most recent Halloween fact.""" + index, fact = self.random_fact() embed = self._build_embed(index, fact) await ctx.send(embed=embed) @staticmethod def _build_embed(index, fact): - """ - Builds a Discord embed from the given fact and its index. - """ + """Builds a Discord embed from the given fact and its index.""" + emoji = random.choice(SPOOKY_EMOJIS) title = f"{emoji} Halloween Fact #{index + 1}" return discord.Embed(title=title, description=fact, color=PUMPKIN_ORANGE) def setup(bot): + """Halloween facts Cog load.""" + bot.add_cog(HalloweenFacts(bot)) log.debug("HalloweenFacts cog loaded") diff --git a/bot/seasons/halloween/halloweenify.py b/bot/seasons/halloween/halloweenify.py index cda07472..5b710f7f 100644 --- a/bot/seasons/halloween/halloweenify.py +++ b/bot/seasons/halloween/halloweenify.py @@ -11,9 +11,7 @@ log = logging.getLogger(__name__) class Halloweenify: - """ - A cog to change a invokers nickname to a spooky one! - """ + """A cog to change a invokers nickname to a spooky one.""" def __init__(self, bot): self.bot = bot @@ -21,9 +19,8 @@ class Halloweenify: @commands.cooldown(1, 300, BucketType.user) @commands.command() async def halloweenify(self, ctx): - """ - Change your nickname into a much spookier one! - """ + """Change your nickname into a much spookier one.""" + async with ctx.typing(): with open(Path('bot', 'resources', 'halloween', 'halloweenify.json'), 'r') as f: data = load(f) @@ -51,5 +48,7 @@ class Halloweenify: def setup(bot): + """Halloweenify Cog load.""" + bot.add_cog(Halloweenify(bot)) log.debug("Halloweenify cog loaded") diff --git a/bot/seasons/halloween/monstersurvey.py b/bot/seasons/halloween/monstersurvey.py index 08873f24..44400a72 100644 --- a/bot/seasons/halloween/monstersurvey.py +++ b/bot/seasons/halloween/monstersurvey.py @@ -16,8 +16,10 @@ EMOJIS = { class MonsterSurvey: """ - Vote for your favorite monster! - This command allows users to vote for their favorite listed monster. + Vote for your favorite monster. + + This Cog allows users to vote for their favorite listed monster. + Users may change their vote, but only their current vote will be counted. """ @@ -30,12 +32,18 @@ class MonsterSurvey: self.voter_registry = json.load(jason) def json_write(self): + """Write voting results to a local JSON file.""" + log.info("Saved Monster Survey Results") with open(self.registry_location, 'w') as jason: json.dump(self.voter_registry, jason, indent=2) def cast_vote(self, id: int, monster: str): """ + Cast a user's vote for the specified monster. + + If the user has already voted, their existing vote is removed. + :param id: The id of the person voting :param monster: the string key of the json that represents a monster :return: None @@ -50,6 +58,8 @@ class MonsterSurvey: vr[m]['votes'].remove(id) def get_name_by_leaderboard_index(self, n): + """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) @@ -61,9 +71,7 @@ class MonsterSurvey: aliases=('ms',) ) async def monster_group(self, ctx: Context): - """ - The base voting command. If nothing is called, then it will return an embed. - """ + """The base voting command. If nothing is called, then it will return an embed.""" if ctx.invoked_subcommand is None: async with ctx.typing(): @@ -95,8 +103,9 @@ class MonsterSurvey: ) async def monster_vote(self, ctx: Context, name=None): """ - Casts a vote for a particular monster, or displays a list of monsters that can be voted for - if one is not given. + Cast a vote for a particular monster. + + Displays a list of monsters that can be voted for if one is not specified. """ if name is None: @@ -185,6 +194,7 @@ class MonsterSurvey: async def monster_leaderboard(self, ctx: Context): """ Shows the current standings. + :param ctx: :return: """ @@ -214,5 +224,7 @@ class MonsterSurvey: def setup(bot): + """Monster survey Cog load.""" + bot.add_cog(MonsterSurvey(bot)) log.debug("MonsterSurvey cog loaded") diff --git a/bot/seasons/halloween/scarymovie.py b/bot/seasons/halloween/scarymovie.py index b280781e..9108c76f 100644 --- a/bot/seasons/halloween/scarymovie.py +++ b/bot/seasons/halloween/scarymovie.py @@ -14,18 +14,15 @@ TMDB_TOKEN = environ.get('TMDB_TOKEN') class ScaryMovie: - """ - Selects a random scary movie and embeds info into discord chat - """ + """Selects a random scary movie and embeds info into Discord chat.""" def __init__(self, bot): self.bot = bot @commands.command(name='scarymovie', alias=['smovie']) async def random_movie(self, ctx): - """ - Randomly select a scary movie and display information about it. - """ + """Randomly select a scary movie and display information about it.""" + async with ctx.typing(): selection = await self.select_movie() movie_details = await self.format_metadata(selection) @@ -34,9 +31,7 @@ class ScaryMovie: @staticmethod async def select_movie(): - """ - Selects a random movie and returns a json of movie details from TMDb - """ + """Selects a random movie and returns a json of movie details from TMDb.""" url = 'https://api.themoviedb.org/4/discover/movie' params = { @@ -70,9 +65,7 @@ class ScaryMovie: @staticmethod async def format_metadata(movie): - """ - Formats raw TMDb data to be embedded in discord chat - """ + """Formats raw TMDb data to be embedded in discord chat.""" # Build the relevant URLs. movie_id = movie.get("id") @@ -137,5 +130,7 @@ class ScaryMovie: def setup(bot): + """Scary movie Cog load.""" + bot.add_cog(ScaryMovie(bot)) log.debug("ScaryMovie cog loaded") diff --git a/bot/seasons/halloween/spookyavatar.py b/bot/seasons/halloween/spookyavatar.py index a1173740..5c13ad77 100644 --- a/bot/seasons/halloween/spookyavatar.py +++ b/bot/seasons/halloween/spookyavatar.py @@ -13,18 +13,14 @@ log = logging.getLogger(__name__) class SpookyAvatar: - - """ - A cog that spookifies an avatar. - """ + """A cog that spookifies an avatar.""" def __init__(self, bot): self.bot = bot async def get(self, url): - """ - Returns the contents of the supplied url. - """ + """Returns the contents of the supplied url.""" + async with aiohttp.ClientSession() as session: async with session.get(url) as resp: return await resp.read() @@ -32,9 +28,8 @@ class SpookyAvatar: @commands.command(name='savatar', aliases=('spookyavatar', 'spookify'), brief='Spookify an user\'s avatar.') async def spooky_avatar(self, ctx, user: discord.Member = None): - """ - A command to print the user's spookified avatar. - """ + """A command to print the user's spookified avatar.""" + if user is None: user = ctx.message.author @@ -54,5 +49,7 @@ class SpookyAvatar: def setup(bot): + """Spooky avatar Cog load.""" + bot.add_cog(SpookyAvatar(bot)) log.debug("SpookyAvatar cog loaded") diff --git a/bot/seasons/halloween/spookygif.py b/bot/seasons/halloween/spookygif.py index 1233773b..f898d28b 100644 --- a/bot/seasons/halloween/spookygif.py +++ b/bot/seasons/halloween/spookygif.py @@ -10,18 +10,14 @@ log = logging.getLogger(__name__) class SpookyGif: - """ - A cog to fetch a random spooky gif from the web! - """ + """A cog to fetch a random spooky gif from the web.""" def __init__(self, bot): self.bot = bot @commands.command(name="spookygif", aliases=("sgif", "scarygif")) async def spookygif(self, ctx): - """ - Fetches a random gif from the GIPHY API and responds with it. - """ + """Fetches a random gif from the GIPHY API and responds with it.""" async with ctx.typing(): async with aiohttp.ClientSession() as session: @@ -39,5 +35,7 @@ class SpookyGif: def setup(bot): + """Spooky GIF Cog load.""" + bot.add_cog(SpookyGif(bot)) log.debug("SpookyGif cog loaded") diff --git a/bot/seasons/halloween/spookyreact.py b/bot/seasons/halloween/spookyreact.py index f63cd7e5..553bd285 100644 --- a/bot/seasons/halloween/spookyreact.py +++ b/bot/seasons/halloween/spookyreact.py @@ -17,22 +17,20 @@ SPOOKY_TRIGGERS = { class SpookyReact: - - """ - A cog that makes the bot react to message triggers. - """ + """A cog that makes the bot react to message triggers.""" def __init__(self, bot): self.bot = bot async def on_message(self, ctx: discord.Message): """ - A command to send the seasonalbot github project + A command to send the seasonalbot github project. Lines that begin with the bot's command prefix are ignored Seasonalbot's own messages are ignored """ + for trigger in SPOOKY_TRIGGERS.keys(): trigger_test = re.search(SPOOKY_TRIGGERS[trigger][0], ctx.content.lower()) if trigger_test: @@ -52,6 +50,7 @@ class SpookyReact: * author is the bot * 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}") @@ -68,5 +67,7 @@ class SpookyReact: def setup(bot): + """Spooky reaction Cog load.""" + bot.add_cog(SpookyReact(bot)) log.debug("SpookyReact cog loaded") diff --git a/bot/seasons/halloween/spookysound.py b/bot/seasons/halloween/spookysound.py index 4cab1239..cff50897 100644 --- a/bot/seasons/halloween/spookysound.py +++ b/bot/seasons/halloween/spookysound.py @@ -11,9 +11,7 @@ log = logging.getLogger(__name__) class SpookySound: - """ - A cog that plays a spooky sound in a voice channel on command. - """ + """A cog that plays a spooky sound in a voice channel on command.""" def __init__(self, bot): self.bot = bot @@ -24,9 +22,11 @@ class SpookySound: @commands.command(brief="Play a spooky sound, restricted to once per 2 mins") async def spookysound(self, ctx): """ - Connect to the Hacktoberbot voice channel, play a random spooky sound, then disconnect. Cannot be used more than - once in 2 minutes. + Connect to the Hacktoberbot voice channel, play a random spooky sound, then disconnect. + + Cannot be used more than once in 2 minutes. """ + if not self.channel: await self.bot.wait_until_ready() self.channel = self.bot.get_channel(Hacktoberfest.voice_id) @@ -39,12 +39,12 @@ class SpookySound: @staticmethod async def disconnect(voice): - """ - Helper method to disconnect a given voice client. - """ + """Helper method to disconnect a given voice client.""" await voice.disconnect() def setup(bot): + """Spooky sound Cog load.""" + bot.add_cog(SpookySound(bot)) log.debug("SpookySound cog loaded") diff --git a/bot/utils/halloween/spookifications.py b/bot/utils/halloween/spookifications.py index 5f2369ae..390cfa49 100644 --- a/bot/utils/halloween/spookifications.py +++ b/bot/utils/halloween/spookifications.py @@ -8,17 +8,20 @@ log = logging.getLogger() def inversion(im): - """Inverts an image. + """ + Inverts the image. Returns an inverted image when supplied with an Image object. """ + im = im.convert('RGB') inv = ImageOps.invert(im) return inv def pentagram(im): - """Adds pentagram to image.""" + """Adds pentagram to the image.""" + im = im.convert('RGB') wt, ht = im.size penta = Image.open('bot/resources/halloween/bloody-pentagram.png') @@ -28,10 +31,13 @@ def pentagram(im): def bat(im): - """Adds a bat silhoutte to the image. + """ + Adds a bat silhoutte to the 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. + """ - The bat silhoutte is of a size at least one-fifths that of the original - image and may be rotated upto 90 degrees anti-clockwise.""" im = im.convert('RGB') wt, ht = im.size bat = Image.open('bot/resources/halloween/bat-clipart.png') @@ -49,6 +55,7 @@ def bat(im): def get_random_effect(im): """Randomly selects and applies an effect.""" + effects = [inversion, pentagram, bat] effect = choice(effects) log.info("Spookyavatar's chosen effect: " + effect.__name__) -- cgit v1.2.3 From 9ec955812bc160a389a24fe7737e2ab8f9580d3c Mon Sep 17 00:00:00 2001 From: sco1 Date: Tue, 19 Mar 2019 13:38:49 -0400 Subject: Docstring pass for Christmas cogs --- bot/seasons/christmas/__init__.py | 11 ++-- bot/seasons/christmas/adventofcode.py | 118 +++++++++++++++------------------- 2 files changed, 58 insertions(+), 71 deletions(-) (limited to 'bot') diff --git a/bot/seasons/christmas/__init__.py b/bot/seasons/christmas/__init__.py index 99d81b0c..f0a7c2c6 100644 --- a/bot/seasons/christmas/__init__.py +++ b/bot/seasons/christmas/__init__.py @@ -4,12 +4,15 @@ from bot.seasons import SeasonBase class Christmas(SeasonBase): """ - We are getting into the festive spirit with a new server icon, new - bot name and avatar, and some new commands for you to check out! + Christmas seasonal event attributes. - No matter who you are, where you are or what beliefs you may follow, - we hope every one of you enjoy this festive season! + We are getting into the festive spirit with a new server icon, new bot name and avatar, and some + new commands for you to check out! + + No matter who you are, where you are or what beliefs you may follow, we hope every one of you + enjoy this festive season! """ + name = "christmas" bot_name = "Merrybot" greeting = "Happy Holidays!" diff --git a/bot/seasons/christmas/adventofcode.py b/bot/seasons/christmas/adventofcode.py index a926a6cb..2fd474db 100644 --- a/bot/seasons/christmas/adventofcode.py +++ b/bot/seasons/christmas/adventofcode.py @@ -25,19 +25,15 @@ COUNTDOWN_STEP = 60 * 5 def is_in_advent() -> bool: - """ - Utility function to check if we are between December 1st - and December 25th. - """ + """Utility function to check if we are between December 1st and December 25th.""" + # Run the code from the 1st to the 24th return datetime.now(EST).day in range(1, 25) and datetime.now(EST).month == 12 def time_left_to_aoc_midnight() -> Tuple[datetime, timedelta]: - """ - This calculates the amount of time left until midnight in - UTC-5 (Advent of Code maintainer timezone). - """ + """Calculates the amount of time left until midnight in UTC-5 (Advent of Code maintainer timezone).""" + # Change all time properties back to 00:00 todays_midnight = datetime.now(EST).replace(microsecond=0, second=0, @@ -52,10 +48,8 @@ def time_left_to_aoc_midnight() -> Tuple[datetime, timedelta]: async def countdown_status(bot: commands.Bot): - """ - Every `COUNTDOWN_STEP` seconds set the playing status of the bot to - the number of minutes & hours left until the next day's release. - """ + """Set the playing status of the bot to the minutes & hours left until the next day's challenge.""" + while is_in_advent(): _, time_left = time_left_to_aoc_midnight() @@ -83,11 +77,12 @@ async def countdown_status(bot: commands.Bot): async def day_countdown(bot: commands.Bot): """ - Calculate the number of seconds left until the next day of advent. Once - we have calculated this we should then sleep that number and when the time - is reached ping the advent of code role notifying them that the new task is - ready. + Calculate the number of seconds left until the next day of advent. + + Once we have calculated this we should then sleep that number and when the time is reached, ping + the Advent of Code role notifying them that the new challenge is ready. """ + while is_in_advent(): tomorrow, time_left = time_left_to_aoc_midnight() @@ -109,9 +104,8 @@ async def day_countdown(bot: commands.Bot): class AdventOfCode: - """ - Advent of Code festivities! Ho Ho Ho! - """ + """Advent of Code festivities! Ho Ho Ho.""" + def __init__(self, bot: commands.Bot): self.bot = bot @@ -136,9 +130,7 @@ class AdventOfCode: @commands.group(name="adventofcode", aliases=("aoc",), invoke_without_command=True) async def adventofcode_group(self, ctx: commands.Context): - """ - All of the Advent of Code commands - """ + """All of the Advent of Code commands.""" await ctx.invoke(self.bot.get_command("help"), "adventofcode") @@ -148,9 +140,8 @@ class AdventOfCode: brief="Notifications for new days" ) async def aoc_subscribe(self, ctx: commands.Context): - """ - Assign the role for notifications about new days being ready. - """ + """Assign the role for notifications about new days being ready.""" + role = ctx.guild.get_role(AocConfig.role_id) unsubscribe_command = f"{ctx.prefix}{ctx.command.root_parent} unsubscribe" @@ -164,9 +155,8 @@ class AdventOfCode: @adventofcode_group.command(name="unsubscribe", aliases=("unsub",), brief="Notifications for new days") async def aoc_unsubscribe(self, ctx: commands.Context): - """ - Remove the role for notifications about new days being ready. - """ + """Remove the role for notifications about new days being ready.""" + role = ctx.guild.get_role(AocConfig.role_id) if role in ctx.author.roles: @@ -177,9 +167,8 @@ class AdventOfCode: @adventofcode_group.command(name="countdown", aliases=("count", "c"), brief="Return time left until next day") async def aoc_countdown(self, ctx: commands.Context): - """ - Return time left until next day - """ + """Return time left until next day.""" + if not is_in_advent(): datetime_now = datetime.now(EST) december_first = datetime(datetime_now.year + 1, 12, 1, tzinfo=EST) @@ -196,17 +185,13 @@ class AdventOfCode: @adventofcode_group.command(name="about", aliases=("ab", "info"), brief="Learn about Advent of Code") async def about_aoc(self, ctx: commands.Context): - """ - Respond with an explanation of all things Advent of Code - """ + """Respond with an explanation of all things Advent of Code.""" await ctx.send("", embed=self.cached_about_aoc) @adventofcode_group.command(name="join", aliases=("j",), brief="Learn how to join PyDis' private AoC leaderboard") async def join_leaderboard(self, ctx: commands.Context): - """ - DM the user the information for joining the PyDis AoC private leaderboard - """ + """DM the user the information for joining the PyDis AoC private leaderboard.""" author = ctx.message.author log.info(f"{author.name} ({author.id}) has requested the PyDis AoC leaderboard code") @@ -228,7 +213,7 @@ class AdventOfCode: ) async def aoc_leaderboard(self, ctx: commands.Context, number_of_people_to_display: int = 10): """ - Pull the top number_of_people_to_display members from the PyDis leaderboard and post an embed + Pull the top number_of_people_to_display members from the PyDis leaderboard and post an embed. For readability, number_of_people_to_display defaults to 10. A maximum value is configured in the Advent of Code section of the bot constants. number_of_people_to_display values greater than this @@ -270,7 +255,7 @@ class AdventOfCode: ) async def private_leaderboard_daily_stats(self, ctx: commands.Context): """ - Respond with a table of the daily completion statistics for the PyDis private leaderboard + Respond with a table of the daily completion statistics for the PyDis private leaderboard. Embed will display the total members and the number of users who have completed each day's puzzle """ @@ -314,7 +299,7 @@ class AdventOfCode: ) async def global_leaderboard(self, ctx: commands.Context, number_of_people_to_display: int = 10): """ - Pull the top number_of_people_to_display members from the global AoC leaderboard and post an embed + Pull the top number_of_people_to_display members from the global AoC leaderboard and post an embed. For readability, number_of_people_to_display defaults to 10. A maximum value is configured in the Advent of Code section of the bot constants. number_of_people_to_display values greater than this @@ -347,7 +332,7 @@ class AdventOfCode: async def _check_leaderboard_cache(self, ctx, global_board: bool = False): """ - Check age of current leaderboard & pull a new one if the board is too old + Check age of current leaderboard & pull a new one if the board is too old. global_board is a boolean to toggle between the global board and the Pydis private board """ @@ -404,9 +389,7 @@ class AdventOfCode: return number_of_people_to_display def _build_about_embed(self) -> discord.Embed: - """ - Build and return the informational "About AoC" embed from the resources file - """ + """Build and return the informational "About AoC" embed from the resources file.""" with self.about_aoc_filepath.open("r") as f: embed_fields = json.load(f) @@ -421,9 +404,8 @@ class AdventOfCode: return about_embed async def _boardgetter(self, global_board: bool): - """ - Invoke the proper leaderboard getter based on the global_board boolean - """ + """Invoke the proper leaderboard getter based on the global_board boolean.""" + if global_board: self.cached_global_leaderboard = await AocGlobalLeaderboard.from_url() else: @@ -431,6 +413,8 @@ class AdventOfCode: class AocMember: + """Object representing the Advent of Code user.""" + def __init__(self, name: str, aoc_id: int, stars: int, starboard: list, local_score: int, global_score: int): self.name = name self.aoc_id = aoc_id @@ -441,12 +425,14 @@ class AocMember: self.completions = self._completions_from_starboard(self.starboard) def __repr__(self): + """Generate a user-friendly representation of the AocMember & their score.""" + return f"<{self.name} ({self.aoc_id}): {self.local_score}>" @classmethod def member_from_json(cls, injson: dict) -> "AocMember": """ - Generate an AocMember from AoC's private leaderboard API JSON + Generate an AocMember from AoC's private leaderboard API JSON. injson is expected to be the dict contained in: @@ -467,7 +453,7 @@ class AocMember: @staticmethod def _starboard_from_json(injson: dict) -> list: """ - Generate starboard from AoC's private leaderboard API JSON + Generate starboard from AoC's private leaderboard API JSON. injson is expected to be the dict contained in: @@ -500,9 +486,7 @@ class AocMember: @staticmethod def _completions_from_starboard(starboard: list) -> tuple: - """ - Return days completed, as a (1 star, 2 star) tuple, from starboard - """ + """Return days completed, as a (1 star, 2 star) tuple, from starboard.""" completions = [0, 0] for day in starboard: @@ -515,6 +499,8 @@ class AocMember: class AocPrivateLeaderboard: + """Object representing the Advent of Code private leaderboard.""" + def __init__(self, members: list, owner_id: int, event_year: int): self.members = members self._owner_id = owner_id @@ -534,7 +520,7 @@ class AocPrivateLeaderboard: def calculate_daily_completion(self) -> List[tuple]: """ - Calculate member completion rates by day + Calculate member completion rates by day. Return a list of tuples for each day containing the number of users who completed each part of the challenge @@ -560,7 +546,7 @@ class AocPrivateLeaderboard: leaderboard_id: int = AocConfig.leaderboard_id, year: int = AocConfig.year ) -> "AocPrivateLeaderboard": """ - Request the API JSON from Advent of Code for leaderboard_id for the specified year's event + Request the API JSON from Advent of Code for leaderboard_id for the specified year's event. If no year is input, year defaults to the current year """ @@ -580,9 +566,7 @@ class AocPrivateLeaderboard: @classmethod def from_json(cls, injson: dict) -> "AocPrivateLeaderboard": - """ - Generate an AocPrivateLeaderboard object from AoC's private leaderboard API JSON - """ + """Generate an AocPrivateLeaderboard object from AoC's private leaderboard API JSON.""" return cls( members=cls._sorted_members(injson["members"]), owner_id=injson["owner_id"], event_year=injson["event"] @@ -590,9 +574,7 @@ class AocPrivateLeaderboard: @classmethod async def from_url(cls) -> "AocPrivateLeaderboard": - """ - Helper wrapping of AocPrivateLeaderboard.json_from_url and AocPrivateLeaderboard.from_json - """ + """Helper wrapping of AocPrivateLeaderboard.json_from_url and AocPrivateLeaderboard.from_json.""" api_json = await cls.json_from_url() return cls.from_json(api_json) @@ -600,7 +582,7 @@ class AocPrivateLeaderboard: @staticmethod def _sorted_members(injson: dict) -> list: """ - Generate a sorted list of AocMember objects from AoC's private leaderboard API JSON + Generate a sorted list of AocMember objects from AoC's private leaderboard API JSON. Output list is sorted based on the AocMember.local_score """ @@ -613,7 +595,7 @@ class AocPrivateLeaderboard: @staticmethod def build_leaderboard_embed(members_to_print: List[AocMember]) -> str: """ - Build a text table from members_to_print, a list of AocMember objects + Build a text table from members_to_print, a list of AocMember objects. Returns a string to be used as the content of the bot's leaderboard response """ @@ -638,6 +620,8 @@ class AocPrivateLeaderboard: class AocGlobalLeaderboard: + """Object representing the Advent of Code global leaderboard.""" + def __init__(self, members: List[tuple]): self.members = members self.last_updated = datetime.utcnow() @@ -654,7 +638,7 @@ class AocGlobalLeaderboard: @classmethod async def from_url(cls) -> "AocGlobalLeaderboard": """ - Generate an list of tuples for the entries on AoC's global leaderboard + Generate an list of tuples for the entries on AoC's global leaderboard. Because there is no API for this, web scraping needs to be used """ @@ -700,7 +684,7 @@ class AocGlobalLeaderboard: @staticmethod def build_leaderboard_embed(members_to_print: List[tuple]) -> str: """ - Build a text table from members_to_print, a list of tuples + Build a text table from members_to_print, a list of tuples. Returns a string to be used as the content of the bot's leaderboard response """ @@ -721,13 +705,13 @@ class AocGlobalLeaderboard: def _error_embed_helper(title: str, description: str) -> discord.Embed: - """ - Return a red-colored Embed with the given title and description - """ + """Return a red-colored Embed with the given title and description.""" return discord.Embed(title=title, description=description, colour=discord.Colour.red()) def setup(bot: commands.Bot) -> None: + """Advent of Code Cog load.""" + bot.add_cog(AdventOfCode(bot)) log.info("Cog loaded: adventofcode") -- cgit v1.2.3 From 74d6012bc80b93842c6081d115e87d6a8a77b95b Mon Sep 17 00:00:00 2001 From: sco1 Date: Tue, 19 Mar 2019 14:17:59 -0400 Subject: Docstring pass for Evergreen cogs --- bot/seasons/evergreen/__init__.py | 2 + bot/seasons/evergreen/error_handler.py | 6 +- bot/seasons/evergreen/fun.py | 12 ++- bot/seasons/evergreen/snakes/__init__.py | 2 + bot/seasons/evergreen/snakes/converter.py | 15 +++- bot/seasons/evergreen/snakes/snakes_cog.py | 99 +++++++++++++----------- bot/seasons/evergreen/snakes/utils.py | 119 ++++++++++++++++++++--------- bot/seasons/evergreen/uptime.py | 12 ++- 8 files changed, 167 insertions(+), 100 deletions(-) (limited to 'bot') diff --git a/bot/seasons/evergreen/__init__.py b/bot/seasons/evergreen/__init__.py index db610e7c..ac32c199 100644 --- a/bot/seasons/evergreen/__init__.py +++ b/bot/seasons/evergreen/__init__.py @@ -2,4 +2,6 @@ from bot.seasons import SeasonBase class Evergreen(SeasonBase): + """Evergreen Seasonal event attributes.""" + bot_icon = "/logos/logo_seasonal/evergreen/logo_evergreen.png" diff --git a/bot/seasons/evergreen/error_handler.py b/bot/seasons/evergreen/error_handler.py index 47e18a31..dcdbe4e9 100644 --- a/bot/seasons/evergreen/error_handler.py +++ b/bot/seasons/evergreen/error_handler.py @@ -9,13 +9,13 @@ log = logging.getLogger(__name__) class CommandErrorHandler: - """A error handler for the PythonDiscord server!""" + """A error handler for the PythonDiscord server.""" def __init__(self, bot): self.bot = bot async def on_command_error(self, ctx, error): - """Activates when a command opens an error""" + """Activates when a command opens an error.""" if hasattr(ctx.command, 'on_error'): return logging.debug( @@ -108,5 +108,7 @@ class CommandErrorHandler: def setup(bot): + """Error handler Cog load.""" + bot.add_cog(CommandErrorHandler(bot)) log.debug("CommandErrorHandler cog loaded") diff --git a/bot/seasons/evergreen/fun.py b/bot/seasons/evergreen/fun.py index 4da01dd1..f5814a80 100644 --- a/bot/seasons/evergreen/fun.py +++ b/bot/seasons/evergreen/fun.py @@ -9,18 +9,15 @@ log = logging.getLogger(__name__) class Fun: - """ - A collection of general commands for fun. - """ + """A collection of general commands for fun.""" def __init__(self, bot): self.bot = bot @commands.command() async def roll(self, ctx, num_rolls: int = 1): - """ - Outputs a number of random dice emotes (up to 6) - """ + """Outputs a number of random dice emotes (up to 6).""" + output = "" if num_rolls > 6: num_rolls = 6 @@ -32,7 +29,8 @@ class Fun: await ctx.send(output) -# Required in order to load the cog, use the class name in the add_cog function. def setup(bot): + """Fun Cog load.""" + bot.add_cog(Fun(bot)) log.debug("Fun cog loaded") diff --git a/bot/seasons/evergreen/snakes/__init__.py b/bot/seasons/evergreen/snakes/__init__.py index 367aea4d..88793308 100644 --- a/bot/seasons/evergreen/snakes/__init__.py +++ b/bot/seasons/evergreen/snakes/__init__.py @@ -6,5 +6,7 @@ log = logging.getLogger(__name__) def setup(bot): + """Snakes Cog load.""" + bot.add_cog(Snakes(bot)) log.info("Cog loaded: Snakes") diff --git a/bot/seasons/evergreen/snakes/converter.py b/bot/seasons/evergreen/snakes/converter.py index c091d9c1..ec9c9870 100644 --- a/bot/seasons/evergreen/snakes/converter.py +++ b/bot/seasons/evergreen/snakes/converter.py @@ -13,10 +13,14 @@ log = logging.getLogger(__name__) class Snake(Converter): + """Snake converter for the Snakes Cog.""" + snakes = None special_cases = None async def convert(self, ctx, name): + """Convert the input snake name to the closest matching Snake object.""" + await self.build_list() name = name.lower() @@ -56,6 +60,8 @@ class Snake(Converter): @classmethod async def build_list(cls): + """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() as snakefile: @@ -70,11 +76,14 @@ class Snake(Converter): @classmethod async def random(cls): """ - This is stupid. We should find a way to - somehow get the global session into a - global context, so I can get it from here. + Get a random Snake from the loaded resources. + + This is stupid. We should find a way to somehow get the global session into a global context, + so I can get it from here. + :return: """ + await cls.build_list() names = [snake['scientific'] for snake in cls.snakes] return random.choice(names) diff --git a/bot/seasons/evergreen/snakes/snakes_cog.py b/bot/seasons/evergreen/snakes/snakes_cog.py index 57eb7a52..92b28c55 100644 --- a/bot/seasons/evergreen/snakes/snakes_cog.py +++ b/bot/seasons/evergreen/snakes/snakes_cog.py @@ -134,12 +134,11 @@ CARD = { class Snakes: """ - Commands related to snakes. These were created by our - community during the first code jam. + Commands related to snakes, created by our community during the first code jam. More information can be found in the code-jam-1 repo. - https://gitlab_bot_repo.com/discord-python/code-jams/code-jam-1 + https://github.com/python-discord/code-jam-1 """ wiki_brief = re.compile(r'(.*?)(=+ (.*?) =+)', flags=re.DOTALL) @@ -156,9 +155,8 @@ class Snakes: # region: Helper methods @staticmethod def _beautiful_pastel(hue): - """ - Returns random bright pastels. - """ + """Returns random bright pastels.""" + light = random.uniform(0.7, 0.85) saturation = 1 @@ -178,6 +176,7 @@ class Snakes: Written by juan and Someone during the first code jam. """ + snake = Image.open(buffer) # Get the size of the snake icon, configure the height of the image box (yes, it changes) @@ -254,9 +253,8 @@ class Snakes: @staticmethod def _snakify(message): - """ - Sssnakifffiesss a sstring. - """ + """Sssnakifffiesss a sstring.""" + # Replace fricatives with exaggerated snake fricatives. simple_fricatives = [ "f", "s", "z", "h", @@ -278,9 +276,8 @@ class Snakes: return message async def _fetch(self, session, url, params=None): - """ - Asyncronous web request helper method. - """ + """Asyncronous web request helper method.""" + if params is None: params = {} @@ -290,11 +287,11 @@ class Snakes: def _get_random_long_message(self, messages, retries=10): """ - Fetch a message that's at least 3 words long, - but only if it is possible to do so in retries - attempts. Else, just return whatever the last - message is. + Fetch a message that's at least 3 words long, if possible to do so in retries attempts. + + Else, just return whatever the last message is. """ + long_message = random.choice(messages) if len(long_message.split()) < 3 and retries > 0: return self._get_random_long_message( @@ -306,14 +303,16 @@ class Snakes: async def _get_snek(self, name: str) -> Dict[str, Any]: """ - Goes online and fetches all the data from a wikipedia article - about a snake. Builds a dict that the .get() method can use. + Fetches all the data from a wikipedia article about a snake. + + Builds a dict that the .get() method can use. Created by Ava and eivl. :param name: The name of the snake to get information for - omit for a random snake :return: A dict containing information on a snake """ + snake_info = {} async with aiohttp.ClientSession() as session: @@ -412,20 +411,21 @@ class Snakes: async def _get_snake_name(self) -> Dict[str, str]: """ Gets a random snake name. + :return: A random snake name, as a string. """ return random.choice(self.snake_names) async def _validate_answer(self, ctx: Context, message: Message, answer: str, options: list): """ - Validate the answer using a reaction event loop + Validate the answer using a reaction event loop. + :return: """ def predicate(reaction, user): - """ - Test if the the answer is valid and can be evaluated. - """ + """Test if the the answer is valid and can be evaluated.""" + return ( reaction.message.id == message.id # The reaction is attached to the question we asked. and user == ctx.author # It's the user who triggered the quiz. @@ -465,7 +465,7 @@ class Snakes: @locked() async def antidote_command(self, ctx: Context): """ - Antidote - Can you create the antivenom before the patient dies? + Antidote - Can you create the antivenom before the patient dies. Rules: You have 4 ingredients for each antidote, you only have 10 attempts Once you synthesize the antidote, you will be presented with 4 markers @@ -480,9 +480,7 @@ class Snakes: """ def predicate(reaction_: Reaction, user_: Member): - """ - Make sure that this reaction is what we want to operate on - """ + """Make sure that this reaction is what we want to operate on.""" return ( all(( @@ -610,7 +608,7 @@ class Snakes: @snakes_group.command(name='draw') async def draw_command(self, ctx: Context): """ - Draws a random snek using Perlin noise + Draws a random snek using Perlin noise. Written by Momo and kel. Modified by juan and lemon. @@ -652,12 +650,14 @@ class Snakes: async def get_command(self, ctx: Context, *, name: Snake = None): """ Fetches information about a snake from Wikipedia. + :param ctx: Context object passed from discord.py :param name: Optional, the name of the snake to get information for - omit for a random snake Created by Ava and eivl. """ + with ctx.typing(): if name is None: name = await Snake.random() @@ -702,11 +702,12 @@ class Snakes: @locked() async def guess_command(self, ctx): """ - Snake identifying game! + Snake identifying game. Made by Ava and eivl. Modified by lemon. """ + with ctx.typing(): image = None @@ -736,10 +737,11 @@ class Snakes: @snakes_group.command(name='hatch') async def hatch_command(self, ctx: Context): """ - Hatches your personal snake + Hatches your personal snake. Written by Momo and kel. """ + # Pick a random snake to hatch. snake_name = random.choice(list(utils.snakes.keys())) snake_image = utils.snakes[snake_name] @@ -772,6 +774,7 @@ class Snakes: Written by Samuel. Modified by gdude. """ + url = "http://www.omdbapi.com/" page = random.randint(1, 27) @@ -842,6 +845,7 @@ class Snakes: This was created by Mushy and Cardium, and modified by Urthas and lemon. """ + # Prepare a question. question = random.choice(self.snake_quizzes) answer = question["answerkey"] @@ -862,6 +866,8 @@ class Snakes: @snakes_group.command(name='name', aliases=('name_gen',)) async def name_command(self, ctx: Context, *, name: str = None): """ + Snakifies a username. + Slices the users name at the last vowel (or second last if the name ends with a vowel), and then combines it with a random snake name, which is sliced at the first vowel (or second if the name starts with @@ -880,6 +886,7 @@ class Snakes: 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_prefix = "" @@ -932,11 +939,12 @@ class Snakes: @locked() async def sal_command(self, ctx: Context): """ - Play a game of Snakes and Ladders! + Play a game of Snakes and Ladders. Written by Momo and kel. Modified by lemon. """ + # check if there is already a game in this channel if ctx.channel in self.active_sal: await ctx.send(f"{ctx.author.mention} A game is already in progress in this channel.") @@ -949,10 +957,8 @@ class Snakes: @snakes_group.command(name='about') async def about_command(self, ctx: Context): - """ - A command that shows an embed with information about the event, - it's participants, and its winners. - """ + """Show an embed with information about the event, its participants, and its winners.""" + contributors = [ "<@!245270749919576066>", "<@!396290259907903491>", @@ -996,10 +1002,11 @@ class Snakes: @snakes_group.command(name='card') async def card_command(self, ctx: Context, *, name: Snake = None): """ - Create an interesting little card from a snake! + Create an interesting little card from a snake. Created by juan and Someone during the first code jam. """ + # Get the snake data we need if not name: name_obj = await self._get_snake_name() @@ -1034,11 +1041,12 @@ class Snakes: @snakes_group.command(name='fact') async def fact_command(self, ctx: Context): """ - Gets a snake-related fact + Gets a snake-related fact. Written by Andrew and Prithaj. Modified by lemon. """ + question = random.choice(self.snake_facts)["fact"] embed = Embed( title="Snake fact", @@ -1049,16 +1057,16 @@ class Snakes: @snakes_group.command(name='help') async def help_command(self, ctx: Context): - """ - This just invokes the help command on this cog. - """ + """Invokes the help command for the Snakes Cog.""" + log.debug(f"{ctx.author} requested info about the snakes cog") return await ctx.invoke(self.bot.get_command("help"), "Snakes") @snakes_group.command(name='snakify') async def snakify_command(self, ctx: Context, *, message: str = None): """ - How would I talk if I were a snake? + How would I talk if I were a snake. + :param ctx: context :param message: If this is passed, it will snakify the message. If not, it will snakify a random message from @@ -1067,6 +1075,7 @@ class Snakes: Written by Momo and kel. Modified by lemon. """ + with ctx.typing(): embed = Embed() user = ctx.message.author @@ -1100,13 +1109,14 @@ class Snakes: @snakes_group.command(name='video', aliases=('get_video',)) async def video_command(self, ctx: Context, *, search: str = None): """ - Gets a YouTube video about snakes + Gets a YouTube video about snakes. :param ctx: Context object passed from discord.py :param search: Optional, a name of a snake. Used to search for videos with that name Written by Andrew and Prithaj. """ + # Are we searching for anything specific? if search: query = search + ' snake' @@ -1141,12 +1151,12 @@ class Snakes: @snakes_group.command(name='zen') async def zen_command(self, ctx: Context): """ - Gets a random quote from the Zen of Python, - except as if spoken by a snake. + Gets a random quote from the Zen of Python, except as if spoken by a snake. Written by Prithaj and Andrew. Modified by lemon. """ + embed = Embed( title="Zzzen of Pythhon", color=SNAKE_COLOR @@ -1168,6 +1178,7 @@ class Snakes: @card_command.error @video_command.error async def command_error(self, ctx, error): + """Local error handler for the Snake Cog.""" embed = Embed() embed.colour = Colour.red() @@ -1190,5 +1201,7 @@ class Snakes: def setup(bot): + """Snake Cog load.""" + bot.add_cog(Snakes(bot)) log.info("Cog loaded: Snakes") diff --git a/bot/seasons/evergreen/snakes/utils.py b/bot/seasons/evergreen/snakes/utils.py index 605c7ef3..ba2068d5 100644 --- a/bot/seasons/evergreen/snakes/utils.py +++ b/bot/seasons/evergreen/snakes/utils.py @@ -1,8 +1,3 @@ -""" -Perlin noise implementation. -Taken from: https://gist.github.com/eevee/26f547457522755cb1fb8739d0ea89a1 -Licensed under ISC -""" import asyncio import io import json @@ -117,43 +112,54 @@ ANGLE_RANGE = math.pi * 2 def get_resource(file: str) -> List[dict]: + """Load Snake resources JSON.""" + with (SNAKE_RESOURCES / f"{file}.json").open() as snakefile: return json.load(snakefile) def smoothstep(t): - """Smooth curve with a zero derivative at 0 and 1, making it useful for - interpolating. - """ + """Smooth curve with a zero derivative at 0 and 1, making it useful for interpolating.""" + return t * t * (3. - 2. * t) def lerp(t, a, b): """Linear interpolation between a and b, given a fraction t.""" + return a + t * (b - a) class PerlinNoiseFactory(object): - """Callable that produces Perlin noise for an arbitrary point in an - arbitrary number of dimensions. The underlying grid is aligned with the - integers. - There is no limit to the coordinates used; new gradients are generated on - the fly as necessary. + """ + Callable that produces Perlin noise for an arbitrary point in an arbitrary number of dimensions. + + The underlying grid is aligned with the integers. + + There is no limit to the coordinates used; new gradients are generated on the fly as necessary. + + Taken from: https://gist.github.com/eevee/26f547457522755cb1fb8739d0ea89a1 + Licensed under ISC """ def __init__(self, dimension, octaves=1, tile=(), unbias=False): - """Create a new Perlin noise factory in the given number of dimensions, - which should be an integer and at least 1. - More octaves create a foggier and more-detailed noise pattern. More - than 4 octaves is rather excessive. - ``tile`` can be used to make a seamlessly tiling pattern. For example: + """ + Create a new Perlin noise factory in the given number of dimensions. + + dimension should be an integer and at least 1. + + More octaves create a foggier and more-detailed noise pattern. More than 4 octaves is rather excessive. + + ``tile`` can be used to make a seamlessly tiling pattern. + For example: pnf = PerlinNoiseFactory(2, tile=(0, 3)) - This will produce noise that tiles every 3 units vertically, but never - tiles horizontally. - If ``unbias`` is true, the smoothstep function will be applied to the - output before returning it, to counteract some of Perlin noise's - significant bias towards the center of its output range. + + This will produce noise that tiles every 3 units vertically, but never tiles horizontally. + + If ``unbias`` is true, the smoothstep function will be applied to the output before returning + it, to counteract some of Perlin noise's significant bias towards the center of its output range. """ + self.dimension = dimension self.octaves = octaves self.tile = tile + (0,) * dimension @@ -166,8 +172,11 @@ class PerlinNoiseFactory(object): self.gradient = {} def _generate_gradient(self): - # Generate a random unit vector at each grid point -- this is the - # "gradient" vector, in that the grid tile slopes towards it + """ + Generate a random unit vector at each grid point. + + This is the "gradient" vector, in that the grid tile slopes towards it + """ # 1 dimension is special, since the only unit vector is trivial; # instead, use a slope between -1 and 1 @@ -184,9 +193,8 @@ class PerlinNoiseFactory(object): return tuple(coord * scale for coord in random_point) def get_plain_noise(self, *point): - """Get plain noise for a single point, without taking into account - either octaves or tiling. - """ + """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))) @@ -234,9 +242,12 @@ class PerlinNoiseFactory(object): return dots[0] * self.scale_factor def __call__(self, *point): - """Get the value of this Perlin noise function at the given point. The - number of values given should match the number of dimensions. """ + Get the value of this Perlin noise function at the given point. + + The number of values given should match the number of dimensions. + """ + ret = 0 for o in range(self.octaves): o2 = 1 << o @@ -281,6 +292,7 @@ def create_snek_frame( ) -> Image: """ Creates a single random snek frame using Perlin noise. + :param perlin_factory: the perlin noise factory used. Required. :param perlin_lookup_vertical_shift: the Perlin noise shift in the Y-dimension for this frame :param image_dimensions: the size of the output image. @@ -288,14 +300,15 @@ def create_snek_frame( :param snake_length: the length of the snake, in segments. :param snake_color: the color of the snake. :param bg_color: the background color. - :param segment_length_range: the range of the segment length. Values will be generated inside this range, including - the bounds. + :param segment_length_range: the range of the segment length. Values will be generated inside + this range, including the bounds. :param snake_width: the width of the snek, in pixels. :param text: the text to display with the snek. Set to None for no text. :param text_position: the position of the text. :param text_color: the color of the text. :return: a PIL image, representing a single frame. """ + start_x = random.randint(image_margins[X], image_dimensions[X] - image_margins[X]) start_y = random.randint(image_margins[Y], image_dimensions[Y] - image_margins[Y]) points = [(start_x, start_y)] @@ -349,6 +362,8 @@ def create_snek_frame( def frame_to_png_bytes(image: Image): + """Convert image to byte stream.""" + stream = io.BytesIO() image.save(stream, format='PNG') return stream.getvalue() @@ -371,6 +386,8 @@ GAME_SCREEN_EMOJI = [ class SnakeAndLaddersGame: + """Snakes and Ladders game Cog.""" + def __init__(self, snakes, context: Context): self.snakes = snakes self.ctx = context @@ -393,10 +410,10 @@ class SnakeAndLaddersGame: Listen for reactions until players have joined, and the game has been started. """ + def startup_event_check(reaction_: Reaction, user_: Member): - """ - Make sure that this reaction is what we want to operate on - """ + """Make sure that this reaction is what we want to operate on.""" + return ( all(( reaction_.message.id == startup.id, # Reaction is on startup message @@ -471,6 +488,13 @@ class SnakeAndLaddersGame: self.avatar_images[user.id] = im async def player_join(self, user: Member): + """ + Handle players joining the game. + + Prevent player joining if they have already joined, if the game is full, or if the game is + in a waiting state. + """ + for p in self.players: if user == p: await self.channel.send(user.mention + " You are already in the game.", delete_after=10) @@ -491,6 +515,13 @@ class SnakeAndLaddersGame: ) async def player_leave(self, user: Member): + """ + Handle players leaving the game. + + Leaving is prevented if the user initiated the game or if they weren't part of it in the + first place. + """ + if user == self.author: await self.channel.send( user.mention + " You are the author, and cannot leave the game. Execute " @@ -515,6 +546,8 @@ class SnakeAndLaddersGame: await self.channel.send(user.mention + " You are not in the match.", delete_after=10) async def cancel_game(self, user: Member): + """Allow the game author to cancel the running game.""" + if not user == self.author: await self.channel.send(user.mention + " Only the author of the game can cancel it.", delete_after=10) return @@ -522,6 +555,13 @@ class SnakeAndLaddersGame: self._destruct() async def start_game(self, user: Member): + """ + Allow the game author to begin the game. + + The game cannot be started if there aren't enough players joined or if the game is in a + waiting state. + """ + if not user == self.author: await self.channel.send(user.mention + " Only the author of the game can start it.", delete_after=10) return @@ -540,10 +580,11 @@ class SnakeAndLaddersGame: await self.start_round() async def start_round(self): + """Begin the round.""" + def game_event_check(reaction_: Reaction, user_: Member): - """ - Make sure that this reaction is what we want to operate on - """ + """Make sure that this reaction is what we want to operate on.""" + return ( all(( reaction_.message.id == self.positions.id, # Reaction is on positions message @@ -634,6 +675,8 @@ class SnakeAndLaddersGame: await self._complete_round() async def player_roll(self, user: Member): + """Handle the player's roll.""" + if user.id not in self.player_tiles: await self.channel.send(user.mention + " You are not in the match.", delete_after=10) return diff --git a/bot/seasons/evergreen/uptime.py b/bot/seasons/evergreen/uptime.py index 1321da19..d6b59a69 100644 --- a/bot/seasons/evergreen/uptime.py +++ b/bot/seasons/evergreen/uptime.py @@ -10,18 +10,15 @@ log = logging.getLogger(__name__) class Uptime: - """ - A cog for posting the bots uptime. - """ + """A cog for posting the bot's uptime.""" def __init__(self, bot): self.bot = bot @commands.command(name="uptime") async def uptime(self, ctx): - """ - Returns the uptime of the bot. - """ + """Responds with the uptime of the bot.""" + difference = relativedelta(start_time - arrow.utcnow()) uptime_string = start_time.shift( seconds=-difference.seconds, @@ -32,7 +29,8 @@ class Uptime: await ctx.send(f"I started up {uptime_string}.") -# Required in order to load the cog, use the class name in the add_cog function. def setup(bot): + """Uptime Cog load.""" + bot.add_cog(Uptime(bot)) log.debug("Uptime cog loaded") -- cgit v1.2.3 From 5193e46787184a5baf5751fc20944a0811c970a3 Mon Sep 17 00:00:00 2001 From: sco1 Date: Tue, 19 Mar 2019 14:47:30 -0400 Subject: Docstring pass for Valentine's cogs --- bot/seasons/valentines/__init__.py | 1 + bot/seasons/valentines/be_my_valentine.py | 51 ++++++++++++++++-------------- bot/seasons/valentines/lovecalculator.py | 6 ++-- bot/seasons/valentines/movie_generator.py | 11 +++---- bot/seasons/valentines/myvalenstate.py | 11 +++++-- bot/seasons/valentines/pickuplines.py | 11 ++++--- bot/seasons/valentines/savethedate.py | 11 +++---- bot/seasons/valentines/valentine_zodiac.py | 14 ++++---- bot/seasons/valentines/whoisvalentine.py | 14 ++++---- 9 files changed, 73 insertions(+), 57 deletions(-) (limited to 'bot') diff --git a/bot/seasons/valentines/__init__.py b/bot/seasons/valentines/__init__.py index f1489cf9..e3e04421 100644 --- a/bot/seasons/valentines/__init__.py +++ b/bot/seasons/valentines/__init__.py @@ -8,6 +8,7 @@ class Valentines(SeasonBase): Get yourself into the bot-commands channel and check out the new features! """ + name = "valentines" bot_name = "Tenderbot" greeting = "Get loved-up!" diff --git a/bot/seasons/valentines/be_my_valentine.py b/bot/seasons/valentines/be_my_valentine.py index 0046ceb4..6abccceb 100644 --- a/bot/seasons/valentines/be_my_valentine.py +++ b/bot/seasons/valentines/be_my_valentine.py @@ -16,9 +16,7 @@ HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_hea class BeMyValentine: - """ - A cog that sends valentines to other users ! - """ + """A cog that sends Valentines to other users.""" def __init__(self, bot): self.bot = bot @@ -26,6 +24,8 @@ class BeMyValentine: @staticmethod def load_json(): + """Load Valentines messages from the static resources.""" + p = Path('bot', 'resources', 'valentines', 'bemyvalentine_valentines.json') with p.open() as json_data: valentines = load(json_data) @@ -34,19 +34,20 @@ class BeMyValentine: @commands.group(name="lovefest", invoke_without_command=True) async def lovefest_role(self, ctx): """ - You can have yourself the lovefest role or remove it. + Subscribe or unsubscribe from the lovefest role. + The lovefest role makes you eligible to receive anonymous valentines from other users. 1) use the command \".lovefest sub\" to get the lovefest role. 2) use the command \".lovefest unsub\" to get rid of the lovefest role. """ + await ctx.invoke(self.bot.get_command("help"), "lovefest") @lovefest_role.command(name="sub") async def add_role(self, ctx): - """ - This command adds the lovefest role. - """ + """Adds 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]: @@ -57,9 +58,8 @@ class BeMyValentine: @lovefest_role.command(name="unsub") async def remove_role(self, ctx): - """ - This command removes the lovefest role. - """ + """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]: @@ -72,7 +72,7 @@ class BeMyValentine: @commands.group(name='bemyvalentine', invoke_without_command=True) async def send_valentine(self, ctx, user: typing.Optional[discord.Member] = None, *, valentine_type=None): """ - This command sends valentine to user if specified or a random user having lovefest role. + Send a valentine to user, if specified, or to a random user with the lovefest role. syntax: .bemyvalentine [user](optional) [p/poem/c/compliment/or you can type your own valentine message] (optional) @@ -119,7 +119,7 @@ class BeMyValentine: @send_valentine.command(name='secret') async def anonymous(self, ctx, user: typing.Optional[discord.Member] = None, *, valentine_type=None): """ - This command DMs a valentine to be given anonymous to a user if specified or a random user having lovefest role. + Send an anonymous Valentine via DM to to a user, if specified, or to a random with the lovefest role. **This command should be DMed to the bot.** @@ -171,6 +171,8 @@ class BeMyValentine: await ctx.author.send(f"Your message has been sent to {user}") def valentine_check(self, valentine_type): + """Return the appropriate Valentine type & title based on the invoking user's input.""" + if valentine_type is None: valentine, title = self.random_valentine() @@ -191,12 +193,14 @@ class BeMyValentine: @staticmethod def random_user(author, members): """ - Picks a random member from the list provided in `members`, ensuring - the author is not one of the options. + Picks a random member from the list provided in `members`. + + The invoking author is ignored. :param author: member who invoked the command :param members: list of discord.Member objects """ + if author in members: members.remove(author) @@ -204,14 +208,15 @@ class BeMyValentine: @staticmethod def random_emoji(): + """Return two random emoji from the module-defined constants.""" + EMOJI_1 = random.choice(HEART_EMOJIS) EMOJI_2 = random.choice(HEART_EMOJIS) return EMOJI_1, EMOJI_2 def random_valentine(self): - """ - Grabs a random poem or a compliment (any message). - """ + """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']) random_valentine = random.choice([valentine_compliment, valentine_poem]) @@ -222,20 +227,20 @@ class BeMyValentine: return random_valentine, title def valentine_poem(self): - """ - Grabs a random poem. - """ + """Grabs a random poem.""" + valentine_poem = random.choice(self.valentines['valentine_poems']) return valentine_poem def valentine_compliment(self): - """ - Grabs a random compliment. - """ + """Grabs a random compliment.""" + valentine_compliment = random.choice(self.valentines['valentine_compliments']) return valentine_compliment def setup(bot): + """Be my Valentine Cog load.""" + bot.add_cog(BeMyValentine(bot)) log.debug("Be My Valentine cog loaded") diff --git a/bot/seasons/valentines/lovecalculator.py b/bot/seasons/valentines/lovecalculator.py index 4df33b93..56cb551d 100644 --- a/bot/seasons/valentines/lovecalculator.py +++ b/bot/seasons/valentines/lovecalculator.py @@ -21,9 +21,7 @@ with Path('bot', 'resources', 'valentines', 'love_matches.json').open() as file: class LoveCalculator: - """ - A cog for calculating the love between two people - """ + """A cog for calculating the love between two people.""" def __init__(self, bot): self.bot = bot @@ -103,4 +101,6 @@ class LoveCalculator: def setup(bot): + """Love calculator Cog load.""" + bot.add_cog(LoveCalculator(bot)) diff --git a/bot/seasons/valentines/movie_generator.py b/bot/seasons/valentines/movie_generator.py index b52eba7f..19fbf25e 100644 --- a/bot/seasons/valentines/movie_generator.py +++ b/bot/seasons/valentines/movie_generator.py @@ -12,18 +12,15 @@ log = logging.getLogger(__name__) class RomanceMovieFinder: - """ - A cog that returns a random romance movie suggestion to a user - """ + """A cog that returns a random romance movie suggestion to a user.""" def __init__(self, bot): self.bot = bot @commands.command(name="romancemovie") async def romance_movie(self, ctx): - """ - Randomly selects a romance movie and displays information about it - """ + """Randomly selects a romance movie and displays information about it.""" + # selecting a random int to parse it to the page parameter random_page = random.randint(0, 20) # TMDB api params @@ -62,5 +59,7 @@ class RomanceMovieFinder: def setup(bot): + """Romance movie Cog load.""" + bot.add_cog(RomanceMovieFinder(bot)) log.debug("Random romance movie cog loaded!") diff --git a/bot/seasons/valentines/myvalenstate.py b/bot/seasons/valentines/myvalenstate.py index 9f06553d..46fdebbc 100644 --- a/bot/seasons/valentines/myvalenstate.py +++ b/bot/seasons/valentines/myvalenstate.py @@ -16,13 +16,14 @@ with open(Path('bot', 'resources', 'valentines', 'valenstates.json'), 'r') as fi class MyValenstate: + """A Cog to find your most likely Valentine's vacation destination.""" + def __init__(self, bot): self.bot = bot def levenshtein(self, source, goal): - """ - Calculates the Levenshtein Distance between source and goal. - """ + """Calculates the Levenshtein Distance between source and goal.""" + if len(source) < len(goal): return self.levenshtein(goal, source) if len(source) == 0: @@ -43,6 +44,8 @@ class MyValenstate: @commands.command() async def myvalenstate(self, ctx, *, name=None): + """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(' ', '') @@ -81,5 +84,7 @@ class MyValenstate: def setup(bot): + """Valenstate Cog load.""" + bot.add_cog(MyValenstate(bot)) log.debug("MyValenstate cog loaded") diff --git a/bot/seasons/valentines/pickuplines.py b/bot/seasons/valentines/pickuplines.py index 4462478f..bce5cdcc 100644 --- a/bot/seasons/valentines/pickuplines.py +++ b/bot/seasons/valentines/pickuplines.py @@ -15,9 +15,7 @@ with open(Path('bot', 'resources', 'valentines', 'pickup_lines.json'), 'r', enco class PickupLine: - """ - A cog that gives random cheesy pickup lines. - """ + """A cog that gives random cheesy pickup lines.""" def __init__(self, bot): self.bot = bot @@ -25,8 +23,11 @@ class PickupLine: @commands.command() async def pickupline(self, ctx): """ - Gives you a random pickup line. Note that most of them are very cheesy! + Gives you a random pickup line. + + Note that most of them are very cheesy. """ + random_line = random.choice(pickup_lines['lines']) embed = discord.Embed( title=':cheese: Your pickup line :cheese:', @@ -40,5 +41,7 @@ class PickupLine: def setup(bot): + """Pickup lines Cog load.""" + bot.add_cog(PickupLine(bot)) log.info('Pickup line cog loaded') diff --git a/bot/seasons/valentines/savethedate.py b/bot/seasons/valentines/savethedate.py index b9484be9..d296a23e 100644 --- a/bot/seasons/valentines/savethedate.py +++ b/bot/seasons/valentines/savethedate.py @@ -14,18 +14,15 @@ HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_hea class SaveTheDate: - """ - A cog that gives random suggestion, for a valentines date ! - """ + """A cog that gives random suggestion for a Valentine's date.""" def __init__(self, bot): self.bot = bot @commands.command() async def savethedate(self, ctx): - """ - Gives you ideas for what to do on a date with your valentine. - """ + """Gives you ideas for what to do on a date with your valentine.""" + with open(Path('bot', 'resources', 'valentines', 'date_ideas.json'), 'r', encoding="utf8") as f: valentine_dates = load(f) random_date = random.choice(valentine_dates['ideas']) @@ -40,5 +37,7 @@ class SaveTheDate: def setup(bot): + """Save the date Cog Load.""" + bot.add_cog(SaveTheDate(bot)) log.debug("Save the date cog loaded") diff --git a/bot/seasons/valentines/valentine_zodiac.py b/bot/seasons/valentines/valentine_zodiac.py index 06c0237d..e18196fc 100644 --- a/bot/seasons/valentines/valentine_zodiac.py +++ b/bot/seasons/valentines/valentine_zodiac.py @@ -15,15 +15,16 @@ HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_hea class ValentineZodiac: - """ - A cog that returns a counter compatible zodiac sign to the given user's zodiac sign. - """ + """A cog that returns a counter compatible zodiac sign to the given user's zodiac sign.""" + def __init__(self, bot): self.bot = bot self.zodiacs = self.load_json() @staticmethod def load_json(): + """Load Zodiac compatibility from static JSON resource.""" + p = Path('bot', 'resources', 'valentines', 'zodiac_compatibility.json') with p.open() as json_data: zodiacs = load(json_data) @@ -31,9 +32,8 @@ class ValentineZodiac: @commands.command(name="partnerzodiac") async def counter_zodiac(self, ctx, zodiac_sign): - """ - Provides a counter compatible zodiac sign to the given user's zodiac sign. - """ + """Provides a counter compatible zodiac sign to the given user's zodiac sign.""" + try: compatible_zodiac = random.choice(self.zodiacs[zodiac_sign.lower()]) except KeyError: @@ -55,5 +55,7 @@ class ValentineZodiac: def setup(bot): + """Valentine Zodiac Cog load.""" + bot.add_cog(ValentineZodiac(bot)) log.debug("Valentine Zodiac cog loaded") diff --git a/bot/seasons/valentines/whoisvalentine.py b/bot/seasons/valentines/whoisvalentine.py index 2fe07aba..9ac2ee94 100644 --- a/bot/seasons/valentines/whoisvalentine.py +++ b/bot/seasons/valentines/whoisvalentine.py @@ -15,14 +15,15 @@ with open(Path("bot", "resources", "valentines", "valentine_facts.json"), "r") a class ValentineFacts: + """A Cog for displaying facts about Saint Valentine.""" + def __init__(self, bot): self.bot = bot @commands.command(aliases=('whoisvalentine', 'saint_valentine')) async def who_is_valentine(self, ctx): - """ - Displays info about Saint Valentine. - """ + """Displays info about Saint Valentine.""" + embed = discord.Embed( title="Who is Saint Valentine?", description=FACTS['whois'], @@ -37,9 +38,8 @@ class ValentineFacts: @commands.command() async def valentine_fact(self, ctx): - """ - Shows a random fact about Valentine's Day. - """ + """Shows a random fact about Valentine's Day.""" + embed = discord.Embed( title=choice(FACTS['titles']), description=choice(FACTS['text']), @@ -50,4 +50,6 @@ class ValentineFacts: def setup(bot): + """Who is Valentine Cog load.""" + bot.add_cog(ValentineFacts(bot)) -- cgit v1.2.3 From 49ecb82d789ed09ef54c5312c6f1ddf11490ea59 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Tue, 19 Mar 2019 12:45:39 -0700 Subject: Fix docstring typo Kaizened Co-Authored-By: sco1 --- bot/seasons/evergreen/snakes/snakes_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/evergreen/snakes/snakes_cog.py b/bot/seasons/evergreen/snakes/snakes_cog.py index 92b28c55..8835e9eb 100644 --- a/bot/seasons/evergreen/snakes/snakes_cog.py +++ b/bot/seasons/evergreen/snakes/snakes_cog.py @@ -276,7 +276,7 @@ class Snakes: return message async def _fetch(self, session, url, params=None): - """Asyncronous web request helper method.""" + """Asynchronous web request helper method.""" if params is None: params = {} -- cgit v1.2.3 From 174bb756af7287c882f182531b07b0ff1b1b7ad8 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Tue, 19 Mar 2019 12:49:13 -0700 Subject: Update bot/seasons/evergreen/snakes/snakes_cog.py Co-Authored-By: sco1 --- bot/seasons/evergreen/snakes/snakes_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/evergreen/snakes/snakes_cog.py b/bot/seasons/evergreen/snakes/snakes_cog.py index 8835e9eb..f5a0ee1c 100644 --- a/bot/seasons/evergreen/snakes/snakes_cog.py +++ b/bot/seasons/evergreen/snakes/snakes_cog.py @@ -465,7 +465,7 @@ class Snakes: @locked() async def antidote_command(self, ctx: Context): """ - Antidote - Can you create the antivenom before the patient dies. + A game where you race to create an antivenom before the patient dies. Rules: You have 4 ingredients for each antidote, you only have 10 attempts Once you synthesize the antidote, you will be presented with 4 markers -- cgit v1.2.3 From 6a4e33cb6f47a6d67891ff6729af0614374d3e53 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Tue, 19 Mar 2019 12:49:39 -0700 Subject: Update awkward docstring syntax Co-Authored-By: sco1 --- bot/seasons/evergreen/snakes/snakes_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/evergreen/snakes/snakes_cog.py b/bot/seasons/evergreen/snakes/snakes_cog.py index f5a0ee1c..5ca4f8c0 100644 --- a/bot/seasons/evergreen/snakes/snakes_cog.py +++ b/bot/seasons/evergreen/snakes/snakes_cog.py @@ -1065,7 +1065,7 @@ class Snakes: @snakes_group.command(name='snakify') async def snakify_command(self, ctx: Context, *, message: str = None): """ - How would I talk if I were a snake. + Shows the given message in snake-speak. :param ctx: context :param message: If this is passed, it will snakify the message. -- cgit v1.2.3 From 722ed131b59982caf8e54ca8bfbc36989ec2aae4 Mon Sep 17 00:00:00 2001 From: Suhail Date: Tue, 19 Mar 2019 23:09:42 +0000 Subject: Spooky Rating - Fixed Requested Changes - Removed the documentation on how Users are converted - Changed random to a local state rather than using global seed - Changed the JSON minimum to -1 due to how bisect works --- bot/resources/halloween/spooky_rating.json | 2 +- bot/seasons/halloween/spookyrating.py | 14 +++----------- 2 files changed, 4 insertions(+), 12 deletions(-) (limited to 'bot') diff --git a/bot/resources/halloween/spooky_rating.json b/bot/resources/halloween/spooky_rating.json index 2c459926..b83624fe 100644 --- a/bot/resources/halloween/spooky_rating.json +++ b/bot/resources/halloween/spooky_rating.json @@ -1,5 +1,5 @@ { - "0": { + "-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://www.thoughtco.com/thmb/qv6FUFM6V_p7YUAk9lT6fZG8s3M=/768x0/filters:no_upscale():max_bytes(150000):strip_icc()/smoke-trailing-from-extinguished-white-candle-565954737-584daec23df78c491e6282a5.jpg" diff --git a/bot/seasons/halloween/spookyrating.py b/bot/seasons/halloween/spookyrating.py index 92f71b9e..8c4f1b12 100644 --- a/bot/seasons/halloween/spookyrating.py +++ b/bot/seasons/halloween/spookyrating.py @@ -23,6 +23,7 @@ class SpookyRating: def __init__(self, bot): self.bot = bot + self.local_random = random.Random() @commands.command() @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) @@ -30,14 +31,6 @@ class SpookyRating: """ Calculates the spooky rating of someone. - This command accepts an optional user as an argument, defaulting to the command executor. - Users are converted from: - - User ID - - Mention - - name#discrim - - name - - nickname - Any user will always yield the same result, no matter who calls the command """ @@ -45,9 +38,8 @@ class SpookyRating: who = ctx.author # This ensures that the same result over multiple runtimes - random.seed(who.id) - - spooky_percent = random.randint(1, 100) + self.local_random.seed(who.id) + spooky_percent = self.local_random.randint(1, 101) # We need the -1 due to how bisect returns the point # see the documentation for further detail -- cgit v1.2.3 From 194e97ef065fb574629f9a8cf4343b271f8a0a9e Mon Sep 17 00:00:00 2001 From: sco1 Date: Wed, 20 Mar 2019 19:11:51 -0400 Subject: Add D400 to ignored linting error codes & revert affected docstrings --- bot/seasons/evergreen/snakes/snakes_cog.py | 4 ++-- tox.ini | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'bot') diff --git a/bot/seasons/evergreen/snakes/snakes_cog.py b/bot/seasons/evergreen/snakes/snakes_cog.py index 5ca4f8c0..dc5fb104 100644 --- a/bot/seasons/evergreen/snakes/snakes_cog.py +++ b/bot/seasons/evergreen/snakes/snakes_cog.py @@ -465,7 +465,7 @@ class Snakes: @locked() async def antidote_command(self, ctx: Context): """ - A game where you race to create an antivenom before the patient dies. + Antidote! Can you create the antivenom before the patient dies? Rules: You have 4 ingredients for each antidote, you only have 10 attempts Once you synthesize the antidote, you will be presented with 4 markers @@ -1065,7 +1065,7 @@ class Snakes: @snakes_group.command(name='snakify') async def snakify_command(self, ctx: Context, *, message: str = None): """ - Shows the given message in snake-speak. + How would I talk if I were a snake? :param ctx: context :param message: If this is passed, it will snakify the message. diff --git a/tox.ini b/tox.ini index 62aadda9..8becd147 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,7 @@ ignore= # Docstring Quotes D301,D302, # Docstring Content - D401,D402,D405,D406,D407,D408,D409,D410,D411,D412,D413,D414 + D400,D401,D402,D405,D406,D407,D408,D409,D410,D411,D412,D413,D414 exclude= __pycache__,.cache, venv,.venv, -- cgit v1.2.3 From 73db03aad6f1ab602e00cd7304fdf82a394f37f7 Mon Sep 17 00:00:00 2001 From: sco1 Date: Wed, 20 Mar 2019 19:13:29 -0400 Subject: Remove unnecessary pass statement --- bot/pagination.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'bot') diff --git a/bot/pagination.py b/bot/pagination.py index 3916809d..89a261b5 100644 --- a/bot/pagination.py +++ b/bot/pagination.py @@ -20,8 +20,6 @@ log = logging.getLogger(__name__) class EmptyPaginatorEmbed(Exception): """Base Exception class for an empty paginator embed.""" - pass - class LinePaginator(Paginator): """ -- cgit v1.2.3 From 6d7f12fc8729f019b01cb795941ae90c620cdf82 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 20 Mar 2019 16:15:07 -0700 Subject: Rephrase docstring to be consistent with function verbage Co-Authored-By: sco1 --- bot/bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/bot.py b/bot/bot.py index 2885379c..96caf9e3 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -28,7 +28,7 @@ class SeasonalBot(Bot): ) def load_extensions(self, exts: List[str]): - """Unload all current cogs, then load in the ones passed into `cogs`.""" + """Unload all current extensions, then load the given extensions.""" # Unload all cogs extensions = list(self.extensions.keys()) -- cgit v1.2.3 From 18f2d44120486a02a0b19df4c334ed9305b0848e Mon Sep 17 00:00:00 2001 From: sco1 Date: Wed, 20 Mar 2019 19:26:37 -0400 Subject: Remove attribute autodoc prefix --- bot/pagination.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'bot') diff --git a/bot/pagination.py b/bot/pagination.py index 89a261b5..1091878a 100644 --- a/bot/pagination.py +++ b/bot/pagination.py @@ -57,7 +57,7 @@ class LinePaginator(Paginator): """ Adds a line to the current page. - If the line exceeds the :attr:`max_size` then an exception is raised. + If the line exceeds the `max_size` then an exception is raised. Overrides the Paginator.add_line from inside discord.ext.commands in order to allow configuration of the maximum number of lines per page. @@ -72,7 +72,7 @@ class LinePaginator(Paginator): Raises ------ RuntimeError - The line was too big for the current :attr:`max_size`. + The line was too big for the current `max_size`. """ if len(line) > self.max_size - len(self.prefix) - 2: -- cgit v1.2.3 From f932dbc886ed2e6437baaca7c8cae0379249751f Mon Sep 17 00:00:00 2001 From: Suhail Date: Sun, 24 Mar 2019 13:40:39 +0000 Subject: Time Left - Requested Changes --- bot/seasons/halloween/timeleft.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'bot') diff --git a/bot/seasons/halloween/timeleft.py b/bot/seasons/halloween/timeleft.py index b28f94f0..b920560e 100644 --- a/bot/seasons/halloween/timeleft.py +++ b/bot/seasons/halloween/timeleft.py @@ -1,4 +1,4 @@ -import datetime +from datetime import datetime import logging from discord.ext import commands @@ -16,16 +16,23 @@ class TimeLeft: @staticmethod def in_october(): - return datetime.datetime.now().month == 10 + """ + checks that the current month is October + """ + return datetime.now().month == 10 @staticmethod def load_date(): - now = datetime.datetime.now() + """ + Grabs the current time in additon to the first and last day of the following October + """ + now = datetime.now() year = now.year if now.month > 10: year += 1 - end = datetime.datetime(year, 10, 31, 11, 59, 59) - return now, end + end = datetime(year, 10, 31, 11, 59, 59) + start = datetime(year, 10, 1) + return now, end, start @commands.command() async def timeleft(self, ctx): @@ -33,10 +40,10 @@ class TimeLeft: Calculates the time left until the end of Hacktober Whilst in October, displays the days, hours and minutes left. - Only displays the days left whilst in a different month + Only displays the days left until the beginning and end whilst in a different month """ - now, end = self.load_date() + now, end, start = self.load_date() diff = end - now days, seconds = diff.days, diff.seconds if self.in_october(): @@ -45,7 +52,12 @@ class TimeLeft: await ctx.send(f"There is currently only {days} days, {hours} hours and {minutes}" "minutes left until the end of Hacktober.") else: - await ctx.send(f"It is not currently Hacktober. However, the next one will finish in {days} days.") + start_diff = start - now + start_days = start_diff.days + await ctx.send( + f"It is not currently Hacktober. However, the next one will start in {start_days} days " + f"and will finish in {days} days." + ) def setup(bot): -- cgit v1.2.3 From acc43a7a41848db265e25ff517363e7e21ab2b8a Mon Sep 17 00:00:00 2001 From: Suhail Date: Sun, 24 Mar 2019 13:41:58 +0000 Subject: Time Left - Fixed import order --- bot/seasons/halloween/timeleft.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/halloween/timeleft.py b/bot/seasons/halloween/timeleft.py index b920560e..cfb7ec79 100644 --- a/bot/seasons/halloween/timeleft.py +++ b/bot/seasons/halloween/timeleft.py @@ -1,5 +1,5 @@ -from datetime import datetime import logging +from datetime import datetime from discord.ext import commands -- cgit v1.2.3 From 494316e94ae8266d8db66f393af4934e821e768e Mon Sep 17 00:00:00 2001 From: scragly <29337040+scragly@users.noreply.github.com> Date: Mon, 25 Mar 2019 23:53:40 +1000 Subject: Add docstring spacing for consistency. --- bot/seasons/valentines/savethedate.py | 1 + 1 file changed, 1 insertion(+) (limited to 'bot') diff --git a/bot/seasons/valentines/savethedate.py b/bot/seasons/valentines/savethedate.py index 9582dea4..9f1dbaaa 100644 --- a/bot/seasons/valentines/savethedate.py +++ b/bot/seasons/valentines/savethedate.py @@ -25,6 +25,7 @@ class SaveTheDate: @commands.command() async def savethedate(self, ctx): """Gives you ideas for what to do on a date with your valentine.""" + random_date = random.choice(VALENTINES_DATES['ideas']) emoji_1 = random.choice(HEART_EMOJIS) emoji_2 = random.choice(HEART_EMOJIS) -- cgit v1.2.3 From b1d0ee0d4ae2a269bd5de2b6f100d5f469c1f22d Mon Sep 17 00:00:00 2001 From: scragly <29337040+scragly@users.noreply.github.com> Date: Tue, 26 Mar 2019 00:03:05 +1000 Subject: Respect docstring summary/description spacing. Optionally, removed the "Happy Pride Month" at the end of the docstring because it will be shown in the announcement automatically through the greeting attribute (shown as embed author). --- bot/seasons/pride/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/pride/__init__.py b/bot/seasons/pride/__init__.py index d8a7e34b..cbd21ee2 100644 --- a/bot/seasons/pride/__init__.py +++ b/bot/seasons/pride/__init__.py @@ -4,8 +4,8 @@ from bot.seasons import SeasonBase class Pride(SeasonBase): """ No matter your origin, identity or sexuality, we come together to celebrate each and everyone's individuality. + Feature contributions to ProudBot is encouraged to commemorate the history and challenges of the LGBTQ+ community. - Happy Pride Month """ name = "pride" -- cgit v1.2.3 From caa0d7673ae2212e0794b3a7349dde2ad58acbfc Mon Sep 17 00:00:00 2001 From: Ava Date: Mon, 25 Mar 2019 14:44:13 +0000 Subject: Time left - requested change Co-Authored-By: Suhail6inkling <38522108+Suhail6inkling@users.noreply.github.com> --- bot/seasons/halloween/timeleft.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/halloween/timeleft.py b/bot/seasons/halloween/timeleft.py index cfb7ec79..2fc0fb76 100644 --- a/bot/seasons/halloween/timeleft.py +++ b/bot/seasons/halloween/timeleft.py @@ -19,7 +19,7 @@ class TimeLeft: """ checks that the current month is October """ - return datetime.now().month == 10 + return datetime.utcnow().month == 10 @staticmethod def load_date(): -- cgit v1.2.3 From 2c4b92f0886062100b9989b85c881bfc4aea7ece Mon Sep 17 00:00:00 2001 From: Suhail Date: Mon, 25 Mar 2019 14:46:13 +0000 Subject: Time Left - Changed `datetime.now()` to `datetime.utcnow()` And previous change also did that --- bot/seasons/halloween/timeleft.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/halloween/timeleft.py b/bot/seasons/halloween/timeleft.py index 2fc0fb76..9488f660 100644 --- a/bot/seasons/halloween/timeleft.py +++ b/bot/seasons/halloween/timeleft.py @@ -26,7 +26,7 @@ class TimeLeft: """ Grabs the current time in additon to the first and last day of the following October """ - now = datetime.now() + now = datetime.utcnow() year = now.year if now.month > 10: year += 1 -- cgit v1.2.3 From 870fe69a85b7478b0425927e61d2db1bf3abcc35 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 26 Mar 2019 09:45:56 +0000 Subject: Apply suggestions from code review Co-Authored-By: Suhail6inkling <38522108+Suhail6inkling@users.noreply.github.com> --- bot/seasons/halloween/timeleft.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'bot') diff --git a/bot/seasons/halloween/timeleft.py b/bot/seasons/halloween/timeleft.py index 9488f660..af205573 100644 --- a/bot/seasons/halloween/timeleft.py +++ b/bot/seasons/halloween/timeleft.py @@ -17,15 +17,17 @@ class TimeLeft: @staticmethod def in_october(): """ - checks that the current month is October + Return True if the current month is October. """ + return datetime.utcnow().month == 10 @staticmethod def load_date(): """ - Grabs the current time in additon to the first and last day of the following October + Return of a tuple of the current time and the end and start times of the following October. """ + now = datetime.utcnow() year = now.year if now.month > 10: @@ -37,7 +39,7 @@ class TimeLeft: @commands.command() async def timeleft(self, ctx): """ - Calculates the time left until the end of Hacktober + Calculates the time left until the end of Hacktober. Whilst in October, displays the days, hours and minutes left. Only displays the days left until the beginning and end whilst in a different month -- cgit v1.2.3 From bea4ccf2114a50cbe3cfca9859946e732998b1d1 Mon Sep 17 00:00:00 2001 From: sco1 Date: Tue, 26 Mar 2019 14:16:56 -0400 Subject: Add missing Cog inheritance to 8ball --- bot/seasons/evergreen/magic_8ball.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/evergreen/magic_8ball.py b/bot/seasons/evergreen/magic_8ball.py index 88c9fd26..8e2f9164 100644 --- a/bot/seasons/evergreen/magic_8ball.py +++ b/bot/seasons/evergreen/magic_8ball.py @@ -8,7 +8,7 @@ from discord.ext import commands log = logging.getLogger(__name__) -class Magic8ball: +class Magic8ball(commands.Cog): """ A Magic 8ball command to respond to a users question. """ -- cgit v1.2.3 From 8ccefa2468262cc125a369560d2a34b0c757899c Mon Sep 17 00:00:00 2001 From: sco1 Date: Fri, 29 Mar 2019 08:11:21 -0400 Subject: Update docstrings for new linting --- bot/seasons/halloween/timeleft.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'bot') diff --git a/bot/seasons/halloween/timeleft.py b/bot/seasons/halloween/timeleft.py index af205573..56a3eaed 100644 --- a/bot/seasons/halloween/timeleft.py +++ b/bot/seasons/halloween/timeleft.py @@ -7,26 +7,20 @@ log = logging.getLogger(__name__) class TimeLeft: - """ - A Cog that tells you how long left until Hacktober is over! - """ + """A Cog that tells you how long left until Hacktober is over!""" def __init__(self, bot): self.bot = bot @staticmethod def in_october(): - """ - Return True if the current month is October. - """ + """Return True if the current month is October.""" return datetime.utcnow().month == 10 @staticmethod def load_date(): - """ - Return of a tuple of the current time and the end and start times of the following October. - """ + """Return of a tuple of the current time and the end and start times of the next October.""" now = datetime.utcnow() year = now.year @@ -63,5 +57,7 @@ class TimeLeft: def setup(bot): + """Cog load.""" + bot.add_cog(TimeLeft(bot)) log.info("TimeLeft cog loaded") -- cgit v1.2.3 From dfcb396ce0c13b0d21028337e4c9e509195d02d3 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sat, 30 Mar 2019 14:13:10 +1000 Subject: Add Easter icon url and embed colour. --- bot/seasons/easter/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'bot') diff --git a/bot/seasons/easter/__init__.py b/bot/seasons/easter/__init__.py index bfad772d..f4e14821 100644 --- a/bot/seasons/easter/__init__.py +++ b/bot/seasons/easter/__init__.py @@ -1,3 +1,4 @@ +from bot.constants import Colours from bot.seasons import SeasonBase @@ -15,3 +16,6 @@ class Easter(SeasonBase): # Duration of season start_date = "01/04" end_date = "30/04" + + colour = Colours.pink + icon = "/logos/logo_seasonal/easter/easter.png" -- cgit v1.2.3 From 58b9e81f3576aafdd1f5cefd27b5e17b3867a851 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sat, 30 Mar 2019 15:15:37 +1000 Subject: Always announce if docstring, better formatting. --- bot/seasons/season.py | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) (limited to 'bot') diff --git a/bot/seasons/season.py b/bot/seasons/season.py index b7892606..c78de981 100644 --- a/bot/seasons/season.py +++ b/bot/seasons/season.py @@ -278,7 +278,18 @@ class SeasonBase: channel = guild.get_channel(Channels.announcements) mention = f"<@&{Roles.announcements}>" - # collect seasonal cogs + # build cog info output + doc = inspect.getdoc(self) + announce = "\n\n".join(l.replace("\n", " ") for l in doc.split("\n\n")) + + # no announcement message found + if not doc: + return + + embed = discord.Embed(description=f"{announce}\n\n", colour=self.colour or guild.me.colour) + embed.set_author(name=self.greeting) + + # find any seasonal commands cogs = [] for cog in bot.cogs.values(): if "evergreen" in cog.__module__: @@ -287,30 +298,21 @@ class SeasonBase: if cog_name != "SeasonManager": cogs.append(cog_name) - # no cogs, so no seasonal commands - if not cogs: - return + if cogs: + def cog_name(cog): + return type(cog).__name__ - # build cog info output - doc = inspect.getdoc(self) - announce_text = doc + "\n\n" if doc else "" - - def cog_name(cog): - return type(cog).__name__ - - cog_info = [] - for cog in sorted(cogs, key=cog_name): - doc = inspect.getdoc(bot.get_cog(cog)) - if doc: - cog_info.append(f"**{cog}**\n*{doc}*") - else: - cog_info.append(f"**{cog}**") + cog_info = [] + for cog in sorted(cogs, key=cog_name): + doc = inspect.getdoc(bot.get_cog(cog)) + if doc: + cog_info.append(f"**{cog}**\n*{doc}*") + else: + cog_info.append(f"**{cog}**") - embed = discord.Embed(description=announce_text, colour=self.colour or guild.me.colour) - embed.set_author(name=self.greeting) - cogs_text = "\n".join(cog_info) - embed.add_field(name="New Command Categories", value=cogs_text) - embed.set_footer(text="To see the new commands, use .help Category") + cogs_text = "\n".join(cog_info) + embed.add_field(name="New Command Categories", value=cogs_text) + embed.set_footer(text="To see the new commands, use .help Category") await channel.send(mention, embed=embed) -- cgit v1.2.3 From 135a254a516341995fd6dea53807088b92d3b239 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sat, 30 Mar 2019 15:25:44 +1000 Subject: Improve easter announcement. --- bot/seasons/easter/__init__.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/__init__.py b/bot/seasons/easter/__init__.py index bfad772d..d1273a6d 100644 --- a/bot/seasons/easter/__init__.py +++ b/bot/seasons/easter/__init__.py @@ -3,14 +3,23 @@ from bot.seasons import SeasonBase class Easter(SeasonBase): """ - Easter is a beautiful time of the year often celebrated after the first Full Moon of the new spring season. - This time is quite beautiful due to the colorful flowers coming out to greet us. So. let's greet Spring - in an Easter celebration of contributions. + To celebrate, we have a lovely new colourful server icon. Thanks to <@140605665772175361> for + the design. + + Easter is a beautiful time of the year often celebrated after the first Full Moon of the + Northern Hemisphere's new spring seasonm, making it a beautiful time of the year with colorful + flowers coming out to greet us. + + We hope you can join us in the Spring and Easter celebrations by heading over to the + [SeasonalBot GitHub repo](https://git.io/fjkvQ) and either submit some issues with ideas for + commands of this season, or to pick out an issue that you think looks fun to build yourself. + + Come over to <#542272993192050698> if you're interested or had any questions! """ name = "easter" bot_name = "BunnyBot" - greeting = "Happy Easter to us all!" + greeting = "Happy Easter!" # Duration of season start_date = "01/04" -- cgit v1.2.3 From f5237aca7a52dcf86a3b87da89dc71e816e849f6 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 30 Mar 2019 16:54:04 +1000 Subject: Tidy capitalization and wording. Co-Authored-By: scragly <29337040+scragly@users.noreply.github.com> --- bot/seasons/easter/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/__init__.py b/bot/seasons/easter/__init__.py index d1273a6d..872a6c60 100644 --- a/bot/seasons/easter/__init__.py +++ b/bot/seasons/easter/__init__.py @@ -6,15 +6,15 @@ class Easter(SeasonBase): To celebrate, we have a lovely new colourful server icon. Thanks to <@140605665772175361> for the design. - Easter is a beautiful time of the year often celebrated after the first Full Moon of the - Northern Hemisphere's new spring seasonm, making it a beautiful time of the year with colorful + Easter is a beautiful time of the year often celebrated after the first full moon of the + Northern Hemisphere's new spring season, making it a beautiful time of the year with colorful flowers coming out to greet us. - We hope you can join us in the Spring and Easter celebrations by heading over to the + We hope you can join us in the spring and Easter celebrations by heading over to the [SeasonalBot GitHub repo](https://git.io/fjkvQ) and either submit some issues with ideas for - commands of this season, or to pick out an issue that you think looks fun to build yourself. + commands for this season, or to pick out an issue that you think looks fun to work on yourself. - Come over to <#542272993192050698> if you're interested or had any questions! + Come over to <#542272993192050698> if you're interested or have any questions! """ name = "easter" -- cgit v1.2.3 From 0c1587a8e1203a45a0dfe6221ecfa896ec3ddbd7 Mon Sep 17 00:00:00 2001 From: scragly <29337040+scragly@users.noreply.github.com> Date: Sat, 30 Mar 2019 17:05:01 +1000 Subject: Adjust subject order a bit. --- bot/seasons/easter/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/__init__.py b/bot/seasons/easter/__init__.py index 872a6c60..3be52b10 100644 --- a/bot/seasons/easter/__init__.py +++ b/bot/seasons/easter/__init__.py @@ -3,13 +3,13 @@ from bot.seasons import SeasonBase class Easter(SeasonBase): """ - To celebrate, we have a lovely new colourful server icon. Thanks to <@140605665772175361> for - the design. - Easter is a beautiful time of the year often celebrated after the first full moon of the Northern Hemisphere's new spring season, making it a beautiful time of the year with colorful flowers coming out to greet us. + To celebrate, we have a lovely new colourful server icon. Thanks to <@140605665772175361> for + the design. + We hope you can join us in the spring and Easter celebrations by heading over to the [SeasonalBot GitHub repo](https://git.io/fjkvQ) and either submit some issues with ideas for commands for this season, or to pick out an issue that you think looks fun to work on yourself. -- cgit v1.2.3 From 57d9c2be752578b555c0b7bfffeba111c78a6847 Mon Sep 17 00:00:00 2001 From: scragly <29337040+scragly@users.noreply.github.com> Date: Sat, 30 Mar 2019 17:36:31 +1000 Subject: Fix weird wording of leading sentence. --- bot/seasons/easter/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/__init__.py b/bot/seasons/easter/__init__.py index 3be52b10..6bfec0fe 100644 --- a/bot/seasons/easter/__init__.py +++ b/bot/seasons/easter/__init__.py @@ -3,9 +3,8 @@ from bot.seasons import SeasonBase class Easter(SeasonBase): """ - Easter is a beautiful time of the year often celebrated after the first full moon of the - Northern Hemisphere's new spring season, making it a beautiful time of the year with colorful - flowers coming out to greet us. + Often celebrated after the first full moon of the Northern Hemisphere's new spring season, + Easter is a beautiful time of the year with colorful flowers coming out to greet us. To celebrate, we have a lovely new colourful server icon. Thanks to <@140605665772175361> for the design. -- cgit v1.2.3 From f3dd0333eba60f1b8a09b3564651f2ff361032bf Mon Sep 17 00:00:00 2001 From: scragly <29337040+scragly@users.noreply.github.com> Date: Sat, 30 Mar 2019 18:03:22 +1000 Subject: Word announcement as suggested by lmn. --- bot/seasons/easter/__init__.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/__init__.py b/bot/seasons/easter/__init__.py index 6bfec0fe..4a7b6be6 100644 --- a/bot/seasons/easter/__init__.py +++ b/bot/seasons/easter/__init__.py @@ -3,17 +3,20 @@ from bot.seasons import SeasonBase class Easter(SeasonBase): """ - Often celebrated after the first full moon of the Northern Hemisphere's new spring season, - Easter is a beautiful time of the year with colorful flowers coming out to greet us. + Here at Python Discord, we celebrate our version of Easter during the entire month of April. + While this celebration takes place, you'll notice a few changes: - To celebrate, we have a lovely new colourful server icon. Thanks to <@140605665772175361> for - the design. + • The server icon has changed to our Easter icon. Thanks to <@140605665772175361> for the + design! - We hope you can join us in the spring and Easter celebrations by heading over to the - [SeasonalBot GitHub repo](https://git.io/fjkvQ) and either submit some issues with ideas for - commands for this season, or to pick out an issue that you think looks fun to work on yourself. + • [Easter issues now available for SeasonalBot on the repo](https://git.io/fjkvQ). - Come over to <#542272993192050698> if you're interested or have any questions! + • You may see stuff like an Easter themed esoteric challenge, a celebration of Earth Day, or + Easter-related micro-events for you to join. Stay tuned! + + If you'd like to contribute, head on over to <#542272993192050698> and we will help you get + started. It doesn't matter if you're new to open source or Python, if you'd like to help, we + will find you a task and teach you what you need to know. """ name = "easter" -- cgit v1.2.3 From 51d36c768e8c6338f02f1275b58b811928d18f82 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sat, 30 Mar 2019 18:11:15 +1000 Subject: Add season icon as announcement image. --- bot/seasons/season.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'bot') diff --git a/bot/seasons/season.py b/bot/seasons/season.py index c78de981..cfc27172 100644 --- a/bot/seasons/season.py +++ b/bot/seasons/season.py @@ -17,6 +17,8 @@ from bot.decorators import with_role log = logging.getLogger(__name__) +ICON_BASE_URL = "https://raw.githubusercontent.com/python-discord/branding/master" + def get_seasons() -> List[str]: """ @@ -163,12 +165,11 @@ class SeasonBase: `https://raw.githubusercontent.com/python-discord/branding/master` """ - base_url = "https://raw.githubusercontent.com/python-discord/branding/master" if avatar: icon = self.bot_icon or self.icon else: icon = self.icon - full_url = base_url + icon + full_url = ICON_BASE_URL + icon log.debug(f"Getting icon from: {full_url}") async with bot.http_session.get(full_url) as resp: return await resp.read() @@ -289,6 +290,9 @@ class SeasonBase: embed = discord.Embed(description=f"{announce}\n\n", colour=self.colour or guild.me.colour) embed.set_author(name=self.greeting) + if self.icon: + embed.set_image(url=ICON_BASE_URL+self.icon) + # find any seasonal commands cogs = [] for cog in bot.cogs.values(): -- cgit v1.2.3 From 8873cfb2b9a4200f87ec55d709417ba5c4f67e55 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sat, 30 Mar 2019 20:39:30 +1000 Subject: Blank line required between summary line and description. --- bot/seasons/easter/__init__.py | 1 + 1 file changed, 1 insertion(+) (limited to 'bot') diff --git a/bot/seasons/easter/__init__.py b/bot/seasons/easter/__init__.py index 4d7e8467..a816fe08 100644 --- a/bot/seasons/easter/__init__.py +++ b/bot/seasons/easter/__init__.py @@ -5,6 +5,7 @@ from bot.seasons import SeasonBase class Easter(SeasonBase): """ Here at Python Discord, we celebrate our version of Easter during the entire month of April. + While this celebration takes place, you'll notice a few changes: • The server icon has changed to our Easter icon. Thanks to <@140605665772175361> for the -- cgit v1.2.3 From d99013ca0fc489b4774ea080fd441741aab14c97 Mon Sep 17 00:00:00 2001 From: sco1 Date: Sat, 30 Mar 2019 11:00:09 -0400 Subject: Re-add cog inheritance removed by poor merge conflict resolution --- bot/seasons/christmas/adventofcode.py | 2 +- bot/seasons/evergreen/error_handler.py | 2 +- bot/seasons/evergreen/fun.py | 2 +- bot/seasons/evergreen/uptime.py | 2 +- bot/seasons/halloween/candy_collection.py | 2 +- bot/seasons/halloween/hacktoberstats.py | 2 +- bot/seasons/halloween/halloween_facts.py | 2 +- bot/seasons/halloween/halloweenify.py | 2 +- bot/seasons/halloween/scarymovie.py | 2 +- bot/seasons/halloween/spookyavatar.py | 2 +- bot/seasons/halloween/spookygif.py | 2 +- bot/seasons/halloween/spookyreact.py | 2 +- bot/seasons/halloween/spookysound.py | 2 +- bot/seasons/halloween/timeleft.py | 2 +- bot/seasons/season.py | 2 +- bot/seasons/valentines/be_my_valentine.py | 2 +- bot/seasons/valentines/movie_generator.py | 2 +- bot/seasons/valentines/myvalenstate.py | 2 +- bot/seasons/valentines/pickuplines.py | 2 +- bot/seasons/valentines/savethedate.py | 2 +- bot/seasons/valentines/valentine_zodiac.py | 2 +- bot/seasons/valentines/whoisvalentine.py | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) (limited to 'bot') diff --git a/bot/seasons/christmas/adventofcode.py b/bot/seasons/christmas/adventofcode.py index f61f34b5..5d05dce6 100644 --- a/bot/seasons/christmas/adventofcode.py +++ b/bot/seasons/christmas/adventofcode.py @@ -103,7 +103,7 @@ async def day_countdown(bot: commands.Bot): await asyncio.sleep(120) -class AdventOfCode: +class AdventOfCode(commands.Cog): """Advent of Code festivities! Ho Ho Ho.""" def __init__(self, bot: commands.Bot): diff --git a/bot/seasons/evergreen/error_handler.py b/bot/seasons/evergreen/error_handler.py index b0d05c41..26afe814 100644 --- a/bot/seasons/evergreen/error_handler.py +++ b/bot/seasons/evergreen/error_handler.py @@ -8,7 +8,7 @@ from discord.ext import commands log = logging.getLogger(__name__) -class CommandErrorHandler: +class CommandErrorHandler(commands.Cog): """A error handler for the PythonDiscord server.""" def __init__(self, bot): diff --git a/bot/seasons/evergreen/fun.py b/bot/seasons/evergreen/fun.py index 286d8462..05cf504e 100644 --- a/bot/seasons/evergreen/fun.py +++ b/bot/seasons/evergreen/fun.py @@ -8,7 +8,7 @@ from bot.constants import Emojis log = logging.getLogger(__name__) -class Fun: +class Fun(commands.Cog): """A collection of general commands for fun.""" def __init__(self, bot): diff --git a/bot/seasons/evergreen/uptime.py b/bot/seasons/evergreen/uptime.py index 4d5ac584..32c2b59d 100644 --- a/bot/seasons/evergreen/uptime.py +++ b/bot/seasons/evergreen/uptime.py @@ -9,7 +9,7 @@ from bot import start_time log = logging.getLogger(__name__) -class Uptime: +class Uptime(commands.Cog): """A cog for posting the bot's uptime.""" def __init__(self, bot): diff --git a/bot/seasons/halloween/candy_collection.py b/bot/seasons/halloween/candy_collection.py index 2e010cbc..70648e64 100644 --- a/bot/seasons/halloween/candy_collection.py +++ b/bot/seasons/halloween/candy_collection.py @@ -20,7 +20,7 @@ ADD_SKULL_REACTION_CHANCE = 50 # 2% ADD_SKULL_EXISTING_REACTION_CHANCE = 20 # 5% -class CandyCollection: +class CandyCollection(commands.Cog): """Candy collection game Cog.""" def __init__(self, bot): diff --git a/bot/seasons/halloween/hacktoberstats.py b/bot/seasons/halloween/hacktoberstats.py index ce32ad9f..42623669 100644 --- a/bot/seasons/halloween/hacktoberstats.py +++ b/bot/seasons/halloween/hacktoberstats.py @@ -13,7 +13,7 @@ from discord.ext import commands log = logging.getLogger(__name__) -class HacktoberStats: +class HacktoberStats(commands.Cog): """Hacktoberfest statistics Cog.""" def __init__(self, bot): diff --git a/bot/seasons/halloween/halloween_facts.py b/bot/seasons/halloween/halloween_facts.py index 3ec65b87..ee90dbd3 100644 --- a/bot/seasons/halloween/halloween_facts.py +++ b/bot/seasons/halloween/halloween_facts.py @@ -25,7 +25,7 @@ PUMPKIN_ORANGE = discord.Color(0xFF7518) INTERVAL = timedelta(hours=6).total_seconds() -class HalloweenFacts: +class HalloweenFacts(commands.Cog): """A Cog for displaying interesting facts about Halloween.""" def __init__(self, bot): diff --git a/bot/seasons/halloween/halloweenify.py b/bot/seasons/halloween/halloweenify.py index daf64ac0..ce057889 100644 --- a/bot/seasons/halloween/halloweenify.py +++ b/bot/seasons/halloween/halloweenify.py @@ -10,7 +10,7 @@ from discord.ext.commands.cooldowns import BucketType log = logging.getLogger(__name__) -class Halloweenify: +class Halloweenify(commands.Cog): """A cog to change a invokers nickname to a spooky one!""" def __init__(self, bot): diff --git a/bot/seasons/halloween/scarymovie.py b/bot/seasons/halloween/scarymovie.py index 5651c9bb..3878ef7f 100644 --- a/bot/seasons/halloween/scarymovie.py +++ b/bot/seasons/halloween/scarymovie.py @@ -13,7 +13,7 @@ TMDB_API_KEY = environ.get('TMDB_API_KEY') TMDB_TOKEN = environ.get('TMDB_TOKEN') -class ScaryMovie: +class ScaryMovie(commands.Cog): """Selects a random scary movie and embeds info into Discord chat.""" def __init__(self, bot): diff --git a/bot/seasons/halloween/spookyavatar.py b/bot/seasons/halloween/spookyavatar.py index 042df701..15c7c431 100644 --- a/bot/seasons/halloween/spookyavatar.py +++ b/bot/seasons/halloween/spookyavatar.py @@ -12,7 +12,7 @@ from bot.utils.halloween import spookifications log = logging.getLogger(__name__) -class SpookyAvatar: +class SpookyAvatar(commands.Cog): """A cog that spookifies an avatar.""" def __init__(self, bot): diff --git a/bot/seasons/halloween/spookygif.py b/bot/seasons/halloween/spookygif.py index ce8aef06..37d46c01 100644 --- a/bot/seasons/halloween/spookygif.py +++ b/bot/seasons/halloween/spookygif.py @@ -9,7 +9,7 @@ from bot.constants import Tokens log = logging.getLogger(__name__) -class SpookyGif: +class SpookyGif(commands.Cog): """A cog to fetch a random spooky gif from the web!""" def __init__(self, bot): diff --git a/bot/seasons/halloween/spookyreact.py b/bot/seasons/halloween/spookyreact.py index f1dbb905..9b14507a 100644 --- a/bot/seasons/halloween/spookyreact.py +++ b/bot/seasons/halloween/spookyreact.py @@ -17,7 +17,7 @@ SPOOKY_TRIGGERS = { } -class SpookyReact: +class SpookyReact(Cog): """A cog that makes the bot react to message triggers.""" def __init__(self, bot): diff --git a/bot/seasons/halloween/spookysound.py b/bot/seasons/halloween/spookysound.py index b62a9893..7c4d8113 100644 --- a/bot/seasons/halloween/spookysound.py +++ b/bot/seasons/halloween/spookysound.py @@ -10,7 +10,7 @@ from bot.constants import Hacktoberfest log = logging.getLogger(__name__) -class SpookySound: +class SpookySound(commands.Cog): """A cog that plays a spooky sound in a voice channel on command.""" def __init__(self, bot): diff --git a/bot/seasons/halloween/timeleft.py b/bot/seasons/halloween/timeleft.py index 56a3eaed..3ea2d9ad 100644 --- a/bot/seasons/halloween/timeleft.py +++ b/bot/seasons/halloween/timeleft.py @@ -6,7 +6,7 @@ from discord.ext import commands log = logging.getLogger(__name__) -class TimeLeft: +class TimeLeft(commands.Cog): """A Cog that tells you how long left until Hacktober is over!""" def __init__(self, bot): diff --git a/bot/seasons/season.py b/bot/seasons/season.py index a64f6ca6..89ca3ca7 100644 --- a/bot/seasons/season.py +++ b/bot/seasons/season.py @@ -349,7 +349,7 @@ class SeasonBase: await bot.send_log("SeasonalBot Loaded!", f"Active Season: **{self.name_clean}**") -class SeasonManager: +class SeasonManager(commands.Cog): """A cog for managing seasons.""" def __init__(self, bot): diff --git a/bot/seasons/valentines/be_my_valentine.py b/bot/seasons/valentines/be_my_valentine.py index d90e73aa..55c4adb1 100644 --- a/bot/seasons/valentines/be_my_valentine.py +++ b/bot/seasons/valentines/be_my_valentine.py @@ -15,7 +15,7 @@ log = logging.getLogger(__name__) HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_heart:", ":two_hearts:"] -class BeMyValentine: +class BeMyValentine(commands.Cog): """A cog that sends Valentines to other users!""" def __init__(self, bot): diff --git a/bot/seasons/valentines/movie_generator.py b/bot/seasons/valentines/movie_generator.py index 1b1a4a2d..a09a563f 100644 --- a/bot/seasons/valentines/movie_generator.py +++ b/bot/seasons/valentines/movie_generator.py @@ -11,7 +11,7 @@ TMDB_API_KEY = environ.get("TMDB_API_KEY") log = logging.getLogger(__name__) -class RomanceMovieFinder: +class RomanceMovieFinder(commands.Cog): """A cog that returns a random romance movie suggestion to a user.""" def __init__(self, bot): diff --git a/bot/seasons/valentines/myvalenstate.py b/bot/seasons/valentines/myvalenstate.py index 0ea8fbab..344f52f6 100644 --- a/bot/seasons/valentines/myvalenstate.py +++ b/bot/seasons/valentines/myvalenstate.py @@ -15,7 +15,7 @@ with open(Path('bot', 'resources', 'valentines', 'valenstates.json'), 'r') as fi STATES = json.load(file) -class MyValenstate: +class MyValenstate(commands.Cog): """A Cog to find your most likely Valentine's vacation destination.""" def __init__(self, bot): diff --git a/bot/seasons/valentines/pickuplines.py b/bot/seasons/valentines/pickuplines.py index 193eb788..ad75c93f 100644 --- a/bot/seasons/valentines/pickuplines.py +++ b/bot/seasons/valentines/pickuplines.py @@ -14,7 +14,7 @@ with open(Path('bot', 'resources', 'valentines', 'pickup_lines.json'), 'r', enco pickup_lines = load(f) -class PickupLine: +class PickupLine(commands.Cog): """A cog that gives random cheesy pickup lines.""" def __init__(self, bot): diff --git a/bot/seasons/valentines/savethedate.py b/bot/seasons/valentines/savethedate.py index 76f418a2..281625a4 100644 --- a/bot/seasons/valentines/savethedate.py +++ b/bot/seasons/valentines/savethedate.py @@ -16,7 +16,7 @@ with open(Path('bot', 'resources', 'valentines', 'date_ideas.json'), 'r', encodi VALENTINES_DATES = load(f) -class SaveTheDate: +class SaveTheDate(commands.Cog): """A cog that gives random suggestion for a Valentine's date.""" def __init__(self, bot): diff --git a/bot/seasons/valentines/valentine_zodiac.py b/bot/seasons/valentines/valentine_zodiac.py index 764c8ccc..1700260e 100644 --- a/bot/seasons/valentines/valentine_zodiac.py +++ b/bot/seasons/valentines/valentine_zodiac.py @@ -14,7 +14,7 @@ LETTER_EMOJI = ':love_letter:' HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_heart:", ":two_hearts:"] -class ValentineZodiac: +class ValentineZodiac(commands.Cog): """A cog that returns a counter compatible zodiac sign to the given user's zodiac sign.""" def __init__(self, bot): diff --git a/bot/seasons/valentines/whoisvalentine.py b/bot/seasons/valentines/whoisvalentine.py index b7c47121..96d97e22 100644 --- a/bot/seasons/valentines/whoisvalentine.py +++ b/bot/seasons/valentines/whoisvalentine.py @@ -14,7 +14,7 @@ with open(Path("bot", "resources", "valentines", "valentine_facts.json"), "r") a FACTS = json.load(file) -class ValentineFacts: +class ValentineFacts(commands.Cog): """A Cog for displaying facts about Saint Valentine.""" def __init__(self, bot): -- cgit v1.2.3 From ebd4155a2b7eb2b713bf1a04c12feaca3a1dfc1e Mon Sep 17 00:00:00 2001 From: Rohan Date: Sun, 31 Mar 2019 14:34:57 +0530 Subject: added command that gets a random april fools video --- bot/resources/easter/april_fools_vids.json | 125 +++++++++++++++++++++++++++++ bot/seasons/easter/april_fools_vids.py | 50 ++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 bot/resources/easter/april_fools_vids.json create mode 100644 bot/seasons/easter/april_fools_vids.py (limited to 'bot') diff --git a/bot/resources/easter/april_fools_vids.json b/bot/resources/easter/april_fools_vids.json new file mode 100644 index 00000000..dfc01b7b --- /dev/null +++ b/bot/resources/easter/april_fools_vids.json @@ -0,0 +1,125 @@ +{ + "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 Gnome", + "link": "https://youtu.be/vNOllWX-2aE" + }, + { + "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" + } + ] + +} \ No newline at end of file diff --git a/bot/seasons/easter/april_fools_vids.py b/bot/seasons/easter/april_fools_vids.py new file mode 100644 index 00000000..1640e214 --- /dev/null +++ b/bot/seasons/easter/april_fools_vids.py @@ -0,0 +1,50 @@ +import logging +import random +from json import load +from pathlib import Path + +import discord +from discord.ext import commands + +from bot.constants import Colours + +log = logging.getLogger(__name__) + + +class AprilFoolVideos(commands.Cog): + """A cog for april fools that gets a random april fools video from youtube.""" + def __init__(self, bot): + self.bot = bot + self.yt_vids = self.load_json() + self.youtubers = ['google'] # will add more in future + + @staticmethod + def load_json(): + p = Path('bot/resources/april_fools_vids.json') + with p.open() as json_file: + all_vids = load(json_file) + return all_vids + + @commands.command(name='fool') + async def aprial_fools(self, ctx): + """Gets a random april fools video from youtube.""" + random_youtuber = 'google' + + # Change the above line of code to "random_youtuber = random.choice(self.youtubers)". + # I will add more youtubers and their vids in the future, for now only google, Around 30 vids. + + category = self.yt_vids[random_youtuber] + random_vid = random.choice(category) + embed = discord.Embed() + embed.title = random_vid['title'] + embed.colour = Colours.yellow + embed.description = f'Checkout this april fools video by {random_youtuber}' + embed.url = random_vid['link'] + await ctx.send(embed=embed) + await ctx.send(random_vid["link"]) + + +def setup(bot): + """A function to add the cog.""" + bot.add_cog(AprilFoolVideos(bot)) + log.info('April Fools videos cog loaded!') -- cgit v1.2.3 From 9c0e6f01309c3f59a72fc5142285bd567bf96f3c Mon Sep 17 00:00:00 2001 From: Rohan Date: Sun, 31 Mar 2019 14:38:14 +0530 Subject: added yellow color to contants.py --- bot/constants.py | 1 + 1 file changed, 1 insertion(+) (limited to 'bot') diff --git a/bot/constants.py b/bot/constants.py index b19d494b..05253444 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -59,6 +59,7 @@ class Client(NamedTuple): class Colours: + yellow = 0xf9f586 soft_red = 0xcd6d6d soft_green = 0x68c290 bright_green = 0x01d277 -- cgit v1.2.3 From ea8c666c23946f2530ae8896a45ce9a91f601c5e Mon Sep 17 00:00:00 2001 From: Rohan Date: Sun, 31 Mar 2019 14:41:53 +0530 Subject: fixed lint error: --- bot/seasons/easter/april_fools_vids.py | 1 + 1 file changed, 1 insertion(+) (limited to 'bot') diff --git a/bot/seasons/easter/april_fools_vids.py b/bot/seasons/easter/april_fools_vids.py index 1640e214..6ff572b2 100644 --- a/bot/seasons/easter/april_fools_vids.py +++ b/bot/seasons/easter/april_fools_vids.py @@ -20,6 +20,7 @@ class AprilFoolVideos(commands.Cog): @staticmethod def load_json(): + """A function to load json data.""" p = Path('bot/resources/april_fools_vids.json') with p.open() as json_file: all_vids = load(json_file) -- cgit v1.2.3 From f0b2a978b67a6277459e580626aba02a479ba713 Mon Sep 17 00:00:00 2001 From: Suhail Date: Sun, 31 Mar 2019 14:07:03 +0100 Subject: Fixed Images to Public Use images Added them to the repo and linked to the raw --- bot/resources/halloween/spooky_rating.json | 18 +++++++++--------- bot/resources/halloween/spookyrating/baby.jpeg | Bin 0 -> 110346 bytes bot/resources/halloween/spookyrating/candle.jpeg | Bin 0 -> 45981 bytes bot/resources/halloween/spookyrating/clown.jpeg | Bin 0 -> 53035 bytes bot/resources/halloween/spookyrating/costume.jpeg | Bin 0 -> 88629 bytes bot/resources/halloween/spookyrating/devil.jpeg | Bin 0 -> 336208 bytes bot/resources/halloween/spookyrating/ghost.jpeg | Bin 0 -> 29635 bytes .../halloween/spookyrating/jackolantern.jpeg | Bin 0 -> 17598 bytes bot/resources/halloween/spookyrating/necromancer.jpeg | Bin 0 -> 139672 bytes bot/resources/halloween/spookyrating/tiger.jpeg | Bin 0 -> 52851 bytes 10 files changed, 9 insertions(+), 9 deletions(-) create mode 100644 bot/resources/halloween/spookyrating/baby.jpeg create mode 100644 bot/resources/halloween/spookyrating/candle.jpeg create mode 100644 bot/resources/halloween/spookyrating/clown.jpeg create mode 100644 bot/resources/halloween/spookyrating/costume.jpeg create mode 100644 bot/resources/halloween/spookyrating/devil.jpeg create mode 100644 bot/resources/halloween/spookyrating/ghost.jpeg create mode 100644 bot/resources/halloween/spookyrating/jackolantern.jpeg create mode 100644 bot/resources/halloween/spookyrating/necromancer.jpeg create mode 100644 bot/resources/halloween/spookyrating/tiger.jpeg (limited to 'bot') diff --git a/bot/resources/halloween/spooky_rating.json b/bot/resources/halloween/spooky_rating.json index b83624fe..1815befc 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://www.thoughtco.com/thmb/qv6FUFM6V_p7YUAk9lT6fZG8s3M=/768x0/filters:no_upscale():max_bytes(150000):strip_icc()/smoke-trailing-from-extinguished-white-candle-565954737-584daec23df78c491e6282a5.jpg" + "image": "https://raw.githubusercontent.com/python-discord/seasonalbot/master/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://static3.babygagaimages.com/wordpress/wp-content/uploads/2017/01/78ibcx9ire.jpg" + "image": "https://raw.githubusercontent.com/python-discord/seasonalbot/master/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://www.kimballstock.com/pix/TGR/04/TGR-04-AC0011-01P.JPG" + "image": "https://raw.githubusercontent.com/python-discord/seasonalbot/master/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://inst-2.cdn.shockers.de/hs_cdn/out/pictures//master/product/1/3d-skelett-kostuem-adult--skelett-overall-fuer-halloween--skeleton-jumpsuit-costume--15555.jpg" + "image": "https://raw.githubusercontent.com/python-discord/seasonalbot/master/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://d2v9y0dukr6mq2.cloudfront.net/video/thumbnail/B3mqp6o/videoblocks-shadow-silhouette-of-a-dark-halloween-ghost-dancing-in-fog-behind-the-glass_sfmu3or_g_thumbnail-full01.png" + "image": "https://raw.githubusercontent.com/python-discord/seasonalbot/master/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://gswiki.play.net/images/thumb/7/7e/Necromancer.jpg/450px-Necromancer.jpg" + "image": "https://raw.githubusercontent.com/python-discord/seasonalbot/master/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://www.indiewire.com/wp-content/uploads/2018/07/pennywise-bill-skarsgard-it-movie.jpg?w=780" + "image": "https://raw.githubusercontent.com/python-discord/seasonalbot/master/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://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX6978186.jpg" + "image": "https://raw.githubusercontent.com/python-discord/seasonalbot/master/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://images.drawinghub.com/13941_501/how-to-draw-a-devil-face_5afe20070a9c10.00090710_10491_3_4.png" + "image": "https://raw.githubusercontent.com/python-discord/seasonalbot/master/bot/resources/halloween/spookyrating/devil.jpeg" } } \ No newline at end of file diff --git a/bot/resources/halloween/spookyrating/baby.jpeg b/bot/resources/halloween/spookyrating/baby.jpeg new file mode 100644 index 00000000..199f8bca Binary files /dev/null and b/bot/resources/halloween/spookyrating/baby.jpeg differ diff --git a/bot/resources/halloween/spookyrating/candle.jpeg b/bot/resources/halloween/spookyrating/candle.jpeg new file mode 100644 index 00000000..9913752b Binary files /dev/null and b/bot/resources/halloween/spookyrating/candle.jpeg differ diff --git a/bot/resources/halloween/spookyrating/clown.jpeg b/bot/resources/halloween/spookyrating/clown.jpeg new file mode 100644 index 00000000..f23c4f70 Binary files /dev/null and b/bot/resources/halloween/spookyrating/clown.jpeg differ diff --git a/bot/resources/halloween/spookyrating/costume.jpeg b/bot/resources/halloween/spookyrating/costume.jpeg new file mode 100644 index 00000000..b3c21af0 Binary files /dev/null and b/bot/resources/halloween/spookyrating/costume.jpeg differ diff --git a/bot/resources/halloween/spookyrating/devil.jpeg b/bot/resources/halloween/spookyrating/devil.jpeg new file mode 100644 index 00000000..4f45aaa7 Binary files /dev/null and b/bot/resources/halloween/spookyrating/devil.jpeg differ diff --git a/bot/resources/halloween/spookyrating/ghost.jpeg b/bot/resources/halloween/spookyrating/ghost.jpeg new file mode 100644 index 00000000..0cb13346 Binary files /dev/null and b/bot/resources/halloween/spookyrating/ghost.jpeg differ diff --git a/bot/resources/halloween/spookyrating/jackolantern.jpeg b/bot/resources/halloween/spookyrating/jackolantern.jpeg new file mode 100644 index 00000000..d7cf3d08 Binary files /dev/null and b/bot/resources/halloween/spookyrating/jackolantern.jpeg differ diff --git a/bot/resources/halloween/spookyrating/necromancer.jpeg b/bot/resources/halloween/spookyrating/necromancer.jpeg new file mode 100644 index 00000000..60b1e689 Binary files /dev/null and b/bot/resources/halloween/spookyrating/necromancer.jpeg differ diff --git a/bot/resources/halloween/spookyrating/tiger.jpeg b/bot/resources/halloween/spookyrating/tiger.jpeg new file mode 100644 index 00000000..0419f5df Binary files /dev/null and b/bot/resources/halloween/spookyrating/tiger.jpeg differ -- cgit v1.2.3 From 8f1c52d10041185d94c53dafc65baaa9d4f88889 Mon Sep 17 00:00:00 2001 From: sco1 Date: Sun, 31 Mar 2019 09:14:45 -0400 Subject: Migrate season manager cog unload to new function --- bot/seasons/season.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/season.py b/bot/seasons/season.py index 89ca3ca7..96aea5b8 100644 --- a/bot/seasons/season.py +++ b/bot/seasons/season.py @@ -538,5 +538,5 @@ class SeasonManager(commands.Cog): await self.season.announce_season() - def __unload(self): + def cog_unload(self): self.season_task.cancel() -- cgit v1.2.3 From 6b044e8e8a596d10b9284a1c14fb549ec19b02cd Mon Sep 17 00:00:00 2001 From: Suhail Date: Sun, 31 Mar 2019 14:14:49 +0100 Subject: Docstring Fixes --- bot/seasons/halloween/spookyrating.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'bot') diff --git a/bot/seasons/halloween/spookyrating.py b/bot/seasons/halloween/spookyrating.py index 8c4f1b12..01fb607b 100644 --- a/bot/seasons/halloween/spookyrating.py +++ b/bot/seasons/halloween/spookyrating.py @@ -17,9 +17,7 @@ with Path('bot', 'resources', 'halloween', 'spooky_rating.json').open() as file: class SpookyRating: - """ - A cog for calculating one's spooky rating - """ + """A cog for calculating one's spooky rating""" def __init__(self, bot): self.bot = bot @@ -65,5 +63,6 @@ class SpookyRating: def setup(bot): + """Cog load.""" bot.add_cog(SpookyRating(bot)) log.info("SpookyRating cog loaded") -- cgit v1.2.3 From cfd14448ae8900f3caec20a232f6e09fdfd2e4b3 Mon Sep 17 00:00:00 2001 From: sco1 Date: Sun, 31 Mar 2019 09:17:37 -0400 Subject: Add cog unload docstring --- bot/seasons/season.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'bot') diff --git a/bot/seasons/season.py b/bot/seasons/season.py index 96aea5b8..6d992276 100644 --- a/bot/seasons/season.py +++ b/bot/seasons/season.py @@ -539,4 +539,6 @@ class SeasonManager(commands.Cog): await self.season.announce_season() def cog_unload(self): + """Cancel season-related tasks on cog unload.""" + self.season_task.cancel() -- cgit v1.2.3 From 7306ca0fb508c5192c47ac5db02ca998f443fc77 Mon Sep 17 00:00:00 2001 From: Suhail Date: Sun, 31 Mar 2019 14:52:30 +0100 Subject: add `commands.Cog` --- bot/seasons/halloween/spookyrating.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/halloween/spookyrating.py b/bot/seasons/halloween/spookyrating.py index 01fb607b..a9cfda9b 100644 --- a/bot/seasons/halloween/spookyrating.py +++ b/bot/seasons/halloween/spookyrating.py @@ -16,7 +16,7 @@ with Path('bot', 'resources', 'halloween', 'spooky_rating.json').open() as file: SPOOKY_DATA = sorted((int(key), value) for key, value in SPOOKY_DATA.items()) -class SpookyRating: +class SpookyRating(commands.Cog): """A cog for calculating one's spooky rating""" def __init__(self, bot): -- cgit v1.2.3 From 57471e2a0eef102aeb0db921154bd59285a75479 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sun, 31 Mar 2019 20:15:35 +0530 Subject: random selection of youtuber insted of hardcoding google --- bot/seasons/easter/april_fools_vids.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/april_fools_vids.py b/bot/seasons/easter/april_fools_vids.py index 6ff572b2..b71beae2 100644 --- a/bot/seasons/easter/april_fools_vids.py +++ b/bot/seasons/easter/april_fools_vids.py @@ -29,11 +29,7 @@ class AprilFoolVideos(commands.Cog): @commands.command(name='fool') async def aprial_fools(self, ctx): """Gets a random april fools video from youtube.""" - random_youtuber = 'google' - - # Change the above line of code to "random_youtuber = random.choice(self.youtubers)". - # I will add more youtubers and their vids in the future, for now only google, Around 30 vids. - + random_youtuber = random.choice(self.youtubers) category = self.yt_vids[random_youtuber] random_vid = random.choice(category) embed = discord.Embed() -- cgit v1.2.3 From 7220b18baf1bbada4be542145315f5ae90b7d3b7 Mon Sep 17 00:00:00 2001 From: sco1 Date: Sun, 31 Mar 2019 11:35:00 -0400 Subject: Bump Easter event 1 day to accommodate April Fools --- bot/seasons/easter/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/easter/__init__.py b/bot/seasons/easter/__init__.py index a816fe08..83d12ead 100644 --- a/bot/seasons/easter/__init__.py +++ b/bot/seasons/easter/__init__.py @@ -26,7 +26,7 @@ class Easter(SeasonBase): greeting = "Happy Easter!" # Duration of season - start_date = "01/04" + start_date = "02/04" end_date = "30/04" colour = Colours.pink -- cgit v1.2.3 From 63bb8ef804451af0c9475e19ad53b2d52a78c50d Mon Sep 17 00:00:00 2001 From: Suhail Date: Sun, 31 Mar 2019 17:57:33 +0100 Subject: Egghead Quiz Easter quiz including the Cog Python file and the Questions JSON file --- bot/resources/easter/egghead_questions.json | 181 ++++++++++++++++++++++++++++ bot/seasons/easter/egghead_quiz.py | 118 ++++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 bot/resources/easter/egghead_questions.json create mode 100644 bot/seasons/easter/egghead_quiz.py (limited to 'bot') diff --git a/bot/resources/easter/egghead_questions.json b/bot/resources/easter/egghead_questions.json new file mode 100644 index 00000000..141a39d5 --- /dev/null +++ b/bot/resources/easter/egghead_questions.json @@ -0,0 +1,181 @@ +[ + { + "question": "Where did the idea of the Easter Bunny originate?", + "answers": [ + "Russia", + "The United States", + "The UK", + "Germany" + ], + "correct_answer": 4 + }, + { + "question": "The Easter Bunny was originally going to be a...", + "answers": [ + "hare", + "possum", + "cat", + "dove" + ], + "correct_answer": 1 + }, + { + "question": "Which of the following is NOT a movie about Easter?", + "answers": [ + "Winnie the Pooh - Springtime with Roo", + "It's a Wonderful Life", + "The Passion of the Christ", + "Here Comes Peter Cottontail" + ], + "correct_answer": 2 + }, + { + "question": "In Australia, what animal is used instead of the Easter Bunny?", + "answers": [ + "kangaroo", + "wombat", + "koala", + "bilby" + ], + "correct_answer": 4 + }, + { + "question": "When was the first Earth Day?", + "answers": [ + "1982", + "2003", + "1999", + "1970" + ], + "correct_answer": 3 + }, + { + "question": "Who is considered to be the founder of Earth Day?", + "answers": [ + "President Jimmy Carter", + "President John F. Kennedy", + "Vice President Al Gore", + "Sentator Gaylord Nelson" + ], + "correct_answer": 4 + }, + { + "question": "Approximately how many countries participated in Earth Day 2000?", + "answers": [ + "60", + "140", + "180", + "240" + ], + "correct_answer": 3 + }, + { + "question": "As Earth Day is this month, how old is the Earth?", + "answers": [ + "4.5 billion years old", + "5 million years old", + "10 billion years old", + "6.7 billion years old" + ], + "correct_answer": 1 + }, + { + "question": "As a celebration of Earth Day, what is the percentage of Oxygen in the Earth's atmosphere?", + "answers": [ + "18%", + "21%", + "25%", + "31%" + ], + "correct_answer": 2 + }, + { + "question": "In what year did Google begin its tradition of April Fools Jokes?", + "answers": [ + "1997", + "2000", + "2003", + "2007" + ], + "correct_answer": 2 + }, + { + "question": "Which type of chocolate is the most healthy?", + "answers": [ + "Dark", + "White", + "Milk" + ], + "correct_answer": 1 + }, + { + "question": "How many bars of milk chocolate would you have to eat to get the same amount of caffeine as in one cup of coffee?", + "answers": [ + "3", + "9", + "14", + "20" + ], + "correct_answer": 3 + }, + { + "question": "Aztecs used to use one of the ingedients of chocolate, cocoa beans, as...", + "answers": [ + "currency", + "medicine", + "dye", + "fertilizer" + ], + "correct_answer": 1 + }, + { + "question": "Which European country was the first to enjoy chocolate?", + "answers": [ + "France", + "Spain", + "England", + "Switxherland" + ], + "correct_answer": 2 + }, + { + "question": "The first European Chocolate Shop opened in what city in 1657?", + "answers": [ + "Paris, France", + "Madrid, Spain", + "Zürich, Switzerland", + "London, England" + ], + "correct_answer": 4 + }, + { + "question": "On average, how many eggs does a hen lay in a year?", + "answers": [ + "Between 200-230", + "Between 250-270", + "Between 300-330", + "Between 370-400" + ], + "correct_answer": 2 + }, + { + "question": "What determines the colour of an egg yolk?", + "answers": [ + "The size of the hen", + "The age of a hen", + "The diet of a hen", + "The colour of a hen's feathers" + ], + "correct_answer": 3 + }, + { + "question": "What country produces the most eggs in a year?", + "answers": [ + "China", + "India", + "The United States", + "Japan" + ], + "correct_answer": 1 + } +] \ No newline at end of file diff --git a/bot/seasons/easter/egghead_quiz.py b/bot/seasons/easter/egghead_quiz.py new file mode 100644 index 00000000..056aedf8 --- /dev/null +++ b/bot/seasons/easter/egghead_quiz.py @@ -0,0 +1,118 @@ +import asyncio +import logging +import random +from json import load +from pathlib import Path + +import discord +from discord.ext import commands + +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) + + +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' +] + + +class EggheadQuiz(commands.Cog): + """This cog contains the command for the Easter quiz!""" + + def __init__(self, bot): + self.bot = bot + self.quiz_messages = {} + + @commands.command(aliases=["eggheadquiz", "easterquiz"]) + async def eggquiz(self, ctx): + """ + Gives a random quiz question, waits 30 seconds and then outputs the answer + + Also informs of the percentages and votes of each option + """ + + random_question = random.choice(EGGHEAD_QUESTIONS) + question, answers = random_question["question"], random_question["answers"] + answers = [(EMOJIS[i], a) for i, a in enumerate(answers)] + correct = EMOJIS[random_question["correct_answer"]-1] + + valid_emojis = [emoji for emoji, _ in answers] + + description = "\n".join([f"{emoji} -> **{answer}**" for emoji, answer in answers]) + + q_embed = discord.Embed(title=question, description=description, colour=Colours.pink) + + msg = await ctx.send(embed=q_embed) + for emoji in valid_emojis: + await msg.add_reaction(emoji) + + self.quiz_messages[msg.id] = valid_emojis + + await asyncio.sleep(30) + + del self.quiz_messages[msg.id] + + msg = await ctx.channel.get_message(msg.id) # Refreshes message + + total_no = sum([len(await r.users().flatten()) for r in msg.reactions]) - len(valid_emojis) # - bot's reactions + + if total_no == 0: + return await msg.delete() # to avoid ZeroDivisionError if nobody reacts + + results = ["**VOTES:**"] + for emoji, _ in answers: + num = [len(await r.users().flatten()) for r in msg.reactions if str(r.emoji) == emoji][0] - 1 + percent = round(100 * num / total_no) + s = "" if num == 1 else "s" + string = f"{emoji} - {num} vote{s} ({percent}%)" + results.append(string) + + mentions = " ".join([ + u.mention for u in [ + await r.users().flatten() for r in msg.reactions if str(r.emoji) == correct + ][0] if not u.bot + ]) + + content = f"Well done {mentions} for getting it correct!" if mentions else "Nobody got it right..." + + a_embed = discord.Embed( + title=f"The correct answer was {correct}!", + description="\n".join(results), + colour=Colours.pink + ) + + await ctx.send(content, embed=a_embed) + + @staticmethod + async def already_reacted(message, user): + """Returns whether a given user has reacted more than once to a given message""" + users = [u.id for reaction in [await r.users().flatten() for r in message.reactions] for u in reaction] + return users.count(user.id) > 1 # Old reaction plus new reaction + + @commands.Cog.listener() + async def on_reaction_add(self, reaction, user): + """Listener to listen specifically for reactions of quiz messages""" + if user.bot: + return + if reaction.message.id not in self.quiz_messages: + return + if str(reaction.emoji) not in self.quiz_messages[reaction.message.id]: + return await reaction.message.remove_reaction(reaction, user) + if await self.already_reacted(reaction.message, user): + return await reaction.message.remove_reaction(reaction, user) + + +def setup(bot): + """Cog load.""" + + bot.add_cog(EggheadQuiz(bot)) + log.info("EggheadQuiz bot loaded") -- cgit v1.2.3 From 5093beb643f0c16ee19ba5fbfb822c9b3c64a704 Mon Sep 17 00:00:00 2001 From: sco1 Date: Sun, 31 Mar 2019 17:01:44 -0400 Subject: Add lemon counter For April Fools --- bot/seasons/evergreen/lemonstats.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 bot/seasons/evergreen/lemonstats.py (limited to 'bot') diff --git a/bot/seasons/evergreen/lemonstats.py b/bot/seasons/evergreen/lemonstats.py new file mode 100644 index 00000000..b23c65a4 --- /dev/null +++ b/bot/seasons/evergreen/lemonstats.py @@ -0,0 +1,31 @@ +import logging + +from discord.ext import commands + + +log = logging.getLogger(__name__) + + +class LemonStats(commands.Cog): + """A cog for generating useful lemon-related statistics.""" + + def __init__(self, bot): + self.bot = bot + + @commands.command() + async def lemoncount(self, ctx: commands.Context): + """Count the number of users on the server with `'lemon'` in their nickname.""" + + async with ctx.typing(): + lemoncount = sum( + ['lemon' in server_member.display_name.lower() for server_member in self.bot.guilds[0].members] + ) + + await ctx.send(f"There are currently {lemoncount} lemons on the server.") + + +def setup(bot): + """Load LemonStats Cog.""" + + bot.add_cog(LemonStats(bot)) + log.info("LemonStats cog loaded") -- cgit v1.2.3 From 3417793d0ebe6da7395d10af8887be7bac4a1198 Mon Sep 17 00:00:00 2001 From: Suhail Date: Sun, 31 Mar 2019 23:08:28 +0100 Subject: Egghead Quiz - Requested changes - Changed `get_message` to `fetch_message` - Added comment explaining `EMOJIS` - Edited the answers in the JSON and align it with Python's indexing convention - Added `TIMELIMIT` constant --- bot/resources/easter/egghead_questions.json | 36 ++++++++++++++--------------- bot/seasons/easter/egghead_quiz.py | 13 +++++++---- 2 files changed, 26 insertions(+), 23 deletions(-) (limited to 'bot') diff --git a/bot/resources/easter/egghead_questions.json b/bot/resources/easter/egghead_questions.json index 141a39d5..1de2b203 100644 --- a/bot/resources/easter/egghead_questions.json +++ b/bot/resources/easter/egghead_questions.json @@ -7,7 +7,7 @@ "The UK", "Germany" ], - "correct_answer": 4 + "correct_answer": 3 }, { "question": "The Easter Bunny was originally going to be a...", @@ -17,7 +17,7 @@ "cat", "dove" ], - "correct_answer": 1 + "correct_answer": 0 }, { "question": "Which of the following is NOT a movie about Easter?", @@ -27,7 +27,7 @@ "The Passion of the Christ", "Here Comes Peter Cottontail" ], - "correct_answer": 2 + "correct_answer": 1 }, { "question": "In Australia, what animal is used instead of the Easter Bunny?", @@ -37,7 +37,7 @@ "koala", "bilby" ], - "correct_answer": 4 + "correct_answer": 3 }, { "question": "When was the first Earth Day?", @@ -47,7 +47,7 @@ "1999", "1970" ], - "correct_answer": 3 + "correct_answer": 2 }, { "question": "Who is considered to be the founder of Earth Day?", @@ -57,7 +57,7 @@ "Vice President Al Gore", "Sentator Gaylord Nelson" ], - "correct_answer": 4 + "correct_answer": 3 }, { "question": "Approximately how many countries participated in Earth Day 2000?", @@ -67,7 +67,7 @@ "180", "240" ], - "correct_answer": 3 + "correct_answer": 2 }, { "question": "As Earth Day is this month, how old is the Earth?", @@ -77,7 +77,7 @@ "10 billion years old", "6.7 billion years old" ], - "correct_answer": 1 + "correct_answer": 0 }, { "question": "As a celebration of Earth Day, what is the percentage of Oxygen in the Earth's atmosphere?", @@ -87,7 +87,7 @@ "25%", "31%" ], - "correct_answer": 2 + "correct_answer": 1 }, { "question": "In what year did Google begin its tradition of April Fools Jokes?", @@ -97,7 +97,7 @@ "2003", "2007" ], - "correct_answer": 2 + "correct_answer": 1 }, { "question": "Which type of chocolate is the most healthy?", @@ -106,7 +106,7 @@ "White", "Milk" ], - "correct_answer": 1 + "correct_answer": 0 }, { "question": "How many bars of milk chocolate would you have to eat to get the same amount of caffeine as in one cup of coffee?", @@ -116,7 +116,7 @@ "14", "20" ], - "correct_answer": 3 + "correct_answer": 2 }, { "question": "Aztecs used to use one of the ingedients of chocolate, cocoa beans, as...", @@ -126,7 +126,7 @@ "dye", "fertilizer" ], - "correct_answer": 1 + "correct_answer": 0 }, { "question": "Which European country was the first to enjoy chocolate?", @@ -136,7 +136,7 @@ "England", "Switxherland" ], - "correct_answer": 2 + "correct_answer": 1 }, { "question": "The first European Chocolate Shop opened in what city in 1657?", @@ -146,7 +146,7 @@ "Zürich, Switzerland", "London, England" ], - "correct_answer": 4 + "correct_answer": 3 }, { "question": "On average, how many eggs does a hen lay in a year?", @@ -156,7 +156,7 @@ "Between 300-330", "Between 370-400" ], - "correct_answer": 2 + "correct_answer": 1 }, { "question": "What determines the colour of an egg yolk?", @@ -166,7 +166,7 @@ "The diet of a hen", "The colour of a hen's feathers" ], - "correct_answer": 3 + "correct_answer": 2 }, { "question": "What country produces the most eggs in a year?", @@ -176,6 +176,6 @@ "The United States", "Japan" ], - "correct_answer": 1 + "correct_answer": 0 } ] \ No newline at end of file diff --git a/bot/seasons/easter/egghead_quiz.py b/bot/seasons/easter/egghead_quiz.py index 056aedf8..8dd2c21d 100644 --- a/bot/seasons/easter/egghead_quiz.py +++ b/bot/seasons/easter/egghead_quiz.py @@ -22,7 +22,9 @@ EMOJIS = [ '\U0001f1f5', '\U0001f1f6', '\U0001f1f7', '\U0001f1f8', '\U0001f1f9', '\U0001f1fa', '\U0001f1fb', '\U0001f1fc', '\U0001f1fd', '\U0001f1fe', '\U0001f1ff' -] +] # Regional Indicators A-Z (used for voting) + +TIMELIMIT = 30 class EggheadQuiz(commands.Cog): @@ -43,11 +45,12 @@ class EggheadQuiz(commands.Cog): random_question = random.choice(EGGHEAD_QUESTIONS) question, answers = random_question["question"], random_question["answers"] answers = [(EMOJIS[i], a) for i, a in enumerate(answers)] - correct = EMOJIS[random_question["correct_answer"]-1] + correct = EMOJIS[random_question["correct_answer"]] valid_emojis = [emoji for emoji, _ in answers] - description = "\n".join([f"{emoji} -> **{answer}**" for emoji, answer in answers]) + description = f"You have {TIMELIMIT} seconds to vote.\n\n" + description += "\n".join([f"{emoji} -> **{answer}**" for emoji, answer in answers]) q_embed = discord.Embed(title=question, description=description, colour=Colours.pink) @@ -57,11 +60,11 @@ class EggheadQuiz(commands.Cog): self.quiz_messages[msg.id] = valid_emojis - await asyncio.sleep(30) + await asyncio.sleep(TIMELIMIT) del self.quiz_messages[msg.id] - msg = await ctx.channel.get_message(msg.id) # Refreshes message + msg = await ctx.channel.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 -- cgit v1.2.3 From 0205ed9ed83d282153da2406d4652e8a5b0a40ff Mon Sep 17 00:00:00 2001 From: Suhail Date: Mon, 1 Apr 2019 01:33:15 +0100 Subject: Easter Egg Decorating - Added command that uses PIL - Added 6 Easter decorations --- bot/resources/easter/easter_eggs/design1.png | Bin 0 -> 3996 bytes bot/resources/easter/easter_eggs/design2.png | Bin 0 -> 3918 bytes bot/resources/easter/easter_eggs/design3.png | Bin 0 -> 3349 bytes bot/resources/easter/easter_eggs/design4.png | Bin 0 -> 3355 bytes bot/resources/easter/easter_eggs/design5.png | Bin 0 -> 3054 bytes bot/resources/easter/easter_eggs/design6.png | Bin 0 -> 4758 bytes bot/seasons/easter/egg_decorating.py | 95 +++++++++++++++++++++++++++ 7 files changed, 95 insertions(+) create mode 100644 bot/resources/easter/easter_eggs/design1.png create mode 100644 bot/resources/easter/easter_eggs/design2.png create mode 100644 bot/resources/easter/easter_eggs/design3.png create mode 100644 bot/resources/easter/easter_eggs/design4.png create mode 100644 bot/resources/easter/easter_eggs/design5.png create mode 100644 bot/resources/easter/easter_eggs/design6.png create mode 100644 bot/seasons/easter/egg_decorating.py (limited to 'bot') diff --git a/bot/resources/easter/easter_eggs/design1.png b/bot/resources/easter/easter_eggs/design1.png new file mode 100644 index 00000000..d887c590 Binary files /dev/null and b/bot/resources/easter/easter_eggs/design1.png differ diff --git a/bot/resources/easter/easter_eggs/design2.png b/bot/resources/easter/easter_eggs/design2.png new file mode 100644 index 00000000..c4fff644 Binary files /dev/null and b/bot/resources/easter/easter_eggs/design2.png differ diff --git a/bot/resources/easter/easter_eggs/design3.png b/bot/resources/easter/easter_eggs/design3.png new file mode 100644 index 00000000..803bc1e3 Binary files /dev/null and b/bot/resources/easter/easter_eggs/design3.png differ diff --git a/bot/resources/easter/easter_eggs/design4.png b/bot/resources/easter/easter_eggs/design4.png new file mode 100644 index 00000000..38e6a83f Binary files /dev/null and b/bot/resources/easter/easter_eggs/design4.png differ diff --git a/bot/resources/easter/easter_eggs/design5.png b/bot/resources/easter/easter_eggs/design5.png new file mode 100644 index 00000000..56662c26 Binary files /dev/null and b/bot/resources/easter/easter_eggs/design5.png differ diff --git a/bot/resources/easter/easter_eggs/design6.png b/bot/resources/easter/easter_eggs/design6.png new file mode 100644 index 00000000..5372439a Binary files /dev/null and b/bot/resources/easter/easter_eggs/design6.png differ diff --git a/bot/seasons/easter/egg_decorating.py b/bot/seasons/easter/egg_decorating.py new file mode 100644 index 00000000..46351534 --- /dev/null +++ b/bot/seasons/easter/egg_decorating.py @@ -0,0 +1,95 @@ +import logging +import random +from io import BytesIO +from pathlib import Path + + +import discord +from PIL import Image +from discord.ext import commands + +log = logging.getLogger(__name__) + +COLOURS = [ + (255, 0, 0, 255), (255, 128, 0, 255), (255, 255, 0, 255), (0, 255, 0, 255), + (0, 255, 255, 255), (0, 0, 255, 255), (255, 0, 255, 255), (128, 0, 128, 255) +] # Colours to be replaced - Red, Orange, Yellow, Green, Light Blue, Dark Blue, Pink, Purple + +IRREPLACEABLE = [ + (0, 0, 0, 0), (0, 0, 0, 255) +] # Colours that are meant to stay the same - Transparent and Black + + +class EggDecorating(commands.Cog): + """A Command that lets you decorate some easter eggs!""" + + def __init__(self, bot): + self.bot = bot + + @commands.command(aliases=["decorateegg"]) + async def eggdecorate(self, ctx, *colours): + """ + This 'paints' a beautiful egg using inputted colours + + It picks from a random set of designs and alters the colours to the user's liking + """ + + if len(colours) < 2: + return await ctx.send("You must include at least 2 colours!") + + invalid = [] + converted = [] + for c in colours: + try: + colour = await commands.ColourConverter().convert(ctx, c) + # Attempts to convert the arguments into discord.Colour + converted.append(colour) + except commands.BadArgument: + invalid.append(c) + + if len(invalid) > 1: + return await ctx.send(f"The following colours are invalid: {' '.join(invalid)}") + elif len(invalid) == 1: + return await ctx.send(f"{invalid[0]} is an invalid colour!") + + colours = converted + + async with ctx.typing(): + colours *= 4 + # This is to ensure that no IndexErrors are raised since the most amount of colours on an egg is 8 + num = random.randint(1, 6) + im = Image.open(Path("bot", "resources", "easter", "easter_eggs", f"design{num}.png")) + data = list(im.getdata()) + + replaceable = {x for x in data if x not in IRREPLACEABLE} # Turns it into a set to avoid duplicates + replaceable = sorted(replaceable, key=COLOURS.index) # Sorts it by colour order + + replacing_colours = {colour: colours[i] for i, colour in enumerate(replaceable)} + new_data = [] + for x in data: + if x in replacing_colours: + new_data.append((*replacing_colours[x].to_rgb(), 255)) + # Also ensures that the alpha channel has a value + else: + new_data.append(x) + new_im = Image.new(im.mode, im.size) + new_im.putdata(new_data) + + bufferedio = BytesIO() + new_im.save(bufferedio, format="PNG") + + bufferedio.seek(0) + + file = discord.File(bufferedio, filename="egg.png") # Creates file to be used in embed + embed = discord.Embed(title="Your egg", description="Here is your pretty little egg. Hope you like it!") + embed.set_image(url="attachment://egg.png") + embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar_url) + + await ctx.send(file=file, embed=embed) + + +def setup(bot): + """Cog load.""" + + bot.add_cog(EggDecorating(bot)) + log.info("EggDecorating cog loaded.") -- cgit v1.2.3 From 0cc8bec36c1c236d1658d1117da8d18132dad41e Mon Sep 17 00:00:00 2001 From: Suhail Date: Mon, 1 Apr 2019 11:58:37 +0100 Subject: Egghead Quiz - Typo fixes --- bot/resources/easter/egghead_questions.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'bot') diff --git a/bot/resources/easter/egghead_questions.json b/bot/resources/easter/egghead_questions.json index 1de2b203..e4e21ebe 100644 --- a/bot/resources/easter/egghead_questions.json +++ b/bot/resources/easter/egghead_questions.json @@ -55,7 +55,7 @@ "President Jimmy Carter", "President John F. Kennedy", "Vice President Al Gore", - "Sentator Gaylord Nelson" + "Senator Gaylord Nelson" ], "correct_answer": 3 }, @@ -134,7 +134,7 @@ "France", "Spain", "England", - "Switxherland" + "Switzerland" ], "correct_answer": 1 }, -- cgit v1.2.3 From ce25477b5df2bf727b35c02a3c8b8167c4c7d058 Mon Sep 17 00:00:00 2001 From: sco1 Date: Tue, 2 Apr 2019 09:44:18 -0400 Subject: Update contributor doc Remove April Fools lemon counter --- CONTRIBUTING.md | 67 ++++++++++++++++++++++++++++++++++--- bot/seasons/evergreen/lemonstats.py | 31 ----------------- 2 files changed, 63 insertions(+), 35 deletions(-) delete mode 100644 bot/seasons/evergreen/lemonstats.py (limited to 'bot') diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4161715e..af82dece 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,10 +4,12 @@ Seasonalbot is a community project for the Python Discord community over at http 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. +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 by or approved a staff member or event handler. These will be labeled with the `approved` label. Feel free to suggest issues of your own, which staff can choose to approve. +2. Your pull request must solve an issue created by or approved a staff member or event handler. 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! @@ -21,10 +23,13 @@ Our projects are open-source and are automatically deployed whenever commits are 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. 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 a pull request, **do not open your own pull request for the same task**. Instead, collaborate with the author(s) of the existing pull request. Communication is key, and there's no point in two separate implementations of the same thing. +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. -10. **Work as a team** and collaborate whereever possible. Keep things friendly and help each other out - these are shared projects and nobody likes to have their feet trodden on. + * 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. **Internal projects are internal**. As a contributor, you have access to information that the rest of the server does not. With this trust comes responsibility - do not release any information you have learned as a result of your contributor position. We are very strict about announcing things at specific times, and many staff members will not appreciate a disruption of the announcement schedule. +12. 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, especially in relation to Rule 7. @@ -34,7 +39,56 @@ All projects evolve over time, and this contribution guide is no different. This ## Supplemental Information ### Developer Environment -Seasonalbot 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. +Seasonalbot 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 [Seasonalbot's README](https://github.com/python-discord/seasonalbot/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. + +### 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 +def foo(input_1: int, input_2: dict) -> bool: +``` + +Tells us that `foo` accepts an `int` and a `dict` 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 +def foo(bar: int, baz: dict=None) -> bool: + """ + Does some things with some stuff. + + :param bar: Some input + :param baz: Optional, some other input + + :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 +def foo(bar: int, baz: dict=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: @@ -45,6 +99,11 @@ The project currently defines [`logging`](https://docs.python.org/3/library/logg * **ERROR:** An error that affects the specific part that is being interacted with * **CRITICAL:** An error that affects the whole application. +### Work in Progress (WIP) PRs +Github [has introduced a new 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. + ## Footnotes This document was inspired by the [Glowstone contribution guidelines](https://github.com/GlowstoneMC/Glowstone/blob/dev/docs/CONTRIBUTING.md). diff --git a/bot/seasons/evergreen/lemonstats.py b/bot/seasons/evergreen/lemonstats.py deleted file mode 100644 index b23c65a4..00000000 --- a/bot/seasons/evergreen/lemonstats.py +++ /dev/null @@ -1,31 +0,0 @@ -import logging - -from discord.ext import commands - - -log = logging.getLogger(__name__) - - -class LemonStats(commands.Cog): - """A cog for generating useful lemon-related statistics.""" - - def __init__(self, bot): - self.bot = bot - - @commands.command() - async def lemoncount(self, ctx: commands.Context): - """Count the number of users on the server with `'lemon'` in their nickname.""" - - async with ctx.typing(): - lemoncount = sum( - ['lemon' in server_member.display_name.lower() for server_member in self.bot.guilds[0].members] - ) - - await ctx.send(f"There are currently {lemoncount} lemons on the server.") - - -def setup(bot): - """Load LemonStats Cog.""" - - bot.add_cog(LemonStats(bot)) - log.info("LemonStats cog loaded") -- cgit v1.2.3 From 1d37b848c655af1b17511d5b4b7d78fe3888e0ae Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 2 Apr 2019 21:53:18 +0530 Subject: sending only one message insted of 2 (removed the embed) --- bot/seasons/easter/april_fools_vids.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/april_fools_vids.py b/bot/seasons/easter/april_fools_vids.py index b71beae2..553b2a78 100644 --- a/bot/seasons/easter/april_fools_vids.py +++ b/bot/seasons/easter/april_fools_vids.py @@ -21,7 +21,7 @@ class AprilFoolVideos(commands.Cog): @staticmethod def load_json(): """A function to load json data.""" - p = Path('bot/resources/april_fools_vids.json') + p = Path('bot', 'resources', 'easter', 'april_fools_vids.json') with p.open() as json_file: all_vids = load(json_file) return all_vids @@ -37,8 +37,7 @@ class AprilFoolVideos(commands.Cog): embed.colour = Colours.yellow embed.description = f'Checkout this april fools video by {random_youtuber}' embed.url = random_vid['link'] - await ctx.send(embed=embed) - await ctx.send(random_vid["link"]) + await ctx.send(f"Check out this April Fools' video by {random_youtuber}.\n\n{random_vid['link']}") def setup(bot): -- cgit v1.2.3 From 505243031c87108fb5fc70528d54b5dad3207458 Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Tue, 2 Apr 2019 19:13:04 +0100 Subject: Created traditions.py and completed the easter_tradition command --- bot/seasons/easter/traditions.py | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 bot/seasons/easter/traditions.py (limited to 'bot') diff --git a/bot/seasons/easter/traditions.py b/bot/seasons/easter/traditions.py new file mode 100644 index 00000000..b4aa9f89 --- /dev/null +++ b/bot/seasons/easter/traditions.py @@ -0,0 +1,41 @@ +import logging +from discord.ext import commands +import random + +log = logging.getLogger(__name__) + + +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): + self.bot = bot + + @commands.command(name="easter_tradition") + async def easter_tradition(self, ctx): + """Responds with a random tradition or custom""" + + traditions = {'England': 'Easter in England is celebrated through the exchange of Easter Eggs and other gifts like clothes, chocolates or holidays packages. Easter bonnets or baskets are also made that have fillings like daffodils in them.', + 'Haiti': 'In Haiti, kids have the freedom to spend Good Friday playing outdoors. On this day colourful kites fill the sky and children run long distances, often barefoot, trying to get their kite higher than their friends.', + 'Indonesia': 'Slightly unconventional, but kids in Indonesia celebrate Easter with a tooth brushing competition!', + 'Ethipoia': 'In Ethiopia, Easter is called Fasika and marks the end of a 55-day fast during which Christians have only eaten one vegetarian meal a day. Ethiopians will often break their fast after church by eating injera (a type of bread) or teff pancakes, made from grass flour.', + 'El Salvador': 'On Good Friday communities make rug-like paintings on the streets with sand and sawdust. These later become the path for processions and main avenues and streets are closed', + 'Ghana': 'Ghanaians dress in certain colours to mark the different days of Easter. On Good Friday, depending on the church denomination, men and women will either dress in dark mourning clothes or bright colours. On Easter Sunday everyone wears white.', + 'Kenya': 'On Easter Sunday, kids in Kenya look forward to a sumptuous Easter meal after church (Easter services are known to last for three hours!). Children share Nyama Choma (roasted meat) and have a soft drink with their meal!', + 'Guatemala': 'In Guatemala, Easter customs include a large, colourful celebration marked by countless processions. The main roads are closed, and the sound of music rings through the streets. Special food is prepared such as curtido (a diced vegetable mix which is cooked in vinegar to achieve a sour taste), fish, eggs, chickpeas, fruit mix, pumpkin, pacaya palm and spondias fruit (a Spanish version of a plum.)', + 'Germany': 'In Germany, Easter is known by the name of Ostern. Easter holidays for children last for about three weeks. Good Friday, Easter Saturday and Easter Sunday are the days when people do not work at all.', + 'Mexico': 'Semana Santa and Pascua (two separate observances) form a part of Easter celebrations in Mexico. Semana Santa stands for the entire Holy Week, from Palm Sunday to Easter Saturday, whereas the Pascua is the observance of the period from the Resurrection Sunday to the following Saturday.', + 'Poland': 'They shape the Easter Butter Lamg (Baranek Wielkanocyny) from a chunk of butter. They attempt to make it look like a fluffy sheep!', + 'Greece': 'They burn an effigy of Judas Iscariot, they betrayer of Jesus, sometimes is done as part of a Passion Play! It is hung by the neck and then burnt.', + 'Philippines': 'Some Christians put themselves through the same pain that Christ endured, they have someone naile them to a cross and put a crown of thornes on their head.'} + + random_country = random.choice(list(traditions)) + + await ctx.send(f"{random_country}:\n{traditions[random_country]}") + +def setup(bot): + """Traditions Cog load.""" + + bot.add_cog(Traditions(bot)) + log.info("Traditions cog loaded") -- cgit v1.2.3 From 00ad8a81ff15fa890d9db5f9d62a9173b36c9b82 Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Tue, 2 Apr 2019 19:33:02 +0100 Subject: Fixed PEP8 --- bot/seasons/easter/traditions.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/traditions.py b/bot/seasons/easter/traditions.py index b4aa9f89..678a966c 100644 --- a/bot/seasons/easter/traditions.py +++ b/bot/seasons/easter/traditions.py @@ -1,13 +1,14 @@ import logging -from discord.ext import commands import random +from discord.ext import commands + log = logging.getLogger(__name__) class Traditions(commands.Cog): """A cog which allows users to get a random easter tradition or - custom from a random country.""" + custom from a random country.""" def __init__(self, bot): self.bot = bot @@ -16,24 +17,27 @@ class Traditions(commands.Cog): async def easter_tradition(self, ctx): """Responds with a random tradition or custom""" - traditions = {'England': 'Easter in England is celebrated through the exchange of Easter Eggs and other gifts like clothes, chocolates or holidays packages. Easter bonnets or baskets are also made that have fillings like daffodils in them.', - 'Haiti': 'In Haiti, kids have the freedom to spend Good Friday playing outdoors. On this day colourful kites fill the sky and children run long distances, often barefoot, trying to get their kite higher than their friends.', - 'Indonesia': 'Slightly unconventional, but kids in Indonesia celebrate Easter with a tooth brushing competition!', - 'Ethipoia': 'In Ethiopia, Easter is called Fasika and marks the end of a 55-day fast during which Christians have only eaten one vegetarian meal a day. Ethiopians will often break their fast after church by eating injera (a type of bread) or teff pancakes, made from grass flour.', - 'El Salvador': 'On Good Friday communities make rug-like paintings on the streets with sand and sawdust. These later become the path for processions and main avenues and streets are closed', - 'Ghana': 'Ghanaians dress in certain colours to mark the different days of Easter. On Good Friday, depending on the church denomination, men and women will either dress in dark mourning clothes or bright colours. On Easter Sunday everyone wears white.', - 'Kenya': 'On Easter Sunday, kids in Kenya look forward to a sumptuous Easter meal after church (Easter services are known to last for three hours!). Children share Nyama Choma (roasted meat) and have a soft drink with their meal!', - 'Guatemala': 'In Guatemala, Easter customs include a large, colourful celebration marked by countless processions. The main roads are closed, and the sound of music rings through the streets. Special food is prepared such as curtido (a diced vegetable mix which is cooked in vinegar to achieve a sour taste), fish, eggs, chickpeas, fruit mix, pumpkin, pacaya palm and spondias fruit (a Spanish version of a plum.)', - 'Germany': 'In Germany, Easter is known by the name of Ostern. Easter holidays for children last for about three weeks. Good Friday, Easter Saturday and Easter Sunday are the days when people do not work at all.', - 'Mexico': 'Semana Santa and Pascua (two separate observances) form a part of Easter celebrations in Mexico. Semana Santa stands for the entire Holy Week, from Palm Sunday to Easter Saturday, whereas the Pascua is the observance of the period from the Resurrection Sunday to the following Saturday.', - 'Poland': 'They shape the Easter Butter Lamg (Baranek Wielkanocyny) from a chunk of butter. They attempt to make it look like a fluffy sheep!', - 'Greece': 'They burn an effigy of Judas Iscariot, they betrayer of Jesus, sometimes is done as part of a Passion Play! It is hung by the neck and then burnt.', - 'Philippines': 'Some Christians put themselves through the same pain that Christ endured, they have someone naile them to a cross and put a crown of thornes on their head.'} + traditions = { + "England": "Easter in England is celebrated through the exchange of Easter Eggs and other gifts like clothes, chocolates or holidays packages. Easter bonnets or baskets are also made that have fillings like daffodils in them.", + "Haiti": "In Haiti, kids have the freedom to spend Good Friday playing outdoors. On this day colourful kites fill the sky and children run long distances, often barefoot, trying to get their kite higher than their friends.", + "Indonesia": "Slightly unconventional, but kids in Indonesia celebrate Easter with a tooth brushing competition!", + "Ethipoia": "In Ethiopia, Easter is called Fasika and marks the end of a 55-day fast during which Christians have only eaten one vegetarian meal a day. Ethiopians will often break their fast after church by eating injera (a type of bread) or teff pancakes, made from grass flour.", + "El Salvador": "On Good Friday communities make rug-like paintings on the streets with sand and sawdust. These later become the path for processions and main avenues and streets are closed", + "Ghana": "Ghanaians dress in certain colours to mark the different days of Easter. On Good Friday, depending on the church denomination, men and women will either dress in dark mourning clothes or bright colours. On Easter Sunday everyone wears white.", + "Kenya": "On Easter Sunday, kids in Kenya look forward to a sumptuous Easter meal after church (Easter services are known to last for three hours!). Children share Nyama Choma (roasted meat) and have a soft drink with their meal!", + "Guatemala": "In Guatemala, Easter customs include a large, colourful celebration marked by countless processions. The main roads are closed, and the sound of music rings through the streets. Special food is prepared such as curtido (a diced vegetable mix which is cooked in vinegar to achieve a sour taste), fish, eggs, chickpeas, fruit mix, pumpkin, pacaya palm and spondias fruit (a Spanish version of a plum.)", + "Germany": "In Germany, Easter is known by the name of Ostern. Easter holidays for children last for about three weeks. Good Friday, Easter Saturday and Easter Sunday are the days when people do not work at all.", + "Mexico": "Semana Santa and Pascua (two separate observances) form a part of Easter celebrations in Mexico. Semana Santa stands for the entire Holy Week, from Palm Sunday to Easter Saturday, whereas the Pascua is the observance of the period from the Resurrection Sunday to the following Saturday.", + "Poland": "They shape the Easter Butter Lamg (Baranek Wielkanocyny) from a chunk of butter. They attempt to make it look like a fluffy sheep!", + "Greece": "They burn an effigy of Judas Iscariot, they betrayer of Jesus, sometimes is done as part of a Passion Play! It is hung by the neck and then burnt.", + "Philippines": "Some Christians put themselves through the same pain that Christ endured, they have someone naile them to a cross and put a crown of thornes on their head.", + } random_country = random.choice(list(traditions)) await ctx.send(f"{random_country}:\n{traditions[random_country]}") + def setup(bot): """Traditions Cog load.""" -- cgit v1.2.3 From 8e3736b1b56afa7dbae994cda75e0b91d16984b8 Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Tue, 2 Apr 2019 20:05:06 +0100 Subject: E501 fix --- bot/seasons/easter/traditions.py | 70 ++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 14 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/traditions.py b/bot/seasons/easter/traditions.py index 678a966c..b0bd169c 100644 --- a/bot/seasons/easter/traditions.py +++ b/bot/seasons/easter/traditions.py @@ -7,8 +7,7 @@ log = logging.getLogger(__name__) class Traditions(commands.Cog): - """A cog which allows users to get a random easter tradition or - custom from a random country.""" + """A cog which allows users to get a random easter tradition or custom from a random country.""" def __init__(self, bot): self.bot = bot @@ -18,19 +17,62 @@ class Traditions(commands.Cog): """Responds with a random tradition or custom""" traditions = { - "England": "Easter in England is celebrated through the exchange of Easter Eggs and other gifts like clothes, chocolates or holidays packages. Easter bonnets or baskets are also made that have fillings like daffodils in them.", - "Haiti": "In Haiti, kids have the freedom to spend Good Friday playing outdoors. On this day colourful kites fill the sky and children run long distances, often barefoot, trying to get their kite higher than their friends.", + "England": ( + "Easter in England is celebrated through the exchange of Easter Eggs and other gifts like clothes, " + "chocolates or holidays packages. Easter bonnets or baskets are also made that have fillings like daffodils in them." + ), + "Haiti": ( + "In Haiti, kids have the freedom to spend Good Friday playing outdoors. " + "On this day colourful kites fill the sky and children run long distances, often barefoot, " + "trying to get their kite higher than their friends." + ), "Indonesia": "Slightly unconventional, but kids in Indonesia celebrate Easter with a tooth brushing competition!", - "Ethipoia": "In Ethiopia, Easter is called Fasika and marks the end of a 55-day fast during which Christians have only eaten one vegetarian meal a day. Ethiopians will often break their fast after church by eating injera (a type of bread) or teff pancakes, made from grass flour.", - "El Salvador": "On Good Friday communities make rug-like paintings on the streets with sand and sawdust. These later become the path for processions and main avenues and streets are closed", - "Ghana": "Ghanaians dress in certain colours to mark the different days of Easter. On Good Friday, depending on the church denomination, men and women will either dress in dark mourning clothes or bright colours. On Easter Sunday everyone wears white.", - "Kenya": "On Easter Sunday, kids in Kenya look forward to a sumptuous Easter meal after church (Easter services are known to last for three hours!). Children share Nyama Choma (roasted meat) and have a soft drink with their meal!", - "Guatemala": "In Guatemala, Easter customs include a large, colourful celebration marked by countless processions. The main roads are closed, and the sound of music rings through the streets. Special food is prepared such as curtido (a diced vegetable mix which is cooked in vinegar to achieve a sour taste), fish, eggs, chickpeas, fruit mix, pumpkin, pacaya palm and spondias fruit (a Spanish version of a plum.)", - "Germany": "In Germany, Easter is known by the name of Ostern. Easter holidays for children last for about three weeks. Good Friday, Easter Saturday and Easter Sunday are the days when people do not work at all.", - "Mexico": "Semana Santa and Pascua (two separate observances) form a part of Easter celebrations in Mexico. Semana Santa stands for the entire Holy Week, from Palm Sunday to Easter Saturday, whereas the Pascua is the observance of the period from the Resurrection Sunday to the following Saturday.", - "Poland": "They shape the Easter Butter Lamg (Baranek Wielkanocyny) from a chunk of butter. They attempt to make it look like a fluffy sheep!", - "Greece": "They burn an effigy of Judas Iscariot, they betrayer of Jesus, sometimes is done as part of a Passion Play! It is hung by the neck and then burnt.", - "Philippines": "Some Christians put themselves through the same pain that Christ endured, they have someone naile them to a cross and put a crown of thornes on their head.", + "Ethipoia": ( + "In Ethiopia, Easter is called Fasika and marks the end of a 55-day fast during which Christians " + "have only eaten one vegetarian meal a day. Ethiopians will often break their fast after church by eating injera " + "(a type of bread) or teff pancakes, made from grass flour." + ), + "El Salvador": ( + "On Good Friday communities make rug-like paintings on the streets with sand and sawdust. " + "These later become the path for processions and main avenues and streets are closed" + ), + "Ghana": ( + "Ghanaians dress in certain colours to mark the different days of Easter. " + "On Good Friday, depending on the church denomination, men and women will either dress in dark mourning " + "clothes or bright colours. On Easter Sunday everyone wears white." + ), + "Kenya": ( + "On Easter Sunday, kids in Kenya look forward to a sumptuous Easter meal after church " + "(Easter services are known to last for three hours!). " + "Children share Nyama Choma (roasted meat) and have a soft drink with their meal!" + ), + "Guatemala": ( + "In Guatemala, Easter customs include a large, colourful celebration marked by countless processions. " + "The main roads are closed, and the sound of music rings through the streets. " + "Special food is prepared such as curtido (a diced vegetable mix which is cooked in vinegar to achieve a sour taste), " + "fish, eggs, chickpeas, fruit mix, pumpkin, pacaya palm and spondias fruit (a Spanish version of a plum.)" + ), + "Germany": ( + "In Germany, Easter is known by the name of Ostern. " + "Easter holidays for children last for about three weeks. " + "Good Friday, Easter Saturday and Easter Sunday are the days when people do not work at all." + ), + "Mexico": ( + "Semana Santa and Pascua (two separate observances) form a part of Easter celebrations in Mexico. " + "Semana Santa stands for the entire Holy Week, from Palm Sunday to Easter Saturday, " + "whereas the Pascua is the observance of the period from the Resurrection Sunday to the following Saturday." + ), + "Poland": ( + "They shape the Easter Butter Lamg (Baranek Wielkanocyny) from a chunk of butter. " + "They attempt to make it look like a fluffy sheep!"), + "Greece": ( + "They burn an effigy of Judas Iscariot, they betrayer of Jesus, " + "sometimes is done as part of a Passion Play! It is hung by the neck and then burnt." + ), + "Philippines": ( + "Some Christians put themselves through the same pain that Christ endured, " + "they have someone naile them to a cross and put a crown of thornes on their head." + ), } random_country = random.choice(list(traditions)) -- cgit v1.2.3 From d7765d1c8ee7e4f700585992ff1597fd5ffb8a76 Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Tue, 2 Apr 2019 20:20:42 +0100 Subject: Changed traditions to be stored in a JSON file. --- bot/seasons/easter/traditions.py | 62 +++------------------------------------- traditions.json | 15 ++++++++++ 2 files changed, 19 insertions(+), 58 deletions(-) create mode 100644 traditions.json (limited to 'bot') diff --git a/bot/seasons/easter/traditions.py b/bot/seasons/easter/traditions.py index b0bd169c..ac4d750e 100644 --- a/bot/seasons/easter/traditions.py +++ b/bot/seasons/easter/traditions.py @@ -1,5 +1,6 @@ import logging import random +import json from discord.ext import commands @@ -16,64 +17,9 @@ class Traditions(commands.Cog): async def easter_tradition(self, ctx): """Responds with a random tradition or custom""" - traditions = { - "England": ( - "Easter in England is celebrated through the exchange of Easter Eggs and other gifts like clothes, " - "chocolates or holidays packages. Easter bonnets or baskets are also made that have fillings like daffodils in them." - ), - "Haiti": ( - "In Haiti, kids have the freedom to spend Good Friday playing outdoors. " - "On this day colourful kites fill the sky and children run long distances, often barefoot, " - "trying to get their kite higher than their friends." - ), - "Indonesia": "Slightly unconventional, but kids in Indonesia celebrate Easter with a tooth brushing competition!", - "Ethipoia": ( - "In Ethiopia, Easter is called Fasika and marks the end of a 55-day fast during which Christians " - "have only eaten one vegetarian meal a day. Ethiopians will often break their fast after church by eating injera " - "(a type of bread) or teff pancakes, made from grass flour." - ), - "El Salvador": ( - "On Good Friday communities make rug-like paintings on the streets with sand and sawdust. " - "These later become the path for processions and main avenues and streets are closed" - ), - "Ghana": ( - "Ghanaians dress in certain colours to mark the different days of Easter. " - "On Good Friday, depending on the church denomination, men and women will either dress in dark mourning " - "clothes or bright colours. On Easter Sunday everyone wears white." - ), - "Kenya": ( - "On Easter Sunday, kids in Kenya look forward to a sumptuous Easter meal after church " - "(Easter services are known to last for three hours!). " - "Children share Nyama Choma (roasted meat) and have a soft drink with their meal!" - ), - "Guatemala": ( - "In Guatemala, Easter customs include a large, colourful celebration marked by countless processions. " - "The main roads are closed, and the sound of music rings through the streets. " - "Special food is prepared such as curtido (a diced vegetable mix which is cooked in vinegar to achieve a sour taste), " - "fish, eggs, chickpeas, fruit mix, pumpkin, pacaya palm and spondias fruit (a Spanish version of a plum.)" - ), - "Germany": ( - "In Germany, Easter is known by the name of Ostern. " - "Easter holidays for children last for about three weeks. " - "Good Friday, Easter Saturday and Easter Sunday are the days when people do not work at all." - ), - "Mexico": ( - "Semana Santa and Pascua (two separate observances) form a part of Easter celebrations in Mexico. " - "Semana Santa stands for the entire Holy Week, from Palm Sunday to Easter Saturday, " - "whereas the Pascua is the observance of the period from the Resurrection Sunday to the following Saturday." - ), - "Poland": ( - "They shape the Easter Butter Lamg (Baranek Wielkanocyny) from a chunk of butter. " - "They attempt to make it look like a fluffy sheep!"), - "Greece": ( - "They burn an effigy of Judas Iscariot, they betrayer of Jesus, " - "sometimes is done as part of a Passion Play! It is hung by the neck and then burnt." - ), - "Philippines": ( - "Some Christians put themselves through the same pain that Christ endured, " - "they have someone naile them to a cross and put a crown of thornes on their head." - ), - } + traditions = {} + with open('userbalance.json', 'r') as f: + traditions = json.load(f) random_country = random.choice(list(traditions)) diff --git a/traditions.json b/traditions.json new file mode 100644 index 00000000..c0e2c30e --- /dev/null +++ b/traditions.json @@ -0,0 +1,15 @@ +traditions = { + "England": "Easter in England is celebrated through the exchange of Easter Eggs and other gifts like clothes, chocolates or holidays packages. Easter bonnets or baskets are also made that have fillings like daffodils in them.", + "Haiti": "In Haiti, kids have the freedom to spend Good Friday playing outdoors. On this day colourful kites fill the sky and children run long distances, often barefoot, trying to get their kite higher than their friends.", + "Indonesia": "Slightly unconventional, but kids in Indonesia celebrate Easter with a tooth brushing competition!", + "Ethipoia": "In Ethiopia, Easter is called Fasika and marks the end of a 55-day fast during which Christians have only eaten one vegetarian meal a day. Ethiopians will often break their fast after church by eating injera (a type of bread) or teff pancakes, made from grass flour.", + "El Salvador": "On Good Friday communities make rug-like paintings on the streets with sand and sawdust. These later become the path for processions and main avenues and streets are closed", + "Ghana": "Ghanaians dress in certain colours to mark the different days of Easter. On Good Friday, depending on the church denomination, men and women will either dress in dark mourning clothes or bright colours. On Easter Sunday everyone wears white.", + "Kenya": "On Easter Sunday, kids in Kenya look forward to a sumptuous Easter meal after church (Easter services are known to last for three hours!). Children share Nyama Choma (roasted meat) and have a soft drink with their meal!", + "Guatemala": "In Guatemala, Easter customs include a large, colourful celebration marked by countless processions. The main roads are closed, and the sound of music rings through the streets. Special food is prepared such as curtido (a diced vegetable mix which is cooked in vinegar to achieve a sour taste), fish, eggs, chickpeas, fruit mix, pumpkin, pacaya palm and spondias fruit (a Spanish version of a plum.)", + "Germany": "In Germany, Easter is known by the name of Ostern. Easter holidays for children last for about three weeks. Good Friday, Easter Saturday and Easter Sunday are the days when people do not work at all.", + "Mexico": "Semana Santa and Pascua (two separate observances) form a part of Easter celebrations in Mexico. Semana Santa stands for the entire Holy Week, from Palm Sunday to Easter Saturday, whereas the Pascua is the observance of the period from the Resurrection Sunday to the following Saturday.", + "Poland": "They shape the Easter Butter Lamg (Baranek Wielkanocyny) from a chunk of butter. They attempt to make it look like a fluffy sheep!", + "Greece": "They burn an effigy of Judas Iscariot, they betrayer of Jesus, sometimes is done as part of a Passion Play! It is hung by the neck and then burnt.", + "Philippines": "Some Christians put themselves through the same pain that Christ endured, they have someone naile them to a cross and put a crown of thornes on their head.", +} -- cgit v1.2.3 From f877de741221faedabaad8aa272bfd3b06a342f3 Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Tue, 2 Apr 2019 20:24:25 +0100 Subject: Switched importing od JSON and random --- bot/seasons/easter/traditions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/easter/traditions.py b/bot/seasons/easter/traditions.py index ac4d750e..b9b998e3 100644 --- a/bot/seasons/easter/traditions.py +++ b/bot/seasons/easter/traditions.py @@ -1,6 +1,6 @@ import logging -import random import json +import random from discord.ext import commands -- cgit v1.2.3 From 05c692ab620194f160f01840f4b3134f3ae118f2 Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Tue, 2 Apr 2019 20:27:21 +0100 Subject: I HATE flake8 --- bot/seasons/easter/traditions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/easter/traditions.py b/bot/seasons/easter/traditions.py index b9b998e3..d9ef74e7 100644 --- a/bot/seasons/easter/traditions.py +++ b/bot/seasons/easter/traditions.py @@ -1,5 +1,5 @@ -import logging import json +import logging import random from discord.ext import commands -- cgit v1.2.3 From eda111127e4415b886af3f59696be581fb19658c Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Tue, 2 Apr 2019 22:17:32 +0100 Subject: Moved traditions.json location --- bot/resources/easter/traditions.json | 13 +++++++++++++ traditions.json | 15 --------------- 2 files changed, 13 insertions(+), 15 deletions(-) create mode 100644 bot/resources/easter/traditions.json delete mode 100644 traditions.json (limited to 'bot') diff --git a/bot/resources/easter/traditions.json b/bot/resources/easter/traditions.json new file mode 100644 index 00000000..f9dd6d81 --- /dev/null +++ b/bot/resources/easter/traditions.json @@ -0,0 +1,13 @@ +{"England": "Easter in England is celebrated through the exchange of Easter Eggs and other gifts like clothes, chocolates or holidays packages. Easter bonnets or baskets are also made that have fillings like daffodils in them.", +"Haiti": "In Haiti, kids have the freedom to spend Good Friday playing outdoors. On this day colourful kites fill the sky and children run long distances, often barefoot, trying to get their kite higher than their friends.", +"Indonesia": "Slightly unconventional, but kids in Indonesia celebrate Easter with a tooth brushing competition!", +"Ethipoia": "In Ethiopia, Easter is called Fasika and marks the end of a 55-day fast during which Christians have only eaten one vegetarian meal a day. Ethiopians will often break their fast after church by eating injera (a type of bread) or teff pancakes, made from grass flour.", +"El Salvador": "On Good Friday communities make rug-like paintings on the streets with sand and sawdust. These later become the path for processions and main avenues and streets are closed", +"Ghana": "Ghanaians dress in certain colours to mark the different days of Easter. On Good Friday, depending on the church denomination, men and women will either dress in dark mourning clothes or bright colours. On Easter Sunday everyone wears white.", +"Kenya": "On Easter Sunday, kids in Kenya look forward to a sumptuous Easter meal after church (Easter services are known to last for three hours!). Children share Nyama Choma (roasted meat) and have a soft drink with their meal!", +"Guatemala": "In Guatemala, Easter customs include a large, colourful celebration marked by countless processions. The main roads are closed, and the sound of music rings through the streets. Special food is prepared such as curtido (a diced vegetable mix which is cooked in vinegar to achieve a sour taste), fish, eggs, chickpeas, fruit mix, pumpkin, pacaya palm and spondias fruit (a Spanish version of a plum.)", +"Germany": "In Germany, Easter is known by the name of Ostern. Easter holidays for children last for about three weeks. Good Friday, Easter Saturday and Easter Sunday are the days when people do not work at all.", +"Mexico": "Semana Santa and Pascua (two separate observances) form a part of Easter celebrations in Mexico. Semana Santa stands for the entire Holy Week, from Palm Sunday to Easter Saturday, whereas the Pascua is the observance of the period from the Resurrection Sunday to the following Saturday.", +"Poland": "They shape the Easter Butter Lamb (Baranek Wielkanocyny) from a chunk of butter. They attempt to make it look like a fluffy lamb!", +"Greece": "They burn an effigy of Judas Iscariot, the betrayer of Jesus, sometimes is done as part of a Passion Play! It is hung by the neck and then burnt.", +"Philippines": "Some Christians put themselves through the same pain that Christ endured, they have someone naile them to a cross and put a crown of thornes on their head."} diff --git a/traditions.json b/traditions.json deleted file mode 100644 index 04a9b20c..00000000 --- a/traditions.json +++ /dev/null @@ -1,15 +0,0 @@ -traditions = { - "England": "Easter in England is celebrated through the exchange of Easter Eggs and other gifts like clothes, chocolates or holidays packages. Easter bonnets or baskets are also made that have fillings like daffodils in them.", - "Haiti": "In Haiti, kids have the freedom to spend Good Friday playing outdoors. On this day colourful kites fill the sky and children run long distances, often barefoot, trying to get their kite higher than their friends.", - "Indonesia": "Slightly unconventional, but kids in Indonesia celebrate Easter with a tooth brushing competition!", - "Ethipoia": "In Ethiopia, Easter is called Fasika and marks the end of a 55-day fast during which Christians have only eaten one vegetarian meal a day. Ethiopians will often break their fast after church by eating injera (a type of bread) or teff pancakes, made from grass flour.", - "El Salvador": "On Good Friday communities make rug-like paintings on the streets with sand and sawdust. These later become the path for processions and main avenues and streets are closed", - "Ghana": "Ghanaians dress in certain colours to mark the different days of Easter. On Good Friday, depending on the church denomination, men and women will either dress in dark mourning clothes or bright colours. On Easter Sunday everyone wears white.", - "Kenya": "On Easter Sunday, kids in Kenya look forward to a sumptuous Easter meal after church (Easter services are known to last for three hours!). Children share Nyama Choma (roasted meat) and have a soft drink with their meal!", - "Guatemala": "In Guatemala, Easter customs include a large, colourful celebration marked by countless processions. The main roads are closed, and the sound of music rings through the streets. Special food is prepared such as curtido (a diced vegetable mix which is cooked in vinegar to achieve a sour taste), fish, eggs, chickpeas, fruit mix, pumpkin, pacaya palm and spondias fruit (a Spanish version of a plum.)", - "Germany": "In Germany, Easter is known by the name of Ostern. Easter holidays for children last for about three weeks. Good Friday, Easter Saturday and Easter Sunday are the days when people do not work at all.", - "Mexico": "Semana Santa and Pascua (two separate observances) form a part of Easter celebrations in Mexico. Semana Santa stands for the entire Holy Week, from Palm Sunday to Easter Saturday, whereas the Pascua is the observance of the period from the Resurrection Sunday to the following Saturday.", - "Poland": "They shape the Easter Butter Lamb (Baranek Wielkanocyny) from a chunk of butter. They attempt to make it look like a fluffy lamb!", - "Greece": "They burn an effigy of Judas Iscariot, they betrayer of Jesus, sometimes is done as part of a Passion Play! It is hung by the neck and then burnt.", - "Philippines": "Some Christians put themselves through the same pain that Christ endured, they have someone naile them to a cross and put a crown of thornes on their head.", -} -- cgit v1.2.3 From dd7dc4536373041d2995c869079fa2cd187395d7 Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Tue, 2 Apr 2019 22:17:51 +0100 Subject: Fixed importing of JSON file in traditions.py --- bot/seasons/easter/traditions.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/traditions.py b/bot/seasons/easter/traditions.py index d9ef74e7..e5656be3 100644 --- a/bot/seasons/easter/traditions.py +++ b/bot/seasons/easter/traditions.py @@ -1,33 +1,33 @@ import json import logging import random +from pathlib import Path from discord.ext import commands log = logging.getLogger(__name__) +with open(Path('bot', 'resources', , 'easter', 'traditions.json', 'r', encoding="utf8") as f: + traditions = json.load(f) -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): - self.bot = bot +class Traditions(commands.Cog): + """A cog which allows users to get a random easter tradition or custom from a random country.""" - @commands.command(name="easter_tradition") - async def easter_tradition(self, ctx): - """Responds with a random tradition or custom""" + def __init__(self, bot): + self.bot = bot - traditions = {} - with open('userbalance.json', 'r') as f: - traditions = json.load(f) + @commands.command(alias='eastercustoms') + async def easter_tradition(self, ctx): + """Responds with a random tradition or custom""" - random_country = random.choice(list(traditions)) + random_country = random.choice(list(traditions)) - await ctx.send(f"{random_country}:\n{traditions[random_country]}") + await ctx.send(f"{random_country}:\n{traditions[random_country]}") def setup(bot): - """Traditions Cog load.""" + """Traditions Cog load.""" - bot.add_cog(Traditions(bot)) - log.info("Traditions cog loaded") + bot.add_cog(Traditions(bot)) + log.info("Traditions cog loaded") -- cgit v1.2.3 From 5d86c2a3c937f1d3c503a5a95d50b1ca14dd0fbe Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Tue, 2 Apr 2019 22:19:51 +0100 Subject: Identified syntax error and fixed. --- bot/seasons/easter/traditions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/easter/traditions.py b/bot/seasons/easter/traditions.py index e5656be3..163a1714 100644 --- a/bot/seasons/easter/traditions.py +++ b/bot/seasons/easter/traditions.py @@ -7,7 +7,7 @@ from discord.ext import commands log = logging.getLogger(__name__) -with open(Path('bot', 'resources', , 'easter', 'traditions.json', 'r', encoding="utf8") as f: +with open(Path('bot', 'resources', 'easter', 'traditions.json', 'r', encoding="utf8") as f: traditions = json.load(f) -- cgit v1.2.3 From a8935821e96f61b4fe455acfbc5c5052449ec365 Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Tue, 2 Apr 2019 22:36:46 +0100 Subject: Fixed PATH call --- bot/seasons/easter/traditions.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/traditions.py b/bot/seasons/easter/traditions.py index 163a1714..d15de3a6 100644 --- a/bot/seasons/easter/traditions.py +++ b/bot/seasons/easter/traditions.py @@ -7,27 +7,27 @@ from discord.ext import commands log = logging.getLogger(__name__) -with open(Path('bot', 'resources', 'easter', 'traditions.json', 'r', encoding="utf8") as f: - traditions = json.load(f) +with open(Path('bot', 'resources', 'easter', 'traditions.json'), 'r', encoding="utf8") as f: + traditions = json.load(f) class Traditions(commands.Cog): - """A cog which allows users to get a random easter tradition or custom from a random country.""" + """A cog which allows users to get a random easter tradition or custom from a random country.""" - def __init__(self, bot): - self.bot = bot + def __init__(self, bot): + self.bot = bot - @commands.command(alias='eastercustoms') - async def easter_tradition(self, ctx): - """Responds with a random tradition or custom""" + @commands.command(alias='eastercustoms') + async def easter_tradition(self, ctx): + """Responds with a random tradition or custom""" - random_country = random.choice(list(traditions)) + random_country = random.choice(list(traditions)) - await ctx.send(f"{random_country}:\n{traditions[random_country]}") + await ctx.send(f"{random_country}:\n{traditions[random_country]}") def setup(bot): - """Traditions Cog load.""" + """Traditions Cog load.""" - bot.add_cog(Traditions(bot)) - log.info("Traditions cog loaded") + bot.add_cog(Traditions(bot)) + log.info("Traditions cog loaded") -- cgit v1.2.3 From 9c28442039a5828569d2c77a7bdba44b0803491b Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Tue, 2 Apr 2019 22:39:45 +0100 Subject: Fixed indentation of doc string --- bot/seasons/easter/traditions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/easter/traditions.py b/bot/seasons/easter/traditions.py index d15de3a6..69197031 100644 --- a/bot/seasons/easter/traditions.py +++ b/bot/seasons/easter/traditions.py @@ -19,7 +19,7 @@ class Traditions(commands.Cog): @commands.command(alias='eastercustoms') async def easter_tradition(self, ctx): - """Responds with a random tradition or custom""" + """Responds with a random tradition or custom""" random_country = random.choice(list(traditions)) -- cgit v1.2.3 From 724df41604d4e8483ac83a474e2b0305be6b4cf2 Mon Sep 17 00:00:00 2001 From: "S. Co1" Date: Tue, 2 Apr 2019 22:58:48 +0100 Subject: Update bot/seasons/easter/traditions.py Co-Authored-By: RohanRadia --- bot/seasons/easter/traditions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/easter/traditions.py b/bot/seasons/easter/traditions.py index 69197031..05cd79f3 100644 --- a/bot/seasons/easter/traditions.py +++ b/bot/seasons/easter/traditions.py @@ -17,7 +17,7 @@ class Traditions(commands.Cog): def __init__(self, bot): self.bot = bot - @commands.command(alias='eastercustoms') + @commands.command(aliases=('eastercustoms',)) async def easter_tradition(self, ctx): """Responds with a random tradition or custom""" -- cgit v1.2.3 From 5b5f67b02c2a6e59a30eba7e65dffde4119abd09 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Wed, 3 Apr 2019 13:44:01 +1000 Subject: Add xkcd and html colour name data. --- bot/resources/evergreen/html_colours.json | 150 +++++ bot/resources/evergreen/xkcd_colours.json | 951 ++++++++++++++++++++++++++++++ 2 files changed, 1101 insertions(+) create mode 100644 bot/resources/evergreen/html_colours.json create mode 100644 bot/resources/evergreen/xkcd_colours.json (limited to 'bot') diff --git a/bot/resources/evergreen/html_colours.json b/bot/resources/evergreen/html_colours.json new file mode 100644 index 00000000..086083d6 --- /dev/null +++ b/bot/resources/evergreen/html_colours.json @@ -0,0 +1,150 @@ +{ + "aliceblue": "0xf0f8ff", + "antiquewhite": "0xfaebd7", + "aqua": "0x00ffff", + "aquamarine": "0x7fffd4", + "azure": "0xf0ffff", + "beige": "0xf5f5dc", + "bisque": "0xffe4c4", + "black": "0x000000", + "blanchedalmond": "0xffebcd", + "blue": "0x0000ff", + "blueviolet": "0x8a2be2", + "brown": "0xa52a2a", + "burlywood": "0xdeb887", + "cadetblue": "0x5f9ea0", + "chartreuse": "0x7fff00", + "chocolate": "0xd2691e", + "coral": "0xff7f50", + "cornflowerblue": "0x6495ed", + "cornsilk": "0xfff8dc", + "crimson": "0xdc143c", + "cyan": "0x00ffff", + "darkblue": "0x00008b", + "darkcyan": "0x008b8b", + "darkgoldenrod": "0xb8860b", + "darkgray": "0xa9a9a9", + "darkgreen": "0x006400", + "darkgrey": "0xa9a9a9", + "darkkhaki": "0xbdb76b", + "darkmagenta": "0x8b008b", + "darkolivegreen": "0x556b2f", + "darkorange": "0xff8c00", + "darkorchid": "0x9932cc", + "darkred": "0x8b0000", + "darksalmon": "0xe9967a", + "darkseagreen": "0x8fbc8f", + "darkslateblue": "0x483d8b", + "darkslategray": "0x2f4f4f", + "darkslategrey": "0x2f4f4f", + "darkturquoise": "0x00ced1", + "darkviolet": "0x9400d3", + "deeppink": "0xff1493", + "deepskyblue": "0x00bfff", + "dimgray": "0x696969", + "dimgrey": "0x696969", + "dodgerblue": "0x1e90ff", + "firebrick": "0xb22222", + "floralwhite": "0xfffaf0", + "forestgreen": "0x228b22", + "fuchsia": "0xff00ff", + "gainsboro": "0xdcdcdc", + "ghostwhite": "0xf8f8ff", + "goldenrod": "0xdaa520", + "gold": "0xffd700", + "gray": "0x808080", + "green": "0x008000", + "greenyellow": "0xadff2f", + "grey": "0x808080", + "honeydew": "0xf0fff0", + "hotpink": "0xff69b4", + "indianred": "0xcd5c5c", + "indigo": "0x4b0082", + "ivory": "0xfffff0", + "khaki": "0xf0e68c", + "lavenderblush": "0xfff0f5", + "lavender": "0xe6e6fa", + "lawngreen": "0x7cfc00", + "lemonchiffon": "0xfffacd", + "lightblue": "0xadd8e6", + "lightcoral": "0xf08080", + "lightcyan": "0xe0ffff", + "lightgoldenrodyellow": "0xfafad2", + "lightgray": "0xd3d3d3", + "lightgreen": "0x90ee90", + "lightgrey": "0xd3d3d3", + "lightpink": "0xffb6c1", + "lightsalmon": "0xffa07a", + "lightseagreen": "0x20b2aa", + "lightskyblue": "0x87cefa", + "lightslategray": "0x778899", + "lightslategrey": "0x778899", + "lightsteelblue": "0xb0c4de", + "lightyellow": "0xffffe0", + "lime": "0x00ff00", + "limegreen": "0x32cd32", + "linen": "0xfaf0e6", + "magenta": "0xff00ff", + "maroon": "0x800000", + "mediumaquamarine": "0x66cdaa", + "mediumblue": "0x0000cd", + "mediumorchid": "0xba55d3", + "mediumpurple": "0x9370db", + "mediumseagreen": "0x3cb371", + "mediumslateblue": "0x7b68ee", + "mediumspringgreen": "0x00fa9a", + "mediumturquoise": "0x48d1cc", + "mediumvioletred": "0xc71585", + "midnightblue": "0x191970", + "mintcream": "0xf5fffa", + "mistyrose": "0xffe4e1", + "moccasin": "0xffe4b5", + "navajowhite": "0xffdead", + "navy": "0x000080", + "oldlace": "0xfdf5e6", + "olive": "0x808000", + "olivedrab": "0x6b8e23", + "orange": "0xffa500", + "orangered": "0xff4500", + "orchid": "0xda70d6", + "palegoldenrod": "0xeee8aa", + "palegreen": "0x98fb98", + "paleturquoise": "0xafeeee", + "palevioletred": "0xdb7093", + "papayawhip": "0xffefd5", + "peachpuff": "0xffdab9", + "peru": "0xcd853f", + "pink": "0xffc0cb", + "plum": "0xdda0dd", + "powderblue": "0xb0e0e6", + "purple": "0x800080", + "rebeccapurple": "0x663399", + "red": "0xff0000", + "rosybrown": "0xbc8f8f", + "royalblue": "0x4169e1", + "saddlebrown": "0x8b4513", + "salmon": "0xfa8072", + "sandybrown": "0xf4a460", + "seagreen": "0x2e8b57", + "seashell": "0xfff5ee", + "sienna": "0xa0522d", + "silver": "0xc0c0c0", + "skyblue": "0x87ceeb", + "slateblue": "0x6a5acd", + "slategray": "0x708090", + "slategrey": "0x708090", + "snow": "0xfffafa", + "springgreen": "0x00ff7f", + "steelblue": "0x4682b4", + "tan": "0xd2b48c", + "teal": "0x008080", + "thistle": "0xd8bfd8", + "tomato": "0xff6347", + "turquoise": "0x40e0d0", + "violet": "0xee82ee", + "wheat": "0xf5deb3", + "white": "0xffffff", + "whitesmoke": "0xf5f5f5", + "yellow": "0xffff00", + "yellowgreen": "0x9acd32" +} diff --git a/bot/resources/evergreen/xkcd_colours.json b/bot/resources/evergreen/xkcd_colours.json new file mode 100644 index 00000000..3feeb639 --- /dev/null +++ b/bot/resources/evergreen/xkcd_colours.json @@ -0,0 +1,951 @@ +{ + "cloudy blue": "0xacc2d9", + "dark pastel green": "0x56ae57", + "dust": "0xb2996e", + "electric lime": "0xa8ff04", + "fresh green": "0x69d84f", + "light eggplant": "0x894585", + "nasty green": "0x70b23f", + "really light blue": "0xd4ffff", + "tea": "0x65ab7c", + "warm purple": "0x952e8f", + "yellowish tan": "0xfcfc81", + "cement": "0xa5a391", + "dark grass green": "0x388004", + "dusty teal": "0x4c9085", + "grey teal": "0x5e9b8a", + "macaroni and cheese": "0xefb435", + "pinkish tan": "0xd99b82", + "spruce": "0x0a5f38", + "strong blue": "0x0c06f7", + "toxic green": "0x61de2a", + "windows blue": "0x3778bf", + "blue blue": "0x2242c7", + "blue with a hint of purple": "0x533cc6", + "booger": "0x9bb53c", + "bright sea green": "0x05ffa6", + "dark green blue": "0x1f6357", + "deep turquoise": "0x017374", + "green teal": "0x0cb577", + "strong pink": "0xff0789", + "bland": "0xafa88b", + "deep aqua": "0x08787f", + "lavender pink": "0xdd85d7", + "light moss green": "0xa6c875", + "light seafoam green": "0xa7ffb5", + "olive yellow": "0xc2b709", + "pig pink": "0xe78ea5", + "deep lilac": "0x966ebd", + "desert": "0xccad60", + "dusty lavender": "0xac86a8", + "purpley grey": "0x947e94", + "purply": "0x983fb2", + "candy pink": "0xff63e9", + "light pastel green": "0xb2fba5", + "boring green": "0x63b365", + "kiwi green": "0x8ee53f", + "light grey green": "0xb7e1a1", + "orange pink": "0xff6f52", + "tea green": "0xbdf8a3", + "very light brown": "0xd3b683", + "egg shell": "0xfffcc4", + "eggplant purple": "0x430541", + "powder pink": "0xffb2d0", + "reddish grey": "0x997570", + "baby shit brown": "0xad900d", + "liliac": "0xc48efd", + "stormy blue": "0x507b9c", + "ugly brown": "0x7d7103", + "custard": "0xfffd78", + "darkish pink": "0xda467d", + "deep brown": "0x410200", + "greenish beige": "0xc9d179", + "manilla": "0xfffa86", + "off blue": "0x5684ae", + "battleship grey": "0x6b7c85", + "browny green": "0x6f6c0a", + "bruise": "0x7e4071", + "kelley green": "0x009337", + "sickly yellow": "0xd0e429", + "sunny yellow": "0xfff917", + "azul": "0x1d5dec", + "darkgreen": "0x054907", + "green/yellow": "0xb5ce08", + "lichen": "0x8fb67b", + "light light green": "0xc8ffb0", + "pale gold": "0xfdde6c", + "sun yellow": "0xffdf22", + "tan green": "0xa9be70", + "burple": "0x6832e3", + "butterscotch": "0xfdb147", + "toupe": "0xc7ac7d", + "dark cream": "0xfff39a", + "indian red": "0x850e04", + "light lavendar": "0xefc0fe", + "poison green": "0x40fd14", + "baby puke green": "0xb6c406", + "bright yellow green": "0x9dff00", + "charcoal grey": "0x3c4142", + "squash": "0xf2ab15", + "cinnamon": "0xac4f06", + "light pea green": "0xc4fe82", + "radioactive green": "0x2cfa1f", + "raw sienna": "0x9a6200", + "baby purple": "0xca9bf7", + "cocoa": "0x875f42", + "light royal blue": "0x3a2efe", + "orangeish": "0xfd8d49", + "rust brown": "0x8b3103", + "sand brown": "0xcba560", + "swamp": "0x698339", + "tealish green": "0x0cdc73", + "burnt siena": "0xb75203", + "camo": "0x7f8f4e", + "dusk blue": "0x26538d", + "fern": "0x63a950", + "old rose": "0xc87f89", + "pale light green": "0xb1fc99", + "peachy pink": "0xff9a8a", + "rosy pink": "0xf6688e", + "light bluish green": "0x76fda8", + "light bright green": "0x53fe5c", + "light neon green": "0x4efd54", + "light seafoam": "0xa0febf", + "tiffany blue": "0x7bf2da", + "washed out green": "0xbcf5a6", + "browny orange": "0xca6b02", + "nice blue": "0x107ab0", + "sapphire": "0x2138ab", + "greyish teal": "0x719f91", + "orangey yellow": "0xfdb915", + "parchment": "0xfefcaf", + "straw": "0xfcf679", + "very dark brown": "0x1d0200", + "terracota": "0xcb6843", + "ugly blue": "0x31668a", + "clear blue": "0x247afd", + "creme": "0xffffb6", + "foam green": "0x90fda9", + "grey/green": "0x86a17d", + "light gold": "0xfddc5c", + "seafoam blue": "0x78d1b6", + "topaz": "0x13bbaf", + "violet pink": "0xfb5ffc", + "wintergreen": "0x20f986", + "yellow tan": "0xffe36e", + "dark fuchsia": "0x9d0759", + "indigo blue": "0x3a18b1", + "light yellowish green": "0xc2ff89", + "pale magenta": "0xd767ad", + "rich purple": "0x720058", + "sunflower yellow": "0xffda03", + "green/blue": "0x01c08d", + "leather": "0xac7434", + "racing green": "0x014600", + "vivid purple": "0x9900fa", + "dark royal blue": "0x02066f", + "hazel": "0x8e7618", + "muted pink": "0xd1768f", + "booger green": "0x96b403", + "canary": "0xfdff63", + "cool grey": "0x95a3a6", + "dark taupe": "0x7f684e", + "darkish purple": "0x751973", + "true green": "0x089404", + "coral pink": "0xff6163", + "dark sage": "0x598556", + "dark slate blue": "0x214761", + "flat blue": "0x3c73a8", + "mushroom": "0xba9e88", + "rich blue": "0x021bf9", + "dirty purple": "0x734a65", + "greenblue": "0x23c48b", + "icky green": "0x8fae22", + "light khaki": "0xe6f2a2", + "warm blue": "0x4b57db", + "dark hot pink": "0xd90166", + "deep sea blue": "0x015482", + "carmine": "0x9d0216", + "dark yellow green": "0x728f02", + "pale peach": "0xffe5ad", + "plum purple": "0x4e0550", + "golden rod": "0xf9bc08", + "neon red": "0xff073a", + "old pink": "0xc77986", + "very pale blue": "0xd6fffe", + "blood orange": "0xfe4b03", + "grapefruit": "0xfd5956", + "sand yellow": "0xfce166", + "clay brown": "0xb2713d", + "dark blue grey": "0x1f3b4d", + "flat green": "0x699d4c", + "light green blue": "0x56fca2", + "warm pink": "0xfb5581", + "dodger blue": "0x3e82fc", + "gross green": "0xa0bf16", + "ice": "0xd6fffa", + "metallic blue": "0x4f738e", + "pale salmon": "0xffb19a", + "sap green": "0x5c8b15", + "algae": "0x54ac68", + "bluey grey": "0x89a0b0", + "greeny grey": "0x7ea07a", + "highlighter green": "0x1bfc06", + "light light blue": "0xcafffb", + "light mint": "0xb6ffbb", + "raw umber": "0xa75e09", + "vivid blue": "0x152eff", + "deep lavender": "0x8d5eb7", + "dull teal": "0x5f9e8f", + "light greenish blue": "0x63f7b4", + "mud green": "0x606602", + "pinky": "0xfc86aa", + "red wine": "0x8c0034", + "shit green": "0x758000", + "tan brown": "0xab7e4c", + "darkblue": "0x030764", + "rosa": "0xfe86a4", + "lipstick": "0xd5174e", + "pale mauve": "0xfed0fc", + "claret": "0x680018", + "dandelion": "0xfedf08", + "orangered": "0xfe420f", + "poop green": "0x6f7c00", + "ruby": "0xca0147", + "dark": "0x1b2431", + "greenish turquoise": "0x00fbb0", + "pastel red": "0xdb5856", + "piss yellow": "0xddd618", + "bright cyan": "0x41fdfe", + "dark coral": "0xcf524e", + "algae green": "0x21c36f", + "darkish red": "0xa90308", + "reddy brown": "0x6e1005", + "blush pink": "0xfe828c", + "camouflage green": "0x4b6113", + "lawn green": "0x4da409", + "putty": "0xbeae8a", + "vibrant blue": "0x0339f8", + "dark sand": "0xa88f59", + "purple/blue": "0x5d21d0", + "saffron": "0xfeb209", + "twilight": "0x4e518b", + "warm brown": "0x964e02", + "bluegrey": "0x85a3b2", + "bubble gum pink": "0xff69af", + "duck egg blue": "0xc3fbf4", + "greenish cyan": "0x2afeb7", + "petrol": "0x005f6a", + "royal": "0x0c1793", + "butter": "0xffff81", + "dusty orange": "0xf0833a", + "off yellow": "0xf1f33f", + "pale olive green": "0xb1d27b", + "orangish": "0xfc824a", + "leaf": "0x71aa34", + "light blue grey": "0xb7c9e2", + "dried blood": "0x4b0101", + "lightish purple": "0xa552e6", + "rusty red": "0xaf2f0d", + "lavender blue": "0x8b88f8", + "light grass green": "0x9af764", + "light mint green": "0xa6fbb2", + "sunflower": "0xffc512", + "velvet": "0x750851", + "brick orange": "0xc14a09", + "lightish red": "0xfe2f4a", + "pure blue": "0x0203e2", + "twilight blue": "0x0a437a", + "violet red": "0xa50055", + "yellowy brown": "0xae8b0c", + "carnation": "0xfd798f", + "muddy yellow": "0xbfac05", + "dark seafoam green": "0x3eaf76", + "deep rose": "0xc74767", + "dusty red": "0xb9484e", + "grey/blue": "0x647d8e", + "lemon lime": "0xbffe28", + "purple/pink": "0xd725de", + "brown yellow": "0xb29705", + "purple brown": "0x673a3f", + "wisteria": "0xa87dc2", + "banana yellow": "0xfafe4b", + "lipstick red": "0xc0022f", + "water blue": "0x0e87cc", + "brown grey": "0x8d8468", + "vibrant purple": "0xad03de", + "baby green": "0x8cff9e", + "barf green": "0x94ac02", + "eggshell blue": "0xc4fff7", + "sandy yellow": "0xfdee73", + "cool green": "0x33b864", + "pale": "0xfff9d0", + "blue/grey": "0x758da3", + "hot magenta": "0xf504c9", + "greyblue": "0x77a1b5", + "purpley": "0x8756e4", + "baby shit green": "0x889717", + "brownish pink": "0xc27e79", + "dark aquamarine": "0x017371", + "diarrhea": "0x9f8303", + "light mustard": "0xf7d560", + "pale sky blue": "0xbdf6fe", + "turtle green": "0x75b84f", + "bright olive": "0x9cbb04", + "dark grey blue": "0x29465b", + "greeny brown": "0x696006", + "lemon green": "0xadf802", + "light periwinkle": "0xc1c6fc", + "seaweed green": "0x35ad6b", + "sunshine yellow": "0xfffd37", + "ugly purple": "0xa442a0", + "medium pink": "0xf36196", + "puke brown": "0x947706", + "very light pink": "0xfff4f2", + "viridian": "0x1e9167", + "bile": "0xb5c306", + "faded yellow": "0xfeff7f", + "very pale green": "0xcffdbc", + "vibrant green": "0x0add08", + "bright lime": "0x87fd05", + "spearmint": "0x1ef876", + "light aquamarine": "0x7bfdc7", + "light sage": "0xbcecac", + "yellowgreen": "0xbbf90f", + "baby poo": "0xab9004", + "dark seafoam": "0x1fb57a", + "deep teal": "0x00555a", + "heather": "0xa484ac", + "rust orange": "0xc45508", + "dirty blue": "0x3f829d", + "fern green": "0x548d44", + "bright lilac": "0xc95efb", + "weird green": "0x3ae57f", + "peacock blue": "0x016795", + "avocado green": "0x87a922", + "faded orange": "0xf0944d", + "grape purple": "0x5d1451", + "hot green": "0x25ff29", + "lime yellow": "0xd0fe1d", + "mango": "0xffa62b", + "shamrock": "0x01b44c", + "bubblegum": "0xff6cb5", + "purplish brown": "0x6b4247", + "vomit yellow": "0xc7c10c", + "pale cyan": "0xb7fffa", + "key lime": "0xaeff6e", + "tomato red": "0xec2d01", + "lightgreen": "0x76ff7b", + "merlot": "0x730039", + "night blue": "0x040348", + "purpleish pink": "0xdf4ec8", + "apple": "0x6ecb3c", + "baby poop green": "0x8f9805", + "green apple": "0x5edc1f", + "heliotrope": "0xd94ff5", + "yellow/green": "0xc8fd3d", + "almost black": "0x070d0d", + "cool blue": "0x4984b8", + "leafy green": "0x51b73b", + "mustard brown": "0xac7e04", + "dusk": "0x4e5481", + "dull brown": "0x876e4b", + "frog green": "0x58bc08", + "vivid green": "0x2fef10", + "bright light green": "0x2dfe54", + "fluro green": "0x0aff02", + "kiwi": "0x9cef43", + "seaweed": "0x18d17b", + "navy green": "0x35530a", + "ultramarine blue": "0x1805db", + "iris": "0x6258c4", + "pastel orange": "0xff964f", + "yellowish orange": "0xffab0f", + "perrywinkle": "0x8f8ce7", + "tealish": "0x24bca8", + "dark plum": "0x3f012c", + "pear": "0xcbf85f", + "pinkish orange": "0xff724c", + "midnight purple": "0x280137", + "light urple": "0xb36ff6", + "dark mint": "0x48c072", + "greenish tan": "0xbccb7a", + "light burgundy": "0xa8415b", + "turquoise blue": "0x06b1c4", + "ugly pink": "0xcd7584", + "sandy": "0xf1da7a", + "electric pink": "0xff0490", + "muted purple": "0x805b87", + "mid green": "0x50a747", + "greyish": "0xa8a495", + "neon yellow": "0xcfff04", + "banana": "0xffff7e", + "carnation pink": "0xff7fa7", + "tomato": "0xef4026", + "sea": "0x3c9992", + "muddy brown": "0x886806", + "turquoise green": "0x04f489", + "buff": "0xfef69e", + "fawn": "0xcfaf7b", + "muted blue": "0x3b719f", + "pale rose": "0xfdc1c5", + "dark mint green": "0x20c073", + "amethyst": "0x9b5fc0", + "blue/green": "0x0f9b8e", + "chestnut": "0x742802", + "sick green": "0x9db92c", + "pea": "0xa4bf20", + "rusty orange": "0xcd5909", + "stone": "0xada587", + "rose red": "0xbe013c", + "pale aqua": "0xb8ffeb", + "deep orange": "0xdc4d01", + "earth": "0xa2653e", + "mossy green": "0x638b27", + "grassy green": "0x419c03", + "pale lime green": "0xb1ff65", + "light grey blue": "0x9dbcd4", + "pale grey": "0xfdfdfe", + "asparagus": "0x77ab56", + "blueberry": "0x464196", + "purple red": "0x990147", + "pale lime": "0xbefd73", + "greenish teal": "0x32bf84", + "caramel": "0xaf6f09", + "deep magenta": "0xa0025c", + "light peach": "0xffd8b1", + "milk chocolate": "0x7f4e1e", + "ocher": "0xbf9b0c", + "off green": "0x6ba353", + "purply pink": "0xf075e6", + "lightblue": "0x7bc8f6", + "dusky blue": "0x475f94", + "golden": "0xf5bf03", + "light beige": "0xfffeb6", + "butter yellow": "0xfffd74", + "dusky purple": "0x895b7b", + "french blue": "0x436bad", + "ugly yellow": "0xd0c101", + "greeny yellow": "0xc6f808", + "orangish red": "0xf43605", + "shamrock green": "0x02c14d", + "orangish brown": "0xb25f03", + "tree green": "0x2a7e19", + "deep violet": "0x490648", + "gunmetal": "0x536267", + "blue/purple": "0x5a06ef", + "cherry": "0xcf0234", + "sandy brown": "0xc4a661", + "warm grey": "0x978a84", + "dark indigo": "0x1f0954", + "midnight": "0x03012d", + "bluey green": "0x2bb179", + "grey pink": "0xc3909b", + "soft purple": "0xa66fb5", + "blood": "0x770001", + "brown red": "0x922b05", + "medium grey": "0x7d7f7c", + "berry": "0x990f4b", + "poo": "0x8f7303", + "purpley pink": "0xc83cb9", + "light salmon": "0xfea993", + "snot": "0xacbb0d", + "easter purple": "0xc071fe", + "light yellow green": "0xccfd7f", + "dark navy blue": "0x00022e", + "drab": "0x828344", + "light rose": "0xffc5cb", + "rouge": "0xab1239", + "purplish red": "0xb0054b", + "slime green": "0x99cc04", + "baby poop": "0x937c00", + "irish green": "0x019529", + "pink/purple": "0xef1de7", + "dark navy": "0x000435", + "greeny blue": "0x42b395", + "light plum": "0x9d5783", + "pinkish grey": "0xc8aca9", + "dirty orange": "0xc87606", + "rust red": "0xaa2704", + "pale lilac": "0xe4cbff", + "orangey red": "0xfa4224", + "primary blue": "0x0804f9", + "kermit green": "0x5cb200", + "brownish purple": "0x76424e", + "murky green": "0x6c7a0e", + "wheat": "0xfbdd7e", + "very dark purple": "0x2a0134", + "bottle green": "0x044a05", + "watermelon": "0xfd4659", + "deep sky blue": "0x0d75f8", + "fire engine red": "0xfe0002", + "yellow ochre": "0xcb9d06", + "pumpkin orange": "0xfb7d07", + "pale olive": "0xb9cc81", + "light lilac": "0xedc8ff", + "lightish green": "0x61e160", + "carolina blue": "0x8ab8fe", + "mulberry": "0x920a4e", + "shocking pink": "0xfe02a2", + "auburn": "0x9a3001", + "bright lime green": "0x65fe08", + "celadon": "0xbefdb7", + "pinkish brown": "0xb17261", + "poo brown": "0x885f01", + "bright sky blue": "0x02ccfe", + "celery": "0xc1fd95", + "dirt brown": "0x836539", + "strawberry": "0xfb2943", + "dark lime": "0x84b701", + "copper": "0xb66325", + "medium brown": "0x7f5112", + "muted green": "0x5fa052", + "robin's egg": "0x6dedfd", + "bright aqua": "0x0bf9ea", + "bright lavender": "0xc760ff", + "ivory": "0xffffcb", + "very light purple": "0xf6cefc", + "light navy": "0x155084", + "pink red": "0xf5054f", + "olive brown": "0x645403", + "poop brown": "0x7a5901", + "mustard green": "0xa8b504", + "ocean green": "0x3d9973", + "very dark blue": "0x000133", + "dusty green": "0x76a973", + "light navy blue": "0x2e5a88", + "minty green": "0x0bf77d", + "adobe": "0xbd6c48", + "barney": "0xac1db8", + "jade green": "0x2baf6a", + "bright light blue": "0x26f7fd", + "light lime": "0xaefd6c", + "dark khaki": "0x9b8f55", + "orange yellow": "0xffad01", + "ocre": "0xc69c04", + "maize": "0xf4d054", + "faded pink": "0xde9dac", + "british racing green": "0x05480d", + "sandstone": "0xc9ae74", + "mud brown": "0x60460f", + "light sea green": "0x98f6b0", + "robin egg blue": "0x8af1fe", + "aqua marine": "0x2ee8bb", + "dark sea green": "0x11875d", + "soft pink": "0xfdb0c0", + "orangey brown": "0xb16002", + "cherry red": "0xf7022a", + "burnt yellow": "0xd5ab09", + "brownish grey": "0x86775f", + "camel": "0xc69f59", + "purplish grey": "0x7a687f", + "marine": "0x042e60", + "greyish pink": "0xc88d94", + "pale turquoise": "0xa5fbd5", + "pastel yellow": "0xfffe71", + "bluey purple": "0x6241c7", + "canary yellow": "0xfffe40", + "faded red": "0xd3494e", + "sepia": "0x985e2b", + "coffee": "0xa6814c", + "bright magenta": "0xff08e8", + "mocha": "0x9d7651", + "ecru": "0xfeffca", + "purpleish": "0x98568d", + "cranberry": "0x9e003a", + "darkish green": "0x287c37", + "brown orange": "0xb96902", + "dusky rose": "0xba6873", + "melon": "0xff7855", + "sickly green": "0x94b21c", + "silver": "0xc5c9c7", + "purply blue": "0x661aee", + "purpleish blue": "0x6140ef", + "hospital green": "0x9be5aa", + "shit brown": "0x7b5804", + "mid blue": "0x276ab3", + "amber": "0xfeb308", + "easter green": "0x8cfd7e", + "soft blue": "0x6488ea", + "cerulean blue": "0x056eee", + "golden brown": "0xb27a01", + "bright turquoise": "0x0ffef9", + "red pink": "0xfa2a55", + "red purple": "0x820747", + "greyish brown": "0x7a6a4f", + "vermillion": "0xf4320c", + "russet": "0xa13905", + "steel grey": "0x6f828a", + "lighter purple": "0xa55af4", + "bright violet": "0xad0afd", + "prussian blue": "0x004577", + "slate green": "0x658d6d", + "dirty pink": "0xca7b80", + "dark blue green": "0x005249", + "pine": "0x2b5d34", + "yellowy green": "0xbff128", + "dark gold": "0xb59410", + "bluish": "0x2976bb", + "darkish blue": "0x014182", + "dull red": "0xbb3f3f", + "pinky red": "0xfc2647", + "bronze": "0xa87900", + "pale teal": "0x82cbb2", + "military green": "0x667c3e", + "barbie pink": "0xfe46a5", + "bubblegum pink": "0xfe83cc", + "pea soup green": "0x94a617", + "dark mustard": "0xa88905", + "shit": "0x7f5f00", + "medium purple": "0x9e43a2", + "very dark green": "0x062e03", + "dirt": "0x8a6e45", + "dusky pink": "0xcc7a8b", + "red violet": "0x9e0168", + "lemon yellow": "0xfdff38", + "pistachio": "0xc0fa8b", + "dull yellow": "0xeedc5b", + "dark lime green": "0x7ebd01", + "denim blue": "0x3b5b92", + "teal blue": "0x01889f", + "lightish blue": "0x3d7afd", + "purpley blue": "0x5f34e7", + "light indigo": "0x6d5acf", + "swamp green": "0x748500", + "brown green": "0x706c11", + "dark maroon": "0x3c0008", + "hot purple": "0xcb00f5", + "dark forest green": "0x002d04", + "faded blue": "0x658cbb", + "drab green": "0x749551", + "light lime green": "0xb9ff66", + "snot green": "0x9dc100", + "yellowish": "0xfaee66", + "light blue green": "0x7efbb3", + "bordeaux": "0x7b002c", + "light mauve": "0xc292a1", + "ocean": "0x017b92", + "marigold": "0xfcc006", + "muddy green": "0x657432", + "dull orange": "0xd8863b", + "steel": "0x738595", + "electric purple": "0xaa23ff", + "fluorescent green": "0x08ff08", + "yellowish brown": "0x9b7a01", + "blush": "0xf29e8e", + "soft green": "0x6fc276", + "bright orange": "0xff5b00", + "lemon": "0xfdff52", + "purple grey": "0x866f85", + "acid green": "0x8ffe09", + "pale lavender": "0xeecffe", + "violet blue": "0x510ac9", + "light forest green": "0x4f9153", + "burnt red": "0x9f2305", + "khaki green": "0x728639", + "cerise": "0xde0c62", + "faded purple": "0x916e99", + "apricot": "0xffb16d", + "dark olive green": "0x3c4d03", + "grey brown": "0x7f7053", + "green grey": "0x77926f", + "true blue": "0x010fcc", + "pale violet": "0xceaefa", + "periwinkle blue": "0x8f99fb", + "light sky blue": "0xc6fcff", + "blurple": "0x5539cc", + "green brown": "0x544e03", + "bluegreen": "0x017a79", + "bright teal": "0x01f9c6", + "brownish yellow": "0xc9b003", + "pea soup": "0x929901", + "forest": "0x0b5509", + "barney purple": "0xa00498", + "ultramarine": "0x2000b1", + "purplish": "0x94568c", + "puke yellow": "0xc2be0e", + "bluish grey": "0x748b97", + "dark periwinkle": "0x665fd1", + "dark lilac": "0x9c6da5", + "reddish": "0xc44240", + "light maroon": "0xa24857", + "dusty purple": "0x825f87", + "terra cotta": "0xc9643b", + "avocado": "0x90b134", + "marine blue": "0x01386a", + "teal green": "0x25a36f", + "slate grey": "0x59656d", + "lighter green": "0x75fd63", + "electric green": "0x21fc0d", + "dusty blue": "0x5a86ad", + "golden yellow": "0xfec615", + "bright yellow": "0xfffd01", + "light lavender": "0xdfc5fe", + "umber": "0xb26400", + "poop": "0x7f5e00", + "dark peach": "0xde7e5d", + "jungle green": "0x048243", + "eggshell": "0xffffd4", + "denim": "0x3b638c", + "yellow brown": "0xb79400", + "dull purple": "0x84597e", + "chocolate brown": "0x411900", + "wine red": "0x7b0323", + "neon blue": "0x04d9ff", + "dirty green": "0x667e2c", + "light tan": "0xfbeeac", + "ice blue": "0xd7fffe", + "cadet blue": "0x4e7496", + "dark mauve": "0x874c62", + "very light blue": "0xd5ffff", + "grey purple": "0x826d8c", + "pastel pink": "0xffbacd", + "very light green": "0xd1ffbd", + "dark sky blue": "0x448ee4", + "evergreen": "0x05472a", + "dull pink": "0xd5869d", + "aubergine": "0x3d0734", + "mahogany": "0x4a0100", + "reddish orange": "0xf8481c", + "deep green": "0x02590f", + "vomit green": "0x89a203", + "purple pink": "0xe03fd8", + "dusty pink": "0xd58a94", + "faded green": "0x7bb274", + "camo green": "0x526525", + "pinky purple": "0xc94cbe", + "pink purple": "0xdb4bda", + "brownish red": "0x9e3623", + "dark rose": "0xb5485d", + "mud": "0x735c12", + "brownish": "0x9c6d57", + "emerald green": "0x028f1e", + "pale brown": "0xb1916e", + "dull blue": "0x49759c", + "burnt umber": "0xa0450e", + "medium green": "0x39ad48", + "clay": "0xb66a50", + "light aqua": "0x8cffdb", + "light olive green": "0xa4be5c", + "brownish orange": "0xcb7723", + "dark aqua": "0x05696b", + "purplish pink": "0xce5dae", + "dark salmon": "0xc85a53", + "greenish grey": "0x96ae8d", + "jade": "0x1fa774", + "ugly green": "0x7a9703", + "dark beige": "0xac9362", + "emerald": "0x01a049", + "pale red": "0xd9544d", + "light magenta": "0xfa5ff7", + "sky": "0x82cafc", + "light cyan": "0xacfffc", + "yellow orange": "0xfcb001", + "reddish purple": "0x910951", + "reddish pink": "0xfe2c54", + "orchid": "0xc875c4", + "dirty yellow": "0xcdc50a", + "orange red": "0xfd411e", + "deep red": "0x9a0200", + "orange brown": "0xbe6400", + "cobalt blue": "0x030aa7", + "neon pink": "0xfe019a", + "rose pink": "0xf7879a", + "greyish purple": "0x887191", + "raspberry": "0xb00149", + "aqua green": "0x12e193", + "salmon pink": "0xfe7b7c", + "tangerine": "0xff9408", + "brownish green": "0x6a6e09", + "red brown": "0x8b2e16", + "greenish brown": "0x696112", + "pumpkin": "0xe17701", + "pine green": "0x0a481e", + "charcoal": "0x343837", + "baby pink": "0xffb7ce", + "cornflower": "0x6a79f7", + "blue violet": "0x5d06e9", + "chocolate": "0x3d1c02", + "greyish green": "0x82a67d", + "scarlet": "0xbe0119", + "green yellow": "0xc9ff27", + "dark olive": "0x373e02", + "sienna": "0xa9561e", + "pastel purple": "0xcaa0ff", + "terracotta": "0xca6641", + "aqua blue": "0x02d8e9", + "sage green": "0x88b378", + "blood red": "0x980002", + "deep pink": "0xcb0162", + "grass": "0x5cac2d", + "moss": "0x769958", + "pastel blue": "0xa2bffe", + "bluish green": "0x10a674", + "green blue": "0x06b48b", + "dark tan": "0xaf884a", + "greenish blue": "0x0b8b87", + "pale orange": "0xffa756", + "vomit": "0xa2a415", + "forrest green": "0x154406", + "dark lavender": "0x856798", + "dark violet": "0x34013f", + "purple blue": "0x632de9", + "dark cyan": "0x0a888a", + "olive drab": "0x6f7632", + "pinkish": "0xd46a7e", + "cobalt": "0x1e488f", + "neon purple": "0xbc13fe", + "light turquoise": "0x7ef4cc", + "apple green": "0x76cd26", + "dull green": "0x74a662", + "wine": "0x80013f", + "powder blue": "0xb1d1fc", + "off white": "0xffffe4", + "electric blue": "0x0652ff", + "dark turquoise": "0x045c5a", + "blue purple": "0x5729ce", + "azure": "0x069af3", + "bright red": "0xff000d", + "pinkish red": "0xf10c45", + "cornflower blue": "0x5170d7", + "light olive": "0xacbf69", + "grape": "0x6c3461", + "greyish blue": "0x5e819d", + "purplish blue": "0x601ef9", + "yellowish green": "0xb0dd16", + "greenish yellow": "0xcdfd02", + "medium blue": "0x2c6fbb", + "dusty rose": "0xc0737a", + "light violet": "0xd6b4fc", + "midnight blue": "0x020035", + "bluish purple": "0x703be7", + "red orange": "0xfd3c06", + "dark magenta": "0x960056", + "greenish": "0x40a368", + "ocean blue": "0x03719c", + "coral": "0xfc5a50", + "cream": "0xffffc2", + "reddish brown": "0x7f2b0a", + "burnt sienna": "0xb04e0f", + "brick": "0xa03623", + "sage": "0x87ae73", + "grey green": "0x789b73", + "white": "0xffffff", + "robin's egg blue": "0x98eff9", + "moss green": "0x658b38", + "steel blue": "0x5a7d9a", + "eggplant": "0x380835", + "light yellow": "0xfffe7a", + "leaf green": "0x5ca904", + "light grey": "0xd8dcd6", + "puke": "0xa5a502", + "pinkish purple": "0xd648d7", + "sea blue": "0x047495", + "pale purple": "0xb790d4", + "slate blue": "0x5b7c99", + "blue grey": "0x607c8e", + "hunter green": "0x0b4008", + "fuchsia": "0xed0dd9", + "crimson": "0x8c000f", + "pale yellow": "0xffff84", + "ochre": "0xbf9005", + "mustard yellow": "0xd2bd0a", + "light red": "0xff474c", + "cerulean": "0x0485d1", + "pale pink": "0xffcfdc", + "deep blue": "0x040273", + "rust": "0xa83c09", + "light teal": "0x90e4c1", + "slate": "0x516572", + "goldenrod": "0xfac205", + "dark yellow": "0xd5b60a", + "dark grey": "0x363737", + "army green": "0x4b5d16", + "grey blue": "0x6b8ba4", + "seafoam": "0x80f9ad", + "puce": "0xa57e52", + "spring green": "0xa9f971", + "dark orange": "0xc65102", + "sand": "0xe2ca76", + "pastel green": "0xb0ff9d", + "mint": "0x9ffeb0", + "light orange": "0xfdaa48", + "bright pink": "0xfe01b1", + "chartreuse": "0xc1f80a", + "deep purple": "0x36013f", + "dark brown": "0x341c02", + "taupe": "0xb9a281", + "pea green": "0x8eab12", + "puke green": "0x9aae07", + "kelly green": "0x02ab2e", + "seafoam green": "0x7af9ab", + "blue green": "0x137e6d", + "khaki": "0xaaa662", + "burgundy": "0x610023", + "dark teal": "0x014d4e", + "brick red": "0x8f1402", + "royal purple": "0x4b006e", + "plum": "0x580f41", + "mint green": "0x8fff9f", + "gold": "0xdbb40c", + "baby blue": "0xa2cffe", + "yellow green": "0xc0fb2d", + "bright purple": "0xbe03fd", + "dark red": "0x840000", + "pale blue": "0xd0fefe", + "grass green": "0x3f9b0b", + "navy": "0x01153e", + "aquamarine": "0x04d8b2", + "burnt orange": "0xc04e01", + "neon green": "0x0cff0c", + "bright blue": "0x0165fc", + "rose": "0xcf6275", + "light pink": "0xffd1df", + "mustard": "0xceb301", + "indigo": "0x380282", + "lime": "0xaaff32", + "sea green": "0x53fca1", + "periwinkle": "0x8e82fe", + "dark pink": "0xcb416b", + "olive green": "0x677a04", + "peach": "0xffb07c", + "pale green": "0xc7fdb5", + "light brown": "0xad8150", + "hot pink": "0xff028d", + "black": "0x000000", + "lilac": "0xcea2fd", + "navy blue": "0x001146", + "royal blue": "0x0504aa", + "beige": "0xe6daa6", + "salmon": "0xff796c", + "olive": "0x6e750e", + "maroon": "0x650021", + "bright green": "0x01ff07", + "dark purple": "0x35063e", + "mauve": "0xae7181", + "forest green": "0x06470c", + "aqua": "0x13eac9", + "cyan": "0x00ffff", + "tan": "0xd1b26f", + "dark blue": "0x00035b", + "lavender": "0xc79fef", + "turquoise": "0x06c2ac", + "dark green": "0x033500", + "violet": "0x9a0eea", + "light purple": "0xbf77f6", + "lime green": "0x89fe05", + "grey": "0x929591", + "sky blue": "0x75bbfd", + "yellow": "0xffff14", + "magenta": "0xc20078", + "light green": "0x96f97b", + "orange": "0xf97306", + "teal": "0x029386", + "light blue": "0x95d0fc", + "red": "0xe50000", + "brown": "0x653700", + "pink": "0xff81c0", + "blue": "0x0343df", + "green": "0x15b01a", + "purple": "0x7e1e9c" +} -- cgit v1.2.3 From 8bb3078b2a3cd7d9defcf26958bde4a184f5e1be Mon Sep 17 00:00:00 2001 From: scragly <29337040+scragly@users.noreply.github.com> Date: Wed, 3 Apr 2019 15:22:57 +0100 Subject: Apply suggestions from code review Co-Authored-By: Suhail6inkling <38522108+Suhail6inkling@users.noreply.github.com> --- bot/seasons/easter/egg_decorating.py | 54 +++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 16 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_decorating.py b/bot/seasons/easter/egg_decorating.py index 46351534..fbbd09f5 100644 --- a/bot/seasons/easter/egg_decorating.py +++ b/bot/seasons/easter/egg_decorating.py @@ -1,8 +1,10 @@ +import json import logging import random +from contextlib import suppress from io import BytesIO from pathlib import Path - +from typing import Union import discord from PIL import Image @@ -10,6 +12,12 @@ from discord.ext import commands log = logging.getLogger(__name__) +with open(Path("bot", "resources", "evergreen", "html_colours.json")) as f: + HTML_COLOURS = json.load(f) + +with open(Path("bot", "resources", "evergreen", "xkcd_colours.json")) as f: + XKCD_COLOURS = json.load(f) + COLOURS = [ (255, 0, 0, 255), (255, 128, 0, 255), (255, 255, 0, 255), (0, 255, 0, 255), (0, 255, 255, 255), (0, 0, 255, 255), (255, 0, 255, 255), (128, 0, 128, 255) @@ -21,17 +29,27 @@ IRREPLACEABLE = [ class EggDecorating(commands.Cog): - """A Command that lets you decorate some easter eggs!""" + """Decorate some easter eggs!""" def __init__(self, bot): self.bot = bot + @staticmethod + def replace_invalid(colour: str): + """Attempts to match with HTML or XKCD colour names, returning the int value.""" + with suppress(KeyError): + return int(HTML_COLOURS[colour], 16) + with suppress(KeyError): + return int(XKCD_COLOURS[colour], 16) + return None + @commands.command(aliases=["decorateegg"]) - async def eggdecorate(self, ctx, *colours): + async def eggdecorate(self, ctx, *colours: Union[discord.Colour, str]): """ - This 'paints' a beautiful egg using inputted colours + Picks a random egg design and decorates it using the given colours. - It picks from a random set of designs and alters the colours to the user's liking + 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. """ if len(colours) < 2: @@ -48,21 +66,22 @@ class EggDecorating(commands.Cog): invalid.append(c) if len(invalid) > 1: - return await ctx.send(f"The following colours are invalid: {' '.join(invalid)}") + return await ctx.send(f"Sorry, I don't know these colours: {' '.join(invalid)}") elif len(invalid) == 1: - return await ctx.send(f"{invalid[0]} is an invalid colour!") - - colours = converted + return await ctx.send(f"Sorry, I don't know the colour {invalid[0]}!") async with ctx.typing(): - colours *= 4 - # This is to ensure that no IndexErrors are raised since the most amount of colours on an egg is 8 + # expand list to 8 colours + colours_n = len(colours) + if colours_n < 8: + q, r = divmod(8, colours_n) + colours = colours * q + colours[:r] num = random.randint(1, 6) im = Image.open(Path("bot", "resources", "easter", "easter_eggs", f"design{num}.png")) data = list(im.getdata()) - replaceable = {x for x in data if x not in IRREPLACEABLE} # Turns it into a set to avoid duplicates - replaceable = sorted(replaceable, key=COLOURS.index) # Sorts it by colour order + replaceable = {x for x in data if x not in IRREPLACEABLE} + replaceable = sorted(replaceable, key=COLOURS.index) replacing_colours = {colour: colours[i] for i, colour in enumerate(replaceable)} new_data = [] @@ -81,11 +100,14 @@ class EggDecorating(commands.Cog): bufferedio.seek(0) file = discord.File(bufferedio, filename="egg.png") # Creates file to be used in embed - embed = discord.Embed(title="Your egg", description="Here is your pretty little egg. Hope you like it!") + embed = discord.Embed( + title="Your Colourful Easter Egg", + description="Here is your pretty little egg. Hope you like it!" + ) embed.set_image(url="attachment://egg.png") - embed.set_author(name=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) + await ctx.send(file=file, embed=embed) def setup(bot): -- cgit v1.2.3 From 87a2c0fcd6919cb236645df6da284c8b4e8fe6dc Mon Sep 17 00:00:00 2001 From: Suhail Date: Wed, 3 Apr 2019 15:28:14 +0100 Subject: More suggestions from code review Co-Authored-By: scragly <29337040+scragly@users.noreply.github.com> --- bot/seasons/easter/egg_decorating.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_decorating.py b/bot/seasons/easter/egg_decorating.py index fbbd09f5..b5f3e428 100644 --- a/bot/seasons/easter/egg_decorating.py +++ b/bot/seasons/easter/egg_decorating.py @@ -56,14 +56,15 @@ class EggDecorating(commands.Cog): return await ctx.send("You must include at least 2 colours!") invalid = [] - converted = [] - for c in colours: - try: - colour = await commands.ColourConverter().convert(ctx, c) - # Attempts to convert the arguments into discord.Colour - converted.append(colour) - except commands.BadArgument: - invalid.append(c) + colours = list(colours) + for idx, colour in enumerate(colours): + if isinstance(colour, discord.Colour): + continue + value = self.replace_invalid(colour) + if value: + colours[idx] = discord.Colour(value) + else: + invalid.append(colour) if len(invalid) > 1: return await ctx.send(f"Sorry, I don't know these colours: {' '.join(invalid)}") -- cgit v1.2.3 From 6dc9b4e7b232c095cb07108d5a37e075af50d2ee Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Thu, 4 Apr 2019 13:17:41 +0100 Subject: JSON file to store all conversation starters in as requested. --- bot/resources/easter/starter.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 bot/resources/easter/starter.json (limited to 'bot') diff --git a/bot/resources/easter/starter.json b/bot/resources/easter/starter.json new file mode 100644 index 00000000..31e2cbc9 --- /dev/null +++ b/bot/resources/easter/starter.json @@ -0,0 +1,24 @@ +{ + "starters": [ + "What is your favourite Easter candy or treat?", + "What is your earliest memory of Easter?", + "What is the title of the last book you read?", + "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?", + "Name one famous person you would like to have at your easter dinner.", + "What was the last movie you saw in a cinema?", + "What is your favourite food?", + "If you could travel anywhere in the world, where would you go?", + "Tell us 5 things you do well.", + "What is your favourite place that you have visited?", + "What is your favourite color?", + "If you had $100 bill in your Easter Basket, what would you do with it?", + "What would you do if you know you could succeed at anything you chose to do?", + "If you could take only three things from your house, what would they be?" + ] +} -- cgit v1.2.3 From b58def41b6b30d0291d99eec434fdcf4c98a7fed Mon Sep 17 00:00:00 2001 From: RohanRadia Date: Thu, 4 Apr 2019 14:11:45 +0100 Subject: Just changed the conversation starters directory. --- bot/seasons/easter/conversationstarters.py | 31 ++++++++++++++++++++++++++++++ conversationstarters.py | 31 ------------------------------ 2 files changed, 31 insertions(+), 31 deletions(-) create mode 100644 bot/seasons/easter/conversationstarters.py delete mode 100644 conversationstarters.py (limited to 'bot') diff --git a/bot/seasons/easter/conversationstarters.py b/bot/seasons/easter/conversationstarters.py new file mode 100644 index 00000000..b479406b --- /dev/null +++ b/bot/seasons/easter/conversationstarters.py @@ -0,0 +1,31 @@ +import json +import logging +import random +from pathlib import Path + +from discord.ext import commands + +log = logging.getLogger(__name__) + +with open(Path('bot', 'resources', 'easter', 'starter.json'), 'r', encoding="utf8") as f: + starters = json.load(f) + + +class ConvoStarters(commands.Cog): + """Easter conversation topics.""" + + def __init__(self, bot): + self.bot = bot + + @commands.command() + async def topic(self, ctx): + """Responds with a random topic to start a conversation.""" + + await ctx.send(random.choice(starters['starters'])) + + +def setup(bot): + """Conversation starters Cog load.""" + + bot.add_cog(ConvoStarters(bot)) + log.info("ConvoStarters cog loaded") diff --git a/conversationstarters.py b/conversationstarters.py deleted file mode 100644 index b479406b..00000000 --- a/conversationstarters.py +++ /dev/null @@ -1,31 +0,0 @@ -import json -import logging -import random -from pathlib import Path - -from discord.ext import commands - -log = logging.getLogger(__name__) - -with open(Path('bot', 'resources', 'easter', 'starter.json'), 'r', encoding="utf8") as f: - starters = json.load(f) - - -class ConvoStarters(commands.Cog): - """Easter conversation topics.""" - - def __init__(self, bot): - self.bot = bot - - @commands.command() - async def topic(self, ctx): - """Responds with a random topic to start a conversation.""" - - await ctx.send(random.choice(starters['starters'])) - - -def setup(bot): - """Conversation starters Cog load.""" - - bot.add_cog(ConvoStarters(bot)) - log.info("ConvoStarters cog loaded") -- cgit v1.2.3 From 84192820dd41aef7458665da1ef50e6215c016e0 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 4 Apr 2019 21:23:34 +0530 Subject: removed the embed lines --- bot/seasons/easter/april_fools_vids.py | 5 ----- 1 file changed, 5 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/april_fools_vids.py b/bot/seasons/easter/april_fools_vids.py index 553b2a78..a851f3cc 100644 --- a/bot/seasons/easter/april_fools_vids.py +++ b/bot/seasons/easter/april_fools_vids.py @@ -32,11 +32,6 @@ class AprilFoolVideos(commands.Cog): random_youtuber = random.choice(self.youtubers) category = self.yt_vids[random_youtuber] random_vid = random.choice(category) - embed = discord.Embed() - embed.title = random_vid['title'] - embed.colour = Colours.yellow - embed.description = f'Checkout this april fools video by {random_youtuber}' - embed.url = random_vid['link'] await ctx.send(f"Check out this April Fools' video by {random_youtuber}.\n\n{random_vid['link']}") -- cgit v1.2.3 From 9f72691bd3bcb32f22d99be148f8f5b3950923ba Mon Sep 17 00:00:00 2001 From: Suhail Date: Fri, 5 Apr 2019 15:23:56 +0100 Subject: Avatar Easterifier --- bot/resources/easter/chocolate_bunny.png | Bin 0 -> 7789 bytes bot/seasons/easter/avatar_easterifier.py | 135 +++++++++++++++++++++++++++++++ bot/seasons/easter/egg_decorating.py | 1 + 3 files changed, 136 insertions(+) create mode 100644 bot/resources/easter/chocolate_bunny.png create mode 100644 bot/seasons/easter/avatar_easterifier.py (limited to 'bot') diff --git a/bot/resources/easter/chocolate_bunny.png b/bot/resources/easter/chocolate_bunny.png new file mode 100644 index 00000000..6b25aa5a Binary files /dev/null and b/bot/resources/easter/chocolate_bunny.png differ diff --git a/bot/seasons/easter/avatar_easterifier.py b/bot/seasons/easter/avatar_easterifier.py new file mode 100644 index 00000000..b81f7434 --- /dev/null +++ b/bot/seasons/easter/avatar_easterifier.py @@ -0,0 +1,135 @@ +import asyncio +import logging +from io import BytesIO +from pathlib import Path +from typing import Union + +import aiohttp +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): + self.bot = bot + self.session = aiohttp.ClientSession() + + @staticmethod + def closest(x): + """ + 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): + """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, *colours: Union[discord.Colour, str]): + """ + 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): + """ + 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 + async with self.session.get(ctx.author.avatar_url_as(size=256)) as resp: + image_bytes = await resp.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): + """Cog load.""" + + bot.add_cog(AvatarEasterifier(bot)) + log.info("AvatarEasterifier cog loaded") diff --git a/bot/seasons/easter/egg_decorating.py b/bot/seasons/easter/egg_decorating.py index b5f3e428..d283e42a 100644 --- a/bot/seasons/easter/egg_decorating.py +++ b/bot/seasons/easter/egg_decorating.py @@ -109,6 +109,7 @@ class EggDecorating(commands.Cog): embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) await ctx.send(file=file, embed=embed) + return new_im def setup(bot): -- cgit v1.2.3 From 7adf2eadad8583f88c617a026f4f9635a14a888e Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 5 Apr 2019 21:30:29 +0530 Subject: removed unused imports --- bot/seasons/easter/april_fools_vids.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/april_fools_vids.py b/bot/seasons/easter/april_fools_vids.py index a851f3cc..5dae8485 100644 --- a/bot/seasons/easter/april_fools_vids.py +++ b/bot/seasons/easter/april_fools_vids.py @@ -3,11 +3,8 @@ import random from json import load from pathlib import Path -import discord from discord.ext import commands -from bot.constants import Colours - log = logging.getLogger(__name__) -- cgit v1.2.3 From 0bb32b4634f6becf944dff04642918a290f9fcfb Mon Sep 17 00:00:00 2001 From: Suhail Date: Fri, 5 Apr 2019 18:13:41 +0100 Subject: Replaced new ClientSession with the one already located in the bot --- bot/seasons/easter/avatar_easterifier.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/avatar_easterifier.py b/bot/seasons/easter/avatar_easterifier.py index b81f7434..9f7ea271 100644 --- a/bot/seasons/easter/avatar_easterifier.py +++ b/bot/seasons/easter/avatar_easterifier.py @@ -4,7 +4,6 @@ from io import BytesIO from pathlib import Path from typing import Union -import aiohttp import discord from PIL import Image from PIL.ImageOps import posterize @@ -24,7 +23,6 @@ class AvatarEasterifier(commands.Cog): def __init__(self, bot): self.bot = bot - self.session = aiohttp.ClientSession() @staticmethod def closest(x): @@ -73,7 +71,7 @@ class AvatarEasterifier(commands.Cog): async with ctx.typing(): # Grabs image of avatar - async with self.session.get(ctx.author.avatar_url_as(size=256)) as resp: + async with self.bot.http_session.get(ctx.author.avatar_url_as(size=256)) as resp: image_bytes = await resp.read() old = Image.open(BytesIO(image_bytes)) -- cgit v1.2.3 From 77d36eb1bf8c3c7d30b5c6015c00b23d1fe13d6b Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 06:22:20 +1000 Subject: Suppress excessive PIL debug logs. --- bot/__init__.py | 1 + 1 file changed, 1 insertion(+) (limited to 'bot') diff --git a/bot/__init__.py b/bot/__init__.py index 21ff8c97..7c564178 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -54,6 +54,7 @@ if root.handlers: # Silence irrelevant loggers logging.getLogger("discord").setLevel(logging.ERROR) logging.getLogger("websockets").setLevel(logging.ERROR) +logging.getLogger("PIL").setLevel(logging.ERROR) # Setup new logging configuration logging.basicConfig( -- cgit v1.2.3 From 0e674ec8ecd7090706b64be8bee4994907830ac2 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 06:28:19 +1000 Subject: Add seasonalbot-chat and python-discussion channels. --- bot/constants.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'bot') diff --git a/bot/constants.py b/bot/constants.py index b19d494b..6d10cf8f 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -46,8 +46,10 @@ class Channels(NamedTuple): off_topic_2 = 463035268514185226 python = 267624335836053506 reddit = 458224812528238616 + seasonalbot_chat = int(environ.get('CHANNEL_SEASONALBOT_CHAT', 542272993192050698)) staff_lounge = 464905259261755392 verification = 352442727016693763 + python_discussion = 267624335836053506 class Client(NamedTuple): -- cgit v1.2.3 From 271e5313765fe899978fadd0ec4e8dff4eb9dfa2 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 06:29:38 +1000 Subject: Create EggHunt extension. --- bot/seasons/easter/egg_hunt/__init__.py | 12 + bot/seasons/easter/egg_hunt/cog.py | 502 +++++++++++++++++++++++++++++++ bot/seasons/easter/egg_hunt/constants.py | 39 +++ 3 files changed, 553 insertions(+) create mode 100644 bot/seasons/easter/egg_hunt/__init__.py create mode 100644 bot/seasons/easter/egg_hunt/cog.py create mode 100644 bot/seasons/easter/egg_hunt/constants.py (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/__init__.py b/bot/seasons/easter/egg_hunt/__init__.py new file mode 100644 index 00000000..3f722c9b --- /dev/null +++ b/bot/seasons/easter/egg_hunt/__init__.py @@ -0,0 +1,12 @@ +import logging + +from .cog import EggHunt + +log = logging.getLogger(__name__) + + +def setup(bot): + """Conversation starters Cog load.""" + + bot.add_cog(EggHunt()) + log.info("EggHunt cog loaded") diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py new file mode 100644 index 00000000..61099b49 --- /dev/null +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -0,0 +1,502 @@ +import asyncio +import contextlib +import logging +import random +import sqlite3 +from datetime import datetime, timezone +from pathlib import Path + +import discord +from discord.ext import commands + +from bot.constants import Channels, Client, Roles as MainRoles, bot +from bot.decorators import with_role +from .constants import Colours, EggHuntSettings, Emoji, Roles + +log = logging.getLogger(__name__) + +DB_PATH = Path("bot", "resources", "easter", "egg_hunt.sqlite") + +TEAM_MAP = { + Roles.white: Emoji.egg_white, + Roles.blurple: Emoji.egg_blurple, + Emoji.egg_white: Roles.white, + Emoji.egg_blurple: Roles.blurple +} + +GUILD = bot.get_guild(Client.guild) + + +def get_team_role(user: discord.Member): + """Helper function to get the team role for a member.""" + + if Roles.white in user.roles: + return Roles.white + if Roles.blurple in user.roles: + return Roles.blurple + + +async def assign_team(user: discord.Member): + """Helper function to assign a new team role for a member.""" + db = sqlite3.connect(DB_PATH) + c = db.cursor() + c.execute(f"SELECT team FROM user_scores WHERE user_id = {user.id}") + result = c.fetchone() + if not result: + new_team = random.choice([Roles.white, Roles.blurple]) + log.debug(f"Assigned role {new_team} to {user}.") + else: + if result[0] == "WHITE": + new_team = Roles.white + else: + new_team = Roles.blurple + log.debug(f"Restored role {new_team} to {user}.") + + await user.add_roles(new_team) + return GUILD.get_member(user.id) + + +class EggMessage: + """Handles a single egg reaction drop session.""" + + def __init__(self, message: discord.Message, egg: discord.Emoji): + self.message = message + self.egg = egg + self.first = None + self.users = [] + self.teams = {Roles.white: "WHITE", Roles.blurple: "BLURPLE"} + self.new_team_assignments = {} + self.timeout_task = None + + @staticmethod + def add_user_score(user_id: int, team: str, score: int): + """Builds the SQL for adding a score to a user in the database.""" + + return ( + "INSERT INTO user_scores(user_id, team, score)" + f"VALUES({user_id}, '{team}', {score})" + f"ON CONFLICT (user_id) DO UPDATE SET score=score+{score}" + ) + + @staticmethod + def add_team_score(team_name: str, score: int): + """Builds the SQL for adding a score to a team in the database.""" + + return f"UPDATE team_scores SET team_score=team_score+{score} WHERE team_id='{team_name}'" + + def finalise_score(self): + """Sums and actions scoring for this egg drop session.""" + + db = sqlite3.connect(DB_PATH) + c = db.cursor() + + team_scores = {"WHITE": 0, "BLURPLE": 0} + + first_team = get_team_role(self.first) + if not first_team: + log.debug("User without team role!") + db.close() + return + + score = 3 if first_team == TEAM_MAP[first_team] else 2 + + c.execute(self.add_user_score(self.first.id, self.teams[first_team], score)) + team_scores[self.teams[first_team]] += score + + for user in self.users: + team = get_team_role(user) + if not team: + log.debug("User without team role!") + continue + + team_name = self.teams[team] + team_scores[team_name] += 1 + score = 2 if team == first_team else 1 + c.execute(self.add_user_score(user.id, team_name, score)) + + for team_name, score in team_scores.items(): + if not score: + continue + c.execute(self.add_team_score(team_name, score)) + + db.commit() + db.close() + + log.debug( + f"EggHunt session finalising: ID({self.message.id}) " + f"FIRST({self.first}) REST({self.users})." + ) + + async def start_timeout(self, seconds: int = 5): + """Begins a task that will sleep until the given seconds before finalizing the session.""" + + if self.timeout_task: + self.timeout_task.cancel() + self.timeout_task = None + + await asyncio.sleep(seconds) + + bot.remove_listener(self.collect_reacts, name="on_reaction_add") + + with contextlib.suppress(discord.Forbidden): + await self.message.clear_reactions() + + if self.first: + self.finalise_score() + + def is_valid_react(self, reaction: discord.Reaction, user: discord.Member): + """Validates a reaction event was meant for this session.""" + + if user.bot: + return False + if reaction.message.id != self.message.id: + return False + if reaction.emoji != self.egg: + return False + return True + + async def collect_reacts(self, reaction: discord.Reaction, user: discord.Member): + """Handles emitted reaction_add events via listener.""" + + if not self.is_valid_react(reaction, user): + return + + team = get_team_role(user) + if not team: + log.debug(f"Assigning a team for {user}.") + user = await assign_team(user) + + if not self.first: + log.debug(f"{user} was first to react to egg on {self.message.id}.") + self.first = user + await self.start_timeout() + else: + self.users.append(user) + + async def start(self): + """Starts the egg drop session.""" + + log.debug(f"EggHunt session started for message {self.message.id}.") + bot.add_listener(self.collect_reacts, name="on_reaction_add") + await self.message.add_reaction(self.egg) + self.timeout_task = asyncio.create_task(self.start_timeout(300)) + + +class SuperEggMessage(EggMessage): + """Handles a super egg session.""" + + def __init__(self, message: discord.Message, egg: discord.Emoji, window: int): + super().__init__(message, egg) + self.window = window + + async def finalise_score(self): + """Sums and actions scoring for this super egg session.""" + + message = await self.message.channel.get_message(self.message.id) + + count = 0 + white = 0 + blurple = 0 + react_users = [] + for reaction in message.reactions: + if reaction.emoji == self.egg: + react_users = await reaction.users().flatten() + for user in react_users: + team = get_team_role(user) + if team == Roles.white: + white += 1 + elif team == Roles.blurple: + blurple += 1 + count = reaction.count - 1 + break + + score = 50 if self.egg == Emoji.egg_gold else 100 + if white == blurple: + log.debug("Tied SuperEgg Result.") + team = None + score = score / 2 + elif white > blurple: + team = Roles.white + else: + team = Roles.blurple + + embed = self.message.embeds[0] + + db = sqlite3.connect(DB_PATH) + c = db.cursor() + + user_bonus = 5 if self.egg == Emoji.egg_gold else 10 + for user in react_users: + if user.bot: + continue + role = get_team_role(user) + if not role: + print('issue') + user_score = 1 if user != self.first else user_bonus + c.execute(self.add_user_score(user.id, self.teams[role], user_score)) + + if not team: + embed.description = f"{embed.description}\n\nA Tie!\nBoth got {score} points!" + c.execute(self.add_team_score(self.teams[Roles.white], score)) + c.execute(self.add_team_score(self.teams[Roles.blurple], score)) + team_name = "TIE" + else: + team_name = self.teams[team] + embed.description = ( + f"{embed.description}\n\nTeam {team_name.capitalize()} won the points!" + ) + c.execute(self.add_team_score(team_name, score)) + + c.execute( + "INSERT INTO super_eggs (message_id, egg_type, team, window) " + f"VALUES ({self.message.id}, '{self.egg.name}', '{team_name}', {self.window});" + ) + + log.debug("Committing Super Egg scores.") + db.commit() + db.close() + + embed.set_footer(text=f"Finished with {count} total reacts.") + await self.message.edit(embed=embed) + + async def start_timeout(self, seconds=None): + """Starts the super egg session.""" + + if not seconds: + return + count = 4 + for _ in range(count): + await asyncio.sleep(1) + embed = self.message.embeds[0] + embed.set_footer(text=f"Finishing in {count} minutes.") + await self.message.edit(embed=embed) + count -= 1 + bot.remove_listener(self.collect_reacts, name="on_reaction_add") + await self.finalise_score() + + +class EggHunt(commands.Cog): + """Easter Egg Hunt Event""" + + def __init__(self): + self.event_channel = GUILD.get_channel(Channels.seasonalbot_chat) + self.task = asyncio.create_task(self.super_egg()) + self.task.add_done_callback(self.task_cleanup) + + @staticmethod + def task_cleanup(task): + """Returns a task result. Used as a done callback to show raised exceptions.""" + + task.result() + + @staticmethod + def current_timestamp(): + """Returns a timestamp of the current UTC time.""" + + return int(datetime.utcnow().replace(tzinfo=timezone.utc).timestamp()) + + async def super_egg(self): + """Manages the timing of super egg drops.""" + + while True: + now = self.current_timestamp() + + if now > EggHuntSettings.end_time: + log.debug(f"Hunt ended. Ending task.") + break + + if now < EggHuntSettings.start_time: + remaining = EggHuntSettings.start_time - now + log.debug(f"Hunt not started yet. Sleeping for {remaining}.") + await asyncio.sleep(remaining) + + log.debug(f"Hunt started.") + current_window = EggHuntSettings.start_time + next_window = 0 + for window in EggHuntSettings.windows: + window = int(window) + if window < now: + current_window = window + continue + if not next_window: + next_window = window + else: + break + + log.debug(f"Current Window: {current_window}. Next Window {next_window}") + + db = sqlite3.connect(DB_PATH) + c = db.cursor() + c.execute(f"SELECT COUNT(*) FROM super_eggs WHERE window={current_window}") + count = c.fetchone()[0] + db.close() + + if not count: + log.debug(f"test") + next_drop = random.randrange(now, next_window) + log.debug(f"Sleeping until next super egg drop: {next_drop}.") + await asyncio.sleep(next_drop) + if random.randrange(10) <= 2: + egg = Emoji.egg_diamond + egg_type = "Diamond" + score = "100" + colour = Colours.diamond + else: + egg = Emoji.egg_gold + egg_type = "Gold" + score = "50" + colour = Colours.gold + + embed = discord.Embed( + title=f"A {egg_type} Egg Has Appeared!", + description=f"**Worth {score} team points!**\n\n" + "The team with the most reactions after 5 minutes wins!", + colour=colour + ) + embed.set_thumbnail(url=egg.url) + embed.set_footer(text=f"Finishing in 5 minutes.") + msg = await self.event_channel.send(embed=embed) + await SuperEggMessage(msg, egg, current_window).start() + + log.debug(f"Sleeping until next window.") + next_loop = max(next_window - self.current_timestamp(), 0) + await asyncio.sleep(next_loop) + + @commands.Cog.listener() + async def on_message(self, message): + """Message event listener for random egg drops.""" + + if self.current_timestamp() < EggHuntSettings.start_time: + return + + if message.channel.id not in EggHuntSettings.allowed_channels: + log.debug("Message not in Egg Hunt channel; ignored.") + return + + if message.author.bot: + return + + if random.randrange(100) <= 40: + await EggMessage(message, random.choice([Emoji.egg_white, Emoji.egg_blurple])).start() + + @commands.group(invoke_without_command=True) + async def hunt(self, ctx): + """ + For 48 hours, hunt down as many eggs randomly appearing as possible. + + Standard Eggs + ============== + Egg React: +1pt + Team Bonus for Claimed Egg: +1pt + First React on Other Team Egg: +1pt + First React on Your Team Egg: +2pt + + If you get first react, you will claim that egg for your team, allowing + your team to get the Team Bonus point, but be quick, as the egg will + disappear after 5 seconds of the first react. + + Super Eggs + =========== + Gold Egg: 50 team pts, 5pts to first react + Diamond Egg: 100 team pts, 10pts to first react + + Super Eggs only appear in #seasonalbot-chat so be sure to keep an eye + out. They stay around for 5 minutes and the team with the most reacts + wins the points. + """ + await ctx.invoke(bot.get_command("help"), "hunt") + + @hunt.command() + async def countdown(self, ctx): + """Show the time status of the Egg Hunt event.""" + + now = self.current_timestamp() + if now > EggHuntSettings.end_time: + return await ctx.send("The Hunt has ended.") + + difference = EggHuntSettings.start_time - now + if difference < 0: + difference = EggHuntSettings.end_time - now + msg = "The Egg Hunt will end in" + else: + msg = "The Egg Hunt will start in" + + hours, r = divmod(difference, 3600) + minutes, r = divmod(r, 60) + await ctx.send(f"{msg} {hours:.0f}hrs, {minutes:.0f}mins & {r:.0f}secs") + + @hunt.command() + async def leaderboard(self, ctx): + """Show the Egg Hunt Leaderboards.""" + + db = sqlite3.connect(DB_PATH) + c = db.cursor() + c.execute(f"SELECT *, RANK() OVER(ORDER BY score DESC) AS rank FROM user_scores LIMIT 10") + user_result = c.fetchall() + c.execute(f"SELECT * FROM team_scores ORDER BY team_score DESC") + team_result = c.fetchall() + db.close() + output = [] + scr_len = max(len(str(r[2])) for r in user_result) + for user_id, team, score, rank in user_result: + user = GUILD.get_member(user_id) or user_id + team = team.capitalize() + score = f"{score}pts" + output.append(f"{rank:>2}. {score:>{scr_len+3}} - {user} ({team})") + user_board = '\n'.join(output) + output = [] + for team, score in team_result: + output.append(f"{team:<7}: {score}") + team_board = '\n'.join(output) + embed = discord.Embed( + title="Egg Hunt Leaderboards", + description=f"**Teams**\n```\n{team_board}\n```\n" + f"**Top 10 Members**\n```\n{user_board}\n```" + ) + await ctx.send(embed=embed) + + @hunt.command() + async def rank(self, ctx, *, member: discord.Member = None): + """Get your ranking in the Egg Hunt Leaderboard.""" + + member = member or ctx.author + db = sqlite3.connect(DB_PATH) + c = db.cursor() + c.execute( + f"SELECT RANK() OVER(ORDER BY score DESC) AS rank FROM user_scores " + f"WHERE user_id={member.id}" + ) + result = c.fetchone() + db.close() + if not result: + embed = discord.Embed().set_author(name=f"Egg Hunt - No Ranking") + else: + embed = discord.Embed().set_author(name=f"Egg Hunt - Rank #{result[0]}") + await ctx.send(embed=embed) + + @with_role(MainRoles.admin) + @hunt.command() + async def clear_db(self, ctx): + """Resets the database to it's initial state.""" + + def check(msg): + if msg.author != ctx.author: + return False + if msg.channel != ctx.channel: + return False + return True + await ctx.send( + "WARNING: This will delete all current event data.\n" + "Please verify this action by replying with 'Yes, I want to delete all data.'" + ) + reply_msg = await bot.wait_for('message', check=check) + if reply_msg.content != "Yes, I want to delete all data.": + return await ctx.send("Reply did not match. Aborting database deletion.") + db = sqlite3.connect(DB_PATH) + c = db.cursor() + c.execute("DELETE FROM super_eggs;") + c.execute("DELETE FROM user_scores;") + c.execute(f"UPDATE team_scores SET team_score=0") + db.commit() + db.close() + await ctx.send("Database successfully cleared.") diff --git a/bot/seasons/easter/egg_hunt/constants.py b/bot/seasons/easter/egg_hunt/constants.py new file mode 100644 index 00000000..d3ab5962 --- /dev/null +++ b/bot/seasons/easter/egg_hunt/constants.py @@ -0,0 +1,39 @@ +import os + +from discord import Colour + +from bot.constants import Channels, Client, bot + + +GUILD = bot.get_guild(Client.guild) + + +class EggHuntSettings: + start_time = int(os.environ["HUNT_START"]) + end_time = start_time + 172800 + windows = os.environ.get("HUNT_WINDOWS").split(',') or [] + allowed_channels = [ + Channels.seasonalbot_chat, + Channels.off_topic_0, + Channels.off_topic_1, + Channels.off_topic_2, + ] + + +class Roles: + white = GUILD.get_role(569123918795898921) + blurple = GUILD.get_role(569124011406000139) + + +class Emoji: + egg_white = bot.get_emoji(569006388437844023) + egg_blurple = bot.get_emoji(569006905972752384) + egg_gold = bot.get_emoji(569079769736675328) + egg_diamond = bot.get_emoji(569109259288182784) + + +class Colours: + white = Colour(0xFFFFFF) + blurple = Colour(0x7289DA) + gold = Colour(0xE4E415) + diamond = Colour(0xECF5FF) -- cgit v1.2.3 From 74a8f0c5f05434444aec2c7062f52ea8cd5838be Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 06:32:04 +1000 Subject: Adjust docstring underline style. --- bot/seasons/easter/egg_hunt/cog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 61099b49..a4cf823d 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -385,7 +385,7 @@ class EggHunt(commands.Cog): For 48 hours, hunt down as many eggs randomly appearing as possible. Standard Eggs - ============== + -------------- Egg React: +1pt Team Bonus for Claimed Egg: +1pt First React on Other Team Egg: +1pt @@ -396,7 +396,7 @@ class EggHunt(commands.Cog): disappear after 5 seconds of the first react. Super Eggs - =========== + ----------- Gold Egg: 50 team pts, 5pts to first react Diamond Egg: 100 team pts, 10pts to first react -- cgit v1.2.3 From 6dc4b16438d490f9c17ef1b85d80e0dc01e4e602 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 06:32:52 +1000 Subject: Create clean egg_hunt sqlite db. --- bot/resources/easter/egg_hunt.sqlite | Bin 0 -> 16384 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 bot/resources/easter/egg_hunt.sqlite (limited to 'bot') diff --git a/bot/resources/easter/egg_hunt.sqlite b/bot/resources/easter/egg_hunt.sqlite new file mode 100644 index 00000000..6a7ae32d Binary files /dev/null and b/bot/resources/easter/egg_hunt.sqlite differ -- cgit v1.2.3 From 20120e189818987e2a3827b0550c28a2fa90d632 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 06:49:47 +1000 Subject: Change devlog to seasonalbot-log channel. --- bot/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/constants.py b/bot/constants.py index 6d10cf8f..7011bd8c 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -29,7 +29,7 @@ class Channels(NamedTuple): bot = 267659945086812160 checkpoint_test = 422077681434099723 devalerts = 460181980097675264 - devlog = int(environ.get('CHANNEL_DEVLOG', 409308876241108992)) + devlog = int(environ.get('CHANNEL_DEVLOG', 548438471685963776)) devtest = 414574275865870337 help_0 = 303906576991780866 help_1 = 303906556754395136 -- cgit v1.2.3 From 42ebba0622dd63da735c916f4d4eca39c020c45a Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 08:47:29 +1000 Subject: Allow prefix env, move db location. --- bot/constants.py | 2 +- bot/resources/easter/egg_hunt.sqlite | Bin 16384 -> 0 bytes bot/resources/persist/egg_hunt.sqlite | Bin 0 -> 16384 bytes bot/seasons/easter/egg_hunt/cog.py | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 bot/resources/easter/egg_hunt.sqlite create mode 100644 bot/resources/persist/egg_hunt.sqlite (limited to 'bot') diff --git a/bot/constants.py b/bot/constants.py index 7011bd8c..a62166af 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -54,7 +54,7 @@ class Channels(NamedTuple): class Client(NamedTuple): guild = int(environ.get('SEASONALBOT_GUILD', 267624335836053506)) - prefix = "." + prefix = environ.get("PREFIX", ".") token = environ.get('SEASONALBOT_TOKEN') debug = environ.get('SEASONALBOT_DEBUG', '').lower() == 'true' season_override = environ.get('SEASON_OVERRIDE') diff --git a/bot/resources/easter/egg_hunt.sqlite b/bot/resources/easter/egg_hunt.sqlite deleted file mode 100644 index 6a7ae32d..00000000 Binary files a/bot/resources/easter/egg_hunt.sqlite and /dev/null differ diff --git a/bot/resources/persist/egg_hunt.sqlite b/bot/resources/persist/egg_hunt.sqlite new file mode 100644 index 00000000..6a7ae32d Binary files /dev/null and b/bot/resources/persist/egg_hunt.sqlite differ diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index a4cf823d..e963f22b 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -15,7 +15,7 @@ from .constants import Colours, EggHuntSettings, Emoji, Roles log = logging.getLogger(__name__) -DB_PATH = Path("bot", "resources", "easter", "egg_hunt.sqlite") +DB_PATH = Path("bot", "resources", "persist", "egg_hunt.sqlite") TEAM_MAP = { Roles.white: Emoji.egg_white, -- cgit v1.2.3 From 890a0e1eb045b3a984ccb10a6e3007c21761edda Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 09:35:36 +1000 Subject: Prevent weird errors, adjust constants to prod IDs. --- bot/seasons/easter/egg_hunt/cog.py | 19 +++++++++++++------ bot/seasons/easter/egg_hunt/constants.py | 8 ++++---- 2 files changed, 17 insertions(+), 10 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index e963f22b..758a4da3 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -178,7 +178,8 @@ class EggMessage: log.debug(f"EggHunt session started for message {self.message.id}.") bot.add_listener(self.collect_reacts, name="on_reaction_add") - await self.message.add_reaction(self.egg) + with contextlib.suppress(discord.Forbidden): + await self.message.add_reaction(self.egg) self.timeout_task = asyncio.create_task(self.start_timeout(300)) @@ -191,8 +192,10 @@ class SuperEggMessage(EggMessage): async def finalise_score(self): """Sums and actions scoring for this super egg session.""" - - message = await self.message.channel.get_message(self.message.id) + try: + message = await self.message.channel.get_message(self.message.id) + except discord.NotFound: + return count = 0 white = 0 @@ -257,7 +260,8 @@ class SuperEggMessage(EggMessage): db.close() embed.set_footer(text=f"Finished with {count} total reacts.") - await self.message.edit(embed=embed) + with contextlib.suppress(discord.HTTPException): + await self.message.edit(embed=embed) async def start_timeout(self, seconds=None): """Starts the super egg session.""" @@ -269,7 +273,10 @@ class SuperEggMessage(EggMessage): await asyncio.sleep(1) embed = self.message.embeds[0] embed.set_footer(text=f"Finishing in {count} minutes.") - await self.message.edit(embed=embed) + try: + await self.message.edit(embed=embed) + except discord.HTTPException: + break count -= 1 bot.remove_listener(self.collect_reacts, name="on_reaction_add") await self.finalise_score() @@ -376,7 +383,7 @@ class EggHunt(commands.Cog): if message.author.bot: return - if random.randrange(100) <= 40: + if random.randrange(100) <= 5: await EggMessage(message, random.choice([Emoji.egg_white, Emoji.egg_blurple])).start() @commands.group(invoke_without_command=True) diff --git a/bot/seasons/easter/egg_hunt/constants.py b/bot/seasons/easter/egg_hunt/constants.py index d3ab5962..6a673686 100644 --- a/bot/seasons/easter/egg_hunt/constants.py +++ b/bot/seasons/easter/egg_hunt/constants.py @@ -26,10 +26,10 @@ class Roles: class Emoji: - egg_white = bot.get_emoji(569006388437844023) - egg_blurple = bot.get_emoji(569006905972752384) - egg_gold = bot.get_emoji(569079769736675328) - egg_diamond = bot.get_emoji(569109259288182784) + egg_white = bot.get_emoji(569266762428841989) + egg_blurple = bot.get_emoji(569266666094067819) + egg_gold = bot.get_emoji(569266900106739712) + egg_diamond = bot.get_emoji(569266839738384384) class Colours: -- cgit v1.2.3 From 9245565f5fa4d58afbfad4ee7c43609079aa7056 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 09:59:33 +1000 Subject: Add prod role IDs to constants. --- bot/seasons/easter/egg_hunt/constants.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/constants.py b/bot/seasons/easter/egg_hunt/constants.py index 6a673686..592235b2 100644 --- a/bot/seasons/easter/egg_hunt/constants.py +++ b/bot/seasons/easter/egg_hunt/constants.py @@ -21,8 +21,8 @@ class EggHuntSettings: class Roles: - white = GUILD.get_role(569123918795898921) - blurple = GUILD.get_role(569124011406000139) + white = GUILD.get_role(569304397054607363) + blurple = GUILD.get_role(569304472820514816) class Emoji: -- cgit v1.2.3 From 0c9a38d016e08b604adf51dfabd84d9cd3fdd68c Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 10:49:01 +1000 Subject: Name clarification and docstring consistency. --- bot/seasons/easter/egg_hunt/cog.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 758a4da3..251f555f 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -38,6 +38,7 @@ def get_team_role(user: discord.Member): async def assign_team(user: discord.Member): """Helper function to assign a new team role for a member.""" + db = sqlite3.connect(DB_PATH) c = db.cursor() c.execute(f"SELECT team FROM user_scores WHERE user_id = {user.id}") @@ -69,7 +70,7 @@ class EggMessage: self.timeout_task = None @staticmethod - def add_user_score(user_id: int, team: str, score: int): + def add_user_score_sql(user_id: int, team: str, score: int): """Builds the SQL for adding a score to a user in the database.""" return ( @@ -79,7 +80,7 @@ class EggMessage: ) @staticmethod - def add_team_score(team_name: str, score: int): + def add_team_score_sql(team_name: str, score: int): """Builds the SQL for adding a score to a team in the database.""" return f"UPDATE team_scores SET team_score=team_score+{score} WHERE team_id='{team_name}'" @@ -100,7 +101,7 @@ class EggMessage: score = 3 if first_team == TEAM_MAP[first_team] else 2 - c.execute(self.add_user_score(self.first.id, self.teams[first_team], score)) + c.execute(self.add_user_score_sql(self.first.id, self.teams[first_team], score)) team_scores[self.teams[first_team]] += score for user in self.users: @@ -112,12 +113,12 @@ class EggMessage: team_name = self.teams[team] team_scores[team_name] += 1 score = 2 if team == first_team else 1 - c.execute(self.add_user_score(user.id, team_name, score)) + c.execute(self.add_user_score_sql(user.id, team_name, score)) for team_name, score in team_scores.items(): if not score: continue - c.execute(self.add_team_score(team_name, score)) + c.execute(self.add_team_score_sql(team_name, score)) db.commit() db.close() @@ -236,19 +237,19 @@ class SuperEggMessage(EggMessage): if not role: print('issue') user_score = 1 if user != self.first else user_bonus - c.execute(self.add_user_score(user.id, self.teams[role], user_score)) + c.execute(self.add_user_score_sql(user.id, self.teams[role], user_score)) if not team: embed.description = f"{embed.description}\n\nA Tie!\nBoth got {score} points!" - c.execute(self.add_team_score(self.teams[Roles.white], score)) - c.execute(self.add_team_score(self.teams[Roles.blurple], score)) + c.execute(self.add_team_score_sql(self.teams[Roles.white], score)) + c.execute(self.add_team_score_sql(self.teams[Roles.blurple], score)) team_name = "TIE" else: team_name = self.teams[team] embed.description = ( f"{embed.description}\n\nTeam {team_name.capitalize()} won the points!" ) - c.execute(self.add_team_score(team_name, score)) + c.execute(self.add_team_score_sql(team_name, score)) c.execute( "INSERT INTO super_eggs (message_id, egg_type, team, window) " @@ -411,6 +412,7 @@ class EggHunt(commands.Cog): out. They stay around for 5 minutes and the team with the most reacts wins the points. """ + await ctx.invoke(bot.get_command("help"), "hunt") @hunt.command() -- cgit v1.2.3 From 7bb62669d8e60ab8a5375eb1a8c711b4debe8292 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 10:53:09 +1000 Subject: Add return annotations. --- bot/seasons/easter/egg_hunt/cog.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 251f555f..c8a314a1 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -27,7 +27,7 @@ TEAM_MAP = { GUILD = bot.get_guild(Client.guild) -def get_team_role(user: discord.Member): +def get_team_role(user: discord.Member) -> discord.Role: """Helper function to get the team role for a member.""" if Roles.white in user.roles: @@ -36,7 +36,7 @@ def get_team_role(user: discord.Member): return Roles.blurple -async def assign_team(user: discord.Member): +async def assign_team(user: discord.Member) -> discord.Member: """Helper function to assign a new team role for a member.""" db = sqlite3.connect(DB_PATH) @@ -70,7 +70,7 @@ class EggMessage: self.timeout_task = None @staticmethod - def add_user_score_sql(user_id: int, team: str, score: int): + def add_user_score_sql(user_id: int, team: str, score: int) -> str: """Builds the SQL for adding a score to a user in the database.""" return ( @@ -80,7 +80,7 @@ class EggMessage: ) @staticmethod - def add_team_score_sql(team_name: str, score: int): + def add_team_score_sql(team_name: str, score: int) -> str: """Builds the SQL for adding a score to a team in the database.""" return f"UPDATE team_scores SET team_score=team_score+{score} WHERE team_id='{team_name}'" @@ -145,7 +145,7 @@ class EggMessage: if self.first: self.finalise_score() - def is_valid_react(self, reaction: discord.Reaction, user: discord.Member): + def is_valid_react(self, reaction: discord.Reaction, user: discord.Member) -> bool: """Validates a reaction event was meant for this session.""" if user.bot: @@ -298,7 +298,7 @@ class EggHunt(commands.Cog): task.result() @staticmethod - def current_timestamp(): + def current_timestamp() -> int: """Returns a timestamp of the current UTC time.""" return int(datetime.utcnow().replace(tzinfo=timezone.utc).timestamp()) -- cgit v1.2.3 From 00f10109ec8d1808b0e4eeca3a6c6a3ad0889368 Mon Sep 17 00:00:00 2001 From: "S. Co1" Date: Sun, 21 Apr 2019 11:51:45 +1000 Subject: Fix setup docstring. Co-Authored-By: scragly <29337040+scragly@users.noreply.github.com> --- bot/seasons/easter/egg_hunt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/__init__.py b/bot/seasons/easter/egg_hunt/__init__.py index 3f722c9b..43bda223 100644 --- a/bot/seasons/easter/egg_hunt/__init__.py +++ b/bot/seasons/easter/egg_hunt/__init__.py @@ -6,7 +6,7 @@ log = logging.getLogger(__name__) def setup(bot): - """Conversation starters Cog load.""" + """Easter Egg Hunt Cog load.""" bot.add_cog(EggHunt()) log.info("EggHunt cog loaded") -- cgit v1.2.3 From c2776d04222452523c4c2c49e227b02d79a5b5b7 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 12:10:13 +1000 Subject: Fix consistencies, address @fiskenslakt's comments. --- bot/seasons/easter/egg_hunt/cog.py | 19 +++++++++---------- bot/seasons/easter/egg_hunt/constants.py | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index c8a314a1..7c6214d2 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -218,7 +218,7 @@ class SuperEggMessage(EggMessage): if white == blurple: log.debug("Tied SuperEgg Result.") team = None - score = score / 2 + score /= 2 elif white > blurple: team = Roles.white else: @@ -235,7 +235,7 @@ class SuperEggMessage(EggMessage): continue role = get_team_role(user) if not role: - print('issue') + print("issue") user_score = 1 if user != self.first else user_bonus c.execute(self.add_user_score_sql(user.id, self.teams[role], user_score)) @@ -284,7 +284,7 @@ class SuperEggMessage(EggMessage): class EggHunt(commands.Cog): - """Easter Egg Hunt Event""" + """Easter Egg Hunt Event.""" def __init__(self): self.event_channel = GUILD.get_channel(Channels.seasonalbot_chat) @@ -310,7 +310,7 @@ class EggHunt(commands.Cog): now = self.current_timestamp() if now > EggHuntSettings.end_time: - log.debug(f"Hunt ended. Ending task.") + log.debug("Hunt ended. Ending task.") break if now < EggHuntSettings.start_time: @@ -340,7 +340,6 @@ class EggHunt(commands.Cog): db.close() if not count: - log.debug(f"test") next_drop = random.randrange(now, next_window) log.debug(f"Sleeping until next super egg drop: {next_drop}.") await asyncio.sleep(next_drop) @@ -362,11 +361,11 @@ class EggHunt(commands.Cog): colour=colour ) embed.set_thumbnail(url=egg.url) - embed.set_footer(text=f"Finishing in 5 minutes.") + embed.set_footer(text="Finishing in 5 minutes.") msg = await self.event_channel.send(embed=embed) await SuperEggMessage(msg, egg, current_window).start() - log.debug(f"Sleeping until next window.") + log.debug("Sleeping until next window.") next_loop = max(next_window - self.current_timestamp(), 0) await asyncio.sleep(next_loop) @@ -452,11 +451,11 @@ class EggHunt(commands.Cog): team = team.capitalize() score = f"{score}pts" output.append(f"{rank:>2}. {score:>{scr_len+3}} - {user} ({team})") - user_board = '\n'.join(output) + user_board = "\n".join(output) output = [] for team, score in team_result: output.append(f"{team:<7}: {score}") - team_board = '\n'.join(output) + team_board = "\n".join(output) embed = discord.Embed( title="Egg Hunt Leaderboards", description=f"**Teams**\n```\n{team_board}\n```\n" @@ -505,7 +504,7 @@ class EggHunt(commands.Cog): c = db.cursor() c.execute("DELETE FROM super_eggs;") c.execute("DELETE FROM user_scores;") - c.execute(f"UPDATE team_scores SET team_score=0") + c.execute("UPDATE team_scores SET team_score=0") db.commit() db.close() await ctx.send("Database successfully cleared.") diff --git a/bot/seasons/easter/egg_hunt/constants.py b/bot/seasons/easter/egg_hunt/constants.py index 592235b2..3f3c3bbe 100644 --- a/bot/seasons/easter/egg_hunt/constants.py +++ b/bot/seasons/easter/egg_hunt/constants.py @@ -10,7 +10,7 @@ GUILD = bot.get_guild(Client.guild) class EggHuntSettings: start_time = int(os.environ["HUNT_START"]) - end_time = start_time + 172800 + end_time = start_time + 172800 # 48 hrs later windows = os.environ.get("HUNT_WINDOWS").split(',') or [] allowed_channels = [ Channels.seasonalbot_chat, -- cgit v1.2.3 From 81e36629da6bd664d35f74f2792bb28d45760acd Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 20:02:00 +1000 Subject: Make team assignment deterministic for event distribution. --- bot/seasons/easter/egg_hunt/cog.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 7c6214d2..7d592bba 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -44,14 +44,18 @@ async def assign_team(user: discord.Member) -> discord.Member: c.execute(f"SELECT team FROM user_scores WHERE user_id = {user.id}") result = c.fetchone() if not result: - new_team = random.choice([Roles.white, Roles.blurple]) - log.debug(f"Assigned role {new_team} to {user}.") + c.execute( + "SELECT team, COUNT(*) AS count FROM user_scores " + "GROUP BY team ORDER BY count ASC LIMIT 1;" + ) + result = c.fetchone()[0] + + if result[0] == "WHITE": + new_team = Roles.white else: - if result[0] == "WHITE": - new_team = Roles.white - else: - new_team = Roles.blurple - log.debug(f"Restored role {new_team} to {user}.") + new_team = Roles.blurple + + log.debug(f"Assigned role {new_team} to {user}.") await user.add_roles(new_team) return GUILD.get_member(user.id) -- cgit v1.2.3 From 079d107c35ef11a3d55ff9a88aa8e5dc34f99dbb Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 20:03:00 +1000 Subject: Ensure super egg drops. --- bot/seasons/easter/egg_hunt/cog.py | 41 +++++++++++++++++++------------- bot/seasons/easter/egg_hunt/constants.py | 2 +- 2 files changed, 25 insertions(+), 18 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 7d592bba..56d4e71d 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -295,11 +295,11 @@ class EggHunt(commands.Cog): self.task = asyncio.create_task(self.super_egg()) self.task.add_done_callback(self.task_cleanup) - @staticmethod - def task_cleanup(task): - """Returns a task result. Used as a done callback to show raised exceptions.""" + def task_cleanup(self, task): + """Returns task result and restarts. Used as a done callback to show raised exceptions.""" task.result() + self.task = asyncio.create_task(self.super_egg()) @staticmethod def current_timestamp() -> int: @@ -323,26 +323,33 @@ class EggHunt(commands.Cog): await asyncio.sleep(remaining) log.debug(f"Hunt started.") - current_window = EggHuntSettings.start_time - next_window = 0 - for window in EggHuntSettings.windows: - window = int(window) - if window < now: - current_window = window + + db = sqlite3.connect(DB_PATH) + c = db.cursor() + + current_window = None + next_window = None + windows = EggHuntSettings.windows.copy() + windows.insert(0, EggHuntSettings.start_time) + for i, window in enumerate(windows): + c.execute(f"SELECT COUNT(*) FROM super_eggs WHERE window={window}") + alread_dropped = c.fetchone()[0] + if alread_dropped: continue - if not next_window: - next_window = window else: + current_window = window + next_window = window[i+1] break - log.debug(f"Current Window: {current_window}. Next Window {next_window}") - - db = sqlite3.connect(DB_PATH) - c = db.cursor() - c.execute(f"SELECT COUNT(*) FROM super_eggs WHERE window={current_window}") count = c.fetchone()[0] db.close() + if not current_window: + log.debug(f"Suitable window not found.") + break + + log.debug(f"Current Window: {current_window}. Next Window {next_window}") + if not count: next_drop = random.randrange(now, next_window) log.debug(f"Sleeping until next super egg drop: {next_drop}.") @@ -370,7 +377,7 @@ class EggHunt(commands.Cog): await SuperEggMessage(msg, egg, current_window).start() log.debug("Sleeping until next window.") - next_loop = max(next_window - self.current_timestamp(), 0) + next_loop = max(next_window - self.current_timestamp(), 60*60) await asyncio.sleep(next_loop) @commands.Cog.listener() diff --git a/bot/seasons/easter/egg_hunt/constants.py b/bot/seasons/easter/egg_hunt/constants.py index 3f3c3bbe..c7d9818b 100644 --- a/bot/seasons/easter/egg_hunt/constants.py +++ b/bot/seasons/easter/egg_hunt/constants.py @@ -11,7 +11,7 @@ GUILD = bot.get_guild(Client.guild) class EggHuntSettings: start_time = int(os.environ["HUNT_START"]) end_time = start_time + 172800 # 48 hrs later - windows = os.environ.get("HUNT_WINDOWS").split(',') or [] + windows = [int(w) for w in os.environ.get("HUNT_WINDOWS").split(',')] or [] allowed_channels = [ Channels.seasonalbot_chat, Channels.off_topic_0, -- cgit v1.2.3 From 64d555ddee02481cac6fee07262d8c7d789fa5fd Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 20:03:18 +1000 Subject: Fix rank command. --- bot/seasons/easter/egg_hunt/cog.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 56d4e71d..5120497b 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -482,8 +482,9 @@ class EggHunt(commands.Cog): db = sqlite3.connect(DB_PATH) c = db.cursor() c.execute( - f"SELECT RANK() OVER(ORDER BY score DESC) AS rank FROM user_scores " - f"WHERE user_id={member.id}" + "SELECT rank FROM " + "(SELECT RANK() OVER(ORDER BY score DESC) AS rank, user_id FROM user_scores)" + f"WHERE user_id = {member.id};" ) result = c.fetchone() db.close() -- cgit v1.2.3 From 925930c9010dc59f52bb78eda0640e1a3e21626c Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 20:03:52 +1000 Subject: Close db after role assignment. --- bot/seasons/easter/egg_hunt/cog.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 5120497b..30d20f30 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -55,6 +55,8 @@ async def assign_team(user: discord.Member) -> discord.Member: else: new_team = Roles.blurple + db.close() + log.debug(f"Assigned role {new_team} to {user}.") await user.add_roles(new_team) -- cgit v1.2.3 From 674bfac2cf0be2435e1d75ebf6688bd904e75c67 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 20:10:26 +1000 Subject: Account for leaderboard being invoked before scores exist. --- bot/seasons/easter/egg_hunt/cog.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 30d20f30..68cac717 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -458,20 +458,26 @@ class EggHunt(commands.Cog): team_result = c.fetchall() db.close() output = [] - scr_len = max(len(str(r[2])) for r in user_result) - for user_id, team, score, rank in user_result: - user = GUILD.get_member(user_id) or user_id - team = team.capitalize() - score = f"{score}pts" - output.append(f"{rank:>2}. {score:>{scr_len+3}} - {user} ({team})") - user_board = "\n".join(output) - output = [] - for team, score in team_result: - output.append(f"{team:<7}: {score}") - team_board = "\n".join(output) + if user_result: + scr_len = max(len(str(r[2])) for r in user_result) + for user_id, team, score, rank in user_result: + user = GUILD.get_member(user_id) or user_id + team = team.capitalize() + score = f"{score}pts" + output.append(f"{rank:>2}. {score:>{scr_len+3}} - {user} ({team})") + user_board = "\n".join(output) + else: + user_board = "No entries." + if team_result: + output = [] + for team, score in team_result: + output.append(f"{team:<7}: {score}") + team_board = "\n".join(output) + else: + team_board = "No entries." embed = discord.Embed( title="Egg Hunt Leaderboards", - description=f"**Teams**\n```\n{team_board}\n```\n" + description=f"**Team Scores**\n```\n{team_board}\n```\n" f"**Top 10 Members**\n```\n{user_board}\n```" ) await ctx.send(embed=embed) -- cgit v1.2.3 From 9629cd0347cd2cd52fcf42a99dad505d41bde8a8 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 21:49:00 +1000 Subject: Ignore the punished. --- bot/seasons/easter/egg_hunt/cog.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 68cac717..704c3173 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -26,6 +26,8 @@ TEAM_MAP = { GUILD = bot.get_guild(Client.guild) +MUTED = GUILD.get_role(MainRoles.muted) + def get_team_role(user: discord.Member) -> discord.Role: """Helper function to get the team role for a member.""" @@ -160,6 +162,11 @@ class EggMessage: return False if reaction.emoji != self.egg: return False + + # ignore the pushished + if MUTED in user.roles: + return False + return True async def collect_reacts(self, reaction: discord.Reaction, user: discord.Member): -- cgit v1.2.3 From b40064d2eeea96e58d67e260c673e5ea98e77e2d Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 21 Apr 2019 22:07:16 +1000 Subject: Fix typos. Co-Authored-By: scragly <29337040+scragly@users.noreply.github.com> --- bot/seasons/easter/egg_hunt/cog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 704c3173..eb81744c 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -342,8 +342,8 @@ class EggHunt(commands.Cog): windows.insert(0, EggHuntSettings.start_time) for i, window in enumerate(windows): c.execute(f"SELECT COUNT(*) FROM super_eggs WHERE window={window}") - alread_dropped = c.fetchone()[0] - if alread_dropped: + already_dropped = c.fetchone()[0] + if already_dropped: continue else: current_window = window -- cgit v1.2.3 From 903e12b25a839dd259b284f7766728e14cd53be0 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 22:26:43 +1000 Subject: Cleanup alignment code. --- bot/seasons/easter/egg_hunt/cog.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index eb81744c..6ea332e4 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -466,12 +466,18 @@ class EggHunt(commands.Cog): db.close() output = [] if user_result: - scr_len = max(len(str(r[2])) for r in user_result) + # Get the alignment needed for the score + score_lengths = [] + for result in user_result: + length = len(str(result[2])) + score_lengths.append(length) + + score_length = max(score_lengths) for user_id, team, score, rank in user_result: user = GUILD.get_member(user_id) or user_id team = team.capitalize() score = f"{score}pts" - output.append(f"{rank:>2}. {score:>{scr_len+3}} - {user} ({team})") + output.append(f"{rank:>2}. {score:>{score_length+3}} - {user} ({team})") user_board = "\n".join(output) else: user_board = "No entries." -- cgit v1.2.3 From e8f4b0927ec9db5fbf6ec35a3340eeb013c72873 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Sun, 21 Apr 2019 23:20:24 +1000 Subject: Fix super eggs and ctx.invoke usage. --- bot/seasons/easter/egg_hunt/cog.py | 51 ++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 13 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 6ea332e4..6ebc016d 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -50,7 +50,8 @@ async def assign_team(user: discord.Member) -> discord.Member: "SELECT team, COUNT(*) AS count FROM user_scores " "GROUP BY team ORDER BY count ASC LIMIT 1;" ) - result = c.fetchone()[0] + result = c.fetchone() + result = result[0] if result else "WHITE" if result[0] == "WHITE": new_team = Roles.white @@ -195,6 +196,15 @@ class EggMessage: with contextlib.suppress(discord.Forbidden): await self.message.add_reaction(self.egg) self.timeout_task = asyncio.create_task(self.start_timeout(300)) + while True: + if not self.timeout_task: + break + if not self.timeout_task.done(): + await self.timeout_task + else: + # make sure any exceptions raise if necessary + self.timeout_task.result() + break class SuperEggMessage(EggMessage): @@ -207,7 +217,7 @@ class SuperEggMessage(EggMessage): async def finalise_score(self): """Sums and actions scoring for this super egg session.""" try: - message = await self.message.channel.get_message(self.message.id) + message = await self.message.channel.fetch_message(self.message.id) except discord.NotFound: return @@ -301,6 +311,7 @@ class EggHunt(commands.Cog): def __init__(self): self.event_channel = GUILD.get_channel(Channels.seasonalbot_chat) + self.super_egg_buffer = 60*60 self.task = asyncio.create_task(self.super_egg()) self.task.add_done_callback(self.task_cleanup) @@ -343,26 +354,40 @@ class EggHunt(commands.Cog): for i, window in enumerate(windows): c.execute(f"SELECT COUNT(*) FROM super_eggs WHERE window={window}") already_dropped = c.fetchone()[0] + if already_dropped: + log.debug(f"Window {window} already dropped, checking next one.") continue - else: - current_window = window - next_window = window[i+1] - break - count = c.fetchone()[0] + if now < window: + log.debug("Drop windows up to date, sleeping until next one.") + await asyncio.sleep(window-now) + now = self.current_timestamp() + + current_window = window + next_window = windows[i+1] + break + + count = c.fetchone() db.close() if not current_window: - log.debug(f"Suitable window not found.") + log.debug("No drop windows left, ending task.") break log.debug(f"Current Window: {current_window}. Next Window {next_window}") if not count: - next_drop = random.randrange(now, next_window) - log.debug(f"Sleeping until next super egg drop: {next_drop}.") - await asyncio.sleep(next_drop) + if next_window < now: + log.debug("An Egg Drop Window was missed, dropping one now.") + next_drop = 0 + else: + next_drop = random.randrange(now, next_window) + + if next_drop: + log.debug(f"Sleeping until next super egg drop: {next_drop}.") + await asyncio.sleep(next_drop) + if random.randrange(10) <= 2: egg = Emoji.egg_diamond egg_type = "Diamond" @@ -386,7 +411,7 @@ class EggHunt(commands.Cog): await SuperEggMessage(msg, egg, current_window).start() log.debug("Sleeping until next window.") - next_loop = max(next_window - self.current_timestamp(), 60*60) + next_loop = max(next_window - self.current_timestamp(), self.super_egg_buffer) await asyncio.sleep(next_loop) @commands.Cog.listener() @@ -432,7 +457,7 @@ class EggHunt(commands.Cog): wins the points. """ - await ctx.invoke(bot.get_command("help"), "hunt") + await ctx.invoke(bot.get_command("help"), command="hunt") @hunt.command() async def countdown(self, ctx): -- cgit v1.2.3 From 29660cd339438ff20a0ef79e09b917ec409e7e98 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Mon, 22 Apr 2019 00:18:05 +1000 Subject: Improve user store efficiency. --- bot/seasons/easter/egg_hunt/cog.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 6ebc016d..20b6f1d9 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -73,7 +73,7 @@ class EggMessage: self.message = message self.egg = egg self.first = None - self.users = [] + self.users = set() self.teams = {Roles.white: "WHITE", Roles.blurple: "BLURPLE"} self.new_team_assignments = {} self.timeout_task = None @@ -186,7 +186,8 @@ class EggMessage: self.first = user await self.start_timeout() else: - self.users.append(user) + if user != self.first: + self.users.add(user) async def start(self): """Starts the egg drop session.""" -- cgit v1.2.3 From 7cfb3f41c997c335ba6dd850fc7ebb90a9d0d6c7 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Mon, 22 Apr 2019 01:05:25 +1000 Subject: Log reactions for later checking of behaviour analysis and anti-cheat checks. --- bot/seasons/easter/egg_hunt/cog.py | 77 +++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 5 deletions(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 20b6f1d9..85615549 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -313,9 +313,59 @@ class EggHunt(commands.Cog): def __init__(self): self.event_channel = GUILD.get_channel(Channels.seasonalbot_chat) self.super_egg_buffer = 60*60 + self.tables = { + "super_eggs": ( + "CREATE TABLE super_eggs (" + "message_id INTEGER NOT NULL " + " CONSTRAINT super_eggs_pk PRIMARY KEY, " + "egg_type TEXT NOT NULL, " + "team TEXT NOT NULL, " + "window INTEGER);" + ), + "team_scores": ( + "CREATE TABLE team_scores (" + "team_id TEXT, " + "team_score INTEGER DEFAULT 0);" + ), + "user_scores": ( + "CREATE TABLE user_scores(" + "user_id INTEGER NOT NULL " + " CONSTRAINT user_scores_pk PRIMARY KEY, " + "team TEXT NOT NULL, " + "score INTEGER DEFAULT 0 NOT NULL);" + ), + "react_logs": ( + "CREATE TABLE react_logs(" + "member_id INTEGER NOT NULL, " + "message_id INTEGER NOT NULL, " + "reaction_id TEXT NOT NULL, " + "react_timestamp REAL NOT NULL);" + ) + } + self.prepare_db() self.task = asyncio.create_task(self.super_egg()) self.task.add_done_callback(self.task_cleanup) + def prepare_db(self): + db = sqlite3.connect(DB_PATH) + c = db.cursor() + + exists_sql = "SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}';" + + missing_tables = [] + for table in self.tables: + c.execute(exists_sql.format(table_name=table)) + result = c.fetchone() + if not result: + missing_tables.append(table) + + for table in missing_tables: + log.info(f"Table {table} is missing, building new one.") + c.execute(self.tables[table]) + + db.commit() + db.close() + def task_cleanup(self, task): """Returns task result and restarts. Used as a done callback to show raised exceptions.""" @@ -323,16 +373,16 @@ class EggHunt(commands.Cog): self.task = asyncio.create_task(self.super_egg()) @staticmethod - def current_timestamp() -> int: + def current_timestamp() -> float: """Returns a timestamp of the current UTC time.""" - return int(datetime.utcnow().replace(tzinfo=timezone.utc).timestamp()) + return datetime.utcnow().replace(tzinfo=timezone.utc).timestamp() async def super_egg(self): """Manages the timing of super egg drops.""" while True: - now = self.current_timestamp() + now = int(self.current_timestamp()) if now > EggHuntSettings.end_time: log.debug("Hunt ended. Ending task.") @@ -363,7 +413,7 @@ class EggHunt(commands.Cog): if now < window: log.debug("Drop windows up to date, sleeping until next one.") await asyncio.sleep(window-now) - now = self.current_timestamp() + now = int(self.current_timestamp()) current_window = window next_window = windows[i+1] @@ -412,9 +462,26 @@ class EggHunt(commands.Cog): await SuperEggMessage(msg, egg, current_window).start() log.debug("Sleeping until next window.") - next_loop = max(next_window - self.current_timestamp(), self.super_egg_buffer) + next_loop = max(next_window - int(self.current_timestamp()), self.super_egg_buffer) await asyncio.sleep(next_loop) + @commands.Cog.listener() + async def on_raw_reaction_add(self, payload): + """Reaction event listener for reaction logging for later anti-cheat analysis.""" + + if payload.channel_id not in EggHuntSettings.allowed_channels: + return + + now = self.current_timestamp() + db = sqlite3.connect(DB_PATH) + c = db.cursor() + c.execute( + "INSERT INTO react_logs(member_id, message_id, reaction_id, react_timestamp) " + f"VALUES({payload.user_id}, {payload.message_id}, '{payload.emoji}', {now})" + ) + db.commit() + db.close() + @commands.Cog.listener() async def on_message(self, message): """Message event listener for random egg drops.""" -- cgit v1.2.3 From 1371511bb78e4ac8de6c0aceed9a977dd80b6306 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Mon, 22 Apr 2019 01:11:15 +1000 Subject: Add prepare_db docstring. --- bot/seasons/easter/egg_hunt/cog.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index 85615549..bbbecd7f 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -347,6 +347,8 @@ class EggHunt(commands.Cog): self.task.add_done_callback(self.task_cleanup) def prepare_db(self): + """Ensures database tables all exist and if not, creates them.""" + db = sqlite3.connect(DB_PATH) c = db.cursor() -- cgit v1.2.3 From e2876cccfa051b9c66e9292edeb9251ff99ad147 Mon Sep 17 00:00:00 2001 From: Scragly <29337040+scragly@users.noreply.github.com> Date: Mon, 22 Apr 2019 01:41:17 +1000 Subject: change counter to minute --- bot/seasons/easter/egg_hunt/cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot') diff --git a/bot/seasons/easter/egg_hunt/cog.py b/bot/seasons/easter/egg_hunt/cog.py index bbbecd7f..c9e2dc18 100644 --- a/bot/seasons/easter/egg_hunt/cog.py +++ b/bot/seasons/easter/egg_hunt/cog.py @@ -295,7 +295,7 @@ class SuperEggMessage(EggMessage): return count = 4 for _ in range(count): - await asyncio.sleep(1) + await asyncio.sleep(60) embed = self.message.embeds[0] embed.set_footer(text=f"Finishing in {count} minutes.") try: -- cgit v1.2.3 From 1049ee3e51ca4dcc3faa6ab02f378fadf5ec231f Mon Sep 17 00:00:00 2001 From: Suhail Date: Tue, 23 Apr 2019 18:28:04 +0100 Subject: Constants cleaning --- bot/constants.py | 3 --- bot/seasons/christmas/adventofcode.py | 4 ++-- bot/seasons/halloween/candy_collection.py | 10 +++++----- bot/seasons/halloween/halloween_facts.py | 4 ++-- bot/seasons/valentines/be_my_valentine.py | 4 ++-- 5 files changed, 11 insertions(+), 14 deletions(-) (limited to 'bot') diff --git a/bot/constants.py b/bot/constants.py index d362c90e..52c76cda 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -18,7 +18,6 @@ class AdventOfCode: leaderboard_join_code = str(environ.get("AOC_JOIN_CODE", None)) leaderboard_max_displayed_members = 10 year = 2018 - channel_id = int(environ.get("AOC_CHANNEL_ID", 517745814039166986)) role_id = int(environ.get("AOC_ROLE_ID", 518565788744024082)) @@ -84,12 +83,10 @@ class Emojis: class Lovefest: - channel_id = int(environ.get("LOVEFEST_CHANNEL_ID", 542272993192050698)) role_id = int(environ.get("LOVEFEST_ROLE_ID", 542431903886606399)) class Hacktoberfest(NamedTuple): - channel_id = 498804484324196362 voice_id = 514420006474219521 diff --git a/bot/seasons/christmas/adventofcode.py b/bot/seasons/christmas/adventofcode.py index 5d05dce6..32858673 100644 --- a/bot/seasons/christmas/adventofcode.py +++ b/bot/seasons/christmas/adventofcode.py @@ -13,7 +13,7 @@ from bs4 import BeautifulSoup from discord.ext import commands from pytz import timezone -from bot.constants import AdventOfCode as AocConfig, Colours, Emojis, Tokens +from bot.constants import AdventOfCode as AocConfig, Channels, Colours, Emojis, Tokens log = logging.getLogger(__name__) @@ -88,7 +88,7 @@ async def day_countdown(bot: commands.Bot): await asyncio.sleep(time_left.seconds) - channel = bot.get_channel(AocConfig.channel_id) + channel = bot.get_channel(Channels.seasonalbot_chat) if not channel: log.error("Could not find the AoC channel to send notification in") diff --git a/bot/seasons/halloween/candy_collection.py b/bot/seasons/halloween/candy_collection.py index 70648e64..f8ab4c60 100644 --- a/bot/seasons/halloween/candy_collection.py +++ b/bot/seasons/halloween/candy_collection.py @@ -7,7 +7,7 @@ import random import discord from discord.ext import commands -from bot.constants import Hacktoberfest +from bot.constants import Channels log = logging.getLogger(__name__) @@ -41,7 +41,7 @@ class CandyCollection(commands.Cog): if message.author.bot: return # ensure it's hacktober channel - if message.channel.id != Hacktoberfest.channel_id: + if message.channel.id != Channels.seasonalbot_chat: return # do random check for skull first as it has the lower chance @@ -65,7 +65,7 @@ class CandyCollection(commands.Cog): return # check to ensure it is in correct channel - if message.channel.id != Hacktoberfest.channel_id: + if message.channel.id != Channels.seasonalbot_chat: return # if its not a candy or skull, and it is one of 10 most recent messages, @@ -127,7 +127,7 @@ class CandyCollection(commands.Cog): ten_recent = [] recent_msg = max(message.id for message in self.bot._connection._messages - if message.channel.id == Hacktoberfest.channel_id) + if message.channel.id == Channels.seasonalbot_chat) channel = await self.hacktober_channel() ten_recent.append(recent_msg.id) @@ -159,7 +159,7 @@ class CandyCollection(commands.Cog): async def hacktober_channel(self): """Get #hacktoberbot channel from its ID.""" - return self.bot.get_channel(id=Hacktoberfest.channel_id) + return self.bot.get_channel(id=Channels.seasonalbot_chat) async def remove_reactions(self, reaction): """Remove all candy/skull reactions.""" diff --git a/bot/seasons/halloween/halloween_facts.py b/bot/seasons/halloween/halloween_facts.py index ee90dbd3..ad9aa716 100644 --- a/bot/seasons/halloween/halloween_facts.py +++ b/bot/seasons/halloween/halloween_facts.py @@ -7,7 +7,7 @@ from pathlib import Path import discord from discord.ext import commands -from bot.constants import Hacktoberfest +from bot.constants import Channels log = logging.getLogger(__name__) @@ -40,7 +40,7 @@ class HalloweenFacts(commands.Cog): async def on_ready(self): """Get event Channel object and initialize fact task loop.""" - self.channel = self.bot.get_channel(Hacktoberfest.channel_id) + self.channel = self.bot.get_channel(Channels.seasonalbot_chat) self.bot.loop.create_task(self._fact_publisher_task()) def random_fact(self): diff --git a/bot/seasons/valentines/be_my_valentine.py b/bot/seasons/valentines/be_my_valentine.py index 55c4adb1..19788577 100644 --- a/bot/seasons/valentines/be_my_valentine.py +++ b/bot/seasons/valentines/be_my_valentine.py @@ -8,7 +8,7 @@ import discord from discord.ext import commands from discord.ext.commands.cooldowns import BucketType -from bot.constants import Client, Colours, Lovefest +from bot.constants import Channels, Client, Colours, Lovefest log = logging.getLogger(__name__) @@ -99,7 +99,7 @@ class BeMyValentine(commands.Cog): emoji_1, emoji_2 = self.random_emoji() lovefest_role = discord.utils.get(ctx.guild.roles, id=Lovefest.role_id) - channel = self.bot.get_channel(Lovefest.channel_id) + channel = self.bot.get_channel(Channels.seasonalbot_chat) valentine, title = self.valentine_check(valentine_type) if user is None: -- cgit v1.2.3