Ability to play Uno implemented in early stages!

This commit is contained in:
2026-01-30 21:43:20 +02:00
parent 5b1163c7af
commit 0a9145728e
6 changed files with 687 additions and 1 deletions

195
bot/commands/uno.py Normal file
View File

@@ -0,0 +1,195 @@
"""
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 <room_code>
"""
if not room_code:
await message.channel.send("🎴 Please provide a room code! Usage: `!uno join <ROOM_CODE>`")
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 <code>` 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 <subcommand> [args]
Subcommands:
!uno join <code> - 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 <ROOM_CODE>`")
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 <CODE>` to make me join!\n4. I'll play automatically and trash talk in chat! 🎶",
color=discord.Color.green()
)
commands = [
("!uno join <CODE>", "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)