diff options
Diffstat (limited to 'bot/seasons')
| -rw-r--r-- | bot/seasons/evergreen/error_handler.py | 5 | ||||
| -rw-r--r-- | bot/seasons/evergreen/fun.py | 38 | ||||
| -rw-r--r-- | bot/seasons/valentines/lovecalculator.py | 106 | ||||
| -rw-r--r-- | bot/seasons/valentines/movie_generator.py | 66 | ||||
| -rw-r--r-- | bot/seasons/valentines/savethedate.py | 3 | ||||
| -rw-r--r-- | bot/seasons/valentines/valentine_zodiac.py | 59 | ||||
| -rw-r--r-- | bot/seasons/valentines/whoisvalentine.py | 53 |
7 files changed, 329 insertions, 1 deletions
diff --git a/bot/seasons/evergreen/error_handler.py b/bot/seasons/evergreen/error_handler.py index 6de35e60..47e18a31 100644 --- a/bot/seasons/evergreen/error_handler.py +++ b/bot/seasons/evergreen/error_handler.py @@ -42,9 +42,12 @@ class CommandErrorHandler: f"{ctx.author} called the command '{ctx.command}' "
"but they were on cooldown!"
)
+ seconds = error.retry_after
+ remaining_minutes, remaining_seconds = divmod(seconds, 60)
+ time_remaining = f'{int(remaining_minutes)} minutes {math.ceil(remaining_seconds)} seconds'
return await ctx.send(
"This command is on cooldown,"
- f" please retry in {math.ceil(error.retry_after)}s."
+ f" please retry in {time_remaining}."
)
if isinstance(error, commands.DisabledCommand):
logging.debug(
diff --git a/bot/seasons/evergreen/fun.py b/bot/seasons/evergreen/fun.py new file mode 100644 index 00000000..4da01dd1 --- /dev/null +++ b/bot/seasons/evergreen/fun.py @@ -0,0 +1,38 @@ +import logging +import random + +from discord.ext import commands + +from bot.constants import Emojis + +log = logging.getLogger(__name__) + + +class Fun: + """ + A collection of general commands for fun. + """ + + def __init__(self, bot): + self.bot = bot + + @commands.command() + async def roll(self, ctx, num_rolls: int = 1): + """ + Outputs a number of random dice emotes (up to 6) + """ + output = "" + if num_rolls > 6: + num_rolls = 6 + elif num_rolls < 1: + output = ":no_entry: You must roll at least once." + for _ in range(num_rolls): + terning = f"terning{random.randint(1, 6)}" + output += getattr(Emojis, terning, '') + await ctx.send(output) + + +# Required in order to load the cog, use the class name in the add_cog function. +def setup(bot): + bot.add_cog(Fun(bot)) + log.debug("Fun cog loaded") diff --git a/bot/seasons/valentines/lovecalculator.py b/bot/seasons/valentines/lovecalculator.py new file mode 100644 index 00000000..4df33b93 --- /dev/null +++ b/bot/seasons/valentines/lovecalculator.py @@ -0,0 +1,106 @@ +import bisect +import hashlib +import json +import logging +import random +from pathlib import Path +from typing import Union + +import discord +from discord import Member +from discord.ext import commands +from discord.ext.commands import BadArgument, clean_content + +from bot.constants import Roles + +log = logging.getLogger(__name__) + +with Path('bot', 'resources', 'valentines', 'love_matches.json').open() as file: + LOVE_DATA = json.load(file) + LOVE_DATA = sorted((int(key), value) for key, value in LOVE_DATA.items()) + + +class LoveCalculator: + """ + A cog for calculating the love between two people + """ + + def __init__(self, bot): + self.bot = bot + + @commands.command(aliases=('love_calculator', 'love_calc')) + @commands.cooldown(rate=1, per=5, type=commands.BucketType.user) + async def love(self, ctx, who: Union[Member, str], whom: Union[Member, str] = None): + """ + Tells you how much the two love each other. + + This command accepts users or arbitrary strings as arguments. + Users are converted from: + - User ID + - Mention + - name#discrim + - name + - nickname + + Any two arguments will always yield the same result, though the order of arguments matters: + Running .love joseph erlang will always yield the same result. + Running .love erlang joseph won't yield the same result as .love joseph erlang + + If you want to use multiple words for one argument, you must include quotes. + .love "Zes Vappa" "morning coffee" + + If only one argument is provided, the subject will become one of the helpers at random. + """ + + if whom is None: + staff = ctx.guild.get_role(Roles.helpers).members + whom = random.choice(staff) + + def normalize(arg): + if isinstance(arg, Member): + # if we are given a member, return name#discrim without any extra changes + arg = str(arg) + else: + # otherwise normalise case and remove any leading/trailing whitespace + arg = arg.strip().title() + # this has to be done manually to be applied to usernames + return clean_content(escape_markdown=True).convert(ctx, arg) + + who, whom = [await normalize(arg) for arg in (who, whom)] + + # make sure user didn't provide something silly such as 10 spaces + if not (who and whom): + raise BadArgument('Arguments be non-empty strings.') + + # hash inputs to guarantee consistent results (hashing algorithm choice arbitrary) + # + # hashlib is used over the builtin hash() function + # to guarantee same result over multiple runtimes + m = hashlib.sha256(who.encode() + whom.encode()) + # mod 101 for [0, 100] + love_percent = sum(m.digest()) % 101 + + # We need the -1 due to how bisect returns the point + # see the documentation for further detail + # https://docs.python.org/3/library/bisect.html#bisect.bisect + index = bisect.bisect(LOVE_DATA, (love_percent,)) - 1 + # we already have the nearest "fit" love level + # we only need the dict, so we can ditch the first element + _, data = LOVE_DATA[index] + + status = random.choice(data['titles']) + embed = discord.Embed( + title=status, + description=f'{who} \N{HEAVY BLACK HEART} {whom} scored {love_percent}%!\n\u200b', + color=discord.Color.dark_magenta() + ) + embed.add_field( + name='A letter from Dr. Love:', + value=data['text'] + ) + + await ctx.send(embed=embed) + + +def setup(bot): + bot.add_cog(LoveCalculator(bot)) diff --git a/bot/seasons/valentines/movie_generator.py b/bot/seasons/valentines/movie_generator.py new file mode 100644 index 00000000..b52eba7f --- /dev/null +++ b/bot/seasons/valentines/movie_generator.py @@ -0,0 +1,66 @@ +import logging +import random +from os import environ +from urllib import parse + +import discord +from discord.ext import commands + +TMDB_API_KEY = environ.get("TMDB_API_KEY") + +log = logging.getLogger(__name__) + + +class RomanceMovieFinder: + """ + A cog that returns a random romance movie suggestion to a user + """ + + def __init__(self, bot): + self.bot = bot + + @commands.command(name="romancemovie") + async def romance_movie(self, ctx): + """ + Randomly selects a romance movie and displays information about it + """ + # selecting a random int to parse it to the page parameter + random_page = random.randint(0, 20) + # TMDB api params + params = { + "api_key": TMDB_API_KEY, + "language": "en-US", + "sort_by": "popularity.desc", + "include_adult": "false", + "include_video": "false", + "page": random_page, + "with_genres": "10749" + } + # the api request url + request_url = "https://api.themoviedb.org/3/discover/movie?" + parse.urlencode(params) + async with self.bot.http_session.get(request_url) as resp: + # trying to load the json file returned from the api + try: + data = await resp.json() + # selecting random result from results object in the json file + selected_movie = random.choice(data["results"]) + + embed = discord.Embed( + title=f":sparkling_heart: {selected_movie['title']} :sparkling_heart:", + description=selected_movie["overview"], + ) + embed.set_image(url=f"http://image.tmdb.org/t/p/w200/{selected_movie['poster_path']}") + embed.add_field(name="Release date :clock1:", value=selected_movie["release_date"]) + embed.add_field(name="Rating :star2:", value=selected_movie["vote_average"]) + await ctx.send(embed=embed) + except KeyError: + warning_message = "A KeyError was raised while fetching information on the movie. The API service" \ + " could be unavailable or the API key could be set incorrectly." + embed = discord.Embed(title=warning_message) + log.warning(warning_message) + await ctx.send(embed=embed) + + +def setup(bot): + bot.add_cog(RomanceMovieFinder(bot)) + log.debug("Random romance movie cog loaded!") diff --git a/bot/seasons/valentines/savethedate.py b/bot/seasons/valentines/savethedate.py index a9bddd34..b9484be9 100644 --- a/bot/seasons/valentines/savethedate.py +++ b/bot/seasons/valentines/savethedate.py @@ -23,6 +23,9 @@ class SaveTheDate: @commands.command() async def savethedate(self, ctx): + """ + Gives you ideas for what to do on a date with your valentine. + """ with open(Path('bot', 'resources', 'valentines', 'date_ideas.json'), 'r', encoding="utf8") as f: valentine_dates = load(f) random_date = random.choice(valentine_dates['ideas']) diff --git a/bot/seasons/valentines/valentine_zodiac.py b/bot/seasons/valentines/valentine_zodiac.py new file mode 100644 index 00000000..06c0237d --- /dev/null +++ b/bot/seasons/valentines/valentine_zodiac.py @@ -0,0 +1,59 @@ +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__) + +LETTER_EMOJI = ':love_letter:' +HEART_EMOJIS = [":heart:", ":gift_heart:", ":revolving_hearts:", ":sparkling_heart:", ":two_hearts:"] + + +class ValentineZodiac: + """ + A cog that returns a counter compatible zodiac sign to the given user's zodiac sign. + """ + def __init__(self, bot): + self.bot = bot + self.zodiacs = self.load_json() + + @staticmethod + def load_json(): + p = Path('bot', 'resources', 'valentines', 'zodiac_compatibility.json') + with p.open() as json_data: + zodiacs = load(json_data) + return zodiacs + + @commands.command(name="partnerzodiac") + async def counter_zodiac(self, ctx, zodiac_sign): + """ + Provides a counter compatible zodiac sign to the given user's zodiac sign. + """ + try: + compatible_zodiac = random.choice(self.zodiacs[zodiac_sign.lower()]) + except KeyError: + return await ctx.send(zodiac_sign.capitalize() + " zodiac sign does not exist.") + + emoji1 = random.choice(HEART_EMOJIS) + emoji2 = random.choice(HEART_EMOJIS) + embed = discord.Embed( + title="Zodic Compatibility", + description=f'{zodiac_sign.capitalize()}{emoji1}{compatible_zodiac["Zodiac"]}\n' + f'{emoji2}Compatibility meter : {compatible_zodiac["compatibility_score"]}{emoji2}', + color=Colours.pink + ) + embed.add_field( + name=f'A letter from Dr.Zodiac {LETTER_EMOJI}', + value=compatible_zodiac['description'] + ) + await ctx.send(embed=embed) + + +def setup(bot): + bot.add_cog(ValentineZodiac(bot)) + log.debug("Valentine Zodiac cog loaded") diff --git a/bot/seasons/valentines/whoisvalentine.py b/bot/seasons/valentines/whoisvalentine.py new file mode 100644 index 00000000..2fe07aba --- /dev/null +++ b/bot/seasons/valentines/whoisvalentine.py @@ -0,0 +1,53 @@ +import json +import logging +from pathlib import Path +from random import choice + +import discord +from discord.ext import commands + +from bot.constants import Colours + +log = logging.getLogger(__name__) + +with open(Path("bot", "resources", "valentines", "valentine_facts.json"), "r") as file: + FACTS = json.load(file) + + +class ValentineFacts: + def __init__(self, bot): + self.bot = bot + + @commands.command(aliases=('whoisvalentine', 'saint_valentine')) + async def who_is_valentine(self, ctx): + """ + Displays info about Saint Valentine. + """ + embed = discord.Embed( + title="Who is Saint Valentine?", + description=FACTS['whois'], + color=Colours.pink + ) + embed.set_thumbnail( + url='https://upload.wikimedia.org/wikipedia/commons/thumb/f/f1/Saint_Valentine_-_' + 'facial_reconstruction.jpg/1024px-Saint_Valentine_-_facial_reconstruction.jpg' + ) + + await ctx.channel.send(embed=embed) + + @commands.command() + async def valentine_fact(self, ctx): + """ + Shows a random fact about Valentine's Day. + """ + embed = discord.Embed( + title=choice(FACTS['titles']), + description=choice(FACTS['text']), + color=Colours.pink + ) + + await ctx.channel.send(embed=embed) + + +def setup(bot): + bot.add_cog(ValentineFacts(bot)) |