From b0ffc2cd6946cbe5a6105494cfa8e7b5779a2942 Mon Sep 17 00:00:00 2001 From: damencho Date: Thu, 5 Dec 2024 10:57:52 -0600 Subject: [PATCH] feat(visitors): Propagate moderator role to visitor nodes. Features that depend on presence and moderator role will start working for visitors (like follow-me). --- resources/prosody-plugins/mod_fmuc.lua | 58 ++++++++++++++++++---- resources/prosody-plugins/mod_visitors.lua | 42 ++++++++++++++-- 2 files changed, 87 insertions(+), 13 deletions(-) diff --git a/resources/prosody-plugins/mod_fmuc.lua b/resources/prosody-plugins/mod_fmuc.lua index e83ed7ff71..81acfe9d93 100644 --- a/resources/prosody-plugins/mod_fmuc.lua +++ b/resources/prosody-plugins/mod_fmuc.lua @@ -14,7 +14,8 @@ 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 array = require 'util.array'; +local set = require 'util.set'; local util = module:require 'util'; local ends_with = util.ends_with; @@ -69,13 +70,11 @@ end local function send_transcriptions_update(room) -- let's notify main prosody - local lang_array = array{}; + local lang_array = array(); local count = 0; for k, v in pairs(room._transcription_languages) do - if not lang_array[v] then - lang_array:push(v); - end + lang_array:push(v); count = count + 1; end @@ -90,7 +89,7 @@ local function send_transcriptions_update(room) room = jid.join(jid.node(room.jid), muc_domain_prefix..'.'..main_domain) }) :tag('transcription-languages', { xmlns = 'jitsi:visitors', - langs = lang_array:sort():concat(','), + langs = lang_array:unique():sort():concat(','), count = tostring(count) }):up()); end @@ -127,8 +126,14 @@ end 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 prosody.hosts[host] and not is_admin(occupant.bare_jid) then + 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' })); @@ -136,6 +141,9 @@ module:hook('muc-occupant-pre-join', function (event) else occupant.role = 'visitor'; end + elseif room.moderators_list:contains(resource) then + -- remote participants, host is the main prosody + occupant.role = 'moderator'; end end, 3); @@ -399,7 +407,7 @@ local function stanza_handler(event) local room = get_room_from_jid(room_jid_match_rewrite(room_jid)); if not room then - module:log('warn', 'No room found %s', room_jid); + module:log('warn', 'No room found %s in stanza_handler', room_jid); return; end @@ -546,7 +554,15 @@ module:hook('jicofo-unlock-room', function(e) return true; end); --- handles incoming iq connect stanzas +-- 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; @@ -577,7 +593,7 @@ local function iq_from_main_handler(event) local room = get_room_from_jid(room_jid_match_rewrite(room_jid)); if not room then - module:log('warn', 'No room found %s', room_jid); + module:log('warn', 'No room found %s in iq_from_main_handler for:%s', room_jid, visitors_iq); return; end @@ -625,6 +641,28 @@ local function iq_from_main_handler(event) 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 + if fire_jicofo_unlock then -- everything is connected allow participants to join module:fire_event('jicofo-unlock-room', { room = room; fmuc_fired = true; }); diff --git a/resources/prosody-plugins/mod_visitors.lua b/resources/prosody-plugins/mod_visitors.lua index d97b6cab5f..bcbd54f35a 100644 --- a/resources/prosody-plugins/mod_visitors.lua +++ b/resources/prosody-plugins/mod_visitors.lua @@ -58,7 +58,7 @@ local function send_visitors_iq(conference_service, room, type) -- send iq informing the vnode that the connect is done and it will allow visitors to join local iq_id = new_id(); sent_iq_cache:set(iq_id, socket.gettime()); - local connect_done = st.iq({ + local visitors_iq = st.iq({ type = 'set', to = conference_service, from = module.host, @@ -71,9 +71,23 @@ local function send_visitors_iq(conference_service, room, type) meetingId = room._data.meetingId, moderatorId = room._data.moderator_id, -- can be used from external modules to set single moderator for meetings createdTimestamp = room.created_timestamp and tostring(room.created_timestamp) or nil - }):up(); + }); - module:send(connect_done); + if type == 'update' then + visitors_iq:tag('moderators', { xmlns = 'jitsi:visitors' }); + + for _, o in room:each_occupant() do + if not is_admin(o.bare_jid) and o.role == 'moderator' then + visitors_iq:tag('item', { epId = jid.resource(o.nick) }):up(); + end + end + + visitors_iq:up(); + end + + visitors_iq:up(); + + module:send(visitors_iq); end -- an event received from visitors component, which receives iqs from jicofo @@ -97,6 +111,9 @@ local function connect_vnode(event) local sent_main_participants = 0; + -- send update initially so we can report the moderators that will join + send_visitors_iq(conference_service, room, 'update'); + for _, o in room:each_occupant() do if not is_admin(o.bare_jid) then local fmuc_pr = st.clone(o:get_presence()); @@ -258,6 +275,10 @@ process_host_module(main_muc_component_config, function(host_module, host) local user, _, res = jid.split(occupant.nick); -- a main participant we need to update all active visitor nodes for k in pairs(vnodes) do + if occupant.role == 'moderator' then + -- first send that the participant is a moderator + send_visitors_iq(k, room, 'update'); + end local fmuc_pr = st.clone(stanza); fmuc_pr.attr.to = jid.join(user, k, res); fmuc_pr.attr.from = occupant.jid; @@ -332,6 +353,21 @@ process_host_module(main_muc_component_config, function(host_module, host) end end end, -100); -- we want to run last in order to check is the status code 104 + + host_module:hook('muc-set-affiliation', function (event) + if event.actor and not is_admin(event.actor) and event.affiliation == 'owner' then + local room = event.room; + + if not visitors_nodes[room.jid] then + return; + end + -- we need to update all vnodes + local vnodes = visitors_nodes[room.jid].nodes; + for conference_service in pairs(vnodes) do + send_visitors_iq(conference_service, room, 'update'); + end + end + end, -2); end); module:hook('jitsi-lobby-enabled', function(event)