Terms&Conditions (#18) WIP:

* channel terms
This commit is contained in:
John Livingston 2024-06-25 11:28:46 +02:00
parent b110456029
commit a06ef00e2a
No known key found for this signature in database
GPG Key ID: B17B5640CE66CDBC
9 changed files with 101 additions and 42 deletions

View File

@ -14,6 +14,9 @@ local get_room_from_jid = rawget(mod_muc, "get_room_from_jid");
module:depends"http"; module:depends"http";
local mod_muc_peertubelivechat_terms = module:depends"muc_peertubelivechat_terms";
local set_muc_terms = rawget(mod_muc_peertubelivechat_terms, "set_muc_terms");
function check_auth(routes) function check_auth(routes)
local function check_request_auth(event) local function check_request_auth(event)
local apikey = module:get_option_string("peertubelivechat_manage_rooms_apikey", "") local apikey = module:get_option_string("peertubelivechat_manage_rooms_apikey", "")
@ -94,6 +97,12 @@ local function update_room(event)
must104 = true; must104 = true;
end end
end end
if (type(config.livechat_muc_terms) == "string") then
-- to easily detect if the value is given or not, we consider that the caller passes "" when terms must be deleted.
if set_muc_terms then
set_muc_terms(room, config.livechat_muc_terms or nil);
end
end
if type(config.removeAffiliationsFor) == "table" then if type(config.removeAffiliationsFor) == "table" then
-- array of jids -- array of jids
for _, jid in ipairs(config.removeAffiliationsFor) do for _, jid in ipairs(config.removeAffiliationsFor) do

View File

@ -121,6 +121,11 @@ local function apply_config(room, settings)
if (type(config.mute_anonymous) == "boolean") then if (type(config.mute_anonymous) == "boolean") then
room._data.x_peertubelivechat_mute_anonymous = config.mute_anonymous; room._data.x_peertubelivechat_mute_anonymous = config.mute_anonymous;
end end
if (type(config.livechat_muc_terms) == "string") then
-- we don't need to use set_muc_terms here, as this is called for a newly created room
-- (and thus we don't need to broadcast changes)
room._data.livechat_muc_terms = config.livechat_muc_terms;
end
elseif config ~= nil then elseif config ~= nil then
module:log("error", "Invalid config returned from API for %s: %q", room.jid, config); module:log("error", "Invalid config returned from API for %s: %q", room.jid, config);
return nil, "format", { field = "config" }; return nil, "format", { field = "config" };

View File

@ -29,6 +29,6 @@ The nickname that will be used by service messages.
This module reserves the nickname, so than nobody can use it in MUC rooms This module reserves the nickname, so than nobody can use it in MUC rooms
(we don't want any user to spoof this nickname). (we don't want any user to spoof this nickname).
### muc_terms ### muc_terms_global
The global terms. The global terms.

View File

@ -7,30 +7,71 @@
-- Please see the Peertube livechat plugin copyright information. -- Please see the Peertube livechat plugin copyright information.
-- https://livingston.frama.io/peertube-plugin-livechat/credits/ -- https://livingston.frama.io/peertube-plugin-livechat/credits/
-- --
-- Exposed functions:
-- get_muc_terms
-- set_muc_terms
local jid_escape = require "util.jid".escape; local jid_escape = require "util.jid".escape;
local jid_resource = require "util.jid".resource; local jid_resource = require "util.jid".resource;
local st = require "util.stanza"; local st = require "util.stanza";
local id = require "util.id"; local id = require "util.id";
local service_nickname = module:get_option_string("muc_terms_service_nickname", "Service"); local service_nickname = module:get_option_string("muc_terms_service_nickname", "Service");
local global_terms = module:get_option_string("muc_terms", ""); local global_terms = module:get_option_string("muc_terms_global", "");
local function create_terms_message(room, type, terms)
local from = room.jid .. '/' .. jid_escape(service_nickname);
module:log("debug", "Creating %s terms message from %s (room %s)", type, from, room);
local msg = st.message({
type = "groupchat",
from = from,
id = id.medium()
}, terms)
:tag('x-livechat-terms', { type = type }):up(); -- adding a custom tag to specify that it is a "terms" message, so that frontend can display it with a special template.
return msg;
end
-- MUC Getter/Setter
function get_muc_terms(room)
return room._data.livechat_muc_terms or nil;
end
function set_muc_terms(room, terms)
terms = terms or nil;
if get_muc_terms(room) == terms then return false; end
room._data.livechat_muc_terms = terms;
if terms ~= nil then
-- we must send new terms to all occupants.
local msg = create_terms_message(room, "muc", terms);
module:log("debug", "Broadcasting terms message to room %s", room);
room:broadcast_message(msg);
end
return true;
end
-- send the terms when joining: -- send the terms when joining:
function send_terms(event) local function send_terms(event)
local origin = event.origin; local origin = event.origin;
local room = event.room; local room = event.room;
local occupant = event.occupant; local occupant = event.occupant;
if global_terms then if global_terms then
module:log("debug", "Sending global terms to %s", occupant.jid);
local msg = create_terms_message(room, "global", global_terms);
msg.attr.to = occupant.jid;
origin.send(msg);
end
local muc_terms = get_muc_terms(room);
if muc_terms then
local from = room.jid .. '/' .. jid_escape(service_nickname); local from = room.jid .. '/' .. jid_escape(service_nickname);
module:log("debug", "Sending global terms to %s from %s (room %s)", occupant.jid, from, room); module:log("debug", "Sending muc terms to %s", occupant.jid);
local message = st.message({ local msg = create_terms_message(room, "muc", muc_terms);
type = "groupchat", msg.attr.to = occupant.jid;
to = occupant.jid, origin.send(msg);
from = from,
id = id.medium()
}, global_terms)
:tag('x-livechat-terms', { type = "global" }):up(); -- adding a custom tag to specify that it is a "terms" message, so that frontend can display it with a special template.
origin.send(message);
end end
end end
-- Note: we could do that on muc-occupant-joined or muc-occupant-session-new. -- Note: we could do that on muc-occupant-joined or muc-occupant-session-new.
@ -39,7 +80,7 @@ end
module:hook("muc-occupant-session-new", send_terms); module:hook("muc-occupant-session-new", send_terms);
-- reserve the service_nickname: -- reserve the service_nickname:
function enforce_nick_policy(event) local function enforce_nick_policy(event)
local origin, stanza = event.origin, event.stanza; local origin, stanza = event.origin, event.stanza;
local requested_nick = jid_resource(stanza.attr.to); local requested_nick = jid_resource(stanza.attr.to);
local room = event.room; local room = event.room;

View File

@ -119,6 +119,7 @@ async function initChannelConfiguration (options: RegisterServerOptions): Promis
// FIXME: this piece of code should not be in this file (nothing to do with initChannelConfiguration, // FIXME: this piece of code should not be in this file (nothing to do with initChannelConfiguration,
// but will be more efficient to add here, as we already tested hasChat). // but will be more efficient to add here, as we already tested hasChat).
// Note: no need to await here, would only degrade performances. // Note: no need to await here, would only degrade performances.
// FIXME: should also update livechat_muc_terms if channel has changed.
updateProsodyRoom(options, video.uuid, { updateProsodyRoom(options, video.uuid, {
name: video.name name: video.name
}).then( }).then(

View File

@ -64,6 +64,7 @@ async function updateProsodyRoom (
data: { data: {
name?: string name?: string
slow_mode_duration?: number slow_mode_duration?: number
livechat_muc_terms?: string
addAffiliations?: Affiliations addAffiliations?: Affiliations
removeAffiliationsFor?: string[] removeAffiliationsFor?: string[]
} }
@ -92,6 +93,9 @@ async function updateProsodyRoom (
if (('slow_mode_duration' in data) && data.slow_mode_duration !== undefined) { if (('slow_mode_duration' in data) && data.slow_mode_duration !== undefined) {
apiData.slow_mode_duration = data.slow_mode_duration apiData.slow_mode_duration = data.slow_mode_duration
} }
if ('livechat_muc_terms' in data) {
apiData.livechat_muc_terms = data.livechat_muc_terms ?? ''
}
if (('addAffiliations' in data) && data.addAffiliations !== undefined) { if (('addAffiliations' in data) && data.addAffiliations !== undefined) {
apiData.addAffiliations = data.addAffiliations apiData.addAffiliations = data.addAffiliations
} }

View File

@ -246,7 +246,7 @@ class ProsodyConfigContent {
this.muc.add('modules_enabled', 'muc_peertubelivechat_terms') this.muc.add('modules_enabled', 'muc_peertubelivechat_terms')
this.muc.set('muc_terms_service_nickname', 'Peertube') this.muc.set('muc_terms_service_nickname', 'Peertube')
if (chatTerms) { if (chatTerms) {
this.muc.set('muc_terms', new ConfigEntryValueMultiLineString(chatTerms)) this.muc.set('muc_terms_global', new ConfigEntryValueMultiLineString(chatTerms))
} }
} }

View File

@ -5,7 +5,7 @@
import type { RegisterServerOptions } from '@peertube/peertube-types' import type { RegisterServerOptions } from '@peertube/peertube-types'
import type { RoomConf } from 'xmppjs-chat-bot' import type { RoomConf } from 'xmppjs-chat-bot'
import { getProsodyDomain } from '../prosody/config/domain' import { getProsodyDomain } from '../prosody/config/domain'
import { listProsodyRooms } from '../prosody/api/manage-rooms' import { listProsodyRooms, updateProsodyRoom } from '../prosody/api/manage-rooms'
import { getChannelInfosById } from '../database/channel' import { getChannelInfosById } from '../database/channel'
import { ChannelConfigurationOptions } from '../../../shared/lib/types' import { ChannelConfigurationOptions } from '../../../shared/lib/types'
import { import {
@ -292,10 +292,7 @@ class RoomChannel {
this.logger.info('Syncing...') this.logger.info('Syncing...')
this.isWriting = true this.isWriting = true
// Note 2024-05-15: slow_mode_duration becomes a default value. const prosodyRoomUpdates = new Map<string, Parameters<typeof updateProsodyRoom>[2]>()
// We dont need prosodyRoomUpdates anymore. but keeping the code commented,
// if we want to enable again a similar sync.
// const prosodyRoomUpdates = new Map<string, Parameters<typeof updateProsodyRoom>[2]>()
try { try {
const data = this._serializeData() // must be atomic const data = this._serializeData() // must be atomic
@ -355,12 +352,12 @@ class RoomChannel {
await BotConfiguration.singleton().updateRoom(roomJID, botConf) await BotConfiguration.singleton().updateRoom(roomJID, botConf)
// // Now we also must update some room metadata (slow mode duration, ...) // Now we also must update some room metadata on Prosody side (livechat_muc_terms, ...)
// // This can be done without waiting for the API call to finish, but we don't want to send thousands of // This can be done without waiting for the API call to finish, but we don't want to send thousands of
// // API calls at the same time. So storing data in a map, and we well launch it sequentially at the end // API calls at the same time. So storing data in a map, and we well launch it sequentially at the end
// prosodyRoomUpdates.set(roomJID, { prosodyRoomUpdates.set(roomJID, {
// slow_mode_duration: channelConfigurationOptions.slowMode.duration livechat_muc_terms: channelConfigurationOptions.terms
// }) })
this.roomConfToUpdate.delete(roomJID) this.roomConfToUpdate.delete(roomJID)
} }
@ -374,22 +371,22 @@ class RoomChannel {
this.isWriting = false this.isWriting = false
} }
// if (prosodyRoomUpdates.size) { if (prosodyRoomUpdates.size) {
// // Here we don't have to wait. // Here we don't have to wait.
// // If it fails (for example because we are turning off prosody), it is not a big deal. // If it fails (for example because we are turning off prosody), it is not a big deal.
// // Does not worth the cost to wait. // Does not worth the cost to wait.
// // eslint-disable-next-line @typescript-eslint/no-misused-promises // eslint-disable-next-line @typescript-eslint/no-misused-promises
// setTimeout(async () => { setTimeout(async () => {
// this.logger.info('Syncing done, but still some data to send to Prosody') this.logger.info('Syncing done, but still some data to send to Prosody')
// for (const [roomJID, data] of prosodyRoomUpdates.entries()) { for (const [roomJID, data] of prosodyRoomUpdates.entries()) {
// try { try {
// await updateProsodyRoom(this.options, roomJID, data) await updateProsodyRoom(this.options, roomJID, data)
// } catch (err) { } catch (err) {
// this.logger.error(`Failed updating prosody room info: "${err as string}".`) this.logger.error(`Failed updating prosody room info: "${err as string}".`)
// } }
// } }
// }, 0) }, 0)
// } }
} }
/** /**

View File

@ -37,6 +37,7 @@ interface RoomDefaults {
// Following fields are specific to livechat (for now), and requires a customized version for mod_muc_http_defaults. // Following fields are specific to livechat (for now), and requires a customized version for mod_muc_http_defaults.
slow_mode_duration?: number slow_mode_duration?: number
mute_anonymous?: boolean mute_anonymous?: boolean
livechat_muc_terms?: string
} }
affiliations?: Affiliations affiliations?: Affiliations
} }
@ -50,7 +51,8 @@ async function _getChannelSpecificOptions (
return { return {
slow_mode_duration: channelOptions.slowMode.duration, slow_mode_duration: channelOptions.slowMode.duration,
mute_anonymous: channelOptions.mute.anonymous mute_anonymous: channelOptions.mute.anonymous,
livechat_muc_terms: channelOptions.terms
} }
} }