Chat Federation, connection to remote chat:
Front-end connect using BOSH or WS directly on the remote server. If use is logged-in, his nickname is use as default nickname.
This commit is contained in:
parent
b85a1dc90a
commit
5d323b2dfd
@ -1,6 +1,6 @@
|
|||||||
import type { Video } from '@peertube/peertube-types'
|
import type { Video } from '@peertube/peertube-types'
|
||||||
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
|
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
|
||||||
import { videoHasWebchat } from 'shared/lib/video'
|
import { videoHasWebchat, videoHasRemoteWebchat } from 'shared/lib/video'
|
||||||
import { logger } from './videowatch/logger'
|
import { logger } from './videowatch/logger'
|
||||||
import { closeSVG, openBlankChatSVG, openChatSVG, shareChatUrlSVG } from './videowatch/buttons'
|
import { closeSVG, openBlankChatSVG, openChatSVG, shareChatUrlSVG } from './videowatch/buttons'
|
||||||
import { displayButton, displayButtonOptions } from './videowatch/button'
|
import { displayButton, displayButtonOptions } from './videowatch/button'
|
||||||
@ -247,19 +247,21 @@ function register (registerOptions: RegisterClientOptions): void {
|
|||||||
logger.log('No chat for anonymous users')
|
logger.log('No chat for anonymous users')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!videoHasWebchat(s, video)) {
|
if (!videoHasWebchat(s, video) && !videoHasRemoteWebchat(s, video)) {
|
||||||
logger.log('This video has no webchat')
|
logger.log('This video has no webchat')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let showShareUrlButton: boolean = false
|
let showShareUrlButton: boolean = false
|
||||||
const chatShareUrl = settings['chat-share-url'] ?? ''
|
if (video.isLocal) { // No need for shareButton on remote chats.
|
||||||
if (chatShareUrl === 'everyone') {
|
const chatShareUrl = settings['chat-share-url'] ?? ''
|
||||||
showShareUrlButton = true
|
if (chatShareUrl === 'everyone') {
|
||||||
} else if (chatShareUrl === 'owner') {
|
showShareUrlButton = true
|
||||||
showShareUrlButton = guessIsMine(registerOptions, video)
|
} else if (chatShareUrl === 'owner') {
|
||||||
} else if (chatShareUrl === 'owner+moderators') {
|
showShareUrlButton = guessIsMine(registerOptions, video)
|
||||||
showShareUrlButton = guessIsMine(registerOptions, video) || guessIamIModerator(registerOptions)
|
} else if (chatShareUrl === 'owner+moderators') {
|
||||||
|
showShareUrlButton = guessIsMine(registerOptions, video) || guessIamIModerator(registerOptions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insertChatDom(container as HTMLElement, video, !!settings['chat-open-blank'], showShareUrlButton).then(() => {
|
insertChatDom(container as HTMLElement, video, !!settings['chat-open-blank'], showShareUrlButton).then(() => {
|
||||||
|
@ -82,6 +82,7 @@ function randomNick (base: string): string {
|
|||||||
|
|
||||||
interface InitConverseParams {
|
interface InitConverseParams {
|
||||||
jid: string
|
jid: string
|
||||||
|
remoteAnonymousXMPPServer: boolean
|
||||||
assetsPath: string
|
assetsPath: string
|
||||||
room: string
|
room: string
|
||||||
boshServiceUrl: string
|
boshServiceUrl: string
|
||||||
@ -95,6 +96,7 @@ interface InitConverseParams {
|
|||||||
}
|
}
|
||||||
window.initConverse = async function initConverse ({
|
window.initConverse = async function initConverse ({
|
||||||
jid,
|
jid,
|
||||||
|
remoteAnonymousXMPPServer,
|
||||||
assetsPath,
|
assetsPath,
|
||||||
room,
|
room,
|
||||||
boshServiceUrl,
|
boshServiceUrl,
|
||||||
@ -205,6 +207,7 @@ window.initConverse = async function initConverse ({
|
|||||||
// TODO: params.clear_messages_on_reconnection = true when muc_mam will be available.
|
// TODO: params.clear_messages_on_reconnection = true when muc_mam will be available.
|
||||||
|
|
||||||
let isAuthenticated: boolean = false
|
let isAuthenticated: boolean = false
|
||||||
|
let isRemoteWithNicknameSet: boolean = false
|
||||||
if (authenticationUrl === '') {
|
if (authenticationUrl === '') {
|
||||||
throw new Error('Missing authenticationUrl')
|
throw new Error('Missing authenticationUrl')
|
||||||
}
|
}
|
||||||
@ -218,19 +221,27 @@ window.initConverse = async function initConverse ({
|
|||||||
|
|
||||||
const auth = await authenticatedMode(authenticationUrl)
|
const auth = await authenticatedMode(authenticationUrl)
|
||||||
if (auth) {
|
if (auth) {
|
||||||
params.authentication = 'login'
|
if (remoteAnonymousXMPPServer) {
|
||||||
params.auto_login = true
|
// Spécial case: anonymous connection to remote XMPP server.
|
||||||
params.jid = auth.jid
|
if (auth.nickname) {
|
||||||
params.password = auth.password
|
params.nickname = auth.nickname
|
||||||
if (auth.nickname) {
|
isRemoteWithNicknameSet = true
|
||||||
params.nickname = auth.nickname
|
}
|
||||||
} else {
|
} else {
|
||||||
params.muc_nickname_from_jid = true
|
params.authentication = 'login'
|
||||||
|
params.auto_login = true
|
||||||
|
params.jid = auth.jid
|
||||||
|
params.password = auth.password
|
||||||
|
if (auth.nickname) {
|
||||||
|
params.nickname = auth.nickname
|
||||||
|
} else {
|
||||||
|
params.muc_nickname_from_jid = true
|
||||||
|
}
|
||||||
|
// We dont need the keepalive. And I suppose it is related to some bugs when opening a previous chat window.
|
||||||
|
params.keepalive = false
|
||||||
|
isAuthenticated = true
|
||||||
|
// FIXME: use params.oauth_providers?
|
||||||
}
|
}
|
||||||
// We dont need the keepalive. And I suppose it is related to some bugs when opening a previous chat window.
|
|
||||||
params.keepalive = false
|
|
||||||
isAuthenticated = true
|
|
||||||
// FIXME: use params.oauth_providers?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
@ -267,7 +278,7 @@ window.initConverse = async function initConverse ({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (autoViewerMode && !isAuthenticated) {
|
if (autoViewerMode && !isAuthenticated && !isRemoteWithNicknameSet) {
|
||||||
converse.plugins.add('livechatViewerModePlugin', {
|
converse.plugins.add('livechatViewerModePlugin', {
|
||||||
dependencies: ['converse-muc', 'converse-muc-views'],
|
dependencies: ['converse-muc', 'converse-muc-views'],
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
initConverse({
|
initConverse({
|
||||||
jid: '{{JID}}',
|
jid: '{{JID}}',
|
||||||
|
remoteAnonymousXMPPServer: '{{REMOTE_ANONYMOUS_XMPP_SERVER}}' === 'true',
|
||||||
assetsPath : '{{BASE_STATIC_URL}}conversejs/',
|
assetsPath : '{{BASE_STATIC_URL}}conversejs/',
|
||||||
room: '{{ROOM}}',
|
room: '{{ROOM}}',
|
||||||
boshServiceUrl: '{{BOSH_SERVICE_URL}}',
|
boshServiceUrl: '{{BOSH_SERVICE_URL}}',
|
||||||
|
36
server/lib/federation/connection-infos.ts
Normal file
36
server/lib/federation/connection-infos.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import type { LiveChatJSONLDAttribute } from './types'
|
||||||
|
|
||||||
|
interface AnonymousConnectionInfos {
|
||||||
|
roomJID: string
|
||||||
|
boshUri?: string
|
||||||
|
wsUri?: string
|
||||||
|
userJID: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function anonymousConnectionInfos (livechatInfos: LiveChatJSONLDAttribute | false): AnonymousConnectionInfos | null {
|
||||||
|
if (!livechatInfos) { return null }
|
||||||
|
if (!livechatInfos.links) { return null }
|
||||||
|
if (livechatInfos.type !== 'xmpp') { return null }
|
||||||
|
const r: AnonymousConnectionInfos = {
|
||||||
|
roomJID: livechatInfos.jid,
|
||||||
|
userJID: ''
|
||||||
|
}
|
||||||
|
for (const link of livechatInfos.links) {
|
||||||
|
// Note: userJID is on both links. But should have the same value.
|
||||||
|
if (link.type === 'xmpp-bosh-anonymous') {
|
||||||
|
r.boshUri = link.url
|
||||||
|
r.userJID = link.jid
|
||||||
|
} else if (link.type === 'xmpp-websocket-anonymous') {
|
||||||
|
r.wsUri = link.url
|
||||||
|
r.userJID = link.jid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (r.userJID === '') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
anonymousConnectionInfos
|
||||||
|
}
|
@ -7,7 +7,7 @@ import { prosodyCheckUserPassword, prosodyRegisterUser, prosodyUserRegistered }
|
|||||||
import { getUserNickname } from '../helpers'
|
import { getUserNickname } from '../helpers'
|
||||||
import { Affiliations, getVideoAffiliations, getChannelAffiliations } from '../prosody/config/affiliations'
|
import { Affiliations, getVideoAffiliations, getChannelAffiliations } from '../prosody/config/affiliations'
|
||||||
import { getProsodyDomain } from '../prosody/config/domain'
|
import { getProsodyDomain } from '../prosody/config/domain'
|
||||||
import { fillVideoCustomFields, fillVideoRemoteLiveChat } from '../custom-fields'
|
import { fillVideoCustomFields } from '../custom-fields'
|
||||||
import { getChannelInfosById } from '../database/channel'
|
import { getChannelInfosById } from '../database/channel'
|
||||||
|
|
||||||
// See here for description: https://modules.prosody.im/mod_muc_http_defaults.html
|
// See here for description: https://modules.prosody.im/mod_muc_http_defaults.html
|
||||||
@ -98,7 +98,6 @@ async function initApiRouter (options: RegisterServerOptions): Promise<Router> {
|
|||||||
|
|
||||||
// Adding the custom fields and data:
|
// Adding the custom fields and data:
|
||||||
await fillVideoCustomFields(options, video)
|
await fillVideoCustomFields(options, video)
|
||||||
if (video.remote) { await fillVideoRemoteLiveChat(options, video) }
|
|
||||||
|
|
||||||
// check settings (chat enabled for this video?)
|
// check settings (chat enabled for this video?)
|
||||||
const settings = await options.settingsManager.getSettings([
|
const settings = await options.settingsManager.getSettings([
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { RegisterServerOptions, MVideoThumbnail } from '@peertube/peertube-types'
|
import type { RegisterServerOptions, MVideoThumbnail, SettingEntries } from '@peertube/peertube-types'
|
||||||
import type { Router, Request, Response, NextFunction } from 'express'
|
import type { Router, Request, Response, NextFunction } from 'express'
|
||||||
import type {
|
import type {
|
||||||
ProsodyListRoomsResult, ProsodyListRoomsResultRoom
|
ProsodyListRoomsResult, ProsodyListRoomsResultRoom
|
||||||
@ -13,6 +13,9 @@ import { getAPIKey } from '../apikey'
|
|||||||
import { getChannelInfosById, getChannelNameById } from '../database/channel'
|
import { getChannelInfosById, getChannelNameById } from '../database/channel'
|
||||||
import { isAutoColorsAvailable, areAutoColorsValid, AutoColors } from '../../../shared/lib/autocolors'
|
import { isAutoColorsAvailable, areAutoColorsValid, AutoColors } from '../../../shared/lib/autocolors'
|
||||||
import { getBoshUri, getWSUri } from '../uri/webchat'
|
import { getBoshUri, getWSUri } from '../uri/webchat'
|
||||||
|
import { getVideoLiveChatInfos } from '../federation/storage'
|
||||||
|
import { LiveChatJSONLDAttribute } from '../federation/types'
|
||||||
|
import { anonymousConnectionInfos } from '../federation/connection-infos'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
const got = require('got')
|
const got = require('got')
|
||||||
|
|
||||||
@ -46,15 +49,10 @@ async function initWebchatRouter (options: RegisterServerOptionsV5): Promise<Rou
|
|||||||
const settings = await settingsManager.getSettings([
|
const settings = await settingsManager.getSettings([
|
||||||
'prosody-room-type',
|
'prosody-room-type',
|
||||||
'disable-websocket',
|
'disable-websocket',
|
||||||
'converse-theme', 'converse-autocolors'
|
'converse-theme', 'converse-autocolors',
|
||||||
|
'federation-no-remote-chat'
|
||||||
])
|
])
|
||||||
|
|
||||||
const boshUri = getBoshUri(options)
|
|
||||||
const wsUri = settings['disable-websocket']
|
|
||||||
? ''
|
|
||||||
: (getWSUri(options) ?? '')
|
|
||||||
|
|
||||||
let room: string
|
|
||||||
let autoViewerMode: boolean = false
|
let autoViewerMode: boolean = false
|
||||||
let forceReadonly: 'true' | 'false' | 'noscroll' = 'false'
|
let forceReadonly: 'true' | 'false' | 'noscroll' = 'false'
|
||||||
let converseJSTheme: string = settings['converse-theme'] as string
|
let converseJSTheme: string = settings['converse-theme'] as string
|
||||||
@ -62,27 +60,6 @@ async function initWebchatRouter (options: RegisterServerOptionsV5): Promise<Rou
|
|||||||
if (!/^\w+$/.test(converseJSTheme)) {
|
if (!/^\w+$/.test(converseJSTheme)) {
|
||||||
converseJSTheme = 'peertube'
|
converseJSTheme = 'peertube'
|
||||||
}
|
}
|
||||||
const prosodyDomain = await getProsodyDomain(options)
|
|
||||||
const jid = 'anon.' + prosodyDomain
|
|
||||||
if (req.query.forcetype === '1') {
|
|
||||||
// We come from the room list in the settings page.
|
|
||||||
// Here we don't read the prosody-room-type settings,
|
|
||||||
// but use the roomKey format.
|
|
||||||
// NB: there is no extra security. Any user can add this parameter.
|
|
||||||
// This is not an issue: the setting will be tested at the room creation.
|
|
||||||
// No room can be created in the wrong mode.
|
|
||||||
if (/^channel\.\d+$/.test(roomKey)) {
|
|
||||||
room = 'channel.{{CHANNEL_ID}}@room.' + prosodyDomain
|
|
||||||
} else {
|
|
||||||
room = '{{VIDEO_UUID}}@room.' + prosodyDomain
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (settings['prosody-room-type'] === 'channel') {
|
|
||||||
room = 'channel.{{CHANNEL_ID}}@room.' + prosodyDomain
|
|
||||||
} else {
|
|
||||||
room = '{{VIDEO_UUID}}@room.' + prosodyDomain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const authenticationUrl = options.peertubeHelpers.config.getWebserverUrl() +
|
const authenticationUrl = options.peertubeHelpers.config.getWebserverUrl() +
|
||||||
getBaseRouterRoute(options) +
|
getBaseRouterRoute(options) +
|
||||||
@ -100,6 +77,7 @@ async function initWebchatRouter (options: RegisterServerOptionsV5): Promise<Rou
|
|||||||
|
|
||||||
let video: MVideoThumbnail | undefined
|
let video: MVideoThumbnail | undefined
|
||||||
let channelId: number
|
let channelId: number
|
||||||
|
let remoteChatInfos: LiveChatJSONLDAttribute | undefined
|
||||||
const channelMatches = roomKey.match(/^channel\.(\d+)$/)
|
const channelMatches = roomKey.match(/^channel\.(\d+)$/)
|
||||||
if (channelMatches?.[1]) {
|
if (channelMatches?.[1]) {
|
||||||
channelId = parseInt(channelMatches[1])
|
channelId = parseInt(channelMatches[1])
|
||||||
@ -113,7 +91,17 @@ async function initWebchatRouter (options: RegisterServerOptionsV5): Promise<Rou
|
|||||||
const uuid = roomKey // must be a video UUID.
|
const uuid = roomKey // must be a video UUID.
|
||||||
video = await peertubeHelpers.videos.loadByIdOrUUID(uuid)
|
video = await peertubeHelpers.videos.loadByIdOrUUID(uuid)
|
||||||
if (!video) {
|
if (!video) {
|
||||||
throw new Error('Video not found')
|
res.status(404)
|
||||||
|
res.send('Not found')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (video.remote) {
|
||||||
|
remoteChatInfos = settings['federation-no-remote-chat'] ? false : await getVideoLiveChatInfos(options, video)
|
||||||
|
if (!remoteChatInfos) {
|
||||||
|
res.status(404)
|
||||||
|
res.send('Not found')
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
channelId = video.channelId
|
channelId = video.channelId
|
||||||
}
|
}
|
||||||
@ -121,28 +109,28 @@ async function initWebchatRouter (options: RegisterServerOptionsV5): Promise<Rou
|
|||||||
let page = '' + (converseJSIndex as string)
|
let page = '' + (converseJSIndex as string)
|
||||||
const baseStaticUrl = getBaseStaticRoute(options)
|
const baseStaticUrl = getBaseStaticRoute(options)
|
||||||
page = page.replace(/{{BASE_STATIC_URL}}/g, baseStaticUrl)
|
page = page.replace(/{{BASE_STATIC_URL}}/g, baseStaticUrl)
|
||||||
page = page.replace(/{{JID}}/g, jid)
|
|
||||||
// Computing the room name...
|
let connectionInfos: ConnectionInfos | null
|
||||||
if (room.includes('{{VIDEO_UUID}}')) {
|
if (video?.remote) {
|
||||||
if (!video) {
|
connectionInfos = await _remoteConnectionInfos(remoteChatInfos ?? false)
|
||||||
throw new Error('Missing video')
|
} else {
|
||||||
}
|
connectionInfos = await _localConnectionInfos(
|
||||||
room = room.replace(/{{VIDEO_UUID}}/g, video.uuid)
|
options,
|
||||||
|
settings,
|
||||||
|
roomKey,
|
||||||
|
video,
|
||||||
|
channelId,
|
||||||
|
req.query.forcetype === '1'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
room = room.replace(/{{CHANNEL_ID}}/g, `${channelId}`)
|
if (!connectionInfos) {
|
||||||
if (room.includes('{{CHANNEL_NAME}}')) {
|
res.status(404)
|
||||||
const channelName = await getChannelNameById(options, channelId)
|
res.send('No compatible way to connect to remote chat')
|
||||||
if (channelName === null) {
|
return
|
||||||
throw new Error('Channel not found')
|
|
||||||
}
|
|
||||||
if (!/^[a-zA-Z0-9_.]+$/.test(channelName)) {
|
|
||||||
// FIXME: see if there is a response here https://github.com/Chocobozzz/PeerTube/issues/4301 for allowed chars
|
|
||||||
peertubeHelpers.logger.error(`Invalid channel name, contains unauthorized chars: '${channelName}'`)
|
|
||||||
throw new Error('Invalid channel name, contains unauthorized chars')
|
|
||||||
}
|
|
||||||
room = room.replace(/{{CHANNEL_NAME}}/g, channelName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
page = page.replace(/{{JID}}/g, connectionInfos.userJID)
|
||||||
|
|
||||||
let autocolorsStyles = ''
|
let autocolorsStyles = ''
|
||||||
if (
|
if (
|
||||||
settings['converse-autocolors'] &&
|
settings['converse-autocolors'] &&
|
||||||
@ -195,9 +183,10 @@ async function initWebchatRouter (options: RegisterServerOptionsV5): Promise<Rou
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ... then inject it in the page.
|
// ... then inject it in the page.
|
||||||
page = page.replace(/{{ROOM}}/g, room)
|
page = page.replace(/{{ROOM}}/g, connectionInfos.roomJID)
|
||||||
page = page.replace(/{{BOSH_SERVICE_URL}}/g, boshUri)
|
page = page.replace(/{{BOSH_SERVICE_URL}}/g, connectionInfos.boshUri)
|
||||||
page = page.replace(/{{WS_SERVICE_URL}}/g, wsUri)
|
page = page.replace(/{{WS_SERVICE_URL}}/g, connectionInfos.wsUri ?? '')
|
||||||
|
page = page.replace(/{{REMOTE_ANONYMOUS_XMPP_SERVER}}/g, connectionInfos.remoteXMPPServer ? 'true' : 'false')
|
||||||
page = page.replace(/{{AUTHENTICATION_URL}}/g, authenticationUrl)
|
page = page.replace(/{{AUTHENTICATION_URL}}/g, authenticationUrl)
|
||||||
page = page.replace(/{{AUTOVIEWERMODE}}/g, autoViewerMode ? 'true' : 'false')
|
page = page.replace(/{{AUTOVIEWERMODE}}/g, autoViewerMode ? 'true' : 'false')
|
||||||
page = page.replace(/{{CONVERSEJS_THEME}}/g, converseJSTheme)
|
page = page.replace(/{{CONVERSEJS_THEME}}/g, converseJSTheme)
|
||||||
@ -377,6 +366,95 @@ async function enableProxyRoute (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ConnectionInfos {
|
||||||
|
userJID: string
|
||||||
|
roomJID: string
|
||||||
|
boshUri: string
|
||||||
|
wsUri?: string
|
||||||
|
remoteXMPPServer: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _remoteConnectionInfos (remoteChatInfos: LiveChatJSONLDAttribute): Promise<ConnectionInfos | null> {
|
||||||
|
if (!remoteChatInfos) { throw new Error('Should have remote chat infos for remote videos') }
|
||||||
|
const connectionInfos = anonymousConnectionInfos(remoteChatInfos ?? false)
|
||||||
|
if (!connectionInfos || !connectionInfos.boshUri) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
userJID: connectionInfos.userJID,
|
||||||
|
roomJID: connectionInfos.roomJID,
|
||||||
|
boshUri: connectionInfos.boshUri,
|
||||||
|
wsUri: connectionInfos.wsUri,
|
||||||
|
remoteXMPPServer: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _localConnectionInfos (
|
||||||
|
options: RegisterServerOptions,
|
||||||
|
settings: SettingEntries,
|
||||||
|
roomKey: string,
|
||||||
|
video: MVideoThumbnail | undefined,
|
||||||
|
channelId: number,
|
||||||
|
forceType: boolean
|
||||||
|
): Promise<ConnectionInfos> {
|
||||||
|
const prosodyDomain = await getProsodyDomain(options)
|
||||||
|
const jid = 'anon.' + prosodyDomain
|
||||||
|
const boshUri = getBoshUri(options)
|
||||||
|
const wsUri = settings['disable-websocket']
|
||||||
|
? ''
|
||||||
|
: (getWSUri(options) ?? '')
|
||||||
|
|
||||||
|
// Computing the room name...
|
||||||
|
let room: string
|
||||||
|
if (forceType) {
|
||||||
|
// We come from the room list in the settings page.
|
||||||
|
// Here we don't read the prosody-room-type settings,
|
||||||
|
// but use the roomKey format.
|
||||||
|
// NB: there is no extra security. Any user can add this parameter.
|
||||||
|
// This is not an issue: the setting will be tested at the room creation.
|
||||||
|
// No room can be created in the wrong mode.
|
||||||
|
if (/^channel\.\d+$/.test(roomKey)) {
|
||||||
|
room = 'channel.{{CHANNEL_ID}}@room.' + prosodyDomain
|
||||||
|
} else {
|
||||||
|
room = '{{VIDEO_UUID}}@room.' + prosodyDomain
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (settings['prosody-room-type'] === 'channel') {
|
||||||
|
room = 'channel.{{CHANNEL_ID}}@room.' + prosodyDomain
|
||||||
|
} else {
|
||||||
|
room = '{{VIDEO_UUID}}@room.' + prosodyDomain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (room.includes('{{VIDEO_UUID}}')) {
|
||||||
|
if (!video) {
|
||||||
|
throw new Error('Missing video')
|
||||||
|
}
|
||||||
|
room = room.replace(/{{VIDEO_UUID}}/g, video.uuid)
|
||||||
|
}
|
||||||
|
room = room.replace(/{{CHANNEL_ID}}/g, `${channelId}`)
|
||||||
|
if (room.includes('{{CHANNEL_NAME}}')) {
|
||||||
|
const channelName = await getChannelNameById(options, channelId)
|
||||||
|
if (channelName === null) {
|
||||||
|
throw new Error('Channel not found')
|
||||||
|
}
|
||||||
|
if (!/^[a-zA-Z0-9_.]+$/.test(channelName)) {
|
||||||
|
// FIXME: see if there is a response here https://github.com/Chocobozzz/PeerTube/issues/4301 for allowed chars
|
||||||
|
options.peertubeHelpers.logger.error(`Invalid channel name, contains unauthorized chars: '${channelName}'`)
|
||||||
|
throw new Error('Invalid channel name, contains unauthorized chars')
|
||||||
|
}
|
||||||
|
room = room.replace(/{{CHANNEL_NAME}}/g, channelName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
userJID: jid,
|
||||||
|
roomJID: room,
|
||||||
|
boshUri,
|
||||||
|
wsUri,
|
||||||
|
remoteXMPPServer: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
initWebchatRouter,
|
initWebchatRouter,
|
||||||
disableProxyRoute,
|
disableProxyRoute,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { parseConfigUUIDs } from './config'
|
import { parseConfigUUIDs } from './config'
|
||||||
|
|
||||||
interface SharedSettings {
|
interface VideoHasWebchatSettings {
|
||||||
'chat-per-live-video': boolean
|
'chat-per-live-video': boolean
|
||||||
'chat-all-lives': boolean
|
'chat-all-lives': boolean
|
||||||
'chat-all-non-lives': boolean
|
'chat-all-non-lives': boolean
|
||||||
@ -12,6 +12,7 @@ interface SharedVideoBase {
|
|||||||
isLive: boolean
|
isLive: boolean
|
||||||
pluginData?: {
|
pluginData?: {
|
||||||
'livechat-active'?: boolean
|
'livechat-active'?: boolean
|
||||||
|
'livechat-remote'?: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +26,13 @@ interface SharedVideoBackend extends SharedVideoBase {
|
|||||||
|
|
||||||
type SharedVideo = SharedVideoBackend | SharedVideoFrontend
|
type SharedVideo = SharedVideoBackend | SharedVideoFrontend
|
||||||
|
|
||||||
function videoHasWebchat (settings: SharedSettings, video: SharedVideo): boolean {
|
/**
|
||||||
|
* Indicate if the video has a local chat.
|
||||||
|
* @param settings plugin settings
|
||||||
|
* @param video the video
|
||||||
|
* @returns true if the video has a local chat
|
||||||
|
*/
|
||||||
|
function videoHasWebchat (settings: VideoHasWebchatSettings, video: SharedVideo): boolean {
|
||||||
// Never use webchat on remote videos.
|
// Never use webchat on remote videos.
|
||||||
if ('isLocal' in video) {
|
if ('isLocal' in video) {
|
||||||
if (!video.isLocal) return false
|
if (!video.isLocal) return false
|
||||||
@ -53,6 +60,29 @@ function videoHasWebchat (settings: SharedSettings, video: SharedVideo): boolean
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
interface VideoHasRemoteWebchatSettings {
|
||||||
videoHasWebchat
|
'federation-no-remote-chat': boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the video has a remote chat.
|
||||||
|
* @param settings plugin settings
|
||||||
|
* @param video the video
|
||||||
|
* @returns true if the video has a remote chat
|
||||||
|
*/
|
||||||
|
function videoHasRemoteWebchat (settings: VideoHasRemoteWebchatSettings, video: SharedVideo): boolean {
|
||||||
|
if (settings['federation-no-remote-chat']) { return false }
|
||||||
|
if ('isLocal' in video) {
|
||||||
|
if (video.isLocal) return false
|
||||||
|
} else {
|
||||||
|
if (!video.remote) return false
|
||||||
|
}
|
||||||
|
if (!video.pluginData) { return false }
|
||||||
|
if (!video.pluginData['livechat-remote']) { return false }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
videoHasWebchat,
|
||||||
|
videoHasRemoteWebchat
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user