changeset 6561:93544117ddcd

muc_invite_remote_affiliated_jids: new module
author nicoco <nicoco@nicoco.fr>
date Mon, 08 Jun 2026 22:42:05 +0200
parents 7dee00093c88
children 5da6fb562df9
files mod_muc_invite_remote_affiliated_jids/README.md mod_muc_invite_remote_affiliated_jids/mod_muc_invite_remote_affiliated_jids.lua
diffstat 2 files changed, 96 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_muc_invite_remote_affiliated_jids/README.md	Mon Jun 08 22:42:05 2026 +0200
@@ -0,0 +1,35 @@
+---
+labels:
+- 'Stage-Alpha'
+summary: 'Send invites to MUC affiliated remote JIDs on startup'
+...
+
+Purpose
+=======
+
+This module sends invites to non-local JIDs that are affiliated as "member",
+"owner" or "admin" of rooms. It is a workaround for situations where
+participants are kicked of a room by a server shutdown, to signal to their
+client that they can join the room again.
+
+Configuration
+=============
+
+Add the module to the MUC host (not the global modules\_enabled):
+
+```lua
+Component "conference.example.com" "muc"
+    modules_enabled = { "muc_invite_remote_affiliated_jids" }
+```
+
+You can configure a message associated to the invite with the
+`muc_invite_remote_affiliated_jids_reason` configuration entry.
+
+Compatibility
+=============
+
+  ------- -------
+  trunk*  Works
+  ------- -------
+
+*as of 2026-06-08
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_muc_invite_remote_affiliated_jids/mod_muc_invite_remote_affiliated_jids.lua	Mon Jun 08 22:42:05 2026 +0200
@@ -0,0 +1,61 @@
+local st          = require "util.stanza"
+local jid         = require "util.jid"
+
+local module_host = module:get_host()
+
+local mod_muc     = module:depends("muc") --[[@as muc_module]]
+
+local reason      = module:get_option_string(
+	"muc_invite_remote_affiliated_jids_reason",
+	"You are a member of this room, please join it."
+)
+
+local function is_remote(bare_jid)
+	local _user, jid_host = jid.split(bare_jid)
+	return not prosody.hosts[jid_host]
+end
+
+local function build_invite(room_jid, to_jid, room_name, from_jid)
+	local invite = st.message({ to = to_jid, from = module_host })
+		:tag("x", {
+			xmlns  = "jabber:x:conference",
+			jid    = room_jid,
+			reason = reason
+		})
+
+	if room_name and room_name ~= "" then
+		invite:attr("name", room_name)
+	end
+
+	return invite
+end
+
+local function invite_remote_members()
+	local invite_count = 0
+	local room_count   = 0
+
+	for room in mod_muc.all_rooms() do
+		if room:get_members_only() then
+			room_count      = room_count + 1
+			local room_jid  = room.jid
+			local room_name = room:get_name()
+
+			for member_jid, aff in room:each_affiliation() do
+				if aff == "owner" or aff == "admin" or aff == "member" then
+					local bare = jid.bare(member_jid)
+					if is_remote(bare) then
+						local invite_stanza = build_invite(room_jid, bare, room_name)
+						module:send(invite_stanza)
+						module:log("debug", "Sent invite for %s to %s", room_jid, bare)
+						invite_count = invite_count + 1
+					end
+				end
+			end
+		end
+	end
+
+	module:log("info", "Sent %d invite(s) across %d members-only room(s)",
+		invite_count, room_count)
+end
+
+module:hook_global("server-started", invite_remote_members)