feat: show irc network status in settings list

This commit is contained in:
Ludvig Lundgren 2022-01-08 21:18:38 +01:00
parent dcd1d458cf
commit f103dff221
6 changed files with 221 additions and 11 deletions

View file

@ -27,10 +27,13 @@ type channelHealth struct {
name string
monitoring bool
monitoringSince time.Time
lastPing time.Time
lastAnnounce time.Time
}
func (h *channelHealth) SetLastAnnounce() {
h.lastAnnounce = time.Now()
}
type Handler struct {
network *domain.IrcNetwork
filterService filter.Service
@ -44,11 +47,15 @@ type Handler struct {
stopped chan struct{}
cancel context.CancelFunc
lastPing time.Time
lastAnnounce time.Time
lastPing time.Time
lastAnnounce time.Time
connected bool
connectedSince time.Time
// tODO disconnectedTime
validAnnouncers map[string]struct{}
channels map[string]channelHealth
validChannels map[string]struct{}
channelHealth map[string]*channelHealth
}
func NewHandler(network domain.IrcNetwork, filterService filter.Service, releaseService release.Service, definitions []domain.IndexerDefinition) *Handler {
@ -63,6 +70,8 @@ func NewHandler(network domain.IrcNetwork, filterService filter.Service, release
definitions: definitions,
announceProcessors: map[string]announce.Processor{},
validAnnouncers: map[string]struct{}{},
validChannels: map[string]struct{}{},
channelHealth: map[string]*channelHealth{},
}
// Networks can be shared by multiple indexers but channels are unique
@ -73,12 +82,22 @@ func NewHandler(network domain.IrcNetwork, filterService filter.Service, release
channel = strings.ToLower(channel)
h.announceProcessors[channel] = announce.NewAnnounceProcessor(definition, filterService, releaseService)
h.channelHealth[channel] = &channelHealth{
name: channel,
monitoring: false,
}
}
// create map of valid announcers
for _, announcer := range definition.IRC.Announcers {
h.validAnnouncers[announcer] = struct{}{}
}
// create map of valid channels
for _, ch := range definition.IRC.Channels {
h.validChannels[ch] = struct{}{}
}
}
return h
@ -216,10 +235,19 @@ func (s *Handler) Run() error {
s.client = client
// set connected since now
s.connectedSince = time.Now()
s.connected = true
// Connect
err = client.RunContext(ctx)
if err != nil {
log.Error().Err(err).Msgf("could not connect to %v", addr)
// set connected false if we loose connection or stop
s.connectedSince = time.Time{}
s.connected = false
return err
}
@ -279,7 +307,7 @@ func (s *Handler) onConnect(channels []domain.IrcChannel) error {
if s.network.NickServ.Password != "" {
err := s.handleNickServPRIVMSG(s.network.NickServ.Account, s.network.NickServ.Password)
if err != nil {
log.Error().Err(err).Msgf("error nickserv: %v", s.network.Name)
log.Error().Stack().Err(err).Msgf("error nickserv: %v", s.network.Name)
return err
}
identified = true
@ -291,7 +319,7 @@ func (s *Handler) onConnect(channels []domain.IrcChannel) error {
err := s.handleInvitePRIVMSG(s.network.InviteCommand)
if err != nil {
log.Error().Err(err).Msgf("error sending connect command %v to network: %v", s.network.InviteCommand, s.network.Name)
log.Error().Stack().Err(err).Msgf("error sending connect command %v to network: %v", s.network.InviteCommand, s.network.Name)
return err
}
@ -302,7 +330,7 @@ func (s *Handler) onConnect(channels []domain.IrcChannel) error {
for _, channel := range channels {
err := s.HandleJoinChannel(channel.Name, channel.Password)
if err != nil {
log.Error().Err(err)
log.Error().Stack().Err(err)
return err
}
}
@ -322,6 +350,12 @@ func (s *Handler) onMessage(msg *irc.Message) error {
message := msg.Trailing()
// TODO add network
// check if message is from a valid channel, if not return
validChannel := s.isValidChannel(*channel)
if !validChannel {
return nil
}
// check if message is from announce bot, if not return
validAnnouncer := s.isValidAnnouncer(*announcer)
if !validAnnouncer {
@ -358,6 +392,13 @@ func (s *Handler) sendToAnnounceProcessor(channel string, msg string) error {
return err
}
v, ok := s.channelHealth[channel]
if !ok {
return nil
}
v.SetLastAnnounce()
return nil
}
@ -392,11 +433,16 @@ func (s *Handler) HandleJoinChannel(channel string, password string) error {
err := s.client.Write(m.String())
if err != nil {
log.Error().Err(err).Msgf("error handling join: %v", m.String())
log.Error().Stack().Err(err).Msgf("error handling join: %v", m.String())
return err
}
//log.Info().Msgf("Monitoring channel %v %s", s.network.Name, channel)
// only set values if channel is found in map
v, ok := s.channelHealth[channel]
if ok {
v.monitoring = true
v.monitoringSince = time.Now()
}
return nil
}
@ -570,6 +616,15 @@ func (s *Handler) isValidAnnouncer(nick string) bool {
return true
}
func (s *Handler) isValidChannel(channel string) bool {
_, ok := s.validChannels[channel]
if !ok {
return false
}
return true
}
func (s *Handler) setLastAnnounce() {
s.lastAnnounce = time.Now()
}
@ -578,6 +633,14 @@ func (s *Handler) GetLastAnnounce() time.Time {
return s.lastAnnounce
}
//func (s *Handler) setConnectedSince() {
// s.network.ConnectedSince = time.Now()
//}
//
//func (s *Handler) GetConnectedSince() time.Time {
// return s.lastAnnounce
//}
func (s *Handler) setLastPing() {
s.lastPing = time.Now()
}
@ -586,6 +649,10 @@ func (s *Handler) GetLastPing() time.Time {
return s.lastPing
}
func (s *Handler) GetChannelHealth() map[string]*channelHealth {
return s.channelHealth
}
// irc line can contain lots of extra stuff like color so lets clean that
func cleanMessage(message string) string {
var regexMessageClean = `\x0f|\x1f|\x02|\x03(?:[\d]{1,2}(?:,[\d]{1,2})?)?`

View file

@ -3,6 +3,7 @@ package irc
import (
"context"
"fmt"
"strings"
"sync"
"github.com/autobrr/autobrr/internal/domain"
@ -18,6 +19,7 @@ type Service interface {
StopHandlers()
StopNetwork(name string) error
ListNetworks(ctx context.Context) ([]domain.IrcNetwork, error)
GetNetworksWithHealth(ctx context.Context) ([]domain.IrcNetworkWithHealth, error)
GetNetworkByID(id int64) (*domain.IrcNetwork, error)
DeleteNetwork(ctx context.Context, id int64) error
StoreNetwork(ctx context.Context, network *domain.IrcNetwork) error
@ -349,12 +351,86 @@ func (s *service) ListNetworks(ctx context.Context) ([]domain.IrcNetwork, error)
}
n.Channels = append(n.Channels, channels...)
// TODO get handler channelHealth
ret = append(ret, n)
}
return ret, nil
}
func (s *service) GetNetworksWithHealth(ctx context.Context) ([]domain.IrcNetworkWithHealth, error) {
networks, err := s.repo.ListNetworks(ctx)
if err != nil {
log.Error().Err(err).Msgf("failed to list networks: %v", err)
return nil, err
}
var ret []domain.IrcNetworkWithHealth
for _, n := range networks {
netw := domain.IrcNetworkWithHealth{
ID: n.ID,
Name: n.Name,
Enabled: n.Enabled,
Server: n.Server,
Port: n.Port,
TLS: n.TLS,
Pass: n.Pass,
InviteCommand: n.InviteCommand,
NickServ: n.NickServ,
Connected: false,
}
handler, ok := s.handlers[n.Server]
if ok {
// only set connected and connected since if we have an active handler and connection
if handler.conn != nil {
netw.Connected = handler.connected
netw.ConnectedSince = handler.connectedSince
}
}
channels, err := s.repo.ListChannels(n.ID)
if err != nil {
log.Error().Msgf("failed to list channels for network %q: %v", n.Server, err)
return nil, err
}
// combine from repo and handler
for _, channel := range channels {
ch := domain.ChannelWithHealth{
ID: channel.ID,
Enabled: channel.Enabled,
Name: channel.Name,
Password: channel.Password,
Detached: channel.Detached,
//Monitoring: false,
//MonitoringSince: time.Time{},
//LastAnnounce: time.Time{},
}
// only check if we have a handler
if handler != nil {
name := strings.ToLower(channel.Name)
chan1, ok := handler.channelHealth[name]
if ok {
ch.Monitoring = chan1.monitoring
ch.MonitoringSince = chan1.monitoringSince
ch.LastAnnounce = chan1.lastAnnounce
}
}
netw.Channels = append(netw.Channels, ch)
}
ret = append(ret, netw)
}
return ret, nil
}
func (s *service) DeleteNetwork(ctx context.Context, id int64) error {
network, err := s.GetNetworkByID(id)
if err != nil {