Skip to main content

max / balanced_breakfast

3.5 KB · 154 lines History Blame Raw
1 // Generic RSS/Atom feed plugin. Accepts one or more feed URLs.
2 // Parses XML via the built-in parse_feed() host function, which handles
3 // both RSS 2.0 and Atom formats. Falls back to feed title for author.
4
5 fn id() {
6 "rss"
7 }
8
9 fn name() {
10 "RSS"
11 }
12
13 fn capabilities() {
14 #{
15 supports_pagination: true,
16 supports_date_filter: true
17 }
18 }
19
20 fn config_schema() {
21 #{
22 description: "Subscribe to RSS and Atom feeds. Add one or more feed URLs.",
23 fields: [
24 #{
25 key: "feed_url",
26 label: "Feed URL",
27 field_type: "url",
28 required: true,
29 description: "The URL of the RSS or Atom feed",
30 placeholder: "https://example.com/feed.xml"
31 }
32 ]
33 }
34 }
35
36 fn fetch(config, cursor) {
37 let items = [];
38
39 // Get all feed URLs from config
40 let feed_urls = [];
41 if config.feed_url != () {
42 feed_urls.push(config.feed_url);
43 }
44 // Also check feeds array
45 if config.feeds != () {
46 for url in config.feeds {
47 feed_urls.push(url);
48 }
49 }
50
51 if feed_urls.is_empty() {
52 throw_config_error("No feed URL configured");
53 }
54
55 // Fetch each feed
56 for feed_url in feed_urls {
57 let feed_items = fetch_feed(feed_url);
58 for item in feed_items {
59 items.push(item);
60 }
61 }
62
63 #{
64 items: items,
65 has_more: false
66 }
67 }
68
69 fn fetch_feed(url) {
70 let items = [];
71
72 // Fetch the feed XML
73 let xml = http_get(url);
74 let feed = parse_feed(xml);
75
76 let feed_title = "RSS";
77 if feed.title != () {
78 feed_title = feed.title;
79 }
80
81 if feed.entries != () {
82 for entry in feed.entries {
83 let entry_id = "";
84 if entry.id != () {
85 entry_id = entry.id;
86 } else if entry.link != () {
87 entry_id = entry.link;
88 }
89
90 let title = "";
91 if entry.title != () {
92 title = entry.title;
93 }
94
95 let summary = "";
96 if entry.summary != () {
97 summary = entry.summary;
98 }
99
100 let link = "";
101 if entry.link != () {
102 link = entry.link;
103 }
104
105 let author = feed_title;
106 if entry.author != () {
107 author = entry.author;
108 }
109
110 let published = timestamp_now();
111 if entry.published != () {
112 published = entry.published;
113 }
114
115 // Create bite display text
116 let bite_text = "";
117 if title != "" {
118 bite_text = truncate(title, 100);
119 } else {
120 bite_text = truncate(summary, 100);
121 }
122
123 // Build tags array
124 let tags = [];
125 if entry.tags != () {
126 for tag in entry.tags {
127 tags.push(tag);
128 }
129 }
130
131 items.push(#{
132 id: #{ source: "rss", item_id: entry_id },
133 bite: #{
134 author: author,
135 text: bite_text,
136 indicator: "📰"
137 },
138 content: #{
139 title: title,
140 body: summary,
141 url: link
142 },
143 meta: #{
144 source_name: feed_title,
145 published_at: published,
146 tags: tags
147 }
148 });
149 }
150 }
151
152 items
153 }
154