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:
@@ -1,473 +1,585 @@
|
|||||||
# Mood-Based Activities for Miku Discord Bot
|
|
||||||
# Each mood has a list of activities with:
|
|
||||||
# type: "listening" (🎵) or "playing" (🎮)
|
|
||||||
# name: display text shown in Discord status
|
|
||||||
# weight: integer for weighted random selection (higher = more likely)
|
|
||||||
#
|
|
||||||
# The bot picks a random activity (weighted) each time its mood changes.
|
|
||||||
# You can edit this file directly or via the Web UI Status tab.
|
|
||||||
|
|
||||||
normal:
|
normal:
|
||||||
bubbly:
|
bubbly:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Tell Your World"
|
name: Tell Your World
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by kz (livetune)
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "World is Mine"
|
name: World is Mine
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by ryo (supercell)
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "PoPiPo"
|
name: PoPiPo
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by Lamaze-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Miku Miku ni Shite Ageru♪"
|
name: Miku Miku ni Shite Ageru♪
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by ika
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Love is War"
|
name: Love is War
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by ryo (supercell)
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Hatsune Miku: Project DIVA Mega Mix"
|
name: 'Hatsune Miku: Project DIVA Mega Mix'
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Rhythm Game
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Project SEKAI: Colorful Stage!"
|
name: 'Project SEKAI: Colorful Stage!'
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Rhythm Game
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Hatsune Miku: Project DIVA Future Tone"
|
name: 'Hatsune Miku: Project DIVA Future Tone'
|
||||||
weight: 1
|
weight: 1
|
||||||
|
state: Rhythm Game
|
||||||
excited:
|
excited:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Melt"
|
name: Melt
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by ryo (supercell)
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Electric Angel"
|
name: Electric Angel
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by Yasuo-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Tell Your World"
|
name: Tell Your World
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by kz (livetune)
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "SPiCa"
|
name: SPiCa
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by kentaro-P
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Hatsune Miku: Project DIVA Future Tone"
|
name: 'Hatsune Miku: Project DIVA Future Tone'
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Rhythm Game
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Beat Saber"
|
name: Beat Saber
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: VR Rhythm Game
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "osu!"
|
name: osu!
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Rhythm Game
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Muse Dash"
|
name: Muse Dash
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Rhythm Game
|
||||||
neutral:
|
neutral:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Miku Miku ni Shite Ageru♪"
|
name: Miku Miku ni Shite Ageru♪
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by ika
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "World is Mine"
|
name: World is Mine
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by ryo (supercell)
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Tell Your World"
|
name: Tell Your World
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by kz (livetune)
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Packaged"
|
name: Packaged
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by kz (livetune)
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Minecraft"
|
name: Minecraft
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Sandbox
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Stardew Valley"
|
name: Stardew Valley
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Farming Sim
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Project SEKAI: Colorful Stage!"
|
name: 'Project SEKAI: Colorful Stage!'
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Rhythm Game
|
||||||
sleepy:
|
sleepy:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Yuki no Hahen"
|
name: Yuki no Hahen
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by hachi
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Hajimete no Oto"
|
name: Hajimete no Oto
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by malo
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Kirameki"
|
name: Kirameki
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by baker
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Teo"
|
name: Teo
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by Oster Projekt
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Animal Crossing: New Horizons"
|
name: 'Animal Crossing: New Horizons'
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Life Sim
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Stardew Valley"
|
name: Stardew Valley
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Farming Sim
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "A Short Hike"
|
name: A Short Hike
|
||||||
weight: 1
|
weight: 1
|
||||||
|
state: Exploration
|
||||||
curious:
|
curious:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Kokoro"
|
name: Kokoro
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by Toraboruta-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "The Secret Garden"
|
name: The Secret Garden
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by 40mP
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Maple Dream"
|
name: Maple Dream
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by Oster Projekt
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Deep Sea City Underground"
|
name: Deep Sea City Underground
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by OSTER Projekt
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Minecraft"
|
name: Minecraft
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Sandbox
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Portal 2"
|
name: Portal 2
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Puzzle
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Outer Wilds"
|
name: Outer Wilds
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Exploration
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "The Legend of Zelda: Tears of the Kingdom"
|
name: 'The Legend of Zelda: Tears of the Kingdom'
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Adventure
|
||||||
shy:
|
shy:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Koi wo Sensou"
|
name: Koi wo Sensou
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by ryo (supercell)
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Plastic Voice"
|
name: Plastic Voice
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by Circus-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Tsugihagi Staccato"
|
name: Tsugihagi Staccato
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by 40mP
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "mobius"
|
name: mobius
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by POWAPOWA-P
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Animal Crossing: New Horizons"
|
name: 'Animal Crossing: New Horizons'
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Life Sim
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Hatsune Miku: Project DIVA (Practice Mode)"
|
name: 'Hatsune Miku: Project DIVA (Practice Mode)'
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Rhythm Game
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Stardew Valley"
|
name: Stardew Valley
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Farming Sim
|
||||||
serious:
|
serious:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "This is the Happiness and Peace of Mind Committee"
|
name: This is the Happiness and Peace of Mind Committee
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by Utata-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Hibana"
|
name: Hibana
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by DECO*27
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Uraniwa no Amphibia"
|
name: Uraniwa no Amphibia
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by niki
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Chess"
|
name: Chess
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Strategy
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Final Fantasy XIV"
|
name: Final Fantasy XIV
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: MMORPG
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Civilization VI"
|
name: Civilization VI
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: 4X Strategy
|
||||||
melancholy:
|
melancholy:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Kokoro"
|
name: Kokoro
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by Toraboruta-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "The Disappearance of Hatsune Miku"
|
name: The Disappearance of Hatsune Miku
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by cosMo@Bousou-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Yuki no Hahen"
|
name: Yuki no Hahen
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by hachi
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Prisoner"
|
name: Prisoner
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by PENGUIN PROJECT
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Soundless Voice"
|
name: Soundless Voice
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by hachi
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "NieR: Automata"
|
name: 'NieR: Automata'
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Action RPG
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Final Fantasy X"
|
name: Final Fantasy X
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: JRPG
|
||||||
flirty:
|
flirty:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "World is Mine"
|
name: World is Mine
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by ryo (supercell)
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Love is War"
|
name: Love is War
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by ryo (supercell)
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Romeo and Cinderella"
|
name: Romeo and Cinderella
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by doriko
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Ura Omote Lovers"
|
name: Ura Omote Lovers
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by wowaka
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Project SEKAI: Colorful Stage!"
|
name: 'Project SEKAI: Colorful Stage!'
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Rhythm Game
|
||||||
romantic:
|
romantic:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Romeo and Cinderella"
|
name: Romeo and Cinderella
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by doriko
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Cantarella"
|
name: Cantarella
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by KAITO & Hatsune Miku
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Ai no Uta"
|
name: Ai no Uta
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by Pikotaro-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Koi wo Sensou"
|
name: Koi wo Sensou
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by ryo (supercell)
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Stardew Valley"
|
name: Stardew Valley
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Farming Sim
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Final Fantasy XIV"
|
name: Final Fantasy XIV
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: MMORPG
|
||||||
irritated:
|
irritated:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Ievan Polkka (rock ver.)"
|
name: Ievan Polkka (rock ver.)
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by Otomania
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Two-Faced Lovers"
|
name: Two-Faced Lovers
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by wowaka
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Getting Over It with Bennett Foddy"
|
name: Getting Over It with Bennett Foddy
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Frustration
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Dark Souls III"
|
name: Dark Souls III
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Action RPG
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Elden Ring"
|
name: Elden Ring
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Action RPG
|
||||||
angry:
|
angry:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Two-Faced Lovers"
|
name: Two-Faced Lovers
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by wowaka
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "The Disappearance of Hatsune Miku"
|
name: The Disappearance of Hatsune Miku
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by cosMo@Bousou-P
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "DOOM Eternal"
|
name: DOOM Eternal
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: FPS
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Dark Souls III"
|
name: Dark Souls III
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Action RPG
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Ultrakill"
|
name: Ultrakill
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: FPS
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Hades"
|
name: Hades
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Roguelike
|
||||||
silly:
|
silly:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "PoPiPo"
|
name: PoPiPo
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by Lamaze-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Ievan Polkka"
|
name: Ievan Polkka
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by Otomania
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Nyan Cat"
|
name: Nyan Cat
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by daniwell-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Fukkireta"
|
name: Fukkireta
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by Lamaze-P
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Among Us"
|
name: Among Us
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Social Deduction
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Goat Simulator"
|
name: Goat Simulator
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Sandbox Comedy
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "osu!taiko"
|
name: osu!taiko
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Rhythm Game
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Fall Guys"
|
name: Fall Guys
|
||||||
|
weight: 2
|
||||||
|
state: Party Game
|
||||||
|
test:
|
||||||
|
- type: playing
|
||||||
|
name: G
|
||||||
weight: 2
|
weight: 2
|
||||||
|
|
||||||
evil:
|
evil:
|
||||||
aggressive:
|
aggressive:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Two-Faced Lovers"
|
name: Two-Faced Lovers
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by wowaka
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Secret Police"
|
name: Secret Police
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by doriko × UMA
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "DOOM Eternal"
|
name: DOOM Eternal
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: FPS
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Ultrakill"
|
name: Ultrakill
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: FPS
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Devil May Cry 5"
|
name: Devil May Cry 5
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Action
|
||||||
cunning:
|
cunning:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Gekkabijin"
|
name: Gekkabijin
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by masai-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "The World is Mine"
|
name: The World is Mine
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by ryo (supercell)
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Persona 5 Royal"
|
name: Persona 5 Royal
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: JRPG
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Among Us"
|
name: Among Us
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Social Deduction
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Hitman: World of Assassination"
|
name: 'Hitman: World of Assassination'
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Stealth
|
||||||
sarcastic:
|
sarcastic:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "I'm Sorry I'm Sorry"
|
name: I'm Sorry I'm Sorry
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by kikuo
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Karakuri Pierrot"
|
name: Karakuri Pierrot
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by 40mP
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "The Stanley Parable"
|
name: The Stanley Parable
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Narrative
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Portal 2"
|
name: Portal 2
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Puzzle
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Untitled Goose Game"
|
name: Untitled Goose Game
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Comedy
|
||||||
evil_neutral:
|
evil_neutral:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Dark Woods Circus"
|
name: Dark Woods Circus
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by machigerita-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Aku no Meshitsukai"
|
name: Aku no Meshitsukai
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by mothy (Akuno-P)
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Kagome Kagome"
|
name: Kagome Kagome
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by subtractor-P
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "The Binding of Isaac: Repentance"
|
name: 'The Binding of Isaac: Repentance'
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Roguelike
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Darkest Dungeon II"
|
name: Darkest Dungeon II
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Roguelike RPG
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Hollow Knight"
|
name: Hollow Knight
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Metroidvania
|
||||||
bored:
|
bored:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Karakuri Pierrot"
|
name: Karakuri Pierrot
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by 40mP
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Twilight Homicide"
|
name: Twilight Homicide
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by yuzuki-P
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Cookie Clicker"
|
name: Cookie Clicker
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Idle Game
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Vampire Survivors"
|
name: Vampire Survivors
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Roguelike
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Brawl Stars"
|
name: Brawl Stars
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Mobile MOBA
|
||||||
manic:
|
manic:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Bacterial Contamination"
|
name: Bacterial Contamination
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by kikuo
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Secret Police"
|
name: Secret Police
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by doriko × UMA
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Brain Fluid Explosion Girl"
|
name: Brain Fluid Explosion Girl
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by rerulili
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Ultrakill"
|
name: Ultrakill
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: FPS
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Muse Dash"
|
name: Muse Dash
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Rhythm Game
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Neon White"
|
name: Neon White
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: FPS Platformer
|
||||||
jealous:
|
jealous:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Rotten Girl Grotesque Romance"
|
name: Rotten Girl Grotesque Romance
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by cosMo@Bousou-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Aishite Aishite Aishite"
|
name: Aishite Aishite Aishite
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by kikuo
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Witch Hunt"
|
name: Witch Hunt
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by No.D
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Yandere Simulator"
|
name: Yandere Simulator
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Stealth
|
||||||
melancholic:
|
melancholic:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Prisoner"
|
name: Prisoner
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by PENGUIN PROJECT
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Dark Woods Circus"
|
name: Dark Woods Circus
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by machigerita-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Shinitagari"
|
name: Shinitagari
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by rerulili
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "NieR: Automata"
|
name: 'NieR: Automata'
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Action RPG
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Silent Hill 2"
|
name: Silent Hill 2
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Survival Horror
|
||||||
playful_cruel:
|
playful_cruel:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Fear Garden"
|
name: Fear Garden
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by COSMOS-P
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Kanashimi no Nami ni Oboreru"
|
name: Kanashimi no Nami ni Oboreru
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by Sasanomaly
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Dead by Daylight"
|
name: Dead by Daylight
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Survival Horror
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Lethal Company"
|
name: Lethal Company
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: Co-op Horror
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Content Warning"
|
name: Content Warning
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Co-op Horror
|
||||||
contemptuous:
|
contemptuous:
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "The World is Mine"
|
name: The World is Mine
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: by ryo (supercell)
|
||||||
- type: listening
|
- type: listening
|
||||||
name: "Queen of the Night"
|
name: Queen of the Night
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: by Nightcord at 25:00
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Civilization VI"
|
name: Civilization VI
|
||||||
weight: 3
|
weight: 3
|
||||||
|
state: 4X Strategy
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Chess"
|
name: Chess
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Strategy
|
||||||
- type: playing
|
- type: playing
|
||||||
name: "Crusader Kings III"
|
name: Crusader Kings III
|
||||||
weight: 2
|
weight: 2
|
||||||
|
state: Grand Strategy
|
||||||
|
|||||||
@@ -6883,7 +6883,9 @@ function activitiesRenderView(section, mood, entries) {
|
|||||||
const label = entry.type === 'listening' ? 'Listening to' : 'Playing';
|
const label = entry.type === 'listening' ? 'Listening to' : 'Playing';
|
||||||
html += `<div class="act-entry">`;
|
html += `<div class="act-entry">`;
|
||||||
html += `<span class="act-entry-icon">${icon}</span>`;
|
html += `<span class="act-entry-icon">${icon}</span>`;
|
||||||
html += `<span style="flex:1;">${escapeHtml(entry.name)}</span>`;
|
html += `<span style="flex:1;">${escapeHtml(entry.name)}`;
|
||||||
|
if (entry.state) html += ` <span style="color:#aaa; font-size:0.85rem;">— ${escapeHtml(entry.state)}</span>`;
|
||||||
|
html += `</span>`;
|
||||||
html += `<span style="color:#888; font-size:0.8rem;">weight: ${entry.weight}</span>`;
|
html += `<span style="color:#888; font-size:0.8rem;">weight: ${entry.weight}</span>`;
|
||||||
html += `</div>`;
|
html += `</div>`;
|
||||||
}
|
}
|
||||||
@@ -6902,8 +6904,9 @@ function activitiesRenderEditForm(section, mood, entries) {
|
|||||||
html += `<option value="listening" ${e.type === 'listening' ? 'selected' : ''}>🎵 Listening</option>`;
|
html += `<option value="listening" ${e.type === 'listening' ? 'selected' : ''}>🎵 Listening</option>`;
|
||||||
html += `<option value="playing" ${e.type === 'playing' ? 'selected' : ''}>🎮 Playing</option>`;
|
html += `<option value="playing" ${e.type === 'playing' ? 'selected' : ''}>🎮 Playing</option>`;
|
||||||
html += `</select>`;
|
html += `</select>`;
|
||||||
html += `<input type="text" id="act-name-${section}-${mood}-${i}" value="${escapeHtml(e.name)}" placeholder="Song/Game name">`;
|
html += `<input type="text" id="act-name-${section}-${mood}-${i}" value="${escapeHtml(e.name)}" placeholder="Song/Game name" style="flex:2; min-width:120px;">`;
|
||||||
html += `<input type="number" id="act-weight-${section}-${mood}-${i}" value="${e.weight}" min="1" max="20">`;
|
html += `<input type="text" id="act-state-${section}-${mood}-${i}" value="${escapeHtml(e.state || '')}" placeholder="Artist / Genre (optional)" style="flex:1.5; min-width:100px;">`;
|
||||||
|
html += `<input type="number" id="act-weight-${section}-${mood}-${i}" value="${e.weight}" min="1" max="20" style="width:60px;">`;
|
||||||
html += `<button onclick="activitiesRemoveEntry('${section}','${mood}',${i})" style="background:#c0392b; padding:0.3rem 0.5rem;" title="Remove">✕</button>`;
|
html += `<button onclick="activitiesRemoveEntry('${section}','${mood}',${i})" style="background:#c0392b; padding:0.3rem 0.5rem;" title="Remove">✕</button>`;
|
||||||
html += `</div>`;
|
html += `</div>`;
|
||||||
}
|
}
|
||||||
@@ -6949,7 +6952,7 @@ function activitiesAddEntry(section, mood) {
|
|||||||
const key = `${section}/${mood}`;
|
const key = `${section}/${mood}`;
|
||||||
// First, sync current form values to cache
|
// First, sync current form values to cache
|
||||||
activitiesSyncFormToCache(section, mood);
|
activitiesSyncFormToCache(section, mood);
|
||||||
activitiesEditCache[key].push({ type: 'listening', name: '', weight: 1 });
|
activitiesEditCache[key].push({ type: 'listening', name: '', state: '', weight: 1 });
|
||||||
activitiesRenderSection(section);
|
activitiesRenderSection(section);
|
||||||
// Keep the mood panel open
|
// Keep the mood panel open
|
||||||
const el = document.getElementById(`act-content-${section}-${mood}`);
|
const el = document.getElementById(`act-content-${section}-${mood}`);
|
||||||
@@ -6971,9 +6974,11 @@ function activitiesSyncFormToCache(section, mood) {
|
|||||||
for (let i = 0; i < entries.length; i++) {
|
for (let i = 0; i < entries.length; i++) {
|
||||||
const typeEl = document.getElementById(`act-type-${section}-${mood}-${i}`);
|
const typeEl = document.getElementById(`act-type-${section}-${mood}-${i}`);
|
||||||
const nameEl = document.getElementById(`act-name-${section}-${mood}-${i}`);
|
const nameEl = document.getElementById(`act-name-${section}-${mood}-${i}`);
|
||||||
|
const stateEl = document.getElementById(`act-state-${section}-${mood}-${i}`);
|
||||||
const weightEl = document.getElementById(`act-weight-${section}-${mood}-${i}`);
|
const weightEl = document.getElementById(`act-weight-${section}-${mood}-${i}`);
|
||||||
if (typeEl) entries[i].type = typeEl.value;
|
if (typeEl) entries[i].type = typeEl.value;
|
||||||
if (nameEl) entries[i].name = nameEl.value;
|
if (nameEl) entries[i].name = nameEl.value;
|
||||||
|
if (stateEl) entries[i].state = stateEl.value || undefined;
|
||||||
if (weightEl) entries[i].weight = parseInt(weightEl.value) || 1;
|
if (weightEl) entries[i].weight = parseInt(weightEl.value) || 1;
|
||||||
}
|
}
|
||||||
activitiesEditCache[key] = entries;
|
activitiesEditCache[key] = entries;
|
||||||
|
|||||||
@@ -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'")
|
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:
|
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")
|
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"
|
section = "evil" if is_evil else "normal"
|
||||||
data = _load_activities()
|
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.
|
"""Pick a weighted-random activity for a mood.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
tuple: (activity_type, name) e.g. ("listening", "World is Mine")
|
tuple: (activity_type, name, state) e.g. ("listening", "World is Mine", "by ryo (supercell)")
|
||||||
Returns ("listening", "Vocaloid") as fallback if mood has no entries.
|
state may be None if not defined. Fallback: ("listening", "Vocaloid", None)
|
||||||
"""
|
"""
|
||||||
activities = get_activities_for_mood(mood_name, is_evil)
|
activities = get_activities_for_mood(mood_name, is_evil)
|
||||||
|
|
||||||
if not activities:
|
if not activities:
|
||||||
logger.debug(f"No activities defined for {'evil/' if is_evil else ''}{mood_name}, using fallback")
|
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
|
# Weighted random selection
|
||||||
weights = [entry.get("weight", 1) for entry in activities]
|
weights = [entry.get("weight", 1) for entry in activities]
|
||||||
chosen = random.choices(activities, weights=weights, k=1)[0]
|
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):
|
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)")
|
logger.info("Set presence: idle (asleep)")
|
||||||
return
|
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":
|
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}"
|
log_label = f"Listening to {name}"
|
||||||
else:
|
else:
|
||||||
activity = discord.Game(name=name)
|
activity = discord.Activity(type=discord.ActivityType.playing, name=name, state=state)
|
||||||
log_label = f"Playing {name}"
|
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})")
|
logger.info(f"Set presence: {log_label} (mood={'evil/' if is_evil else ''}{mood_name})")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user