Mercurial > 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 |
| parents | d9749ed44f6e |
| children | 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); |
