Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added inputs/files/.gitkeep
Empty file.
1 change: 1 addition & 0 deletions reset.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ set -u
echo 'Resetting containers and databases'
docker compose down
sudo rm -f files/homeserver.db
sudo rm -rf files/media_store/local_{content,thumbnails}
rm -f db.sqlite
docker compose up -d

Expand Down
1 change: 1 addition & 0 deletions src/handlers/messages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const rcMessage: RcMessage = {
ts: {
$date: '1970-01-02T06:51:51.0Z', // UNIX-TS: 111111000
},
type: 'm.text',
}

const matrixMessage: MatrixMessage = {
Expand Down
124 changes: 122 additions & 2 deletions src/handlers/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
getMessageId,
getRoomId,
getUserId,
getAccessToken,
getUserMappingByName,
save,
} from '../helpers/storage'
Expand All @@ -18,6 +19,7 @@ import {
} from '../helpers/synapse'
import emojiMap from '../emojis.json'
import { executeAndHandleMissingMember } from './rooms'
import fs from 'fs/promises'

const applicationServiceToken = process.env.AS_TOKEN || ''
if (!applicationServiceToken) {
Expand All @@ -26,6 +28,16 @@ if (!applicationServiceToken) {
throw new Error(message)
}

type attachment = {
type?: string
description?: string
message_link?: string
image_url?: string
image_type?: string
title: string
title_link?: string
}

/**
* Type of Rocket.Chat messages
*/
Expand All @@ -34,6 +46,14 @@ export type RcMessage = {
t?: string // Event type
rid: string // The unique id for the room
msg: string // The content of the message.
attachments?: attachment[]
file?: {
_id: string
name: string
type: string
url: string
}
type: string
tmid?: string
ts: {
$date: string
Expand Down Expand Up @@ -81,6 +101,7 @@ export type MatrixMessage = {
event_id: string
}
}
url?: string
}

/**
Expand Down Expand Up @@ -132,7 +153,7 @@ export async function mapTextMessage(
const htmled = converter.makeHtml(emojified)
const matrixMessage: MatrixMessage = {
type: 'm.room.message',
msgtype: 'm.text',
msgtype: rcMessage.type,
body: emojified,
}
if (mentions && (mentions.room || mentions.user_ids)) {
Expand Down Expand Up @@ -204,6 +225,49 @@ export async function createMessage(
).data.event_id
}

/**
* Send a File to Synapse
* @param user_id The user the media will be posted by
* @param ts The timestamp to which the file will be dated
* @param filePath the path on the local filesystem
* @param fileName the filename
* @param content_type: Content type of the file
* @returns The Matrix Message/event ID
*/
export async function uploadFile(
user_id: string,
ts: number,
filePath: string,
fileName: string,
content_type: string
): Promise<string> {
const accessToken = await getAccessToken(user_id)
log.http(`Uploading ${fileName}...`)

let fd: fs.FileHandle | undefined
try {
fd = await fs.open(filePath)
} catch (err) {
Comment thread
HerHde marked this conversation as resolved.
Outdated
log.warn(`Unable to open ${filePath}:`, err)
throw err
}
const fileStream = fd.createReadStream()

return (
await axios.post(
`/_matrix/media/v3/upload?user_id=${user_id}&ts=${ts}&filename=${fileName}`,
fileStream,
{
headers: {
'Content-Type': content_type,
'Content-Length': (await fd.stat()).size,
Authorization: `Bearer ${accessToken}`,
},
}
)
).data.content_uri
}

/**
* Add reactions to the event
* @param reactions A Rocket.Chat reactions object
Expand Down Expand Up @@ -324,6 +388,60 @@ export async function handle(rcMessage: RcMessage): Promise<void> {
return
}

const ts = new Date(rcMessage.ts.$date).valueOf()
if (rcMessage.file) {
if (rcMessage.attachments?.length == 1) {
Comment thread
HerHde marked this conversation as resolved.
const path = './inputs/files/' + rcMessage.file._id
Comment thread
flying-scorpio marked this conversation as resolved.
let mxcurl: string
try {
mxcurl = await uploadFile(
rcMessage.u._id,
ts,
path,
rcMessage.file.name,
rcMessage.file.type
)
} catch (err) {
log.warn(`Error uploading file ${path}, skipping Upload.`)
return
}
if (rcMessage.attachments[0].description) {
// send the description as a separate text message
const saved_id = rcMessage._id
rcMessage._id = rcMessage.file._id
rcMessage.msg = rcMessage.attachments[0].description
rcMessage.type = 'm.text'
await handleMessage(rcMessage, room_id, ts)
rcMessage._id = saved_id
}
rcMessage.msg = rcMessage.file.name
rcMessage.file.url = mxcurl
if (rcMessage.attachments[0].image_type) {
rcMessage.type = 'm.image'
} else {
rcMessage.type = 'm.file'
}
} else {
log.warn(
`Many attachments in ${rcMessage.u._id} not handled, skipping Upload.`
)
return
}
} else if (rcMessage.attachments && rcMessage.attachments.length > 0) {
log.warn(`Attachment in ${rcMessage.u._id} not handled, skipping.`)
return
} else {
rcMessage.type = 'm.text'
}

await handleMessage(rcMessage, room_id, ts)
}

async function handleMessage(
rcMessage: RcMessage,
room_id: string,
ts: number
) {
const user_id = await getUserId(rcMessage.u._id)
if (!user_id) {
log.warn(
Expand All @@ -332,7 +450,9 @@ export async function handle(rcMessage: RcMessage): Promise<void> {
return
}
const matrixMessage = await mapMessage(rcMessage)
const ts = new Date(rcMessage.ts.$date).valueOf()
if (rcMessage.file) {
matrixMessage.url = rcMessage.file.url
}

if (rcMessage.tmid) {
const event_id = await getMessageId(rcMessage.tmid)
Expand Down