summaryrefslogtreecommitdiff
path: root/src/papod.clj
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2026-04-27 22:53:03 -0300
committerEuAndreh <eu@euandre.org>2026-04-27 22:53:03 -0300
commit27f27e08f3f3d6ac6d29bcf7010a7fc8a5ab6f93 (patch)
treea1b3686b0574f97ee9cedfc32fb23162237f3041 /src/papod.clj
parentMONITOR command and online-state notifications (diff)
downloadpapod-27f27e08f3f3d6ac6d29bcf7010a7fc8a5ab6f93.tar.gz
papod-27f27e08f3f3d6ac6d29bcf7010a7fc8a5ab6f93.tar.xz
extended-monitor capability
Watchers that negotiate extended-monitor (alongside the appropriate gating cap) now receive AWAY, SETNAME, and ACCOUNT events for monitored nicks even when they don't share a channel. The channel- member broadcast and the extended-monitor fan-out share a `seen` set so a watcher never gets a line twice. Implementation: notify-extended-monitors\! looks up the nick's watchers in :nick-monitors, filters by both extended-monitor and the gating cap (away-notify / setname / account-notify), and skips anyone already delivered to. handle-away, handle-setname, and broadcast-account- notify\! each call it after the existing channel-member loop.
Diffstat (limited to 'src/papod.clj')
-rw-r--r--src/papod.clj119
1 files changed, 76 insertions, 43 deletions
diff --git a/src/papod.clj b/src/papod.clj
index 9f148c3..96530a8 100644
--- a/src/papod.clj
+++ b/src/papod.clj
@@ -1030,26 +1030,30 @@
(when (cracha/user-by-email c nick)
nick)))
+(declare notify-extended-monitors!)
+
(defn- broadcast-account-notify!
"Send :nick ACCOUNT <account-or-*> to every other member of every
channel `nick` is in, gated on the recipient negotiating account-
- notify."
+ notify. Also fans out to extended-monitor watchers."
[components nick account]
(let [{:keys [clients channels]} components
- line (str ":" nick " ACCOUNT " (or account "*"))]
+ line (str ":" nick " ACCOUNT " (or account "*"))
+ seen (atom #{nick})]
(when (and clients channels)
- (let [seen (volatile! #{})]
- (doseq [[handle members] @channels
- :when (contains? members nick)
- mn members
- :when (and (not= mn nick)
- (not (@seen mn)))]
- (vswap! seen conj mn)
- (when-let [m (get @clients mn)]
- (let [ca (:client-atom m)
- caps (when ca (or (:caps @ca) #{}))]
- (when (caps "account-notify")
- (deliver-to-client! (:w m) line)))))))))
+ (doseq [[handle members] @channels
+ :when (contains? members nick)
+ mn members
+ :when (and (not= mn nick)
+ (not (@seen mn)))]
+ (swap! seen conj mn)
+ (when-let [m (get @clients mn)]
+ (let [ca (:client-atom m)
+ caps (when ca (or (:caps @ca) #{}))]
+ (when (caps "account-notify")
+ (deliver-to-client! (:w m) line))))))
+ (notify-extended-monitors!
+ components nick "account-notify" line seen)))
(defn- authenticate!
[client _components]
@@ -1400,6 +1404,7 @@
" away-notify utf8only"
" account-notify account-tag chghost"
" setname invite-notify"
+ " extended-monitor"
" draft/multiline=max-bytes=4096"
",max-lines=32"
" draft/read-marker"
@@ -1415,6 +1420,7 @@
" away-notify utf8only"
" account-notify account-tag chghost"
" setname invite-notify"
+ " extended-monitor"
" draft/multiline draft/read-marker"
" draft/message-redaction"
" draft/message-editing"
@@ -1432,6 +1438,7 @@
"away-notify" "utf8only"
"account-notify" "account-tag"
"chghost" "setname" "invite-notify"
+ "extended-monitor"
"draft/multiline" "draft/read-marker"
"draft/message-redaction"
"draft/message-editing"
@@ -3761,23 +3768,27 @@
src (client-prefix client)
notify!
(fn [line]
- (let [{:keys [clients channels]} components]
+ (let [{:keys [clients channels]} components
+ seen (atom #{nick})]
(when (and clients channels)
- (let [seen (atom #{nick})]
- (doseq [[ch members] @channels
- :when (contains? members nick)
- mn members
- :when (not (contains? @seen mn))
- :let [m (get @clients mn)
- ca (:client-atom m)
- caps (or (when ca
- (:caps @ca))
- #{})]
- :when (and m
- (contains?
- caps "away-notify"))]
- (deliver-to-client! (:w m) line)
- (swap! seen conj mn))))))]
+ (doseq [[ch members] @channels
+ :when (contains? members nick)
+ mn members
+ :when (not (contains? @seen mn))
+ :let [m (get @clients mn)
+ ca (:client-atom m)
+ caps (or (when ca
+ (:caps @ca))
+ #{})]
+ :when (and m
+ (contains?
+ caps "away-notify"))]
+ (deliver-to-client! (:w m) line)
+ (swap! seen conj mn)))
+ ;; extended-monitor: AWAY also goes to watchers
+ ;; that negotiated extended-monitor + away-notify
+ (notify-extended-monitors!
+ components nick "away-notify" line seen)))]
(if (or (empty? params) (string/blank? msg))
(do (swap! client dissoc :away)
(notify! (str ":" src " AWAY"))
@@ -3912,6 +3923,24 @@
(str ":" +server-name+ " " numeric " "
wn " :" nick))))))
+(defn- notify-extended-monitors!
+ "Deliver `line` to each watcher of `nick` that negotiated
+ extended-monitor and the given gating cap (away-notify, setname,
+ account-notify, chghost), excluding any watcher already in `seen`."
+ [components nick gating-cap line seen]
+ (when-let [nm (:nick-monitors components)]
+ (let [low (string/lower-case nick)]
+ (doseq [watcher (get @nm low #{})
+ :let [w (:w @watcher)
+ caps (or (:caps @watcher) #{})
+ wn (:nick @watcher)]
+ :when (and w
+ (caps "extended-monitor")
+ (caps gating-cap)
+ (not (contains? @seen wn)))]
+ (deliver-to-client! w line)
+ (swap! seen conj wn)))))
+
(defn- handle-setname
[params client components]
(let [{:keys [clients channels]} components
@@ -3927,21 +3956,25 @@
[(str "FAIL SETNAME INVALID_REALNAME :Invalid realname")]
:else
- (let [line (str ":" src " SETNAME :" new-name)]
+ (let [line (str ":" src " SETNAME :" new-name)
+ seen (atom #{nick})]
(swap! client assoc-in [:user :realname] new-name)
(when (and clients channels)
- (let [seen (atom #{nick})]
- (doseq [[ch members] @channels
- :when (contains? members nick)
- mn members
- :when (not (contains? @seen mn))
- :let [m (get @clients mn)
- ca (:client-atom m)
- caps (or (when ca (:caps @ca))
- #{})]
- :when (and m (caps "setname"))]
- (deliver-to-client! (:w m) line)
- (swap! seen conj mn))))
+ (doseq [[ch members] @channels
+ :when (contains? members nick)
+ mn members
+ :when (not (contains? @seen mn))
+ :let [m (get @clients mn)
+ ca (:client-atom m)
+ caps (or (when ca (:caps @ca))
+ #{})]
+ :when (and m (caps "setname"))]
+ (deliver-to-client! (:w m) line)
+ (swap! seen conj mn)))
+ ;; extended-monitor: SETNAME also goes to watchers
+ ;; with extended-monitor + setname.
+ (notify-extended-monitors!
+ components nick "setname" line seen)
[line]))))
(defn- handle-batch-open!