1
0
Fork 0
mirror of https://git.sr.ht/~ashkeel/strimertul synced 2024-09-18 01:50:50 +00:00

Add callable functions to custom commands

This commit is contained in:
Ash Keel 2021-09-18 22:06:22 +02:00
parent de7c23f14b
commit 8596ce3f6d
No known key found for this signature in database
GPG key ID: BAD8D93E7314ED3E
8 changed files with 151 additions and 35 deletions

View file

@ -225,11 +225,11 @@ export default function TwitchBotCommandsPage(
dispatch(
setCommands({
...commands,
[oldName]: undefined,
[newName]: {
...commands[oldName],
...data,
},
[oldName]: undefined,
}),
);
setShowModifyCommand(null);

7
go.mod
View file

@ -3,10 +3,17 @@ module github.com/strimertul/strimertul
go 1.16
require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/dgraph-io/badger/v3 v3.2011.1
github.com/gempir/go-twitch-irc/v2 v2.5.0
github.com/google/uuid v1.3.0 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/json-iterator/go v1.1.11
github.com/mattn/go-colorable v0.1.8
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/nicklaw5/helix v1.15.0
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4
github.com/sirupsen/logrus v1.8.1

18
go.sum
View file

@ -2,6 +2,12 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
@ -44,11 +50,17 @@ github.com/google/flatbuffers v1.12.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@ -63,8 +75,12 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mmcloughlin/avo v0.0.0-20201105074841-5d2f697d268f/go.mod h1:6aKT4zZIrpGqB3RpFU14ByCSSyKY6LfJz4J/JJChHfI=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -131,6 +147,7 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210505212654-3497b51f5e64 h1:QuAh/1Gwc0d+u9walMU1NqzhRemNegsv5esp2ALQIY4=
golang.org/x/crypto v0.0.0-20210505212654-3497b51f5e64/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -190,5 +207,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View file

@ -5,6 +5,7 @@ import (
"flag"
"fmt"
"io/fs"
"math/rand"
"net/http"
"runtime"
"time"
@ -69,6 +70,8 @@ func main() {
loglevel := flag.String("loglevel", "info", "Logging level (debug, info, warn, error)")
flag.Parse()
rand.Seed(time.Now().UnixNano())
log.SetLevel(parseLogLevel(*loglevel))
// Ok this is dumb but listen, I like colors.

View file

@ -110,21 +110,29 @@ func (m *Manager) update(kvs []database.ModifiedKV) error {
// Check for config changes/RPC
switch key {
case ConfigKey:
m.mu.Lock()
err = jsoniter.ConfigFastest.Unmarshal(kv.Data, &m.config)
m.mu.Unlock()
err = func() error {
m.mu.Lock()
defer m.mu.Unlock()
return jsoniter.ConfigFastest.Unmarshal(kv.Data, &m.config)
}()
case GoalsKey:
m.mu.Lock()
err = jsoniter.ConfigFastest.Unmarshal(kv.Data, &m.goals)
m.mu.Unlock()
err = func() error {
m.mu.Lock()
defer m.mu.Unlock()
return jsoniter.ConfigFastest.Unmarshal(kv.Data, &m.goals)
}()
case RewardsKey:
m.mu.Lock()
err = jsoniter.ConfigFastest.Unmarshal(kv.Data, &m.rewards)
m.mu.Unlock()
err = func() error {
m.mu.Lock()
defer m.mu.Unlock()
return jsoniter.ConfigFastest.Unmarshal(kv.Data, &m.rewards)
}()
case QueueKey:
m.mu.Lock()
err = jsoniter.ConfigFastest.Unmarshal(kv.Data, &m.queue)
m.mu.Unlock()
err = func() error {
m.mu.Lock()
defer m.mu.Unlock()
return jsoniter.ConfigFastest.Unmarshal(kv.Data, &m.queue)
}()
case CreateRedeemRPC:
var redeem Redeem
err = jsoniter.ConfigFastest.Unmarshal(kv.Data, &redeem)
@ -145,9 +153,11 @@ func (m *Manager) update(kvs []database.ModifiedKV) error {
var entry PointsEntry
err = jsoniter.ConfigFastest.Unmarshal(kv.Data, &entry)
user := kv.Key[len(PointsPrefix):]
m.mu.Lock()
m.points[user] = entry
m.mu.Unlock()
func() {
m.mu.Lock()
defer m.mu.Unlock()
m.points[user] = entry
}()
}
}
if err != nil {

View file

@ -4,8 +4,10 @@ import (
"context"
"strings"
"sync"
"text/template"
"time"
"github.com/Masterminds/sprig"
irc "github.com/gempir/go-twitch-irc/v2"
jsoniter "github.com/json-iterator/go"
"github.com/sirupsen/logrus"
@ -25,8 +27,10 @@ type Bot struct {
banlist map[string]bool
chatHistory []irc.PrivateMessage
commands map[string]BotCommand
customCommands map[string]BotCustomCommand
commands map[string]BotCommand
customCommands map[string]BotCustomCommand
customTemplates map[string]*template.Template
customFunctions template.FuncMap
mu sync.Mutex
@ -39,17 +43,18 @@ func NewBot(api *Client, config BotConfig) *Bot {
client := irc.NewClient(config.Username, config.Token)
bot := &Bot{
Client: client,
username: strings.ToLower(config.Username), // Normalize username
config: config,
logger: api.logger,
api: api,
lastMessage: time.Now(),
activeUsers: make(map[string]bool),
banlist: make(map[string]bool),
mu: sync.Mutex{},
commands: make(map[string]BotCommand),
customCommands: make(map[string]BotCustomCommand),
Client: client,
username: strings.ToLower(config.Username), // Normalize username
config: config,
logger: api.logger,
api: api,
lastMessage: time.Now(),
activeUsers: make(map[string]bool),
banlist: make(map[string]bool),
mu: sync.Mutex{},
commands: make(map[string]BotCommand),
customCommands: make(map[string]BotCustomCommand),
customTemplates: make(map[string]*template.Template),
}
client.OnPrivateMessage(func(message irc.PrivateMessage) {
@ -81,7 +86,7 @@ func NewBot(api *Client, config BotConfig) *Bot {
continue
}
if strings.HasPrefix(message.Message, cmd) {
go cmdCustom(bot, data, message)
go cmdCustom(bot, cmd, data, message)
bot.lastMessage = time.Now()
}
}
@ -124,7 +129,12 @@ func NewBot(api *Client, config BotConfig) *Bot {
bot.Client.Join(config.Channel)
// Load custom commands
bot.setupFunctions()
api.db.GetJSON(CustomCommandsKey, &bot.customCommands)
err := bot.updateTemplates()
if err != nil {
bot.logger.WithError(err).Error("failed to load custom commands")
}
go api.db.Subscribe(context.Background(), bot.updateCommands, CustomCommandsKey)
return bot
@ -135,9 +145,28 @@ func (b *Bot) updateCommands(kvs []database.ModifiedKV) error {
key := string(kv.Key)
switch key {
case CustomCommandsKey:
b.mu.Lock()
err := jsoniter.ConfigFastest.Unmarshal(kv.Data, &b.customCommands)
b.mu.Unlock()
err := func() error {
b.mu.Lock()
defer b.mu.Unlock()
return jsoniter.ConfigFastest.Unmarshal(kv.Data, &b.customCommands)
}()
if err != nil {
return err
}
// Recreate templates
if err := b.updateTemplates(); err != nil {
return err
}
}
}
return nil
}
func (b *Bot) updateTemplates() error {
for cmd, tmpl := range b.customCommands {
var err error
b.customTemplates[cmd], err = template.New("").Funcs(sprig.TxtFuncMap()).Funcs(b.customFunctions).Parse(tmpl.Response)
if err != nil {
return err
}
}

View file

@ -1,7 +1,14 @@
package twitch
import (
"bytes"
"math/rand"
"strconv"
"strings"
"text/template"
irc "github.com/gempir/go-twitch-irc/v2"
"github.com/nicklaw5/helix"
)
type AccessLevelType string
@ -23,7 +30,47 @@ type BotCommand struct {
Enabled bool
}
func cmdCustom(bot *Bot, cmd BotCustomCommand, message irc.PrivateMessage) {
func cmdCustom(bot *Bot, cmd string, data BotCustomCommand, message irc.PrivateMessage) {
// Add future logic (like counters etc) here, for now it's just fixed messages
bot.Client.Say(message.Channel, cmd.Response)
var buf bytes.Buffer
err := bot.customTemplates[cmd].Execute(&buf, message)
if err != nil {
bot.logger.WithError(err).Error("Failed to execute custom command template")
return
}
bot.Client.Say(message.Channel, buf.String())
}
func (b *Bot) setupFunctions() {
b.customFunctions = template.FuncMap{
"user": func(message irc.PrivateMessage) string {
return message.User.DisplayName
},
"param": func(num int, message irc.PrivateMessage) string {
parts := strings.Split(message.Message, " ")
if num >= len(parts) {
return parts[len(parts)-1]
}
return parts[num]
},
"randomInt": func(min int, max int) int {
return rand.Intn(max-min) + min
},
"game": func(channel string) string {
info, err := b.api.API.SearchChannels(&helix.SearchChannelsParams{Channel: channel, First: 1, LiveOnly: false})
if err != nil {
return "unknown"
}
return info.Data.Channels[0].GameName
},
"count": func(name string) int {
counter := 0
if byt, err := b.api.db.GetKey(BotCounterPrefix + name); err == nil {
counter, _ = strconv.Atoi(string(byt))
}
counter += 1
b.api.db.PutKey(BotCounterPrefix+name, []byte(strconv.Itoa(counter)))
return counter
},
}
}

View file

@ -31,3 +31,5 @@ type BotCustomCommand struct {
const CustomCommandsKey = "twitch/bot-custom-commands"
const WriteMessageRPC = "twitch/@send-chat-message"
const BotCounterPrefix = "twitch/bot-counters/"