summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2026-04-25 06:34:56 -0300
committerEuAndreh <eu@euandre.org>2026-04-25 06:34:56 -0300
commit7b1572e68f886d4b4eadb16a9193b968c86fbc74 (patch)
treea6688c7274643a02c61419b6ac8e403c7db07dee
parentAdd unit+integration tests for WHOIS, WHO, MODE, TOPIC, KICK, AWAY, (diff)
downloadpapod-7b1572e68f886d4b4eadb16a9193b968c86fbc74.tar.gz
papod-7b1572e68f886d4b4eadb16a9193b968c86fbc74.tar.xz
Implement LUSERS, WHOWAS, INFO, INVITE; fix AWAY, LIST, WHO
New commands: - LUSERS: return 251-255 with user/channel counts (only counts registered clients to avoid stale-state inflation) - WHOWAS: return 406 (no such nickname) + 369 (end of WHOWAS) for all queries (no history tracking yet) - INFO: return 371 (server info) + 374 (end of INFO) - INVITE: send invite to target, return 341 (inviting) or 443 (already on channel) Fixes: - AWAY: empty trailing param (AWAY :) now unsets away status - LIST: only show channels with active members; support LIST #specific filtering - WHO: include away flag (H=here, G=gone) in 352 replies irctest: 214 passed, 440 failed, 218 skipped (up from 205). Unit: 260 assertions, Integration: 38 assertions — all pass.
-rw-r--r--src/papod.clj141
1 files changed, 113 insertions, 28 deletions
diff --git a/src/papod.clj b/src/papod.clj
index f29b3f6..94c7bb2 100644
--- a/src/papod.clj
+++ b/src/papod.clj
@@ -2432,16 +2432,17 @@
(defn- handle-away
[params client]
- (if (empty? params)
- (do (swap! client dissoc :away)
- [(numeric-reply client "305"
- ":You are no longer marked as being away")])
- (let [msg (string/join " " params)
- msg (cond-> msg
- (string/starts-with? msg ":") (subs 1))]
- (swap! client assoc :away msg)
- [(numeric-reply client "306"
- ":You have been marked as being away")])))
+ (let [msg (when (seq params)
+ (let [raw (string/join " " params)]
+ (cond-> raw
+ (string/starts-with? raw ":") (subs 1))))]
+ (if (or (empty? params) (string/blank? msg))
+ (do (swap! client dissoc :away)
+ [(numeric-reply client "305"
+ ":You are no longer marked as being away")])
+ (do (swap! client assoc :away msg)
+ [(numeric-reply client "306"
+ ":You have been marked as being away")]))))
(defn- replies-for!
[message client components]
@@ -2566,38 +2567,107 @@
(for [mn members
:let [m (when clients
(get @clients mn))]
- :when m]
+ :when m
+ :let [ca (:client-atom m)
+ away (when ca (:away @ca))
+ flag (if away "G" "H")
+ uname (or (some-> ca deref
+ :user :username)
+ mn)]]
(numeric-reply client "352"
- (str target " "
- (or (some-> (:client-atom m) deref
- :user :username)
- mn)
+ (str target " " uname
" localhost " +server-name+
- " " mn " H :0 " mn))))
+ " " mn " " flag " :0 " mn))))
[(numeric-reply client "315"
(str target " :End of /WHO list"))]))
;; WHO <nick>
:else
(let [m (when clients (get @clients target))]
(if m
- [(numeric-reply client "352"
- (str "* "
- (or (some-> (:client-atom m) deref
- :user :username)
- target)
- " localhost " +server-name+
- " " target " H :0 " target))
- (numeric-reply client "315"
- (str target " :End of /WHO list"))]
+ (let [ca (:client-atom m)
+ away (when ca (:away @ca))
+ flag (if away "G" "H")
+ uname (or (some-> ca deref
+ :user :username)
+ target)]
+ [(numeric-reply client "352"
+ (str "* " uname
+ " localhost " +server-name+
+ " " target " " flag
+ " :0 " target))
+ (numeric-reply client "315"
+ (str target " :End of /WHO list"))])
[(numeric-reply client "315"
(str target " :End of /WHO list"))]))))
(= command "LUSERS")
- []
+ (let [{:keys [clients channels]} components
+ n-users (if clients
+ (count
+ (filter
+ (fn [[_ m]]
+ (some-> (:client-atom m) deref
+ :registered?))
+ @clients))
+ 0)
+ n-chans (if channels (count @channels) 0)]
+ [(numeric-reply client "251"
+ (str ":There are " n-users
+ " users and 0 invisible on 1 servers"))
+ (numeric-reply client "252"
+ "0 :operator(s) online")
+ (numeric-reply client "253"
+ "0 :unknown connection(s)")
+ (numeric-reply client "254"
+ (str n-chans " :channels formed"))
+ (numeric-reply client "255"
+ (str ":I have " n-users
+ " clients and 0 servers"))])
(= command "MOTD")
[(numeric-reply client "422" ":MOTD File is missing")]
+ (= command "WHOWAS")
+ (if (empty? params)
+ [(numeric-reply client "431" ":No nickname given")]
+ (let [target (first params)]
+ [(numeric-reply client "406"
+ (str target " :There was no such nickname"))
+ (numeric-reply client "369"
+ (str target " :End of WHOWAS"))]))
+
+ (= command "INFO")
+ [(numeric-reply client "371"
+ (str ":papod " +version+
+ " - IRC server"))
+ (numeric-reply client "374"
+ ":End of /INFO list")]
+
+ (= command "INVITE")
+ (let [{:keys [clients channels]} components
+ target (first params)
+ handle (second params)]
+ (cond
+ (< (count params) 2)
+ [(numeric-reply client "461"
+ "INVITE :Not enough parameters")]
+
+ (and channels handle
+ (contains? (get @channels handle) target))
+ [(numeric-reply client "443"
+ (str target " " handle
+ " :is already on channel"))]
+
+ :else
+ (let [nick (client-target client)]
+ (when-let [m (and clients
+ (get @clients target))]
+ (deliver-to-client! (:w m)
+ (str ":" nick " INVITE " target
+ " " handle)))
+ [(numeric-reply client "341"
+ (str target " " handle))])))
+
;; Network-scoped commands
(not (:network-id @client))
[(numeric-reply client "451"
@@ -2616,8 +2686,23 @@
"EDIT" (handle-edit params client components)
"TAGMSG" (handle-tagmsg message client components)
"MARKREAD" (handle-markread params client components)
- "LIST" [(numeric-reply client "323"
- ":End of /LIST")]
+ "LIST" (let [filter-ch (first params)
+ chans (when (:channels components)
+ @(:channels components))
+ active (filter
+ (fn [[ch members]]
+ (and (seq members)
+ (or (nil? filter-ch)
+ (= ch filter-ch))))
+ chans)]
+ (conj
+ (vec
+ (for [[ch members] active]
+ (numeric-reply client "322"
+ (str ch " "
+ (count members) " :"))))
+ (numeric-reply client "323"
+ ":End of /LIST")))
"NAMES" (let [handle (first params)]
(if (and handle
(:channels components)