Get the list of rooms

I’ll post my progress there.

I was thinking about this too… but I am not experienced in prosody/xmpp area… my thinking was as there are event handlers (for user_join,user_leave,mute/unmute and others) so we can maintain a database of live/prev information in server side triggered by events. there will be tables for live rooms and information of that specific room in that table and when the room is destroyed it will be copied to another database file where all previous data are saved and removed from the current one. by these format we can just check the live database for live rooms info and prev database for previous info’s. It was just a thinking… and easier way who has not a great understanding server side components…

1 Like

For now that i have enabled Secure Domain :

secure domain : authentication = internal_plain
=> get-info-rooms is empty
if i switch to token => get-info-rooms is good but guest are asked for login !

myDomain.cfg.lua

VirtualHost "myDomain"
        allow_registration = true
        -- enabled = false -- Remove this line to enable this host
--        authentication = "token"
        authentication = "internal_plain" //secure domain 
        -- Properties below are modified by jitsi-meet-tokens package config
        -- and authentication above is switched to "token"
        app_id="xxx"
        app_secret="xxx"
        allow_empty_token = true
        -- Assign this host a certificate for TLS, otherwise it would use the one
        -- set in the global section (if any).
        -- Note that old-style SSL on port 5223 only supports one certificate, and will always
        -- use the global one.
        ssl = {
                key = "/etc/prosody/certs/myDomain.key";
                certificate = "/etc/prosody/certs/myDomain.crt";
        }
        -- we need bosh
        modules_enabled = {
            "bosh";
            "pubsub";
            "ping"; -- Enable mod_ping
            "muc_size";
        }
        consider_bosh_secure = true;
        c2s_require_encryption = false
 muc_mapper_domain_prefix = "muc"
 muc_domain_prefix = "muc"

localhost.cfg.lua

VirtualHost "localhost"
        app_id="xxx"
        app_secret="xxx"
--        authentication = "token"  //if enable : get-info-rooms work, but guest are asked for login !
                                                  //if disable : guest are not asked for login but get-info-rooms is empty
        modules_enabled = {
            "muc_size";
        }
 muc_mapper_domain_prefix = "muc"
 muc_domain_prefix = "muc"

idea ?

Ideally someone from the team could add a status log message with “number of rooms” and “number of endpoints” maybe also “number of endpoints with video”.
Then this script could be pretty stable, otherwise you need to track join and leave, which sounds pretty messy.

urs is working fine I think? where is the pb?

Here is a working code to get room names and participants

-- Prosody IM
-- Copyright (C) 2017 Atlassian
--

local jid = require "util.jid";
local it = require "util.iterators";
local json = require "util.json";
local iterators = require "util.iterators";
local array = require"util.array";
local wrap_async_run = module:require "util".wrap_async_run;

local tostring = tostring;
local neturl = require "net.url";
local parse = neturl.parseQuery;

-- option to enable/disable room API token verifications
local enableTokenVerification
    = module:get_option_boolean("enable_roomsize_token_verification", false);

local token_util = module:require "token/util".new(module);
local get_room_from_jid = module:require "util".get_room_from_jid;

-- no token configuration but required
if token_util == nil and enableTokenVerification then
    log("error", "no token configuration but it is required");
    return;
end

-- required parameter for custom muc component prefix,
-- defaults to "conference"
local muc_domain_prefix
    = module:get_option_string("muc_mapper_domain_prefix", "conference");

--- Verifies room name, domain name with the values in the token
-- @param token the token we received
-- @param room_address the full room address jid
-- @return true if values are ok or false otherwise
function verify_token(token, room_address)
    if not enableTokenVerification then
        return true;
    end

    -- if enableTokenVerification is enabled and we do not have token
    -- stop here, cause the main virtual host can have guest access enabled
    -- (allowEmptyToken = true) and we will allow access to rooms info without
    -- a token
    if token == nil then
        log("warn", "no token provided");
        return false;
    end

    local session = {};
    session.auth_token = token;
    local verified, reason = token_util:process_and_verify_token(session);
    if not verified then
        log("warn", "not a valid token %s", tostring(reason));
        return false;
    end

for k,v in pairs(session) do
		            log("info", "key %s", tostring(k));
		            log("info", "value %s", tostring(v));
		        end
		        
		        
    if not token_util:verify_room(session, room_address) then
        log("warn", "Token %s not allowed to join: %s",
            tostring(token), tostring(room_address));
        return false;
    end

    return true;
