From 5bd54dd15c6beb6f83395c55e8ee293f8102e7b8 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Sat, 7 Aug 2021 16:16:14 +0200 Subject: [PATCH] Add persistent store and module IDs Signed-off-by: Knut Ahlers --- config.go | 6 ++- main.go | 18 ++++++++- metastore.go | 86 +++++++++++++++++++++++++++++++++++++++++++ mod_clearChannel.go | 6 ++- mod_livePosting.go | 6 ++- mod_liveRole.go | 6 ++- mod_presence.go | 6 ++- mod_streamSchedule.go | 6 ++- modules.go | 3 +- 9 files changed, 134 insertions(+), 9 deletions(-) create mode 100644 metastore.go diff --git a/config.go b/config.go index c1b1bcb..5a1ccb1 100644 --- a/config.go +++ b/config.go @@ -13,13 +13,15 @@ import ( type ( configFile struct { - BotToken string `yaml:"bot_token"` - GuildID string `yaml:"guild_id"` + BotToken string `yaml:"bot_token"` + GuildID string `yaml:"guild_id"` + StoreLocation string `yaml:"store_location"` ModuleConfigs []moduleConfig `yaml:"module_configs"` } moduleConfig struct { + ID string `yaml:"id"` Type string `yaml:"type"` Attributes moduleAttributeStore `yaml:"attributes"` } diff --git a/main.go b/main.go index ba2159f..bc2128b 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,7 @@ var ( }{} config *configFile + store *metaStore version = "dev" ) @@ -57,6 +58,14 @@ func main() { log.WithError(err).Fatal("Unable to load config file") } + if config.StoreLocation == "" { + log.Fatal("Config contains no store location") + } + + if store, err = newMetaStoreFromDisk(config.StoreLocation); err != nil { + log.WithError(err).Fatal("Unable to load store") + } + // Connect to Discord if discord, err = discordgo.New(strings.Join([]string{"Bot", config.BotToken}, " ")); err != nil { log.WithError(err).Fatal("Unable to create discord client") @@ -66,15 +75,22 @@ func main() { for i, mc := range config.ModuleConfigs { logger := log.WithFields(log.Fields{ + "id": mc.ID, "idx": i, "module": mc.Type, }) + + if mc.ID == "" { + logger.Error("Module contains no ID and will be disabled") + continue + } + mod := GetModuleByName(mc.Type) if mod == nil { logger.Fatal("Found configuration for unsupported module") } - if err = mod.Initialize(crontab, discord, mc.Attributes); err != nil { + if err = mod.Initialize(mc.ID, crontab, discord, mc.Attributes); err != nil { logger.WithError(err).Fatal("Unable to initialize module") } diff --git a/metastore.go b/metastore.go new file mode 100644 index 0000000..729a6a1 --- /dev/null +++ b/metastore.go @@ -0,0 +1,86 @@ +package main + +import ( + "encoding/json" + "os" + "sync" + + "github.com/pkg/errors" +) + +type metaStore struct { + ModuleAttributes map[string]moduleAttributeStore `json:"module_attributes"` + + filename string + lock sync.RWMutex +} + +func newMetaStoreFromDisk(filename string) (*metaStore, error) { + out := &metaStore{ + ModuleAttributes: map[string]moduleAttributeStore{}, + filename: filename, + } + + f, err := os.Open(filename) + switch { + case err == nil: + // This is fine + + case os.IsNotExist(err): + // No store yet, return empty store + return out, nil + + default: + return nil, errors.Wrap(err, "opening store") + } + defer f.Close() + + return out, errors.Wrap( + json.NewDecoder(f).Decode(out), + "decoding store", + ) +} + +func (m *metaStore) Save() error { + m.lock.RLock() + defer m.lock.RUnlock() + + return m.save() +} + +func (m *metaStore) save() error { + f, err := os.Create(m.filename) + if err != nil { + return errors.Wrap(err, "creating storage file") + } + defer f.Close() + + return errors.Wrap( + json.NewEncoder(f).Encode(m), + "encoding storage file", + ) +} + +func (m *metaStore) Set(moduleID, key string, value interface{}) error { + m.lock.Lock() + defer m.lock.Unlock() + + if m.ModuleAttributes[moduleID] == nil { + m.ModuleAttributes[moduleID] = make(moduleAttributeStore) + } + + m.ModuleAttributes[moduleID][key] = value + + return errors.Wrap(m.save(), "saving store") +} + +func (m *metaStore) ReadWithLock(moduleID string, fn func(m moduleAttributeStore) error) error { + m.lock.RLock() + defer m.lock.RUnlock() + + if m.ModuleAttributes[moduleID] == nil { + return fn(moduleAttributeStore{}) + } + + return fn(m.ModuleAttributes[moduleID]) +} diff --git a/mod_clearChannel.go b/mod_clearChannel.go index e738034..2c530bb 100644 --- a/mod_clearChannel.go +++ b/mod_clearChannel.go @@ -28,11 +28,15 @@ func init() { type modClearChannel struct { attrs moduleAttributeStore discord *discordgo.Session + id string } -func (m *modClearChannel) Initialize(crontab *cron.Cron, discord *discordgo.Session, attrs moduleAttributeStore) error { +func (m modClearChannel) ID() string { return m.id } + +func (m *modClearChannel) Initialize(id string, crontab *cron.Cron, discord *discordgo.Session, attrs moduleAttributeStore) error { m.attrs = attrs m.discord = discord + m.id = id if err := attrs.Expect( "discord_channel_id", diff --git a/mod_livePosting.go b/mod_livePosting.go index 5b941e3..9c41c18 100644 --- a/mod_livePosting.go +++ b/mod_livePosting.go @@ -36,11 +36,15 @@ func init() { type modLivePosting struct { attrs moduleAttributeStore discord *discordgo.Session + id string } -func (m *modLivePosting) Initialize(crontab *cron.Cron, discord *discordgo.Session, attrs moduleAttributeStore) error { +func (m modLivePosting) ID() string { return m.id } + +func (m *modLivePosting) Initialize(id string, crontab *cron.Cron, discord *discordgo.Session, attrs moduleAttributeStore) error { m.attrs = attrs m.discord = discord + m.id = id if err := attrs.Expect( "discord_channel_id", diff --git a/mod_liveRole.go b/mod_liveRole.go index a044bdf..cea3842 100644 --- a/mod_liveRole.go +++ b/mod_liveRole.go @@ -24,11 +24,15 @@ func init() { type modLiveRole struct { attrs moduleAttributeStore discord *discordgo.Session + id string } -func (m *modLiveRole) Initialize(crontab *cron.Cron, discord *discordgo.Session, attrs moduleAttributeStore) error { +func (m modLiveRole) ID() string { return m.id } + +func (m *modLiveRole) Initialize(id string, crontab *cron.Cron, discord *discordgo.Session, attrs moduleAttributeStore) error { m.attrs = attrs m.discord = discord + m.id = id if err := attrs.Expect( "role_streamers_live", diff --git a/mod_presence.go b/mod_presence.go index 2e88233..4c6ae94 100644 --- a/mod_presence.go +++ b/mod_presence.go @@ -28,11 +28,15 @@ func init() { type modPresence struct { attrs moduleAttributeStore discord *discordgo.Session + id string } -func (m *modPresence) Initialize(crontab *cron.Cron, discord *discordgo.Session, attrs moduleAttributeStore) error { +func (m modPresence) ID() string { return m.id } + +func (m *modPresence) Initialize(id string, crontab *cron.Cron, discord *discordgo.Session, attrs moduleAttributeStore) error { m.attrs = attrs m.discord = discord + m.id = id if err := attrs.Expect( "fallback_text", diff --git a/mod_streamSchedule.go b/mod_streamSchedule.go index 579b34b..e5541fd 100644 --- a/mod_streamSchedule.go +++ b/mod_streamSchedule.go @@ -34,11 +34,15 @@ func init() { type modStreamSchedule struct { attrs moduleAttributeStore discord *discordgo.Session + id string } -func (m *modStreamSchedule) Initialize(crontab *cron.Cron, discord *discordgo.Session, attrs moduleAttributeStore) error { +func (m modStreamSchedule) ID() string { return m.id } + +func (m *modStreamSchedule) Initialize(id string, crontab *cron.Cron, discord *discordgo.Session, attrs moduleAttributeStore) error { m.attrs = attrs m.discord = discord + m.id = id if err := attrs.Expect( "discord_channel_id", diff --git a/modules.go b/modules.go index 7182160..6cb02a5 100644 --- a/modules.go +++ b/modules.go @@ -15,7 +15,8 @@ var ( type ( module interface { - Initialize(crontab *cron.Cron, discord *discordgo.Session, attrs moduleAttributeStore) error + ID() string + Initialize(id string, crontab *cron.Cron, discord *discordgo.Session, attrs moduleAttributeStore) error Setup() error }