# HG changeset patch
# User Kim Alvefur <zash@zash.se>
# Date 1723385424 -7200
# Node ID d6a695abb33ce536433f3394b853ae7dd40f0689
# Parent  f89367e421d1f652a7b9e9dc6baf522180919802
mod_ping_muc: Delay ping a configurable amount of time

If a server is restarting, checking immediately before it has a chance
to complete its restart and get ready would often fail, preventing the
possibility of transparent restarts as supported by Prosody's mod_muc.

Reconnecting immediately when a connection is closed for being idle, or
because the remote server is trying to reclaim some resources, is also
counter-productive as the connection may fail.

Also, if there is some Internet routing problem affecting s2s, it may
help to wait a bit before checking, in case the problem resolved itself
in the mean time.

diff -r f89367e421d1 -r d6a695abb33c mod_ping_muc/mod_ping_muc.lua
--- a/mod_ping_muc/mod_ping_muc.lua	Wed Aug 07 21:28:11 2024 +0200
+++ b/mod_ping_muc/mod_ping_muc.lua	Sun Aug 11 16:10:24 2024 +0200
@@ -28,43 +28,45 @@
 		for host, state in pairs(s2s_session.hosts) do if state.authed then related_hosts:add(host); end end
 	end
 
-	for _, user_session in pairs(local_sessions) do
-		for _, session in pairs(user_session.sessions) do
-			if session.rooms_joined then
-				for room, info in pairs(session.rooms_joined) do
-					local nick = info.nick or info;
-					local room_nick = room .. "/" .. nick;
-					if related_hosts:contains(jid.host(room)) then
-						-- User is in a MUC room for which the s2s connection was lost. Now what?
+	local ping_delay = module:get_option_number("ping_muc_delay", 60, 1);
+
+	module:add_timer(ping_delay, function ()
+		for _, user_session in pairs(local_sessions) do
+			for _, session in pairs(user_session.sessions) do
+				if session.rooms_joined then
+					for room, info in pairs(session.rooms_joined) do
+						local nick = info.nick or info;
+						local room_nick = room .. "/" .. nick;
+						if related_hosts:contains(jid.host(room)) then
+							-- User is in a MUC room for which the s2s connection was lost. Now what?
 
-						-- Self-ping
-						-- =========
-						--
-						-- Response of <iq type=result> means the user is still in the room
-						-- (and self-ping is supported), so we do nothing.
-						--
-						-- An error reply either means the user has fallen out of the room,
-						-- or that self-ping is unsupported. In the later case, whether the
-						-- user is still joined is indeterminate and we might as well
-						-- pretend they fell out.
-						module:send_iq(st.iq({ type = "get"; id = id.medium(); from = session.full_jid; to = room_nick })
-								:tag("ping", { xmlns = "urn:xmpp:ping"; }))
-						:catch(function(err)
-							module:send(
-								st.presence({ type = "unavailable"; id = id.medium(); to = session.full_jid; from = room_nick })
-									:tag("x", { xmlns = "http://jabber.org/protocol/muc#user" })
-										:tag("item", { affiliation = "none"; role = "none" })
-											:text_tag("reason", err.text or "Connection to remote server lost")
-										:up()
-										:tag("status", { code = "110" }):up()
-										:tag("status", { code = "333" }):up()
-									:reset());
-						end);
-						-- TODO do this with some delay?
+							-- Self-ping
+							-- =========
+							--
+							-- Response of <iq type=result> means the user is still in the room
+							-- (and self-ping is supported), so we do nothing.
+							--
+							-- An error reply either means the user has fallen out of the room,
+							-- or that self-ping is unsupported. In the later case, whether the
+							-- user is still joined is indeterminate and we might as well
+							-- pretend they fell out.
+								module:send_iq(st.iq({ type = "get"; id = id.medium(); from = session.full_jid; to = room_nick })
+										:tag("ping", { xmlns = "urn:xmpp:ping"; }))
+								:catch(function(err)
+									module:send(
+										st.presence({ type = "unavailable"; id = id.medium(); to = session.full_jid; from = room_nick })
+											:tag("x", { xmlns = "http://jabber.org/protocol/muc#user" })
+												:tag("item", { affiliation = "none"; role = "none" })
+													:text_tag("reason", err.text or "Connection to remote server lost")
+												:up()
+												:tag("status", { code = "110" }):up()
+												:tag("status", { code = "333" }):up()
+											:reset());
+								end);
+						end
 					end
 				end
-
 			end
 		end
-	end
+	end)
 end);