Bots settings (WIP).

This commit is contained in:
John Livingston 2021-12-07 10:29:20 +01:00
parent 7bb444af2c
commit 62456aead1
No known key found for this signature in database
GPG Key ID: B17B5640CE66CDBC
7 changed files with 132 additions and 19 deletions

View File

@ -211,6 +211,7 @@ function register ({ registerHook, registerSettingsScript, peertubeHelpers }: Re
case 'prosody-muc-log-by-default':
case 'prosody-muc-expiration':
case 'prosody-c2s':
case 'prosody-component-port':
return options.formValues['chat-type'] !== ('builtin-prosody' as ChatType)
case 'prosody-c2s-port':
return !(

View File

@ -3,15 +3,28 @@ For internal API, we will generate an api Key that must be provided as
GET parameter for every API call.
*/
async function getAPIKey ({ storageManager }: RegisterServerOptions): Promise<string> {
let value: string = await storageManager.getData('APIKEY')
async function _getKey ({ storageManager }: RegisterServerOptions, key: string): Promise<string> {
let value: string = await storageManager.getData(key)
if (!value) {
value = Math.random().toString(36).slice(2, 12)
await storageManager.storeData('APIKEY', value)
await storageManager.storeData(key, value)
}
return value
}
export {
getAPIKey
async function getAPIKey (options: RegisterServerOptions): Promise<string> {
return _getKey(options, 'APIKEY')
}
async function getExternalComponentKey (options: RegisterServerOptions, componentName: string): Promise<string> {
if (!/^[A-Z]+$/.test(componentName)) {
throw new Error('Invalid component name: ' + componentName)
}
const key = 'EXTERNALCOMPONENTKEY_' + componentName
return _getKey(options, key)
}
export {
getAPIKey,
getExternalComponentKey
}

View File

@ -50,6 +50,10 @@ export async function diagProsody (test: string, options: RegisterServerOptions)
}
result.messages.push(`Room content will be saved for '${wantedConfig.logExpiration.value}'`)
if (wantedConfig.bots.demo) {
result.messages.push(`The Demo bot is active for videos: ${wantedConfig.bots.demo.join(', ')}`)
}
await fs.promises.access(filePath, fs.constants.R_OK) // throw an error if file does not exist.
result.messages.push(`The prosody configuration file (${filePath}) exists`)
const actualContent = await fs.promises.readFile(filePath, {

View File

@ -4,8 +4,9 @@ import { getBaseRouterRoute } from '../helpers'
import { ProsodyFilePaths } from './config/paths'
import { ConfigLogExpiration, ProsodyConfigContent } from './config/content'
import { getProsodyDomain } from './config/domain'
import { getAPIKey } from '../apikey'
import { getAPIKey, getExternalComponentKey } from '../apikey'
import type { ProsodyLogLevel } from './config/content'
import { parseConfigDemoBotUUIDs } from './config/bots'
async function getWorkingDir (options: RegisterServerOptions): Promise<string> {
const peertubeHelpers = options.peertubeHelpers
@ -63,6 +64,9 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise<Pro
}
}
interface ProsodyConfigBots {
demo?: string[] // if the demo bot is activated, here are the video UUIDS where it will be.
}
interface ProsodyConfig {
content: string
paths: ProsodyFilePaths
@ -72,25 +76,40 @@ interface ProsodyConfig {
roomType: 'video' | 'channel'
logByDefault: boolean
logExpiration: ConfigLogExpiration
bots: ProsodyConfigBots
}
async function getProsodyConfig (options: RegisterServerOptions): Promise<ProsodyConfig> {
const logger = options.peertubeHelpers.logger
logger.debug('Calling getProsodyConfig')
const port = (await options.settingsManager.getSetting('prosody-port') as string) || '52800'
let useExternalComponents = false
const bots: ProsodyConfigBots = {}
const settings = await options.settingsManager.getSettings([
'prosody-port',
'prosody-muc-log-by-default',
'prosody-muc-expiration',
'prosody-c2s',
'prosody-room-type',
'prosody-peertube-uri',
'prosody-c2s-port',
'prosody-component-port',
'chat-videos-list'
])
const port = (settings['prosody-port'] as string) || '52800'
if (!/^\d+$/.test(port)) {
throw new Error('Invalid port')
}
const logByDefault = (await options.settingsManager.getSetting('prosody-muc-log-by-default') as boolean) ?? true
const logExpirationSetting =
(await options.settingsManager.getSetting('prosody-muc-expiration') as string) ?? DEFAULTLOGEXPIRATION
const enableC2s = (await options.settingsManager.getSetting('prosody-c2s') as boolean) || false
const logByDefault = (settings['prosody-muc-log-by-default'] as boolean) ?? true
const logExpirationSetting = (settings['prosody-muc-expiration'] as string) ?? DEFAULTLOGEXPIRATION
const enableC2s = (settings['prosody-c2s'] as boolean) || false
const prosodyDomain = await getProsodyDomain(options)
const paths = await getProsodyFilePaths(options)
const roomType = (await options.settingsManager.getSetting('prosody-room-type')) === 'channel' ? 'channel' : 'video'
const roomType = (settings['prosody-room-type']) === 'channel' ? 'channel' : 'video'
const apikey = await getAPIKey(options)
let baseApiUrl = await options.settingsManager.getSetting('prosody-peertube-uri') as string
let baseApiUrl = settings['prosody-peertube-uri'] as string
if (baseApiUrl && !/^https?:\/\/[a-z0-9.-_]+(?::\d+)?$/.test(baseApiUrl)) {
throw new Error('Invalid prosody-peertube-uri')
}
@ -109,7 +128,7 @@ async function getProsodyConfig (options: RegisterServerOptions): Promise<Prosod
config.useMucHttpDefault(roomApiUrl)
if (enableC2s) {
const c2sPort = (await options.settingsManager.getSetting('prosody-c2s-port') as string) || '52822'
const c2sPort = (settings['prosody-c2s-port'] as string) || '52822'
if (!/^\d+$/.test(c2sPort)) {
throw new Error('Invalid c2s port')
}
@ -139,6 +158,22 @@ async function getProsodyConfig (options: RegisterServerOptions): Promise<Prosod
logLevel = 'info'
}
config.setLog(logLevel)
const demoBotUUIDs = parseConfigDemoBotUUIDs((settings['chat-videos-list'] as string) || '')
if (demoBotUUIDs?.length > 0) {
useExternalComponents = true
config.useDemoBot(await getExternalComponentKey(options, 'DEMOBOT'))
bots.demo = demoBotUUIDs
}
if (useExternalComponents) {
const externalComponentsPort = (settings['prosody-component-port'] as string) || '53470'
if (!/^\d+$/.test(externalComponentsPort)) {
throw new Error('Invalid external components port')
}
config.useExternalComponents(externalComponentsPort)
}
const content = config.write()
return {
@ -149,7 +184,8 @@ async function getProsodyConfig (options: RegisterServerOptions): Promise<Prosod
host: prosodyDomain,
roomType,
logByDefault,
logExpiration
logExpiration,
bots
}
}

View File

@ -0,0 +1,19 @@
function parseConfigDemoBotUUIDs (s: string): string[] {
if (!s) {
return []
}
let a = s.split('\n')
// find lines that are like:
// 6432f147-83c7-4fa3-b3b5-e49c2590e825 #!demobot
a = a.filter(line => /#!demobot\b/.test(line))
a = a.map(line => {
return line.replace(/#.*$/, '')
.replace(/^\s+/, '')
.replace(/\s+$/, '')
})
return a.filter(line => line !== '')
}
export {
parseConfigDemoBotUUIDs
}

View File

@ -102,16 +102,19 @@ class ProsodyConfigVirtualHost extends ProsodyConfigBlock {
class ProsodyConfigComponent extends ProsodyConfigBlock {
name: string
type: string
type?: string
constructor (type: string, name: string) {
constructor (name: string, type?: string) {
super(' ')
this.type = type
this.name = name
}
write (): string {
return `Component "${this.name}" "${this.type}"\n` + super.write()
if (this.type !== undefined) {
return `Component "${this.name}" "${this.type}"\n` + super.write()
}
return `Component "${this.name}"\n` + super.write()
}
}
@ -123,6 +126,7 @@ class ProsodyConfigContent {
authenticated?: ProsodyConfigVirtualHost
anon: ProsodyConfigVirtualHost
muc: ProsodyConfigComponent
externalComponents: ProsodyConfigComponent[] = []
log: string
prosodyDomain: string
@ -132,7 +136,7 @@ class ProsodyConfigContent {
this.log = ''
this.prosodyDomain = prosodyDomain
this.anon = new ProsodyConfigVirtualHost('anon.' + prosodyDomain)
this.muc = new ProsodyConfigComponent('muc', 'room.' + prosodyDomain)
this.muc = new ProsodyConfigComponent('room.' + prosodyDomain, 'muc')
this.global.set('daemonize', false)
this.global.set('allow_registration', false)
@ -281,6 +285,21 @@ class ProsodyConfigContent {
this.muc.set('peertubelivechat_test_peertube_api_url', apiurl)
}
useExternalComponents (componentsPort: string): void {
this.global.set('component_ports', [componentsPort])
this.global.set('component_interfaces', ['127.0.0.1', '::1'])
}
useDemoBot (componentSecret: string): void {
const demoBot = new ProsodyConfigComponent('demobot.' + this.prosodyDomain)
demoBot.set('component_secret', componentSecret)
// If we want the bot to be moderator, should do the trick:
// this.global.add('admins', 'demobot.' + this.prosodyDomain)
this.externalComponents.push(demoBot)
}
setLog (level: ProsodyLogLevel, syslog?: ProsodyLogLevel[]): void {
let log = ''
log += 'log = {\n'
@ -309,6 +328,11 @@ class ProsodyConfigContent {
content += '\n\n'
content += this.muc.write()
content += '\n\n'
this.externalComponents.forEach((externalComponent) => {
content += '\n\n'
content += externalComponent.write()
content += '\n\n'
})
return content
}
}

View File

@ -359,6 +359,22 @@ archiving for a specific room, by editing its properties.
</ul>`
})
registerSetting({
name: 'prosody-component-port',
label: 'The port to be use for external components',
type: 'input',
default: '53470',
private: true,
descriptionHTML:
`The port that will be used for extra components used by the builtin Prosody server.<br>
This is only used when one of these special features is used:<br>
<ul>
<li>Demo bot: this is a hidden feature, for demonstration purposes. See the documentation for more information.</li>
</ul><br>
Change it if this port is already in use on your server.
`
})
registerSetting({
name: 'prosody-c2s',
label: 'Enable client to server connections',