2026-01-30 21:43:20 +02:00
"""
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
2026-02-18 12:01:08 +02:00
from utils . task_tracker import create_tracked_task
2026-01-30 21:43:20 +02:00
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
2026-02-18 12:01:08 +02:00
create_tracked_task ( player . play_game ( ) , task_name = f " uno_game_ { room_code } " )
2026-01-30 21:43:20 +02:00
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 } \n Channel: <# { 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:** \n 1. Create a room at http://192.168.1.2:3002 \n 2. Copy the room code \n 3. Use `!uno join <CODE>` to make me join! \n 4. 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 )