Mod_muc_size module problem in docker using lib-jitsi-meet

It’s ok, waiting :heart:

@Fuji

Hello. I had same issue, but I finally figured out how to work room-size by using compose ( https://github.com/jitsi/docker-jitsi-meet ) .

  1. Please set meet.conf of /room-size as below:
    location /room-size {
        proxy_pass http://xmpp.meet.jitsi:5280/room-size?$args&domain=meet.jitsi;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host meet.jitsi;
    }
  1. Add below line to prosody.cfg.lua (because default value of muc_mapper_domain_prefix is different from compose’s config! ) :
    muc_mapper_domain_prefix = "muc"

I hope I can help you.

1 Like

I will give a try soon and really thanx for sharing :heart:

@jenus

muc_mapper_domain_prefix = "muc"

is this because of the default value “conference” ? and domain = meet.jitsi completely make sense :heart: and I solved a issue with changing domain name and muc domain (libjitsi meet api calling to docker jitsi server)
but I am stille having problem,same error as before…

here is my meet.conf in /web/nginx/ :
server_name _;

client_max_body_size 0;

root /usr/share/jitsi-meet;
index index.html
error_page 404 /static/404.html;

location ~ ^/([a-zA-Z0-9=\?]+)$ {
    rewrite ^/(.*)$ / break;
}

location /config.js {
    alias /config/config.js;
}

location /interface_config.js {
    alias /config/interface_config.js;
}

location /external_api.js {
    alias /usr/share/jitsi-meet/libs/external_api.min.js;
}

location / {
    ssi on;
}

# BOSH
location /http-bind {

    
#    add_header 'Access-Control-Allow-Methods' '*';
#    add_header 'Access-Control-Allow-Headers' '*';
#    add_header 'Access-Control-Allow-Origin' '*';
 
    
    proxy_pass http://xmpp.meet.jitsi:5280/http-bind;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Host meet.jitsi;

}


location /room-size {
        proxy_pass http://xmpp.meet.jitsi:5280/room-size?$args&domain=meet.jitsi;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host meet.jitsi;
    }

here is my prosody.cfg.lua in /prosody/ :
admins = { }

-- Enable use of libevent for better performance under high load
-- For more information see: http://prosody.im/doc/libevent
--use_libevent = true;

-- This is the list of modules Prosody will load on startup.
-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
-- Documentation on modules can be found at: http://prosody.im/doc/modules
modules_enabled = {
		"muc_size";
	-- Generally required
		"roster"; -- Allow users to have a roster. Recommended ;)
		"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
		"tls"; -- Add support for secure TLS on c2s/s2s connections
		"dialback"; -- s2s dialback support
		"disco"; -- Service discovery

	-- Not essential, but recommended
		"private"; -- Private XML storage (for room bookmarks, etc.)
		"vcard"; -- Allow users to set vCards
	
	-- These are commented by default as they have a performance impact
		--"privacy"; -- Support privacy lists
		--"compression"; -- Stream compression (Debian: requires lua-zlib module to work)

	-- Nice to have
		"version"; -- Replies to server version requests
		"uptime"; -- Report how long server has been running
		"time"; -- Let others know the time here on this server
		"ping"; -- Replies to XMPP pings with pongs
		"pep"; -- Enables users to publish their mood, activity, playing music and more
		"register"; -- Allow users to register on this server using a client and change passwords

	-- Admin interfaces
		"admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
		--"admin_telnet"; -- Opens telnet console interface on localhost port 5582
	
	-- HTTP modules
		--"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
		--"http_files"; -- Serve static files from a directory over HTTP

	-- Other specific functionality
		"posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
		--"groups"; -- Shared roster support
		--"announce"; -- Send announcement to all online users
		--"welcome"; -- Welcome users who register accounts
		--"watchregistrations"; -- Alert admins of registrations
		--"motd"; -- Send a message to users when they log in
		--"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
        
};

https_ports = { }

-- These modules are auto-loaded, but should you want
-- to disable them then uncomment them here:
modules_disabled = {
	-- "offline"; -- Store offline messages
	-- "c2s"; -- Handle client connections
	-- "s2s"; -- Handle server-to-server connections
};

enable_roomsize_token_verification=false;