end

function verify_token_getrooms(token)
    if not enableTokenVerification then
        return true;
    end

    -- if enableTokenVerification is enabled and we do not have token
    -- stop here, cause the main virtual host can have guest access enabled
    -- (allowEmptyToken = true) and we will allow access to rooms info without
    -- a token
    if token == nil then
        log("warn", "no token provided");
        return false;
    end

    local session = {};
    session.auth_token = token;
    local verified, reason = token_util:process_and_verify_token(session);
    if not verified then
        log("warn", "not a valid token %s", tostring(reason));
        return false;
    end

		        
	
	if (tostring(session.jitsi_meet_room) ~= "modrooms") then 
    
        log("warn", "Token %s %s not allowed to join modrooms",
            tostring(token), tostring(session.jitsi_meet_room));
        return false;
    end

    return true;
end





--- Handles request for retrieving the room size
-- @param event the http event, holds the request query
-- @return GET response, containing a json with participants count,
--         tha value is without counting the focus.
function handle_get_room_size(event)
    if (not event.request.url.query) then
        return 400;
    end

	local params = parse(event.request.url.query);
	local room_name = params["room"];
	local domain_name = params["domain"];
    local subdomain = params["subdomain"];

    local room_address
        = jid.join(room_name, muc_domain_prefix.."."..domain_name);

    if subdomain and subdomain ~= "" then
        room_address = "["..subdomain.."]"..room_address;
    end

    if not verify_token(params["token"], room_address) then
        return 403;
    end

	local room = get_room_from_jid(room_address);
	local participant_count = 0;

	log("debug", "Querying room %s", tostring(room_address));

	if room then
		local occupants = room._occupants;
		if occupants then
			participant_count = iterators.count(room:each_occupant());
		end
		log("debug",
            "there are %s occupants in room", tostring(participant_count));
	else
		log("debug", "no such room exists");
		return 404;
	end

	if participant_count > 1 then
		participant_count = participant_count - 1;
	end

	return [[{"participants":]]..participant_count..[[}]];
end

--- Handles request for retrieving the room participants details
-- @param event the http event, holds the request query
-- @return GET response, containing a json with participants details
function handle_get_room (event)
    if (not event.request.url.query) then
        return 400;
    end

	local params = parse(event.request.url.query);
	local room_name = params["room"];
	local domain_name = params["domain"];
    local subdomain = params["subdomain"];
    local room_address
        = jid.join(room_name, muc_domain_prefix.."."..domain_name);

    if subdomain ~= "" then
        room_address = "["..subdomain.."]"..room_address;
    end

    if not verify_token(params["token"], room_address) then
        return 403;
    end

	local room = get_room_from_jid(room_address);
	local participant_count = 0;
	local occupants_json = array();

	log("debug", "Querying room %s", tostring(room_address));

	if room then
		local occupants = room._occupants;
		if occupants then
			participant_count = iterators.count(room:each_occupant());
			for _, occupant in room:each_occupant() do
			    -- filter focus as we keep it as hidden participant
			    if string.sub(occupant.nick,-string.len("/focus"))~="/focus" then
				    for _, pr in occupant:each_session() do
					local nick = pr:get_child_text("nick", "http://jabber.org/protocol/nick") or "";
					local email = pr:get_child_text("email") or "";
					occupants_json:push({
					    jid = tostring(occupant.nick),
					    email = tostring(email),
					    display_name = tostring(nick)});
				    end
			    end
			end
		end
		log("debug",
            "there are %s occupants in room", tostring(participant_count));
	else
		log("debug", "no such room exists");
		return 404;
	end

	if participant_count > 1 then
		participant_count = participant_count - 1;
	end

	return json.encode(occupants_json);
end;


    
    
