Files
upstream-jitsi-meet/resources/prosody-plugins/mod_time_restricted.lua
upstream-mirror 1d2bfcac0c
Some checks failed
Prosody Plugin Tests / test (push) Failing after 10m11s
Close stale issues and PRs / stale (push) Failing after 1s
snapshot of https://github.com/jitsi/jitsi-meet.git @ fd1749dd28f4138aa80234fc7f311b19aaffa0e7 (2026-09-15)
2026-09-15 15:56:32 +05:00

117 lines
4.4 KiB
Lua

--- activate under the main muc component
local MIN = module:get_option_number("conference_max_minutes", 5)
local TIMEOUT = MIN * 60
-- Gratis time granted past the limit so people can say their goodbyes and
-- leave on their own instead of being cut off mid-sentence. Clients still see
-- their countdown end at TIMEOUT - they spend the grace period in the visible
-- over-schedule state, which is the cue to wrap up - the room is simply not
-- torn down until it elapses. 0 (the default) destroys the room the moment the
-- limit is reached.
local GRACE = module:get_option_number("conference_grace_seconds", 0)
local DESTROY_AT = TIMEOUT + GRACE
local is_healthcheck_room = module:require "util".is_healthcheck_room
local st = require "util.stanza"
local json = require "cjson.safe"
local array = require "util.array";
module:log('info', "loaded")
local restricted_rooms = array{};
-- The `type` the time-timer feature in jitsi-meet matches on. Sent as a
-- json-message from the room JID so lib-jitsi-meet surfaces it as a
-- NON_PARTICIPANT_MESSAGE_RECEIVED (a server-originated, non-participant
-- message) rather than a participant endpoint message.
local TIME_RESTRICTED_MESSAGE_TYPE = 'time_restricted';
-- Sends the time-timer json-message to a single occupant. `durationSeconds` is
-- the full meeting length and `elapsedSeconds` how far in we are, so every
-- client - first joiner or last - lands on the same countdown.
local function send_time_timer(room, to_jid)
local elapsed = os.time() - (room.time_restricted_created or os.time());
local body, error = json.encode({
type = TIME_RESTRICTED_MESSAGE_TYPE;
durationSeconds = TIMEOUT;
elapsedSeconds = elapsed;
});
if not body then
module:log('error', 'failed to encode time-timer message room:%s error:%s', room.jid, error);
return;
end
-- from = room.jid so the stanza routes through ChatRoom.onMessage in
-- lib-jitsi-meet (which only processes messages whose bare from matches the
-- room) and is emitted as JSON_MESSAGE_RECEIVED -> NON_PARTICIPANT_MESSAGE_RECEIVED.
local stanza = st.message({ from = room.jid; to = to_jid; })
:tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' }):text(body):up();
module:send(stanza);
end
module:hook("muc-room-created", function (event)
local room = event.room
if is_healthcheck_room(room.jid) then
return
end
room.time_restricted_created = os.time();
room.destroy_timer = module:add_timer(DESTROY_AT, function()
if is_healthcheck_room(room.jid) then
return
end
restricted_rooms[room.jid] = true;
-- %g (not %d) so a fractional conference_max_minutes does not crash the
-- timer; for the usual whole-minute configs it renders identically.
room:destroy(nil, string.format('This meeting reached its %g-minute time limit and has ended.', MIN));
module:log('info', "the conference terminated %s", room.jid);
end)
end)
-- Every occupant is told the meeting's limit as soon as they join, so the
-- client holds the full picture from the start and decides for itself when to
-- surface it (`timeTimer.suppressForSeconds` in jitsi-meet delays the visible
-- countdown by a configurable number of seconds). `elapsedSeconds` keeps late
-- joiners in sync with everyone else.
module:hook("muc-occupant-joined", function (event)
local room, occupant = event.room, event.occupant;
if is_healthcheck_room(room.jid) then
return
end
-- Only for rooms this module saw created: without a creation stamp we
-- cannot say how far in the meeting is, and reporting elapsed = 0 for a
-- room that has been running for a while would be worse than saying
-- nothing.
if room.time_restricted_created then
send_time_timer(room, occupant.jid);
end
end);
module:hook("muc-room-pre-create", function(event)
local origin, stanza, room = event.origin, event.stanza, event.room;
if is_healthcheck_room(room.jid) then
return
end
if restricted_rooms[room.jid] then
module:log('info', "the conference id blocked %s", room.jid);
origin.send(st.error_reply(stanza, "cancel", "resource-constraint", nil, module.host));
return true;
end
end, 200);
module:hook('muc-room-destroyed',function(event)
local room = event.room;
if room.destroy_timer then
room.destroy_timer:stop();
room.destroy_timer = nil;
end
end, 1); -- prosody handles it at 0