Skip to main content

max / balanced_breakfast

3.9 KB · 181 lines History Blame Raw
1 // Lobste.rs link-aggregation feed. Fetches stories from the public JSON
2 // API with hottest/newest/active ordering. Paginates by page number.
3
4 const LOBSTERS_URL = "https://lobste.rs";
5
6 fn id() {
7 "lobsters"
8 }
9
10 fn name() {
11 "Lobsters"
12 }
13
14 fn capabilities() {
15 #{
16 supports_pagination: true
17 }
18 }
19
20 fn config_schema() {
21 #{
22 description: "Fetch stories from Lobste.rs, a computing-focused link aggregation community.",
23 fields: [
24 #{
25 key: "feed_type",
26 label: "Feed Type",
27 field_type: "select",
28 required: true,
29 description: "Which stories to fetch",
30 default_value: "hottest",
31 options: ["hottest", "newest", "active"]
32 },
33 #{
34 key: "limit",
35 label: "Items per fetch",
36 field_type: "text",
37 description: "Number of stories to fetch (max 50)",
38 default_value: "25",
39 placeholder: "25"
40 }
41 ]
42 }
43 }
44
45 fn fetch(config, cursor) {
46 let feed_type = "hottest";
47 if config.feed_type != () {
48 feed_type = config.feed_type;
49 }
50
51 let limit = 25;
52 if config.limit != () {
53 let parsed = parse_int(config.limit);
54 if parsed != () && parsed > 0 {
55 if parsed > 50 {
56 limit = 50;
57 } else {
58 limit = parsed;
59 }
60 }
61 }
62
63 // Parse cursor for page number
64 let page = 1;
65 if cursor != () {
66 let parsed = parse_int(cursor);
67 if parsed != () && parsed > 0 {
68 page = parsed;
69 }
70 }
71
72 let url = LOBSTERS_URL + "/" + feed_type + ".json?page=" + page;
73 let stories = http_get_json(url);
74
75 let items = [];
76 let count = 0;
77
78 for story in stories {
79 if count >= limit {
80 break;
81 }
82
83 let item = parse_story(story);
84 if item != () {
85 items.push(item);
86 count += 1;
87 }
88 }
89
90 let has_more = items.len() >= limit;
91 let result = #{
92 items: items,
93 has_more: has_more
94 };
95
96 if has_more {
97 result.next_cursor = "" + (page + 1);
98 }
99
100 result
101 }
102
103 fn parse_story(story) {
104 if story.title == () {
105 return ();
106 }
107
108 let title = story.title;
109
110 let score = 0;
111 if story.score != () {
112 score = story.score;
113 }
114
115 let comments = 0;
116 if story.comment_count != () {
117 comments = story.comment_count;
118 }
119
120 let author = "anonymous";
121 if story.submitter_user != () {
122 if story.submitter_user.username != () {
123 author = story.submitter_user.username;
124 }
125 }
126
127 let url = "";
128 if story.url != () && story.url != "" {
129 url = story.url;
130 } else if story.comments_url != () {
131 url = story.comments_url;
132 }
133
134 let created = timestamp_now();
135 if story.created_at != () {
136 created = story.created_at;
137 }
138
139 let short_id = "";
140 if story.short_id != () {
141 short_id = story.short_id;
142 }
143
144 // Build tags from story tags
145 let tags = ["lobsters"];
146 if story.tags != () {
147 for tag in story.tags {
148 tags.push(tag);
149 }
150 }
151
152 let secondary = "" + score + " pts · " + comments + " comments";
153
154 // Description/body
155 let body = ();
156 if story.description != () && story.description != "" {
157 body = story.description;
158 }
159
160 #{
161 id: #{ source: "lobsters", item_id: short_id },
162 bite: #{
163 author: author,
164 text: truncate(title, 100),
165 secondary: secondary,
166 indicator: "🦞"
167 },
168 content: #{
169 title: title,
170 body: body,
171 url: url
172 },
173 meta: #{
174 source_name: "Lobsters",
175 published_at: created,
176 score: score,
177 tags: tags
178 }
179 }
180 }
181