This adds an -events option to log JOINs, PARTs, etc., as mentioned in [1].
The log formatting in WriteMessage is copied from formatMessage in
soju/msgstore/znclog. It differs only in ZNC 1.9-style "Joins:" lines for
logged-in users [2]. "Joins:" parsing in UnmarshalLine should not be affected.
[1] https://todo.sr.ht/~emersion/chathistorysync/1
[2] https://github.com/znc/znc/pull/1870
---
client.go | 14 ++++++++++++
main.go | 4 ++++
msgstore.go | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/client.go b/client.go
index 318bf0c..2db24bd 100644
--- a/client.go
+++ b/client.go
@@ -45,6 +45,7 @@ type client struct {
*ircConn
Debug bool
+ Events bool
caps struct {
chatHistory bool
@@ -71,6 +72,9 @@ func dialTLS(addr string) (*client, error) {
func (c *client) Register(nick, pass, netid string) {
caps := []string{"draft/chathistory", "sasl", "message-tags", "batch", "echo-message", "server-time", "causal.agency/passive", "soju.im/bouncer-networks"}
+ if c.Events {
+ caps = append(caps, "cap-notify", "draft/event-playback", "account-tag")
+ }
for _, name := range caps {
c.WriteMessage(&irc.Message{
Command: "CAP",
@@ -167,6 +171,16 @@ func (c *client) ReadMessage() (*message, error) {
return nil, fmt.Errorf("server doesn't support SASL authentication")
}
}
+ case "NEW":
+ for _, name := range msg.Params[2:] {
+ switch name {
+ case "draft/event-playback", "message-tags", "account-tag":
+ c.WriteMessage(&irc.Message{
+ Command: "CAP",
+ Params: []string{"REQ", name},
+ })
+ }
+ }
}
case "BATCH":
name := strings.TrimLeft(msg.Params[0], "+-")
diff --git a/main.go b/main.go
index d6afc23..587cefb 100644
--- a/main.go
+++ b/main.go
@@ -41,8 +41,10 @@ type bouncerNetwork struct {
func main() {
var afterStr string
var debug bool
+ var events bool
flag.StringVar(&afterStr, "after", "", "Only fetch logs after the specified date (format: YYYY-MM-DD)")
flag.BoolVar(&debug, "debug", false, "Enable debug logs")
+ flag.BoolVar(&events, "events", false, "Record JOINs, PARTs, etc.")
flag.Usage = func() {
fmt.Fprint(flag.CommandLine.Output(), usage)
flag.PrintDefaults()
@@ -109,6 +111,7 @@ func main() {
defer c.Close()
c.Debug = debug
+ c.Events = events
c.Register(nick, pass, "")
waitRegistered(c)
@@ -125,6 +128,7 @@ func main() {
}
c.Debug = debug
+ c.Events = events
c.Register(nick, pass, network.id)
waitRegistered(c)
diff --git a/msgstore.go b/msgstore.go
index 1b0f09c..6e696d4 100644
--- a/msgstore.go
+++ b/msgstore.go
@@ -78,6 +78,27 @@ func (mw *messageWriter) advance(t time.Time) error {
return nil
}
+// parseCTCPMessage is ParseCTCPMessage from git.sr.ht/~emersion/soju/xirc.
+func parseCTCPMessage(msg *message) (cmd string, params string, ok bool) {
+ if (msg.Command != "PRIVMSG" && msg.Command != "NOTICE") || len(msg.Params) < 2 {
+ return "", "", false
+ }
+ text := msg.Params[1]
+
+ if !strings.HasPrefix(text, "\x01") {
+ return "", "", false
+ }
+ text = strings.Trim(text, "\x01")
+
+ words := strings.SplitN(text, " ", 2)
+ cmd = strings.ToUpper(words[0])
+ if len(words) > 1 {
+ params = words[1]
+ }
+
+ return cmd, params, true
+}
+
func (mw *messageWriter) WriteMessage(msg *message) error {
t := msg.time.Local()
@@ -96,10 +117,49 @@ func (mw *messageWriter) WriteMessage(msg *message) error {
var s string
switch msg.Command {
+ case "NICK":
+ s = fmt.Sprintf("*** %s is now known as %s", msg.Prefix.Name, msg.Params[0])
+ case "JOIN":
+ s = fmt.Sprintf("*** Joins: %s (%s@%s)", msg.Prefix.Name, msg.Prefix.User, msg.Prefix.Host)
+ // ZNC 1.9 appends account: https://github.com/znc/znc/pull/1870.
+ if account, ok := msg.Tags.GetTag("account"); ok {
+ s += " " + account
+ }
+ case "PART":
+ var reason string
+ if len(msg.Params) > 1 {
+ reason = msg.Params[1]
+ }
+ s = fmt.Sprintf("*** Parts: %s (%s@%s) (%s)", msg.Prefix.Name, msg.Prefix.User, msg.Prefix.Host, reason)
+ case "KICK":
+ nick := msg.Params[1]
+ var reason string
+ if len(msg.Params) > 2 {
+ reason = msg.Params[2]
+ }
+ s = fmt.Sprintf("*** %s was kicked by %s (%s)", nick, msg.Prefix.Name, reason)
+ case "QUIT":
+ var reason string
+ if len(msg.Params) > 0 {
+ reason = msg.Params[0]
+ }
+ s = fmt.Sprintf("*** Quits: %s (%s@%s) (%s)", msg.Prefix.Name, msg.Prefix.User, msg.Prefix.Host, reason)
+ case "TOPIC":
+ var topic string
+ if len(msg.Params) > 1 {
+ topic = msg.Params[1]
+ }
+ s = fmt.Sprintf("*** %s changes topic to '%s'", msg.Prefix.Name, topic)
+ case "MODE":
+ s = fmt.Sprintf("*** %s sets mode: %s", msg.Prefix.Name, strings.Join(msg.Params[1:], " "))
case "NOTICE":
s = fmt.Sprintf("-%s- %s", msg.Prefix.Name, msg.Params[1])
case "PRIVMSG":
- s = fmt.Sprintf("<%s> %s", msg.Prefix.Name, msg.Params[1])
+ if cmd, params, ok := parseCTCPMessage(msg); ok && cmd == "ACTION" {
+ s = fmt.Sprintf("* %s %s", msg.Prefix.Name, params)
+ } else {
+ s = fmt.Sprintf("<%s> %s", msg.Prefix.Name, msg.Params[1])
+ }
}
if s == "" {
return nil
--
2.45.2