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 {