encode.ml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. module E = Decoders_ezjsonm.Encode
  2. let (<:) = function
  3. | (_, None) -> fun _ -> []
  4. | (field, Some vl) -> fun ty -> [field, ty vl]
  5. let (@) field vl = (field, Some vl)
  6. let (@?) field vl = (field, vl)
  7. let (@?.) field vl = (field, match vl with | [] -> None | l -> Some l)
  8. let ptime time = time |> Ptime.to_rfc3339 ~tz_offset_s:0 |> E.string
  9. let uri ~base u = u |> Uri.resolve "https" base |> Uri.to_string |> E.string
  10. let obj ls = E.obj @@ List.flatten ls
  11. let obj0 ls =
  12. let ls = ls |> List.flatten in
  13. let ls = ("@context", `A [ `O [ "@language", `Null ] ]) :: ls in
  14. E.obj ls
  15. let ap_obj ?(context = None) ty ls =
  16. let ls = ls |> List.flatten in
  17. let ls = ("type", E.string ty) :: ls in
  18. let ls = match context with
  19. | None -> ls
  20. | Some lang -> Constants.ActivityStreams.context lang :: ls in
  21. E.obj ls
  22. let or_raw conv = function
  23. | `Raw v -> v
  24. | `Value v -> conv v
  25. (** https://www.w3.org/TR/activitystreams-core/#collections *)
  26. let collection_page ~base enc
  27. ({ id;
  28. current;
  29. first;
  30. is_ordered;
  31. items;
  32. last;
  33. next;
  34. part_of;
  35. prev;
  36. total_items
  37. }: _ Types.collection_page) =
  38. ap_obj ~context:Constants.ActivityStreams.und "OrderedCollectionPage" [
  39. "id" @ id <: uri ~base;
  40. "current" @? current <: uri ~base;
  41. "first" @? first <: uri ~base;
  42. "last" @? last <: uri ~base;
  43. "next" @? next <: uri ~base;
  44. "partOf" @? part_of <: uri ~base;
  45. "prev" @? prev <: uri ~base;
  46. "totalItems" @? total_items <: E.int;
  47. (match is_ordered with
  48. | true -> "orderedItems"
  49. | false -> "items") @ items <: E.list enc
  50. ]
  51. let collection ~base enc
  52. ({ id;
  53. current;
  54. first;
  55. is_ordered;
  56. items;
  57. last;
  58. total_items;
  59. }: _ Types.collection) =
  60. ap_obj ~context:Constants.ActivityStreams.und "OrderedCollection" [
  61. "id" @ id <: uri ~base;
  62. "current" @? current <: uri ~base;
  63. "first" @? first <: uri ~base;
  64. "last" @? last <: uri ~base;
  65. "totalItems" @? total_items <: E.int;
  66. (match is_ordered with
  67. | true -> "orderedItems"
  68. | false -> "items") @? items <: E.list enc
  69. ]
  70. let create ?(context = None) ~base enc ({ id; actor; published; to_; cc; direct_message; obj(*(*; raw=_*)*) }:
  71. _ Types.create) =
  72. ap_obj ~context "Create" [
  73. "id" @ id <: uri ~base;
  74. "actor" @ actor <: uri ~base;
  75. "published" @? published <: ptime;
  76. "to" @ to_ <: E.(list (uri ~base));
  77. "cc" @ cc <: E.(list (uri ~base));
  78. "directMessage" @ direct_message <: E.bool;
  79. "object" @ obj <: enc;
  80. ]
  81. let update ?(context = None) ~base enc ({ id; actor; published; to_; cc; direct_message; obj(*(*; raw=_*)*) }:
  82. _ Types.update) =
  83. ap_obj ~context "Update" [
  84. "id" @ id <: uri ~base;
  85. "actor" @ actor <: uri ~base;
  86. "published" @? published <: ptime;
  87. "to" @ to_ <: E.(list (uri ~base));
  88. "cc" @ cc <: E.(list (uri ~base));
  89. "directMessage" @ direct_message <: E.bool;
  90. "object" @ obj <: enc;
  91. ]
  92. let announce ~base enc ({ id; actor; published; to_; cc; obj(*(*; raw=_*)*) } : _ Types.announce) =
  93. ap_obj ~context:Constants.ActivityStreams.und "Announce" [
  94. "id" @ id <: uri ~base;
  95. "actor" @ actor <: uri ~base;
  96. "published" @? published <: ptime;
  97. "to" @ to_ <: E.(list (uri ~base));
  98. "cc" @ cc <: E.(list (uri ~base));
  99. "object" @ obj <: enc;
  100. ]
  101. let accept ~base enc ({ id; actor; published; obj(*; raw=_*) } : _ Types.accept) =
  102. ap_obj ~context:Constants.ActivityStreams.und "Accept" [
  103. "id" @ id <: uri ~base;
  104. "actor" @ actor <: uri ~base;
  105. "published" @? published <: ptime;
  106. "object" @ obj <: enc;
  107. ]
  108. let reject ~base enc ({ id; actor; published; obj(*; raw=_*) } : _ Types.reject) =
  109. ap_obj ~context:Constants.ActivityStreams.und "Reject" [
  110. "id" @ id <: uri ~base;
  111. "actor" @ actor <: uri ~base;
  112. "published" @? published <: ptime;
  113. "object" @ obj <: enc;
  114. ]
  115. let undo ?(context = Constants.ActivityStreams.und) ~base enc ({ id; actor; published; obj(*; raw=_*) } : _ Types.undo) =
  116. ap_obj ~context "Undo" [
  117. "id" @ id <: uri ~base;
  118. "actor" @ actor <: uri ~base;
  119. "published" @? published <: ptime;
  120. "object" @ obj <: enc;
  121. ]
  122. let delete ~base enc ({ id; actor; published; obj(*; raw=_*) } : _ Types.delete) =
  123. ap_obj ~context:Constants.ActivityStreams.und "Delete" [
  124. "id" @ id <: uri ~base;
  125. "actor" @ actor <: uri ~base;
  126. "published" @? published <: ptime;
  127. "object" @ obj <: enc;
  128. ]
  129. (* https://www.w3.org/TR/activitystreams-vocabulary/#dfn-link *)
  130. let link ?(base = Uri.empty) ({ href; name; name_map; rel } : Types.link) =
  131. let _ = name_map
  132. and _ = rel in
  133. obj [
  134. "type" @ "Link" <: E.string;
  135. "href" @ href <: uri ~base;
  136. "name" @? name <: E.string;
  137. "nameMap" @?. [] <: E.obj;
  138. ]
  139. (** * Objects *)
  140. let public_key ~base (key: Types.public_key) =
  141. obj0 [
  142. "id" @ key.id <: uri ~base;
  143. "owner" @? key.owner <: uri ~base;
  144. "publicKeyPem" @ key.pem <: E.string;
  145. "signatureAlgorithm" @? key.signatureAlgorithm <: E.string;
  146. ]
  147. let property_value (v : Types.property_value) =
  148. let kv f (k,v) = (k,f v) in
  149. let name_map = v.name_map |> List.map (kv E.string) in
  150. let value_map = v.value_map |> List.map (kv E.string) in
  151. obj [
  152. "type" @ "PropertyValue" <: E.string;
  153. "name" @ v.name <: E.string;
  154. "nameMap" @?. name_map <: E.obj;
  155. "value" @ v.value <: E.string;
  156. "valueMap" @?. value_map <: E.obj;
  157. ]
  158. let image ~base url =
  159. obj [
  160. "type" @ "Image" <: E.string;
  161. (* mediatype? *)
  162. "url" @ url <: uri ~base;
  163. ]
  164. let person ~base ~context
  165. ({ id; name; name_map; url; inbox; outbox;
  166. preferred_username; preferred_username_map; summary; summary_map;
  167. manually_approves_followers;
  168. discoverable; generator; followers; following;
  169. public_key=key; published; attachment; icon=ic; image=im(*; raw=_*) }: Types.person) =
  170. let name_map = name_map |> List.map (fun (k,v) -> (k,E.string v)) in
  171. let preferred_username_map = preferred_username_map |> List.map (fun (k,v) -> (k,E.string v)) in
  172. let summary_map = summary_map |> List.map (fun (k,v) -> (k,E.string v)) in
  173. ap_obj ~context "Person" [
  174. "id" @ id <: uri ~base;
  175. "inbox" @ inbox <: uri ~base;
  176. "outbox" @ outbox <: uri ~base;
  177. "followers" @? followers <: uri ~base;
  178. "following" @? following <: uri ~base;
  179. "name" @? name <: E.string;
  180. "nameMap" @?. name_map <: E.obj;
  181. "url" @?. url <: E.list (uri ~base);
  182. "preferredUsername" @? preferred_username <: E.string;
  183. "preferredUsernameMap" @?. preferred_username_map <: E.obj;
  184. "summary" @? summary <: E.string;
  185. "summaryMap" @?. summary_map <: E.obj;
  186. "publicKey" @ key <: public_key ~base;
  187. "published" @? published <: ptime;
  188. "manuallyApprovesFollowers" @ manually_approves_followers <: E.bool;
  189. "discoverable" @ discoverable <: E.bool;
  190. "generator" @? generator <: link;
  191. "attachment" @ attachment <: E.list property_value;
  192. "icon" @? ic <: image ~base;
  193. "image" @? im <: image ~base;
  194. ]
  195. let state = function
  196. `Pending -> E.string "pending"
  197. | `Cancelled -> E.string "cancelled"
  198. let follow ?(context = Constants.ActivityStreams.und) ~base ({ id; actor; cc; object_; to_; state=st(*; raw=_*) }: Types.follow) =
  199. ap_obj ~context "Follow" [
  200. "id" @ id <: uri ~base;
  201. "actor" @ actor <: uri ~base;
  202. "to" @ to_ <: E.list (uri ~base);
  203. "cc" @ cc <: E.list (uri ~base);
  204. "object" @ object_ <: uri ~base;
  205. "state" @? st <: state;
  206. ]
  207. (* https://www.w3.org/TR/activitystreams-vocabulary/#microsyntaxes *)
  208. let tag ~base ({ ty; href; name }: Types.tag) =
  209. let ty,pre = match ty with
  210. | `Mention -> "Mention","@"
  211. | `Hashtag -> "Hashtag","#" in
  212. ap_obj ty [
  213. "href" @ href <: uri ~base;
  214. "name" @ (pre ^ name) <: E.string;
  215. ]
  216. let attachment ~base ({media_type; name; url; type_}: Types.attachment) =
  217. obj [
  218. "type" @? type_ <: E.string;
  219. "mediaType" @? media_type <: E.string;
  220. "name" @? name <: E.string;
  221. "url" @ url <: uri ~base;
  222. ]
  223. let note ?(context = None)
  224. ~base
  225. ({ id; actor; to_; in_reply_to; cc; media_type; content; content_map; sensitive; source; summary; summary_map;
  226. attachment=att;
  227. published; tags; url(*; raw=_*) }: Types.note) =
  228. let in_reply_to = match in_reply_to with
  229. | [] -> None (* ActivityStreams goes insane on optional lists being present but empty. https://www.w3.org/TR/activitystreams-vocabulary/#dfn-inreplyto *)
  230. | l -> Some l in
  231. let content_map = content_map |> List.map (fun (k,v) -> (k,E.string v)) in
  232. let summary_map = summary_map |> List.map (fun (k,v) -> (k,E.string v)) in
  233. let att = match att with [] -> None | _ -> Some att in
  234. ap_obj ~context "Note" [
  235. "id" @ id <: uri ~base;
  236. "actor" @ actor <: uri ~base;
  237. "attachment" @? att <: E.list (attachment ~base);
  238. "to" @ to_ <: E.list (uri ~base);
  239. "cc" @ cc <: E.list (uri ~base);
  240. "inReplyTo" @? in_reply_to <: E.list (uri ~base);
  241. "mediaType" @? media_type <: E.string;
  242. "content" @ content <: E.string;
  243. "contentMap" @?. content_map<: E.obj;
  244. "sensitive" @ sensitive <: E.bool;
  245. "source" @? source <: uri ~base;
  246. "summary" @? summary <: E.string;
  247. "summaryMap" @?. summary_map<: E.obj;
  248. "published" @? published <: ptime;
  249. "tags" @ tags <: E.list (tag ~base);
  250. "url" @ url <: E.list (uri ~base);
  251. ]
  252. let block ~base ({ id; obj; published; actor(*; raw=_*) }: Types.block) =
  253. ap_obj ~context:Constants.ActivityStreams.und "Block" [
  254. "id" @ id <: uri ~base;
  255. "object" @ obj <: uri ~base;
  256. "actor" @ actor <: uri ~base;
  257. "published" @? published <: ptime;
  258. ]
  259. let like ~base ({ id; actor; published; obj(*; raw=_*) }: Types.like) =
  260. ap_obj ~context:Constants.ActivityStreams.und "Like" [
  261. "id" @ id <: uri ~base;
  262. "actor" @ actor <: uri ~base;
  263. "object" @ obj <: uri ~base;
  264. "published" @? published <: ptime;
  265. ]
  266. let core_obj ?(context = Constants.ActivityStreams.und) ~base : Types.core_obj E.encoder = function
  267. | `Block b -> block ~base b
  268. | `Follow f -> follow ~base f
  269. | `Like l -> like ~base l
  270. | `Link r -> E.string r
  271. | `Note n -> note ~base n
  272. | `Person p -> person ~base ~context p
  273. let event ~base enc : _ Types.event E.encoder = function
  274. | `Accept a -> accept ~base enc a
  275. | `Reject a -> reject ~base enc a
  276. | `Announce a -> announce ~base enc a
  277. | `Create c -> create ~base enc c
  278. | `Update c -> update ~base enc c
  279. | `Delete d -> delete ~base enc d
  280. | `Undo u -> undo ~base enc u
  281. let object_ ~base : Types.obj E.encoder = function
  282. | #Types.core_obj as c -> core_obj ~base c
  283. | #Types.core_event as e -> event ~base (core_obj ~base) e
  284. module Webfinger = struct
  285. let ty = function
  286. | `ActivityJson_ -> E.string Constants.ContentType._app_act_json
  287. | `ActivityJsonLd -> E.string Constants.ContentType.app_jlda
  288. | `Atom -> E.string Constants.ContentType.app_atom_xml
  289. | `Html -> E.string Constants.ContentType.text_html
  290. | `Json -> E.string Constants.ContentType.app_json
  291. | `Xml -> E.string Constants.ContentType.text_xml
  292. let link ~base = function
  293. | Types.Webfinger.Self (t, href) -> obj [
  294. "href" @ href <: uri ~base;
  295. "rel" @ Constants.Webfinger.self_rel <: E.string;
  296. "type" @ t <: ty;
  297. ]
  298. | ProfilePage (t, href) -> obj [
  299. "href" @ href <: uri ~base;
  300. "rel" @ Constants.Webfinger.profile_page <: E.string;
  301. "type" @ t <: ty;
  302. ]
  303. | Alternate (t, href) -> obj [
  304. "href" @ href <: uri ~base;
  305. "rel" @ Constants.Webfinger.alternate <: E.string;
  306. "type" @ t <: ty;
  307. ]
  308. | OStatusSubscribe template -> obj [
  309. "rel" @ Constants.Webfinger.ostatus_rel <: E.string;
  310. "template" @ template <: E.string;
  311. ]
  312. let query_result ~base ({subject;aliases;links}: Types.Webfinger.query_result) =
  313. let l = ( "links" @ links <: E.list (link ~base); ) :: [] in
  314. let l = match aliases with
  315. | [] -> l
  316. | _ -> ( "aliases" @ aliases <: E.(list string); ) :: l in
  317. let l = ( "subject" @ subject <: E.string; ) :: l in
  318. obj l
  319. end