|
1 local http = require "net.http"; |
|
2 local t_insert, t_remove = table.insert, table.remove; |
|
3 local now = os.time; |
|
4 |
|
5 local debug = function() end; |
|
6 |
|
7 local ttl = 3600; |
|
8 local data = { |
|
9 rfc = { |
|
10 source = "http://www.ietf.org/download/rfc-index.txt", |
|
11 links = "http://tools.ietf.org/html/rfc%s", |
|
12 }, |
|
13 draft = { |
|
14 source = "http://www.ietf.org/download/id-index.txt", |
|
15 links = "http://tools.ietf.org/html/%s", |
|
16 }, |
|
17 } |
|
18 |
|
19 |
|
20 function data.rfc:update(cb) |
|
21 debug("fetch %s", self.source); |
|
22 http.request(self.source, { |
|
23 headers = { |
|
24 ["If-Modified-Since"] = self.updated_at |
|
25 and os.date("!%a, %d %b %Y %H:%M:%S %Z", self.updated_at) or nil; |
|
26 } |
|
27 }, function (data, code) |
|
28 debug("got status %d", code); |
|
29 if code == 200 then |
|
30 debug("got %d bytes of data", #data); |
|
31 self.data = data |
|
32 :gsub("\n\n[^\n]+%b()\n%-+\n\n", "\n\n") |
|
33 :gsub("\n ", " ") |
|
34 :gsub("\n\n ", "\n") |
|
35 :gsub("\n ", " "); |
|
36 -- TODO Can this be made better? |
|
37 self.updated_at = now(); |
|
38 end |
|
39 self.expires = now() + ttl; |
|
40 cb(); |
|
41 end); |
|
42 end |
|
43 |
|
44 data.draft.update = data.rfc.update; |
|
45 |
|
46 function data.rfc:_search(string, cb) |
|
47 debug("really search for %s", string); |
|
48 local number = tonumber(string); |
|
49 local link, match, matches; |
|
50 if number then |
|
51 number = ("%04d"):format(number); |
|
52 debug("search for RFC%s", number); |
|
53 link, match = self.data:match("\n(" .. number .. ")%s*([^\n]*)"); |
|
54 else |
|
55 local pat = string:gsub("[()]", function(s) return "%" .. s end) |
|
56 :gsub("[%[]",function(s) return "%" .. s end) |
|
57 :gsub("%%(%b[])",function(s) return (#s > 2 and "" or "%") .. s end) |
|
58 :gsub("\n+", " "):gsub("\\n", ""); |
|
59 debug("fulltext search for \"%s\"", pat); |
|
60 --link, match = self.data:match("\n(%d%d%d%d) ([^\n]-"..pat.."[^\n]*)"); |
|
61 for l,m in self.data:gmatch("\n(%d%d%d%d) ([^\n]-"..pat.."[^\n]*)") do |
|
62 link, match = l, m |
|
63 -- Note: This allways returns the last result. |
|
64 -- FIXME Decide on what to do if >1 results. |
|
65 end |
|
66 --[[ |
|
67 matches = {}; |
|
68 for link, match in g do |
|
69 t_insert(matches, {link=link, match=match}); |
|
70 end |
|
71 matches = t_remove(matches); |
|
72 matches.link, matches.match; |
|
73 --]] |
|
74 end |
|
75 |
|
76 if match then |
|
77 debug("matched %d bytes, number is %s", #match, link); |
|
78 if #match > 300 then |
|
79 cb("Match was too big"); |
|
80 return |
|
81 end |
|
82 local remove = { |
|
83 Also = true, |
|
84 Format = true, |
|
85 --Obsoleted = true, |
|
86 Obsoletes = true, |
|
87 --Updated = true, |
|
88 Updates = true, |
|
89 --Status = true, |
|
90 }; |
|
91 match = match:gsub("%s*\n%s+", " ") |
|
92 match = match:gsub("%s*%b()", function(s) |
|
93 local first = s:match("%(([^: ]*)"); return first and remove[first] and "" or s |
|
94 end); |
|
95 link = self.links:format(link); |
|
96 match = match:gsub("%. ", ".\n", 1); -- Add a newline between title and authors |
|
97 cb(match .. "\n" .. link); |
|
98 else |
|
99 cb("Sorry, no match"); |
|
100 end |
|
101 end |
|
102 |
|
103 function data.draft:_search(string, cb) |
|
104 debug("really search for %s", string); |
|
105 local pat = string:gsub("[()]", function(s) return "%" .. s end) |
|
106 :gsub("[%[]",function(s) return "%" .. s end) |
|
107 :gsub("%%(%b[])",function(s) return (#s > 2 and "" or "%") .. s end) |
|
108 :gsub("\n+", " "):gsub("\\n", ""); |
|
109 debug("fulltext search for \"%s\"", pat); |
|
110 local match = self.data:match("\n([^\n]-"..pat.."[^\n]*)") |
|
111 |
|
112 if match then |
|
113 debug("match: %s", match); |
|
114 local match, link = match:match("(.-)%s(%b<>)") |
|
115 link = link and self.links:format(link:sub(2,-2)) or "no link"; |
|
116 cb(match .. "\n" .. link); |
|
117 else |
|
118 cb("Sorry, no match"); |
|
119 end |
|
120 end |
|
121 |
|
122 function data.rfc:search(string, cb) |
|
123 debug("search for %s", string); |
|
124 if not self.data then --or self.expires < now() then |
|
125 self:update(function() self:_search(string,cb) end); |
|
126 return |
|
127 else |
|
128 self:_search(string,cb) |
|
129 end |
|
130 end |
|
131 |
|
132 data.draft.search = data.rfc.search; |
|
133 |
|
134 |
|
135 function riddim.plugins.ietf(bot) |
|
136 if bot.stream.debug then |
|
137 function debug(...) |
|
138 return bot.stream:debug(...) |
|
139 end |
|
140 end |
|
141 |
|
142 bot:hook("commands/rfc", function(command) |
|
143 local rfc = data.rfc; |
|
144 debug("search for %s", command.param); |
|
145 return rfc:search(command.param, function(match) |
|
146 command:reply(match) |
|
147 end) |
|
148 end) |
|
149 |
|
150 bot:hook("commands/draft", function(command) |
|
151 local draft = data.draft; |
|
152 debug("search for %s", command.param); |
|
153 return draft:search(command.param, function(match) |
|
154 command:reply(match) |
|
155 end) |
|
156 end) |
|
157 end |