diff --git a/package.json b/package.json index 377554e..5e9f54f 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "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-typing": "src/matrix-typing.js" + "matrix-typing": "src/matrix-typing.js", + "matrix-user-settings": "src/matrix-user-settings.js" } }, "engines": { diff --git a/src/matrix-room-state-events.js b/src/matrix-room-state-events.js index 951a634..de48888 100644 --- a/src/matrix-room-state-events.js +++ b/src/matrix-room-state-events.js @@ -1,5 +1,5 @@ module.exports = function(RED) { - function MatrixRoomSettings(n) { + function MatrixRoomStateEvents(n) { RED.nodes.createNode(this, n); var node = this; @@ -265,6 +265,7 @@ module.exports = function(RED) { } } setToValue(value, rule); + cachedGetters[rule.p] = value; } catch(e) { getterErrors[rule.p] = e; } @@ -288,5 +289,5 @@ module.exports = function(RED) { node.server.deregister(node); }); } - RED.nodes.registerType("matrix-room-state-events", MatrixRoomSettings); + RED.nodes.registerType("matrix-room-state-events", MatrixRoomStateEvents); } \ No newline at end of file diff --git a/src/matrix-user-settings.html b/src/matrix-user-settings.html new file mode 100644 index 0000000..fb4ea04 --- /dev/null +++ b/src/matrix-user-settings.html @@ -0,0 +1,330 @@ + + + + + + \ No newline at end of file diff --git a/src/matrix-user-settings.js b/src/matrix-user-settings.js new file mode 100644 index 0000000..1c504ac --- /dev/null +++ b/src/matrix-user-settings.js @@ -0,0 +1,184 @@ +module.exports = function(RED) { + function MatrixUserSettings(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')) { + try { + value = RED.util.evaluateNodeProperty(rule.to, rule.tot, node, msg); + } catch(e2) { + throw new Error("Invalid value evaluation"); + } + } else if (rule.tot === 'date') { + value = Date.now(); + } else if (rule.tot === 'jsonata') { + try { + value = RED.util.evaluateJSONataExpression(rule.to,msg); + } catch(e3) { + throw new Error("Invalid expression"); + } + 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 "display_name": + await node.server.matrixClient.setDisplayName(value); + break; + case "avatar_url": + await node.server.matrixClient.setAvatarUrl(typeof value === "string" ? 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 { + // normalize some simpler events for easier access + switch(rule.p) { + case "display_name": + value = (await node.server.matrixClient.getProfileInfo(node.server.matrixClient.getUserId(), 'displayname')).displayname || false; + break; + case "avatar_url": + value = (await node.server.matrixClient.getProfileInfo(node.server.matrixClient.getUserId(), 'avatar_url')).avatar_url || false; + break; + } + setToValue(value, rule); + cachedGetters[rule.p] = value; + } 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-user-settings", MatrixUserSettings); +} \ No newline at end of file