overhaul config, refactor twitch labels, init satellite

This commit is contained in:
ari melody 2026-07-24 17:24:07 +01:00
parent b2485eda79
commit 097a36ac69
Signed by: ari
GPG key ID: CF99829C92678188
6 changed files with 161 additions and 152 deletions

View file

@ -40,16 +40,16 @@ type (
}
ServiceOptions struct {
Host string
Port int16
}
serviceConfig struct {
ChannelName string `json:"channel_name"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
ChannelName string
ClientID string
ClientSecret string
}
Service struct {
host string
port int16
channelName string
@ -85,30 +85,9 @@ func New(ctx context.Context, opts ServiceOptions) (*Service, error) {
if err := os.MkdirAll(DATA_PATH, 0750); err != nil { panic(err) }
if err := os.MkdirAll(path.Join(DATA_PATH, "state"), 0750); err != nil { panic(err) }
config := serviceConfig{}
if configFile, err := os.OpenFile(CONFIG_FILEPATH, os.O_CREATE | os.O_RDWR, 0600); err != nil {
return nil, fmt.Errorf("open %s: %v", CONFIG_FILEPATH, err)
} else {
defer configFile.Close()
stat, err := configFile.Stat()
if err != nil { return nil, fmt.Errorf("stat %s: %v", CONFIG_FILEPATH, err) }
if stat.Size() == 0 {
enc := json.NewEncoder(configFile)
enc.SetIndent("", "\t")
if err := enc.Encode(&config); err != nil {
return nil, fmt.Errorf("write %s: %v", CONFIG_FILEPATH, err)
}
return nil, fmt.Errorf("Config file is empty: %s", CONFIG_FILEPATH)
} else if err := json.NewDecoder(configFile).Decode(&config); err != nil {
return nil, fmt.Errorf("read %s: %v", CONFIG_FILEPATH, err)
}
}
if len(config.ChannelName) == 0 { return nil, errors.New("config: channel_name cannot be empty") }
if len(config.ClientID) == 0 { return nil, errors.New("config: client_id cannot be empty") }
if len(config.ClientSecret) == 0 { return nil, errors.New("config: client_secret cannot be empty") }
if len(opts.ChannelName) == 0 { return nil, errors.New("config: channel_name cannot be empty") }
if len(opts.ClientID) == 0 { return nil, errors.New("config: client_id cannot be empty") }
if len(opts.ClientSecret) == 0 { return nil, errors.New("config: client_secret cannot be empty") }
latestFollowerC := make(chan string)
latestFollowerBroadcast := broadcast.NewBroadcastChannel(
@ -121,6 +100,7 @@ func New(ctx context.Context, opts ServiceOptions) (*Service, error) {
ctx, latestCheerC)
srv := &Service{
host: opts.Host,
port: opts.Port,
labels: &twitchLabels{
LatestFollower: &twitchLabel{
@ -136,12 +116,12 @@ func New(ctx context.Context, opts ServiceOptions) (*Service, error) {
Broadcast: latestCheerBroadcast,
},
},
channelName: config.ChannelName,
clientID: config.ClientID,
clientSecret: config.ClientSecret,
channelName: opts.ChannelName,
clientID: opts.ClientID,
clientSecret: opts.ClientSecret,
oauthConfig: &oauth2.Config{
ClientID: config.ClientID,
ClientSecret: config.ClientSecret,
ClientID: opts.ClientID,
ClientSecret: opts.ClientSecret,
Endpoint: twitch.Endpoint,
Scopes: []string{
"moderator:read:followers",
@ -156,7 +136,7 @@ func New(ctx context.Context, opts ServiceOptions) (*Service, error) {
"channel:read:hype_train",
"moderator:read:shoutouts",
},
RedirectURL: fmt.Sprintf("http://localhost:%d/twitch/auth", opts.Port),
RedirectURL: fmt.Sprintf("http://%s:%d/twitch/auth", opts.Host, opts.Port),
},
}
@ -223,13 +203,13 @@ func (srv *Service) BindRoutes(group *gin.RouterGroup) {
go srv.start(ctx)
})
group.GET("/sse", func(ctx *gin.Context) {
group.GET("/sse/:label", func(ctx *gin.Context) {
ctx.Header("connection", "keep-alive")
listeningTo := ctx.Query("l")
labelParam := ctx.Param("label")
var label *twitchLabel
switch listeningTo {
switch labelParam {
case LABEL_LATEST_FOLLOWER:
label = srv.labels.LatestFollower
case LABEL_LATEST_SUBSCRIBER:
@ -237,7 +217,7 @@ func (srv *Service) BindRoutes(group *gin.RouterGroup) {
case LABEL_LATEST_CHEER:
label = srv.labels.LatestCheer
default:
ctx.String(http.StatusBadRequest, "Unknown label %s", listeningTo)
ctx.String(http.StatusBadRequest, "Unknown label %s", labelParam)
return
}
@ -257,14 +237,24 @@ func (srv *Service) BindRoutes(group *gin.RouterGroup) {
})
})
group.GET("/label", func(ctx *gin.Context) {
group.GET("/labels/:label", func(ctx *gin.Context) {
labelParam := ctx.Param("label")
switch labelParam {
case LABEL_LATEST_FOLLOWER:
case LABEL_LATEST_SUBSCRIBER:
case LABEL_LATEST_CHEER:
default:
http.NotFound(ctx.Writer, ctx.Request)
return
}
http.ServeFileFS(ctx.Writer, ctx.Request, pagesFS, "pages/labels.html")
})
}
func (srv *Service) Run(ctx context.Context) {
if srv.oauthToken == nil || !srv.oauthToken.Valid() {
log.Printf("Log in with Twitch: http://localhost:%d/twitch/login", srv.port)
log.Printf("Log in with Twitch: http://%s:%d/twitch/login", srv.host, srv.port)
} else {
if err := srv.start(ctx); err != nil {
log.Printf("Failed to start Twitch service: %v", err)