encode.ml 13 KB

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