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.
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 {

10
irc.go
View file

@ -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

View file

@ -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 {