Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

mod_speakerstats_component.lua 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. local get_room_from_jid = module:require "util".get_room_from_jid;
  2. local jid_resource = require "util.jid".resource;
  3. local ext_events = module:require "ext_events"
  4. local st = require "util.stanza";
  5. local socket = require "socket";
  6. local json = require "util.json";
  7. local muc_component_host = module:get_option_string("muc_component");
  8. if muc_component_host == nil then
  9. log("error", "No muc_component specified. No muc to operate on!");
  10. return;
  11. end
  12. log("info", "Starting speakerstats for %s", muc_component_host);
  13. -- receives messages from client currently connected to the room
  14. -- clients indicates their own dominant speaker events
  15. function on_message(event)
  16. -- Check the type of the incoming stanza to avoid loops:
  17. if event.stanza.attr.type == "error" then
  18. return; -- We do not want to reply to these, so leave.
  19. end
  20. local speakerStats
  21. = event.stanza:get_child('speakerstats', 'http://jitsi.org/jitmeet');
  22. if speakerStats then
  23. local roomAddress = speakerStats.attr.room;
  24. local room = get_room_from_jid(roomAddress);
  25. if not room then
  26. log("warn", "No room found %s", roomAddress);
  27. return false;
  28. end
  29. local roomSpeakerStats = room.speakerStats;
  30. local from = event.stanza.attr.from;
  31. local occupant = room:get_occupant_by_real_jid(from);
  32. if not occupant then
  33. log("warn", "No occupant %s found for %s", from, roomAddress);
  34. return false;
  35. end
  36. local newDominantSpeaker = roomSpeakerStats[occupant.jid];
  37. local oldDominantSpeakerId = roomSpeakerStats['dominantSpeakerId'];
  38. if oldDominantSpeakerId then
  39. roomSpeakerStats[oldDominantSpeakerId]:setDominantSpeaker(false);
  40. end
  41. if newDominantSpeaker then
  42. newDominantSpeaker:setDominantSpeaker(true);
  43. end
  44. room.speakerStats['dominantSpeakerId'] = occupant.jid;
  45. end
  46. return true
  47. end
  48. --- Start SpeakerStats implementation
  49. local SpeakerStats = {};
  50. SpeakerStats.__index = SpeakerStats;
  51. function new_SpeakerStats(nick, context_user)
  52. return setmetatable({
  53. totalDominantSpeakerTime = 0;
  54. _dominantSpeakerStart = 0;
  55. nick = nick;
  56. context_user = context_user;
  57. displayName = nil;
  58. }, SpeakerStats);
  59. end
  60. -- Changes the dominantSpeaker data for current occupant
  61. -- saves start time if it is new dominat speaker
  62. -- or calculates and accumulates time of speaking
  63. function SpeakerStats:setDominantSpeaker(isNowDominantSpeaker)
  64. log("debug",
  65. "set isDominant %s for %s", tostring(isNowDominantSpeaker), self.nick);
  66. if not self:isDominantSpeaker() and isNowDominantSpeaker then
  67. self._dominantSpeakerStart = socket.gettime()*1000;
  68. elseif self:isDominantSpeaker() and not isNowDominantSpeaker then
  69. local now = socket.gettime()*1000;
  70. local timeElapsed = math.floor(now - self._dominantSpeakerStart);
  71. self.totalDominantSpeakerTime
  72. = self.totalDominantSpeakerTime + timeElapsed;
  73. self._dominantSpeakerStart = 0;
  74. end
  75. end
  76. -- Returns true if the tracked user is currently a dominant speaker.
  77. function SpeakerStats:isDominantSpeaker()
  78. return self._dominantSpeakerStart > 0;
  79. end
  80. --- End SpeakerStats
  81. -- create speakerStats for the room
  82. function room_created(event)
  83. local room = event.room;
  84. room.speakerStats = {};
  85. end
  86. -- Create SpeakerStats object for the joined user
  87. function occupant_joined(event)
  88. local room = event.room;
  89. local occupant = event.occupant;
  90. local nick = jid_resource(occupant.nick);
  91. if room.speakerStats then
  92. -- lets send the current speaker stats to that user, so he can update
  93. -- its local stats
  94. if next(room.speakerStats) ~= nil then
  95. local users_json = {};
  96. for jid, values in pairs(room.speakerStats) do
  97. -- skip reporting those without a nick('dominantSpeakerId')
  98. -- and skip focus if sneaked into the table
  99. if values.nick ~= nil and values.nick ~= 'focus' then
  100. local resultSpeakerStats = {};
  101. local totalDominantSpeakerTime
  102. = values.totalDominantSpeakerTime;
  103. -- before sending we need to calculate current dominant speaker
  104. -- state
  105. if values:isDominantSpeaker() then
  106. local timeElapsed = math.floor(
  107. socket.gettime()*1000 - values._dominantSpeakerStart);
  108. totalDominantSpeakerTime = totalDominantSpeakerTime
  109. + timeElapsed;
  110. end
  111. resultSpeakerStats.displayName = values.displayName;
  112. resultSpeakerStats.totalDominantSpeakerTime
  113. = totalDominantSpeakerTime;
  114. users_json[values.nick] = resultSpeakerStats;
  115. end
  116. end
  117. local body_json = {};
  118. body_json.type = 'speakerstats';
  119. body_json.users = users_json;
  120. local stanza = st.message({
  121. from = module.host;
  122. to = occupant.jid; })
  123. :tag("json-message", {xmlns='http://jitsi.org/jitmeet'})
  124. :text(json.encode(body_json)):up();
  125. room:route_stanza(stanza);
  126. end
  127. local context_user = event.origin and event.origin.jitsi_meet_context_user or nil;
  128. room.speakerStats[occupant.jid] = new_SpeakerStats(nick, context_user);
  129. end
  130. end
  131. -- Occupant left set its dominant speaker to false and update the store the
  132. -- display name
  133. function occupant_leaving(event)
  134. local room = event.room;
  135. local occupant = event.occupant;
  136. local speakerStatsForOccupant = room.speakerStats[occupant.jid];
  137. if speakerStatsForOccupant then
  138. speakerStatsForOccupant:setDominantSpeaker(false);
  139. -- set display name
  140. local displayName = occupant:get_presence():get_child_text(
  141. 'nick', 'http://jabber.org/protocol/nick');
  142. speakerStatsForOccupant.displayName = displayName;
  143. end
  144. end
  145. -- Conference ended, send speaker stats
  146. function room_destroyed(event)
  147. local room = event.room;
  148. ext_events.speaker_stats(room, room.speakerStats);
  149. end
  150. module:hook("message/host", on_message);
  151. -- executed on every host added internally in prosody, including components
  152. function process_host(host)
  153. if host == muc_component_host then -- the conference muc component
  154. module:log("info","Hook to muc events on %s", host);
  155. local muc_module = module:context(host);
  156. muc_module:hook("muc-room-created", room_created, -1);
  157. muc_module:hook("muc-occupant-joined", occupant_joined, -1);
  158. muc_module:hook("muc-occupant-pre-leave", occupant_leaving, -1);
  159. muc_module:hook("muc-room-destroyed", room_destroyed, -1);
  160. end
  161. end
  162. if prosody.hosts[muc_component_host] == nil then
  163. module:log("info","No muc component found, will listen for it: %s", muc_component_host)
  164. -- when a host or component is added
  165. prosody.events.add_handler("host-activated", process_host);
  166. else
  167. process_host(muc_component_host);
  168. end