added basic ws commands

This commit is contained in:
zomo
2026-01-07 00:16:15 -06:00
parent a6d932a560
commit e01bd2eb9b
4 changed files with 107 additions and 36 deletions

View File

@@ -5,5 +5,9 @@ CLIENT_SECRET= # Twitch Client Secret
REDIR_URI= # Twitch OAuth Redirect URI REDIR_URI= # Twitch OAuth Redirect URI
# Required # Required
WS_AUTHORIZATION= # the authorization code all websocket clients will need to connect
# Required
# TODO this is not the final version, this will not be a permanent config option
SQLITE_DB= # SQlite DB location SQLITE_DB= # SQlite DB location
# Default: ./db.sqlite # Default: ./db.sqlite

View File

@@ -10,7 +10,7 @@ import (
func InitApiServer(conf *util.Config, dbConn *db.DBConn, twitchConn *ttv.TwitchConn) (*ApiServer, error) { func InitApiServer(conf *util.Config, dbConn *db.DBConn, twitchConn *ttv.TwitchConn) (*ApiServer, error) {
engine := gin.Default() engine := gin.Default()
wsServer, err := ws.InitWSServer(dbConn, twitchConn) wsServer, err := ws.InitWSServer(conf, dbConn, twitchConn)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -1,24 +1,35 @@
package ws package ws
import ( import (
"errors"
"fmt"
"log" "log"
"net"
"net/http" "net/http"
"strings"
"github.com/gobwas/ws" "github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil" "github.com/gobwas/ws/wsutil"
"zomo.dev/largehadroncollider/db" "zomo.dev/largehadroncollider/db"
"zomo.dev/largehadroncollider/ttv" "zomo.dev/largehadroncollider/ttv"
"zomo.dev/largehadroncollider/util"
) )
func InitWSServer(dbConn *db.DBConn, twitchConn *ttv.TwitchConn) (*WSServer, error) { func InitWSServer(conf *util.Config, dbConn *db.DBConn, twitchConn *ttv.TwitchConn) (*WSServer, error) {
return &WSServer{}, nil return &WSServer{conf, dbConn, twitchConn}, nil
} }
type WSServer struct { type WSServer struct {
conf *util.Config
db *db.DBConn
twitch *ttv.TwitchConn
} }
type WSConn struct { type WSConn struct {
conf *util.Config
c net.Conn
events []string events []string
authrorized bool
} }
func (wsServer *WSServer) Handle(w http.ResponseWriter, r *http.Request) error { func (wsServer *WSServer) Handle(w http.ResponseWriter, r *http.Request) error {
@@ -27,13 +38,19 @@ func (wsServer *WSServer) Handle(w http.ResponseWriter, r *http.Request) error {
return err return err
} }
authrorization := "" wsconn := &WSConn{}
go wsconn.handleThread(wsServer.conf, conn)
go func() { return nil
defer conn.Close() }
func (conn *WSConn) handleThread(conf *util.Config, c net.Conn) {
conn.conf = conf
conn.c = c
defer c.Close()
for { for {
msg, _, err := wsutil.ReadClientData(conn) msg, _, err := wsutil.ReadClientData(conn.c)
if err != nil { if err != nil {
// TODO handle error better, print more conn info // TODO handle error better, print more conn info
log.Printf("Error reading data from ws connection %v", err) log.Printf("Error reading data from ws connection %v", err)
@@ -48,21 +65,64 @@ func (wsServer *WSServer) Handle(w http.ResponseWriter, r *http.Request) error {
log.Printf("%+v", cmd) log.Printf("%+v", cmd)
if authrorization == "" { err = conn.runCommand(cmd)
// TODO authorize connection
continue if err != nil {
_, err := fmt.Fprintf(conn.c, "error running command: %s: %v", cmd.Command, err)
if err != nil {
// TODO better print
log.Printf("ERROR: unable to send error to client: %v", err)
log.Printf("ERROR: client details: %+v", conn)
log.Printf("ERROR: command details: %+v", cmd)
return
}
}
}
}
func (conn *WSConn) runCommand(cmd *Command) error {
switch strings.ToLower(cmd.Command) {
case "ping":
return conn.runCommandPing(cmd.Args)
case "authorization":
return conn.runCommandAuthorization(cmd.Args)
case "events-add":
return conn.runCommandEventsAdd(cmd.Args)
default:
return fmt.Errorf("invalid command: %s", cmd.Command)
}
}
func (conn *WSConn) runCommandPing(_ []string) error {
_, err := fmt.Fprint(conn.c, "pong")
return err
}
func (conn *WSConn) runCommandAuthorization(args []string) error {
if conn.authrorized {
return errors.New("connection already authorized")
}
if len(args) < 1 {
return errors.New("no authorization key given")
} }
// TODO run with cmd key := args[0]
if key != conn.conf.WSAuthorization {
// TODO errors will be responded to the client return errors.New("invalid authorization key")
// if the response errors, log and exit the loop
} }
}()
conn.authrorized = true
return nil return nil
} }
func runCommand(cmd *Command) { func (conn *WSConn) runCommandEventsAdd(args []string) error {
if !conn.authrorized {
return errors.New("unauthorized")
}
// TODO subscribe
return nil
} }

View File

@@ -27,6 +27,7 @@ type Config struct {
ClientSecret string ClientSecret string
RedirectURI string RedirectURI string
SQliteDB string SQliteDB string
WSAuthorization string
} }
func (c *Config) def() { func (c *Config) def() {
@@ -51,6 +52,9 @@ func (c *Config) loadEnv() error {
if str, found := os.LookupEnv("SQLITE_DB"); found { if str, found := os.LookupEnv("SQLITE_DB"); found {
c.SQliteDB = strings.TrimSpace(str) c.SQliteDB = strings.TrimSpace(str)
} }
if str, found := os.LookupEnv("WS_AUTHORIZATION"); found {
c.WSAuthorization = strings.TrimSpace(str)
}
return nil return nil
} }
@@ -65,6 +69,9 @@ func (c *Config) verify() error {
if c.RedirectURI == "" { if c.RedirectURI == "" {
return errors.New("unable to load a configured Redirect URI") return errors.New("unable to load a configured Redirect URI")
} }
if c.WSAuthorization == "" {
return errors.New("unable to load a configured WS Authorization code")
}
return nil return nil
} }