summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordelthas <delthas@dille.cc>2022-08-10 13:06:54 +0200
committerdelthas <delthas@dille.cc>2022-08-10 13:06:54 +0200
commit0ba374bf2b7be92317777216f2dfcd607d4f9e8f (patch)
treef5644e7d276eeda8029bd68584d4a63d384eb638
parentDocument selecting with shift (diff)
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
-rw-r--r--app.go58
-rw-r--r--irc/events.go5
-rw-r--r--irc/session.go26
-rw-r--r--ui/buffers.go14
-rw-r--r--ui/ui.go5
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)
}