Software /
code /
prosody
File
plugins/mod_account_activity.lua @ 13684:026a75a443de 13.0
mod_admin_shell: Hide secondary role commands, focus on primary roles
Secondary roles are an advanced feature without any strong use cases
currently. Having multiple ways to manage roles is confusing.
Now the 'user:role' command will just show the primary role if that is all
there is, but will list secondary roles too if there are any (which in 99.9%
of cases there won't be).
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Thu, 13 Feb 2025 16:18:59 +0000 |
parent | 13682:0055c177a54c |
line wrap: on
line source
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; });