Skip to main content

max / balanced_breakfast

3.5 KB · 161 lines History Blame Raw
1 // XKCD comic feed. Fetches recent comics from the xkcd JSON info endpoint.
2 // Walks backward from the latest comic number; skips #404 (intentionally missing).
3 // Renders the comic image inline with alt-text below.
4
5 const XKCD_URL = "https://xkcd.com";
6
7 fn id() {
8 "xkcd"
9 }
10
11 fn name() {
12 "XKCD"
13 }
14
15 fn capabilities() {
16 #{
17 supports_pagination: true
18 }
19 }
20
21 fn config_schema() {
22 #{
23 description: "Fetch comics from XKCD. Shows the latest comics with title, alt text, and image.",
24 fields: [
25 #{
26 key: "count",
27 label: "Comics to fetch",
28 field_type: "text",
29 description: "Number of recent comics to fetch (max 20)",
30 default_value: "10",
31 placeholder: "10"
32 }
33 ]
34 }
35 }
36
37 fn fetch(config, cursor) {
38 let count = 10;
39 if config.count != () {
40 let parsed = parse_int(config.count);
41 if parsed != () && parsed > 0 {
42 if parsed > 20 {
43 count = 20;
44 } else {
45 count = parsed;
46 }
47 }
48 }
49
50 // Get the latest comic to find the current number
51 let latest = http_get_json(XKCD_URL + "/info.0.json");
52 if latest == () {
53 return #{ items: [], has_more: false };
54 }
55
56 let current_num = latest.num;
57
58 // Parse cursor for starting comic number
59 let start_num = current_num;
60 if cursor != () {
61 let parsed = parse_int(cursor);
62 if parsed != () && parsed > 0 {
63 start_num = parsed;
64 }
65 }
66
67 let items = [];
68 let num = start_num;
69 let fetched = 0;
70
71 while fetched < count && num > 0 {
72 // Comic 404 doesn't exist (it's a joke)
73 if num == 404 {
74 num -= 1;
75 continue;
76 }
77
78 let comic = ();
79 if num == current_num {
80 comic = latest;
81 } else {
82 comic = http_get_json(XKCD_URL + "/" + num + "/info.0.json");
83 }
84
85 if comic != () {
86 let item = parse_comic(comic, XKCD_URL);
87 if item != () {
88 items.push(item);
89 fetched += 1;
90 }
91 }
92
93 num -= 1;
94 }
95
96 let has_more = num > 0;
97 let result = #{
98 items: items,
99 has_more: has_more
100 };
101
102 if has_more {
103 result.next_cursor = "" + num;
104 }
105
106 result
107 }
108
109 fn parse_comic(comic, base_url) {
110 if comic.title == () {
111 return ();
112 }
113
114 let title = comic.title;
115 let num = comic.num;
116
117 let alt = "";
118 if comic.alt != () {
119 alt = comic.alt;
120 }
121
122 let img = "";
123 if comic.img != () {
124 img = comic.img;
125 }
126
127 let url = base_url + "/" + num + "/";
128
129 // Build a simple HTML body with the comic image and alt text
130 let body = "";
131 if img != "" {
132 body = `<img src="` + img + `" alt="` + title + `" style="max-width:100%">`;
133 }
134 if alt != "" {
135 body += "\n\n_" + alt + "_";
136 }
137
138 // Build date from comic fields
139 let published = timestamp_now();
140
141 #{
142 id: #{ source: "xkcd", item_id: "" + num },
143 bite: #{
144 author: "xkcd",
145 text: "#" + num + ": " + truncate(title, 90),
146 secondary: truncate(alt, 80),
147 indicator: "📐"
148 },
149 content: #{
150 title: "#" + num + ": " + title,
151 body: body,
152 url: url
153 },
154 meta: #{
155 source_name: "XKCD",
156 published_at: published,
157 tags: ["xkcd", "comics"]
158 }
159 }
160 }
161