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
This commit is contained in:
John Livingston 2024-03-07 16:22:14 +01:00
parent 2115b352a4
commit 4dd4f18965
No known key found for this signature in database
GPG Key ID: B17B5640CE66CDBC
10 changed files with 205 additions and 51 deletions

View File

@ -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

View File

@ -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.

View File

@ -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;
};
});

View File

@ -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 {

View File

@ -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<ProsodyRoomDesc[]> {
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
}

View File

@ -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<ProsodyRoomDesc[]> {
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<boolean> {
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
}

View File

@ -313,7 +313,7 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
// TODO: add a settings to choose?
config.useDefaultPersistent()
config.useListRoomsApi(apikey)
config.useManageRoomsApi(apikey)
config.usePeertubeVCards(basePeertubeUrl)
config.useAnonymousRandomVCards(paths.avatars, paths.avatarsFiles)

View File

@ -415,9 +415,9 @@ class ProsodyConfigContent {
this.muc.set('muc_room_default_persistent', true)
}
useListRoomsApi (apikey: string): void {
this.muc.add('modules_enabled', 'http_peertubelivechat_list_rooms')
this.muc.set('peertubelivechat_list_rooms_apikey', apikey)
useManageRoomsApi (apikey: string): void {
this.muc.add('modules_enabled', 'http_peertubelivechat_manage_rooms')
this.muc.set('peertubelivechat_manage_rooms_apikey', apikey)
}
useTestModule (prosodyApikey: string, apiurl: string): void {

View File

@ -1,7 +1,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/list-rooms'
import { listProsodyRooms } from '../prosody/api/manage-rooms'
import { getChannelInfosById } from '../database/channel'
import { ChannelConfigurationOptions } from '../../../shared/lib/types'
import {

View File

@ -9,7 +9,7 @@ import { fetchMissingRemoteServerInfos } from '../federation/fetch-infos'
import { getConverseJSParams } from '../conversejs/params'
import { setCurrentProsody, delCurrentProsody } from '../prosody/api/host'
import { getChannelInfosById } from '../database/channel'
import { listProsodyRooms } from '../prosody/api/list-rooms'
import { listProsodyRooms } from '../prosody/api/manage-rooms'
import * as path from 'path'
const fs = require('fs').promises