""" UNO Game Commands for Miku Allows Miku to play UNO games via Discord """ import discord import asyncio import requests import json import logging from typing import Optional, Dict, Any from utils.logger import get_logger logger = get_logger('uno') # UNO game server configuration (use host IP from container) UNO_SERVER_URL = "http://192.168.1.2:5000" UNO_CLIENT_URL = "http://192.168.1.2:3002" # Active games tracking active_uno_games: Dict[str, Dict[str, Any]] = {} async def join_uno_game(message: discord.Message, room_code: str): """ Miku joins an UNO game as Player 2 Usage: !uno join """ if not room_code: await message.channel.send("🎴 Please provide a room code! Usage: `!uno join `") return room_code = room_code.strip() # Keep exact case - don't convert to uppercase! # Check if already in a game if room_code in active_uno_games: await message.channel.send(f"🎴 I'm already playing in room **{room_code}**! Let me finish this game first~ 🎶") return await message.channel.send(f"🎤 Joining UNO game **{room_code}** as Player 2! Time to show you how it's done! ✨") try: # Import here to avoid circular imports from utils.uno_game import MikuUnoPlayer # Define cleanup callback to remove from active games async def cleanup_game(code: str): if code in active_uno_games: logger.info(f"[UNO] Removing room {code} from active games") del active_uno_games[code] # Create Miku's player instance with cleanup callback player = MikuUnoPlayer(room_code, message.channel, cleanup_callback=cleanup_game) # Join the game (this will open browser and join) success = await player.join_game() if success: active_uno_games[room_code] = { 'player': player, 'channel': message.channel, 'started_by': message.author.id } await message.channel.send(f"✅ Joined room **{room_code}**! Waiting for Player 1 to start the game... 🎮") # Start the game loop asyncio.create_task(player.play_game()) else: await message.channel.send(f"❌ Couldn't join room **{room_code}**. Make sure the room exists and has space!") except Exception as e: logger.error(f"Error joining UNO game: {e}", exc_info=True) await message.channel.send(f"❌ Oops! Something went wrong: {str(e)}") async def list_uno_games(message: discord.Message): """ List active UNO games Miku is in Usage: !uno list """ if not active_uno_games: await message.channel.send("🎴 I'm not in any UNO games right now! Create a room and use `!uno join ` to make me play! 🎤") return embed = discord.Embed( title="🎴 Active UNO Games", description="Here are the games I'm currently playing:", color=discord.Color.blue() ) for room_code, game_info in active_uno_games.items(): player = game_info['player'] status = "🎮 Playing" if player.is_game_active() else "⏸️ Waiting" embed.add_field( name=f"Room: {room_code}", value=f"Status: {status}\nChannel: <#{game_info['channel'].id}>", inline=False ) await message.channel.send(embed=embed) async def quit_uno_game(message: discord.Message, room_code: Optional[str] = None): """ Miku quits an UNO game Usage: !uno quit [room_code] """ if not room_code: # Quit all games if not active_uno_games: await message.channel.send("🎴 I'm not in any games right now!") return for code, game_info in list(active_uno_games.items()): await game_info['player'].quit_game() del active_uno_games[code] await message.channel.send("👋 I quit all my UNO games! See you next time~ 🎶") return room_code = room_code.strip() # Keep exact case if room_code not in active_uno_games: await message.channel.send(f"🤔 I'm not in room **{room_code}**!") return game_info = active_uno_games[room_code] await game_info['player'].quit_game() del active_uno_games[room_code] await message.channel.send(f"👋 I left room **{room_code}**! That was fun~ 🎤") async def handle_uno_command(message: discord.Message): """ Main UNO command router Usage: !uno [args] Subcommands: !uno join - Join an existing game as Player 2 !uno list - List active games !uno quit [code] - Quit a game (or all games) !uno help - Show this help """ content = message.content.strip() parts = content.split() if len(parts) == 1: # Just !uno await show_uno_help(message) return subcommand = parts[1].lower() if subcommand == "join": if len(parts) < 3: await message.channel.send("❌ Please provide a room code! Usage: `!uno join `") return await join_uno_game(message, parts[2]) elif subcommand == "list": await list_uno_games(message) elif subcommand == "quit" or subcommand == "leave": room_code = parts[2] if len(parts) > 2 else None await quit_uno_game(message, room_code) elif subcommand == "help": await show_uno_help(message) else: await message.channel.send(f"❌ Unknown command: `{subcommand}`. Use `!uno help` to see available commands!") async def show_uno_help(message: discord.Message): """Show UNO command help""" embed = discord.Embed( title="🎴 Miku's UNO Commands", description="Play UNO with me! I'll join as Player 2 and use my AI to make strategic moves~ 🎤✨\n\n**How to play:**\n1. Create a room at http://192.168.1.2:3002\n2. Copy the room code\n3. Use `!uno join ` to make me join!\n4. I'll play automatically and trash talk in chat! 🎶", color=discord.Color.green() ) commands = [ ("!uno join ", "Make me join your UNO game as Player 2"), ("!uno list", "List all active games I'm playing"), ("!uno quit [CODE]", "Make me quit a game (or all games if no code)"), ("!uno help", "Show this help message"), ] for cmd, desc in commands: embed.add_field(name=cmd, value=desc, inline=False) embed.set_footer(text="I'll trash talk and celebrate in chat during games! 🎶") await message.channel.send(embed=embed)