From 9ed689b5f131c2ea6380b9a8c06b996f9dba1a5d Mon Sep 17 00:00:00 2001 From: John Livingston Date: Tue, 13 Feb 2024 16:45:12 +0100 Subject: [PATCH] Slow mode WIP (#192): * backend rejects messages when the slow mode is not respected. --- CHANGELOG.md | 1 + .../mod_muc_slow_mode/mod_muc_slow_mode.lua | 86 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf4e176e..9e47d9cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Slow mode (#192): * new option in room configuration to set the slow mode delay (new prosody module mod_muc_slow_mode). * default delay is configurable in channel's chat rooms options. + * backend rejects messages when the slow mode is not respected. ### Minor changes and fixes diff --git a/prosody-modules/mod_muc_slow_mode/mod_muc_slow_mode.lua b/prosody-modules/mod_muc_slow_mode/mod_muc_slow_mode.lua index 7471a510..1a0001b4 100644 --- a/prosody-modules/mod_muc_slow_mode/mod_muc_slow_mode.lua +++ b/prosody-modules/mod_muc_slow_mode/mod_muc_slow_mode.lua @@ -9,10 +9,19 @@ -- 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"; + -- Getter/Setter local function get_slow_mode_delay(room) return room._data.slow_mode_delay or 0; @@ -60,3 +69,80 @@ module:hook("muc-config-submitted/muc#roomconfig_slow_mode_delay", function(even end); module:hook("muc-config-form", add_form_option, 100-4); + +-- handling groupchat messages +function handle_groupchat(event) + local origin, stanza = event.origin, event.stanza; + local room = event.room; + + local delay = get_slow_mode_delay(room) or 0; + if delay <= 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, delay is %s, now - previous is %s", + -- actor_jid, + -- previous or 0, + -- now, + -- delay, + -- (now - (previous or 0)) + -- ); + if ((not previous) or (now - previous > delay)) 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 " .. delay .. " seconds between messages. Please try again later" + ); + + -- Note: following commented lines were inspired by mod_muc_limits, but it seems it is not required. + -- local body = stanza:get_child_text("body"); + -- 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);