diff --git a/mesh_bot.py b/mesh_bot.py index bbf8923..bb33a77 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -851,9 +851,11 @@ def handleTicTacToe(message, nodeID, deviceID): def quizHandler(message, nodeID, deviceID): user_name = get_name_from_number(nodeID) user_id = nodeID - msg = "" - user_answer = message.lower().replace("quiz","").replace("q:","").replace("quiz ","").replace("q: ","").strip() - if message.startswith("quiz") or message.lower().startswith("q:"): + msg = '' + user_answer = '' + user_answer = message.lower() + user_answer = user_answer.replace("quiz","").replace("q:","").strip() + if user_answer: if user_answer.startswith("start"): msg = quizGamePlayer.start_game(user_id) elif user_answer.startswith("stop"): @@ -875,19 +877,34 @@ def quizHandler(message, nodeID, deviceID): elif user_answer.startswith("broadcast"): broadcast_msg = user_answer.replace("broadcast", "", 1).strip() msg = quizGamePlayer.broadcast(user_id, broadcast_msg) - msg = f"Broadcast message to players spud, finish this later" elif user_answer.startswith("?"): msg = ("Quiz Commands:\n" "q: join - Join the current quiz\n" "q: leave - Leave the current quiz\n" - "q: next - Get the next question\n" "q: - Answer the current question\n" "q: score - Show your current score\n" "q: top - Show top 3 players\n") else: msg = quizGamePlayer.answer(user_id, user_answer) - return msg + # set username on top 3 + if "πŸ† Top" in msg: + #replace all the 10 digit numbers with the short name + for part in msg.split(): + part = part.rstrip(":") + if len(part) == 10 and part.isdigit(): + player_name = get_name_from_number(int(part), 'short', deviceID) + msg = msg.replace(part, player_name) + + # broadcast message to all players if user is in bbs_admin_list and msg is a dict with 'message' key + if isinstance(msg, dict) and str(nodeID) in bbs_admin_list and 'message' in msg: + for player_id in quizGamePlayer.players: + send_message(msg['message'], 0, player_id, deviceID) + msg = f"Message sent to {len(quizGamePlayer.players)} players" + + return msg + else: + return "🧠Please provide an answer or command, or send q: ?" def handle_riverFlow(message, message_from_id, deviceID): location = get_node_location(message_from_id, deviceID) diff --git a/modules/games/quiz.py b/modules/games/quiz.py index b63c1a4..647e0e4 100644 --- a/modules/games/quiz.py +++ b/modules/games/quiz.py @@ -15,6 +15,7 @@ class QuizGame: self.active = False self.players = {} # user_id: {'score': int, 'current_q': int, 'answered': set()} self.questions = [] # Loaded from JSON + self.first_correct = {} # q_idx: user_id self.load_questions() def start_game(self, quizmaster_id): @@ -23,7 +24,9 @@ class QuizGame: if self.active: return "Quiz already running." self.active = True + logger.debug(f"QuizMaster: {quizmaster_id} started a new quiz round.") self.players = {} + self.first_correct = {} # Reset on new game self.load_questions() return "Quiz started! Players can now join." @@ -31,7 +34,8 @@ class QuizGame: try: with open(QUIZ_JSON, 'r') as f: self.questions = json.load(f) - random.shuffle(self.questions) + # Shuffle questions to ensure randomness each game + #random.shuffle(self.questions) except Exception as e: logger.error(f"Failed to load quiz questions: {e}") self.questions = [] @@ -40,6 +44,7 @@ class QuizGame: if not self.active or str(quizmaster_id) not in self.quizmaster: return "Only the quizmaster can stop the quiz." return_msg = "Quiz stopped! Final scores:\n" + self.top_three() + logger.debug(f"QuizMaster: {quizmaster_id} stopped the quiz.") self.active = False self.players = {} return return_msg @@ -50,12 +55,14 @@ class QuizGame: if user_id in self.players: return "You are already in the quiz." self.players[user_id] = {'score': 0, 'current_q': 0, 'answered': set()} - reminder = f"Joined!\n'Q: ' to answer, 'Q: ?' for more.\n" + reminder = f"Joined!\n'Q: ' 'Q: ?' for more.\n" + logger.debug(f"QuizMaster: Player {user_id} joined the round.") return reminder + self.next_question(user_id) def leave(self, user_id): if user_id in self.players: del self.players[user_id] + logger.debug(f"QuizMaster: Player {user_id} left the round.") return "You left the quiz." return "You are not in the quiz." @@ -72,6 +79,7 @@ class QuizGame: if "answers" in q: for i, opt in enumerate(q['answers']): msg += f"{chr(65+i)}. {opt}\n" + msg = msg.strip() return msg def answer(self, user_id, answer): @@ -86,11 +94,14 @@ class QuizGame: q = self.questions[q_idx] # Check if it's multiple choice or free-text if "answers" in q and "correct" in q: - # Multiple choice try: ans_idx = ord(answer.upper()) - 65 if ans_idx == q['correct']: player['score'] += 1 + # Track first correct answer + if q_idx not in self.first_correct: + self.first_correct[q_idx] = user_id + logger.info(f"QuizMaster: Question {q_idx+1} first user with correct answer by {user_id}") result = "Correct! πŸŽ‰" else: result = f"Wrong. Correct answer: {chr(65+q['correct'])}" @@ -100,11 +111,13 @@ class QuizGame: except Exception: return "Invalid answer. Use A, B, C, etc." elif "answer" in q: - # Free-text answer user_ans = answer.strip().lower() correct_ans = str(q['answer']).strip().lower() if user_ans == correct_ans: player['score'] += 1 + if q_idx not in self.first_correct: + self.first_correct[q_idx] = user_id + logger.info(f"QuizMaster: Question {q_idx+1} first user with correct answer by {user_id}") result = "Correct! πŸŽ‰" else: result = f"Wrong. Correct answer: {q['answer']}" @@ -118,9 +131,10 @@ class QuizGame: if not self.players: return "No players in the quiz." ranking = sorted(self.players.items(), key=lambda x: x[1]['score'], reverse=True) - msg = "πŸ† Top 3 Players:\n" - for i, (uid, pdata) in enumerate(ranking[:3]): - msg += f"{i+1}. {uid}: {pdata['score']}\n" + count = min(3, len(ranking)) + msg = f"πŸ† Top {count} Player{'s' if count > 1 else ''}:\n" + for idx, (uid, pdata) in enumerate(ranking[:count], start=1): + msg += f"{idx}. {uid}: @{pdata['score']}\n" return msg def broadcast(self, quizmaster_id, message):