gambot/db.go

194 lines
3.8 KiB
Go
Raw Normal View History

2025-01-20 22:39:01 -08:00
package main
import (
"database/sql"
"errors"
"fmt"
"github.com/yuin/gluamapper"
"github.com/yuin/gopher-lua"
"golang.org/x/text/language"
"golang.org/x/text/message"
"io"
"os"
"sort"
"strings"
)
var db *sql.DB
const (
dbName = "gambot.sqlite"
)
func initDB() (err error) {
var newDb bool
_, err = os.Stat(dbName)
if errors.Is(err, os.ErrNotExist) {
newDb = true
}
db, err = sql.Open("sqlite3", dbName)
if err != nil {
return
}
if newDb {
commands := []string{
`CREATE TABLE 'players' (
'name' VARCHAR(18) NOT NULL PRIMARY KEY,
'val' integer SIGNED NOT NULL);`,
}
for _, command := range commands {
_, err = db.Exec(command)
}
}
return
}
type Player struct {
Name string
Val int
}
func (p *Player) FormatVal() string {
pr := message.NewPrinter(language.English)
return pr.Sprintf("%d", p.Val)
}
func getPlayers() ([]Player, error) {
players := make([]Player, 0)
tx, err := db.Begin()
if err != nil {
return nil, fmt.Errorf("failed to begin transaction: %s", err)
}
stmt, err := tx.Prepare("SELECT * FROM players")
if err != nil {
return nil, fmt.Errorf("failed to prepare statement: %s", err)
}
defer stmt.Close()
rows, err := stmt.Query()
if err != nil {
return nil, fmt.Errorf("failed to query rows: %s", err)
}
defer rows.Close()
for rows.Next() {
var name string
var val int
err = rows.Scan(&name, &val)
if err != nil {
return nil, fmt.Errorf("failed to scan row: %s", err)
}
players = append(players, Player{name, val})
}
sort.Slice(players, func(i, j int) bool {
return players[i].Val > players[j].Val
})
return players, nil
}
type WoWGoldGamblerDB struct {
ProfileKeys map[string]string
Global struct {
Game struct {
ChatChannel string
Wager int
RealmFilter bool
}
Stats struct {
Records struct {
Overall map[string]any
Classic map[string]any
}
Player map[string]int
Aliases map[string][]string
}
}
}
func readTable(path string) error {
L := lua.NewState()
defer L.Close()
L.OpenLibs()
file, err := os.Open(path)
if err != nil {
return fmt.Errorf("failed to open lua file: %w", err)
}
defer file.Close()
fData, err := io.ReadAll(file)
if err != nil {
return fmt.Errorf("failed to read from file: %w", err)
}
fStr := cleanup(fData)
if err = L.DoString(fStr); err != nil {
return fmt.Errorf("failed to load %s: %s", path, err)
}
var gamboDB WoWGoldGamblerDB
table := L.GetGlobal("WoWGoldGamblerDB").(*lua.LTable)
err = gluamapper.Map(table, &gamboDB)
if err != nil {
return fmt.Errorf("failed to parse table: %s", err)
}
var playerMap = make(map[string]int)
for name, val := range gamboDB.Global.Stats.Player {
playerMap[name] = val
}
for mainChar, alts := range gamboDB.Global.Stats.Aliases {
for _, alt := range alts {
playerMap[mainChar] += playerMap[alt]
playerMap[alt] = 0
}
}
tx, err := db.Begin()
if err != nil {
return fmt.Errorf("failed to begin transaction: %s", err)
}
stmt, err := tx.Prepare("INSERT OR REPLACE INTO players(name, val) VALUES(?, ?)")
if err != nil {
tx.Rollback()
return fmt.Errorf("failed to prepare statement: %s", err)
}
defer stmt.Close()
for k, v := range playerMap {
if v == 0 {
continue
}
_, err = stmt.Exec(k, v)
if err != nil {
tx.Rollback()
return fmt.Errorf("failed to execute statement: %s", err)
}
}
if err = tx.Commit(); err != nil {
return fmt.Errorf("failed to commit transaction: %s", err)
}
return nil
}
func cleanup(data []byte) string {
s := string(data)
strings.ReplaceAll(s, "Games Played", "GamesPlayed")
strings.Replace(s, "Gold Traded", "GoldTraded", 1)
strings.Replace(s, "Biggest Win", "BiggestWin", 1)
strings.Replace(s, "Biggest Wager", "BiggestWager", 1)
strings.Replace(s, "Luckiest Roll", "LuckiestRoll", 1)
strings.Replace(s, "Unluckiest Roll", "UnluckiestRoll", 1)
return s
}