2024-05-22 16:51:46 +00:00
|
|
|
package chatbot
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"html/template"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/wailsapp/wails/v2/pkg/runtime"
|
2024-06-14 17:49:03 +00:00
|
|
|
"travisty.io/tyler/rum-goggles/v1/internal/events"
|
|
|
|
rumblelivestreamlib "travisty.io/tyler/rumble-livestream-lib-go"
|
2024-05-22 16:51:46 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Runner struct {
|
2024-05-28 20:44:31 +00:00
|
|
|
apiCh chan events.ApiFollower
|
2024-05-22 16:51:46 +00:00
|
|
|
cancel context.CancelFunc
|
|
|
|
cancelMu sync.Mutex
|
|
|
|
channelID *int
|
|
|
|
channelIDMu sync.Mutex
|
|
|
|
chatCh chan events.Chat
|
|
|
|
client *rumblelivestreamlib.Client
|
2024-05-28 20:44:31 +00:00
|
|
|
page string
|
2024-05-22 16:51:46 +00:00
|
|
|
rule Rule
|
|
|
|
run runFunc
|
|
|
|
wails context.Context
|
|
|
|
}
|
|
|
|
|
|
|
|
type chatFields struct {
|
|
|
|
ChannelName string
|
|
|
|
DisplayName string
|
|
|
|
Username string
|
|
|
|
Rant int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Runner) chat(fields *chatFields) error {
|
|
|
|
msg, err := r.rule.Parameters.Message.String()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error getting message string: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if fields != nil {
|
|
|
|
tmpl, err := template.New("chat").Parse(msg)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error creating template: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var msgB bytes.Buffer
|
|
|
|
err = tmpl.Execute(&msgB, fields)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error executing template: %v", err)
|
|
|
|
}
|
|
|
|
msg = msgB.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
err = r.client.Chat(msg, r.channelID)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error sending chat: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-05-28 20:44:31 +00:00
|
|
|
// func (r *Runner) init() error {
|
|
|
|
// if r.rule.Parameters == nil || r.rule.Parameters.Trigger == nil {
|
|
|
|
// return fmt.Errorf("invalid rule")
|
|
|
|
// }
|
2024-05-22 16:51:46 +00:00
|
|
|
|
2024-05-28 20:44:31 +00:00
|
|
|
// channelID, err := r.rule.Parameters.SendAs.ChannelIDInt()
|
|
|
|
// if err != nil {
|
|
|
|
// return fmt.Errorf("error converting channel ID to int: %v", err)
|
|
|
|
// }
|
2024-05-22 16:51:46 +00:00
|
|
|
|
2024-05-28 20:44:31 +00:00
|
|
|
// r.channelIDMu.Lock()
|
|
|
|
// r.channelID = channelID
|
|
|
|
// r.channelIDMu.Unlock()
|
2024-05-22 16:51:46 +00:00
|
|
|
|
2024-05-28 20:44:31 +00:00
|
|
|
// switch {
|
|
|
|
// case r.rule.Parameters.Trigger.OnTimer != nil:
|
|
|
|
// r.run = r.runOnTimer
|
|
|
|
// case r.rule.Parameters.Trigger.OnEvent != nil:
|
|
|
|
// r.run = r.runOnEvent
|
|
|
|
// case r.rule.Parameters.Trigger.OnCommand != nil:
|
|
|
|
// r.run = r.runOnCommand
|
|
|
|
// }
|
2024-05-22 16:51:46 +00:00
|
|
|
|
2024-05-28 20:44:31 +00:00
|
|
|
// return nil
|
|
|
|
// }
|
2024-05-22 16:51:46 +00:00
|
|
|
|
|
|
|
type runFunc func(ctx context.Context) error
|
|
|
|
|
|
|
|
func (r *Runner) runOnCommand(ctx context.Context) error {
|
|
|
|
if r.rule.ID == nil || r.rule.Parameters == nil || r.rule.Parameters.Trigger == nil {
|
|
|
|
return fmt.Errorf("invalid rule")
|
|
|
|
}
|
|
|
|
if r.rule.Parameters.Trigger.OnCommand == nil {
|
|
|
|
return fmt.Errorf("command is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
var prev time.Time
|
|
|
|
for {
|
|
|
|
runtime.EventsEmit(r.wails, fmt.Sprintf("ChatbotRuleActive-%d", *r.rule.ID), true)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil
|
2024-05-28 20:44:31 +00:00
|
|
|
case chat := <-r.chatCh:
|
2024-05-22 16:51:46 +00:00
|
|
|
now := time.Now()
|
|
|
|
if now.Sub(prev) < r.rule.Parameters.Trigger.OnCommand.Timeout*time.Second {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2024-05-28 20:44:31 +00:00
|
|
|
if block := r.blockCommand(chat); block {
|
|
|
|
// if bypass := r.bypassCommand(chat); !bypass {break}
|
2024-05-22 16:51:46 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2024-05-28 20:44:31 +00:00
|
|
|
err := r.handleCommand(chat)
|
2024-05-22 16:51:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error handling command: %v", err)
|
|
|
|
}
|
|
|
|
prev = now
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-28 20:44:31 +00:00
|
|
|
func (r *Runner) blockCommand(chat events.Chat) bool {
|
2024-05-22 16:51:46 +00:00
|
|
|
if r.rule.Parameters.Trigger.OnCommand.Restrict == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.rule.Parameters.Trigger.OnCommand.Restrict.ToFollower &&
|
2024-05-28 20:44:31 +00:00
|
|
|
!chat.Message.IsFollower {
|
2024-05-22 16:51:46 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
subscriber := false
|
2024-05-28 20:44:31 +00:00
|
|
|
for _, badge := range chat.Message.Badges {
|
2024-05-22 16:51:46 +00:00
|
|
|
if badge == rumblelivestreamlib.ChatBadgeLocalsSupporter || badge == rumblelivestreamlib.ChatBadgeRecurringSubscription {
|
|
|
|
subscriber = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.rule.Parameters.Trigger.OnCommand.Restrict.ToSubscriber &&
|
|
|
|
!subscriber {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2024-05-28 20:44:31 +00:00
|
|
|
if chat.Message.Rant < r.rule.Parameters.Trigger.OnCommand.Restrict.ToRant*100 {
|
2024-05-22 16:51:46 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2024-05-28 20:44:31 +00:00
|
|
|
func (r *Runner) handleCommand(chat events.Chat) error {
|
|
|
|
displayName := chat.Message.Username
|
|
|
|
if chat.Message.ChannelName != "" {
|
|
|
|
displayName = chat.Message.ChannelName
|
|
|
|
}
|
|
|
|
|
|
|
|
fields := &chatFields{
|
|
|
|
ChannelName: chat.Message.ChannelName,
|
|
|
|
DisplayName: displayName,
|
|
|
|
Username: chat.Message.Username,
|
|
|
|
Rant: chat.Message.Rant / 100,
|
|
|
|
}
|
|
|
|
|
|
|
|
err := r.chat(fields)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error sending chat: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Runner) runOnEventFromAccountOnFollow(ctx context.Context) error {
|
|
|
|
if r.rule.ID == nil || r.rule.Parameters == nil || r.rule.Parameters.Trigger == nil {
|
|
|
|
return fmt.Errorf("invalid rule")
|
|
|
|
}
|
|
|
|
if r.rule.Parameters.Trigger.OnEvent == nil || r.rule.Parameters.Trigger.OnEvent.FromAccount == nil || r.rule.Parameters.Trigger.OnEvent.FromAccount.OnFollow == nil {
|
|
|
|
return fmt.Errorf("event is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
runtime.EventsEmit(r.wails, fmt.Sprintf("ChatbotRuleActive-%d", *r.rule.ID), true)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil
|
|
|
|
case api := <-r.apiCh:
|
|
|
|
err := r.handleEventOnFollow(api)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error handling event: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Runner) runOnEventFromChannelOnFollow(ctx context.Context) error {
|
|
|
|
if r.rule.ID == nil || r.rule.Parameters == nil || r.rule.Parameters.Trigger == nil {
|
|
|
|
return fmt.Errorf("invalid rule")
|
|
|
|
}
|
|
|
|
if r.rule.Parameters.Trigger.OnEvent == nil || r.rule.Parameters.Trigger.OnEvent.FromChannel == nil || r.rule.Parameters.Trigger.OnEvent.FromChannel.OnFollow == nil {
|
|
|
|
return fmt.Errorf("event is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
runtime.EventsEmit(r.wails, fmt.Sprintf("ChatbotRuleActive-%d", *r.rule.ID), true)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil
|
|
|
|
case api := <-r.apiCh:
|
|
|
|
err := r.handleEventOnFollow(api)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error handling event: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Runner) handleEventOnFollow(follower events.ApiFollower) error {
|
|
|
|
fields := &chatFields{
|
|
|
|
DisplayName: follower.Username,
|
|
|
|
Username: follower.Username,
|
|
|
|
}
|
|
|
|
|
|
|
|
err := r.chat(fields)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error sending chat: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Runner) runOnEventFromLiveStreamOnRaid(ctx context.Context) error {
|
|
|
|
if r.rule.ID == nil || r.rule.Parameters == nil || r.rule.Parameters.Trigger == nil {
|
|
|
|
return fmt.Errorf("invalid rule")
|
|
|
|
}
|
|
|
|
if r.rule.Parameters.Trigger.OnEvent == nil || r.rule.Parameters.Trigger.OnEvent.FromLiveStream == nil || r.rule.Parameters.Trigger.OnEvent.FromLiveStream.OnRaid == nil {
|
|
|
|
return fmt.Errorf("event is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
runtime.EventsEmit(r.wails, fmt.Sprintf("ChatbotRuleActive-%d", *r.rule.ID), true)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil
|
|
|
|
case chat := <-r.chatCh:
|
|
|
|
err := r.handleEventFromLiveStreamOnRaid(chat)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error handling event: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Runner) handleEventFromLiveStreamOnRaid(chat events.Chat) error {
|
|
|
|
if r.rule.Parameters == nil || r.rule.Parameters.Trigger == nil || r.rule.Parameters.Trigger.OnEvent == nil || r.rule.Parameters.Trigger.OnEvent.FromLiveStream == nil || r.rule.Parameters.Trigger.OnEvent.FromLiveStream.OnRaid == nil {
|
|
|
|
return fmt.Errorf("invalid rule")
|
|
|
|
}
|
|
|
|
|
|
|
|
displayName := chat.Message.Username
|
|
|
|
if chat.Message.ChannelName != "" {
|
|
|
|
displayName = chat.Message.ChannelName
|
|
|
|
}
|
|
|
|
|
|
|
|
fields := &chatFields{
|
|
|
|
ChannelName: chat.Message.ChannelName,
|
|
|
|
DisplayName: displayName,
|
|
|
|
Username: chat.Message.Username,
|
|
|
|
Rant: chat.Message.Rant / 100,
|
|
|
|
}
|
|
|
|
|
|
|
|
err := r.chat(fields)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error sending chat: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Runner) runOnEventFromLiveStreamOnRant(ctx context.Context) error {
|
|
|
|
if r.rule.ID == nil || r.rule.Parameters == nil || r.rule.Parameters.Trigger == nil {
|
|
|
|
return fmt.Errorf("invalid rule")
|
|
|
|
}
|
|
|
|
if r.rule.Parameters.Trigger.OnEvent == nil || r.rule.Parameters.Trigger.OnEvent.FromLiveStream == nil || r.rule.Parameters.Trigger.OnEvent.FromLiveStream.OnRant == nil {
|
|
|
|
return fmt.Errorf("event is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
runtime.EventsEmit(r.wails, fmt.Sprintf("ChatbotRuleActive-%d", *r.rule.ID), true)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil
|
|
|
|
case chat := <-r.chatCh:
|
|
|
|
err := r.handleEventFromLiveStreamOnRant(chat)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error handling event: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Runner) handleEventFromLiveStreamOnRant(chat events.Chat) error {
|
|
|
|
if r.rule.Parameters == nil || r.rule.Parameters.Trigger == nil || r.rule.Parameters.Trigger.OnEvent == nil || r.rule.Parameters.Trigger.OnEvent.FromLiveStream == nil || r.rule.Parameters.Trigger.OnEvent.FromLiveStream.OnRant == nil {
|
|
|
|
return fmt.Errorf("invalid rule")
|
|
|
|
}
|
|
|
|
|
|
|
|
rant := chat.Message.Rant / 100
|
|
|
|
minAmount := r.rule.Parameters.Trigger.OnEvent.FromLiveStream.OnRant.MinAmount
|
|
|
|
maxAmount := r.rule.Parameters.Trigger.OnEvent.FromLiveStream.OnRant.MaxAmount
|
|
|
|
if minAmount != 0 && rant < minAmount {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if maxAmount != 0 && rant > maxAmount {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
displayName := chat.Message.Username
|
|
|
|
if chat.Message.ChannelName != "" {
|
|
|
|
displayName = chat.Message.ChannelName
|
|
|
|
}
|
|
|
|
|
|
|
|
fields := &chatFields{
|
|
|
|
ChannelName: chat.Message.ChannelName,
|
|
|
|
DisplayName: displayName,
|
|
|
|
Username: chat.Message.Username,
|
|
|
|
Rant: chat.Message.Rant / 100,
|
|
|
|
}
|
|
|
|
|
|
|
|
err := r.chat(fields)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error sending chat: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Runner) runOnEventFromLiveStreamOnSub(ctx context.Context) error {
|
|
|
|
if r.rule.ID == nil || r.rule.Parameters == nil || r.rule.Parameters.Trigger == nil {
|
|
|
|
return fmt.Errorf("invalid rule")
|
|
|
|
}
|
|
|
|
if r.rule.Parameters.Trigger.OnEvent == nil || r.rule.Parameters.Trigger.OnEvent.FromLiveStream == nil || r.rule.Parameters.Trigger.OnEvent.FromLiveStream.OnSub == nil {
|
|
|
|
return fmt.Errorf("event is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
runtime.EventsEmit(r.wails, fmt.Sprintf("ChatbotRuleActive-%d", *r.rule.ID), true)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil
|
|
|
|
case chat := <-r.chatCh:
|
|
|
|
err := r.handleEventFromLiveStreamOnSub(chat)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error handling event: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Runner) handleEventFromLiveStreamOnSub(chat events.Chat) error {
|
|
|
|
if r.rule.Parameters == nil || r.rule.Parameters.Trigger == nil || r.rule.Parameters.Trigger.OnEvent == nil || r.rule.Parameters.Trigger.OnEvent.FromLiveStream == nil || r.rule.Parameters.Trigger.OnEvent.FromLiveStream.OnSub == nil {
|
|
|
|
return fmt.Errorf("invalid rule")
|
|
|
|
}
|
|
|
|
|
|
|
|
displayName := chat.Message.Username
|
|
|
|
if chat.Message.ChannelName != "" {
|
|
|
|
displayName = chat.Message.ChannelName
|
2024-05-22 16:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fields := &chatFields{
|
2024-05-28 20:44:31 +00:00
|
|
|
ChannelName: chat.Message.ChannelName,
|
2024-05-22 16:51:46 +00:00
|
|
|
DisplayName: displayName,
|
2024-05-28 20:44:31 +00:00
|
|
|
Username: chat.Message.Username,
|
|
|
|
Rant: chat.Message.Rant / 100,
|
2024-05-22 16:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err := r.chat(fields)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error sending chat: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Runner) runOnTimer(ctx context.Context) error {
|
|
|
|
if r.rule.ID == nil || r.rule.Parameters == nil || r.rule.Parameters.Trigger == nil {
|
|
|
|
return fmt.Errorf("invalid rule")
|
|
|
|
}
|
|
|
|
if r.rule.Parameters.Trigger.OnTimer == nil {
|
|
|
|
return fmt.Errorf("timer is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
runtime.EventsEmit(r.wails, fmt.Sprintf("ChatbotRuleActive-%d", *r.rule.ID), true)
|
|
|
|
err := r.chat(nil)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error sending chat: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
trigger := time.NewTimer(*r.rule.Parameters.Trigger.OnTimer * time.Second)
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
trigger.Stop()
|
|
|
|
return nil
|
|
|
|
case <-trigger.C:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Runner) stop() {
|
|
|
|
r.cancelMu.Lock()
|
|
|
|
if r.cancel != nil {
|
|
|
|
r.cancel()
|
|
|
|
}
|
|
|
|
r.cancelMu.Unlock()
|
|
|
|
}
|