You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mod_muc_poltergeist.lua 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. local bare = require "util.jid".bare;
  2. local generate_uuid = require "util.uuid".generate;
  3. local jid = require "util.jid";
  4. local neturl = require "net.url";
  5. local parse = neturl.parseQuery;
  6. local st = require "util.stanza";
  7. local get_room_from_jid = module:require "util".get_room_from_jid;
  8. -- Options
  9. local poltergeist_component
  10. = module:get_option_string("poltergeist_component", module.host);
  11. -- table to store all poltergeists we create
  12. local poltergeists = {};
  13. -- poltergaist management functions
  14. -- Returns the room if available, work and in multidomain mode
  15. -- @param room_name the name of the room
  16. -- @param group name of the group (optional)
  17. -- @return returns room if found or nil
  18. function get_room(room_name, group)
  19. local room_address = jid.join(room_name, module:get_host());
  20. -- if there is a group we are in multidomain mode
  21. if group and group ~= "" then
  22. room_address = "["..group.."]"..room_address;
  23. end
  24. return get_room_from_jid(room_address);
  25. end
  26. -- Stores the username in the table where we store poltergeist usernames
  27. -- based on their room names
  28. -- @param room the room instance
  29. -- @param user_id the user id
  30. -- @param username the username to store
  31. function store_username(room, user_id, username)
  32. local room_name = jid.node(room.jid);
  33. -- we store in poltergeist user ids for room names
  34. if (not poltergeists[room_name]) then
  35. poltergeists[room_name] = {};
  36. end
  37. poltergeists[room_name][user_id] = username;
  38. log("debug", "stored in session: %s", username);
  39. end
  40. -- Retrieve the username for a user
  41. -- @param room the room instance
  42. -- @param user_id the user id
  43. -- @return returns the stored username for user or nil
  44. function get_username(room, user_id)
  45. local room_name = jid.node(room.jid);
  46. if (not poltergeists[room_name]) then
  47. return nil;
  48. end
  49. return poltergeists[room_name][user_id];
  50. end
  51. -- if we found that a session for a user with id has a poltergiest already
  52. -- created, retrieve its jid and return it to the authentication
  53. -- so we can reuse it and we that real user will replace the poltergiest
  54. prosody.events.add_handler("pre-jitsi-authentication", function(session)
  55. if (session.jitsi_meet_context_user) then
  56. local room = get_room(
  57. session.jitsi_bosh_query_room,
  58. session.jitsi_meet_context_group);
  59. if (not room) then
  60. return nil;
  61. end
  62. local username
  63. = get_username(room, session.jitsi_meet_context_user["id"]);
  64. if (not username) then
  65. return nil;
  66. end
  67. log("debug", "Found predefined username %s", username);
  68. -- let's find the room and if the poltergeist occupant is there
  69. -- lets remove him before the real participant joins
  70. -- when we see the unavailable presence to go out the server
  71. -- we will mark it with ignore tag
  72. local nick = string.sub(username, 0, 8);
  73. if (have_poltergeist_occupant(room, nick)) then
  74. remove_poltergeist_occupant(room, nick);
  75. end
  76. return username;
  77. end
  78. return nil;
  79. end);
  80. -- Creates poltergeist occupant
  81. -- @param room the room instance where we create the occupant
  82. -- @param nick the nick to use for the new occupant
  83. -- @param name the display name fot the occupant (optional)
  84. -- @param avatar the avatar to use for the new occupant (optional)
  85. function create_poltergeist_occupant(room, nick, name, avatar)
  86. log("debug", "create_poltergeist_occupant %s:", nick);
  87. -- Join poltergeist occupant to room, with the invited JID as their nick
  88. local join_presence = st.presence({
  89. to = room.jid.."/"..nick,
  90. from = poltergeist_component.."/"..nick
  91. }):tag("x", { xmlns = "http://jabber.org/protocol/muc" }):up();
  92. if (name) then
  93. join_presence:tag(
  94. "nick",
  95. { xmlns = "http://jabber.org/protocol/nick" }):text(name):up();
  96. end
  97. if (avatar) then
  98. join_presence:tag("avatar-url"):text(avatar):up();
  99. end
  100. room:handle_first_presence(
  101. prosody.hosts[poltergeist_component], join_presence);
  102. end
  103. -- Removes poltergeist occupant
  104. -- @param room the room instance where to remove the occupant
  105. -- @param nick the nick of the occupant to remove
  106. function remove_poltergeist_occupant(room, nick)
  107. log("debug", "remove_poltergeist_occupant %s", nick);
  108. local leave_presence = st.presence({
  109. to = room.jid.."/"..nick,
  110. from = poltergeist_component.."/"..nick,
  111. type = "unavailable" });
  112. room:handle_normal_presence(
  113. prosody.hosts[poltergeist_component], leave_presence);
  114. end
  115. -- Checks for existance of a poltergeist occupant
  116. -- @param room the room instance where to check for occupant
  117. -- @param nick the nick of the occupant
  118. -- @return true if occupant is found, false otherwise
  119. function have_poltergeist_occupant(room, nick)
  120. -- Find out if we have a poltergeist occupant in the room for this JID
  121. return not not room:get_occupant_jid(poltergeist_component.."/"..nick);
  122. end
  123. -- Event handlers
  124. --- Note: mod_muc and some of its sub-modules add event handlers between 0 and -100,
  125. --- e.g. to check for banned users, etc.. Hence adding these handlers at priority -100.
  126. module:hook("muc-decline", function (event)
  127. remove_poltergeist_occupant(event.room, bare(event.stanza.attr.from));
  128. end, -100);
  129. -- before sending the presence for a poltergeist leaving add ignore tag
  130. -- as poltergeist is leaving just before the real user joins and in the client
  131. -- we ignore this presence to avoid leaving/joining experience and the real
  132. -- user will reuse all currently created UI components for the same nick
  133. module:hook("muc-broadcast-presence", function (event)
  134. if (bare(event.occupant.jid) == poltergeist_component) then
  135. if(event.stanza.attr.type == "unavailable") then
  136. event.stanza:tag(
  137. "ignore", { xmlns = "http://jitsi.org/jitmeet/" }):up();
  138. end
  139. end
  140. end, -100);
  141. --- Handles request for creating/managing poltergeists
  142. -- @param event the http event, holds the request query
  143. -- @return GET response, containing a json with response details
  144. function handle_create_poltergeist (event)
  145. local params = parse(event.request.url.query);
  146. local user_id = params["user"];
  147. local room_name = params["room"];
  148. local group = params["group"];
  149. local name = params["name"];
  150. local avatar = params["avatar"];
  151. local room = get_room(room_name, group);
  152. if (not room) then
  153. log("error", "no room found %s", room_name);
  154. return 404;
  155. end
  156. local username = generate_uuid();
  157. store_username(room, user_id, username)
  158. create_poltergeist_occupant(room, string.sub(username,0,8), name, avatar);
  159. return 200;
  160. end
  161. --- Handles request for updating poltergeists status
  162. -- @param event the http event, holds the request query
  163. -- @return GET response, containing a json with response details
  164. function handle_update_poltergeist (event)
  165. local params = parse(event.request.url.query);
  166. local user_id = params["user"];
  167. local room_name = params["room"];
  168. local group = params["group"];
  169. local status = params["status"];
  170. local room = get_room(room_name, group);
  171. if (not room) then
  172. log("error", "no room found %s", room_name);
  173. return 404;
  174. end
  175. local username = get_username(room, user_id);
  176. if (not username) then
  177. return 404;
  178. end
  179. local nick = string.sub(username, 0, 8);
  180. if (have_poltergeist_occupant(room, nick)) then
  181. local update_presence = st.presence({
  182. to = room.jid.."/"..nick,
  183. from = poltergeist_component.."/"..nick
  184. }):tag("status"):text(status):up();
  185. room:handle_normal_presence(
  186. prosody.hosts[poltergeist_component], update_presence);
  187. return 200;
  188. else
  189. return 404;
  190. end
  191. end
  192. log("info", "Loading poltergeist service");
  193. module:depends("http");
  194. module:provides("http", {
  195. default_path = "/";
  196. name = "poltergeist";
  197. route = {
  198. ["GET /poltergeist/create"] = handle_create_poltergeist;
  199. ["GET /poltergeist/update"] = handle_update_poltergeist;
  200. };
  201. });