196 lines
6.7 KiB
Python
196 lines
6.7 KiB
Python
|
|
"""
|
||
|
|
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)
|