diff options
-rw-r--r-- | config.go | 10 | ||||
-rw-r--r-- | forge.scfg | 10 | ||||
-rw-r--r-- | irc.go | 111 |
3 files changed, 111 insertions, 20 deletions
@@ -36,9 +36,13 @@ var config struct { Root string `scfg:"root"` } `scfg:"ssh"` IRC struct { - Net string `scfg:"net"` - Addr string `scfg:"addr"` - TLS bool `scfg:"tls"` + 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"` General struct { Title string `scfg:"title"` @@ -15,6 +15,16 @@ http { root https://forge.example.org } +irc { + tls true + net tcp + addr irc.runxiyu.org:6697 + sendq 6000 + nick forge-test + user forge + gecos "Lindenii Forge Test" +} + git { repo_dir /var/lib/lindenii/forge/repos } @@ -4,9 +4,20 @@ import ( "crypto/tls" "net" + "go.lindenii.runxiyu.org/lindenii-common/clog" "go.lindenii.runxiyu.org/lindenii-irc" ) +var ( + ircSendBuffered chan string + ircSendDirectChan chan errorBack[string] +) + +type errorBack[T any] struct { + content T + errorBack chan error +} + func ircBotSession() error { var err error var underlyingConn net.Conn @@ -16,31 +27,97 @@ func ircBotSession() error { underlyingConn, err = net.Dial(config.IRC.Net, config.IRC.Addr) } if err != nil { - return (err) + return err } + defer underlyingConn.Close() + conn := irc.NewConn(underlyingConn) - conn.WriteString("NICK forge\r\nUSER forge 0 * :Forge\r\n") - for { - msg, err := conn.ReadMessage() - if err != nil { - return (err) + conn.WriteString( + "NICK " + config.IRC.Nick + "\r\n" + + "USER " + config.IRC.User + " 0 * :" + config.IRC.Gecos + "\r\n", + ) + + readLoopError := make(chan error) + writeLoopAbort := make(chan struct{}) + go func() { + for { + select { + case <-writeLoopAbort: + return + default: + } + msg, err := conn.ReadMessage() + if err != nil { + readLoopError <- err + return + } + switch msg.Command { + case "001": + _, err = conn.WriteString("JOIN #chat\r\n") + if err != nil { + readLoopError <- err + return + } + case "PING": + _, err = conn.WriteString("PONG :" + msg.Args[0] + "\r\n") + if err != nil { + readLoopError <- err + return + } + case "JOIN": + _, err = conn.WriteString("PRIVMSG #chat :test\r\n") + if err != nil { + readLoopError <- err + return + } + default: + } } - switch msg.Command { - case "001": - conn.WriteString("JOIN #chat\r\n") - case "PING": - conn.WriteString("PONG :") - conn.WriteString(msg.Args[0]) - conn.WriteString("\r\n") - case "JOIN": - conn.WriteString("PRIVMSG #chat :test\r\n") - default: + }() + + for { + select { + case err = <-readLoopError: + return err + case s := <-ircSendBuffered: + _, err = conn.WriteString(s) + if err != nil { + select { + case ircSendBuffered <- s: + default: + clog.Error("unable to requeue IRC message: " + s) + } + writeLoopAbort <- struct{}{} + return err + } + case se := <-ircSendDirectChan: + _, err = conn.WriteString(se.content) + se.errorBack <- err + if err != nil { + writeLoopAbort <- struct{}{} + return err + } } } } +func ircSendDirect(s string) error { + ech := make(chan error, 1) + + ircSendDirectChan <- errorBack[string]{ + content: s, + errorBack: ech, + } + + return <-ech +} + func ircBotLoop() { + ircSendBuffered = make(chan string, config.IRC.SendQ) + ircSendDirectChan = make(chan errorBack[string]) + for { - _ = ircBotSession() + err := ircBotSession() + clog.Error("IRC error: " + err.Error()) } } |