Chat Federation: refactoring ActivityPub data:

The data format used by plugin v6.3.0 was not well suited.
Here comes a new data format, with S2S informations.
The plugin can automatically upgrade old format.
It also continues to provide the old format, so than remote instance
that did not update the plugin will still work.
This commit is contained in:
John Livingston
2023-05-24 15:09:56 +02:00
parent 4f9534dc11
commit 6ed69d2c2f
10 changed files with 547 additions and 212 deletions

View File

@ -1,4 +1,4 @@
import type { LiveChatJSONLDAttribute } from './types'
import type { LiveChatJSONLDAttributeV1 } from './types'
interface AnonymousConnectionInfos {
roomJID: string
@ -7,42 +7,59 @@ interface AnonymousConnectionInfos {
userJID: string
}
function anonymousConnectionInfos (livechatInfos: LiveChatJSONLDAttribute | false): AnonymousConnectionInfos | null {
function anonymousConnectionInfos (livechatInfos: LiveChatJSONLDAttributeV1 | false): AnonymousConnectionInfos | null {
if (!livechatInfos) { return null }
if (!livechatInfos.links) { return null }
if (livechatInfos.type !== 'xmpp') { return null }
if (!livechatInfos.xmppserver) { return null }
if (!livechatInfos.xmppserver.anonymous) { return null }
const r: AnonymousConnectionInfos = {
roomJID: livechatInfos.jid,
userJID: ''
userJID: livechatInfos.xmppserver.anonymous.virtualhost
}
for (const link of livechatInfos.links) {
// Note: userJID is on both links. But should have the same value.
if (link.type === 'xmpp-bosh-anonymous') {
r.boshUri = link.url
r.userJID = link.jid
} else if (link.type === 'xmpp-websocket-anonymous') {
r.wsUri = link.url
r.userJID = link.jid
}
if (livechatInfos.xmppserver.anonymous.bosh) {
r.boshUri = livechatInfos.xmppserver.anonymous.bosh
}
if (r.userJID === '') {
if (livechatInfos.xmppserver.anonymous.websocket) {
r.wsUri = livechatInfos.xmppserver.anonymous.websocket
}
if (!r.boshUri && !r.wsUri) {
return null
}
return r
}
function remoteAuthenticatedConnectionEnabled (livechatInfos: LiveChatJSONLDAttribute | false): boolean {
function remoteAuthenticatedConnectionEnabled (livechatInfos: LiveChatJSONLDAttributeV1): boolean {
if (!livechatInfos) { return false }
if (!livechatInfos.links) { return false }
if (livechatInfos.type !== 'xmpp') { return false }
for (const link of livechatInfos.links) {
if (link.type === 'xmpp-peertube-livechat-ws-s2s') { return true }
if (link.type === 'xmpp-s2s') { return true }
}
if (!('xmppserver' in livechatInfos)) { return false }
if (!livechatInfos.xmppserver) { return false }
if (livechatInfos.xmppserver.websockets2s) { return true }
if (livechatInfos.xmppserver.directs2s) { return true }
return false
}
function compatibleRemoteAuthenticatedConnectionEnabled (
livechatInfos: LiveChatJSONLDAttributeV1,
canWebsocketS2S: boolean,
canDirectS2S: boolean
): boolean {
if (!livechatInfos) { return false }
if (livechatInfos.type !== 'xmpp') { return false }
if (!('xmppserver' in livechatInfos)) { return false }
if (!livechatInfos.xmppserver) { return false }
if (canWebsocketS2S && livechatInfos.xmppserver.websockets2s) { return true }
if (canDirectS2S && livechatInfos.xmppserver.directs2s) { return true }
return false
}
export {
anonymousConnectionInfos,
remoteAuthenticatedConnectionEnabled
remoteAuthenticatedConnectionEnabled,
compatibleRemoteAuthenticatedConnectionEnabled
}

View File

@ -16,7 +16,7 @@ async function readIncomingAPVideo (
let peertubeLiveChat = ('peertubeLiveChat' in videoAPObject) ? videoAPObject.peertubeLiveChat : false
// We must sanitize peertubeLiveChat, as it comes for the outer world.
peertubeLiveChat = sanitizePeertubeLiveChatInfos(peertubeLiveChat)
peertubeLiveChat = sanitizePeertubeLiveChatInfos(options, peertubeLiveChat)
await storeVideoLiveChatInfos(options, video, peertubeLiveChat)
if (video.remote) {

View File

@ -1,5 +1,11 @@
import type { RegisterServerOptions, VideoObject } from '@peertube/peertube-types'
import type { LiveChatVideoObject, VideoBuildResultContext, LiveChatJSONLDLink, LiveChatJSONLDAttribute } from './types'
import type { RegisterServerOptions, VideoObject, SettingValue } from '@peertube/peertube-types'
import type {
LiveChatVideoObject,
VideoBuildResultContext,
LiveChatJSONLDLink,
LiveChatJSONLDAttribute,
PeertubeXMPPServerInfos
} from './types'
import { storeVideoLiveChatInfos } from './storage'
import { videoHasWebchat } from '../../../shared/lib/video'
import { getBoshUri, getWSUri, getWSS2SUri } from '../uri/webchat'
@ -63,7 +69,6 @@ async function videoBuildJSONLD (
logger.debug(`Adding LiveChat data on video uuid=${video.uuid}...`)
const prosodyDomain = await getProsodyDomain(options)
const userJID = 'anon.' + prosodyDomain
let roomJID: string
if (settings['prosody-room-type'] === 'channel') {
roomJID = `channel.${video.channelId}@room.${prosodyDomain}`
@ -71,51 +76,38 @@ async function videoBuildJSONLD (
roomJID = `${video.uuid}@room.${prosodyDomain}`
}
const serverInfos = await _serverBuildInfos(options, {
'federation-dont-publish-remotely': settings['federation-dont-publish-remotely'],
'prosody-s2s-port': settings['prosody-s2s-port'],
'prosody-room-allow-s2s': settings['prosody-room-allow-s2s'],
'disable-websocket': settings['disable-websocket'],
'chat-no-anonymous': settings['chat-no-anonymous']
})
// For backward compatibility with remote servers, using plugin <=6.3.0, we must provide links:
const links: LiveChatJSONLDLink[] = []
if (!settings['federation-dont-publish-remotely']) {
const wsS2SUri = getWSS2SUri(options)
if (wsS2SUri) {
if (serverInfos.anonymous) {
if (serverInfos.anonymous.bosh) {
links.push({
type: 'xmpp-peertube-livechat-ws-s2s',
url: canonicalizePluginUri(options, wsS2SUri, {
removePluginVersion: true,
protocol: 'ws'
})
type: 'xmpp-bosh-anonymous',
url: serverInfos.anonymous.bosh,
jid: serverInfos.anonymous.virtualhost
})
}
}
if (settings['prosody-room-allow-s2s']) {
links.push({
type: 'xmpp-s2s',
host: prosodyDomain,
port: (settings['prosody-s2s-port'] as string) ?? ''
})
}
if (!settings['chat-no-anonymous']) {
links.push({
type: 'xmpp-bosh-anonymous',
url: canonicalizePluginUri(options, getBoshUri(options), { removePluginVersion: true }),
jid: userJID
})
if (!settings['disable-websocket']) {
const wsUri = getWSUri(options)
if (wsUri) {
links.push({
type: 'xmpp-websocket-anonymous',
url: canonicalizePluginUri(options, wsUri, {
removePluginVersion: true,
protocol: 'ws'
}),
jid: userJID
})
}
if (serverInfos.anonymous.websocket) {
links.push({
type: 'xmpp-websocket-anonymous',
url: serverInfos.anonymous.websocket,
jid: serverInfos.anonymous.virtualhost
})
}
}
const peertubeLiveChat: LiveChatJSONLDAttribute = {
type: 'xmpp',
jid: roomJID,
links
links,
xmppserver: serverInfos
}
Object.assign(jsonld, {
peertubeLiveChat
@ -125,6 +117,85 @@ async function videoBuildJSONLD (
return jsonld
}
export {
videoBuildJSONLD
async function serverBuildInfos (options: RegisterServerOptions): Promise<PeertubeXMPPServerInfos> {
const settings = await options.settingsManager.getSettings([
'federation-dont-publish-remotely',
'prosody-s2s-port',
'prosody-room-allow-s2s',
'disable-websocket',
'chat-no-anonymous'
])
return _serverBuildInfos(options, {
'federation-dont-publish-remotely': settings['federation-dont-publish-remotely'],
'prosody-s2s-port': settings['prosody-s2s-port'],
'prosody-room-allow-s2s': settings['prosody-room-allow-s2s'],
'disable-websocket': settings['disable-websocket'],
'chat-no-anonymous': settings['chat-no-anonymous']
})
}
async function _serverBuildInfos (
options: RegisterServerOptions,
settings: {
'federation-dont-publish-remotely': SettingValue
'prosody-s2s-port': SettingValue
'prosody-room-allow-s2s': SettingValue
'disable-websocket': SettingValue
'chat-no-anonymous': SettingValue
}
): Promise<PeertubeXMPPServerInfos> {
const prosodyDomain = await getProsodyDomain(options)
const mucDomain = 'room.' + prosodyDomain
const anonDomain = 'anon.' + prosodyDomain
let directs2s
if (settings['prosody-room-allow-s2s'] && settings['prosody-s2s-port']) {
directs2s = {
port: (settings['prosody-s2s-port'] as string) ?? ''
}
}
let websockets2s
if (!settings['federation-dont-publish-remotely']) {
const wsS2SUri = getWSS2SUri(options)
if (wsS2SUri) { // can be undefined for old Peertube version that dont allow WS for plugins
websockets2s = {
url: canonicalizePluginUri(options, wsS2SUri, {
removePluginVersion: true,
protocol: 'ws'
})
}
}
}
let anonymous: PeertubeXMPPServerInfos['anonymous'] | undefined
if (!settings['chat-no-anonymous']) {
anonymous = {
bosh: canonicalizePluginUri(options, getBoshUri(options), { removePluginVersion: true }),
virtualhost: anonDomain
}
if (!settings['disable-websocket']) {
const wsUri = getWSUri(options)
if (wsUri) {
anonymous.websocket = canonicalizePluginUri(options, wsUri, {
removePluginVersion: true,
protocol: 'ws'
})
}
}
}
return {
host: prosodyDomain,
muc: mucDomain,
directs2s,
websockets2s,
anonymous
}
}
export {
videoBuildJSONLD,
serverBuildInfos
}

View File

@ -0,0 +1,83 @@
import type { RegisterServerOptions } from '@peertube/peertube-types'
import { getBaseRouterRoute } from '../helpers'
import { canonicalizePluginUri } from '../uri/canonicalize'
import { URL } from 'url'
const got = require('got')
/**
* This function returns remote server connection informations.
* If these informations are not available (because we receive no ActivityPub
* data from this remote server), we will fetch them on a dedicated url.
*
* This information will also be stored.
*
* For all remote videos that our instance federated, remote server information
* are sent using ActivityPub.
* But there is a case in which we need information about potentially unknown
* servers.
*
* Use case:
* - server A: our server, proposing video V
* - server B: server that follows ours (or used to watch V, without following A)
* - user from B connect to the B XMPP server
* - server B has server A connection informations (got it using ActivityPub)
* - but, when using Websocket S2S, server A needs information from B, that he never receives
*
* Indeed, the XMPP S2S dialback mecanism will try to connect back to
* server A, and transmit a secret key, to ensure that all incomming connection
* are valid.
*
* For more informations about dialback: https://xmpp.org/extensions/xep-0220.html
*
* @param options server options
* @param remoteInstanceUrl remote instance url to check (as readed in the request header)
* @returns true if the remote instance is ok
*/
async function remoteServerInfos (
options: RegisterServerOptions,
remoteInstanceUrl: string
): Promise<boolean> {
const logger = options.peertubeHelpers.logger
logger.debug(`remoteServerInfos: checking if we have remote server infos for host ${remoteInstanceUrl}.`)
let url: string
try {
const u = new URL(remoteInstanceUrl)
// Assuming that the path on the remote instance is the same as on this one
// (but canonicalized to remove the plugin version)
u.pathname = getBaseRouterRoute(options) + 'api/federation_server_infos'
url = canonicalizePluginUri(options, u.toString(), {
protocol: 'http',
removePluginVersion: true
})
} catch (_err) {
logger.info('remoteServerInfos: Invalid remote instance url provided: ' + remoteInstanceUrl)
return false
}
try {
logger.debug('remoteServerInfos: We must check remote server infos using url: ' + url)
const response = await got(url, {
method: 'GET',
headers: {},
responseType: 'json'
}).json()
if (!response) {
logger.info('remoteServerInfos: Invalid remote server options')
return false
}
// FIXME/TODO
return true
} catch (_err) {
logger.info('remoteServerInfos: Can\'t get remote instance informations using url ' + url)
return false
}
}
export {
remoteServerInfos
}

View File

@ -1,73 +1,99 @@
import type { LiveChatJSONLDInfos, LiveChatJSONLDAttribute } from './types'
import type { RegisterServerOptions } from '@peertube/peertube-types'
import type { LiveChatJSONLDAttributeV1 } from './types'
import { URL } from 'url'
function sanitizePeertubeLiveChatInfos (chatInfos: any): LiveChatJSONLDAttribute {
/**
* Use this function for incoming remote informations.
* It will sanitize them, by checking everything is ok.
* It can also migrate from old format to the new one.
*
* This function can be used when informations are incoming,
* or when reading stored information (to automatically migrate them)
*
* @param chatInfos remote chat informations
* @returns a sanitized version of the remote chat informations
*/
function sanitizePeertubeLiveChatInfos (options: RegisterServerOptions, chatInfos: any): LiveChatJSONLDAttributeV1 {
if (chatInfos === false) { return false }
if (typeof chatInfos !== 'object') { return false }
if (chatInfos.type !== 'xmpp') { return false }
if ((typeof chatInfos.jid) !== 'string') { return false }
if (!Array.isArray(chatInfos.links)) { return false }
const r: LiveChatJSONLDInfos = {
if (!('xmppserver' in chatInfos)) {
// V0 format, migrating on the fly to v1.
return _sanitizePeertubeLiveChatInfosV0(options, chatInfos)
}
if (!chatInfos.xmppserver || (typeof chatInfos.xmppserver !== 'object')) {
return false
}
const xmppserver = chatInfos.xmppserver
if ((typeof xmppserver.host) !== 'string') { return false }
const host = _validateHost(xmppserver.host)
if (!host) { return false }
if ((typeof xmppserver.muc) !== 'string') { return false }
const muc = _validateHost(xmppserver.muc)
if (!muc) { return false }
const r: LiveChatJSONLDAttributeV1 = {
type: chatInfos.type,
jid: chatInfos.jid,
links: []
}
for (const link of chatInfos.links) {
if ((typeof link) !== 'object') { continue }
if (['xmpp-bosh-anonymous', 'xmpp-websocket-anonymous'].includes(link.type)) {
if ((typeof link.jid) !== 'string') { continue }
if ((typeof link.url) !== 'string') { continue }
if (
!_validUrl(link.url, {
noSearchParams: true,
protocol: link.type === 'xmpp-websocket-anonymous' ? 'ws.' : 'http.'
})
) {
continue
}
r.links.push({
type: link.type,
jid: link.jid,
url: link.url
})
}
if (link.type === 'xmpp-s2s') {
if (!/^\d+$/.test(link.port)) {
continue
}
const host = _validateHost(link.host)
if (!host) {
continue
}
r.links.push({
type: link.type,
host,
port: link.port
})
}
if (link.type === 'xmpp-peertube-livechat-ws-s2s') {
if ((typeof link.url) !== 'string') { continue }
if (
!_validUrl(link.url, {
noSearchParams: true,
protocol: 'ws.'
})
) {
continue
}
r.links.push({
type: link.type,
url: link.url
})
xmppserver: {
host,
muc
}
}
if (xmppserver.directs2s) {
if ((typeof xmppserver.directs2s) === 'object') {
const port = xmppserver.directs2s.port
if ((typeof port === 'string') && /^\d+$/.test(port)) {
r.xmppserver.directs2s = {
port
}
}
}
}
if (xmppserver.websockets2s) {
if ((typeof xmppserver.websockets2s) === 'object') {
const url = xmppserver.websockets2s.url
if ((typeof url === 'string') && _validUrl(url, {
noSearchParams: true,
protocol: 'ws.'
})) {
r.xmppserver.websockets2s = {
url
}
}
}
}
if (xmppserver.anonymous) {
const virtualhost = _validateHost(xmppserver.anonymous.virtualhost)
if (virtualhost) {
r.xmppserver.anonymous = {
virtualhost
}
const bosh = xmppserver.anonymous.bosh
if ((typeof bosh === 'string') && _validUrl(bosh, {
noSearchParams: true,
protocol: 'http.'
})) {
r.xmppserver.anonymous.bosh = bosh
}
const websocket = xmppserver.anonymous.websocket
if ((typeof websocket === 'string') && _validUrl(websocket, {
noSearchParams: true,
protocol: 'ws.'
})) {
r.xmppserver.anonymous.websocket = websocket
}
}
}
return r
}
@ -107,8 +133,9 @@ function _validUrl (s: string, constraints: URLConstraints): boolean {
return true
}
function _validateHost (s: string): false | string {
function _validateHost (s: any): false | string {
try {
if (typeof s !== 'string') { return false }
if (s.includes('/')) { return false }
const url = new URL('http://' + s)
return url.hostname
@ -117,6 +144,68 @@ function _validateHost (s: string): false | string {
}
}
function _sanitizePeertubeLiveChatInfosV0 (options: RegisterServerOptions, chatInfos: any): LiveChatJSONLDAttributeV1 {
const logger = options.peertubeHelpers.logger
logger.debug('We are have to migrate data from the old JSONLD format')
if (chatInfos === false) { return false }
if (typeof chatInfos !== 'object') { return false }
if (chatInfos.type !== 'xmpp') { return false }
if ((typeof chatInfos.jid) !== 'string') { return false }
// no link? invalid! dropping all.
if (!Array.isArray(chatInfos.links)) { return false }
const muc = _validateHost(chatInfos.jid.split('@')[1])
if (!muc) { return false }
if (!muc.startsWith('room.')) {
logger.error('We expected old format host to begin with "room.". Discarding.')
return false
}
const host = _validateHost(muc.replace(/^room\./, ''))
if (!host) { return false }
const r: LiveChatJSONLDAttributeV1 = {
type: chatInfos.type,
jid: chatInfos.jid,
xmppserver: {
host,
muc
}
}
for (const link of chatInfos.links) {
if ((typeof link) !== 'object') { continue }
if (['xmpp-bosh-anonymous', 'xmpp-websocket-anonymous'].includes(link.type)) {
if ((typeof link.jid) !== 'string') { continue }
if ((typeof link.url) !== 'string') { continue }
if (
!_validUrl(link.url, {
noSearchParams: true,
protocol: link.type === 'xmpp-websocket-anonymous' ? 'ws.' : 'http.'
})
) {
continue
}
if (!r.xmppserver.anonymous) {
r.xmppserver.anonymous = {
virtualhost: link.jid
}
}
if (link.type === 'xmpp-bosh-anonymous') {
r.xmppserver.anonymous.bosh = link.url
} else if (link.type === 'xmpp-websocket-anonymous') {
r.xmppserver.anonymous.websocket = link.url
}
}
}
return r
}
export {
sanitizePeertubeLiveChatInfos
}

View File

@ -1,5 +1,5 @@
import type { RegisterServerOptions, MVideoFullLight, MVideoAP, Video, MVideoThumbnail } from '@peertube/peertube-types'
import type { LiveChatJSONLDAttribute, LiveChatJSONLDS2SLink, LiveChatJSONLDPeertubeWSS2SLink } from './types'
import type { LiveChatJSONLDAttribute, LiveChatJSONLDAttributeV1 } from './types'
import { sanitizePeertubeLiveChatInfos } from './sanitize'
import { URL } from 'url'
import * as fs from 'fs'
@ -17,7 +17,7 @@ If a file exists, it means the video has a chat.
The file itself contains the JSON LiveChatInfos object.
*/
const cache: Map<string, LiveChatJSONLDAttribute> = new Map<string, LiveChatJSONLDAttribute>()
const cache: Map<string, LiveChatJSONLDAttributeV1> = new Map<string, LiveChatJSONLDAttributeV1>()
/**
* This function stores remote LiveChat infos that are contained in ActivityPub objects.
@ -72,7 +72,7 @@ async function storeVideoLiveChatInfos (
async function getVideoLiveChatInfos (
options: RegisterServerOptions,
video: MVideoFullLight | MVideoAP | Video | MVideoThumbnail
): Promise<LiveChatJSONLDAttribute> {
): Promise<LiveChatJSONLDAttributeV1> {
const logger = options.peertubeHelpers.logger
const cached = cache.get(video.url)
@ -92,7 +92,7 @@ async function getVideoLiveChatInfos (
return false
}
// We must sanitize here, in case a previous plugin version did not sanitize enougth.
const r = sanitizePeertubeLiveChatInfos(content)
const r = sanitizePeertubeLiveChatInfos(options, content)
cache.set(video.url, r)
return r
}
@ -103,51 +103,65 @@ async function getVideoLiveChatInfos (
* These information can then be read by Prosody module mod_s2s_peertubelivechat.
*
* We simply store the more recent informations. Indeed, it should be consistent between videos.
*
* Note: XMPP actively uses subdomains to seperate components.
* Peertube chats are on the domain `room.your_instance.tld`. But the server will
* be contacted using `your_instance.tld`.
* We must make sure that the Prosody module mod_s2s_peertubelivechat finds both
* kind of urls.
*
* @param options server optiosn
* @param liveChatInfos livechat stored data
*/
async function storeRemoteServerInfos (
options: RegisterServerOptions,
liveChatInfos: LiveChatJSONLDAttribute
liveChatInfos: LiveChatJSONLDAttributeV1
): Promise<void> {
if (!liveChatInfos) { return }
if (!liveChatInfos.xmppserver) { return }
const logger = options.peertubeHelpers.logger
const roomJID = liveChatInfos.jid
const host = roomJID.split('@')[1]
if (!host) {
logger.error(`Room JID seems not correct, no host: ${roomJID}`)
return
}
if (host.includes('..')) {
logger.error(`Room host seems not correct, contains ..: ${host}`)
return
}
const dir = path.resolve(
options.peertubeHelpers.plugin.getDataDirectoryPath(),
'serverInfos',
host
)
const s2sFilePath = path.resolve(dir, 's2s')
const wsS2SFilePath = path.resolve(dir, 'ws-s2s')
const mainHost = liveChatInfos.xmppserver.host
const hosts = [
liveChatInfos.xmppserver.host,
liveChatInfos.xmppserver.muc
]
const s2sLink = liveChatInfos.links.find(v => v.type === 'xmpp-s2s')
if (s2sLink) {
await _store(options, s2sFilePath, {
host: (s2sLink as LiveChatJSONLDS2SLink).host,
port: (s2sLink as LiveChatJSONLDS2SLink).port
})
} else {
await _del(options, s2sFilePath)
}
const wsS2SLink = liveChatInfos.links.find(v => v.type === 'xmpp-peertube-livechat-ws-s2s')
if (wsS2SLink) {
await _store(options, wsS2SFilePath, {
url: (wsS2SLink as LiveChatJSONLDPeertubeWSS2SLink).url
})
} else {
await _del(options, wsS2SFilePath)
for (const host of hosts) {
if (!host) { continue }
// Some security check, just in case.
if (host.includes('..')) {
logger.error(`Host seems not correct, contains ..: ${host}`)
continue
}
const dir = path.resolve(
options.peertubeHelpers.plugin.getDataDirectoryPath(),
'serverInfos',
host
)
const s2sFilePath = path.resolve(dir, 's2s')
const wsS2SFilePath = path.resolve(dir, 'ws-s2s')
if (liveChatInfos.xmppserver.directs2s?.port) {
await _store(options, s2sFilePath, {
host: mainHost,
port: liveChatInfos.xmppserver.directs2s.port
})
} else {
await _del(options, s2sFilePath)
}
if (liveChatInfos.xmppserver.websockets2s?.url) {
await _store(options, wsS2SFilePath, {
host: mainHost,
url: liveChatInfos.xmppserver.websockets2s.url
})
} else {
await _del(options, wsS2SFilePath)
}
}
}

View File

@ -4,42 +4,64 @@ interface VideoBuildResultContext {
video: MVideoAP
}
interface LiveChatJSONLDPeertubeWSS2SLink {
type: 'xmpp-peertube-livechat-ws-s2s'
url: string
}
interface LiveChatJSONLDS2SLink {
type: 'xmpp-s2s'
host: string
port: string
interface PeertubeXMPPServerInfos {
host: string // main host (should be the peertube url)
muc: string // muc component url
directs2s?: { // if direct S2S is enabled
port: string
}
websockets2s?: { // if Websocket S2S is enabled
url: string
}
anonymous?: { // provide an anonymous component that can be used externally
virtualhost: string
bosh?: string // BOSH endpoint url
websocket?: string // Websocket endpoint url
}
}
// DEPRECATED, but still used for backward compat
interface LiveChatJSONLDAnonymousWebsocketLink {
type: 'xmpp-websocket-anonymous'
url: string
jid: string
}
// DEPRECATED, but still used for backward compat
interface LiveChatJSONLDAnonymousBOSHLink {
type: 'xmpp-bosh-anonymous'
url: string
jid: string
}
type LiveChatJSONLDLink =
LiveChatJSONLDPeertubeWSS2SLink
| LiveChatJSONLDS2SLink
| LiveChatJSONLDAnonymousBOSHLink
| LiveChatJSONLDAnonymousWebsocketLink
// DEPRECATED, but still used for backward compat
type LiveChatJSONLDLink = LiveChatJSONLDAnonymousBOSHLink | LiveChatJSONLDAnonymousWebsocketLink
interface LiveChatJSONLDInfos {
// LiveChatJSONLDInfosV0 is the data format for the plugin v6.3.0. This format is replaced in newer versions.
// DEPRECATED, but still used for backward compat
interface LiveChatJSONLDInfosV0 {
type: 'xmpp'
jid: string
jid: string // room JID
links: LiveChatJSONLDLink[]
}
// LiveChatJSONLDInfosV1 is the data format that comes with plugin v6.4.0.
interface LiveChatJSONLDInfosV1 {
type: 'xmpp'
jid: string // room JID
xmppserver: PeertubeXMPPServerInfos
}
// LiveChatJSONLDInfosV1CompatV0 is a mix of both interface.
// Used for outgoing data, so that older plugin version can still use it.
interface LiveChatJSONLDInfosV1CompatV0 extends LiveChatJSONLDInfosV1 {
links: LiveChatJSONLDLink[]
}
type LiveChatJSONLDInfos = LiveChatJSONLDInfosV0 | LiveChatJSONLDInfosV1 | LiveChatJSONLDInfosV1CompatV0
type LiveChatJSONLDAttribute = LiveChatJSONLDInfos | false
type LiveChatJSONLDAttributeV1 = LiveChatJSONLDInfosV1 | false
interface LiveChatVideoObject extends VideoObject {
peertubeLiveChat: LiveChatJSONLDAttribute
@ -53,10 +75,11 @@ interface RemoteVideoHandlerParams {
export {
VideoBuildResultContext,
LiveChatJSONLDLink,
LiveChatJSONLDS2SLink,
LiveChatJSONLDPeertubeWSS2SLink,
LiveChatJSONLDInfos,
LiveChatJSONLDInfosV1CompatV0,
LiveChatJSONLDAttribute,
LiveChatJSONLDAttributeV1,
LiveChatVideoObject,
RemoteVideoHandlerParams
RemoteVideoHandlerParams,
PeertubeXMPPServerInfos
}