194 lines
3.8 KiB
Go
194 lines
3.8 KiB
Go
|
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
|
||
|
}
|