aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRunxi Yu <me@runxiyu.org>2025-04-06 01:24:58 +0800
committerRunxi Yu <me@runxiyu.org>2025-04-06 01:24:58 +0800
commit72d0c8d9bce125be1b851b142b0f41242d496f6f (patch)
tree18c5b2e6cde83497358b0245fb75b6e2d5ea6615
parentHooks, git2d: Simplify deployment logic (diff)
downloadforge-72d0c8d9bce125be1b851b142b0f41242d496f6f.tar.gz
forge-72d0c8d9bce125be1b851b142b0f41242d496f6f.tar.zst
forge-72d0c8d9bce125be1b851b142b0f41242d496f6f.zip
irc: Factor the IRC stuff into its own package
-rw-r--r--config.go11
-rw-r--r--git_hooks_handle_linux.go7
-rw-r--r--git_hooks_handle_other.go7
-rw-r--r--internal/irc/bot.go (renamed from irc.go)80
-rw-r--r--internal/misc/back.go6
-rw-r--r--server.go8
6 files changed, 66 insertions, 53 deletions
diff --git a/config.go b/config.go
index b8e5e28..d845ce8 100644
--- a/config.go
+++ b/config.go
@@ -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
+}
diff --git a/server.go b/server.go
index 3e02586..8c14e3e 100644
--- a/server.go
+++ b/server.go
@@ -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 {}
}