summaryrefslogtreecommitdiff
path: root/src/papod.clj
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2026-04-26 18:31:39 -0300
committerEuAndreh <eu@euandre.org>2026-04-26 18:31:39 -0300
commitd648097914710316f9f9d5b39cd6e7002a06463e (patch)
tree75d0094d7d2d98298d7169f3894c976e62c6ce71 /src/papod.clj
parentMARKREAD: monotonic timestamps and FAIL on missing params (diff)
downloadpapod-d648097914710316f9f9d5b39cd6e7002a06463e.tar.gz
papod-d648097914710316f9f9d5b39cd6e7002a06463e.tar.xz
MARKREAD on JOIN and DM targets
When a client with draft/read-marker JOINs a channel, send a MARKREAD line with the stored timestamp (or '*' if none) before the closing RPL_ENDOFNAMES. Also support MARKREAD against direct-message targets (user nicknames, not just channels): per-(owner,target) markers are stored in a new in-memory atom :user-markers.
Diffstat (limited to 'src/papod.clj')
-rw-r--r--src/papod.clj93
1 files changed, 61 insertions, 32 deletions
diff --git a/src/papod.clj b/src/papod.clj
index 27dfad0..0f2001b 100644
--- a/src/papod.clj
+++ b/src/papod.clj
@@ -2375,6 +2375,27 @@
" " handle " " nick " "
(quot
(System/currentTimeMillis) 1000))))
+ ;; draft/read-marker: send MARKREAD before
+ ;; RPL_ENDOFNAMES if the client has the cap
+ (when (and w
+ (contains?
+ (or (:caps @client) #{})
+ "draft/read-marker"))
+ (let [stored (when (and conn ch-eid')
+ (ffirst
+ (d/q '{:find [?ts]
+ :in [$ ?ch ?nick]
+ :where
+ [[?r :papod.read-marker/channel ?ch]
+ [?r :papod.read-marker/nick ?nick]
+ [?r :papod.read-marker/timestamp ?ts]]}
+ db' ch-eid' nick)))]
+ (deliver-to-client! w
+ (str ":" +server-name+ " MARKREAD "
+ handle " "
+ (if stored
+ (str "timestamp=" stored)
+ "*")))))
(when (and w members)
(deliver-to-client! w
(str ":" +server-name+ " 353 " nick
@@ -2788,34 +2809,48 @@
(let [handle (first params)
timestamp (second params)
db (when conn (d/db conn))
- chan-eid (when db (resolve-channel db handle))
- ts-rgx #"timestamp=(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)"]
+ chan? (channel-handle? handle)
+ chan-eid (when (and db chan?) (resolve-channel db handle))
+ ts-rgx #"timestamp=(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)"
+ user-key (when-not chan?
+ [(string/lower-case nick)
+ (string/lower-case handle)])
+ stored-fn
+ (fn []
+ (cond
+ (and chan? db chan-eid)
+ (ffirst
+ (d/q '{:find [?ts]
+ :in [$ ?ch ?nick]
+ :where
+ [[?r :papod.read-marker/channel ?ch]
+ [?r :papod.read-marker/nick ?nick]
+ [?r :papod.read-marker/timestamp ?ts]]}
+ db chan-eid nick))
+ user-key
+ (some-> components :user-markers
+ deref (get user-key))))]
(cond
;; SET with valid timestamp=...
(and timestamp
(re-matches ts-rgx timestamp))
(let [ts-only (second
(re-matches ts-rgx timestamp))
- stored (when (and db chan-eid)
- (ffirst
- (d/q '{:find [?ts]
- :in [$ ?ch ?nick]
- :where
- [[?r :papod.read-marker/channel
- ?ch]
- [?r :papod.read-marker/nick
- ?nick]
- [?r :papod.read-marker/timestamp
- ?ts]]}
- db chan-eid nick)))
+ stored (stored-fn)
advance? (or (nil? stored)
(pos? (compare ts-only stored)))]
- (when (and advance? conn chan-eid)
- @(d/transact conn
- [{:papod.read-marker/channel chan-eid
- :papod.read-marker/nick nick
- :papod.read-marker/timestamp
- ts-only}]))
+ (when advance?
+ (cond
+ (and chan? conn chan-eid)
+ @(d/transact conn
+ [{:papod.read-marker/channel chan-eid
+ :papod.read-marker/nick nick
+ :papod.read-marker/timestamp
+ ts-only}])
+ user-key
+ (when (:user-markers components)
+ (swap! (:user-markers components)
+ assoc user-key ts-only))))
[(str ":" +server-name+ " MARKREAD " handle
" timestamp="
(if advance? ts-only stored))])
@@ -2827,16 +2862,9 @@
" " timestamp
" :Invalid timestamp")]
- ;; GET: MARKREAD #channel
+ ;; GET
:else
- (let [stored (when (and db chan-eid)
- (ffirst
- (d/q '{:find [?ts]
- :in [$ ?ch ?nick]
- :where [[?r :papod.read-marker/channel ?ch]
- [?r :papod.read-marker/nick ?nick]
- [?r :papod.read-marker/timestamp ?ts]]}
- db chan-eid nick)))]
+ (let [stored (stored-fn)]
[(str ":" +server-name+ " MARKREAD " handle " "
(if stored
(str "timestamp=" stored)
@@ -4162,9 +4190,10 @@
:chan-keys (atom {})
:chan-limits (atom {})
:invites (atom {})
- :whowas (atom [])
- :max-users (atom 0)
- :n-unreg (atom 0)}))
+ :whowas (atom [])
+ :max-users (atom 0)
+ :n-unreg (atom 0)
+ :user-markers (atom {})}))
(defconst- +idle-timeout-ms+
(if-let [t (System/getenv "PAPOD_IDLE_TIMEOUT")]