diff options
-rw-r--r-- | bot/seasons/halloween/__init__.py | 2 | ||||
-rw-r--r-- | bot/seasons/halloween/candy_collection.py | 48 | ||||
-rw-r--r-- | bot/seasons/halloween/hacktoberstats.py | 63 | ||||
-rw-r--r-- | bot/seasons/halloween/halloween_facts.py | 17 | ||||
-rw-r--r-- | bot/seasons/halloween/halloweenify.py | 11 | ||||
-rw-r--r-- | bot/seasons/halloween/monstersurvey.py | 26 | ||||
-rw-r--r-- | bot/seasons/halloween/scarymovie.py | 19 | ||||
-rw-r--r-- | bot/seasons/halloween/spookyavatar.py | 17 | ||||
-rw-r--r-- | bot/seasons/halloween/spookygif.py | 10 | ||||
-rw-r--r-- | bot/seasons/halloween/spookyreact.py | 11 | ||||
-rw-r--r-- | bot/seasons/halloween/spookysound.py | 16 | ||||
-rw-r--r-- | bot/utils/halloween/spookifications.py | 17 |
12 files changed, 138 insertions, 119 deletions
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__) |