diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/mod_account_activity.lua | 152 | ||||
-rw-r--r-- | plugins/mod_storage_sql.lua | 2 |
2 files changed, 153 insertions, 1 deletions
diff --git a/plugins/mod_account_activity.lua b/plugins/mod_account_activity.lua new file mode 100644 index 00000000..63a7fb39 --- /dev/null +++ b/plugins/mod_account_activity.lua @@ -0,0 +1,152 @@ +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); + 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; +}); diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index fa0588b2..ff44adba 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -42,7 +42,7 @@ end local function has_upsert(engine) if engine.params.driver == "SQLite3" then -- SQLite3 >= 3.24.0 - return (engine.sqlite_version[2] or 0) >= 24; + return engine.sqlite_version and (engine.sqlite_version[2] or 0) >= 24; elseif engine.params.driver == "PostgreSQL" then -- PostgreSQL >= 9.5 -- Versions without support have long since reached end of life. |