1
0
Fork 0
mirror of https://git.sr.ht/~ashkeel/strimertul synced 2024-09-18 01:50:50 +00:00
strimertul/main.go
2021-09-18 22:06:22 +02:00

224 lines
6.2 KiB
Go

package main
import (
"embed"
"flag"
"fmt"
"io/fs"
"math/rand"
"net/http"
"runtime"
"time"
kv "github.com/strimertul/kilovolt/v4"
"github.com/strimertul/strimertul/database"
"github.com/strimertul/strimertul/modules"
"github.com/strimertul/strimertul/modules/loyalty"
"github.com/strimertul/strimertul/modules/stulbe"
"github.com/strimertul/strimertul/modules/twitch"
"github.com/dgraph-io/badger/v3"
"github.com/pkg/browser"
"github.com/mattn/go-colorable"
"github.com/sirupsen/logrus"
_ "net/http/pprof"
)
const AppTitle = "strimertül"
const AppHeader = `
_ _ _ O O _
__| |_ _ _(_)_ __ ___ _ _| |_ _ _| |
(_-< _| '_| | ' \/ -_) '_| _| || | |
/__/\__|_| |_|_|_|_\___|_| \__|\_,_|_| `
const DefaultBind = "localhost:4337"
//go:embed frontend/dist/*
var frontend embed.FS
var log = logrus.New()
func wrapLogger(module string) logrus.FieldLogger {
return log.WithField("module", module)
}
func parseLogLevel(level string) logrus.Level {
switch level {
case "error":
return logrus.ErrorLevel
case "warn", "warning":
return logrus.WarnLevel
case "info", "notice":
return logrus.InfoLevel
case "debug":
return logrus.DebugLevel
case "trace":
return logrus.TraceLevel
default:
return logrus.InfoLevel
}
}
func main() {
// Get cmd line parameters
dbdir := flag.String("dbdir", "data", "Path to strimertul database dir")
loglevel := flag.String("loglevel", "info", "Logging level (debug, info, warn, error)")
flag.Parse()
rand.Seed(time.Now().UnixNano())
log.SetLevel(parseLogLevel(*loglevel))
// Ok this is dumb but listen, I like colors.
if runtime.GOOS == "windows" {
log.SetFormatter(&logrus.TextFormatter{ForceColors: true})
log.SetOutput(colorable.NewColorableStdout())
}
// Print the app header :D
fmt.Println(AppHeader)
// Loading routine
dblogger := wrapLogger("db")
db, err := database.Open(badger.DefaultOptions(*dbdir).WithLogger(dblogger), dblogger)
failOnError(err, "Could not open DB")
defer db.Close()
// Check if onboarding was completed
var moduleConfig modules.ModuleConfig
err = db.GetJSON(modules.ModuleConfigKey, &moduleConfig)
if err != nil {
if err == badger.ErrKeyNotFound {
moduleConfig = modules.ModuleConfig{CompletedOnboarding: false}
} else {
fatalError(err, "Could not read from DB")
}
}
if !moduleConfig.CompletedOnboarding {
// Initialize DB as empty and default endpoint
failOnError(db.PutJSON(modules.HTTPServerConfigKey, modules.HTTPServerConfig{
Bind: DefaultBind,
}), "could not save http config")
failOnError(db.PutJSON(modules.ModuleConfigKey, modules.ModuleConfig{
EnableKV: true,
EnableStaticServer: false,
EnableTwitch: false,
EnableStulbe: false,
CompletedOnboarding: true,
}), "could not save onboarding config")
fmt.Printf("It appears this is your first time running %s! Please go to http://%s and make sure to configure anything you want!\n\n", AppTitle, DefaultBind)
}
// Initialize KV (required)
hub, err := kv.NewHub(db.Client(), wrapLogger("kv"))
failOnError(err, "Could not initialize kilovolt hub")
go hub.Run()
// Get HTTP config
var httpConfig modules.HTTPServerConfig
failOnError(db.GetJSON(modules.HTTPServerConfigKey, &httpConfig), "Could not retrieve HTTP server config")
// Get Stulbe config, if enabled
var stulbeManager *stulbe.Manager = nil
if moduleConfig.EnableStulbe {
stulbeLogger := wrapLogger("stulbe")
stulbeManager, err = stulbe.Initialize(db, stulbeLogger)
if err != nil {
log.WithError(err).Error("Stulbe initialization failed! Module was temporarely disabled")
moduleConfig.EnableStulbe = false
} else {
defer stulbeManager.Close()
go func() {
err := stulbeManager.ReceiveEvents()
stulbeLogger.WithError(err).Error("Stulbe subscription died unexpectedly!")
}()
}
}
var loyaltyManager *loyalty.Manager
loyaltyLogger := wrapLogger("loyalty")
if moduleConfig.EnableLoyalty {
loyaltyManager, err = loyalty.NewManager(db, loyaltyLogger)
if err != nil {
log.WithError(err).Error("Loyalty initialization failed! Module was temporarily disabled")
moduleConfig.EnableLoyalty = false
}
if stulbeManager != nil {
go stulbeManager.ReplicateKey(loyalty.ConfigKey)
go stulbeManager.ReplicateKey(loyalty.RewardsKey)
go stulbeManager.ReplicateKey(loyalty.GoalsKey)
go stulbeManager.ReplicateKey(loyalty.PointsPrefix)
}
}
//TODO Refactor this to something sane
if moduleConfig.EnableTwitch {
// Create logger
twitchLogger := wrapLogger("twitch")
// Get Twitch config
var twitchConfig twitch.Config
failOnError(db.GetJSON(twitch.ConfigKey, &twitchConfig), "Could not retrieve twitch config")
// Create Twitch client
twitchClient, err := twitch.NewClient(db, twitchConfig, twitchLogger)
if err == nil {
// Get Twitchbot config
var twitchBotConfig twitch.BotConfig
failOnError(db.GetJSON(twitch.BotConfigKey, &twitchBotConfig), "Could not retrieve twitch bot config")
// Create and run IRC bot
bot := twitch.NewBot(twitchClient, twitchBotConfig)
if moduleConfig.EnableLoyalty {
bot.SetupLoyalty(loyaltyManager)
}
go func() {
failOnError(bot.Connect(), "connection failed")
}()
} else {
log.WithError(err).Error("Twitch initialization failed! Module was temporarily disabled")
moduleConfig.EnableTwitch = false
}
}
// Create logger and endpoints
httpLogger := wrapLogger("http")
fedir, _ := fs.Sub(frontend, "frontend/dist")
http.Handle("/ui/", http.StripPrefix("/ui/", FileServerWithDefault(http.FS(fedir))))
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
kv.ServeWs(hub, w, r)
})
if moduleConfig.EnableStaticServer {
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(httpConfig.Path))))
httpLogger.WithField("path", httpConfig.Path).Info("serving %s")
}
go func() {
time.Sleep(time.Second) // THIS IS STUPID
browser.OpenURL(fmt.Sprintf("http://%s/ui", httpConfig.Bind))
}()
// Start HTTP server
fatalError(http.ListenAndServe(httpConfig.Bind, nil), "HTTP server died unexepectedly")
}
func failOnError(err error, text string) {
if err != nil {
fatalError(err, text)
}
}
func fatalError(err error, text string) {
log.Fatalf("FATAL ERROR OCCURRED: %s\n\n%s", text, err.Error())
}