-- Disable account creation by default, for security
-- For more information see http://prosody.im/doc/creating_accounts
allow_registration = false;

daemonize = false;

pidfile = "/config/data/prosody.pid";


-- Force clients to use encrypted connections? This option will
-- prevent clients from authenticating unless they are using encryption.

c2s_require_encryption = false;


--The traditional solution to solving the same-origin problem is to have the web server that serves the app script also act as a proxy to the real BOSH server at some URL.
consider_bosh_secure = true

--There is a relatively new HTTP extension that allows web servers to tell browsers that cross-domain requests are ok and permitted, called CORS.
cross_domain_bosh = true

-- Force certificate authentication for server-to-server connections?
-- This provides ideal security, but requires servers you communicate
-- with to support encryption AND present valid, trusted certificates.
-- NOTE: Your version of LuaSec must support certificate verification!
-- For more information see http://prosody.im/doc/s2s#security

s2s_secure_auth = false;

-- Many servers don't support encryption or have invalid or self-signed
-- certificates. You can list domains here that will not be required to
-- authenticate using certificates. They will be authenticated using DNS.

--s2s_insecure_domains = { "gmail.com" }

-- Even if you leave s2s_secure_auth disabled, you can still require valid
-- certificates for some domains by specifying a list here.

--s2s_secure_domains = { "jabber.org" }

-- Select the authentication backend to use. The 'internal' providers
-- use Prosody's configured data storage to store the authentication data.
-- To allow Prosody to offer secure authentication mechanisms to clients, the
-- default provider stores passwords in plaintext. If you do not trust your
-- server please see http://prosody.im/doc/modules/mod_auth_internal_hashed
-- for information about using the hashed backend.

authentication = "internal_plain";

-- Select the storage backend to use. By default Prosody uses flat files
-- in its configured data directory, but it also supports more backends
-- through modules. An "sql" backend is included by default, but requires
-- additional dependencies. See http://prosody.im/doc/storage for more info.

--storage = "sql" -- Default is "internal" (Debian: "sql" requires one of the
-- lua-dbi-sqlite3, lua-dbi-mysql or lua-dbi-postgresql packages to work)

-- For the "sql" backend, you can uncomment *one* of the below to configure:
--sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename.
--sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
--sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }

-- Logging configuration
-- For advanced logging see http://prosody.im/doc/logging
--
-- Debian:
--  Logs info and higher to /var/log
--  Logs errors to syslog also

muc_mapper_domain_prefix = "muc"



log = {
	{ levels = {min = "info"}, to = "console"};
}



component_interface = { "*" }

data_path = "/config/data"

Include "conf.d/*.cfg.lua"
here is my mod_mic_size.lua :
-- 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

    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

--- 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

function getNbConfPart() 
    local tab={};

    --conferences count (without eliminating duplicates )
    for key, value in pairs(prosody.full_sessions) do
            if tostring(value["username"]:lower()) ~= "focus" then
                    table.insert(tab,tostring(value["jitsi_meet_room"]:lower()));
            end
    end

    local nbPart = 0
    if it.count(it.keys(prosody.full_sessions))-1 >= 0 --participants count
            then nbPart= it.count(it.keys(prosody.full_sessions))-1
    end

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

     --create result as json
    local result_json=array();
    result_json:push({
                    part= nbPart,
                    conf=tablelength(tab) -- eliminates count number of rooms
            });

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

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

function get_info_rooms()
   local innerValues = {};
   local keyRoom = "jitsi_bosh_query_room";
   for _, value in pairs(prosody.full_sessions) do
       if tostring(value["username"]:lower()) ~= "focus" then
           for innerKey, innerValue in pairs(value) do
               if innerKey == keyRoom and not has_value(innerValues, innerValue) then
                   table.insert(innerValues, innerValue)
               end
           end
       end
   end
return json.encode(innerValues);
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;
                       ["GET get-info-rooms"] = function (event) return wrap_async_run(event,get_info_rooms) end;
		};
	});
end

