mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +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:
@@ -6,6 +6,7 @@ import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -56,8 +57,23 @@ func Get() *sql.DB {
|
||||
}
|
||||
|
||||
func runMigrations(d *sql.DB) error {
|
||||
_, err := d.Exec(schema)
|
||||
return err
|
||||
if _, err := d.Exec(schema); err != nil {
|
||||
return err
|
||||
}
|
||||
// Column migrations — ALTER TABLE ADD COLUMN is a no-op if it already
|
||||
// exists in SQLite (we just swallow "duplicate column name" errors).
|
||||
columnMigrations := []string{
|
||||
`ALTER TABLE adventure_characters ADD COLUMN holiday_action_taken INTEGER NOT NULL DEFAULT 0`,
|
||||
}
|
||||
for _, stmt := range columnMigrations {
|
||||
if _, err := d.Exec(stmt); err != nil {
|
||||
// "duplicate column name" means it already exists — safe to ignore.
|
||||
if !strings.Contains(err.Error(), "duplicate column") {
|
||||
return fmt.Errorf("migration %q: %w", stmt, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// JobCompleted checks if a scheduled job has already completed for the given date key.
|
||||
@@ -148,6 +164,10 @@ func RunMaintenance() {
|
||||
|
||||
// Forex rates older than 2 years (analysis needs 52 weeks, keep buffer)
|
||||
{"forex_rates", `DELETE FROM forex_rates WHERE date < ?`, []interface{}{now.AddDate(-2, 0, 0).Format("2006-01-02")}},
|
||||
|
||||
// Market snapshots older than 1 year
|
||||
{"market_snapshots", `DELETE FROM market_snapshots WHERE snapshot_date < ?`, []interface{}{now.AddDate(-1, 0, 0).Format("2006-01-02")}},
|
||||
{"market_daily_summary", `DELETE FROM market_daily_summary WHERE snapshot_date < ?`, []interface{}{now.AddDate(-1, 0, 0).Format("2006-01-02")}},
|
||||
}
|
||||
|
||||
totalDeleted := int64(0)
|
||||
@@ -813,6 +833,7 @@ CREATE TABLE IF NOT EXISTS adventure_characters (
|
||||
alive INTEGER NOT NULL DEFAULT 1,
|
||||
dead_until DATETIME,
|
||||
action_taken_today INTEGER NOT NULL DEFAULT 0,
|
||||
holiday_action_taken INTEGER NOT NULL DEFAULT 0,
|
||||
arena_wins INTEGER NOT NULL DEFAULT 0,
|
||||
arena_losses INTEGER NOT NULL DEFAULT 0,
|
||||
invasion_score INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -950,6 +971,44 @@ CREATE TABLE IF NOT EXISTS adventure_events_log (
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_adv_events_user_outcome ON adventure_events_log(user_id, outcome);
|
||||
|
||||
-- Arena (Phase 2)
|
||||
CREATE TABLE IF NOT EXISTS arena_runs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id TEXT NOT NULL,
|
||||
room_id TEXT NOT NULL,
|
||||
start_tier INTEGER NOT NULL,
|
||||
tier INTEGER NOT NULL,
|
||||
round INTEGER NOT NULL DEFAULT 1,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
earnings INTEGER NOT NULL DEFAULT 0,
|
||||
rounds_survived INTEGER NOT NULL DEFAULT 0,
|
||||
last_monster TEXT NOT NULL DEFAULT '',
|
||||
started_at INTEGER NOT NULL,
|
||||
ended_at INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS arena_history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id TEXT NOT NULL,
|
||||
start_tier INTEGER NOT NULL,
|
||||
tier INTEGER NOT NULL,
|
||||
rounds_survived INTEGER NOT NULL,
|
||||
earnings INTEGER NOT NULL,
|
||||
outcome TEXT NOT NULL,
|
||||
monster_name TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS arena_stats (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
total_runs INTEGER NOT NULL DEFAULT 0,
|
||||
total_earnings INTEGER NOT NULL DEFAULT 0,
|
||||
total_deaths INTEGER NOT NULL DEFAULT 0,
|
||||
highest_tier INTEGER NOT NULL DEFAULT 0,
|
||||
tier5_completions INTEGER NOT NULL DEFAULT 0,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- Forex
|
||||
CREATE TABLE IF NOT EXISTS forex_rates (
|
||||
currency TEXT NOT NULL,
|
||||
@@ -983,6 +1042,27 @@ CREATE TABLE IF NOT EXISTS miniflux_seen (
|
||||
PRIMARY KEY (feed_id, entry_id)
|
||||
);
|
||||
|
||||
-- Market snapshots (daily index data)
|
||||
CREATE TABLE IF NOT EXISTS market_snapshots (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
snapshot_date TEXT NOT NULL,
|
||||
symbol TEXT NOT NULL,
|
||||
display_name TEXT NOT NULL,
|
||||
price REAL,
|
||||
prev_close REAL,
|
||||
change_pct REAL,
|
||||
source TEXT NOT NULL,
|
||||
pulled_at INTEGER NOT NULL,
|
||||
UNIQUE(snapshot_date, symbol)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_market_snap_date ON market_snapshots(snapshot_date, symbol);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS market_daily_summary (
|
||||
snapshot_date TEXT PRIMARY KEY,
|
||||
summary TEXT,
|
||||
generated_at INTEGER
|
||||
);
|
||||
|
||||
`
|
||||
|
||||
// SeedSchedulerDefaults inserts default scheduler jobs if they don't exist.
|
||||
|
||||
Reference in New Issue
Block a user