view mod_minimix/mod_minimix.lua @ 5173:460f78654864

mod_muc_rtbl: also filter messages This was a bit tricky because we don't want to run the JIDs through SHA256 on each message. Took a while to come up with this simple plan of just caching the SHA256 of the JIDs on the occupants. This will leave some dirt in the occupants after unloading the module, but that should be ok; once they cycle the room, the hashes will be gone. This is direly needed, otherwise, there is a tight race between the moderation activities and the actors joining the room.
author Jonas Schäfer <jonas@wielicki.name>
date Tue, 21 Feb 2023 21:37:27 +0100
parents 1745021c0a73
children
line wrap: on
line source

-- mod_minimix
--
-- Rewrite MUC stanzas suich that the account / bare JID joins rooms instead of clients / full JIDs
--
local jid_split, jid_join, jid_node, jid_bare = import("util.jid", "split", "join", "node", "bare");
local st = require "util.stanza";
local mt = require "util.multitable";

local users = prosody.hosts[module.host].sessions;

local data = mt.new();

-- FIXME You can join but you can never leave.

module:hook("pre-presence/full", function (event)
	local origin, stanza = event.origin, event.stanza;

	local room_node, room_host, nickname = jid_split(stanza.attr.to);
	local room_jid = jid_join(room_node, room_host);
	local username = origin.username;

	if stanza.attr.type == nil and stanza:get_child("x", "http://jabber.org/protocol/muc") then
		module:log("debug", "Joining %s as %s", room_jid, nickname);

		-- TODO Should this be kept track of before the *initial* join has been confirmed or?
		if origin.joined_rooms then
			origin.joined_rooms[room_jid] = nickname;
		else
			origin.joined_rooms = { [room_jid] = nickname };
		end

		if data:get(username, room_jid, "subject") then
			module:log("debug", "Already joined to %s as %s", room_jid, nickname);
			local presences = data:get(username, room_jid, "presence");
			if presences then
				-- Joined but no presence? Weird
				for _, pres in pairs(presences) do
					pres = st.clone(pres);
					pres.attr.to = origin.full_jid;
					origin.send(pres);
				end
			end
			-- FIXME should send ones own presence last
			local subject = data:get(username, room_jid, "subject");
			if subject then
				origin.send(st.clone(subject));
			end
			-- Send on-join stanzas from local state, somehow
			-- Maybe tell them their nickname was changed if it doesn't match the account one
			return true;
		end

		local account_join = st.clone(stanza);
		account_join.attr.from = jid_join(origin.username, origin.host);
		module:send(account_join);

		data:set(username, room_jid, "joined", nickname);

		return true;
	elseif stanza.attr.type == "unavailable" then
		if origin.joined_rooms and origin.joined_rooms[room_jid] then
			origin.joined_rooms[room_jid] = nil;
		end
		origin.send(st.reply(stanza));
		return true;
	elseif stanza.attr.type == nil and origin.joined_rooms and origin.joined_rooms[room_jid] then
		return true; -- Suppress these
	end
end);

module:hook("pre-message/bare", function (event)
	local origin, stanza = event.origin, event.stanza;
	local username = origin.username;
	local room_jid = jid_bare(stanza.attr.to);

	module:log("info", "%s", stanza)
	if origin.joined_rooms and origin.joined_rooms[room_jid] then
		local from_account = st.clone(stanza);
		from_account.attr.from = jid_join(username, origin.host);
		module:log("debug", "Sending:\n%s\nInstead of:\n%s", from_account, stanza);
		module:send(from_account, origin);
		return true;
	end
end);

local function handle_to_bare_jid(event)
	local stanza = event.stanza;
	local username = jid_node(stanza.attr.to);
	local room_jid = jid_bare(stanza.attr.from);

	if data:get(username, room_jid) then
		module:log("debug", "handle_to_bare_jid %q, %s", room_jid, stanza);
		-- Broadcast to clients

		if stanza.name == "message" and stanza.attr.type == "groupchat"
			and not stanza:get_child("body") and stanza:get_child("subject") then
			data:set(username, room_jid, "subject", st.clone(stanza));
		elseif stanza.name == "presence" then
			if stanza.attr.type == nil then
				data:set(username, room_jid, "presence", stanza.attr.from, st.clone(stanza));
			elseif stanza.attr.type == "unavailable" then
				data:set(username, room_jid, "presence", stanza.attr.from, nil);
			end
		end

		if users[username] then
			module:log("debug", "%s has sessions", username);
			for _, session in pairs(users[username].sessions) do
				module:log("debug", "Session: %s", jid_join(session.username, session.host, session.resource));
				if session.joined_rooms and session.joined_rooms[room_jid] then
					module:log("debug", "Is joined");
					local s = st.clone(stanza);
					s.attr.to = session.full_jid;
					session.send(s);
				else
					module:log("debug", "session.joined_rooms = %s", session.joined_rooms);
				end
			end
		end

		return true;
	end
end

module:hook("presence/bare", handle_to_bare_jid);
module:hook("message/bare", handle_to_bare_jid);