IMPORTANT: due to a drive failure, as of 13-Mar-2021, the Mercurial repository had to be re-mirrored, which changed every commit SHA. The old SHAs and trees are backed up in the vault branches. Please migrate to the new branches as soon as you can.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
prosody/plugins/mod_account_activity.lua

152 lines
4.8 KiB

local jid = require "prosody.util.jid";
local time = os.time;
local store = module:open_store(nil, "keyval+");
module:hook("authentication-success", function(event)
local session = event.session;
if session.username then
store:set_key(session.username, "timestamp", time());
end
end);
module:hook("resource-unbind", function(event)
local session = event.session;
if session.username then
store:set_key(session.username, "timestamp", time());
end
end);
local user_sessions = prosody.hosts[module.host].sessions;
function get_last_active(username) --luacheck: ignore 131/get_last_active
if user_sessions[username] then
return os.time(), true; -- Currently connected
else
local last_activity = store:get(username);
if not last_activity then return nil; end
return last_activity.timestamp;
end
end
module:add_item("shell-command", {
section = "user";
section_desc = "View user activity data";
name = "activity";
desc = "View the last recorded user activity for an account";
args = { { name = "jid"; type = "string" } };
host_selector = "jid";
handler = function(self, userjid) --luacheck: ignore 212/self
local username = jid.prepped_split(userjid);
local last_timestamp, is_online = get_last_active(username);
if not last_timestamp then
return true, "No activity";
end
return true, ("%s (%s)"):format(os.date("%Y-%m-%d %H:%M:%S", last_timestamp), (is_online and "online" or "offline"));
end;
});
module:add_item("shell-command", {
section = "user";
section_desc = "View user activity data";
name = "list_inactive";
desc = "List inactive user accounts";
args = {
{ name = "host"; type = "string" };
{ name = "duration"; type = "string" };
};
host_selector = "host";
handler = function(self, host, duration) --luacheck: ignore 212/self
local um = require "prosody.core.usermanager";
local duration_sec = require "prosody.util.human.io".parse_duration(duration or "");
if not duration_sec then
return false, ("Invalid duration %q - try something like \"30d\""):format(duration);
end
local now = os.time();
local n_inactive, n_unknown = 0, 0;
for username in um.users(host) do
local last_active = store:get_key(username, "timestamp");
if not last_active then
local created_at = um.get_account_info(username, host).created;
if created_at and (now - created_at) > duration_sec then
self.session.print(username, "");
n_inactive = n_inactive + 1;
elseif not created_at then
n_unknown = n_unknown + 1;
end
elseif (now - last_active) > duration_sec then
self.session.print(username, os.date("%Y-%m-%dT%T", last_active));
n_inactive = n_inactive + 1;
end
end
if n_unknown > 0 then
return true, ("%d accounts inactive since %s (%d unknown)"):format(n_inactive, os.date("%Y-%m-%dT%T", now - duration_sec), n_unknown);
end
return true, ("%d accounts inactive since %s"):format(n_inactive, os.date("%Y-%m-%dT%T", now - duration_sec));
end;
});
module:add_item("shell-command", {
section = "migrate";
section_desc = "Perform data migrations";
name = "account_activity_lastlog2";
desc = "Migrate account activity information from mod_lastlog2";
args = { { name = "host"; type = "string" } };
host_selector = "host";
handler = function(self, host) --luacheck: ignore 212/host
local lastlog2 = module:open_store("lastlog2", "keyval+");
local n_updated, n_errors, n_skipped = 0, 0, 0;
local async = require "prosody.util.async";
local p = require "prosody.util.promise".new(function (resolve)
local async_runner = async.runner(function ()
local n = 0;
for username in lastlog2:items() do
n = n + 1;
if n % 100 == 0 then
self.session.print(("Processed %d..."):format(n));
async.sleep(0);
end
local lastlog2_data = lastlog2:get(username);
if lastlog2_data then
local current_data, err = store:get(username);
if not current_data then
if not err then
current_data = {};
else
n_errors = n_errors + 1;
end
end
if current_data then
local imported_timestamp = current_data.timestamp;
local latest;
for k, v in pairs(lastlog2_data) do
if k ~= "registered" and (not latest or v.timestamp > latest) then
latest = v.timestamp;
end
end
if latest and (not imported_timestamp or imported_timestamp < latest) then
local ok, err = store:set_key(username, "timestamp", latest);
if ok then
n_updated = n_updated + 1;
else
self.session.print(("WW: Failed to import %q: %s"):format(username, err));
n_errors = n_errors + 1;
end
else
n_skipped = n_skipped + 1;
end
end
end
end
return resolve(("%d accounts imported, %d errors, %d skipped"):format(n_updated, n_errors, n_skipped));
end);
async_runner:run(true);
end);
return p;
end;
});