Software /
code /
prosody-modules
File
mod_report_affiliations/mod_report_affiliations.lua @ 6105:a21a375d79a9
mod_auto_accept_subscriptions: Update Compatibility
author | Menel <menel@snikket.de> |
---|---|
date | Sat, 21 Dec 2024 20:52:10 +0100 |
parent | 6058:e905ef16efb7 |
line wrap: on
line source
local dt = require "util.datetime"; local jid = require "util.jid"; local st = require "util.stanza"; local rm = require "core.rostermanager"; local um = require "core.usermanager"; local traits = module:require("traits"); local xmlns_aff = "urn:xmpp:raa:0"; local is_host_anonymous = module:get_option_string("authentication") == "anonymous"; local trusted_servers = module:get_option_inherited_set("report_affiliations_trusted_servers", {}); local roles = { -- These affiliations are defined by XEP-0489, and we map a set of Prosody roles to each one admin = module:get_option_set("report_affiliations_admin_roles", { "prosody:admin", "prosody:operator" }); member = module:get_option_set("report_affiliations_member_roles", { "prosody:member" }); registered = module:get_option_set("report_affiliations_registered", { "prosody:user", "prosody:registered" }); guest = module:get_option_set("report_affiliations_anonymous", { "prosody:guest" }); }; -- Map of role to affiliation local role_affs = { -- [role (e.g. "prosody:guest")] = affiliation ("admin"|"member"|"registered"|"guest"); }; --Build the role->affiliation map based on the config for aff, aff_roles in pairs(roles) do for role in aff_roles do role_affs[role] = aff; end end local account_details_store = module:open_store("account_details"); local lastlog2_store = module:open_store("lastlog2"); module:add_feature(xmlns_aff); module:add_feature(xmlns_aff.."#embed-presence-sub"); module:add_feature(xmlns_aff.."#embed-presence-directed"); local function get_registered_timestamp(username) if um.get_account_info then local ts = um.get_account_info(username, module.host); if ts then return ts.created; end end local account_details = account_details_store:get(username); if account_details and account_details.registered then return account_details.registered; end local lastlog2 = lastlog2_store:get(username); if lastlog2 and lastlog2.registered then return lastlog2.registered.timestamp; end return nil; end local function get_trust_score(username) return math.floor(100 * (1 - traits.get_probability_bad(username))); end local function get_account_type(username) if is_host_anonymous then return "anonymous"; end if not um.get_user_role then return "registered"; -- COMPAT w/0.12 end local user_role = um.get_user_role(username, module.host); return role_affs[user_role] or "registered"; end function get_info_element(username) local account_type = get_account_type(username); local since, trust; if account_type == "registered" then since = get_registered_timestamp(username); trust = get_trust_score(username); end return st.stanza("info", { affiliation = account_type; since = since and dt.datetime(since - (since%86400)) or nil; trust = ("%d"):format(trust); xmlns = xmlns_aff; }); end -- Outgoing presence local function embed_in_outgoing_presence(pres_type) return function (event) local origin, stanza = event.origin, event.stanza; stanza:remove_children("info", xmlns_aff); -- Unavailable presence is pretty harmless, and blocking it may cause -- weird issues. if (pres_type == "bare" and stanza.attr.type == "unavailable") or (pres_type == "full" and stanza.attr.type ~= nil) then return; end -- Only attach info to stanzas sent to "strangers" (users that have not -- approved us to see their presence) if rm.is_user_subscribed(origin.username, origin.host, stanza.attr.to) then return; end local info = get_info_element(origin.username); if not info then return; end stanza:add_direct_child(info); end; end module:hook("pre-presence/bare", embed_in_outgoing_presence("bare")); module:hook("pre-presence/full", embed_in_outgoing_presence("full")); -- Handle direct queries local function should_permit_query(from_jid, to_username) --luacheck: ignore 212/to_username local from_node, from_host = jid.split(from_jid); if from_node then return false; end -- Who should we respond to? -- Only respond to domains -- Does user have a JID with this domain in directed presence? (doesn't work with bare JIDs) -- Does this user have a JID with domain in pending subscription requests? if trusted_servers:contains(from_host) then return true; end return false; end module:hook("iq-get/bare/urn:xmpp:raa:0:query", function (event) local origin, stanza = event.origin, event.stanza; local username = jid.node(stanza.attr.to); if not should_permit_query(stanza.attr.from, username) then origin.send(st.error_reply(stanza, "auth", "forbidden")); return true; end local info = get_info_element(username); local reply = st.reply(stanza) :add_child(info); origin.send(reply); return true; end);