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

Reload modules on config change

This commit is contained in:
Ash Keel 2021-11-24 11:55:12 +01:00
parent a15b5bafb8
commit ac1ebe65d6
No known key found for this signature in database
GPG key ID: BAD8D93E7314ED3E
3 changed files with 142 additions and 30 deletions

View file

@ -114,7 +114,6 @@ func main() {
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)
}
// Get Stulbe config, if enabled
if moduleConfig.EnableStulbe {
stulbeManager, err := stulbe.Initialize(manager)
if err != nil {

View file

@ -15,6 +15,8 @@ type Manager struct {
Client *stulbe.Client
db *database.DB
logger logrus.FieldLogger
restart chan bool
}
func Initialize(manager *modules.Manager) (*Manager, error) {
@ -47,16 +49,54 @@ func Initialize(manager *modules.Manager) (*Manager, error) {
Client: stulbeClient,
db: db,
logger: logger,
restart: make(chan bool),
}
// Register module
manager.Modules[modules.ModuleStulbe] = stulbeManager
// Receive key updates
go func() {
for {
err := stulbeManager.ReceiveEvents()
if err != nil {
logger.WithError(err).Error("Stulbe subscription died unexpectedly!")
// Wait for config change before retrying
<-stulbeManager.restart
}
}
}()
// Listen for config changes
go db.Subscribe(context.Background(), func(changed []database.ModifiedKV) error {
for _, kv := range changed {
if kv.Key == ConfigKey {
var config Config
err := db.GetJSON(ConfigKey, &config)
if err != nil {
logger.WithError(err).Warn("Failed to get config")
continue
}
client, err := stulbe.NewClient(stulbe.ClientOptions{
Endpoint: config.Endpoint,
Username: config.Username,
AuthKey: config.AuthKey,
Logger: logger,
})
if err != nil {
logger.WithError(err).Warn("Failed to update stulbe client, keeping old settings")
} else {
stulbeManager.Client.Close()
stulbeManager.Client = client
stulbeManager.restart <- true
logger.Info("updated/restarted stulbe client")
}
}
}
return nil
}, ConfigKey)
return stulbeManager, nil
}
@ -66,11 +106,16 @@ func (m *Manager) ReceiveEvents() error {
return err
}
for {
kv := <-chn
select {
case kv := <-chn:
err := m.db.PutKey(kv.Key, []byte(kv.Value))
if err != nil {
return err
}
case <-m.restart:
return nil
}
}
}
@ -99,7 +144,7 @@ func (m *Manager) ReplicateKey(prefix string) error {
m.logger.WithFields(logrus.Fields{
"prefix": prefix,
}).Debug("synched to remote")
}).Debug("synced to remote")
// Subscribe to local datastore and update remote on change
return m.db.Subscribe(context.Background(), func(pairs []database.ModifiedKV) error {

View file

@ -1,9 +1,12 @@
package twitch
import (
"context"
"errors"
"fmt"
jsoniter "github.com/json-iterator/go"
"github.com/strimertul/strimertul/modules"
"github.com/strimertul/strimertul/modules/database"
"github.com/strimertul/strimertul/modules/loyalty"
@ -17,6 +20,8 @@ type Client struct {
db *database.DB
API *helix.Client
logger logrus.FieldLogger
restart chan bool
}
func NewClient(manager *modules.Manager) (*Client, error) {
@ -35,27 +40,16 @@ func NewClient(manager *modules.Manager) (*Client, error) {
}
// Create Twitch client
api, err := helix.NewClient(&helix.Options{
ClientID: config.APIClientID,
ClientSecret: config.APIClientSecret,
})
api, err := getHelixAPI(config.APIClientID, config.APIClientSecret)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to create twitch client: %w", err)
}
// Get access token
resp, err := api.RequestAppAccessToken([]string{"user:read:email"})
if err != nil {
return nil, err
}
// Set the access token on the client
api.SetAppAccessToken(resp.Data.AccessToken)
log.Info("obtained API access token")
client := &Client{
db: db,
API: api,
logger: log,
restart: make(chan bool),
}
// Get Twitch bot config
@ -68,8 +62,13 @@ func NewClient(manager *modules.Manager) (*Client, error) {
// Create and run IRC bot
client.Bot = NewBot(client, twitchBotConfig)
go func() {
if err := client.Bot.Connect(); err != nil {
for {
err := client.RunBot()
if err != nil {
log.WithError(err).Error("failed to connect to Twitch IRC")
// Wait for config change before retrying
<-client.restart
}
}
}()
@ -80,9 +79,78 @@ func NewClient(manager *modules.Manager) (*Client, error) {
manager.Modules[modules.ModuleTwitch] = client
// Listen for config changes
go db.Subscribe(context.Background(), func(changed []database.ModifiedKV) error {
for _, kv := range changed {
switch kv.Key {
case ConfigKey:
err := jsoniter.ConfigFastest.Unmarshal(kv.Data, &config)
if err != nil {
log.WithError(err).Error("failed to unmarshal config")
continue
}
api, err := getHelixAPI(config.APIClientID, config.APIClientSecret)
if err != nil {
log.WithError(err).Warn("failed to create new twitch client, keeping old credentials")
continue
}
client.API = api
log.Info("reloaded/updated Twitch API")
case BotConfigKey:
err := jsoniter.ConfigFastest.Unmarshal(kv.Data, &twitchBotConfig)
if err != nil {
log.WithError(err).Error("failed to unmarshal config")
continue
}
err = client.Bot.Client.Disconnect()
if err != nil {
log.WithError(err).Warn("failed to disconnect from Twitch IRC")
}
client.Bot = NewBot(client, twitchBotConfig)
client.restart <- true
log.Info("reloaded/restarted Twitch bot")
}
}
return nil
}, ConfigKey, BotConfigKey)
return client, nil
}
func getHelixAPI(clientID string, clientSecret string) (*helix.Client, error) {
// Create Twitch client
api, err := helix.NewClient(&helix.Options{
ClientID: clientID,
ClientSecret: clientSecret,
})
if err != nil {
return nil, err
}
// Get access token
resp, err := api.RequestAppAccessToken([]string{"user:read:email"})
if err != nil {
return nil, err
}
// Set the access token on the client
api.SetAppAccessToken(resp.Data.AccessToken)
return api, nil
}
func (c *Client) RunBot() error {
cherr := make(chan error)
go func() {
cherr <- c.Bot.Connect()
}()
select {
case <-c.restart:
return nil
case err := <-cherr:
return err
}
}
func (c *Client) Close() error {
return c.Bot.Client.Disconnect()
}