Software /
code /
prosody-modules
File
mod_muc_slow_mode/mod_muc_slow_mode.lua @ 6055:23c4c61a1068
mod_muc_gateway_optimize: New module to optimize muc presence to remote gateways
Some gateways are happy to receive presence for each participant
in MUCs that they are in only once, to any one of their joined JIDs.
author | Stephen Paul Weber <singpolyma@singpolyma.net> |
---|---|
date | Sun, 17 Nov 2024 22:32:52 -0500 |
parent | 5967:d7c207964aa5 |
line wrap: on
line source
-- mod_muc_slow_mode -- -- SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/> -- SPDX-License-Identifier: AGPL-3.0-only -- -- Implements: XEP-????: MUC Slow Mode (XEP to come). -- -- Imports local st = require "util.stanza"; local jid_bare = require "util.jid".bare; local gettime = require 'socket'.gettime; -- Plugin dependencies local mod_muc = module:depends "muc"; local muc_util = module:require "muc/util"; local valid_roles = muc_util.valid_roles; -- Namespaces local xmlns_muc = "http://jabber.org/protocol/muc"; -- Options -- form_position: the position in the room config form (this value will be passed as priority for the "muc-config-form" hook). -- Depending on your application, it is possible that the slow mode is more important than other fields (for example for a video streaming service). -- So there is an option to change this. -- By default, field will be between muc#roomconfig_changesubject and muc#roomconfig_moderatedroom local form_position = module:get_option_number("slow_mode_duration_form_position") or 80-2; -- Getter/Setter local function get_slow_mode_duration(room) return room._data.slow_mode_duration or 0; end local function set_slow_mode_duration(room, duration) if duration then duration = assert(tonumber(duration), "Slow mode duration is not a valid number"); end if duration and duration < 0 then duration = 0; end if get_slow_mode_duration(room) == duration then return false; end room._data.slow_mode_duration = duration; return true; end -- Discovering support local function add_disco_form(event) table.insert(event.form, { name = "muc#roominfo_slow_mode_duration"; value = ""; }); event.formdata["muc#roominfo_slow_mode_duration"] = get_slow_mode_duration(event.room); end module:hook("muc-disco#info", add_disco_form); -- Config form declaration local function add_form_option(event) table.insert(event.form, { name = "muc#roomconfig_slow_mode_duration"; type = "text-single"; datatype = "xs:integer"; range_min = 0; label = "Slow Mode (0=disabled, any positive integer= users can send a message every X seconds.)"; -- desc = ""; value = get_slow_mode_duration(event.room); }); end module:hook("muc-config-submitted/muc#roomconfig_slow_mode_duration", function(event) if set_slow_mode_duration(event.room, event.value) then -- status 104 = configuration change: Inform occupants that a non-privacy-related room configuration change has occurred event.status_codes["104"] = true; end end); module:hook("muc-config-form", add_form_option, form_position); -- handling groupchat messages function handle_groupchat(event) local origin, stanza = event.origin, event.stanza; local room = event.room; -- only consider messages with body (ie: ignore chatstate and other non-text xmpp messages) local body = stanza:get_child_text("body") if not body or #body < 1 then -- module:log("debug", "No body, message accepted"); return; end local duration = get_slow_mode_duration(room) or 0; if duration <= 0 then -- no slow mode for this room -- module:log("debug", "No slow mode for this room"); return; end -- Checking user's permissions (moderators are not subject to slow mode) local actor = stanza.attr.from; local actor_nick = room:get_occupant_jid(actor); local actor_jid = jid_bare(actor); -- Only checking role, not affiliation (slow mode only applies on users currently connected to the room) local role = room:get_role(actor_nick); if valid_roles[role or "none"] >= valid_roles.moderator then -- user bypasses the slow mode. -- module:log("debug", "User is moderator, bypassing slow mode"); return; end if not room.slow_mode_last_messages then -- We store last message time for each users in room.slow_mode_last_messages: -- * key: bare jid (without the nickname) -- * value: last message timestamp -- If room is cleared from memory, these data are lost. But should not be an issue. -- For now, i don't clean slow_mode_last_messages, it should not use too much memory. -- module:log("debug", "Initializing slow_mode_last_messages for the room."); room.slow_mode_last_messages = {}; end local now = gettime(); local previous = room.slow_mode_last_messages[actor_jid]; -- module:log( -- "debug", -- "Last message for user %s was at %s, now is %s, duration is %s, now - previous is %s", -- actor_jid, -- previous or 0, -- now, -- duration, -- (now - (previous or 0)) -- ); if ((not previous) or (now - previous > duration)) then -- module:log("debug", "Message accepted"); room.slow_mode_last_messages[actor_jid] = now; return; end module:log("debug", "Bouncing message for user %s", actor_nick); local reply = st.error_reply( stanza, -- error_type = 'wait' (see descriptions in RFC 6120 https://xmpp.org/rfcs/rfc6120.html#stanzas-error-syntax) "wait", -- error_condition = 'policy-violation' (see RFC 6120 Defined Error Conditions https://xmpp.org/rfcs/rfc6120.html#stanzas-error-conditions) "policy-violation", "You have exceeded the limit imposed by the slow mode in this room. You have to wait " .. duration .. " seconds between messages. Please try again later" ); -- Note: following commented lines were inspired by mod_muc_limits, but it seems it is not required. -- if body then -- reply:up():tag("body"):text(body):up(); -- end -- local x = stanza:get_child("x", xmlns_muc); -- if x then -- reply:add_child(st.clone(x)); -- end origin.send(reply); return true; -- stoping propagation end module:hook("muc-occupant-groupchat", handle_groupchat);