diff mod_pubsub_feeds/mod_pubsub_feeds.lua @ 5653:62c6e17a5e9d

Merge
author Stephen Paul Weber <singpolyma@singpolyma.net>
date Mon, 18 Sep 2023 08:24:19 -0500
parents bc292c84f56c
children 3f75ac4311bf
line wrap: on
line diff
--- a/mod_pubsub_feeds/mod_pubsub_feeds.lua	Mon Sep 18 08:22:07 2023 -0500
+++ b/mod_pubsub_feeds/mod_pubsub_feeds.lua	Mon Sep 18 08:24:19 2023 -0500
@@ -1,17 +1,4 @@
 -- Fetches Atom feeds and publishes to PubSub nodes
---
--- Config:
--- Component "pubsub.example.com" "pubsub"
--- modules_enabled = {
---   "pubsub_feeds";
--- }
--- feeds = { -- node -> url
---   prosody_blog = "http://blog.prosody.im/feed/atom.xml";
--- }
--- feed_pull_interval = 20 -- minutes
---
--- Reference
--- http://pubsubhubbub.googlecode.com/svn/trunk/pubsubhubbub-core-0.4.html
 
 local pubsub = module:depends"pubsub";
 
@@ -36,7 +23,7 @@
 	return nil, "unsupported-format";
 end
 
-local use_pubsubhubub = module:get_option_boolean("use_pubsubhubub", true);
+local use_pubsubhubub = module:get_option_boolean("use_pubsubhubub", false);
 if use_pubsubhubub then
 	module:depends"http";
 end
@@ -46,7 +33,8 @@
 local formencode = http.formencode;
 
 local feed_list = module:shared("feed_list");
-local refresh_interval = module:get_option_number("feed_pull_interval", 15) * 60;
+local legacy_refresh_interval = module:get_option_number("feed_pull_interval", 15);
+local refresh_interval = module:get_option_number("feed_pull_interval_seconds", legacy_refresh_interval*60);
 local lease_length = tostring(math.floor(module:get_option_number("feed_lease_length", 86400)));
 
 function module.load()
@@ -60,7 +48,12 @@
 		end
 		new_feed_list[node] = true;
 		if not feed_list[node] then
-			feed_list[node] = { url = url; node = node; last_update = 0 };
+			local ok, err = pubsub.service:create(node, true);
+			if ok or err == "conflict" then
+				feed_list[node] = { url = url; node = node; last_update = 0 };
+			else
+				module:log("error", "Could not create node %s: %s", node, err);
+			end
 		else
 			feed_list[node].url = url;
 		end
@@ -75,58 +68,68 @@
 	end
 end
 
-function update_entry(item)
+function update_entry(item, data)
 	local node = item.node;
