My agentic slop goes here. Not intended for anyone else!
1(*
2 * Copyright (c) 2014, OCaml.org project
3 * Copyright (c) 2015 KC Sivaramakrishnan <sk826@cl.cam.ac.uk>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *)
17
18let src = Logs.Src.create "river" ~doc:"River RSS/Atom aggregator"
19module Log = (val Logs.src_log src : Logs.LOG)
20
21(* Keep Client module internal *)
22module Internal_client = Client
23
24(* Abstract session type *)
25type session = Client.t
26
27type source = Feed.source = { name : string; url : string }
28type feed = Feed.t
29type post = Post.t
30
31(* Session management *)
32let init ~sw env =
33 Log.info (fun m -> m "Initializing River session");
34 Internal_client.create ~sw env
35
36let with_session env f =
37 Log.info (fun m -> m "Creating River session");
38 Internal_client.with_client env f
39
40(* Feed operations *)
41let fetch session source =
42 Log.info (fun m -> m "Fetching feed: %s" source.name);
43 Feed.fetch session source
44
45let name feed = feed.Feed.name
46let url feed = feed.Feed.url
47
48let posts feeds =
49 Log.info (fun m -> m "Aggregating posts from %d feed(s)" (List.length feeds));
50 let result = Post.get_posts feeds in
51 Log.info (fun m -> m "Aggregated %d posts total" (List.length result));
52 result
53
54let title post = post.Post.title
55let link post = post.Post.link
56let date post = post.Post.date
57let feed post = post.Post.feed
58let author post = post.Post.author
59let email post = post.Post.email
60let content post = Soup.to_string post.Post.content
61let id post = post.Post.id
62let tags post = post.Post.tags
63let summary post = post.Post.summary
64
65let meta_description _post =
66 (* TODO: This requires environment for HTTP access *)
67 Log.debug (fun m -> m "meta_description not implemented (requires environment)");
68 None
69
70let seo_image _post =
71 (* TODO: This requires environment for HTTP access *)
72 Log.debug (fun m -> m "seo_image not implemented (requires environment)");
73 None
74
75let create_atom_entries posts =
76 Log.info (fun m -> m "Creating Atom entries for %d posts" (List.length posts));
77 Post.mk_entries posts
78
79(* JSONFeed support *)
80let create_jsonfeed_items posts =
81 Log.info (fun m -> m "Creating JSONFeed items for %d posts" (List.length posts));
82 Post.mk_jsonfeed_items posts
83
84let create_jsonfeed ~title ?home_page_url ?feed_url ?description ?icon ?favicon posts =
85 Log.info (fun m -> m "Creating JSONFeed with %d posts" (List.length posts));
86 let items = create_jsonfeed_items posts in
87 Jsonfeed.create ~title ?home_page_url ?feed_url ?description ?icon ?favicon ~items ()
88
89let jsonfeed_to_string ?(minify = false) jsonfeed =
90 match Jsonfeed.to_string ~minify jsonfeed with
91 | Ok s -> Ok s
92 | Error err -> Error (Jsont.Error.to_string err)
93
94type feed_content =
95 | Atom of Syndic.Atom.feed
96 | Rss2 of Syndic.Rss2.channel
97 | JSONFeed of Jsonfeed.t
98
99let feed_content feed =
100 match feed.Feed.content with
101 | Feed.Atom f -> Atom f
102 | Feed.Rss2 ch -> Rss2 ch
103 | Feed.Json jf -> JSONFeed jf