Destroy the room after the last moderator leaves (prosody custom module)

update:
after destroying the room, users will receive a message
изображение

local LOGLEVEL = "debug"
local MIN = module:get_option_number("conference_timeout", 10)
local TIMEOUT = MIN

local is_healthcheck_room = module:require "util".is_healthcheck_room
local st = require "util.stanza"
local timer = require "util.timer"
module:log(LOGLEVEL, "loaded")

module:hook("muc-occupant-left", function (event)
    local room = event.room
    local mods = room:each_affiliation("owner");
    local leaver = event.occupant.bare_jid;
    local a = 0;
    local b = 0;

    for mod in mods do
        a = a + 1;
        if mod == leaver then
            room:set_affiliation(true, leaver, "outcast");
            module:log("info", "still a moderator present");
            a = a - 1;
        end
    end

  for mod in mods do
        module:log("info", "mods: %s", a);
        if a == 1 then

            if is_healthcheck_room(room.jid) then
                module:log(LOGLEVEL, "skip restriction")
                return
            end

            room:broadcast_message(
                 st.message({ type="groupchat", from=room.jid })
                 :tag("body")
                 :text("The conference will be terminated in "..MIN.." sec"))

            module:log(LOGLEVEL, "set timeout for conference, %s secs, %s",
                                 TIMEOUT, room.jid)

            timer.add_task(TIMEOUT, function()
                if is_healthcheck_room(room.jid) then
                    return
                end

                for mod in mods do
                    b = b + 1;
                end

                if b == 1 then
                   event.room:destroy();
                   for _, p in room:each_occupant() do
                       room:set_affiliation(true, p.jid, "outcast")
                       module:log("info", "kick the occupant, %s", p.jid)
                       module:log("info", "the conference terminated")
                   end
                else
                   module:log("info", "still a moderator present");
                   room:broadcast_message(
                     st.message({ type="groupchat", from=room.jid })
                     :tag("body")
                     :text("A moderator has joined. The termination of the conference was canceled."))
                end
            end)
        end
    end
end)
3 Likes

This updated lua does not seem to work in my testing. The call terminated message is received, but the room is never destroyed. If the moderator tries to create/join the same room again, there is a “connection failed” error with no obvious error logs in prosody even in jicofo and jvb.

Prosody’s version is 0.11.4-1.

The previous “mod_disconnect.lua” works though.

I don’t have such a problem.
after receiving the call terminated message, the conference time continues to run. but the conference is destroyed. and the moderator can create a new conference with the same name.
and if the moderator reconnects to the same conference before receiving the message, everything is ok too …

try updating prosody to the latest version.
I have 0.11.10-1

Hi. Thanks for the reply.
I upgraded prosody as recommended and now using the same version you posted but it’s the same.
Does it matter if I am using unstable?

Still, the 1st lua script you posted, the one without the “event.room:destroy()” function works.

Kind regards,

I confirm that in the latest version of jitsi meet “event.room:destroy()” does not work as expected…
I’ll see what I can do.
The first version in the header (without “event.room:destroy()”) works without any problems.

ok… update:

screenshots


updated code
local LOGLEVEL = "debug"
local MIN = module:get_option_number("conference_timeout", 10)
local TIMEOUT = MIN

local is_healthcheck_room = module:require "util".is_healthcheck_room
local st = require "util.stanza"
local timer = require "util.timer"
module:log(LOGLEVEL, "loaded")

module:hook("muc-occupant-left", function (event)
    local room = event.room
    local mods = room:each_affiliation("owner");
    local leaver = event.occupant.bare_jid;
    local a = 0;
    local b = 0;

    for mod in mods do
        a = a + 1;
        if mod == leaver then
            room:set_affiliation(true, leaver, "outcast");
            module:log("info", "still a moderator present");
            a = a - 1;
        end
    end

  for mod in mods do
        module:log("info", "mods: %s", a);
        if a == 1 then

            if is_healthcheck_room(room.jid) then
                module:log(LOGLEVEL, "skip restriction")
                return
            end

            room:broadcast_message(
                 st.message({ type="groupchat", from=room.jid })
                 :tag("subject")
                 :text("The conference will be terminated in "..MIN.." sec"))

            module:log(LOGLEVEL, "set timeout for conference, %s secs, %s",
                                 TIMEOUT, room.jid)

            timer.add_task(TIMEOUT, function()
                if is_healthcheck_room(room.jid) then
                    return
                end

                for mod in mods do
                    b = b + 1;
                end

                if b == 1 then

                   room:broadcast_message(
                       st.message({ type="groupchat", from=room.jid })
                       :tag("subject")
                       :text("THE CONFERENCE IS OVER"))
                       local start = os.clock()
                       while os.clock() - start < 3 do end

                   for _, p in room:each_occupant() do
                       room:set_affiliation(true, p.jid, "outcast")
                       module:log("info", "kick the occupant, %s", p.jid)
                       module:log("info", "the conference terminated")
                   end
                else
                   module:log("info", "still a moderator present");
                   room:broadcast_message(
                     st.message({ type="groupchat", from=room.jid })
                     :tag("subject")
                     :text(roomName))
                end
            end)
        end
    end
end)
2 Likes

slightly corrected the code in the previous post.
if the moderator has rejoined, the upper message about the termination of the conference is changed to the name of the room.

Thank you. I confirm that the updated script is working as intended. Thanks so much for sharing this with the community.

fixed:
for some users the message “conference is over” was replaced by the previous one about the end of the conference after 15 seconds

updated code:

local LOGLEVEL = "debug"
local MIN = module:get_option_number("conference_timeout", 10)
local TIMEOUT = MIN

