diff --git a/config.go b/config.go index 3ecb363..1c12610 100644 --- a/config.go +++ b/config.go @@ -56,6 +56,7 @@ type ( PermitAllowModerator bool `yaml:"permit_allow_moderator"` PermitTimeout time.Duration `yaml:"permit_timeout"` RawLog string `yaml:"raw_log"` + ModuleConfig plugins.ModuleConfig `yaml:"module_config"` Rules []*plugins.Rule `yaml:"rules"` Variables map[string]interface{} `yaml:"variables"` diff --git a/docs/content/configuration/config-file.md b/docs/content/configuration/config-file.md index f43a525..4e24a3b 100644 --- a/docs/content/configuration/config-file.md +++ b/docs/content/configuration/config-file.md @@ -77,6 +77,17 @@ auto_messages: # Disable message using templating, must yield string `true` to disable the automated message disable_on_template: '{{ ne .myvariable true }}' +# Module configuration by channel or defining bot-wide defaults. See +# module specific documentation for options to configure in this +# section. All modules come with internal defaults so there is no +# need to configure this but you can overwrite the internal defaults. +module_config: + some-module: # Name of the module to configure + default: # Bot-wide, fallback for all channels + some_option: true + mychannel: # Channel-specific, only valid for this channel + some_option: false + # List of rules. See documentation for details or use web-interface # to configure. rules: # See below for examples diff --git a/plugins/interface.go b/plugins/interface.go index df49d9a..102b23b 100644 --- a/plugins/interface.go +++ b/plugins/interface.go @@ -43,6 +43,8 @@ type ( LoggerCreationFunc func(moduleName string) *log.Entry + ModuleConfigGetterFunc func(module, channel string) *FieldCollection + MsgFormatter func(tplString string, m *irc.Message, r *Rule, fields *FieldCollection) (string, error) MsgModificationFunc func(*irc.Message) error @@ -65,6 +67,8 @@ type ( GetDatabaseConnector func() database.Connector // GetLogger returns a sirupsen log.Entry pre-configured with the module name GetLogger LoggerCreationFunc + // GetModuleConfigForChannel returns the module configuration for the given channel if available + GetModuleConfigForChannel ModuleConfigGetterFunc // GetTwitchClient retrieves a fully configured Twitch client with initialized cache GetTwitchClient func() *twitch.Client // GetTwitchClientForChannel retrieves a fully configured Twitch client with initialized cache for extended permission channels diff --git a/plugins/moduleConfig.go b/plugins/moduleConfig.go new file mode 100644 index 0000000..3afec4c --- /dev/null +++ b/plugins/moduleConfig.go @@ -0,0 +1,37 @@ +package plugins + +import "strings" + +// DefaultConfigName is the name the default configuration must have +// when defined +const DefaultConfigName = "default" + +type ( + // ModuleConfig represents a mapping of configurations per channel + // and module + ModuleConfig map[string]map[string]*FieldCollection +) + +// GetChannelConfig reads the channel specific configuration for the +// given module. This is created by taking an empty FieldCollection, +// merging in the default configuration and finally overwriting all +// existing channel configurations. +func (m ModuleConfig) GetChannelConfig(module, channel string) *FieldCollection { + channel = strings.TrimLeft(channel, "#@") + composed := NewFieldCollection() + + for _, i := range []string{DefaultConfigName, channel} { + f := m[module][i] + if f == nil { + // That config does not exist, don't apply + continue + } + + for k, v := range f.Data() { + // Overwrite all keys defined in this config + composed.Set(k, v) + } + } + + return composed +} diff --git a/plugins/moduleConfig_test.go b/plugins/moduleConfig_test.go new file mode 100644 index 0000000..0e02d69 --- /dev/null +++ b/plugins/moduleConfig_test.go @@ -0,0 +1,38 @@ +package plugins + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestModuleConfigGet(t *testing.T) { + strPtrEmpty := func(v string) *string { return &v }("") + m := ModuleConfig{ + "test": map[string]*FieldCollection{ + DefaultConfigName: FieldCollectionFromData(map[string]any{ + "setindefault": DefaultConfigName, + "setinboth": DefaultConfigName, + }), + "test": FieldCollectionFromData(map[string]any{ + "setinchannel": "channel", + "setinboth": "channel", + }), + }, + } + + fields := m.GetChannelConfig("module_does_not_exist", "test") + require.NotNil(t, fields, "must always return a valid FieldCollection") + assert.Len(t, fields.Data(), 0) + + fields = m.GetChannelConfig("test", "test") + assert.Equal(t, DefaultConfigName, fields.MustString("setindefault", strPtrEmpty)) + assert.Equal(t, "channel", fields.MustString("setinchannel", strPtrEmpty)) + assert.Equal(t, "channel", fields.MustString("setinboth", strPtrEmpty)) + + fields = m.GetChannelConfig("test", "channel_not_configured") + assert.Equal(t, DefaultConfigName, fields.MustString("setindefault", strPtrEmpty)) + assert.Equal(t, "", fields.MustString("setinchannel", strPtrEmpty)) + assert.Equal(t, DefaultConfigName, fields.MustString("setinboth", strPtrEmpty)) +} diff --git a/plugins_core.go b/plugins_core.go index 69f8294..e9c56b0 100644 --- a/plugins_core.go +++ b/plugins_core.go @@ -148,10 +148,6 @@ func registerRoute(route plugins.HTTPRouteRegistrationArgs) error { func getRegistrationArguments() plugins.RegistrationArguments { return plugins.RegistrationArguments{ - CreateEvent: func(evt string, eventData *plugins.FieldCollection) error { - handleMessage(ircHdl.Client(), nil, &evt, eventData) - return nil - }, FormatMessage: formatMessage, FrontendNotify: func(mt string) { frontendNotifyHooks.Ping(mt) }, GetDatabaseConnector: func() database.Connector { return db }, @@ -170,6 +166,15 @@ func getRegistrationArguments() plugins.RegistrationArguments { SendMessage: sendMessage, ValidateToken: validateAuthToken, + CreateEvent: func(evt string, eventData *plugins.FieldCollection) error { + handleMessage(ircHdl.Client(), nil, &evt, eventData) + return nil + }, + + GetModuleConfigForChannel: func(module, channel string) *plugins.FieldCollection { + return config.ModuleConfig.GetChannelConfig(module, channel) + }, + GetTwitchClientForChannel: func(channel string) (*twitch.Client, error) { return accessService.GetTwitchClientForChannel(channel, access.ClientConfig{ TwitchClient: cfg.TwitchClient,