It would be nice to see urs…!
I hosted the server in https://fuji where I set fuji for 192.168.0.149 (my local ip) in /etc/hosts
I even commented out the proxy passing things like commenting “location /room-size{}” but I am still getting same result :confused: I am really confused here… is the proxy passing thing even working? or it just outside the docker network? how can I check?
I am getting results by going to
192.168.0.149:5280/sessions or 192.168.0.149:5280/room-size?room=aa
by going to “https://fuji/sessions” or “https://fuji/room-size?room=aa” doesnt work (this should work…!)
or can u just elaborate what u did as it would be really great to know the details :heart:
Thanx in advance :sparkling_heart:

may be I did something wrong…! but now “/room-size” is working :heart:
muc
but /room or others isnt working… is this the write way to do it?

my meet.conf now :
server_name _;

client_max_body_size 0;

root /usr/share/jitsi-meet;
index index.html
error_page 404 /static/404.html;

location ~ ^/([a-zA-Z0-9=\?]+)$ {
    rewrite ^/(.*)$ / break;
}

location /config.js {
    alias /config/config.js;
}

location /interface_config.js {
    alias /config/interface_config.js;
}

location /external_api.js {
    alias /usr/share/jitsi-meet/libs/external_api.min.js;
}

location / {
    ssi on;
}

# BOSH
location /http-bind {

    
#    add_header 'Access-Control-Allow-Methods' '*';
#    add_header 'Access-Control-Allow-Headers' '*';
#    add_header 'Access-Control-Allow-Origin' '*';
 
    
    proxy_pass http://xmpp.meet.jitsi:5280/http-bind;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Host meet.jitsi;
    


}

location /room-size {
        
	proxy_pass http://xmpp.meet.jitsi:5280/room-size?$args&domain=meet.jitsi;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host meet.jitsi;

    }

location /room {
        
	proxy_pass http://xmpp.meet.jitsi:5280/room?$args&domain=meet.jitsi;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host meet.jitsi;

    }

location /nbConfPart {
        
	proxy_pass http://xmpp.meet.jitsi:5280/nbConfPart;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host meet.jitsi;

    }

location /get-info-rooms {
        
	proxy_pass http://xmpp.meet.jitsi:5280/get-info-rooms;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host meet.jitsi;

    }

location /sessions {
        
	proxy_pass http://xmpp.meet.jitsi:5280/sessions;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host meet.jitsi;

    }

Nearly same problem here

  • /sessions => ok
  • /get-info-room => ok
  • /nbConfPart => 500

my prosody err log :

runnerdz-06dQP  error   Encountered error: /usr/share/jitsi-meet/prosody-plugins/mod_muc_size.lua:201: attempt to index field 'jitsi_meet_room' (a nil value)
stack traceback:
    /usr/share/jitsi-meet/prosody-plugins/mod_muc_size.lua:201: in function 'handler'
    /usr/share/jitsi-meet/prosody-plugins/util.lib.lua:69: in function 'func'
    /usr/lib/prosody/util/async.lua:127: in function </usr/lib/prosody/util/async.lua:125>
Mar 19 15:46:09 http.server     error   Traceback[httpserver]: /usr/lib/prosody/util/async.lua:137: /usr/share/jitsi-meet/prosody-plugins/mod_muc_size.lua:201: attempt to index field 'jitsi_meet_room' (a
nil value)
stack traceback:
    /usr/share/jitsi-meet/prosody-plugins/mod_muc_size.lua:201: in function 'handler'
    /usr/share/jitsi-meet/prosody-plugins/util.lib.lua:69: in function 'func'
    /usr/lib/prosody/util/async.lua:127: in function </usr/lib/prosody/util/async.lua:125>
stack traceback:
    [C]: in function 'error'
    /usr/lib/prosody/util/async.lua:137: in function 'handler'
    /usr/lib/prosody/util/async.lua:211: in function 'run'
    /usr/share/jitsi-meet/prosody-plugins/util.lib.lua:80: in function </usr/share/jitsi-meet/prosody-plugins/util.lib.lua:64>
    (...tail calls...)
    /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:80: in function 'loop'
    /usr/bin/prosody:90: in main chunk
    [C]: in ?

Im running apache2

any idea ?

may b ur pb is in the mod_muc_size module… look at the error and just track… then after muc_size go to util.lib.lua and check… where it is crashing… may be it cant find some prerequisite things that I am not sure… I had problems regarding prerequisite installations… but nice to hear that “get-info-room” is working… :slight_smile: mine isn’t and I am using docker…

