fcm_notify splits userName internally via jid_split, so callers were double-splitting: passing jid_split(to) fed the bare node as userName and shifted every subsequent argument by one (host→title, resource→body, …), corrupting all notification payloads. Pass the full JID instead. In mod_vnc_muc_fcm, replace event.stanza:clone() with st.clone() and add the util.stanza import for robustness. Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/11>
176 lines
6.3 KiB
Lua
176 lines
6.3 KiB
Lua
-- mod_vnc_muc_fcm.lua
|
|
--
|
|
-- FCM push notifications for MUC / group chat.
|
|
--
|
|
-- Shared logic (caching, FCM HTTP, token building, stale-token cleanup)
|
|
-- lives in vnc_fcm_common.lua. This module retains the MUC broadcast
|
|
-- handler and event hooks that are specific to the conference component.
|
|
--
|
|
-- The affiliate-notification loop is deferred via timer.add_task(0, …) so
|
|
-- that FCM work (cache lookups, HTTP request setup) does not block the
|
|
-- muc-broadcast-message event path.
|
|
|
|
local storagemanager = require "core.storagemanager";
|
|
local jid_bare = require "util.jid".bare;
|
|
local jid_split = require "util.jid".split;
|
|
local timer = require "util.timer";
|
|
local st = require "util.stanza";
|
|
local common = require "vnc_fcm_common";
|
|
|
|
local trim = common.utils.trim;
|
|
local starts_with = common.utils.starts_with;
|
|
|
|
local storage_host = module:get_option_string("storage_host");
|
|
|
|
module:log("info","founs storage_host: %s", storage_host);
|
|
|
|
local fcmAPIKey = module:get_option_string("fcm_api_key");
|
|
local fcmAPIURL = module:get_option_string("fcm_api_url");
|
|
|
|
local moduleIsActive = fcmAPIURL and fcmAPIKey;
|
|
|
|
local inactive_devices = {};
|
|
local ThisDomain = module.host;
|
|
|
|
local stores = {
|
|
private = module:open_store("private"),
|
|
vcard = module:open_store("vcard"),
|
|
fcmtoken = module:open_store("fcmtoken"),
|
|
fcmtoken_map = module:open_store("fcmtoken", "map"),
|
|
};
|
|
|
|
timer.add_task(3, function ()
|
|
stores.private = storagemanager.open(storage_host, "private");
|
|
stores.vcard = storagemanager.open(storage_host, "vcard");
|
|
stores.fcmtoken = storagemanager.open(storage_host, "fcmtoken");
|
|
stores.fcmtoken_map = storagemanager.open(storage_host, "fcmtoken", "map");
|
|
if not stores.private or not stores.vcard then
|
|
module:log("info", "private/vcard_store %s/%s not found? - will try again", stores.private, stores.vcard);
|
|
return 3;
|
|
else
|
|
module:log("info", "private/vcard_store are now %s/%s", stores.private, stores.vcard);
|
|
end
|
|
end);
|
|
|
|
local F = common.new({
|
|
mod = module,
|
|
stores = stores,
|
|
domain = storage_host,
|
|
is_muc = true,
|
|
ios_always_push = true,
|
|
global_mute_early_return = true,
|
|
override_topic_on_hidden = false,
|
|
allow_etype_or_readtarget = false,
|
|
domain_check_enabled = true,
|
|
fcm_api_key = fcmAPIKey,
|
|
fcm_api_url = fcmAPIURL,
|
|
module_is_active = moduleIsActive,
|
|
inactive_devices = inactive_devices,
|
|
});
|
|
|
|
local fcm_notify = F.fcm_notify;
|
|
local getDisplayName_cached = F.getDisplayName_cached;
|
|
local invalidateNotifyCache = F.invalidateNotifyCache;
|
|
local invalidateVcardCache = F.invalidateVcardCache;
|
|
|
|
|
|
-- ─── MUC broadcast handler (deferred) ──────────────────────────────
|
|
--
|
|
-- handle_muc_message runs on the muc-broadcast-message event. The
|
|
-- notification work (cache lookups, HTTP request setup, affiliate loop)
|
|
-- is deferred to a zero-delay timer so it does not block message
|
|
-- delivery to room occupants. The stanza is cloned before deferral
|
|
-- because the original may be recycled after the event handler returns.
|
|
|
|
local function handle_muc_message(event)
|
|
local room = event.room
|
|
local stanza = st.clone(event.stanza)
|
|
|
|
timer.add_task(0, function()
|
|
local message_id = stanza.attr.id;
|
|
local type = stanza.attr.type or "normal";
|
|
local from = jid_bare(stanza.attr.from);
|
|
local to = jid_bare(stanza.attr.to) or from;
|
|
|
|
local vncTalkConferenceEtype = stanza:find("{xmpp:vnctalk}vncTalkConference/eventType#") or nil;
|
|
|
|
local content = trim(stanza:find("body#"));
|
|
local delayedDelivery = stanza:get_child("delay", "urn:xmpp:delay");
|
|
|
|
local vncTalkIncomingType = stanza:find("{xmpp:vnctalk}vncTalkConference/conferenceType#");
|
|
local vncTalkConferenceId = stanza:find("{xmpp:vnctalk}vncTalkConference/conferenceId#") or nil;
|
|
local jitsiURL = stanza:find("{xmpp:vnctalk}vncTalkConference/jitsiURL#") or nil;
|
|
local jitsiRoom = stanza:find("{xmpp:vnctalk}vncTalkConference/jitsiRoom#") or nil;
|
|
local vncTalkWhiteboard = stanza:find("{xmpp:vnctalk}whiteboard");
|
|
local isSentCarbonMessage = stanza:get_child("sent", "urn:xmpp:carbons:2");
|
|
local vnc_attachment_type = stanza:find("{xmpp:vnctalk}attachment/fileType#");
|
|
|
|
local vnctalk_broadcast = stanza:get_child("vncTalkBroadcast", "xmpp:vnctalk");
|
|
if vnctalk_broadcast then
|
|
if starts_with(to, "broadcast") then
|
|
return;
|
|
end
|
|
return;
|
|
end
|
|
|
|
local isMessageCorrection = stanza:find("{urn:xmpp:message-correct:0}replace@id") or nil;
|
|
if (isMessageCorrection) then
|
|
content = " ";
|
|
type = "CORRECTION";
|
|
end
|
|
|
|
local auxType = vncTalkIncomingType or (vncTalkWhiteboard and "whiteboard");
|
|
|
|
if (auxType) then
|
|
content = auxType;
|
|
type = auxType;
|
|
end;
|
|
|
|
local doNotify = content and (content ~= " ") and (content ~= "") and (delayedDelivery == nil) and (isSentCarbonMessage == nil) and (
|
|
(type == "chat") or (type == "groupchat") or (type == "audio") or (type == "video") or (type == "whiteboard") or (type == "screen") or (type == "CORRECTION")
|
|
);
|
|
|
|
if (doNotify) then
|
|
local affs = room._affiliations;
|
|
if not affs then return end
|
|
local sender = "";
|
|
local f2, h2, n2 = jid_split(stanza.attr.from);
|
|
if (affs[n2] ~= nil) then sender = n2; end
|
|
local roomTopic = room._data.subject or nil;
|
|
local senderDisplayName = getDisplayName_cached(sender);
|
|
local roomJid = to;
|
|
for aff_jid, aff in pairs(affs) do
|
|
if aff ~= "outcast" then
|
|
if (sender ~= aff_jid) then
|
|
module:log("info", "from is: %s", from);
|
|
fcm_notify(aff_jid, roomJid, content, type, senderDisplayName, roomTopic, vnc_attachment_type, message_id, vncTalkConferenceEtype, vncTalkConferenceId, sender, jitsiURL, jitsiRoom, isMessageCorrection);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
|
|
-- ─── event hooks ───────────────────────────────────────────────────
|
|
|
|
module:hook("muc-broadcast-message", handle_muc_message, 5)
|
|
|
|
module:hook("host-activated", function(host)
|
|
module:log("info", "got activated event for host %s", host);
|
|
end);
|
|
|
|
module:hook_global("vnc-fcm-invalidate-notify-cache", function(event)
|
|
if event and event.username then
|
|
invalidateNotifyCache(event.username);
|
|
end
|
|
end);
|
|
|
|
module:hook_global("vnc-fcm-invalidate-vcard-cache", function(event)
|
|
if event and event.username then
|
|
invalidateVcardCache(event.username);
|
|
end
|
|
end);
|
|
|
|
module:log("debug", "Module loaded")
|