Software /
code /
prosody-modules
Diff
mod_push2/mod_push2.lua @ 6232:d72010642b31 draft
Merge update
author | Trần H. Trung <xmpp:trần.h.trung@trung.fun> |
---|---|
date | Fri, 11 Apr 2025 23:19:21 +0700 |
parent | 6219:06621ab30be0 |
child | 6233:1c16bb49f6f6 |
line wrap: on
line diff
--- a/mod_push2/mod_push2.lua Tue Mar 18 00:31:36 2025 +0700 +++ b/mod_push2/mod_push2.lua Fri Apr 11 23:19:21 2025 +0700 @@ -9,11 +9,14 @@ local crypto = require "util.crypto"; local jwt = require "util.jwt"; +pcall(function() module:depends("track_muc_joins") end) + local xmlns_push = "urn:xmpp:push2:0"; -- configuration local contact_uri = module:get_option_string("contact_uri", "xmpp:" .. module.host) local extended_hibernation_timeout = module:get_option_number("push_max_hibernation_timeout", 72*3600) -- use same timeout like ejabberd +local hibernate_past_first_push = module:get_option_boolean("hibernate_past_first_push", true) local host_sessions = prosody.hosts[module.host].sessions local push2_registrations = module:open_store("push2_registrations", "keyval") @@ -28,7 +31,22 @@ module:hook("account-disco-info", account_dico_info); local function parse_match(matchel) - local match = { match = matchel.attr.profile } + local match = { match = matchel.attr.profile, chats = {} } + + for chatel in matchel:childtags("chat") do + local chat = {} + if chatel:get_child("mention") then + chat.mention = true + end + if chatel:get_child("reply") then + chat.reply = true + end + match.chats[chatel.attr.jid] = chat + end + + match.grace = matchel:get_child_text("grace") + if match.grace then match.grace = tonumber(match.grace) end + local send = matchel:get_child("send", "urn:xmpp:push2:send:notify-only:0") if send then match.send = send.attr.xmlns @@ -156,7 +174,7 @@ end -- is this push a high priority one -local function is_important(stanza) +local function is_important(stanza, session) local is_voip_stanza, urgent_reason = is_voip(stanza) if is_voip_stanza then return true; end @@ -177,8 +195,17 @@ -- carbon copied outgoing messages are not important if carbon and stanza_direction == "out" then return false; end - -- groupchat subjects are not important here - if st_type == "groupchat" and stanza:get_child_text("subject") then + -- groupchat reflections are not important here + if st_type == "groupchat" and session and session.rooms_joined then + local muc = jid.bare(stanza.attr.from) + local from_nick = jid.resource(stanza.attr.from) + if from_nick == session.rooms_joined[muc] then + return false + end + end + + -- edits are not imporatnt + if stanza:get_child("replace", "urn:xmpp:message-correct:0") then return false end @@ -222,7 +249,7 @@ end if string.len(envelope_bytes) > max_data_size then local body = stanza:get_child_text("body") - if string.len(body) > 50 then + if body and string.len(body) > 50 then stanza_clone:maptags(function(el) if el.name == "body" then return nil @@ -251,8 +278,9 @@ end) envelope_bytes = tostring(envelope) end - if string.len(envelope_bytes) < max_data_size/2 then - envelope:text_tag("rpad", base64.encode(random.bytes(math.min(150, max_data_size/3 - string.len(envelope_bytes))))) + local padding_size = math.min(150, max_data_size/3 - string.len(envelope_bytes)) + if padding_size > 0 then + envelope:text_tag("rpad", base64.encode(random.bytes(padding_size))) envelope_bytes = tostring(envelope) end @@ -294,12 +322,12 @@ push_notification_payload:text_tag("jwt", signer(payload), { key = base64.encode(public_key) }) end -local function handle_notify_request(stanza, node, user_push_services, log_push_decline) +local function handle_notify_request(stanza, node, user_push_services, session, log_push_decline) local pushes = 0; if not #user_push_services then return pushes end local notify_push_services = {}; - if is_important(stanza) then + if is_important(stanza, session) then notify_push_services = user_push_services else for identifier, push_info in pairs(user_push_services) do @@ -329,7 +357,7 @@ if send_push then local push_notification_payload = st.stanza("notification", { xmlns = xmlns_push }) push_notification_payload:text_tag("client", push_info.client) - push_notification_payload:text_tag("priority", is_voip(stanza) and "high" or (is_important(stanza) and "normal" or "low")) + push_notification_payload:text_tag("priority", is_voip(stanza) and "high" or (is_important(stanza, session) and "normal" or "low")) if is_voip(stanza) then push_notification_payload:tag("voip"):up() end @@ -340,13 +368,47 @@ if match.match == "urn:xmpp:push2:match:all" then does_match = true elseif match.match == "urn:xmpp:push2:match:important" then - does_match = is_important(stanza) + does_match = is_important(stanza, session) elseif match.match == "urn:xmpp:push2:match:archived" then does_match = stanza:get_child("stana-id", "urn:xmpp:sid:0") elseif match.match == "urn:xmpp:push2:match:archived-with-body" then does_match = stanza:get_child("stana-id", "urn:xmpp:sid:0") and has_body(stanza) end + local to_user, to_host = jid.split(stanza.attr.to) + to_user = to_user or session.username + to_host = to_host or module.host + + -- If another session has recent activity within configured grace period, don't send push + if does_match and match.grace and to_host == module.host and host_sessions[to_user] then + local now = os_time() + for _, session in pairs(host_sessions[to_user].sessions) do + if session.last_activity and session.push_registration_id ~= push_registration_id and (now - session.last_activity) < match.grace then + does_match = false + end + end + end + + local chat = match.chats and (match.chats[stanza.attr.from] or match.chats[jid.bare(stanza.attr.from)] or match.chats[jid.host(stanza.attr.from)]) + if does_match and chat then + does_match = false + + local nick = (session.rooms_joined and session.rooms_joined[jid.bare(stanza.attr.from)]) or to_user + + if not does_match and chat.mention then + local body = stanza:get_child_text("body") + if body and body:find(nick, 1, true) then + does_match = true + end + end + if not does_match and chat.reply then + local reply = stanza:get_child("reply", "urn:xmpp:reply:0") + if reply and (reply.attr.to == to_user.."@"..to_host or (jid.bare(reply.attr.to) == jid.bare(stanza.attr.from) and jid.resource(reply.attr.to) == nick)) then + does_match = true + end + end + end + if does_match and not sends_added[match.send] then sends_added[match.send] = true if match.send == "urn:xmpp:push2:send:notify-only" then @@ -384,7 +446,7 @@ module:hook("message/offline/handle", function(event) local node, user_push_services = get_push_settings(event.stanza, event.origin); module:log("debug", "Invoking handle_notify_request() for offline stanza"); - handle_notify_request(event.stanza, node, user_push_services, true); + handle_notify_request(event.stanza, node, user_push_services, event.origin, true); end, 1); -- publish on bare groupchat @@ -402,7 +464,7 @@ end end - handle_notify_request(event.stanza, node, notify_push_services, true); + handle_notify_request(event.stanza, node, notify_push_services, event.origin, true); end, 1); local function process_stanza_queue(queue, session, queue_type) @@ -415,14 +477,14 @@ local node, all_push_services = get_push_settings(stanza, session) local user_push_services = {[session.push_registration_id] = all_push_services[session.push_registration_id]} local stanza_type = "unimportant"; - if is_important(stanza) then stanza_type = "important"; end + if is_important(stanza, session) then stanza_type = "important"; end if not notified[stanza_type] then -- only notify if we didn't try to push for this stanza type already - if handle_notify_request(stanza, node, user_push_services, false) ~= 0 then + if handle_notify_request(stanza, node, user_push_services, session, false) ~= 0 then if session.hibernating and not session.first_hibernated_push then -- if the message was important -- then record the time of first push in the session for the smack module which will extend its hibernation -- timeout based on the value of session.first_hibernated_push - if is_important(stanza) then + if is_important(stanza, session) and not hibernate_past_first_push then session.first_hibernated_push = os_time(); -- check for prosody 0.12 mod_smacks if session.hibernating_watchdog and session.original_smacks_callback and session.original_smacks_timeout then @@ -567,7 +629,7 @@ end end - handle_notify_request(stanza, to_user, notify_push_services, true); + handle_notify_request(stanza, to_user, notify_push_services, event.origin, true); end end @@ -577,6 +639,15 @@ module:hook("smacks-hibernation-stanza-queued", process_smacks_stanza); module:hook("archive-message-added", archive_message_added); +local function track_activity(event) + if has_body(event.stanza) or event.stanza:child_with_ns("http://jabber.org/protocol/chatstates") then + event.origin.last_activity = os_time() + end +end + +module:hook("pre-message/bare", track_activity) +module:hook("pre-message/full", track_activity) + module:log("info", "Module loaded"); function module.unload() module:log("info", "Unloading module");