diff options
Diffstat (limited to 'bot/exts/easter')
| -rw-r--r-- | bot/exts/easter/__init__.py | 0 | ||||
| -rw-r--r-- | bot/exts/easter/april_fools_vids.py | 38 | ||||
| -rw-r--r-- | bot/exts/easter/avatar_easterifier.py | 128 | ||||
| -rw-r--r-- | bot/exts/easter/bunny_name_generator.py | 92 | ||||
| -rw-r--r-- | bot/exts/easter/conversationstarters.py | 28 | ||||
| -rw-r--r-- | bot/exts/easter/easter_riddle.py | 100 | ||||
| -rw-r--r-- | bot/exts/easter/egg_decorating.py | 118 | ||||
| -rw-r--r-- | bot/exts/easter/egg_facts.py | 60 | ||||
| -rw-r--r-- | bot/exts/easter/egghead_quiz.py | 119 | ||||
| -rw-r--r-- | bot/exts/easter/traditions.py | 30 |
10 files changed, 713 insertions, 0 deletions
diff --git a/bot/exts/easter/__init__.py b/bot/exts/easter/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/bot/exts/easter/__init__.py diff --git a/bot/exts/easter/april_fools_vids.py b/bot/exts/easter/april_fools_vids.py new file mode 100644 index 00000000..06108f02 --- /dev/null +++ b/bot/exts/easter/april_fools_vids.py @@ -0,0 +1,38 @@ +import logging +import random +from json import load +from pathlib import Path + +from discord.ext import commands + +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: commands.Bot): + self.bot = bot + self.yt_vids = self.load_json() + self.youtubers = ['google'] # will add more in future + + @staticmethod + def load_json() -> dict: + """A function to load JSON data.""" + p = Path('bot/resources/easter/april_fools_vids.json') + with p.open() as json_file: + all_vids = load(json_file) + return all_vids + + @commands.command(name='fool') + async def april_fools(self, ctx: commands.Context) -> None: + """Get a random April Fools' video from Youtube.""" + random_youtuber = random.choice(self.youtubers) + category = self.yt_vids[random_youtuber] + random_vid = random.choice(category) + await ctx.send(f"Check out this April Fools' video by {random_youtuber}.\n\n{random_vid['link']}") + + +def setup(bot: commands.Bot) -> None: + """April Fools' Cog load.""" + bot.add_cog(AprilFoolVideos(bot)) diff --git a/bot/exts/easter/avatar_easterifier.py b/bot/exts/easter/avatar_easterifier.py new file mode 100644 index 00000000..8e8a3500 --- /dev/null +++ b/bot/exts/easter/avatar_easterifier.py @@ -0,0 +1,128 @@ +import asyncio +import logging +from io import BytesIO +from pathlib import Path +from typing import Tuple, Union + +import discord +from PIL import Image +from PIL.ImageOps import posterize +from discord.ext import commands + +log = logging.getLogger(__name__) + +COLOURS = [ + (255, 247, 0), (255, 255, 224), (0, 255, 127), (189, 252, 201), (255, 192, 203), + (255, 160, 122), (181, 115, 220), (221, 160, 221), (200, 162, 200), (238, 130, 238), + (135, 206, 235), (0, 204, 204), (64, 224, 208) +] # Pastel colours - Easter-like + + +class AvatarEasterifier(commands.Cog): + """Put an Easter spin on your avatar or image!""" + + def __init__(self, bot: commands.Bot): + self.bot = bot + + @staticmethod + def closest(x: Tuple[int, int, int]) -> Tuple[int, int, int]: + """ + Finds the closest easter colour to a given pixel. + + Returns a merge between the original colour and the closest colour + """ + r1, g1, b1 = x + + def distance(point: Tuple[int, int, int]) -> Tuple[int, int, int]: + """Finds the difference between a pastel colour and the original pixel colour.""" + r2, g2, b2 = point + return ((r1 - r2)**2 + (g1 - g2)**2 + (b1 - b2)**2) + + closest_colours = sorted(COLOURS, key=lambda point: distance(point)) + r2, g2, b2 = closest_colours[0] + r = (r1 + r2) // 2 + g = (g1 + g2) // 2 + b = (b1 + b2) // 2 + + return (r, g, b) + + @commands.command(pass_context=True, aliases=["easterify"]) + async def avatareasterify(self, ctx: commands.Context, *colours: Union[discord.Colour, str]) -> None: + """ + This "Easterifies" the user's avatar. + + Given colours will produce a personalised egg in the corner, similar to the egg_decorate command. + If colours are not given, a nice little chocolate bunny will sit in the corner. + Colours are split by spaces, unless you wrap the colour name in double quotes. + Discord colour names, HTML colour names, XKCD colour names and hex values are accepted. + """ + async def send(*args, **kwargs) -> str: + """ + This replaces the original ctx.send. + + When invoking the egg decorating command, the egg itself doesn't print to to the channel. + Returns the message content so that if any errors occur, the error message can be output. + """ + if args: + return args[0] + + async with ctx.typing(): + + # Grabs image of avatar + image_bytes = await ctx.author.avatar_url_as(size=256).read() + + old = Image.open(BytesIO(image_bytes)) + old = old.convert("RGBA") + + # Grabs alpha channel since posterize can't be used with an RGBA image. + alpha = old.getchannel("A").getdata() + old = old.convert("RGB") + old = posterize(old, 6) + + data = old.getdata() + setted_data = set(data) + new_d = {} + + for x in setted_data: + new_d[x] = self.closest(x) + await asyncio.sleep(0) # Ensures discord doesn't break in the background. + new_data = [(*new_d[x], alpha[i]) if x in new_d else x for i, x in enumerate(data)] + + im = Image.new("RGBA", old.size) + im.putdata(new_data) + + if colours: + send_message = ctx.send + ctx.send = send # Assigns ctx.send to a fake send + egg = await ctx.invoke(self.bot.get_command("eggdecorate"), *colours) + if isinstance(egg, str): # When an error message occurs in eggdecorate. + return await send_message(egg) + + ratio = 64 / egg.height + egg = egg.resize((round(egg.width * ratio), round(egg.height * ratio))) + egg = egg.convert("RGBA") + im.alpha_composite(egg, (im.width - egg.width, (im.height - egg.height)//2)) # Right centre. + ctx.send = send_message # Reassigns ctx.send + else: + bunny = Image.open(Path("bot/resources/easter/chocolate_bunny.png")) + im.alpha_composite(bunny, (im.width - bunny.width, (im.height - bunny.height)//2)) # Right centre. + + bufferedio = BytesIO() + im.save(bufferedio, format="PNG") + + bufferedio.seek(0) + + file = discord.File(bufferedio, filename="easterified_avatar.png") # Creates file to be used in embed + embed = discord.Embed( + name="Your Lovely Easterified Avatar", + description="Here is your lovely avatar, all bright and colourful\nwith Easter pastel colours. Enjoy :D" + ) + embed.set_image(url="attachment://easterified_avatar.png") + embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url) + + await ctx.send(file=file, embed=embed) + + +def setup(bot: commands.Bot) -> None: + """Avatar Easterifier Cog load.""" + bot.add_cog(AvatarEasterifier(bot)) diff --git a/bot/exts/easter/bunny_name_generator.py b/bot/exts/easter/bunny_name_generator.py new file mode 100644 index 00000000..3ecf9be9 --- /dev/null +++ b/bot/exts/easter/bunny_name_generator.py @@ -0,0 +1,92 @@ +import json +import logging +import random +import re +from pathlib import Path +from typing import List, Union + +from discord.ext import commands + +log = logging.getLogger(__name__) + +with Path("bot/resources/easter/bunny_names.json").open("r", encoding="utf8") as f: + BUNNY_NAMES = json.load(f) + + +class BunnyNameGenerator(commands.Cog): + """Generate a random bunny name, or bunnify your Discord username!""" + + def __init__(self, bot: commands.Bot): + self.bot = bot + + def find_separators(self, displayname: str) -> Union[List[str], None]: + """Check if Discord name contains spaces so we can bunnify an individual word in the name.""" + new_name = re.split(r'[_.\s]', displayname) + if displayname not in new_name: + return new_name + + def find_vowels(self, displayname: str) -> str: + """ + Finds vowels in the user's display name. + + If the Discord name contains a vowel and the letter y, it will match one or more of these patterns. + + Only the most recently matched pattern will apply the changes. + """ + expressions = [ + (r'a.+y', 'patchy'), + (r'e.+y', 'ears'), + (r'i.+y', 'ditsy'), + (r'o.+y', 'oofy'), + (r'u.+y', 'uffy'), + ] + + for exp, vowel_sub in expressions: + new_name = re.sub(exp, vowel_sub, displayname) + if new_name != displayname: + return new_name + + def append_name(self, displayname: str) -> str: + """Adds a suffix to the end of the Discord name.""" + extensions = ['foot', 'ear', 'nose', 'tail'] + suffix = random.choice(extensions) + appended_name = displayname + suffix + + return appended_name + + @commands.command() + async def bunnyname(self, ctx: commands.Context) -> None: + """Picks a random bunny name from a JSON file.""" + await ctx.send(random.choice(BUNNY_NAMES["names"])) + + @commands.command() + async def bunnifyme(self, ctx: commands.Context) -> None: + """Gets your Discord username and bunnifies it.""" + username = ctx.message.author.display_name + + # If name contains spaces or other separators, get the individual words to randomly bunnify + spaces_in_name = self.find_separators(username) + + # If name contains vowels, see if it matches any of the patterns in this function + # If there are matches, the bunnified name is returned. + vowels_in_name = self.find_vowels(username) + + # Default if the checks above return None + unmatched_name = self.append_name(username) + + if spaces_in_name is not None: + replacements = ['Cotton', 'Fluff', 'Floof' 'Bounce', 'Snuffle', 'Nibble', 'Cuddle', 'Velvetpaw', 'Carrot'] + word_to_replace = random.choice(spaces_in_name) + substitute = random.choice(replacements) + bunnified_name = username.replace(word_to_replace, substitute) + elif vowels_in_name is not None: + bunnified_name = vowels_in_name + elif unmatched_name: + bunnified_name = unmatched_name + + await ctx.send(bunnified_name) + + +def setup(bot: commands.Bot) -> None: + """Bunny Name Generator Cog load.""" + bot.add_cog(BunnyNameGenerator(bot)) diff --git a/bot/exts/easter/conversationstarters.py b/bot/exts/easter/conversationstarters.py new file mode 100644 index 00000000..a5f40445 --- /dev/null +++ b/bot/exts/easter/conversationstarters.py @@ -0,0 +1,28 @@ +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: commands.Bot): + self.bot = bot + + @commands.command() + async def topic(self, ctx: commands.Context) -> None: + """Responds with a random topic to start a conversation.""" + await ctx.send(random.choice(starters['starters'])) + + +def setup(bot: commands.Bot) -> None: + """Conversation starters Cog load.""" + bot.add_cog(ConvoStarters(bot)) diff --git a/bot/exts/easter/easter_riddle.py b/bot/exts/easter/easter_riddle.py new file mode 100644 index 00000000..8977534f --- /dev/null +++ b/bot/exts/easter/easter_riddle.py @@ -0,0 +1,100 @@ +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 Path("bot/resources/easter/easter_riddle.json").open("r", encoding="utf8") as f: + RIDDLE_QUESTIONS = load(f) + +TIMELIMIT = 10 + + +class EasterRiddle(commands.Cog): + """This cog contains the command for the Easter quiz!""" + + def __init__(self, bot: commands.Bot): + self.bot = bot + self.winners = [] + self.correct = "" + self.current_channel = None + + @commands.command(aliases=["riddlemethis", "riddleme"]) + async def riddle(self, ctx: commands.Context) -> None: + """ + Gives a random riddle, then provides 2 hints at certain intervals before revealing the answer. + + The duration of the hint interval can be configured by changing the TIMELIMIT constant in this file. + """ + if self.current_channel: + return await ctx.send(f"A riddle is already being solved in {self.current_channel.mention}!") + + self.current_channel = ctx.message.channel + + random_question = random.choice(RIDDLE_QUESTIONS) + question = random_question["question"] + hints = random_question["riddles"] + self.correct = random_question["correct_answer"] + + description = f"You have {TIMELIMIT} seconds before the first hint." + + riddle_embed = discord.Embed(title=question, description=description, colour=Colours.pink) + + await ctx.send(embed=riddle_embed) + await asyncio.sleep(TIMELIMIT) + + hint_embed = discord.Embed( + title=f"Here's a hint: {hints[0]}!", + colour=Colours.pink + ) + + await ctx.send(embed=hint_embed) + await asyncio.sleep(TIMELIMIT) + + hint_embed = discord.Embed( + title=f"Here's a hint: {hints[1]}!", + colour=Colours.pink + ) + + await ctx.send(embed=hint_embed) + await asyncio.sleep(TIMELIMIT) + + if self.winners: + win_list = " ".join(self.winners) + content = f"Well done {win_list} for getting it right!" + else: + content = "Nobody got it right..." + + answer_embed = discord.Embed( + title=f"The answer is: {self.correct}!", + colour=Colours.pink + ) + + await ctx.send(content, embed=answer_embed) + + self.winners = [] + self.current_channel = None + + @commands.Cog.listener() + async def on_message(self, message: discord.Message) -> None: + """If a non-bot user enters a correct answer, their username gets added to self.winners.""" + if self.current_channel != message.channel: + return + + if self.bot.user == message.author: + return + + if message.content.lower() == self.correct.lower(): + self.winners.append(message.author.mention) + + +def setup(bot: commands.Bot) -> None: + """Easter Riddle Cog load.""" + bot.add_cog(EasterRiddle(bot)) diff --git a/bot/exts/easter/egg_decorating.py b/bot/exts/easter/egg_decorating.py new file mode 100644 index 00000000..be228b2c --- /dev/null +++ b/bot/exts/easter/egg_decorating.py @@ -0,0 +1,118 @@ +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 +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) +] # 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): + """Decorate some easter eggs!""" + + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + + @staticmethod + def replace_invalid(colour: str) -> Union[int, None]: + """Attempts to match with HTML or XKCD colour names, returning the int value.""" + 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: commands.Context, *colours: Union[discord.Colour, str] + ) -> Union[Image.Image, discord.Message]: + """ + Picks a random egg design and decorates it using the given colours. + + 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: + return await ctx.send("You must include at least 2 colours!") + + invalid = [] + 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)}") + elif len(invalid) == 1: + return await ctx.send(f"Sorry, I don't know the colour {invalid[0]}!") + + async with ctx.typing(): + # 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(f"bot/resources/easter/easter_eggs/design{num}.png")) + data = list(im.getdata()) + + 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 = [] + 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 Colourful Easter Egg", + description="Here is your pretty little egg. Hope you like it!" + ) + embed.set_image(url="attachment://egg.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) + return new_im + + +def setup(bot: commands.bot) -> None: + """Egg decorating Cog load.""" + bot.add_cog(EggDecorating(bot)) diff --git a/bot/exts/easter/egg_facts.py b/bot/exts/easter/egg_facts.py new file mode 100644 index 00000000..83918fb0 --- /dev/null +++ b/bot/exts/easter/egg_facts.py @@ -0,0 +1,60 @@ +import logging +import random +from json import load +from pathlib import Path + +import discord +from discord.ext import commands + +from bot.constants import Channels, Colours, Month +from bot.utils.decorators import seasonal_task + +log = logging.getLogger(__name__) + + +class EasterFacts(commands.Cog): + """ + A cog contains a command that will return an easter egg fact when called. + + It also contains a background task which sends an easter egg fact in the event channel everyday. + """ + + def __init__(self, bot: commands.Bot): + self.bot = bot + self.facts = self.load_json() + + self.daily_fact_task = self.bot.loop.create_task(self.send_egg_fact_daily()) + + @staticmethod + def load_json() -> dict: + """Load a list of easter egg facts from the resource JSON file.""" + p = Path("bot/resources/easter/easter_egg_facts.json") + with p.open(encoding="utf8") as f: + return load(f) + + @seasonal_task(Month.APRIL) + async def send_egg_fact_daily(self) -> None: + """A background task that sends an easter egg fact in the event channel everyday.""" + await self.bot.wait_until_ready() + + channel = self.bot.get_channel(Channels.seasonalbot_commands) + await channel.send(embed=self.make_embed()) + + @commands.command(name='eggfact', aliases=['fact']) + async def easter_facts(self, ctx: commands.Context) -> None: + """Get easter egg facts.""" + embed = self.make_embed() + await ctx.send(embed=embed) + + def make_embed(self) -> discord.Embed: + """Makes a nice embed for the message to be sent.""" + return discord.Embed( + colour=Colours.soft_red, + title="Easter Egg Fact", + description=random.choice(self.facts) + ) + + +def setup(bot: commands.Bot) -> None: + """Easter Egg facts cog load.""" + bot.add_cog(EasterFacts(bot)) diff --git a/bot/exts/easter/egghead_quiz.py b/bot/exts/easter/egghead_quiz.py new file mode 100644 index 00000000..0498d9db --- /dev/null +++ b/bot/exts/easter/egghead_quiz.py @@ -0,0 +1,119 @@ +import asyncio +import logging +import random +from json import load +from pathlib import Path +from typing import Union + +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' +] # Regional Indicators A-Z (used for voting) + +TIMELIMIT = 30 + + +class EggheadQuiz(commands.Cog): + """This cog contains the command for the Easter quiz!""" + + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + self.quiz_messages = {} + + @commands.command(aliases=["eggheadquiz", "easterquiz"]) + async def eggquiz(self, ctx: commands.Context) -> None: + """ + Gives a random quiz question, waits 30 seconds and then outputs the answer. + + 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"]] + + valid_emojis = [emoji for emoji, _ 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) + + 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(TIMELIMIT) + + del self.quiz_messages[msg.id] + + 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 + + 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: discord.Message, user: Union[discord.Member, discord.User]) -> bool: + """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: discord.Reaction, user: Union[discord.Member, discord.User]) -> None: + """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: commands.Bot) -> None: + """Egghead Quiz Cog load.""" + bot.add_cog(EggheadQuiz(bot)) diff --git a/bot/exts/easter/traditions.py b/bot/exts/easter/traditions.py new file mode 100644 index 00000000..85b4adfb --- /dev/null +++ b/bot/exts/easter/traditions.py @@ -0,0 +1,30 @@ +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: commands.Bot): + self.bot = bot + + @commands.command(aliases=('eastercustoms',)) + async def easter_tradition(self, ctx: commands.Context) -> None: + """Responds with a random tradition or custom.""" + random_country = random.choice(list(traditions)) + + await ctx.send(f"{random_country}:\n{traditions[random_country]}") + + +def setup(bot: commands.Bot) -> None: + """Traditions Cog load.""" + bot.add_cog(Traditions(bot)) |