Add matrix-sendfile and matrix-recvfile nodes for JPEG/attachment support #2

Closed
claude wants to merge 1 commit from feature/matrix-file-attachments into modernize/matrix-js-sdk-42-upgrade
Owner

Stacked on #1 (matrix-js-sdk v42 upgrade) - this needs that base branch's dynamic-import/async-client-init pattern and the v42 SDK's uploadContent/sendImageMessage/mxcUrlToHttp APIs, so it's built on top rather than against master.

This is the follow-up requested during the audit: the old pinned matrix-js-sdk v5 was blamed for the plugin never supporting attachments (JPEGs etc). Checked the current SDK's types and even v5's own JSDoc - uploadContent/sendImageMessage have supported Node.js (Buffer/String/ReadStream) for a long time, so it looks like this was never really an SDK limitation, just a feature the plugin never implemented. Adding it now:

matrix-sendfile (function node, 1 input): takes msg.payload (a Buffer), uploads it via client.uploadContent(), then sends it as m.image (via sendImageMessage) when msg.mimetype starts with image/, or as a generic m.file event otherwise. msg.filename/msg.mimetype are optional (default to "file" / "application/octet-stream"). Room selection mirrors matrix-sendtext: msg.roomId > node's room config > server's default room.

matrix-recvfile (function node, 1 output): listens for m.room.message events whose msgtype is m.image/m.file/m.video/m.audio, resolves the mxc:// URL via mxcUrlToHttp(..., useAuthentication: true) and downloads it with a Bearer Authorization header (current Synapse/Dendrite require authenticated media per MSC3916), emitting msg.payload (Buffer), msg.filename, msg.mimetype, msg.msgtype, msg.sender, msg.roomId. filterself and room filtering mirror matrix-recvtext. An encrypted attachment (content carries file instead of url) is skipped with a warning, since this module doesn't enable end-to-end encryption (same stated boundary as the SDK-upgrade PR).

Testing done: wrote a small local fake HTTP homeserver (plain Node http) that implements just enough surface - a media upload endpoint, a room-send endpoint, and an authenticated-media-download endpoint that actually rejects requests missing the bearer token - and drove the real, npm-installed matrix-js-sdk@42.4.0 against it directly (upload → content_uri → sendImageMessage/sendEvent → event_id, and mxcUrlToHttp → authenticated fetch → correct bytes back, plus confirming an unauthenticated download is rejected). Then ran a second, full-integration test that require()s this actual matrix.js against a stub Node-RED runtime, wires a real matrix-server config node to that fake server, and drives matrix-sendfile (image path, generic-file path, and a non-Buffer-payload rejection case) and matrix-recvfile (successful download producing the right msg fields, filterself correctly dropping our own message, and a plain m.text message correctly being ignored) end-to-end through the real node code. All assertions passed.

Not tested: against a real Matrix homeserver or inside an actual running Node-RED instance - please verify there before merging, especially the MSC3916 authenticated-media assumption against your actual homeserver version, and larger file uploads (uploadContent's progressHandler/abortController options exist but aren't wired up here - happy to add if useful, e.g. for large video files).

Stacked on #1 (matrix-js-sdk v42 upgrade) - this needs that base branch's dynamic-import/async-client-init pattern and the v42 SDK's `uploadContent`/`sendImageMessage`/`mxcUrlToHttp` APIs, so it's built on top rather than against `master`. This is the follow-up requested during the audit: the old pinned matrix-js-sdk v5 was blamed for the plugin never supporting attachments (JPEGs etc). Checked the current SDK's types and even v5's own JSDoc - `uploadContent`/`sendImageMessage` have supported Node.js (Buffer/String/ReadStream) for a long time, so it looks like this was never really an SDK limitation, just a feature the plugin never implemented. Adding it now: **matrix-sendfile** (function node, 1 input): takes `msg.payload` (a Buffer), uploads it via `client.uploadContent()`, then sends it as `m.image` (via `sendImageMessage`) when `msg.mimetype` starts with `image/`, or as a generic `m.file` event otherwise. `msg.filename`/`msg.mimetype` are optional (default to `"file"` / `"application/octet-stream"`). Room selection mirrors matrix-sendtext: `msg.roomId` > node's room config > server's default room. **matrix-recvfile** (function node, 1 output): listens for `m.room.message` events whose `msgtype` is `m.image`/`m.file`/`m.video`/`m.audio`, resolves the `mxc://` URL via `mxcUrlToHttp(..., useAuthentication: true)` and downloads it with a Bearer `Authorization` header (current Synapse/Dendrite require authenticated media per MSC3916), emitting `msg.payload` (Buffer), `msg.filename`, `msg.mimetype`, `msg.msgtype`, `msg.sender`, `msg.roomId`. `filterself` and room filtering mirror matrix-recvtext. An encrypted attachment (content carries `file` instead of `url`) is skipped with a warning, since this module doesn't enable end-to-end encryption (same stated boundary as the SDK-upgrade PR). **Testing done:** wrote a small local fake HTTP homeserver (plain Node `http`) that implements just enough surface - a media upload endpoint, a room-send endpoint, and an authenticated-media-download endpoint that actually rejects requests missing the bearer token - and drove the real, npm-installed `matrix-js-sdk@42.4.0` against it directly (upload → content_uri → sendImageMessage/sendEvent → event_id, and mxcUrlToHttp → authenticated fetch → correct bytes back, plus confirming an unauthenticated download is rejected). Then ran a second, full-integration test that `require()`s this actual `matrix.js` against a stub Node-RED runtime, wires a real `matrix-server` config node to that fake server, and drives `matrix-sendfile` (image path, generic-file path, and a non-Buffer-payload rejection case) and `matrix-recvfile` (successful download producing the right msg fields, `filterself` correctly dropping our own message, and a plain `m.text` message correctly being ignored) end-to-end through the real node code. All assertions passed. **Not tested:** against a real Matrix homeserver or inside an actual running Node-RED instance - please verify there before merging, especially the MSC3916 authenticated-media assumption against your actual homeserver version, and larger file uploads (uploadContent's `progressHandler`/`abortController` options exist but aren't wired up here - happy to add if useful, e.g. for large video files).
Adds attachment support that never existed in this module (see PR
against modernize/matrix-js-sdk-42-upgrade for the SDK bump this is
built on):

