split channel management in separate object

Signed-off-by: Luca Bigliardi <shammash@google.com>
This commit is contained in:
Luca Bigliardi 2021-03-26 22:53:46 +01:00
parent cba9deb152
commit 7c999191d7
2 changed files with 119 additions and 65 deletions

75
irc.go
View File

@ -57,11 +57,6 @@ func loggerHandler(_ *irc.Conn, line *irc.Line) {
log.Printf("Received: '%s'", line.Raw) log.Printf("Received: '%s'", line.Raw)
} }
type ChannelState struct {
Channel IRCChannel
BackoffCounter Delayer
}
type IRCNotifier struct { type IRCNotifier struct {
// Nick stores the nickname specified in the config, because irc.Client // Nick stores the nickname specified in the config, because irc.Client
// might change its copy. // might change its copy.
@ -82,8 +77,7 @@ type IRCNotifier struct {
sessionUpSignal chan bool sessionUpSignal chan bool
sessionDownSignal chan bool sessionDownSignal chan bool
PreJoinChannels []IRCChannel channelReconciler *ChannelReconciler
JoinedChannels map[string]ChannelState
UsePrivmsg bool UsePrivmsg bool
@ -108,21 +102,24 @@ func NewIRCNotifier(stopCtx context.Context, stopWg *sync.WaitGroup, config *Con
ircConfig.Timeout = connectionTimeoutSecs * time.Second ircConfig.Timeout = connectionTimeoutSecs * time.Second
ircConfig.NewNick = func(n string) string { return n + "^" } ircConfig.NewNick = func(n string) string { return n + "^" }
client := irc.Client(ircConfig)
backoffCounter := delayerMaker.NewDelayer( backoffCounter := delayerMaker.NewDelayer(
ircConnectMaxBackoffSecs, ircConnectBackoffResetSecs, ircConnectMaxBackoffSecs, ircConnectBackoffResetSecs,
time.Second) time.Second)
channelReconciler := NewChannelReconciler(config, client, delayerMaker)
notifier := &IRCNotifier{ notifier := &IRCNotifier{
Nick: config.IRCNick, Nick: config.IRCNick,
NickPassword: config.IRCNickPass, NickPassword: config.IRCNickPass,
Client: irc.Client(ircConfig), Client: client,
AlertMsgs: alertMsgs, AlertMsgs: alertMsgs,
stopCtx: stopCtx, stopCtx: stopCtx,
stopWg: stopWg, stopWg: stopWg,
sessionUpSignal: make(chan bool), sessionUpSignal: make(chan bool),
sessionDownSignal: make(chan bool), sessionDownSignal: make(chan bool),
PreJoinChannels: config.IRCChannels, channelReconciler: channelReconciler,
JoinedChannels: make(map[string]ChannelState),
UsePrivmsg: config.UsePrivmsg, UsePrivmsg: config.UsePrivmsg,
NickservDelayWait: nickservWaitSecs * time.Second, NickservDelayWait: nickservWaitSecs * time.Second,
BackoffCounter: backoffCounter, BackoffCounter: backoffCounter,
@ -146,63 +143,11 @@ func (n *IRCNotifier) registerHandlers() {
n.sessionDownSignal <- false n.sessionDownSignal <- false
}) })
n.Client.HandleFunc(irc.KICK,
func(_ *irc.Conn, line *irc.Line) {
n.HandleKick(line.Args[1], line.Args[0])
})
for _, event := range []string{irc.NOTICE, "433"} { for _, event := range []string{irc.NOTICE, "433"} {
n.Client.HandleFunc(event, loggerHandler) n.Client.HandleFunc(event, loggerHandler)
} }
} }
func (n *IRCNotifier) HandleKick(nick string, channel string) {
if nick != n.Client.Me().Nick {
// received kick info for somebody else
return
}
state, ok := n.JoinedChannels[channel]
if !ok {
log.Printf("Being kicked out of non-joined channel (%s), ignoring", channel)
return
}
log.Printf("Being kicked out of %s, re-joining", channel)
go func() {
if ok := state.BackoffCounter.DelayContext(n.stopCtx); !ok {
return
}
n.Client.Join(state.Channel.Name, state.Channel.Password)
}()
}
func (n *IRCNotifier) CleanupChannels() {
log.Printf("Deregistering all channels.")
n.JoinedChannels = make(map[string]ChannelState)
}
func (n *IRCNotifier) JoinChannel(channel *IRCChannel) {
if _, joined := n.JoinedChannels[channel.Name]; joined {
return
}
log.Printf("Joining %s", channel.Name)
n.Client.Join(channel.Name, channel.Password)
bm := BackoffMaker{}
state := ChannelState{
Channel: *channel,
BackoffCounter: bm.NewDelayer(
ircConnectMaxBackoffSecs, ircConnectBackoffResetSecs,
time.Second),
}
n.JoinedChannels[channel.Name] = state
}
func (n *IRCNotifier) JoinChannels() {
for _, channel := range n.PreJoinChannels {
n.JoinChannel(&channel)
}
}
func (n *IRCNotifier) MaybeIdentifyNick() { func (n *IRCNotifier) MaybeIdentifyNick() {
if n.NickPassword == "" { if n.NickPassword == "" {
return return
@ -233,7 +178,7 @@ func (n *IRCNotifier) MaybeSendAlertMsg(alertMsg *AlertMsg) {
ircSendMsgErrors.WithLabelValues(alertMsg.Channel, "not_connected").Inc() ircSendMsgErrors.WithLabelValues(alertMsg.Channel, "not_connected").Inc()
return return
} }
n.JoinChannel(&IRCChannel{Name: alertMsg.Channel}) n.channelReconciler.JoinChannel(&IRCChannel{Name: alertMsg.Channel})
if n.UsePrivmsg { if n.UsePrivmsg {
n.Client.Privmsg(alertMsg.Channel, alertMsg.Alert) n.Client.Privmsg(alertMsg.Channel, alertMsg.Alert)
@ -265,7 +210,7 @@ func (n *IRCNotifier) ConnectedPhase() {
n.MaybeSendAlertMsg(&alertMsg) n.MaybeSendAlertMsg(&alertMsg)
case <-n.sessionDownSignal: case <-n.sessionDownSignal:
n.sessionUp = false n.sessionUp = false
n.CleanupChannels() n.channelReconciler.CleanupChannels()
n.Client.Quit("see ya") n.Client.Quit("see ya")
ircConnectedGauge.Set(0) ircConnectedGauge.Set(0)
case <-n.stopCtx.Done(): case <-n.stopCtx.Done():
@ -289,7 +234,7 @@ func (n *IRCNotifier) SetupPhase() {
case <-n.sessionUpSignal: case <-n.sessionUpSignal:
n.sessionUp = true n.sessionUp = true
n.MaybeIdentifyNick() n.MaybeIdentifyNick()
n.JoinChannels() n.channelReconciler.JoinChannels()
ircConnectedGauge.Set(1) ircConnectedGauge.Set(1)
case <-n.sessionDownSignal: case <-n.sessionDownSignal:
log.Printf("Receiving a session down before the session is up, this is odd") log.Printf("Receiving a session down before the session is up, this is odd")

109
reconciler.go Normal file
View File

@ -0,0 +1,109 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"log"
"sync"
"time"
irc "github.com/fluffle/goirc/client"
)
type channelState struct {
Channel IRCChannel
BackoffCounter Delayer
}
type ChannelReconciler struct {
preJoinChannels []IRCChannel
client *irc.Conn
delayerMaker DelayerMaker
channels map[string]*channelState
stopCtx context.Context
stopCtxCancel context.CancelFunc
stopWg sync.WaitGroup
mu sync.Mutex
}
func NewChannelReconciler(config *Config, client *irc.Conn, delayerMaker DelayerMaker) *ChannelReconciler {
reconciler := &ChannelReconciler{
preJoinChannels: config.IRCChannels,
client: client,
delayerMaker: delayerMaker,
channels: make(map[string]*channelState),
}
reconciler.registerHandlers()
return reconciler
}
func (r *ChannelReconciler) registerHandlers() {
r.client.HandleFunc(irc.KICK,
func(_ *irc.Conn, line *irc.Line) {
r.HandleKick(line.Args[1], line.Args[0])
})
}
func (r *ChannelReconciler) HandleKick(nick string, channel string) {
if nick != r.client.Me().Nick {
// received kick info for somebody else
return
}
state, ok := r.channels[channel]
if !ok {
log.Printf("Being kicked out of non-joined channel (%s), ignoring", channel)
return
}
log.Printf("Being kicked out of %s, re-joining", channel)
go func() {
if ok := state.BackoffCounter.DelayContext(r.stopCtx); !ok {
return
}
r.client.Join(state.Channel.Name, state.Channel.Password)
}()
}
func (r *ChannelReconciler) CleanupChannels() {
log.Printf("Deregistering all channels.")
r.channels = make(map[string]*channelState)
}
func (r *ChannelReconciler) JoinChannel(channel *IRCChannel) {
if _, joined := r.channels[channel.Name]; joined {
return
}
log.Printf("Joining %s", channel.Name)
r.client.Join(channel.Name, channel.Password)
state := &channelState{
Channel: *channel,
BackoffCounter: r.delayerMaker.NewDelayer(
ircConnectMaxBackoffSecs, ircConnectBackoffResetSecs,
time.Second),
}
r.channels[channel.Name] = state
}
func (r *ChannelReconciler) JoinChannels() {
for _, channel := range r.preJoinChannels {
r.JoinChannel(&channel)
}
}