diff options
-rw-r--r-- | config.go | 11 | ||||
-rw-r--r-- | git_hooks_handle_linux.go | 7 | ||||
-rw-r--r-- | git_hooks_handle_other.go | 7 | ||||
-rw-r--r-- | internal/irc/bot.go (renamed from irc.go) | 80 | ||||
-rw-r--r-- | internal/misc/back.go | 6 | ||||
-rw-r--r-- | server.go | 8 |
6 files changed, 66 insertions, 53 deletions
@@ -10,6 +10,7 @@ import ( "os" "go.lindenii.runxiyu.org/forge/internal/database" + "go.lindenii.runxiyu.org/forge/internal/irc" "go.lindenii.runxiyu.org/forge/internal/scfg" ) @@ -46,15 +47,7 @@ type Config struct { Key string `scfg:"key"` Root string `scfg:"root"` } `scfg:"ssh"` - IRC struct { - Net string `scfg:"net"` - Addr string `scfg:"addr"` - TLS bool `scfg:"tls"` - SendQ uint `scfg:"sendq"` - Nick string `scfg:"nick"` - User string `scfg:"user"` - Gecos string `scfg:"gecos"` - } `scfg:"irc"` + IRC irc.Config `scfg:"irc"` General struct { Title string `scfg:"title"` } `scfg:"general"` diff --git a/git_hooks_handle_linux.go b/git_hooks_handle_linux.go index aba47d3..fa4f86f 100644 --- a/git_hooks_handle_linux.go +++ b/git_hooks_handle_linux.go @@ -12,7 +12,6 @@ import ( "errors" "fmt" "io" - "log/slog" "net" "os" "path/filepath" @@ -250,11 +249,7 @@ func (s *Server) hooksHandler(conn net.Conn) { mergeRequestWebURL := fmt.Sprintf("%s/contrib/%d/", s.genHTTPRemoteURL(packPass.groupPath, packPass.repoName), newMRLocalID) fmt.Fprintln(sshStderr, ansiec.Blue+"Created merge request at", mergeRequestWebURL+ansiec.Reset) - select { - case s.ircSendBuffered <- "PRIVMSG #chat :New merge request at " + mergeRequestWebURL: - default: - slog.Error("IRC SendQ exceeded") - } + s.ircBot.Send("PRIVMSG #chat :New merge request at " + mergeRequestWebURL) } else { // Existing contrib branch var existingMRUser int var isAncestor bool diff --git a/git_hooks_handle_other.go b/git_hooks_handle_other.go index d4a6aea..da40bb6 100644 --- a/git_hooks_handle_other.go +++ b/git_hooks_handle_other.go @@ -12,7 +12,6 @@ import ( "errors" "fmt" "io" - "log/slog" "net" "path/filepath" "strconv" @@ -226,11 +225,7 @@ func (s *Server) hooksHandler(conn net.Conn) { mergeRequestWebURL := fmt.Sprintf("%s/contrib/%d/", s.genHTTPRemoteURL(packPass.groupPath, packPass.repoName), newMRLocalID) fmt.Fprintln(sshStderr, ansiec.Blue+"Created merge request at", mergeRequestWebURL+ansiec.Reset) - select { - case s.ircSendBuffered <- "PRIVMSG #chat :New merge request at " + mergeRequestWebURL: - default: - slog.Error("IRC SendQ exceeded") - } + s.ircBot.Send("PRIVMSG #chat :New merge request at " + mergeRequestWebURL) } else { // Existing contrib branch var existingMRUser int var isAncestor bool diff --git a/irc.go b/internal/irc/bot.go index 4f4c5c9..6f7ba71 100644 --- a/irc.go +++ b/internal/irc/bot.go @@ -1,28 +1,44 @@ -// SPDX-License-Identifier: AGPL-3.0-only -// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org> - -package forge +package irc import ( "crypto/tls" "log/slog" "net" + "go.lindenii.runxiyu.org/forge/internal/misc" irc "go.lindenii.runxiyu.org/lindenii-irc" ) -type errorBack[T any] struct { - content T - errorBack chan error +type Config struct { + Net string `scfg:"net"` + Addr string `scfg:"addr"` + TLS bool `scfg:"tls"` + SendQ uint `scfg:"sendq"` + Nick string `scfg:"nick"` + User string `scfg:"user"` + Gecos string `scfg:"gecos"` +} + +type Bot struct { + config *Config + ircSendBuffered chan string + ircSendDirectChan chan misc.ErrorBack[string] } -func (s *Server) ircBotSession() error { +func NewBot(c *Config) (b *Bot) { + b = &Bot{ + config: c, + } + return +} + +func (b *Bot) Connect() error { var err error var underlyingConn net.Conn - if s.config.IRC.TLS { - underlyingConn, err = tls.Dial(s.config.IRC.Net, s.config.IRC.Addr, nil) + if b.config.TLS { + underlyingConn, err = tls.Dial(b.config.Net, b.config.Addr, nil) } else { - underlyingConn, err = net.Dial(s.config.IRC.Net, s.config.IRC.Addr) + underlyingConn, err = net.Dial(b.config.Net, b.config.Addr) } if err != nil { return err @@ -36,11 +52,11 @@ func (s *Server) ircBotSession() error { return conn.WriteString(s + "\r\n") } - _, err = logAndWriteLn("NICK " + s.config.IRC.Nick) + _, err = logAndWriteLn("NICK " + b.config.Nick) if err != nil { return err } - _, err = logAndWriteLn("USER " + s.config.IRC.User + " 0 * :" + s.config.IRC.Gecos) + _, err = logAndWriteLn("USER " + b.config.User + " 0 * :" + b.config.Gecos) if err != nil { return err } @@ -81,7 +97,7 @@ func (s *Server) ircBotSession() error { if !ok { slog.Error("unable to convert source of JOIN to client") } - if c.Nick != s.config.IRC.Nick { + if c.Nick != b.config.Nick { continue } default: @@ -93,20 +109,20 @@ func (s *Server) ircBotSession() error { select { case err = <-readLoopError: return err - case line := <-s.ircSendBuffered: + case line := <-b.ircSendBuffered: _, err = logAndWriteLn(line) if err != nil { select { - case s.ircSendBuffered <- line: + case b.ircSendBuffered <- line: default: slog.Error("unable to requeue message", "line", line) } writeLoopAbort <- struct{}{} return err } - case lineErrorBack := <-s.ircSendDirectChan: - _, err = logAndWriteLn(lineErrorBack.content) - lineErrorBack.errorBack <- err + case lineErrorBack := <-b.ircSendDirectChan: + _, err = logAndWriteLn(lineErrorBack.Content) + lineErrorBack.ErrorChan <- err if err != nil { writeLoopAbort <- struct{}{} return err @@ -115,26 +131,34 @@ func (s *Server) ircBotSession() error { } } -// ircSendDirect sends an IRC message directly to the connection and bypasses +// SendDirect sends an IRC message directly to the connection and bypasses // the buffering system. -func (s *Server) ircSendDirect(line string) error { +func (b *Bot) SendDirect(line string) error { ech := make(chan error, 1) - s.ircSendDirectChan <- errorBack[string]{ - content: line, - errorBack: ech, + b.ircSendDirectChan <- misc.ErrorBack[string]{ + Content: line, + ErrorChan: ech, } return <-ech } +func (b *Bot) Send(line string) { + select { + case b.ircSendBuffered <- line: + default: + slog.Error("irc sendq full", "line", line) + } +} + // TODO: Delay and warnings? -func (s *Server) ircBotLoop() { - s.ircSendBuffered = make(chan string, s.config.IRC.SendQ) - s.ircSendDirectChan = make(chan errorBack[string]) +func (b *Bot) ConnectLoop() { + b.ircSendBuffered = make(chan string, b.config.SendQ) + b.ircSendDirectChan = make(chan misc.ErrorBack[string]) for { - err := s.ircBotSession() + err := b.Connect() slog.Error("irc session error", "error", err) } } diff --git a/internal/misc/back.go b/internal/misc/back.go new file mode 100644 index 0000000..6b57e30 --- /dev/null +++ b/internal/misc/back.go @@ -0,0 +1,6 @@ +package misc + +type ErrorBack[T any] struct { + Content T + ErrorChan chan error +} @@ -18,6 +18,7 @@ import ( "time" "go.lindenii.runxiyu.org/forge/internal/database" + "go.lindenii.runxiyu.org/forge/internal/irc" "go.lindenii.runxiyu.org/forge/internal/misc" "go.lindenii.runxiyu.org/lindenii-common/cmap" goSSH "golang.org/x/crypto/ssh" @@ -31,9 +32,6 @@ type Server struct { sourceHandler http.Handler staticHandler http.Handler - ircSendBuffered chan string - ircSendDirectChan chan errorBack[string] - // globalData is passed as "global" when rendering HTML templates. globalData map[string]any @@ -45,6 +43,8 @@ type Server struct { packPasses cmap.Map[string, packPass] templates *template.Template + + ircBot *irc.Bot } func (s *Server) Setup() { @@ -192,7 +192,7 @@ func (s *Server) Run() { } // IRC bot - go s.ircBotLoop() + go s.ircBot.ConnectLoop() select {} } |