From f4e0c51366d9f4776e79ef55762210fcd9554850 Mon Sep 17 00:00:00 2001 From: delthas Date: Thu, 21 May 2020 07:04:34 +0200 Subject: [PATCH] Add support for TAGMSG and client message tags Previously we dropped all TAGMSG as well as any client message tag sent from downstream. This adds support for properly forwarding TAGMSG and client message tags from downstreams and upstreams. TAGMSG messages are intentionally not logged, because they are currently typically used for +typing, which can generate a lot of traffic and is only useful for a few seconds after it is sent. --- downstream.go | 34 ++++++++++++++++++++++++++++++---- irc.go | 10 ++++++++++ upstream.go | 25 +++++++++++++++++++------ 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/downstream.go b/downstream.go index 47f7af4..d80ced5 100644 --- a/downstream.go +++ b/downstream.go @@ -250,6 +250,9 @@ func (dc *downstreamConn) readMessages(ch chan<- event) error { // This can only called from the user goroutine. func (dc *downstreamConn) SendMessage(msg *irc.Message) { if !dc.caps["message-tags"] { + if msg.Command == "TAGMSG" { + return + } msg = msg.Copy() for name := range msg.Tags { supported := false @@ -274,7 +277,7 @@ func (dc *downstreamConn) marshalMessage(msg *irc.Message, net *network) *irc.Me msg.Prefix = dc.marshalUserPrefix(net, msg.Prefix) switch msg.Command { - case "PRIVMSG", "NOTICE": + case "PRIVMSG", "NOTICE", "TAGMSG": msg.Params[0] = dc.marshalEntity(net, msg.Params[0]) case "NICK": // Nick change for another user @@ -1397,6 +1400,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error { if err := parseMessageParams(msg, &targetsStr, &text); err != nil { return err } + tags := copyClientTags(msg.Tags) for _, name := range strings.Split(targetsStr, ",") { if name == serviceNick { @@ -1418,14 +1422,15 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error { unmarshaledText = dc.unmarshalText(uc, text) } uc.SendMessageLabeled(dc.id, &irc.Message{ + Tags: tags, Command: "PRIVMSG", Params: []string{upstreamName, unmarshaledText}, }) + echoTags := tags.Copy() + echoTags["time"] = irc.TagValue(time.Now().UTC().Format(serverTimeLayout)) echoMsg := &irc.Message{ - Tags: irc.Tags{ - "time": irc.TagValue(time.Now().UTC().Format(serverTimeLayout)), - }, + Tags: echoTags, Prefix: &irc.Prefix{ Name: uc.nick, User: uc.username, @@ -1440,6 +1445,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error { if err := parseMessageParams(msg, &targetsStr, &text); err != nil { return err } + tags := copyClientTags(msg.Tags) for _, name := range strings.Split(targetsStr, ",") { uc, upstreamName, err := dc.unmarshalEntity(name) @@ -1452,10 +1458,30 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error { unmarshaledText = dc.unmarshalText(uc, text) } uc.SendMessageLabeled(dc.id, &irc.Message{ + Tags: tags, Command: "NOTICE", Params: []string{upstreamName, unmarshaledText}, }) } + case "TAGMSG": + var targetsStr string + if err := parseMessageParams(msg, &targetsStr); err != nil { + return err + } + tags := copyClientTags(msg.Tags) + + for _, name := range strings.Split(targetsStr, ",") { + uc, upstreamName, err := dc.unmarshalEntity(name) + if err != nil { + return err + } + + uc.SendMessageLabeled(dc.id, &irc.Message{ + Tags: tags, + Command: "TAGMSG", + Params: []string{upstreamName}, + }) + } case "INVITE": var user, channel string if err := parseMessageParams(msg, &user, &channel); err != nil { diff --git a/irc.go b/irc.go index 50a22c1..8fae660 100644 --- a/irc.go +++ b/irc.go @@ -274,6 +274,16 @@ func parseMessageParams(msg *irc.Message, out ...*string) error { return nil } +func copyClientTags(tags irc.Tags) irc.Tags { + t := make(irc.Tags, len(tags)) + for k, v := range tags { + if strings.HasPrefix(k, "+") { + t[k] = v + } + } + return t +} + type batch struct { Type string Params []string diff --git a/upstream.go b/upstream.go index 1ea282f..60f75fc 100644 --- a/upstream.go +++ b/upstream.go @@ -320,14 +320,20 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { Params: msg.Params, }) return nil - case "NOTICE", "PRIVMSG": + case "NOTICE", "PRIVMSG", "TAGMSG": if msg.Prefix == nil { return fmt.Errorf("expected a prefix") } var entity, text string - if err := parseMessageParams(msg, &entity, &text); err != nil { - return err + if msg.Command != "TAGMSG" { + if err := parseMessageParams(msg, &entity, &text); err != nil { + return err + } + } else { + if err := parseMessageParams(msg, &entity); err != nil { + return err + } } if msg.Prefix.Name == serviceNick { @@ -341,7 +347,7 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { if msg.Prefix.User == "" && msg.Prefix.Host == "" { // server message uc.produce("", msg, nil) - } else { // regular user NOTICE or PRIVMSG + } else { // regular user message target := entity if target == uc.nick { target = msg.Prefix.Name @@ -1274,8 +1280,6 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { }) }) } - case "TAGMSG": - // TODO: relay to downstream connections that accept message-tags case "ACK": // Ignore case irc.RPL_NOWAWAY, irc.RPL_UNAWAY: @@ -1487,6 +1491,15 @@ func (uc *upstreamConn) readMessages(ch chan<- event) error { return nil } +func (uc *upstreamConn) SendMessage(msg *irc.Message) { + if !uc.caps["message-tags"] { + msg = msg.Copy() + msg.Tags = nil + } + + uc.conn.SendMessage(msg) +} + func (uc *upstreamConn) SendMessageLabeled(downstreamID uint64, msg *irc.Message) { if uc.caps["labeled-response"] { if msg.Tags == nil {