Files
jitsi-meet/resources/prosody-plugins/mod_fmuc.lua
damencho c069c0d7c3 feat(display-name): Handles new display-name extension in messages.
The display name is used in messages when messages are coming from visitors or from the history. The display name is used only when the participant is not available in the meeting to get its name.
2025-09-03 08:07:11 -05:00

820 lines
29 KiB
Lua

--- activate under main muc component
--- Add the following config under the main muc component
--- muc_room_default_presence_broadcast = {
--- visitor = false;
--- participant = true;
--- moderator = true;
--- };
--- Enable in global modules: 's2s_bidi'
--- Make sure 's2s' is not in modules_disabled
--- NOTE: Make sure all communication between prosodies is using the real jids ([foo]room1@muc.example.com), as there
--- are certain configs for whitelisted domains and connections that are domain based
--- TODO: filter presence from main occupants back to main prosody
local jid = require 'util.jid';
local st = require 'util.stanza';
local new_id = require 'util.id'.medium;
local filters = require 'util.filters';
local array = require 'util.array';
local set = require 'util.set';
local json = require 'cjson.safe';
local util = module:require 'util';
local is_admin = util.is_admin;
local ends_with = util.ends_with;
local is_vpaas = util.is_vpaas;
local room_jid_match_rewrite = util.room_jid_match_rewrite;
local get_room_from_jid = util.get_room_from_jid;
local get_focus_occupant = util.get_focus_occupant;
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
local presence_check_status = util.presence_check_status;
local respond_iq_result = util.respond_iq_result;
local table_compare = util.table_compare;
local PARTICIPANT_PROP_RAISE_HAND = 'jitsi_participant_raisedHand';
local PARTICIPANT_PROP_REQUEST_TRANSCRIPTION = 'jitsi_participant_requestingTranscription';
local PARTICIPANT_PROP_TRANSLATION_LANG = 'jitsi_participant_translation_language';
local TRANSCRIPT_DEFAULT_LANG = module:get_option_string('transcriptions_default_language', 'en');
-- this is the main virtual host of this vnode
local local_domain = module:get_option_string('muc_mapper_domain_base');
if not local_domain then
module:log('warn', "No 'muc_mapper_domain_base' option set, disabling fmuc plugin");
return;
end
-- this is the main virtual host of the main prosody that this vnode serves
local main_domain = module:get_option_string('main_domain');
if not main_domain then
module:log('warn', "No 'main_domain' option set, disabling fmuc plugin");
return;
end
local muc_domain_prefix = module:get_option_string('muc_mapper_domain_prefix', 'conference');
local local_muc_domain = muc_domain_prefix..'.'..local_domain;
local NICK_NS = 'http://jabber.org/protocol/nick';
local DISPLAY_NAME_NS = 'http://jitsi.org/protocol/display-name';
-- in certain cases we consider participants with token as moderators, this is the default behavior which can be turned off
local auto_promoted_with_token = module:get_option_boolean('visitors_auto_promoted_with_token', true);
-- we send stats for the total number of rooms, total number of participants and total number of visitors
local measure_rooms = module:measure('vnode-rooms', 'amount');
local measure_participants = module:measure('vnode-participants', 'amount');
local measure_visitors = module:measure('vnode-visitors', 'amount');
local sent_iq_cache = require 'util.cache'.new(200);
local sessions = prosody.full_sessions;
local function send_transcriptions_update(room)
-- let's notify main prosody
local lang_array = array();
local count = 0;
for k, v in pairs(room._transcription_languages) do
lang_array:push(v);
count = count + 1;
end
local iq_id = new_id();
sent_iq_cache:set(iq_id, socket.gettime());
module:send(st.iq({
type = 'set',
to = 'visitors.'..main_domain,
from = local_domain,
id = iq_id })
:tag('visitors', { xmlns = 'jitsi:visitors',
room = jid.join(jid.node(room.jid), muc_domain_prefix..'.'..main_domain) })
:tag('transcription-languages', {
xmlns = 'jitsi:visitors',
langs = lang_array:unique():sort():concat(','),
count = tostring(count)
}):up());
end
local function remove_transcription(room, occupant)
local send_update = false;
if room._transcription_languages then
if room._transcription_languages[occupant.jid] then
send_update = true;
end
room._transcription_languages[occupant.jid] = nil;
end
if send_update then
send_transcriptions_update(room);
end
end
-- if lang is nil we will remove it from the list
local function add_transcription(room, occupant, lang)
if not room._transcription_languages then
room._transcription_languages = {};
end
local old = room._transcription_languages[occupant.jid];
room._transcription_languages[occupant.jid] = lang or TRANSCRIPT_DEFAULT_LANG;
if old ~= room._transcription_languages[occupant.jid] then
send_transcriptions_update(room);
end
end
-- mark all occupants as visitors
module:hook('muc-occupant-pre-join', function (event)
local occupant, room, origin, stanza = event.occupant, event.room, event.origin, event.stanza;
local node, host = jid.split(occupant.bare_jid);
local resource = jid.resource(occupant.nick);
if is_admin(occupant.bare_jid) then
return;
end
if prosody.hosts[host] then
-- local participants which host is defined in this prosody
if room._main_room_lobby_enabled then
origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Visitors not allowed while lobby is on!')
:tag('no-visitors-lobby', { xmlns = 'jitsi:visitors' }));
return true;
else
occupant.role = 'visitor';
end
elseif room.moderators_list and room.moderators_list:contains(resource) then
-- remote participants, host is the main prosody
occupant.role = 'moderator';
end
end, 3);
-- if a visitor leaves we want to lower its hand if it was still raised before leaving
-- this is to clear indication for promotion on moderators visitors list
module:hook('muc-occupant-pre-leave', function (event)
local occupant = event.occupant;
---- we are interested only of visitors presence
if occupant.role ~= 'visitor' then
return;
end
local room = event.room;
-- let's check if the visitor has a raised hand send a lower hand
-- to main prosody
local pr = occupant:get_presence();
local raiseHand = pr:get_child_text(PARTICIPANT_PROP_RAISE_HAND);
-- a promotion detected let's send it to main prosody
if raiseHand and #raiseHand > 0 then
local iq_id = new_id();
sent_iq_cache:set(iq_id, socket.gettime());
local promotion_request = st.iq({
type = 'set',
to = 'visitors.'..main_domain,
from = local_domain,
id = iq_id })
:tag('visitors', { xmlns = 'jitsi:visitors',
room = jid.join(jid.node(room.jid), muc_domain_prefix..'.'..main_domain) })
:tag('promotion-request', {
xmlns = 'jitsi:visitors',
jid = occupant.jid,
time = nil;
}):up();
module:send(promotion_request);
end
remove_transcription(room, occupant);
end, 1); -- rate limit is 0
-- Returns the main participants count and the visitors count
local function get_occupant_counts(room)
local main_count = 0;
local visitors_count = 0;
for _, o in room:each_occupant() do
if o.role == 'visitor' then
visitors_count = visitors_count + 1;
elseif not is_admin(o.bare_jid) then
main_count = main_count + 1;
end
end
return main_count, visitors_count;
end
local function cancel_destroy_timer(room)
if room.visitors_destroy_timer then
room.visitors_destroy_timer:stop();
room.visitors_destroy_timer = nil;
end
end
local function destroy_with_conference_ended(room)
-- if the room is being destroyed, ignore
if room.destroying then
return;
end
cancel_destroy_timer(room);
local main_count, visitors_count = get_occupant_counts(room);
module:log('info', 'Will destroy:%s main_occupants:%s visitors:%s', room.jid, main_count, visitors_count);
room:destroy(nil, 'Conference ended.');
return true;
end
-- schedules a new destroy timer which will destroy the room if there are no visitors after the timeout
local function schedule_destroy_timer(room)
cancel_destroy_timer(room);
room.visitors_destroy_timer = module:add_timer(15, function()
-- if the room is being destroyed, ignore
if room.destroying then
return;
end
local main_count, visitors_count = get_occupant_counts(room);
if visitors_count == 0 then
module:log('info', 'Will destroy:%s main_occupants:%s visitors:%s', room.jid, main_count, visitors_count);
room:destroy(nil, 'No visitors.');
end
end);
end
-- when occupant is leaving forward presences to jicofo for visitors
-- do not check occupant.role as it maybe already reset
-- if there are no main occupants or no visitors, destroy the room (give 15 seconds of grace period for reconnections)
module:hook('muc-occupant-left', function (event)
local room, occupant = event.room, event.occupant;
local occupant_domain = jid.host(occupant.bare_jid);
if prosody.hosts[occupant_domain] and not is_admin(occupant.bare_jid) then
local focus_occupant = get_focus_occupant(room);
if not focus_occupant then
if not room.destroying then
module:log('warn', 'No focus found for %s', room.jid);
end
return;
end
-- Let's forward unavailable presence to the special jicofo
room:route_stanza(st.presence({
to = focus_occupant.jid,
from = internal_room_jid_match_rewrite(occupant.nick),
type = 'unavailable' })
:tag('x', { xmlns = 'http://jabber.org/protocol/muc#user' })
:tag('item', {
affiliation = room:get_affiliation(occupant.bare_jid) or 'none';
role = 'none';
nick = event.nick;
jid = occupant.bare_jid }):up():up());
end
-- if the room is being destroyed, ignore
if room.destroying then
return;
end
-- if there are no main participants, the main room will be destroyed and
-- we can destroy and the visitor one as when jicofo leaves all visitors will reload
-- if there are no visitors give them 15 secs to reconnect, if not destroy it
local main_count, visitors_count = get_occupant_counts(room);
if visitors_count == 0 then
schedule_destroy_timer(room);
end
if main_count == 0 then
destroy_with_conference_ended(room);
end
end);
-- forward visitor presences to jicofo
-- detects raise hand in visitors presence, this is request for promotion
-- detects the requested transcription and its language to send updates for it
module:hook('muc-broadcast-presence', function (event)
local occupant = event.occupant;
---- we are interested only of visitors presence to send it to jicofo
if occupant.role ~= 'visitor' then
return;
end
local room = event.room;
local focus_occupant = get_focus_occupant(room);
if not focus_occupant then
return;
end
local actor, base_presence, nick, reason, x = event.actor, event.stanza, event.nick, event.reason, event.x;
local actor_nick;
if actor then
actor_nick = jid.resource(room:get_occupant_jid(actor));
end
-- create a presence to send it to jicofo, as jicofo is special :)
local full_x = st.clone(x.full or x);
room:build_item_list(occupant, full_x, false, nick, actor_nick, actor, reason);
local full_p = st.clone(base_presence):add_child(full_x);
full_p.attr.to = focus_occupant.jid;
room:route_to_occupant(focus_occupant, full_p);
local raiseHand = full_p:get_child_text(PARTICIPANT_PROP_RAISE_HAND);
-- a promotion detected let's send it to main prosody
if raiseHand then
local user_id;
local group_id;
local is_moderator;
local session = sessions[occupant.jid];
local identity = session and session.jitsi_meet_context_user;
if is_vpaas(room) and identity then
-- in case of moderator in vpaas meeting we want to do auto-promotion
local is_vpaas_moderator = identity.moderator;
if is_vpaas_moderator == 'true' or is_vpaas_moderator == true then
is_moderator = true;
end
else
-- The case with single moderator in the room, we want to report our id
-- so we can be auto promoted
if identity and identity.id then
user_id = session.jitsi_meet_context_user.id;
group_id = session.jitsi_meet_context_group;
if session.auth_token and auto_promoted_with_token then
if not session.jitsi_meet_tenant_mismatch or session.jitsi_web_query_prefix == '' then
-- non-vpaas and having a token is considered a moderator, and if it is not in '/' tenant
-- the tenant from url and token should match
is_moderator = true;
end
end
end
end
local iq_id = new_id();
sent_iq_cache:set(iq_id, socket.gettime());
local promotion_request = st.iq({
type = 'set',
to = 'visitors.'..main_domain,
from = local_domain,
id = iq_id })
:tag('visitors', { xmlns = 'jitsi:visitors',
room = jid.join(jid.node(room.jid), muc_domain_prefix..'.'..main_domain) })
:tag('promotion-request', {
xmlns = 'jitsi:visitors',
jid = occupant.jid,
time = raiseHand,
userId = user_id,
groupId = group_id,
forcePromote = is_moderator and 'true' or 'false';
}):up();
local nick_element = occupant:get_presence():get_child('nick', NICK_NS);
if nick_element then
promotion_request:add_child(nick_element);
end
module:send(promotion_request);
end
local requestTranscriptionValue = full_p:get_child_text(PARTICIPANT_PROP_REQUEST_TRANSCRIPTION);
local hasTranscriptionEnabled = room._transcription_languages and room._transcription_languages[occupant.jid];
-- detect transcription
if requestTranscriptionValue == 'true' then
local lang = full_p:get_child_text(PARTICIPANT_PROP_TRANSLATION_LANG);
add_transcription(room, occupant, lang);
elseif hasTranscriptionEnabled then
remove_transcription(room, occupant, nil);
end
return;
end);
-- listens for responses to the iq sent for requesting promotion and forward it to the visitor
local function stanza_handler(event)
local origin, stanza = event.origin, event.stanza;
if stanza.name ~= 'iq' then
return;
end
if stanza.attr.type == 'result' and sent_iq_cache:get(stanza.attr.id) then
sent_iq_cache:set(stanza.attr.id, nil);
return true;
end
if stanza.attr.type ~= 'set' then
return;
end
local visitors_iq = event.stanza:get_child('visitors', 'jitsi:visitors');
if not visitors_iq then
return;
end
if stanza.attr.from ~= 'visitors.'..main_domain then
module:log('warn', 'not from visitors component, ignore! %s', stanza);
return true;
end
local room_jid = visitors_iq.attr.room;
local room = get_room_from_jid(room_jid_match_rewrite(room_jid));
if not room then
return;
end
local request_promotion = visitors_iq:get_child('promotion-response');
if not request_promotion then
return;
end
-- respond with successful receiving the iq
respond_iq_result(origin, stanza);
local req_jid = request_promotion.attr.jid;
-- now let's find the occupant and forward the response
local occupant = room:get_occupant_by_real_jid(req_jid);
if occupant then
stanza.attr.to = occupant.jid;
stanza.attr.from = room.jid;
room:route_to_occupant(occupant, stanza);
return true;
end
end
--process a host module directly if loaded or hooks to wait for its load
function process_host_module(name, callback)
local function process_host(host)
if host == name then
callback(module:context(host), host);
end
end
if prosody.hosts[name] == nil then
module:log('debug', 'No host/component found, will wait for it: %s', name)
-- when a host or component is added
prosody.events.add_handler('host-activated', process_host);
else
process_host(name);
end
end
-- if the message received ends with the main domain, these are system messages
-- for visitors, let's correct the room name there
local function message_handler(event)
local origin, stanza = event.origin, event.stanza;
if ends_with(stanza.attr.from, main_domain) then
stanza.attr.from = stanza.attr.from:sub(1, -(main_domain:len() + 1))..local_domain;
end
end
process_host_module(local_domain, function(host_module, host)
host_module:hook('iq/host', stanza_handler, 10);
host_module:hook('message/full', message_handler);
end);
-- add to message stanza display name for the visitor
-- remove existing nick to avoid forgery
local function process_display_name(stanza, occupant)
-- TODO drop nick extension used in messages in favor of new display-name extension
-- we want mobile client to update before dropping support for nick from messages
stanza:remove_children('nick', NICK_NS);
stanza:remove_children('display-name', DISPLAY_NAME_NS);
local nick_element = occupant:get_presence():get_child('nick', NICK_NS);
if nick_element then
stanza:add_child(nick_element);
stanza:tag('display-name', {
xmlns = DISPLAY_NAME_NS,
source = 'visitor'
}):text(nick_element:get_text()):up();
else
stanza:tag('nick', { xmlns = NICK_NS })
:text('anonymous'):up();
stanza:tag('display-name', {
xmlns = DISPLAY_NAME_NS,
source = 'visitor'
}):text('anonymous'):up();
end
end
-- only live chat is supported for visitors
module:hook('muc-occupant-groupchat', function(event)
local occupant, room, stanza = event.occupant, event.room, event.stanza;
local from = stanza.attr.from;
local occupant_host;
-- if there is no occupant this is a message from main, probably coming from other vnode
if occupant then
occupant_host = jid.host(occupant.bare_jid);
-- we manage nick only for visitors
if occupant_host ~= main_domain then
process_display_name(stanza, occupant);
end
stanza.attr.from = occupant.nick;
else
stanza.attr.from = jid.join(jid.node(from), module.host);
end
-- let's send it to main chat and rest of visitors here
for _, o in room:each_occupant() do
-- filter remote occupants
if jid.host(o.bare_jid) == local_domain then
room:route_to_occupant(o, stanza)
end
end
-- send to main participants only messages from local occupants (skip from remote vnodes)
if occupant and occupant_host == local_domain then
local main_message = st.clone(stanza);
main_message.attr.to = jid.join(jid.node(room.jid), muc_domain_prefix..'.'..main_domain);
-- make sure we fix the from to be the real jid
main_message.attr.from = room_jid_match_rewrite(stanza.attr.from);
module:send(main_message);
end
stanza.attr.from = from; -- something prosody does internally
return true;
end, 55); -- prosody check for visitor's chat is prio 50, we want to override it
-- Private messaging support for visitors
module:hook('muc-private-message', function(event)
local room, stanza = event.room, event.stanza;
local from = stanza.attr.from;
local to = stanza.attr.to;
local recipient_occupant = room:get_occupant_by_nick(to);
local recipient_domain = recipient_occupant and jid.host(recipient_occupant.bare_jid) or nil;
local sender_occupant = room:get_occupant_by_nick(from);
local sender_domain = sender_occupant and jid.host(sender_occupant.bare_jid) or nil;
if sender_domain == nil or recipient_domain == nil then
return false;
end
-- If both sender and recipient are local (on this vnode)
if sender_domain == local_domain and recipient_domain == local_domain then
event.origin.send(st.error_reply(event.stanza, 'auth', 'forbidden',
'Private messaging between visitors is disabled on visitor nodes'));
return false; -- Prevent sending the original message and stop further processing
end
-- If sender is local (visitor node) and recipient is from main prosody, forward to main prosody
if sender_domain == local_domain and recipient_domain == main_domain then
local original_to = stanza.attr.to;
local original_from = stanza.attr.from;
process_display_name(stanza, sender_occupant);
-- Forward to main prosody, preserving the resource and original from
stanza.attr.to = jid.join(jid.node(room.jid), muc_domain_prefix..'.'..main_domain, jid.resource(to));
module:send(stanza);
return false; -- Prevent sending the original message and stop further processing
end
-- For main->visitor messages, let the default MUC handler process it
-- We don't need to do anything special
if sender_domain == main_domain and recipient_domain == local_domain then
return; -- Return nothing, let other handlers continue. The default MUC handler will process it.
end
return false; -- Prevent sending the original message and stop further processing
end, 100); -- Lower priority to run after other handlers
-- we calculate the stats on the configured interval (60 seconds by default)
module:hook_global('stats-update', function ()
local participants_count, rooms_count, visitors_count = 0, 0, 0;
-- iterate over all rooms
for room in prosody.hosts[module.host].modules.muc.each_room() do
rooms_count = rooms_count + 1;
for _, o in room:each_occupant() do
if not is_admin(o.bare_jid) then
local _, host = jid.split(o.bare_jid);
if prosody.hosts[host] then -- local hosts are visitors (including jigasi)
visitors_count = visitors_count + 1;
else
participants_count = participants_count + 1;
end
end
end
end
measure_rooms(rooms_count);
measure_visitors(visitors_count);
measure_participants(participants_count);
end);
-- we skip it till the main participants are added from the main prosody
module:hook('jicofo-unlock-room', function(e)
-- we do not block events we fired
if e.fmuc_fired then
return;
end
return true;
end);
-- handles incoming iq visitors stanzas
-- connect - sent after sending all main participant's presences
-- disconnect - sent when main room is destroyed or when we receive a 'disconnect-vnode' iq from jicofo
-- update - sent on:
-- * room secret is changed
-- * lobby enabled or disabled
-- * initially before connect to report currently joined moderators
-- * moderator participant joins main room
-- * a participant has been granted moderator rights
local function iq_from_main_handler(event)
local origin, stanza = event.origin, event.stanza;
if stanza.name ~= 'iq' then
return;
end
if stanza.attr.type == 'result' and sent_iq_cache:get(stanza.attr.id) then
sent_iq_cache:set(stanza.attr.id, nil);
return true;
end
if stanza.attr.type ~= 'set' then
return;
end
local visitors_iq = event.stanza:get_child('visitors', 'jitsi:visitors');
if not visitors_iq then
return;
end
if stanza.attr.from ~= main_domain then
module:log('warn', 'not from main prosody, ignore! %s', stanza);
return true;
end
local room_jid = visitors_iq.attr.room;
local room = get_room_from_jid(room_jid_match_rewrite(room_jid));
if not room then
module:log('warn', 'No room found %s in iq_from_main_handler for:%s', room_jid, visitors_iq);
return;
end
local node = visitors_iq:get_child('connect');
local fire_jicofo_unlock = true;
local process_disconnect = false;
if not node then
node = visitors_iq:get_child('update');
fire_jicofo_unlock = false;
end
if not node then
node = visitors_iq:get_child('disconnect');
process_disconnect = true;
end
if not node then
return;
end
-- respond with successful receiving the iq
respond_iq_result(origin, stanza);
if process_disconnect then
return destroy_with_conference_ended(room);
end
-- if there is password supplied use it
-- if this is update it will either set or remove the password
room:set_password(node.attr.password);
room._data.meetingId = node.attr.meetingId;
local createdTimestamp = node.attr.createdTimestamp;
room.created_timestamp = createdTimestamp and tonumber(createdTimestamp) or nil;
if node.attr.lobby == 'true' then
room._main_room_lobby_enabled = true;
elseif node.attr.lobby == 'false' then
room._main_room_lobby_enabled = false;
end
-- read the moderators list
room.moderators_list = room.moderators_list or set.new();
local moderators = node:get_child('moderators');
if moderators then
for _, child in ipairs(moderators.tags) do
if child.name == 'item' then
room.moderators_list:add(child.attr.epId);
end
end
-- let's check current occupants roles and promote them if needed
-- we change only main participants which are not moderators, but participant
for _, o in room:each_occupant() do
if not is_admin(o.bare_jid)
and o.role == 'participant'
and room.moderators_list:contains(jid.resource(o.nick)) then
room:set_affiliation(true, o.bare_jid, 'owner');
end
end
end
local files = node:get_child('files');
if files then
local received_files = {};
for _, child in ipairs(files.tags) do
if child.name == 'file' then
received_files[child.attr.id] = json.decode(child:get_text());
end
end
-- fire events so file sharing component will add/remove files and will notify clients
local removed, added = table_compare(room.jitsi_shared_files or {}, received_files)
for _, id in ipairs(removed) do
module:context(local_domain):fire_event('jitsi-filesharing-remove', {
room = room; id = id;
});
end
for _, id in ipairs(added) do
module:context(local_domain):fire_event('jitsi-filesharing-add', {
room = room; file = received_files[id];
});
end
end
if fire_jicofo_unlock then
-- everything is connected allow participants to join
module:fire_event('jicofo-unlock-room', { room = room; fmuc_fired = true; });
end
return true;
end
module:hook('iq/host', iq_from_main_handler, 10);
-- Filters presences (if detected) that are with destination the main prosody
function filter_stanza(stanza, session)
if (stanza.name == 'presence' or stanza.name == 'message') and session.type ~= 'c2s' then
-- we clone it so we do not affect broadcast using same stanza, sending it to clients
local f_st = st.clone(stanza);
f_st.skipMapping = true;
return f_st;
elseif stanza.name == 'presence' and session.type == 'c2s' and jid.node(stanza.attr.to) == 'focus' then
local x = stanza:get_child('x', 'http://jabber.org/protocol/muc#user');
if presence_check_status(x, '110') then
return stanza; -- no filter
end
-- we want to filter presences to jicofo for the main participants, skipping visitors
-- no point of having them, but if it is the one of the first to be sent
-- when first visitor is joining can produce the 'No hosts[from_host]' error as we
-- rewrite the from, but we need to not do it to be able to filter it later for the s2s
if jid.host(room_jid_match_rewrite(stanza.attr.from)) ~= local_muc_domain then
return nil; -- returning nil filters the stanza
end
end
return stanza; -- no filter
end
function filter_session(session)
-- domain mapper is filtering on default priority 0, and we need it before that
filters.add_filter(session, 'stanzas/out', filter_stanza, 2);
end
filters.add_filter_hook(filter_session);
function route_s2s_stanza(event)
local from_host, to_host, stanza = event.from_host, event.to_host, event.stanza;
if to_host ~= main_domain then
return; -- continue with hook listeners
end
if stanza.name == 'message' then
if jid.resource(stanza.attr.to) then
-- there is no point of delivering messages to main participants individually
return true; -- drop it
end
return;
end
if stanza.name == 'presence' then
-- we want to leave only unavailable presences to go to main node
-- all other presences from jicofo or the main participants there is no point to go to the main node
-- they are anyway not handled
if stanza.attr.type ~= 'unavailable' then
return true; -- drop it
end
return;
end
end
-- routing to sessions in mod_s2s is -1 and -10, we want to hook before that to make sure to is correct
-- or if we want to filter that stanza
module:hook("route/remote", route_s2s_stanza, 10);