Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. -- This module provides persistence for the "polls" feature,
  2. -- by keeping track of the state of polls in each room, and sending
  3. -- that state to new participants when they join.
  4. local json = require 'cjson.safe';
  5. local st = require("util.stanza");
  6. local jid = require "util.jid";
  7. local util = module:require("util");
  8. local muc = module:depends("muc");
  9. local NS_NICK = 'http://jabber.org/protocol/nick';
  10. local is_healthcheck_room = util.is_healthcheck_room;
  11. local POLLS_LIMIT = 128;
  12. local POLL_PAYLOAD_LIMIT = 1024;
  13. -- Logs a warning and returns true if a room does not
  14. -- have poll data associated with it.
  15. local function check_polls(room)
  16. if room.polls == nil then
  17. module:log("warn", "no polls data in room");
  18. return true;
  19. end
  20. return false;
  21. end
  22. --- Returns a table having occupant id and occupant name.
  23. --- If the id cannot be extracted from nick a nil value is returned
  24. --- if the occupant name cannot be extracted from presence the Fellow Jitster
  25. --- name is used
  26. local function get_occupant_details(occupant)
  27. if not occupant then
  28. return nil
  29. end
  30. local presence = occupant:get_presence();
  31. local occupant_name;
  32. if presence then
  33. occupant_name = presence:get_child("nick", NS_NICK) and presence:get_child("nick", NS_NICK):get_text() or 'Fellow Jitster';
  34. else
  35. occupant_name = 'Fellow Jitster'
  36. end
  37. local _, _, occupant_id = jid.split(occupant.nick)
  38. if not occupant_id then
  39. return nil
  40. end
  41. return { ["occupant_id"] = occupant_id, ["occupant_name"] = occupant_name }
  42. end
  43. -- Sets up poll data in new rooms.
  44. module:hook("muc-room-created", function(event)
  45. local room = event.room;
  46. if is_healthcheck_room(room.jid) then return end
  47. module:log("debug", "setting up polls in room %s", room.jid);
  48. room.polls = {
  49. by_id = {};
  50. order = {};
  51. count = 0;
  52. };
  53. end);
  54. -- Keeps track of the current state of the polls in each room,
  55. -- by listening to "new-poll" and "answer-poll" messages,
  56. -- and updating the room poll data accordingly.
  57. -- This mirrors the client-side poll update logic.
  58. module:hook('jitsi-endpoint-message-received', function(event)
  59. local data, error, occupant, room, origin, stanza
  60. = event.message, event.error, event.occupant, event.room, event.origin, event.stanza;
  61. if not data or (data.type ~= "new-poll" and data.type ~= "answer-poll") then
  62. return;
  63. end
  64. if string.len(event.raw_message) >= POLL_PAYLOAD_LIMIT then
  65. module:log('error', 'Poll payload too large, discarding. Sender: %s to:%s', stanza.attr.from, stanza.attr.to);
  66. return nil;
  67. end
  68. if data.type == "new-poll" then
  69. if check_polls(room) then return end
  70. local poll_creator = get_occupant_details(occupant)
  71. if not poll_creator then
  72. module:log("error", "Cannot retrieve poll creator id and name for %s from %s", occupant.jid, room.jid)
  73. return
  74. end
  75. if room.polls.count >= POLLS_LIMIT then
  76. module:log("error", "Too many polls created in %s", room.jid)
  77. return
  78. end
  79. if room.polls.by_id[data.pollId] ~= nil then
  80. module:log("error", "Poll already exists: %s", data.pollId);
  81. origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Poll already exists'));
  82. return true;
  83. end
  84. local answers = {}
  85. local compact_answers = {}
  86. for i, name in ipairs(data.answers) do
  87. table.insert(answers, { name = name, voters = {} });
  88. table.insert(compact_answers, { key = i, name = name});
  89. end
  90. local poll = {
  91. id = data.pollId,
  92. sender_id = poll_creator.occupant_id,
  93. sender_name = poll_creator.occupant_name,
  94. question = data.question,
  95. answers = answers
  96. };
  97. room.polls.by_id[data.pollId] = poll
  98. table.insert(room.polls.order, poll)
  99. room.polls.count = room.polls.count + 1;
  100. local pollData = {
  101. event = event,
  102. room = room,
  103. poll = {
  104. pollId = data.pollId,
  105. senderId = poll_creator.occupant_id,
  106. senderName = poll_creator.occupant_name,
  107. question = data.question,
  108. answers = compact_answers
  109. }
  110. }
  111. module:fire_event("poll-created", pollData);
  112. elseif data.type == "answer-poll" then
  113. if check_polls(room) then return end
  114. local poll = room.polls.by_id[data.pollId];
  115. if poll == nil then
  116. module:log("warn", "answering inexistent poll");
  117. return;
  118. end
  119. local voter = get_occupant_details(occupant)
  120. if not voter then
  121. module:log("error", "Cannot retrieve voter id and name for %s from %s", occupant.jid, room.jid)
  122. return
  123. end
  124. local answers = {};
  125. for vote_option_idx, vote_flag in ipairs(data.answers) do
  126. table.insert(answers, {
  127. key = vote_option_idx,
  128. value = vote_flag,
  129. name = poll.answers[vote_option_idx].name,
  130. });
  131. poll.answers[vote_option_idx].voters[voter.occupant_id] = vote_flag and voter.occupant_name or nil;
  132. end
  133. local answerData = {
  134. event = event,
  135. room = room,
  136. pollId = poll.id,
  137. voterName = voter.occupant_name,
  138. voterId = voter.occupant_id,
  139. answers = answers
  140. }
  141. module:fire_event("answer-poll", answerData);
  142. end
  143. end);
  144. -- Sends the current poll state to new occupants after joining a room.
  145. module:hook("muc-occupant-joined", function(event)
  146. local room = event.room;
  147. if is_healthcheck_room(room.jid) then return end
  148. if room.polls == nil or #room.polls.order == 0 then
  149. return
  150. end
  151. local data = {
  152. type = "old-polls",
  153. polls = {},
  154. };
  155. for i, poll in ipairs(room.polls.order) do
  156. data.polls[i] = {
  157. id = poll.id,
  158. senderId = poll.sender_id,
  159. senderName = poll.sender_name,
  160. question = poll.question,
  161. answers = poll.answers
  162. };
  163. end
  164. local json_msg_str, error = json.encode(data);
  165. if not json_msg_str then
  166. module:log('error', 'Error encoding data room:%s error:%s', room.jid, error);
  167. end
  168. local stanza = st.message({
  169. from = room.jid,
  170. to = event.occupant.jid
  171. })
  172. :tag("json-message", { xmlns = "http://jitsi.org/jitmeet" })
  173. :text(json_msg_str)
  174. :up();
  175. room:route_stanza(stanza);
  176. end);