[core] Add channel specific module configuration interface

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2023-09-02 13:49:15 +02:00
parent dc648d1dba
commit 0a53863b69
Signed by: luzifer
GPG key ID: D91C3E91E4CAD6F5
6 changed files with 100 additions and 4 deletions

View file

@ -56,6 +56,7 @@ type (
PermitAllowModerator bool `yaml:"permit_allow_moderator"` PermitAllowModerator bool `yaml:"permit_allow_moderator"`
PermitTimeout time.Duration `yaml:"permit_timeout"` PermitTimeout time.Duration `yaml:"permit_timeout"`
RawLog string `yaml:"raw_log"` RawLog string `yaml:"raw_log"`
ModuleConfig plugins.ModuleConfig `yaml:"module_config"`
Rules []*plugins.Rule `yaml:"rules"` Rules []*plugins.Rule `yaml:"rules"`
Variables map[string]interface{} `yaml:"variables"` Variables map[string]interface{} `yaml:"variables"`

View file

@ -77,6 +77,17 @@ auto_messages:
# Disable message using templating, must yield string `true` to disable the automated message # Disable message using templating, must yield string `true` to disable the automated message
disable_on_template: '{{ ne .myvariable true }}' 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 # List of rules. See documentation for details or use web-interface
# to configure. # to configure.
rules: # See below for examples rules: # See below for examples

View file

@ -43,6 +43,8 @@ type (
LoggerCreationFunc func(moduleName string) *log.Entry 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) MsgFormatter func(tplString string, m *irc.Message, r *Rule, fields *FieldCollection) (string, error)
MsgModificationFunc func(*irc.Message) error MsgModificationFunc func(*irc.Message) error
@ -65,6 +67,8 @@ type (
GetDatabaseConnector func() database.Connector GetDatabaseConnector func() database.Connector
// GetLogger returns a sirupsen log.Entry pre-configured with the module name // GetLogger returns a sirupsen log.Entry pre-configured with the module name
GetLogger LoggerCreationFunc 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 retrieves a fully configured Twitch client with initialized cache
GetTwitchClient func() *twitch.Client GetTwitchClient func() *twitch.Client
// GetTwitchClientForChannel retrieves a fully configured Twitch client with initialized cache for extended permission channels // GetTwitchClientForChannel retrieves a fully configured Twitch client with initialized cache for extended permission channels

37
plugins/moduleConfig.go Normal file
View file

@ -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
}

View file

@ -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))
}

View file

@ -148,10 +148,6 @@ func registerRoute(route plugins.HTTPRouteRegistrationArgs) error {
func getRegistrationArguments() plugins.RegistrationArguments { func getRegistrationArguments() plugins.RegistrationArguments {
return plugins.RegistrationArguments{ return plugins.RegistrationArguments{
CreateEvent: func(evt string, eventData *plugins.FieldCollection) error {
handleMessage(ircHdl.Client(), nil, &evt, eventData)
return nil
},
FormatMessage: formatMessage, FormatMessage: formatMessage,
FrontendNotify: func(mt string) { frontendNotifyHooks.Ping(mt) }, FrontendNotify: func(mt string) { frontendNotifyHooks.Ping(mt) },
GetDatabaseConnector: func() database.Connector { return db }, GetDatabaseConnector: func() database.Connector { return db },
@ -170,6 +166,15 @@ func getRegistrationArguments() plugins.RegistrationArguments {
SendMessage: sendMessage, SendMessage: sendMessage,
ValidateToken: validateAuthToken, 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) { GetTwitchClientForChannel: func(channel string) (*twitch.Client, error) {
return accessService.GetTwitchClientForChannel(channel, access.ClientConfig{ return accessService.GetTwitchClientForChannel(channel, access.ClientConfig{
TwitchClient: cfg.TwitchClient, TwitchClient: cfg.TwitchClient,