From 4dd4f18965378e4e20831153c45779821847a3e9 Mon Sep 17 00:00:00 2001 From: John Livingston Date: Thu, 7 Mar 2024 16:22:14 +0100 Subject: [PATCH] Fix #87: updating chat room title when video/channel title is changed: * renaming module list_rooms to manage_rooms * added some API to update room info * when a video or a channel is updated, sending an API call to update the room --- CHANGELOG.md | 1 + .../README.md | 4 +- ...od_http_peertubelivechat_manage_rooms.lua} | 49 +++++++- server/lib/configuration/channel/init.ts | 39 +++++++ server/lib/prosody/api/list-rooms.ts | 41 ------- server/lib/prosody/api/manage-rooms.ts | 110 ++++++++++++++++++ server/lib/prosody/config.ts | 2 +- server/lib/prosody/config/content.ts | 6 +- server/lib/room-channel/room-channel-class.ts | 2 +- server/lib/routers/webchat.ts | 2 +- 10 files changed, 205 insertions(+), 51 deletions(-) rename prosody-modules/{mod_http_peertubelivechat_list_rooms => mod_http_peertubelivechat_manage_rooms}/README.md (63%) rename prosody-modules/{mod_http_peertubelivechat_list_rooms/mod_http_peertubelivechat_list_rooms.lua => mod_http_peertubelivechat_manage_rooms/mod_http_peertubelivechat_manage_rooms.lua} (54%) delete mode 100644 server/lib/prosody/api/list-rooms.ts create mode 100644 server/lib/prosody/api/manage-rooms.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 381868a0..34a05024 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## ??? (Not Released Yet) +* Fix #87: updating chat room title when video/channel title is changed. * Updating xmppjs-chat-box version. ## 8.3.2 diff --git a/prosody-modules/mod_http_peertubelivechat_list_rooms/README.md b/prosody-modules/mod_http_peertubelivechat_manage_rooms/README.md similarity index 63% rename from prosody-modules/mod_http_peertubelivechat_list_rooms/README.md rename to prosody-modules/mod_http_peertubelivechat_manage_rooms/README.md index 6c4fe736..e14aaefd 100644 --- a/prosody-modules/mod_http_peertubelivechat_list_rooms/README.md +++ b/prosody-modules/mod_http_peertubelivechat_manage_rooms/README.md @@ -1,5 +1,5 @@ -# mod_http_peertubelivechat_list_rooms +# mod_http_peertubelivechat_manage_rooms -This module is a custom module that allows Peertube server to list chat rooms. +This module is a custom module that allows Peertube server to list chat rooms, and update some meta data. This module is part of peertube-plugin-livechat, and is under the same LICENSE. diff --git a/prosody-modules/mod_http_peertubelivechat_list_rooms/mod_http_peertubelivechat_list_rooms.lua b/prosody-modules/mod_http_peertubelivechat_manage_rooms/mod_http_peertubelivechat_manage_rooms.lua similarity index 54% rename from prosody-modules/mod_http_peertubelivechat_list_rooms/mod_http_peertubelivechat_list_rooms.lua rename to prosody-modules/mod_http_peertubelivechat_manage_rooms/mod_http_peertubelivechat_manage_rooms.lua index 83ee98be..5e732e64 100644 --- a/prosody-modules/mod_http_peertubelivechat_list_rooms/mod_http_peertubelivechat_list_rooms.lua +++ b/prosody-modules/mod_http_peertubelivechat_manage_rooms/mod_http_peertubelivechat_manage_rooms.lua @@ -1,15 +1,17 @@ local json = require "util.json"; local jid_split = require"util.jid".split; local array = require "util.array"; +local st = require "util.stanza"; local mod_muc = module:depends"muc"; -local all_rooms = rawget(mod_muc, "all_rooms") +local all_rooms = rawget(mod_muc, "all_rooms"); +local get_room_from_jid = rawget(mod_muc, "get_room_from_jid"); module:depends"http"; function check_auth(routes) local function check_request_auth(event) - local apikey = module:get_option_string("peertubelivechat_list_rooms_apikey", "") + local apikey = module:get_option_string("peertubelivechat_manage_rooms_apikey", "") if apikey == "" then return false, 500; end @@ -55,8 +57,51 @@ local function list_rooms(event) return json.encode_array(rooms_json); end +local function update_room(event) + local request, response = event.request, event.response; + event.response.headers["Content-Type"] = "application/json"; + + local config = json.decode(request.body); + if not config.jid then + return json.encode({ + result = "failed"; + }); + end + + local room = get_room_from_jid(config.jid); + if not room then + return json.encode({ + result = "failed"; + }); + end + + local must104 = false; + + if type(config.name) == "string" then + if room:get_name() ~= config.name then + room:set_name(config.name); + must104 = true; + end + end + + if must104 then + -- we must broadcast a status 104 message, so that clients can update room info + local msg = st.message({type='groupchat', from=room.jid}) + :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) + msg:tag("status", {code = '104';}):up(); + msg:up(); + room:broadcast_message(msg); + end + + return json.encode({ + result = "ok"; + changed = must104; + }); +end + module:provides("http", { route = check_auth { ["GET /list-rooms"] = list_rooms; + ["POST /update-room"] = update_room; }; }); diff --git a/server/lib/configuration/channel/init.ts b/server/lib/configuration/channel/init.ts index 7bb021c8..9aae761f 100644 --- a/server/lib/configuration/channel/init.ts +++ b/server/lib/configuration/channel/init.ts @@ -2,6 +2,8 @@ import type { RegisterServerOptions, MVideoFullLight, VideoChannel } from '@peer import { RoomChannel } from '../../room-channel' import { fillVideoCustomFields } from '../../custom-fields' import { videoHasWebchat } from '../../../../shared/lib/video' +import { updateProsodyRoom } from '../../prosody/api/manage-rooms' +import { getChannelInfosById } from '../../database/channel' /** * Register stuffs related to channel configuration @@ -98,11 +100,48 @@ async function initChannelConfiguration (options: RegisterServerOptions): Promis logger.debug(`Ensuring a room-channel link between room ${roomLocalPart} and channel ${video.channelId}`) RoomChannel.singleton().link(video.channelId, roomLocalPart) + + if (settings['prosody-room-type'] === 'video') { + // Also updating chatroom meta data. + // 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. + updateProsodyRoom(options, video.channelId, video.uuid, { + name: video.name + }).then( + () => {}, + (err) => logger.error(err) + ) + } } catch (err) { logger.error(err) } } }) + + registerHook({ + target: 'action:api.video-channel.updated', + handler: async (params: { videoChannel: VideoChannel }) => { + const channel = await getChannelInfosById(options, params.videoChannel.id, true) + if (!channel) { return } + + // FIXME: this piece of code should not be in this file (nothing to do with initChannelConfiguration, + // but for now i keep it close to the similar code on video.updated hook). + const settings = await options.settingsManager.getSettings([ + 'prosody-room-type' + ]) + if (settings['prosody-room-type'] === 'channel') { + const jid = 'channel.' + channel.id.toString() + // Note: no need to await here, would only degrade performances. + updateProsodyRoom(options, channel.id, jid, { + name: channel.displayName + }).then( + () => {}, + (err) => logger.error(err) + ) + } + } + }) } export { diff --git a/server/lib/prosody/api/list-rooms.ts b/server/lib/prosody/api/list-rooms.ts deleted file mode 100644 index 19781e4e..00000000 --- a/server/lib/prosody/api/list-rooms.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { RegisterServerOptions } from '@peertube/peertube-types' -import { getCurrentProsody } from './host' -import { getAPIKey } from '../../apikey' -const got = require('got') - -interface ProsodyRoomDesc { - jid: string - localpart: string - name: string - lang: string - description: string - lasttimestamp?: number -} - -async function listProsodyRooms (options: RegisterServerOptions): Promise { - const logger = options.peertubeHelpers.logger - - const currentProsody = getCurrentProsody() - if (!currentProsody) { - throw new Error('It seems that prosody is not binded... Cant list rooms.') - } - - // Requesting on localhost, because currentProsody.host does not always resolves correctly (docker use case, ...) - const apiUrl = `http://localhost:${currentProsody.port}/peertubelivechat_list_rooms/list-rooms` - logger.debug('Calling list rooms API on url: ' + apiUrl) - const rooms = await got(apiUrl, { - method: 'GET', - headers: { - authorization: 'Bearer ' + await getAPIKey(options), - host: currentProsody.host - }, - responseType: 'json', - resolveBodyOnly: true - }) - - return rooms -} - -export { - listProsodyRooms -} diff --git a/server/lib/prosody/api/manage-rooms.ts b/server/lib/prosody/api/manage-rooms.ts new file mode 100644 index 00000000..44c14e57 --- /dev/null +++ b/server/lib/prosody/api/manage-rooms.ts @@ -0,0 +1,110 @@ +import type { RegisterServerOptions } from '@peertube/peertube-types' +import { getCurrentProsody } from './host' +import { getAPIKey } from '../../apikey' +import { getProsodyDomain } from '../config/domain' +const got = require('got') + +interface ProsodyRoomDesc { + jid: string + localpart: string + name: string + lang: string + description: string + lasttimestamp?: number +} + +/** + * Lists existing chatrooms. + * @param options Peertube server options + * @returns List of chat rooms on the Prosody server + */ +async function listProsodyRooms (options: RegisterServerOptions): Promise { + const logger = options.peertubeHelpers.logger + + const currentProsody = getCurrentProsody() + if (!currentProsody) { + throw new Error('It seems that prosody is not binded... Cant list rooms.') + } + + // Requesting on localhost, because currentProsody.host does not always resolves correctly (docker use case, ...) + const apiUrl = `http://localhost:${currentProsody.port}/peertubelivechat_manage_rooms/list-rooms` + logger.debug('Calling list rooms API on url: ' + apiUrl) + const rooms = await got(apiUrl, { + method: 'GET', + headers: { + authorization: 'Bearer ' + await getAPIKey(options), + host: currentProsody.host + }, + responseType: 'json', + resolveBodyOnly: true + }) + + return rooms +} + +/** + * Update room metadata on the Prosody server. + * Uses an API provided by mod_http_peertubelivechat_manage_rooms. + * + * Note: could be called without verifying that the room exists. + * On the Prosody side, non existing rooms will be ignored. + * @param options Peertube server options + * @param channelId associated channelId + * @param jid Room JID (can be only the local part, or the local + domain) + * @param data Data to update + * @returns true if success + */ +async function updateProsodyRoom ( + options: RegisterServerOptions, + channelId: number | string, + jid: string, + data: { + name: string + } +): Promise { + const logger = options.peertubeHelpers.logger + + const currentProsody = getCurrentProsody() + if (!currentProsody) { + throw new Error('It seems that prosody is not binded... Cant update room.') + } + + if (!jid.includes('@')) { + jid = jid + '@room.' + await getProsodyDomain(options) + } + + logger.debug('Calling update room for ' + jid) + + // Requesting on localhost, because currentProsody.host does not always resolves correctly (docker use case, ...) + const apiUrl = `http://localhost:${currentProsody.port}/peertubelivechat_manage_rooms/update-room` + const apiData = { + jid, + name: data.name + } + try { + logger.debug('Calling update room API on url: ' + apiUrl + ', with data: ' + JSON.stringify(apiData)) + const result = await got(apiUrl, { + method: 'POST', + headers: { + authorization: 'Bearer ' + await getAPIKey(options), + host: currentProsody.host + }, + json: apiData, + responseType: 'json', + resolveBodyOnly: true + }) + + logger.debug('Update room API response: ' + JSON.stringify(result)) + } catch (err) { + // We consider it is not very bad if the metadata are not correctly updated. + // Nothing too important. + logger.error(`Failed to update room: ' ${err as string}`) + return false + } + return true +} + +export { + listProsodyRooms, + updateProsodyRoom +} diff --git a/server/lib/prosody/config.ts b/server/lib/prosody/config.ts index e44d78d6..f26a108e 100644 --- a/server/lib/prosody/config.ts +++ b/server/lib/prosody/config.ts @@ -313,7 +313,7 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise