~emersion/public-inbox

Add support for draft/event-playback v1 PROPOSED

F. Jason Park: 1
 Add support for draft/event-playback

 3 files changed, 79 insertions(+), 1 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~emersion/public-inbox/patches/53956/mbox | git am -3
Learn more about email & git

[PATCH] Add support for draft/event-playback Export this patch

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