From 024980ee14c9995af1fe2060f6de3669490d0599 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Wed, 21 Apr 2021 22:43:33 +0200 Subject: [PATCH] Extract template functions into registry Signed-off-by: Knut Ahlers --- functions.go | 64 +++++++++++++++++++++++++++++++++++ functions_counter.go | 25 ++++++++++++++ functions_irc.go | 41 +++++++++++++++++++++++ functions_twitch.go | 16 +++++++++ msgformatter.go | 80 +++++--------------------------------------- 5 files changed, 155 insertions(+), 71 deletions(-) create mode 100644 functions.go create mode 100644 functions_counter.go create mode 100644 functions_irc.go create mode 100644 functions_twitch.go diff --git a/functions.go b/functions.go new file mode 100644 index 0000000..9ab6b86 --- /dev/null +++ b/functions.go @@ -0,0 +1,64 @@ +package main + +import ( + "strings" + "sync" + "text/template" + + korvike "github.com/Luzifer/korvike/functions" + "github.com/go-irc/irc" +) + +var tplFuncs = newTemplateFuncProvider() + +type ( + templateFuncGetter func(*irc.Message, *rule, map[string]interface{}) interface{} + templateFuncProvider struct { + funcs map[string]templateFuncGetter + lock *sync.RWMutex + } +) + +func newTemplateFuncProvider() *templateFuncProvider { + out := &templateFuncProvider{ + funcs: map[string]templateFuncGetter{}, + lock: new(sync.RWMutex), + } + + return out +} + +func (t *templateFuncProvider) GetFuncMap(m *irc.Message, r *rule, fields map[string]interface{}) template.FuncMap { + t.lock.RLock() + defer t.lock.RUnlock() + + out := make(template.FuncMap) + + for n, fg := range t.funcs { + out[n] = fg(m, r, fields) + } + + return out +} + +func (t *templateFuncProvider) Register(name string, fg templateFuncGetter) { + t.lock.Lock() + defer t.lock.Unlock() + + t.funcs[name] = fg +} + +func genericTemplateFunctionGetter(f interface{}) templateFuncGetter { + return func(*irc.Message, *rule, map[string]interface{}) interface{} { return f } +} + +func init() { + // Register Korvike functions + for n, f := range korvike.GetFunctionMap() { + tplFuncs.Register(n, genericTemplateFunctionGetter(f)) + } + + tplFuncs.Register("toLower", genericTemplateFunctionGetter(strings.ToLower)) + tplFuncs.Register("toUpper", genericTemplateFunctionGetter(strings.ToUpper)) + tplFuncs.Register("followDate", genericTemplateFunctionGetter(twitch.GetFollowDate)) +} diff --git a/functions_counter.go b/functions_counter.go new file mode 100644 index 0000000..678ff2c --- /dev/null +++ b/functions_counter.go @@ -0,0 +1,25 @@ +package main + +import ( + "strings" + + "github.com/go-irc/irc" + "github.com/pkg/errors" +) + +func init() { + tplFuncs.Register("channelCounter", func(m *irc.Message, r *rule, fields map[string]interface{}) interface{} { + return func(name string) (string, error) { + channel, ok := fields["channel"].(string) + if !ok { + return "", errors.New("channel not available") + } + + return strings.Join([]string{channel, name}, ":"), nil + } + }) + + tplFuncs.Register("counterValue", genericTemplateFunctionGetter(func(name string, _ ...string) int64 { + return store.GetCounterValue(name) + })) +} diff --git a/functions_irc.go b/functions_irc.go new file mode 100644 index 0000000..798d531 --- /dev/null +++ b/functions_irc.go @@ -0,0 +1,41 @@ +package main + +import ( + "strings" + + "github.com/go-irc/irc" + "github.com/pkg/errors" +) + +func init() { + tplFuncs.Register("arg", func(m *irc.Message, r *rule, fields map[string]interface{}) interface{} { + return func(arg int) (string, error) { + msgParts := strings.Split(m.Trailing(), " ") + if len(msgParts) <= arg { + return "", errors.New("argument not found") + } + + return msgParts[arg], nil + } + }) + + tplFuncs.Register("fixUsername", genericTemplateFunctionGetter(func(username string) string { return strings.TrimLeft(username, "@#") })) + + tplFuncs.Register("group", func(m *irc.Message, r *rule, fields map[string]interface{}) interface{} { + return func(idx int) (string, error) { + fields := r.matchMessage.FindStringSubmatch(m.Trailing()) + if len(fields) <= idx { + return "", errors.New("group not found") + } + + return fields[idx], nil + } + }) + + tplFuncs.Register("tag", func(m *irc.Message, r *rule, fields map[string]interface{}) interface{} { + return func(tag string) string { + s, _ := m.GetTag(tag) + return s + } + }) +} diff --git a/functions_twitch.go b/functions_twitch.go new file mode 100644 index 0000000..13131b7 --- /dev/null +++ b/functions_twitch.go @@ -0,0 +1,16 @@ +package main + +import ( + "strings" +) + +func init() { + tplFuncs.Register("recentGame", genericTemplateFunctionGetter(func(username string, v ...string) (string, error) { + game, _, err := twitch.GetRecentStreamInfo(strings.TrimLeft(username, "#")) + if err != nil && len(v) > 0 { + return v[0], nil + } + + return game, err + })) +} diff --git a/msgformatter.go b/msgformatter.go index 77b50ec..23a3905 100644 --- a/msgformatter.go +++ b/msgformatter.go @@ -2,85 +2,14 @@ package main import ( "bytes" - "strings" "text/template" "time" - korvike "github.com/Luzifer/korvike/functions" - "github.com/go-irc/irc" "github.com/pkg/errors" ) func formatMessage(tplString string, m *irc.Message, r *rule, fields map[string]interface{}) (string, error) { - // Create anonymous functions in current context in order to access function variables - messageFunctions := make(template.FuncMap) - for n, f := range korvike.GetFunctionMap() { - messageFunctions[n] = f - } - - // Generic functions - messageFunctions["toLower"] = strings.ToLower - messageFunctions["toUpper"] = strings.ToUpper - messageFunctions["followDate"] = twitch.GetFollowDate - - // Message specific functions - messageFunctions["arg"] = func(arg int) (string, error) { - msgParts := strings.Split(m.Trailing(), " ") - if len(msgParts) <= arg { - return "", errors.New("argument not found") - } - - return msgParts[arg], nil - } - - messageFunctions["channelCounter"] = func(name string) (string, error) { - channel, ok := fields["channel"].(string) - if !ok { - return "", errors.New("channel not available") - } - - return strings.Join([]string{channel, name}, ":"), nil - } - - messageFunctions["counterValue"] = func(name string, _ ...string) int64 { - return store.GetCounterValue(name) - } - - messageFunctions["fixUsername"] = func(username string) string { return strings.TrimLeft(username, "@#") } - - messageFunctions["group"] = func(idx int) (string, error) { - fields := r.matchMessage.FindStringSubmatch(m.Trailing()) - if len(fields) <= idx { - return "", errors.New("group not found") - } - - return fields[idx], nil - } - - messageFunctions["recentGame"] = func(username string, v ...string) (string, error) { - game, _, err := twitch.GetRecentStreamInfo(strings.TrimLeft(username, "#")) - if err != nil && len(v) > 0 { - return v[0], nil - } - - return game, err - } - - messageFunctions["tag"] = func(tag string) string { - s, _ := m.GetTag(tag) - return s - } - - // Parse and execute template - tpl, err := template. - New(tplString). - Funcs(messageFunctions). - Parse(tplString) - if err != nil { - return "", errors.Wrap(err, "parse template") - } - if fields == nil { fields = map[string]interface{}{} } @@ -93,6 +22,15 @@ func formatMessage(tplString string, m *irc.Message, r *rule, fields map[string] fields["channel"] = m.Params[0] } + // Parse and execute template + tpl, err := template. + New(tplString). + Funcs(tplFuncs.GetFuncMap(m, r, fields)). + Parse(tplString) + if err != nil { + return "", errors.Wrap(err, "parse template") + } + buf := new(bytes.Buffer) err = tpl.Execute(buf, fields)