Alert templates are precalculate on config load/change

This commit is contained in:
Ash Keel 2021-12-06 14:32:03 +01:00
parent ee8c88b94c
commit 65d6be5ec3
No known key found for this signature in database
GPG Key ID: BAD8D93E7314ED3E
5 changed files with 188 additions and 42 deletions

View File

@ -247,7 +247,8 @@
"variation-header": "Variations",
"add-variation": "Add variation",
"variation-condition": "Condition",
"min-months": "Subscription months (at least)"
"min-months": "Subscription months (at least)",
"delete-variation": "Delete variation"
}
},
"auth": {

View File

@ -133,6 +133,7 @@ export default function TwitchBotAlertsPage(
<input
type="radio"
name={`sub-var-${i}`}
checked={variation.is_gifted ?? false}
onChange={(ev) =>
dispatch(
apiReducer.actions.twitchBotAlertsChanged({
@ -163,6 +164,7 @@ export default function TwitchBotAlertsPage(
<label className="radio">
<input
type="radio"
checked={!!variation.min_streak}
name={`sub-var-${i}`}
onChange={(ev) =>
dispatch(
@ -195,7 +197,34 @@ export default function TwitchBotAlertsPage(
</Field>
{variation.min_streak ? (
<Field name={t('twitch.bot-alerts.min-months')}>
<input type="number" className="input" min="1" step="1" />
<input
type="number"
className="input"
min="1"
step="1"
value={variation.min_streak}
onChange={(ev) =>
dispatch(
apiReducer.actions.twitchBotAlertsChanged({
...twitchBotAlerts,
subscription: {
...twitchBotAlerts.subscription,
variations:
// Replace messages in nth variation
twitchBotAlerts?.subscription?.variations.map(
(v, j) =>
j === i
? {
...v,
min_streak: ev.target.value,
}
: v,
),
},
}),
)
}
/>
</Field>
) : null}
<Field name={t('twitch.bot-alerts.messages')}>
@ -218,6 +247,24 @@ export default function TwitchBotAlertsPage(
}
/>
</Field>
<button
className="button is-small is-danger"
onClick={() => {
const variations =
twitchBotAlerts?.subscription?.variations ?? [];
dispatch(
apiReducer.actions.twitchBotAlertsChanged({
...twitchBotAlerts,
subscription: {
...twitchBotAlerts.subscription,
variations: variations.filter((v, j) => j !== i),
},
}),
);
}}
>
{t('twitch.bot-alerts.delete-variation')}
</button>
</article>
))}
<button

2
go.mod
View File

@ -14,3 +14,5 @@ require (
github.com/strimertul/kilovolt/v6 v6.1.0
github.com/strimertul/stulbe-client-go v0.6.0
)
replace github.com/nicklaw5/helix/v2 => github.com/ashkeel/helix/v2 v2.2.1-0.20211206103917-e677e633538f

4
go.sum
View File

@ -10,6 +10,8 @@ github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFP
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=
github.com/ashkeel/helix/v2 v2.2.1-0.20211206103917-e677e633538f h1:ZpYT5G9ktPZdLbRh5Y9gNpBwK8xis9LfcNaofP5BJpo=
github.com/ashkeel/helix/v2 v2.2.1-0.20211206103917-e677e633538f/go.mod h1:0ONzvVi1cH+k3a7EDIFNNqxfW0podhf+CqlmFvuexq8=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
@ -101,8 +103,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nicklaw5/helix/v2 v2.2.0 h1:vJQVH9ayABEDWgqLfg+f5tjN1bKy0cFnoHuNiWfu7f4=
github.com/nicklaw5/helix/v2 v2.2.0/go.mod h1:0ONzvVi1cH+k3a7EDIFNNqxfW0podhf+CqlmFvuexq8=
github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc h1:Ak86L+yDSOzKFa7WM5bf5itSOo1e3Xh8bm5YCMUXIjQ=
github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=

View File

@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"math/rand"
"sync"
"text/template"
@ -68,13 +69,16 @@ type BotAlertsConfig struct {
type BotAlertsModule struct {
Config BotAlertsConfig
bot *Bot
mu sync.Mutex
bot *Bot
mu sync.Mutex
templates map[string]*template.Template
}
func SetupAlerts(bot *Bot) *BotAlertsModule {
mod := &BotAlertsModule{
bot: bot,
bot: bot,
mu: sync.Mutex{},
templates: make(map[string]*template.Template),
}
// Load config from database
@ -86,6 +90,8 @@ func SetupAlerts(bot *Bot) *BotAlertsModule {
bot.api.db.PutJSON(BotAlertsKey, mod.Config)
}
mod.compileTemplates()
go bot.api.db.Subscribe(context.Background(), func(changed []database.ModifiedKV) error {
for _, kv := range changed {
if kv.Key == BotAlertsKey {
@ -95,6 +101,7 @@ func SetupAlerts(bot *Bot) *BotAlertsModule {
} else {
bot.logger.Info("reloaded timer config")
}
mod.compileTemplates()
}
}
return nil
@ -117,26 +124,37 @@ func SetupAlerts(bot *Bot) *BotAlertsModule {
return
}
// Assign random message
msg := mod.Config.Subscription.Messages[rand.Intn(len(mod.Config.Subscription.Messages))]
messageID := rand.Intn(len(mod.Config.Subscription.Messages))
tpl, ok := mod.templates[fmt.Sprintf("sub-%d", messageID)]
// If template is broken, write it as is (soft fail, plus we raise attention I guess?)
if !ok {
mod.bot.WriteMessage(mod.Config.Subscription.Messages[messageID])
return
}
// Check for variations, either by streak or gifted
if sub.IsGift {
for _, variation := range mod.Config.Subscription.Variations {
for variationIndex, variation := range mod.Config.Subscription.Variations {
if variation.IsGifted != nil && *variation.IsGifted {
msg = variation.Messages[rand.Intn(len(variation.Messages))]
break
// Make sure template is valid
if temp, ok := mod.templates[fmt.Sprintf("sub-v%d-%d", variationIndex, messageID)]; ok {
tpl = temp
break
}
}
}
} else if sub.DurationMonths > 0 {
minMonths := -1
for _, variation := range mod.Config.Subscription.Variations {
for variationIndex, variation := range mod.Config.Subscription.Variations {
if variation.MinStreak != nil && sub.DurationMonths >= *variation.MinStreak && sub.DurationMonths >= minMonths {
msg = variation.Messages[rand.Intn(len(variation.Messages))]
minMonths = *variation.MinStreak
break
// Make sure template is valid
if temp, ok := mod.templates[fmt.Sprintf("sub-v%d-%d", variationIndex, messageID)]; ok {
tpl = temp
minMonths = *variation.MinStreak
}
}
}
}
writeTemplate(bot, msg, sub)
writeTemplate(bot, tpl, sub)
}
addPendingSub := func(ev interface{}) {
switch ev.(type) {
@ -173,7 +191,7 @@ func SetupAlerts(bot *Bot) *BotAlertsModule {
// Already pending, add extra data
ev.StreakMonths = sub.StreakMonths
ev.DurationMonths = sub.DurationMonths
ev.CumulativeTotal = sub.CumulativeTotal
ev.CumulativeMonths = sub.CumulativeMonths
ev.Message = sub.Message
return
}
@ -187,7 +205,7 @@ func SetupAlerts(bot *Bot) *BotAlertsModule {
Tier: sub.Tier,
StreakMonths: sub.StreakMonths,
DurationMonths: sub.DurationMonths,
CumulativeTotal: sub.CumulativeTotal,
CumulativeMonths: sub.CumulativeMonths,
Message: sub.Message,
}
go func() {
@ -221,9 +239,14 @@ func SetupAlerts(bot *Bot) *BotAlertsModule {
continue
}
// Pick a random message
msg := mod.Config.Follow.Messages[rand.Intn(len(mod.Config.Follow.Messages))]
messageID := rand.Intn(len(mod.Config.Follow.Messages))
// Pick compiled template or fallback to plain text
if tpl, ok := mod.templates[fmt.Sprintf("follow-%d", messageID)]; ok {
writeTemplate(bot, tpl, &followEv)
} else {
bot.WriteMessage(mod.Config.Follow.Messages[messageID])
}
// Compile template and send
writeTemplate(bot, msg, &followEv)
case helix.EventSubTypeChannelRaid:
// Only process if we care about raids
if !mod.Config.Raid.Enabled {
@ -237,19 +260,28 @@ func SetupAlerts(bot *Bot) *BotAlertsModule {
continue
}
// Pick a random message from base set
msg := mod.Config.Raid.Messages[rand.Intn(len(mod.Config.Raid.Messages))]
messageID := rand.Intn(len(mod.Config.Raid.Messages))
tpl, ok := mod.templates[fmt.Sprintf("raid-%d", messageID)]
if !ok {
// Broken template!
mod.bot.WriteMessage(mod.Config.Raid.Messages[messageID])
continue
}
// If we have variations, loop through all the available variations and pick the one with the highest minimum viewers that are met
if len(mod.Config.Raid.Variations) > 0 {
minViewers := -1
for _, variation := range mod.Config.Raid.Variations {
for variationIndex, variation := range mod.Config.Raid.Variations {
if variation.MinViewers != nil && *variation.MinViewers > minViewers && raidEv.Viewers >= *variation.MinViewers {
msg = variation.Messages[rand.Intn(len(variation.Messages))]
minViewers = *variation.MinViewers
// Make sure the template is valid
if varTemplate, ok := mod.templates[fmt.Sprintf("raid-v%d-%d", variationIndex, messageID)]; ok {
tpl = varTemplate
minViewers = *variation.MinViewers
}
}
}
}
// Compile template and send
writeTemplate(bot, msg, &raidEv)
writeTemplate(bot, tpl, &raidEv)
case helix.EventSubTypeChannelCheer:
// Only process if we care about bits
if !mod.Config.Cheer.Enabled {
@ -263,19 +295,28 @@ func SetupAlerts(bot *Bot) *BotAlertsModule {
continue
}
// Pick a random message from base set
msg := mod.Config.Cheer.Messages[rand.Intn(len(mod.Config.Cheer.Messages))]
messageID := rand.Intn(len(mod.Config.Cheer.Messages))
tpl, ok := mod.templates[fmt.Sprintf("cheer-%d", messageID)]
if !ok {
// Broken template!
mod.bot.WriteMessage(mod.Config.Raid.Messages[messageID])
continue
}
// If we have variations, loop through all the available variations and pick the one with the highest minimum amount that is met
if len(mod.Config.Cheer.Variations) > 0 {
minAmount := -1
for _, variation := range mod.Config.Cheer.Variations {
for variationIndex, variation := range mod.Config.Cheer.Variations {
if variation.MinAmount != nil && *variation.MinAmount > minAmount && cheerEv.Bits >= *variation.MinAmount {
msg = variation.Messages[rand.Intn(len(variation.Messages))]
minAmount = *variation.MinAmount
// Make sure the template is valid
if varTemplate, ok := mod.templates[fmt.Sprintf("cheer-v%d-%d", variationIndex, messageID)]; ok {
tpl = varTemplate
minAmount = *variation.MinAmount
}
}
}
}
// Compile template and send
writeTemplate(bot, msg, &cheerEv)
writeTemplate(bot, tpl, &cheerEv)
case helix.EventSubTypeChannelSubscription:
// Only process if we care about subscriptions
if !mod.Config.Subscription.Enabled {
@ -315,28 +356,40 @@ func SetupAlerts(bot *Bot) *BotAlertsModule {
continue
}
// Pick a random message from base set
msg := mod.Config.GiftSub.Messages[rand.Intn(len(mod.Config.GiftSub.Messages))]
messageID := rand.Intn(len(mod.Config.GiftSub.Messages))
tpl, ok := mod.templates[fmt.Sprintf("gift-%d", messageID)]
if !ok {
// Broken template!
mod.bot.WriteMessage(mod.Config.GiftSub.Messages[messageID])
continue
}
// If we have variations, loop through all the available variations and pick the one with the highest minimum cumulative total that are met
if len(mod.Config.GiftSub.Variations) > 0 {
if giftEv.IsAnonymous {
for _, variation := range mod.Config.GiftSub.Variations {
for variationIndex, variation := range mod.Config.GiftSub.Variations {
if variation.IsAnonymous != nil && *variation.IsAnonymous {
msg = variation.Messages[rand.Intn(len(variation.Messages))]
break
// Make sure template is valid
if temp, ok := mod.templates[fmt.Sprintf("gift-v%d-%d", variationIndex, messageID)]; ok {
tpl = temp
break
}
}
}
} else if giftEv.CumulativeTotal > 0 {
minCumulative := -1
for _, variation := range mod.Config.GiftSub.Variations {
for variationIndex, variation := range mod.Config.GiftSub.Variations {
if variation.MinCumulative != nil && *variation.MinCumulative > minCumulative && giftEv.CumulativeTotal >= *variation.MinCumulative {
msg = variation.Messages[rand.Intn(len(variation.Messages))]
minCumulative = *variation.MinCumulative
// Make sure template is valid
if temp, ok := mod.templates[fmt.Sprintf("gift-v%d-%d", variationIndex, messageID)]; ok {
tpl = temp
minCumulative = *variation.MinCumulative
}
}
}
}
}
// Compile template and send
writeTemplate(bot, msg, &giftEv)
writeTemplate(bot, tpl, &giftEv)
}
}
}
@ -348,14 +401,57 @@ func SetupAlerts(bot *Bot) *BotAlertsModule {
return mod
}
func writeTemplate(bot *Bot, msg string, data interface{}) {
func (m *BotAlertsModule) compileTemplates() {
m.templates = make(map[string]*template.Template)
for index, msg := range m.Config.Follow.Messages {
m.addTemplate(fmt.Sprintf("follow-%d", index), msg)
}
for index, msg := range m.Config.Subscription.Messages {
m.addTemplate(fmt.Sprintf("sub-%d", index), msg)
}
for varIndex, variation := range m.Config.Subscription.Variations {
for index, msg := range variation.Messages {
m.addTemplate(fmt.Sprintf("sub-v%d-%d", varIndex, index), msg)
}
}
for index, msg := range m.Config.Raid.Messages {
m.addTemplate(fmt.Sprintf("raid-%d", index), msg)
}
for varIndex, variation := range m.Config.Raid.Variations {
for index, msg := range variation.Messages {
m.addTemplate(fmt.Sprintf("raid-v%d-%d", varIndex, index), msg)
}
}
for index, msg := range m.Config.Cheer.Messages {
m.addTemplate(fmt.Sprintf("cheer-%d", index), msg)
}
for varIndex, variation := range m.Config.Cheer.Variations {
for index, msg := range variation.Messages {
m.addTemplate(fmt.Sprintf("cheer-v%d-%d", varIndex, index), msg)
}
}
for index, msg := range m.Config.GiftSub.Messages {
m.addTemplate(fmt.Sprintf("gift-%d", index), msg)
}
for varIndex, variation := range m.Config.GiftSub.Variations {
for index, msg := range variation.Messages {
m.addTemplate(fmt.Sprintf("gift-v%d-%d", varIndex, index), msg)
}
}
}
func (m *BotAlertsModule) addTemplate(id string, msg string) {
tpl, err := template.New("").Funcs(sprig.TxtFuncMap()).Parse(msg)
if err != nil {
bot.logger.WithError(err).Error("error parsing template for alert")
m.bot.logger.WithError(err).Error("error compiling template")
return
}
m.templates[id] = tpl
}
func writeTemplate(bot *Bot, tpl *template.Template, data interface{}) {
var buf bytes.Buffer
err = tpl.Execute(&buf, data)
err := tpl.Execute(&buf, data)
if err != nil {
bot.logger.WithError(err).Error("error executing template for alert")
return
@ -372,7 +468,7 @@ type subMixedEvent struct {
BroadcasterUserName string
Tier string
IsGift bool
CumulativeTotal int
CumulativeMonths int
StreakMonths int
DurationMonths int
Message helix.EventSubMessage