mirror of https://github.com/velour/catbase.git
178 lines
3.3 KiB
Go
178 lines
3.3 KiB
Go
package twitch
|
|
|
|
import (
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/velour/catbase/config"
|
|
"github.com/velour/velour/irc"
|
|
"io"
|
|
"time"
|
|
)
|
|
|
|
var throttle <-chan time.Time
|
|
|
|
type eventFunc func(channel, who, body string)
|
|
|
|
type IRC struct {
|
|
t *Twitch
|
|
c *config.Config
|
|
client *irc.Client
|
|
event eventFunc
|
|
quit chan bool
|
|
}
|
|
|
|
func (t *Twitch) ConnectIRC(server, user, pass string, handler eventFunc, disconnect func()) (*IRC, error) {
|
|
log.Debug().Msgf("Connecting to %s, %s:%s", server, user, pass)
|
|
i := &IRC{
|
|
t: t,
|
|
c: t.c,
|
|
event: handler}
|
|
wait := make(chan bool)
|
|
go i.serve(server, user, pass, wait, disconnect)
|
|
<-wait
|
|
return i, nil
|
|
}
|
|
|
|
func (i *IRC) Join(channel string) error {
|
|
i.client.Out <- irc.Msg{Cmd: irc.JOIN, Args: []string{channel}}
|
|
return nil
|
|
}
|
|
|
|
func (i *IRC) Say(channel, body string) error {
|
|
if _, err := i.sendMessage(channel, body); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (i *IRC) serve(server, user, pass string, wait chan bool, disconnect func()) {
|
|
if i.event == nil {
|
|
log.Error().Msgf("Missing event handler")
|
|
wait <- true
|
|
return
|
|
}
|
|
|
|
var err error
|
|
i.client, err = irc.DialSSL(server, user, user, pass, true)
|
|
if err != nil {
|
|
log.Error().
|
|
Err(err).
|
|
Strs("args", []string{server, user, pass}).
|
|
Msgf("Could not connect")
|
|
wait <- true
|
|
return
|
|
}
|
|
|
|
i.client.Out <- irc.Msg{Cmd: "CAP REQ", Args: []string{":twitch.tv/membership"}}
|
|
|
|
i.quit = make(chan bool)
|
|
go i.handleConnection()
|
|
wait <- true
|
|
<-i.quit
|
|
disconnect()
|
|
}
|
|
|
|
func (i *IRC) handleMsg(msg irc.Msg) {
|
|
switch msg.Cmd {
|
|
case irc.ERROR:
|
|
log.Info().Msgf("Received error: " + msg.Raw)
|
|
|
|
case irc.PING:
|
|
i.client.Out <- irc.Msg{Cmd: irc.PONG}
|
|
|
|
case irc.PONG:
|
|
// OK, ignore
|
|
|
|
case irc.KICK:
|
|
fallthrough
|
|
|
|
case irc.TOPIC:
|
|
fallthrough
|
|
|
|
case irc.NOTICE:
|
|
fallthrough
|
|
|
|
case irc.PRIVMSG:
|
|
if len(msg.Args) < 2 {
|
|
break
|
|
}
|
|
i.event(msg.Args[0], msg.Origin, msg.Args[1])
|
|
|
|
case irc.QUIT:
|
|
log.Debug().
|
|
Interface("msg", msg).
|
|
Msgf("QUIT")
|
|
i.quit <- true
|
|
|
|
default:
|
|
log.Debug().
|
|
Interface("msg", msg).
|
|
Msgf("IRC EVENT")
|
|
}
|
|
}
|
|
|
|
func (i *IRC) sendMessage(channel, message string, args ...any) (string, error) {
|
|
for len(message) > 0 {
|
|
m := irc.Msg{
|
|
Cmd: "PRIVMSG",
|
|
Args: []string{channel, message},
|
|
}
|
|
_, err := m.RawString()
|
|
if err != nil {
|
|
mtl := err.(irc.MsgTooLong)
|
|
m.Args[1] = message[:mtl.NTrunc]
|
|
message = message[mtl.NTrunc:]
|
|
} else {
|
|
message = ""
|
|
}
|
|
|
|
if throttle == nil {
|
|
ratePerSec := i.c.GetInt("RatePerSec", 5)
|
|
throttle = time.Tick(time.Second / time.Duration(ratePerSec))
|
|
}
|
|
|
|
<-throttle
|
|
|
|
i.client.Out <- m
|
|
}
|
|
return "NO_IRC_IDENTIFIERS", nil
|
|
}
|
|
|
|
func (i *IRC) handleConnection() {
|
|
pingTime := time.Duration(i.c.GetInt("twitch.pingtime", 60)) * time.Second
|
|
t := time.NewTimer(pingTime)
|
|
|
|
defer func() {
|
|
t.Stop()
|
|
close(i.client.Out)
|
|
for err := range i.client.Errors {
|
|
if err != io.EOF {
|
|
log.Error().Err(err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case msg, ok := <-i.client.In:
|
|
if !ok { // disconnect
|
|
i.quit <- true
|
|
return
|
|
}
|
|
t.Stop()
|
|
t = time.NewTimer(pingTime)
|
|
i.handleMsg(msg)
|
|
|
|
case <-t.C:
|
|
i.client.Out <- irc.Msg{Cmd: irc.PING, Args: []string{i.client.Server}}
|
|
t = time.NewTimer(pingTime)
|
|
|
|
case err, ok := <-i.client.Errors:
|
|
if ok && err != io.EOF {
|
|
log.Error().Err(err)
|
|
i.quit <- true
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|