nandi/frqpublic Fork 0
94e59a2
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Ask for the backlog freeq restores channels without

A signed-in user's channel came up empty but for its own "Joined #test". The
server re-joins an authenticated user's channels at registration and sends
JOIN, topic and NAMES for each — but not the history a fresh join is replayed.
It leaves that to the client, which had never asked.

So the end of the names list is now where a channel notices it was restored
rather than joined: an ordinary join has already been replayed its backlog by
the time 366 arrives, so a buffer still empty at that point asks for one with
CHATHISTORY LATEST. The check ignores system lines, since the join notice is
itself the first thing in an otherwise empty buffer.

Guests are unaffected — their buffer is full when 366 lands, and no request
goes out. Verified against irc.freeq.at both ways: a normal join still comes
back with its 102 messages and no duplicates, and an emptied buffer asking for
its own history gets 101.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-08-30T10:48:35-07:00 Browse files
94e59a2 parent: 75e557e
modified README.md +1 -0
@@ -101,6 +101,7 @@ surface — that surface does not work on Android either, while the syscalls do.
101101 * Guest connect (`NICK`/`USER`), `001` welcome, `PING`/`PONG` keepalive
102102 * Auto-joins `#test` on `irc.freeq.at`
103103 * Join channels, channel buffers with unread counts, send and receive `PRIVMSG`
104+* Backlog on join, and `CHATHISTORY` for the channels freeq restores instead
104105 * Join/part notices, DMs bucketed under the sender's nick
105106 * Discover list, search over buffers, disconnect
106107 * Conversations listed most recently opened first
@@ -101,6 +101,7 @@ surface — that surface does not work on Android either, while the syscalls do.
101 * Guest connect (`NICK`/`USER`), `001` welcome, `PING`/`PONG` keepalive101 * Guest connect (`NICK`/`USER`), `001` welcome, `PING`/`PONG` keepalive
102 * Auto-joins `#test` on `irc.freeq.at`102 * Auto-joins `#test` on `irc.freeq.at`
103 * Join channels, channel buffers with unread counts, send and receive `PRIVMSG`103 * Join channels, channel buffers with unread counts, send and receive `PRIVMSG`
104+* Backlog on join, and `CHATHISTORY` for the channels freeq restores instead
104 * Join/part notices, DMs bucketed under the sender's nick105 * Join/part notices, DMs bucketed under the sender's nick
105 * Discover list, search over buffers, disconnect106 * Discover list, search over buffers, disconnect
106 * Conversations listed most recently opened first107 * Conversations listed most recently opened first
modified src/frq/state.jolt +13 -0
@@ -51,6 +51,9 @@
5151 ;; joined as soon as the server sends 001
5252 (def auto-join "#test")
5353
54+;; How much backlog to ask for when the server did not volunteer any.
55+(def history-limit 100)
56+
5457 ;; name -> {:name :messages [{:from :text}] :unread n :joined? bool}
5558 (defonce channels (atom {}))
5659 (defonce current (atom nil))
@@ -138,6 +141,16 @@
138141 ;; already there is just a second line of noise.
139142 (when fresh? (push-message! ch "*" (str "Joined " ch))))
140143 (push-message! ch "*" (str from " joined"))))
144+ ;; End of NAMES. A plain JOIN is replayed history before this arrives, so
145+ ;; a channel that reaches here with nothing in it was restored rather
146+ ;; than joined — freeq re-joins an authenticated user's channels at
147+ ;; registration and leaves the backlog for the client to ask for.
148+ "366" (let [ch (second params)
149+ said (remove :system? (get-in @channels [ch :messages]))]
150+ (when (and ch @conn (empty? said))
151+ (irc/send-line! @conn
152+ (str "CHATHISTORY LATEST " ch " * " history-limit))))
153+
141154 "PART" (let [ch (first params)]
142155 (if (= from @form-nick)
143156 (swap! channels #(-> % (assoc-in [ch :joined?] false)
@@ -51,6 +51,9 @@
51 ;; joined as soon as the server sends 00151 ;; joined as soon as the server sends 001
52 (def auto-join "#test")52 (def auto-join "#test")
53 53
54+;; How much backlog to ask for when the server did not volunteer any.
55+(def history-limit 100)
56+
54 ;; name -> {:name :messages [{:from :text}] :unread n :joined? bool}57 ;; name -> {:name :messages [{:from :text}] :unread n :joined? bool}
55 (defonce channels (atom {}))58 (defonce channels (atom {}))
56 (defonce current (atom nil))59 (defonce current (atom nil))
@@ -138,6 +141,16 @@
138 ;; already there is just a second line of noise.141 ;; already there is just a second line of noise.
139 (when fresh? (push-message! ch "*" (str "Joined " ch))))142 (when fresh? (push-message! ch "*" (str "Joined " ch))))
140 (push-message! ch "*" (str from " joined"))))143 (push-message! ch "*" (str from " joined"))))
144+ ;; End of NAMES. A plain JOIN is replayed history before this arrives, so
145+ ;; a channel that reaches here with nothing in it was restored rather
146+ ;; than joined — freeq re-joins an authenticated user's channels at
147+ ;; registration and leaves the backlog for the client to ask for.
148+ "366" (let [ch (second params)
149+ said (remove :system? (get-in @channels [ch :messages]))]
150+ (when (and ch @conn (empty? said))
151+ (irc/send-line! @conn
152+ (str "CHATHISTORY LATEST " ch " * " history-limit))))
153+
141 "PART" (let [ch (first params)]154 "PART" (let [ch (first params)]
142 (if (= from @form-nick)155 (if (= from @form-nick)
143 (swap! channels #(-> % (assoc-in [ch :joined?] false)156 (swap! channels #(-> % (assoc-in [ch :joined?] false)