diff options
Diffstat (limited to 'bot/seasons/evergreen')
| -rw-r--r-- | bot/seasons/evergreen/travia_quiz.py | 225 | ||||
| -rw-r--r-- | bot/seasons/evergreen/trivia_quiz.py | 266 | 
2 files changed, 225 insertions, 266 deletions
diff --git a/bot/seasons/evergreen/travia_quiz.py b/bot/seasons/evergreen/travia_quiz.py new file mode 100644 index 00000000..84a16654 --- /dev/null +++ b/bot/seasons/evergreen/travia_quiz.py @@ -0,0 +1,225 @@ +import asyncio +import json +import logging +import random +from pathlib import Path + +import discord +from discord.ext import commands +from fuzzywuzzy import fuzz + +from bot.constants import Roles + + +logger = logging.getLogger(__name__) + + +annoyed_expressions = ["-_-", "-.-"] + +wrong_ans_responses = [ +    "No one gave the correct answer", +    "Losers", +    "You guys really need to learn" +] + +game_rules = [ +    "No cheating!", +    "Idk what rules to put in here haha." + +] + + +class TriviaQuiz(commands.Cog): +    """A cog for all quiz commands.""" + +    def __init__(self, bot): +        self.bot = bot +        self.questions = self.load_questions() +        self.game_status = {} +        self.game_owners = {} +        self.question_limit = 3 +        self.categories = { +            "general": "Test your general knwoledge", +            "retro": "Questions related to retro gaming." +        } + +    @staticmethod +    def load_questions() -> dict: +        """Load the questions from  json file.""" +        p = Path("bot", "resources", "evergreen", "trivia_quiz.json") +        with p.open() as json_data: +            questions = json.load(json_data) +            return questions + +    @commands.command(name="quiz") +    async def start(self, ctx, option: str, category: str = "retro") -> None: +        """ +        Start/Stop a quiz! + +        arguments: +        option: +        - start : to start a quiz in a channel +        - stop  : stop the quiz running in that channel. + +        Questions for the quiz can be selected from the following categories: +        - general : Test your general knowledge. (default) +        - Retro : questions related to retro gaming. +        """ +        category = category.lower() +        player_data = {}  # a dict to store players and their points. + +        if ctx.channel.id not in self.game_status: +            self.game_status[ctx.channel.id] = None + +        if option == "start": +            if self.game_status[ctx.channel.id] is True: +                return await ctx.send("Game already running.") +            else: +                self.game_owners[ctx.channel.id] = ctx.author +                self.game_status[ctx.channel.id] = True +                start_embed = discord.Embed(colour=discord.Colour.red()) +                start_embed.title = "Quiz game Starting!!" +                start_embed.description = "Each game consists of 5 questions.\n" +                start_embed.description += "**Rules :**\n" +                for rule in game_rules: +                    start_embed.description += f"- {rule}\n" +                start_embed.set_footer(text="2 hints per question sent after every 10s") +                await ctx.send(embed=start_embed)  # send an embed with the rules + +        elif option == "stop": +            if self.game_status[ctx.channel.id] is False: +                return await ctx.send("No game running, nothing to stop here.") +            else: +                if ( +                        ctx.author == self.game_owners[ctx.channel.id] or +                        Roles.moderator in [role.id for role in ctx.author.roles] +                ): +                    await self.declare_winner(ctx.channel, player_data) +                    self.game_status[ctx.channel.id] = False +                else: +                    await ctx.send(f"{ctx.author.mention}, you are not authorised to stop this game :ghost: !") +        else: +            self.game_status[ctx.channel.id] = False +            return await ctx.send("start or stop only") +         +        unanswerd = 0 +        done_question = [] +        hint_no = 0 +        answer = None +        hints = None +        while self.game_status[ctx.channel.id] is True: +            if len(done_question) > self.question_limit and hint_no == 0: +                await ctx.send("The round ends here.") +                await self.declare_winner(ctx.channel, player_data) +                break +            if unanswerd > 3: +                await ctx.send("Game stopped due to inactivity.") +                await self.declare_winner(ctx.channel, player_data) +                break +            if category not in self.categories: +                embed = self.category_embed +                await ctx.send(embed=embed) +                break +            topic = self.questions[category] +            if hint_no == 0: +                while True: +                    question_dict = random.choice(topic) +                    if question_dict["id"] not in done_question: +                        done_question.append(question_dict["id"]) +                        break +                q = question_dict["question"] +                answer = question_dict["answer"] +                hints = question_dict["hints"] + +                embed = discord.Embed(colour=discord.Colour.gold()) +                embed.title = f"Question #{len(done_question)}" +                embed.description = q +                await ctx.send(embed=embed) + +            def check(m): +                ratio = fuzz.ratio(answer.lower(), m.content) +                return ratio > 80 and m.channel == ctx.channel +            try: +                msg = await self.bot.wait_for('message', check=check, timeout=10) +            except Exception as e: +                if self.game_status[ctx.channel.id] is False: +                    break +                if isinstance(e, asyncio.TimeoutError): +                    if hint_no < 2: +                        await ctx.send(f"**Hint #{hint_no+1}\n**{hints[hint_no]}") +                        hint_no += 1 +                    else: +                        response = random.choice(wrong_ans_responses) +                        expression = random.choice(annoyed_expressions) +                        await ctx.send(f"{response} {expression}, the correct answer is **{answer}**.") +                        hint_no = 0 +                        unanswerd += 1 +                        await self.send_score(ctx.channel, player_data) + +            else: +                points = 100 - 25*hint_no +                if msg.author in player_data: +                    player_data[msg.author] += points +                else: +                    player_data[msg.author] = points +                hint_no = 0 +                unanswerd = 0 +                await ctx.send(f"{msg.author.mention} got the correct answer :tada: {points} points for ya.") +                await ctx.send(f"Correct answer is **{answer}**") +                await self.send_score(ctx.channel, player_data) + +    @staticmethod +    async def send_score(channel, player_data): +        """A function which sends the score.""" +        embed = discord.Embed(colour=discord.Colour.blue()) +        embed.title = "Score Board" +        embed.description = "" +        for k, v in player_data.items(): +            embed.description += f"{k} : {v}\n" +        await channel.send(embed=embed) + +    @staticmethod +    async def declare_winner(channel, player_data): +        """A function declare the winner of the quiz.""" + +        if player_data: +            highest_points = max(list(player_data.values())) +            no_of_winners = list(player_data.values()).count(highest_points) + +            # Check if more than 1 player has highest points. +            if no_of_winners > 1: +                winners = [] +                points_copy = list(player_data.values()).copy() +                for _ in range(no_of_winners): +                    index = points_copy.index(highest_points) +                    winners.append(list(player_data.keys())[index]) +                    points_copy[index] = 0 +                winners_mention = None +                for winner in winners: +                    winners_mention += f"{winner.mention} " + +            else: +                author_index = list(player_data.values()).index(highest_points) +                winner = list(player_data.keys())[author_index] +                winners_mention = winner.mention +            await channel.send( +                f"Congratz {winners_mention} :tada: " +                f"You have won this quiz game with a grand total of {highest_points} points!!" +            ) + +    @property +    def category_embed(self) -> discord.Embed: +        """A function which returns an embed showing all avilable categories.""" +        embed = discord.Embed(colour=discord.Colour.blue()) +        embed.title = "The available question categories are:" +        embed.description = "" +        for cat, description in self.categories.items(): +            embed.description += f"**- {cat.capitalize()}**\n{description.capitalize()}\n" +        embed.set_footer(text="If not category is chosen, then a random one will be selected.") +        return embed + + +def setup(bot): +    """Loading the cog.""" +    bot.add_cog(TriviaQuiz(bot)) +    logger.debug("TriviaQuiz cog loaded!") diff --git a/bot/seasons/evergreen/trivia_quiz.py b/bot/seasons/evergreen/trivia_quiz.py deleted file mode 100644 index 433a021d..00000000 --- a/bot/seasons/evergreen/trivia_quiz.py +++ /dev/null @@ -1,266 +0,0 @@ -import asyncio -import json -import logging -import random -from dataclasses import dataclass -from pathlib import Path - -import discord -from discord.ext import commands -from fuzzywuzzy import fuzz - -from bot.constants import Roles - - -logger = logging.getLogger(__name__) - - -annoyed_expressions = ["-_-", "-.-"] - -wrong_ans_responses = [ -    "No one gave the correct answer", -    "Losers", -    "You guys really need to learn" -] - - -@dataclass -class GameData: -    """A dataclass for game data.""" - -    owner: int -    players = [] -    points = [] -    done_questions = [] -    category: str -    question: dict = None -    hints = 0 -    unanswered_questions = 0 - - -class TriviaQuiz(commands.Cog): -    """A cog for all quiz commands.""" - -    def __init__(self, bot): -        self.bot = bot -        self.questions = self.load_questions() -        self.running_games = {}  # channel as key and value as instance of dataclass GameData -        self.categories = { -            "retro": "Questions related to retro gaming." -        } -        self.inactivity_limit = 3  # Number of questions unanswered in a row after which quiz stops. - -    @staticmethod -    def load_questions(): -        """Load the questions from  json file.""" -        p = Path("bot", "resources", "evergreen", "trivia_quiz.json") -        with p.open() as json_data: -            questions = json.load(json_data) -            return questions - -    @commands.group(name="tquiz", invoke_without_command=True) -    async def tquiz(self, ctx): -        """Trivia Quiz game for fun!""" -        await ctx.send_help("tquiz") - -    @tquiz.command(name="start") -    async def start(self, ctx, category=None): -        """ -        Start a quiz! - -        Questions for the quiz can be selected from the following categories: -        - Retro : questions related to retro gaming. -        """ -        await ctx.send("Quiz triggered! Gonna start in a couple of seconds...") -        await asyncio.sleep(1) - -        # Checking if there is already a game running in that channel. -        if ctx.channel.id in self.running_games: -            return await ctx.send("Game already running in this channel!") - -        if category is None: -            category = random.choice(self.categories) - -        else: -            category = category.lower() -            if category not in self.categories: -                embed = self.category_embed() -                return await ctx.send(f"Category {category} does not exist!", embed=embed) - -        self.running_games[ctx.channel.id] = GameData( -            owner=ctx.author.id, -            category=category -        ) - -        await self.send_question(ctx.channel) - -    def category_embed(self): -        """A function which returns an embed showing all avilable categories""" -        embed = discord.Embed(colour=discord.Colour.blue()) -        embed.title = "The available question categories are:" -        embed.description = "" -        for cat, description in self.categories.items(): -            embed.description += f"**- {cat.capitalize()}**\n{description.capitalize()}\n" -        embed.set_footer(text="If not category is chosen, then a random one will be selected.") -        return embed - -    async def send_question(self, channel): -        """This function is to be called whenever a question needs to be sent.""" -        await asyncio.sleep(2) -        game = self.running_games[channel.id] -        if game.unanswered_questions == self.inactivity_limit: -            del self.running_games[channel.id] -            return await channel.send("Game stopped due to inactivity.") - -        category = game.category -        category_dict = self.questions[category] -        question_dict = random.choice(category_dict) - -        same_question = True - -        # Making sure a question is not repeated. -        while same_question is True: -            question_dict = random.choice(category_dict) -            if question_dict["id"] not in game.done_questions: -                same_question = False - -        # Initial points for a question. -        question_dict["points"] = 100 -        game.question = question_dict -        game.hints = 0 - -        embed = discord.Embed(colour=discord.Colour.dark_gold()) - -        question = question_dict["question"] -        question_id = question_dict["id"] - -        game.done_questions.append(question_id) -        question_number = len(game.done_questions) - -        embed.title = f"#{question_number} Question" -        embed.description = question -        embed.set_footer(text="A hint will be provided after every 10s if no one gives the right answer.Max of 2 hints") - -        await channel.send(embed=embed) -        await self.send_hint(channel, question_dict) - -    @commands.Cog.listener() -    async def on_message(self, message): -        """A function triggered when a message is sent.""" -        channel = message.channel -        if channel.id not in self.running_games: -            return -        if message.author.bot: -            return - -        game = self.running_games[message.channel.id] -        question_data = game.question -        answer = question_data["answer"].lower() -        user_answer = message.content.lower() -        ratio = fuzz.ratio(answer, user_answer) -        if ratio > 84: -            points = question_data["points"] - game.hints*25 -            if message.author in game.players: -                author_index = game.players.index(message.author) -                game.points[author_index] = game.points[author_index] + points -            else: -                game.players.append(message.author) -                game.points.append(points) - -            await channel.send(f"{message.author.mention} got it right! Good job :tada:" -                               f"You got {points} points.") -            await self.send_score(channel) -            await self.send_question(channel) -        elif ratio in range(75, 84): -            await channel.send(f"Your close to the answer {message.author.mention}") - -    async def send_hint(self, channel, question_dict): -        """Function to be called whenever a hint has to be sent.""" -        await asyncio.sleep(10) -        try: -            game = self.running_games[channel.id] -        except KeyError: -            return - -        # Checking if the question is the same after 10 seconds. -        if question_dict["id"] == game.question["id"]: - -            # If the max number of hints is already reached, then send the answer. -            if 2 - game.hints == 0: -                return await self.send_answer(channel) - -            hint_list = question_dict["hints"] -            hint_index = game.hints -            hint = hint_list[hint_index] -            game.hints += 1 -            message = f"**Hint {game.hints}**: {hint}\n*Number of hints remaining: {2-game.hints}*" -            await channel.send(message) -            await self.send_hint(channel, question_dict) - -    async def send_answer(self, channel): -        """A function to send the answer in the channel if no user have given the correct answer even after 2 hints.""" -        game = self.running_games[channel.id] -        answer = game.question["answer"] -        response = random.choice(wrong_ans_responses) -        expression = random.choice(annoyed_expressions) -        await channel.send(f"{response} {expression}, the correct answer is **{answer}**.") -        self.running_games[channel.id].unanswered_questions += 1 -        await self.send_score(channel) -        await self.send_question(channel) - -    @tquiz.command(name="score") -    async def show_score(self, ctx): -        """Show scoreboard of the game running in this channel.""" -        await self.send_score(ctx.channel) - -    async def send_score(self, channel): -        """Show score of each player in the quiz.""" -        if channel.id not in self.running_games: -            return await channel.send("There are no games running in this channel!") -        game = self.running_games[channel.id] -        players = game.players -        if len(players) == 0: -            return -        points = game.points -        embed = discord.Embed(color=discord.Colour.dark_gold()) -        embed.title = "Scoreboard" -        embed.description = "" -        for player, score in zip(players, points): -            embed.description += f"{player} - {score}\n" -        await channel.send(embed=embed) - -    @tquiz.command(name="stop") -    async def stop_quiz(self, ctx): -        """Stop the quiz.""" -        if ctx.channel.id not in self.running_games: -            return await ctx.send("No game running, nothing to stop here -.-") -        game = self.running_games[ctx.channel.id] -        owner = game.owner -        mods = Roles.moderator -        if ctx.author.id == owner or mods in [role.id for role in ctx.author.roles]: -            await ctx.send("Game is not running anymore!") -            await self.send_score(ctx.channel) -            if game.players: -                highest_points = max(game.points) - -                # Check if more than 1 player has highest points. - -                if game.points.count(highest_points) > 1: -                    pass -                else: -                    author_index = game.points.index(highest_points) -                    winner = game.players[author_index] -                    await ctx.send( -                        f"Congratz {winner.mention} :tada: " -                        f"You have won this quiz game with a grand total of {highest_points} points!!" -                    ) -            await asyncio.sleep(2) -            del self.running_games[ctx.channel.id] -        else: -            await ctx.send("You are not authorised to close this game!") - - -def setup(bot): -    """Loading the cog.""" -    bot.add_cog(TriviaQuiz(bot)) -    logger.debug("TriviaQuiz cog loaded!")  |