"Fossies" - the Fresh Open Source Software Archive

Member "jitsi-meet-5186/resources/prosody-plugins/mod_speakerstats_component.lua" (30 Jul 2021, 8474 Bytes) of package /linux/misc/jitsi-meet-5186.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.

    1 local get_room_from_jid = module:require "util".get_room_from_jid;
    2 local room_jid_match_rewrite = module:require "util".room_jid_match_rewrite;
    3 local is_healthcheck_room = module:require "util".is_healthcheck_room;
    4 local jid_resource = require "util.jid".resource;
    5 local ext_events = module:require "ext_events"
    6 local st = require "util.stanza";
    7 local socket = require "socket";
    8 local json = require "util.json";
    9 local um_is_admin = require "core.usermanager".is_admin;
   10 
   11 -- we use async to detect Prosody 0.10 and earlier
   12 local have_async = pcall(require, "util.async");
   13 if not have_async then
   14     module:log("warn", "speaker stats will not work with Prosody version 0.10 or less.");
   15     return;
   16 end
   17 
   18 local muc_component_host = module:get_option_string("muc_component");
   19 if muc_component_host == nil then
   20     log("error", "No muc_component specified. No muc to operate on!");
   21     return;
   22 end
   23 
   24 log("info", "Starting speakerstats for %s", muc_component_host);
   25 
   26 local function is_admin(jid)
   27     return um_is_admin(jid, module.host);
   28 end
   29 
   30 -- receives messages from client currently connected to the room
   31 -- clients indicates their own dominant speaker events
   32 function on_message(event)
   33     -- Check the type of the incoming stanza to avoid loops:
   34     if event.stanza.attr.type == "error" then
   35         return; -- We do not want to reply to these, so leave.
   36     end
   37 
   38     local speakerStats
   39         = event.stanza:get_child('speakerstats', 'http://jitsi.org/jitmeet');
   40     if speakerStats then
   41         local roomAddress = speakerStats.attr.room;
   42         local room = get_room_from_jid(room_jid_match_rewrite(roomAddress));
   43 
   44         if not room then
   45             log("warn", "No room found %s", roomAddress);
   46             return false;
   47         end
   48  
   49         if not room.speakerStats then
   50             log("warn", "No speakerStats found for %s", roomAddress);
   51             return false;
   52         end
   53 
   54         local roomSpeakerStats = room.speakerStats;
   55         local from = event.stanza.attr.from;
   56 
   57         local occupant = room:get_occupant_by_real_jid(from);
   58         if not occupant then
   59             log("warn", "No occupant %s found for %s", from, roomAddress);
   60             return false;
   61         end
   62 
   63         local newDominantSpeaker = roomSpeakerStats[occupant.jid];
   64         local oldDominantSpeakerId = roomSpeakerStats['dominantSpeakerId'];
   65 
   66         if oldDominantSpeakerId then
   67             local oldDominantSpeaker = roomSpeakerStats[oldDominantSpeakerId];
   68             if oldDominantSpeaker then
   69                 oldDominantSpeaker:setDominantSpeaker(false);
   70             end
   71         end
   72 
   73         if newDominantSpeaker then
   74             newDominantSpeaker:setDominantSpeaker(true);
   75         end
   76 
   77         room.speakerStats['dominantSpeakerId'] = occupant.jid;
   78     end
   79 
   80     return true
   81 end
   82 
   83 --- Start SpeakerStats implementation
   84 local SpeakerStats = {};
   85 SpeakerStats.__index = SpeakerStats;
   86 
   87 function new_SpeakerStats(nick, context_user)
   88     return setmetatable({
   89         totalDominantSpeakerTime = 0;
   90         _dominantSpeakerStart = 0;
   91         nick = nick;
   92         context_user = context_user;
   93         displayName = nil;
   94     }, SpeakerStats);
   95 end
   96 
   97 -- Changes the dominantSpeaker data for current occupant
   98 -- saves start time if it is new dominat speaker
   99 -- or calculates and accumulates time of speaking
  100 function SpeakerStats:setDominantSpeaker(isNowDominantSpeaker)
  101     -- log("debug", "set isDominant %s for %s", tostring(isNowDominantSpeaker), self.nick);
  102 
  103     if not self:isDominantSpeaker() and isNowDominantSpeaker then
  104         self._dominantSpeakerStart = socket.gettime()*1000;
  105     elseif self:isDominantSpeaker() and not isNowDominantSpeaker then
  106         local now = socket.gettime()*1000;
  107         local timeElapsed = math.floor(now - self._dominantSpeakerStart);
  108 
  109         self.totalDominantSpeakerTime
  110             = self.totalDominantSpeakerTime + timeElapsed;
  111         self._dominantSpeakerStart = 0;
  112     end
  113 end
  114 
  115 -- Returns true if the tracked user is currently a dominant speaker.
  116 function SpeakerStats:isDominantSpeaker()
  117     return self._dominantSpeakerStart > 0;
  118 end
  119 --- End SpeakerStats
  120 
  121 -- create speakerStats for the room
  122 function room_created(event)
  123     local room = event.room;
  124 
  125     if is_healthcheck_room(room.jid) then
  126         return;
  127     end
  128 
  129     room.speakerStats = {};
  130 end
  131 
  132 -- Create SpeakerStats object for the joined user
  133 function occupant_joined(event)
  134     local occupant, room = event.occupant, event.room;
  135 
  136     if is_healthcheck_room(room.jid) or is_admin(occupant.bare_jid) then
  137         return;
  138     end
  139 
  140     local occupant = event.occupant;
  141 
  142     local nick = jid_resource(occupant.nick);
  143 
  144     if room.speakerStats then
  145         -- lets send the current speaker stats to that user, so he can update
  146         -- its local stats
  147         if next(room.speakerStats) ~= nil then
  148             local users_json = {};
  149             for jid, values in pairs(room.speakerStats) do
  150                 -- skip reporting those without a nick('dominantSpeakerId')
  151                 -- and skip focus if sneaked into the table
  152                 if values.nick ~= nil and values.nick ~= 'focus' then
  153                     local totalDominantSpeakerTime = values.totalDominantSpeakerTime;
  154                     if totalDominantSpeakerTime > 0 or room:get_occupant_jid(jid) == nil then
  155                         -- before sending we need to calculate current dominant speaker state
  156                         if values:isDominantSpeaker() then
  157                             local timeElapsed = math.floor(socket.gettime()*1000 - values._dominantSpeakerStart);
  158                             totalDominantSpeakerTime = totalDominantSpeakerTime + timeElapsed;
  159                         end
  160 
  161                         users_json[values.nick] =  {
  162                             displayName = values.displayName,
  163                             totalDominantSpeakerTime = totalDominantSpeakerTime
  164                         };
  165                     end
  166                 end
  167             end
  168 
  169             if next(users_json) ~= nil then
  170                 local body_json = {};
  171                 body_json.type = 'speakerstats';
  172                 body_json.users = users_json;
  173 
  174                 local stanza = st.message({
  175                     from = module.host;
  176                     to = occupant.jid; })
  177                 :tag("json-message", {xmlns='http://jitsi.org/jitmeet'})
  178                 :text(json.encode(body_json)):up();
  179 
  180                 room:route_stanza(stanza);
  181             end
  182         end
  183 
  184         local context_user = event.origin and event.origin.jitsi_meet_context_user or nil;
  185         room.speakerStats[occupant.jid] = new_SpeakerStats(nick, context_user);
  186     end
  187 end
  188 
  189 -- Occupant left set its dominant speaker to false and update the store the
  190 -- display name
  191 function occupant_leaving(event)
  192     local room = event.room;
  193 
  194     if is_healthcheck_room(room.jid) then
  195         return;
  196     end
  197  
  198     if not room.speakerStats then
  199         return;
  200     end
  201 
  202     local occupant = event.occupant;
  203 
  204     local speakerStatsForOccupant = room.speakerStats[occupant.jid];
  205     if speakerStatsForOccupant then
  206         speakerStatsForOccupant:setDominantSpeaker(false);
  207 
  208         -- set display name
  209         local displayName = occupant:get_presence():get_child_text(
  210             'nick', 'http://jabber.org/protocol/nick');
  211         speakerStatsForOccupant.displayName = displayName;
  212     end
  213 end
  214 
  215 -- Conference ended, send speaker stats
  216 function room_destroyed(event)
  217     local room = event.room;
  218 
  219     if is_healthcheck_room(room.jid) then
  220         return;
  221     end
  222 
  223     ext_events.speaker_stats(room, room.speakerStats);
  224 end
  225 
  226 module:hook("message/host", on_message);
  227 
  228 -- executed on every host added internally in prosody, including components
  229 function process_host(host)
  230     if host == muc_component_host then -- the conference muc component
  231         module:log("info","Hook to muc events on %s", host);
  232 
  233         local muc_module = module:context(host);
  234         muc_module:hook("muc-room-created", room_created, -1);
  235         muc_module:hook("muc-occupant-joined", occupant_joined, -1);
  236         muc_module:hook("muc-occupant-pre-leave", occupant_leaving, -1);
  237         muc_module:hook("muc-room-destroyed", room_destroyed, -1);
  238     end
  239 end
  240 
  241 if prosody.hosts[muc_component_host] == nil then
  242     module:log("info","No muc component found, will listen for it: %s", muc_component_host)
  243 
  244     -- when a host or component is added
  245     prosody.events.add_handler("host-activated", process_host);
  246 else
  247     process_host(muc_component_host);
  248 end