Diff

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
child 1343:7dbde05b48a9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_message_logging/mod_message_logging.lua	Thu May 09 10:39:20 2013 +0100
@@ -0,0 +1,127 @@
+module:set_global();
+
+local jid_bare = require "util.jid".bare;
+local jid_split = require "util.jid".split;
+
+local stat, mkdir = require "lfs".attributes, require "lfs".mkdir;
+
+-- Get a filesystem-safe string
+local function fsencode_char(c)
+	return ("%%%02x"):format(c:byte());
+end
+local function fsencode(s)
+	return (s:gsub("[^%w._-@]", fsencode_char):gsub("^%.", "_"));
+end
+
+local log_base_path = module:get_option("message_logging_dir", prosody.paths.data.."/message_logs");
+mkdir(log_base_path);
+
+local function get_host_path(host)
+	return log_base_path.."/"..fsencode(host);
+end
+
+local function get_user_path(jid)
+	local username, host = jid_split(jid);
+	local base = get_host_path(host)..os.date("/%Y-%m-%d");
+	if not stat(base) then
+		mkdir(base);
+	end
+	return base.."/"..fsencode(username)..".msglog";
+end
+
+local open_files_mt = { __index = function (open_files, jid)
+	local f, err = io.open(get_user_path(jid), "a+");
+	if not f then
+		module:log("error", "Failed to open message log for writing [%s]: %s", jid, err);
+	end
+	rawset(open_files, jid, f);
+	return f;
+end };
+
+-- [user@host] = filehandle
+local open_files = setmetatable({}, open_files_mt);
+
+function close_open_files()
+	module:log("debug", "Closing all open files");
+	for jid, filehandle in pairs(open_files) do
+		filehandle:close();
+		open_files[jid] = nil;
+	end
+end
+module:hook_global("logging-reloaded", close_open_files);
+
+local function handle_incoming_message(event)
+	local origin, stanza = event.origin, event.stanza;
+	local message_type = stanza.attr.type;
+	
+	if message_type == "error" then return; end
+	
+	local from, to = jid_bare(stanza.attr.from), jid_bare(stanza.attr.to);
+	local body = stanza:get_child("body");
+	if not body then return; end
+	body = body:get_text();
+	
+	local f = open_files[to];
+	if not f then return; end
+	if message_type == "groupchat" then
+		-- Add the nickname
+		from = from.." <"..(select(3, jid_split(stanza.attr.from)) or "")..">";
+	end
+	body = body:gsub("\n", "\n    "); -- Indent newlines
+	f:write("RECV: ", from, ": ", body, "\n");
+	f:flush();
+end
+
+local function handle_outgoing_message(event)
+	local origin, stanza = event.origin, event.stanza;
+	local message_type = stanza.attr.type;
+	
+	if message_type == "error" or message_type == "groupchat" then return; end
+	
+	local from, to = jid_bare(stanza.attr.from), jid_bare(stanza.attr.to);
+	local body = stanza:get_child("body");
+	if not body then return; end
+	body = body:get_text();
+	
+	local f = open_files[from];
+	if not f then return; end
+	body = body:gsub("\n", "\n    "); -- Indent newlines
+	f:write("SEND: ", to, ": ", body, "\n");
+	f:flush();
+end
+
+
+
+function module.add_host(module)
+	local host_base_path = get_host_path(module.host);
+	if not stat(host_base_path) then
+		mkdir(host_base_path);
+	end
+
+	module:hook("message/bare", handle_incoming_message, 1);
+	module:hook("message/full", handle_incoming_message, 1);
+	
+	module:hook("pre-message/bare", handle_outgoing_message, 1);
+	module:hook("pre-message/full", handle_outgoing_message, 1);
+	module:hook("pre-message/host", handle_outgoing_message, 1);
+	
+end
+
+function module.command(arg)
+	local command = table.remove(arg, 1);
+	if command == "path" then
+		print(get_user_path(arg[1]));
+	else
+		io.stderr:write("Unrecognised command: ", command);
+		return 1;
+	end
+	return 0;
+end
+
+function module.save()
+	return { open_files = open_files };
+end
+
+function module.restore(saved)
+	open_files = setmetatable(saved.open_files or {}, open_files_mt);
+end