mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 00:52:40 +00:00
Add market plugin, arena, holidays, UNO stacking fix, and multiple UX improvements
- Market plugin (#49): daily index snapshots (Yahoo Finance + Finnhub fallback), Ollama-generated summaries, !howsthemarket, !marketstatus, !marketreport commands, exchange hours with DST support, 30-min room cooldown - Adventure arena: 5-tier combat gauntlet with 20 monsters, risk-reward cashout, death lockout changed from 24h to midnight UTC across all code and flavor text - Adventure holidays: double daily actions on holidays - Adventure DM fix: 15-minute response window prevents bare numbers from triggering adventure during UNO games - Adventure scheduler: jitter between morning DMs to avoid Matrix rate limits - Adventure revive: wire up adv_revived achievement on admin revive - Column migration system for existing databases (holiday_action_taken) - Fix italic markdown rendering after newlines - Fix holdem DMs broken by space groups including DM rooms - UNO No Mercy: remove stacking escalation rule (any draw card stacks on any other) - UNO multiplayer: add turn announcements after stack absorption and turn passes, jitter between rapid-fire messages with safe mutex handling - Birthday: add €1,000 gift and 10 XP + €100 community celebration bonus - Market: guard against division by zero, nil pointer, and timezone errors - README updates: plugin count 48→49, all new commands, feature flags, architecture Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -916,6 +916,105 @@ func (p *AchievementsPlugin) buildAchievements() []achievementDef {
|
||||
return false
|
||||
},
|
||||
},
|
||||
|
||||
// ── Arena ──────────────────────────────────────────────────────────
|
||||
{
|
||||
ID: "arena_first_blood", Name: "First Blood", Description: "The Arena has noted your existence.",
|
||||
Emoji: "⚔️",
|
||||
Check: func(d *sql.DB, u id.UserID) bool {
|
||||
// Granted by arena plugin on first round survived
|
||||
return false
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "arena_tier1", Name: "Scrub Slayer", Description: "Gelatinous Homunculus has been avenged by no one.",
|
||||
Emoji: "⚔️",
|
||||
Check: func(d *sql.DB, u id.UserID) bool {
|
||||
var count int
|
||||
_ = d.QueryRow(`SELECT COUNT(*) FROM arena_history WHERE user_id = ? AND tier = 1 AND outcome IN ('completed','cashed_out')`, string(u)).Scan(&count)
|
||||
return count > 0
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "arena_tier2", Name: "Promoted to Violence", Description: "The Impersonator was not, in fact, a chest.",
|
||||
Emoji: "⚔️",
|
||||
Check: func(d *sql.DB, u id.UserID) bool {
|
||||
var count int
|
||||
_ = d.QueryRow(`SELECT COUNT(*) FROM arena_history WHERE user_id = ? AND tier = 2 AND outcome IN ('completed','cashed_out')`, string(u)).Scan(&count)
|
||||
return count > 0
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "arena_tier3", Name: "Brute Force", Description: "The Inevitable was, on this occasion, avoidable.",
|
||||
Emoji: "⚔️",
|
||||
Check: func(d *sql.DB, u id.UserID) bool {
|
||||
var count int
|
||||
_ = d.QueryRow(`SELECT COUNT(*) FROM arena_history WHERE user_id = ? AND tier = 3 AND outcome IN ('completed','cashed_out')`, string(u)).Scan(&count)
|
||||
return count > 0
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "arena_tier4", Name: "Horror Show", Description: "The Collector of Faces does not have yours. Yet.",
|
||||
Emoji: "⚔️",
|
||||
Check: func(d *sql.DB, u id.UserID) bool {
|
||||
var count int
|
||||
_ = d.QueryRow(`SELECT COUNT(*) FROM arena_history WHERE user_id = ? AND tier = 4 AND outcome IN ('completed','cashed_out')`, string(u)).Scan(&count)
|
||||
return count > 0
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "arena_tier5", Name: "World Eater", Description: "That Which Has Always Been has now lost once.",
|
||||
Emoji: "⚔️",
|
||||
Check: func(d *sql.DB, u id.UserID) bool {
|
||||
var count int
|
||||
_ = d.QueryRow(`SELECT COUNT(*) FROM arena_history WHERE user_id = ? AND tier = 5 AND outcome = 'completed'`, string(u)).Scan(&count)
|
||||
return count > 0
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "arena_descend", Name: "Going Deeper", Description: "Greed is a valid strategy.",
|
||||
Emoji: "⚔️",
|
||||
Check: func(d *sql.DB, u id.UserID) bool {
|
||||
// Granted by arena plugin on descend
|
||||
return false
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "arena_full_run", Name: "All the Way Down", Description: "Statistically impossible. Empirically: you.",
|
||||
Emoji: "🏆",
|
||||
Check: func(d *sql.DB, u id.UserID) bool {
|
||||
// A full run = cleared all 5 tiers without cashing out
|
||||
var count int
|
||||
_ = d.QueryRow(`SELECT COUNT(*) FROM arena_history WHERE user_id = ? AND tier = 5 AND outcome = 'completed' AND rounds_survived = 20`, string(u)).Scan(&count)
|
||||
return count > 0
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "arena_death_t5", Name: "Acceptable Losses", Description: "You made it to Tier 5. That's something.",
|
||||
Emoji: "💀",
|
||||
Check: func(d *sql.DB, u id.UserID) bool {
|
||||
var count int
|
||||
_ = d.QueryRow(`SELECT COUNT(*) FROM arena_history WHERE user_id = ? AND tier = 5 AND outcome = 'dead'`, string(u)).Scan(&count)
|
||||
return count > 0
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "arena_omega", Name: "The Test", Description: "The machine has logged a loss for the first time.",
|
||||
Emoji: "🤖",
|
||||
Check: func(d *sql.DB, u id.UserID) bool {
|
||||
// Granted by arena plugin when defeating Omega Mk. Zero
|
||||
return false
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "arena_cashout_big", Name: "Take the Money", Description: "Discretion. Valor. Etc.",
|
||||
Emoji: "💰",
|
||||
Check: func(d *sql.DB, u id.UserID) bool {
|
||||
var count int
|
||||
_ = d.QueryRow(`SELECT COUNT(*) FROM arena_history WHERE user_id = ? AND outcome = 'cashed_out' AND earnings >= 10000`, string(u)).Scan(&count)
|
||||
return count > 0
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user