~emersion/public-inbox

soju: bouncer-networks: Add a read-only error attribute v2 SUPERSEDED

delthas: 1
 bouncer-networks: Add a read-only error attribute

 3 files changed, 43 insertions(+), 6 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/public-inbox/patches/30960/mbox | git am -3
Learn more about email & git

[PATCH soju v2] bouncer-networks: Add a read-only error attribute Export this patch

This is useful for clients to display additional info about why a
network is disconnected.

With this change, we also stop sending network disconnect/connect
NOTICEs to clients which support bouncer-networks-notify, because they
now have access to all the information that would be sent in that
message.
---
 doc/ext/bouncer-networks.md |  4 ++++
 downstream.go               |  4 ++++
 user.go                     | 41 +++++++++++++++++++++++++++++++------
 3 files changed, 43 insertions(+), 6 deletions(-)

diff --git a/doc/ext/bouncer-networks.md b/doc/ext/bouncer-networks.md
index cefb72d..1f55a80 100644
--- a/doc/ext/bouncer-networks.md
+++ b/doc/ext/bouncer-networks.md
@@ -242,6 +242,10 @@ Bouncers MUST recognise the following network attributes:
* `realname`: the realname to use during registration.
* `pass`: the server password (PASS) to use during registration.

Bouncers MAY recognise the following network attributes:
* `error` (read-only): a human-readable short text describing an error with the current network.
  This is typically used when the bouncer state is `disconnected` to describe the reason why the bouncer is disconnected.

TODO: more attributes

### Examples
diff --git a/downstream.go b/downstream.go
index bef6adf..01b1812 100644
--- a/downstream.go
+++ b/downstream.go
@@ -142,6 +142,10 @@ func getNetworkAttrs(network *network) irc.Tags {
		attrs["realname"] = irc.TagValue(realname)
	}

	if network.lastError != nil {
		attrs["error"] = irc.TagValue(network.lastError.Error())
	}

	fillNetworkAddrAttrs(attrs, &network.Network)

	return attrs
diff --git a/user.go b/user.go
index 29bbe85..eced60e 100644
--- a/user.go
+++ b/user.go
@@ -562,7 +562,7 @@ func (u *user) run() {
			uc.forEachDownstream(func(dc *downstreamConn) {
				dc.updateSupportedCaps()

				if !dc.caps.IsEnabled("soju.im/bouncer-networks") {
				if !dc.caps.IsEnabled("soju.im/bouncer-networks-notify") {
					sendServiceNOTICE(dc, fmt.Sprintf("connected to %s", uc.network.GetName()))
				}

@@ -576,7 +576,10 @@ func (u *user) run() {
					dc.SendMessage(&irc.Message{
						Prefix:  dc.srv.prefix(),
						Command: "BOUNCER",
						Params:  []string{"NETWORK", netIDStr, "state=connected"},
						Params: []string{"NETWORK", netIDStr, irc.Tags{
							"state": "connected",
							"error": "",
						}.String()},
					})
				}
			})
@@ -595,17 +598,43 @@ func (u *user) run() {

			if !stopped && (net.lastError == nil || net.lastError.Error() != e.err.Error()) {
				net.forEachDownstream(func(dc *downstreamConn) {
					sendServiceNOTICE(dc, fmt.Sprintf("failed connecting/registering to %s: %v", net.GetName(), e.err))
					if !dc.caps.IsEnabled("soju.im/bouncer-networks-notify") {
						sendServiceNOTICE(dc, fmt.Sprintf("failed connecting/registering to %s: %v", net.GetName(), e.err))
					}
				})
			}
			net.lastError = e.err
			u.forEachDownstream(func(dc *downstreamConn) {
				if dc.caps.IsEnabled("soju.im/bouncer-networks-notify") {
					dc.SendMessage(&irc.Message{
						Prefix:  dc.srv.prefix(),
						Command: "BOUNCER",
						Params: []string{"NETWORK", fmt.Sprintf("%v", net.ID), irc.Tags{
							"error": irc.TagValue(net.lastError.Error()),
						}.String()},
					})
				}
			})
		case eventUpstreamError:
			uc := e.uc

			uc.forEachDownstream(func(dc *downstreamConn) {
				sendServiceNOTICE(dc, fmt.Sprintf("disconnected from %s: %v", uc.network.GetName(), e.err))
				if !dc.caps.IsEnabled("soju.im/bouncer-networks-notify") {
					sendServiceNOTICE(dc, fmt.Sprintf("disconnected from %s: %v", uc.network.GetName(), e.err))
				}
			})
			uc.network.lastError = e.err
			u.forEachDownstream(func(dc *downstreamConn) {
				if dc.caps.IsEnabled("soju.im/bouncer-networks-notify") {
					dc.SendMessage(&irc.Message{
						Prefix:  dc.srv.prefix(),
						Command: "BOUNCER",
						Params: []string{"NETWORK", fmt.Sprintf("%v", uc.network.ID), irc.Tags{
							"error": irc.TagValue(uc.network.lastError.Error()),
						}.String()},
					})
				}
			})
		case eventUpstreamMessage:
			msg, uc := e.msg, e.uc
			if uc.isClosed() {
@@ -651,7 +680,7 @@ func (u *user) run() {
			u.downstreamConns = append(u.downstreamConns, dc)

			dc.forEachNetwork(func(network *network) {
				if network.lastError != nil {
				if network.lastError != nil && dc.caps.IsEnabled("soju.im/bouncer-networks") {
					sendServiceNOTICE(dc, fmt.Sprintf("disconnected from %s: %v", network.GetName(), network.lastError))
				}
			})
@@ -774,7 +803,7 @@ func (u *user) handleUpstreamDisconnected(uc *upstreamConn) {

	if uc.network.lastError == nil {
		uc.forEachDownstream(func(dc *downstreamConn) {
			if !dc.caps.IsEnabled("soju.im/bouncer-networks") {
			if !dc.caps.IsEnabled("soju.im/bouncer-networks-notify") {
				sendServiceNOTICE(dc, fmt.Sprintf("disconnected from %s", uc.network.GetName()))
			}
		})

base-commit: d8ca6d22224893fb6286495033c72e7d7be3f9af
-- 
2.30.0
Hm, after some more discussion on IRC, unfortunately this approach
won't cut it. bouncer-networks-notify is typically just enabled on a
single connection (one where BOUNCER BIND isn't used).

I've opened [1] to track this issue.

I've merged the first version of the patch. Thanks!

[1]: https://todo.sr.ht/~emersion/soju/195