-	module:log("debug", "parsing %d bytes of data in node %s", #item.data or 0, node)
-	local feed, err = parse_feed(item.data);
+	module:log("debug", "parsing %d bytes of data in node %s", #data or 0, node)
+	local feed, err = parse_feed(data);
 	if not feed then
 		module:log("error", "Could not parse feed %q: %s", item.url, err);
-		module:log("debug", "Feed data:\n%s\n.", item.data);
+		module:log("debug", "Feed data:\n%s\n.", data);
 		return;
 	end
 	local entries = {};
 	for entry in feed:childtags("entry") do
 		table.insert(entries, entry);
 	end
-	local ok, items = pubsub.service:get_items(node, true);
+	local ok, last_id = pubsub.service:get_last_item(node, true);
 	if not ok then
-		local ok, err = pubsub.service:create(node, true);
-		if not ok then
-			module:log("error", "Could not create node %s: %s", node, err);
-			return;
+		module:log("error", "PubSub node %q missing: %s", node, last_id);
+		return
+	end
+
+	local start_from = #entries;
+	for i, entry in ipairs(entries) do
+		local id = entry:get_child_text("id");
+		if not id then
+			local link = entry:get_child("link");
+			if link then
+				module:log("debug", "Feed %q item %s is missing an id, using <link> instead", item.url, entry:top_tag());
+				id = link and link.attr.href;
+			else
+				module:log("error", "Feed %q item %s is missing both id and link, this feed is unusable", item.url, entry:top_tag());
+				return;
+			end
+			entry:text_tag("id", id);
 		end
-		items = {};
+
+		if last_id == id then
+			-- This should be the first item that we already have.
+			start_from = i-1;
+			break
+		end
 	end
-	for i = #entries, 1, -1 do -- Feeds are usually in reverse order
+
+	for i = start_from, 1, -1 do -- Feeds are usually in reverse order
 		local entry = entries[i];
 		entry.attr.xmlns = xmlns_atom;
 
-		local e_published = entry:get_child_text("published");
-		e_published = e_published and dt_parse(e_published);
-		local e_updated = entry:get_child_text("updated");
-		e_updated = e_updated and dt_parse(e_updated);
+		local id = entry:get_child_text("id");
 
-		local timestamp = e_updated or e_published or nil;
-		--module:log("debug", "timestamp is %s, item.last_update is %s", tostring(timestamp), tostring(item.last_update));
+		local timestamp = dt_parse(entry:get_child_text("published"));
+		if not timestamp then
+			timestamp = time();
+			entry:text_tag("published", dt_datetime(timestamp));
+		end
+
 		if not timestamp or not item.last_update or timestamp > item.last_update then
-			local id = entry:get_child_text("id");
-			if not id then
-				local link = entry:get_child("link");
-				id = link and link.attr.href;
-			end
-			if not id then
-				-- Sigh, no link?
-				id = feed.url .. "#" .. hmac_sha1(feed.url, tostring(entry), true) .. "@" .. dt_datetime(timestamp);
-			end
-			if not items[id] then
-				local xitem = st.stanza("item", { id = id, xmlns = "http://jabber.org/protocol/pubsub" }):add_child(entry);
-				-- TODO Put data from /feed into item/source
+			local xitem = st.stanza("item", { id = id, xmlns = "http://jabber.org/protocol/pubsub" }):add_child(entry);
+			-- TODO Put data from /feed into item/source
 
-				--module:log("debug", "publishing to %s, id %s", node, id);
-				local ok, err = pubsub.service:publish(node, true, id, xitem);
-				if not ok then
-					module:log("error", "Publishing to node %s failed: %s", node, err);
-				end
+			local ok, err = pubsub.service:publish(node, true, id, xitem);
+			if not ok then
+				module:log("error", "Publishing to node %s failed: %s", node, err);
+			elseif timestamp then
+				item.last_update = timestamp;
 			end
 		end
 	end
@@ -148,20 +151,18 @@
 end
 
 function fetch(item, callback) -- HTTP Pull
-	local headers = { };
-	if item.data and item.etag then
-		headers["If-None-Match"] = item.etag;
-	end
+	local headers = {
+		["If-None-Match"] = item.etag;
+		["Accept"] = "application/atom+xml, application/x-rss+xml, application/xml";
+	};
 	http.request(item.url, { headers = headers }, function(data, code, resp)
 		if code == 200 then
-			item.data = data;
-			if callback then callback(item) end
-			item.last_update = time();
+			if callback then callback(item, data) end
 			if resp.headers then
 				item.etag = resp.headers.etag
 			end
 		elseif code == 304 then
-			item.last_update = time();
+			module:log("debug", "No updates to %q", item.url);
 		elseif code == 301 and resp.headers.location then
 			module:log("info", "Feed %q has moved to %q", item.url, resp.headers.location);
 		elseif code <= 100 then
@@ -268,9 +269,7 @@
 				end
 				module:log("debug", "Valid signature");
 			end
-			feed.data = body;
-			update_entry(feed);
-			feed.last_update = time();
+			update_entry(feed, body);
 			return 202;
 		end
 		return 400;