From 0ba374bf2b7be92317777216f2dfcd607d4f9e8f Mon Sep 17 00:00:00 2001 From: delthas Date: Wed, 10 Aug 2022 13:06:54 +0200 Subject: Add support for soju.im/bouncer-networks-notify This enables dynamic discovery of new and deleted networks. Fixes: https://todo.sr.ht/~taiite/senpai/71 --- app.go | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++------- irc/events.go | 5 +++-- irc/session.go | 26 ++++++++++++++++---------- ui/buffers.go | 14 ++++++++++++++ ui/ui.go | 5 +++++ 5 files changed, 89 insertions(+), 19 deletions(-) diff --git a/app.go b/app.go index a87df2d..318fd4c 100644 --- a/app.go +++ b/app.go @@ -8,6 +8,7 @@ import ( "os" "os/exec" "strings" + "sync" "time" "unicode" @@ -84,7 +85,7 @@ type boundKey struct { type App struct { win *ui.UI - sessions map[string]*irc.Session + sessions map[string]*irc.Session // map of network IDs to their current session pasting bool events chan event @@ -99,12 +100,18 @@ type App struct { monitor map[string]map[string]struct{} // set of targets we want to monitor per netID, best-effort. netID->target->{} + networkLock sync.RWMutex // locks networks + networks map[string]struct{} // set of network IDs we want to connect to; to be locked with networkLock + lastMessageTime time.Time lastCloseTime time.Time } func NewApp(cfg Config) (app *App, err error) { app = &App{ + networks: map[string]struct{}{ + "": {}, // add the master network by default + }, sessions: map[string]*irc.Session{}, events: make(chan event, eventChanSize), cfg: cfg, @@ -262,6 +269,16 @@ func (app *App) handleEvent(ev event) bool { return true } +func (app *App) wantsNetwork(netID string) bool { + if app.win.ShouldExit() { + return false + } + app.networkLock.RLock() + _, ok := app.networks[netID] + app.networkLock.RUnlock() + return ok +} + // ircLoop maintains a connection to the IRC server by connecting and then // forwarding IRC events to app.events repeatedly. func (app *App) ircLoop(netID string) { @@ -279,8 +296,11 @@ func (app *App) ircLoop(netID string) { NetID: netID, Auth: auth, } - for !app.win.ShouldExit() { + for app.wantsNetwork(netID) { conn := app.connect(netID) + if conn == nil { + break + } in, out := irc.ChanInOut(conn) if app.cfg.Debug { out = app.debugOutputMessages(netID, out) @@ -320,7 +340,7 @@ func (app *App) ircLoop(netID string) { HeadColor: tcell.ColorRed, Body: ui.PlainString("Connection lost"), }) - if app.win.ShouldExit() { + if !app.wantsNetwork(netID) { break } time.Sleep(10 * time.Second) @@ -328,7 +348,7 @@ func (app *App) ircLoop(netID string) { } func (app *App) connect(netID string) net.Conn { - for { + for app.wantsNetwork(netID) { app.queueStatusLine(netID, ui.Line{ Head: "--", Body: ui.PlainSprintf("Connecting to %s...", app.cfg.Addr), @@ -344,6 +364,7 @@ func (app *App) connect(netID string) net.Conn { }) time.Sleep(1 * time.Minute) } + return nil } func (app *App) tryConnect() (conn net.Conn, err error) { @@ -663,6 +684,12 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) { if s, ok := app.sessions[netID]; ok { s.Close() } + if !app.wantsNetwork(netID) { + delete(app.sessions, netID) + delete(app.monitor, netID) + s.Close() + return + } app.sessions[netID] = s if _, ok := app.monitor[netID]; !ok { app.monitor[netID] = make(map[string]struct{}) @@ -921,9 +948,26 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) { case irc.ReadEvent: app.win.SetRead(netID, ev.Target, ev.Timestamp) case irc.BouncerNetworkEvent: - _, added := app.win.AddBuffer(ev.ID, ev.Name, "") - if added { - go app.ircLoop(ev.ID) + if !ev.Delete { + _, added := app.win.AddBuffer(ev.ID, ev.Name, "") + if added { + app.networkLock.Lock() + app.networks[ev.ID] = struct{}{} + app.networkLock.Unlock() + go app.ircLoop(ev.ID) + } + } else { + app.networkLock.Lock() + delete(app.networks, ev.ID) + app.networkLock.Unlock() + // if a session was already opened, close it now. + // otherwise, we'll close it when it sends a new session event. + if s, ok := app.sessions[ev.ID]; ok { + s.Close() + delete(app.sessions, ev.ID) + delete(app.monitor, ev.ID) + } + app.win.RemoveNetworkBuffers(ev.ID) } case irc.ErrorEvent: if isBlackListed(msg.Command) { diff --git a/irc/events.go b/irc/events.go index 7e92371..b7bb430 100644 --- a/irc/events.go +++ b/irc/events.go @@ -104,6 +104,7 @@ type SearchEvent struct { } type BouncerNetworkEvent struct { - ID string - Name string + ID string + Name string + Delete bool } diff --git a/irc/session.go b/irc/session.go index 1a9b659..0776436 100644 --- a/irc/session.go +++ b/irc/session.go @@ -62,11 +62,12 @@ var SupportedCapabilities = map[string]struct{}{ "sasl": {}, "setname": {}, - "draft/chathistory": {}, - "draft/event-playback": {}, - "draft/read-marker": {}, - "soju.im/bouncer-networks": {}, - "soju.im/search": {}, + "draft/chathistory": {}, + "draft/event-playback": {}, + "draft/read-marker": {}, + "soju.im/bouncer-networks-notify": {}, + "soju.im/bouncer-networks": {}, + "soju.im/search": {}, } // Values taken by the "@+typing=" client tag. TypingUnspec means the value or @@ -1336,11 +1337,16 @@ func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, er break } id := msg.Params[1] - attrs := parseTags(msg.Params[2]) - return BouncerNetworkEvent{ - ID: id, - Name: attrs["name"], - }, nil + event := BouncerNetworkEvent{ + ID: id, + } + if msg.Params[2] != "*" { + attrs := parseTags(msg.Params[2]) + event.Name = attrs["name"] + } else { + event.Delete = true + } + return event, nil case "PING": var payload string if err := msg.ParseParams(&payload); err != nil { diff --git a/ui/buffers.go b/ui/buffers.go index 915a1c8..b13465f 100644 --- a/ui/buffers.go +++ b/ui/buffers.go @@ -342,6 +342,20 @@ func (bs *BufferList) Remove(netID, title string) bool { return true } +func (bs *BufferList) RemoveNetwork(netID string) { + for idx := 0; idx < len(bs.list); idx++ { + b := &bs.list[idx] + if b.netID != netID { + continue + } + bs.list = append(bs.list[:idx], bs.list[idx+1:]...) + if len(bs.list) <= bs.current { + bs.current-- + } + idx-- + } +} + func (bs *BufferList) mergeLine(former *Line, addition Line) (keepLine bool) { bs.doMergeLine(former, addition) if former.Body.string == "" { diff --git a/ui/ui.go b/ui/ui.go index 4866969..0a380a7 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -260,6 +260,11 @@ func (ui *UI) RemoveBuffer(netID, title string) { ui.memberOffset = 0 } +func (ui *UI) RemoveNetworkBuffers(netID string) { + ui.bs.RemoveNetwork(netID) + ui.memberOffset = 0 +} + func (ui *UI) AddLine(netID, buffer string, notify NotifyType, line Line) { ui.bs.AddLine(netID, buffer, notify, line) } -- cgit v1.2.3