Skip to content

Commit

Permalink
add support for linked messages
Browse files Browse the repository at this point in the history
  • Loading branch information
ghuet committed Apr 16, 2024
1 parent c6ec1b0 commit 261e62b
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 83 deletions.
276 changes: 193 additions & 83 deletions src/handlers/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {
getUserMappingByName,
save,
} from '../helpers/storage'
import { axios, formatUserSessionOptions } from '../helpers/synapse'
import {
axios,
formatUserSessionOptions,
getDomainName,
} from '../helpers/synapse'
import reactionKeys from '../reactions.json'
import { executeAndHandleMissingMember } from './rooms'
import { AxiosError } from 'axios'
Expand All @@ -22,6 +26,24 @@ if (!applicationServiceToken) {
throw new Error(message)
}

export type Md = {
type: string
language?: string
value: {
type: string
value: string
}[]
}[]

export type Attachements = {
text: string
md?: Md
message_link?: string
author_name?: string
author_icon?: string
type?: string
}[]

/**
* Type of Rocket.Chat messages
*/
Expand Down Expand Up @@ -53,14 +75,8 @@ export type RcMessage = {
usernames: string[]
}
}
md?: {
type: string
language?: string
value: {
type: string
value: string
}[]
}[]
md?: Md
attachments?: Attachements
}

/**
Expand All @@ -81,8 +97,8 @@ export type MatrixMessage = {
'm.mentions'?: {
user_ids: string[]
}
'format'?: string
'formatted_body'?: string
format?: string
formatted_body?: string
}

/**
Expand Down Expand Up @@ -228,18 +244,119 @@ export async function handleReactions(
}
}

/**
* format message body to handle Links
* @param formatted_body, matrixMessage
*/
export async function formatLink(formatted_body: string): Promise<string[]> {
const regexMessageId: RegExp = /\[ \]\(https:\/[^?]+\?msg=([^)]+)\)/
let msgMatrixId: string = ''
let messageMatrixId: string = ''

const match: RegExpExecArray | null = regexMessageId.exec(formatted_body)
if (match && match[1]) {
const msgId: string = match[1]
log.warn(msgId)
const msgMatrixId = await getMessageId(msgId)
if (msgMatrixId) {
messageMatrixId = msgMatrixId
log.debug(`message matrix id ${messageMatrixId}`)
}
} else {
log.warn('No message ID found')
}
return [formatted_body, messageMatrixId]
}

/**
* get text and author from linked message and compute new formatted body
* @param formatted_body, attachments
*/
export async function getFromAttachement(
formatted_body: string,
attachements: Attachements,
matrixMessage: MatrixMessage
): Promise<string[]> {
const regexMessageId: RegExp = /\[ \]\(https:\/[^?]+\?msg=([^)]+)\)/
const regexUserName: RegExp = /\/avatar\/([^?]+)/
let authorMatrixId: string = ''
let message: string = ''
for (const item of attachements) {
if (item.message_link && item.author_icon && item.text) {
const match: RegExpExecArray | null = regexUserName.exec(item.author_icon)
if (match && match[1]) {
const username: string = match[1]
log.warn(username)
const userMapping = await getUserMappingByName(username)
if (!userMapping || !userMapping.matrixId) {
log.warn(`Could not find user mapping for name: ${username}`)
} else {
log.warn(userMapping.matrixId)
authorMatrixId = userMapping.matrixId
}
} else {
log.warn('No message ID found')
}
const textInside: string = item.text.replace(regexMessageId, '')
message = textInside
break
}
}
return [authorMatrixId, formatted_body, message]
}

