2023-07-03 12:39:42 +00:00
// @ts-check
const reg = require ( "../../matrix/read-registration" )
const userRegex = reg . namespaces . users . map ( u => new RegExp ( u . regex ) )
2023-09-22 05:47:36 +00:00
const assert = require ( "assert" ) . strict
/** @type {import("xxhash-wasm").XXHashAPI} */ // @ts-ignore
let hasher = null
// @ts-ignore
require ( "xxhash-wasm" ) ( ) . then ( h => hasher = h )
2023-09-26 19:20:18 +00:00
const BLOCK _ELEMENTS = [
"ADDRESS" , "ARTICLE" , "ASIDE" , "AUDIO" , "BLOCKQUOTE" , "BODY" , "CANVAS" ,
"CENTER" , "DD" , "DETAILS" , "DIR" , "DIV" , "DL" , "DT" , "FIELDSET" , "FIGCAPTION" , "FIGURE" ,
"FOOTER" , "FORM" , "FRAMESET" , "H1" , "H2" , "H3" , "H4" , "H5" , "H6" , "HEADER" ,
"HGROUP" , "HR" , "HTML" , "ISINDEX" , "LI" , "MAIN" , "MENU" , "NAV" , "NOFRAMES" ,
"NOSCRIPT" , "OL" , "OUTPUT" , "P" , "PRE" , "SECTION" , "SUMMARY" , "TABLE" , "TBODY" , "TD" ,
"TFOOT" , "TH" , "THEAD" , "TR" , "UL"
]
2023-10-27 11:24:42 +00:00
const NEWLINE _ELEMENTS = BLOCK _ELEMENTS . concat ( [ "BR" ] )
2023-09-26 19:20:18 +00:00
2023-07-03 12:39:42 +00:00
/ * *
* Determine whether an event is the bridged representation of a discord message .
* Such messages shouldn ' t be bridged again .
* @ param { string } sender
* /
function eventSenderIsFromDiscord ( sender ) {
// If it's from a user in the bridge's namespace, then it originated from discord
// This includes messages sent by the appservice's bot user, because that is what's used for webhooks
// TODO: It would be nice if bridge system messages wouldn't trigger this check and could be bridged from matrix to discord, while webhook reflections would remain ignored...
2023-08-23 00:45:19 +00:00
// TODO that only applies to the above todo: But you'd have to watch out for the /icon command, where the bridge bot would set the room avatar, and that shouldn't be reflected into the room a second time.
2023-07-03 12:39:42 +00:00
if ( userRegex . some ( x => sender . match ( x ) ) ) {
return true
}
return false
}
2023-08-26 10:22:54 +00:00
/ * *
* @ param { string } mxc
* @ returns { string ? }
* /
function getPublicUrlForMxc ( mxc ) {
const avatarURLParts = mxc ? . match ( /^mxc:\/\/([^/]+)\/(\w+)$/ )
2023-09-12 08:43:56 +00:00
if ( avatarURLParts ) return ` ${ reg . ooye . server _origin } /_matrix/media/r0/download/ ${ avatarURLParts [ 1 ] } / ${ avatarURLParts [ 2 ] } `
2023-08-26 10:22:54 +00:00
else return null
}
2023-09-22 05:47:36 +00:00
/ * *
* Event IDs are really big and have more entropy than we need .
* If we want to store the event ID in the database , we can store a more compact version by hashing it with this .
2023-09-25 03:26:48 +00:00
* I choose a 64 - bit non - cryptographic hash as only a 32 - bit hash will see birthday collisions unreasonably frequently : https : //en.wikipedia.org/wiki/Birthday_attack#Mathematics
2023-09-22 05:47:36 +00:00
* xxhash outputs an unsigned 64 - bit integer .
* Converting to a signed 64 - bit integer with no bit loss so that it can be stored in an SQLite integer field as - is : https : //www.sqlite.org/fileformat2.html#record_format
* This should give very efficient storage with sufficient entropy .
* @ param { string } eventID
* /
function getEventIDHash ( eventID ) {
assert ( hasher , "xxhash is not ready yet" )
if ( eventID [ 0 ] === "$" && eventID . length >= 13 ) {
eventID = eventID . slice ( 1 ) // increase entropy per character to potentially help xxhash
}
const unsignedHash = hasher . h64 ( eventID )
const signedHash = unsignedHash - 0x8000000000000000 n // shifting down to signed 64-bit range
return signedHash
}
2023-10-27 11:24:42 +00:00
class MatrixStringBuilder {
constructor ( ) {
this . body = ""
this . formattedBody = ""
}
/ * *
* @ param { string } body
* @ param { string } formattedBody
* @ param { any } [ condition ]
* /
add ( body , formattedBody , condition = true ) {
if ( condition ) {
if ( formattedBody == undefined ) formattedBody = body
this . body += body
this . formattedBody += formattedBody
}
return this
}
/ * *
* @ param { string } body
* @ param { string } [ formattedBody ]
* @ param { any } [ condition ]
* /
addLine ( body , formattedBody , condition = true ) {
if ( condition ) {
if ( formattedBody == undefined ) formattedBody = body
if ( this . body . length && this . body . slice ( - 1 ) !== "\n" ) this . body += "\n"
this . body += body
const match = this . formattedBody . match ( /<\/?([a-zA-Z]+[a-zA-Z0-9]*)[^>]*>\s*$/ )
if ( this . formattedBody . length && ( ! match || ! NEWLINE _ELEMENTS . includes ( match [ 1 ] . toUpperCase ( ) ) ) ) this . formattedBody += "<br>"
this . formattedBody += formattedBody
}
return this
}
/ * *
* @ param { string } body
* @ param { string } [ formattedBody ]
* @ param { any } [ condition ]
* /
addParagraph ( body , formattedBody , condition = true ) {
if ( condition ) {
if ( formattedBody == undefined ) formattedBody = body
if ( this . body . length && this . body . slice ( - 1 ) !== "\n" ) this . body += "\n\n"
this . body += body
formattedBody = ` <p> ${ formattedBody } </p> `
this . formattedBody += formattedBody
}
return this
}
get ( ) {
return {
msgtype : "m.text" ,
body : this . body ,
format : "org.matrix.custom.html" ,
formatted _body : this . formattedBody
}
}
}
2023-09-26 19:20:18 +00:00
module . exports . BLOCK _ELEMENTS = BLOCK _ELEMENTS
2023-07-03 12:39:42 +00:00
module . exports . eventSenderIsFromDiscord = eventSenderIsFromDiscord
2023-08-26 10:22:54 +00:00
module . exports . getPublicUrlForMxc = getPublicUrlForMxc
2023-09-22 05:47:36 +00:00
module . exports . getEventIDHash = getEventIDHash
2023-10-27 11:24:42 +00:00
module . exports . MatrixStringBuilder = MatrixStringBuilder