diff options
Diffstat (limited to 'bot/exts/holidays')
| -rw-r--r-- | bot/exts/holidays/__init__.py | 0 | ||||
| -rw-r--r-- | bot/exts/holidays/earth_day/__init__.py | 0 | ||||
| -rw-r--r-- | bot/exts/holidays/earth_day/save_the_planet.py | 25 | ||||
| -rw-r--r-- | bot/exts/holidays/easter/__init__.py | 0 | ||||
| -rw-r--r-- | bot/exts/holidays/easter/april_fools_vids.py | 30 | ||||
| -rw-r--r-- | bot/exts/holidays/easter/bunny_name_generator.py | 94 | ||||
| -rw-r--r-- | bot/exts/holidays/easter/earth_photos.py | 66 | ||||
| -rw-r--r-- | bot/exts/holidays/easter/easter_riddle.py | 112 | ||||
| -rw-r--r-- | bot/exts/holidays/easter/egg_decorating.py | 119 | ||||
| -rw-r--r-- | bot/exts/holidays/easter/egg_facts.py | 55 | ||||
| -rw-r--r-- | bot/exts/holidays/easter/egghead_quiz.py | 118 | ||||
| -rw-r--r-- | bot/exts/holidays/easter/traditions.py | 28 | 
12 files changed, 647 insertions, 0 deletions
| diff --git a/bot/exts/holidays/__init__.py b/bot/exts/holidays/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/bot/exts/holidays/__init__.py diff --git a/bot/exts/holidays/earth_day/__init__.py b/bot/exts/holidays/earth_day/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/bot/exts/holidays/earth_day/__init__.py diff --git a/bot/exts/holidays/earth_day/save_the_planet.py b/bot/exts/holidays/earth_day/save_the_planet.py new file mode 100644 index 00000000..13c84886 --- /dev/null +++ b/bot/exts/holidays/earth_day/save_the_planet.py @@ -0,0 +1,25 @@ +import json +from pathlib import Path + +from discord import Embed +from discord.ext import commands + +from bot.bot import Bot +from bot.utils.randomization import RandomCycle + +EMBED_DATA = RandomCycle(json.loads(Path("bot/resources/holidays/earth_day/save_the_planet.json").read_text("utf8"))) + + +class SaveThePlanet(commands.Cog): +    """A cog that teaches users how they can help our planet.""" + +    @commands.command(aliases=("savetheearth", "saveplanet", "saveearth")) +    async def savetheplanet(self, ctx: commands.Context) -> None: +        """Responds with a random tip on how to be eco-friendly and help our planet.""" +        return_embed = Embed.from_dict(next(EMBED_DATA)) +        await ctx.send(embed=return_embed) + + +def setup(bot: Bot) -> None: +    """Load the Save the Planet Cog.""" +    bot.add_cog(SaveThePlanet()) diff --git a/bot/exts/holidays/easter/__init__.py b/bot/exts/holidays/easter/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/bot/exts/holidays/easter/__init__.py diff --git a/bot/exts/holidays/easter/april_fools_vids.py b/bot/exts/holidays/easter/april_fools_vids.py new file mode 100644 index 00000000..ae22f751 --- /dev/null +++ b/bot/exts/holidays/easter/april_fools_vids.py @@ -0,0 +1,30 @@ +import logging +import random +from json import loads +from pathlib import Path + +from discord.ext import commands + +from bot.bot import Bot + +log = logging.getLogger(__name__) + +ALL_VIDS = loads(Path("bot/resources/holidays/easter/april_fools_vids.json").read_text("utf-8")) + + +class AprilFoolVideos(commands.Cog): +    """A cog for April Fools' that gets a random April Fools' video from Youtube.""" + +    @commands.command(name="fool") +    async def april_fools(self, ctx: commands.Context) -> None: +        """Get a random April Fools' video from Youtube.""" +        video = random.choice(ALL_VIDS) + +        channel, url = video["channel"], video["url"] + +        await ctx.send(f"Check out this April Fools' video by {channel}.\n\n{url}") + + +def setup(bot: Bot) -> None: +    """Load the April Fools' Cog.""" +    bot.add_cog(AprilFoolVideos()) diff --git a/bot/exts/holidays/easter/bunny_name_generator.py b/bot/exts/holidays/easter/bunny_name_generator.py new file mode 100644 index 00000000..f767f7c5 --- /dev/null +++ b/bot/exts/holidays/easter/bunny_name_generator.py @@ -0,0 +1,94 @@ +import json +import logging +import random +import re +from pathlib import Path +from typing import Optional + +from discord.ext import commands + +from bot.bot import Bot + +log = logging.getLogger(__name__) + +BUNNY_NAMES = json.loads(Path("bot/resources/holidays/easter/bunny_names.json").read_text("utf8")) + + +class BunnyNameGenerator(commands.Cog): +    """Generate a random bunny name, or bunnify your Discord username!""" + +    @staticmethod +    def find_separators(displayname: str) -> Optional[list[str]]: +        """Check if Discord name contains spaces so we can bunnify an individual word in the name.""" +        new_name = re.split(r"[_.\s]", displayname) +        if displayname not in new_name: +            return new_name +        return None + +    @staticmethod +    def find_vowels(displayname: str) -> Optional[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 = [ +            ("a.+y", "patchy"), +            ("e.+y", "ears"), +            ("i.+y", "ditsy"), +            ("o.+y", "oofy"), +            ("u.+y", "uffy"), +        ] + +        for exp, vowel_sub in expressions: +            new_name = re.sub(exp, vowel_sub, displayname) +            if new_name != displayname: +                return new_name + +    @staticmethod +    def append_name(displayname: str) -> str: +        """Adds a suffix to the end of the Discord name.""" +        extensions = ["foot", "ear", "nose", "tail"] +        suffix = random.choice(extensions) +        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.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: Bot) -> None: +    """Load the Bunny Name Generator Cog.""" +    bot.add_cog(BunnyNameGenerator()) diff --git a/bot/exts/holidays/easter/earth_photos.py b/bot/exts/holidays/easter/earth_photos.py new file mode 100644 index 00000000..f65790af --- /dev/null +++ b/bot/exts/holidays/easter/earth_photos.py @@ -0,0 +1,66 @@ +import logging + +import discord +from discord.ext import commands + +from bot.bot import Bot +from bot.constants import Colours +from bot.constants import Tokens + +log = logging.getLogger(__name__) + +API_URL = "https://api.unsplash.com/photos/random" + + +class EarthPhotos(commands.Cog): +    """This cog contains the command for earth photos.""" + +    def __init__(self, bot: Bot): +        self.bot = bot + +    @commands.command(aliases=("earth",)) +    async def earth_photos(self, ctx: commands.Context) -> None: +        """Returns a random photo of earth, sourced from Unsplash.""" +        async with ctx.typing(): +            async with self.bot.http_session.get( +                    API_URL, +                    params={"query": "planet_earth", "client_id": Tokens.unsplash_access_key} +            ) as r: +                jsondata = await r.json() +                linksdata = jsondata.get("urls") +                embedlink = linksdata.get("regular") +                downloadlinksdata = jsondata.get("links") +                userdata = jsondata.get("user") +                username = userdata.get("name") +                userlinks = userdata.get("links") +                profile = userlinks.get("html") +                # Referral flags +                rf = "?utm_source=Sir%20Lancebot&utm_medium=referral" +            async with self.bot.http_session.get( +                downloadlinksdata.get("download_location"), +                    params={"client_id": Tokens.unsplash_access_key} +            ) as _: +                pass + +            embed = discord.Embed( +                title="Earth Photo", +                description="A photo of Earth 🌎 from Unsplash.", +                color=Colours.grass_green +            ) +            embed.set_image(url=embedlink) +            embed.add_field( +                name="Author", +                value=( +                    f"Photo by [{username}]({profile}{rf}) " +                    f"on [Unsplash](https://unsplash.com{rf})." +                ) +            ) +            await ctx.send(embed=embed) + + +def setup(bot: Bot) -> None: +    """Load the Earth Photos cog.""" +    if not Tokens.unsplash_access_key: +        log.warning("No Unsplash access key found. Cog not loading.") +        return +    bot.add_cog(EarthPhotos(bot)) diff --git a/bot/exts/holidays/easter/easter_riddle.py b/bot/exts/holidays/easter/easter_riddle.py new file mode 100644 index 00000000..c9b7fc53 --- /dev/null +++ b/bot/exts/holidays/easter/easter_riddle.py @@ -0,0 +1,112 @@ +import asyncio +import logging +import random +from json import loads +from pathlib import Path + +import discord +from discord.ext import commands + +from bot.bot import Bot +from bot.constants import Colours, NEGATIVE_REPLIES + +log = logging.getLogger(__name__) + +RIDDLE_QUESTIONS = loads(Path("bot/resources/holidays/easter/easter_riddle.json").read_text("utf8")) + +TIMELIMIT = 10 + + +class EasterRiddle(commands.Cog): +    """This cog contains the command for the Easter quiz!""" + +    def __init__(self, bot: Bot): +        self.bot = bot +        self.winners = set() +        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: +            await ctx.send(f"A riddle is already being solved in {self.current_channel.mention}!") +            return + +        # Don't let users start in a DM +        if not ctx.guild: +            await ctx.send( +                embed=discord.Embed( +                    title=random.choice(NEGATIVE_REPLIES), +                    description="You can't start riddles in DMs", +                    colour=discord.Colour.red() +                ) +            ) +            return + +        self.current_channel = ctx.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.clear() +        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.add(message.author.mention) + + +def setup(bot: Bot) -> None: +    """Easter Riddle Cog load.""" +    bot.add_cog(EasterRiddle(bot)) diff --git a/bot/exts/holidays/easter/egg_decorating.py b/bot/exts/holidays/easter/egg_decorating.py new file mode 100644 index 00000000..1db9b347 --- /dev/null +++ b/bot/exts/holidays/easter/egg_decorating.py @@ -0,0 +1,119 @@ +import json +import logging +import random +from contextlib import suppress +from io import BytesIO +from pathlib import Path +from typing import Optional, Union + +import discord +from PIL import Image +from discord.ext import commands + +from bot.bot import Bot +from bot.utils import helpers + +log = logging.getLogger(__name__) + +HTML_COLOURS = json.loads(Path("bot/resources/fun/html_colours.json").read_text("utf8")) + +XKCD_COLOURS = json.loads(Path("bot/resources/fun/xkcd_colours.json").read_text("utf8")) + +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!""" + +    @staticmethod +    def replace_invalid(colour: str) -> Optional[int]: +        """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] +    ) -> Optional[Image.Image]: +        """ +        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: +            await ctx.send("You must include at least 2 colours!") +            return + +        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(helpers.suppress_links(colour)) + +        if len(invalid) > 1: +            await ctx.send(f"Sorry, I don't know these colours: {' '.join(invalid)}") +            return +        elif len(invalid) == 1: +            await ctx.send(f"Sorry, I don't know the colour {invalid[0]}!") +            return + +        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/holidays/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.display_avatar.url) + +        await ctx.send(file=file, embed=embed) +        return new_im + + +def setup(bot: Bot) -> None: +    """Load the Egg decorating Cog.""" +    bot.add_cog(EggDecorating()) diff --git a/bot/exts/holidays/easter/egg_facts.py b/bot/exts/holidays/easter/egg_facts.py new file mode 100644 index 00000000..5f216e0d --- /dev/null +++ b/bot/exts/holidays/easter/egg_facts.py @@ -0,0 +1,55 @@ +import logging +import random +from json import loads +from pathlib import Path + +import discord +from discord.ext import commands + +from bot.bot import Bot +from bot.constants import Channels, Colours, Month +from bot.utils.decorators import seasonal_task + +log = logging.getLogger(__name__) + +EGG_FACTS = loads(Path("bot/resources/holidays/easter/easter_egg_facts.json").read_text("utf8")) + + +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: Bot): +        self.bot = bot +        self.daily_fact_task = self.bot.loop.create_task(self.send_egg_fact_daily()) + +    @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_guild_available() + +        channel = self.bot.get_channel(Channels.community_bot_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) + +    @staticmethod +    def make_embed() -> 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(EGG_FACTS) +        ) + + +def setup(bot: Bot) -> None: +    """Load the Easter Egg facts Cog.""" +    bot.add_cog(EasterFacts(bot)) diff --git a/bot/exts/holidays/easter/egghead_quiz.py b/bot/exts/holidays/easter/egghead_quiz.py new file mode 100644 index 00000000..06229537 --- /dev/null +++ b/bot/exts/holidays/easter/egghead_quiz.py @@ -0,0 +1,118 @@ +import asyncio +import logging +import random +from json import loads +from pathlib import Path +from typing import Union + +import discord +from discord.ext import commands + +from bot.bot import Bot +from bot.constants import Colours + +log = logging.getLogger(__name__) + +EGGHEAD_QUESTIONS = loads(Path("bot/resources/holidays/easter/egghead_questions.json").read_text("utf8")) + + +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): +        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.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: Bot) -> None: +    """Load the Egghead Quiz Cog.""" +    bot.add_cog(EggheadQuiz()) diff --git a/bot/exts/holidays/easter/traditions.py b/bot/exts/holidays/easter/traditions.py new file mode 100644 index 00000000..f54ab5c4 --- /dev/null +++ b/bot/exts/holidays/easter/traditions.py @@ -0,0 +1,28 @@ +import json +import logging +import random +from pathlib import Path + +from discord.ext import commands + +from bot.bot import Bot + +log = logging.getLogger(__name__) + +traditions = json.loads(Path("bot/resources/holidays/easter/traditions.json").read_text("utf8")) + + +class Traditions(commands.Cog): +    """A cog which allows users to get a random easter tradition or custom from a random country.""" + +    @commands.command(aliases=("eastercustoms",)) +    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: Bot) -> None: +    """Load the Traditions Cog.""" +    bot.add_cog(Traditions()) | 