local is_healthcheck_room = module:require "util".is_healthcheck_room
local st = require "util.stanza"
local timer = require "util.timer"
module:log(LOGLEVEL, "loaded")

module:hook("muc-occupant-left", function (event)
    local room = event.room
    local mods = room:each_affiliation("owner");
    local leaver = event.occupant.bare_jid;
    local a = 0;
    local b = 0;

    for mod in mods do
        a = a + 1;
        if mod == leaver then
            room:set_affiliation(true, leaver, "outcast");
            module:log("info", "still a moderator present");
            a = a - 1;
        end
    end

  for mod in mods do
        module:log("info", "mods: %s", a);
        if a == 1 then

            if is_healthcheck_room(room.jid) then
                module:log(LOGLEVEL, "skip restriction")
                return
            end

            room:broadcast_message(
                 st.message({ type="groupchat", from=room.jid })
                 :tag("subject")
                 :text("The conference will end in "..MIN.." sec"))

            module:log(LOGLEVEL, "set timeout for conference, %s secs, %s",
                                 TIMEOUT, room.jid)

            timer.add_task(TIMEOUT, function()
                if is_healthcheck_room(room.jid) then
                    return
                end

                for mod in mods do
                    b = b + 1;
                end

                if b == 1 then

                   for _, p in room:each_occupant() do
                       room:broadcast_message(
                           st.message({ type="groupchat", from=room.jid })
                           :tag("subject")
                           :text("THE CONFERENCE IS OVER"))
                       room:set_affiliation(true, p.jid, "outcast")
                       module:log("info", "kick the occupant, %s", p.jid)
                       module:log("info", "the conference terminated")
                   end
                else
                   module:log("info", "still a moderator present");
                   room:broadcast_message(
                     st.message({ type="groupchat", from=room.jid })
                     :tag("subject")
                     :text(roomName))
                end
            end)
        end
    end
end)

to redirect to the main page (only for web clients)

in /usr/share/jitsi-meet/body.html

<script>
function redirect() {
    window.location.href="homepage";
}

function subscribeToEvents() {
    try {
        if (!APP.store.getState()) {
            throw new Error("state is not ready. try again");
        } else if (!APP.store.getState()["features/base/connection"]) {
            throw new Error("connection is not ready. try again");
        }

        cnn = APP.store.getState()["features/base/connection"];
        if (cnn.error) {
            return redirect();
        }

        APP.conference._room.on("conference.left", redirect);
    } catch(e) {
        setTimeout(() => subscribeToEvents(), 3000);
    }
}

subscribeToEvents();
</script>

and change the homepage to the desired URL!

thanks to @emrah Mod_time_restricted doesn't work? - #15 by emrah

6 Likes

Hi,
I’m hooking up to this post to ask you something.
If I wanted to send a broadcast message, which comes to me from prosody, to all the participants in the lobby room, what could I do?

I have a version of Jisti without the ConferenceInfo class …

Hi @DmDS @damencho and everyone else, I use mod_reservations.lua and mod_disconnect.lua
with a secure domain mechanism and I can guarantee both that the room is opened by an authorized person (owner of that room name) and that when it is closed, the timeout is started (60sec for us) and at the end of this all the participants are expelled from the room.
The timeout system allows the moderator to re-enter his room and keep it alive in case of a mistaken exit.

But there is a problem:

  1. “A” tries to start a room that is not hers. The system blocks it. CORRECT

  2. “A” enters his room. CORRECT

  3. “B” joins "A "'s room. CORRECT

  4. “A” leaves her room and “B” stays inside until the timeout has elapsed. CORRECT

  5. “A” starts another room that is not hers and manages to do so by becoming its moderator. NOT CORRECT

After step 2. “A” in console receives an exception: onConferenceFaild Authentication Require

After point 5. “A” doesn’t get that exception and I think it’s because Prosody sees it authenticated (reservation active until her room is destroyed) and then lets her start the new room …

Does he go back there?

While maintaining the timeout system, I would like moderator “A” once he leaves his room to be able to enter another as a participant without becoming a moderator. Should I destroy the reservation of his previous room? Do you have any suggestions for me?

Thank you in advance

Have you tried disabling auto login in jicofo?
Does that work in this situation jicofo/reference.conf at a64005759f9408b92648f9f8cabc3f6f8e77da9e · jitsi/jicofo · GitHub
I guess this is causing this behavior.

I confirm it, I had already set it to false.

Sounds like it could be related to this issue, where mod_reservation is bypassed if user joins on guest domain but is recognised by jicofo as a previously logged in user and gets granted moderator rights:

That would be tricky if you use Secure Domain since by design Jicofo will give moderator rights to all authenticated users. You’ll need a custom prosody module if want finer grained control over that.

Perhaps follow up in a similar discussion here:

thank you very much

hi we solved it like this:

To do this we have modified jicofo, in particular the XMPPDomainAuthAuthority class where

authenticateJidWithSession(session, peerJid, response)

is called in the processAuthLocked method.

I make this call only and only if the room is that of whoever tries to access it …

if (query.getRoom() != null && session.getRoomName() != null
                     && !query.getRoom().equals(session.getRoomName())) {
                 //I do not do anything
                 } else {
                 authenticateJidWithSession(session, peerJid, response);
             }

Forgive me the denials in the code but it was important that it was concrete … I will fix it …

@damencho do you see problems in this change?
We have recompiled Jicofo and we are testing it with excellent results.

I await your news.

It’s better to create prosody modules for things like this; the burden of maintaining a customized Jicofo through updates will be onerous.

I agree with you, I wanted to know if this modification can have contraindications …