- matrix-sendfile: takes msg.payload (a Buffer), uploads it via
  client.uploadContent(), then sends it as m.image (via
  sendImageMessage) when msg.mimetype starts with "image/", or as a
  generic m.file event otherwise. msg.filename/msg.mimetype are
  optional (default to "file" / "application/octet-stream"). Room
  selection mirrors matrix-sendtext (msg.roomId > node config > server
  config default).

- matrix-recvfile: listens for m.room.message events whose msgtype is
  m.image/m.file/m.video/m.audio, resolves the mxc:// URL via
  mxcUrlToHttp(..., useAuthentication: true) and downloads it with an
  Authorization header, emitting msg.payload (Buffer), msg.filename,
  msg.mimetype, msg.msgtype, msg.sender and msg.roomId. filterself and
  room filtering mirror matrix-recvtext. Encrypted attachments (content
  carries "file" instead of "url") are skipped with a warning, since
  this module doesn't enable end-to-end encryption.

Testing: matrix-js-sdk's own attachment methods (uploadContent,
sendImageMessage, sendEvent for m.file, mxcUrlToHttp) were exercised
against a small local fake homeserver (upload endpoint, room-send
endpoint, and an authenticated media-download endpoint that rejects
requests missing the bearer token) to confirm the request/response
shapes are used correctly end-to-end. Also ran an integration test
that requires this actual matrix.js against a stub Node-RED runtime,
wires a matrix-server config node to that fake server, and drives
matrix-sendfile (image + generic file + a non-Buffer-payload rejection
case) and matrix-recvfile (successful download, filterself, ignoring
non-attachment m.text messages) through it - all as expected. Not
tested against a real Matrix homeserver or inside a real Node-RED
instance.

Note on MSC3916 (authenticated media): current Synapse/Dendrite
require the Authorization header for media downloads; the fake-server
test specifically checks that an unauthenticated request is rejected
and an authenticated one succeeds, so this isn't just asserted, it's
exercised.
Author
Owner

Update: the repo's stale default-branch setting (pointed at a non-existent "main" ref) has been fixed, and the base PR is now correctly open directly against the upstream repo as jochen/node-red-contrib-matrix#1.

This PR (attachment support) stays open here against modernize/matrix-js-sdk-42-upgrade for now, since that branch only exists in this fork and cross-repo PR bases must exist in the target repo. Once jochen/node-red-contrib-matrix#1 is merged, I'll open a fresh PR of feature/matrix-file-attachments directly against jochen/node-red-contrib-matrix master, which will then have a clean, minimal diff (just the two new nodes, not the SDK upgrade too).

Update: the repo's stale default-branch setting (pointed at a non-existent "main" ref) has been fixed, and the base PR is now correctly open directly against the upstream repo as jochen/node-red-contrib-matrix#1. This PR (attachment support) stays open here against `modernize/matrix-js-sdk-42-upgrade` for now, since that branch only exists in this fork and cross-repo PR bases must exist in the target repo. Once jochen/node-red-contrib-matrix#1 is merged, I'll open a fresh PR of `feature/matrix-file-attachments` directly against jochen/node-red-contrib-matrix master, which will then have a clean, minimal diff (just the two new nodes, not the SDK upgrade too).
Author
Owner

Superseded by jochen/node-red-contrib-matrix#2, opened directly against master now that #1 is merged and the default-branch bug is fixed - clean diff there (just matrix.js + matrix.html, no SDK-upgrade changes mixed in). Closing this fork-internal duplicate.

Superseded by jochen/node-red-contrib-matrix#2, opened directly against master now that #1 is merged and the default-branch bug is fixed - clean diff there (just matrix.js + matrix.html, no SDK-upgrade changes mixed in). Closing this fork-internal duplicate.
claude closed this pull request 2026-09-16 20:20:23 +00:00

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
claude/node-red-contrib-matrix!2
No description provided.