Changeset

13657:404d3644ac4a

mod_muc: Integrate support for vcards/avatars on MUC rooms This was previously served by a community module (mod_vcard_muc). It can be disabled by setting `vcard_muc = false` in the config.
author Matthew Wild <mwild1@gmail.com>
date Thu, 06 Feb 2025 17:08:46 +0000
parents 13656:4298e6565fec
children 13658:33806569d7c5
files core/features.lua plugins/muc/mod_muc.lua plugins/muc/vcard.lib.lua
diffstat 3 files changed, 87 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/core/features.lua	Thu Feb 06 17:03:03 2025 +0000
+++ b/core/features.lua	Thu Feb 06 17:08:46 2025 +0000
@@ -10,6 +10,8 @@
 		"mod_flags";
 		-- mod_cloud_notify bundled
 		"mod_cloud_notify";
+		-- mod_muc has built-in vcard support
+		"muc_vcard";
 		-- Roles, module.may and per-session authz
 		"permissions";
 		-- prosody.* namespace
--- a/plugins/muc/mod_muc.lua	Thu Feb 06 17:03:03 2025 +0000
+++ b/plugins/muc/mod_muc.lua	Thu Feb 06 17:08:46 2025 +0000
@@ -116,6 +116,10 @@
 module:require "muc/hats";
 module:require "muc/lock";
 
+if module:get_option_boolean("muc_vcard", true) ~= false then
+	module:require "muc/vcard";
+end
+
 module:default_permissions("prosody:admin", {
 	":automatic-ownership";
 	":create-room";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/muc/vcard.lib.lua	Thu Feb 06 17:08:46 2025 +0000
@@ -0,0 +1,81 @@
+local mod_vcard = module:depends("vcard");
+
+local jid = require "prosody.util.jid";
+local st = require "prosody.util.stanza";
+
+-- This must be the same event that mod_vcard hooks
+local vcard_event = "iq/bare/vcard-temp:vCard";
+local advertise_hashes = module:get_option("muc_avatar_advertise_hashes");
+
+--luacheck: ignore 113/get_room_from_jid
+
+local function get_avatar_hash(room)
+	if room.avatar_hash then return room.avatar_hash; end
+
+	local room_node = jid.split(room.jid);
+	local hash = mod_vcard.get_avatar_hash(room_node);
+	room.avatar_hash = hash;
+
+	return hash;
+end
+
+local function send_avatar_hash(room, to)
+	local hash = get_avatar_hash(room);
+
+	local presence_vcard = st.presence({to = to, from = room.jid})
+		:tag("x", { xmlns = "vcard-temp:x:update" })
+			:tag("photo"):text(hash):up();
+
+	if to == nil then
+		if not advertise_hashes or advertise_hashes == "presence" then
+			room:broadcast_message(presence_vcard);
+		end
+		if not advertise_hashes or advertise_hashes == "message" then
+			room:broadcast_message(st.message({ from = room.jid, type = "groupchat" })
+				:tag("x", { xmlns = "http://jabber.org/protocol/muc#user" })
+					:tag("status", { code = "104" }));
+		end
+
+	else
+		module:send(presence_vcard);
+	end
+end
+
+module:hook(vcard_event, function (event)
+	local stanza = event.stanza;
+	local to = stanza.attr.to;
+
+	if stanza.attr.type ~= "set" then
+		return;
+	end
+
+	local room = get_room_from_jid(to);
+	if not room then
+		return;
+	end
+
+	local sender_affiliation = room:get_affiliation(stanza.attr.from);
+	if sender_affiliation == "owner" then
+		event.allow_vcard_modification = true;
+	end
+end, 10);
+
+if advertise_hashes ~= "none" then
+	module:hook("muc-occupant-joined", function (event)
+		send_avatar_hash(event.room, event.stanza.attr.from);
+	end);
+	module:hook("vcard-updated", function (event)
+		local room = get_room_from_jid(event.stanza.attr.to);
+		send_avatar_hash(room, nil);
+	end);
+end
+
+module:hook("muc-disco#info", function (event)
+	event.reply:tag("feature", { var = "vcard-temp" }):up();
+
+	table.insert(event.form, {
+			name = "muc#roominfo_avatarhash",
+			type = "text-multi",
+		});
+	event.formdata["muc#roominfo_avatarhash"] = get_avatar_hash(event.room);
+end);