Mercurial > prosody-modules
comparison mod_message_logging/mod_message_logging.lua @ 1007:ba220790a59c
mod_message_logging: New module to log user conversations to text files
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Thu, 09 May 2013 10:39:20 +0100 |
| parents | |
| children | 7dbde05b48a9 |
comparison
equal
deleted
inserted
replaced
| 1006:9c88960b0f81 | 1007:ba220790a59c |
|---|---|
| 1 module:set_global(); | |
| 2 | |
| 3 local jid_bare = require "util.jid".bare; | |
| 4 local jid_split = require "util.jid".split; | |
| 5 | |
| 6 local stat, mkdir = require "lfs".attributes, require "lfs".mkdir; | |
| 7 | |
| 8 -- Get a filesystem-safe string | |
| 9 local function fsencode_char(c) | |
| 10 return ("%%%02x"):format(c:byte()); | |
| 11 end | |
| 12 local function fsencode(s) | |
| 13 return (s:gsub("[^%w._-@]", fsencode_char):gsub("^%.", "_")); | |
| 14 end | |
| 15 | |
| 16 local log_base_path = module:get_option("message_logging_dir", prosody.paths.data.."/message_logs"); | |
| 17 mkdir(log_base_path); | |
| 18 | |
| 19 local function get_host_path(host) | |
| 20 return log_base_path.."/"..fsencode(host); | |
| 21 end | |
| 22 | |
| 23 local function get_user_path(jid) | |
| 24 local username, host = jid_split(jid); | |
| 25 local base = get_host_path(host)..os.date("/%Y-%m-%d"); | |
| 26 if not stat(base) then | |
| 27 mkdir(base); | |
| 28 end | |
| 29 return base.."/"..fsencode(username)..".msglog"; | |
| 30 end | |
| 31 | |
| 32 local open_files_mt = { __index = function (open_files, jid) | |
| 33 local f, err = io.open(get_user_path(jid), "a+"); | |
| 34 if not f then | |
| 35 module:log("error", "Failed to open message log for writing [%s]: %s", jid, err); | |
| 36 end | |
| 37 rawset(open_files, jid, f); | |
| 38 return f; | |
| 39 end }; | |
| 40 | |
| 41 -- [user@host] = filehandle | |
| 42 local open_files = setmetatable({}, open_files_mt); | |
| 43 | |
| 44 function close_open_files() | |
| 45 module:log("debug", "Closing all open files"); | |
| 46 for jid, filehandle in pairs(open_files) do | |
| 47 filehandle:close(); | |
| 48 open_files[jid] = nil; | |
| 49 end | |
| 50 end | |
| 51 module:hook_global("logging-reloaded", close_open_files); | |
| 52 | |
| 53 local function handle_incoming_message(event) | |
| 54 local origin, stanza = event.origin, event.stanza; | |
| 55 local message_type = stanza.attr.type; | |
| 56 | |
| 57 if message_type == "error" then return; end | |
| 58 | |
| 59 local from, to = jid_bare(stanza.attr.from), jid_bare(stanza.attr.to); | |
| 60 local body = stanza:get_child("body"); | |
| 61 if not body then return; end | |
| 62 body = body:get_text(); | |
| 63 | |
| 64 local f = open_files[to]; | |
| 65 if not f then return; end | |
| 66 if message_type == "groupchat" then | |
| 67 -- Add the nickname | |
| 68 from = from.." <"..(select(3, jid_split(stanza.attr.from)) or "")..">"; | |
| 69 end | |
| 70 body = body:gsub("\n", "\n "); -- Indent newlines | |
| 71 f:write("RECV: ", from, ": ", body, "\n"); | |
| 72 f:flush(); | |
| 73 end | |
| 74 | |
| 75 local function handle_outgoing_message(event) | |
| 76 local origin, stanza = event.origin, event.stanza; | |
| 77 local message_type = stanza.attr.type; | |
| 78 | |
| 79 if message_type == "error" or message_type == "groupchat" then return; end | |
| 80 | |
| 81 local from, to = jid_bare(stanza.attr.from), jid_bare(stanza.attr.to); | |
| 82 local body = stanza:get_child("body"); | |
| 83 if not body then return; end | |
| 84 body = body:get_text(); | |
| 85 | |
| 86 local f = open_files[from]; | |
| 87 if not f then return; end | |
| 88 body = body:gsub("\n", "\n "); -- Indent newlines | |
| 89 f:write("SEND: ", to, ": ", body, "\n"); | |
| 90 f:flush(); | |
| 91 end | |
| 92 | |
| 93 | |
| 94 | |
| 95 function module.add_host(module) | |
| 96 local host_base_path = get_host_path(module.host); | |
| 97 if not stat(host_base_path) then | |
| 98 mkdir(host_base_path); | |
| 99 end | |
| 100 | |
| 101 module:hook("message/bare", handle_incoming_message, 1); | |
| 102 module:hook("message/full", handle_incoming_message, 1); | |
| 103 | |
| 104 module:hook("pre-message/bare", handle_outgoing_message, 1); | |
| 105 module:hook("pre-message/full", handle_outgoing_message, 1); | |
| 106 module:hook("pre-message/host", handle_outgoing_message, 1); | |
| 107 | |
| 108 end | |
| 109 | |
| 110 function module.command(arg) | |
| 111 local command = table.remove(arg, 1); | |
| 112 if command == "path" then | |
| 113 print(get_user_path(arg[1])); | |
| 114 else | |
| 115 io.stderr:write("Unrecognised command: ", command); | |
| 116 return 1; | |
| 117 end | |
| 118 return 0; | |
| 119 end | |
| 120 | |
| 121 function module.save() | |
| 122 return { open_files = open_files }; | |
| 123 end | |
| 124 | |
| 125 function module.restore(saved) | |
| 126 open_files = setmetatable(saved.open_files or {}, open_files_mt); | |
| 127 end |
