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:
parent
a15b5bafb8
commit
ac1ebe65d6
3 changed files with 142 additions and 30 deletions
1
main.go
1
main.go
|
@ -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)
|
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 {
|
if moduleConfig.EnableStulbe {
|
||||||
stulbeManager, err := stulbe.Initialize(manager)
|
stulbeManager, err := stulbe.Initialize(manager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -15,6 +15,8 @@ type Manager struct {
|
||||||
Client *stulbe.Client
|
Client *stulbe.Client
|
||||||
db *database.DB
|
db *database.DB
|
||||||
logger logrus.FieldLogger
|
logger logrus.FieldLogger
|
||||||
|
|
||||||
|
restart chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func Initialize(manager *modules.Manager) (*Manager, error) {
|
func Initialize(manager *modules.Manager) (*Manager, error) {
|
||||||
|
@ -44,19 +46,57 @@ func Initialize(manager *modules.Manager) (*Manager, error) {
|
||||||
|
|
||||||
// Create manager
|
// Create manager
|
||||||
stulbeManager := &Manager{
|
stulbeManager := &Manager{
|
||||||
Client: stulbeClient,
|
Client: stulbeClient,
|
||||||
db: db,
|
db: db,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
restart: make(chan bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register module
|
// Register module
|
||||||
manager.Modules[modules.ModuleStulbe] = stulbeManager
|
manager.Modules[modules.ModuleStulbe] = stulbeManager
|
||||||
|
|
||||||
|
// Receive key updates
|
||||||
go func() {
|
go func() {
|
||||||
err := stulbeManager.ReceiveEvents()
|
for {
|
||||||
logger.WithError(err).Error("Stulbe subscription died unexpectedly!")
|
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
|
return stulbeManager, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,11 +106,16 @@ func (m *Manager) ReceiveEvents() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
kv := <-chn
|
select {
|
||||||
err := m.db.PutKey(kv.Key, []byte(kv.Value))
|
case kv := <-chn:
|
||||||
if err != nil {
|
err := m.db.PutKey(kv.Key, []byte(kv.Value))
|
||||||
return err
|
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{
|
m.logger.WithFields(logrus.Fields{
|
||||||
"prefix": prefix,
|
"prefix": prefix,
|
||||||
}).Debug("synched to remote")
|
}).Debug("synced to remote")
|
||||||
|
|
||||||
// Subscribe to local datastore and update remote on change
|
// Subscribe to local datastore and update remote on change
|
||||||
return m.db.Subscribe(context.Background(), func(pairs []database.ModifiedKV) error {
|
return m.db.Subscribe(context.Background(), func(pairs []database.ModifiedKV) error {
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package twitch
|
package twitch
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
|
||||||
"github.com/strimertul/strimertul/modules"
|
"github.com/strimertul/strimertul/modules"
|
||||||
"github.com/strimertul/strimertul/modules/database"
|
"github.com/strimertul/strimertul/modules/database"
|
||||||
"github.com/strimertul/strimertul/modules/loyalty"
|
"github.com/strimertul/strimertul/modules/loyalty"
|
||||||
|
@ -17,6 +20,8 @@ type Client struct {
|
||||||
db *database.DB
|
db *database.DB
|
||||||
API *helix.Client
|
API *helix.Client
|
||||||
logger logrus.FieldLogger
|
logger logrus.FieldLogger
|
||||||
|
|
||||||
|
restart chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(manager *modules.Manager) (*Client, error) {
|
func NewClient(manager *modules.Manager) (*Client, error) {
|
||||||
|
@ -35,27 +40,16 @@ func NewClient(manager *modules.Manager) (*Client, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Twitch client
|
// Create Twitch client
|
||||||
api, err := helix.NewClient(&helix.Options{
|
api, err := getHelixAPI(config.APIClientID, config.APIClientSecret)
|
||||||
ClientID: config.APIClientID,
|
|
||||||
ClientSecret: config.APIClientSecret,
|
|
||||||
})
|
|
||||||
if err != nil {
|
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{
|
client := &Client{
|
||||||
db: db,
|
db: db,
|
||||||
API: api,
|
API: api,
|
||||||
logger: log,
|
logger: log,
|
||||||
|
restart: make(chan bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Twitch bot config
|
// Get Twitch bot config
|
||||||
|
@ -68,8 +62,13 @@ func NewClient(manager *modules.Manager) (*Client, error) {
|
||||||
// Create and run IRC bot
|
// Create and run IRC bot
|
||||||
client.Bot = NewBot(client, twitchBotConfig)
|
client.Bot = NewBot(client, twitchBotConfig)
|
||||||
go func() {
|
go func() {
|
||||||
if err := client.Bot.Connect(); err != nil {
|
for {
|
||||||
log.WithError(err).Error("failed to connect to Twitch IRC")
|
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
|
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
|
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 {
|
func (c *Client) Close() error {
|
||||||
return c.Bot.Client.Disconnect()
|
return c.Bot.Client.Disconnect()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue