diff options
Diffstat (limited to 'bot/exts/fun')
| -rw-r--r-- | bot/exts/fun/anagram.py | 110 | ||||
| -rw-r--r-- | bot/exts/fun/quack.py | 75 |
2 files changed, 185 insertions, 0 deletions
diff --git a/bot/exts/fun/anagram.py b/bot/exts/fun/anagram.py new file mode 100644 index 00000000..9aee5f18 --- /dev/null +++ b/bot/exts/fun/anagram.py @@ -0,0 +1,110 @@ +import asyncio +import json +import logging +import random +from pathlib import Path + +import discord +from discord.ext import commands + +from bot.bot import Bot +from bot.constants import Colours + +log = logging.getLogger(__name__) + +TIME_LIMIT = 60 + +# anagram.json file contains all the anagrams +with open(Path("bot/resources/fun/anagram.json"), "r") as f: + ANAGRAMS_ALL = json.load(f) + + +class AnagramGame: + """ + Used for creating instances of anagram games. + + Once multiple games can be run at the same time, this class' instances + can be used for keeping track of each anagram game. + """ + + def __init__(self, scrambled: str, correct: list[str]) -> None: + self.scrambled = scrambled + self.correct = set(correct) + + self.winners = set() + + async def message_creation(self, message: discord.Message) -> None: + """Check if the message is a correct answer and remove it from the list of answers.""" + if message.content.lower() in self.correct: + self.winners.add(message.author.mention) + self.correct.remove(message.content.lower()) + + +class Anagram(commands.Cog): + """Cog for the Anagram game command.""" + + def __init__(self, bot: Bot): + self.bot = bot + + self.games: dict[int, AnagramGame] = {} + + @commands.command(name="anagram", aliases=("anag", "gram", "ag")) + @commands.guild_only() + async def anagram_command(self, ctx: commands.Context) -> None: + """ + Given shuffled letters, rearrange them into anagrams. + + Show an embed with scrambled letters which if rearranged can form words. + After a specific amount of time, list the correct answers and whether someone provided a + correct answer. + """ + if self.games.get(ctx.channel.id): + await ctx.send("An anagram is already being solved in this channel!") + return + + scrambled_letters, correct = random.choice(list(ANAGRAMS_ALL.items())) + + game = AnagramGame(scrambled_letters, correct) + self.games[ctx.channel.id] = game + + anagram_embed = discord.Embed( + title=f"Find anagrams from these letters: '{scrambled_letters.upper()}'", + description=f"You have {TIME_LIMIT} seconds to find correct words.", + colour=Colours.purple, + ) + + await ctx.send(embed=anagram_embed) + await asyncio.sleep(TIME_LIMIT) + + if game.winners: + win_list = ", ".join(game.winners) + content = f"Well done {win_list} for getting it right!" + else: + content = "Nobody got it right." + + answer_embed = discord.Embed( + title=f"The words were: `{'`, `'.join(ANAGRAMS_ALL[game.scrambled])}`!", + colour=Colours.pink, + ) + + await ctx.send(content, embed=answer_embed) + + # Game is finished, let's remove it from the dict + self.games.pop(ctx.channel.id) + + @commands.Cog.listener() + async def on_message(self, message: discord.Message) -> None: + """Check a message for an anagram attempt and pass to an ongoing game.""" + if message.author.bot or not message.guild: + return + + game = self.games.get(message.channel.id) + if not game: + return + + await game.message_creation(message) + + +def setup(bot: Bot) -> None: + """Load the Anagram cog.""" + bot.add_cog(Anagram(bot)) diff --git a/bot/exts/fun/quack.py b/bot/exts/fun/quack.py new file mode 100644 index 00000000..0c228aed --- /dev/null +++ b/bot/exts/fun/quack.py @@ -0,0 +1,75 @@ +import logging +import random +from typing import Literal, Optional + +import discord +from discord.ext import commands + +from bot.bot import Bot +from bot.constants import Colours, NEGATIVE_REPLIES + +API_URL = 'https://quackstack.pythondiscord.com' + +log = logging.getLogger(__name__) + + +class Quackstack(commands.Cog): + """Cog used for wrapping Quackstack.""" + + def __init__(self, bot: Bot): + self.bot = bot + + @commands.command() + async def quack( + self, + ctx: commands.Context, + ducktype: Literal["duck", "manduck"] = "duck", + *, + seed: Optional[str] = None + ) -> None: + """ + Use the Quackstack API to generate a random duck. + + If a seed is provided, a duck is generated based on the given seed. + Either "duck" or "manduck" can be provided to change the duck type generated. + """ + ducktype = ducktype.lower() + quackstack_url = f"{API_URL}/{ducktype}" + params = {} + if seed is not None: + try: + seed = int(seed) + except ValueError: + # We just need to turn the string into an integer any way possible + seed = int.from_bytes(seed.encode(), "big") + params["seed"] = seed + + async with self.bot.http_session.get(quackstack_url, params=params) as response: + error_embed = discord.Embed( + title=random.choice(NEGATIVE_REPLIES), + description="The request failed. Please try again later.", + color=Colours.soft_red, + ) + if response.status != 200: + log.error(f"Response to Quackstack returned code {response.status}") + await ctx.send(embed=error_embed) + return + + data = await response.json() + file = data["file"] + + embed = discord.Embed( + title=f"Quack! Here's a {ducktype} for you.", + description=f"A {ducktype} from Quackstack.", + color=Colours.grass_green, + url=f"{API_URL}/docs" + ) + + embed.set_image(url=API_URL + file) + + await ctx.send(embed=embed) + + +def setup(bot: Bot) -> None: + """Loads the Quack cog.""" + bot.add_cog(Quackstack(bot)) |