/**
* format message body to handle code
* @param formatted_body, matrixMessage
*/
export function formatCode(
formatted_body: string,
matrixMessage: MatrixMessage
): string {
let concat: string = ''
const substrings = formatted_body.split('```')
for (let i = 0; i < substrings.length; i++) {
if (i !== 0 && i !== substrings.length - 1) {
if (substrings[i].startsWith('\n')) {
substrings[i] = substrings[i].substring(1)
}
if (substrings[i].endsWith('\n')) {
substrings[i] = substrings[i].substring(0, substrings[i].length - 1)
}
substrings[i] = '<pre><code>' + substrings[i] + '</code></pre>'
}
if (substrings.length === 2) {
substrings[1] = '<pre><code>' + substrings[1] + '</code></pre>'
}
concat = concat + substrings[i]
}
if (concat !== formatted_body) {
matrixMessage.formatted_body = concat
matrixMessage.format = `org.matrix.custom.html`
}
return concat
}

export function replaceFirstTripleBackticks(str: string, toreplace: string, replacement: string): string {
/**
* replace string by other string
* @param input string, string to replace, replacement string
*/
export function replaceString(
str: string,
toreplace: string,
replacement: string
): string {
// Find the index of the first occurrence of triple backticks
const index = str.indexOf(toreplace);
const index = str.indexOf(toreplace)
if (index === -1) {
return str; // Return the original string if no triple backticks found
return str // Return the original string if no triple backticks found
}

// Replace the first occurrence of triple backticks with the replacement string
const replacedString = str.slice(0, index) + replacement + str.slice(index + toreplace.length);
const replacedString =
str.slice(0, index) + replacement + str.slice(index + toreplace.length)

return replacedString;
return replacedString
}

/**
Expand Down Expand Up @@ -268,7 +385,6 @@ export async function handle(rcMessage: RcMessage): Promise<void> {
case 'ru': // User removed by
case 'ul': // User left
case 'ult': // User left team
/*
case 'removed-user-from-team': // Removed user from team
log.info(
`Message ${rcMessage._id} is of type ${rcMessage.t}, removing member ${rcMessage.msg} from room ${room_id}`
Expand Down Expand Up @@ -306,7 +422,6 @@ export async function handle(rcMessage: RcMessage): Promise<void> {
formatUserSessionOptions(userMapping.accessToken)
)
return
*/
case 'uj': // User joined channel
case 'ujt': // User joined team
case 'ut': // User joined conversation
Expand All @@ -315,16 +430,16 @@ export async function handle(rcMessage: RcMessage): Promise<void> {
case 'added-user-to-team': // Added user to team
case 'r': // Room name changed
case 'rm': // Message removed
//log.warn(
// `Message ${rcMessage._id} is of type ${rcMessage.t}, for which Rocket.Chat does not provide the initial state information, skipping.`
//)
log.warn(
`Message ${rcMessage._id} is of type ${rcMessage.t}, for which Rocket.Chat does not provide the initial state information, skipping.`
)
return

case 'user-muted': // User muted by
default:
//log.warn(
// `Message ${rcMessage._id} is of unhandled type ${rcMessage.t}, skipping.`
//)
log.warn(
`Message ${rcMessage._id} is of unhandled type ${rcMessage.t}, skipping.`
)
return
}
}
Expand All @@ -336,72 +451,67 @@ export async function handle(rcMessage: RcMessage): Promise<void> {
)
return
}
const matrixMessage = mapMessage(rcMessage)
const ts = new Date(rcMessage.ts.$date).valueOf()

