~emersion/soju-dev

Add support for the upstream echo-message capability v1 PROPOSED

delthas: 1
 Add support for the upstream echo-message capability

 3 files changed, 58 insertions(+), 18 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~emersion/soju-dev/patches/27199/mbox | git am -3
Learn more about email & git

[PATCH] Add support for the upstream echo-message capability Export this patch

This adds support for upstream echo-message. This capability is only
enabled when all downstreams support it.

When it is enabled, we don't echo downstream messages in the downstream
handler, but rather wait for the upstream to echo it, to produce it to
all downstreams.

When it is disabled, we keep the same behaviour as before: produce the
message to all downstreams as soon as it is received from the
downstream.
---
 downstream.go | 31 ++++++++++++++++++++-----------
 upstream.go   | 43 ++++++++++++++++++++++++++++++++++++-------
 user.go       |  2 ++
 3 files changed, 58 insertions(+), 18 deletions(-)

diff --git a/downstream.go b/downstream.go
index 6efe756..2d784da 100644
--- a/downstream.go
+++ b/downstream.go
@@ -904,6 +904,10 @@ func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {

		if !dc.registered {
			dc.negotiatingCaps = true
		} else {
			dc.forEachUpstream(func(uc *upstreamConn) {
				uc.updateCaps()
			})
		}
	case "END":
		dc.negotiatingCaps = false
@@ -2353,18 +2357,23 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
				Params:  []string{upstreamName, unmarshaledText},
			})

			echoTags := tags.Copy()
			echoTags["time"] = irc.TagValue(time.Now().UTC().Format(serverTimeLayout))
			if uc.account != "" {
				echoTags["account"] = irc.TagValue(uc.account)
			}
			echoMsg := &irc.Message{
				Tags:    echoTags,
				Prefix:  &irc.Prefix{Name: uc.nick},
				Command: msg.Command,
				Params:  []string{upstreamName, text},
			// If the upstream supports echo message, we'll produce the mesasge
			// when it is echoed from the upstream.
			// Otherwise, produce/log it here because it's the last time we'll see it.
			if !uc.caps["echo-message"] {
				echoTags := tags.Copy()
				echoTags["time"] = irc.TagValue(time.Now().UTC().Format(serverTimeLayout))
				if uc.account != "" {
					echoTags["account"] = irc.TagValue(uc.account)
				}
				echoMsg := &irc.Message{
					Tags:    echoTags,
					Prefix:  &irc.Prefix{Name: uc.nick},
					Command: msg.Command,
					Params:  []string{upstreamName, text},
				}
				uc.produce(upstreamName, echoMsg, dc)
			}
			uc.produce(upstreamName, echoMsg, dc)

			uc.updateChannelAutoDetach(upstreamName)
		}
diff --git a/upstream.go b/upstream.go
index 1a454d3..655fade 100644
--- a/upstream.go
+++ b/upstream.go
@@ -39,6 +39,12 @@ var permanentUpstreamCaps = map[string]bool{
	"draft/extended-monitor":     true,
}

// needAllUpstreamCaps is the list of upstream capabilities that
// require support from all downstreams to be enabled
var needAllUpstreamCaps = map[string]bool{
	"echo-message": true,
}

type registrationError struct {
	*irc.Message
}
@@ -488,8 +494,10 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
				target = msg.Prefix.Name
			}

			self := uc.isOurNick(msg.Prefix.Name)

			ch := uc.network.channels.Value(target)
			if ch != nil && msg.Command != "TAGMSG" {
			if ch != nil && msg.Command != "TAGMSG" && !self {
				if ch.Detached {
					uc.handleDetachedMessage(ctx, ch, msg)
				}
@@ -500,7 +508,10 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
				}
			}

			uc.produce(target, msg, nil)
			if !(self && msg.Command == "TAGMSG") {
				// (Don't echo TAGMSG messages, to keep the same behaviour as upstreams without echo-message.)
				uc.produce(target, msg, nil)
			}
		}
	case "CAP":
		var subCmd string
@@ -523,7 +534,7 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
				break // wait to receive all capabilities
			}

			uc.requestCaps()
			uc.updateCaps()

			if uc.requestSASL() {
				break // we'll send CAP END after authentication is completed
@@ -540,7 +551,14 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
			caps := strings.Fields(subParams[0])

			for _, name := range caps {
				if err := uc.handleCapAck(strings.ToLower(name), subCmd == "ACK"); err != nil {
				var enable bool
				if strings.HasPrefix(name, "-") {
					name = strings.TrimPrefix(name, "-")
					enable = false
				} else {
					enable = subCmd == "ACK"
				}
				if err := uc.handleCapAck(strings.ToLower(name), enable); err != nil {
					return err
				}
			}
@@ -555,7 +573,7 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
				return newNeedMoreParamsError(msg.Command)
			}
			uc.handleSupportedCaps(subParams[0])
			uc.requestCaps()
			uc.updateCaps()
		case "DEL":
			if len(subParams) < 1 {
				return newNeedMoreParamsError(msg.Command)
@@ -1813,13 +1831,24 @@ func (uc *upstreamConn) handleSupportedCaps(capsStr string) {
	}
}

func (uc *upstreamConn) requestCaps() {
func (uc *upstreamConn) updateCaps() {
	var requestCaps []string
	for c := range permanentUpstreamCaps {
		if _, ok := uc.supportedCaps[c]; ok && !uc.caps[c] {
			requestCaps = append(requestCaps, c)
		}
	}
	for c := range needAllUpstreamCaps {
		enabled := true
		uc.forEachDownstream(func(dc *downstreamConn) {
			enabled = enabled && dc.caps[c]
		})
		if !uc.caps[c] && enabled {
			requestCaps = append(requestCaps, c)
		} else if uc.caps[c] && !enabled {
			requestCaps = append(requestCaps, "-"+c)
		}
	}

	if len(requestCaps) == 0 {
		return
@@ -1887,7 +1916,7 @@ func (uc *upstreamConn) handleCapAck(name string, ok bool) error {
			Params:  []string{auth.Mechanism},
		})
	default:
		if permanentUpstreamCaps[name] {
		if permanentUpstreamCaps[name] || needAllUpstreamCaps[name] {
			break
		}
		uc.logger.Printf("received CAP ACK/NAK for a cap we don't support: %v", name)
diff --git a/user.go b/user.go
index 78c638c..3826148 100644
--- a/user.go
+++ b/user.go
@@ -646,6 +646,7 @@ func (u *user) run() {

			u.forEachUpstream(func(uc *upstreamConn) {
				uc.updateAway()
				uc.updateCaps()
			})
		case eventDownstreamDisconnected:
			dc := e.dc
@@ -663,6 +664,7 @@ func (u *user) run() {

			u.forEachUpstream(func(uc *upstreamConn) {
				uc.cancelPendingCommandsByDownstreamID(dc.id)
				uc.updateCaps()
				uc.updateAway()
				uc.updateMonitor()
			})

base-commit: f7e151396d78a1b63be171e03a284735829e88ee
-- 
2.17.1