By default, nginx proxies request for alphabetical path to frontend (see here) :

location ~ ^/([a-zA-Z0-9=\?]+)$ {
    rewrite ^/(.*)$ / break;
}

So, you should change from

location /room {

to

location /room-info {

then it might work. Please try it.

1 Like

yeah, it is working fine after changing to "room-info’ and I also had to change in mod_muc_size.lua after getting error… can u provide a correct mod_muc_size.lua with all of this working?
right now “/room-size”, “/room-info” and “/sessions-info” is working :heartbeat:

my meet.conf
server_name _;

client_max_body_size 0;

root /usr/share/jitsi-meet;
index index.html
error_page 404 /static/404.html;

location ~ ^/([a-zA-Z0-9=\?]+)$ {
    rewrite ^/(.*)$ / break;
}

location /config.js {
    alias /config/config.js;
}

location /interface_config.js {
    alias /config/interface_config.js;
}

location /external_api.js {
    alias /usr/share/jitsi-meet/libs/external_api.min.js;
}

location / {
    ssi on;
}

# BOSH
location /http-bind {

    
#    add_header 'Access-Control-Allow-Methods' '*';
#    add_header 'Access-Control-Allow-Headers' '*';
#    add_header 'Access-Control-Allow-Origin' '*';
 
    
    proxy_pass http://xmpp.meet.jitsi:5280/http-bind;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Host meet.jitsi;
    


}

#muc_size
 location /get-info-rooms {
     proxy_pass http://xmpp.meet.jitsi:5280/get-info-rooms?domain=meet.jitsi;
     proxy_set_header X-Forwarded-For $remote_addr;
     proxy_set_header Host meet.jitsi;

 }

 location /nbConfPart-info {
     proxy_pass http://xmpp.meet.jitsi:5280/nbConfPart?domain=meet.jitsi;
     proxy_set_header X-Forwarded-For $remote_addr;
     proxy_set_header Host meet.jitsi;
     add_header 'Access-Control-Allow-Origin' '*';

 }

 location /room-size {
     proxy_pass http://xmpp.meet.jitsi:5280/room-size?$args&domain=meet.jitsi;
     proxy_set_header X-Forwarded-For $remote_addr;
     proxy_set_header Host meet.jitsi;

 }

 location /room-info {
     proxy_pass http://xmpp.meet.jitsi:5280/room?$args&domain=meet.jitsi;
     proxy_set_header X-Forwarded-For $remote_addr;
     proxy_set_header Host meet.jitsi;

 }

 location /sessions-info {
     proxy_pass http://xmpp.meet.jitsi:5280/sessions?domain=meet.jitsi;
     proxy_set_header X-Forwarded-For $remote_addr;
     proxy_set_header Host meet.jitsi;

 }
my mod_muc_size.lua
-- 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

    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

--- 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 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;
	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

function getNbConfPart() 
    local tab={};

    --conferences count (without eliminating duplicates )
    for key, value in pairs(prosody.full_sessions) do
            if tostring(value["username"]:lower()) ~= "focus" then
                    table.insert(tab,tostring(value["jitsi_meet_room"]:lower()));
            end
    end

    local nbPart = 0
    if it.count(it.keys(prosody.full_sessions))-1 >= 0 --participants count
            then nbPart= it.count(it.keys(prosody.full_sessions))-1
    end

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

     --create result as json
    local result_json=array();
    result_json:push({
                    part= nbPart,
                    conf=tablelength(tab) -- eliminates count number of rooms
            });

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

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

function get_info_rooms()
   local innerValues = {};
   local keyRoom = "jitsi_bosh_query_room";
   for _, value in pairs(prosody.full_sessions) do
       if tostring(value["username"]:lower()) ~= "focus" then
           for innerKey, innerValue in pairs(value) do
               if innerKey == keyRoom and not has_value(innerValues, innerValue) then
                   table.insert(innerValues, innerValue)
               end
           end
       end
   end
return json.encode(innerValues);
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;
      			["GET get-info-rooms"] = function (event) return wrap_async_run(event,get_info_rooms) end;
		};
	});
