feat: add 'state' field to mood activities for richer Discord presence
- Add 'state' field to all 139 activity entries in activities.yaml - Songs: state shows artist (e.g. 'by kz (livetune)') - Games: state shows genre (e.g. 'Rhythm Game', 'Sandbox', 'FPS') - Update pick_activity_for_mood() to return 3-tuple (type, name, state) - Update update_bot_presence() to pass state to discord.Activity() - Add state validation in set_activities_for_mood() (optional string) - Update Web UI editor: view shows state, edit form has state input - State is fully optional — backward compatible, no breaking changes The 'state' field appears as a secondary text line in Discord profile popup, the richest display possible for bot accounts (full Rich Presence with cover art/buttons is server-side restricted to OAuth applications).
This commit is contained in:
@@ -103,6 +103,8 @@ def set_activities_for_mood(mood_name: str, is_evil: bool, activities: list):
|
||||
raise ValueError(f"Entry {i} must have a non-empty string 'name'")
|
||||
if not isinstance(entry.get("weight", 0), int) or entry.get("weight", 0) < 1:
|
||||
raise ValueError(f"Entry {i} weight must be a positive integer")
|
||||
if "state" in entry and entry["state"] is not None and not isinstance(entry["state"], str):
|
||||
raise ValueError(f"Entry {i} 'state' must be a string if provided")
|
||||
|
||||
section = "evil" if is_evil else "normal"
|
||||
data = _load_activities()
|
||||
@@ -116,19 +118,19 @@ def pick_activity_for_mood(mood_name: str, is_evil: bool = False):
|
||||
"""Pick a weighted-random activity for a mood.
|
||||
|
||||
Returns:
|
||||
tuple: (activity_type, name) e.g. ("listening", "World is Mine")
|
||||
Returns ("listening", "Vocaloid") as fallback if mood has no entries.
|
||||
tuple: (activity_type, name, state) e.g. ("listening", "World is Mine", "by ryo (supercell)")
|
||||
state may be None if not defined. Fallback: ("listening", "Vocaloid", None)
|
||||
"""
|
||||
activities = get_activities_for_mood(mood_name, is_evil)
|
||||
|
||||
if not activities:
|
||||
logger.debug(f"No activities defined for {'evil/' if is_evil else ''}{mood_name}, using fallback")
|
||||
return ("listening", "Vocaloid")
|
||||
return ("listening", "Vocaloid", None)
|
||||
|
||||
# Weighted random selection
|
||||
weights = [entry.get("weight", 1) for entry in activities]
|
||||
chosen = random.choices(activities, weights=weights, k=1)[0]
|
||||
return (chosen["type"], chosen["name"])
|
||||
return (chosen["type"], chosen["name"], chosen.get("state"))
|
||||
|
||||
|
||||
async def update_bot_presence(mood_name: str, is_evil: bool = False):
|
||||
@@ -151,16 +153,19 @@ async def update_bot_presence(mood_name: str, is_evil: bool = False):
|
||||
logger.info("Set presence: idle (asleep)")
|
||||
return
|
||||
|
||||
activity_type, name = pick_activity_for_mood(mood_name, is_evil)
|
||||
activity_type, name, state = pick_activity_for_mood(mood_name, is_evil)
|
||||
|
||||
if activity_type == "listening":
|
||||
activity = discord.Activity(type=discord.ActivityType.listening, name=name)
|
||||
activity = discord.Activity(type=discord.ActivityType.listening, name=name, state=state)
|
||||
log_label = f"Listening to {name}"
|
||||
else:
|
||||
activity = discord.Game(name=name)
|
||||
activity = discord.Activity(type=discord.ActivityType.playing, name=name, state=state)
|
||||
log_label = f"Playing {name}"
|
||||
|
||||
await globals.client.change_presence(activity=activity)
|
||||
if state:
|
||||
log_label += f" ({state})"
|
||||
|
||||
await globals.client.change_presence(status=discord.Status.online, activity=activity)
|
||||
logger.info(f"Set presence: {log_label} (mood={'evil/' if is_evil else ''}{mood_name})")
|
||||
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user