Terms&Conditions (#18) WIP:
* Converse module to display terms. * Prosody module to send terms.
This commit is contained in:
parent
45a63eaecd
commit
b110456029
@ -46,6 +46,7 @@ import './plugins/fullscreen/index.js'
|
|||||||
|
|
||||||
import '../custom/plugins/size/index.js'
|
import '../custom/plugins/size/index.js'
|
||||||
import '../custom/plugins/tasks/index.js'
|
import '../custom/plugins/tasks/index.js'
|
||||||
|
import '../custom/plugins/terms/index.js'
|
||||||
/* END: Removable components */
|
/* END: Removable components */
|
||||||
|
|
||||||
import { CORE_PLUGINS } from './headless/shared/constants.js'
|
import { CORE_PLUGINS } from './headless/shared/constants.js'
|
||||||
@ -53,6 +54,7 @@ import { ROOM_FEATURES } from './headless/plugins/muc/constants.js'
|
|||||||
// We must add our custom plugins to CORE_PLUGINS (so it is white listed):
|
// We must add our custom plugins to CORE_PLUGINS (so it is white listed):
|
||||||
CORE_PLUGINS.push('livechat-converse-size')
|
CORE_PLUGINS.push('livechat-converse-size')
|
||||||
CORE_PLUGINS.push('livechat-converse-tasks')
|
CORE_PLUGINS.push('livechat-converse-tasks')
|
||||||
|
CORE_PLUGINS.push('livechat-converse-terms')
|
||||||
// We must also add our custom ROOM_FEATURES, so that they correctly resets
|
// We must also add our custom ROOM_FEATURES, so that they correctly resets
|
||||||
// (see headless/plugins/muc, getDiscoInfoFeatures, which loops on this const)
|
// (see headless/plugins/muc, getDiscoInfoFeatures, which loops on this const)
|
||||||
ROOM_FEATURES.push('x_peertubelivechat_mute_anonymous')
|
ROOM_FEATURES.push('x_peertubelivechat_mute_anonymous')
|
||||||
|
62
conversejs/custom/plugins/terms/components/muc-terms.js
Normal file
62
conversejs/custom/plugins/terms/components/muc-terms.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { CustomElement } from 'shared/components/element.js'
|
||||||
|
import { api } from '@converse/headless/core'
|
||||||
|
import { html } from 'lit'
|
||||||
|
import { __ } from 'i18n'
|
||||||
|
|
||||||
|
import '../styles/muc-terms.scss'
|
||||||
|
|
||||||
|
export default class MUCTermsView extends CustomElement {
|
||||||
|
static get properties () {
|
||||||
|
return {
|
||||||
|
model: { type: Object, attribute: true },
|
||||||
|
termstype: { type: String, attribute: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async initialize () {
|
||||||
|
if (!this.model) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.listenTo(this.model, 'change:x_livechat_terms_' + this.termstype, () => this.requestUpdate())
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const terms = this.model?.get('x_livechat_terms_' + this.termstype)
|
||||||
|
return html`
|
||||||
|
${terms && terms.body && !this._hideInfoBox(terms.body)
|
||||||
|
? html`
|
||||||
|
<div>
|
||||||
|
<converse-rich-text text=${terms.body} render_styling></converse-rich-text>
|
||||||
|
<i class="livechat-hide-terms-info-box" @click=${this.closeInfoBox} title=${__('Close')}>
|
||||||
|
<converse-icon class="fa fa-times" size="1em"></converse-icon>
|
||||||
|
</i>
|
||||||
|
</div>`
|
||||||
|
: ''
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
|
||||||
|
closeInfoBox (ev) {
|
||||||
|
ev.preventDefault()
|
||||||
|
const terms = this.model?.get('x_livechat_terms_' + this.termstype)
|
||||||
|
if (terms) {
|
||||||
|
localStorage?.setItem('x_livechat_terms_' + this.termstype + '_hidden', terms.body)
|
||||||
|
}
|
||||||
|
this.requestUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
_hideInfoBox (body) {
|
||||||
|
// When hiding the infobox, we store in localStorage the current body, so we will show it again if message change.
|
||||||
|
// Note: for termstype=global we don't store the MUC server, so if user join chat from different instances,
|
||||||
|
// it will show terms again
|
||||||
|
// Note: same for termstype=muc, we don't store the MUC JID, so if user changes channel,
|
||||||
|
// it will probably show terms again
|
||||||
|
const lsHideInfoBox = localStorage?.getItem('x_livechat_terms_' + this.termstype + '_hidden')
|
||||||
|
return lsHideInfoBox === body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.elements.define('livechat-converse-muc-terms', MUCTermsView)
|
48
conversejs/custom/plugins/terms/index.js
Normal file
48
conversejs/custom/plugins/terms/index.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { converse, api } from '../../../src/headless/core.js'
|
||||||
|
import './components/muc-terms.js'
|
||||||
|
|
||||||
|
const { sizzle } = converse.env
|
||||||
|
|
||||||
|
converse.plugins.add('livechat-converse-terms', {
|
||||||
|
dependencies: ['converse-muc'],
|
||||||
|
initialize () {
|
||||||
|
api.listen.on('parseMUCMessage', (stanza, attrs) => {
|
||||||
|
const livechatTerms = sizzle('x-livechat-terms', stanza)
|
||||||
|
if (!livechatTerms.length) {
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
return Object.assign(
|
||||||
|
attrs,
|
||||||
|
{
|
||||||
|
x_livechat_terms: livechatTerms[0].getAttribute('type')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
overrides: {
|
||||||
|
ChatRoom: {
|
||||||
|
onMessage: function onMessage (attrs) {
|
||||||
|
if (!attrs.x_livechat_terms) {
|
||||||
|
return this.__super__.onMessage(attrs)
|
||||||
|
}
|
||||||
|
// We received a x-livechat-terms message, we don't forward it to standard onMessage,
|
||||||
|
// but we just update the room attribute.
|
||||||
|
const type = attrs.x_livechat_terms
|
||||||
|
if (type !== 'global' && type !== 'muc') {
|
||||||
|
console.error('Invalid x-livechat-terms type: ', type)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// console.info('Received a x-livechat-terms message', attrs)
|
||||||
|
const options = {}
|
||||||
|
options['x_livechat_terms_' + type] = attrs
|
||||||
|
this.set(options)
|
||||||
|
// this will be displayed by the livechat-converse-muc-terms custom element,
|
||||||
|
// which is inserted in the DOM by the muc.js template overload.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
41
conversejs/custom/plugins/terms/styles/muc-terms.scss
Normal file
41
conversejs/custom/plugins/terms/styles/muc-terms.scss
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
.conversejs {
|
||||||
|
livechat-converse-muc-terms {
|
||||||
|
background-color: var(--peertube-main-background);
|
||||||
|
color: var(--peertube-main-foreground);
|
||||||
|
|
||||||
|
div {
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--peertube-menu-background);
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
|
||||||
|
converse-rich-text {
|
||||||
|
flex-grow: 2;
|
||||||
|
max-height: 5em;
|
||||||
|
overflow-y: scroll;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.livechat-hide-terms-info-box {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
flex-shrink: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.livechat-readonly .conversejs {
|
||||||
|
livechat-converse-muc-terms {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
@ -63,7 +63,7 @@ class SlowMode extends CustomElement {
|
|||||||
LOC_slow_mode_info,
|
LOC_slow_mode_info,
|
||||||
this.model.config.get('slow_mode_duration')
|
this.model.config.get('slow_mode_duration')
|
||||||
)}
|
)}
|
||||||
<i class="livechat-hide-slow-mode-info-box" @click=${this.closeSlowModeInfoBox}>
|
<i class="livechat-hide-slow-mode-info-box" @click=${this.closeSlowModeInfoBox} title=${__('Close')}>
|
||||||
<converse-icon class="fa fa-times" size="1em"></converse-icon>
|
<converse-icon class="fa fa-times" size="1em"></converse-icon>
|
||||||
</i>
|
</i>
|
||||||
</div>`
|
</div>`
|
||||||
|
27
conversejs/custom/templates/muc.js
Normal file
27
conversejs/custom/templates/muc.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2013-2024 JC Brand <https://github.com/conversejs/converse.js/>
|
||||||
|
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// Must import the original muc.js, because it imports some custom elements files.
|
||||||
|
import '../../src/plugins/muc-views/templates/muc.js'
|
||||||
|
import { getChatRoomBodyTemplate } from '../../src/plugins/muc-views/utils.js'
|
||||||
|
import { html } from 'lit'
|
||||||
|
|
||||||
|
// Overloading the original muc.js, to add some custom elements.
|
||||||
|
export default (o) => {
|
||||||
|
return html`
|
||||||
|
<div class="flyout box-flyout">
|
||||||
|
<converse-dragresize></converse-dragresize>
|
||||||
|
${
|
||||||
|
o.model
|
||||||
|
? html`
|
||||||
|
<converse-muc-heading jid="${o.model.get('jid')}" class="chat-head chat-head-chatroom row no-gutters">
|
||||||
|
</converse-muc-heading>
|
||||||
|
<livechat-converse-muc-terms .model=${o.model} termstype="global"></livechat-converse-muc-terms>
|
||||||
|
<livechat-converse-muc-terms .model=${o.model} termstype="muc"></livechat-converse-muc-terms>
|
||||||
|
<div class="chat-body chatroom-body row no-gutters">${getChatRoomBodyTemplate(o)}</div>`
|
||||||
|
: ''}
|
||||||
|
</div>`
|
||||||
|
}
|
@ -40,6 +40,7 @@ module.exports = merge(prod, {
|
|||||||
alias: {
|
alias: {
|
||||||
'./templates/muc-bottom-panel.js': path.resolve('custom/templates/muc-bottom-panel.js'),
|
'./templates/muc-bottom-panel.js': path.resolve('custom/templates/muc-bottom-panel.js'),
|
||||||
'./templates/muc-head.js': path.resolve(__dirname, 'custom/templates/muc-head.js'),
|
'./templates/muc-head.js': path.resolve(__dirname, 'custom/templates/muc-head.js'),
|
||||||
|
'./templates/muc.js': path.resolve(__dirname, 'custom/templates/muc.js'),
|
||||||
'../../templates/background_logo.js$': path.resolve(__dirname, 'custom/templates/background_logo.js'),
|
'../../templates/background_logo.js$': path.resolve(__dirname, 'custom/templates/background_logo.js'),
|
||||||
'./templates/muc-chatarea.js': path.resolve('custom/templates/muc-chatarea.js'),
|
'./templates/muc-chatarea.js': path.resolve('custom/templates/muc-chatarea.js'),
|
||||||
|
|
||||||
|
34
prosody-modules/mod_muc_peertubelivechat_terms/README.md
Normal file
34
prosody-modules/mod_muc_peertubelivechat_terms/README.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
# mod_muc_peertubelivechat_terms
|
||||||
|
|
||||||
|
This module is a custom module to handle Terms&Conditions in the livechat Peertube plugin.
|
||||||
|
|
||||||
|
This module is part of peertube-plugin-livechat, and is under the same LICENSE.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
When a new occupant session is created for a MUC, this module will send to the user the global terms,
|
||||||
|
and the MUC-specific terms (if defined).
|
||||||
|
|
||||||
|
This is done by sending groupchat messages.
|
||||||
|
These messages will contain a "x-livechat-terms" tag, so that livechat front-end can detect these messages, and display them differently.
|
||||||
|
For standard XMPP clients, these messages will show as standard MUC message coming from a specific nickname.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
This modules take following options.
|
||||||
|
|
||||||
|
### muc_terms_service_nickname
|
||||||
|
|
||||||
|
The nickname that will be used by service messages.
|
||||||
|
This module reserves the nickname, so than nobody can use it in MUC rooms
|
||||||
|
(we don't want any user to spoof this nickname).
|
||||||
|
|
||||||
|
### muc_terms
|
||||||
|
|
||||||
|
The global terms.
|
@ -0,0 +1,66 @@
|
|||||||
|
-- mod_muc_peertubelivechat_terms
|
||||||
|
--
|
||||||
|
-- SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||||
|
-- SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
--
|
||||||
|
-- This file is AGPL-v3 licensed.
|
||||||
|
-- Please see the Peertube livechat plugin copyright information.
|
||||||
|
-- https://livingston.frama.io/peertube-plugin-livechat/credits/
|
||||||
|
--
|
||||||
|
local jid_escape = require "util.jid".escape;
|
||||||
|
local jid_resource = require "util.jid".resource;
|
||||||
|
local st = require "util.stanza";
|
||||||
|
local id = require "util.id";
|
||||||
|
|
||||||
|
local service_nickname = module:get_option_string("muc_terms_service_nickname", "Service");
|
||||||
|
local global_terms = module:get_option_string("muc_terms", "");
|
||||||
|
|
||||||
|
-- send the terms when joining:
|
||||||
|
function send_terms(event)
|
||||||
|
local origin = event.origin;
|
||||||
|
local room = event.room;
|
||||||
|
local occupant = event.occupant;
|
||||||
|
if global_terms then
|
||||||
|
local from = room.jid .. '/' .. jid_escape(service_nickname);
|
||||||
|
module:log("debug", "Sending global terms to %s from %s (room %s)", occupant.jid, from, room);
|
||||||
|
local message = st.message({
|
||||||
|
type = "groupchat",
|
||||||
|
to = occupant.jid,
|
||||||
|
from = from,
|
||||||
|
id = id.medium()
|
||||||
|
}, global_terms)
|
||||||
|
:tag('x-livechat-terms', { type = "global" }):up(); -- adding a custom tag to specify that it is a "terms" message, so that frontend can display it with a special template.
|
||||||
|
origin.send(message);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Note: we could do that on muc-occupant-joined or muc-occupant-session-new.
|
||||||
|
-- The first will not send it to multiple clients, the second will.
|
||||||
|
-- After some reflexion, i will try muc-occupant-session-new, and see if it works as expected.
|
||||||
|
module:hook("muc-occupant-session-new", send_terms);
|
||||||
|
|
||||||
|
-- reserve the service_nickname:
|
||||||
|
function enforce_nick_policy(event)
|
||||||
|
local origin, stanza = event.origin, event.stanza;
|
||||||
|
local requested_nick = jid_resource(stanza.attr.to);
|
||||||
|
local room = event.room;
|
||||||
|
if not room then return; end
|
||||||
|
|
||||||
|
if requested_nick == service_nickname then
|
||||||
|
module:log("debug", "Occupant tried to use the %s reserved nickname, blocking it.", service_nickname);
|
||||||
|
local reply = st.error_reply(stanza, "cancel", "conflict", nil, room.jid):up();
|
||||||
|
origin.send(reply);
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
module:hook("muc-occupant-pre-join", enforce_nick_policy);
|
||||||
|
module:hook("muc-occupant-pre-change", enforce_nick_policy);
|
||||||
|
|
||||||
|
-- security check: we must remove all "x-livechat-terms" tag, to be sure nobody tries to spoof terms!
|
||||||
|
module:hook("muc-occupant-groupchat", function(event)
|
||||||
|
event.stanza:maptags(function (child)
|
||||||
|
if child.name == 'x-livechat-terms' then
|
||||||
|
return nil;
|
||||||
|
end
|
||||||
|
return child;
|
||||||
|
end);
|
||||||
|
end, 100);
|
@ -175,7 +175,8 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
|||||||
'chat-no-anonymous',
|
'chat-no-anonymous',
|
||||||
'auto-ban-anonymous-ip',
|
'auto-ban-anonymous-ip',
|
||||||
'federation-dont-publish-remotely',
|
'federation-dont-publish-remotely',
|
||||||
'disable-channel-configuration'
|
'disable-channel-configuration',
|
||||||
|
'chat-terms'
|
||||||
])
|
])
|
||||||
|
|
||||||
const valuesToHideInDiagnostic = new Map<string, string>()
|
const valuesToHideInDiagnostic = new Map<string, string>()
|
||||||
@ -199,6 +200,9 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
|||||||
let certificates: ProsodyConfigCertificates = false
|
let certificates: ProsodyConfigCertificates = false
|
||||||
const useBots = !settings['disable-channel-configuration']
|
const useBots = !settings['disable-channel-configuration']
|
||||||
const bots: ProsodyConfig['bots'] = {}
|
const bots: ProsodyConfig['bots'] = {}
|
||||||
|
const chatTerms = (typeof settings['chat-terms'] === 'string') && settings['chat-terms']
|
||||||
|
? settings['chat-terms']
|
||||||
|
: undefined
|
||||||
|
|
||||||
let useExternal: boolean = false
|
let useExternal: boolean = false
|
||||||
try {
|
try {
|
||||||
@ -260,7 +264,7 @@ async function getProsodyConfig (options: RegisterServerOptionsV5): Promise<Pros
|
|||||||
const roomApiUrl = baseApiUrl + 'room?apikey=' + apikey + '&jid={room.jid|jid_node}'
|
const roomApiUrl = baseApiUrl + 'room?apikey=' + apikey + '&jid={room.jid|jid_node}'
|
||||||
const testApiUrl = baseApiUrl + 'test?apikey=' + apikey
|
const testApiUrl = baseApiUrl + 'test?apikey=' + apikey
|
||||||
|
|
||||||
const config = new ProsodyConfigContent(paths, prosodyDomain)
|
const config = new ProsodyConfigContent(paths, prosodyDomain, chatTerms)
|
||||||
if (!disableAnon) {
|
if (!disableAnon) {
|
||||||
config.useAnonymous(autoBanIP)
|
config.useAnonymous(autoBanIP)
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,30 @@ import type { ExternalComponent } from './components'
|
|||||||
import { BotConfiguration } from '../../configuration/bot'
|
import { BotConfiguration } from '../../configuration/bot'
|
||||||
import { userInfo } from 'os'
|
import { userInfo } from 'os'
|
||||||
|
|
||||||
type ConfigEntryValue = boolean | number | string | ConfigEntryValue[]
|
/**
|
||||||
|
* Use this class to construct a string that will be writen as a multiline Lua string.
|
||||||
|
*/
|
||||||
|
class ConfigEntryValueMultiLineString extends String {
|
||||||
|
public serialize (): string {
|
||||||
|
const s = this.toString()
|
||||||
|
let i = 0
|
||||||
|
// Lua multiline strings can be escaped by [[ ]], or [==[ ]==] with any number of =
|
||||||
|
// http://lua-users.org/wiki/StringsTutorial
|
||||||
|
// So, to have a proper value, we will check if the string contains [[ or ]],
|
||||||
|
// and try again by adding "=" until we do not found the pattern.
|
||||||
|
while (true) {
|
||||||
|
const opening = '[' + '='.repeat(i) + '['
|
||||||
|
const closing = ']' + '='.repeat(i) + ']'
|
||||||
|
if (!s.includes(opening) && !s.includes(closing)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return '[' + '='.repeat(i) + '[' + s + ']' + '='.repeat(i) + ']'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigEntryValue = boolean | number | string | ConfigEntryValueMultiLineString | ConfigEntryValue[]
|
||||||
|
|
||||||
type ConfigEntries = Map<string, ConfigEntryValue>
|
type ConfigEntries = Map<string, ConfigEntryValue>
|
||||||
|
|
||||||
@ -33,6 +56,9 @@ type ConfigLogExpiration =
|
|||||||
ConfigLogExpirationNever | ConfigLogExpirationPeriod | ConfigLogExpirationSeconds | ConfigLogExpirationError
|
ConfigLogExpirationNever | ConfigLogExpirationPeriod | ConfigLogExpirationSeconds | ConfigLogExpirationError
|
||||||
|
|
||||||
function writeValue (value: ConfigEntryValue): string {
|
function writeValue (value: ConfigEntryValue): string {
|
||||||
|
if (value instanceof ConfigEntryValueMultiLineString) {
|
||||||
|
return value.serialize() + ';\n'
|
||||||
|
}
|
||||||
if (typeof value === 'boolean') {
|
if (typeof value === 'boolean') {
|
||||||
return value.toString() + ';\n'
|
return value.toString() + ';\n'
|
||||||
}
|
}
|
||||||
@ -151,7 +177,7 @@ class ProsodyConfigContent {
|
|||||||
log: string
|
log: string
|
||||||
prosodyDomain: string
|
prosodyDomain: string
|
||||||
|
|
||||||
constructor (paths: ProsodyFilePaths, prosodyDomain: string) {
|
constructor (paths: ProsodyFilePaths, prosodyDomain: string, chatTerms?: string) {
|
||||||
this.paths = paths
|
this.paths = paths
|
||||||
this.global = new ProsodyConfigGlobal()
|
this.global = new ProsodyConfigGlobal()
|
||||||
this.log = ''
|
this.log = ''
|
||||||
@ -212,9 +238,16 @@ class ProsodyConfigContent {
|
|||||||
this.muc.set('muc_room_default_history_length', 20)
|
this.muc.set('muc_room_default_history_length', 20)
|
||||||
|
|
||||||
this.muc.add('modules_enabled', 'muc_slow_mode')
|
this.muc.add('modules_enabled', 'muc_slow_mode')
|
||||||
|
this.muc.add('slow_mode_duration_form_position', 120)
|
||||||
|
|
||||||
this.muc.add('modules_enabled', 'pubsub_peertubelivechat')
|
this.muc.add('modules_enabled', 'pubsub_peertubelivechat')
|
||||||
this.muc.add('modules_enabled', 'muc_peertubelivechat_roles')
|
this.muc.add('modules_enabled', 'muc_peertubelivechat_roles')
|
||||||
this.muc.add('slow_mode_duration_form_position', 120)
|
|
||||||
|
this.muc.add('modules_enabled', 'muc_peertubelivechat_terms')
|
||||||
|
this.muc.set('muc_terms_service_nickname', 'Peertube')
|
||||||
|
if (chatTerms) {
|
||||||
|
this.muc.set('muc_terms', new ConfigEntryValueMultiLineString(chatTerms))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useAnonymous (autoBanIP: boolean): void {
|
useAnonymous (autoBanIP: boolean): void {
|
||||||
|
Loading…
Reference in New Issue
Block a user