end

but “/get-info-rooms” and “/nbConfPart-info” is not working …!
"/get-info-rooms" always return “{}” @ledahu is urs is working fine?
and “/nbConfPart” returns 500 Internal Server Error and I think I have the same as @ledahu where it says the roomname is null, in the console log :

error
jero_web_service_1      | 192.168.0.118 - - [27/Mar/2020:09:36:04 +0600] "GET /nbConfPart-info HTTP/1.1" 500 447 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"
jero_prosody_service_1  | runnerJSoqD3px                            error	Encountered error: /prosody-plugins/mod_muc_size.lua:201: attempt to index field 'jitsi_meet_room' (a nil value)
jero_prosody_service_1  | stack traceback:
jero_prosody_service_1  | 	/prosody-plugins/mod_muc_size.lua:201: in function 'handler'
jero_prosody_service_1  | 	/prosody-plugins/util.lib.lua:69: in function 'func'
jero_prosody_service_1  | 	/usr/lib/prosody/util/async.lua:127: in function </usr/lib/prosody/util/async.lua:125>
jero_prosody_service_1  | http.server                               error	Traceback[httpserver]: /usr/lib/prosody/util/async.lua:137: /prosody-plugins/mod_muc_size.lua:201: attempt to index field 'jitsi_meet_room' (a nil value)
jero_prosody_service_1  | stack traceback:
jero_prosody_service_1  | 	/prosody-plugins/mod_muc_size.lua:201: in function 'handler'
jero_prosody_service_1  | 	/prosody-plugins/util.lib.lua:69: in function 'func'
jero_prosody_service_1  | 	/usr/lib/prosody/util/async.lua:127: in function </usr/lib/prosody/util/async.lua:125>
jero_prosody_service_1  | stack traceback:
jero_prosody_service_1  | 	[C]: in function 'error'
jero_prosody_service_1  | 	/usr/lib/prosody/util/async.lua:137: in function 'handler'
jero_prosody_service_1  | 	/usr/lib/prosody/util/async.lua:211: in function 'run'
jero_prosody_service_1  | 	/prosody-plugins/util.lib.lua:80: in function </prosody-plugins/util.lib.lua:64>
jero_prosody_service_1  | 	(...tail calls...)
jero_prosody_service_1  | 	/usr/lib/prosody/util/events.lua:79: in function </usr/lib/prosody/util/events.lua:75>
jero_prosody_service_1  | 	(...tail calls...)
jero_prosody_service_1  | 	/usr/lib/prosody/net/http/server.lua:248: in function </usr/lib/prosody/net/http/server.lua:176>
jero_prosody_service_1  | 	[C]: in function 'xpcall'
jero_prosody_service_1  | 	/usr/lib/prosody/net/http/server.lua:108: in function 'process_next'
jero_prosody_service_1  | 	/usr/lib/prosody/net/http/server.lua:124: in function 'success_cb'
jero_prosody_service_1  | 	/usr/lib/prosody/net/http/parser.lua:177: in function 'feed'
jero_prosody_service_1  | 	/usr/lib/prosody/net/http/server.lua:155: in function </usr/lib/prosody/net/http/server.lua:154>
jero_prosody_service_1  | 	(...tail calls...)
jero_prosody_service_1  | 	/usr/lib/prosody/net/server_select.lua:915: in function </usr/lib/prosody/net/server_select.lua:899>
jero_prosody_service_1  | 	[C]: in function 'xpcall'
jero_prosody_service_1  | 	/usr/bin/prosody:80: in function 'loop'
jero_prosody_service_1  | 	/usr/bin/prosody:90: in main chunk
jero_prosody_service_1  | 	[C]: in ?

do u think the problem is in mod_muc_size module or other?
does nbConfPart-info and get-info-rooms need roomname parameters or something?
Thanx in advance :heartbeat:

Ah, you modifies original mod_muc_size.lua and want to add functions, right?
I uses original mod_muc_size, so I can’t help you.

But, I read your error log; I think your new functions are wrong.
I’ve searched forum and I found same issue, and the thread author seems already fixed it : How to get room name in prosody plugin?
Please try modification of How to get room name in prosody plugin? .

