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">&lt;###NICK###&gt;</font> ###MSG###<br />]]; 71 html.day.message = [[###TIME_STUFF###<font class="muc_msg_nick">&lt;###NICK###&gt;</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);