mod_muc_moderation
This commit is contained in:
parent
a602c7870a
commit
236a9301bc
@ -6,6 +6,7 @@
|
||||
|
||||
* Builtin prosody use a working dir provided by Peertube (needs Peertube >= 3.2.0)
|
||||
* Starting with Peertube 3.2.0, builtin prosody save room history on server. So when a user connects, he can get previously send messages.
|
||||
* Starting with Peertube 3.2.0, builtin prosody also activate mod_muc_moderation, enabling moderators to moderate messages. NB: unfortunately it requires Prosody>=0.11.8, which is not released yet (ability to change overwrite old messages on internal storages).
|
||||
|
||||
### Fixes
|
||||
|
||||
|
@ -18,7 +18,7 @@ This roadmap is given as an indication. It will be updated as we go along accord
|
||||
[ ] | [ ] | Builtin Prosody | Check with yunohost how to integrate.
|
||||
[ ] | [ ] | Documentation | Rewrite documentation for more clarity. Add screenshots. Separate user and admin documentation.
|
||||
[ ] | [ ] | ConverseJS | UI: make custom templates, for a better UI/UX. Autoshow muc participants depending on the chat window width.
|
||||
[ ] | [ ] | Builtin Prosody | Allow moderators to delete messages (mod_muc_moderation)
|
||||
[x] | [ ] | Builtin Prosody | Allow moderators to delete messages (mod_muc_moderation). NB: Prosody dont allow it for now on «internal» storage, will be available in next version (0.11.8?). | Not Released yet
|
||||
[ ] | [ ] | ConverseJS | For anonymous user, automatically log in with a random nickname (and allow to change afterward)
|
||||
[ ] | [x] | JS | Modernise code to use new placeholders provided by Peertube 3.2.0 (with or without backward compatibility)
|
||||
[ ] | [x] | Settings | Replace some checkbox by a select (for the webchat mode). Migrate old checkbox values.
|
||||
|
32
prosody-modules/mod_muc_moderation/README.markdown
Normal file
32
prosody-modules/mod_muc_moderation/README.markdown
Normal file
@ -0,0 +1,32 @@
|
||||
# Introduction
|
||||
|
||||
This module implements [XEP-0425: Message Moderation].
|
||||
|
||||
# Usage
|
||||
|
||||
Moderation is done via a supporting client and requires a `moderator`
|
||||
role in the channel / group chat.
|
||||
|
||||
# Configuration
|
||||
|
||||
Example [MUC component][doc:chatrooms] configuration:
|
||||
|
||||
``` {.lua}
|
||||
VirtualHost "channels.example.com" "muc"
|
||||
modules_enabled = {
|
||||
"muc_mam",
|
||||
"muc_moderation",
|
||||
}
|
||||
```
|
||||
|
||||
# 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.
|
||||
|
||||
## Clients
|
||||
|
||||
- Tested with [Converse.js](https://conversejs.org/)
|
||||
[v6.0.1](https://github.com/conversejs/converse.js/releases/tag/v6.0.1)
|
123
prosody-modules/mod_muc_moderation/mod_muc_moderation.lua
Normal file
123
prosody-modules/mod_muc_moderation/mod_muc_moderation.lua
Normal file
@ -0,0 +1,123 @@
|
||||
-- mod_muc_moderation
|
||||
--
|
||||
-- Copyright (C) 2015-2020 Kim Alvefur
|
||||
--
|
||||
-- This file is MIT licensed.
|
||||
--
|
||||
-- Implements: XEP-0425: Message Moderation
|
||||
--
|
||||
-- Imports
|
||||
local dt = require "util.datetime";
|
||||
local id = require "util.id";
|
||||
local jid = require "util.jid";
|
||||
local st = require "util.stanza";
|
||||
|
||||
-- Plugin dependencies
|
||||
local mod_muc = module:depends "muc";
|
||||
|
||||
local muc_util = module:require "muc/util";
|
||||
local valid_roles = muc_util.valid_roles;
|
||||
|
||||
local muc_log_archive = module:open_store("muc_log", "archive");
|
||||
|
||||
if not muc_log_archive.set then
|
||||
module:log("warn", "Selected archive storage module does not support message replacement, no tombstones will be saved");
|
||||
end
|
||||
|
||||
-- Namespaces
|
||||
local xmlns_fasten = "urn:xmpp:fasten:0";
|
||||
local xmlns_moderate = "urn:xmpp:message-moderate:0";
|
||||
local xmlns_retract = "urn:xmpp:message-retract:0";
|
||||
|
||||
-- Discovering support
|
||||
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;
|
||||
|
||||
-- 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 room_jid = stanza.attr.to;
|
||||
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;
|
||||
local actor_nick = room:get_occupant_jid(actor);
|
||||
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
|
||||
origin.send(st.error_reply(stanza, "auth", "forbidden", "You need a role of at least 'moderator'"));
|
||||
return true;
|
||||
end
|
||||
|
||||
-- Original stanza to base tombstone on
|
||||
local original, err;
|
||||
if muc_log_archive.get then
|
||||
original, err = muc_log_archive:get(room_node, stanza_id);
|
||||
else
|
||||
-- COMPAT missing :get API
|
||||
err = "item-not-found";
|
||||
for i, item in muc_log_archive:find(room_node, { key = stanza_id, limit = 1 }) do
|
||||
if i == stanza_id then
|
||||
original, err = item, nil;
|
||||
end
|
||||
end
|
||||
end
|
||||
if not original then
|
||||
if err == "item-not-found" then
|
||||
origin.send(st.error_reply(stanza, "modify", "item-not-found"));
|
||||
else
|
||||
origin.send(st.error_reply(stanza, "wait", "internal-server-error"));
|
||||
end
|
||||
return true;
|
||||
end
|
||||
|
||||
-- Replacements
|
||||
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();
|
||||
|
||||
local announcement = st.message({ from = room_jid, type = "groupchat", id = id.medium(), })
|
||||
:tag("apply-to", { xmlns = xmlns_fasten, id = stanza_id })
|
||||
:tag("moderated", { xmlns = xmlns_moderate, by = actor_nick })
|
||||
:tag("retract", { xmlns = xmlns_retract }):up();
|
||||
|
||||
if reason then
|
||||
tombstone:text_tag("reason", reason);
|
||||
announcement:text_tag("reason", reason);
|
||||
end
|
||||
|
||||
if muc_log_archive.set then
|
||||
-- Tombstone
|
||||
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;
|
||||
end
|
||||
end
|
||||
|
||||
-- Done, tell people about it
|
||||
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);
|
||||
|
||||
origin.send(st.reply(stanza));
|
||||
return true;
|
||||
end);
|
||||
|
||||
module:hook("muc-message-is-historic", function (event)
|
||||
-- Ensure moderation messages are stored
|
||||
if event.stanza.attr.from == event.room.jid then
|
||||
return event.stanza:get_child("apply-to", xmlns_fasten);
|
||||
end
|
||||
end, 1);
|
@ -120,6 +120,7 @@ class ProsodyConfigContent {
|
||||
this.global.set('pidfile', this.paths.pid)
|
||||
this.global.set('plugin_paths', [this.paths.modules])
|
||||
this.global.set('data_path', this.paths.data)
|
||||
this.global.set('default_storage', 'internal')
|
||||
this.global.set('storage', 'internal')
|
||||
|
||||
this.global.set('modules_enabled', [
|
||||
@ -222,6 +223,9 @@ class ProsodyConfigContent {
|
||||
this.muc.set('log_all_rooms', true)
|
||||
this.muc.set('muc_log_expires_after', duration)
|
||||
this.muc.set('muc_log_cleanup_interval', 4 * 60 * 60)
|
||||
|
||||
// We can also use mod_muc_moderation
|
||||
this.muc.add('modules_enabled', 'muc_moderation')
|
||||
}
|
||||
|
||||
setLog (level: ProsodyLogLevel, syslog?: ProsodyLogLevel[]): void {
|
||||
|
Loading…
x
Reference in New Issue
Block a user