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.
This commit is contained in:
delthas 2020-05-21 07:04:34 +02:00 committed by Simon Ser
parent cdef46d0da
commit f4e0c51366
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
3 changed files with 59 additions and 10 deletions

View File

@ -250,6 +250,9 @@ func (dc *downstreamConn) readMessages(ch chan<- event) error {
// This can only called from the user goroutine. // This can only called from the user goroutine.
func (dc *downstreamConn) SendMessage(msg *irc.Message) { func (dc *downstreamConn) SendMessage(msg *irc.Message) {
if !dc.caps["message-tags"] { if !dc.caps["message-tags"] {
if msg.Command == "TAGMSG" {
return
}
msg = msg.Copy() msg = msg.Copy()
for name := range msg.Tags { for name := range msg.Tags {
supported := false supported := false
@ -274,7 +277,7 @@ func (dc *downstreamConn) marshalMessage(msg *irc.Message, net *network) *irc.Me
msg.Prefix = dc.marshalUserPrefix(net, msg.Prefix) msg.Prefix = dc.marshalUserPrefix(net, msg.Prefix)
switch msg.Command { switch msg.Command {
case "PRIVMSG", "NOTICE": case "PRIVMSG", "NOTICE", "TAGMSG":
msg.Params[0] = dc.marshalEntity(net, msg.Params[0]) msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
case "NICK": case "NICK":
// Nick change for another user // 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 { if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
return err return err
} }
tags := copyClientTags(msg.Tags)
for _, name := range strings.Split(targetsStr, ",") { for _, name := range strings.Split(targetsStr, ",") {
if name == serviceNick { if name == serviceNick {
@ -1418,14 +1422,15 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
unmarshaledText = dc.unmarshalText(uc, text) unmarshaledText = dc.unmarshalText(uc, text)
} }
uc.SendMessageLabeled(dc.id, &irc.Message{ uc.SendMessageLabeled(dc.id, &irc.Message{
Tags: tags,
Command: "PRIVMSG", Command: "PRIVMSG",
Params: []string{upstreamName, unmarshaledText}, Params: []string{upstreamName, unmarshaledText},
}) })
echoTags := tags.Copy()
echoTags["time"] = irc.TagValue(time.Now().UTC().Format(serverTimeLayout))
echoMsg := &irc.Message{ echoMsg := &irc.Message{
Tags: irc.Tags{ Tags: echoTags,
"time": irc.TagValue(time.Now().UTC().Format(serverTimeLayout)),
},
Prefix: &irc.Prefix{ Prefix: &irc.Prefix{
Name: uc.nick, Name: uc.nick,
User: uc.username, User: uc.username,
@ -1440,6 +1445,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
if err := parseMessageParams(msg, &targetsStr, &text); err != nil { if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
return err return err
} }
tags := copyClientTags(msg.Tags)
for _, name := range strings.Split(targetsStr, ",") { for _, name := range strings.Split(targetsStr, ",") {
uc, upstreamName, err := dc.unmarshalEntity(name) uc, upstreamName, err := dc.unmarshalEntity(name)
@ -1452,10 +1458,30 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
unmarshaledText = dc.unmarshalText(uc, text) unmarshaledText = dc.unmarshalText(uc, text)
} }
uc.SendMessageLabeled(dc.id, &irc.Message{ uc.SendMessageLabeled(dc.id, &irc.Message{
Tags: tags,
Command: "NOTICE", Command: "NOTICE",
Params: []string{upstreamName, unmarshaledText}, 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": case "INVITE":
var user, channel string var user, channel string
if err := parseMessageParams(msg, &user, &channel); err != nil { if err := parseMessageParams(msg, &user, &channel); err != nil {

10
irc.go
View File

@ -274,6 +274,16 @@ func parseMessageParams(msg *irc.Message, out ...*string) error {
return nil 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 batch struct {
Type string Type string
Params []string Params []string

View File

@ -320,14 +320,20 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
Params: msg.Params, Params: msg.Params,
}) })
return nil return nil
case "NOTICE", "PRIVMSG": case "NOTICE", "PRIVMSG", "TAGMSG":
if msg.Prefix == nil { if msg.Prefix == nil {
return fmt.Errorf("expected a prefix") return fmt.Errorf("expected a prefix")
} }
var entity, text string var entity, text string
if err := parseMessageParams(msg, &entity, &text); err != nil { if msg.Command != "TAGMSG" {
return err 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 { 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 if msg.Prefix.User == "" && msg.Prefix.Host == "" { // server message
uc.produce("", msg, nil) uc.produce("", msg, nil)
} else { // regular user NOTICE or PRIVMSG } else { // regular user message
target := entity target := entity
if target == uc.nick { if target == uc.nick {
target = msg.Prefix.Name 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": case "ACK":
// Ignore // Ignore
case irc.RPL_NOWAWAY, irc.RPL_UNAWAY: case irc.RPL_NOWAWAY, irc.RPL_UNAWAY:
@ -1487,6 +1491,15 @@ func (uc *upstreamConn) readMessages(ch chan<- event) error {
return nil 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) { func (uc *upstreamConn) SendMessageLabeled(downstreamID uint64, msg *irc.Message) {
if uc.caps["labeled-response"] { if uc.caps["labeled-response"] {
if msg.Tags == nil { if msg.Tags == nil {