mirror of
https://github.com/Skylar-Tech/node-red-contrib-matrix-chat.git
synced 2026-05-19 05:33:18 -06:00
Compare commits
33 Commits
0.5.8
...
fd605005d1
| Author | SHA1 | Date | |
|---|---|---|---|
| fd605005d1 | |||
| 85de450a1a | |||
| e7e0f2967b | |||
| 611e23b845 | |||
| 9d050a0d44 | |||
| c833a40a84 | |||
| 0e755bc350 | |||
| c920dd12cb | |||
| 9661922f78 | |||
| 20c7182511 | |||
| f48ba74a72 | |||
| 124a0cba34 | |||
| 8ca11f36d8 | |||
| 8a7fba39e8 | |||
| c61eadd05d | |||
| 78f8ab7abb | |||
| ce8be4a30f | |||
| 3e808cabec | |||
| 2fdc7482ce | |||
| c7f9d56df2 | |||
| 3c042ae47d | |||
| 0a34870fa3 | |||
| 768a1c8ce0 | |||
| 22dd9b4ca3 | |||
| 462f9670c2 | |||
| e4b01c40c2 | |||
| 908d60835d | |||
| 4c17a21008 | |||
| bd4f6ea486 | |||
| 5ef0b6a11f | |||
| 97f27e61c6 | |||
| 7bdadc0fe9 | |||
| 5f129560aa |
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
# node-red-contrib-matrix-chat
|
||||
Matrix chat server client for [Node-RED](https://nodered.org/)
|
||||
[Matrix](https://matrix.org/) chat server client for [Node-RED](https://nodered.org/)
|
||||
|
||||
***Currently we are in beta. We ask that you open any issues you have on our repository to help us reach a stable well tested version. Things may change & break before our first release so check changelog before updating.***
|
||||
|
||||
@@ -11,8 +11,10 @@ The following is supported from this package:
|
||||
|
||||
- End-to-end encryption
|
||||
- [Currently a WIP](#end-to-end-encryption-notes)
|
||||
- Receive events from a room (messages, reactions, images, and files) whether encrypted or not
|
||||
- Receive events from a room (messages, reactions, images, audio, locations, and files) whether encrypted or not
|
||||
- Send Images/Files (sending files to e2ee room doesn't currently encrypt them yet)
|
||||
- Edit messages
|
||||
- Delete events (messages, reactions, etc)
|
||||
- Decrypt files in e2ee rooms
|
||||
- Send HTML/Plain Text Message/Notice
|
||||
- React to messages
|
||||
@@ -24,9 +26,7 @@ The following is supported from this package:
|
||||
- Get a user list from a room
|
||||
- Kick user from room
|
||||
- Ban user from room
|
||||
- Join a room
|
||||
- Create a room
|
||||
- Invite to a room
|
||||
- Join, Create, Invite, and Leave rooms
|
||||
- Synapse admin API to force add user to room (requires bot to be in same room already)
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ Build something cool with these nodes? Feel free to submit a pull request to sha
|
||||
- [Respond to "image" with an uploaded image](#respond-to-image-with-an-uploaded-image)
|
||||
- [Respond to "file" with an uploaded file](#respond-to-file-with-an-uploaded-file)
|
||||
- [Respond to "react" with a reaction](#respond-to-react-with-a-reaction)
|
||||
- [Accept room invites from specific user](#accept-room-invites-from-specific-user)
|
||||
- [Leave room when someone says "bye"](#leave-room-when-someone-says-bye)
|
||||
- [Remove messages containing "delete"](#remove-messages-containing-delete)
|
||||
- [Respond to "users" with full list of server users](#respond-to-users-with-full-list-of-server-users)
|
||||
- [Respond to "newroom" by creating new room and inviting user](#respond-to-newroom-by-creating-new-room-and-inviting-user)
|
||||
@@ -113,6 +115,26 @@ Give a 👍 reaction when someone says "react"
|
||||
|
||||
|
||||
|
||||
### Accept room invites from specific user
|
||||
|
||||
[View JSON](accept-room-invites.json)
|
||||
|
||||
Accept room invites from specific user.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### Leave room when someone says bye
|
||||
|
||||
[View JSON](leave-room-bye.json)
|
||||
|
||||
Leave room when someone says "bye".
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### Remove messages containing "delete"
|
||||
|
||||
[View JSON](delete-event.json)
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
[
|
||||
{
|
||||
"id": "64f76474ff7a3727",
|
||||
"type": "matrix-room-invite",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "",
|
||||
"server": "8da0ef83f77f8e24",
|
||||
"roomId": null,
|
||||
"x": 270,
|
||||
"y": 2380,
|
||||
"wires": [
|
||||
[
|
||||
"22f6056fa5bc5bd0"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "6d6f304a0a6342b8",
|
||||
"type": "matrix-join-room",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "",
|
||||
"server": "8da0ef83f77f8e24",
|
||||
"x": 770,
|
||||
"y": 2380,
|
||||
"wires": [
|
||||
[
|
||||
"1409ebb4a0e65663"
|
||||
],
|
||||
[
|
||||
"1409ebb4a0e65663"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "22f6056fa5bc5bd0",
|
||||
"type": "switch",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "msg.userId == @skylord123:skylar.tech",
|
||||
"property": "userId",
|
||||
"propertyType": "msg",
|
||||
"rules": [
|
||||
{
|
||||
"t": "eq",
|
||||
"v": "@skylord123:skylar.tech",
|
||||
"vt": "str"
|
||||
}
|
||||
],
|
||||
"checkall": "true",
|
||||
"repair": false,
|
||||
"outputs": 1,
|
||||
"x": 520,
|
||||
"y": 2380,
|
||||
"wires": [
|
||||
[
|
||||
"6d6f304a0a6342b8"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "043f74e6d76b1eb0",
|
||||
"type": "comment",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "Only accept room invites from specific user",
|
||||
"info": "",
|
||||
"x": 360,
|
||||
"y": 2340,
|
||||
"wires": []
|
||||
},
|
||||
{
|
||||
"id": "1409ebb4a0e65663",
|
||||
"type": "debug",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "",
|
||||
"active": true,
|
||||
"tosidebar": true,
|
||||
"console": false,
|
||||
"tostatus": false,
|
||||
"complete": "true",
|
||||
"targetType": "full",
|
||||
"statusVal": "",
|
||||
"statusType": "auto",
|
||||
"x": 910,
|
||||
"y": 2380,
|
||||
"wires": []
|
||||
},
|
||||
{
|
||||
"id": "8da0ef83f77f8e24",
|
||||
"type": "matrix-server-config",
|
||||
"name": null,
|
||||
"autoAcceptRoomInvites": false,
|
||||
"enableE2ee": true,
|
||||
"global": true
|
||||
}
|
||||
]
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,103 @@
|
||||
[
|
||||
{
|
||||
"id": "997c354038202dba",
|
||||
"type": "comment",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "Leave room when someone says \"bye\"",
|
||||
"info": "",
|
||||
"x": 350,
|
||||
"y": 2520,
|
||||
"wires": []
|
||||
},
|
||||
{
|
||||
"id": "69c4ea189be94feb",
|
||||
"type": "matrix-receive",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "",
|
||||
"server": "8da0ef83f77f8e24",
|
||||
"roomId": "",
|
||||
"acceptText": true,
|
||||
"acceptEmotes": true,
|
||||
"acceptStickers": true,
|
||||
"acceptReactions": true,
|
||||
"acceptFiles": true,
|
||||
"acceptAudio": true,
|
||||
"acceptImages": true,
|
||||
"acceptLocations": true,
|
||||
"x": 280,
|
||||
"y": 2560,
|
||||
"wires": [
|
||||
[
|
||||
"19e1d64b63ae8a1f"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "19e1d64b63ae8a1f",
|
||||
"type": "switch",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "msg.payload == bye",
|
||||
"property": "payload",
|
||||
"propertyType": "msg",
|
||||
"rules": [
|
||||
{
|
||||
"t": "eq",
|
||||
"v": "bye",
|
||||
"vt": "str"
|
||||
}
|
||||
],
|
||||
"checkall": "true",
|
||||
"repair": false,
|
||||
"outputs": 1,
|
||||
"x": 480,
|
||||
"y": 2560,
|
||||
"wires": [
|
||||
[
|
||||
"db0e51f8e7793f92"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "db0e51f8e7793f92",
|
||||
"type": "matrix-leave-room",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "",
|
||||
"server": "8da0ef83f77f8e24",
|
||||
"roomId": null,
|
||||
"x": 670,
|
||||
"y": 2560,
|
||||
"wires": [
|
||||
[
|
||||
"3791f551bf0e4fc4"
|
||||
],
|
||||
[
|
||||
"3791f551bf0e4fc4"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "3791f551bf0e4fc4",
|
||||
"type": "debug",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "",
|
||||
"active": true,
|
||||
"tosidebar": true,
|
||||
"console": false,
|
||||
"tostatus": false,
|
||||
"complete": "true",
|
||||
"targetType": "full",
|
||||
"statusVal": "",
|
||||
"statusType": "auto",
|
||||
"x": 810,
|
||||
"y": 2560,
|
||||
"wires": []
|
||||
},
|
||||
{
|
||||
"id": "8da0ef83f77f8e24",
|
||||
"type": "matrix-server-config",
|
||||
"name": null,
|
||||
"autoAcceptRoomInvites": false,
|
||||
"enableE2ee": true,
|
||||
"global": true
|
||||
}
|
||||
]
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Generated
+14736
-8392
File diff suppressed because it is too large
Load Diff
+15
-9
@@ -1,14 +1,17 @@
|
||||
{
|
||||
"name": "node-red-contrib-matrix-chat",
|
||||
"version": "0.5.8",
|
||||
"version": "0.7.1",
|
||||
"description": "Matrix chat server client for Node-RED",
|
||||
"dependencies": {
|
||||
"fs-extra": "^10.0.1",
|
||||
"abort-controller": "^3.0.0",
|
||||
"fs-extra": "^11.1.0",
|
||||
"got": "^12.0.2",
|
||||
"isomorphic-webcrypto": "^2.3.8",
|
||||
"matrix-js-sdk": "^16.0.0",
|
||||
"matrix-js-sdk": "^28.0.0",
|
||||
"node-fetch": "^3.3.0",
|
||||
"node-localstorage": "^2.2.1",
|
||||
"olm": "https://gitlab.matrix.org/matrix-org/olm/-/package_files/271/download",
|
||||
"olm": "https://gitlab.matrix.org/matrix-org/olm/-/package_files/2572/download",
|
||||
"request": "^2.88.2",
|
||||
"utf8": "^3.0.0"
|
||||
},
|
||||
"node-red": {
|
||||
@@ -23,17 +26,20 @@
|
||||
"matrix-react": "src/matrix-react.js",
|
||||
"matrix-create-room": "src/matrix-create-room.js",
|
||||
"matrix-invite-room": "src/matrix-invite-room.js",
|
||||
"matrix-room-invite": "src/matrix-room-invite.js",
|
||||
"matrix-join-room": "src/matrix-join-room.js",
|
||||
"matrix-leave-room": "src/matrix-leave-room.js",
|
||||
"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": {
|
||||
@@ -42,9 +48,9 @@
|
||||
"keywords": [
|
||||
"node-red",
|
||||
"matrix",
|
||||
"support",
|
||||
"bot",
|
||||
"chat"
|
||||
"chat",
|
||||
"chatbot",
|
||||
"federated"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs: 2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" }
|
||||
server: { type: "matrix-server-config" }
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Create Room";
|
||||
|
||||
@@ -8,9 +8,10 @@ module.exports = function(RED) {
|
||||
this.server = RED.nodes.getNode(n.server);
|
||||
|
||||
if(!this.server) {
|
||||
node.error('Server must be configured on the node.');
|
||||
node.error('Server must be configured on the node.', {});
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
this.encodeUri = function(pathTemplate, variables) {
|
||||
for (const key in variables) {
|
||||
@@ -41,8 +42,9 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.payload) {
|
||||
@@ -64,6 +66,10 @@ module.exports = function(RED) {
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-create-room", MatrixCreateRoom);
|
||||
}
|
||||
@@ -12,22 +12,22 @@ module.exports = function(RED) {
|
||||
const { got } = await import('got');
|
||||
|
||||
if(!msg.type) {
|
||||
node.error('msg.type is required.');
|
||||
node.error('msg.type is required.', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.content) {
|
||||
node.error('msg.content is required.');
|
||||
node.error('msg.content is required.', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.content.file) {
|
||||
node.error('msg.content.file is required.');
|
||||
node.error('msg.content.file is required.', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.url) {
|
||||
node.error('msg.url is required.');
|
||||
node.error('msg.url is required.', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
reason: { value: "" },
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ module.exports = function(RED) {
|
||||
node.warn("No configuration node");
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
@@ -27,7 +28,7 @@ module.exports = function(RED) {
|
||||
node.on('input', function(msg) {
|
||||
|
||||
if(!msg.eventId) {
|
||||
node.error("eventId is missing");
|
||||
node.error("eventId is missing", msg);
|
||||
node.send([null, msg])
|
||||
return;
|
||||
}
|
||||
@@ -38,7 +39,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
@@ -70,6 +71,10 @@ module.exports = function(RED) {
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-delete-event",MatrixDeleteEvent);
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
outputs: 2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Room Invite";
|
||||
return this.name || "Invite to Room";
|
||||
},
|
||||
paletteLabel: 'Room Invite'
|
||||
paletteLabel: 'Invite to Room'
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -9,9 +9,10 @@ module.exports = function(RED) {
|
||||
this.roomId = n.roomId;
|
||||
|
||||
if(!this.server) {
|
||||
node.error('Server must be configured on the node.');
|
||||
node.error('Server must be configured on the node.', {});
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
this.encodeUri = function(pathTemplate, variables) {
|
||||
for (const key in variables) {
|
||||
@@ -37,18 +38,19 @@ module.exports = function(RED) {
|
||||
|
||||
node.on("input", function (msg) {
|
||||
if (! node.server || ! node.server.matrixClient) {
|
||||
node.error("No matrix server selected");
|
||||
node.error("No matrix server selected", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.topic = node.roomId || msg.topic;
|
||||
if(!msg.topic) {
|
||||
node.error("room must be defined in either msg.topic or in node config");
|
||||
node.error("room must be defined in either msg.topic or in node config", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -64,6 +66,10 @@ module.exports = function(RED) {
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-invite-room", MatrixInviteRoom);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" }
|
||||
server: { type: "matrix-server-config" }
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Join Room";
|
||||
|
||||
+10
-4
@@ -11,6 +11,7 @@ module.exports = function(RED) {
|
||||
node.warn("No configuration node");
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
@@ -24,17 +25,18 @@ module.exports = function(RED) {
|
||||
|
||||
node.on("input", function (msg) {
|
||||
if (! node.server || ! node.server.matrixClient) {
|
||||
node.error("No matrix server selected");
|
||||
node.error("No matrix server selected", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.topic) {
|
||||
node.error("Room must be specified in msg.topic");
|
||||
node.error("Room must be specified in msg.topic", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,11 +48,15 @@ module.exports = function(RED) {
|
||||
node.send([msg, null]);
|
||||
})
|
||||
.catch(function(e){
|
||||
node.error("Error trying to join room " + msg.topic + ":" + e);
|
||||
node.error("Error trying to join room " + msg.topic + ":" + e, msg);
|
||||
msg.error = e;
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-join-room", MatrixJoinRoom);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('matrix-leave-room', {
|
||||
category: 'matrix',
|
||||
color: '#00b7ca',
|
||||
icon: "matrix.png",
|
||||
outputLabels: ["success", "error"],
|
||||
inputs: 1,
|
||||
outputs: 2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Leave Room";
|
||||
},
|
||||
paletteLabel: 'Leave Room'
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="matrix-leave-room">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-server"><i class="fa fa-user"></i> Matrix Server Config</label>
|
||||
<input type="text" id="node-input-server">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="matrix-leave-room">
|
||||
<h3>Details</h3>
|
||||
<p>
|
||||
This node leaves a room
|
||||
</p>
|
||||
|
||||
<h3>Inputs</h3>
|
||||
<dl class="message-properties">
|
||||
<dt>msg.topic
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd> The room identifier to leave: for example, <code>!h8zld9j31:example.com</code>.</dd>
|
||||
</dl>
|
||||
|
||||
<h3>Outputs</h3>
|
||||
<ol class="node-ports">
|
||||
<li>Success
|
||||
<dl class="message-properties">
|
||||
<dt>msg.payload <span class="property-type">object</span></dt>
|
||||
<dd>Returns the same message that was received</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li>Error
|
||||
<dl class="message-properties">
|
||||
<dt>msg.error <span class="property-type">string</span></dt>
|
||||
<dd>the error that occurred.</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,60 @@
|
||||
module.exports = function(RED) {
|
||||
function MatrixLeaveRoom(n) {
|
||||
RED.nodes.createNode(this, n);
|
||||
|
||||
let node = this;
|
||||
|
||||
this.name = n.name;
|
||||
this.server = RED.nodes.getNode(n.server);
|
||||
this.roomId = n.roomId;
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
if (!node.server) {
|
||||
node.error("No configuration node", {});
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
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', function(msg) {
|
||||
if (! node.server || ! node.server.matrixClient) {
|
||||
node.error("No matrix server selected", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.topic) {
|
||||
node.error('No room provided in msg.topic', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
node.log("Leaving room " + msg.topic);
|
||||
node.server.matrixClient.leave(msg.topic);
|
||||
node.send([msg, null]);
|
||||
} catch(e) {
|
||||
node.error("Failed to leave room " + msg.topic + ": " + e, msg);
|
||||
msg.payload = e;
|
||||
node.send([null, msg]);
|
||||
}
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-leave-room", MatrixLeaveRoom);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
reaction: { value: null }
|
||||
},
|
||||
|
||||
+11
-5
@@ -13,6 +13,7 @@ module.exports = function(RED) {
|
||||
node.warn("No configuration node");
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
@@ -26,30 +27,31 @@ module.exports = function(RED) {
|
||||
|
||||
node.on("input", function (msg) {
|
||||
if (!node.server || !node.server.matrixClient) {
|
||||
node.error("No matrix server selected");
|
||||
node.error("No matrix server selected", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.topic = node.roomId || msg.topic;
|
||||
if(!msg.topic) {
|
||||
node.error("Room must be specified in msg.topic or in configuration");
|
||||
node.error("Room must be specified in msg.topic or in configuration", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
let payload = n.reaction || msg.payload;
|
||||
if(!payload) {
|
||||
node.error('msg.payload must be defined or the reaction configured on the node.');
|
||||
node.error('msg.payload must be defined or the reaction configured on the node.', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
let eventId = msg.referenceEventId || msg.eventId;
|
||||
if(!eventId) {
|
||||
node.error('Either msg.referenceEventId or msg.eventId must be defined to react to a message.');
|
||||
node.error('Either msg.referenceEventId or msg.eventId must be defined to react to a message.', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -75,6 +77,10 @@ module.exports = function(RED) {
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-react", MatrixReact);
|
||||
}
|
||||
+79
-7
@@ -8,14 +8,17 @@
|
||||
outputs:1,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: {"value": null},
|
||||
acceptText: {"value": true},
|
||||
acceptEmotes: {"value": true},
|
||||
acceptStickers: {"value": true},
|
||||
acceptReactions: {"value": true},
|
||||
acceptFiles: {"value": true},
|
||||
acceptAudio: {"value": true},
|
||||
acceptImages: {"value": true},
|
||||
acceptVideos: {"value": true},
|
||||
acceptLocations: {"value": true},
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Matrix Receive";
|
||||
@@ -49,7 +52,7 @@
|
||||
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||
/>
|
||||
<label for="node-input-acceptText" style="width: auto">
|
||||
Accept text <code>m.text</code>
|
||||
Accept text <code style="text-transform: none;">m.text</code>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@@ -59,7 +62,7 @@
|
||||
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||
/>
|
||||
<label for="node-input-acceptEmotes" style="width: auto">
|
||||
Accept emotes <code>m.emote</code>
|
||||
Accept emotes <code style="text-transform: none;">m.emote</code>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@@ -69,7 +72,7 @@
|
||||
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||
/>
|
||||
<label for="node-input-acceptStickers" style="width: auto">
|
||||
Accept stickers <code>m.sticker</code>
|
||||
Accept stickers <code style="text-transform: none;">m.sticker</code>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@@ -79,7 +82,7 @@
|
||||
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||
/>
|
||||
<label for="node-input-acceptReactions" style="width: auto">
|
||||
Accept reactions <code>m.reaction</code>
|
||||
Accept reactions <code style="text-transform: none;">m.reaction</code>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@@ -89,7 +92,17 @@
|
||||
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||
/>
|
||||
<label for="node-input-acceptFiles" style="width: auto">
|
||||
Accept files <code>m.file</code>
|
||||
Accept files <code style="text-transform: none;">m.file</code>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="node-input-acceptAudio"
|
||||
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||
/>
|
||||
<label for="node-input-acceptAudio" style="width: auto">
|
||||
Accept files <code style="text-transform: none;">m.audio</code>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@@ -99,7 +112,27 @@
|
||||
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||
/>
|
||||
<label for="node-input-acceptImages" style="width: auto">
|
||||
Accept images <code>m.image</code>
|
||||
Accept images <code style="text-transform: none;">m.image</code>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="node-input-acceptVideos"
|
||||
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||
/>
|
||||
<label for="node-input-acceptVideos" style="width: auto">
|
||||
Accept videos <code style="text-transform: none;">m.video</code>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="node-input-acceptLocations"
|
||||
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||
/>
|
||||
<label for="node-input-acceptLocations" style="width: auto">
|
||||
Accept locations <code style="text-transform: none;">m.location</code>
|
||||
</label>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
@@ -237,6 +270,38 @@
|
||||
</dl>
|
||||
</li>
|
||||
|
||||
<li><code>msg.type</code> == '<strong>m.audio</strong>'
|
||||
<dl class="message-properties">
|
||||
<dt>msg.filename <span class="property-type">string</span></dt>
|
||||
<dd>the image's parsed filename</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.mimetype <span class="property-type">string</span></dt>
|
||||
<dd>audio file mimetype (ex: audio/ogg)</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.url <span class="property-type">string</span></dt>
|
||||
<dd>the file's URL</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.mxc_url <span class="property-type">string</span></dt>
|
||||
<dd>the file's Matrix URL</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.duration <span class="property-type">integer</span></dt>
|
||||
<dd>duration of audio file in milliseconds</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.waveform <span class="property-type">array[int]</span></dt>
|
||||
<dd>waveform of the audio clip</dd>
|
||||
</dl>
|
||||
</li>
|
||||
|
||||
<li><code>msg.type</code> == '<strong>m.image</strong>'
|
||||
<dl class="message-properties">
|
||||
<dt>msg.filename <span class="property-type">string</span></dt>
|
||||
@@ -263,5 +328,12 @@
|
||||
<dd>the image's thumbnail Matrix URL</dd>
|
||||
</dl>
|
||||
</li>
|
||||
|
||||
<li><code>msg.type</code> == '<strong>m.location</strong>'
|
||||
<dl class="message-properties">
|
||||
<dt>msg.geo_uri <span class="property-type">string</span></dt>
|
||||
<dd>URI format of the geolocation</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
</script>
|
||||
+53
-1
@@ -11,16 +11,20 @@ module.exports = function(RED) {
|
||||
this.acceptStickers = n.acceptStickers;
|
||||
this.acceptReactions = n.acceptReactions;
|
||||
this.acceptFiles = n.acceptFiles;
|
||||
this.acceptAudio = n.acceptAudio;
|
||||
this.acceptImages = n.acceptImages;
|
||||
this.acceptVideos = n.acceptVideos;
|
||||
this.acceptLocations = n.acceptLocations;
|
||||
this.roomId = n.roomId;
|
||||
this.roomIds = this.roomId ? this.roomId.split(',') : [];
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
if (!node.server) {
|
||||
node.error("No configuration node");
|
||||
node.error("No configuration node", {});
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
node.server.on("disconnected", function(){
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
@@ -72,6 +76,27 @@ module.exports = function(RED) {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'm.audio':
|
||||
if(!node.acceptAudio) return;
|
||||
if(msg.encrypted) {
|
||||
msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.file.url);
|
||||
msg.mxc_url = msg.content.file.url;
|
||||
} else {
|
||||
msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.url);
|
||||
msg.mxc_url = msg.content.url;
|
||||
}
|
||||
|
||||
if('org.matrix.msc1767.file' in msg.content) {
|
||||
msg.filename = msg.content['org.matrix.msc1767.file'].name;
|
||||
msg.mimetype = msg.content['org.matrix.msc1767.file'].mimetype;
|
||||
}
|
||||
|
||||
if('org.matrix.msc1767.audio' in msg.content) {
|
||||
msg.duration = msg.content['org.matrix.msc1767.audio'].duration;
|
||||
msg.waveform = msg.content['org.matrix.msc1767.audio'].waveform;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'm.image':
|
||||
if(!node.acceptImages) return;
|
||||
msg.filename = msg.content.filename || msg.content.body;
|
||||
@@ -88,6 +113,29 @@ module.exports = function(RED) {
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'm.video':
|
||||
if(!node.acceptVideos) return;
|
||||
msg.filename = msg.content.filename || msg.content.body;
|
||||
if(msg.encrypted) {
|
||||
msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.file.url);
|
||||
msg.mxc_url = msg.content.file.url;
|
||||
msg.thumbnail_url = node.server.matrixClient.mxcUrlToHttp(msg.content.info.thumbnail_file.url);
|
||||
msg.thumbnail_mxc_url = msg.content.info.thumbnail_file.url;
|
||||
} else {
|
||||
msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.url);
|
||||
msg.mxc_url = msg.content.url;
|
||||
msg.thumbnail_url = node.server.matrixClient.mxcUrlToHttp(msg.content.info.thumbnail_url);
|
||||
msg.thumbnail_mxc_url = msg.content.info.thumbnail_url;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'm.location':
|
||||
if(!node.acceptLocations) return;
|
||||
msg.geo_uri = msg.content.geo_uri;
|
||||
msg.payload = msg.content.body;
|
||||
break;
|
||||
|
||||
case 'm.reaction':
|
||||
if(!node.acceptReactions) return;
|
||||
msg.info = msg.content["m.relates_to"].info;
|
||||
@@ -102,6 +150,10 @@ module.exports = function(RED) {
|
||||
|
||||
node.send(msg);
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-receive", MatrixReceiveMessage);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
reason: { value: null }
|
||||
},
|
||||
|
||||
+11
-5
@@ -13,6 +13,7 @@ module.exports = function(RED) {
|
||||
node.warn("No configuration node");
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
@@ -26,23 +27,24 @@ module.exports = function(RED) {
|
||||
|
||||
node.on("input", function (msg) {
|
||||
if (! node.server || ! node.server.matrixClient) {
|
||||
node.error("No matrix server selected");
|
||||
node.error("No matrix server selected", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.topic = node.roomId || msg.topic;
|
||||
if(!msg.topic) {
|
||||
node.error("Room must be specified in msg.topic or in configuration");
|
||||
node.error("Room must be specified in msg.topic or in configuration", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.userId) {
|
||||
node.error("msg.userId was not set.");
|
||||
node.error("msg.userId was not set.", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,11 +55,15 @@ module.exports = function(RED) {
|
||||
node.send([msg, null]);
|
||||
})
|
||||
.catch(function(e){
|
||||
node.error("Error trying to ban " + msg.userId + " from " + msg.topic);
|
||||
node.error("Error trying to ban " + msg.userId + " from " + msg.topic, msg);
|
||||
msg.error = e;
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-room-ban", MatrixBan);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('matrix-room-invite', {
|
||||
category: 'matrix',
|
||||
color: '#00b7ca',
|
||||
icon: "matrix.png",
|
||||
outputLabels: ["success", "error"],
|
||||
inputs: 0,
|
||||
outputs: 1,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Room Invite";
|
||||
},
|
||||
paletteLabel: 'Room Invite'
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="matrix-room-invite">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-server"><i class="fa fa-user"></i> Matrix Server Config</label>
|
||||
<input type="text" id="node-input-server">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="matrix-room-invite">
|
||||
<h3>Details</h3>
|
||||
<p>
|
||||
This node receives room invites.
|
||||
</p>
|
||||
|
||||
<h3>Outputs</h3>
|
||||
<ol class="node-ports">
|
||||
<li>Success
|
||||
<dl class="message-properties">
|
||||
<dt>msg.type <span class="property-type">string</span></dt>
|
||||
<dd>Always <code>m.room.member</code></dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.userId <span class="property-type">string</span></dt>
|
||||
<dd>ID of the user the invite is from</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.topic <span class="property-type">string</span></dt>
|
||||
<dd>The room identifier for the invite: for example, <code>!h8zld9j31:example.com</code>.</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.topicName <span class="property-type">string</span></dt>
|
||||
<dd>The invited room name.</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.event <span class="property-type">object</span></dt>
|
||||
<dd>The event object for this invite to get extra details.</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.eventId <span class="property-type">object</span></dt>
|
||||
<dd>The ID of the event for this invite.</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li>Error
|
||||
<dl class="message-properties">
|
||||
<dt>msg.error <span class="property-type">string</span></dt>
|
||||
<dd>the error that occurred.</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,36 @@
|
||||
module.exports = function(RED) {
|
||||
function MatrixRoomInvite(n) {
|
||||
RED.nodes.createNode(this, n);
|
||||
|
||||
let node = this;
|
||||
|
||||
this.name = n.name;
|
||||
this.server = RED.nodes.getNode(n.server);
|
||||
this.roomId = n.roomId;
|
||||
|
||||
if(!this.server) {
|
||||
node.error('Server must be configured on the 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.server.on("Room.invite", async function(msg) {
|
||||
node.send(msg);
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-room-invite", MatrixRoomInvite);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
reason: { value: null }
|
||||
},
|
||||
|
||||
+11
-5
@@ -13,6 +13,7 @@ module.exports = function(RED) {
|
||||
node.warn("No configuration node");
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
@@ -26,23 +27,24 @@ module.exports = function(RED) {
|
||||
|
||||
node.on("input", function (msg) {
|
||||
if (! node.server || ! node.server.matrixClient) {
|
||||
node.error("No matrix server selected");
|
||||
node.error("No matrix server selected", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.topic = node.roomId || msg.topic;
|
||||
if(!msg.topic) {
|
||||
node.error("Room must be specified in msg.topic or in configuration");
|
||||
node.error("Room must be specified in msg.topic or in configuration", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.userId) {
|
||||
node.error("msg.userId was not set.");
|
||||
node.error("msg.userId was not set.", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,11 +55,15 @@ module.exports = function(RED) {
|
||||
node.send([msg, null]);
|
||||
})
|
||||
.catch(function(e){
|
||||
node.error("Error trying to kick " + msg.userId + " from " + msg.topic);
|
||||
node.error("Error trying to kick " + msg.userId + " from " + msg.topic, msg);
|
||||
msg.error = e;
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-room-kick", MatrixKick);
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
<script type="text/html" data-template-name="matrix-room-state-events">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-server"><i class="fa fa-user"></i> Matrix Server Config</label>
|
||||
<input type="text" id="node-input-server">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-roomId"><i class="fa fa-comments"></i> Room ID</label>
|
||||
<input type="text" id="node-input-roomId" placeholder="msg.topic">
|
||||
<pre class="form-tips" id="node-input-roomId-error" style="color: #721c24;background-color: #f8d7da;border-color: #f5c6cb;margin-bottom: 12px;margin-top: 12px;display:none;"></pre>
|
||||
</div>
|
||||
<div class="form-row" style="margin-bottom:0;">
|
||||
<label><i class="fa fa-list"></i> <span data-i18n="change.label.rules"></span></label>
|
||||
</div>
|
||||
<div class="form-row node-input-rule-container-row">
|
||||
<ol id="node-input-rule-container"></ol>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
$("#node-input-roomId").on("keyup", function() {
|
||||
if($(this).val() && !$(this).val().startsWith("!")) {
|
||||
$("#node-input-roomId-error").html(`Room IDs start with exclamation point "!"<br />Example: !OGEhHVWSdvArJzumhm:matrix.org`).show();
|
||||
} else {
|
||||
$("#node-input-roomId-error").hide();
|
||||
}
|
||||
}).trigger('keyup');
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="matrix-room-state-events">
|
||||
<h3>Details</h3>
|
||||
<p>
|
||||
Set and Get a list of room state events for a given room. Allows you to set/get room name, topic, avatar, etc.
|
||||
</p>
|
||||
|
||||
<h3>Inputs</h3>
|
||||
<dl class="message-properties">
|
||||
<dt class="optional">msg.topic
|
||||
<span class="property-type">string | null</span>
|
||||
</dt>
|
||||
<dd> The room to set/get settings for.</dd>
|
||||
|
||||
<dt class="optional">dynamic
|
||||
<span class="property-type">string|object</span>
|
||||
</dt>
|
||||
<dd> You configure what room state events in the node configuration. <code style="white-space: normal;">m.room.name</code>, <code style="white-space: normal;">m.room.avatar</code>, and <code style="white-space: normal;">m.room.guest_access</code> allow you to pass a string to set their value but all other room state events will require the full content object (find this by referencing the <a href="https://spec.matrix.org/latest/client-server-api" target="_blank">Matrix Client-Server docs</a>)</dd>
|
||||
</dl>
|
||||
|
||||
<h3>Outputs</h3>
|
||||
<ol class="node-ports">
|
||||
<li>Success
|
||||
<dl class="message-properties">
|
||||
<dt>msg <span class="property-type">object</span></dt>
|
||||
<dd>Original message object with modifications based on config.</dd>
|
||||
</dl>
|
||||
<dl class="message-properties">
|
||||
<dt>msg.setter_errors <span class="property-type">undefined|object</span></dt>
|
||||
<dd>Returned if setting a room state event failed. The key of the object is the room state event type and the value is the error that occurred.</dd>
|
||||
</dl>
|
||||
<dl class="message-properties">
|
||||
<dt>msg.getter_errors <span class="property-type">undefined|object</span></dt>
|
||||
<dd>Returned if getting a room state event failed. The key of the object is the room state event type and the value is the error that occurred. Note that you will get an error if you try getting a room state event that doesn't exist (such as fetching avatar on a room that doesn't have one).</dd>
|
||||
</dl>
|
||||
<dt class="optional">dynamic
|
||||
<span class="property-type">string|object</span>
|
||||
</dt>
|
||||
<dd> You configure what room state events to output in the node configuration. <code style="white-space: normal;">m.room.name</code>, <code style="white-space: normal;">m.room.avatar</code>, and <code style="white-space: normal;">m.room.guest_access</code> will come back as strings otherwise you will get the full content object of the event (find this by referencing the <a href="https://spec.matrix.org/latest/client-server-api" target="_blank">Matrix Client-Server docs</a>)</dd>
|
||||
</li>
|
||||
</ol>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function(){
|
||||
var roomEventTypeOptions = [
|
||||
{ value: "m.room.name", label: "m.room.name"},
|
||||
{ value: "m.room.topic", label: "m.room.topic"},
|
||||
{ value: "m.room.avatar", label: "m.room.avatar"},
|
||||
{ value: "m.room.power_levels", label: "m.room.power_levels"},
|
||||
{ value: "m.room.guest_access", label: "m.room.guest_access"},
|
||||
{ value: "m.room.join_rules", label: "m.room.join_rules"},
|
||||
{ value: "m.room.canonical_alias", label: "m.room.canonical_alias"}
|
||||
];
|
||||
var defaultRules = [{
|
||||
t: "set",
|
||||
p: roomEventTypeOptions[0].value,
|
||||
to: "payload",
|
||||
tot: "msg"
|
||||
}];
|
||||
|
||||
function isInvalidProperty(v,vt) {
|
||||
if (/msg|flow|global/.test(vt)) {
|
||||
if (!RED.utils.validatePropertyExpression(v)) {
|
||||
return "Invalid property: " + v;
|
||||
}
|
||||
} else if (vt === "jsonata") {
|
||||
try{ jsonata(v); } catch(e) {
|
||||
return "Invalid expression: " + e.message;
|
||||
}
|
||||
} else if (vt === "json") {
|
||||
try{ JSON.parse(v); } catch(e) {
|
||||
return "Invalid JSON data: " + e.message;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
RED.nodes.registerType('matrix-room-state-events',{
|
||||
category: 'matrix',
|
||||
color: '#00b7ca',
|
||||
icon: "matrix.png",
|
||||
outputLabels: ["success", "error"],
|
||||
inputs:1,
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
reason: { value: null },
|
||||
rules: {
|
||||
value: defaultRules,
|
||||
validate: function(rules, opt) {
|
||||
let msg;
|
||||
const errors = []
|
||||
if (!rules || rules.length === 0) { return true }
|
||||
for (let i=0;i<rules.length;i++) {
|
||||
const opt = { label: "Rule"+' '+(i+1) }
|
||||
const r = rules[i];
|
||||
if (r.t === 'set' || r.t === 'get') {
|
||||
if ((msg = isInvalidProperty(r.p,r.pt)) !== false) {
|
||||
return msg;
|
||||
}
|
||||
if ((msg = isInvalidProperty(r.to,r.tot)) !== false) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.length ? errors : true;
|
||||
}
|
||||
},
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var set = "Set";
|
||||
var to = "to the value";
|
||||
var toValueLabel = "to the property";
|
||||
var search = this._("change.action.search");
|
||||
var replace = this._("change.action.replace");
|
||||
var regex = this._("change.label.regex");
|
||||
|
||||
function createPropertyValue(row2_1, row2_2, type, defaultType) {
|
||||
var propValInput = $('<input/>',{class:"node-input-rule-property-value",type:"text"})
|
||||
.appendTo(row2_1)
|
||||
.typedInput({
|
||||
default: defaultType || (type === 'set' ? 'str' : 'msg'),
|
||||
types: (type === 'set' ? ['msg','flow','global','str','num','bool','json','bin','date','jsonata'] : ['msg', 'flow', 'global'])
|
||||
});
|
||||
|
||||
var dcLabel = $('<label style="padding-left: 130px;"></label>').appendTo(row2_2);
|
||||
|
||||
propValInput.on("change", function(evt,type,val) {
|
||||
row2_2.toggle(type === "msg" || type === "flow" || type === "global" || type === "env");
|
||||
})
|
||||
return [propValInput];
|
||||
}
|
||||
|
||||
$('#node-input-rule-container').css('min-height','150px').css('min-width','450px').editableList({
|
||||
addItem: function(container,i,opt) {
|
||||
var rule = opt;
|
||||
if (!rule.hasOwnProperty('t')) {
|
||||
rule = {t:"set",p:roomEventTypeOptions[0].value,to:"payload",tot:"msg"};
|
||||
}
|
||||
if (rule.t === "set" && !rule.tot) {
|
||||
if (rule.to.indexOf("msg.") === 0 && !rule.tot) {
|
||||
rule.to = rule.to.substring(4);
|
||||
rule.tot = "msg";
|
||||
} else {
|
||||
rule.tot = "str";
|
||||
}
|
||||
}
|
||||
container.css({
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap'
|
||||
});
|
||||
let fragment = document.createDocumentFragment();
|
||||
var row1 = $('<div/>',{style:"display:flex; align-items: center"}).appendTo(fragment);
|
||||
var row2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(fragment);
|
||||
var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(fragment);
|
||||
var row4 = $('<div/>',{style:"display:flex;margin-top:8px;align-items: baseline"}).appendTo(fragment);
|
||||
|
||||
var selectField = $('<select/>',{class:"node-input-rule-type",style:"width:110px; margin-right:10px;"}).appendTo(row1);
|
||||
var selectOptions = [
|
||||
{v:"set",l:"Set"},
|
||||
{v:"get",l:"Get"}
|
||||
];
|
||||
for (var x=0; x<selectOptions.length; x++) {
|
||||
selectField.append($("<option></option>").val(selectOptions[x].v).text(selectOptions[x].l));
|
||||
}
|
||||
|
||||
var propertyName = $('<input/>',{class:"node-input-rule-property-name",type:"text"})
|
||||
.appendTo(row1)
|
||||
.autoComplete({
|
||||
minLength:0,
|
||||
search: function(val) {
|
||||
if(!val) {
|
||||
return roomEventTypeOptions.sort(function(A,B){return A.i-B.i});
|
||||
}
|
||||
var matches = [];
|
||||
roomEventTypeOptions.map((x) => x.value).forEach(v => {
|
||||
var i = v.toLowerCase().indexOf(val.toLowerCase());
|
||||
if (i > -1) {
|
||||
matches.push({
|
||||
value: v,
|
||||
label: v,
|
||||
i: i
|
||||
})
|
||||
}
|
||||
});
|
||||
matches.sort(function(A,B){return A.i-B.i})
|
||||
return matches
|
||||
}
|
||||
})
|
||||
.on('focus', function(evt){
|
||||
// following is a fix so autocomplete will show list on focus
|
||||
if(!evt.isTrigger) {
|
||||
evt.stopPropagation();
|
||||
$(this).trigger("keyup.red-ui-autoComplete");
|
||||
}
|
||||
}).on("keyup", function(evt) {
|
||||
// following allows autocomplete to display even when backspace/delete is used
|
||||
if (evt.keyCode === 8 || evt.keyCode === 46) {
|
||||
evt.stopPropagation();
|
||||
$(this).trigger("keyup.red-ui-autoComplete");
|
||||
}
|
||||
});
|
||||
|
||||
var row2_1 = $('<div/>', {style:"display:flex;align-items: baseline"}).appendTo(row2);
|
||||
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
|
||||
.text(toValueLabel)
|
||||
.appendTo(row2_1);
|
||||
|
||||
var row2_2 = $('<div/>', {style:"margin-top: 4px;"}).appendTo(row2);
|
||||
|
||||
var row3_1 = $('<div/>', {style:"display:flex;align-items: baseline"}).appendTo(row3);
|
||||
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
|
||||
.text(search)
|
||||
.appendTo(row3_1);
|
||||
|
||||
var row3_2 = $('<div/>',{style:"display:flex;margin-top:8px;align-items: baseline"}).appendTo(row3);
|
||||
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
|
||||
.text(replace)
|
||||
.appendTo(row3_2);
|
||||
|
||||
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
|
||||
.text(to)
|
||||
.appendTo(row4);
|
||||
|
||||
let propertyValue = null;
|
||||
let fromValue = null;
|
||||
let toValue = null;
|
||||
|
||||
selectField.on("change", function() {
|
||||
var type = $(this).val();
|
||||
if (propertyValue) {
|
||||
propertyValue.typedInput('hide');
|
||||
}
|
||||
if (fromValue) {
|
||||
fromValue.typedInput('hide');
|
||||
}
|
||||
if (toValue) {
|
||||
toValue.typedInput('hide');
|
||||
}
|
||||
|
||||
if (!propertyValue) {
|
||||
var parts = createPropertyValue(row2_1, row2_2, type);
|
||||
propertyValue = parts[0];
|
||||
} else {
|
||||
propertyValue.typedInput('types', (type === 'set' ? ['msg','flow','global','str','num','bool','json','bin','date','jsonata','env'] : ['msg', 'flow', 'global']));
|
||||
}
|
||||
|
||||
propertyValue.typedInput('show');
|
||||
row2.show();
|
||||
row3.hide();
|
||||
row4.hide();
|
||||
});
|
||||
|
||||
selectField.val(rule.t);
|
||||
propertyName.val(rule.p);
|
||||
if (rule.t === "set" || rule.t === "get") {
|
||||
var parts = createPropertyValue(row2_1, row2_2, rule.t, rule.tot);
|
||||
propertyValue = parts[0];
|
||||
propertyValue.typedInput('value',rule.to);
|
||||
}
|
||||
selectField.change();
|
||||
container[0].appendChild(fragment);
|
||||
},
|
||||
removable: true,
|
||||
sortable: true
|
||||
});
|
||||
|
||||
for (var i=0; i<this.rules.length; i++) {
|
||||
var rule = this.rules[i];
|
||||
$("#node-input-rule-container").editableList('addItem',rule);
|
||||
}
|
||||
},
|
||||
oneditsave: function() {
|
||||
var rules = $("#node-input-rule-container").editableList('items');
|
||||
var node = this;
|
||||
node.rules= [];
|
||||
rules.each(function(i) {
|
||||
var rule = $(this);
|
||||
var type = rule.find(".node-input-rule-type").val();
|
||||
var r = {
|
||||
t:type,
|
||||
p:rule.find(".node-input-rule-property-name").val(),
|
||||
to:rule.find(".node-input-rule-property-value").typedInput('value'),
|
||||
tot:rule.find(".node-input-rule-property-value").typedInput('type')
|
||||
};
|
||||
node.rules.push(r);
|
||||
});
|
||||
},
|
||||
oneditresize: function(size) {
|
||||
var rows = $("#dialog-form>div:not(.node-input-rule-container-row)");
|
||||
var height = size.height;
|
||||
for (var i=0; i<rows.length; i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-input-rule-container-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
height += 16;
|
||||
$("#node-input-rule-container").editableList('height',height);
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Room State Events";
|
||||
},
|
||||
paletteLabel: 'Room State Events'
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@@ -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);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: "" }
|
||||
},
|
||||
label: function() {
|
||||
|
||||
@@ -13,6 +13,7 @@ module.exports = function(RED) {
|
||||
node.warn("No configuration node");
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
@@ -31,13 +32,14 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
let roomId = node.roomId || msg.topic;
|
||||
if(!roomId) {
|
||||
node.error("msg.topic is required. Specify in the input or configure the room ID on the node.");
|
||||
node.error("msg.topic is required. Specify in the input or configure the room ID on the node.", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -65,6 +67,10 @@ module.exports = function(RED) {
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-room-users", MatrixRoomUsers);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
contentType: { value: null }
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ module.exports = function(RED) {
|
||||
node.warn("No configuration node");
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
@@ -31,8 +32,9 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.topic = node.roomId || msg.topic;
|
||||
@@ -57,7 +59,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!msg.payload) {
|
||||
node.error('msg.payload is required');
|
||||
node.error('msg.payload is required', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -94,6 +96,10 @@ module.exports = function(RED) {
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-send-file", MatrixSendFile);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
contentType: { value: null }
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ module.exports = function(RED) {
|
||||
node.warn("No configuration node");
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
@@ -31,8 +32,9 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.topic = node.roomId || msg.topic;
|
||||
@@ -57,7 +59,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!msg.payload) {
|
||||
node.error('msg.payload is required');
|
||||
node.error('msg.payload is required', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -98,6 +100,10 @@ module.exports = function(RED) {
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-send-image", MatrixSendImage);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
message: { value: null },
|
||||
messageType: { value: 'm.text' },
|
||||
|
||||
@@ -54,6 +54,7 @@ module.exports = function(RED) {
|
||||
node.warn("No configuration node");
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
@@ -71,7 +72,7 @@ module.exports = function(RED) {
|
||||
|
||||
if(msgType === 'msg.type') {
|
||||
if(!msg.type) {
|
||||
node.error("msg.type type is set to be passed in via msg.type but was not defined");
|
||||
node.error("msg.type type is set to be passed in via msg.type but was not defined", msg);
|
||||
return;
|
||||
}
|
||||
msgType = msg.type;
|
||||
@@ -79,7 +80,7 @@ module.exports = function(RED) {
|
||||
|
||||
if(msgFormat === 'msg.format') {
|
||||
if(!msg.format) {
|
||||
node.error("Message format is set to be passed in via msg.format but was not defined");
|
||||
node.error("Message format is set to be passed in via msg.format but was not defined", msg);
|
||||
return;
|
||||
}
|
||||
msgFormat = msg.format;
|
||||
@@ -91,7 +92,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
@@ -104,7 +105,7 @@ module.exports = function(RED) {
|
||||
|
||||
let payload = n.message || msg.payload;
|
||||
if(!payload) {
|
||||
node.error('msg.payload must be defined or the message configured on the node.');
|
||||
node.error('msg.payload must be defined or the message configured on the node.', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -152,6 +153,10 @@ module.exports = function(RED) {
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-send-message", MatrixSendImage);
|
||||
}
|
||||
@@ -162,7 +162,7 @@
|
||||
$("#matrix-access-token-loader").show();
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/matrix-chat/login',
|
||||
url: 'matrix-chat/login',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
'userId': userId,
|
||||
@@ -214,4 +214,4 @@
|
||||
<p>If this is a server you do not administrate/have access to follow these instructions:</p>
|
||||
<ol><li>In a private/incognito browser window, open Element.</li><li>Log in to the account you want to get the access token for, such as the bot's account. <strong>Do not setup key storage</strong>.</li><li>Click on the bot's name in the top left corner then "Settings".</li><li>(Optional) Set your bot's display name and avatar.</li><li>Click the "Help & About" tab (left side of the dialog).</li><li>Scroll to the bottom and click the <code><click to reveal></code> part of <code>Access Token: <click to reveal></code>.</li><li>Copy your access token to a safe place, like the bot's configuration file.</li><li><strong>Do not log out.</strong> Instead, just close the window. If you used a private browsing session, you should be able to still use Element for your own account. Logging out deletes the access token from the server, making the bot unable to use it.</li></ol>
|
||||
</div>
|
||||
</script>
|
||||
</script>
|
||||
|
||||
+73
-28
@@ -4,11 +4,30 @@ const sdk = require("matrix-js-sdk");
|
||||
const { resolve } = require('path');
|
||||
const { LocalStorage } = require('node-localstorage');
|
||||
const { LocalStorageCryptoStore } = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
|
||||
const {RoomEvent, RoomMemberEvent, HttpApiEvent, ClientEvent} = require("matrix-js-sdk");
|
||||
const {deriveKey} = require("matrix-js-sdk/lib/crypto/key_passphrase");
|
||||
const {encryptAES} = require("matrix-js-sdk/lib/crypto/aes");
|
||||
const {RoomEvent, RoomMemberEvent, HttpApiEvent, ClientEvent, MemoryStore} = require("matrix-js-sdk");
|
||||
const request = require("request");
|
||||
require("abort-controller/polyfill"); // polyfill abort-controller if we don't have it
|
||||
if (!globalThis.fetch) {
|
||||
// polyfill fetch if we don't have it
|
||||
if (!globalThis.fetch) {
|
||||
import('node-fetch').then(({ default: fetch, Headers, Request, Response }) => {
|
||||
Object.assign(globalThis, { fetch, Headers, Request, Response })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function(RED) {
|
||||
// disable logging if set to "off"
|
||||
let loggingSettings = RED.settings.get('logging');
|
||||
if(
|
||||
typeof loggingSettings.console !== 'undefined' &&
|
||||
typeof loggingSettings.console.level !== 'undefined' &&
|
||||
['info','debug','trace'].indexOf(loggingSettings.console.level.toLowerCase()) >= 0
|
||||
) {
|
||||
const { logger } = require('matrix-js-sdk/lib/logger');
|
||||
logger.disableAll();
|
||||
}
|
||||
|
||||
function MatrixFolderNameFromUserId(name) {
|
||||
return name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
||||
}
|
||||
@@ -25,6 +44,7 @@ module.exports = function(RED) {
|
||||
this.credentials = {};
|
||||
}
|
||||
|
||||
this.users = {};
|
||||
this.connected = null;
|
||||
this.name = n.name;
|
||||
this.userId = this.credentials.userId;
|
||||
@@ -36,6 +56,14 @@ module.exports = function(RED) {
|
||||
|
||||
this.globalAccess = n.global;
|
||||
this.initializedAt = new Date();
|
||||
|
||||
// Keep track of all consumers of this node to be able to catch errors
|
||||
node.register = function(consumerNode) {
|
||||
node.users[consumerNode.id] = consumerNode;
|
||||
};
|
||||
node.deregister = function(consumerNode) {
|
||||
delete node.users[consumerNode.id];
|
||||
};
|
||||
|
||||
if(!this.userId) {
|
||||
node.log("Matrix connection failed: missing user ID in configuration.");
|
||||
@@ -49,9 +77,9 @@ module.exports = function(RED) {
|
||||
let retryStartTimeout = null;
|
||||
|
||||
if(!this.credentials.accessToken) {
|
||||
node.error("Matrix connection failed: missing access token in configuration.");
|
||||
node.error("Matrix connection failed: missing access token in configuration.", {});
|
||||
} else if(!this.url) {
|
||||
node.error("Matrix connection failed: missing server URL in configuration.");
|
||||
node.error("Matrix connection failed: missing server URL in configuration.", {});
|
||||
} else {
|
||||
node.setConnected = async function(connected, cb) {
|
||||
if (node.connected !== connected) {
|
||||
@@ -69,7 +97,7 @@ module.exports = function(RED) {
|
||||
device_id = this.matrixClient.getDeviceId();
|
||||
|
||||
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 {
|
||||
if(!stored_device_id || stored_device_id !== device_id) {
|
||||
node.log(`Saving Device ID (old:${stored_device_id} new:${device_id})`);
|
||||
@@ -88,13 +116,13 @@ module.exports = function(RED) {
|
||||
}).then(
|
||||
function(response) {},
|
||||
function(error) {
|
||||
node.error("Failed to set device label: " + error);
|
||||
node.error("Failed to set device label: " + error, {});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
function(error) {
|
||||
node.error("Failed to fetch device: " + error);
|
||||
node.error("Failed to fetch device: " + error, {});
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -118,10 +146,13 @@ module.exports = function(RED) {
|
||||
node.matrixClient = sdk.createClient({
|
||||
baseUrl: this.url,
|
||||
accessToken: this.credentials.accessToken,
|
||||
sessionStore: new sdk.WebStorageSessionStore(localStorage),
|
||||
cryptoStore: new LocalStorageCryptoStore(localStorage),
|
||||
store: new MemoryStore({
|
||||
localStorage: localStorage,
|
||||
}),
|
||||
userId: this.userId,
|
||||
deviceId: (this.deviceId || getStoredDeviceId(localStorage)) || undefined,
|
||||
request
|
||||
// verificationMethods: ["m.sas.v1"]
|
||||
});
|
||||
|
||||
@@ -167,7 +198,7 @@ module.exports = function(RED) {
|
||||
try {
|
||||
await node.matrixClient.decryptEventIfNeeded(event);
|
||||
} catch (error) {
|
||||
node.error(error);
|
||||
node.error(error, {});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -232,16 +263,29 @@ module.exports = function(RED) {
|
||||
// handle auto-joining rooms
|
||||
|
||||
node.matrixClient.on(RoomMemberEvent.Membership, async function(event, member) {
|
||||
if(node.initializedAt > event.getDate()) {
|
||||
return; // skip events that occurred before our client initialized
|
||||
}
|
||||
|
||||
if (member.membership === "invite" && member.userId === node.userId) {
|
||||
node.log("Got invite to join room " + member.roomId);
|
||||
if(node.autoAcceptRoomInvites) {
|
||||
node.matrixClient.joinRoom(member.roomId).then(function() {
|
||||
node.log("Automatically accepted invitation to join room " + member.roomId);
|
||||
}).catch(function(e) {
|
||||
node.warn("Cannot join room (could be from being kicked/banned) " + member.roomId + ": " + e);
|
||||
});
|
||||
} else {
|
||||
node.log("Got invite to join room " + member.roomId);
|
||||
}
|
||||
|
||||
let room = node.matrixClient.getRoom(event.getRoomId());
|
||||
node.emit("Room.invite", {
|
||||
type : 'm.room.member',
|
||||
userId : event.getSender(),
|
||||
topic : event.getRoomId(),
|
||||
topicName : (room ? room.name : null) || null,
|
||||
event : event,
|
||||
eventId : event.getId(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -256,7 +300,7 @@ module.exports = function(RED) {
|
||||
} else if(prevState === null && state === "ERROR") {
|
||||
// Occurs when the initial sync failed first time.
|
||||
node.setConnected(false, function(){
|
||||
node.error("Failed to connect to Matrix server");
|
||||
node.error("Failed to connect to Matrix server", {});
|
||||
});
|
||||
} else if(prevState === "ERROR" && state === "PREPARED") {
|
||||
// Occurs when the initial sync succeeds
|
||||
@@ -273,18 +317,18 @@ module.exports = function(RED) {
|
||||
} else if(prevState === "SYNCING" && state === "RECONNECTING") {
|
||||
// Occurs when the live update fails.
|
||||
node.setConnected(false, function(){
|
||||
node.error("Connection to Matrix server lost");
|
||||
node.error("Connection to Matrix server lost", {});
|
||||
});
|
||||
} else if(prevState === "RECONNECTING" && state === "RECONNECTING") {
|
||||
// Can occur if the update calls continue to fail,
|
||||
// but the keepalive calls (to /versions) succeed.
|
||||
node.setConnected(false, function(){
|
||||
node.error("Connection to Matrix server lost");
|
||||
node.error("Connection to Matrix server lost", {});
|
||||
});
|
||||
} else if(prevState === "RECONNECTING" && state === "ERROR") {
|
||||
// Occurs when the keepalive call also fails
|
||||
node.setConnected(false, function(){
|
||||
node.error("Connection to Matrix server lost");
|
||||
node.error("Connection to Matrix server lost", {});
|
||||
});
|
||||
} else if(prevState === "ERROR" && state === "SYNCING") {
|
||||
// Occurs when the client has performed a
|
||||
@@ -296,7 +340,7 @@ module.exports = function(RED) {
|
||||
// Occurs when the client has failed to
|
||||
// keepalive for a second time or more.
|
||||
node.setConnected(false, function(){
|
||||
node.error("Connection to Matrix server lost");
|
||||
node.error("Connection to Matrix server lost", {});
|
||||
});
|
||||
} else if(prevState === "SYNCING" && state === "SYNCING") {
|
||||
// Occurs when the client has performed a live update.
|
||||
@@ -308,7 +352,7 @@ module.exports = function(RED) {
|
||||
// Occurs once the client has stopped syncing or
|
||||
// trying to sync after stopClient has been called.
|
||||
node.setConnected(false, function(){
|
||||
node.error("Connection to Matrix server lost");
|
||||
node.error("Connection to Matrix server lost", {});
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -326,7 +370,7 @@ module.exports = function(RED) {
|
||||
// httpStatus: 401
|
||||
// }
|
||||
|
||||
node.error("Authentication failure: " + errorObj);
|
||||
node.error("Authentication failure: " + errorObj, {});
|
||||
stopClient();
|
||||
});
|
||||
|
||||
@@ -335,14 +379,14 @@ 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({
|
||||
initialSyncLimit: 8
|
||||
});
|
||||
} catch(error) {
|
||||
node.error(error);
|
||||
node.error(error, {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,7 +407,7 @@ module.exports = function(RED) {
|
||||
.then(
|
||||
function(data) {
|
||||
if((typeof data['device_id'] === undefined || !data['device_id']) && !node.deviceId && !getStoredDeviceId(localStorage)) {
|
||||
node.error("/whoami request did not return device_id. You will need to manually set one in your configuration because this cannot be automatically fetched.");
|
||||
node.error("/whoami request did not return device_id. You will need to manually set one in your configuration because this cannot be automatically fetched.", {});
|
||||
}
|
||||
if('device_id' in data && data['device_id'] && !node.deviceId) {
|
||||
// if we have no device_id configured lets use the one
|
||||
@@ -373,7 +417,7 @@ module.exports = function(RED) {
|
||||
|
||||
// make sure our userId matches the access token's
|
||||
if(data['user_id'].toLowerCase() !== node.userId.toLowerCase()) {
|
||||
node.error(`User ID provided is ${node.userId} but token belongs to ${data['user_id']}`);
|
||||
node.error(`User ID provided is ${node.userId} but token belongs to ${data['user_id']}`, {});
|
||||
return;
|
||||
}
|
||||
run().catch((error) => node.error(error));
|
||||
@@ -382,7 +426,7 @@ module.exports = function(RED) {
|
||||
// if the error isn't authentication related retry in a little bit
|
||||
if(err.code !== "M_UNKNOWN_TOKEN") {
|
||||
retryStartTimeout = setTimeout(checkAuthTokenThenStart, 15000);
|
||||
node.error("Auth check failed: " + err);
|
||||
node.error("Auth check failed: " + err, {});
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -413,7 +457,8 @@ module.exports = function(RED) {
|
||||
const matrixClient = sdk.createClient({
|
||||
baseUrl: baseUrl,
|
||||
deviceId: deviceId,
|
||||
localTimeoutMs: '30000'
|
||||
localTimeoutMs: '30000',
|
||||
request
|
||||
});
|
||||
|
||||
matrixClient.login(
|
||||
@@ -444,7 +489,7 @@ module.exports = function(RED) {
|
||||
let oldStorageDir = './matrix-local-storage',
|
||||
oldStorageDir2 = './matrix-client-storage';
|
||||
|
||||
// if the old storage location exists lets move it to it's new location
|
||||
// if the old storage location exists lets move it to the new location
|
||||
if(fs.pathExistsSync(oldStorageDir)){
|
||||
RED.nodes.eachNode(function(n){
|
||||
try {
|
||||
@@ -457,7 +502,7 @@ module.exports = function(RED) {
|
||||
fs.copySync(oldStorageDir, dir);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
node.error(err, {});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -466,7 +511,7 @@ module.exports = function(RED) {
|
||||
fs.renameSync(oldStorageDir, oldStorageDir + "-backup");
|
||||
}
|
||||
|
||||
if(RED.settings.userDir !== resolve('./')) {
|
||||
if(RED.settings.userDir !== resolve('./') && resolve(oldStorageDir2) !== resolve(storageDir)) {
|
||||
// user directory does not match running directory
|
||||
// check if we stored stuff in wrong directory and move it
|
||||
if(fs.pathExistsSync(oldStorageDir2)){
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Synapse Add/Edit User";
|
||||
|
||||
@@ -8,9 +8,10 @@ module.exports = function(RED) {
|
||||
this.server = RED.nodes.getNode(n.server);
|
||||
|
||||
if(!this.server) {
|
||||
node.error('Server must be configured on the node.');
|
||||
node.error('Server must be configured on the node.', {});
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
this.encodeUri = function(pathTemplate, variables) {
|
||||
for (const key in variables) {
|
||||
@@ -41,18 +42,18 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.userId) {
|
||||
node.error("msg.userId must be set to edit/create a user (ex: @user:server.com)");
|
||||
node.error("msg.userId must be set to edit/create a user (ex: @user:server.com)", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
node.server.matrixClient.http
|
||||
.authedRequest(
|
||||
undefined,
|
||||
'PUT',
|
||||
node.encodeUri(
|
||||
"/_synapse/admin/v2/users/$userId",
|
||||
@@ -62,13 +63,17 @@ module.exports = function(RED) {
|
||||
msg.payload,
|
||||
{ prefix: '' }
|
||||
).then(function(e){
|
||||
msg.payload = e;
|
||||
node.send([msg, null]);
|
||||
}).catch(function(e){
|
||||
node.warn("Error creating/editing user " + e);
|
||||
msg.error = e;
|
||||
node.send([null, msg]);
|
||||
});
|
||||
msg.payload = e;
|
||||
node.send([msg, null]);
|
||||
}).catch(function(e){
|
||||
node.warn("Error creating/editing user " + e);
|
||||
msg.error = e;
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-synapse-create-edit-user", MatrixSynapseCreateEditUser);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Synapse Deactivate User";
|
||||
|
||||
@@ -8,9 +8,10 @@ module.exports = function(RED) {
|
||||
this.server = RED.nodes.getNode(n.server);
|
||||
|
||||
if(!this.server) {
|
||||
node.error('Server must be configured on the node.');
|
||||
node.error('Server must be configured on the node.', {});
|
||||
return;
|
||||
}
|
||||
node.server.register(node);
|
||||
|
||||
this.encodeUri = function(pathTemplate, variables) {
|
||||
for (const key in variables) {
|
||||
@@ -41,12 +42,13 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.userId) {
|
||||
node.error("msg.userId must be set to edit/create a user (ex: @user:server.com)");
|
||||
node.error("msg.userId must be set to edit/create a user (ex: @user:server.com)", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -56,7 +58,6 @@ module.exports = function(RED) {
|
||||
);
|
||||
node.server.matrixClient.http
|
||||
.authedRequest(
|
||||
undefined,
|
||||
'POST',
|
||||
path,
|
||||
undefined,
|
||||
@@ -71,6 +72,10 @@ module.exports = function(RED) {
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-synapse-deactivate-user", MatrixSynapseDeactivateUser);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs: 2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
},
|
||||
label: function() {
|
||||
|
||||
@@ -9,10 +9,9 @@ module.exports = function(RED) {
|
||||
this.roomId = n.roomId;
|
||||
|
||||
if(!this.server) {
|
||||
node.error('Server must be configured on the node.');
|
||||
node.error('Server must be configured on the node.', {});
|
||||
return;
|
||||
}
|
||||
|
||||
this.encodeUri = function(pathTemplate, variables) {
|
||||
for (const key in variables) {
|
||||
if (!variables.hasOwnProperty(key)) {
|
||||
@@ -42,25 +41,25 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.topic = node.roomId || msg.topic;
|
||||
if(!msg.topic) {
|
||||
node.error("room must be defined in either msg.topic or in node config");
|
||||
node.error("room must be defined in either msg.topic or in node config", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.userId) {
|
||||
node.error("msg.userId is required to set user into a room");
|
||||
node.error("msg.userId is required to set user into a room", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// we need the status code, so set onlydata to false for this request
|
||||
node.server.matrixClient.http
|
||||
.authedRequest(
|
||||
undefined,
|
||||
'POST',
|
||||
node.encodeUri(
|
||||
"/_synapse/admin/v1/join/$room_id_or_alias",
|
||||
@@ -78,6 +77,10 @@ module.exports = function(RED) {
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-synapse-join-room", MatrixJoinRoom);
|
||||
}
|
||||
@@ -12,12 +12,12 @@ module.exports = function(RED) {
|
||||
this.sharedSecret = this.credentials.sharedSecret;
|
||||
|
||||
if(!this.server) {
|
||||
node.error('Server URL must be configured on the node.');
|
||||
node.error('Server URL must be configured on the node.', {});
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.sharedSecret) {
|
||||
node.error('Shared registration secret must be configured on the node.');
|
||||
node.error('Shared registration secret must be configured on the node.', {});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -25,12 +25,12 @@ module.exports = function(RED) {
|
||||
const { got } = await import('got');
|
||||
|
||||
if(!msg.payload.username) {
|
||||
node.error("msg.payload.username is required");
|
||||
node.error("msg.payload.username is required", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.payload.password) {
|
||||
node.error("msg.payload.password is required");
|
||||
node.error("msg.payload.password is required", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ module.exports = function(RED) {
|
||||
|
||||
var nonce = response.body.nonce;
|
||||
if(!nonce) {
|
||||
node.error('Could not get nonce from /_synapse/admin/v1/register');
|
||||
node.error('Could not get nonce from /_synapse/admin/v1/register', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -96,6 +96,10 @@ module.exports = function(RED) {
|
||||
node.send(msg);
|
||||
})();
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-synapse-register", MatrixSynapseRegister, {
|
||||
credentials: {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" }
|
||||
server: { type: "matrix-server-config" }
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Synapse User List";
|
||||
|
||||
@@ -12,6 +12,8 @@ module.exports = function(RED) {
|
||||
return;
|
||||
}
|
||||
|
||||
node.server.register(node);
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
node.server.on("disconnected", function(){
|
||||
@@ -29,8 +31,9 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
let queryParams = {
|
||||
@@ -48,7 +51,6 @@ module.exports = function(RED) {
|
||||
|
||||
node.server.matrixClient.http
|
||||
.authedRequest(
|
||||
undefined,
|
||||
'GET',
|
||||
"/_synapse/admin/v2/users",
|
||||
queryParams,
|
||||
@@ -63,6 +65,10 @@ module.exports = function(RED) {
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-synapse-users", MatrixSynapseUsers);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
server: { type: "matrix-server-config" },
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "WhoIs User";
|
||||
|
||||
@@ -8,10 +8,12 @@ module.exports = function(RED) {
|
||||
this.server = RED.nodes.getNode(n.server);
|
||||
|
||||
if(!this.server) {
|
||||
node.error('Server must be configured on the node.');
|
||||
node.error('Server must be configured on the node.', {});
|
||||
return;
|
||||
}
|
||||
|
||||
node.server.register(node);
|
||||
|
||||
this.encodeUri = function(pathTemplate, variables) {
|
||||
for (const key in variables) {
|
||||
if (!variables.hasOwnProperty(key)) {
|
||||
@@ -41,19 +43,19 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.error("Matrix server connection is currently closed", msg);
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.userId) {
|
||||
node.error("msg.userId must be set to get user whois data");
|
||||
node.error("msg.userId must be set to get user whois data", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// we need the status code, so set onlydata to false for this request
|
||||
node.server.matrixClient.http
|
||||
.authedRequest(
|
||||
undefined,
|
||||
'GET',
|
||||
node.encodeUri(
|
||||
"/_matrix/client/r0/admin/whois/$userId",
|
||||
@@ -71,6 +73,10 @@ module.exports = function(RED) {
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.server.deregister(node);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-whois-user", MatrixWhoIsUser);
|
||||
}
|
||||
Reference in New Issue
Block a user