function tablelength(T)
        local hash = {}
        local res = {}

        for _,v in ipairs(T) do
           if (not hash[v]) then
               res[#res+1] = v
               hash[v] = true
           end
        end

        local count = 0
        for _ in pairs(res) do count = count + 1 end
        return count
end

local function has_value (tab, val)
    for index, value in ipairs(tab) do
        if value == val then
            return true
        end
    end

    return false
end

function getNbConfPart(event) 
	
	local params = parse(event.request.url.query);
	
    local tab={};
	local jitsi_meet_room = "";
	local nbPart = 0
	
	
	if not verify_token_getrooms(params["token"]) then
        return 403;
    end
    
    
    --conference rooms count (with eliminating duplicates )
    for key, value in pairs(prosody.full_sessions) do
            if (tostring(value["username"]:lower()) ~= "focus" and tostring(value["username"]:lower()) ~= "jibri" and value["jitsi_meet_room"] ~= nil) then
	            log("info", "room %s", tostring(value));
	            
--	            for k,v in pairs(value) do
--		            log("info", "key %s", tostring(k));
--		            log("info", "value %s", tostring(v));
--		        end
		        jitsi_meet_room = tostring(value["jitsi_meet_room"]:lower());
		        nbPart = nbPart + 1;
		        
		        if (has_value (tab, jitsi_meet_room)) then
			    	log("info", "room %s already in table", tostring(jitsi_meet_room));
			    else
                    table.insert(tab,jitsi_meet_room);
                   
                end
            end
    end

 

    local nbConf = tablelength(tab) --conferences count
    if nbPart == 0
            then nbConf=0
    end

     --create result as json
    local result_json={
                    participants= nbPart,
                    roomscount = nbConf,
                    rooms = tab
                    
            };

    -- create json response 
    return json.encode(result_json);
end

function module.load()
    module:depends("http");
	module:provides("http", {
		default_path = "/";
		route = {
			["GET room-size"] = function (event) return wrap_async_run(event,handle_get_room_size) end;
			["GET sessions"] = function () return tostring(it.count(it.keys(prosody.full_sessions))); end;
			["GET room"] = function (event) return wrap_async_run(event,handle_get_room) end;
			["GET nbConfPart"] = function (event) return wrap_async_run(event,getNbConfPart) end;
		};
	});
end

If using token authentication, the room name in JWT has to be “modrooms”

1 Like

now i have update to JVB2 i get this in the log (tried to update prosody to 11.5 but no effect)

Apr 01 08:47:27 http.server     error   Traceback[httpserver]: /usr/share/jitsi-meet/prosody-plugins/mod_muc_size.lua:259: attempt to call upvalue 'wrap_async_run' (a nil value)
stack traceback:
        /usr/share/jitsi-meet/prosody-plugins/mod_muc_size.lua:259: in function '?'
        /usr/lib/prosody/util/events.lua:79: in function </usr/lib/prosody/util/events.lua:75>
        (...tail calls...)
        /usr/lib/prosody/net/http/server.lua:248: in function </usr/lib/prosody/net/http/server.lua:176>
        [C]: in function 'xpcall'
        /usr/lib/prosody/net/http/server.lua:108: in function 'process_next'
        /usr/lib/prosody/net/http/server.lua:124: in function 'success_cb'
        /usr/lib/prosody/net/http/parser.lua:177: in function 'feed'
        /usr/lib/prosody/net/http/server.lua:155: in function </usr/lib/prosody/net/http/server.lua:154>
        (...tail calls...)
        /usr/lib/prosody/net/server_select.lua:915: in function </usr/lib/prosody/net/server_select.lua:899>
        [C]: in function 'xpcall'
        /usr/bin/prosody:76: in function 'loop'
        /usr/bin/prosody:86: in main chunk
        [C]: in ?

There seems to be some changes, replace the following code.

function tablelength(T)
        local hash = {}
        local res = {}

        for _,v in ipairs(T) do
           if (not hash[v]) then
               res[#res+1] = v
               hash[v] = true
           end
        end

        local count = 0
        for _ in pairs(res) do count = count + 1 end
        return count
end

local function has_value (tab, val)
    for index, value in ipairs(tab) do
        if value == val then
            return true
        end
    end

    return false
end

function getNbConfPart(event) 
	
	local params = parse(event.request.url.query);
	
    local tab={};
	local jitsi_meet_room = "";
	local nbPart = 0
	local roomscount = 0;
	
	
	if not verify_token_getrooms(params["token"]) then
        return 403;
    end
    
    
    --conference rooms count (with eliminating duplicates )
    for key, value in pairs(prosody.full_sessions) do
            if (tostring(value["username"]:lower()) ~= "focus" and tostring(value["username"]:lower()) ~= "jibri" and value["jitsi_meet_room"] ~= nil) then
	            log("info", "room %s", tostring(value));
	            
--	            for k,v in pairs(value) do
--		            log("info", "key %s", tostring(k));
--		            log("info", "value %s", tostring(v));
--		        end
		        jitsi_meet_room = tostring(value["jitsi_meet_room"]:lower());
		        nbPart = nbPart + 1;
		        
		        if (tab[tostring(jitsi_meet_room)]  ~= nil) then
					tab[tostring(jitsi_meet_room)] = tab[tostring(jitsi_meet_room)] + 1;
			    	log("info", "room %s already in table", tostring(jitsi_meet_room));
			        
			    else
                    
			        tab[tostring(jitsi_meet_room)] = 1
					roomscount = roomscount + 1;
                end
            end
    end

 

    

     --create result as json
    local result_json={
                    participants= nbPart,
                    roomscount = roomscount,
                    rooms = tab
                    
            };

    -- create json response 
    return { status_code = 200; body = json.encode(result_json); };
end

function module.load()
    module:depends("http");
	module:provides("http", {
		default_path = "/";
		route = {
			["GET room-size"] = function (event) return async_handler_wrapper(event,handle_get_room_size) end;
			["GET sessions"] = function () return tostring(it.count(it.keys(prosody.full_sessions))); end;
			["GET room"] = function (event) return async_handler_wrapper(event,handle_get_room) end;
			["GET nbConfPart"] = function (event) return async_handler_wrapper(event,getNbConfPart) end;
		};
	});
end
1 Like

Hi rubenm,

I tried your code, and I think value["jitsi_meet_room"] is not used anymore.
I might be wrong, but `/sessions’ give the right amount of users (off by 2) but nbConfPart gives none.

You are right, value[“jitsi_bosh_query_room”] seems to be correct.

1 Like

Any updates here? It doesnt work for me!
This is my setup:

  • Ubuntu 18.04.4 LTS.
  • Prosody 0.11.5
  • Lua 5.2
  • LuaRocks 2.4.2

Installed rocks:
basexx
0.4.1-1 (installed) - /usr/local/lib/luarocks/rocks
lbase64
20120807-3 (installed) - /usr/local/lib/luarocks/rocks
lua-cjson
2.1.0-1 (installed) - /usr/local/lib/luarocks/rocks
luacrypto
0.3.2-2 (installed) - /usr/local/lib/luarocks/rocks
luajwtjitsi
1.3-7 (installed) - /usr/local/lib/luarocks/rocks
net-url
0.9-1 (installed) - /usr/local/lib/luarocks/rocks

Everything works fine in muc_size. But with the lates posted version of getNbConfPart I am not able to fetch the data. I always get:

{“rooms”:{},“participants”:0,“roomscount”:0}

I tried to uncomment the commented logging part:

			for k,v in pairs(value) do
				log("info", "key %s", tostring(k));
				log("info", "value %s", tostring(v));
			end

If i look in my logs and grab for my roomname i got no match.
Many keys and values are logged but no roomname.

There is no key like “jitsi_bosh_query_room” or “jitsi_meet_room” although there should be atleast one. The outputs of all other endpoints in muc_size are fine. The room endpoint and the room-size endpoint are working well. So the internal call get_room_from_jid(room_address); returns the correct room.

How is the roomname addressed in full_sessions object?

Ok, I solved it on my own but took a different approach.

I inspected the folowing files:
https://github.com/jitsi/jitsi-meet/blob/master/resources/prosody-plugins/util.lib.lua
https://github.com/bjc/prosody/blob/master/plugins/muc/mod_muc.lua

I saw that get_room_from_jid in jitsi-meets util.lib.lua was implmented by calling get_room_from_jid from MUC API (>0.10). So i opened prosodys mod_muc.lua and found this method. But I found another method, too. It was all_rooms! And it returns a list of all rooms. Each room object in this list is holding an occupants object and this what the builtin method handle_get_room is inspecting. (Take a look the orignial muc_size module)

So I tried a little bit and got it:

	function get_raw_rooms(ahost)
		local component = hosts[ahost];
		if component then
			local muc = component.modules.muc
			if muc and rawget(muc,"all_rooms") then
				return muc.all_rooms();
			end
		end
	end
	
	function handle_get_all_rooms(event)
		if (not event.request.url.query) then
			return { status_code = 400; };
		end
	
		local params = parse(event.request.url.query);
		local domain_name = params["domain"];
	
		local raw_rooms = get_raw_rooms(domain_name);
	
		local rooms_json = array();
	
		for room in raw_rooms do
	
			local room_jid = room.jid;
			local participant_count = 0;
			local occupants_json = array();
			local occupants = room._occupants;
			if occupants then
				for _, occupant in room:each_occupant() do
					-- filter focus as we keep it as hidden participant
					if string.sub(occupant.nick,-string.len("/focus"))~="/focus" then
						for _, pr in occupant:each_session() do
							participant_count = participant_count + 1;
							local nick = pr:get_child_text("nick", "http://jabber.org/protocol/nick") or "";
							local email = pr:get_child_text("email") or "";
							occupants_json:push({
								jid = tostring(occupant.nick),
								email = tostring(email),
								display_name = tostring(nick)});
						end
					end
				end
			end
	
			rooms_json:push({
				jid = room_jid,
				participant_count = participant_count,
				participants = occupants_json
			});
	
		end
	
		local result_json={
			rooms = rooms_json;
		};
		-- create json response
		return { status_code = 200; body = json.encode(result_json); };
	end

Now the new method needs to be routed:

function module.load()
	module:depends("http");
	module:provides("http", {
		default_path = "/";
		route = {
			["GET room-size"] = function (event) return async_handler_wrapper(event,handle_get_room_size) end;
			["GET sessions"] = function () return tostring(it.count(it.keys(prosody.full_sessions))); end;
			["GET room"] = function (event) return async_handler_wrapper(event,handle_get_room) end;
			["GET all-rooms"] = function (event) return async_handler_wrapper(event,handle_get_all_rooms) end;
		};
	});
end

I am able to receive the list of all rooms by curl request:

curl 'http://localhost:5280/all-rooms?domain=conference.jitsi-meet.example.com' -i

Be careful! Although my domain is jitsi-meet.example… I need to request conference.jitsi-meet.example… because my get_raw_rooms function is not respecting the muc_mapper_domain_prefix. This needs to be implemented like jitsi-meets util.lib.lua did.

I hope this helps anybody and maybe it will find its way into the jitsi source code or maybe we can offer a module by ourself.

4 Likes

Great job tmaass, i have open a new post with some improvment

next step, finding a dump of prosody.full_session…

Please add full tutorial step by step, how to make this thing work.

New Error When I try to install: luajwtjitsi

Error: Failed installing dependency: http://rocks.moonscript.org/luacrypto-0.3.2-2.src.rock - Build error: Failed compiling object src/lcrypto.o

And when i try to install luacrypto:
Error: Build error: Failed compiling object src/lcrypto.o

luacrypto 0.3.2-2 depends on lua >= 5.1 (5.2-1 provided by VM)

Please look at my setup. I struggled around with that, too. Finally I had success with that setup above.

I edited the code a bit. If someone has use for this, here it is. Comments welcome.

local log = module._log;
local async_handler_wrapper = module:require "util".async_handler_wrapper;
local json = require "util.json";

function getNbConfPart()
    local result={};

    for key, value in pairs(prosody.full_sessions) do
            if value["jitsi_bosh_query_room"] then
                if result[value["jitsi_bosh_query_room"]] then
                        result[value["jitsi_bosh_query_room"]] = result[value["jitsi_bosh_query_room"]] + 1;
                else
                        result[value["jitsi_bosh_query_room"]] = 1;
                end
            end
    end

    return { status_code = 200; body = json.encode(result); };
end

function module.load()
    module:depends("http");
        module:provides("http", {
                default_path = "/";
                route = {
                        ["GET nbConfPart"] = function (event) return async_handler_wrapper(event,getNbConfPart) end;
                };
        });
end
1 Like

Hi, I also installed JVB2 and enabled jwt auth, got the same problem

with prosody err :Apr 01 08:47:27 http.server error Traceback[httpserver]: /usr/share/jitsi-meet/prosody-plugins/mod_muc_size.lua:259: attempt to call upvalue ‘wrap_async_run’ (a nil value)
stack traceback:

How do you solve it?

https://example.com/sessions (it’s OK ,and return 2)
https://example.com/room-info?room=123 (return 500 Internal Server Error)

Is there a way to get this info from command line on the server where it’s running? Sounds like a lot of hassle to go through to get this info.

Yes, you can execute a curl from command line on your local machine:

 # curl 'http://localhost:5280/all-rooms?domain=conference.your.domain.com'

But you need to activate your module with the posted modifications before.
If you want just to use it like this you need not to route your ngix to make the request accessable from outside.