diff --git a/bot.py b/bot.py index bc7d372..92791e8 100644 --- a/bot.py +++ b/bot.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 +from asyncio.tasks import wait_for import os import re +from discord.message import Message from dotenv import load_dotenv import discord @@ -24,13 +26,20 @@ class QuizClient(discord.Client): self.channel = None self.quiz = None + self.quizmaster = None + self.players = dict() + self.scores = list() self.triggers = {} # automatic actions # Example: self.triggers[lambda m: "jeremy" in m.author.nick.lower()] = self.autoreact_to_jeremy self.matchers = {} # react to messages + self.matchers["hilfe$"] = self.help self.matchers["init: .*"] = self.init_quiz - self.matchers["start"] = self.start_quiz + self.matchers["start$"] = self.run_quiz + self.matchers["reset$"] = self.reset_quiz + self.matchers["scores$"] = self.show_scores + self.matchers["players$"] = self.show_players ### Voicelines @@ -86,58 +95,218 @@ class QuizClient(discord.Client): ### Commands ----------------------------------------------------------------------------------- + async def help(self, message): + """ + Quiz, hilfe - Hilfetext anzeigen + """ + await message.channel.send(self._help_text()) + + + async def reset_quiz(self, message): + """ + Quiz, reset - Gesetzte Werte zurücksetzen (zum neu Initialisieren) + """ + await message.channel.send("Resetting...") + + self.quiz = None + self.channel = None + self.quizmaster = None + self.players = dict() + self.scores = list() + + await message.channel.send("Finished.") + + + def is_init(self): + return not (self.quiz == None or + self.channel == None or + self.quizmaster == None or + self.players == dict()) + + async def init_quiz(self, message): """ Quiz, init: [NAME] - Initialisiere ein neues Quiz. """ - # Set self.channel - if "quiz" not in message.channel.name: - await message.channel.send("Kein Quizchannel!") - return + # Reset to enable multiple inits + await self.reset_quiz(message) + # Set self.channel + if "quiz" not in message.channel.name.lower(): + await message.channel.send("Kein Quizchannel mann!") + return self.channel = message.channel - await self.channel.send("Quiz starting in channel " + self.channel.name) - await self.channel.send("-" * 50) + + # Set self.quizmaster + if message.author.top_role.name != "QuizMaster": + await self.channel.send("Nur für QuizMaster ey!") + return + self.quizmaster = message.author # Set self.quiz - self.quiz = Quiz((message.content.split(": "))[1]) + try: + self.quiz = Quiz((message.content.split(": "))[1]) + except: + await self.channel.send("Hab das Quiz nicht gefunden") + return # Set self.players await self.channel.send("Determining players:") react_message = await self.channel.send("Hier mit individuellem Emoji reagieren, am Ende mit dem Haken bestätigen!") await react_message.add_reaction("✅") - def check(reaction, user): - return reaction.message == react_message and str(reaction.emoji) == "✅" and user != client.user + def check_confirm_players(reaction, user): + return reaction.message == react_message and str(reaction.emoji) == "✅" and user == self.quizmaster - await self.wait_for('reaction_add', check=check) + await self.wait_for("reaction_add", check=check_confirm_players) + react_message = discord.utils.get(client.cached_messages, id=react_message.id) + assert isinstance(react_message, Message), "This should be a Message!" # silence pyright - # TODO: get players from emojis - await self.channel.send("yay") + # Get players from emojis + for reaction in react_message.reactions: + if reaction.emoji == "✅": + continue + + async for user in reaction.users(): + if user == self.quizmaster: + continue + + self.players[reaction.emoji] = user # TODO: key value which order? + + # Send starting message + await self.channel.send("Quiz will start in channel \"" + self.channel.name + "\"") + await self.channel.send("Players:") + for emoji, player in self.players.items(): + await self.channel.send(str(emoji) + ": " + str(player.display_name)) + await self.channel.send("-" * 80) - async def start_quiz(self, message): + async def run_quiz(self, message): """ - Quiz, start - Starte das Quiz + Quiz, run - Starte das Quiz """ - if self.quiz == None or self.channel == None: + if not self.is_init(): await message.channel.send("Vorher init du kek") return + if not message.author == self.quizmaster: + await self.channel.send("Kein QuizMaster kein Quiz!") + return + + for question, answer in self.quiz: + + # post question to players + for player in self.players.values(): + await player.send("Frage: **" + question + "**") + + # post question to channel for confirmation + await self.channel.send("Frage: **" + question + "**") + + # wait for answers from all players + for player in self.players.values(): + def check_answers_given(message): + return message.author == player + + await self.wait_for("message", check=check_answers_given) + + # wait for confirmation + cmsg = await self.channel.send("Alle Spieler haben geantwortet, fortfahren?") + await cmsg.add_reaction("✅") + + def check_question_finished(reaction, user): + return reaction.message == cmsg and str(reaction.emoji) == "✅" and user == self.quizmaster + + await self.wait_for("reaction_add", check=check_question_finished) + await self.channel.send("- " * 40) + + # Antworten + await self.channel.send("**Antworten:**") + for emoji, player in self.players.items(): + await self.channel.send(str(emoji) + ": " + str((await player.dm_channel.history(limit=1).flatten())[0].content)) + + amsg = await self.channel.send("Korrekte Antwort: " + answer) + await amsg.add_reaction("✅") + for emoji, player in self.players.items(): + await amsg.add_reaction(emoji) + + # Set Points + def check_confirm_points(reaction, user): + return reaction.message == amsg and str(reaction.emoji) == "✅" and user == self.quizmaster + + await self.wait_for("reaction_add", check=check_confirm_points) + amsg = discord.utils.get(client.cached_messages, id=amsg.id) + assert isinstance(amsg, Message), "This should be a Message!" # silence pyright + + turn_scores = list() + for reaction in amsg.reactions: + if reaction.emoji == "✅": + continue + + async for user in reaction.users(): + if user != self.quizmaster: + continue + + turn_scores.append(reaction.emoji) + + self.scores.append(turn_scores) + + # Separators at the end + await self.channel.send("-" * 80) + for player in self.players.values(): + await player.send("-" * 80) + + await self.channel.send("Quiz vorbei!") + # Ablauf: - # - post question with green checkmark reaction - # - players post answers + # - post question to quiz-channel and all private player channels + # - players post answers in private bot-channel # - answer multiple choice with a, b, c, d emojis - # - close question with checkmark + # - bot reacts with green checkmark after everyone answered, then quizmaster can close with checkmark # - print answer with player emojis - # - automatic winner for number questions? robust? maybe only print but set manually. # - set winners by choosing the right emoji # - # # - track the points and make graphs + async def show_scores(self, message): + """ + Quiz, scores - Zeigt den aktuellen Punktestand + """ + if not self.is_init(): + await message.channel.send("Vorher init du kek") + return + + if not message.author == self.quizmaster: + await self.channel.send("Kein QuizMaster keine Punkte!") + return + + # scores = [[A, B], [A], [B, C], ...] + flat_scores = [player for round in self.scores for player in round] + score_dict = dict() + for emoji, _ in self.players.items(): + score_dict[emoji] = len(list(filter(lambda x: x == emoji, flat_scores))) + + await self.channel.send("Punktestand:") + for emoji, score in sorted(score_dict.items(), key=lambda item: item[1]): + await self.channel.send(str(emoji) + ": " + str(score) + " Punkte") + + + async def show_players(self, message): + """ + Quiz, players - Zeigt die Spielerliste + """ + if not self.is_init(): + await message.channel.send("Vorher init du kek") + return + + if not message.author == self.quizmaster: + await self.channel.send("Kein QuizMaster keine Punkte!") + return + + await self.channel.send("Players:") + for emoji, player in self.players.items(): + await self.channel.send(str(emoji) + ": " + str(player.display_name)) client = QuizClient() client.run(TOKEN) diff --git a/quiz.py b/quiz.py index d4a604e..fe7942d 100644 --- a/quiz.py +++ b/quiz.py @@ -9,3 +9,14 @@ class Quiz(object): for line in file: question = tuple(string.strip() for string in tuple(line.split(" "))) self.questions.append(question) + + + def __iter__(self): + def questions_gen(questions): + current = 0 + + while current < len(questions): + yield questions[current] + current += 1 + + return questions_gen(self.questions)