diff --git a/prosody-modules/mod_http_peertubelivechat_manage_rooms/mod_http_peertubelivechat_manage_rooms.lua b/prosody-modules/mod_http_peertubelivechat_manage_rooms/mod_http_peertubelivechat_manage_rooms.lua index 48d074c8..ae3f1e18 100644 --- a/prosody-modules/mod_http_peertubelivechat_manage_rooms/mod_http_peertubelivechat_manage_rooms.lua +++ b/prosody-modules/mod_http_peertubelivechat_manage_rooms/mod_http_peertubelivechat_manage_rooms.lua @@ -14,6 +14,9 @@ local get_room_from_jid = rawget(mod_muc, "get_room_from_jid"); 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) local function check_request_auth(event) local apikey = module:get_option_string("peertubelivechat_manage_rooms_apikey", "") @@ -94,6 +97,12 @@ local function update_room(event) must104 = true; 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 -- array of jids for _, jid in ipairs(config.removeAffiliationsFor) do diff --git a/prosody-modules/mod_muc_http_defaults/mod_muc_http_defaults.lua b/prosody-modules/mod_muc_http_defaults/mod_muc_http_defaults.lua index 2899eb43..cbb94992 100644 --- a/prosody-modules/mod_muc_http_defaults/mod_muc_http_defaults.lua +++ b/prosody-modules/mod_muc_http_defaults/mod_muc_http_defaults.lua @@ -121,6 +121,11 @@ local function apply_config(room, settings) if (type(config.mute_anonymous) == "boolean") then room._data.x_peertubelivechat_mute_anonymous = config.mute_anonymous; 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 module:log("error", "Invalid config returned from API for %s: %q", room.jid, config); return nil, "format", { field = "config" }; diff --git a/prosody-modules/mod_muc_peertubelivechat_terms/README.md b/prosody-modules/mod_muc_peertubelivechat_terms/README.md index 0c2229f5..6bd5cadd 100644 --- a/prosody-modules/mod_muc_peertubelivechat_terms/README.md +++ b/prosody-modules/mod_muc_peertubelivechat_terms/README.md @@ -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 (we don't want any user to spoof this nickname). -### muc_terms +### muc_terms_global The global terms. diff --git a/prosody-modules/mod_muc_peertubelivechat_terms/mod_muc_peertubelivechat_terms.lua b/prosody-modules/mod_muc_peertubelivechat_terms/mod_muc_peertubelivechat_terms.lua index be4272ca..f45ffe82 100644 --- a/prosody-modules/mod_muc_peertubelivechat_terms/mod_muc_peertubelivechat_terms.lua +++ b/prosody-modules/mod_muc_peertubelivechat_terms/mod_muc_peertubelivechat_terms.lua @@ -7,30 +7,71 @@ -- Please see the Peertube livechat plugin copyright information. -- 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_resource = require "util.jid".resource; local st = require "util.stanza"; local id = require "util.id"; 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: -function send_terms(event) +local function send_terms(event) local origin = event.origin; local room = event.room; local occupant = event.occupant; 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); - module:log("debug", "Sending global terms to %s from %s (room %s)", occupant.jid, from, room); - local message = st.message({ - type = "groupchat", - to = occupant.jid, - 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); + module:log("debug", "Sending muc terms to %s", occupant.jid); + local msg = create_terms_message(room, "muc", muc_terms); + msg.attr.to = occupant.jid; + origin.send(msg); end end -- 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); -- reserve the service_nickname: -function enforce_nick_policy(event) +local function enforce_nick_policy(event) local origin, stanza = event.origin, event.stanza; local requested_nick = jid_resource(stanza.attr.to); local room = event.room; diff --git a/server/lib/configuration/channel/init.ts b/server/lib/configuration/channel/init.ts index 205b8ece..87788bd4 100644 --- a/server/lib/configuration/channel/init.ts +++ b/server/lib/configuration/channel/init.ts @@ -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, // but will be more efficient to add here, as we already tested hasChat). // 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, { name: video.name }).then( diff --git a/server/lib/prosody/api/manage-rooms.ts b/server/lib/prosody/api/manage-rooms.ts index 5b331566..61e65bd8 100644 --- a/server/lib/prosody/api/manage-rooms.ts +++ b/server/lib/prosody/api/manage-rooms.ts @@ -64,6 +64,7 @@ async function updateProsodyRoom ( data: { name?: string slow_mode_duration?: number + livechat_muc_terms?: string addAffiliations?: Affiliations removeAffiliationsFor?: string[] } @@ -92,6 +93,9 @@ async function updateProsodyRoom ( if (('slow_mode_duration' in data) && data.slow_mode_duration !== undefined) { 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) { apiData.addAffiliations = data.addAffiliations } diff --git a/server/lib/prosody/config/content.ts b/server/lib/prosody/config/content.ts index 2dfbee44..9853c302 100644 --- a/server/lib/prosody/config/content.ts +++ b/server/lib/prosody/config/content.ts @@ -246,7 +246,7 @@ class ProsodyConfigContent { this.muc.add('modules_enabled', 'muc_peertubelivechat_terms') this.muc.set('muc_terms_service_nickname', 'Peertube') if (chatTerms) { - this.muc.set('muc_terms', new ConfigEntryValueMultiLineString(chatTerms)) + this.muc.set('muc_terms_global', new ConfigEntryValueMultiLineString(chatTerms)) } } diff --git a/server/lib/room-channel/room-channel-class.ts b/server/lib/room-channel/room-channel-class.ts index 1c082eff..193d4323 100644 --- a/server/lib/room-channel/room-channel-class.ts +++ b/server/lib/room-channel/room-channel-class.ts @@ -5,7 +5,7 @@ import type { RegisterServerOptions } from '@peertube/peertube-types' import type { RoomConf } from 'xmppjs-chat-bot' 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 { ChannelConfigurationOptions } from '../../../shared/lib/types' import { @@ -292,10 +292,7 @@ class RoomChannel { this.logger.info('Syncing...') this.isWriting = true - // Note 2024-05-15: slow_mode_duration becomes a default value. - // We dont need prosodyRoomUpdates anymore. but keeping the code commented, - // if we want to enable again a similar sync. - // const prosodyRoomUpdates = new Map[2]>() + const prosodyRoomUpdates = new Map[2]>() try { const data = this._serializeData() // must be atomic @@ -355,12 +352,12 @@ class RoomChannel { await BotConfiguration.singleton().updateRoom(roomJID, botConf) - // // Now we also must update some room metadata (slow mode duration, ...) - // // 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 - // prosodyRoomUpdates.set(roomJID, { - // slow_mode_duration: channelConfigurationOptions.slowMode.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 + // API calls at the same time. So storing data in a map, and we well launch it sequentially at the end + prosodyRoomUpdates.set(roomJID, { + livechat_muc_terms: channelConfigurationOptions.terms + }) this.roomConfToUpdate.delete(roomJID) } @@ -374,22 +371,22 @@ class RoomChannel { this.isWriting = false } - // if (prosodyRoomUpdates.size) { - // // Here we don't have to wait. - // // If it fails (for example because we are turning off prosody), it is not a big deal. - // // Does not worth the cost to wait. - // // eslint-disable-next-line @typescript-eslint/no-misused-promises - // setTimeout(async () => { - // this.logger.info('Syncing done, but still some data to send to Prosody') - // for (const [roomJID, data] of prosodyRoomUpdates.entries()) { - // try { - // await updateProsodyRoom(this.options, roomJID, data) - // } catch (err) { - // this.logger.error(`Failed updating prosody room info: "${err as string}".`) - // } - // } - // }, 0) - // } + if (prosodyRoomUpdates.size) { + // Here we don't have to wait. + // If it fails (for example because we are turning off prosody), it is not a big deal. + // Does not worth the cost to wait. + // eslint-disable-next-line @typescript-eslint/no-misused-promises + setTimeout(async () => { + this.logger.info('Syncing done, but still some data to send to Prosody') + for (const [roomJID, data] of prosodyRoomUpdates.entries()) { + try { + await updateProsodyRoom(this.options, roomJID, data) + } catch (err) { + this.logger.error(`Failed updating prosody room info: "${err as string}".`) + } + } + }, 0) + } } /** diff --git a/server/lib/routers/api/room.ts b/server/lib/routers/api/room.ts index 57a740b8..11dc10a6 100644 --- a/server/lib/routers/api/room.ts +++ b/server/lib/routers/api/room.ts @@ -37,6 +37,7 @@ interface RoomDefaults { // Following fields are specific to livechat (for now), and requires a customized version for mod_muc_http_defaults. slow_mode_duration?: number mute_anonymous?: boolean + livechat_muc_terms?: string } affiliations?: Affiliations } @@ -50,7 +51,8 @@ async function _getChannelSpecificOptions ( return { slow_mode_duration: channelOptions.slowMode.duration, - mute_anonymous: channelOptions.mute.anonymous + mute_anonymous: channelOptions.mute.anonymous, + livechat_muc_terms: channelOptions.terms } }