| 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 |