Matrix protocol in OCaml, Eio specialised
at main 232 lines 8.3 kB view raw
1(** Message sending and retrieval. *) 2 3(* Transaction ID generator *) 4let next_txn_id = 5 let counter = ref 0 in 6 fun () -> 7 incr counter; 8 Printf.sprintf "%d_%f" !counter (Unix.gettimeofday ()) 9 10(* Send event response *) 11type send_response = { 12 event_id : Matrix_proto.Id.Event_id.t; 13} 14 15let send_response_jsont = 16 Jsont.Object.( 17 map (fun event_id -> { event_id }) 18 |> mem "event_id" Matrix_proto.Id.Event_id.jsont 19 |> finish) 20 21(* Generic send event *) 22let send_event client ~room_id ~event_type ~content = 23 let room_id_str = Matrix_proto.Id.Room_id.to_string room_id in 24 let txn_id = next_txn_id () in 25 let path = Printf.sprintf "/rooms/%s/send/%s/%s" 26 (Uri.pct_encode room_id_str) 27 (Uri.pct_encode event_type) 28 (Uri.pct_encode txn_id) 29 in 30 match Client.encode_body Jsont.json content with 31 | Error e -> Error e 32 | Ok body -> 33 match Client.put client ~path ~body () with 34 | Error e -> Error e 35 | Ok body -> 36 match Client.decode_response send_response_jsont body with 37 | Error e -> Error e 38 | Ok resp -> Ok resp.event_id 39 40(* Text message content *) 41type text_content = { 42 msgtype : string; 43 body : string; 44 format : string option; 45 formatted_body : string option; 46} [@@warning "-69"] 47 48let text_content_jsont = 49 Jsont.Object.( 50 map (fun msgtype body format formatted_body -> 51 { msgtype; body; format; formatted_body }) 52 |> mem "msgtype" Jsont.string ~enc:(fun t -> t.msgtype) 53 |> mem "body" Jsont.string ~enc:(fun t -> t.body) 54 |> opt_mem "format" Jsont.string ~enc:(fun t -> t.format) 55 |> opt_mem "formatted_body" Jsont.string ~enc:(fun t -> t.formatted_body) 56 |> finish) 57 58let send_text client ~room_id ~body ?format ?formatted_body () = 59 let content = { msgtype = "m.text"; body; format; formatted_body } in 60 match Client.encode_body text_content_jsont content with 61 | Error e -> Error e 62 | Ok json_str -> 63 match Client.decode_response Jsont.json json_str with 64 | Error e -> Error e 65 | Ok json -> send_event client ~room_id ~event_type:"m.room.message" ~content:json 66 67let send_emote client ~room_id ~body () = 68 let content = { msgtype = "m.emote"; body; format = None; formatted_body = None } in 69 match Client.encode_body text_content_jsont content with 70 | Error e -> Error e 71 | Ok json_str -> 72 match Client.decode_response Jsont.json json_str with 73 | Error e -> Error e 74 | Ok json -> send_event client ~room_id ~event_type:"m.room.message" ~content:json 75 76let send_notice client ~room_id ~body () = 77 let content = { msgtype = "m.notice"; body; format = None; formatted_body = None } in 78 match Client.encode_body text_content_jsont content with 79 | Error e -> Error e 80 | Ok json_str -> 81 match Client.decode_response Jsont.json json_str with 82 | Error e -> Error e 83 | Ok json -> send_event client ~room_id ~event_type:"m.room.message" ~content:json 84 85(* Media message content *) 86type media_content = { 87 msgtype : string; 88 body : string; 89 url : string; 90 info : Jsont.json option; 91} [@@warning "-69"] 92 93let media_content_jsont = 94 Jsont.Object.( 95 map (fun msgtype body url info -> 96 { msgtype; body; url; info }) 97 |> mem "msgtype" Jsont.string ~enc:(fun t -> t.msgtype) 98 |> mem "body" Jsont.string ~enc:(fun t -> t.body) 99 |> mem "url" Jsont.string ~enc:(fun t -> t.url) 100 |> opt_mem "info" Jsont.json ~enc:(fun t -> t.info) 101 |> finish) 102 103let send_image client ~room_id ~body ~url ?info () = 104 let content = { msgtype = "m.image"; body; url; info } in 105 match Client.encode_body media_content_jsont content with 106 | Error e -> Error e 107 | Ok json_str -> 108 match Client.decode_response Jsont.json json_str with 109 | Error e -> Error e 110 | Ok json -> send_event client ~room_id ~event_type:"m.room.message" ~content:json 111 112let send_file client ~room_id ~body ~url ?info () = 113 let content = { msgtype = "m.file"; body; url; info } in 114 match Client.encode_body media_content_jsont content with 115 | Error e -> Error e 116 | Ok json_str -> 117 match Client.decode_response Jsont.json json_str with 118 | Error e -> Error e 119 | Ok json -> send_event client ~room_id ~event_type:"m.room.message" ~content:json 120 121(* Redaction *) 122type redact_request = { 123 reason : string option; 124} [@@warning "-69"] 125 126let redact_request_jsont = 127 Jsont.Object.( 128 map (fun reason -> { reason }) 129 |> opt_mem "reason" Jsont.string ~enc:(fun t -> t.reason) 130 |> finish) 131 132let redact client ~room_id ~event_id ?reason () = 133 let room_id_str = Matrix_proto.Id.Room_id.to_string room_id in 134 let event_id_str = Matrix_proto.Id.Event_id.to_string event_id in 135 let txn_id = next_txn_id () in 136 let path = Printf.sprintf "/rooms/%s/redact/%s/%s" 137 (Uri.pct_encode room_id_str) 138 (Uri.pct_encode event_id_str) 139 (Uri.pct_encode txn_id) 140 in 141 let request = { reason } in 142 match Client.encode_body redact_request_jsont request with 143 | Error e -> Error e 144 | Ok body -> 145 match Client.put client ~path ~body () with 146 | Error e -> Error e 147 | Ok body -> 148 match Client.decode_response send_response_jsont body with 149 | Error e -> Error e 150 | Ok resp -> Ok resp.event_id 151 152(* Get messages *) 153type direction = Forward | Backward 154 155type messages_response = { 156 start : string; 157 end_ : string option; 158 chunk : Matrix_proto.Event.Raw_event.t list; 159 state : Matrix_proto.Event.Raw_event.t list; 160} 161 162let messages_response_jsont = 163 Jsont.Object.( 164 map (fun start end_ chunk state -> 165 { start; end_; chunk; state }) 166 |> mem "start" Jsont.string 167 |> opt_mem "end" Jsont.string ~enc:(fun t -> t.end_) 168 |> mem "chunk" (Jsont.list Matrix_proto.Event.Raw_event.jsont) ~dec_absent:[] 169 |> mem "state" (Jsont.list Matrix_proto.Event.Raw_event.jsont) ~dec_absent:[] 170 |> finish) 171 172let get_messages client ~room_id ~from ~dir ?limit ?filter () = 173 let room_id_str = Matrix_proto.Id.Room_id.to_string room_id in 174 let path = Printf.sprintf "/rooms/%s/messages" (Uri.pct_encode room_id_str) in 175 let dir_str = match dir with Forward -> "f" | Backward -> "b" in 176 let query = 177 [("from", from); ("dir", dir_str)] 178 |> (fun q -> match limit with Some l -> ("limit", string_of_int l) :: q | None -> q) 179 |> (fun q -> match filter with Some f -> ("filter", f) :: q | None -> q) 180 in 181 match Client.get client ~path ~query () with 182 | Error e -> Error e 183 | Ok body -> Client.decode_response messages_response_jsont body 184 185(* Get single event *) 186let get_event client ~room_id ~event_id = 187 let room_id_str = Matrix_proto.Id.Room_id.to_string room_id in 188 let event_id_str = Matrix_proto.Id.Event_id.to_string event_id in 189 let path = Printf.sprintf "/rooms/%s/event/%s" 190 (Uri.pct_encode room_id_str) 191 (Uri.pct_encode event_id_str) 192 in 193 match Client.get client ~path () with 194 | Error e -> Error e 195 | Ok body -> Client.decode_response Matrix_proto.Event.Raw_event.jsont body 196 197(* Get context *) 198type context = { 199 start : string; 200 end_ : string; 201 event : Matrix_proto.Event.Raw_event.t; 202 events_before : Matrix_proto.Event.Raw_event.t list; 203 events_after : Matrix_proto.Event.Raw_event.t list; 204 state : Matrix_proto.Event.Raw_event.t list; 205} 206 207let context_jsont = 208 Jsont.Object.( 209 map (fun start end_ event events_before events_after state -> 210 { start; end_; event; events_before; events_after; state }) 211 |> mem "start" Jsont.string 212 |> mem "end" Jsont.string 213 |> mem "event" Matrix_proto.Event.Raw_event.jsont 214 |> mem "events_before" (Jsont.list Matrix_proto.Event.Raw_event.jsont) ~dec_absent:[] 215 |> mem "events_after" (Jsont.list Matrix_proto.Event.Raw_event.jsont) ~dec_absent:[] 216 |> mem "state" (Jsont.list Matrix_proto.Event.Raw_event.jsont) ~dec_absent:[] 217 |> finish) 218 219let get_context client ~room_id ~event_id ?limit () = 220 let room_id_str = Matrix_proto.Id.Room_id.to_string room_id in 221 let event_id_str = Matrix_proto.Id.Event_id.to_string event_id in 222 let path = Printf.sprintf "/rooms/%s/context/%s" 223 (Uri.pct_encode room_id_str) 224 (Uri.pct_encode event_id_str) 225 in 226 let query = match limit with 227 | Some l -> Some [("limit", string_of_int l)] 228 | None -> None 229 in 230 match Client.get client ~path ?query () with 231 | Error e -> Error e 232 | Ok body -> Client.decode_response context_jsont body