plugins/xkcd.lua

1
local parse_xkcd_list;
2
local xkcd_list_updated_at = 0;
3
local xkcd_list = { };
4
local handle_xkcd_command;
5
 
6
function riddim.plugins.xkcd(bot)
7
	local http = require("prosody.net.http");
8
	bot:hook("commands/xkcd", function(command)
9
		if os.difftime(os.time(), xkcd_list_updated_at) > (3 * 60 * 60) then -- Not refreshed within 3 hours
10
			http.request('http://xkcd.com/archive/', { headers = { ["If-Modified-Since"] = os.date("!%a, %d %b %Y %H:%M:%S %Z", xkcd_list_updated_at) } }, function (data, code)
11
				if code == 200 then
12
					xkcd_list_updated_at = os.time();
13
					print("debug", "got "..(#data or 0).." bytes of data");
14
					parse_xkcd_list(data);
15
				elseif code == 304 then
16
					xkcd_list_updated_at = os.time();
17
				else
18
					if code > 0 then
19
						command:reply("Received HTTP "..code.." error trying to fetch the XKCD archive");
20
					else
21
						command:reply("Unable to fetch the XKCD archive from xkcd.com: "..data:gsub("%-", " "));
22
					end
23
					return;
24
				end
25
				command:reply(handle_xkcd_command(command));
26
			end);
27
		else
28
			return handle_xkcd_command(command);
29
		end
30
	end);
31
end
32
 
33
function handle_xkcd_command(command)
34
	local xkcdnum = command.param;
35
	if not xkcdnum then
36
		xkcdnum = #xkcd_list;
37
	elseif not tonumber(xkcdnum) then -- Search for an xkcd
38
		local xkcdname = xkcdnum:lower();
39
		if xkcd_list[xkcdname] then
40
			xkcdnum = xkcd_list[xkcdname];
41
			local xkcd = xkcd_list[xkcdnum];
42
			return xkcd..", http://xkcd.org/"..xkcdnum.."/";
43
		end
44
 
45
		local xkcdpat = xkcdname:gsub("[-()%[]", "%%%0")
46
			:gsub("%%(%b[])",function(s) return (#s > 2 and "" or "%") .. s end);
47
		local results = {};
48
 
49
		for i, xkcd in pairs(xkcd_list) do
50
			if type(i) == "number" and xkcd:lower():match(xkcdpat) then
51
				results[#results+1] = i;
52
			end
53
		end
54
 
55
		if #results == 0 then
56
			return "Sorry, I couldn't find a match";
57
		elseif #results == 1 then
58
			xkcdnum = results[1];
59
		else
60
			-- We have more than one match
61
			local ret = "Multiple matches:";
62
			for i, xkcdnum in ipairs(results) do
63
				local xkcd = xkcd_list[xkcdnum];
64
				ret = string.format("%s %s (%d)%s", ret, xkcd, xkcdnum, ((i < #results) and ",") or "");
65
				if i > 5 then ret = ret .. " " .. (#results - 5) .. " more"; break; end
66
			end
67
			return ret;
68
		end
69
	end
70
	-- Check that xkcdnum is a valid number
71
	xkcdnum = tonumber(xkcdnum);
72
	local xkcd = xkcd_list[xkcdnum];
73
	if not xkcd then return "Sorry, I don't think there is a XKCD #"..xkcdnum; end
74
	return xkcd..", http://xkcd.org/"..xkcdnum.."/";
75
end
76
 
77
function parse_xkcd_list(t)
78
	if not t then return nil; end
79
	for number, name in string.gmatch(t,"<a [^>]*href=\"/(%d+)/\"[^>]*>([^<]+)") do
80
		number = tonumber(number);
81
		if number then
82
			xkcd_list[name:lower()] = number;
83
			xkcd_list[number] = name;
84
		end
85
	end
86
	return true;
87
end