Software /
code /
prosody-modules
Comparison
mod_muc_log/mod_muc_log.lua @ 61:e609da067e9f
mod_muc_log: display room's current title; show kicked messages with and without reason
author | Thilo Cestonaro <thilo@cestona.ro> |
---|---|
date | Wed, 21 Oct 2009 22:32:58 +0200 |
parent | 60:5cca708c9f11 |
child | 62:0dfd65bfedb0 |
comparison
equal
deleted
inserted
replaced
60:5cca708c9f11 | 61:e609da067e9f |
---|---|
34 <!-- | 34 <!-- |
35 .timestuff {color: #AAAAAA; text-decoration: none;} | 35 .timestuff {color: #AAAAAA; text-decoration: none;} |
36 .muc_join {color: #009900; font-style: italic;} | 36 .muc_join {color: #009900; font-style: italic;} |
37 .muc_leave {color: #009900; font-style: italic;} | 37 .muc_leave {color: #009900; font-style: italic;} |
38 .muc_statusChange {color: #009900; font-style: italic;} | 38 .muc_statusChange {color: #009900; font-style: italic;} |
39 .muc_title {color: #009900;} | 39 .muc_title {color: #BBBBBB; font-size: 32px;} |
40 .muc_titlenick {color: #009900; font-style: italic;} | 40 .muc_titleChange {color: #009900; font-style: italic;} |
41 .muc_kick {color: #009900; font-style: italic;} | 41 .muc_kick {color: #009900; font-style: italic;} |
42 .muc_bann {color: #009900; font-style: italic;} | 42 .muc_bann {color: #009900; font-style: italic;} |
43 .muc_name {color: #0000AA;} | 43 .muc_msg_nick {color: #0000AA;} |
44 //--> | 44 //--> |
45 </style> | 45 </style> |
46 <body> | 46 <body> |
47 ###BODY_STUFF### | 47 ###BODY_STUFF### |
48 </body> | 48 </body> |
59 html.days.body = [[<h2>available logged days of room: ###JID###</h2><hr /><p> | 59 html.days.body = [[<h2>available logged days of room: ###JID###</h2><hr /><p> |
60 ###DAYS_STUFF### | 60 ###DAYS_STUFF### |
61 </p><hr />]]; | 61 </p><hr />]]; |
62 | 62 |
63 html.day = {}; | 63 html.day = {}; |
64 html.day.title = [[Subject: <font class="muc_title">###TITLE###</font>]]; | |
64 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 | 65 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 |
65 html.day.presence = {}; | 66 html.day.presence = {}; |
66 html.day.presence.join = [[###TIME_STUFF###<font class="muc_join"> *** ###NICK### joins the room</font><br />]]; | 67 html.day.presence.join = [[###TIME_STUFF###<font class="muc_join"> *** ###NICK### joins the room</font><br />]]; |
67 html.day.presence.leave = [[###TIME_STUFF###<font class="muc_leave"> *** ###NICK### leaves the room</font><br />]]; | 68 html.day.presence.leave = [[###TIME_STUFF###<font class="muc_leave"> *** ###NICK### leaves the room</font><br />]]; |
68 html.day.presence.statusText = [[ and his status message is "###STATUS###"]]; | 69 html.day.presence.statusText = [[ and his status message is "###STATUS###"]]; |
69 html.day.presence.statusChange = [[###TIME_STUFF###<font class="muc_statusChange"> *** ###NICK### shows now as "###SHOW###"###STATUS_STUFF###</font><br />]]; | 70 html.day.presence.statusChange = [[###TIME_STUFF###<font class="muc_statusChange"> *** ###NICK### shows now as "###SHOW###"###STATUS_STUFF###</font><br />]]; |
70 html.day.message = [[###TIME_STUFF###<font class="muc_name"><###NICK###></font> ###MSG###<br />]]; | 71 html.day.message = [[###TIME_STUFF###<font class="muc_msg_nick"><###NICK###></font> ###MSG###<br />]]; |
71 html.day.titleChange = [[###TIME_STUFF###<font class="muc_titlenick"> *** ###NICK### changed the title to</font> <font class="muc_title">"###TITLE###"</font><br />]]; | 72 html.day.titleChange = [[###TIME_STUFF###<font class="muc_titleChange"> *** ###NICK### changed the title to "###TITLE###"</font><br />]]; |
72 html.day.kick = [[###TIME_STUFF###<font class="muc_titlenick"> *** ###NICK### kicked ###VICTIM###</font><br />]]; | 73 html.day.reason = [[, the reason was "###REASON###"]] |
73 html.day.bann = [[###TIME_STUFF###<font class="muc_titlenick"> *** ###NICK### banned ###VICTIM###</font><br />]]; | 74 html.day.kick = [[###TIME_STUFF###<font class="muc_kick"> *** ###VICTIM### got kicked###REASON_STUFF###</font><br />]]; |
74 html.day.body = [[<h2>room ###JID### logging of 20###YEAR###/###MONTH###/###DAY###</h2><hr /><p> | 75 html.day.bann = [[###TIME_STUFF###<font class="muc_bann"> *** ###VICTIM### got banned###REASON_STUFF###</font><br />]]; |
76 html.day.body = [[<h2>room ###JID### logging of 20###YEAR###/###MONTH###/###DAY###</h2> | |
77 <p>###TITLE_STUFF###</p> | |
78 <hr /><p> | |
75 ###DAY_STUFF### | 79 ###DAY_STUFF### |
76 </p><hr />]]; | 80 </p><hr />]]; |
77 | 81 |
78 html.help = [[ | 82 html.help = [[ |
79 MUC logging is not configured correctly.<br /> | 83 MUC logging is not configured correctly.<br /> |
111 local stanza, origin = e.stanza, e.origin; | 115 local stanza, origin = e.stanza, e.origin; |
112 if validateLogFolder() == false then | 116 if validateLogFolder() == false then |
113 return; | 117 return; |
114 end | 118 end |
115 | 119 |
116 if (stanza.name == "presence") or | 120 if (stanza.name == "presence") or |
121 (stanza.name == "iq") or | |
117 (stanza.name == "message" and tostring(stanza.attr.type) == "groupchat") | 122 (stanza.name == "message" and tostring(stanza.attr.type) == "groupchat") |
118 then | 123 then |
119 local node, host, resource = splitJid(stanza.attr.to); | 124 local node, host, resource = splitJid(stanza.attr.to); |
120 if node ~= nil and host ~= nil then | 125 if node ~= nil and host ~= nil then |
121 local bare = node .. "@" .. host; | 126 local bare = node .. "@" .. host; |
122 if prosody.hosts[host] ~= nil and prosody.hosts[host].muc ~= nil and prosody.hosts[host].muc.rooms[bare] ~= nil then | 127 if prosody.hosts[host] ~= nil and prosody.hosts[host].muc ~= nil and prosody.hosts[host].muc.rooms[bare] ~= nil then |
123 local room = prosody.hosts[host].muc.rooms[bare] | 128 local room = prosody.hosts[host].muc.rooms[bare] |
124 local today = os.date("%y%m%d"); | 129 local today = os.date("%y%m%d"); |
125 local now = os.date("%X") | 130 local now = os.date("%X") |
126 local fn = config.folder .. "/" .. today .. "_" .. bare .. ".log"; | 131 local fn = config.folder .. "/" .. today .. "_" .. bare .. ".log"; |
132 local mucTo = nil | |
127 local mucFrom = nil; | 133 local mucFrom = nil; |
134 local alreadyJoined = false; | |
128 | 135 |
129 if stanza.name == "presence" and stanza.attr.type == nil then | 136 if stanza.name == "presence" and stanza.attr.type == nil then |
130 mucFrom = stanza.attr.to; | 137 mucFrom = stanza.attr.to; |
138 if room._occupants ~= nil and room._occupants[stanza.attr.to] ~= nil then -- if true, the user has already joined the room | |
139 alreadyJoined = true; | |
140 stanza:tag("alreadyJoined"):text("true"); -- we need to log the information that the user has already joined, so add this and remove after logging | |
141 end | |
142 elseif stanza.name == "iq" and stanza.attr.type == "set" then -- kick, to is the room, from is the admin, nick who is kicked is attr of iq->query->item | |
143 if stanza.tags[1] ~= nil and stanza.tags[1].name == "query" then | |
144 local tmp = stanza.tags[1]; | |
145 if tmp.tags[1] ~= nil and tmp.tags[1].name == "item" and tmp.tags[1].attr.nick ~= nil then | |
146 tmp = tmp.tags[1]; | |
147 for jid, nick in pairs(room._jid_nick) do | |
148 module:log("debug", "%s == %s", nick, stanza.attr.to .. "/" .. tmp.attr.nick) | |
149 if nick == stanza.attr.to .. "/" .. tmp.attr.nick then | |
150 mucTo = nick; | |
151 break; | |
152 end | |
153 end | |
154 end | |
155 end | |
131 else | 156 else |
132 for jid, nick in pairs(room._jid_nick) do | 157 for jid, nick in pairs(room._jid_nick) do |
133 if jid == stanza.attr.from then | 158 if jid == stanza.attr.from then |
134 mucFrom = nick; | 159 mucFrom = nick; |
160 break; | |
135 end | 161 end |
136 end | 162 end |
137 end | 163 end |
138 | 164 |
139 if mucFrom ~= nil then | 165 if mucFrom ~= nil or mucTo ~= nil then |
140 module:log("debug", "try to open room log: %s", fn); | 166 module:log("debug", "try to open room log: %s", fn); |
141 local f = assert(io.open(fn, "a")); | 167 local f = assert(io.open(fn, "a")); |
142 local realFrom = stanza.attr.from; | 168 local realFrom = stanza.attr.from; |
143 local realTo = stanza.attr.to; | 169 local realTo = stanza.attr.to; |
144 stanza.attr.from = mucFrom; | 170 stanza.attr.from = mucFrom; |
145 stanza.attr.to = nil; | 171 stanza.attr.to = mucTo; |
146 f:write("<stanza time=\"".. now .. "\">" .. tostring(stanza) .. "</stanza>\n"); | 172 f:write("<stanza time=\"".. now .. "\">" .. tostring(stanza) .. "</stanza>\n"); |
147 stanza.attr.from = realFrom; | 173 stanza.attr.from = realFrom; |
148 stanza.attr.to = realTo; | 174 stanza.attr.to = realTo; |
175 if alreadyJoined == true then | |
176 if stanza[#stanza].name == "alreadyJoined" then -- normaly the faked element should be the last, remove it when it is the last | |
177 stanza[#stanza] = nil; | |
178 else | |
179 for i = 1, #stanza, 1 do | |
180 if stanza[i].name == "alreadyJoined" then -- remove the faked element | |
181 stanza[i] = nil; | |
182 break; | |
183 end | |
184 end | |
185 end | |
186 end | |
149 f:close() | 187 f:close() |
150 end | 188 end |
151 end | 189 end |
152 end | 190 end |
191 else | |
192 module:log("debug", serialize(stanza)); | |
153 end | 193 end |
154 return; | 194 return; |
155 end | 195 end |
156 | 196 |
157 function createDoc(body) | 197 function createDoc(body) |
238 else | 278 else |
239 return generateRoomListSiteContent(); -- fallback | 279 return generateRoomListSiteContent(); -- fallback |
240 end | 280 end |
241 end | 281 end |
242 | 282 |
283 local function parseIqStanza(stanza, timeStuff, nick) | |
284 local text = nil; | |
285 -- module:log("debug", serialize(stanza)); | |
286 for _,tag in ipairs(stanza) do | |
287 if tag.tag == "query" then | |
288 for _,item in ipairs(tag) do | |
289 if item.tag == "item" then | |
290 for _,reason in ipairs(item) do | |
291 if reason.tag == "reason" then | |
292 text = reason[1]; | |
293 break; | |
294 end | |
295 end | |
296 break; | |
297 end | |
298 end | |
299 break; | |
300 end | |
301 end | |
302 | |
303 if text ~= nil then | |
304 text = html.day.reason:gsub("###REASON###", htmlEscape(text)); | |
305 else | |
306 text = ""; | |
307 end | |
308 return html.day.kick:gsub("###TIME_STUFF###", timeStuff):gsub("###VICTIM###", nick):gsub("###REASON_STUFF###", text); | |
309 end | |
310 | |
243 local function parsePresenceStanza(stanza, timeStuff, nick) | 311 local function parsePresenceStanza(stanza, timeStuff, nick) |
244 local ret = ""; | 312 local ret = ""; |
245 -- module:log("debug", serialize(stanza)); | 313 if stanza.attr.type == nil then |
246 if stanza[1].attr.type == nil then | |
247 local show, status = nil, ""; | 314 local show, status = nil, ""; |
248 for _, tag in ipairs(stanza[1]) do | 315 local alreadyJoined = false; |
249 module:log("debug", serialize(tag)); | 316 for _, tag in ipairs(stanza) do |
250 if tag.tag == "show" then | 317 if tag.tag == "alreadyJoined" then |
318 alreadyJoined = true; | |
319 elseif tag.tag == "show" then | |
251 show = tag[1]; | 320 show = tag[1]; |
252 elseif tag.tag == "status" then | 321 elseif tag.tag == "status" then |
253 status = tag[1]; | 322 status = tag[1]; |
254 end | 323 end |
255 end | 324 end |
256 if show ~= nil then | 325 if alreadyJoined == true then |
326 if show == nil then | |
327 show = "online"; | |
328 end | |
257 ret = html.day.presence.statusChange:gsub("###TIME_STUFF###", timeStuff); | 329 ret = html.day.presence.statusChange:gsub("###TIME_STUFF###", timeStuff); |
258 if status ~= "" then | 330 if status ~= "" then |
259 status = html.day.presence.statusText:gsub("###STATUS###", htmlEscape(status)); | 331 status = html.day.presence.statusText:gsub("###STATUS###", htmlEscape(status)); |
260 end | 332 end |
261 ret = ret:gsub("###SHOW###", show):gsub("###NICK###", nick):gsub("###STATUS_STUFF###", status); | 333 ret = ret:gsub("###SHOW###", show):gsub("###NICK###", nick):gsub("###STATUS_STUFF###", status); |
262 else | 334 else |
263 ret = html.day.presence.join:gsub("###TIME_STUFF###", timeStuff):gsub("###NICK###", nick); | 335 ret = html.day.presence.join:gsub("###TIME_STUFF###", timeStuff):gsub("###NICK###", nick); |
264 end | 336 end |
265 elseif stanza[1].attr.type ~= nil and stanza[1].attr.type == "unavailable" then | 337 elseif stanza.attr.type ~= nil and stanza.attr.type == "unavailable" then |
266 ret = html.day.presence.leave:gsub("###TIME_STUFF###", timeStuff):gsub("###NICK###", nick); | 338 ret = html.day.presence.leave:gsub("###TIME_STUFF###", timeStuff):gsub("###NICK###", nick); |
267 end | 339 end |
268 return ret; | 340 return ret; |
269 end | 341 end |
270 | 342 |
271 local function parseMessageStanza(stanza, timeStuff, nick) | 343 local function parseMessageStanza(stanza, timeStuff, nick) |
272 local body, title, ret = nil, nil, ""; | 344 local body, title, ret = nil, nil, ""; |
273 | 345 |
274 for _,tag in ipairs(stanza[1]) do | 346 for _,tag in ipairs(stanza) do |
275 if tag.tag == "body" then | 347 if tag.tag == "body" then |
276 body = tag[1]; | 348 body = tag[1]; |
277 if nick ~= nil then | 349 if nick ~= nil then |
278 break; | 350 break; |
279 end | 351 end |
297 ret = html.day.titleChange:gsub("###TIME_STUFF###", timeStuff):gsub("###NICK###", nick):gsub("###TITLE###", title); | 369 ret = html.day.titleChange:gsub("###TIME_STUFF###", timeStuff):gsub("###NICK###", nick):gsub("###TITLE###", title); |
298 end | 370 end |
299 return ret; | 371 return ret; |
300 end | 372 end |
301 | 373 |
302 local function parseDay(bareRoomJid, query) | 374 local function parseDay(bareRoomJid, roomSubject, query) |
303 local ret = ""; | 375 local ret = ""; |
304 local year; | 376 local year; |
305 local month; | 377 local month; |
306 local day; | 378 local day; |
307 local tmp; | 379 local tmp; |
319 local timeStuff = html.day.time:gsub("###TIME###", stanza.attr.time); | 391 local timeStuff = html.day.time:gsub("###TIME###", stanza.attr.time); |
320 if stanza[1] ~= nil then | 392 if stanza[1] ~= nil then |
321 local nick; | 393 local nick; |
322 | 394 |
323 -- grep nick from "from" resource | 395 -- grep nick from "from" resource |
324 if stanza[1].attr.from ~= nil then | 396 if stanza[1].attr.from ~= nil then -- presence and messages |
325 nick = htmlEscape(stanza[1].attr.from:match("/(.+)$")); | 397 nick = htmlEscape(stanza[1].attr.from:match("/(.+)$")); |
398 elseif stanza[1].attr.to ~= nil then -- iq | |
399 nick = htmlEscape(stanza[1].attr.to:match("/(.+)$")); | |
326 end | 400 end |
327 | 401 |
328 if stanza[1].tag == "presence" and nick ~= nil then | 402 if stanza[1].tag == "presence" and nick ~= nil then |
329 ret = ret .. parsePresenceStanza(stanza, timeStuff, nick); | 403 ret = ret .. parsePresenceStanza(stanza[1], timeStuff, nick); |
330 elseif stanza[1].tag == "message" then | 404 elseif stanza[1].tag == "message" then |
331 ret = ret .. parseMessageStanza(stanza, timeStuff, nick); | 405 ret = ret .. parseMessageStanza(stanza[1], timeStuff, nick); |
406 elseif stanza[1].tag == "iq" then | |
407 ret = ret .. parseIqStanza(stanza[1], timeStuff, nick); | |
332 else | 408 else |
333 module:log("info", "unknown stanza subtag in log found. room: %s; day: %s", bareRoomJid, query.year .. "/" .. query.month .. "/" .. query.day); | 409 module:log("info", "unknown stanza subtag in log found. room: %s; day: %s", bareRoomJid, query.year .. "/" .. query.month .. "/" .. query.day); |
334 end | 410 end |
335 end | 411 end |
336 end | 412 end |
337 end | 413 end |
338 else | 414 else |
339 module:log("warn", "could not parse room log. room: %s; day: %s", bareRoomJid, query.year .. "/" .. query.month .. "/" .. query.day); | 415 module:log("warn", "could not parse room log. room: %s; day: %s", bareRoomJid, query.year .. "/" .. query.month .. "/" .. query.day); |
340 end | 416 end |
341 else | 417 else |
342 ret = err; | 418 return generateDayListSiteContentByRoom(bareRoomJid); -- fallback |
343 end | 419 end |
344 tmp = html.day.body:gsub("###DAY_STUFF###", ret):gsub("###JID###", bareRoomJid); | 420 tmp = html.day.body:gsub("###DAY_STUFF###", ret):gsub("###JID###", bareRoomJid); |
345 tmp = tmp:gsub("###YEAR###", query.year):gsub("###MONTH###", query.month):gsub("###DAY###", query.day); | 421 tmp = tmp:gsub("###YEAR###", query.year):gsub("###MONTH###", query.month):gsub("###DAY###", query.day); |
422 tmp = tmp:gsub("###TITLE_STUFF###", html.day.title:gsub("###TITLE###", roomSubject)); | |
346 return tmp; | 423 return tmp; |
347 else | 424 else |
348 return generateDayListSiteContentByRoom(bareRoomJid); -- fallback | 425 return generateDayListSiteContentByRoom(bareRoomJid); -- fallback |
349 end | 426 end |
350 end | 427 end |
351 | 428 |
352 function handle_request(method, body, request) | 429 function handle_request(method, body, request) |
353 module:log("debug", "got a request ...") | |
354 local query = splitQuery(request.url.query); | 430 local query = splitQuery(request.url.query); |
355 local node, host = grepRoomJid(request.url.path); | 431 local node, host = grepRoomJid(request.url.path); |
356 | 432 |
357 if validateLogFolder() == false then | 433 if validateLogFolder() == false then |
358 return createDoc(html.help); | 434 return createDoc(html.help); |
362 if prosody.hosts[host] ~= nil and prosody.hosts[host].muc ~= nil and prosody.hosts[host].muc.rooms[bare] ~= nil then | 438 if prosody.hosts[host] ~= nil and prosody.hosts[host].muc ~= nil and prosody.hosts[host].muc.rooms[bare] ~= nil then |
363 local room = prosody.hosts[host].muc.rooms[bare]; | 439 local room = prosody.hosts[host].muc.rooms[bare]; |
364 if request.url.query == nil then | 440 if request.url.query == nil then |
365 return createDoc(generateDayListSiteContentByRoom(bare)); | 441 return createDoc(generateDayListSiteContentByRoom(bare)); |
366 else | 442 else |
367 return createDoc(parseDay(bare, query)); | 443 local subject = "" |
444 if room._data ~= nil and room._data.subject ~= nil then | |
445 subject = room._data.subject; | |
446 end | |
447 return createDoc(parseDay(bare, subject, query)); | |
368 end | 448 end |
369 else | 449 else |
370 module:log("warn", "room instance not found. bare room jid: %s", tostring(bare)); | 450 return createDoc(generateRoomListSiteContent()); |
371 end | 451 end |
372 else | 452 else |
373 return createDoc(generateRoomListSiteContent()); | 453 return createDoc(generateRoomListSiteContent()); |
374 end | 454 end |
375 return; | 455 return; |
376 end | 456 end |
377 | 457 |
378 config = config_get(module:get_host(), "core", "muc_log"); | 458 config = config_get(module:get_host(), "core", "muc_log"); |
379 module:log("debug", serialize(config)); | |
380 | 459 |
381 httpserver.new_from_config({ config.http_port or true }, handle_request, { base = "muc_log" }); | 460 httpserver.new_from_config({ config.http_port or true }, handle_request, { base = "muc_log" }); |
382 | 461 |
383 module:hook("message/bare", logIfNeeded, 500); | 462 module:hook("message/bare", logIfNeeded, 500); |
384 module:hook("pre-message/bare", logIfNeeded, 500); | 463 module:hook("pre-message/bare", logIfNeeded, 500); |
464 module:hook("iq/bare", logIfNeeded, 500); | |
465 module:hook("pre-iq/bare", logIfNeeded, 500); | |
385 module:hook("presence/full", logIfNeeded, 500); | 466 module:hook("presence/full", logIfNeeded, 500); |
386 module:hook("pre-presence/full", logIfNeeded, 500); | 467 module:hook("pre-presence/full", logIfNeeded, 500); |