"Fossies" - the Fresh Open Source Software Archive

Member "jitsi-meet-7309/resources/prosody-plugins/mod_av_moderation_component.lua" (31 May 2023, 13330 Bytes) of package /linux/misc/jitsi-meet-7309.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Lua source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "mod_av_moderation_component.lua": jitsi-meet_8319_vs_jitsi-meet_8615.

    1 local get_room_by_name_and_subdomain = module:require 'util'.get_room_by_name_and_subdomain;
    2 local is_healthcheck_room = module:require 'util'.is_healthcheck_room;
    3 local internal_room_jid_match_rewrite = module:require "util".internal_room_jid_match_rewrite;
    4 local room_jid_match_rewrite = module:require "util".room_jid_match_rewrite;
    5 local array = require "util.array";
    6 local json = require 'util.json';
    7 local st = require 'util.stanza';
    8 
    9 local muc_component_host = module:get_option_string('muc_component');
   10 if muc_component_host == nil then
   11     module:log('error', 'No muc_component specified. No muc to operate on!');
   12     return;
   13 end
   14 
   15 module:log('info', 'Starting av_moderation for %s', muc_component_host);
   16 
   17 -- Returns the index of the given element in the table
   18 -- @param table in which to look
   19 -- @param elem the element for which to find the index
   20 function get_index_in_table(table, elem)
   21     for index, value in pairs(table) do
   22         if value == elem then
   23             return index
   24         end
   25     end
   26 end
   27 
   28 -- Sends a json-message to the destination jid
   29 -- @param to_jid the destination jid
   30 -- @param json_message the message content to send
   31 function send_json_message(to_jid, json_message)
   32     local stanza = st.message({ from = module.host; to = to_jid; })
   33          :tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' }):text(json_message):up();
   34     module:send(stanza);
   35 end
   36 
   37 -- Notifies that av moderation has been enabled or disabled
   38 -- @param jid the jid to notify, if missing will notify all occupants
   39 -- @param enable whether it is enabled or disabled
   40 -- @param room the room
   41 -- @param actorJid the jid that is performing the enable/disable operation (the muc jid)
   42 -- @param mediaType the media type for the moderation
   43 function notify_occupants_enable(jid, enable, room, actorJid, mediaType)
   44     local body_json = {};
   45     body_json.type = 'av_moderation';
   46     body_json.enabled = enable;
   47     body_json.room = internal_room_jid_match_rewrite(room.jid);
   48     body_json.actor = actorJid;
   49     body_json.mediaType = mediaType;
   50     local body_json_str = json.encode(body_json);
   51 
   52     if jid then
   53         send_json_message(jid, body_json_str)
   54     else
   55         for _, occupant in room:each_occupant() do
   56             send_json_message(occupant.jid, body_json_str)
   57         end
   58     end
   59 end
   60 
   61 -- Notifies about a change to the whitelist. Notifies all moderators and admin and the jid itself
   62 -- @param jid the jid to notify about the change
   63 -- @param moderators whether to notify all moderators in the room
   64 -- @param room the room where to send it
   65 -- @param mediaType used only when a participant is approved (not sent to moderators)
   66 -- @param removed whether the jid is removed or added
   67 function notify_whitelist_change(jid, moderators, room, mediaType, removed)
   68     local body_json = {};
   69     body_json.type = 'av_moderation';
   70     body_json.room = internal_room_jid_match_rewrite(room.jid);
   71     body_json.whitelists = room.av_moderation;
   72     if removed then
   73         body_json.removed = true;
   74     end
   75     body_json.mediaType = mediaType;
   76     local moderators_body_json_str = json.encode(body_json);
   77     body_json.whitelists = nil;
   78     if not removed then
   79         body_json.approved = true; -- we want to send to participants only that they were approved to unmute
   80     end
   81     local participant_body_json_str = json.encode(body_json);
   82 
   83     for _, occupant in room:each_occupant() do
   84         if moderators and occupant.role == 'moderator' then
   85             send_json_message(occupant.jid, moderators_body_json_str);
   86         elseif occupant.jid == jid then
   87             -- if the occupant is not moderator we send him that it is approved
   88             -- if it is moderator we update him with the list, this is moderator joining or grant moderation was executed
   89             if occupant.role == 'moderator' then
   90                 send_json_message(occupant.jid, moderators_body_json_str);
   91             else
   92                 send_json_message(occupant.jid, participant_body_json_str);
   93             end
   94         end
   95     end
   96 end
   97 
   98 -- Notifies jid that is approved. This is a moderator to jid message to ask to unmute,
   99 -- @param jid the jid to notify about the change
  100 -- @param from the jid that triggered this
  101 -- @param room the room where to send it
  102 -- @param mediaType the mediaType it was approved for
  103 function notify_jid_approved(jid, from, room, mediaType)
  104     local body_json = {};
  105     body_json.type = 'av_moderation';
  106     body_json.room = internal_room_jid_match_rewrite(room.jid);
  107     body_json.approved = true; -- we want to send to participants only that they were approved to unmute
  108     body_json.mediaType = mediaType;
  109     body_json.from = from;
  110 
  111     send_json_message(jid, json.encode(body_json));
  112 end
  113 
  114 -- receives messages from clients to the component sending A/V moderation enable/disable commands or adding
  115 -- jids to the whitelist
  116 function on_message(event)
  117     local session = event.origin;
  118 
  119     -- Check the type of the incoming stanza to avoid loops:
  120     if event.stanza.attr.type == 'error' then
  121         return; -- We do not want to reply to these, so leave.
  122     end
  123 
  124     if not session or not session.jitsi_web_query_room then
  125         return false;
  126     end
  127 
  128     local moderation_command = event.stanza:get_child('av_moderation');
  129 
  130     if moderation_command then
  131         -- get room name with tenant and find room
  132         local room = get_room_by_name_and_subdomain(session.jitsi_web_query_room, session.jitsi_web_query_prefix);
  133 
  134         if not room then
  135             module:log('warn', 'No room found found for %s/%s',
  136                     session.jitsi_web_query_prefix, session.jitsi_web_query_room);
  137             return false;
  138         end
  139 
  140         -- check that the participant requesting is a moderator and is an occupant in the room
  141         local from = event.stanza.attr.from;
  142         local occupant = room:get_occupant_by_real_jid(from);
  143         if not occupant then
  144             module:log('warn', 'No occupant %s found for %s', from, room.jid);
  145             return false;
  146         end
  147         if occupant.role ~= 'moderator' then
  148             module:log('warn', 'Occupant %s is not moderator and not allowed this operation for %s', from, room.jid);
  149             return false;
  150         end
  151 
  152         local mediaType = moderation_command.attr.mediaType;
  153         if mediaType then
  154             if mediaType ~= 'audio' and mediaType ~= 'video' then
  155                 module:log('warn', 'Wrong mediaType %s for %s', mediaType, room.jid);
  156                 return false;
  157             end
  158         else
  159             module:log('warn', 'Missing mediaType for %s', room.jid);
  160             return false;
  161         end
  162 
  163         if moderation_command.attr.enable ~= nil then
  164             local enabled;
  165             if moderation_command.attr.enable == 'true' then
  166                 enabled = true;
  167                 if room.av_moderation and room.av_moderation[mediaType] then
  168                     module:log('warn', 'Concurrent moderator enable/disable request or something is out of sync');
  169                     return true;
  170                 else
  171                     if not room.av_moderation then
  172                         room.av_moderation = {};
  173                         room.av_moderation_actors = {};
  174                     end
  175                     room.av_moderation[mediaType] = array{};
  176                     room.av_moderation_actors[mediaType] = occupant.nick;
  177                 end
  178             else
  179                 enabled = false;
  180                 if not room.av_moderation then
  181                     module:log('warn', 'Concurrent moderator enable/disable request or something is out of sync');
  182                     return true;
  183                 else
  184                     room.av_moderation[mediaType] = nil;
  185                     room.av_moderation_actors[mediaType] = nil;
  186 
  187                     -- clears room.av_moderation if empty
  188                     local is_empty = true;
  189                     for key,_ in pairs(room.av_moderation) do
  190                         if room.av_moderation[key] then
  191                             is_empty = false;
  192                         end
  193                     end
  194                     if is_empty then
  195                         room.av_moderation = nil;
  196                     end
  197                 end
  198             end
  199 
  200             -- send message to all occupants
  201             notify_occupants_enable(nil, enabled, room, occupant.nick, mediaType);
  202             return true;
  203         elseif moderation_command.attr.jidToWhitelist then
  204             local occupant_jid = moderation_command.attr.jidToWhitelist;
  205             -- check if jid is in the room, if so add it to whitelist
  206             -- inform all moderators and admins and the jid
  207             local occupant_to_add = room:get_occupant_by_nick(room_jid_match_rewrite(occupant_jid));
  208             if not occupant_to_add then
  209                 module:log('warn', 'No occupant %s found for %s', occupant_jid, room.jid);
  210                 return false;
  211             end
  212 
  213             if room.av_moderation then
  214                 local whitelist = room.av_moderation[mediaType];
  215                 if not whitelist then
  216                     whitelist = array{};
  217                     room.av_moderation[mediaType] = whitelist;
  218                 end
  219                 whitelist:push(occupant_jid);
  220 
  221                 notify_whitelist_change(occupant_to_add.jid, true, room, mediaType, false);
  222 
  223                 return true;
  224             else
  225                 -- this is a moderator asking the jid to unmute without enabling av moderation
  226                 -- let's just send the event
  227                 notify_jid_approved(occupant_to_add.jid, occupant.nick, room, mediaType);
  228             end
  229         elseif moderation_command.attr.jidToBlacklist then
  230             local occupant_jid = moderation_command.attr.jidToBlacklist;
  231             -- check if jid is in the room, if so remove it from the whitelist
  232             -- inform all moderators and admins
  233             local occupant_to_remove = room:get_occupant_by_nick(room_jid_match_rewrite(occupant_jid));
  234             if not occupant_to_remove then
  235                 module:log('warn', 'No occupant %s found for %s', occupant_jid, room.jid);
  236                 return false;
  237             end
  238 
  239             if room.av_moderation then
  240                 local whitelist = room.av_moderation[mediaType];
  241                 if whitelist then
  242                     local index = get_index_in_table(whitelist, occupant_jid)
  243                     if(index) then
  244                         whitelist:pop(index);
  245                         notify_whitelist_change(occupant_to_remove.jid, true, room, mediaType, true);
  246                     end
  247                 end
  248 
  249                 return true;
  250             end
  251         end
  252     end
  253 
  254     -- return error
  255     return false
  256 end
  257 
  258 -- handles new occupants to inform them about the state enabled/disabled, new moderators also get and the whitelist
  259 function occupant_joined(event)
  260     local room, occupant = event.room, event.occupant;
  261 
  262     if is_healthcheck_room(room.jid) then
  263         return;
  264     end
  265 
  266     if room.av_moderation then
  267         for _,mediaType in pairs({'audio', 'video'}) do
  268             if room.av_moderation[mediaType] then
  269                 notify_occupants_enable(
  270                     occupant.jid, true, room, room.av_moderation_actors[mediaType], mediaType);
  271             end
  272         end
  273 
  274         -- NOTE for some reason event.occupant.role is not reflecting the actual occupant role (when changed
  275         -- from allowners module) but iterating over room occupants returns the correct role
  276         for _, room_occupant in room:each_occupant() do
  277             -- if moderator send the whitelist
  278             if room_occupant.nick == occupant.nick and room_occupant.role == 'moderator'  then
  279                 notify_whitelist_change(room_occupant.jid, false, room);
  280             end
  281         end
  282     end
  283 end
  284 
  285 -- when a occupant was granted moderator we need to update him with the whitelist
  286 function occupant_affiliation_changed(event)
  287     -- the actor can be nil if is coming from allowners or similar module we want to skip it here
  288     -- as we will handle it in occupant_joined
  289     if event.actor and event.affiliation == 'owner' and event.room.av_moderation then
  290         local room = event.room;
  291         -- event.jid is the bare jid of participant
  292         for _, occupant in room:each_occupant() do
  293             if occupant.bare_jid == event.jid then
  294                 notify_whitelist_change(occupant.jid, false, room);
  295             end
  296         end
  297     end
  298 end
  299 
  300 -- we will receive messages from the clients
  301 module:hook('message/host', on_message);
  302 
  303 -- executed on every host added internally in prosody, including components
  304 function process_host(host)
  305     if host == muc_component_host then -- the conference muc component
  306         module:log('info','Hook to muc events on %s', host);
  307 
  308         local muc_module = module:context(host);
  309         muc_module:hook('muc-occupant-joined', occupant_joined, -2); -- make sure it runs after allowners or similar
  310         muc_module:hook('muc-set-affiliation', occupant_affiliation_changed, -1);
  311     end
  312 end
  313 
  314 if prosody.hosts[muc_component_host] == nil then
  315     module:log('info', 'No muc component found, will listen for it: %s', muc_component_host);
  316 
  317     -- when a host or component is added
  318     prosody.events.add_handler('host-activated', process_host);
  319 else
  320     process_host(muc_component_host);
  321 end