(** Account management operations. *) (* Account data *) let get_account_data client ~event_type = match Client.user_id client with | None -> Error (Error.Network_error "Not logged in") | Some user_id -> let user_id_str = Matrix_proto.Id.User_id.to_string user_id in let path = Printf.sprintf "/user/%s/account_data/%s" (Uri.pct_encode user_id_str) (Uri.pct_encode event_type) in match Client.get client ~path () with | Error e -> Error e | Ok body -> Client.decode_response Jsont.json body let set_account_data client ~event_type ~content = match Client.user_id client with | None -> Error (Error.Network_error "Not logged in") | Some user_id -> let user_id_str = Matrix_proto.Id.User_id.to_string user_id in let path = Printf.sprintf "/user/%s/account_data/%s" (Uri.pct_encode user_id_str) (Uri.pct_encode event_type) in match Client.encode_body Jsont.json content with | Error e -> Error e | Ok body -> match Client.put client ~path ~body () with | Error e -> Error e | Ok _ -> Ok () let get_room_account_data client ~room_id ~event_type = match Client.user_id client with | None -> Error (Error.Network_error "Not logged in") | Some user_id -> let user_id_str = Matrix_proto.Id.User_id.to_string user_id in let room_id_str = Matrix_proto.Id.Room_id.to_string room_id in let path = Printf.sprintf "/user/%s/rooms/%s/account_data/%s" (Uri.pct_encode user_id_str) (Uri.pct_encode room_id_str) (Uri.pct_encode event_type) in match Client.get client ~path () with | Error e -> Error e | Ok body -> Client.decode_response Jsont.json body let set_room_account_data client ~room_id ~event_type ~content = match Client.user_id client with | None -> Error (Error.Network_error "Not logged in") | Some user_id -> let user_id_str = Matrix_proto.Id.User_id.to_string user_id in let room_id_str = Matrix_proto.Id.Room_id.to_string room_id in let path = Printf.sprintf "/user/%s/rooms/%s/account_data/%s" (Uri.pct_encode user_id_str) (Uri.pct_encode room_id_str) (Uri.pct_encode event_type) in match Client.encode_body Jsont.json content with | Error e -> Error e | Ok body -> match Client.put client ~path ~body () with | Error e -> Error e | Ok _ -> Ok () (* Third-party identifiers *) type threepid = { medium : string; address : string; validated_at : int64; added_at : int64; } let threepid_jsont = Jsont.Object.( map (fun medium address validated_at added_at -> { medium; address; validated_at; added_at }) |> mem "medium" Jsont.string |> mem "address" Jsont.string |> mem "validated_at" Jsont.int64 |> mem "added_at" Jsont.int64 |> finish) type threepids_response = { threepids : threepid list; } let threepids_response_jsont = Jsont.Object.( map (fun threepids -> { threepids }) |> mem "threepids" (Jsont.list threepid_jsont) ~dec_absent:[] |> finish) let get_3pids client = match Client.get client ~path:"/account/3pid" () with | Error e -> Error e | Ok body -> match Client.decode_response threepids_response_jsont body with | Error e -> Error e | Ok resp -> Ok resp.threepids (* Email token request *) type email_token_request = { email : string; client_secret : string; send_attempt : int; } [@@warning "-69"] let email_token_request_jsont = Jsont.Object.( map (fun email client_secret send_attempt -> { email; client_secret; send_attempt }) |> mem "email" Jsont.string |> mem "client_secret" Jsont.string |> mem "send_attempt" Jsont.int |> finish) type token_response = { sid : string; } let token_response_jsont = Jsont.Object.( map (fun sid -> { sid }) |> mem "sid" Jsont.string |> finish) let request_email_token client ~email ~client_secret ~send_attempt = let request = { email; client_secret; send_attempt } in match Client.encode_body email_token_request_jsont request with | Error e -> Error e | Ok body -> match Client.post client ~path:"/account/3pid/email/requestToken" ~body () with | Error e -> Error e | Ok body -> match Client.decode_response token_response_jsont body with | Error e -> Error e | Ok resp -> Ok resp.sid (* MSISDN token request *) type msisdn_token_request = { country : string; phone_number : string; client_secret : string; send_attempt : int; } [@@warning "-69"] let msisdn_token_request_jsont = Jsont.Object.( map (fun country phone_number client_secret send_attempt -> { country; phone_number; client_secret; send_attempt }) |> mem "country" Jsont.string |> mem "phone_number" Jsont.string |> mem "client_secret" Jsont.string |> mem "send_attempt" Jsont.int |> finish) let request_msisdn_token client ~country ~phone_number ~client_secret ~send_attempt = let request = { country; phone_number; client_secret; send_attempt } in match Client.encode_body msisdn_token_request_jsont request with | Error e -> Error e | Ok body -> match Client.post client ~path:"/account/3pid/msisdn/requestToken" ~body () with | Error e -> Error e | Ok body -> match Client.decode_response token_response_jsont body with | Error e -> Error e | Ok resp -> Ok resp.sid (* Add 3pid *) type add_3pid_request = { client_secret : string; sid : string; } [@@warning "-69"] let add_3pid_request_jsont = Jsont.Object.( map (fun client_secret sid -> { client_secret; sid }) |> mem "client_secret" Jsont.string |> mem "sid" Jsont.string |> finish) let add_3pid client ~client_secret ~sid = let request = { client_secret; sid } in match Client.encode_body add_3pid_request_jsont request with | Error e -> Error e | Ok body -> match Client.post client ~path:"/account/3pid/add" ~body () with | Error e -> Error e | Ok _ -> Ok () (* Delete 3pid *) type delete_3pid_request = { medium : string; address : string; } [@@warning "-69"] let delete_3pid_request_jsont = Jsont.Object.( map (fun medium address -> { medium; address }) |> mem "medium" Jsont.string |> mem "address" Jsont.string |> finish) let delete_3pid client ~medium ~address = let request = { medium; address } in match Client.encode_body delete_3pid_request_jsont request with | Error e -> Error e | Ok body -> match Client.post client ~path:"/account/3pid/delete" ~body () with | Error e -> Error e | Ok _ -> Ok () (* Password change - simplified without UIAA *) type change_password_request = { new_password : string; logout_devices : bool; } [@@warning "-69"] let change_password_request_jsont = Jsont.Object.( map (fun new_password logout_devices -> { new_password; logout_devices }) |> mem "new_password" Jsont.string |> mem "logout_devices" Jsont.bool ~dec_absent:false |> finish) let change_password client ~new_password ?(logout_devices = false) () = let request = { new_password; logout_devices } in match Client.encode_body change_password_request_jsont request with | Error e -> Error e | Ok body -> match Client.post client ~path:"/account/password" ~body () with | Error e -> Error e | Ok _ -> Ok () (* Account deactivation - simplified without UIAA *) type deactivate_request = { erase : bool; } [@@warning "-69"] let deactivate_request_jsont = Jsont.Object.( map (fun erase -> { erase }) |> mem "erase" Jsont.bool ~dec_absent:false |> finish) let deactivate client ?(erase = false) () = let request = { erase } in match Client.encode_body deactivate_request_jsont request with | Error e -> Error e | Ok body -> match Client.post client ~path:"/account/deactivate" ~body () with | Error e -> Error e | Ok _ -> Ok () (* Ignored users - stored in account data *) type ignored_users_content = { ignored_users : (string * Jsont.json) list; } let ignored_users_content_jsont = let module StringMap = Map.Make(String) in let map_jsont = Jsont.Object.as_string_map Jsont.json |> Jsont.map ~dec:(fun m -> StringMap.bindings m) ~enc:(fun l -> List.to_seq l |> StringMap.of_seq) in Jsont.Object.( map (fun ignored_users -> { ignored_users }) |> mem "ignored_users" map_jsont ~dec_absent:[] |> finish) let get_ignored_users client = match get_account_data client ~event_type:"m.ignored_user_list" with | Error (Error.Matrix_error { errcode = Error.M_NOT_FOUND; _ }) -> Ok [] | Error e -> Error e | Ok json -> match Jsont_bytesrw.decode_string ignored_users_content_jsont (Result.get_ok (Jsont_bytesrw.encode_string Jsont.json json)) with | Error _ -> Ok [] | Ok content -> let user_ids = List.filter_map (fun (uid, _) -> match Matrix_proto.Id.User_id.of_string uid with | Ok id -> Some id | Error _ -> None ) content.ignored_users in Ok user_ids let ignore_user client ~user_id = match get_ignored_users client with | Error e -> Error e | Ok current -> let user_id_str = Matrix_proto.Id.User_id.to_string user_id in if List.exists (fun u -> Matrix_proto.Id.User_id.to_string u = user_id_str) current then Ok () (* Already ignored *) else let new_list = user_id :: current in let ignored_map = List.map (fun u -> (Matrix_proto.Id.User_id.to_string u, Jsont.Json.object' []) ) new_list in let content = { ignored_users = ignored_map } in match Client.encode_body ignored_users_content_jsont content with | Error e -> Error e | Ok body -> match Client.decode_response Jsont.json body with | Error e -> Error e | Ok json -> set_account_data client ~event_type:"m.ignored_user_list" ~content:json let unignore_user client ~user_id = match get_ignored_users client with | Error e -> Error e | Ok current -> let user_id_str = Matrix_proto.Id.User_id.to_string user_id in let new_list = List.filter (fun u -> Matrix_proto.Id.User_id.to_string u <> user_id_str ) current in let ignored_map = List.map (fun u -> (Matrix_proto.Id.User_id.to_string u, Jsont.Json.object' []) ) new_list in let content = { ignored_users = ignored_map } in match Client.encode_body ignored_users_content_jsont content with | Error e -> Error e | Ok body -> match Client.decode_response Jsont.json body with | Error e -> Error e | Ok json -> set_account_data client ~event_type:"m.ignored_user_list" ~content:json