init
This commit is contained in:
commit
6f469d088b
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.idea
|
||||
token
|
||||
run.sh
|
||||
*.sqlite
|
||||
*.lua
|
11
README.md
Normal file
11
README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Dammaz Korn
|
||||
|
||||
A book of grudges for your discord server
|
||||
|
||||
## Usage
|
||||
|
||||
Requires the following flags to run:
|
||||
|
||||
- `-t` Discord bot token
|
||||
- `-g` Guild ID (server ID)
|
||||
- `-r` The one role that is allowed to add and remove to the pug banlist. I advise making a special role just for this
|
193
db.go
Normal file
193
db.go
Normal file
@ -0,0 +1,193 @@
|
||||
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
|
||||
}
|
72
discord.go
Normal file
72
discord.go
Normal file
@ -0,0 +1,72 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var commands = []*discordgo.ApplicationCommand{
|
||||
{
|
||||
Name: "gambo",
|
||||
Description: "print gambo data",
|
||||
},
|
||||
}
|
||||
|
||||
var token, appID, guildID string
|
||||
|
||||
func handleMessage(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
log.Println("Received gambo command")
|
||||
c, err := content()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
embed := &discordgo.MessageEmbed{
|
||||
Title: "== Degen Hall of Fame ==",
|
||||
Description: c,
|
||||
}
|
||||
|
||||
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Embeds: []*discordgo.MessageEmbed{embed},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("could not respond to interaction: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func content() (string, error) {
|
||||
players, err := getPlayers()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var b strings.Builder
|
||||
b.WriteString("**All Hail Gambolord " + players[0].Name + "!**\n")
|
||||
b.WriteString("## Current Rankings\n")
|
||||
|
||||
m := 0
|
||||
for _, player := range players {
|
||||
if len(player.Name) > m {
|
||||
m = len(player.Name)
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteString("```\n")
|
||||
for _, player := range players {
|
||||
formattedVal := player.FormatVal()
|
||||
if player.Val > 0 {
|
||||
b.WriteString(fmt.Sprintf("%-*s +%*s\n", m+2, player.Name, len(formattedVal), formattedVal))
|
||||
} else {
|
||||
b.WriteString(fmt.Sprintf("%-*s %*s\n", m+2, player.Name, len(formattedVal), formattedVal))
|
||||
}
|
||||
}
|
||||
b.WriteString("```\n")
|
||||
|
||||
return b.String(), nil
|
||||
}
|
18
go.mod
Normal file
18
go.mod
Normal file
@ -0,0 +1,18 @@
|
||||
module gambot
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/bwmarrin/discordgo v0.28.1
|
||||
github.com/mattn/go-sqlite3 v1.14.24
|
||||
github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7
|
||||
github.com/yuin/gopher-lua v1.1.1
|
||||
golang.org/x/text v0.11.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
golang.org/x/crypto v0.11.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
)
|
25
go.sum
Normal file
25
go.sum
Normal file
@ -0,0 +1,25 @@
|
||||
github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=
|
||||
github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 h1:noHsffKZsNfU38DwcXWEPldrTjIZ8FPNKx8mYMGnqjs=
|
||||
github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7/go.mod h1:bbMEM6aU1WDF1ErA5YJ0p91652pGv140gGw4Ww3RGp8=
|
||||
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
|
||||
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
79
main.go
Normal file
79
main.go
Normal file
@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := initDB(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
var tablePath string
|
||||
|
||||
flag.StringVar(&token, "t", "", "Bot Token")
|
||||
flag.StringVar(&appID, "a", "", "App ID")
|
||||
flag.StringVar(&guildID, "g", "", "Guild ID")
|
||||
flag.StringVar(&tablePath, "p", "", "Path to table")
|
||||
flag.Parse()
|
||||
|
||||
if err := readTable(tablePath); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := runBot(token); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func runBot(token string) error {
|
||||
bot, err := discordgo.New("Bot " + token)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create Discord session: %w\n", err)
|
||||
}
|
||||
|
||||
bot.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
if i.Type != discordgo.InteractionApplicationCommand {
|
||||
return
|
||||
}
|
||||
|
||||
data := i.ApplicationCommandData()
|
||||
if data.Name != "gambo" {
|
||||
return
|
||||
}
|
||||
|
||||
handleMessage(s, i)
|
||||
})
|
||||
|
||||
bot.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) {
|
||||
log.Printf("Logged in as %s", r.User.String())
|
||||
})
|
||||
|
||||
_, err = bot.ApplicationCommandBulkOverwrite(appID, guildID, commands)
|
||||
if err != nil {
|
||||
log.Fatalf("could not register commands: %s", err)
|
||||
}
|
||||
|
||||
err = bot.Open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open bot session: %w\n", err)
|
||||
}
|
||||
defer bot.Close()
|
||||
|
||||
fmt.Println("Starting bot...")
|
||||
|
||||
sc := make(chan os.Signal, 1)
|
||||
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
|
||||
<-sc
|
||||
return nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user