add WHO command

This commit is contained in:
tali 2024-01-31 01:08:10 -05:00
parent 2e15ae3aac
commit 65b3dc6672
4 changed files with 77 additions and 7 deletions

View File

@ -359,11 +359,7 @@ let list_names t me chan =
let nicks = let nicks =
List.map List.map
(fun (m : membership) -> (fun (m : membership) ->
let nick = User.nick m.mem_user in Router.membership_prefix m.mem_priv ^ User.nick m.mem_user)
match m.mem_priv with
| Normal -> nick
| Voice -> "+" ^ nick
| Operator -> "@" ^ nick)
members members
in in
@ -540,6 +536,56 @@ let on_msg_join_0 t =
Ok () Ok ()
(* user queries *)
let user_who_flags user =
(* Away status: the letter H ('H', 0x48) to indicate that the user is here, or
the letter G ('G', 0x47) to indicate that the user is gone. *)
begin match User.away user with
| Some _ -> "G"
| None -> "H"
end ^
(* Optionally, the highest channel membership prefix that the client has in <channel>,
if the client has one. *)
Router.membership_prefix (User.highest_membership_priv user)
let list_who t chan users =
let chan_str = match chan with
| None -> "*"
| Some chan -> Chan.name chan
in
List.iter
(fun user ->
let server = t.server_info.hostname in
let flags = user_who_flags user in
let { username; hostname; realname } = user.userinfo in
reply t ("352", [chan_str; username; hostname; server;
User.nick user; flags; "0 " ^ realname]))
users
let on_msg_who t mask =
let* me = require_registered t in
let* chan, users =
try
match name_type mask with
| `nick ->
let user = Router.find_user t.router mask in
let chan = try Some (User.find_common_channel user me)
with Not_found -> None in
Ok (chan, [user])
| `chan ->
let chan = Router.find_chan t.router mask in
Ok (Some chan, Chan.members chan)
| `invalid ->
raise Not_found
with Not_found ->
Ok (None, [])
in
list_who t chan users;
reply t ("315", [mask; "End of WHO list"]);
Ok ()
(* welcome and quit *) (* welcome and quit *)
let motd t = let motd t =
@ -737,7 +783,8 @@ let dispatch t = function
on_msg_kick t chn tgt (concat_args comment) on_msg_kick t chn tgt (concat_args comment)
| "AWAY", args -> on_msg_away t (concat_args args) | "AWAY", args -> on_msg_away t (concat_args args)
| "MODE", tgt :: args when tgt <> "" -> on_msg_mode t tgt args | "MODE", tgt :: args when tgt <> "" -> on_msg_mode t tgt args
| ("USER" | "JOIN" | "NAMES" | "PART" | "KICK" | "MODE") as cmd, _ -> | "WHO", mask :: _ when mask <> "" -> on_msg_who t mask
| ("USER" | "JOIN" | "NAMES" | "PART" | "KICK" | "MODE" | "WHO") as cmd, _ ->
Error (needmoreparams cmd) Error (needmoreparams cmd)
(* TODO: "LIST" *) (* TODO: "LIST" *)
(* TODO: "ADMIN" *) (* TODO: "ADMIN" *)
@ -748,7 +795,6 @@ let dispatch t = function
(* TODO: "HELP" *) (* TODO: "HELP" *)
(* TODO: "INFO" *) (* TODO: "INFO" *)
(* TODO: "NOTICE" *) (* TODO: "NOTICE" *)
(* TODO: "WHO" *)
(* TODO: "WHOIS" *) (* TODO: "WHOIS" *)
(* TODO: "WHOWAS" *) (* TODO: "WHOWAS" *)
(* TODO: "KILL" *) (* TODO: "KILL" *)

View File

@ -55,6 +55,11 @@ let membership chan user =
Dllist.find_node_l (fun mem -> mem.mem_chan == chan) Dllist.find_node_l (fun mem -> mem.mem_chan == chan)
user.membership |> Dllist.get user.membership |> Dllist.get
let membership_prefix = function
| Normal -> ""
| Voice -> "+"
| Operator -> "@"
let part mem = let part mem =
try try
Dllist.remove (Option.get mem.mem_in_user); Dllist.remove (Option.get mem.mem_in_user);

View File

@ -53,6 +53,7 @@ let%expect_test _ =
print_int_nl c1.member_count; [%expect "2"]; print_int_nl c1.member_count; [%expect "2"];
print_bool_nl (Router.membership c1 u1 == m11); [%expect "true"]; print_bool_nl (Router.membership c1 u1 == m11); [%expect "true"];
print_bool_nl (Router.membership c1 u2 == m12); [%expect "true"]; print_bool_nl (Router.membership c1 u2 == m12); [%expect "true"];
print_bool_nl (User.find_common_channel u1 u2 == c1); [%expect "true"];
Router.part m11; Router.part m11;
print_bool_nl (Chan.is_empty c1); [%expect "false"]; print_bool_nl (Chan.is_empty c1); [%expect "false"];
print_int_nl c1.member_count; [%expect "1"]; print_int_nl c1.member_count; [%expect "1"];

View File

@ -39,3 +39,21 @@ let membership t =
let channels t = let channels t =
Dllist.fold_r (fun m xs -> m.mem_chan :: xs) t.membership [] Dllist.fold_r (fun m xs -> m.mem_chan :: xs) t.membership []
let highest_membership_priv t =
Dllist.fold_l (fun m p -> max m.mem_priv p) t.membership Normal
let is_member t chan =
try
Dllist.find_node_l (fun m -> m.mem_chan == chan)
t.membership |> ignore;
true
with Not_found ->
false
let find_common_channel t1 t2 =
let node =
Dllist.find_node_l (fun m -> is_member t2 m.mem_chan)
t1.membership
in
(Dllist.get node).mem_chan