From a2c207d357fe734f57c0f761541a12246c17ebd6 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 13 Apr 2021 19:11:05 +0200 Subject: [PATCH] Relay detached channel backlog as BouncerServ NOTICE if necessary Instead of ignoring detached channels wehn replaying backlog, process them as usual and relay messages as BouncerServ NOTICEs if necessary. Advance the delivery receipts as if the channel was attached. Closes: https://todo.sr.ht/~emersion/soju/98 --- downstream.go | 31 +++++++++++++++++++++++++------ upstream.go | 23 ++++++++--------------- user.go | 21 +++++++++++++++++++++ 3 files changed, 54 insertions(+), 21 deletions(-) diff --git a/downstream.go b/downstream.go index b6129d2..4707591 100644 --- a/downstream.go +++ b/downstream.go @@ -1030,9 +1030,8 @@ func (dc *downstreamConn) sendTargetBacklog(net *network, target, msgID string) if dc.caps["draft/chathistory"] || dc.user.msgStore == nil { return } - if ch := net.channels.Value(target); ch != nil && ch.Detached { - return - } + + ch := net.channels.Value(target) limit := 4000 targetCM := net.casemap(target) @@ -1056,10 +1055,16 @@ func (dc *downstreamConn) sendTargetBacklog(net *network, target, msgID string) continue } - if dc.caps["batch"] { - msg.Tags["batch"] = irc.TagValue(batchRef) + if ch != nil && ch.Detached { + if net.detachedMessageNeedsRelay(ch, msg) { + dc.relayDetachedMessage(net, msg) + } + } else { + if dc.caps["batch"] { + msg.Tags["batch"] = irc.TagValue(batchRef) + } + dc.SendMessage(dc.marshalMessage(msg, net)) } - dc.SendMessage(dc.marshalMessage(msg, net)) } if dc.caps["batch"] { @@ -1071,6 +1076,20 @@ func (dc *downstreamConn) sendTargetBacklog(net *network, target, msgID string) } } +func (dc *downstreamConn) relayDetachedMessage(net *network, msg *irc.Message) { + if msg.Command != "PRIVMSG" && msg.Command != "NOTICE" { + return + } + + sender := msg.Prefix.Name + target, text := msg.Params[0], msg.Params[1] + if net.isHighlight(msg) { + sendServiceNOTICE(dc, fmt.Sprintf("highlight in %v: <%v> %v", dc.marshalEntity(net, target), sender, text)) + } else { + sendServiceNOTICE(dc, fmt.Sprintf("message in %v: <%v> %v", dc.marshalEntity(net, target), sender, text)) + } +} + func (dc *downstreamConn) runUntilRegistered() error { for !dc.registered { msg, err := dc.ReadMessage() diff --git a/upstream.go b/upstream.go index 8eaee2e..b61fc67 100644 --- a/upstream.go +++ b/upstream.go @@ -391,10 +391,10 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { ch := uc.network.channels.Value(target) if ch != nil { if ch.Detached { - uc.handleDetachedMessage(msg.Prefix.Name, text, ch) + uc.handleDetachedMessage(ch, msg) } - highlight := msg.Prefix.Name != uc.nick && isHighlight(text, uc.nick) + highlight := uc.network.isHighlight(msg) if ch.DetachOn == FilterMessage || ch.DetachOn == FilterDefault || (ch.DetachOn == FilterHighlight && highlight) { uc.updateChannelAutoDetach(target) } @@ -1437,18 +1437,13 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error { return nil } -func (uc *upstreamConn) handleDetachedMessage(sender string, text string, ch *Channel) { - highlight := sender != uc.nick && isHighlight(text, uc.nick) - if ch.RelayDetached == FilterMessage || ((ch.RelayDetached == FilterHighlight || ch.RelayDetached == FilterDefault) && highlight) { +func (uc *upstreamConn) handleDetachedMessage(ch *Channel, msg *irc.Message) { + if uc.network.detachedMessageNeedsRelay(ch, msg) { uc.forEachDownstream(func(dc *downstreamConn) { - if highlight { - sendServiceNOTICE(dc, fmt.Sprintf("highlight in %v: <%v> %v", dc.marshalEntity(uc.network, ch.Name), sender, text)) - } else { - sendServiceNOTICE(dc, fmt.Sprintf("message in %v: <%v> %v", dc.marshalEntity(uc.network, ch.Name), sender, text)) - } + dc.relayDetachedMessage(uc.network, msg) }) } - if ch.ReattachOn == FilterMessage || (ch.ReattachOn == FilterHighlight && highlight) { + if ch.ReattachOn == FilterMessage || (ch.ReattachOn == FilterHighlight && uc.network.isHighlight(msg)) { uc.network.attach(ch) if err := uc.srv.db.StoreChannel(uc.network.ID, ch); err != nil { uc.logger.Printf("failed to update channel %q: %v", ch.Name, err) @@ -1743,12 +1738,10 @@ func (uc *upstreamConn) produce(target string, msg *irc.Message, origin *downstr // Don't forward messages if it's a detached channel ch := uc.network.channels.Value(target) - if ch != nil && ch.Detached { - return - } + detached := ch != nil && ch.Detached uc.forEachDownstream(func(dc *downstreamConn) { - if dc != origin || dc.caps["echo-message"] { + if !detached && (dc != origin || dc.caps["echo-message"]) { dc.sendMessageWithID(dc.marshalMessage(msg, uc.network), msgID) } else { dc.advanceMessageWithID(msg, msgID) diff --git a/user.go b/user.go index e2e06ee..ac3facd 100644 --- a/user.go +++ b/user.go @@ -351,6 +351,27 @@ func (net *network) storeClientDeliveryReceipts(clientName string) { } } +func (net *network) isHighlight(msg *irc.Message) bool { + if msg.Command != "PRIVMSG" && msg.Command != "NOTICE" { + return false + } + + text := msg.Params[1] + + nick := net.Nick + if net.conn != nil { + nick = net.conn.nick + } + + // TODO: use case-mapping aware comparison here + return msg.Prefix.Name != nick && isHighlight(text, nick) +} + +func (net *network) detachedMessageNeedsRelay(ch *Channel, msg *irc.Message) bool { + highlight := net.isHighlight(msg) + return ch.RelayDetached == FilterMessage || ((ch.RelayDetached == FilterHighlight || ch.RelayDetached == FilterDefault) && highlight) +} + type user struct { User srv *Server