aboutsummaryrefslogtreecommitdiffstats
path: root/bot/seasons
diff options
context:
space:
mode:
Diffstat (limited to 'bot/seasons')
-rw-r--r--bot/seasons/evergreen/error_handler.py5
-rw-r--r--bot/seasons/evergreen/fun.py38
-rw-r--r--bot/seasons/valentines/lovecalculator.py106
-rw-r--r--bot/seasons/valentines/movie_generator.py66
-rw-r--r--bot/seasons/valentines/savethedate.py3
-rw-r--r--bot/seasons/valentines/valentine_zodiac.py59
-rw-r--r--bot/seasons/valentines/whoisvalentine.py53
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))