[core] BREAKING: Allow actors to set fields those after them (#11)

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2021-11-11 14:59:08 +01:00
parent 3ba25e6db2
commit 8ba6d2ef08
Signed by: luzifer
GPG key ID: 0066F03ED215AD7D
29 changed files with 402 additions and 158 deletions

View file

@ -107,7 +107,7 @@ func init() {
type ActorCounter struct{}
func (a ActorCounter) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData plugins.FieldCollection, attrs plugins.FieldCollection) (preventCooldown bool, err error) {
func (a ActorCounter) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (preventCooldown bool, err error) {
counterName, err := formatMessage(attrs.MustString("counter", nil), m, r, eventData)
if err != nil {
return false, errors.Wrap(err, "preparing response")
@ -144,7 +144,7 @@ func (a ActorCounter) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, ev
func (a ActorCounter) IsAsync() bool { return false }
func (a ActorCounter) Name() string { return "counter" }
func (a ActorCounter) Validate(attrs plugins.FieldCollection) (err error) {
func (a ActorCounter) Validate(attrs *plugins.FieldCollection) (err error) {
if cn, err := attrs.String("counter"); err != nil || cn == "" {
return errors.New("counter name must be non-empty string")
}

View file

@ -46,7 +46,7 @@ func init() {
type ActorScript struct{}
func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData plugins.FieldCollection, attrs plugins.FieldCollection) (preventCooldown bool, err error) {
func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (preventCooldown bool, err error) {
command, err := attrs.StringSlice("command")
if err != nil {
return false, errors.Wrap(err, "getting command")
@ -123,7 +123,7 @@ func (a ActorScript) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eve
func (a ActorScript) IsAsync() bool { return false }
func (a ActorScript) Name() string { return "script" }
func (a ActorScript) Validate(attrs plugins.FieldCollection) (err error) {
func (a ActorScript) Validate(attrs *plugins.FieldCollection) (err error) {
if cmd, err := attrs.StringSlice("command"); err != nil || len(cmd) == 0 {
return errors.New("command must be slice of strings with length > 0")
}

View file

@ -92,7 +92,7 @@ func init() {
type ActorSetVariable struct{}
func (a ActorSetVariable) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData plugins.FieldCollection, attrs plugins.FieldCollection) (preventCooldown bool, err error) {
func (a ActorSetVariable) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (preventCooldown bool, err error) {
varName, err := formatMessage(attrs.MustString("variable", nil), m, r, eventData)
if err != nil {
return false, errors.Wrap(err, "preparing variable name")
@ -119,7 +119,7 @@ func (a ActorSetVariable) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule
func (a ActorSetVariable) IsAsync() bool { return false }
func (a ActorSetVariable) Name() string { return "setvariable" }
func (a ActorSetVariable) Validate(attrs plugins.FieldCollection) (err error) {
func (a ActorSetVariable) Validate(attrs *plugins.FieldCollection) (err error) {
if v, err := attrs.String("variable"); err != nil || v == "" {
return errors.New("variable name must be non-empty string")
}

View file

@ -40,7 +40,7 @@ func registerAction(name string, acf plugins.ActorCreationFunc) {
availableActions[name] = acf
}
func triggerAction(c *irc.Client, m *irc.Message, rule *plugins.Rule, ra *plugins.RuleAction, eventData plugins.FieldCollection) (preventCooldown bool, err error) {
func triggerAction(c *irc.Client, m *irc.Message, rule *plugins.Rule, ra *plugins.RuleAction, eventData *plugins.FieldCollection) (preventCooldown bool, err error) {
availableActionsLock.RLock()
defer availableActionsLock.RUnlock()
@ -64,7 +64,7 @@ func triggerAction(c *irc.Client, m *irc.Message, rule *plugins.Rule, ra *plugin
return apc, errors.Wrap(err, "execute action")
}
func handleMessage(c *irc.Client, m *irc.Message, event *string, eventData plugins.FieldCollection) {
func handleMessage(c *irc.Client, m *irc.Message, event *string, eventData *plugins.FieldCollection) {
for _, r := range config.GetMatchingRules(m, event, eventData) {
var preventCooldown bool

View file

@ -7,6 +7,7 @@ import (
"time"
"github.com/Luzifer/go_helpers/v2/str"
"github.com/Luzifer/twitch-bot/plugins"
"github.com/go-irc/irc"
"github.com/mitchellh/hashstructure/v2"
"github.com/pkg/errors"
@ -163,9 +164,10 @@ func (a *autoMessage) allowExecuteDisableOnTemplate() bool {
return true
}
res, err := formatMessage(*a.DisableOnTemplate, nil, nil, map[string]interface{}{
"channel": a.Channel,
})
fields := plugins.NewFieldCollection()
fields.Set("channel", a.Channel)
res, err := formatMessage(*a.DisableOnTemplate, nil, nil, fields)
if err != nil {
log.WithError(err).Error("Error in auto-message disable template")
// Caused an error, forbid execution

View file

@ -250,7 +250,7 @@ func (c *configFile) CloseRawMessageWriter() error {
return c.rawLogWriter.Close()
}
func (c configFile) GetMatchingRules(m *irc.Message, event *string, eventData map[string]interface{}) []*plugins.Rule {
func (c configFile) GetMatchingRules(m *irc.Message, event *string, eventData *plugins.FieldCollection) []*plugins.Rule {
configLock.RLock()
defer configLock.RUnlock()

View file

@ -29,7 +29,7 @@ func newTemplateFuncProvider() *templateFuncProvider {
return out
}
func (t *templateFuncProvider) GetFuncMap(m *irc.Message, r *plugins.Rule, fields map[string]interface{}) template.FuncMap {
func (t *templateFuncProvider) GetFuncMap(m *irc.Message, r *plugins.Rule, fields *plugins.FieldCollection) template.FuncMap {
t.lock.RLock()
defer t.lock.RUnlock()

View file

@ -9,11 +9,11 @@ import (
)
func init() {
tplFuncs.Register("channelCounter", func(m *irc.Message, r *plugins.Rule, fields plugins.FieldCollection) interface{} {
tplFuncs.Register("channelCounter", func(m *irc.Message, r *plugins.Rule, fields *plugins.FieldCollection) interface{} {
return func(name string) (string, error) {
channel, ok := fields["channel"].(string)
if !ok {
return "", errors.New("channel not available")
channel, err := fields.String("channel")
if err != nil {
return "", errors.Wrap(err, "channel not available")
}
return strings.Join([]string{channel, name}, ":"), nil

View file

@ -10,7 +10,7 @@ import (
)
func init() {
tplFuncs.Register("arg", func(m *irc.Message, r *plugins.Rule, fields plugins.FieldCollection) interface{} {
tplFuncs.Register("arg", func(m *irc.Message, r *plugins.Rule, fields *plugins.FieldCollection) interface{} {
return func(arg int) (string, error) {
msgParts := strings.Split(m.Trailing(), " ")
if len(msgParts) <= arg {
@ -21,10 +21,10 @@ func init() {
}
})
tplFuncs.Register("botHasBadge", func(m *irc.Message, r *plugins.Rule, fields plugins.FieldCollection) interface{} {
tplFuncs.Register("botHasBadge", func(m *irc.Message, r *plugins.Rule, fields *plugins.FieldCollection) interface{} {
return func(badge string) bool {
channel, ok := fields["channel"].(string)
if !ok {
channel, err := fields.String("channel")
if err != nil {
log.Trace("Fields for botHasBadge function had no channel")
return false
}
@ -40,7 +40,7 @@ func init() {
tplFuncs.Register("fixUsername", plugins.GenericTemplateFunctionGetter(func(username string) string { return strings.TrimLeft(username, "@#") }))
tplFuncs.Register("group", func(m *irc.Message, r *plugins.Rule, fields plugins.FieldCollection) interface{} {
tplFuncs.Register("group", func(m *irc.Message, r *plugins.Rule, fields *plugins.FieldCollection) interface{} {
return func(idx int, fallback ...string) (string, error) {
fields := r.GetMatchMessage().FindStringSubmatch(m.Trailing())
if len(fields) <= idx {
@ -55,7 +55,7 @@ func init() {
}
})
tplFuncs.Register("tag", func(m *irc.Message, r *plugins.Rule, fields plugins.FieldCollection) interface{} {
tplFuncs.Register("tag", func(m *irc.Message, r *plugins.Rule, fields *plugins.FieldCollection) interface{} {
return func(tag string) string {
s, _ := m.GetTag(tag)
return s

View file

@ -36,7 +36,7 @@ func Register(args plugins.RegistrationArguments) error {
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 }("")
cmd := []string{
@ -63,4 +63,4 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
func (a actor) IsAsync() bool { return false }
func (a actor) Name() string { return actorName }
func (a actor) Validate(attrs plugins.FieldCollection) (err error) { return nil }
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) { return nil }

View file

@ -45,7 +45,7 @@ func Register(args plugins.RegistrationArguments) error {
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 (
ptrZeroDuration = func(v time.Duration) *time.Duration { return &v }(0)
delay = attrs.MustDuration("delay", ptrZeroDuration)
@ -68,4 +68,4 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
func (a actor) IsAsync() bool { return false }
func (a actor) Name() string { return actorName }
func (a actor) Validate(attrs plugins.FieldCollection) (err error) { return nil }
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) { return nil }

View file

@ -24,7 +24,7 @@ func Register(args plugins.RegistrationArguments) error {
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) {
msgID, ok := m.Tags.GetTag("id")
if !ok || msgID == "" {
return false, nil
@ -45,4 +45,4 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
func (a actor) IsAsync() bool { return false }
func (a actor) Name() string { return actorName }
func (a actor) Validate(attrs plugins.FieldCollection) (err error) { return nil }
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) { return nil }

View file

@ -64,7 +64,7 @@ func Register(args plugins.RegistrationArguments) error {
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 (
ptrStringEmpty = func(v string) *string { return &v }("")
game = attrs.MustString("game", ptrStringEmpty)
@ -109,7 +109,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) Name() string { return actorName }
func (a actor) Validate(attrs plugins.FieldCollection) (err error) {
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
if v, err := attrs.String("channel"); err != nil || v == "" {
return errors.New("channel must be non-empty string")
}

View file

@ -143,7 +143,7 @@ type (
}
)
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) {
rawMatch, err := formatMessage(attrs.MustString("match", nil), m, r, eventData)
if err != nil {
return false, errors.Wrap(err, "formatting match")
@ -229,7 +229,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) Name() string { return actorName }
func (a actor) Validate(attrs plugins.FieldCollection) (err error) {
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
if v, err := attrs.String("match"); err != nil || v == "" {
return errors.New("match must be non-empty string")
}

View file

@ -142,7 +142,7 @@ type (
// Punish
func (a actorPunish) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData plugins.FieldCollection, attrs plugins.FieldCollection) (preventCooldown bool, err error) {
func (a actorPunish) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (preventCooldown bool, err error) {
var (
cooldown = attrs.MustDuration("cooldown", ptrDefaultCooldown)
reason = attrs.MustString("reason", ptrStringEmpty)
@ -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) Name() string { return actorNamePunish }
func (a actorPunish) Validate(attrs plugins.FieldCollection) (err error) {
func (a actorPunish) Validate(attrs *plugins.FieldCollection) (err error) {
if v, err := attrs.String("user"); err != nil || v == "" {
return errors.New("user must be non-empty string")
}
@ -228,7 +228,7 @@ func (a actorPunish) Validate(attrs plugins.FieldCollection) (err error) {
// Reset
func (a actorResetPunish) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData plugins.FieldCollection, attrs plugins.FieldCollection) (preventCooldown bool, err error) {
func (a actorResetPunish) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (preventCooldown bool, err error) {
var (
user = attrs.MustString("user", nil)
uuid = attrs.MustString("uuid", ptrStringEmpty)
@ -249,7 +249,7 @@ func (a actorResetPunish) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule
func (a actorResetPunish) IsAsync() bool { return false }
func (a actorResetPunish) Name() string { return actorNameResetPunish }
func (a actorResetPunish) Validate(attrs plugins.FieldCollection) (err error) {
func (a actorResetPunish) Validate(attrs *plugins.FieldCollection) (err error) {
if v, err := attrs.String("user"); err != nil || v == "" {
return errors.New("user must be non-empty string")
}

View file

@ -79,7 +79,7 @@ func Register(args plugins.RegistrationArguments) error {
registerAPI(args.RegisterAPIRoute)
args.RegisterTemplateFunction("lastQuoteIndex", func(m *irc.Message, r *plugins.Rule, fields plugins.FieldCollection) interface{} {
args.RegisterTemplateFunction("lastQuoteIndex", func(m *irc.Message, r *plugins.Rule, fields *plugins.FieldCollection) interface{} {
return func() int {
return storedObject.GetMaxQuoteIdx(plugins.DeriveChannel(m, nil))
}
@ -101,7 +101,7 @@ type (
}
)
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 (
action = attrs.MustString("action", ptrStringEmpty)
indexStr = attrs.MustString("index", ptrStringZero)
@ -149,12 +149,9 @@ func (a actor) Execute(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData
return false, nil
}
fields := make(plugins.FieldCollection)
for k, v := range eventData {
fields[k] = v
}
fields["index"] = idx
fields["quote"] = quote
fields := eventData.Clone()
fields.Set("index", idx)
fields.Set("quote", quote)
format := attrs.MustString("format", ptrStringOutFormat)
msg, err := formatMessage(format, m, r, fields)
@ -180,7 +177,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) Name() string { return actorName }
func (a actor) Validate(attrs plugins.FieldCollection) (err error) {
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
action := attrs.MustString("action", ptrStringEmpty)
switch action {

View file

@ -38,7 +38,7 @@ func Register(args plugins.RegistrationArguments) error {
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) {
rawMsg, err := formatMessage(attrs.MustString("message", nil), m, r, eventData)
if err != nil {
return false, errors.Wrap(err, "preparing raw message")
@ -58,7 +58,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) Name() string { return actorName }
func (a actor) Validate(attrs plugins.FieldCollection) (err error) {
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
if v, err := attrs.String("message"); err != nil || v == "" {
return errors.New("message must be non-empty string")
}

View file

@ -74,7 +74,7 @@ func Register(args plugins.RegistrationArguments) error {
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) {
msg, err := formatMessage(attrs.MustString("message", nil), m, r, eventData)
if err != nil {
if !attrs.CanString("fallback") || attrs.MustString("fallback", nil) == "" {
@ -118,7 +118,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) Name() string { return actorName }
func (a actor) Validate(attrs plugins.FieldCollection) (err error) {
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
if v, err := attrs.String("message"); err != nil || v == "" {
return errors.New("message must be non-empty string")
}

View file

@ -37,7 +37,7 @@ func Register(args plugins.RegistrationArguments) error {
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) {
return false, errors.Wrap(
c.WriteMessage(&irc.Message{
Command: "PRIVMSG",
@ -53,7 +53,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) Name() string { return actorName }
func (a actor) Validate(attrs plugins.FieldCollection) (err error) {
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
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")
}

View file

@ -49,7 +49,7 @@ func Register(args plugins.RegistrationArguments) error {
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) {
to, err := formatMessage(attrs.MustString("to", nil), m, r, eventData)
if err != nil {
return false, errors.Wrap(err, "preparing whisper receiver")
@ -77,7 +77,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) Name() string { return actorName }
func (a actor) Validate(attrs plugins.FieldCollection) (err error) {
func (a actor) Validate(attrs *plugins.FieldCollection) (err error) {
if v, err := attrs.String("to"); err != nil || v == "" {
return errors.New("to must be non-empty string")
}

60
irc.go
View file

@ -198,32 +198,32 @@ func (i ircHandler) handleClearChat(m *irc.Message) {
var (
evt *string
fields = plugins.FieldCollection{
"channel": i.getChannel(m), // Compatibility to plugins.DeriveChannel
}
fields = plugins.NewFieldCollection()
)
fields.Set("channel", i.getChannel(m)) // Compatibility to plugins.DeriveChannel
switch {
case secondsErr == nil && hasTargetUserID:
// User & Duration = Timeout
evt = eventTypeTimeout
fields["duration"] = time.Duration(seconds) * time.Second
fields["seconds"] = seconds
fields["target_id"] = targetUserID
fields["target_name"] = m.Trailing()
log.WithFields(log.Fields(fields)).Info("User was timed out")
fields.Set("duration", time.Duration(seconds)*time.Second)
fields.Set("seconds", seconds)
fields.Set("target_id", targetUserID)
fields.Set("target_name", m.Trailing())
log.WithFields(log.Fields(fields.Data())).Info("User was timed out")
case hasTargetUserID:
// User w/o Duration = Ban
evt = eventTypeBan
fields["target_id"] = targetUserID
fields["target_name"] = m.Trailing()
log.WithFields(log.Fields(fields)).Info("User was banned")
fields.Set("target_id", targetUserID)
fields.Set("target_name", m.Trailing())
log.WithFields(log.Fields(fields.Data())).Info("User was banned")
default:
// No User = /clear
evt = eventTypeClearChat
log.WithFields(log.Fields(fields)).Info("Chat was cleared")
log.WithFields(log.Fields(fields.Data())).Info("Chat was cleared")
}
go handleMessage(i.c, m, evt, fields)
@ -254,7 +254,7 @@ func (i ircHandler) handlePermit(m *irc.Message) {
log.WithField("user", username).Debug("Added permit")
timerStore.AddPermit(m.Params[0], username)
go handleMessage(i.c, m, eventTypePermit, plugins.FieldCollection{"username": username})
go handleMessage(i.c, m, eventTypePermit, plugins.FieldCollectionFromData(map[string]interface{}{"username": username}))
}
func (i ircHandler) handleTwitchNotice(m *irc.Message) {
@ -301,9 +301,9 @@ func (i ircHandler) handleTwitchPrivmsg(m *irc.Message) {
}
if bits, err := strconv.ParseInt(string(m.Tags["bits"]), 10, 64); err == nil {
go handleMessage(i.c, m, eventTypeBits, plugins.FieldCollection{
go handleMessage(i.c, m, eventTypeBits, plugins.FieldCollectionFromData(map[string]interface{}{
"bits": bits,
})
}))
}
go handleMessage(i.c, m, nil, nil)
@ -322,61 +322,61 @@ func (i ircHandler) handleTwitchUsernotice(m *irc.Message) {
log.WithField("msg", m).Warn("Received usernotice without msg-id")
case "raid":
evtData := plugins.FieldCollection{
evtData := plugins.FieldCollectionFromData(map[string]interface{}{
"channel": i.getChannel(m), // Compatibility to plugins.DeriveChannel
"from": m.Tags["login"],
"user": m.Tags["login"], // Compatibility to plugins.DeriveUser
"viewercount": m.Tags["msg-param-viewerCount"],
}
log.WithFields(log.Fields(evtData)).Info("Incoming raid")
})
log.WithFields(log.Fields(evtData.Data())).Info("Incoming raid")
go handleMessage(i.c, m, eventTypeRaid, evtData)
case "resub":
evtData := plugins.FieldCollection{
evtData := plugins.FieldCollectionFromData(map[string]interface{}{
"channel": i.getChannel(m), // Compatibility to plugins.DeriveChannel
"from": m.Tags["login"],
"subscribed_months": m.Tags["msg-param-cumulative-months"],
"plan": m.Tags["msg-param-sub-plan"],
"user": m.Tags["login"], // Compatibility to plugins.DeriveUser
}
log.WithFields(log.Fields(evtData)).Info("User re-subscribed")
})
log.WithFields(log.Fields(evtData.Data())).Info("User re-subscribed")
go handleMessage(i.c, m, eventTypeResub, evtData)
case "sub":
evtData := plugins.FieldCollection{
evtData := plugins.FieldCollectionFromData(map[string]interface{}{
"channel": i.getChannel(m), // Compatibility to plugins.DeriveChannel
"from": m.Tags["login"],
"plan": m.Tags["msg-param-sub-plan"],
"user": m.Tags["login"], // Compatibility to plugins.DeriveUser
}
log.WithFields(log.Fields(evtData)).Info("User subscribed")
})
log.WithFields(log.Fields(evtData.Data())).Info("User subscribed")
go handleMessage(i.c, m, eventTypeSub, evtData)
case "subgift", "anonsubgift":
evtData := plugins.FieldCollection{
evtData := plugins.FieldCollectionFromData(map[string]interface{}{
"channel": i.getChannel(m), // Compatibility to plugins.DeriveChannel
"from": m.Tags["login"],
"gifted_months": m.Tags["msg-param-gift-months"],
"plan": m.Tags["msg-param-sub-plan"],
"to": m.Tags["msg-param-recipient-user-name"],
"user": m.Tags["login"], // Compatibility to plugins.DeriveUser
}
log.WithFields(log.Fields(evtData)).Info("User gifted a sub")
})
log.WithFields(log.Fields(evtData.Data())).Info("User gifted a sub")
go handleMessage(i.c, m, eventTypeSubgift, evtData)
case "submysterygift":
evtData := plugins.FieldCollection{
evtData := plugins.FieldCollectionFromData(map[string]interface{}{
"channel": i.getChannel(m), // Compatibility to plugins.DeriveChannel
"from": m.Tags["login"],
"number": m.Tags["msg-param-mass-gift-count"],
"plan": m.Tags["msg-param-sub-plan"],
"user": m.Tags["login"], // Compatibility to plugins.DeriveUser
}
log.WithFields(log.Fields(evtData)).Info("User gifted subs to the community")
})
log.WithFields(log.Fields(evtData.Data())).Info("User gifted subs to the community")
go handleMessage(i.c, m, eventTypeSubmysterygift, evtData)

View file

@ -13,27 +13,23 @@ import (
// Compile-time assertion
var _ plugins.MsgFormatter = formatMessage
func formatMessage(tplString string, m *irc.Message, r *plugins.Rule, fields plugins.FieldCollection) (string, error) {
compiledFields := map[string]interface{}{}
func formatMessage(tplString string, m *irc.Message, r *plugins.Rule, fields *plugins.FieldCollection) (string, error) {
compiledFields := plugins.NewFieldCollection()
if config != nil {
configLock.RLock()
for k, v := range config.Variables {
compiledFields[k] = v
}
compiledFields["permitTimeout"] = int64(config.PermitTimeout / time.Second)
compiledFields.SetFromData(config.Variables)
compiledFields.Set("permitTimeout", int64(config.PermitTimeout/time.Second))
configLock.RUnlock()
}
for k, v := range fields {
compiledFields[k] = v
}
compiledFields.SetFromData(fields.Data())
if m != nil {
compiledFields["msg"] = m
compiledFields.Set("msg", m)
}
compiledFields["username"] = plugins.DeriveUser(m, fields)
compiledFields["channel"] = plugins.DeriveChannel(m, fields)
compiledFields.Set("username", plugins.DeriveUser(m, fields))
compiledFields.Set("channel", plugins.DeriveChannel(m, fields))
// Parse and execute template
tpl, err := template.
@ -45,7 +41,7 @@ func formatMessage(tplString string, m *irc.Message, r *plugins.Rule, fields plu
}
buf := new(bytes.Buffer)
err = tpl.Execute(buf, compiledFields)
err = tpl.Execute(buf, compiledFields.Data())
return buf.String(), errors.Wrap(err, "execute template")
}

View file

@ -1,9 +1,11 @@
package plugins
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"sync"
"time"
"github.com/pkg/errors"
@ -14,33 +16,88 @@ var (
ErrValueMismatch = errors.New("specified value has different format")
)
type FieldCollection map[string]interface{}
type FieldCollection struct {
data map[string]interface{}
lock sync.RWMutex
}
func (f FieldCollection) CanBool(name string) bool {
// NewFieldCollection creates a new FieldCollection with empty data store
func NewFieldCollection() *FieldCollection {
return &FieldCollection{data: make(map[string]interface{})}
}
// FieldCollectionFromData is a wrapper around NewFieldCollection and SetFromData
func FieldCollectionFromData(data map[string]interface{}) *FieldCollection {
o := NewFieldCollection()
o.SetFromData(data)
return o
}
// CanBool tries to read key name as bool and checks whether error is nil
func (f *FieldCollection) CanBool(name string) bool {
_, err := f.Bool(name)
return err == nil
}
func (f FieldCollection) CanDuration(name string) bool {
// CanDuration tries to read key name as time.Duration and checks whether error is nil
func (f *FieldCollection) CanDuration(name string) bool {
_, err := f.Duration(name)
return err == nil
}
func (f FieldCollection) CanInt64(name string) bool {
// CanInt64 tries to read key name as int64 and checks whether error is nil
func (f *FieldCollection) CanInt64(name string) bool {
_, err := f.Int64(name)
return err == nil
}
func (f FieldCollection) CanString(name string) bool {
// CanString tries to read key name as string and checks whether error is nil
func (f *FieldCollection) CanString(name string) bool {
_, err := f.String(name)
return err == nil
}
func (f FieldCollection) Expect(keys ...string) error {
// Clone is a wrapper around n.SetFromData(o.Data())
func (f *FieldCollection) Clone() *FieldCollection {
out := new(FieldCollection)
out.SetFromData(f.Data())
return out
}
// Data creates a map-copy of the data stored inside the FieldCollection
func (f *FieldCollection) Data() map[string]interface{} {
if f == nil {
return nil
}
f.lock.RLock()
defer f.lock.RUnlock()
out := make(map[string]interface{})
for k := range f.data {
out[k] = f.data[k]
}
return out
}
// Expect takes a list of keys and returns an error with all non-found names
func (f *FieldCollection) Expect(keys ...string) error {
if len(keys) == 0 {
return nil
}
if f == nil || f.data == nil {
return errors.New("uninitialized field collection")
}
f.lock.RLock()
defer f.lock.RUnlock()
var missing []string
for _, k := range keys {
if _, ok := f[k]; !ok {
if _, ok := f.data[k]; !ok {
missing = append(missing, k)
}
}
@ -52,17 +109,13 @@ func (f FieldCollection) Expect(keys ...string) error {
return nil
}
func (f FieldCollection) HasAll(keys ...string) bool {
for _, k := range keys {
if _, ok := f[k]; !ok {
return false
}
}
return true
// HasAll takes a list of keys and returns whether all of them exist inside the FieldCollection
func (f *FieldCollection) HasAll(keys ...string) bool {
return f.Expect(keys...) == nil
}
func (f FieldCollection) MustBool(name string, defVal *bool) bool {
// MustBool is a wrapper around Bool and panics if an error was returned
func (f *FieldCollection) MustBool(name string, defVal *bool) bool {
v, err := f.Bool(name)
if err != nil {
if defVal != nil {
@ -73,7 +126,8 @@ func (f FieldCollection) MustBool(name string, defVal *bool) bool {
return v
}
func (f FieldCollection) MustDuration(name string, defVal *time.Duration) time.Duration {
// MustDuration is a wrapper around Duration and panics if an error was returned
func (f *FieldCollection) MustDuration(name string, defVal *time.Duration) time.Duration {
v, err := f.Duration(name)
if err != nil {
if defVal != nil {
@ -84,7 +138,8 @@ func (f FieldCollection) MustDuration(name string, defVal *time.Duration) time.D
return v
}
func (f FieldCollection) MustInt64(name string, defVal *int64) int64 {
// MustInt64 is a wrapper around Int64 and panics if an error was returned
func (f *FieldCollection) MustInt64(name string, defVal *int64) int64 {
v, err := f.Int64(name)
if err != nil {
if defVal != nil {
@ -95,7 +150,8 @@ func (f FieldCollection) MustInt64(name string, defVal *int64) int64 {
return v
}
func (f FieldCollection) MustString(name string, defVal *string) string {
// MustString is a wrapper around String and panics if an error was returned
func (f *FieldCollection) MustString(name string, defVal *string) string {
v, err := f.String(name)
if err != nil {
if defVal != nil {
@ -106,8 +162,16 @@ func (f FieldCollection) MustString(name string, defVal *string) string {
return v
}
func (f FieldCollection) Bool(name string) (bool, error) {
v, ok := f[name]
// Bool tries to read key name as bool
func (f *FieldCollection) Bool(name string) (bool, error) {
if f == nil || f.data == nil {
return false, errors.New("uninitialized field collection")
}
f.lock.RLock()
defer f.lock.RUnlock()
v, ok := f.data[name]
if !ok {
return false, ErrValueNotSet
}
@ -123,7 +187,15 @@ func (f FieldCollection) Bool(name string) (bool, error) {
return false, ErrValueMismatch
}
func (f FieldCollection) Duration(name string) (time.Duration, error) {
// Duration tries to read key name as time.Duration
func (f *FieldCollection) Duration(name string) (time.Duration, error) {
if f == nil || f.data == nil {
return 0, errors.New("uninitialized field collection")
}
f.lock.RLock()
defer f.lock.RUnlock()
v, err := f.String(name)
if err != nil {
return 0, errors.Wrap(err, "getting string value")
@ -133,8 +205,16 @@ func (f FieldCollection) Duration(name string) (time.Duration, error) {
return d, errors.Wrap(err, "parsing value")
}
func (f FieldCollection) Int64(name string) (int64, error) {
v, ok := f[name]
// Int64 tries to read key name as int64
func (f *FieldCollection) Int64(name string) (int64, error) {
if f == nil || f.data == nil {
return 0, errors.New("uninitialized field collection")
}
f.lock.RLock()
defer f.lock.RUnlock()
v, ok := f.data[name]
if !ok {
return 0, ErrValueNotSet
}
@ -153,8 +233,50 @@ func (f FieldCollection) Int64(name string) (int64, error) {
return 0, ErrValueMismatch
}
func (f FieldCollection) String(name string) (string, error) {
v, ok := f[name]
// Set sets a single key to specified value
func (f *FieldCollection) Set(key string, value interface{}) {
if f == nil {
f = NewFieldCollection()
}
f.lock.Lock()
defer f.lock.Unlock()
if f.data == nil {
f.data = make(map[string]interface{})
}
f.data[key] = value
}
// SetFromData takes a map of data and copies all data into the FieldCollection
func (f *FieldCollection) SetFromData(data map[string]interface{}) {
if f == nil {
f = NewFieldCollection()
}
f.lock.Lock()
defer f.lock.Unlock()
if f.data == nil {
f.data = make(map[string]interface{})
}
for key, value := range data {
f.data[key] = value
}
}
// String tries to read key name as string
func (f *FieldCollection) String(name string) (string, error) {
if f == nil || f.data == nil {
return "", errors.New("uninitialized field collection")
}
f.lock.RLock()
defer f.lock.RUnlock()
v, ok := f.data[name]
if !ok {
return "", ErrValueNotSet
}
@ -170,8 +292,16 @@ func (f FieldCollection) String(name string) (string, error) {
return "", ErrValueMismatch
}
func (f FieldCollection) StringSlice(name string) ([]string, error) {
v, ok := f[name]
// StringSlice tries to read key name as []string
func (f *FieldCollection) StringSlice(name string) ([]string, error) {
if f == nil || f.data == nil {
return nil, errors.New("uninitialized field collection")
}
f.lock.RLock()
defer f.lock.RUnlock()
v, ok := f.data[name]
if !ok {
return nil, ErrValueNotSet
}
@ -196,3 +326,42 @@ func (f FieldCollection) StringSlice(name string) ([]string, error) {
return nil, ErrValueMismatch
}
// Implement JSON marshalling to plain underlying map[string]interface{}
func (f *FieldCollection) MarshalJSON() ([]byte, error) {
if f == nil || f.data == nil {
return []byte("{}"), nil
}
f.lock.RLock()
defer f.lock.RUnlock()
return json.Marshal(f.data)
}
func (f *FieldCollection) UnmarshalJSON(raw []byte) error {
data := make(map[string]interface{})
if err := json.Unmarshal(raw, &data); err != nil {
return errors.Wrap(err, "unmarshalling from JSON")
}
f.SetFromData(data)
return nil
}
// Implement YAML marshalling to plain underlying map[string]interface{}
func (f *FieldCollection) MarshalYAML() (interface{}, error) {
return f.Data(), nil
}
func (f *FieldCollection) UnmarshalYAML(unmarshal func(interface{}) error) error {
data := make(map[string]interface{})
if err := unmarshal(&data); err != nil {
return errors.Wrap(err, "unmarshalling from YAML")
}
f.SetFromData(data)
return nil
}

View file

@ -0,0 +1,80 @@
package plugins
import (
"bytes"
"encoding/json"
"strings"
"testing"
"gopkg.in/yaml.v2"
)
func TestFieldCollectionJSONMarshal(t *testing.T) {
var (
buf = new(bytes.Buffer)
raw = `{"key1":"test1","key2":"test2"}`
f = NewFieldCollection()
)
if err := json.NewDecoder(strings.NewReader(raw)).Decode(f); err != nil {
t.Fatalf("Unable to unmarshal: %s", err)
}
if err := json.NewEncoder(buf).Encode(f); err != nil {
t.Fatalf("Unable to marshal: %s", err)
}
if raw != strings.TrimSpace(buf.String()) {
t.Errorf("Marshalled JSON does not match expectation: res=%s exp=%s", buf.String(), raw)
}
}
func TestFieldCollectionYAMLMarshal(t *testing.T) {
var (
buf = new(bytes.Buffer)
raw = "key1: test1\nkey2: test2"
f = NewFieldCollection()
)
if err := yaml.NewDecoder(strings.NewReader(raw)).Decode(f); err != nil {
t.Fatalf("Unable to unmarshal: %s", err)
}
if err := yaml.NewEncoder(buf).Encode(f); err != nil {
t.Fatalf("Unable to marshal: %s", err)
}
if raw != strings.TrimSpace(buf.String()) {
t.Errorf("Marshalled YAML does not match expectation: res=%s exp=%s", buf.String(), raw)
}
}
func TestFieldCollectionNilModify(t *testing.T) {
var f *FieldCollection
f.Set("foo", "bar")
f = nil
f.SetFromData(map[string]interface{}{"foo": "bar"})
}
func TestFieldCollectionNilClone(t *testing.T) {
var f *FieldCollection
f.Clone()
}
func TestFieldCollectionNilDataGet(t *testing.T) {
var f *FieldCollection
for name, fn := range map[string]func(name string) bool{
"bool": f.CanBool,
"duration": f.CanDuration,
"int64": f.CanInt64,
"string": f.CanString,
} {
if fn("foo") {
t.Errorf("%s key is available", name)
}
}
}

View file

@ -7,7 +7,7 @@ import (
"github.com/go-irc/irc"
)
func DeriveChannel(m *irc.Message, evtData FieldCollection) string {
func DeriveChannel(m *irc.Message, evtData *FieldCollection) string {
if m != nil && len(m.Params) > 0 && strings.HasPrefix(m.Params[0], "#") {
return m.Params[0]
}
@ -19,7 +19,7 @@ func DeriveChannel(m *irc.Message, evtData FieldCollection) string {
return ""
}
func DeriveUser(m *irc.Message, evtData FieldCollection) string {
func DeriveUser(m *irc.Message, evtData *FieldCollection) string {
if m != nil && m.User != "" {
return m.User
}

View file

@ -10,7 +10,7 @@ import (
type (
Actor interface {
// Execute will be called after the config was read into the Actor
Execute(c *irc.Client, m *irc.Message, r *Rule, evtData FieldCollection, attrs FieldCollection) (preventCooldown bool, err error)
Execute(c *irc.Client, m *irc.Message, r *Rule, evtData *FieldCollection, attrs *FieldCollection) (preventCooldown bool, err error)
// IsAsync may return true if the Execute function is to be executed
// in a Go routine as of long runtime. Normally it should return false
// except in very specific cases
@ -21,7 +21,7 @@ type (
// Validate will be called to validate the loaded configuration. It should
// return an error if required keys are missing from the AttributeStore
// or if keys contain broken configs
Validate(FieldCollection) error
Validate(*FieldCollection) error
}
ActorCreationFunc func() Actor
@ -34,7 +34,7 @@ type (
LoggerCreationFunc func(moduleName string) *log.Entry
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)
RawMessageHandlerFunc func(m *irc.Message) error
RawMessageHandlerRegisterFunc func(RawMessageHandlerFunc) error
@ -83,10 +83,10 @@ type (
UnmarshalStoredObject([]byte) error
}
TemplateFuncGetter func(*irc.Message, *Rule, FieldCollection) interface{}
TemplateFuncGetter func(*irc.Message, *Rule, *FieldCollection) interface{}
TemplateFuncRegister func(name string, fg TemplateFuncGetter)
)
func GenericTemplateFunctionGetter(f interface{}) TemplateFuncGetter {
return func(*irc.Message, *Rule, FieldCollection) interface{} { return f }
return func(*irc.Message, *Rule, *FieldCollection) interface{} { return f }
}

View file

@ -49,8 +49,8 @@ type (
}
RuleAction struct {
Type string `json:"type" yaml:"type,omitempty"`
Attributes FieldCollection `json:"attributes" yaml:"attributes,omitempty"`
Type string `json:"type" yaml:"type,omitempty"`
Attributes *FieldCollection `json:"attributes" yaml:"attributes,omitempty"`
}
)
@ -66,7 +66,7 @@ func (r Rule) MatcherID() string {
return fmt.Sprintf("hashstructure:%x", h)
}
func (r *Rule) Matches(m *irc.Message, event *string, timerStore TimerStore, msgFormatter MsgFormatter, twitchClient *twitch.Client, eventData FieldCollection) bool {
func (r *Rule) Matches(m *irc.Message, event *string, timerStore TimerStore, msgFormatter MsgFormatter, twitchClient *twitch.Client, eventData *FieldCollection) bool {
r.msgFormatter = msgFormatter
r.timerStore = timerStore
r.twitchClient = twitchClient
@ -79,7 +79,7 @@ func (r *Rule) Matches(m *irc.Message, event *string, timerStore TimerStore, msg
})
)
for _, matcher := range []func(*log.Entry, *irc.Message, *string, twitch.BadgeCollection, FieldCollection) bool{
for _, matcher := range []func(*log.Entry, *irc.Message, *string, twitch.BadgeCollection, *FieldCollection) bool{
r.allowExecuteDisable,
r.allowExecuteChannelWhitelist,
r.allowExecuteUserWhitelist,
@ -117,7 +117,7 @@ func (r *Rule) GetMatchMessage() *regexp.Regexp {
return r.matchMessage
}
func (r *Rule) SetCooldown(timerStore TimerStore, m *irc.Message, evtData FieldCollection) {
func (r *Rule) SetCooldown(timerStore TimerStore, m *irc.Message, evtData *FieldCollection) {
if r.Cooldown != nil {
timerStore.AddCooldown(TimerTypeCooldown, "", r.MatcherID(), time.Now().Add(*r.Cooldown))
}
@ -131,7 +131,7 @@ func (r *Rule) SetCooldown(timerStore TimerStore, m *irc.Message, evtData FieldC
}
}
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 {
if badges.Has(b) {
logger.Tracef("Non-Match: Disable-Badge %s", b)
@ -142,7 +142,7 @@ func (r *Rule) allowExecuteBadgeBlacklist(logger *log.Entry, m *irc.Message, eve
return true
}
func (r *Rule) allowExecuteBadgeWhitelist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteBadgeWhitelist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if len(r.EnableOn) == 0 {
// No match criteria set, does not speak against matching
return true
@ -157,7 +157,7 @@ func (r *Rule) allowExecuteBadgeWhitelist(logger *log.Entry, m *irc.Message, eve
return false
}
func (r *Rule) allowExecuteChannelCooldown(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteChannelCooldown(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if r.ChannelCooldown == nil || DeriveChannel(m, evtData) == "" {
// No match criteria set, does not speak against matching
return true
@ -176,7 +176,7 @@ func (r *Rule) allowExecuteChannelCooldown(logger *log.Entry, m *irc.Message, ev
return false
}
func (r *Rule) allowExecuteChannelWhitelist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteChannelWhitelist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if len(r.MatchChannels) == 0 {
// No match criteria set, does not speak against matching
return true
@ -190,7 +190,7 @@ func (r *Rule) allowExecuteChannelWhitelist(logger *log.Entry, m *irc.Message, e
return true
}
func (r *Rule) allowExecuteDisable(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteDisable(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if r.Disable == nil {
// No match criteria set, does not speak against matching
return true
@ -204,7 +204,7 @@ func (r *Rule) allowExecuteDisable(logger *log.Entry, m *irc.Message, event *str
return true
}
func (r *Rule) allowExecuteDisableOnOffline(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteDisableOnOffline(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if r.DisableOnOffline == nil || !*r.DisableOnOffline || DeriveChannel(m, evtData) == "" {
// No match criteria set, does not speak against matching
return true
@ -223,7 +223,7 @@ func (r *Rule) allowExecuteDisableOnOffline(logger *log.Entry, m *irc.Message, e
return true
}
func (r *Rule) allowExecuteDisableOnPermit(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteDisableOnPermit(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if r.DisableOnPermit != nil && *r.DisableOnPermit && DeriveChannel(m, evtData) != "" && r.timerStore.HasPermit(DeriveChannel(m, evtData), DeriveUser(m, evtData)) {
logger.Trace("Non-Match: Permit")
return false
@ -232,7 +232,7 @@ func (r *Rule) allowExecuteDisableOnPermit(logger *log.Entry, m *irc.Message, ev
return true
}
func (r *Rule) allowExecuteDisableOnTemplate(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteDisableOnTemplate(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if r.DisableOnTemplate == nil || *r.DisableOnTemplate == "" {
// No match criteria set, does not speak against matching
return true
@ -253,7 +253,7 @@ func (r *Rule) allowExecuteDisableOnTemplate(logger *log.Entry, m *irc.Message,
return true
}
func (r *Rule) allowExecuteEventWhitelist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteEventWhitelist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if r.MatchEvent == nil || *r.MatchEvent == "" {
// No match criteria set, does not speak against matching
return true
@ -267,7 +267,7 @@ func (r *Rule) allowExecuteEventWhitelist(logger *log.Entry, m *irc.Message, eve
return true
}
func (r *Rule) allowExecuteMessageMatcherBlacklist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteMessageMatcherBlacklist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if len(r.DisableOnMatchMessages) == 0 {
// No match criteria set, does not speak against matching
return true
@ -296,7 +296,7 @@ func (r *Rule) allowExecuteMessageMatcherBlacklist(logger *log.Entry, m *irc.Mes
return true
}
func (r *Rule) allowExecuteMessageMatcherWhitelist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteMessageMatcherWhitelist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if r.MatchMessage == nil {
// No match criteria set, does not speak against matching
return true
@ -321,7 +321,7 @@ func (r *Rule) allowExecuteMessageMatcherWhitelist(logger *log.Entry, m *irc.Mes
return true
}
func (r *Rule) allowExecuteRuleCooldown(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteRuleCooldown(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if r.Cooldown == nil {
// No match criteria set, does not speak against matching
return true
@ -340,7 +340,7 @@ func (r *Rule) allowExecuteRuleCooldown(logger *log.Entry, m *irc.Message, event
return false
}
func (r *Rule) allowExecuteUserCooldown(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteUserCooldown(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if r.UserCooldown == nil {
// No match criteria set, does not speak against matching
return true
@ -359,7 +359,7 @@ func (r *Rule) allowExecuteUserCooldown(logger *log.Entry, m *irc.Message, event
return false
}
func (r *Rule) allowExecuteUserWhitelist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData FieldCollection) bool {
func (r *Rule) allowExecuteUserWhitelist(logger *log.Entry, m *irc.Message, event *string, badges twitch.BadgeCollection, evtData *FieldCollection) bool {
if len(r.MatchUsers) == 0 {
// No match criteria set, does not speak against matching
return true

View file

@ -139,7 +139,7 @@ func TestAllowExecuteDisableOnTemplate(t *testing.T) {
} {
// We don't test the message formatter here but only the disable functionality
// so we fake the result of the evaluation
r.msgFormatter = func(tplString string, m *irc.Message, r *Rule, fields FieldCollection) (string, error) {
r.msgFormatter = func(tplString string, m *irc.Message, r *Rule, fields *FieldCollection) (string, error) {
return msg, nil
}

View file

@ -196,10 +196,10 @@ func (t *twitchWatcher) triggerUpdate(channel string, title, category *string, o
"channel": channel,
"category": *category,
}).Debug("Twitch metadata changed")
go handleMessage(ircHdl.Client(), nil, eventTypeTwitchCategoryUpdate, plugins.FieldCollection{
go handleMessage(ircHdl.Client(), nil, eventTypeTwitchCategoryUpdate, plugins.FieldCollectionFromData(map[string]interface{}{
"channel": channel,
"category": *category,
})
}))
}
if title != nil && t.ChannelStatus[channel].Title != *title {
@ -208,10 +208,10 @@ func (t *twitchWatcher) triggerUpdate(channel string, title, category *string, o
"channel": channel,
"title": *title,
}).Debug("Twitch metadata changed")
go handleMessage(ircHdl.Client(), nil, eventTypeTwitchTitleUpdate, plugins.FieldCollection{
go handleMessage(ircHdl.Client(), nil, eventTypeTwitchTitleUpdate, plugins.FieldCollectionFromData(map[string]interface{}{
"channel": channel,
"title": *title,
})
}))
}
if online != nil && t.ChannelStatus[channel].IsLive != *online {
@ -226,8 +226,8 @@ func (t *twitchWatcher) triggerUpdate(channel string, title, category *string, o
evt = eventTypeTwitchStreamOffline
}
go handleMessage(ircHdl.Client(), nil, evt, plugins.FieldCollection{
go handleMessage(ircHdl.Client(), nil, evt, plugins.FieldCollectionFromData(map[string]interface{}{
"channel": channel,
})
}))
}
}