strimertul/twitch/alerts/events.go

212 lines
7.1 KiB
Go

package alerts
import (
"encoding/json"
"math/rand"
"text/template"
"git.sr.ht/~ashkeel/strimertul/log"
"git.sr.ht/~ashkeel/strimertul/twitch/chat"
"github.com/nicklaw5/helix/v2"
)
func (m *Module) onEventSubEvent(_ string, value string) {
var ev eventSubNotification
if err := json.Unmarshal([]byte(value), &ev); err != nil {
m.logger.Warn("Error parsing webhook payload", log.Error(err))
return
}
switch ev.Subscription.Type {
case helix.EventSubTypeChannelFollow:
// Only process if we care about follows
if !m.Config.Follow.Enabled {
return
}
// Parse as a follow event
var followEv helix.EventSubChannelFollowEvent
if err := json.Unmarshal(ev.Event, &followEv); err != nil {
m.logger.Warn("Error parsing follow event", log.Error(err))
return
}
// Pick a random message
messageID := rand.Intn(len(m.Config.Follow.Messages))
// Pick compiled template or fallback to plain text
tpl, ok := m.templates[templateTypeFollow][m.Config.Follow.Messages[messageID]]
if !ok {
chat.WriteMessage(m.db, m.logger, chat.WriteMessageRequest{
Message: m.Config.Follow.Messages[messageID],
Announce: m.Config.Follow.Announce,
})
return
}
m.writeTemplate(tpl, &followEv, m.Config.Follow.Announce)
// Compile template and send
case helix.EventSubTypeChannelRaid:
// Only process if we care about raids
if !m.Config.Raid.Enabled {
return
}
// Parse as raid event
var raidEv helix.EventSubChannelRaidEvent
if err := json.Unmarshal(ev.Event, &raidEv); err != nil {
m.logger.Warn("Error parsing raid event", log.Error(err))
return
}
// Pick a random message from base set
messageID := rand.Intn(len(m.Config.Raid.Messages))
tpl, ok := m.templates[templateTypeRaid][m.Config.Raid.Messages[messageID]]
if !ok {
// Broken template!
chat.WriteMessage(m.db, m.logger, chat.WriteMessageRequest{
Message: m.Config.Raid.Messages[messageID],
Announce: m.Config.Raid.Announce,
})
return
}
// If we have variations, get the available variations and pick the one with the highest minimum viewers that are met
if len(m.Config.Raid.Variations) > 0 {
variation := getBestValidVariation(m.Config.Raid.Variations, func(variation raidVariation) int {
if variation.MinViewers != nil && raidEv.Viewers >= *variation.MinViewers {
return *variation.MinViewers
}
return 0
})
tpl = m.replaceWithVariation(tpl, templateTypeRaid, variation.Messages)
}
// Compile template and send
m.writeTemplate(tpl, &raidEv, m.Config.Raid.Announce)
case helix.EventSubTypeChannelCheer:
// Only process if we care about bits
if !m.Config.Cheer.Enabled {
return
}
// Parse as cheer event
var cheerEv helix.EventSubChannelCheerEvent
if err := json.Unmarshal(ev.Event, &cheerEv); err != nil {
m.logger.Warn("Error parsing cheer event", log.Error(err))
return
}
// Pick a random message from base set
messageID := rand.Intn(len(m.Config.Cheer.Messages))
tpl, ok := m.templates[templateTypeCheer][m.Config.Cheer.Messages[messageID]]
if !ok {
// Broken template!
chat.WriteMessage(m.db, m.logger, chat.WriteMessageRequest{
Message: m.Config.Cheer.Messages[messageID],
Announce: m.Config.Cheer.Announce,
})
return
}
// If we have variations, get the available variations and pick the one with the highest minimum amount that is met
if len(m.Config.Cheer.Variations) > 0 {
variation := getBestValidVariation(m.Config.Cheer.Variations, func(variation cheerVariation) int {
if variation.MinAmount != nil && cheerEv.Bits >= *variation.MinAmount {
return *variation.MinAmount
}
return 0
})
tpl = m.replaceWithVariation(tpl, templateTypeCheer, variation.Messages)
}
// Compile template and send
m.writeTemplate(tpl, &cheerEv, m.Config.Cheer.Announce)
case helix.EventSubTypeChannelSubscription:
// Only process if we care about subscriptions
if !m.Config.Subscription.Enabled {
return
}
// Parse as subscription event
var subEv helix.EventSubChannelSubscribeEvent
if err := json.Unmarshal(ev.Event, &subEv); err != nil {
m.logger.Warn("Error parsing new subscription event", log.Error(err))
return
}
m.addMixedEvent(subEv)
case helix.EventSubTypeChannelSubscriptionMessage:
// Only process if we care about subscriptions
if !m.Config.Subscription.Enabled {
return
}
// Parse as subscription event
var subEv helix.EventSubChannelSubscriptionMessageEvent
err := json.Unmarshal(ev.Event, &subEv)
if err != nil {
m.logger.Warn("Error parsing returning subscription event", log.Error(err))
return
}
m.addMixedEvent(subEv)
case helix.EventSubTypeChannelSubscriptionGift:
// Only process if we care about gifted subs
if !m.Config.GiftSub.Enabled {
return
}
// Parse as gift event
var giftEv helix.EventSubChannelSubscriptionGiftEvent
if err := json.Unmarshal(ev.Event, &giftEv); err != nil {
m.logger.Warn("Error parsing subscription gifted event", log.Error(err))
return
}
// Pick a random message from base set
messageID := rand.Intn(len(m.Config.GiftSub.Messages))
tpl, ok := m.templates[templateTypeGift][m.Config.GiftSub.Messages[messageID]]
if !ok {
// Broken template!
chat.WriteMessage(m.db, m.logger, chat.WriteMessageRequest{
Message: m.Config.GiftSub.Messages[messageID],
Announce: m.Config.GiftSub.Announce,
})
return
}
// 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(m.Config.GiftSub.Variations) > 0 {
if giftEv.IsAnonymous {
variation := getBestValidVariation(m.Config.GiftSub.Variations, func(variation giftSubVariation) int {
if variation.IsAnonymous != nil && *variation.IsAnonymous {
return 1
}
return 0
})
tpl = m.replaceWithVariation(tpl, templateTypeGift, variation.Messages)
} else if giftEv.CumulativeTotal > 0 {
variation := getBestValidVariation(m.Config.GiftSub.Variations, func(variation giftSubVariation) int {
if variation.MinCumulative != nil && *variation.MinCumulative > giftEv.CumulativeTotal {
return *variation.MinCumulative
}
return 0
})
tpl = m.replaceWithVariation(tpl, templateTypeGift, variation.Messages)
}
}
// Compile template and send
m.writeTemplate(tpl, &giftEv, m.Config.GiftSub.Announce)
}
}
func (m *Module) replaceWithVariation(tpl *template.Template, templateType templateType, messages []string) *template.Template {
if messages != nil {
messageID := rand.Intn(len(messages))
// Make sure the template is valid
if temp, ok := m.templates[templateType][messages[messageID]]; ok {
return temp
}
}
return tpl
}
// For variations, some variations are better than others, this function returns the best one
// by using a provided score function. The score is 0 or less if the variation is not valid,
// and 1 or more if it is valid. The variation with the highest score is returned.
func getBestValidVariation[T any](variations []T, filterFunc func(T) int) T {
var best T
var bestScore int
for _, variation := range variations {
score := filterFunc(variation)
if score > bestScore {
best = variation
bestScore = score
}
}
return best
}