Moderation Bot integration WIP:
* Start and stop the bot WIP * Prosody: removing the BOSH module from the global scope (must only be present on relevant virtualhosts) * Some refactoring
This commit is contained in:
parent
65fd49a81c
commit
f97e54d499
@ -1,6 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
## XXX (Unreleased Yet)
|
||||
## 8.0.0 (Unreleased Yet)
|
||||
|
||||
### New features
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
* Links to documentation are now using the front-end language to point to the translated documentation page (except for some links generated from the backend, in the diagnostic tool for example).
|
||||
* Some code refactoring.
|
||||
* You can now configure on which network interfaces Prosody will listen for external components.
|
||||
* Prosody: removing the BOSH module from the global scope (must only be present on relevant virtualhosts).
|
||||
|
||||
## 7.2.2
|
||||
|
||||
|
@ -11,7 +11,7 @@ async function registerConfiguration (clientOptions: RegisterClientOptions): Pro
|
||||
const { peertubeHelpers, registerClientRoute, registerHook } = clientOptions
|
||||
|
||||
const settings = await peertubeHelpers.getSettings()
|
||||
if (settings['disable-configuration']) { return }
|
||||
if (settings['disable-channel-configuration']) { return }
|
||||
|
||||
registerClientRoute({
|
||||
route: 'livechat/configuration',
|
||||
|
@ -302,7 +302,7 @@ configuration_description: |
|
||||
users will be able to add some customization on their channels,
|
||||
activate the moderation bot, ...
|
||||
|
||||
disable_configuration_label: "Disable the advanced channel configuration and the chatbot"
|
||||
disable_channel_configuration_label: "Disable the advanced channel configuration and the chatbot"
|
||||
|
||||
save: "Save"
|
||||
cancel: "Cancel"
|
||||
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -15,7 +15,7 @@
|
||||
"http-proxy": "^1.18.1",
|
||||
"log-rotate": "^0.2.8",
|
||||
"validate-color": "^2.2.1",
|
||||
"xmppjs-chat-bot": "^0.2.1"
|
||||
"xmppjs-chat-bot": "^0.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@peertube/feed": "^5.1.0",
|
||||
@ -12069,9 +12069,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/xmppjs-chat-bot": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/xmppjs-chat-bot/-/xmppjs-chat-bot-0.2.1.tgz",
|
||||
"integrity": "sha512-O2uiVBlgio/5psb+DhCYjBU2HfW0pnQwTuKjutBTLoaTtfG+A+oxFmwarZKFDUjiQK6DrytW+LL+M8792rBaxw==",
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/xmppjs-chat-bot/-/xmppjs-chat-bot-0.2.2.tgz",
|
||||
"integrity": "sha512-/t1L2fSW04M/5zEGYQzXqgTa7CVaE1dAT9kO1C5iSOrd4HzksQ6vVsk0PlAbEZere08pbea8Kw8uxBSwGlTiXw==",
|
||||
"funding": [
|
||||
"https://paypal.me/JohnXLivingston",
|
||||
"https://liberapay.com/JohnLivingston/"
|
||||
@ -21291,9 +21291,9 @@
|
||||
}
|
||||
},
|
||||
"xmppjs-chat-bot": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/xmppjs-chat-bot/-/xmppjs-chat-bot-0.2.1.tgz",
|
||||
"integrity": "sha512-O2uiVBlgio/5psb+DhCYjBU2HfW0pnQwTuKjutBTLoaTtfG+A+oxFmwarZKFDUjiQK6DrytW+LL+M8792rBaxw==",
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/xmppjs-chat-bot/-/xmppjs-chat-bot-0.2.2.tgz",
|
||||
"integrity": "sha512-/t1L2fSW04M/5zEGYQzXqgTa7CVaE1dAT9kO1C5iSOrd4HzksQ6vVsk0PlAbEZere08pbea8Kw8uxBSwGlTiXw==",
|
||||
"requires": {
|
||||
"@xmpp/client": "^0.13.1",
|
||||
"@xmpp/component": "^0.13.1",
|
||||
|
@ -38,7 +38,7 @@
|
||||
"http-proxy": "^1.18.1",
|
||||
"log-rotate": "^0.2.8",
|
||||
"validate-color": "^2.2.1",
|
||||
"xmppjs-chat-bot": "^0.2.1"
|
||||
"xmppjs-chat-bot": "^0.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@peertube/feed": "^5.1.0",
|
||||
|
182
server/lib/bots/ctl.ts
Normal file
182
server/lib/bots/ctl.ts
Normal file
@ -0,0 +1,182 @@
|
||||
import type { RegisterServerOptions } from '@peertube/peertube-types'
|
||||
import type { Config as XMPPChatBotConfig } from 'xmppjs-chat-bot'
|
||||
import { BotConfiguration } from '../configuration/bot'
|
||||
import * as child_process from 'child_process'
|
||||
|
||||
let singleton: BotsCtl | undefined
|
||||
|
||||
/**
|
||||
* This class is to control the plugin bots.
|
||||
* For now there is only one, the Moderation bot.
|
||||
* But all public methods are made as if there was several bots, so it will be easier to add bots.
|
||||
*/
|
||||
class BotsCtl {
|
||||
protected readonly options: RegisterServerOptions
|
||||
protected readonly moderationGlobalConf: XMPPChatBotConfig
|
||||
protected readonly logger: {
|
||||
debug: (s: string) => void
|
||||
info: (s: string) => void
|
||||
warn: (s: string) => void
|
||||
error: (s: string) => void
|
||||
}
|
||||
|
||||
protected moderationBotProcess: ReturnType<typeof child_process.spawn> | undefined
|
||||
|
||||
constructor (params: {
|
||||
options: RegisterServerOptions
|
||||
moderationGlobalConf: XMPPChatBotConfig
|
||||
}) {
|
||||
this.options = params.options
|
||||
this.moderationGlobalConf = params.moderationGlobalConf
|
||||
|
||||
const logger = params.options.peertubeHelpers.logger
|
||||
this.logger = {
|
||||
debug: (s) => logger.debug('[Bots] ' + s),
|
||||
info: (s) => logger.info('[Bots] ' + s),
|
||||
warn: (s) => logger.warn('[Bots] ' + s),
|
||||
error: (s) => logger.error('[Bots] ' + s)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts all the required bots.
|
||||
* If bots are already running, does nothing.
|
||||
*/
|
||||
public async start (): Promise<void> {
|
||||
if (await this.options.settingsManager.getSetting('disable-channel-configuration')) {
|
||||
this.logger.info('Advanced channel configuration is disabled, no bot to start')
|
||||
return
|
||||
}
|
||||
|
||||
this.logger.info('Starting moderation bot...')
|
||||
|
||||
if (this.moderationBotProcess?.exitCode === null) {
|
||||
this.logger.info('Moderation bot still running, nothing to do')
|
||||
return
|
||||
}
|
||||
|
||||
const paths = BotConfiguration.singleton().configurationPaths()
|
||||
|
||||
// We will run: npm exec -- xmppjs-chat-bot [...]
|
||||
const execArgs = [
|
||||
'exec',
|
||||
'--',
|
||||
'xmppjs-chat-bot',
|
||||
'run',
|
||||
'-f',
|
||||
paths.moderation.globalFile,
|
||||
'--room-conf-dir',
|
||||
paths.moderation.roomConfDir
|
||||
]
|
||||
const moderationBotProcess = child_process.spawn('npm', execArgs, {
|
||||
cwd: __dirname, // must be in the livechat plugin tree, so that npm can found the package.
|
||||
env: {
|
||||
...process.env // will include NODE_ENV and co
|
||||
}
|
||||
})
|
||||
moderationBotProcess.stdout?.on('data', (data) => {
|
||||
this.logger.debug(`ModerationBot stdout: ${data as string}`)
|
||||
})
|
||||
moderationBotProcess.stderr?.on('data', (data) => {
|
||||
this.logger.error(`ModerationBot stderr: ${data as string}`)
|
||||
})
|
||||
moderationBotProcess.on('error', (error) => {
|
||||
this.logger.error(`ModerationBot exec error: ${JSON.stringify(error)}`)
|
||||
})
|
||||
moderationBotProcess.on('exit', (code) => {
|
||||
this.logger.info(`ModerationBot process exited with code ${code ?? 'null'}`)
|
||||
})
|
||||
moderationBotProcess.on('close', (code) => {
|
||||
this.logger.info(`ModerationBot process closed all stdio with code ${code ?? 'null'}`)
|
||||
})
|
||||
|
||||
this.moderationBotProcess = moderationBotProcess
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all the bots
|
||||
*/
|
||||
public async stop (): Promise<void> {
|
||||
this.logger.info('Stopping bots...')
|
||||
|
||||
if (!this.moderationBotProcess) {
|
||||
this.logger.info('moderationBot was never running, everything is fine.')
|
||||
return
|
||||
}
|
||||
if (this.moderationBotProcess.exitCode !== null) {
|
||||
this.logger.info('The moderation bot has an exitCode, already stopped.')
|
||||
return
|
||||
}
|
||||
const p = new Promise<void>((resolve, reject) => {
|
||||
try {
|
||||
if (!this.moderationBotProcess) { resolve() }
|
||||
const moderationBotProcess: ReturnType<typeof child_process.spawn> =
|
||||
this.moderationBotProcess as ReturnType<typeof child_process.spawn>
|
||||
|
||||
let resolved = false
|
||||
// Trying to kill, and force kill if it takes more than 2 seconds
|
||||
const timeout = setTimeout(() => {
|
||||
this.logger.error('Moderation bot was not killed within 2 seconds, force killing')
|
||||
moderationBotProcess.kill('SIGKILL')
|
||||
resolved = true
|
||||
resolve()
|
||||
}, 2000)
|
||||
|
||||
moderationBotProcess.on('exit', () => {
|
||||
if (resolved) { return }
|
||||
resolved = true
|
||||
if (timeout) { clearTimeout(timeout) }
|
||||
resolve()
|
||||
})
|
||||
moderationBotProcess.on('close', () => {
|
||||
if (resolved) { return }
|
||||
resolved = true
|
||||
if (timeout) { clearTimeout(timeout) }
|
||||
resolve()
|
||||
})
|
||||
moderationBotProcess.kill()
|
||||
} catch (err) {
|
||||
this.logger.error(err as string)
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
/**
|
||||
* Instanciate a new singleton
|
||||
* @param options server options
|
||||
*/
|
||||
public static async initSingleton (options: RegisterServerOptions): Promise<BotsCtl> {
|
||||
// forceRefresh the bot global configuration file:
|
||||
const moderationGlobalConf = await BotConfiguration.singleton().getModerationBotGlobalConf(true)
|
||||
|
||||
singleton = new BotsCtl({
|
||||
options,
|
||||
moderationGlobalConf
|
||||
})
|
||||
return singleton
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singleton, of thrown an exception if it is not initialized yet.
|
||||
* @returns the singleton
|
||||
*/
|
||||
public static singleton (): BotsCtl {
|
||||
if (!singleton) {
|
||||
throw new Error('Bots singleton not initialized yet')
|
||||
}
|
||||
return singleton
|
||||
}
|
||||
|
||||
public static async destroySingleton (): Promise<void> {
|
||||
if (!singleton) { return }
|
||||
await singleton.stop()
|
||||
singleton = undefined
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
BotsCtl
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import type { RegisterServerOptions } from '@peertube/peertube-types'
|
||||
import type { RoomConf } from 'xmppjs-chat-bot'
|
||||
import type { RoomConf, Config } from 'xmppjs-chat-bot'
|
||||
import { getProsodyDomain } from '../prosody/config/domain'
|
||||
import { isDebugMode } from '../debug'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
|
||||
@ -18,8 +19,10 @@ type ChannelCommonRoomConf = Omit<RoomConf, 'local' | 'domain'>
|
||||
class BotConfiguration {
|
||||
protected readonly options: RegisterServerOptions
|
||||
protected readonly mucDomain: string
|
||||
protected readonly botsDomain: string
|
||||
protected readonly confDir: string
|
||||
protected readonly roomConfDir: string
|
||||
protected readonly moderationBotGlobalConf: string
|
||||
protected readonly logger: {
|
||||
debug: (s: string) => void
|
||||
info: (s: string) => void
|
||||
@ -32,13 +35,17 @@ class BotConfiguration {
|
||||
constructor (params: {
|
||||
options: RegisterServerOptions
|
||||
mucDomain: string
|
||||
botsDomain: string
|
||||
confDir: string
|
||||
roomConfDir: string
|
||||
moderationBotGlobalConf: string
|
||||
}) {
|
||||
this.options = params.options
|
||||
this.mucDomain = params.mucDomain
|
||||
this.botsDomain = params.botsDomain
|
||||
this.confDir = params.confDir
|
||||
this.roomConfDir = params.roomConfDir
|
||||
this.moderationBotGlobalConf = params.moderationBotGlobalConf
|
||||
|
||||
const logger = params.options.peertubeHelpers.logger
|
||||
this.logger = {
|
||||
@ -55,15 +62,21 @@ class BotConfiguration {
|
||||
public static async initSingleton (options: RegisterServerOptions): Promise<BotConfiguration> {
|
||||
const prosodyDomain = await getProsodyDomain(options)
|
||||
const mucDomain = 'room.' + prosodyDomain
|
||||
const botsDomain = 'bot.' + prosodyDomain
|
||||
const confDir = path.resolve(
|
||||
options.peertubeHelpers.plugin.getDataDirectoryPath(),
|
||||
'bot',
|
||||
mucDomain
|
||||
)
|
||||
|
||||
const roomConfDir = path.resolve(
|
||||
confDir,
|
||||
'rooms'
|
||||
)
|
||||
const moderationBotGlobalConf = path.resolve(
|
||||
confDir,
|
||||
'moderation.json'
|
||||
)
|
||||
|
||||
await fs.promises.mkdir(confDir, { recursive: true })
|
||||
await fs.promises.mkdir(roomConfDir, { recursive: true })
|
||||
@ -71,8 +84,10 @@ class BotConfiguration {
|
||||
singleton = new BotConfiguration({
|
||||
options,
|
||||
mucDomain,
|
||||
botsDomain,
|
||||
confDir,
|
||||
roomConfDir
|
||||
roomConfDir,
|
||||
moderationBotGlobalConf
|
||||
})
|
||||
|
||||
return singleton
|
||||
@ -93,7 +108,7 @@ class BotConfiguration {
|
||||
* @param roomJIDParam Room full or local JID
|
||||
* @param conf Configuration to write
|
||||
*/
|
||||
public async update (roomJIDParam: string, conf: ChannelCommonRoomConf): Promise<void> {
|
||||
public async updateRoom (roomJIDParam: string, conf: ChannelCommonRoomConf): Promise<void> {
|
||||
const roomJID = this._canonicJID(roomJIDParam)
|
||||
if (!roomJID) {
|
||||
this.logger.error('Invalid room JID')
|
||||
@ -141,6 +156,65 @@ class BotConfiguration {
|
||||
await this._writeRoomConf(roomJID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the moderation bot global configuration.
|
||||
* It it does not exists, creates it.
|
||||
* @param forceRefresh if true, regenerates the configuration file, even if exists.
|
||||
*/
|
||||
public async getModerationBotGlobalConf (forceRefresh?: boolean): Promise<Config> {
|
||||
let config: Config | undefined
|
||||
if (!forceRefresh) {
|
||||
try {
|
||||
const content = (await fs.promises.readFile(this.moderationBotGlobalConf, {
|
||||
encoding: 'utf-8'
|
||||
})).toString()
|
||||
|
||||
config = JSON.parse(content)
|
||||
} catch (err) {
|
||||
this.logger.info('Error reading the moderation bot global configuration file, assuming it does not exists.')
|
||||
config = undefined
|
||||
}
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
// FIXME: use existing lib to get the port, dont hardcode default value here.
|
||||
const portSetting = await this.options.settingsManager.getSetting('prosody-port')
|
||||
const port = (portSetting as string) || '52800'
|
||||
|
||||
config = {
|
||||
type: 'client',
|
||||
connection: {
|
||||
username: 'moderator',
|
||||
password: Math.random().toString(36).slice(2, 12),
|
||||
domain: this.botsDomain,
|
||||
// Note: using localhost, and not currentProsody.host, because it does not always resolve correctly
|
||||
service: 'xmpp://localhost:' + port
|
||||
},
|
||||
name: 'Moderator',
|
||||
logger: 'ConsoleLogger',
|
||||
log_level: isDebugMode(this.options) ? 'debug' : 'info'
|
||||
}
|
||||
await fs.promises.writeFile(this.moderationBotGlobalConf, JSON.stringify(config), {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
public configurationPaths (): {
|
||||
moderation: {
|
||||
globalFile: string
|
||||
roomConfDir: string
|
||||
}
|
||||
} {
|
||||
return {
|
||||
moderation: {
|
||||
globalFile: this.moderationBotGlobalConf,
|
||||
roomConfDir: this.roomConfDir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* frees the singleton
|
||||
*/
|
||||
|
@ -10,9 +10,9 @@ import type { RequestPromiseHandler } from '../async'
|
||||
function checkConfigurationEnabledMiddleware (options: RegisterServerOptions): RequestPromiseHandler {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
const settings = await options.settingsManager.getSettings([
|
||||
'disable-configuration'
|
||||
'disable-channel-configuration'
|
||||
])
|
||||
if (!settings['disable-configuration']) {
|
||||
if (!settings['disable-channel-configuration']) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { RegisterServerOptions } from '@peertube/peertube-types'
|
||||
import type { Config as XMPPBotConfig } from 'xmppjs-chat-bot'
|
||||
import type { ProsodyLogLevel } from './config/content'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
@ -9,6 +10,7 @@ import { getProsodyDomain } from './config/domain'
|
||||
import { getAPIKey } from '../apikey'
|
||||
import { parseExternalComponents } from './config/components'
|
||||
import { getRemoteServerInfosDir } from '../federation/storage'
|
||||
import { BotConfiguration } from '../configuration/bot'
|
||||
|
||||
async function getWorkingDir (options: RegisterServerOptions): Promise<string> {
|
||||
const peertubeHelpers = options.peertubeHelpers
|
||||
@ -125,6 +127,9 @@ interface ProsodyConfig {
|
||||
logExpiration: ConfigLogExpiration
|
||||
valuesToHideInDiagnostic: Map<string, string>
|
||||
certificates: ProsodyConfigCertificates
|
||||
bots: {
|
||||
moderation?: XMPPBotConfig
|
||||
}
|
||||
}
|
||||
async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<ProsodyConfig> {
|
||||
const logger = options.peertubeHelpers.logger
|
||||
@ -147,7 +152,8 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
||||
'prosody-components-interfaces',
|
||||
'prosody-components-list',
|
||||
'chat-no-anonymous',
|
||||
'federation-dont-publish-remotely'
|
||||
'federation-dont-publish-remotely',
|
||||
'disable-channel-configuration'
|
||||
])
|
||||
|
||||
const valuesToHideInDiagnostic = new Map<string, string>()
|
||||
@ -168,6 +174,7 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
||||
// enableRemoteChatConnections: local users can communicate with external rooms
|
||||
const enableRemoteChatConnections = !(settings['federation-dont-publish-remotely'] as boolean)
|
||||
let certificates: ProsodyConfigCertificates = false
|
||||
const bots: ProsodyConfig['bots'] = {}
|
||||
|
||||
const apikey = await getAPIKey(options)
|
||||
valuesToHideInDiagnostic.set('APIKey', apikey)
|
||||
@ -287,6 +294,12 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
||||
config.usePeertubeVCards(basePeertubeUrl)
|
||||
config.useAnonymousRandomVCards(paths.avatars)
|
||||
|
||||
if (!settings['disable-channel-configuration']) {
|
||||
config.useBotsVirtualHost()
|
||||
bots.moderation = await BotConfiguration.singleton().getModerationBotGlobalConf()
|
||||
valuesToHideInDiagnostic.set('BotPassword', bots.moderation.connection.password)
|
||||
}
|
||||
|
||||
config.useTestModule(apikey, testApiUrl)
|
||||
|
||||
let logLevel: ProsodyLogLevel | undefined
|
||||
@ -315,7 +328,8 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
||||
logByDefault,
|
||||
logExpiration,
|
||||
valuesToHideInDiagnostic,
|
||||
certificates
|
||||
certificates,
|
||||
bots
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,6 +140,7 @@ class ProsodyConfigContent {
|
||||
authenticated?: ProsodyConfigVirtualHost
|
||||
anon?: ProsodyConfigVirtualHost
|
||||
muc: ProsodyConfigComponent
|
||||
bot?: ProsodyConfigVirtualHost
|
||||
externalComponents: ProsodyConfigComponent[] = []
|
||||
log: string
|
||||
prosodyDomain: string
|
||||
@ -170,7 +171,7 @@ class ProsodyConfigContent {
|
||||
'version', // Replies to server version requests
|
||||
'uptime', // Report how long server has been running
|
||||
'ping', // Replies to XMPP pings with pongs
|
||||
'bosh', // Enable BOSH clients, aka "Jabber over HTTP"
|
||||
// 'bosh', // Enable BOSH clients, aka "Jabber over HTTP"
|
||||
// 'websocket', // Enable Websocket clients
|
||||
'posix', // POSIX functionality, sends server to background, enables syslog, etc.
|
||||
// 'pep', // Enables users to publish their avatar, mood, activity, playing music and more
|
||||
@ -192,6 +193,7 @@ class ProsodyConfigContent {
|
||||
this.global.set('certificates', this.paths.certs)
|
||||
}
|
||||
|
||||
this.muc.set('admins', [])
|
||||
this.muc.set('muc_room_locking', false)
|
||||
this.muc.set('muc_tombstones', false)
|
||||
this.muc.set('muc_room_default_language', 'en')
|
||||
@ -403,6 +405,16 @@ class ProsodyConfigContent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the bots virtualhost.
|
||||
*/
|
||||
useBotsVirtualHost (): void {
|
||||
this.bot = new ProsodyConfigVirtualHost('bot.' + this.prosodyDomain)
|
||||
this.bot.set('modules_enabled', ['ping'])
|
||||
|
||||
// TODO: bot vcards
|
||||
}
|
||||
|
||||
setLog (level: ProsodyLogLevel, syslog?: ProsodyLogLevel[]): void {
|
||||
let log = ''
|
||||
log += 'log = {\n'
|
||||
@ -431,6 +443,10 @@ class ProsodyConfigContent {
|
||||
content += this.anon.write()
|
||||
content += '\n\n'
|
||||
}
|
||||
if (this.bot) {
|
||||
content += this.bot.write()
|
||||
content += '\n\n'
|
||||
}
|
||||
content += this.muc.write()
|
||||
content += '\n\n'
|
||||
for (const externalComponent of this.externalComponents) {
|
||||
|
@ -342,7 +342,7 @@ class RoomChannel {
|
||||
channelConfigurationOptionsToBotRoomConf(this.options, channelConfigurationOptions)
|
||||
)
|
||||
|
||||
await BotConfiguration.singleton().update(roomJID, botConf)
|
||||
await BotConfiguration.singleton().updateRoom(roomJID, botConf)
|
||||
this.roomConfToUpdate.delete(roomJID)
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ import type { RegisterServerOptions } from '@peertube/peertube-types'
|
||||
import type { ConverseJSTheme } from '../../shared/lib/types'
|
||||
import { ensureProsodyRunning } from './prosody/ctl'
|
||||
import { RoomChannel } from './room-channel'
|
||||
import { BotsCtl } from './bots/ctl'
|
||||
import { loc } from './loc'
|
||||
|
||||
async function initSettings (options: RegisterServerOptions): Promise<void> {
|
||||
@ -94,9 +95,9 @@ Please read
|
||||
descriptionHTML: loc('experimental_warning')
|
||||
})
|
||||
registerSetting({
|
||||
name: 'disable-configuration',
|
||||
label: loc('disable_configuration_label'),
|
||||
// descriptionHTML: loc('disable_configuration_description'),
|
||||
name: 'disable-channel-configuration',
|
||||
label: loc('disable_channel_configuration_label'),
|
||||
// descriptionHTML: loc('disable_channel_configuration_description'),
|
||||
type: 'input-checkbox',
|
||||
default: false,
|
||||
private: false
|
||||
@ -401,8 +402,16 @@ Please read
|
||||
|
||||
// ********** settings changes management
|
||||
settingsManager.onSettingsChange(async (settings: any) => {
|
||||
// In case the Prosody port has changed, we must rewrite the Bot configuration file.
|
||||
// To avoid race condition, we will just stop and start the bots at every settings saving.
|
||||
await BotsCtl.destroySingleton()
|
||||
await BotsCtl.initSingleton(options)
|
||||
|
||||
peertubeHelpers.logger.info('Saving settings, ensuring prosody is running')
|
||||
await ensureProsodyRunning(options)
|
||||
|
||||
await BotsCtl.singleton().start()
|
||||
|
||||
// In case prosody-room-type changed, we must rebuild room-channel links.
|
||||
if (settings['prosody-room-type'] !== currentProsodyRoomtype) {
|
||||
peertubeHelpers.logger.info('Setting prosody-room-type has changed value, must rebuild room-channel infos')
|
||||
|
@ -11,6 +11,7 @@ import { unloadDebugMode } from './lib/debug'
|
||||
import { loadLoc } from './lib/loc'
|
||||
import { RoomChannel } from './lib/room-channel'
|
||||
import { BotConfiguration } from './lib/configuration/bot'
|
||||
import { BotsCtl } from './lib/bots/ctl'
|
||||
import decache from 'decache'
|
||||
|
||||
// FIXME: Peertube unregister don't have any parameter.
|
||||
@ -35,6 +36,9 @@ async function register (options: RegisterServerOptions): Promise<any> {
|
||||
// roomChannelNeedsDataInit: if true, means that the data file does not exist (or is invalid), so we must initiate it
|
||||
const roomChannelNeedsDataInit = !await roomChannelSingleton.readData()
|
||||
|
||||
// BotsCtl.initSingleton() will force reload the bots conf files, so must be done before generating Prosody Conf.
|
||||
await BotsCtl.initSingleton(options)
|
||||
|
||||
await migrateSettings(options)
|
||||
|
||||
await initSettings(options)
|
||||
@ -48,20 +52,35 @@ async function register (options: RegisterServerOptions): Promise<any> {
|
||||
await prepareProsody(options)
|
||||
await ensureProsodyRunning(options)
|
||||
|
||||
let preBotPromise: Promise<void>
|
||||
if (roomChannelNeedsDataInit) {
|
||||
logger.info('The RoomChannel singleton has not found any data, we must rebuild')
|
||||
// no need to wait here, can be done without await.
|
||||
roomChannelSingleton.rebuildData().then(
|
||||
preBotPromise = roomChannelSingleton.rebuildData().then(
|
||||
() => { logger.info('RoomChannel singleton rebuild done') },
|
||||
(reason) => { logger.error('RoomChannel singleton rebuild failed: ' + (reason as string)) }
|
||||
)
|
||||
} else {
|
||||
preBotPromise = Promise.resolve()
|
||||
}
|
||||
|
||||
// Don't need to wait for the bot to start.
|
||||
preBotPromise.then(
|
||||
async () => {
|
||||
await BotsCtl.singleton().start()
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
} catch (error) {
|
||||
options.peertubeHelpers.logger.error('Error when launching Prosody: ' + (error as string))
|
||||
}
|
||||
}
|
||||
|
||||
async function unregister (): Promise<any> {
|
||||
try {
|
||||
await BotsCtl.destroySingleton()
|
||||
} catch (_error) {} // BotsCtl will log errors.
|
||||
|
||||
if (OPTIONS) {
|
||||
try {
|
||||
await ensureProsodyNotRunning(OPTIONS)
|
||||
|
@ -29,7 +29,7 @@ Following settings concern the federation with other Peertube instances, and oth
|
||||
Following settings concern the advanced channel options:
|
||||
users will be able to add some customization on their channels, activate the moderation bot, ...
|
||||
|
||||
### {{% livechat_label disable_configuration_label %}}
|
||||
### {{% livechat_label disable_channel_configuration_label %}}
|
||||
|
||||
If you encounter any issue with this feature, you can disable it.
|
||||
|
||||
|
@ -93,9 +93,16 @@ There will be some cleaning batch, to delete deprecated files.
|
||||
The `bot/muc_domain` (where muc_domain is the current MUC domain) folder contains configuration files that are read by the moderation bot.
|
||||
This bot uses the [xmppjs-chat-bot](https://github.com/JohnXLivingston/xmppjs-chat-bot) package.
|
||||
|
||||
Note: we include the MUC domain (`room.instance.tld`) in the filename in case the instance domain changes.
|
||||
Note: we include the MUC domain (`room.instance.tld`) in the dirname in case the instance domain changes.
|
||||
In such case, existing rooms could get lost, and we want a way to ignore them to avoid gettings errors.
|
||||
|
||||
## bot/muc_domain/moderation.json
|
||||
|
||||
The `bot/muc_domain/moderation.json` file contains the moderation bot global configuration.
|
||||
This bot uses the [xmppjs-chat-bot](https://github.com/JohnXLivingston/xmppjs-chat-bot) package, see it's README file for more information.
|
||||
|
||||
Note: this includes the bot username and password. Don't let it leak.
|
||||
|
||||
### bot/muc_domain/rooms
|
||||
|
||||
The `bot/muc_domain/rooms` folder contains room configuration files.
|
||||
|
Loading…
x
Reference in New Issue
Block a user