diff --git a/BotSecrets.json.template b/BotSecrets.json.template index b5c2766..c676f43 100644 --- a/BotSecrets.json.template +++ b/BotSecrets.json.template @@ -9,6 +9,7 @@ "WeatherKey": "", "GeocodeKey": "", "AzureTranslateKey": "", + "ApiNinjasKey" : "", "ClassArchiveCategoryIds": [], "ClassNotifsChannelId": "" } diff --git a/bot/bot_secrets.py b/bot/bot_secrets.py index 4f3f395..766aa63 100644 --- a/bot/bot_secrets.py +++ b/bot/bot_secrets.py @@ -17,6 +17,7 @@ def __init__(self) -> None: self._weather_key: str | None = None self._geocode_key: str | None = None self._azure_translate_key: str | None = None + self._api_ninjas_key: str | None = None self._startup_log_channel_ids: list[int] | None = None self._error_log_channel_ids: list[int] | None = None self._class_archive_category_ids: list[int] | None = None @@ -151,6 +152,18 @@ def azure_translate_key(self, value: str | None) -> None: raise ConfigAccessError("azure_translate_key has already been initialized") self._azure_translate_key = value + @property + def api_ninjas_key(self) -> str: + if not self._api_ninjas_key: + raise ConfigAccessError("api_ninjas_key has not been initialized") + return self._api_ninjas_key + + @api_ninjas_key.setter + def api_ninjas_key(self, value: str | None) -> None: + if self._api_ninjas_key: + raise ConfigAccessError("api_ninjas_key has already been initialized") + self._api_ninjas_key = value + @property def class_archive_category_ids(self) -> list[int]: if not self._class_archive_category_ids: @@ -188,6 +201,7 @@ def load_development_secrets(self, lines: str) -> None: self.weather_key = secrets["WeatherKey"] self.geocode_key = secrets["GeocodeKey"] self.azure_translate_key = secrets["AzureTranslateKey"] + self.api_ninjas_key = secrets["ApiNinjasKey"] self.class_archive_category_ids = secrets["ClassArchiveCategoryIds"] self.class_notifs_channel_id = secrets["ClassNotifsChannelId"] @@ -210,6 +224,7 @@ def load_production_secrets(self) -> None: self.weather_key = os.environ.get("WEATHER_KEY") # type: ignore self.geocode_key = os.environ.get("GEOCODE_KEY") # type: ignore self.azure_translate_key = os.environ.get("AZURE_TRANSLATE_KEY") # type: ignore + self.api_ninjas_key = os.environ.get("API_NINJAS_KEY") # type: ignore self.class_archive_category_ids = [ int(n) for n in os.environ.get("CLASS_ARCHIVE_CATEGORY_IDS").split(",") # type: ignore ] diff --git a/bot/cogs/dad_joke_cog.py b/bot/cogs/dad_joke_cog.py new file mode 100644 index 0000000..c7a7a8d --- /dev/null +++ b/bot/cogs/dad_joke_cog.py @@ -0,0 +1,97 @@ +import logging +import random + +import aiohttp +import discord +import discord.ext.commands as commands + +import bot.extensions as ext +from bot.consts import Colors + +log = logging.getLogger(__name__) +DAD_JOKE_URL = 'https://icanhazdadjoke.com/' + +REQ_HEADERS = { + 'User-Agent': 'SockBot (https://github.com/Jay-Madden/SockBot)', + 'Accept' : 'application/json' +} + +class DadJokeCog(commands.Cog): + + def __init__(self, bot): + self.bot = bot + + ########################## + # USER EXECUTABLE COMMANDS + # Random Dad Joke + @ext.group(case_insensitive=True, invoke_without_command=True, aliases=['dad', 'dj']) + @ext.long_help( + """ + This command provides a random dad joke. + """ + ) + @ext.short_help('Get a Dad Joke!') + @ext.example(('dadjoke', 'dad', 'dj')) + async def dadjoke(self, ctx): + try: + async with aiohttp.request("GET", DAD_JOKE_URL, headers=REQ_HEADERS) as response: + if (response.status != 200): + embed = discord.Embed(title='SockDad', color=Colors.Error) + ErrMsg = f'Error Code: {response.status}' + embed.add_field(name='Error with the dad joke API', value=ErrMsg, inline=False) + await ctx.send(embed=embed) + return + joke_json = await response.json() + except Exception as err: + raise Exception(err).with_traceback(err.__traceback__) + + embed = discord.Embed(title='SockDad', color=Colors.Purple) + embed.add_field(name='Joke:', value=joke_json.get("joke", ""), inline=False) + await ctx.send(embed=embed) + return + + # Dad joke with a focus term. + @dadjoke.command() + @ext.long_help( + """ + This command provides a dad joke for a given term. + + Sometimes a Dad needs some direction. + """ + ) + @ext.short_help('Get a special Dad Joke!') + @ext.example(('dadjoke focus ', 'dad focus hipster', 'dj focus dog')) + async def focus(self, ctx, term): + + req_params = { + 'term': term, + 'limit': 30 + } + + try: + async with aiohttp.request("GET", DAD_JOKE_URL + 'search', headers=REQ_HEADERS, params=req_params) as response: + if (response.status != 200): + embed = discord.Embed(title='SockDad', color=Colors.Error) + ErrMsg = f'Error Code: {response.status}' + embed.add_field(name='Error with the dad joke API', value=ErrMsg, inline=False) + await ctx.send(embed=embed) + return + joke_json = await response.json() + except Exception as err: + raise Exception(err).with_traceback(err.__traceback__) + + joke = joke_json.get("results", []) + + if len(joke) > 0: + joke = random.choice(joke).get("joke", "") + else: + joke = 'No results for that term. :\'(' + + embed = discord.Embed(title='SockDad', color=Colors.Purple) + embed.add_field(name='Joke:', value=joke, inline=False) + await ctx.send(embed=embed) + return + + +async def setup(bot): + await bot.add_cog(DadJokeCog(bot)) \ No newline at end of file diff --git a/bot/cogs/riddle_cog.py b/bot/cogs/riddle_cog.py new file mode 100644 index 0000000..aac4af7 --- /dev/null +++ b/bot/cogs/riddle_cog.py @@ -0,0 +1,121 @@ +import logging +import random + +import aiohttp +import discord +import discord.ext.commands as commands + +import bot.extensions as ext +import bot.bot_secrets as bot_secrets +from bot.consts import Colors + +log = logging.getLogger(__name__) +RIDDLES_API_URL = 'https://api.api-ninjas.com/v1/riddles' + +EMOJI_MAP = { + '🇦': 0, + '🇧': 1, + '🇨': 2, + '🇩': 3 +} +REV_EMOJI_MAP = { + 0: '🇦', + 1: '🇧', + 2: '🇨', + 3: '🇩' +} + +class RiddleCog(commands.Cog): + + def __init__(self, bot): + self.bot = bot + + + ########################## + # USER EXECUTABLE COMMANDS + # Guess the riddle + @ext.group(case_insensitive=True, invoke_without_command=True, aliases=['riddler']) + @ext.long_help( + """ + This command allows you to play a riddle guessing game. + + Choose between 4 answers to guess the riddle's answer. + """ + ) + @ext.short_help('Guess a riddle!') + @ext.example(('riddle', 'riddler')) + async def riddle(self, ctx): + self.api_ninjas_key = bot_secrets.secrets.api_ninjas_key + + headers = { + 'X-Api-Key': self.api_ninjas_key + } + + params = { + 'limit': 4 + } + + try: + async with aiohttp.request("GET", RIDDLES_API_URL, headers=headers, params=params) as response: + if (response.status != 200): + embed = discord.Embed(title='The Riddler (in Socks)', color=Colors.Error) + ErrMsg = f'Error Code: {response.status}' + embed.add_field(name='Error with the riddle API', value=ErrMsg, inline=False) + await ctx.send(embed=embed) + return + riddle_list = await response.json() + except Exception as err: + raise Exception(err).with_traceback(err.__traceback__) + + correct_riddle = riddle_list[0] + + # Keep track of original index since 0 is correct + answers = [ + { + "answer": riddle.get("answer", "default answer"), + "index": index + } + for index, riddle in enumerate(riddle_list) + ] + random.shuffle(answers) + + options_text = "" + for index, answer in enumerate(answers): + options_text += f"{REV_EMOJI_MAP[index]} : {answer.get('answer', '')}\n\n" + + embed = discord.Embed(title='The Riddler (in Socks)', color=Colors.Purple) + embed.add_field(name=correct_riddle.get("title", "Riddle:"), value=correct_riddle.get("question", "No question. :\'("), inline=False) + embed.add_field(name="Options:", value=options_text, inline=False) + message = await ctx.send(embed=embed) + + for key in EMOJI_MAP.keys(): + await message.add_reaction(key) + + reaction, _ = await self.bot.wait_for( + "reaction_add", + check=lambda r, u: r.message.id == message.id + and ctx.author.id == u.id, + timeout=None, + ) + + await message.delete() + + # This means it is correct + if answers[EMOJI_MAP[reaction.emoji]].get("index") == 0: + embed = discord.Embed(title='Correct!', color=0x008000) + embed.add_field(name=correct_riddle.get("title", "Riddle:"), value=correct_riddle.get("question", "No question. :\'("), inline=False) + embed.add_field(name="Correct Answer:", value=correct_riddle.get("answer", "default answer"), inline=False) + embed.add_field(name="Your Answer:", value=answers[EMOJI_MAP[reaction.emoji]].get("answer", "default answer"), inline=False) + await ctx.send(embed=embed) + # Incorrect + else: + embed = discord.Embed(title='Wrong!', color=Colors.Error) + embed.add_field(name=correct_riddle.get("title", "Riddle:"), value=correct_riddle.get("question", "No question. :\'("), inline=False) + embed.add_field(name="Correct Answer:", value=correct_riddle.get("answer", "default answer"), inline=False) + embed.add_field(name="Your Answer:", value=answers[EMOJI_MAP[reaction.emoji]].get("answer", "default answer"), inline=False) + await ctx.send(embed=embed) + + return + +async def setup(bot): + await bot.add_cog(RiddleCog(bot)) \ No newline at end of file