diff options
| author | 2021-09-04 23:22:12 -0400 | |
|---|---|---|
| committer | 2021-09-04 23:22:12 -0400 | |
| commit | 2780043e6ddd5dbee82b62d85289f0518613ce7b (patch) | |
| tree | aae3347de61e9d4d53bd5cce301762bad66fe87a /bot/exts/easter | |
| parent | Move AoC and Hacktoberfest into events folder (diff) | |
Move Easter to Holidays Folder
This moves the easter seasonal features into a more cohesive
holidays/easter folder. Additionally, this splits out earth day into
its own holiday folder.
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 | 30 | ||||
| -rw-r--r-- | bot/exts/easter/bunny_name_generator.py | 94 | ||||
| -rw-r--r-- | bot/exts/easter/earth_photos.py | 66 | ||||
| -rw-r--r-- | bot/exts/easter/easter_riddle.py | 112 | ||||
| -rw-r--r-- | bot/exts/easter/egg_decorating.py | 119 | ||||
| -rw-r--r-- | bot/exts/easter/egg_facts.py | 55 | ||||
| -rw-r--r-- | bot/exts/easter/egghead_quiz.py | 118 | ||||
| -rw-r--r-- | bot/exts/easter/save_the_planet.py | 25 | ||||
| -rw-r--r-- | bot/exts/easter/traditions.py | 28 |
10 files changed, 0 insertions, 647 deletions
diff --git a/bot/exts/easter/__init__.py b/bot/exts/easter/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/bot/exts/easter/__init__.py +++ /dev/null diff --git a/bot/exts/easter/april_fools_vids.py b/bot/exts/easter/april_fools_vids.py deleted file mode 100644 index 5ef40704..00000000 --- a/bot/exts/easter/april_fools_vids.py +++ /dev/null @@ -1,30 +0,0 @@ -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/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/easter/bunny_name_generator.py b/bot/exts/easter/bunny_name_generator.py deleted file mode 100644 index 4c3137de..00000000 --- a/bot/exts/easter/bunny_name_generator.py +++ /dev/null @@ -1,94 +0,0 @@ -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/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/easter/earth_photos.py b/bot/exts/easter/earth_photos.py deleted file mode 100644 index f65790af..00000000 --- a/bot/exts/easter/earth_photos.py +++ /dev/null @@ -1,66 +0,0 @@ -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/easter/easter_riddle.py b/bot/exts/easter/easter_riddle.py deleted file mode 100644 index 88b3be2f..00000000 --- a/bot/exts/easter/easter_riddle.py +++ /dev/null @@ -1,112 +0,0 @@ -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/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/easter/egg_decorating.py b/bot/exts/easter/egg_decorating.py deleted file mode 100644 index fb5701c4..00000000 --- a/bot/exts/easter/egg_decorating.py +++ /dev/null @@ -1,119 +0,0 @@ -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/evergreen/html_colours.json").read_text("utf8")) - -XKCD_COLOURS = json.loads(Path("bot/resources/evergreen/xkcd_colours.json").read_text("utf8")) - -COLOURS = [ - (255, 0, 0, 255), (255, 128, 0, 255), (255, 255, 0, 255), (0, 255, 0, 255), - (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/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/easter/egg_facts.py b/bot/exts/easter/egg_facts.py deleted file mode 100644 index 486e735f..00000000 --- a/bot/exts/easter/egg_facts.py +++ /dev/null @@ -1,55 +0,0 @@ -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/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/easter/egghead_quiz.py b/bot/exts/easter/egghead_quiz.py deleted file mode 100644 index ad550567..00000000 --- a/bot/exts/easter/egghead_quiz.py +++ /dev/null @@ -1,118 +0,0 @@ -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/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/easter/save_the_planet.py b/bot/exts/easter/save_the_planet.py deleted file mode 100644 index 1bd515f2..00000000 --- a/bot/exts/easter/save_the_planet.py +++ /dev/null @@ -1,25 +0,0 @@ -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/easter/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/easter/traditions.py b/bot/exts/easter/traditions.py deleted file mode 100644 index 93404f3e..00000000 --- a/bot/exts/easter/traditions.py +++ /dev/null @@ -1,28 +0,0 @@ -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/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()) |