Updated mod_muc_moderation to upstream.
This commit is contained in:
		| @ -12,6 +12,7 @@ | ||||
| * Avatar set for anonymous users: new 'none' choice (that will fallback to Converse new colorized avatars). | ||||
| * New translation: Albanian. | ||||
| * Translation updates: Crotian, Japanese. | ||||
| * Updated mod_muc_moderation to upstream. | ||||
|  | ||||
| ## 10.3.3 | ||||
|  | ||||
|  | ||||
| @ -1,7 +1,9 @@ | ||||
| <!-- | ||||
| --- | ||||
| SPDX-FileCopyrightText: 2015-2021 Kim Alvefur | ||||
| SPDX-License-Identifier: MIT | ||||
| --> | ||||
| summary: Let moderators remove spam and abuse messages | ||||
| --- | ||||
|  | ||||
| # Introduction | ||||
|  | ||||
| This module implements [XEP-0425: Message Moderation]. | ||||
| @ -16,7 +18,7 @@ role in the channel / group chat. | ||||
| Example [MUC component][doc:chatrooms] configuration: | ||||
|  | ||||
| ``` {.lua} | ||||
| VirtualHost "channels.example.com" "muc" | ||||
| Component "channels.example.com" "muc" | ||||
| modules_enabled = { | ||||
|     "muc_mam", | ||||
|     "muc_moderation", | ||||
| @ -25,20 +27,19 @@ modules_enabled = { | ||||
|  | ||||
| # Compatibility | ||||
|  | ||||
| -   Should work with Prosody 0.11.x and later. | ||||
| -   Tested with trunk rev `52c6dfa04dba`. | ||||
| -   Message tombstones requires a compatible storage module implementing | ||||
|     a new message replacement API. | ||||
| -   Basic functionality with Prosody 0.11.x and later | ||||
| -   Full functionality with Prosody 0.12.x and `internal` or `sql` | ||||
|     storage^[Replacing moderated messages with tombstones requires new storage API methods.] | ||||
| -   Works with [mod_storage_xmlarchive] | ||||
|  | ||||
| ## Clients | ||||
|  | ||||
| -   Tested with [Converse.js](https://conversejs.org/) | ||||
|     [v6.0.1](https://github.com/conversejs/converse.js/releases/tag/v6.0.1) | ||||
| -   [Converse.js](https://conversejs.org/) | ||||
| -   [Gajim](https://dev.gajim.org/gajim/gajim/-/issues/10107) | ||||
| -   [clix](https://code.zash.se/clix/rev/6c1953fbe0fa) | ||||
|  | ||||
| ### Feature requests | ||||
|  | ||||
| - [Conv](https://github.com/iNPUTmice/Conversations/issues/3722)[ersa](https://github.com/iNPUTmice/Conversations/issues/3920)[tions](https://github.com/iNPUTmice/Conversations/issues/4227) | ||||
| -   [Conversations](https://codeberg.org/iNPUTmice/Conversations/issues/20) | ||||
| -   [Dino](https://github.com/dino/dino/issues/1133) | ||||
| - [Gajim](https://dev.gajim.org/gajim/gajim/-/issues/10107) | ||||
| - [Poezio](https://lab.louiz.org/poezio/poezio/-/issues/3543) | ||||
| -   [Profanity](https://github.com/profanity-im/profanity/issues/1336) | ||||
|  | ||||
| @ -27,6 +27,7 @@ end | ||||
| -- Namespaces | ||||
| local xmlns_fasten = "urn:xmpp:fasten:0"; | ||||
| local xmlns_moderate = "urn:xmpp:message-moderate:0"; | ||||
| local xmlns_occupant_id = "urn:xmpp:occupant-id:0"; | ||||
| local xmlns_retract = "urn:xmpp:message-retract:0"; | ||||
|  | ||||
| -- Discovering support | ||||
| @ -34,36 +35,17 @@ module:hook("muc-disco#info", function (event) | ||||
| 	event.reply:tag("feature", { var = xmlns_moderate }):up(); | ||||
| end); | ||||
|  | ||||
| -- Main handling | ||||
| module:hook("iq-set/bare/" .. xmlns_fasten .. ":apply-to", function (event) | ||||
| 	local stanza, origin = event.stanza, event.origin; | ||||
| -- TODO error registry, requires Prosody 0.12+ | ||||
|  | ||||
| 	-- Collect info we need | ||||
| 	local apply_to = stanza.tags[1]; | ||||
| 	local moderate_tag = apply_to:get_child("moderate", xmlns_moderate); | ||||
| 	if not moderate_tag then return end -- some other kind of fastening? | ||||
|  | ||||
| 	local reason = moderate_tag:get_child_text("reason"); | ||||
| 	local retract = moderate_tag:get_child("retract", xmlns_retract); | ||||
|  | ||||
| 	local room_jid = stanza.attr.to; | ||||
| -- moderate : function (string, string, string, boolean, string) : boolean, enum, enum, string | ||||
| local function moderate(actor, room_jid, stanza_id, retract, reason) | ||||
| 	local room_node = jid.split(room_jid); | ||||
| 	local room = mod_muc.get_room_from_jid(room_jid); | ||||
|  | ||||
| 	local stanza_id = apply_to.attr.id; | ||||
|  | ||||
| 	-- Permissions | ||||
| 	local actor = stanza.attr.from; | ||||
| 	-- Permissions is based on role, which is a property of a current occupant, | ||||
| 	-- so check if the actor is an occupant, otherwise if they have a reserved | ||||
| 	-- nickname that can be used to retrieve the role. | ||||
| 	local actor_nick = room:get_occupant_jid(actor); | ||||
| 	local affiliation = room:get_affiliation(actor); | ||||
| 	-- Retrieve their current role, iff they are in the room, otherwise what they | ||||
| 	-- would have based on affiliation. | ||||
| 	local role = room:get_role(actor_nick) or room:get_default_role(affiliation); | ||||
| 	if valid_roles[role or "none"] < valid_roles.moderator then | ||||
| 		origin.send(st.error_reply(stanza, "auth", "forbidden", "You need a role of at least 'moderator'")); | ||||
| 		return true; | ||||
| 	end | ||||
|  | ||||
| 	if not actor_nick then | ||||
| 		local reserved_nickname = room:get_affiliation_data(jid.bare(actor), "reserved_nickname"); | ||||
| 		if reserved_nickname then | ||||
| @ -71,6 +53,14 @@ module:hook("iq-set/bare/" .. xmlns_fasten .. ":apply-to", function (event) | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	-- Retrieve their current role, iff they are in the room, otherwise what they | ||||
| 	-- would have based on affiliation. | ||||
| 	local affiliation = room:get_affiliation(actor); | ||||
| 	local role = room:get_role(actor_nick) or room:get_default_role(affiliation); | ||||
| 	if valid_roles[role or "none"] < valid_roles.moderator then | ||||
| 		return false, "auth", "forbidden", "You need a role of at least 'moderator'"; | ||||
| 	end | ||||
|  | ||||
| 	-- Original stanza to base tombstone on | ||||
| 	local original, err; | ||||
| 	if muc_log_archive.get then | ||||
| @ -84,13 +74,13 @@ module:hook("iq-set/bare/" .. xmlns_fasten .. ":apply-to", function (event) | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	if not original then | ||||
| 		if err == "item-not-found" then | ||||
| 			origin.send(st.error_reply(stanza, "modify", "item-not-found")); | ||||
| 			return false, "modify", "item-not-found"; | ||||
| 		else | ||||
| 			origin.send(st.error_reply(stanza, "wait", "internal-server-error")); | ||||
| 			return false, "wait", "internal-server-error"; | ||||
| 		end | ||||
| 		return true; | ||||
| 	end | ||||
|  | ||||
|  | ||||
| @ -106,19 +96,39 @@ module:hook("iq-set/bare/" .. xmlns_fasten .. ":apply-to", function (event) | ||||
| 		announcement:text_tag("reason", reason); | ||||
| 	end | ||||
|  | ||||
| 	local moderated_occupant_id = original:get_child("occupant-id", xmlns_occupant_id); | ||||
| 	if room.get_occupant_id and moderated_occupant_id then | ||||
| 		announcement:add_direct_child(moderated_occupant_id); | ||||
| 	end | ||||
|  | ||||
| 	local actor_occupant = room:get_occupant_by_real_jid(actor) or room:new_occupant(jid.bare(actor), actor_nick); | ||||
| 	if room.get_occupant_id then | ||||
| 		-- This isn't a regular broadcast message going through the events occupant_id.lib hooks so we do this here | ||||
| 		announcement:add_direct_child(st.stanza("occupant-id", { xmlns = xmlns_occupant_id; id = room:get_occupant_id(actor_occupant) })) | ||||
| 	end | ||||
|  | ||||
| 	if muc_log_archive.set and retract then | ||||
| 		local tombstone = st.message({ from = original.attr.from, type = "groupchat", id = original.attr.id }) | ||||
| 			:tag("moderated", { xmlns = xmlns_moderate, by = actor_nick }) | ||||
| 				:tag("retracted", { xmlns = xmlns_retract, stamp = dt.datetime() }):up(); | ||||
|  | ||||
| 		if room.get_occupant_id then | ||||
| 			tombstone:add_direct_child(st.stanza("occupant-id", { xmlns = xmlns_occupant_id; id = room:get_occupant_id(actor_occupant) })) | ||||
|  | ||||
| 			if moderated_occupant_id then | ||||
| 				-- Copy occupant id from moderated message | ||||
| 				tombstone:add_child(moderated_occupant_id); | ||||
| 			end | ||||
| 		end | ||||
|  | ||||
| 		if reason then | ||||
| 			tombstone:text_tag("reason", reason); | ||||
| 		end | ||||
| 		tombstone:reset(); | ||||
|  | ||||
| 		local was_replaced = muc_log_archive:set(room_node, stanza_id, tombstone); | ||||
| 		if not was_replaced then | ||||
| 			origin.send(st.error_reply(stanza, "wait", "internal-server-error")); | ||||
| 			return true; | ||||
| 			return false, "wait", "internal-server-error"; | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| @ -126,6 +136,32 @@ module:hook("iq-set/bare/" .. xmlns_fasten .. ":apply-to", function (event) | ||||
| 	module:log("info", "Message with id '%s' in room %s moderated by %s, reason: %s", stanza_id, room_jid, actor, reason); | ||||
| 	room:broadcast_message(announcement); | ||||
|  | ||||
| 	return true; | ||||
| end | ||||
|  | ||||
| -- Main handling | ||||
| module:hook("iq-set/bare/" .. xmlns_fasten .. ":apply-to", function (event) | ||||
| 	local stanza, origin = event.stanza, event.origin; | ||||
|  | ||||
| 	local actor = stanza.attr.from; | ||||
| 	local room_jid = stanza.attr.to; | ||||
|  | ||||
| 	-- Collect info we need | ||||
| 	local apply_to = stanza.tags[1]; | ||||
| 	local moderate_tag = apply_to:get_child("moderate", xmlns_moderate); | ||||
| 	if not moderate_tag then return end -- some other kind of fastening? | ||||
|  | ||||
| 	local reason = moderate_tag:get_child_text("reason"); | ||||
| 	local retract = moderate_tag:get_child("retract", xmlns_retract); | ||||
|  | ||||
| 	local stanza_id = apply_to.attr.id; | ||||
|  | ||||
| 	local ok, error_type, error_condition, error_text = moderate(actor, room_jid, stanza_id, retract, reason); | ||||
| 	if not ok then | ||||
| 		origin.send(st.error_reply(stanza, error_type, error_condition, error_text)); | ||||
| 		return true; | ||||
| 	end | ||||
|  | ||||
| 	origin.send(st.reply(stanza)); | ||||
| 	return true; | ||||
| end); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user