Software /
code /
prosody-modules
Comparison
mod_muc_log/mod_muc_log.lua @ 56:e9de45beaf5e
mod_muc_log: templify the html stuff
author | Thilo Cestonaro <thilo@cestona.ro> |
---|---|
date | Mon, 19 Oct 2009 22:15:57 +0200 |
parent | 55:d9749ed44f6e |
child | 57:cddcea7c091a |
comparison
equal
deleted
inserted
replaced
55:d9749ed44f6e | 56:e9de45beaf5e |
---|---|
6 local prosody = prosody; | 6 local prosody = prosody; |
7 local splitJid = require "util.jid".split; | 7 local splitJid = require "util.jid".split; |
8 local bareJid = require "util.jid".bare; | 8 local bareJid = require "util.jid".bare; |
9 local config_get = require "core.configmanager".get; | 9 local config_get = require "core.configmanager".get; |
10 local httpserver = require "net.httpserver"; | 10 local httpserver = require "net.httpserver"; |
11 -- local dump = require "util.logger".dump; | 11 local serialize = require "util.serialization".serialize; |
12 local config = {}; | 12 local config = {}; |
13 | |
13 | 14 |
14 --[[ LuaFileSystem | 15 --[[ LuaFileSystem |
15 * URL: http://www.keplerproject.org/luafilesystem/index.html | 16 * URL: http://www.keplerproject.org/luafilesystem/index.html |
16 * Install: luarocks install luafilesystem | 17 * Install: luarocks install luafilesystem |
17 * ]] | 18 * ]] |
18 local lfs = require "lfs"; | 19 local lfs = require "lfs"; |
19 | 20 |
20 local lom = require "lxp.lom"; | 21 local lom = require "lxp.lom"; |
22 | |
23 | |
24 --[[ | |
25 * Default templates for the html output. | |
26 ]]-- | |
27 local html = {}; | |
28 html.doc = [[<html> | |
29 <head> | |
30 <title>muc_log</title> | |
31 </head> | |
32 <style type="text/css"> | |
33 <!-- | |
34 .timestuff {color: #AAAAAA; text-decoration: none;} | |
35 .muc_join {color: #009900; font-style: italic;} | |
36 .muc_leave {color: #009900; font-style: italic;} | |
37 .muc_statusChange {color: #009900; font-style: italic;} | |
38 .muc_title {color: #009900;} | |
39 .muc_titlenick {color: #009900; font-style: italic;} | |
40 .muc_kick {color: #009900; font-style: italic;} | |
41 .muc_bann {color: #009900; font-style: italic;} | |
42 .muc_name {color: #0000AA;} | |
43 //--> | |
44 </style> | |
45 <body> | |
46 ###BODY_STUFF### | |
47 </body> | |
48 </html>]]; | |
49 | |
50 html.hosts = {}; | |
51 html.hosts.bit = [[<a href="/muc_log/###JID###">###JID###</a><br />]] | |
52 html.hosts.body = [[<h2>Rooms hosted on this server:</h2><hr /><p> | |
53 ###HOSTS_STUFF### | |
54 </p><hr />]]; | |
55 | |
56 html.days = {}; | |
57 html.days.bit = [[<a href="/muc_log/###JID###/?year=###YEAR###&month=###MONTH###&day=###DAY###">20###YEAR###/###MONTH###/###DAY###</a><br />]]; | |
58 html.days.body = [[<h2>available logged days of room: ###JID###</h2><hr /><p> | |
59 ###DAYS_STUFF### | |
60 </p><hr />]]; | |
61 | |
62 html.day = {}; | |
63 html.day.time = [[<a name="###TIME###" href="####TIME###" class="timestuff">[###TIME###]</a> ]]; -- the one ####TIME### need to stay! it will evaluate to e.g. #09:10:56 which is an anker then | |
64 html.day.presence = {}; | |
65 html.day.presence.join = [[###TIME_STUFF###<font class="muc_join"> *** ###NICK### joins the room</font><br />]]; | |
66 html.day.presence.leave = [[###TIME_STUFF###<font class="muc_leave"> *** ###NICK### leaves the room</font><br />]]; | |
67 html.day.presence.statusChange = [[###TIME_STUFF###<font class="muc_statusChange"> *** ###NICK### changed his/her status to: ###STATUS###</font><br />]]; | |
68 html.day.message = [[###TIME_STUFF###<font class="muc_name"><###NICK###></font> ###MSG###<br />]]; | |
69 html.day.titleChange = [[###TIME_STUFF###<font class="muc_titlenick"> *** ###NICK### change title to:</font> <font class="muc_title">###MSG###</font><br />]]; | |
70 html.day.kick = [[###TIME_STUFF###<font class="muc_titlenick"> *** ###NICK### kicked ###VICTIM###</font><br />]]; | |
71 html.day.bann = [[###TIME_STUFF###<font class="muc_titlenick"> *** ###NICK### banned ###VICTIM###</font><br />]]; | |
72 html.day.body = [[<h2>room ###JID### logging of 20###YEAR###/###MONTH###/###DAY###</h2><hr /><p> | |
73 ###DAY_STUFF### | |
74 </p><hr />]]; | |
75 | |
76 html.help = [[ | |
77 MUC logging is not configured correctly.<br /> | |
78 Here is a example config:<br /> | |
79 Component "rooms.example.com" "muc"<br /> | |
80 modules_enabled = {<br /> | |
81 "muc_log";<br /> | |
82 }<br /> | |
83 muc_log = {<br /> | |
84 folder = "/opt/local/var/log/prosody/rooms";<br /> | |
85 http_port = "/opt/local/var/log/prosody/rooms";<br /> | |
86 }<br /> | |
87 ]]; | |
21 | 88 |
22 function validateLogFolder() | 89 function validateLogFolder() |
23 if config.folder == nil then | 90 if config.folder == nil then |
24 module:log("warn", "muc_log folder isn't configured. configure it please!"); | 91 module:log("warn", "muc_log folder isn't configured. configure it please!"); |
25 return false; | 92 return false; |
84 end | 151 end |
85 return; | 152 return; |
86 end | 153 end |
87 | 154 |
88 function createDoc(body) | 155 function createDoc(body) |
89 return [[<html> | 156 return html.doc:gsub("###BODY_STUFF###", body); |
90 <head> | 157 end |
91 <title>muc_log</title> | 158 |
92 </head> | 159 local function htmlEscape(t) |
93 <style type="text/css"> | 160 t = t:gsub("\n", "<br />"); |
94 <!-- | 161 -- TODO link text into klickable link and such stuff |
95 .timestuff {color: #AAAAAA; text-decoration: none;} | 162 return t; |
96 .muc_join {color: #009900; font-style: italic;} | |
97 .muc_leave {color: #009900; font-style: italic;} | |
98 .muc_kick {color: #009900; font-style: italic;} | |
99 .muc_bann {color: #009900; font-style: italic;} | |
100 .muc_name {color: #0000AA;} | |
101 //--> | |
102 </style> | |
103 <body> | |
104 ]] .. tostring(body) .. [[ | |
105 </body> | |
106 </html>]]; | |
107 end | 163 end |
108 | 164 |
109 function splitQuery(query) | 165 function splitQuery(query) |
110 local ret = {}; | 166 local ret = {}; |
111 if query == nil then return ret; end | 167 if query == nil then return ret; end |
139 end | 195 end |
140 return node, host; | 196 return node, host; |
141 end | 197 end |
142 | 198 |
143 local function generateRoomListSiteContent() | 199 local function generateRoomListSiteContent() |
144 local ret = "<h2>Rooms hosted on this server:</h2><hr /><p>"; | 200 local rooms = ""; |
145 for host, config in pairs(prosody.hosts) do | 201 for host, config in pairs(prosody.hosts) do |
146 if prosody.hosts[host].muc ~= nil then | 202 if prosody.hosts[host].muc ~= nil then |
147 for jid, room in pairs(prosody.hosts[host].muc.rooms) do | 203 for jid, room in pairs(prosody.hosts[host].muc.rooms) do |
148 ret = ret .. "<a href=\"/muc_log/" .. jid .. "/\">" .. jid .."</a><br />\n"; | 204 rooms = rooms .. html.hosts.bit:gsub("###JID###", jid); |
149 end | 205 end |
150 end | 206 end |
151 end | 207 end |
152 return ret .. "</p><hr />"; | 208 |
209 return html.hosts.body:gsub("###HOSTS_STUFF###", rooms); | |
153 end | 210 end |
154 | 211 |
155 local function generateDayListSiteContentByRoom(bareRoomJid) | 212 local function generateDayListSiteContentByRoom(bareRoomJid) |
156 local ret = ""; | 213 local days = ""; |
214 local tmp; | |
157 | 215 |
158 for file in lfs.dir(config.folder) do | 216 for file in lfs.dir(config.folder) do |
159 local year, month, day = file:match("^(%d%d)(%d%d)(%d%d)_" .. bareRoomJid .. ".log"); | 217 local year, month, day = file:match("^(%d%d)(%d%d)(%d%d)_" .. bareRoomJid .. ".log"); |
160 if year ~= nil and month ~= nil and day ~= nil and | 218 if year ~= nil and month ~= nil and day ~= nil and |
161 year ~= "" and month ~= "" and day ~= "" | 219 year ~= "" and month ~= "" and day ~= "" |
162 then | 220 then |
163 ret = "<a href=\"/muc_log/" .. bareRoomJid .. "/?year=" .. year .. "&month=" .. month .. "&day=" .. day .. "\">20" .. year .. "/" .. month .. "/" .. day .. "</a><br />\n" .. ret; | 221 tmp = html.days.bit; |
164 end | 222 tmp = tmp:gsub("###JID###", bareRoomJid); |
165 end | 223 tmp = tmp:gsub("###YEAR###", year); |
166 if ret ~= "" then | 224 tmp = tmp:gsub("###MONTH###", month); |
167 return "<h2>available logged days of room: " .. bareRoomJid .. "</h2><hr /><p>" .. ret .. "</p><hr />"; | 225 tmp = tmp:gsub("###DAY###", day); |
226 days = tmp .. days; | |
227 end | |
228 end | |
229 if days ~= "" then | |
230 tmp = html.days.body:gsub("###DAYS_STUFF###", days); | |
231 return tmp:gsub("###JID###", bareRoomJid); | |
168 else | 232 else |
169 return generateRoomListSiteContent(); -- fallback | 233 return generateRoomListSiteContent(); -- fallback |
170 end | 234 end |
171 end | 235 end |
172 | 236 |
173 local function parseDay(bareRoomJid, query) | 237 local function parseDay(bareRoomJid, query) |
174 local ret = ""; | 238 local ret = ""; |
175 local year; | 239 local year; |
176 local month; | 240 local month; |
177 local day; | 241 local day; |
242 local tmp; | |
178 | 243 |
179 for _,str in ipairs(query) do | 244 for _,str in ipairs(query) do |
180 local name, value; | 245 local name, value; |
181 name, value = str:match("^(%a+)=(%d+)$"); | 246 name, value = str:match("^(%a+)=(%d+)$"); |
182 if name == "year" then | 247 if name == "year" then |
197 local content = f:read("*a"); | 262 local content = f:read("*a"); |
198 local parsed = lom.parse("<xml>" .. content .. "</xml>"); | 263 local parsed = lom.parse("<xml>" .. content .. "</xml>"); |
199 if parsed ~= nil then | 264 if parsed ~= nil then |
200 for _,stanza in ipairs(parsed) do | 265 for _,stanza in ipairs(parsed) do |
201 if stanza.attr ~= nil and stanza.attr.time ~= nil then | 266 if stanza.attr ~= nil and stanza.attr.time ~= nil then |
202 local tmp = "<a name=\"" .. stanza.attr.time .. "\" href=\"#" .. stanza.attr.time .. "\" class=\"timestuff\">[" .. stanza.attr.time .. "]</a> "; | 267 local timeStuff = html.day.time:gsub("###TIME###", stanza.attr.time); |
203 if stanza[1] ~= nil then | 268 if stanza[1] ~= nil then |
204 local nick; | 269 local nick; |
270 | |
271 -- grep nick from "from" resource | |
205 if stanza[1].attr.from ~= nil then | 272 if stanza[1].attr.from ~= nil then |
206 nick = stanza[1].attr.from:match("/(.+)$"); | 273 nick = stanza[1].attr.from:match("/(.+)$"); |
207 end | 274 end |
275 | |
208 if stanza[1].tag == "presence" and nick ~= nil then | 276 if stanza[1].tag == "presence" and nick ~= nil then |
277 | |
209 if stanza[1].attr.type == nil then | 278 if stanza[1].attr.type == nil then |
210 ret = ret .. tmp .. "<font class=\"muc_join\"> *** " .. nick .. " joins the room</font><br />\n"; | 279 tmp = html.day.presence.join:gsub("###TIME_STUFF###", timeStuff); |
280 ret = ret .. tmp:gsub("###NICK###", nick); | |
211 elseif stanza[1].attr.type ~= nil and stanza[1].attr.type == "unavailable" then | 281 elseif stanza[1].attr.type ~= nil and stanza[1].attr.type == "unavailable" then |
212 ret = ret .. tmp .. "<font class=\"muc_leave\"> *** " .. nick .. " leaves the room</font><br />\n"; | 282 tmp = html.day.presence.leave:gsub("###TIME_STUFF###", timeStuff); |
283 ret = ret .. tmp:gsub("###NICK###", nick); | |
213 else | 284 else |
214 ret = ret .. tmp .. "<font class=\"muc_leave\"> *** " .. nick .. " changed his/her status to: " .. stanza[1].attr.type .. "</font><br />\n"; | 285 tmp = html.day.presence.leave:gsub("###TIME_STUFF###", timeStuff); |
286 tmp = tmp:gsub("###STATUS###", stanza[1].attr.type); | |
287 ret = ret .. tmp:gsub("###NICK###", nick); | |
215 end | 288 end |
216 elseif stanza[1].tag == "message" then | 289 elseif stanza[1].tag == "message" then |
217 local body; | 290 local body; |
218 for _,tag in ipairs(stanza[1]) do | 291 for _,tag in ipairs(stanza[1]) do |
219 if tag.tag == "body" then | 292 if tag.tag == "body" then |
220 body = tag[1]:gsub("\n", "<br />\n"); | 293 body = htmlEscape(tag[1]); |
221 if nick ~= nil then | 294 if nick ~= nil then |
222 break; | 295 break; |
223 end | 296 end |
224 elseif tag.tag == "nick" and nick == nil then | 297 elseif tag.tag == "nick" and nick == nil then |
225 nick = tag[1]; | 298 nick = tag[1]; |
227 break; | 300 break; |
228 end | 301 end |
229 end | 302 end |
230 end | 303 end |
231 if nick ~= nil and body ~= nil then | 304 if nick ~= nil and body ~= nil then |
232 ret = ret .. tmp .. "<font class=\"muc_name\"><" .. nick .. "></font> " .. body .. "<br />\n"; | 305 tmp = html.day.message:gsub("###TIME_STUFF###", timeStuff); |
306 tmp = tmp:gsub("###NICK###", nick); | |
307 ret = ret .. tmp:gsub("###MSG###", body); | |
233 end | 308 end |
234 else | 309 else |
235 module:log("info", "unknown stanza subtag in log found. room: %s; day: %s", bareRoomJid, year .. "/" .. month .. "/" .. day); | 310 module:log("info", "unknown stanza subtag in log found. room: %s; day: %s", bareRoomJid, year .. "/" .. month .. "/" .. day); |
236 end | 311 end |
237 end | 312 end |
242 end | 317 end |
243 f:close(); | 318 f:close(); |
244 else | 319 else |
245 ret = err; | 320 ret = err; |
246 end | 321 end |
247 return "<h2>room " .. bareRoomJid .. " logging of 20" .. year .. "/" .. month .. "/" .. day .. "</h2><hr /><p>" .. ret .. "</p><hr />"; | 322 tmp = html.day.body:gsub("###DAY_STUFF###", ret); |
323 tmp = tmp:gsub("###JID###", bareRoomJid); | |
324 tmp = tmp:gsub("###YEAR###", year); | |
325 tmp = tmp:gsub("###MONTH###", month); | |
326 tmp = tmp:gsub("###DAY###", day); | |
327 return tmp; | |
248 else | 328 else |
249 return generateDayListSiteContentByRoom(bareRoomJid); -- fallback | 329 return generateDayListSiteContentByRoom(bareRoomJid); -- fallback |
250 end | 330 end |
251 end | 331 end |
252 | 332 |
253 function handle_request(method, body, request) | 333 function handle_request(method, body, request) |
334 module:log("debug", "got a request ...") | |
254 local query = splitQuery(request.url.query); | 335 local query = splitQuery(request.url.query); |
255 local node, host = grepRoomJid(request.url.path); | 336 local node, host = grepRoomJid(request.url.path); |
256 | 337 |
257 if validateLogFolder() == false then | 338 if validateLogFolder() == false then |
258 return createDoc([[ | 339 return createDoc(html.help); |
259 MUC logging is not configured correctly. Add a section to Host * "muc_log" and configure the value for the logging "folder".<br /> | |
260 Like:<br /> | |
261 Host "*"<br /> | |
262 ....<br /> | |
263 muc_log = {<br /> | |
264 folder = "/opt/local/var/log/prosody/rooms";<br /> | |
265 }<br /> | |
266 ]]); | |
267 end | 340 end |
268 if node ~= nil and host ~= nil then | 341 if node ~= nil and host ~= nil then |
269 local bare = node .. "@" .. host; | 342 local bare = node .. "@" .. host; |
270 if prosody.hosts[host] ~= nil and prosody.hosts[host].muc ~= nil and prosody.hosts[host].muc.rooms[bare] ~= nil then | 343 if prosody.hosts[host] ~= nil and prosody.hosts[host].muc ~= nil and prosody.hosts[host].muc.rooms[bare] ~= nil then |
271 local room = prosody.hosts[host].muc.rooms[bare]; | 344 local room = prosody.hosts[host].muc.rooms[bare]; |
282 end | 355 end |
283 return; | 356 return; |
284 end | 357 end |
285 | 358 |
286 config = config_get(module:get_host(), "core", "muc_log"); | 359 config = config_get(module:get_host(), "core", "muc_log"); |
360 module:log("debug", serialize(config)); | |
287 | 361 |
288 httpserver.new_from_config({ config.http_port or true }, handle_request, { base = "muc_log" }); | 362 httpserver.new_from_config({ config.http_port or true }, handle_request, { base = "muc_log" }); |
289 | 363 |
290 module:hook("message/bare", logIfNeeded, 500); | 364 module:hook("message/bare", logIfNeeded, 500); |
291 module:hook("pre-message/bare", logIfNeeded, 500); | 365 module:hook("pre-message/bare", logIfNeeded, 500); |