Creating a poltergiest library and using in for mod_muc_poltergeist.
This commit is contained in:
parent
535e5b4f64
commit
6ae5adcb3d
|
@ -39,8 +39,7 @@ local function url_from_room_jid(room_jid)
|
|||
if not(target_node or target_subdomain) then
|
||||
return "https://"..muc_domain_base.."/"..node
|
||||
else
|
||||
return
|
||||
"https://"..muc_domain_base.."/"..target_subdomain.."/"..target_node
|
||||
return "https://"..muc_domain_base.."/"..target_subdomain.."/"..target_node
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,22 +1,15 @@
|
|||
local bare = require "util.jid".bare;
|
||||
local generate_uuid = require "util.uuid".generate;
|
||||
local get_room_from_jid = module:require "util".get_room_from_jid;
|
||||
local jid = require "util.jid";
|
||||
local neturl = require "net.url";
|
||||
local parse = neturl.parseQuery;
|
||||
local st = require "util.stanza";
|
||||
local get_room_from_jid = module:require "util".get_room_from_jid;
|
||||
local poltergeist = module:require "poltergeist";
|
||||
local wrap_async_run = module:require "util".wrap_async_run;
|
||||
local update_presence_identity = module:require "util".update_presence_identity;
|
||||
local timer = require "util.timer";
|
||||
local MUC_NS = "http://jabber.org/protocol/muc";
|
||||
local expired_status = "expired";
|
||||
|
||||
-- Options
|
||||
local poltergeist_component
|
||||
= module:get_option_string("poltergeist_component", module.host);
|
||||
-- defaults to 30 seconds
|
||||
local poltergeist_timeout
|
||||
= module:get_option_string("poltergeist_leave_timeout", 30);
|
||||
|
||||
-- this basically strips the domain from the conference.domain address
|
||||
local parentHostName = string.gmatch(tostring(module.host), "%w+.(%w.+)")();
|
||||
if parentHostName == nil then
|
||||
|
@ -37,12 +30,6 @@ local token_util = module:require "token/util".new(parentCtx);
|
|||
local disableTokenVerification
|
||||
= module:get_option_boolean("disable_polergeist_token_verification", false);
|
||||
|
||||
-- table to store all poltergeists we create
|
||||
local poltergeists = {};
|
||||
-- table to mark that outgoing unavailable presences
|
||||
-- should be marked with ignore
|
||||
local poltergeists_pr_ignore = {};
|
||||
|
||||
-- poltergaist management functions
|
||||
|
||||
-- Returns the room if available, work and in multidomain mode
|
||||
|
@ -60,54 +47,6 @@ function get_room(room_name, group)
|
|||
return get_room_from_jid(room_address);
|
||||
end
|
||||
|
||||
-- Stores the username in the table where we store poltergeist usernames
|
||||
-- based on their room names
|
||||
-- @param room the room instance
|
||||
-- @param user_id the user id
|
||||
-- @param username the username to store
|
||||
function store_username(room, user_id, username)
|
||||
local room_name = jid.node(room.jid);
|
||||
|
||||
-- we store in poltergeist user ids for room names
|
||||
if (not poltergeists[room_name]) then
|
||||
poltergeists[room_name] = {};
|
||||
end
|
||||
poltergeists[room_name][user_id] = username;
|
||||
log("debug", "stored in session: %s", username);
|
||||
end
|
||||
|
||||
-- Retrieve the username for a user
|
||||
-- @param room the room instance
|
||||
-- @param user_id the user id
|
||||
-- @return returns the stored username for user or nil
|
||||
function get_username(room, user_id)
|
||||
local room_name = jid.node(room.jid);
|
||||
|
||||
if (not poltergeists[room_name]) then
|
||||
return nil;
|
||||
end
|
||||
|
||||
return poltergeists[room_name][user_id];
|
||||
end
|
||||
|
||||
-- Removes poltergeist values from table
|
||||
-- @param room the room instance
|
||||
-- @param nick the user nick
|
||||
function remove_username(room, nick)
|
||||
local room_name = jid.node(room.jid);
|
||||
if (poltergeists[room_name]) then
|
||||
local user_id_to_remove;
|
||||
for name,username in pairs(poltergeists[room_name]) do
|
||||
if (string.sub(username, 0, 8) == nick) then
|
||||
user_id_to_remove = name;
|
||||
end
|
||||
end
|
||||
if (user_id_to_remove) then
|
||||
poltergeists[room_name][user_id_to_remove] = nil;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Verifies room name, domain name with the values in the token
|
||||
-- @param token the token we received
|
||||
-- @param room_name the room name
|
||||
|
@ -151,6 +90,8 @@ function verify_token(token, room_name, group, session)
|
|||
return true;
|
||||
end
|
||||
|
||||
-- Event handlers
|
||||
|
||||
-- if we found that a session for a user with id has a poltergiest already
|
||||
-- created, retrieve its jid and return it to the authentication
|
||||
-- so we can reuse it and we that real user will replace the poltergiest
|
||||
|
@ -165,8 +106,10 @@ prosody.events.add_handler("pre-jitsi-authentication", function(session)
|
|||
return nil;
|
||||
end
|
||||
|
||||
local username
|
||||
= get_username(room, session.jitsi_meet_context_user["id"]);
|
||||
local username = poltergeist.get_username(
|
||||
room,
|
||||
session.jitsi_meet_context_user["id"]
|
||||
);
|
||||
|
||||
if (not username) then
|
||||
return nil;
|
||||
|
@ -178,12 +121,12 @@ prosody.events.add_handler("pre-jitsi-authentication", function(session)
|
|||
-- lets remove him before the real participant joins
|
||||
-- when we see the unavailable presence to go out the server
|
||||
-- we will mark it with ignore tag
|
||||
local nick = string.sub(username, 0, 8);
|
||||
if (have_poltergeist_occupant(room, nick)) then
|
||||
local nick = poltergeist.create_nick(username);
|
||||
if (poltergeist.occupies(room, nick)) then
|
||||
module:log("info", "swapping poltergeist for user: %s/%s", room, nick)
|
||||
-- notify that user connected using the poltergeist
|
||||
update_poltergeist_occupant_status(
|
||||
room, nick, "connected");
|
||||
remove_poltergeist_occupant(room, nick, true);
|
||||
poltergeist.update(room, nick, "connected");
|
||||
poltergeist.remove(room, nick, true);
|
||||
end
|
||||
|
||||
return username;
|
||||
|
@ -192,134 +135,10 @@ prosody.events.add_handler("pre-jitsi-authentication", function(session)
|
|||
return nil;
|
||||
end);
|
||||
|
||||
-- Removes poltergeist occupant
|
||||
-- @param room the room instance where to remove the occupant
|
||||
-- @param nick the nick of the occupant to remove
|
||||
-- @param ignore to mark the poltergeist unavailble presence to be ignored
|
||||
function remove_poltergeist_occupant(room, nick, ignore)
|
||||
log("debug", "remove_poltergeist_occupant %s", nick);
|
||||
|
||||
local current_presence = get_presence(room, nick);
|
||||
if (not current_presence) then
|
||||
module:log("info", "attempted to remove a poltergeist with no presence")
|
||||
return;
|
||||
end
|
||||
|
||||
local leave_presence = st.clone(current_presence)
|
||||
leave_presence.attr.to = room.jid.."/"..nick;
|
||||
leave_presence.attr.from = poltergeist_component.."/"..nick;
|
||||
leave_presence.attr.type = "unavailable";
|
||||
|
||||
if (ignore) then
|
||||
poltergeists_pr_ignore[room.jid.."/"..nick] = true;
|
||||
end
|
||||
|
||||
room:handle_normal_presence(
|
||||
prosody.hosts[poltergeist_component], leave_presence);
|
||||
remove_username(room, nick);
|
||||
end
|
||||
|
||||
-- Updates poltergeist occupant status
|
||||
-- @param room the room instance where to remove the occupant
|
||||
-- @param nick the nick of the occupant to remove
|
||||
-- @param status the status to update
|
||||
-- @param call_details is a table of call flow details
|
||||
function update_poltergeist_occupant_status(room, nick, status, call_details)
|
||||
local update_presence = get_presence(room, nick);
|
||||
|
||||
if (not update_presence) then
|
||||
-- TODO: determine if we should provide an error and how that would be
|
||||
-- handled for bosh and http api.
|
||||
module:log("info", "update issued for a non-existing poltergeist")
|
||||
return;
|
||||
end
|
||||
|
||||
-- update occupant presence with appropriate to and from
|
||||
-- so we can send it again
|
||||
update_presence = st.clone(update_presence);
|
||||
update_presence.attr.to = room.jid.."/"..nick;
|
||||
update_presence.attr.from = poltergeist_component.."/"..nick;
|
||||
|
||||
update_presence = update_presence_tags(update_presence, status, call_details)
|
||||
|
||||
room:handle_normal_presence(
|
||||
prosody.hosts[poltergeist_component], update_presence);
|
||||
end
|
||||
|
||||
-- Updates the status tags and call flow tags of an existing poltergeist's
|
||||
-- presence.
|
||||
-- @param presence_stanza is the actual presence stanza for a poltergeist.
|
||||
-- @param status is the new status to be updated in the stanza.
|
||||
-- @param call_details is a table of call flow signal information.
|
||||
function update_presence_tags(presence_stanza, status, call_details)
|
||||
local call_cancel = false;
|
||||
local call_id = nil;
|
||||
|
||||
-- Extract optional call flow signal information.
|
||||
if call_details then
|
||||
call_id = call_details["id"];
|
||||
|
||||
if call_details["cancel"] then
|
||||
call_cancel = call_details["cancel"];
|
||||
end
|
||||
end
|
||||
|
||||
presence_stanza:maptags(function (tag)
|
||||
if tag.name == "status" then
|
||||
if call_cancel then
|
||||
-- If call cancel is set then the status should not be changed.
|
||||
return tag
|
||||
end
|
||||
return st.stanza("status"):text(status);
|
||||
elseif tag.name == "call_id" then
|
||||
if call_id then
|
||||
return st.stanza("call_id"):text(call_id);
|
||||
else
|
||||
-- If no call id is provided the re-use the existing id.
|
||||
return tag;
|
||||
end
|
||||
elseif tag.name == "call_cancel" then
|
||||
if call_cancel then
|
||||
return st.stanza("call_cancel"):text("true");
|
||||
else
|
||||
return st.stanza("call_cancel"):text("false");
|
||||
end
|
||||
end
|
||||
return tag;
|
||||
end);
|
||||
|
||||
return presence_stanza
|
||||
end
|
||||
|
||||
-- Checks for existance of a poltergeist occupant
|
||||
-- @param room the room instance where to check for occupant
|
||||
-- @param nick the nick of the occupant
|
||||
-- @return true if occupant is found, false otherwise
|
||||
function have_poltergeist_occupant(room, nick)
|
||||
-- Find out if we have a poltergeist occupant in the room for this JID
|
||||
return not not room:get_occupant_jid(poltergeist_component.."/"..nick);
|
||||
end
|
||||
|
||||
-- Returns the last presence of occupant
|
||||
-- @param room the room instance where to check for occupant
|
||||
-- @param nick the nick of the occupant
|
||||
-- @return presence of the occupant
|
||||
function get_presence(room, nick)
|
||||
local occupant_jid
|
||||
= room:get_occupant_jid(poltergeist_component.."/"..nick);
|
||||
if (occupant_jid) then
|
||||
return room:get_occupant_by_nick(occupant_jid):get_presence();
|
||||
end
|
||||
|
||||
return nil;
|
||||
end
|
||||
|
||||
-- Event handlers
|
||||
|
||||
--- Note: mod_muc and some of its sub-modules add event handlers between 0 and -100,
|
||||
--- e.g. to check for banned users, etc.. Hence adding these handlers at priority -100.
|
||||
module:hook("muc-decline", function (event)
|
||||
remove_poltergeist_occupant(event.room, bare(event.stanza.attr.from), false);
|
||||
poltergeist.remove(event.room, bare(event.stanza.attr.from), false);
|
||||
end, -100);
|
||||
-- before sending the presence for a poltergeist leaving add ignore tag
|
||||
-- as poltergeist is leaving just before the real user joins and in the client
|
||||
|
@ -328,21 +147,21 @@ end, -100);
|
|||
module:hook("muc-broadcast-presence", function (event)
|
||||
if (bare(event.occupant.jid) == poltergeist_component) then
|
||||
if(event.stanza.attr.type == "unavailable"
|
||||
and poltergeists_pr_ignore[event.occupant.nick]) then
|
||||
and poltergeist.should_ignore(event.occupant.nick)) then
|
||||
event.stanza:tag(
|
||||
"ignore", { xmlns = "http://jitsi.org/jitmeet/" }):up();
|
||||
poltergeists_pr_ignore[event.occupant.nick] = nil;
|
||||
poltergeist.reset_ignored(event.occupant.nick);
|
||||
end
|
||||
end
|
||||
end, -100);
|
||||
|
||||
-- cleanup room table after room is destroyed
|
||||
module:hook("muc-room-destroyed",function(event)
|
||||
local room_name = jid.node(event.room.jid);
|
||||
if (poltergeists[room_name]) then
|
||||
poltergeists[room_name] = nil;
|
||||
module:hook(
|
||||
"muc-room-destroyed",
|
||||
function(event)
|
||||
poltergeist.remove_room(event.room);
|
||||
end
|
||||
end);
|
||||
);
|
||||
|
||||
--- Handles request for creating/managing poltergeists
|
||||
-- @param event the http event, holds the request query
|
||||
|
@ -375,14 +194,17 @@ function handle_create_poltergeist (event)
|
|||
|
||||
-- If the poltergiest is already in the conference then it will
|
||||
-- be in our username store and another can't be added.
|
||||
local username = get_username(room, user_id);
|
||||
if (username ~= nil
|
||||
and have_poltergeist_occupant(room, string.sub(username, 0, 8))) then
|
||||
log("warn", "poltergeist for username:%s already in the room:%s",
|
||||
username, room_name);
|
||||
local username = poltergeist.get_username(room, user_id);
|
||||
if (username ~=nil and
|
||||
poltergeist.occupies(room, poltergeist.create_nick(username))) then
|
||||
log("warn",
|
||||
"poltergeist for username:%s already in the room:%s",
|
||||
username,
|
||||
room_name
|
||||
);
|
||||
return 202;
|
||||
end
|
||||
username = generate_uuid();
|
||||
|
||||
local context = {
|
||||
user = {
|
||||
id = user_id;
|
||||
|
@ -391,99 +213,10 @@ function handle_create_poltergeist (event)
|
|||
creator_user = session.jitsi_meet_context_user;
|
||||
creator_group = session.jitsi_meet_context_group;
|
||||
};
|
||||
|
||||
local nick = string.sub(username, 0, 8)
|
||||
local presence_stanza = original_presence(
|
||||
poltergeist_component,
|
||||
room,
|
||||
nick,
|
||||
name,
|
||||
avatar,
|
||||
username,
|
||||
context,
|
||||
status
|
||||
)
|
||||
store_username(room, user_id, username);
|
||||
|
||||
room:handle_first_presence(
|
||||
prosody.hosts[poltergeist_component],
|
||||
presence_stanza
|
||||
);
|
||||
|
||||
-- the timeout before removing so participants can see the status update
|
||||
local removeTimeout = 5;
|
||||
local timeout = poltergeist_timeout - removeTimeout;
|
||||
|
||||
timer.add_task(timeout,
|
||||
function ()
|
||||
update_poltergeist_occupant_status(
|
||||
room, nick, expired_status);
|
||||
-- and remove it after some time so participant can see
|
||||
-- the update
|
||||
timer.add_task(removeTimeout,
|
||||
function ()
|
||||
if (have_poltergeist_occupant(room, nick)) then
|
||||
remove_poltergeist_occupant(room, nick, false);
|
||||
end
|
||||
end);
|
||||
end);
|
||||
|
||||
poltergeist.add_to_muc(room, user_id, name, avatar, context, status)
|
||||
return 200;
|
||||
end
|
||||
|
||||
-- Generate the original presence for a poltergeist when it is added to a room.
|
||||
-- @param component is the configured component name for poltergeist.
|
||||
-- @param room is the room the poltergeist is being added to.
|
||||
-- @param nick is the nick the poltergeist will use for xmpp.
|
||||
-- @param avatar is the url of the display avatar for the poltergeist.
|
||||
-- @param username is the poltergeist unique username.
|
||||
-- @param context is the context information from the valid auth token.
|
||||
-- @param status is the status string for the presence.
|
||||
-- @return a presence stanza
|
||||
function original_presence(
|
||||
component, room, nick, name, avatar, username, context, status)
|
||||
local p = st.presence({
|
||||
to = room.jid.."/"..nick,
|
||||
from = component.."/"..nick,
|
||||
}):tag("x", { xmlns = MUC_NS }):up();
|
||||
|
||||
p:tag("bot", { type = "poltergeist" }):up();
|
||||
p:tag("call_cancel"):text(nil):up();
|
||||
p:tag("call_id"):text(username):up();
|
||||
|
||||
if status then
|
||||
p:tag("status"):text(status):up();
|
||||
else
|
||||
p:tag("status"):text(nil):up();
|
||||
end
|
||||
|
||||
if (name) then
|
||||
p:tag(
|
||||
"nick",
|
||||
{ xmlns = "http://jabber.org/protocol/nick" }):text(name):up();
|
||||
end
|
||||
|
||||
if (avatar) then
|
||||
p:tag("avatar-url"):text(avatar):up();
|
||||
end
|
||||
|
||||
-- If the room has a password set, let the poltergeist enter using it
|
||||
local room_password = room:get_password();
|
||||
if room_password then
|
||||
local join = p:get_child("x", MUC_NS);
|
||||
join:tag("password", { xmlns = MUC_NS }):text(room_password);
|
||||
end
|
||||
|
||||
update_presence_identity(
|
||||
p,
|
||||
context.user,
|
||||
context.group,
|
||||
context.creator_user,
|
||||
context.creator_group
|
||||
);
|
||||
return p
|
||||
end
|
||||
|
||||
--- Handles request for updating poltergeists status
|
||||
-- @param event the http event, holds the request query
|
||||
-- @return GET response, containing a json with response details
|
||||
|
@ -514,7 +247,7 @@ function handle_update_poltergeist (event)
|
|||
return 404;
|
||||
end
|
||||
|
||||
local username = get_username(room, user_id);
|
||||
local username = poltergeist.get_username(room, user_id);
|
||||
if (not username) then
|
||||
return 404;
|
||||
end
|
||||
|
@ -524,12 +257,12 @@ function handle_update_poltergeist (event)
|
|||
["id"] = call_id;
|
||||
};
|
||||
|
||||
local nick = string.sub(username, 0, 8);
|
||||
if (not have_poltergeist_occupant(room, nick)) then
|
||||
local nick = poltergeist.create_nick(username);
|
||||
if (not poltergeist.occupies(room, nick)) then
|
||||
return 404;
|
||||
end
|
||||
|
||||
update_poltergeist_occupant_status(room, nick, status, call_details);
|
||||
poltergeist.update(room, nick, status, call_details);
|
||||
return 200;
|
||||
end
|
||||
|
||||
|
@ -556,17 +289,17 @@ function handle_remove_poltergeist (event)
|
|||
return 404;
|
||||
end
|
||||
|
||||
local username = get_username(room, user_id);
|
||||
local username = poltergeist.get_username(room, user_id);
|
||||
if (not username) then
|
||||
return 404;
|
||||
end
|
||||
|
||||
local nick = string.sub(username, 0, 8);
|
||||
if (not have_poltergeist_occupant(room, nick)) then
|
||||
local nick = poltergeist.create_nick(username);
|
||||
if (not poltergeist.occupies(room, nick)) then
|
||||
return 404;
|
||||
end
|
||||
|
||||
remove_poltergeist_occupant(room, nick, false);
|
||||
poltergeist.remove(room, nick, false);
|
||||
return 200;
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,372 @@
|
|||
local inspect = require("inspect")
|
||||
local jid = require("util.jid")
|
||||
local stanza = require("util.stanza")
|
||||
local timer = require("util.timer")
|
||||
local update_presence_identity = module:require("util").update_presence_identity
|
||||
local uuid = require("util.uuid")
|
||||
|
||||
local component = module:get_option_string(
|
||||
"poltergeist_component",
|
||||
module.host
|
||||
)
|
||||
|
||||
local expiration_timeout = module:get_option_string(
|
||||
"poltergeist_leave_timeout",
|
||||
30 -- defaults to 30 seconds
|
||||
)
|
||||
|
||||
local MUC_NS = "http://jabber.org/protocol/muc"
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Utility functions for commonly used poltergeist codes.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Creates a nick for a poltergeist.
|
||||
-- @param username is the unique username of the poltergeist
|
||||
-- @return a nick to use for xmpp
|
||||
local function create_nick(username)
|
||||
return string.sub(username, 0,8)
|
||||
end
|
||||
|
||||
-- Returns the last presence of the occupant.
|
||||
-- @param room the room instance where to check for occupant
|
||||
-- @param nick the nick of the occupant
|
||||
-- @return presence stanza of the occupant
|
||||
function get_presence(room, nick)
|
||||
local occupant_jid = room:get_occupant_jid(component.."/"..nick)
|
||||
if occupant_jid then
|
||||
return room:get_occupant_by_nick(occupant_jid):get_presence();
|
||||
end
|
||||
return nil;
|
||||
end
|
||||
|
||||
-- Checks for existance of a poltergeist occupant in a room.
|
||||
-- @param room the room instance where to check for the occupant
|
||||
-- @param nick the nick of the occupant
|
||||
-- @return true if occupant is found, false otherwise
|
||||
function occupies(room, nick)
|
||||
-- Find out if we have a poltergeist occupant in the room for this JID
|
||||
return not not room:get_occupant_jid(component.."/"..nick);
|
||||
end
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Username storage for poltergeist.
|
||||
--
|
||||
-- Every poltergeist will have a username stored in a table underneath
|
||||
-- the room name that they are currently active in. The username can
|
||||
-- be retrieved given a room and a user_id. The username is removed from
|
||||
-- a room by providing the room and the nick.
|
||||
--
|
||||
-- A table with a single entry looks like:
|
||||
-- {
|
||||
-- ["[hug]hostilewerewolvesthinkslightly"] = {
|
||||
-- ["655363:52148a3e-b5fb-4cfc-8fbd-f55e793cf657"] = "ed7757d6-d88d-4e6a-8e24-aca2adc31348",
|
||||
-- ed7757d6 = "655363:52148a3e-b5fb-4cfc-8fbd-f55e793cf657"
|
||||
-- }
|
||||
-- }
|
||||
--------------------------------------------------------------------------------
|
||||
-- username is the table where all poltergeist are stored
|
||||
local usernames = {}
|
||||
|
||||
-- Adds a poltergeist to the store.
|
||||
-- @param room is the room the poltergeist is being added to
|
||||
-- @param user_id is the user_id of the user the poltergeist represents
|
||||
-- @param username is the unique id of the poltergeist itself
|
||||
local function store_username(room, user_id, username)
|
||||
local room_name = jid.node(room.jid)
|
||||
|
||||
if not usernames[room_name] then
|
||||
usernames[room_name] = {}
|
||||
end
|
||||
|
||||
usernames[room_name][user_id] = username
|
||||
usernames[room_name][create_nick(username)] = user_id
|
||||
end
|
||||
|
||||
-- Retrieves a poltergeist username from the store if one exists.
|
||||
-- @param room is the room to check for the poltergeist in the store
|
||||
-- @param user_id is the user id of the user the poltergeist represents
|
||||
local function get_username(room, user_id)
|
||||
local room_name = jid.node(room.jid)
|
||||
|
||||
if not usernames[room_name] then
|
||||
return nil
|
||||
end
|
||||
|
||||
return usernames[room_name][user_id]
|
||||
end
|
||||
|
||||
local function get_username_from_nick(room_name, nick)
|
||||
if not usernames[room_name] then
|
||||
return nil
|
||||
end
|
||||
|
||||
local user_id = usernames[room_name][nick]
|
||||
return usernames[room_name][user_id]
|
||||
end
|
||||
|
||||
-- Removes the username from the store.
|
||||
-- @param room is the room the poltergeist is being removed from
|
||||
-- @param nick is the nick of the muc occupant
|
||||
local function remove_username(room, nick)
|
||||
local room_name = jid.node(room.jid);
|
||||
if not usernames[room_name] then
|
||||
return
|
||||
end
|
||||
|
||||
local user_id = usernames[room_name][nick]
|
||||
usernames[room_name][user_id] = nil
|
||||
usernames[room_name][nick] = nil
|
||||
end
|
||||
|
||||
-- Removes all poltergeists in the store for the provided room.
|
||||
-- @param room is the room all poltergiest will be removed from
|
||||
local function remove_room(room)
|
||||
local room_name = jid.node(room.jid)
|
||||
if usernames[room_name] then
|
||||
usernames[room_name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- State for toggling the tagging of presence stanzas with ignored tag.
|
||||
--
|
||||
-- A poltergeist with it's full room/nick set to ignore will have a jitsi ignore
|
||||
-- tag applied to all presence stanza's broadcasted. The following funcitons
|
||||
-- assisst in managing this state.
|
||||
--------------------------------------------------------------------------------
|
||||
local presence_ignored = {}
|
||||
|
||||
-- Sets the nick to ignored state.
|
||||
-- @param room_nick full room/nick jid
|
||||
local function set_ignored(room_nick)
|
||||
presence_ignored[room_nick] = true
|
||||
end
|
||||
|
||||
-- Resets the nick out of ignored state.
|
||||
-- @param room_nick full room/nick jid
|
||||
local function reset_ignored(room_nick)
|
||||
presence_ignored[room_nick] = nil
|
||||
end
|
||||
|
||||
-- Determines whether or not the leave presence should be tagged with ignored.
|
||||
-- @param room_nick full room/nick jid
|
||||
local function should_ignore(room_nick)
|
||||
if presence_ignored[room_nick] == nil then
|
||||
return false
|
||||
end
|
||||
return presence_ignored[room_nick]
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Poltergeist control functions for adding, updating and removing poltergeist.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Updates the status tags and call flow tags of an existing poltergeist
|
||||
-- presence.
|
||||
-- @param presence_stanza is the actual presence stanza for a poltergeist.
|
||||
-- @param status is the new status to be updated in the stanza.
|
||||
-- @param call_details is a table of call flow signal information.
|
||||
function update_presence_tags(presence_stanza, status, call_details)
|
||||
local call_cancel = false
|
||||
local call_id = nil
|
||||
|
||||
-- Extract optional call flow signal information.
|
||||
if call_details then
|
||||
call_id = call_details["id"]
|
||||
|
||||
if call_details["cancel"] then
|
||||
call_cancel = call_details["cancel"]
|
||||
end
|
||||
end
|
||||
|
||||
presence_stanza:maptags(function (tag)
|
||||
if tag.name == "status" then
|
||||
if call_cancel then
|
||||
-- If call cancel is set then the status should not be changed.
|
||||
return tag
|
||||
end
|
||||
return stanza.stanza("status"):text(status)
|
||||
elseif tag.name == "call_id" then
|
||||
if call_id then
|
||||
return stanza.stanza("call_id"):text(call_id)
|
||||
else
|
||||
-- If no call id is provided the re-use the existing id.
|
||||
return tag
|
||||
end
|
||||
elseif tag.name == "call_cancel" then
|
||||
if call_cancel then
|
||||
return stanza.stanza("call_cancel"):text("true")
|
||||
else
|
||||
return stanza.stanza("call_cancel"):text("false")
|
||||
end
|
||||
end
|
||||
return tag
|
||||
end)
|
||||
|
||||
return presence_stanza
|
||||
end
|
||||
|
||||
-- Updates the presence status of a poltergeist.
|
||||
-- @param room is the room the poltergeist has occupied
|
||||
-- @param nick is the xmpp nick of the poltergeist occupant
|
||||
-- @param status is the status string to set in the presence
|
||||
-- @param call_details is a table of call flow control details
|
||||
local function update(room, nick, status, call_details)
|
||||
local original_presence = get_presence(room, nick)
|
||||
|
||||
if not original_presence then
|
||||
module:log("info", "update issued for a non-existing poltergeist")
|
||||
return
|
||||
end
|
||||
|
||||
-- update occupant presence with appropriate to and from
|
||||
-- so we can send it again
|
||||
update_presence = stanza.clone(original_presence)
|
||||
update_presence.attr.to = room.jid.."/"..nick
|
||||
update_presence.attr.from = component.."/"..nick
|
||||
|
||||
update_presence = update_presence_tags(update_presence, status, call_details)
|
||||
|
||||
module:log("info", "updating poltergeist: %s/%s - %s", room, nick, status)
|
||||
room:handle_normal_presence(
|
||||
prosody.hosts[component],
|
||||
update_presence
|
||||
)
|
||||
end
|
||||
|
||||
-- Removes the poltergeist from the room.
|
||||
-- @param room is the room the poltergeist has occupied
|
||||
-- @param nick is the xmpp nick of the poltergeist occupant
|
||||
-- @param ignore toggles if the leave subsequent leave presence should be tagged
|
||||
local function remove(room, nick, ignore)
|
||||
local original_presence = get_presence(room, nick);
|
||||
if not original_presence then
|
||||
module:log("info", "attempted to remove a poltergeist with no presence")
|
||||
return
|
||||
end
|
||||
|
||||
local leave_presence = stanza.clone(original_presence)
|
||||
leave_presence.attr.to = room.jid.."/"..nick
|
||||
leave_presence.attr.from = component.."/"..nick
|
||||
leave_presence.attr.type = "unavailable"
|
||||
|
||||
if (ignore) then
|
||||
set_ignored(room, nick)
|
||||
end
|
||||
|
||||
remove_username(room, nick)
|
||||
module:log("info", "removing poltergeist: %s/%s", room, nick)
|
||||
room:handle_normal_presence(
|
||||
prosody.hosts[component],
|
||||
leave_presence
|
||||
)
|
||||
end
|
||||
|
||||
-- Adds a poltergeist to a muc/room.
|
||||
-- @param room is the room the poltergeist will occupy
|
||||
-- @param is the id of the user the poltergeist represents
|
||||
-- @param display_name is the display name to use for the poltergeist
|
||||
-- @param avatar is the avatar link used for the poltergeist display
|
||||
-- @param context is the session context of the user making the request
|
||||
-- @param status is the presence status string to use
|
||||
local function add_to_muc(room, user_id, display_name, avatar, context, status)
|
||||
local username = uuid.generate()
|
||||
local presence_stanza = original_presence(
|
||||
room,
|
||||
username,
|
||||
display_name,
|
||||
avatar,
|
||||
context,
|
||||
status
|
||||
)
|
||||
store_username(room, user_id, username)
|
||||
module:log("info", "adding poltergeist: %s/%s", room, create_nick(username))
|
||||
room:handle_first_presence(
|
||||
prosody.hosts[component],
|
||||
presence_stanza
|
||||
)
|
||||
local remove_delay = 5
|
||||
local expiration = expiration_timeout - remove_delay;
|
||||
local nick = create_nick(username)
|
||||
timer.add_task(
|
||||
expiration,
|
||||
function ()
|
||||
update(room, nick, "expired")
|
||||
timer.add_task(
|
||||
remove_delay,
|
||||
function ()
|
||||
if occupies(room, nick) then
|
||||
remove(room, nick, false)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
-- Generates an original presence for a new poltergeist
|
||||
-- @param room is the room the poltergeist will occupy
|
||||
-- @param username is the unique name for the poltergeist
|
||||
-- @param display_name is the display name to use for the poltergeist
|
||||
-- @param avatar is the avatar link used for the poltergeist display
|
||||
-- @param context is the session context of the user making the request
|
||||
-- @param status is the presence status string to use
|
||||
-- @return a presence stanza that can be used to add the poltergeist to the muc
|
||||
function original_presence(room, username, display_name, avatar, context, status)
|
||||
local nick = create_nick(username)
|
||||
local p = stanza.presence({
|
||||
to = room.jid.."/"..nick,
|
||||
from = component.."/"..nick,
|
||||
}):tag("x", { xmlns = MUC_NS }):up();
|
||||
|
||||
p:tag("bot", { type = "poltergeist" }):up();
|
||||
p:tag("call_cancel"):text(nil):up();
|
||||
p:tag("call_id"):text(username):up();
|
||||
|
||||
if status then
|
||||
p:tag("status"):text(status):up();
|
||||
else
|
||||
p:tag("status"):text(nil):up();
|
||||
end
|
||||
|
||||
if display_name then
|
||||
p:tag(
|
||||
"nick",
|
||||
{ xmlns = "http://jabber.org/protocol/nick" }):text(display_name):up();
|
||||
end
|
||||
|
||||
if avatar then
|
||||
p:tag("avatar-url"):text(avatar):up();
|
||||
end
|
||||
|
||||
-- If the room has a password set, let the poltergeist enter using it
|
||||
local room_password = room:get_password();
|
||||
if room_password then
|
||||
local join = p:get_child("x", MUC_NS);
|
||||
join:tag("password", { xmlns = MUC_NS }):text(room_password);
|
||||
end
|
||||
|
||||
update_presence_identity(
|
||||
p,
|
||||
context.user,
|
||||
context.group,
|
||||
context.creator_user,
|
||||
context.creator_group
|
||||
)
|
||||
return p
|
||||
end
|
||||
|
||||
return {
|
||||
get_username = get_username,
|
||||
get_username_from_nick = get_username_from_nick,
|
||||
occupies = occupies,
|
||||
remove_room = remove_room,
|
||||
reset_ignored = reset_ignored,
|
||||
should_ignore = should_ignore,
|
||||
create_nick = create_nick,
|
||||
add_to_muc = add_to_muc,
|
||||
update = update,
|
||||
remove = remove
|
||||
}
|
Loading…
Reference in New Issue