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..e2fac8f 100644 --- a/user.go +++ b/user.go @@ -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()}, }) } }) @@ -599,6 +602,17 @@ func (u *user) run() { }) } 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 @@ -606,6 +620,17 @@ func (u *user) run() { 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() {