2026-04-24 13:30:54 +03:00
|
|
|
# utils/activities.py
|
|
|
|
|
"""
|
|
|
|
|
Mood-based Discord activity status system.
|
|
|
|
|
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
Activity display is driven by the autonomous engine's mood energy profiles:
|
|
|
|
|
- High-energy moods (excited, bubbly) → almost always show an activity
|
|
|
|
|
- Low-energy moods (sleepy, melancholy) → mostly idle, occasionally active
|
|
|
|
|
- Manual override via Web UI bypasses automatic behavior
|
|
|
|
|
|
|
|
|
|
Supports 5 activity types: listening, playing, watching, competing, streaming.
|
2026-04-24 13:30:54 +03:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
import random
|
2026-04-28 00:18:25 +03:00
|
|
|
import tempfile
|
|
|
|
|
import threading
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
import time
|
2026-04-24 13:30:54 +03:00
|
|
|
import yaml
|
|
|
|
|
import discord
|
|
|
|
|
import globals
|
|
|
|
|
from utils.logger import get_logger
|
|
|
|
|
|
|
|
|
|
logger = get_logger('activity')
|
|
|
|
|
|
|
|
|
|
ACTIVITIES_FILE = os.path.join(os.path.dirname(os.path.dirname(__file__)), "activities.yaml")
|
|
|
|
|
|
2026-04-28 00:18:25 +03:00
|
|
|
# Discord activity name character limit
|
|
|
|
|
DISCORD_ACTIVITY_NAME_MAX = 128
|
|
|
|
|
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
# All valid activity types
|
|
|
|
|
VALID_ACTIVITY_TYPES = {"listening", "playing", "watching", "competing", "streaming"}
|
|
|
|
|
|
|
|
|
|
# ── Activity probability per mood (derived from autonomous engine energy profiles) ──
|
|
|
|
|
# Value = probability that the bot WILL have an activity (vs being idle).
|
|
|
|
|
ACTIVITY_PROBABILITY = {
|
|
|
|
|
# Normal moods
|
|
|
|
|
"asleep": 0.00,
|
|
|
|
|
"sleepy": 0.15,
|
|
|
|
|
"melancholy": 0.25,
|
|
|
|
|
"shy": 0.30,
|
|
|
|
|
"irritated": 0.40,
|
|
|
|
|
"neutral": 0.45,
|
|
|
|
|
"serious": 0.50,
|
|
|
|
|
"romantic": 0.55,
|
|
|
|
|
"curious": 0.60,
|
|
|
|
|
"angry": 0.60,
|
|
|
|
|
"flirty": 0.65,
|
|
|
|
|
"silly": 0.75,
|
|
|
|
|
"bubbly": 0.80,
|
|
|
|
|
"excited": 0.85,
|
|
|
|
|
# Evil moods
|
|
|
|
|
"melancholic": 0.25,
|
|
|
|
|
"bored": 0.35,
|
|
|
|
|
"contemptuous": 0.45,
|
|
|
|
|
"evil_neutral": 0.50,
|
|
|
|
|
"sarcastic": 0.55,
|
|
|
|
|
"jealous": 0.60,
|
|
|
|
|
"cunning": 0.65,
|
|
|
|
|
"aggressive": 0.70,
|
|
|
|
|
"playful_cruel": 0.70,
|
|
|
|
|
"manic": 0.85,
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-28 00:18:25 +03:00
|
|
|
# ── Thread lock for all shared mutable state ──
|
|
|
|
|
_state_lock = threading.Lock()
|
|
|
|
|
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
# ── Manual override state ──
|
|
|
|
|
_manual_override = False
|
|
|
|
|
_manual_override_until = 0.0 # Unix timestamp; 0 = no override
|
|
|
|
|
MANUAL_OVERRIDE_DURATION = 1800 # 30 minutes
|
|
|
|
|
|
|
|
|
|
# ── Current activity tracking ──
|
|
|
|
|
_current_activity = None # dict: {type, name, state, url} or None
|
|
|
|
|
|
2026-04-24 13:30:54 +03:00
|
|
|
# Cache: (data_dict, file_mtime)
|
|
|
|
|
_activities_cache = None
|
|
|
|
|
_cache_mtime = 0.0
|
|
|
|
|
|
|
|
|
|
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
|
# YAML Loading / Saving
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
|
|
2026-04-24 13:30:54 +03:00
|
|
|
def _load_activities(force=False):
|
2026-04-28 00:18:25 +03:00
|
|
|
"""Load activities.yaml with file-mtime-based caching. Returns a deep copy."""
|
2026-04-24 13:30:54 +03:00
|
|
|
global _activities_cache, _cache_mtime
|
|
|
|
|
|
2026-04-28 00:18:25 +03:00
|
|
|
with _state_lock:
|
|
|
|
|
try:
|
|
|
|
|
mtime = os.path.getmtime(ACTIVITIES_FILE)
|
|
|
|
|
except OSError:
|
|
|
|
|
logger.warning(f"Activities file not found: {ACTIVITIES_FILE}")
|
|
|
|
|
return {"normal": {}, "evil": {}}
|
|
|
|
|
|
|
|
|
|
if not force and _activities_cache is not None and mtime == _cache_mtime:
|
|
|
|
|
# Return a deep copy so callers cannot mutate the cache
|
|
|
|
|
import copy
|
|
|
|
|
return copy.deepcopy(_activities_cache)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
with open(ACTIVITIES_FILE, "r", encoding="utf-8") as f:
|
|
|
|
|
data = yaml.safe_load(f) or {}
|
|
|
|
|
_activities_cache = data
|
|
|
|
|
_cache_mtime = mtime
|
|
|
|
|
logger.debug(f"Loaded activities from {ACTIVITIES_FILE}")
|
|
|
|
|
import copy
|
|
|
|
|
return copy.deepcopy(data)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to load activities file: {e}")
|
|
|
|
|
if _activities_cache is not None:
|
|
|
|
|
import copy
|
|
|
|
|
return copy.deepcopy(_activities_cache)
|
|
|
|
|
return {"normal": {}, "evil": {}}
|
2026-04-24 13:30:54 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def save_activities(data: dict):
|
2026-04-28 00:18:25 +03:00
|
|
|
"""Write the full activities dict back to YAML using atomic write (temp + rename)."""
|
2026-04-24 13:30:54 +03:00
|
|
|
global _activities_cache, _cache_mtime
|
|
|
|
|
|
2026-04-28 00:18:25 +03:00
|
|
|
with _state_lock:
|
|
|
|
|
try:
|
|
|
|
|
# Atomic write: write to temp file in same directory, then rename
|
|
|
|
|
dir_name = os.path.dirname(ACTIVITIES_FILE)
|
|
|
|
|
fd, tmp_path = tempfile.mkstemp(dir=dir_name, suffix=".yaml.tmp")
|
|
|
|
|
try:
|
|
|
|
|
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
|
|
|
yaml.dump(data, f, default_flow_style=False, allow_unicode=True, sort_keys=False)
|
|
|
|
|
os.replace(tmp_path, ACTIVITIES_FILE)
|
|
|
|
|
except BaseException:
|
|
|
|
|
# Clean up temp file on failure
|
|
|
|
|
try:
|
|
|
|
|
os.unlink(tmp_path)
|
|
|
|
|
except OSError:
|
|
|
|
|
pass
|
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
_activities_cache = data
|
|
|
|
|
_cache_mtime = os.path.getmtime(ACTIVITIES_FILE)
|
|
|
|
|
logger.info(f"Saved activities to {ACTIVITIES_FILE}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to save activities file: {e}")
|
|
|
|
|
raise
|
2026-04-24 13:30:54 +03:00
|
|
|
|
|
|
|
|
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
|
# CRUD for activity data (used by Web UI)
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
|
|
2026-04-24 13:30:54 +03:00
|
|
|
def get_all_activities() -> dict:
|
2026-04-28 00:18:25 +03:00
|
|
|
"""Return the full activities dict (normal + evil sections). Returns a deep copy."""
|
2026-04-24 13:30:54 +03:00
|
|
|
return _load_activities()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_activities_for_mood(mood_name: str, is_evil: bool = False) -> list:
|
|
|
|
|
"""Return the activity list for a specific mood. Returns empty list if not found."""
|
|
|
|
|
section = "evil" if is_evil else "normal"
|
|
|
|
|
data = _load_activities()
|
|
|
|
|
return data.get(section, {}).get(mood_name, [])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def set_activities_for_mood(mood_name: str, is_evil: bool, activities: list):
|
|
|
|
|
"""Validate and save updated activity list for a mood.
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
|
2026-04-24 13:30:54 +03:00
|
|
|
Args:
|
|
|
|
|
mood_name: mood key (e.g. "bubbly", "aggressive")
|
|
|
|
|
is_evil: True for evil section, False for normal
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
activities: list of dicts with keys {type, name, weight, [state], [url]}
|
|
|
|
|
|
2026-04-24 13:30:54 +03:00
|
|
|
Raises:
|
|
|
|
|
ValueError: if validation fails
|
|
|
|
|
"""
|
|
|
|
|
for i, entry in enumerate(activities):
|
|
|
|
|
if not isinstance(entry, dict):
|
|
|
|
|
raise ValueError(f"Entry {i} must be a dict, got {type(entry).__name__}")
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
if entry.get("type") not in VALID_ACTIVITY_TYPES:
|
|
|
|
|
raise ValueError(
|
|
|
|
|
f"Entry {i} has invalid type '{entry.get('type')}', "
|
|
|
|
|
f"must be one of: {', '.join(sorted(VALID_ACTIVITY_TYPES))}"
|
|
|
|
|
)
|
2026-04-24 13:30:54 +03:00
|
|
|
if not entry.get("name") or not isinstance(entry["name"], str):
|
|
|
|
|
raise ValueError(f"Entry {i} must have a non-empty string 'name'")
|
2026-04-28 00:18:25 +03:00
|
|
|
if len(entry["name"]) > DISCORD_ACTIVITY_NAME_MAX:
|
|
|
|
|
raise ValueError(f"Entry {i} name exceeds {DISCORD_ACTIVITY_NAME_MAX} characters")
|
2026-04-24 13:30:54 +03:00
|
|
|
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")
|
2026-04-24 16:46:39 +03:00
|
|
|
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")
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
if "url" in entry and entry["url"] is not None and not isinstance(entry["url"], str):
|
|
|
|
|
raise ValueError(f"Entry {i} 'url' must be a string if provided")
|
2026-04-28 00:18:25 +03:00
|
|
|
if entry.get("type") == "streaming" and not entry.get("url"):
|
|
|
|
|
raise ValueError(f"Entry {i} is streaming type but has no url")
|
2026-04-24 13:30:54 +03:00
|
|
|
|
|
|
|
|
section = "evil" if is_evil else "normal"
|
|
|
|
|
data = _load_activities()
|
|
|
|
|
if section not in data:
|
|
|
|
|
data[section] = {}
|
|
|
|
|
data[section][mood_name] = activities
|
|
|
|
|
save_activities(data)
|
|
|
|
|
|
|
|
|
|
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
|
# Activity Selection
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
|
|
2026-04-24 13:30:54 +03:00
|
|
|
def pick_activity_for_mood(mood_name: str, is_evil: bool = False):
|
|
|
|
|
"""Pick a weighted-random activity for a mood.
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
|
2026-04-28 00:18:25 +03:00
|
|
|
Validates entries and skips malformed ones with a warning.
|
|
|
|
|
|
2026-04-24 13:30:54 +03:00
|
|
|
Returns:
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
dict: {"type": ..., "name": ..., "state": ..., "url": ...}
|
|
|
|
|
state and url may be None.
|
2026-04-28 00:18:25 +03:00
|
|
|
Returns None if mood has no valid entries.
|
2026-04-24 13:30:54 +03:00
|
|
|
"""
|
|
|
|
|
activities = get_activities_for_mood(mood_name, is_evil)
|
|
|
|
|
|
|
|
|
|
if not activities:
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
return None
|
2026-04-24 13:30:54 +03:00
|
|
|
|
2026-04-28 00:18:25 +03:00
|
|
|
# Validate entries, skipping malformed ones
|
|
|
|
|
valid = []
|
|
|
|
|
weights = []
|
|
|
|
|
for i, entry in enumerate(activities):
|
|
|
|
|
if not isinstance(entry, dict):
|
|
|
|
|
logger.warning(f"Skipping non-dict entry {i} in {'evil/' if is_evil else ''}{mood_name}")
|
|
|
|
|
continue
|
|
|
|
|
if "type" not in entry or "name" not in entry:
|
|
|
|
|
logger.warning(f"Skipping entry {i} missing 'type' or 'name' in {'evil/' if is_evil else ''}{mood_name}: {entry}")
|
|
|
|
|
continue
|
|
|
|
|
if entry["type"] not in VALID_ACTIVITY_TYPES:
|
|
|
|
|
logger.warning(f"Skipping entry {i} with unrecognized type '{entry['type']}' in {'evil/' if is_evil else ''}{mood_name}")
|
|
|
|
|
continue
|
|
|
|
|
w = entry.get("weight", 1)
|
|
|
|
|
if not isinstance(w, int) or w < 1:
|
|
|
|
|
logger.warning(f"Skipping entry {i} with invalid weight {w} in {'evil/' if is_evil else ''}{mood_name}")
|
|
|
|
|
continue
|
|
|
|
|
valid.append(entry)
|
|
|
|
|
weights.append(w)
|
|
|
|
|
|
|
|
|
|
if not valid:
|
|
|
|
|
logger.warning(f"No valid entries for {'evil/' if is_evil else ''}{mood_name}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
chosen = random.choices(valid, weights=weights, k=1)[0]
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
return {
|
|
|
|
|
"type": chosen["type"],
|
|
|
|
|
"name": chosen["name"],
|
|
|
|
|
"state": chosen.get("state"),
|
|
|
|
|
"url": chosen.get("url"),
|
|
|
|
|
}
|
2026-04-24 13:30:54 +03:00
|
|
|
|
|
|
|
|
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
def should_have_activity(mood_name: str) -> bool:
|
|
|
|
|
"""Decide whether the bot should show an activity for this mood.
|
|
|
|
|
|
|
|
|
|
Based on mood energy: high-energy moods are more likely to be active,
|
|
|
|
|
low-energy moods are more likely to be idle.
|
|
|
|
|
"""
|
|
|
|
|
probability = ACTIVITY_PROBABILITY.get(mood_name, 0.45)
|
|
|
|
|
return random.random() < probability
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
|
# Manual Override
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
|
|
|
|
|
|
def is_manual_override_active() -> bool:
|
2026-04-28 00:18:25 +03:00
|
|
|
"""Check if a manual override is in effect (hasn't expired). Thread-safe."""
|
|
|
|
|
with _state_lock:
|
|
|
|
|
global _manual_override
|
|
|
|
|
if not _manual_override:
|
|
|
|
|
return False
|
|
|
|
|
if _manual_override_until > 0 and time.time() > _manual_override_until:
|
|
|
|
|
_manual_override = False
|
|
|
|
|
logger.info("Manual override expired, returning to automatic mode")
|
|
|
|
|
return False
|
|
|
|
|
return True
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def set_manual_override(duration: int = MANUAL_OVERRIDE_DURATION):
|
2026-04-28 00:18:25 +03:00
|
|
|
"""Activate manual override for the given duration (seconds). Thread-safe."""
|
|
|
|
|
with _state_lock:
|
|
|
|
|
global _manual_override, _manual_override_until
|
|
|
|
|
_manual_override = True
|
|
|
|
|
expiry = time.time() + duration
|
|
|
|
|
_manual_override_until = expiry
|
|
|
|
|
logger.info(f"Manual override activated for {duration}s (expires at {time.strftime('%H:%M:%S', time.localtime(expiry))})")
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def clear_manual_override():
|
2026-04-28 00:18:25 +03:00
|
|
|
"""Deactivate manual override immediately. Thread-safe."""
|
|
|
|
|
with _state_lock:
|
|
|
|
|
global _manual_override, _manual_override_until
|
|
|
|
|
_manual_override = False
|
|
|
|
|
_manual_override_until = 0.0
|
|
|
|
|
logger.info("Manual override cleared")
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
|
# Current Activity Tracking
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
|
|
|
|
|
|
def get_current_activity():
|
2026-04-28 00:18:25 +03:00
|
|
|
"""Return the current activity dict or None if idle. Thread-safe."""
|
|
|
|
|
with _state_lock:
|
|
|
|
|
return _current_activity
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def _set_current_activity(activity_dict):
|
2026-04-28 00:18:25 +03:00
|
|
|
"""Update the tracked current activity. Thread-safe."""
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
global _current_activity
|
2026-04-28 00:18:25 +03:00
|
|
|
with _state_lock:
|
|
|
|
|
_current_activity = activity_dict
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
|
# Discord Presence Updates
|
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
|
|
|
|
|
|
def _build_activity(payload: dict):
|
2026-04-28 00:18:25 +03:00
|
|
|
"""Build a discord.Activity (or discord.Streaming) from a payload dict.
|
|
|
|
|
|
|
|
|
|
Logs a warning if the activity type is unrecognized (falls back to playing).
|
|
|
|
|
"""
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
atype = payload["type"]
|
|
|
|
|
name = payload["name"]
|
2026-04-28 00:18:25 +03:00
|
|
|
state = payload.get("state") or None # normalize empty string to None
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
url = payload.get("url")
|
|
|
|
|
|
|
|
|
|
if atype == "streaming" and url:
|
|
|
|
|
return discord.Streaming(name=name, url=url)
|
|
|
|
|
|
|
|
|
|
type_map = {
|
|
|
|
|
"listening": discord.ActivityType.listening,
|
|
|
|
|
"playing": discord.ActivityType.playing,
|
|
|
|
|
"watching": discord.ActivityType.watching,
|
|
|
|
|
"competing": discord.ActivityType.competing,
|
|
|
|
|
"streaming": discord.ActivityType.streaming, # fallback without url
|
|
|
|
|
}
|
2026-04-28 00:18:25 +03:00
|
|
|
resolved_type = type_map.get(atype)
|
|
|
|
|
if resolved_type is None:
|
|
|
|
|
logger.warning(f"Unrecognized activity type '{atype}', falling back to 'playing'")
|
|
|
|
|
resolved_type = discord.ActivityType.playing
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
return discord.Activity(
|
2026-04-28 00:18:25 +03:00
|
|
|
type=resolved_type,
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
name=name,
|
|
|
|
|
state=state,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _activity_label(payload: dict) -> str:
|
|
|
|
|
"""Human-readable label for logging."""
|
|
|
|
|
atype = payload["type"]
|
|
|
|
|
name = payload["name"]
|
|
|
|
|
prefixes = {
|
|
|
|
|
"listening": "Listening to",
|
|
|
|
|
"playing": "Playing",
|
|
|
|
|
"watching": "Watching",
|
|
|
|
|
"competing": "Competing in",
|
|
|
|
|
"streaming": "Streaming",
|
|
|
|
|
}
|
|
|
|
|
label = f"{prefixes.get(atype, 'Playing')} {name}"
|
|
|
|
|
state = payload.get("state")
|
|
|
|
|
if state:
|
|
|
|
|
label += f" ({state})"
|
|
|
|
|
return label
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def update_bot_presence(mood_name: str, is_evil: bool = False, force: bool = False):
|
2026-04-24 13:30:54 +03:00
|
|
|
"""Update the bot's Discord presence based on the current mood.
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
|
|
|
|
|
- asleep: idle status, no activity
|
|
|
|
|
- Manual override active: skip (unless force=True)
|
|
|
|
|
- Energy-based probability: may choose to be idle instead of showing an activity
|
|
|
|
|
- force=True bypasses both manual override and probability (used by on_ready and manual set)
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
mood_name: current mood key
|
|
|
|
|
is_evil: whether evil mode is active
|
|
|
|
|
force: bypass manual override and probability checks
|
2026-04-24 13:30:54 +03:00
|
|
|
"""
|
|
|
|
|
if not globals.client or not globals.client.is_ready():
|
|
|
|
|
logger.debug("Bot not ready, skipping presence update")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
# asleep → always idle
|
2026-04-24 13:30:54 +03:00
|
|
|
if mood_name == "asleep":
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
_set_current_activity(None)
|
2026-04-24 13:30:54 +03:00
|
|
|
await globals.client.change_presence(
|
|
|
|
|
status=discord.Status.idle,
|
|
|
|
|
activity=None
|
|
|
|
|
)
|
|
|
|
|
logger.info("Set presence: idle (asleep)")
|
|
|
|
|
return
|
|
|
|
|
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
# Check manual override (skip unless forced)
|
|
|
|
|
if not force and is_manual_override_active():
|
|
|
|
|
logger.debug("Manual override active, skipping automatic presence update")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Energy-based probability: should we show an activity at all?
|
|
|
|
|
if not force and not should_have_activity(mood_name):
|
|
|
|
|
await clear_bot_presence()
|
|
|
|
|
logger.info(f"Decided to be idle (mood={'evil/' if is_evil else ''}{mood_name})")
|
|
|
|
|
return
|
2026-04-24 13:30:54 +03:00
|
|
|
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
# Pick a random activity for this mood
|
|
|
|
|
chosen = pick_activity_for_mood(mood_name, is_evil)
|
|
|
|
|
if not chosen:
|
|
|
|
|
# No activities defined for this mood → idle
|
|
|
|
|
await clear_bot_presence()
|
|
|
|
|
logger.info(f"No activities for {'evil/' if is_evil else ''}{mood_name}, staying idle")
|
|
|
|
|
return
|
2026-04-24 13:30:54 +03:00
|
|
|
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
activity = _build_activity(chosen)
|
|
|
|
|
label = _activity_label(chosen)
|
|
|
|
|
_set_current_activity(chosen)
|
2026-04-24 16:46:39 +03:00
|
|
|
|
|
|
|
|
await globals.client.change_presence(status=discord.Status.online, activity=activity)
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
logger.info(f"Set presence: {label} (mood={'evil/' if is_evil else ''}{mood_name})")
|
2026-04-24 13:30:54 +03:00
|
|
|
|
|
|
|
|
except Exception as e:
|
2026-04-28 00:18:25 +03:00
|
|
|
logger.error(f"Failed to update bot presence: {e}", exc_info=True)
|
2026-04-24 13:30:54 +03:00
|
|
|
|
|
|
|
|
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
async def set_activity_manual(activity_type: str, name: str, state: str = None, url: str = None):
|
|
|
|
|
"""Manually set the bot's activity (bypasses mood system).
|
|
|
|
|
|
|
|
|
|
Raises:
|
2026-04-28 00:18:25 +03:00
|
|
|
ValueError: if activity_type is invalid, name too long, or streaming lacks url
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
RuntimeError: if bot is not ready
|
|
|
|
|
"""
|
|
|
|
|
if activity_type not in VALID_ACTIVITY_TYPES:
|
|
|
|
|
raise ValueError(f"Invalid type '{activity_type}', must be one of: {', '.join(sorted(VALID_ACTIVITY_TYPES))}")
|
|
|
|
|
if not name or not isinstance(name, str):
|
|
|
|
|
raise ValueError("name must be a non-empty string")
|
2026-04-28 00:18:25 +03:00
|
|
|
if len(name) > DISCORD_ACTIVITY_NAME_MAX:
|
|
|
|
|
raise ValueError(f"name exceeds {DISCORD_ACTIVITY_NAME_MAX} characters")
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
if activity_type == "streaming" and not url:
|
|
|
|
|
raise ValueError("streaming type requires a url")
|
|
|
|
|
|
|
|
|
|
if not globals.client or not globals.client.is_ready():
|
|
|
|
|
raise RuntimeError("Bot is not ready")
|
|
|
|
|
|
|
|
|
|
payload = {"type": activity_type, "name": name, "state": state, "url": url}
|
|
|
|
|
activity = _build_activity(payload)
|
|
|
|
|
label = _activity_label(payload)
|
|
|
|
|
_set_current_activity(payload)
|
|
|
|
|
set_manual_override()
|
|
|
|
|
|
|
|
|
|
await globals.client.change_presence(status=discord.Status.online, activity=activity)
|
|
|
|
|
logger.info(f"Set presence (manual): {label}")
|
|
|
|
|
|
|
|
|
|
|
2026-04-24 13:30:54 +03:00
|
|
|
async def clear_bot_presence():
|
|
|
|
|
"""Clear the bot's activity (set to online with no activity)."""
|
|
|
|
|
if not globals.client or not globals.client.is_ready():
|
|
|
|
|
return
|
|
|
|
|
try:
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
_set_current_activity(None)
|
2026-04-24 13:30:54 +03:00
|
|
|
await globals.client.change_presence(status=discord.Status.online, activity=None)
|
|
|
|
|
logger.info("Cleared bot presence")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to clear bot presence: {e}")
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
async def clear_activity_manual():
|
|
|
|
|
"""Manually clear the bot's activity and activate manual override."""
|
|
|
|
|
set_manual_override()
|
|
|
|
|
await clear_bot_presence()
|
|
|
|
|
logger.info("Cleared presence (manual override)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def release_manual_override():
|
2026-04-28 00:18:25 +03:00
|
|
|
"""Release manual override and immediately recalculate presence from current mood.
|
|
|
|
|
|
|
|
|
|
Uses force=True so the bot always gets an activity instead of potentially
|
|
|
|
|
going idle right away (which would be confusing UX after clicking "Return to Auto").
|
|
|
|
|
"""
|
Refactor activity system: energy-based probability, manual override, all 5 activity types
- Rewrite utils/activities.py with mood energy-driven activity probability
(high-energy moods like excited/bubbly show activity ~80-85% of the time,
low-energy moods like sleepy/melancholy only ~15-25%)
- Add manual override system with 30-min auto-expiry for Web UI control
- Support all 5 Discord activity types: listening, playing, watching,
competing, streaming (with purple LIVE badge via discord.Streaming)
- Add current activity tracking (get_current_activity)
- Add force=True param to update_bot_presence for on_ready (bot.py)
- Add 4 new API routes for manual override:
GET/POST/DELETE /activities/current, POST /activities/current/auto
- Expand activities.yaml from 139 to 157 entries, adding watching,
competing, and streaming entries across 11 moods
- Update Web UI: activity type dropdown with all 5 types, conditional
URL field for streaming, 'Current Activity' override panel with
set/clear/auto controls, type-aware icons and labels
2026-04-27 23:39:18 +03:00
|
|
|
clear_manual_override()
|
2026-04-28 00:18:25 +03:00
|
|
|
try:
|
|
|
|
|
if globals.EVIL_MODE:
|
|
|
|
|
mood = globals.EVIL_DM_MOOD
|
|
|
|
|
is_evil = True
|
|
|
|
|
else:
|
|
|
|
|
mood = globals.DM_MOOD
|
|
|
|
|
is_evil = False
|
|
|
|
|
await update_bot_presence(mood, is_evil=is_evil, force=True)
|
|
|
|
|
logger.info(f"Released manual override, set presence for mood={'evil/' if is_evil else ''}{mood}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to recalculate presence after releasing override: {e}")
|