if (rcMessage.tmid) {
const event_id = await getMessageId(rcMessage.tmid)
if (!event_id) {
log.warn(`Related message ${rcMessage.tmid} missing, skipping.`)
return
} else {
matrixMessage['m.relates_to'] = {
rel_type: 'm.thread',
event_id,
is_falling_back: true,
'm.in_reply_to': {
if (rcMessage.msg) {
const matrixMessage = mapMessage(rcMessage)

const ts = new Date(rcMessage.ts.$date).valueOf()

if (rcMessage.tmid) {
const event_id = await getMessageId(rcMessage.tmid)
if (!event_id) {
log.warn(`Related message ${rcMessage.tmid} missing, skipping.`)
return
} else {
matrixMessage['m.relates_to'] = {
rel_type: 'm.thread',
event_id,
},
}
}
}
if (rcMessage.md) {
for (const item of rcMessage.md) {
let formatted_body: string = matrixMessage.body;
if (item.type === 'CODE') {
formatted_body = replaceFirstTripleBackticks(formatted_body, '```\n','<pre><code>')
formatted_body = replaceFirstTripleBackticks(formatted_body, '\n```','</code></pre>\n')
log.warn(matrixMessage.body)
log.warn(formatted_body)
matrixMessage.formatted_body = formatted_body
matrixMessage.format = `org.matrix.custom.html`
} else if (item.type === 'PARAGRAPH') {
for (const val of item.value) {
if (val.type === 'LINK') {
log.warn(`inside link`)
const regex: RegExp = /\[ \]\(https:\/[^?]+\?msg=([^)]+)\)/;
const replacedStr: string = matrixMessage.body.replace(regex, "aa");
const match: RegExpExecArray | null = regex.exec(matrixMessage.body);
if (match && match[1]) {
const msgValue: string = match[1];
log.warn(msgValue); // Output: "rwq7eGNCD7b4LqKaR"
} else {
log.warn("No match found");
}
}
is_falling_back: true,
'm.in_reply_to': {
event_id,
},
}
}
}
/*
const event_id = await getMessageId(rcMessage.tmid)
if (!event_id) {
log.warn(`Related message ${rcMessage.tmid} missing, skipping.`)
return
} else {
matrixMessage['m.relates_to'] = {
rel_type: 'm.thread',
event_id,
is_falling_back: true,
'm.in_reply_to': {
event_id,
},
let domain: string = getDomainName()
let formatted_body: string = matrixMessage.body
formatted_body = formatCodeBis(formatted_body, matrixMessage)
if (rcMessage.attachments) {
for (const attach of rcMessage.attachments) {
if (!attach.type) {
const regexMessageId: RegExp = /\[ \]\(https:\/[^?]+\?msg=([^)]+)\)/
let messageMatrixId: string = ''
let authorMatrixId: string = ''
let message: string = ''
;[formatted_body, messageMatrixId] = await formatLink(formatted_body)
;[authorMatrixId, formatted_body, message] = await getFromAttachement(
formatted_body,
rcMessage.attachments,
matrixMessage
)
const new_body: string = `> <${authorMatrixId}> ${message}`
const form_body: string = `<mx-reply><blockquote><a href=\"https://matrix.to/#/${room_id}/${messageMatrixId}?via=${domain}\">In reply to</a> <a href=\"https://matrix.to/#/${authorMatrixId}\">${authorMatrixId}</a><br>${message}</blockquote></mx-reply>`
const replacedStr: string = matrixMessage.body.replace(
regexMessageId,
new_body
)
const replacedStrForm: string = formatted_body.replace(
regexMessageId,
form_body
)
matrixMessage.body = replacedStr
formatted_body = replacedStrForm
matrixMessage.formatted_body = formatted_body
matrixMessage.format = `org.matrix.custom.html`
}
}
}
*/
await executeAndHandleMissingMember(() =>
createEventsAndMapping(matrixMessage, room_id, user_id, ts, rcMessage)
)
} else {
log.warn('no msg')
return
}
await executeAndHandleMissingMember(() =>
createEventsAndMapping(matrixMessage, room_id, user_id, ts, rcMessage)
)
}

/**
Expand Down
20 changes: 20 additions & 0 deletions src/helpers/synapse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,23 @@ export async function getMatrixMembers(
).data.joined
)
}

/**
* Return domain name
* @param nothing
* @returns domain name
*/
export function getDomainName(): string {
let domain: string = "";
const regexDomain: RegExp = /https:\/\/([^/]+)/;
if (process.env.SYNAPSE_URL) {
const matchh: RegExpExecArray | null = regexDomain.exec(process.env.SYNAPSE_URL);
if (matchh && matchh[1]) {
domain = matchh[1];
log.warn(domain);
} else {
log.warn("No env SYNAPSE_URL provided");
}
}
return domain
}

0 comments on commit 261e62b

Please sign in to comment.