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

Merged
jochen merged 2 commits from claude/node-red-contrib-matrix:feature/matrix-file-attachments into master 2026-09-18 08:03:07 +00:00
Contributor

This is the follow-up requested during the audit: the old pinned matrix-js-sdk v5 (now upgraded in #1) 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 #1).

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).

(Previously staged as claude/node-red-contrib-matrix#2, stacked on the now-merged #1, while this repo's stale default-branch setting blocked opening it directly against the upstream. Re-opened here now that #1 is merged and that's fixed, so this diff is just the two new nodes.)

This is the follow-up requested during the audit: the old pinned matrix-js-sdk v5 (now upgraded in #1) 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 #1). **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). (Previously staged as claude/node-red-contrib-matrix#2, stacked on the now-merged #1, while this repo's stale default-branch setting blocked opening it directly against the upstream. Re-opened here now that #1 is merged and that's fixed, so this diff is just the two new nodes.)
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
Contributor

Version in package.json auf 0.1.1 angehoben (Attachment-Support), Commit 6d7230eb2c auf feature/matrix-file-attachments.

Version in package.json auf 0.1.1 angehoben (Attachment-Support), Commit 6d7230eb2c3a09d709b5895740d8fb680701b134 auf feature/matrix-file-attachments.
jochen approved these changes 2026-09-18 08:02:51 +00:00
Dismissed
jochen approved these changes 2026-09-18 08:03:03 +00:00
jochen merged commit 587b074dd6 into master 2026-09-18 08:03:07 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
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
jochen/node-red-contrib-matrix!2
No description provided.