mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 03:12:29 +00:00
We were using prosody,util.json and cjson at the same time, but the latter is more performant. Adds some error handling which were missing with the prosody util json one.
332 lines
13 KiB
Lua
332 lines
13 KiB
Lua
local util = module:require 'util';
|
|
local get_room_by_name_and_subdomain = util.get_room_by_name_and_subdomain;
|
|
local is_healthcheck_room = util.is_healthcheck_room;
|
|
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
|
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
|
local process_host_module = util.process_host_module;
|
|
local array = require "util.array";
|
|
local json = require 'cjson.safe';
|
|
local st = require 'util.stanza';
|
|
|
|
local muc_component_host = module:get_option_string('muc_component');
|
|
if muc_component_host == nil then
|
|
module:log('error', 'No muc_component specified. No muc to operate on!');
|
|
return;
|
|
end
|
|
|
|
module:log('info', 'Starting av_moderation for %s', muc_component_host);
|
|
|
|
-- Returns the index of the given element in the table
|
|
-- @param table in which to look
|
|
-- @param elem the element for which to find the index
|
|
function get_index_in_table(table, elem)
|
|
for index, value in pairs(table) do
|
|
if value == elem then
|
|
return index
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Sends a json-message to the destination jid
|
|
-- @param to_jid the destination jid
|
|
-- @param json_message the message content to send
|
|
function send_json_message(to_jid, json_message)
|
|
local stanza = st.message({ from = module.host; to = to_jid; })
|
|
:tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' }):text(json_message):up();
|
|
module:send(stanza);
|
|
end
|
|
|
|
-- Notifies that av moderation has been enabled or disabled
|
|
-- @param jid the jid to notify, if missing will notify all occupants
|
|
-- @param enable whether it is enabled or disabled
|
|
-- @param room the room
|
|
-- @param actorJid the jid that is performing the enable/disable operation (the muc jid)
|
|
-- @param mediaType the media type for the moderation
|
|
function notify_occupants_enable(jid, enable, room, actorJid, mediaType)
|
|
local body_json = {};
|
|
body_json.type = 'av_moderation';
|
|
body_json.enabled = enable;
|
|
body_json.room = internal_room_jid_match_rewrite(room.jid);
|
|
body_json.actor = actorJid;
|
|
body_json.mediaType = mediaType;
|
|
local body_json_str, error = json.encode(body_json);
|
|
|
|
if not body_json_str then
|
|
module:log('error', 'error encoding json room:%s error:%s', room.jid, error);
|
|
return;
|
|
end
|
|
|
|
if jid then
|
|
send_json_message(jid, body_json_str)
|
|
else
|
|
for _, occupant in room:each_occupant() do
|
|
send_json_message(occupant.jid, body_json_str)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Notifies about a change to the whitelist. Notifies all moderators and admin and the jid itself
|
|
-- @param jid the jid to notify about the change
|
|
-- @param moderators whether to notify all moderators in the room
|
|
-- @param room the room where to send it
|
|
-- @param mediaType used only when a participant is approved (not sent to moderators)
|
|
-- @param removed whether the jid is removed or added
|
|
function notify_whitelist_change(jid, moderators, room, mediaType, removed)
|
|
local body_json = {};
|
|
body_json.type = 'av_moderation';
|
|
body_json.room = internal_room_jid_match_rewrite(room.jid);
|
|
body_json.whitelists = room.av_moderation;
|
|
if removed then
|
|
body_json.removed = true;
|
|
end
|
|
body_json.mediaType = mediaType;
|
|
local moderators_body_json_str, error = json.encode(body_json);
|
|
|
|
if not moderators_body_json_str then
|
|
module:log('error', 'error encoding moderator json room:%s error:%s', room.jid, error);
|
|
return;
|
|
end
|
|
|
|
body_json.whitelists = nil;
|
|
if not removed then
|
|
body_json.approved = true; -- we want to send to participants only that they were approved to unmute
|
|
end
|
|
local participant_body_json_str, error = json.encode(body_json);
|
|
|
|
if not participant_body_json_str then
|
|
module:log('error', 'error encoding participant json room:%s error:%s', room.jid, error);
|
|
return;
|
|
end
|
|
|
|
for _, occupant in room:each_occupant() do
|
|
if moderators and occupant.role == 'moderator' then
|
|
send_json_message(occupant.jid, moderators_body_json_str);
|
|
elseif occupant.jid == jid then
|
|
-- if the occupant is not moderator we send him that it is approved
|
|
-- if it is moderator we update him with the list, this is moderator joining or grant moderation was executed
|
|
if occupant.role == 'moderator' then
|
|
send_json_message(occupant.jid, moderators_body_json_str);
|
|
else
|
|
send_json_message(occupant.jid, participant_body_json_str);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Notifies jid that is approved. This is a moderator to jid message to ask to unmute,
|
|
-- @param jid the jid to notify about the change
|
|
-- @param from the jid that triggered this
|
|
-- @param room the room where to send it
|
|
-- @param mediaType the mediaType it was approved for
|
|
function notify_jid_approved(jid, from, room, mediaType)
|
|
local body_json = {};
|
|
body_json.type = 'av_moderation';
|
|
body_json.room = internal_room_jid_match_rewrite(room.jid);
|
|
body_json.approved = true; -- we want to send to participants only that they were approved to unmute
|
|
body_json.mediaType = mediaType;
|
|
body_json.from = from;
|
|
|
|
local json_message, error = json.encode(body_json);
|
|
if not json_message then
|
|
module:log('error', 'skip sending json message to:%s error:%s', jid, error);
|
|
return;
|
|
end
|
|
|
|
send_json_message(jid, json_message);
|
|
end
|
|
|
|
-- receives messages from clients to the component sending A/V moderation enable/disable commands or adding
|
|
-- jids to the whitelist
|
|
function on_message(event)
|
|
local session = event.origin;
|
|
|
|
-- Check the type of the incoming stanza to avoid loops:
|
|
if event.stanza.attr.type == 'error' then
|
|
return; -- We do not want to reply to these, so leave.
|
|
end
|
|
|
|
if not session or not session.jitsi_web_query_room then
|
|
return false;
|
|
end
|
|
|
|
local moderation_command = event.stanza:get_child('av_moderation');
|
|
|
|
if moderation_command then
|
|
-- get room name with tenant and find room
|
|
local room = get_room_by_name_and_subdomain(session.jitsi_web_query_room, session.jitsi_web_query_prefix);
|
|
|
|
if not room then
|
|
module:log('warn', 'No room found found for %s/%s',
|
|
session.jitsi_web_query_prefix, session.jitsi_web_query_room);
|
|
return false;
|
|
end
|
|
|
|
-- check that the participant requesting is a moderator and is an occupant in the room
|
|
local from = event.stanza.attr.from;
|
|
local occupant = room:get_occupant_by_real_jid(from);
|
|
if not occupant then
|
|
module:log('warn', 'No occupant %s found for %s', from, room.jid);
|
|
return false;
|
|
end
|
|
if occupant.role ~= 'moderator' then
|
|
module:log('warn', 'Occupant %s is not moderator and not allowed this operation for %s', from, room.jid);
|
|
return false;
|
|
end
|
|
|
|
local mediaType = moderation_command.attr.mediaType;
|
|
if mediaType then
|
|
if mediaType ~= 'audio' and mediaType ~= 'video' then
|
|
module:log('warn', 'Wrong mediaType %s for %s', mediaType, room.jid);
|
|
return false;
|
|
end
|
|
else
|
|
module:log('warn', 'Missing mediaType for %s', room.jid);
|
|
return false;
|
|
end
|
|
|
|
if moderation_command.attr.enable ~= nil then
|
|
local enabled;
|
|
if moderation_command.attr.enable == 'true' then
|
|
enabled = true;
|
|
if room.av_moderation and room.av_moderation[mediaType] then
|
|
module:log('warn', 'Concurrent moderator enable/disable request or something is out of sync');
|
|
return true;
|
|
else
|
|
if not room.av_moderation then
|
|
room.av_moderation = {};
|
|
room.av_moderation_actors = {};
|
|
end
|
|
room.av_moderation[mediaType] = array{};
|
|
room.av_moderation_actors[mediaType] = occupant.nick;
|
|
end
|
|
else
|
|
enabled = false;
|
|
if not room.av_moderation then
|
|
module:log('warn', 'Concurrent moderator enable/disable request or something is out of sync');
|
|
return true;
|
|
else
|
|
room.av_moderation[mediaType] = nil;
|
|
room.av_moderation_actors[mediaType] = nil;
|
|
|
|
-- clears room.av_moderation if empty
|
|
local is_empty = true;
|
|
for key,_ in pairs(room.av_moderation) do
|
|
if room.av_moderation[key] then
|
|
is_empty = false;
|
|
end
|
|
end
|
|
if is_empty then
|
|
room.av_moderation = nil;
|
|
end
|
|
end
|
|
end
|
|
|
|
-- send message to all occupants
|
|
notify_occupants_enable(nil, enabled, room, occupant.nick, mediaType);
|
|
return true;
|
|
elseif moderation_command.attr.jidToWhitelist then
|
|
local occupant_jid = moderation_command.attr.jidToWhitelist;
|
|
-- check if jid is in the room, if so add it to whitelist
|
|
-- inform all moderators and admins and the jid
|
|
local occupant_to_add = room:get_occupant_by_nick(room_jid_match_rewrite(occupant_jid));
|
|
if not occupant_to_add then
|
|
module:log('warn', 'No occupant %s found for %s', occupant_jid, room.jid);
|
|
return false;
|
|
end
|
|
|
|
if room.av_moderation then
|
|
local whitelist = room.av_moderation[mediaType];
|
|
if not whitelist then
|
|
whitelist = array{};
|
|
room.av_moderation[mediaType] = whitelist;
|
|
end
|
|
whitelist:push(occupant_jid);
|
|
|
|
notify_whitelist_change(occupant_to_add.jid, true, room, mediaType, false);
|
|
|
|
return true;
|
|
else
|
|
-- this is a moderator asking the jid to unmute without enabling av moderation
|
|
-- let's just send the event
|
|
notify_jid_approved(occupant_to_add.jid, occupant.nick, room, mediaType);
|
|
end
|
|
elseif moderation_command.attr.jidToBlacklist then
|
|
local occupant_jid = moderation_command.attr.jidToBlacklist;
|
|
-- check if jid is in the room, if so remove it from the whitelist
|
|
-- inform all moderators and admins
|
|
local occupant_to_remove = room:get_occupant_by_nick(room_jid_match_rewrite(occupant_jid));
|
|
if not occupant_to_remove then
|
|
module:log('warn', 'No occupant %s found for %s', occupant_jid, room.jid);
|
|
return false;
|
|
end
|
|
|
|
if room.av_moderation then
|
|
local whitelist = room.av_moderation[mediaType];
|
|
if whitelist then
|
|
local index = get_index_in_table(whitelist, occupant_jid)
|
|
if(index) then
|
|
whitelist:pop(index);
|
|
notify_whitelist_change(occupant_to_remove.jid, true, room, mediaType, true);
|
|
end
|
|
end
|
|
|
|
return true;
|
|
end
|
|
end
|
|
end
|
|
|
|
-- return error
|
|
return false
|
|
end
|
|
|
|
-- handles new occupants to inform them about the state enabled/disabled, new moderators also get and the whitelist
|
|
function occupant_joined(event)
|
|
local room, occupant = event.room, event.occupant;
|
|
|
|
if is_healthcheck_room(room.jid) then
|
|
return;
|
|
end
|
|
|
|
if room.av_moderation then
|
|
for _,mediaType in pairs({'audio', 'video'}) do
|
|
if room.av_moderation[mediaType] then
|
|
notify_occupants_enable(
|
|
occupant.jid, true, room, room.av_moderation_actors[mediaType], mediaType);
|
|
end
|
|
end
|
|
|
|
-- NOTE for some reason event.occupant.role is not reflecting the actual occupant role (when changed
|
|
-- from allowners module) but iterating over room occupants returns the correct role
|
|
for _, room_occupant in room:each_occupant() do
|
|
-- if moderator send the whitelist
|
|
if room_occupant.nick == occupant.nick and room_occupant.role == 'moderator' then
|
|
notify_whitelist_change(room_occupant.jid, false, room);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- when a occupant was granted moderator we need to update him with the whitelist
|
|
function occupant_affiliation_changed(event)
|
|
-- the actor can be nil if is coming from allowners or similar module we want to skip it here
|
|
-- as we will handle it in occupant_joined
|
|
if event.actor and event.affiliation == 'owner' and event.room.av_moderation then
|
|
local room = event.room;
|
|
-- event.jid is the bare jid of participant
|
|
for _, occupant in room:each_occupant() do
|
|
if occupant.bare_jid == event.jid then
|
|
notify_whitelist_change(occupant.jid, false, room);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- we will receive messages from the clients
|
|
module:hook('message/host', on_message);
|
|
|
|
process_host_module(muc_component_host, function(host_module, host)
|
|
module:log('info','Hook to muc events on %s', host);
|
|
host_module:hook('muc-occupant-joined', occupant_joined, -2); -- make sure it runs after allowners or similar
|
|
host_module:hook('muc-set-affiliation', occupant_affiliation_changed, -1);
|
|
end);
|