Prosody mode only:

Removing old modes (ConverseJS and External URI).
Work in progress.
This commit is contained in:
John Livingston 2022-10-10 18:08:20 +02:00
parent e2ec66bf37
commit 7906ddf625
No known key found for this signature in database
GPG Key ID: B17B5640CE66CDBC
20 changed files with 98 additions and 572 deletions

View File

@ -1,5 +1,30 @@
# Changelog # Changelog
## 6.0.0 (Not Released Yet)
### Breaking changes
Following modes are removed:
* Connect to an existing XMPP server with ConverseJS
* Use an external web chat tool
The only remaining mode is the recommanded one: «Prosody server controlled by Peertube».
These modes were here for historical reasons (backward compatibility, etc.).
But they became difficult to maintain, and impossible to document (adding a lot of confusion).
Moreover, it seems that they weren't really used.
**Note:** If you were using one of the 2 removed modes, or if you disabled the plugin in the settings,
the server will try to use the new mode after updating the plugin.
If you don't want the chat server to be active, just uninstall the plugin
(settings won't be lost, you just have to download it again).
### New Features
*
## 5.7.1 ## 5.7.1
* Adding an easy way to customize background transparency in OBS and co. * Adding an easy way to customize background transparency in OBS and co.

View File

@ -1,6 +1,6 @@
import type { RegisterClientOptions } from '@peertube/peertube-types/client' import type { RegisterClientOptions } from '@peertube/peertube-types/client'
import type { Video } from '@peertube/peertube-types' import type { Video } from '@peertube/peertube-types'
import type { ChatType, ProsodyListRoomsResult } from 'shared/lib/types' import type { ProsodyListRoomsResult } from 'shared/lib/types'
interface ActionPluginSettingsParams { interface ActionPluginSettingsParams {
npmName: string npmName: string
@ -202,56 +202,13 @@ function register ({ registerHook, registerSettingsScript, peertubeHelpers }: Re
isSettingHidden: options => { isSettingHidden: options => {
const name = options.setting.name const name = options.setting.name
switch (name) { switch (name) {
case 'chat-type-help-disabled':
return options.formValues['chat-type'] !== ('disabled' as ChatType)
case 'prosody-room-type':
case 'prosody-port':
case 'prosody-peertube-uri':
case 'chat-type-help-builtin-prosody':
case 'prosody-list-rooms':
case 'prosody-advanced':
case 'prosody-muc-log-by-default':
case 'prosody-muc-expiration':
case 'prosody-c2s':
case 'prosody-components':
case 'chat-share-url':
return options.formValues['chat-type'] !== ('builtin-prosody' as ChatType)
case 'prosody-c2s-port': case 'prosody-c2s-port':
return !( return options.formValues['prosody-c2s'] !== true
options.formValues['chat-type'] === ('builtin-prosody' as ChatType) &&
options.formValues['prosody-c2s'] === true
)
case 'prosody-components-port': case 'prosody-components-port':
case 'prosody-components-list': case 'prosody-components-list':
return !( return options.formValues['prosody-components'] !== true
options.formValues['chat-type'] === ('builtin-prosody' as ChatType) &&
options.formValues['prosody-components'] === true
)
case 'chat-server':
case 'chat-room':
case 'chat-bosh-uri':
case 'chat-ws-uri':
case 'chat-type-help-builtin-converse':
return options.formValues['chat-type'] !== ('builtin-converse' as ChatType)
case 'converse-advanced':
case 'converse-theme':
return !(
options.formValues['chat-type'] === ('builtin-converse' as ChatType) ||
options.formValues['chat-type'] === ('builtin-prosody' as ChatType)
)
case 'converse-autocolors': case 'converse-autocolors':
return !( return options.formValues['converse-theme'] !== 'peertube'
(
options.formValues['chat-type'] === ('builtin-converse' as ChatType) ||
options.formValues['chat-type'] === ('builtin-prosody' as ChatType)
) &&
options.formValues['converse-theme'] === 'peertube'
)
case 'chat-uri':
case 'chat-type-help-external-uri':
return options.formValues['chat-type'] !== ('external-uri' as ChatType)
case 'chat-style':
return options.formValues['chat-type'] === 'disabled'
case 'chat-per-live-video-warning': case 'chat-per-live-video-warning':
return !(options.formValues['chat-all-lives'] === true && options.formValues['chat-per-live-video'] === true) return !(options.formValues['chat-all-lives'] === true && options.formValues['chat-per-live-video'] === true)
} }

View File

@ -253,20 +253,15 @@ function register (registerOptions: RegisterClientOptions): void {
} }
let showShareUrlButton: boolean = false let showShareUrlButton: boolean = false
if (settings['chat-type'] === 'builtin-prosody') { const chatShareUrl = settings['chat-share-url'] ?? ''
// The share url functionality should be technically possible for other modes if (chatShareUrl === 'everyone') {
// than builtin-prosody. But it is too difficult to maintain. showShareUrlButton = true
// So I choose to enable it only for builtin-prosody. } else if (chatShareUrl === 'owner') {
showShareUrlButton = guessIsMine(registerOptions, video)
const chatShareUrl = settings['chat-share-url'] ?? '' } else if (chatShareUrl === 'owner+moderators') {
if (chatShareUrl === 'everyone') { showShareUrlButton = guessIsMine(registerOptions, video) || guessIamIModerator(registerOptions)
showShareUrlButton = true
} else if (chatShareUrl === 'owner') {
showShareUrlButton = guessIsMine(registerOptions, video)
} 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(() => {
if (settings['chat-auto-display']) { if (settings['chat-auto-display']) {
openChat(video) openChat(video)

View File

@ -106,7 +106,7 @@ async function shareChatUrl (registerOptions: RegisterClientOptions, settings: a
readonlyOptions.append(transparentLabelEl) readonlyOptions.append(transparentLabelEl)
let autoColors let autoColors
if (isAutoColorsAvailable(settings['chat-type'], settings['converse-theme'])) { if (isAutoColorsAvailable(settings['converse-theme'])) {
const label = document.createElement('label') const label = document.createElement('label')
label.innerText = labelAutocolors label.innerText = labelAutocolors
autoColors = document.createElement('input') autoColors = document.createElement('input')

View File

@ -1,6 +1,5 @@
import type { RegisterClientOptions } from '@peertube/peertube-types/client' import type { RegisterClientOptions } from '@peertube/peertube-types/client'
import type { Video } from '@peertube/peertube-types' import type { Video } from '@peertube/peertube-types'
import type { ChatType } from 'shared/lib/types'
import { AutoColors, isAutoColorsAvailable } from 'shared/lib/autocolors' import { AutoColors, isAutoColorsAvailable } from 'shared/lib/autocolors'
import { logger } from './logger' import { logger } from './logger'
import { computeAutoColors } from './colors' import { computeAutoColors } from './colors'
@ -32,41 +31,15 @@ function getIframeUri (
logger.error('Settings are not initialized, too soon to compute the iframeUri') logger.error('Settings are not initialized, too soon to compute the iframeUri')
return null return null
} }
let iframeUriStr = '' let iframeUriStr = getBaseRoute(registerOptions, uriOptions.permanent)
const chatType: ChatType = (settings['chat-type'] ?? 'disabled') as ChatType iframeUriStr += '/webchat/room/' + encodeURIComponent(video.uuid)
if (chatType === 'builtin-prosody' || chatType === 'builtin-converse') {
// Using the builtin converseJS
iframeUriStr = getBaseRoute(registerOptions, uriOptions.permanent)
iframeUriStr += '/webchat/room/' + encodeURIComponent(video.uuid)
} else if (chatType === 'external-uri') {
iframeUriStr = settings['chat-uri'] || ''
iframeUriStr = iframeUriStr.replace(/{{VIDEO_UUID}}/g, encodeURIComponent(video.uuid))
if (iframeUriStr.includes('{{CHANNEL_ID}}')) {
if (!video.channel || !video.channel.id) {
logger.error('Missing channel info in video object.')
return null
}
iframeUriStr = iframeUriStr.replace(/{{CHANNEL_ID}}/g, encodeURIComponent(video.channel.id))
}
if (!/^https?:\/\//.test(iframeUriStr)) {
logger.error('The webchaturi must begin with https://')
return null
}
} else {
logger.error('Chat disabled.')
return null
}
if (iframeUriStr === '') {
logger.error('No iframe uri')
return null
}
const iFrameUri = new URL(iframeUriStr, window.location.origin) const iFrameUri = new URL(iframeUriStr, window.location.origin)
if ( if (
!uriOptions.ignoreAutoColors && !uriOptions.ignoreAutoColors &&
settings['converse-autocolors'] && settings['converse-autocolors'] &&
isAutoColorsAvailable(settings['chat-type'] as ChatType, settings['converse-theme']) isAutoColorsAvailable(settings['converse-theme'])
) { ) {
logger.info('We have to try to compute autocolors.') logger.info('We have to try to compute autocolors.')
try { try {

8
package-lock.json generated
View File

@ -5,6 +5,7 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "peertube-plugin-livechat",
"version": "5.7.1", "version": "5.7.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
@ -46,6 +47,9 @@
"typescript": "^4.3.5", "typescript": "^4.3.5",
"webpack": "^4.46.0", "webpack": "^4.46.0",
"webpack-cli": "^3.3.12" "webpack-cli": "^3.3.12"
},
"engines": {
"npm": ">=7 <8"
} }
}, },
"node_modules/@aws-crypto/crc32": { "node_modules/@aws-crypto/crc32": {
@ -3712,7 +3716,6 @@
"dependencies": { "dependencies": {
"anymatch": "~3.1.2", "anymatch": "~3.1.2",
"braces": "~3.0.2", "braces": "~3.0.2",
"fsevents": "~2.3.2",
"glob-parent": "~5.1.2", "glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0", "is-binary-path": "~2.1.0",
"is-glob": "~4.0.1", "is-glob": "~4.0.1",
@ -12189,8 +12192,7 @@
"dependencies": { "dependencies": {
"chokidar": "^3.4.1", "chokidar": "^3.4.1",
"graceful-fs": "^4.1.2", "graceful-fs": "^4.1.2",
"neo-async": "^2.5.0", "neo-async": "^2.5.0"
"watchpack-chokidar2": "^2.0.1"
}, },
"optionalDependencies": { "optionalDependencies": {
"watchpack-chokidar2": "^2.0.1" "watchpack-chokidar2": "^2.0.1"

View File

@ -1,7 +1,7 @@
{ {
"name": "peertube-plugin-livechat", "name": "peertube-plugin-livechat",
"description": "PeerTube plugin livechat: offers a way to embed a chat system into Peertube.", "description": "PeerTube plugin livechat: offers a way to embed a chat system into Peertube.",
"version": "5.7.1", "version": "6.0.0",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"author": { "author": {
"name": "John Livingston", "name": "John Livingston",

View File

@ -1,30 +0,0 @@
import type { RegisterServerOptions } from '@peertube/peertube-types'
import { newResult, TestResult } from './utils'
import type { ChatType } from '../../../shared/lib/types'
export async function diagChatType (test: string, { settingsManager }: RegisterServerOptions): Promise<TestResult> {
const result = newResult(test)
const typeSettings = await settingsManager.getSettings([
'chat-type'
])
result.label = 'Webchat type'
const chatType: ChatType = (typeSettings['chat-type'] ?? 'disabled') as ChatType
if (chatType === 'builtin-prosody') {
result.messages.push('Using builtin Prosody')
result.ok = true
result.next = 'prosody'
} else if (chatType === 'builtin-converse') {
result.messages.push('Using builtin ConverseJS to connect to an external XMPP server')
result.ok = true
result.next = 'converse'
} else if (chatType === 'external-uri') {
result.messages.push('Using an external uri')
result.ok = true
result.next = 'use-uri'
} else if (chatType === 'disabled') {
result.messages.push('Webchat disabled')
} else {
result.messages.push('Unknown chat type value: ' + (chatType as string))
}
return result
}

View File

@ -1,77 +0,0 @@
import type { RegisterServerOptions } from '@peertube/peertube-types'
import { newResult, TestResult } from './utils'
export async function diagConverse (test: string, { settingsManager }: RegisterServerOptions): Promise<TestResult> {
const result = newResult(test)
result.label = 'Builtin ConverseJS on XMPP service'
const builtinSettings = await settingsManager.getSettings([
'chat-server',
'chat-room',
'chat-bosh-uri',
'chat-ws-uri'
])
let isBuiltinError = false
const chatServer: string = (builtinSettings['chat-server'] as string) || ''
if (chatServer === '') {
result.messages.push('Missing chat server configuration')
isBuiltinError = true
} else if (!/^([a-z0-9.]+)+[a-z0-9]+$/.test(chatServer)) {
result.messages.push(
'Invalid value for the webchat server: "' +
chatServer +
'"'
)
isBuiltinError = true
} else {
result.messages.push('Chat server is correct')
}
const chatRoom: string = (builtinSettings['chat-room'] as string) || ''
if (chatRoom === '') {
result.messages.push('Missing chat room configuration')
isBuiltinError = true
} else if (
!/^(\w|{{(VIDEO_UUID|CHANNEL_ID|CHANNEL_NAME)}})+@([a-z0-9.]+)+[a-z0-9]+$/
.test(chatRoom)
) {
result.messages.push(
'Invalid value for the webchat room: "' +
chatRoom +
'"'
)
isBuiltinError = true
} else {
result.messages.push('Chat room is correct and will be: ' + chatRoom)
}
const chatBoshUri: string = (builtinSettings['chat-bosh-uri'] as string) || ''
const chatWsUri: string = (builtinSettings['chat-ws-uri'] as string) || ''
if (chatBoshUri === '' && chatWsUri === '') {
result.messages.push('Missing BOSH or Websocket uri')
isBuiltinError = true
}
if (chatBoshUri !== '') {
if (!/^https?:\/\//.test(chatBoshUri)) {
result.messages.push('Invalid BOSH Uri, should begin with https://')
isBuiltinError = true
} else {
result.messages.push('Valid Bosh Uri')
}
}
if (chatWsUri !== '') {
if (!/^wss?:\/\//.test(chatWsUri)) {
result.messages.push('Invalid Websocket Uri, should begin with wss://')
isBuiltinError = true
} else {
result.messages.push('Valid Websocket Uri')
}
}
if (!isBuiltinError) {
result.messages.push('Builtin converse is correctly configured')
result.ok = true
}
return result
}

View File

@ -1,10 +1,7 @@
import type { RegisterServerOptions } from '@peertube/peertube-types' import type { RegisterServerOptions } from '@peertube/peertube-types'
import { diagBackend } from './backend' import { diagBackend } from './backend'
import { diagConverse } from './converse'
import { diagChatType } from './chat-type'
import { TestResult, newResult } from './utils' import { TestResult, newResult } from './utils'
import { diagProsody } from './prosody' import { diagProsody } from './prosody'
import { diagUri } from './uri'
import { diagVideo } from './video' import { diagVideo } from './video'
export async function diag (test: string, options: RegisterServerOptions): Promise<TestResult> { export async function diag (test: string, options: RegisterServerOptions): Promise<TestResult> {
@ -14,14 +11,8 @@ export async function diag (test: string, options: RegisterServerOptions): Promi
result = await diagBackend(test, options) result = await diagBackend(test, options)
} else if (test === 'webchat-video') { } else if (test === 'webchat-video') {
result = await diagVideo(test, options) result = await diagVideo(test, options)
} else if (test === 'webchat-type') {
result = await diagChatType(test, options)
} else if (test === 'prosody') { } else if (test === 'prosody') {
result = await diagProsody(test, options) result = await diagProsody(test, options)
} else if (test === 'converse') {
result = await diagConverse(test, options)
} else if (test === 'use-uri') {
result = await diagUri(test, options)
} else { } else {
result = newResult(test) result = newResult(test)
result.messages.push('Unknown test') result.messages.push('Unknown test')

View File

@ -1,17 +0,0 @@
import type { RegisterServerOptions } from '@peertube/peertube-types'
import { newResult, TestResult } from './utils'
export async function diagUri (test: string, { settingsManager }: RegisterServerOptions): Promise<TestResult> {
const result = newResult(test)
result.label = 'External Webchat using an iframe'
const settings = await settingsManager.getSettings([
'chat-uri'
])
if (/^https:\/\//.test(settings['chat-uri'] as string)) {
result.ok = true
result.messages.push('Chat url will be: ' + (settings['chat-uri'] as string))
} else {
result.messages.push('Incorrect value for the uri (it does not start with https://)')
}
return result
}

View File

@ -1,4 +1,4 @@
type nextValue = 'backend' | 'webchat-video' | 'webchat-type' | 'prosody' | 'converse' | 'use-uri' type nextValue = 'backend' | 'webchat-video' | 'prosody'
interface MessageWithLevel { interface MessageWithLevel {
level: 'info' | 'warning' | 'error' level: 'info' | 'warning' | 'error'

View File

@ -46,7 +46,7 @@ export async function diagVideo (test: string, { settingsManager }: RegisterServ
} }
if (atLeastOne) { if (atLeastOne) {
result.ok = true result.ok = true
result.next = 'webchat-type' result.next = 'prosody'
} else { } else {
result.ok = false result.ok = false
result.messages.push('Chat is activate for no video.') result.messages.push('Chat is activate for no video.')

View File

@ -1,87 +1,9 @@
import type { RegisterServerOptions } from '@peertube/peertube-types' import type { RegisterServerOptions } from '@peertube/peertube-types'
import { pluginShortName } from '../helpers'
import type { ChatType } from '../../../shared/lib/types'
async function _migrateChatTypeSetting (options: RegisterServerOptions): Promise<void> {
const peertubeHelpers = options.peertubeHelpers
const logger = peertubeHelpers.logger
// Previous to plugin v3.0.0, there was multiple checkbox and input-text for settings the plugin mode.
// With Peertube v2.3.0, we can replace all these settings with a single select.
// This function migrates old values if needed.
// NB: we cant use safely settingsManager.getSetting, because settings are not registered yet.
logger.info('Checking if we need to migrate chat-type')
if (!/^[-a-z]+$/.test(pluginShortName)) {
// to prevent sql injection... be sure there is no special char here.
throw new Error(`Wrong pluginShortName '${pluginShortName}'`)
}
const [results] = await peertubeHelpers.database.query(
'SELECT "settings" FROM "plugin"' +
' WHERE "plugin"."name" = :pluginShortName',
{
replacements: {
pluginShortName
}
}
)
if (!Array.isArray(results)) {
throw new Error('_migrateChatTypeSetting: query result is not an array.')
}
if (results.length === 0) {
logger.error('Plugin not found in database')
return
}
if (results.length > 1) {
logger.error('Multiple lines for plugin in database, dont know which one to migrate... Aborting.')
return
}
const settings = results[0].settings
if (!settings) {
logger.info('Plugin settings are empty in database, no migration needed.')
return
}
if (typeof settings !== 'object') {
logger.error('Plugin settings in database seems to be invalid json')
return
}
if ('chat-type' in settings) {
logger.info('The setting chat-type is already here, no need to migrate.')
return
}
logger.info('The setting chat-type is not here, checking if we have to migrate from previous settings...')
let chatType: ChatType | undefined
if (settings['chat-use-prosody'] === true) {
chatType = 'builtin-prosody'
} else if (settings['chat-use-builtin'] === true) {
chatType = 'builtin-converse'
} else if (((settings['chat-uri'] || '') as string) !== '') {
chatType = 'external-uri'
} else {
logger.info('It seems there was no previous active chat configuration.')
return
}
logger.info(`We have to set chat-type to value '${chatType}'.`)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
await peertubeHelpers.database.query(
'UPDATE "plugin" ' +
' SET "settings" = "settings" || :value ' +
' WHERE "name" = :pluginShortName',
{
replacements: {
pluginShortName,
value: JSON.stringify({
'chat-type': chatType
})
}
}
)
}
async function migrateSettings (options: RegisterServerOptions): Promise<void> { async function migrateSettings (options: RegisterServerOptions): Promise<void> {
const logger = options.peertubeHelpers.logger const logger = options.peertubeHelpers.logger
logger.info('Checking if there is a migration script to launch...') logger.info('Checking if there is a migration script to launch...')
await _migrateChatTypeSetting(options) // 2022-10-10: as we removed the «chat-type» settings, there is no migration needed for now.
} }
export { export {

View File

@ -2,7 +2,6 @@ import type { RegisterServerOptions } from '@peertube/peertube-types'
import { getProsodyConfig, getProsodyFilePaths, writeProsodyConfig } from './config' import { getProsodyConfig, getProsodyFilePaths, writeProsodyConfig } from './config'
import { startProsodyLogRotate, stopProsodyLogRotate } from './logrotate' import { startProsodyLogRotate, stopProsodyLogRotate } from './logrotate'
import { changeHttpBindRoute } from '../routers/webchat' import { changeHttpBindRoute } from '../routers/webchat'
import type { ChatType } from '../../../shared/lib/types'
import * as fs from 'fs' import * as fs from 'fs'
import * as child_process from 'child_process' import * as child_process from 'child_process'
@ -139,17 +138,10 @@ async function testProsodyCorrectlyRunning (options: RegisterServerOptions): Pro
} }
async function ensureProsodyRunning (options: RegisterServerOptions): Promise<void> { async function ensureProsodyRunning (options: RegisterServerOptions): Promise<void> {
const { peertubeHelpers, settingsManager } = options const { peertubeHelpers } = options
const logger = peertubeHelpers.logger const logger = peertubeHelpers.logger
logger.debug('Calling ensureProsodyRunning') logger.debug('Calling ensureProsodyRunning')
logger.debug('Checking if prosody should be active')
const setting = await settingsManager.getSetting('chat-type')
if (setting !== ('builtin-prosody' as ChatType)) {
logger.info('Chat type is not set to builtin-prosody, we wont launch it')
return
}
const r = await testProsodyCorrectlyRunning(options) const r = await testProsodyCorrectlyRunning(options)
if (r.ok) { if (r.ok) {
r.messages.forEach(m => logger.debug(m)) r.messages.forEach(m => logger.debug(m))

View File

@ -7,7 +7,6 @@ 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 type { ChatType } from '../../../shared/lib/types'
import { fillVideoCustomFields } from '../custom-fields' import { fillVideoCustomFields } from '../custom-fields'
import { getChannelInfosById } from '../database/channel' import { getChannelInfosById } from '../database/channel'
@ -51,14 +50,8 @@ async function initApiRouter (options: RegisterServerOptions): Promise<Router> {
logger.info(`Requesting room information for room '${jid}'.`) logger.info(`Requesting room information for room '${jid}'.`)
const settings = await options.settingsManager.getSettings([ const settings = await options.settingsManager.getSettings([
'chat-type',
'prosody-room-type' 'prosody-room-type'
]) ])
if (settings['chat-type'] !== ('builtin-prosody' as ChatType)) {
logger.warn('Prosody chat is not active')
res.sendStatus(403)
return
}
// Now, we have two different room type: per video or per channel. // Now, we have two different room type: per video or per channel.
if (settings['prosody-room-type'] === 'channel') { if (settings['prosody-room-type'] === 'channel') {
const matches = jid.match(/^channel\.(\d+)$/) const matches = jid.match(/^channel\.(\d+)$/)
@ -106,17 +99,11 @@ async function initApiRouter (options: RegisterServerOptions): Promise<Router> {
// 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([
'chat-type',
'chat-per-live-video', 'chat-per-live-video',
'chat-all-lives', 'chat-all-lives',
'chat-all-non-lives', 'chat-all-non-lives',
'chat-videos-list' 'chat-videos-list'
]) ])
if (settings['chat-type'] !== ('builtin-prosody' as ChatType)) {
logger.warn('Prosody chat is not active')
res.sendStatus(403)
return
}
if (!videoHasWebchat({ if (!videoHasWebchat({
'chat-per-live-video': !!settings['chat-per-live-video'], 'chat-per-live-video': !!settings['chat-per-live-video'],
'chat-all-lives': !!settings['chat-all-lives'], 'chat-all-lives': !!settings['chat-all-lives'],
@ -186,14 +173,6 @@ async function initApiRouter (options: RegisterServerOptions): Promise<Router> {
router.get('/user/check_password', asyncMiddleware( router.get('/user/check_password', asyncMiddleware(
async (req: Request, res: Response, _next: NextFunction) => { async (req: Request, res: Response, _next: NextFunction) => {
const settings = await options.settingsManager.getSettings([
'chat-type'
])
if (settings['chat-type'] !== ('builtin-prosody' as ChatType)) {
logger.warn('Prosody chat is not active')
res.status(200).send('false')
return
}
const prosodyDomain = await getProsodyDomain(options) const prosodyDomain = await getProsodyDomain(options)
const user = req.query.user const user = req.query.user
const server = req.query.server const server = req.query.server
@ -213,14 +192,6 @@ async function initApiRouter (options: RegisterServerOptions): Promise<Router> {
router.get('/user/user_exists', asyncMiddleware( router.get('/user/user_exists', asyncMiddleware(
async (req: Request, res: Response, _next: NextFunction) => { async (req: Request, res: Response, _next: NextFunction) => {
const settings = await options.settingsManager.getSettings([
'chat-type'
])
if (settings['chat-type'] !== ('builtin-prosody' as ChatType)) {
logger.warn('Prosody chat is not active')
res.status(200).send('false')
return
}
const prosodyDomain = await getProsodyDomain(options) const prosodyDomain = await getProsodyDomain(options)
const user = req.query.user const user = req.query.user
const server = req.query.server const server = req.query.server

View File

@ -2,7 +2,7 @@ import type { RegisterServerOptions, MVideoThumbnail } from '@peertube/peertube-
import type { Router, RequestHandler, Request, Response, NextFunction } from 'express' import type { Router, RequestHandler, Request, Response, NextFunction } from 'express'
import type { ProxyOptions } from 'express-http-proxy' import type { ProxyOptions } from 'express-http-proxy'
import type { import type {
ChatType, ProsodyListRoomsResult, ProsodyListRoomsResultRoom ProsodyListRoomsResult, ProsodyListRoomsResultRoom
} from '../../../shared/lib/types' } from '../../../shared/lib/types'
import { getBaseRouterRoute, getBaseStaticRoute, isUserAdmin } from '../helpers' import { getBaseRouterRoute, getBaseStaticRoute, isUserAdmin } from '../helpers'
import { asyncMiddleware } from '../middlewares/async' import { asyncMiddleware } from '../middlewares/async'
@ -41,17 +41,11 @@ async function initWebchatRouter (options: RegisterServerOptions): Promise<Route
const roomKey = req.params.roomKey const roomKey = req.params.roomKey
const settings = await settingsManager.getSettings([ const settings = await settingsManager.getSettings([
'chat-type', 'chat-room', 'chat-server',
'chat-bosh-uri', 'chat-ws-uri',
'prosody-room-type', 'prosody-room-type',
'converse-theme', 'converse-autocolors' 'converse-theme', 'converse-autocolors'
]) ])
const chatType: ChatType = (settings['chat-type'] ?? 'disabled') as ChatType
let jid: string
let room: string let room: string
let boshUri: string
let wsUri: string
let authenticationUrl: string = '' let authenticationUrl: string = ''
let advancedControls: boolean = false // auto join the chat in viewer mode, if not logged in let advancedControls: boolean = false // auto join the chat in viewer mode, if not logged in
let autoViewerMode: boolean = false let autoViewerMode: boolean = false
@ -61,60 +55,42 @@ async function initWebchatRouter (options: RegisterServerOptions): Promise<Route
if (!/^\w+$/.test(converseJSTheme)) { if (!/^\w+$/.test(converseJSTheme)) {
converseJSTheme = 'peertube' converseJSTheme = 'peertube'
} }
if (chatType === 'builtin-prosody') { const prosodyDomain = await getProsodyDomain(options)
const prosodyDomain = await getProsodyDomain(options) const jid = 'anon.' + prosodyDomain
jid = 'anon.' + prosodyDomain if (req.query.forcetype === '1') {
if (req.query.forcetype === '1') { // We come from the room list in the settings page.
// We come from the room list in the settings page. // Here we don't read the prosody-room-type settings,
// Here we don't read the prosody-room-type settings, // but use the roomKey format.
// but use the roomKey format. // NB: there is no extra security. Any user can add this parameter.
// 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.
// This is not an issue: the setting will be tested at the room creation. // No room can be created in the wrong mode.
// No room can be created in the wrong mode. if (/^channel\.\d+$/.test(roomKey)) {
if (/^channel\.\d+$/.test(roomKey)) { room = 'channel.{{CHANNEL_ID}}@room.' + prosodyDomain
room = 'channel.{{CHANNEL_ID}}@room.' + prosodyDomain
} else {
room = '{{VIDEO_UUID}}@room.' + prosodyDomain
}
} else { } else {
if (settings['prosody-room-type'] === 'channel') { room = '{{VIDEO_UUID}}@room.' + prosodyDomain
room = 'channel.{{CHANNEL_ID}}@room.' + prosodyDomain
} else {
room = '{{VIDEO_UUID}}@room.' + prosodyDomain
}
} }
boshUri = getBaseRouterRoute(options) + 'webchat/http-bind'
wsUri = ''
authenticationUrl = options.peertubeHelpers.config.getWebserverUrl() +
getBaseRouterRoute(options) +
'api/auth'
advancedControls = true
if (req.query._readonly === 'true') {
forceReadonly = 'true'
} else if (req.query._readonly === 'noscroll') {
forceReadonly = 'noscroll'
} else {
autoViewerMode = true // auto join the chat in viewer mode, if not logged in
}
if (req.query._transparent === 'true') {
transparent = true
}
} else if (chatType === 'builtin-converse') {
if (!settings['chat-server']) {
throw new Error('Missing chat-server settings.')
}
if (!settings['chat-room']) {
throw new Error('Missing chat-room settings.')
}
if (!settings['chat-bosh-uri'] && !settings['chat-ws-uri']) {
throw new Error('Missing BOSH or Websocket uri.')
}
jid = settings['chat-server'] as string
room = settings['chat-room'] as string
boshUri = settings['chat-bosh-uri'] as string
wsUri = settings['chat-ws-uri'] as string
} else { } else {
throw new Error('Builtin chat disabled.') if (settings['prosody-room-type'] === 'channel') {
room = 'channel.{{CHANNEL_ID}}@room.' + prosodyDomain
} else {
room = '{{VIDEO_UUID}}@room.' + prosodyDomain
}
}
const boshUri = getBaseRouterRoute(options) + 'webchat/http-bind'
const wsUri = ''
authenticationUrl = options.peertubeHelpers.config.getWebserverUrl() +
getBaseRouterRoute(options) +
'api/auth'
advancedControls = true
if (req.query._readonly === 'true') {
forceReadonly = 'true'
} else if (req.query._readonly === 'noscroll') {
forceReadonly = 'noscroll'
} else {
autoViewerMode = true // auto join the chat in viewer mode, if not logged in
}
if (req.query._transparent === 'true') {
transparent = true
} }
let video: MVideoThumbnail | undefined let video: MVideoThumbnail | undefined
@ -122,10 +98,7 @@ async function initWebchatRouter (options: RegisterServerOptions): Promise<Route
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])
// Here we are on a room... must be in prosody mode. // Here we are on a channel room...
if (chatType !== 'builtin-prosody') {
throw new Error('Cant access a chat by a channel uri if chatType!==builtin-prosody')
}
const channelInfos = await getChannelInfosById(options, channelId) const channelInfos = await getChannelInfosById(options, channelId)
if (!channelInfos) { if (!channelInfos) {
throw new Error('Channel not found') throw new Error('Channel not found')
@ -168,7 +141,7 @@ async function initWebchatRouter (options: RegisterServerOptions): Promise<Route
let autocolorsStyles = '' let autocolorsStyles = ''
if ( if (
settings['converse-autocolors'] && settings['converse-autocolors'] &&
isAutoColorsAvailable(settings['chat-type'] as ChatType, settings['converse-theme'] as string) isAutoColorsAvailable(settings['converse-theme'] as string)
) { ) {
peertubeHelpers.logger.debug('Trying to load AutoColors...') peertubeHelpers.logger.debug('Trying to load AutoColors...')
const autocolors: AutoColors = { const autocolors: AutoColors = {
@ -249,18 +222,6 @@ async function initWebchatRouter (options: RegisterServerOptions): Promise<Route
return return
} }
const chatType: ChatType = await options.settingsManager.getSetting('chat-type') as ChatType
if (chatType !== 'builtin-prosody') {
const message = 'Please save the settings first.' // TODO: translate?
res.status(200)
const r: ProsodyListRoomsResult = {
ok: false,
error: message
}
res.json(r)
return
}
if (!currentProsodyHttpBindInfo) { if (!currentProsodyHttpBindInfo) {
throw new Error('It seems that prosody is not binded... Cant list rooms.') throw new Error('It seems that prosody is not binded... Cant list rooms.')
} }

View File

@ -1,6 +1,6 @@
import type { RegisterServerOptions } from '@peertube/peertube-types' import type { RegisterServerOptions } from '@peertube/peertube-types'
import { ensureProsodyRunning, ensureProsodyNotRunning } from './prosody/ctl' import { ensureProsodyRunning } from './prosody/ctl'
import type { ChatType, ConverseJSTheme } from '../../shared/lib/types' import type { ConverseJSTheme } from '../../shared/lib/types'
function initSettings (options: RegisterServerOptions): void { function initSettings (options: RegisterServerOptions): void {
const { peertubeHelpers, registerSetting, settingsManager } = options const { peertubeHelpers, registerSetting, settingsManager } = options
@ -34,78 +34,18 @@ function initSettings (options: RegisterServerOptions): void {
descriptionHTML: '<h3>Chat mode</h3>' descriptionHTML: '<h3>Chat mode</h3>'
}) })
registerSetting({ registerSetting({
name: 'chat-type', name: 'chat-help-builtin-prosody',
label: 'Chat mode',
type: 'select',
default: 'disabled' as ChatType,
private: false,
options: [
{ value: 'disabled', label: 'Disabled' },
{ value: 'builtin-prosody', label: 'Prosody server controlled by Peertube (recommended)' },
{ value: 'builtin-converse', label: 'Connect to an existing XMPP server with ConverseJS' },
{ value: 'external-uri', label: 'Use an external web chat tool' }
] as Array<{value: ChatType, label: string}>,
descriptionHTML: 'Please choose the webchat mode you want to use.'
})
registerSetting({
name: 'chat-type-help-disabled',
type: 'html', type: 'html',
descriptionHTML: 'The chat is disabled.', label: 'Prosody server',
private: true descriptionHTML: `This plugin uses the Prosody XMPP server to handle chat rooms.<br>
}) The Peertube server will control this Prosody server.<br>
registerSetting({ Important Note: you have to install Prosody on your server.
name: 'chat-type-help-builtin-prosody',
type: 'html',
label: 'Prosody server controlled by Peertube (recommended)',
descriptionHTML: `With this mode, the Peertube server will control a local Prosody XMPP server.<br>
Note: you have to install the Prosody XMPP server.
Please read the <a Please read the <a
href="https://github.com/JohnXLivingston/peertube-plugin-livechat/blob/main/documentation/prosody.md" href="https://github.com/JohnXLivingston/peertube-plugin-livechat/blob/main/documentation/prosody.md"
target="_blank" target="_blank"
>documentation</a>.`, >documentation</a>.`,
private: true private: true
}) })
registerSetting({
name: 'chat-type-help-builtin-converse',
type: 'html',
label: 'Connect to an existing XMPP server with ConverseJS',
descriptionHTML:
`<div class="peertube-plugin-livechat-warning"><b>
This mode is deprecated and will be removed in version 6.0.0.
More information in the
<a href="https://github.com/JohnXLivingston/peertube-plugin-livechat/blob/main/CHANGELOG.md#560" target="_blank">
CHANGELOG
</a>.
</b></div>
With this mode, you can connect to an existing XMPP server, that allow anonymous authentication and room creation.
Please read the
<a
href="https://github.com/JohnXLivingston/peertube-plugin-livechat/blob/main/documentation/conversejs.md"
target="_blank"
>documentation</a>.`,
private: true
})
registerSetting({
name: 'chat-type-help-external-uri',
type: 'html',
label: 'Use an external webchat',
descriptionHTML:
`<div class="peertube-plugin-livechat-warning"><b>
This mode is deprecated and will be removed in version 6.0.0.
More information in the
<a href="https://github.com/JohnXLivingston/peertube-plugin-livechat/blob/main/CHANGELOG.md#560" target="_blank">
CHANGELOG
</a>.
</b></div>
With this mode, you can use any external web chat that can be included in an iframe.
Please read the
<a
href="https://github.com/JohnXLivingston/peertube-plugin-livechat/blob/main/documentation/external.md"
target="_blank"
>documentation</a>.`,
private: true
})
registerSetting({ registerSetting({
name: 'prosody-list-rooms', name: 'prosody-list-rooms',
@ -140,70 +80,6 @@ Change it if this port is already in use on your server.<br>
You can close this port on your firewall, it will not be accessed from the outer world.` You can close this port on your firewall, it will not be accessed from the outer world.`
}) })
registerSetting({
name: 'chat-server',
label: 'XMPP service server',
type: 'input',
default: '',
descriptionHTML: 'Your XMPP server. Without any scheme. Example : peertube.im.your_domain.',
private: true
})
registerSetting({
name: 'chat-room',
label: 'XMPP room template',
type: 'input',
default: '',
descriptionHTML:
`Your XMPP room. You can use following placeholders to inject video metadata in the room name:
<ul>
<li>{{VIDEO_UUID}} to add the video UUID.</li>
<li>{{CHANNEL_ID}} to add the CHANNEL numerical ID.</li>
<li>{{CHANNEL_NAME}} to add the channel name (see the Peertube's documentation for possible characters).</li>
</ul>
Without any placeholder, all videos will point to the same chat room.<br>
Example: public@room.peertube.im.your_domain<br>
Example: public_{{VIDEO_UUID}}@room.peertube.im.your_domain`,
private: true
})
registerSetting({
name: 'chat-bosh-uri',
label: 'BOSH uri',
type: 'input',
default: '',
descriptionHTML:
`URI of the external BOSH server.
Please make sure it accept cross origin request from your domain.<br>
You must at least have a BOSH or a Websocket uri.`,
private: true
})
registerSetting({
name: 'chat-ws-uri',
label: 'Websocket uri',
type: 'input',
default: '',
descriptionHTML: `
URI of the external WS server.
Please make sure it accept cross origin request from your domain.<br>
You must at least have a BOSH or a Websocket uri.`,
private: true
})
registerSetting({
name: 'chat-uri',
label: 'Webchat url',
type: 'input',
default: '',
descriptionHTML:
`Put here your webchat url. An iframe will be created pointing to this url.
You can use following placeholders to inject video metadata in the url:
<ul>
<li>{{VIDEO_UUID}} to add the video UUID.</li>
<li>{{CHANNEL_ID}} to add the CHANNEL numerical ID.</li>
</ul>
Example : https://my_domain/conversejs.html?room=video_{{VIDEO_UUID}}.`,
private: false
})
// ********** Chat behaviour // ********** Chat behaviour
registerSetting({ registerSetting({
type: 'html', type: 'html',
@ -467,17 +343,9 @@ You can keep this port closed on your firewall for now, it will not be accessed
}) })
// ********** settings changes management // ********** settings changes management
settingsManager.onSettingsChange(async (settings: any) => { settingsManager.onSettingsChange(async (_settings: any) => {
if ('chat-type' in settings) { peertubeHelpers.logger.info('Saving settings, ensuring prosody is running')
const chatType: ChatType = settings['chat-type'] ?? 'disabled' await ensureProsodyRunning(options)
if (chatType === 'builtin-prosody') {
peertubeHelpers.logger.info('Saving settings, ensuring prosody is running')
await ensureProsodyRunning(options)
} else {
peertubeHelpers.logger.info('Saving settings, ensuring prosody is not running')
await ensureProsodyNotRunning(options)
}
}
}) })
} }

View File

@ -1,4 +1,3 @@
import type { ChatType } from './types'
const validateColor = require('validate-color').default const validateColor = require('validate-color').default
type AutoColorValue = string type AutoColorValue = string
@ -19,14 +18,10 @@ interface AutoColors {
} }
/** /**
* @param chatType value of the settings 'chat-type'
* @param theme value of the settings 'converse-theme' * @param theme value of the settings 'converse-theme'
* @returns true if the theme can use autocolors * @returns true if the theme can use autocolors
*/ */
function isAutoColorsAvailable (chatType: ChatType, theme: string): boolean { function isAutoColorsAvailable (theme: string): boolean {
if (chatType !== 'builtin-prosody' && chatType !== 'builtin-converse') {
return false
}
return theme === 'peertube' // currently the only theme that can handle autocolors. return theme === 'peertube' // currently the only theme that can handle autocolors.
} }

View File

@ -1,4 +1,3 @@
type ChatType = 'disabled' | 'builtin-prosody' | 'builtin-converse' | 'external-uri'
type ConverseJSTheme = 'peertube' | 'default' | 'concord' type ConverseJSTheme = 'peertube' | 'default' | 'concord'
interface ProsodyListRoomsResultError { interface ProsodyListRoomsResultError {
@ -28,7 +27,6 @@ interface ProsodyListRoomsResultSuccess {
type ProsodyListRoomsResult = ProsodyListRoomsResultError | ProsodyListRoomsResultSuccess type ProsodyListRoomsResult = ProsodyListRoomsResultError | ProsodyListRoomsResultSuccess
export { export {
ChatType,
ConverseJSTheme, ConverseJSTheme,
ProsodyListRoomsResult, ProsodyListRoomsResult,
ProsodyListRoomsResultRoom ProsodyListRoomsResultRoom