Fix module compatibility with Node.js < 22

This commit is contained in:
Skylar Sadlier 2025-02-09 22:36:52 -07:00
parent ad34f018ab
commit 8cb52112c1
7 changed files with 155 additions and 127 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "node-red-contrib-matrix-chat", "name": "node-red-contrib-matrix-chat",
"version": "0.9.0", "version": "0.9.1",
"description": "Matrix chat server client for Node-RED", "description": "Matrix chat server client for Node-RED",
"dependencies": { "dependencies": {
"abort-controller": "^3.0.0", "abort-controller": "^3.0.0",

View File

@ -1,4 +1,4 @@
const {RelationType, EventType, Direction} = require("matrix-js-sdk"); const sdkPromise = import("matrix-js-sdk");
module.exports = function(RED) { module.exports = function(RED) {
function MatrixFetchRelations(n) { function MatrixFetchRelations(n) {
@ -49,14 +49,17 @@ module.exports = function(RED) {
} }
try { try {
const sdk = await sdkPromise;
const Direction = sdk.Direction;
function evaluateNodePropertySafe(value, type, node, msg) { function evaluateNodePropertySafe(value, type, node, msg) {
try { try {
return RED.util.evaluateNodeProperty(value, type, node, msg); return RED.util.evaluateNodeProperty(value, type, node, msg);
} catch (e) { } catch (e) {
if (e instanceof TypeError) { if (e instanceof TypeError) {
return undefined; // Handle TypeError and return undefined return undefined;
} }
throw e; // Re-throw other errors to prevent masking issues throw e;
} }
} }
@ -71,16 +74,16 @@ module.exports = function(RED) {
to = evaluateNodePropertySafe(node.toValue, node.toType, node, msg); to = evaluateNodePropertySafe(node.toValue, node.toType, node, msg);
let opts = { dir: direction }; let opts = { dir: direction };
if(limit) { if (limit) {
opts.limit = limit; opts.limit = limit;
} }
if(recurse === true || recurse === false) { if (recurse === true || recurse === false) {
opts.recurse = recurse; opts.recurse = recurse;
} }
if(from) { if (from) {
opts.from = from; opts.from = from;
} }
if(to) { if (to) {
opts.to = to; opts.to = to;
} }

View File

@ -1,9 +1,8 @@
const {TimelineWindow, RelationType, Filter} = require("matrix-js-sdk");
const crypto = require('crypto'); const crypto = require('crypto');
module.exports = function(RED) {
function MatrixReceiveMessage(n) {
RED.nodes.createNode(this, n);
module.exports = function(RED) {
function MatrixMarkRead(n) {
RED.nodes.createNode(this, n);
let node = this; let node = this;
this.name = n.name; this.name = n.name;
this.server = RED.nodes.getNode(n.server); this.server = RED.nodes.getNode(n.server);
@ -29,7 +28,7 @@ module.exports = function(RED) {
}); });
node.on("input", async function (msg) { node.on("input", async function (msg) {
if (! node.server || ! node.server.matrixClient) { if (!node.server || !node.server.matrixClient) {
node.error("No matrix server selected", msg); node.error("No matrix server selected", msg);
return; return;
} }
@ -38,15 +37,15 @@ module.exports = function(RED) {
let value = property; let value = property;
if (type === "msg") { if (type === "msg") {
value = RED.util.getMessageProperty(msg, property); value = RED.util.getMessageProperty(msg, property);
} else if ((type === 'flow') || (type === 'global')) { } else if (type === 'flow' || type === 'global') {
try { try {
value = RED.util.evaluateNodeProperty(property, type, node, msg); value = RED.util.evaluateNodeProperty(property, type, node, msg);
} catch(e2) { } catch (e2) {
throw new Error("Invalid value evaluation"); throw new Error("Invalid value evaluation");
} }
} else if(type === "bool") { } else if (type === "bool") {
value = (property === 'true'); value = (property === 'true');
} else if(type === "num") { } else if (type === "num") {
value = Number(property); value = Number(property);
} }
return value; return value;
@ -66,9 +65,9 @@ module.exports = function(RED) {
throw new Error(`Event ${eventId} not found in room ${roomId}.`); throw new Error(`Event ${eventId} not found in room ${roomId}.`);
} }
await node.server.matrixClient.sendReceipt(event, "m.read") await node.server.matrixClient.sendReceipt(event, "m.read");
node.send([msg, null]); node.send([msg, null]);
} catch(e) { } catch (e) {
msg.error = `Room pagination error: ${e}`; msg.error = `Room pagination error: ${e}`;
node.error(msg.error, msg); node.error(msg.error, msg);
node.send([null, msg]); node.send([null, msg]);
@ -79,5 +78,5 @@ module.exports = function(RED) {
node.server.deregister(node); node.server.deregister(node);
}); });
} }
RED.nodes.registerType("matrix-mark-read", MatrixReceiveMessage); RED.nodes.registerType("matrix-mark-read", MatrixMarkRead);
} }

View File

@ -1,7 +1,8 @@
const {TimelineWindow, RelationType, Filter} = require("matrix-js-sdk"); const sdkPromise = import("matrix-js-sdk");
const crypto = require('crypto'); const crypto = require('crypto');
module.exports = function(RED) { module.exports = function(RED) {
function MatrixReceiveMessage(n) { function MatrixPaginateRoom(n) {
RED.nodes.createNode(this, n); RED.nodes.createNode(this, n);
let node = this; let node = this;
@ -34,7 +35,7 @@ module.exports = function(RED) {
}); });
node.on("input", async function (msg) { node.on("input", async function (msg) {
if (! node.server || ! node.server.matrixClient) { if (!node.server || !node.server.matrixClient) {
node.error("No matrix server selected", msg); node.error("No matrix server selected", msg);
return; return;
} }
@ -43,43 +44,47 @@ module.exports = function(RED) {
let value = property; let value = property;
if (type === "msg") { if (type === "msg") {
value = RED.util.getMessageProperty(msg, property); value = RED.util.getMessageProperty(msg, property);
} else if ((type === 'flow') || (type === 'global')) { } else if (type === 'flow' || type === 'global') {
try { try {
value = RED.util.evaluateNodeProperty(property, type, node, msg); value = RED.util.evaluateNodeProperty(property, type, node, msg);
} catch(e2) { } catch(e2) {
throw new Error("Invalid value evaluation"); throw new Error("Invalid value evaluation");
} }
} else if(type === "bool") { } else if (type === "bool") {
value = (property === 'true'); value = (property === 'true');
} else if(type === "num") { } else if (type === "num") {
value = Number(property); value = Number(property);
} }
return value; return value;
} }
function setToValue(value, type, property) { function setToValue(value, type, property) {
if(type === 'global' || type === 'flow') { if (type === 'global' || type === 'flow') {
var contextKey = RED.util.parseContextStore(property); var contextKey = RED.util.parseContextStore(property);
if (/\[msg/.test(contextKey.key)) { if (/\[msg/.test(contextKey.key)) {
// The key has a nest msg. reference to evaluate first // The key has a nested msg. reference that must be evaluated first
contextKey.key = RED.util.normalisePropertyExpression(contextKey.key, msg, true) contextKey.key = RED.util.normalisePropertyExpression(contextKey.key, msg, true);
} }
var target = node.context()[type]; var target = node.context()[type];
var callback = err => { target.set(contextKey.key, value, contextKey.store, err => {
if (err) { if (err) {
node.error(err, msg); node.error(err, msg);
getterErrors[rule.p] = err.message;
} }
} });
target.set(contextKey.key, value, contextKey.store, callback); } else if (type === 'msg') {
} else if(type === 'msg') {
if (!RED.util.setMessageProperty(msg, property, value)) { if (!RED.util.setMessageProperty(msg, property, value)) {
node.warn(RED._("change.errors.no-override",{property:property})); node.warn(RED._("change.errors.no-override", { property: property }));
} }
} }
} }
try { try {
// Dynamically load the SDK
const sdk = await sdkPromise;
const TimelineWindow = sdk.TimelineWindow;
const RelationType = sdk.RelationType;
// (Filter was imported originally but is not used, so we omit it.)
let roomId = getToValue(msg, node.roomType, node.roomValue), let roomId = getToValue(msg, node.roomType, node.roomValue),
paginateBackwards = getToValue(msg, node.paginateBackwardsType, node.paginateBackwardsValue), paginateBackwards = getToValue(msg, node.paginateBackwardsType, node.paginateBackwardsValue),
pageSize = getToValue(msg, node.pageSizeType, node.pageSizeValue), pageSize = getToValue(msg, node.pageSizeType, node.pageSizeValue),
@ -87,42 +92,37 @@ module.exports = function(RED) {
let room = node.server.matrixClient.getRoom(roomId); let room = node.server.matrixClient.getRoom(roomId);
if(!room) { if (!room) {
throw new Error(`Room ${roomId} does not exist`); throw new Error(`Room ${roomId} does not exist`);
} }
if(pageSize > node.server.initialSyncLimit) { if (pageSize > node.server.initialSyncLimit) {
throw new Error(`Page size=${pageSize} cannot exceed initialSyncLimit=${node.server.initialSyncLimit}`); throw new Error(`Page size=${pageSize} cannot exceed initialSyncLimit=${node.server.initialSyncLimit}`);
} }
if(!pageKey) { if (!pageKey) {
pageKey = crypto.randomUUID(); pageKey = crypto.randomUUID();
setToValue(pageKey, node.paginateKeyType, node.paginateKeyValue); setToValue(pageKey, node.paginateKeyType, node.paginateKeyValue);
} }
let timelineWindow = node.timelineWindows.get(pageKey), let timelineWindow = node.timelineWindows.get(pageKey),
moreMessages = true; moreMessages = true;
if(!timelineWindow) { if (!timelineWindow) {
let timelineSet = room.getUnfilteredTimelineSet(); let timelineSet = room.getUnfilteredTimelineSet();
// node.debug(JSON.stringify(timelineSet.getFilter())); // MatrixClient's option initialSyncLimit gets set to the filter we are using,
// so override that value with our pageSize.
// MatrixClient's option initialSyncLimit gets set to the filter we are using
// so override that value with our pageSize
timelineWindow = new TimelineWindow(node.server.matrixClient, timelineSet); timelineWindow = new TimelineWindow(node.server.matrixClient, timelineSet);
await timelineWindow.load(msg.eventId || null, pageSize); await timelineWindow.load(msg.eventId || null, pageSize);
node.timelineWindows.set(pageKey, timelineWindow); node.timelineWindows.set(pageKey, timelineWindow);
} else { } else {
moreMessages = await timelineWindow.paginate(paginateBackwards ? 'b' : 'f', pageSize); // b for backwards f for forwards moreMessages = await timelineWindow.paginate(paginateBackwards ? 'b' : 'f', pageSize); // 'b' for backwards, 'f' for forwards
if(moreMessages) { if (moreMessages) {
await timelineWindow.unpaginate(pageSize, !paginateBackwards); await timelineWindow.unpaginate(pageSize, !paginateBackwards);
} }
} }
// MatrixEvent objects are massive so this throws an encode error for the string being too long // To avoid errors converting massive MatrixEvent objects to JSON, we omit them.
// since msg objects convert to JSON
// msg.payload = moreMessages ? timelineWindow.getEvents() : false;
msg.payload = false; msg.payload = false;
msg.start = timelineWindow.getTimelineIndex('b')?.index; msg.start = timelineWindow.getTimelineIndex('b')?.index;
msg.end = timelineWindow.getTimelineIndex('f')?.index; msg.end = timelineWindow.getTimelineIndex('f')?.index;
if(moreMessages) { if (moreMessages) {
msg.payload = timelineWindow.getEvents().map(function(event) { msg.payload = timelineWindow.getEvents().map(function(event) {
return { return {
encrypted : event.isEncrypted(), encrypted : event.isEncrypted(),
@ -152,5 +152,5 @@ module.exports = function(RED) {
node.server.deregister(node); node.server.deregister(node);
}); });
} }
RED.nodes.registerType("matrix-paginate-room", MatrixReceiveMessage); RED.nodes.registerType("matrix-paginate-room", MatrixPaginateRoom);
} }

View File

@ -1,4 +1,3 @@
const {RelationType} = require("matrix-js-sdk");
module.exports = function(RED) { module.exports = function(RED) {
function MatrixReceiveMessage(n) { function MatrixReceiveMessage(n) {
RED.nodes.createNode(this, n); RED.nodes.createNode(this, n);

View File

@ -1,9 +1,8 @@
const {RelationType} = require("matrix-js-sdk"); const sdkPromise = import("matrix-js-sdk");
module.exports = function(RED) { module.exports = function(RED) {
function MatrixSendImage(n) { function MatrixSendImage(n) {
RED.nodes.createNode(this, n); RED.nodes.createNode(this, n);
var node = this; var node = this;
this.name = n.name; this.name = n.name;
@ -68,12 +67,17 @@ module.exports = function(RED) {
node.status({ fill: "green", shape: "ring", text: "connected" }); node.status({ fill: "green", shape: "ring", text: "connected" });
}); });
node.on("input", function (msg) { // Make the input handler async so we can await the dynamic import.
node.on("input", async function (msg) {
// Await the SDK import and get the RelationType constant.
const sdk = await sdkPromise;
const RelationType = sdk.RelationType;
function getToValue(msg, type, property) { function getToValue(msg, type, property) {
let value = property; let value = property;
if (type === "msg") { if (type === "msg") {
value = RED.util.getMessageProperty(msg, property); value = RED.util.getMessageProperty(msg, property);
} else if ((type === 'flow') || (type === 'global')) { } else if (type === 'flow' || type === 'global') {
try { try {
value = RED.util.evaluateNodeProperty(property, type, node, msg); value = RED.util.evaluateNodeProperty(property, type, node, msg);
} catch(e2) { } catch(e2) {
@ -115,19 +119,19 @@ module.exports = function(RED) {
} }
let content = null; let content = null;
if(typeof payload === 'object') { if (typeof payload === 'object') {
content = payload; content = payload;
} else { } else {
if(msgType === 'msg.type') { if (msgType === 'msg.type') {
if(!msg.type) { if (!msg.type) {
node.error("msg.type type is set to be passed in via msg.type but was not defined", msg); node.error("msg.type is set to be passed in via msg.type but was not defined", msg);
return; return;
} }
msgType = msg.type; msgType = msg.type;
} }
if(msgFormat === 'msg.format') { if (msgFormat === 'msg.format') {
if(!Object.hasOwn(msg, 'format')) { if (!Object.hasOwn(msg, 'format')) {
node.error("Message format is set to be passed in via msg.format but was not defined", msg); node.error("Message format is set to be passed in via msg.format but was not defined", msg);
return; return;
} }
@ -139,7 +143,7 @@ module.exports = function(RED) {
body: payload.toString() body: payload.toString()
}; };
if(msgFormat === 'html') { if (msgFormat === 'html') {
content.format = "org.matrix.custom.html"; content.format = "org.matrix.custom.html";
content.formatted_body = content.formatted_body =
(typeof msg.formatted_payload !== 'undefined' && msg.formatted_payload) (typeof msg.formatted_payload !== 'undefined' && msg.formatted_payload)
@ -147,15 +151,15 @@ module.exports = function(RED) {
: payload.toString(); : payload.toString();
} }
if((node.replaceMessage || msg.replace) && msg.eventId) { if ((node.replaceMessage || msg.replace) && msg.eventId) {
content['m.new_content'] = { content['m.new_content'] = {
msgtype: content.msgtype, msgtype: content.msgtype,
body: content.body body: content.body
}; };
if('format' in content) { if ('format' in content) {
content['m.new_content']['format'] = content['format']; content['m.new_content']['format'] = content['format'];
} }
if('formatted_body' in content) { if ('formatted_body' in content) {
content['m.new_content']['formatted_body'] = content['formatted_body']; content['m.new_content']['formatted_body'] = content['formatted_body'];
} }
@ -164,17 +168,19 @@ module.exports = function(RED) {
event_id: msg.eventId event_id: msg.eventId
}; };
content['body'] = ' * ' + content['body']; content['body'] = ' * ' + content['body'];
} else if(threadReply) { } else if (threadReply) {
// if incoming message is a reply to a thread we fetch the thread parent from the m.relates_to property // If incoming message is a reply to a thread we fetch the thread parent from m.relates_to,
// otherwise fallback to msg.eventId // otherwise fallback to msg.eventId.
let threadParent = (msg?.content?.['m.relates_to']?.rel_type === RelationType.Thread ? msg?.content?.['m.relates_to']?.event_id : null) || msg.eventId; let threadParent = (msg?.content?.['m.relates_to']?.rel_type === RelationType.Thread
if(threadParent) { ? msg?.content?.['m.relates_to']?.event_id
: null) || msg.eventId;
if (threadParent) {
content["m.relates_to"] = { content["m.relates_to"] = {
"rel_type": RelationType.Thread, "rel_type": RelationType.Thread,
"event_id": threadParent, "event_id": threadParent,
"is_falling_back": true, "is_falling_back": true,
}; };
if(msg.eventId !== threadParent) { if (msg.eventId !== threadParent) {
content["m.relates_to"]["m.in_reply_to"] = { content["m.relates_to"]["m.in_reply_to"] = {
"event_id": msg.eventId "event_id": msg.eventId
}; };
@ -202,4 +208,4 @@ module.exports = function(RED) {
}); });
} }
RED.nodes.registerType("matrix-send-message", MatrixSendImage); RED.nodes.registerType("matrix-send-message", MatrixSendImage);
} };

View File

@ -1,32 +1,52 @@
const {RelationType, TimelineWindow} = require("matrix-js-sdk");
global.Olm = require('olm'); global.Olm = require('olm');
const fs = require("fs-extra"); const fs = require("fs-extra");
const sdk = require("matrix-js-sdk"); let RelationType, TimelineWindow, sdk, LocalStorageCryptoStore, RoomEvent, RoomMemberEvent, HttpApiEvent, ClientEvent, MemoryStore;
(async () => {
const mod = await import("matrix-js-sdk");
RelationType = mod.RelationType;
TimelineWindow = mod.TimelineWindow;
// matrix-js-sdk doesn't export a default the top-level export is the same object:
sdk = mod;
RoomEvent = mod.RoomEvent;
RoomMemberEvent = mod.RoomMemberEvent;
HttpApiEvent = mod.HttpApiEvent;
ClientEvent = mod.ClientEvent;
MemoryStore = mod.MemoryStore;
// For LocalStorageCryptoStore, specify the file extension for Node 20+:
const cmod = await import("matrix-js-sdk/lib/crypto/store/localStorage-crypto-store.js");
LocalStorageCryptoStore = cmod.LocalStorageCryptoStore;
})();
const { resolve } = require('path'); const { resolve } = require('path');
const { LocalStorage } = require('node-localstorage'); const { LocalStorage } = require('node-localstorage');
const { LocalStorageCryptoStore } = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
const {RoomEvent, RoomMemberEvent, HttpApiEvent, ClientEvent, MemoryStore} = require("matrix-js-sdk");
require("abort-controller/polyfill"); // polyfill abort-controller if we don't have it require("abort-controller/polyfill"); // polyfill abort-controller if we don't have it
if (!globalThis.fetch) { if (!globalThis.fetch) {
// polyfill fetch if we don't have it // polyfill fetch if we don't have it
if (!globalThis.fetch) { if (!globalThis.fetch) {
import('node-fetch').then(({ default: fetch, Headers, Request, Response }) => { import('node-fetch').then(({ default: fetch, Headers, Request, Response }) => {
Object.assign(globalThis, { fetch, Headers, Request, Response }) Object.assign(globalThis, { fetch, Headers, Request, Response });
}) });
} }
} }
module.exports = function(RED) { module.exports = function(RED) {
// disable logging if set to "off" // disable logging if set to "off"
let loggingSettings = RED.settings.get('logging'); let loggingSettings = RED.settings.get('logging');
if( if (
typeof loggingSettings.console !== 'undefined' && typeof loggingSettings.console !== 'undefined' &&
typeof loggingSettings.console.level !== 'undefined' && typeof loggingSettings.console.level !== 'undefined' &&
['info','debug','trace'].indexOf(loggingSettings.console.level.toLowerCase()) >= 0 ['info','debug','trace'].indexOf(loggingSettings.console.level.toLowerCase()) >= 0
) { ) {
const { logger } = require('matrix-js-sdk/lib/logger'); import('matrix-js-sdk/lib/logger.js')
.then(({ logger }) => {
logger.disableAll(); logger.disableAll();
})
.catch((err) => {
console.error("Error loading logger module:", err);
});
} }
function MatrixFolderNameFromUserId(name) { function MatrixFolderNameFromUserId(name) {
@ -59,7 +79,7 @@ module.exports = function(RED) {
this.initializedAt = new Date(); this.initializedAt = new Date();
node.initialSyncLimit = 25; node.initialSyncLimit = 25;
// Keep track of all consumers of this node to be able to catch errors // Keep track of all consumers of this node to catch errors
node.register = function(consumerNode) { node.register = function(consumerNode) {
node.users[consumerNode.id] = consumerNode; node.users[consumerNode.id] = consumerNode;
}; };
@ -99,7 +119,7 @@ module.exports = function(RED) {
device_id = this.matrixClient.getDeviceId(); device_id = this.matrixClient.getDeviceId();
if(!device_id && node.enableE2ee) { if(!device_id && node.enableE2ee) {
node.error("Failed to auto detect deviceId for this auth token. You will need to manually specify one. You may need to login to create a new deviceId.", {}) node.error("Failed to auto detect deviceId for this auth token. You will need to manually specify one. You may need to login to create a new deviceId.", {});
} else { } else {
if(!stored_device_id || stored_device_id !== device_id) { if(!stored_device_id || stored_device_id !== device_id) {
node.log(`Saving Device ID (old:${stored_device_id} new:${device_id})`); node.log(`Saving Device ID (old:${stored_device_id} new:${device_id})`);
@ -116,7 +136,7 @@ module.exports = function(RED) {
node.matrixClient.setDeviceDetails(device_id, { node.matrixClient.setDeviceDetails(device_id, {
display_name: node.deviceLabel display_name: node.deviceLabel
}).then( }).then(
function(response) {}, function() {},
function(error) { function(error) {
node.error("Failed to set device label: " + error, {}); node.error("Failed to set device label: " + error, {});
} }
@ -154,7 +174,6 @@ module.exports = function(RED) {
}), }),
userId: this.userId, userId: this.userId,
deviceId: (this.deviceId || getStoredDeviceId(localStorage)) || undefined deviceId: (this.deviceId || getStoredDeviceId(localStorage)) || undefined
// verificationMethods: ["m.sas.v1"]
}); });
node.debug(`hasLazyLoadMembersEnabled=${node.matrixClient.hasLazyLoadMembersEnabled()}`); node.debug(`hasLazyLoadMembersEnabled=${node.matrixClient.hasLazyLoadMembersEnabled()}`);
@ -180,7 +199,7 @@ module.exports = function(RED) {
if(node.globalAccess) { if(node.globalAccess) {
try { try {
node.context().global.set('matrixClient["'+node.userId+'"]', undefined); node.context().global.set('matrixClient["'+node.userId+'"]', undefined);
} catch(e){ } catch(e) {
node.error(e.message, {}); node.error(e.message, {});
} }
} }
@ -194,15 +213,15 @@ module.exports = function(RED) {
node.matrixClient.on(RoomEvent.Timeline, async function(event, room, toStartOfTimeline, removed, data) { node.matrixClient.on(RoomEvent.Timeline, async function(event, room, toStartOfTimeline, removed, data) {
if (toStartOfTimeline) { if (toStartOfTimeline) {
node.log("Ignoring" + (event.isEncrypted() ? ' encrypted' : '') +" timeline event [" + (event.getContent()['msgtype'] || event.getType()) + "]: (" + room.name + ") " + event.getId() + " for reason: paginated result"); node.log("Ignoring" + (event.isEncrypted() ? ' encrypted' : '') +" timeline event [" + (event.getContent()['msgtype'] || event.getType()) + "]: (" + room.name + ") " + event.getId() + " for reason: paginated result");
return; // ignore paginated results return;
} }
if (!data || !data.liveEvent) { if (!data || !data.liveEvent) {
node.log("Ignoring" + (event.isEncrypted() ? ' encrypted' : '') +" timeline event [" + (event.getContent()['msgtype'] || event.getType()) + "]: (" + room.name + ") " + event.getId() + " for reason: old message"); node.log("Ignoring" + (event.isEncrypted() ? ' encrypted' : '') +" timeline event [" + (event.getContent()['msgtype'] || event.getType()) + "]: (" + room.name + ") " + event.getId() + " for reason: old message");
return; // ignore old message (we only want live events) return;
} }
if(node.initializedAt > event.getDate()) { if(node.initializedAt > event.getDate()) {
node.log("Ignoring" + (event.isEncrypted() ? ' encrypted' : '') +" timeline event [" + (event.getContent()['msgtype'] || event.getType()) + "]: (" + room.name + ") " + event.getId() + " for reason: old message before init"); node.log("Ignoring" + (event.isEncrypted() ? ' encrypted' : '') +" timeline event [" + (event.getContent()['msgtype'] || event.getType()) + "]: (" + room.name + ") " + event.getId() + " for reason: old message before init");
return; // skip events that occurred before our client initialized return;
} }
try { try {
@ -377,7 +396,6 @@ module.exports = function(RED) {
} }
}); });
node.matrixClient.on(HttpApiEvent.SessionLoggedOut, async function(errorObj){ node.matrixClient.on(HttpApiEvent.SessionLoggedOut, async function(errorObj){
// Example if user auth token incorrect: // Example if user auth token incorrect:
// { // {
@ -450,7 +468,7 @@ module.exports = function(RED) {
node.error("Auth check failed: " + err, {}); node.error("Auth check failed: " + err, {});
} }
} }
) );
})(); })();
} }
} }
@ -475,19 +493,21 @@ module.exports = function(RED) {
deviceId = req.body.deviceId || undefined, deviceId = req.body.deviceId || undefined,
displayName = req.body.displayName || undefined; displayName = req.body.displayName || undefined;
const matrixClient = sdk.createClient({ (async () => {
const mod = await import("matrix-js-sdk");
const matrixClient = mod.createClient({
baseUrl: baseUrl, baseUrl: baseUrl,
deviceId: deviceId, deviceId: deviceId,
timelineSupport: true, timelineSupport: true,
localTimeoutMs: '30000' localTimeoutMs: '30000'
}); });
new TimelineWindow() new TimelineWindow(); // from our top-level variable, but to keep minimal changes,
// you can just do: (await import("matrix-js-sdk")).TimelineWindow();
matrixClient.timelineSupport = true; matrixClient.timelineSupport = true;
matrixClient.login( matrixClient.login('m.login.password', {
'm.login.password', {
identifier: { identifier: {
type: 'm.id.user', type: 'm.id.user',
user: userId, user: userId,
@ -511,7 +531,11 @@ module.exports = function(RED) {
}); });
} }
); );
})().catch(err => {
res.json({ result: 'error', message: err.toString() });
}); });
}
);
function upgradeDirectoryIfNecessary(node, storageDir) { function upgradeDirectoryIfNecessary(node, storageDir) {
let oldStorageDir = './matrix-local-storage', let oldStorageDir = './matrix-local-storage',
@ -552,9 +576,6 @@ module.exports = function(RED) {
} }
} }
/**
* If a device ID is stored we will use that for the client
*/
function getStoredDeviceId(localStorage) { function getStoredDeviceId(localStorage) {
let deviceId = localStorage.getItem('my_device_id'); let deviceId = localStorage.getItem('my_device_id');
if(deviceId === "null" || !deviceId) { if(deviceId === "null" || !deviceId) {
@ -570,4 +591,4 @@ module.exports = function(RED) {
localStorage.setItem('my_device_id', deviceId); localStorage.setItem('my_device_id', deviceId);
return true; return true;
} }
} };