diff --git a/package.json b/package.json
index 6aeb583..1084edf 100644
--- a/package.json
+++ b/package.json
@@ -32,13 +32,14 @@
"matrix-crypt-file": "src/matrix-crypt-file.js",
"matrix-room-kick": "src/matrix-room-kick.js",
"matrix-room-ban": "src/matrix-room-ban.js",
+ "matrix-room-users": "src/matrix-room-users.js",
+ "matrix-room-state-events": "src/matrix-room-state-events.js",
"matrix-synapse-users": "src/matrix-synapse-users.js",
"matrix-synapse-register": "src/matrix-synapse-register.js",
"matrix-synapse-create-edit-user": "src/matrix-synapse-create-edit-user.js",
"matrix-synapse-deactivate-user": "src/matrix-synapse-deactivate-user.js",
"matrix-synapse-join-room": "src/matrix-synapse-join-room.js",
- "matrix-whois-user": "src/matrix-whois-user.js",
- "matrix-room-users": "src/matrix-room-users.js"
+ "matrix-whois-user": "src/matrix-whois-user.js"
}
},
"engines": {
diff --git a/src/matrix-create-room.js b/src/matrix-create-room.js
index f9b7fa2..7ff02b4 100644
--- a/src/matrix-create-room.js
+++ b/src/matrix-create-room.js
@@ -44,6 +44,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
if(!msg.payload) {
diff --git a/src/matrix-invite-room.js b/src/matrix-invite-room.js
index df6afac..61c7589 100644
--- a/src/matrix-invite-room.js
+++ b/src/matrix-invite-room.js
@@ -45,6 +45,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
msg.topic = node.roomId || msg.topic;
diff --git a/src/matrix-join-room.js b/src/matrix-join-room.js
index a3b885e..61d54fc 100644
--- a/src/matrix-join-room.js
+++ b/src/matrix-join-room.js
@@ -32,6 +32,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
if(!msg.topic) {
diff --git a/src/matrix-leave-room.js b/src/matrix-leave-room.js
index ecc171b..daebb33 100644
--- a/src/matrix-leave-room.js
+++ b/src/matrix-leave-room.js
@@ -38,6 +38,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
try {
diff --git a/src/matrix-react.js b/src/matrix-react.js
index 4ce6b3d..a176277 100644
--- a/src/matrix-react.js
+++ b/src/matrix-react.js
@@ -34,6 +34,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
msg.topic = node.roomId || msg.topic;
diff --git a/src/matrix-room-ban.js b/src/matrix-room-ban.js
index a7d8435..a0af7db 100644
--- a/src/matrix-room-ban.js
+++ b/src/matrix-room-ban.js
@@ -34,6 +34,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
msg.topic = node.roomId || msg.topic;
diff --git a/src/matrix-room-kick.js b/src/matrix-room-kick.js
index 926311b..ac3efb0 100644
--- a/src/matrix-room-kick.js
+++ b/src/matrix-room-kick.js
@@ -34,6 +34,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
msg.topic = node.roomId || msg.topic;
diff --git a/src/matrix-room-state-events.html b/src/matrix-room-state-events.html
new file mode 100644
index 0000000..f778a47
--- /dev/null
+++ b/src/matrix-room-state-events.html
@@ -0,0 +1,342 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/matrix-room-state-events.js b/src/matrix-room-state-events.js
new file mode 100644
index 0000000..e8f2a1f
--- /dev/null
+++ b/src/matrix-room-state-events.js
@@ -0,0 +1,272 @@
+module.exports = function(RED) {
+ function MatrixRoomSettings(n) {
+ RED.nodes.createNode(this, n);
+
+ var node = this;
+
+ this.name = n.name;
+ this.server = RED.nodes.getNode(n.server);
+ this.roomId = n.roomId;
+ this.rules = n.rules;
+
+ if (!node.server) {
+ node.warn("No configuration node");
+ return;
+ }
+ node.server.register(node);
+
+ node.status({ fill: "red", shape: "ring", text: "disconnected" });
+
+ node.server.on("disconnected", function(){
+ node.status({ fill: "red", shape: "ring", text: "disconnected" });
+ });
+
+ node.server.on("connected", function() {
+ node.status({ fill: "green", shape: "ring", text: "connected" });
+ });
+
+ node.on("input", async function (msg) {
+ if (! node.server || ! node.server.matrixClient) {
+ msg.error = "No matrix server selected";
+ node.error(msg.error, msg);
+ node.send([null, msg]);
+ return;
+ }
+
+ if(!node.server.isConnected()) {
+ msg.error = "Matrix server connection is currently closed";
+ node.error(msg.error, msg);
+ node.send([null, msg]);
+ return;
+ }
+
+ msg.topic = node.roomId || msg.topic;
+ if(!msg.topic) {
+ msg.error = "Room must be specified in msg.topic or in configuration";
+ node.error(msg.error, msg);
+ node.send([null, msg]);
+ return;
+ }
+
+ let getterErrors = {},
+ setterErrors = {};
+
+ if(!Array.isArray(node.rules) || !node.rules.length) {
+ node.warn("No rules configured, skipping", msg);
+ return msg;
+ }
+
+ function getToValue(msg, rule) {
+ var value = rule.to;
+ if (rule.tot === 'json') {
+ try {
+ value = JSON.parse(rule.to);
+ } catch(e) {
+ throw new Error("Invalid JSON");
+ }
+ } else if (rule.tot === 'bin') {
+ try {
+ value = Buffer.from(JSON.parse(rule.to))
+ } catch(e) {
+ throw new Error("Invalid Binary");
+ }
+ }
+ if (rule.tot === "msg") {
+ value = RED.util.getMessageProperty(msg,rule.to);
+ } else if ((rule.tot === 'flow') || (rule.tot === 'global')) {
+ RED.util.evaluateNodeProperty(rule.to, rule.tot, node, msg, (err,value) => {
+ if (err) {
+ throw new Error("Invalid value evaluation");
+ } else {
+ return value;
+ }
+ });
+ return
+ } else if (rule.tot === 'date') {
+ value = Date.now();
+ } else if (rule.tot === 'jsonata') {
+ RED.util.evaluateJSONataExpression(rule.to,msg, (err, value) => {
+ if (err) {
+ throw new Error("Invalid expression");
+ } else {
+ return value;
+ }
+ });
+ return;
+ }
+ return value;
+ }
+
+ function setToValue(value, rule) {
+ if(rule.tot === 'global' || rule.tot === 'flow') {
+ var contextKey = RED.util.parseContextStore(rule.to);
+ if (/\[msg/.test(contextKey.key)) {
+ // The key has a nest msg. reference to evaluate first
+ contextKey.key = RED.util.normalisePropertyExpression(contextKey.key, msg, true)
+ }
+ var target = node.context()[rule.tot];
+ var callback = err => {
+ if (err) {
+ node.error(err, msg);
+ getterErrors[rule.p] = err.message;
+ }
+ }
+ target.set(contextKey.key, value, contextKey.store, callback);
+ } else if(rule.tot === 'msg') {
+ if (!RED.util.setMessageProperty(msg, rule.to, value)) {
+ node.warn(RED._("change.errors.no-override",{property:rule.to}));
+ }
+ }
+ }
+
+ for(let rule of node.rules) {
+ // [
+ // {
+ // "t": "set",
+ // "p": "m.room.topic",
+ // "to": "asdf",
+ // "tot": "str"
+ // }, ...
+ // ]
+
+ let cachedGetters = {};
+ if(rule.t === 'set') {
+ let value;
+ try {
+ value = getToValue(msg, rule);
+ switch(rule.p) {
+ case "m.room.name":
+ await node.server.matrixClient.sendStateEvent(
+ msg.topic,
+ "m.room.name",
+ typeof value === "string"
+ ? { name: value }
+ : value);
+ break;
+ case "m.room.topic":
+ if(typeof value === "string") {
+ await node.server.matrixClient.setRoomTopic(msg.topic, value);
+ } else {
+ await node.server.matrixClient.sendStateEvent(
+ msg.topic,
+ "m.room.topic",
+ value
+ );
+ }
+ break;
+ case "m.room.avatar":
+ await node.server.matrixClient.sendStateEvent(
+ msg.topic,
+ "m.room.avatar",
+ typeof value === "string"
+ ? { "url": value }
+ : value,
+ "");
+ break;
+ case "m.room.power_levels":
+ if(typeof value !== 'object') {
+ setterErrors[rule.p] = "m.room.power_levels content must be object";
+ } else {
+ await node.server.matrixClient.sendStateEvent(
+ msg.topic,
+ "m.room.power_levels",
+ value,
+ "");
+ }
+ break;
+ case "m.room.guest_access":
+ await node.server.matrixClient.sendStateEvent(
+ msg.topic,
+ "m.room.guest_access",
+ typeof value === "string"
+ ? { "guest_access": value }
+ : value,
+ "");
+ break;
+ case "m.room.join_rules":
+ if(typeof value !== 'object') {
+ setterErrors[rule.p] = "m.room.join_rules content must be object";
+ } else {
+ await node.server.matrixClient.sendStateEvent(
+ msg.topic,
+ "m.room.join_rules",
+ value,
+ "");
+ }
+ break;
+ case "m.room.canonical_alias":
+ if(typeof value !== 'object') {
+ setterErrors[rule.p] = "m.room.canonical_alias content must be object";
+ } else {
+ await node.server.matrixClient.sendStateEvent(
+ msg.topic,
+ "m.room.canonical_alias",
+ value,
+ "");
+ }
+ break;
+ default:
+ if(typeof value !== 'object') {
+ setterErrors[rule.p] = "Custom event content must be object";
+ } else {
+ await node.server.matrixClient.sendStateEvent(
+ msg.topic,
+ rule.p,
+ value,
+ "");
+ }
+ break;
+ }
+ } catch(e) {
+ setterErrors[rule.p] = e.message;
+ }
+ } else if(rule.t === 'get') {
+ let value;
+ if(cachedGetters.hasOwnProperty(rule.p)) {
+ value = cachedGetters[rule.p];
+ } else {
+ try {
+ // we may want to fetch from local storage in the future, this is how to do that
+ // const room = this.getRoom(roomId);
+ // const ev = room.currentState.getStateEvents(EventType.RoomEncryption, "");
+ value = await node.server.matrixClient.getStateEvent(msg.topic, rule.p, "");
+ switch(rule.p) {
+ case "m.room.name":
+ value = value?.name
+ break;
+ case "m.room.topic":
+ value = value?.topic
+ break;
+ case "m.room.avatar":
+ value = value?.url
+ break;
+ case "m.room.guest_access":
+ value = value?.guest_access;
+ break;
+ }
+ setToValue(value, rule);
+ } catch(e) {
+ getterErrors[rule.p] = e;
+ }
+ }
+
+ }
+ }
+
+ if(Object.keys(setterErrors).length) {
+ msg.setter_errors = setterErrors;
+ }
+
+ if(Object.keys(getterErrors).length) {
+ msg.getter_errors = getterErrors;
+ }
+
+ node.send([msg, null]);
+ });
+
+ node.on("close", function() {
+ node.server.deregister(node);
+ });
+ }
+ RED.nodes.registerType("matrix-room-state-events", MatrixRoomSettings);
+}
\ No newline at end of file
diff --git a/src/matrix-room-users.js b/src/matrix-room-users.js
index 1a5347e..8a801c6 100644
--- a/src/matrix-room-users.js
+++ b/src/matrix-room-users.js
@@ -34,6 +34,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
let roomId = node.roomId || msg.topic;
diff --git a/src/matrix-send-file.js b/src/matrix-send-file.js
index ad89615..65e9344 100644
--- a/src/matrix-send-file.js
+++ b/src/matrix-send-file.js
@@ -34,6 +34,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
msg.topic = node.roomId || msg.topic;
diff --git a/src/matrix-send-image.js b/src/matrix-send-image.js
index 2e15e8f..29e2173 100644
--- a/src/matrix-send-image.js
+++ b/src/matrix-send-image.js
@@ -34,6 +34,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
msg.topic = node.roomId || msg.topic;
diff --git a/src/matrix-server-config.js b/src/matrix-server-config.js
index 81ea4cf..920f3af 100644
--- a/src/matrix-server-config.js
+++ b/src/matrix-server-config.js
@@ -379,7 +379,7 @@ module.exports = function(RED) {
if(node.e2ee){
node.log("Initializing crypto...");
await node.matrixClient.initCrypto();
- node.matrixClient.setGlobalErrorOnUnknownDevices(false);
+ node.matrixClient.getCrypto().globalBlacklistUnverifiedDevices = false; // prevent errors from unverified devices
}
node.log("Connecting to Matrix server...");
await node.matrixClient.startClient({
diff --git a/src/matrix-synapse-create-edit-user.js b/src/matrix-synapse-create-edit-user.js
index 43be104..e13aa7f 100644
--- a/src/matrix-synapse-create-edit-user.js
+++ b/src/matrix-synapse-create-edit-user.js
@@ -44,6 +44,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
if(!msg.userId) {
diff --git a/src/matrix-synapse-deactivate-user.js b/src/matrix-synapse-deactivate-user.js
index b6e909a..62aaa49 100644
--- a/src/matrix-synapse-deactivate-user.js
+++ b/src/matrix-synapse-deactivate-user.js
@@ -44,6 +44,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
if(!msg.userId) {
diff --git a/src/matrix-synapse-join-room.js b/src/matrix-synapse-join-room.js
index 3fd51c2..9c683b3 100644
--- a/src/matrix-synapse-join-room.js
+++ b/src/matrix-synapse-join-room.js
@@ -43,6 +43,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
msg.topic = node.roomId || msg.topic;
diff --git a/src/matrix-synapse-users.js b/src/matrix-synapse-users.js
index f2079c6..e49ff24 100644
--- a/src/matrix-synapse-users.js
+++ b/src/matrix-synapse-users.js
@@ -33,6 +33,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
let queryParams = {
diff --git a/src/matrix-whois-user.js b/src/matrix-whois-user.js
index 15de6d8..70e335d 100644
--- a/src/matrix-whois-user.js
+++ b/src/matrix-whois-user.js
@@ -45,6 +45,7 @@ module.exports = function(RED) {
if(!node.server.isConnected()) {
node.error("Matrix server connection is currently closed", msg);
node.send([null, msg]);
+ return;
}
if(!msg.userId) {