in your mod_muc_size module ,is there functions for /get-info-rooms and /nbConfpart ?

No. They are not official functions. See original one : https://github.com/jitsi/jitsi-meet/blob/master/resources/prosody-plugins/mod_muc_size.lua

oh yeah, I see… I downloaded this and then added that extra 2 functions discussed in this community… I badly need these functions as I wanna know about all current rooms in the server,not just specific room I know… Thanx for ur help and plz share if u find anything… The link u shared I didnt quite understand all what he did actually though

I created a new function which lists all room names on the server for you. New function, “handle_list_room” returns all of room names with JSON (array of string).
All of codes I posted here are licensed under same license as Jitsi Meet (Apache License 2.0), so you can use freely ( see https://github.com/jitsi/jitsi-meet/blob/master/LICENSE ).

Entire modified mod_muc_size.lua is below:

-- Prosody IM
-- Copyright (C) 2017 Atlassian
-- Modifed by jenus (2020/03/28, License is same as Jitsi meet, See: https://github.com/jitsi/jitsi-meet/blob/master/LICENSE)
--

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");

local muc_component_host = module:get_option_string("muc_component");

--- 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

    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

--- 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;

--- Handles request for retrieving the room lists
-- @param event the http event, holds the request query
-- @return GET response, containing a json with room lists
function handle_list_room(event)
    if muc_component_host == nil then
        return json.encode({})
    end
    local component = hosts[muc_component_host]
    local muc = component.modules.muc;
    local room_names = {}

    for room in muc.all_rooms() do
        table.insert(room_names, tostring(room:get_name()))
    end

    return json.encode(room_names)
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 room-list"] = function (event) return wrap_async_run(event,handle_list_room) end;
		};
	});
end

Patch of for mod_muc_size.lua is below (Optional):

--- raw.lua	2018-07-10 01:45:13.000000000 +0700
+++ mod_muc_size.lua	2020-03-28 15:29:58.852391727 +0700
@@ -31,6 +31,8 @@ end
 local muc_domain_prefix
     = module:get_option_string("muc_mapper_domain_prefix", "conference");
 
+local muc_component_host = module:get_option_string("muc_component");
+
 --- 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
@@ -176,6 +178,24 @@ function handle_get_room (event)
 	return json.encode(occupants_json);
 end;
 
+--- Handles request for retrieving the room lists
+-- @param event the http event, holds the request query
+-- @return GET response, containing a json with room lists
+function handle_list_room(event)
+    if muc_component_host == nil then
+        return json.encode({})
+    end
+    local component = hosts[muc_component_host]
+    local muc = component.modules.muc;
+    local room_names = {}
+
+    for room in muc.all_rooms() do
+        table.insert(room_names, tostring(room:get_name()))
+    end
+
+    return json.encode(room_names)
+end;
+
 function module.load()
     module:depends("http");
 	module:provides("http", {
@@ -184,6 +204,7 @@ function module.load()
 			["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 room-list"] = function (event) return wrap_async_run(event,handle_list_room) end;
 		};
 	});
 end

After copy mod_muc_size.lua (or apply patch), you need to add below line into VirtualHost "meet.jitsi" section in prosody’s jitsi-meet.cfg.lua :

muc_component = "muc.meet.jitsi"

And add below lines to nginx’s meet.conf:

location = /room-list {
    proxy_pass http://xmpp.meet.jitsi:5280/room-list;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Host meet.jitsi;
    add_header Content-Type "application/json; charset=UTF-8";
}

Then after restart, you can obtain all room info by API :

  1. Get rooms list from http://meet.example.com/room-list
  2. Get room info one by one from http://meet.example.com/room-info?room=OBTAINED_ROOM_NAME
1 Like

thanx a lot… I will give it a try :heart:

Hello,

Sorry for my noobness, I tried to add the port 5280, with and without removing the “expose” but docker-compose up -d prosody fails.
What do you have in your docker-compose.yml to get sessions/ ?
Please @Fuji

I get:
docker-compose up -d prosody
Creating jitsi4101-2_prosody_1 … error

