mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-12-20 20:01:17 +00:00
Add automated messages (#2)
This commit is contained in:
parent
396a6452d9
commit
95be0de55b
6 changed files with 194 additions and 6 deletions
132
automessage.go
Normal file
132
automessage.go
Normal file
|
@ -0,0 +1,132 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-irc/irc"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/robfig/cron/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var cronParser = cron.NewParser(cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
|
||||
|
||||
type autoMessage struct {
|
||||
Channel string `yaml:"channel"`
|
||||
Message string `yaml:"message"`
|
||||
UseAction bool `yaml:"use_action"`
|
||||
|
||||
Cron string `yaml:"cron"`
|
||||
MessageInterval int64 `yaml:"message_interval"`
|
||||
OnlyOnLive bool `yaml:"only_on_live"`
|
||||
TimeInterval time.Duration `yaml:"time_interval"`
|
||||
|
||||
disabled bool
|
||||
lastMessageSent time.Time
|
||||
linesSinceLastMessage int64
|
||||
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func (a *autoMessage) CanSend() bool {
|
||||
if a.disabled || !a.IsValid() {
|
||||
return false
|
||||
}
|
||||
|
||||
a.lock.RLock()
|
||||
defer a.lock.RUnlock()
|
||||
|
||||
switch {
|
||||
case a.MessageInterval > a.linesSinceLastMessage:
|
||||
// Not enough chatted lines
|
||||
return false
|
||||
|
||||
case a.TimeInterval > 0 && a.lastMessageSent.Add(a.TimeInterval).After(time.Now()):
|
||||
// Simple timer is not yet expired
|
||||
return false
|
||||
|
||||
case a.Cron != "":
|
||||
sched, _ := cronParser.Parse(a.Cron)
|
||||
if sched.Next(a.lastMessageSent).After(time.Now()) {
|
||||
// Cron timer is not yet expired
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if a.OnlyOnLive {
|
||||
streamLive, err := twitch.HasLiveStream(strings.TrimLeft(a.Channel, "#"))
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Unable to determine channel live status")
|
||||
return false
|
||||
}
|
||||
if !streamLive {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *autoMessage) CountMessage(channel string) {
|
||||
if strings.TrimLeft(channel, "#") != strings.TrimLeft(a.Channel, "#") {
|
||||
return
|
||||
}
|
||||
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
|
||||
a.linesSinceLastMessage++
|
||||
}
|
||||
|
||||
func (a *autoMessage) ID() string {
|
||||
sum := sha256.New()
|
||||
|
||||
fmt.Fprintf(sum, "channel:%q", a.Channel)
|
||||
fmt.Fprintf(sum, "message:%q", a.Message)
|
||||
fmt.Fprintf(sum, "action:%v", a.UseAction)
|
||||
|
||||
return fmt.Sprintf("sha256:%x", sum.Sum(nil))
|
||||
}
|
||||
|
||||
func (a *autoMessage) IsValid() bool {
|
||||
if a.Cron != "" {
|
||||
if _, err := cronParser.Parse(a.Cron); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if a.MessageInterval == 0 && a.TimeInterval == 0 && a.Cron == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *autoMessage) Send(c *irc.Client) error {
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
|
||||
msg := a.Message
|
||||
if a.UseAction {
|
||||
msg = fmt.Sprintf("\001ACTION %s\001", msg)
|
||||
}
|
||||
|
||||
if err := c.WriteMessage(&irc.Message{
|
||||
Command: "PRIVMSG",
|
||||
Params: []string{
|
||||
fmt.Sprintf("#%s", strings.TrimLeft(a.Channel, "#")),
|
||||
msg,
|
||||
},
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "sending auto-message")
|
||||
}
|
||||
|
||||
a.lastMessageSent = time.Now()
|
||||
a.linesSinceLastMessage = 0
|
||||
|
||||
return nil
|
||||
}
|
30
config.go
30
config.go
|
@ -17,6 +17,7 @@ import (
|
|||
)
|
||||
|
||||
type configFile struct {
|
||||
AutoMessages []*autoMessage `yaml:"auto_messages"`
|
||||
Channels []string `yaml:"channels"`
|
||||
PermitAllowModerator bool `yaml:"permit_allow_moderator"`
|
||||
PermitTimeout time.Duration `yaml:"permit_timeout"`
|
||||
|
@ -236,6 +237,35 @@ func loadConfig(filename string) error {
|
|||
log.Warn("Loaded config with empty ruleset")
|
||||
}
|
||||
|
||||
for idx, nam := range tmpConfig.AutoMessages {
|
||||
// By default assume last message to be sent now
|
||||
// in order not to spam messages at startup
|
||||
nam.lastMessageSent = time.Now()
|
||||
|
||||
if !nam.IsValid() {
|
||||
log.WithField("index", idx).Warn("Auto-Message configuration is invalid and therefore disabled")
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
// Initial config load, do not update timers
|
||||
continue
|
||||
}
|
||||
|
||||
for _, oam := range config.AutoMessages {
|
||||
if nam.ID() != oam.ID() {
|
||||
continue
|
||||
}
|
||||
|
||||
// We disable the old message as executing it would
|
||||
// mess up the constraints of the new message
|
||||
oam.lock.Lock()
|
||||
oam.disabled = true
|
||||
|
||||
nam.lastMessageSent = oam.lastMessageSent
|
||||
nam.linesSinceLastMessage = oam.linesSinceLastMessage
|
||||
}
|
||||
}
|
||||
|
||||
configLock.Lock()
|
||||
defer configLock.Unlock()
|
||||
|
||||
|
|
1
go.mod
1
go.mod
|
@ -9,6 +9,7 @@ require (
|
|||
github.com/go-irc/irc v2.1.0+incompatible
|
||||
github.com/gologme/log v1.2.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
gopkg.in/fsnotify.v1 v1.4.7
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
|
|
2
go.sum
2
go.sum
|
@ -83,6 +83,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
|
|
9
irc.go
9
irc.go
|
@ -167,6 +167,15 @@ func (i ircHandler) handleTwitchPrivmsg(m *irc.Message) {
|
|||
"trailing": m.Trailing(),
|
||||
}).Trace("Received privmsg")
|
||||
|
||||
if m.User != i.user {
|
||||
// Count messages from other users than self
|
||||
configLock.RLock()
|
||||
for _, am := range config.AutoMessages {
|
||||
am.CountMessage(m.Params[0])
|
||||
}
|
||||
configLock.RUnlock()
|
||||
}
|
||||
|
||||
if strings.HasPrefix(m.Trailing(), "!permit") {
|
||||
i.handlePermit(m)
|
||||
return
|
||||
|
|
14
main.go
14
main.go
|
@ -72,6 +72,7 @@ func main() {
|
|||
var (
|
||||
irc *ircHandler
|
||||
ircDisconnected = make(chan struct{}, 1)
|
||||
autoMessageTicker = time.NewTicker(time.Second)
|
||||
)
|
||||
|
||||
ircDisconnected <- struct{}{}
|
||||
|
@ -110,6 +111,19 @@ func main() {
|
|||
|
||||
log.Info("Config file reloaded")
|
||||
|
||||
case <-autoMessageTicker.C:
|
||||
configLock.RLock()
|
||||
for _, am := range config.AutoMessages {
|
||||
if !am.CanSend() {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := am.Send(irc.c); err != nil {
|
||||
log.WithError(err).Error("Unable to send automated message")
|
||||
}
|
||||
}
|
||||
configLock.RUnlock()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue