Initial commit
This commit is contained in:
165
main.go
Normal file
165
main.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
_ "embed"
|
||||
|
||||
logger "git.site.quack-lab.dev/dave/cylogger"
|
||||
"github.com/bmatcuk/doublestar/v4"
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
)
|
||||
|
||||
const nsqEndpoint = "https://nsq.site.quack-lab.dev/pub?topic=wowspy"
|
||||
|
||||
var debug *bool
|
||||
var messageQueue = make(chan string, 10000)
|
||||
var nsqWorkers = 32
|
||||
|
||||
func main() {
|
||||
root := flag.String("root", ".", "Root workdir")
|
||||
debug = flag.Bool("d", false, "Debug")
|
||||
flag.Parse()
|
||||
logger.InitFlag()
|
||||
|
||||
logger.Info("Root: %q", *root)
|
||||
cleanedRoot := strings.Replace(*root, "~", os.Getenv("HOME"), 1)
|
||||
cleanedRoot, err := filepath.Abs(cleanedRoot)
|
||||
if err != nil {
|
||||
logger.Error("error getting absolute path: %v", err)
|
||||
return
|
||||
}
|
||||
cleanedRoot = filepath.Clean(cleanedRoot)
|
||||
cleanedRoot = strings.TrimSuffix(cleanedRoot, "/")
|
||||
|
||||
logger.Info("Looking for Heimdall.lua in %q", cleanedRoot)
|
||||
matches, err := doublestar.Glob(os.DirFS(cleanedRoot), "**/Heimdall.lua")
|
||||
if err != nil {
|
||||
logger.Error("error matching Heimdall.lua: %v", err)
|
||||
return
|
||||
}
|
||||
logger.Info("Found %d Heimdall.lua", len(matches))
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
messages := make(chan NSQMessage, 1000)
|
||||
for i := 0; i < nsqWorkers; i++ {
|
||||
wg.Add(1)
|
||||
go NsqWorker(&wg, messages)
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
logger.Info("Processing %q", match)
|
||||
wg.Add(1)
|
||||
go ParseHeimdallFile(filepath.Join(cleanedRoot, match), &wg, messages)
|
||||
}
|
||||
for msg := range messages {
|
||||
logger.Info("Message: %#v", msg)
|
||||
}
|
||||
wg.Wait()
|
||||
close(messages)
|
||||
return
|
||||
}
|
||||
|
||||
func ParseHeimdallFile(path string, wg *sync.WaitGroup, messages chan NSQMessage) {
|
||||
defer wg.Done()
|
||||
L := lua.NewState()
|
||||
defer L.Close()
|
||||
|
||||
if err := L.DoFile(path); err != nil {
|
||||
logger.Error("error executing Lua file %q: %v", path, err)
|
||||
return
|
||||
}
|
||||
|
||||
heimdallAchievements := L.GetGlobal("Heimdall_Achievements")
|
||||
if heimdallAchievements.Type() == lua.LTNil {
|
||||
logger.Error("Heimdall_Achievements not found in %q", path)
|
||||
return
|
||||
}
|
||||
|
||||
playersTableLua := L.GetField(heimdallAchievements, "players")
|
||||
playersTable, ok := playersTableLua.(*lua.LTable)
|
||||
if !ok || playersTableLua.Type() == lua.LTNil {
|
||||
logger.Error("'players' table not found or not a table in Heimdall_Achievements in %q", path)
|
||||
return
|
||||
}
|
||||
|
||||
playersTable.ForEach(func(playerNameLua lua.LValue, playerAchievementsLua lua.LValue) {
|
||||
currentPlayerName := playerNameLua.String()
|
||||
logger.Info("Processing player: %s in %q", currentPlayerName, path)
|
||||
|
||||
achievementsTable, ok := playerAchievementsLua.(*lua.LTable)
|
||||
if !ok {
|
||||
logger.Error("Achievements for player %s is not a table in %q", currentPlayerName, path)
|
||||
return
|
||||
}
|
||||
|
||||
achievementsTable.ForEach(func(_ lua.LValue, achievementDataLua lua.LValue) {
|
||||
achievementTable, ok := achievementDataLua.(*lua.LTable)
|
||||
if !ok {
|
||||
logger.Error("Achievement data for player %s is not a table in %q", currentPlayerName, path)
|
||||
return
|
||||
}
|
||||
|
||||
currentAchievement := NSQMessage{Name: currentPlayerName}
|
||||
|
||||
idVal := achievementTable.RawGetString("id")
|
||||
if idVal.Type() == lua.LTNumber {
|
||||
currentAchievement.ID = lua.LVAsString(idVal)
|
||||
} else if idVal.Type() == lua.LTString {
|
||||
currentAchievement.ID = idVal.String()
|
||||
} else {
|
||||
logger.Warning("Missing or invalid 'id' (expected number or string) for achievement for player %s in %q", currentPlayerName, path)
|
||||
}
|
||||
|
||||
dateVal := achievementTable.RawGetString("date")
|
||||
if dateVal.Type() == lua.LTString {
|
||||
currentAchievement.Date = dateVal.String()
|
||||
} else {
|
||||
logger.Warning("Missing or invalid 'date' (expected string) for achievement for player %s in %q", currentPlayerName, path)
|
||||
}
|
||||
|
||||
completedVal := achievementTable.RawGetString("completed")
|
||||
if completedVal.Type() == lua.LTBool {
|
||||
currentAchievement.Completed = lua.LVAsBool(completedVal)
|
||||
} else {
|
||||
logger.Warning("Missing or invalid 'completed' (expected boolean) for achievement for player %s in %q", currentPlayerName, path)
|
||||
}
|
||||
|
||||
if currentAchievement.ID != "" { // Ensure we have at least an ID before sending
|
||||
logger.Info("Publishing achievement for %s: ID %s, Date: %s, Completed: %t", currentPlayerName, currentAchievement.ID, currentAchievement.Date, currentAchievement.Completed)
|
||||
messages <- currentAchievement
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func NsqWorker(wg *sync.WaitGroup, messages chan NSQMessage) {
|
||||
defer wg.Done()
|
||||
for msg := range messages {
|
||||
err := Publish(msg)
|
||||
if err != nil {
|
||||
logger.Warning("error publishing message: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Publish(msg NSQMessage) error {
|
||||
data := bytes.Buffer{}
|
||||
err := json.NewEncoder(&data).Encode(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = http.Post(nsqEndpoint, "application/json", &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user