Sat, 23 Jan 2016 15:16:36 +0100
plugins.ietf: Include a link in reply if an RFC number was given but no match found
local http = require "net.http"; local t_insert, t_remove = table.insert, table.remove; local now = os.time; local debug = function() end; local ttl = 3600; local data = { rfc = { source = "http://www.ietf.org/download/rfc-index.txt", links = "https://tools.ietf.org/html/rfc%s", }, draft = { source = "http://www.ietf.org/download/id-index.txt", links = "https://tools.ietf.org/html/%s", }, } function data.rfc:update(cb) debug("fetch %s", self.source); http.request(self.source, { headers = { ["If-Modified-Since"] = self.updated_at and os.date("!%a, %d %b %Y %H:%M:%S %Z", self.updated_at) or nil; } }, function (data, code) debug("got status %d", code); if code == 200 then debug("got %d bytes of data", #data); self.data = data :gsub("\n\n[^\n]+%b()\n%-+\n\n", "\n\n") :gsub("\n ", " ") :gsub("\n\n ", "\n") :gsub("\n ", " "); -- TODO Can this be made better? self.updated_at = now(); end self.expires = now() + ttl; cb(); end); end data.draft.update = data.rfc.update; function data.rfc:_search(string, cb) debug("really search for %s", string); local number = tonumber(string); local link, match, matches; local count = 0 if number then number = ("%04d"):format(number); debug("search for RFC%s", number); link, match = self.data:match("\n(" .. number .. ")%s*([^\n]*)"); count = 1 else local pat = string:gsub("[-()%[]", "%%%1") :gsub("%%(%b[])",function(s) return (#s > 2 and "" or "%") .. s end) :gsub("\n+", " "):gsub("\\n", "") :gsub("%w", function(s) return '['..s:lower()..s:upper()..']' end) debug("fulltext search for \"%s\"", pat); --link, match = self.data:match("\n(%d%d%d%d) ([^\n]-"..pat.."[^\n]*)"); for l,m in self.data:gmatch("\n(%d%d%d%d) ([^\n]-"..pat.."[^\n]*)") do link, match = l, m count = count + 1 -- Note: This allways returns the last result. -- FIXME Decide on what to do if >1 results. end --[[ matches = {}; for link, match in g do t_insert(matches, {link=link, match=match}); end matches = t_remove(matches); matches.link, matches.match; --]] end if match then debug("matched %d bytes, number is %s", #match, link); if #match > 300 then cb("Match was too big"); return end local remove = { Also = true, Format = true, --Obsoleted = true, Obsoletes = true, --Updated = true, Updates = true, --Status = true, }; match = match:gsub("%s*\n%s+", " ") match = match:gsub("%s*%b()", function(s) local first = s:match("%(([^: ]*)"); return first and remove[first] and "" or s end); link = self.links:format(link); match = match:gsub("%. ", ".\n", 1); -- Add a newline between title and authors if count > 1 then link = link .. " (" .. count .. " more matches)" end cb(match .. "\n" .. link); elseif number then cb("Sorry, no match, try " .. self.links:format(number)); else cb("Sorry, no match"); end end function data.draft:_search(string, cb) debug("really search for %s", string); local pat = string:gsub("[-()%[]", "%%%1") :gsub("%%(%b[])",function(s) return (#s > 2 and "" or "%") .. s end) :gsub("\n+", " "):gsub("\\n", "") :gsub("%w", function(s) return '['..s:lower()..s:upper()..']' end) debug("fulltext search for \"%s\"", pat); local match = self.data:match("\n([^\n]-"..pat.."[^\n]*)") if match then debug("match: %s", match); local match, link = match:match("(.-)%s(%b<>)") link = link and self.links:format(link:sub(2,-2)) or "no link"; cb(match .. "\n" .. link); else cb("Sorry, no match"); end end function data.rfc:search(string, cb) debug("search for %s", string); if not self.data then --or self.expires < now() then self:update(function() self:_search(string,cb) end); return else self:_search(string,cb) end end data.draft.search = data.rfc.search; function riddim.plugins.ietf(bot) if bot.stream.debug then function debug(...) return bot.stream:debug(...) end end bot:hook("commands/rfc", function(command) local rfc = data.rfc; debug("search for %s", command.param); return rfc:search(command.param, function(match) command:reply(match) end) end) bot:hook("commands/draft", function(command) local draft = data.draft; debug("search for %s", command.param); return draft:search(command.param, function(match) command:reply(match) end) end) end