ERROR: for jitsi4101-2_prosody_1  Cannot start service prosody: driver failed programming external connectivity on endpoint jitsi4101-2_prosody_1 (31d4826ad92314d8c01769c3111c0ae2c65ddfbfd4f4dcb1a4a94aba5214ab15): Error starting userland proxy: listen tcp 0.0.0.0:5280: bind: address already in use

ERROR: for prosody  Cannot start service prosody: driver failed programming external connectivity on endpoint jitsi4101-2_prosody_1 (31d4826ad92314d8c01769c3111c0ae2c65ddfbfd4f4dcb1a4a94aba5214ab15): Error starting userland proxy: listen tcp 0.0.0.0:5280: bind: address already in use
ERROR: Encountered errors while bringing up the project.

But I double checked, the prosody container was removed before running this docker-compose up

it said that the port 5280 is already in use… so may b u stopped the container bt something wrong happened or another service is occupying that port…so,

  1. u can check by command “docker ps -a” and again check the status(up/down) for prosody container or other occupying that port.
  2. u can use following command to see detailed information of services running on ur ports and check whether another service is running on this port or the container didn’t stopped clearly.
$ sudo apt-get install net-tools
$ netstat -pnltu (for detailed information of all ports)
$ netstat -pnltu | grep -i "${port_number}" (to inspect specific port)
  1. u can then also use these following commands to kill the process running in specific port
$ apt-get install psmisc
$ sudo fuser -k ${port_number}/tcp
  1. now again try from start

I hope it will help and this is a common pb and I also faced it …so don’t worry…
if the pb persist may its because u did some changes in dockerfile/.yml file… undo the changes… are u new to docker ? I can also suggest u good tutorials then… but at first just do what is in the official jitsi docker instruction said…
In prosody dockerfile u must expose 5280
and I used new mod_muc_size module instead of their built in in prosody container so that I can update them…it is not related to .yml file…u have by default availability to query. bt at first u have to make it up and running and have to have understanding of docker.
Thanx

Thank you very much @Fuji!
I’m not new to docker but I understand my mistake: a prosody server was also running on my PC, AND it was not registered in /etc/services, so cat /etc/services | grep 5280 results nothing while netstat -a | grep 5280 exposed the port as listening, without more details.
I forgot about this install to better understand prosody.
Now I removed it, I can bind the port and access it.

BTW, for other visitors in the future, note that locally, you can also access sessions via docker network: http://172.18.0.2:5280/sessions, this IP might change each time to run prosody container.
At first, I thought this network was not accessible from the host and reserved internally for containers, but it looks it’s exposed.
Yours is too?

OK, now I have a broader question, I guess you or someone else can easily answer:
What Prosody MUC admins have to manage rooms, users, etc?
Isn’t there a panel in a Prosody client GUI (like Pidgin) or a webmin module, or something to tackle these dark settings?
That’s why I installed prosody in the first place, to see what tools, but I didn’t find any.

I know this question is naive and basically reinterpreting efforts to adapt muc_size, but frankly, I think there might be someone somewhere who thought of MUC admin while developing it for years.

Thanks!

1 Like

Hey… how are u doing…
look, I am not so experienced and I am just here for few months starting from scratch… ur expectation is same like mine but the thing is this area is still evolving and the very deep basics of each components (prosody,video-bridge…) are not so documented well…the last book about prosody/webrtc is nearly 4-5 years ago and not up to date. but a experienced one,who are already familiar with these webrtc things for enough days are capable doing things with prosody anything they want… thats why jitsi team choose this component for their purpose.
If u wanna go deep may be u have to spend more time with searching right things… bcz I already did that and that was not so helpful… but for high level implementation u may not need that level of deep understanding and u can move on by learning what u need…
from my knowledge MUC admins roles are already implemented and there are many prosody modules out there by community like “mod_muc_size” for which u can query… but may b there is not GUI like things… but u can also write and use prosody modules with some good understanding of prosody.For usage,u may not need deep understanding…but for writing modules for ur own need,will need really good understanding… But I am far from that level I think…!! but I hope I will…!

@Fuji thanks!
Yeah, it looks like the experts are focused on their servers :slight_smile:
Well, if they contribute to the code and leave us, poor mortals, to the mundane mud of figuring and configuring the undocumented code, I’d say it’s fair in a sense.
Up to us to share and go forward.

1 Like