mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-12-20 11:51:17 +00:00
[core] Add config validation command
- Fix missing field validation for required fields - Add validation of template fields - Report all issues in configuration Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
ae7879447e
commit
2c71f57d02
23 changed files with 180 additions and 29 deletions
|
@ -37,6 +37,7 @@ Supported sub-commands are:
|
||||||
actor-docs Generate markdown documentation for available actors
|
actor-docs Generate markdown documentation for available actors
|
||||||
api-token <name> <scope...> Generate an api-token to be entered into the config
|
api-token <name> <scope...> Generate an api-token to be entered into the config
|
||||||
migrate-v2 <old file> Migrate old (*.json.gz) storage file into new database
|
migrate-v2 <old file> Migrate old (*.json.gz) storage file into new database
|
||||||
|
validate-config Try to load configuration file and report errors if any
|
||||||
help Prints this help message
|
help Prints this help message
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -124,10 +124,17 @@ func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eve
|
||||||
func (a ActorScript) IsAsync() bool { return false }
|
func (a ActorScript) IsAsync() bool { return false }
|
||||||
func (a ActorScript) Name() string { return "script" }
|
func (a ActorScript) Name() string { return "script" }
|
||||||
|
|
||||||
func (a ActorScript) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a ActorScript) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
if cmd, err := attrs.StringSlice("command"); err != nil || len(cmd) == 0 {
|
cmd, err := attrs.StringSlice("command")
|
||||||
|
if err != nil || len(cmd) == 0 {
|
||||||
return errors.New("command must be slice of strings with length > 0")
|
return errors.New("command must be slice of strings with length > 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i, el := range cmd {
|
||||||
|
if err = tplValidator(el); err != nil {
|
||||||
|
return errors.Wrapf(err, "validating cmd template (element %d)", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
19
config.go
19
config.go
|
@ -357,21 +357,34 @@ func (c *configFile) updateAutoMessagesFromConfig(old *configFile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c configFile) validateRuleActions() error {
|
func (c configFile) validateRuleActions() error {
|
||||||
|
var hasError bool
|
||||||
|
|
||||||
for _, r := range c.Rules {
|
for _, r := range c.Rules {
|
||||||
logger := log.WithField("rule", r.MatcherID())
|
logger := log.WithField("rule", r.MatcherID())
|
||||||
|
|
||||||
|
if err := r.Validate(validateTemplate); err != nil {
|
||||||
|
logger.WithError(err).Error("Rule reported invalid config")
|
||||||
|
hasError = true
|
||||||
|
}
|
||||||
|
|
||||||
for idx, a := range r.Actions {
|
for idx, a := range r.Actions {
|
||||||
actor, err := getActorByName(a.Type)
|
actor, err := getActorByName(a.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.WithField("index", idx).WithError(err).Error("Cannot get actor by type")
|
logger.WithField("index", idx).WithError(err).Error("Cannot get actor by type")
|
||||||
return errors.Wrap(err, "getting actor by type")
|
hasError = true
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = actor.Validate(a.Attributes); err != nil {
|
if err = actor.Validate(validateTemplate, a.Attributes); err != nil {
|
||||||
logger.WithField("index", idx).WithError(err).Error("Actor reported invalid config")
|
logger.WithField("index", idx).WithError(err).Error("Actor reported invalid config")
|
||||||
return errors.Wrap(err, "validating action attributes")
|
hasError = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hasError {
|
||||||
|
return errors.New("config validation reported errors, see log")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,18 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
|
||||||
func (a actor) IsAsync() bool { return false }
|
func (a actor) IsAsync() bool { return false }
|
||||||
func (a actor) Name() string { return actorName }
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) { return nil }
|
func (a actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
|
reasonTemplate, err := attrs.String("reason")
|
||||||
|
if err != nil || reasonTemplate == "" {
|
||||||
|
return errors.New("reason must be non-empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tplValidator(reasonTemplate); err != nil {
|
||||||
|
return errors.Wrap(err, "validating reason template")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func handleAPIBan(w http.ResponseWriter, r *http.Request) {
|
func handleAPIBan(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -187,11 +187,17 @@ func (a ActorCounter) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, ev
|
||||||
func (a ActorCounter) IsAsync() bool { return false }
|
func (a ActorCounter) IsAsync() bool { return false }
|
||||||
func (a ActorCounter) Name() string { return "counter" }
|
func (a ActorCounter) Name() string { return "counter" }
|
||||||
|
|
||||||
func (a ActorCounter) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a ActorCounter) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
if cn, err := attrs.String("counter"); err != nil || cn == "" {
|
if cn, err := attrs.String("counter"); err != nil || cn == "" {
|
||||||
return errors.New("counter name must be non-empty string")
|
return errors.New("counter name must be non-empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, field := range []string{"counter", "counter_step", "counter_set"} {
|
||||||
|
if err = tplValidator(attrs.MustString(field, ptrStringEmpty)); err != nil {
|
||||||
|
return errors.Wrapf(err, "validating %s template", field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,4 +69,6 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
|
||||||
func (a actor) IsAsync() bool { return false }
|
func (a actor) IsAsync() bool { return false }
|
||||||
func (a actor) Name() string { return actorName }
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) { return nil }
|
func (a actor) Validate(plugins.TemplateValidatorFunc, *plugins.FieldCollection) (err error) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -46,4 +46,6 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
|
||||||
func (a actor) IsAsync() bool { return false }
|
func (a actor) IsAsync() bool { return false }
|
||||||
func (a actor) Name() string { return actorName }
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) { return nil }
|
func (a actor) Validate(plugins.TemplateValidatorFunc, *plugins.FieldCollection) (err error) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -106,10 +106,15 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
|
||||||
func (a actor) IsAsync() bool { return true }
|
func (a actor) IsAsync() bool { return true }
|
||||||
func (a actor) Name() string { return actorName }
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
func (a actor) Validate(attrs *plugins.FieldCollection) error {
|
func (a actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) error {
|
||||||
if v, err := attrs.String("source"); err != nil || v == "" {
|
sourceTpl, err := attrs.String("source")
|
||||||
|
if err != nil || sourceTpl == "" {
|
||||||
return errors.New("source is expected to be non-empty string")
|
return errors.New("source is expected to be non-empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = tplValidator(sourceTpl); err != nil {
|
||||||
|
return errors.Wrap(err, "validating source template")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ const actorName = "modchannel"
|
||||||
var (
|
var (
|
||||||
formatMessage plugins.MsgFormatter
|
formatMessage plugins.MsgFormatter
|
||||||
tcGetter func(string) (*twitch.Client, error)
|
tcGetter func(string) (*twitch.Client, error)
|
||||||
|
|
||||||
|
ptrStringEmpty = func(s string) *string { return &s }("")
|
||||||
)
|
)
|
||||||
|
|
||||||
func Register(args plugins.RegistrationArguments) error {
|
func Register(args plugins.RegistrationArguments) error {
|
||||||
|
@ -67,9 +69,8 @@ type actor struct{}
|
||||||
|
|
||||||
func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (preventCooldown bool, err error) {
|
func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (preventCooldown bool, err error) {
|
||||||
var (
|
var (
|
||||||
ptrStringEmpty = func(v string) *string { return &v }("")
|
game = attrs.MustString("game", ptrStringEmpty)
|
||||||
game = attrs.MustString("game", ptrStringEmpty)
|
title = attrs.MustString("title", ptrStringEmpty)
|
||||||
title = attrs.MustString("title", ptrStringEmpty)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if game == "" && title == "" {
|
if game == "" && title == "" {
|
||||||
|
@ -115,10 +116,16 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
|
||||||
func (a actor) IsAsync() bool { return false }
|
func (a actor) IsAsync() bool { return false }
|
||||||
func (a actor) Name() string { return actorName }
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
if v, err := attrs.String("channel"); err != nil || v == "" {
|
if v, err := attrs.String("channel"); err != nil || v == "" {
|
||||||
return errors.New("channel must be non-empty string")
|
return errors.New("channel must be non-empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, field := range []string{"channel", "game", "title"} {
|
||||||
|
if err = tplValidator(attrs.MustString(field, ptrStringEmpty)); err != nil {
|
||||||
|
return errors.Wrapf(err, "validating %s template", field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ var (
|
||||||
messageStoreLock sync.RWMutex
|
messageStoreLock sync.RWMutex
|
||||||
|
|
||||||
ptrStringDelete = func(v string) *string { return &v }("delete")
|
ptrStringDelete = func(v string) *string { return &v }("delete")
|
||||||
|
ptrStringEmpty = func(s string) *string { return &s }("")
|
||||||
ptrString10m = func(v string) *string { return &v }("10m")
|
ptrString10m = func(v string) *string { return &v }("10m")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -230,10 +231,16 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
|
||||||
func (a actor) IsAsync() bool { return false }
|
func (a actor) IsAsync() bool { return false }
|
||||||
func (a actor) Name() string { return actorName }
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
if v, err := attrs.String("match"); err != nil || v == "" {
|
if v, err := attrs.String("match"); err != nil || v == "" {
|
||||||
return errors.New("match must be non-empty string")
|
return errors.New("match must be non-empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, field := range []string{"scan", "action", "match"} {
|
||||||
|
if err = tplValidator(attrs.MustString(field, ptrStringEmpty)); err != nil {
|
||||||
|
return errors.Wrapf(err, "validating %s template", field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,7 +214,7 @@ func (a actorPunish) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eve
|
||||||
func (a actorPunish) IsAsync() bool { return false }
|
func (a actorPunish) IsAsync() bool { return false }
|
||||||
func (a actorPunish) Name() string { return actorNamePunish }
|
func (a actorPunish) Name() string { return actorNamePunish }
|
||||||
|
|
||||||
func (a actorPunish) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a actorPunish) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
if v, err := attrs.String("user"); err != nil || v == "" {
|
if v, err := attrs.String("user"); err != nil || v == "" {
|
||||||
return errors.New("user must be non-empty string")
|
return errors.New("user must be non-empty string")
|
||||||
}
|
}
|
||||||
|
@ -223,6 +223,10 @@ func (a actorPunish) Validate(attrs *plugins.FieldCollection) (err error) {
|
||||||
return errors.New("levels must be slice of strings with length > 0")
|
return errors.New("levels must be slice of strings with length > 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = tplValidator(attrs.MustString("user", ptrStringEmpty)); err != nil {
|
||||||
|
return errors.Wrap(err, "validating user template")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,10 +251,14 @@ func (a actorResetPunish) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule
|
||||||
func (a actorResetPunish) IsAsync() bool { return false }
|
func (a actorResetPunish) IsAsync() bool { return false }
|
||||||
func (a actorResetPunish) Name() string { return actorNameResetPunish }
|
func (a actorResetPunish) Name() string { return actorNameResetPunish }
|
||||||
|
|
||||||
func (a actorResetPunish) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a actorResetPunish) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
if v, err := attrs.String("user"); err != nil || v == "" {
|
if v, err := attrs.String("user"); err != nil || v == "" {
|
||||||
return errors.New("user must be non-empty string")
|
return errors.New("user must be non-empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = tplValidator(attrs.MustString("user", ptrStringEmpty)); err != nil {
|
||||||
|
return errors.Wrap(err, "validating user template")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
|
||||||
func (a actor) IsAsync() bool { return false }
|
func (a actor) IsAsync() bool { return false }
|
||||||
func (a actor) Name() string { return actorName }
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
action := attrs.MustString("action", ptrStringEmpty)
|
action := attrs.MustString("action", ptrStringEmpty)
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
|
@ -194,5 +194,11 @@ func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
|
||||||
return errors.New("action must be one of add, del or get")
|
return errors.New("action must be one of add, del or get")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, field := range []string{"index", "quote", "format"} {
|
||||||
|
if err = tplValidator(attrs.MustString(field, ptrStringEmpty)); err != nil {
|
||||||
|
return errors.Wrapf(err, "validating %s template", field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ const actorName = "raw"
|
||||||
var (
|
var (
|
||||||
formatMessage plugins.MsgFormatter
|
formatMessage plugins.MsgFormatter
|
||||||
send plugins.SendMessageFunc
|
send plugins.SendMessageFunc
|
||||||
|
|
||||||
|
ptrStringEmpty = func(s string) *string { return &s }("")
|
||||||
)
|
)
|
||||||
|
|
||||||
func Register(args plugins.RegistrationArguments) error {
|
func Register(args plugins.RegistrationArguments) error {
|
||||||
|
@ -63,10 +65,14 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
|
||||||
func (a actor) IsAsync() bool { return false }
|
func (a actor) IsAsync() bool { return false }
|
||||||
func (a actor) Name() string { return actorName }
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
if v, err := attrs.String("message"); err != nil || v == "" {
|
if v, err := attrs.String("message"); err != nil || v == "" {
|
||||||
return errors.New("message must be non-empty string")
|
return errors.New("message must be non-empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = tplValidator(attrs.MustString("message", ptrStringEmpty)); err != nil {
|
||||||
|
return errors.Wrap(err, "validating message template")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,8 @@ var (
|
||||||
formatMessage plugins.MsgFormatter
|
formatMessage plugins.MsgFormatter
|
||||||
send plugins.SendMessageFunc
|
send plugins.SendMessageFunc
|
||||||
|
|
||||||
ptrBoolFalse = func(v bool) *bool { return &v }(false)
|
ptrBoolFalse = func(v bool) *bool { return &v }(false)
|
||||||
|
ptrStringEmpty = func(s string) *string { return &s }("")
|
||||||
)
|
)
|
||||||
|
|
||||||
func Register(args plugins.RegistrationArguments) error {
|
func Register(args plugins.RegistrationArguments) error {
|
||||||
|
@ -121,10 +122,16 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
|
||||||
func (a actor) IsAsync() bool { return false }
|
func (a actor) IsAsync() bool { return false }
|
||||||
func (a actor) Name() string { return actorName }
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
if v, err := attrs.String("message"); err != nil || v == "" {
|
if v, err := attrs.String("message"); err != nil || v == "" {
|
||||||
return errors.New("message must be non-empty string")
|
return errors.New("message must be non-empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, field := range []string{"message", "fallback"} {
|
||||||
|
if err = tplValidator(attrs.MustString(field, ptrStringEmpty)); err != nil {
|
||||||
|
return errors.Wrapf(err, "validating %s template", field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,11 +82,19 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
|
||||||
func (a actor) IsAsync() bool { return false }
|
func (a actor) IsAsync() bool { return false }
|
||||||
func (a actor) Name() string { return actorName }
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
if v, err := attrs.Duration("duration"); err != nil || v < time.Second {
|
if v, err := attrs.Duration("duration"); err != nil || v < time.Second {
|
||||||
return errors.New("duration must be of type duration greater or equal one second")
|
return errors.New("duration must be of type duration greater or equal one second")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, err := attrs.String("reason"); err != nil || v == "" {
|
||||||
|
return errors.New("reason must be non-empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tplValidator(attrs.MustString("reason", ptrStringEmpty)); err != nil {
|
||||||
|
return errors.Wrap(err, "validating reason template")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -150,11 +150,17 @@ func (a ActorSetVariable) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule
|
||||||
func (a ActorSetVariable) IsAsync() bool { return false }
|
func (a ActorSetVariable) IsAsync() bool { return false }
|
||||||
func (a ActorSetVariable) Name() string { return "setvariable" }
|
func (a ActorSetVariable) Name() string { return "setvariable" }
|
||||||
|
|
||||||
func (a ActorSetVariable) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a ActorSetVariable) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
if v, err := attrs.String("variable"); err != nil || v == "" {
|
if v, err := attrs.String("variable"); err != nil || v == "" {
|
||||||
return errors.New("variable name must be non-empty string")
|
return errors.New("variable name must be non-empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, field := range []string{"set", "variable"} {
|
||||||
|
if err = tplValidator(attrs.MustString(field, ptrStringEmpty)); err != nil {
|
||||||
|
return errors.Wrapf(err, "validating %s template", field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ const actorName = "whisper"
|
||||||
var (
|
var (
|
||||||
botTwitchClient *twitch.Client
|
botTwitchClient *twitch.Client
|
||||||
formatMessage plugins.MsgFormatter
|
formatMessage plugins.MsgFormatter
|
||||||
|
|
||||||
|
ptrStringEmpty = func(s string) *string { return &s }("")
|
||||||
)
|
)
|
||||||
|
|
||||||
func Register(args plugins.RegistrationArguments) error {
|
func Register(args plugins.RegistrationArguments) error {
|
||||||
|
@ -73,7 +75,7 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
|
||||||
func (a actor) IsAsync() bool { return false }
|
func (a actor) IsAsync() bool { return false }
|
||||||
func (a actor) Name() string { return actorName }
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
if v, err := attrs.String("to"); err != nil || v == "" {
|
if v, err := attrs.String("to"); err != nil || v == "" {
|
||||||
return errors.New("to must be non-empty string")
|
return errors.New("to must be non-empty string")
|
||||||
}
|
}
|
||||||
|
@ -82,5 +84,11 @@ func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
|
||||||
return errors.New("message must be non-empty string")
|
return errors.New("message must be non-empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, field := range []string{"message", "to"} {
|
||||||
|
if err = tplValidator(attrs.MustString(field, ptrStringEmpty)); err != nil {
|
||||||
|
return errors.Wrapf(err, "validating %s template", field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,6 @@ import (
|
||||||
type actor struct{}
|
type actor struct{}
|
||||||
|
|
||||||
func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (preventCooldown bool, err error) {
|
func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (preventCooldown bool, err error) {
|
||||||
ptrStringEmpty := func(v string) *string { return &v }("")
|
|
||||||
|
|
||||||
fd, err := formatMessage(attrs.MustString("fields", ptrStringEmpty), m, r, eventData)
|
fd, err := formatMessage(attrs.MustString("fields", ptrStringEmpty), m, r, eventData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.Wrap(err, "executing fields template")
|
return false, errors.Wrap(err, "executing fields template")
|
||||||
|
@ -51,10 +49,16 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
|
||||||
func (a actor) IsAsync() bool { return false }
|
func (a actor) IsAsync() bool { return false }
|
||||||
func (a actor) Name() string { return actorName }
|
func (a actor) Name() string { return actorName }
|
||||||
|
|
||||||
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
|
func (a actor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
|
||||||
if v, err := attrs.String("fields"); err != nil || v == "" {
|
if v, err := attrs.String("fields"); err != nil || v == "" {
|
||||||
return errors.New("fields is expected to be non-empty string")
|
return errors.New("fields is expected to be non-empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, field := range []string{"fields", "schedule_in"} {
|
||||||
|
if err = tplValidator(attrs.MustString(field, ptrStringEmpty)); err != nil {
|
||||||
|
return errors.Wrapf(err, "validating %s template", field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ var (
|
||||||
eventCreatorFunc plugins.EventHandlerFunc
|
eventCreatorFunc plugins.EventHandlerFunc
|
||||||
formatMessage plugins.MsgFormatter
|
formatMessage plugins.MsgFormatter
|
||||||
mc *memoryCache
|
mc *memoryCache
|
||||||
|
|
||||||
|
ptrStringEmpty = func(s string) *string { return &s }("")
|
||||||
)
|
)
|
||||||
|
|
||||||
func Register(args plugins.RegistrationArguments) error {
|
func Register(args plugins.RegistrationArguments) error {
|
||||||
|
|
6
main.go
6
main.go
|
@ -179,6 +179,7 @@ func handleSubCommand(args []string) {
|
||||||
fmt.Println(" actor-docs Generate markdown documentation for available actors")
|
fmt.Println(" actor-docs Generate markdown documentation for available actors")
|
||||||
fmt.Println(" api-token <name> <scope...> Generate an api-token to be entered into the config")
|
fmt.Println(" api-token <name> <scope...> Generate an api-token to be entered into the config")
|
||||||
fmt.Println(" migrate-v2 <old file> Migrate old (*.json.gz) storage file into new database")
|
fmt.Println(" migrate-v2 <old file> Migrate old (*.json.gz) storage file into new database")
|
||||||
|
fmt.Println(" validate-config Try to load configuration file and report errors if any")
|
||||||
fmt.Println(" help Prints this help message")
|
fmt.Println(" help Prints this help message")
|
||||||
|
|
||||||
case "migrate-v2":
|
case "migrate-v2":
|
||||||
|
@ -197,6 +198,11 @@ func handleSubCommand(args []string) {
|
||||||
|
|
||||||
log.Info("v2 storage file was migrated")
|
log.Info("v2 storage file was migrated")
|
||||||
|
|
||||||
|
case "validate-config":
|
||||||
|
if err := loadConfig(cfg.Config); err != nil {
|
||||||
|
log.WithError(err).Fatal("loading config")
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
handleSubCommand([]string{"help"})
|
handleSubCommand([]string{"help"})
|
||||||
log.Fatalf("Unknown sub-command %q", args[0])
|
log.Fatalf("Unknown sub-command %q", args[0])
|
||||||
|
|
|
@ -86,3 +86,14 @@ func formatMessageFieldUserID(compiledFields *plugins.FieldCollection, m *irc.Me
|
||||||
func formatMessageFieldUsername(compiledFields *plugins.FieldCollection, m *irc.Message, fields *plugins.FieldCollection) {
|
func formatMessageFieldUsername(compiledFields *plugins.FieldCollection, m *irc.Message, fields *plugins.FieldCollection) {
|
||||||
compiledFields.Set("username", plugins.DeriveUser(m, fields))
|
compiledFields.Set("username", plugins.DeriveUser(m, fields))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateTemplate(tplString string) error {
|
||||||
|
// Template in frontend supports newlines, messages do not
|
||||||
|
tplString = stripNewline.ReplaceAllString(tplString, " ")
|
||||||
|
|
||||||
|
_, err := template.
|
||||||
|
New(tplString).
|
||||||
|
Funcs(tplFuncs.GetFuncMap(nil, nil, plugins.NewFieldCollection())).
|
||||||
|
Parse(tplString)
|
||||||
|
return errors.Wrap(err, "parsing template")
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ type (
|
||||||
// Validate will be called to validate the loaded configuration. It should
|
// Validate will be called to validate the loaded configuration. It should
|
||||||
// return an error if required keys are missing from the AttributeStore
|
// return an error if required keys are missing from the AttributeStore
|
||||||
// or if keys contain broken configs
|
// or if keys contain broken configs
|
||||||
Validate(*FieldCollection) error
|
Validate(TemplateValidatorFunc, *FieldCollection) error
|
||||||
}
|
}
|
||||||
|
|
||||||
ActorCreationFunc func() Actor
|
ActorCreationFunc func() Actor
|
||||||
|
@ -105,6 +105,8 @@ type (
|
||||||
TemplateFuncGetter func(*irc.Message, *Rule, *FieldCollection) interface{}
|
TemplateFuncGetter func(*irc.Message, *Rule, *FieldCollection) interface{}
|
||||||
TemplateFuncRegister func(name string, fg TemplateFuncGetter)
|
TemplateFuncRegister func(name string, fg TemplateFuncGetter)
|
||||||
|
|
||||||
|
TemplateValidatorFunc func(raw string) error
|
||||||
|
|
||||||
ValidateTokenFunc func(token string, modules ...string) error
|
ValidateTokenFunc func(token string, modules ...string) error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -193,6 +193,22 @@ func (r *Rule) UpdateFromSubscription() (bool, error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r Rule) Validate(tplValidate TemplateValidatorFunc) error {
|
||||||
|
if r.MatchMessage != nil {
|
||||||
|
if _, err := regexp.Compile(*r.MatchMessage); err != nil {
|
||||||
|
return errors.Wrap(err, "compiling match_message field regex")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.DisableOnTemplate != nil {
|
||||||
|
if err := tplValidate(*r.DisableOnTemplate); err != nil {
|
||||||
|
return errors.Wrap(err, "parsing disable_on_template template")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Rule) allowExecuteBadgeBlacklist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
|
func (r *Rule) allowExecuteBadgeBlacklist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
|
||||||
for _, b := range r.DisableOn {
|
for _, b := range r.DisableOn {
|
||||||
if badges.Has(b) {
|
if badges.Has(b) {
|
||||||
|
|
Loading…
Reference in a new issue