From 3658be9fe4cfc0f9f1f251fbb2afe5850967ba7c Mon Sep 17 00:00:00 2001 From: tali Date: Wed, 24 Jan 2024 12:38:20 -0500 Subject: [PATCH] support TOPIC command --- lib/server/chan.ml | 1 + lib/server/connection.ml | 50 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/lib/server/chan.ml b/lib/server/chan.ml index 918e69c..7f53a43 100644 --- a/lib/server/chan.ml +++ b/lib/server/chan.ml @@ -16,6 +16,7 @@ let make ~name = let name t = t.name let topic t = t.topic +let set_topic t s = t.topic <- s let mode t = t.chan_mode let set_mode t new_mode = t.chan_mode <- new_mode let limit t = t.chan_limit diff --git a/lib/server/connection.ml b/lib/server/connection.ml index 603ff92..360374e 100644 --- a/lib/server/connection.ml +++ b/lib/server/connection.ml @@ -39,8 +39,13 @@ let reply t (num, params) = | Some me -> User.nick me | None -> "*" in + let always_trailing = match num with + | "324" | "332" | "353" -> true + | _ -> false + in Outbox.send t.outbox - (Msg.make ~prefix num (target :: params)) + (Msg.make num (target :: params) + ~prefix ~always_trailing) let tryagain cmd = "263", [cmd; "Please wait a while and try again."] let nosuchnick tgt = "401", [tgt; "No such nick/channel"] @@ -264,6 +269,41 @@ let on_msg_names t name = list_names t chan; Ok () +let get_topic ?(reply_if_missing=true) t chan = + match Chan.topic chan with + | Some topic -> + reply t ("332", [Chan.name chan; topic]) + (* TODO: RPL_TOPICWHOTIME ? *) + | None -> + if reply_if_missing then + reply t ("331", [Chan.name chan; "No topic is set"]) + +let set_topic chan topic = + Chan.set_topic chan topic + +let on_msg_topic t name args = + let* me = require_registered t in + let* chan = + try + match name_type name with + | `chan -> Ok (Router.find_chan t.router name) + | `nick | `invalid -> raise Not_found + with Not_found -> + Error (nosuchchannel name) + in + match args with + | [] -> + (* TODO: if +s then don't send topic to non-members *) + get_topic t chan; + Ok () + | args -> + (* TODO: if +t then only allow +o to set topic *) + let topic = String.concat " " args in + let msg = Msg.make "TOPIC" [Chan.name chan; topic] ~always_trailing:true in + Router.relay msg ~from:me [`to_chan chan; `to_self]; + set_topic chan (if args = [""] then None else Some topic); + Ok () + let join t user chan = (* TODO: check channel mode +k, +l *) let msg = Msg.make "JOIN" [Chan.name chan] in @@ -284,7 +324,10 @@ let on_msg_join t name = try match name_type name with | `chan -> Ok (Router.find_chan t.router name) - | _ -> Error (nosuchchannel name) + | `nick | `invalid -> + (* pretend malformed channel name means the channel doesn't exist and + DON'T try to make a new channel *) + Error (nosuchchannel name) with Not_found -> debug (fun m -> m "making new channel %S" name); Ok (Chan.make ~name) @@ -294,7 +337,7 @@ let on_msg_join t name = Ok () | exception Not_found -> join t me chan; - (* TODO: send channel topic *) + get_topic t chan ~reply_if_missing:false; list_names t chan; Ok () @@ -471,6 +514,7 @@ let dispatch t = function | "PRIVMSG", tgt :: msg :: _ -> on_msg_privmsg t tgt msg | "JOIN", tgt :: _ when tgt <> "" -> on_msg_join t tgt | "NAMES", tgt :: _ when tgt <> "" -> on_msg_names t tgt + | "TOPIC", tgt :: args when tgt <> "" -> on_msg_topic t tgt args | "PART", tgt :: reason when tgt <> "" -> on_msg_part t tgt reason | "MODE", tgt :: args when tgt <> "" -> on_msg_mode t tgt args | ("USER" | "JOIN" | "NAMES" | "PART" | "MODE") as cmd, _ ->