Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce8be4a30f | ||
|
|
3e808cabec | ||
|
|
2fdc7482ce | ||
|
|
c7f9d56df2 | ||
|
|
3c042ae47d | ||
|
|
0a34870fa3 | ||
|
|
768a1c8ce0 | ||
|
|
22dd9b4ca3 | ||
|
|
462f9670c2 | ||
|
|
e4b01c40c2 | ||
|
|
908d60835d | ||
|
|
4c17a21008 | ||
|
|
bd4f6ea486 | ||
|
|
5ef0b6a11f | ||
|
|
97f27e61c6 | ||
|
|
7bdadc0fe9 | ||
|
|
5f129560aa | ||
|
|
00bc14e1c7 | ||
|
|
9f41b67174 | ||
|
|
4e93b7253e | ||
|
|
ecb4427217 | ||
|
|
3e70369cae | ||
|
|
2ce80e2906 | ||
|
|
0c657caf5f | ||
|
|
848fd0ec9d | ||
|
|
fef40f4ea9 | ||
|
|
595fbca3df | ||
|
|
487f17a439 | ||
|
|
68cb5a026e | ||
|
|
d01733c647 | ||
|
|
ed146e98d8 | ||
|
|
3dc6363a88 | ||
|
|
380e548425 | ||
|
|
27dd4d81a2 | ||
|
|
de3c58044f | ||
|
|
db1901ed1e | ||
|
|
ac68060825 | ||
|
|
d304cb95f6 | ||
|
|
85764c08d3 | ||
|
|
050be29d64 | ||
|
|
33823dea25 | ||
|
|
d826c58b51 |
BIN
@matrix-org_olm-3.2.8.tgz
Normal file
BIN
@matrix-org_olm-3.2.8.tgz
Normal file
Binary file not shown.
10
README.md
10
README.md
@@ -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,9 @@ 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)
|
||||
- [Respond to "joinroom <room_id_or_alias>" by joining mentioned room](#respond-to-joinroom-room_id_or_alias-by-joining-mentioned-room)
|
||||
@@ -52,7 +55,7 @@ Allows an administrator to create or modify a user account with a specified `msg
|
||||
|
||||
[View JSON](custom-redact-function-node.json)
|
||||
|
||||
If we do not have a node for something you want to do (such as redacting events/messages) you can do this manually with a function node.
|
||||
If we do not have a node for something you want to do you can do this manually with a function node. We now have a node for removing events but this is still a good example.
|
||||
|
||||
**Note:** You should make sure to catch any errors in your function node otherwise you could cause Node-RED to crash.
|
||||
|
||||
@@ -60,6 +63,8 @@ To view what sort of functions you have access to check out the `client.ts` file
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### Respond to "ping" with "pong"
|
||||
|
||||
[View JSON](respond-ping-pong.json)
|
||||
@@ -110,6 +115,36 @@ 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)
|
||||
|
||||
Any messages containing "delete" will try to be removed by the client.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### Respond to "users" with full list of server users
|
||||
|
||||
[View JSON](respond-users-list.json)
|
||||
|
||||
94
examples/accept-room-invites.json
Normal file
94
examples/accept-room-invites.json
Normal file
@@ -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
|
||||
}
|
||||
]
|
||||
BIN
examples/accept-room-invites.png
Normal file
BIN
examples/accept-room-invites.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
73
examples/delete-event.json
Normal file
73
examples/delete-event.json
Normal file
@@ -0,0 +1,73 @@
|
||||
[
|
||||
{
|
||||
"id": "fed9197df27197a4",
|
||||
"type": "matrix-receive",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "",
|
||||
"server": null,
|
||||
"roomId": "",
|
||||
"acceptText": true,
|
||||
"acceptEmotes": true,
|
||||
"acceptStickers": true,
|
||||
"acceptReactions": true,
|
||||
"acceptFiles": true,
|
||||
"acceptImages": true,
|
||||
"x": 340,
|
||||
"y": 1560,
|
||||
"wires": [
|
||||
[
|
||||
"b289bb4fed9fa166"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "b289bb4fed9fa166",
|
||||
"type": "switch",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "",
|
||||
"property": "payload",
|
||||
"propertyType": "msg",
|
||||
"rules": [
|
||||
{
|
||||
"t": "cont",
|
||||
"v": "delete",
|
||||
"vt": "str"
|
||||
}
|
||||
],
|
||||
"checkall": "true",
|
||||
"repair": false,
|
||||
"outputs": 1,
|
||||
"x": 490,
|
||||
"y": 1560,
|
||||
"wires": [
|
||||
[
|
||||
"48766b632ab2e6a1"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "48766b632ab2e6a1",
|
||||
"type": "matrix-delete-event",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "",
|
||||
"server": null,
|
||||
"roomId": "",
|
||||
"reason": "Requested deletion",
|
||||
"x": 630,
|
||||
"y": 1560,
|
||||
"wires": [
|
||||
[],
|
||||
[]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "11f9cbbed7b95c83",
|
||||
"type": "comment",
|
||||
"z": "f025a8b9fbd1b054",
|
||||
"name": "Delete messages containing \"delete\"",
|
||||
"info": "",
|
||||
"x": 480,
|
||||
"y": 1520,
|
||||
"wires": []
|
||||
}
|
||||
]
|
||||
BIN
examples/delete-event.png
Normal file
BIN
examples/delete-event.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
103
examples/leave-room-bye.json
Normal file
103
examples/leave-room-bye.json
Normal file
@@ -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
|
||||
}
|
||||
]
|
||||
BIN
examples/leave-room-bye.png
Normal file
BIN
examples/leave-room-bye.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
13924
package-lock.json
generated
13924
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@@ -1,14 +1,17 @@
|
||||
{
|
||||
"name": "node-red-contrib-matrix-chat",
|
||||
"version": "0.4.6",
|
||||
"version": "0.7.0",
|
||||
"description": "Matrix chat server client for Node-RED",
|
||||
"dependencies": {
|
||||
"fs-extra": "^10.0.0",
|
||||
"got": "^12.0.1",
|
||||
"abort-controller": "^3.0.0",
|
||||
"fs-extra": "^11.1.0",
|
||||
"got": "^12.0.2",
|
||||
"isomorphic-webcrypto": "^2.3.8",
|
||||
"matrix-js-sdk": "^15.5.0",
|
||||
"matrix-js-sdk": "^22.0.0",
|
||||
"node-fetch": "^3.3.0",
|
||||
"node-localstorage": "^2.2.1",
|
||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
||||
"olm": "https://gitlab.matrix.org/matrix-org/olm/-/package_files/472/download",
|
||||
"request": "^2.88.2",
|
||||
"utf8": "^3.0.0"
|
||||
},
|
||||
"node-red": {
|
||||
@@ -17,12 +20,15 @@
|
||||
"matrix-server-config": "src/matrix-server-config.js",
|
||||
"matrix-receive": "src/matrix-receive.js",
|
||||
"matrix-send-message": "src/matrix-send-message.js",
|
||||
"matrix-delete-event": "src/matrix-delete-event.js",
|
||||
"matrix-send-file": "src/matrix-send-file.js",
|
||||
"matrix-send-image": "src/matrix-send-image.js",
|
||||
"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",
|
||||
@@ -41,9 +47,9 @@
|
||||
"keywords": [
|
||||
"node-red",
|
||||
"matrix",
|
||||
"support",
|
||||
"bot",
|
||||
"chat"
|
||||
"chat",
|
||||
"chatbot",
|
||||
"federated"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-create-room">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-decrypt-file">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="matrix-decrypt-file">
|
||||
<h3>Details</h3>
|
||||
<p>Files sent in an encrypted room are themselves encrypted. Use this node to encrypt/decrypt files. Note: This node will download the encrypted file so be cautious of large downloads.</p>
|
||||
<p>Files sent in an encrypted room are themselves encrypted. Use this node to decrypt files. Note: This node will download the encrypted file so be cautious of large downloads.</p>
|
||||
|
||||
<h3>Inputs</h3>
|
||||
<dl class="message-properties">
|
||||
|
||||
96
src/matrix-delete-event.html
Normal file
96
src/matrix-delete-event.html
Normal file
@@ -0,0 +1,96 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('matrix-delete-event',{
|
||||
category: 'matrix',
|
||||
color: '#00b7ca',
|
||||
icon: "matrix.png",
|
||||
outputLabels: ["success", "error"],
|
||||
inputs:1,
|
||||
outputs:2,
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
reason: { value: "" },
|
||||
},
|
||||
label: function() {
|
||||
return this.name||"Delete Event";
|
||||
},
|
||||
paletteLabel: 'Delete Event'
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="matrix-delete-event">
|
||||
<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">
|
||||
<label for="node-input-reason"><i class="fa fa-sticky-note"></i> Reason</label>
|
||||
<input type="text" id="node-input-reason" placeholder="msg.reason">
|
||||
</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-delete-event">
|
||||
<h3>Details</h3>
|
||||
<p>Delete an event in a room</p>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.topic
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd> Room ID from where the event should be deleted from. Optional if configured on the node. If configured on the node this input will be overridden.</dd>
|
||||
<dt>msg.eventId
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd>Event ID of the Event which should be deleted.</dd>
|
||||
<dt>msg.reason
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd>Reason why the event is deleted. Default an empty string</dd>
|
||||
</dl>
|
||||
|
||||
<h3>Outputs</h3>
|
||||
<ol class="node-ports">
|
||||
<li>Success
|
||||
<dl class="message-properties">
|
||||
<dt>msg.eventId <span class="property-type">string</span></dt>
|
||||
<dd>the eventId from the deleted event.</dd>
|
||||
</dl>
|
||||
<dl class="message-properties">
|
||||
<dt>msg.deleted <span class="property-type">boolean</span></dt>
|
||||
<dd>True, if the event is deleted</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 class="message-properties">
|
||||
<dt>msg.deleted <span class="property-type">boolean</span></dt>
|
||||
<dd>False, if the event is not deleted</dd>
|
||||
</dl>
|
||||
</dl>
|
||||
</li>
|
||||
</ol>
|
||||
</script>
|
||||
75
src/matrix-delete-event.js
Normal file
75
src/matrix-delete-event.js
Normal file
@@ -0,0 +1,75 @@
|
||||
module.exports = function(RED) {
|
||||
function MatrixDeleteEvent(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.reason = n.reason
|
||||
|
||||
if (!node.server) {
|
||||
node.warn("No configuration node");
|
||||
return;
|
||||
}
|
||||
|
||||
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', function(msg) {
|
||||
|
||||
if(!msg.eventId) {
|
||||
node.error("eventId is missing");
|
||||
node.send([null, msg])
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node.server || !node.server.matrixClient) {
|
||||
node.warn("No matrix server selected");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.send([null, msg]);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.topic = node.roomId || msg.topic;
|
||||
if(!msg.topic) {
|
||||
node.warn("Room must be specified in msg.topic or in configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
msg.reason = node.reason || msg.reason;
|
||||
|
||||
if(!msg.reason) {
|
||||
msg.reason = '';
|
||||
}
|
||||
|
||||
node.server.matrixClient.redactEvent(msg.topic, msg.eventId, undefined,{
|
||||
reason: msg.reason
|
||||
})
|
||||
|
||||
.then(function(e) {
|
||||
msg.deleted = true
|
||||
node.send([msg, null]);
|
||||
})
|
||||
.catch(function(e){
|
||||
node.warn("Error deleting event " + e);
|
||||
msg.error = e;
|
||||
msg.deleted = false
|
||||
node.send([null, msg]);
|
||||
});
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-delete-event",MatrixDeleteEvent);
|
||||
}
|
||||
@@ -12,15 +12,15 @@
|
||||
roomId: { value: null },
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Room Invite";
|
||||
return this.name || "Invite to Room";
|
||||
},
|
||||
paletteLabel: 'Room Invite'
|
||||
paletteLabel: 'Invite to Room'
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="matrix-invite-room">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
|
||||
@@ -30,9 +30,21 @@
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-roomId"><i class="fa fa-user"></i> Room ID</label>
|
||||
<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>
|
||||
<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-invite-room">
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-join-room">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<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">
|
||||
|
||||
63
src/matrix-leave-room.html
Normal file
63
src/matrix-leave-room.html
Normal file
@@ -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: { value: "", 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>
|
||||
54
src/matrix-leave-room.js
Normal file
54
src/matrix-leave-room.js
Normal file
@@ -0,0 +1,54 @@
|
||||
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.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");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.topic) {
|
||||
node.error('No room provided in msg.topic');
|
||||
return;
|
||||
}
|
||||
|
||||
if(!node.server.isConnected()) {
|
||||
node.error("Matrix server connection is currently closed");
|
||||
node.send([null, msg]);
|
||||
}
|
||||
|
||||
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.payload = e;
|
||||
node.send([null, msg]);
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-leave-room", MatrixLeaveRoom);
|
||||
}
|
||||
@@ -9,7 +9,8 @@
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
roomId: { value: null }
|
||||
roomId: { value: null },
|
||||
reaction: { value: null }
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "React";
|
||||
@@ -20,7 +21,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-react">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<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">
|
||||
@@ -28,9 +29,25 @@
|
||||
<input type="text" id="node-input-server">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-roomId"><i class="fa fa-user"></i> Room ID</label>
|
||||
<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">
|
||||
<label for="node-input-reaction"><i class="fa fa-thumbs-up"></i> Reaction</label>
|
||||
<input type="text" id="node-input-reaction" placeholder="msg.payload">
|
||||
</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-react">
|
||||
@@ -42,7 +59,7 @@
|
||||
<dt>msg.payload
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd> Usually an emoji but can also be text. </dd>
|
||||
<dd> Usually an emoji but can also be text. If configured on the node this is ignored otherwise it required. </dd>
|
||||
|
||||
<dt>msg.topic
|
||||
<span class="property-type">string | null</span>
|
||||
|
||||
@@ -7,6 +7,7 @@ module.exports = function(RED) {
|
||||
this.name = n.name;
|
||||
this.server = RED.nodes.getNode(n.server);
|
||||
this.roomId = n.roomId;
|
||||
this.reaction = n.reaction;
|
||||
|
||||
if (!node.server) {
|
||||
node.warn("No configuration node");
|
||||
@@ -40,8 +41,9 @@ module.exports = function(RED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.payload) {
|
||||
node.error('msg.payload is required');
|
||||
let payload = n.reaction || msg.payload;
|
||||
if(!payload) {
|
||||
node.error('msg.payload must be defined or the reaction configured on the node.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -59,7 +61,7 @@ module.exports = function(RED) {
|
||||
{
|
||||
"m.relates_to": {
|
||||
event_id: eventId,
|
||||
key: msg.payload,
|
||||
key: payload,
|
||||
rel_type: "m.annotation"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
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";
|
||||
@@ -34,8 +37,9 @@
|
||||
<input type="text" id="node-input-server">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-roomId"><i class="fa fa-user"></i> Room ID</label>
|
||||
<label for="node-input-roomId"><i class="fa fa-comments"></i> Room ID</label>
|
||||
<input type="text" id="node-input-roomId">
|
||||
<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-tips">Enter a single room, comma separated list of rooms, or leave blank to get from all</div>
|
||||
<div class="form-row" style="margin-left: 100px;margin-top:10px;font-weight:bold;">
|
||||
@@ -48,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">
|
||||
@@ -58,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">
|
||||
@@ -68,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">
|
||||
@@ -78,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">
|
||||
@@ -88,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">
|
||||
@@ -98,9 +112,40 @@
|
||||
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">
|
||||
$(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-receive">
|
||||
@@ -116,6 +161,11 @@
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.isDM <span class="property-type">bool</span></dt>
|
||||
<dd> returns true if message is from a direct message room.</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="message-properties">
|
||||
<dt>msg.encrypted <span class="property-type">bool</span></dt>
|
||||
<dd> returns true if message was encrypted (e2ee).</dd>
|
||||
@@ -220,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>
|
||||
@@ -246,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>
|
||||
@@ -11,7 +11,10 @@ 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(',') : [];
|
||||
|
||||
@@ -72,6 +75,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 +112,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;
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
roomId: { value: null }
|
||||
roomId: { value: null },
|
||||
reason: { value: null }
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Room Ban";
|
||||
@@ -20,7 +21,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-room-ban">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<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">
|
||||
@@ -28,9 +29,25 @@
|
||||
<input type="text" id="node-input-server">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-roomId"><i class="fa fa-user"></i> Room ID</label>
|
||||
<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">
|
||||
<label for="node-input-reason"><i class="fa fa-comment"></i> Reason</label>
|
||||
<input type="text" id="node-input-reason" placeholder="msg.topic">
|
||||
</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-ban">
|
||||
@@ -52,7 +69,7 @@
|
||||
<dt class="optional">msg.reason
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd> Reason for banning the user.</dd>
|
||||
<dd> Reason for banning the user. If configured on the node it will overwrite this input</dd>
|
||||
</dl>
|
||||
|
||||
<h3>Outputs</h3>
|
||||
|
||||
@@ -7,6 +7,7 @@ module.exports = function(RED) {
|
||||
this.name = n.name;
|
||||
this.server = RED.nodes.getNode(n.server);
|
||||
this.roomId = n.roomId;
|
||||
this.reason = n.reason;
|
||||
|
||||
if (!node.server) {
|
||||
node.warn("No configuration node");
|
||||
@@ -45,7 +46,7 @@ module.exports = function(RED) {
|
||||
return;
|
||||
}
|
||||
|
||||
node.server.matrixClient.ban(msg.topic, msg.userId, msg.reason || undefined)
|
||||
node.server.matrixClient.ban(msg.topic, msg.userId, n.reason || msg.reason || undefined)
|
||||
.then(function(e) {
|
||||
node.log("Successfully banned " + msg.userId + " from " + msg.topic);
|
||||
msg.eventId = e.event_id;
|
||||
|
||||
80
src/matrix-room-invite.html
Normal file
80
src/matrix-room-invite.html
Normal file
@@ -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: { value: "", 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>
|
||||
31
src/matrix-room-invite.js
Normal file
31
src/matrix-room-invite.js
Normal file
@@ -0,0 +1,31 @@
|
||||
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.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);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-room-invite", MatrixRoomInvite);
|
||||
}
|
||||
@@ -9,7 +9,8 @@
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
roomId: { value: null }
|
||||
roomId: { value: null },
|
||||
reason: { value: null }
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Room Kick";
|
||||
@@ -20,7 +21,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-room-kick">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<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">
|
||||
@@ -28,9 +29,25 @@
|
||||
<input type="text" id="node-input-server">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-roomId"><i class="fa fa-user"></i> Room ID</label>
|
||||
<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">
|
||||
<label for="node-input-reason"><i class="fa fa-comment"></i> Reason</label>
|
||||
<input type="text" id="node-input-reason" placeholder="msg.topic">
|
||||
</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-kick">
|
||||
@@ -52,7 +69,7 @@
|
||||
<dt class="optional">msg.reason
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd> Reason for kicking the user.</dd>
|
||||
<dd> Reason for kicking the user. If configured on the node it will overwrite this input</dd>
|
||||
</dl>
|
||||
|
||||
<h3>Outputs</h3>
|
||||
|
||||
@@ -7,6 +7,7 @@ module.exports = function(RED) {
|
||||
this.name = n.name;
|
||||
this.server = RED.nodes.getNode(n.server);
|
||||
this.roomId = n.roomId;
|
||||
this.reason = n.reason;
|
||||
|
||||
if (!node.server) {
|
||||
node.warn("No configuration node");
|
||||
@@ -45,7 +46,7 @@ module.exports = function(RED) {
|
||||
return;
|
||||
}
|
||||
|
||||
node.server.matrixClient.kick(msg.topic, msg.userId, msg.reason || undefined)
|
||||
node.server.matrixClient.kick(msg.topic, msg.userId, n.reason || msg.reason || undefined)
|
||||
.then(function(e) {
|
||||
node.log("Successfully kicked " + msg.userId + " from " + msg.topic);
|
||||
msg.eventId = e.event_id;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-room-users">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<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">
|
||||
@@ -30,7 +30,19 @@
|
||||
<div class="form-row">
|
||||
<label for="node-input-server"><i class="fa fa-user"></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>
|
||||
<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-users">
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-send-file">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<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">
|
||||
@@ -29,8 +29,9 @@
|
||||
<input type="text" id="node-input-server">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-roomId"><i class="fa fa-user"></i> Room ID</label>
|
||||
<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">
|
||||
<label for="node-input-contentType"><i class="fa fa-user"></i> Content-Type</label>
|
||||
@@ -39,6 +40,17 @@
|
||||
<div class="form-tips">
|
||||
Must be a valid <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types" target="_blank">MIME Type</a> (ex: application/pdf) or left empty
|
||||
</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-send-file">
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-send-image">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<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">
|
||||
@@ -29,8 +29,9 @@
|
||||
<input type="text" id="node-input-server">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-roomId"><i class="fa fa-user"></i> Room ID</label>
|
||||
<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">
|
||||
<label for="node-input-contentType"><i class="fa fa-user"></i> Content-Type</label>
|
||||
@@ -39,6 +40,17 @@
|
||||
<div class="form-tips">
|
||||
Must be a valid <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types" target="_blank">MIME Type</a> (ex: image/png) or left empty
|
||||
</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-send-image">
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
name: { value: null },
|
||||
server: { value: "", type: "matrix-server-config" },
|
||||
roomId: { value: null },
|
||||
message: { value: null },
|
||||
messageType: { value: 'm.text' },
|
||||
messageFormat: { value: '' },
|
||||
replaceMessage : { value: false }
|
||||
},
|
||||
label: function() {
|
||||
return this.name || "Send Message";
|
||||
@@ -22,7 +24,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-send-message">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
|
||||
@@ -32,8 +34,25 @@
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-roomId"><i class="fa fa-user"></i> Room ID</label>
|
||||
<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">
|
||||
<label for="node-input-message"><i class="fa fa-comment"></i> Message</label>
|
||||
<textarea id="node-input-message" placeholder="msg.payload" style="width: 70%;"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="node-input-replaceMessage"
|
||||
style="width: auto; margin-left: 105px; vertical-align: top"
|
||||
/>
|
||||
<label for="node-input-replaceMessage" style="width: auto;max-width:50%;">
|
||||
Update existing message if <code>msg.eventId</code> is set
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
@@ -60,6 +79,17 @@
|
||||
<option value="msg.format">msg.format input</option>
|
||||
</select>
|
||||
</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-send-message">
|
||||
@@ -76,12 +106,17 @@
|
||||
<dt>msg.payload
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd> the message text. </dd>
|
||||
<dd> the message text. If configured on the node this is ignored otherwise it required. </dd>
|
||||
|
||||
<dt>msg.replace
|
||||
<span class="property-type">bool</span>
|
||||
</dt>
|
||||
<dd> If true and <code>msg.eventId</code> is present it will update an existing message. Posts a new message if false or <code>msg.eventId</code> is missing. </dd>
|
||||
|
||||
<dt class="optional">msg.formatted_payload
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd> the formatted HTML message (uses msg.payload if not defined). This only affects HTML messages.</dd>
|
||||
<dd> the formatted HTML message (uses <code>msg.payload</code> if not defined). This only affects HTML messages.</dd>
|
||||
|
||||
<dt class="optional">msg.type
|
||||
<span class="property-type">string | null</span>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const {RelationType} = require("matrix-js-sdk");
|
||||
|
||||
module.exports = function(RED) {
|
||||
function MatrixSendImage(n) {
|
||||
RED.nodes.createNode(this, n);
|
||||
@@ -9,6 +11,8 @@ module.exports = function(RED) {
|
||||
this.roomId = n.roomId;
|
||||
this.messageType = n.messageType;
|
||||
this.messageFormat = n.messageFormat;
|
||||
this.replaceMessage = n.replaceMessage;
|
||||
this.message = n.message;
|
||||
|
||||
// taken from https://github.com/matrix-org/synapse/blob/master/synapse/push/mailer.py
|
||||
this.allowedTags = [
|
||||
@@ -98,14 +102,15 @@ module.exports = function(RED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!msg.payload) {
|
||||
node.error('msg.payload is required');
|
||||
let payload = n.message || msg.payload;
|
||||
if(!payload) {
|
||||
node.error('msg.payload must be defined or the message configured on the node.');
|
||||
return;
|
||||
}
|
||||
|
||||
let content = {
|
||||
msgtype: msgType,
|
||||
body: msg.payload.toString()
|
||||
body: payload.toString()
|
||||
};
|
||||
|
||||
if(msgFormat === 'html') {
|
||||
@@ -113,12 +118,31 @@ module.exports = function(RED) {
|
||||
content.formatted_body =
|
||||
(typeof msg.formatted_payload !== 'undefined' && msg.formatted_payload)
|
||||
? msg.formatted_payload.toString()
|
||||
: msg.payload.toString();
|
||||
: payload.toString();
|
||||
}
|
||||
|
||||
if((node.replaceMessage || msg.replace) && msg.eventId) {
|
||||
content['m.new_content'] = {
|
||||
msgtype: content.msgtype,
|
||||
body: content.body
|
||||
};
|
||||
if('format' in content) {
|
||||
content['m.new_content']['format'] = content['format'];
|
||||
}
|
||||
if('formatted_body' in content) {
|
||||
content['m.new_content']['formatted_body'] = content['formatted_body'];
|
||||
}
|
||||
|
||||
content['m.relates_to'] = {
|
||||
rel_type: RelationType.Replace,
|
||||
event_id: msg.eventId
|
||||
};
|
||||
content['body'] = ' * ' + content['body'];
|
||||
}
|
||||
|
||||
node.server.matrixClient.sendMessage(msg.topic, content)
|
||||
.then(function(e) {
|
||||
node.log("Message sent: " + msg.payload);
|
||||
node.log("Message sent: " + payload);
|
||||
msg.eventId = e.event_id;
|
||||
node.send([msg, null]);
|
||||
})
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
deviceLabel: { type: "text", required: false },
|
||||
accessToken: { type: "password", required: true },
|
||||
deviceId: { type: "text", required: false },
|
||||
url: { type: "text", required: true }
|
||||
url: { type: "text", required: true },
|
||||
},
|
||||
defaults: {
|
||||
name: { value: null },
|
||||
@@ -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>
|
||||
|
||||
@@ -4,8 +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, 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();
|
||||
}
|
||||
@@ -29,8 +51,8 @@ module.exports = function(RED) {
|
||||
this.deviceId = this.credentials.deviceId || null;
|
||||
this.url = this.credentials.url;
|
||||
this.autoAcceptRoomInvites = n.autoAcceptRoomInvites;
|
||||
this.enableE2ee = n.enableE2ee || false;
|
||||
this.e2ee = (this.enableE2ee && this.deviceId);
|
||||
this.e2ee = n.enableE2ee || false;
|
||||
|
||||
this.globalAccess = n.global;
|
||||
this.initializedAt = new Date();
|
||||
|
||||
@@ -50,7 +72,7 @@ module.exports = function(RED) {
|
||||
} else if(!this.url) {
|
||||
node.error("Matrix connection failed: missing server URL in configuration.");
|
||||
} else {
|
||||
node.setConnected = function(connected, cb) {
|
||||
node.setConnected = async function(connected, cb) {
|
||||
if (node.connected !== connected) {
|
||||
node.connected = connected;
|
||||
if(typeof cb === 'function') {
|
||||
@@ -64,32 +86,37 @@ module.exports = function(RED) {
|
||||
// store Device ID internally
|
||||
let stored_device_id = getStoredDeviceId(localStorage),
|
||||
device_id = this.matrixClient.getDeviceId();
|
||||
if(!stored_device_id || stored_device_id !== device_id) {
|
||||
node.log(`Saving Device ID (old:${stored_device_id} new:${device_id})`);
|
||||
storeDeviceId(localStorage, device_id);
|
||||
}
|
||||
|
||||
// update device label
|
||||
if(node.deviceLabel) {
|
||||
node.matrixClient
|
||||
.getDevice(device_id)
|
||||
.then(
|
||||
function(response) {
|
||||
if(response.display_name !== node.deviceLabel) {
|
||||
node.matrixClient.setDeviceDetails(device_id, {
|
||||
display_name: node.deviceLabel
|
||||
}).then(
|
||||
function(response) {},
|
||||
function(error) {
|
||||
node.error("Failed to set device label: " + error);
|
||||
}
|
||||
);
|
||||
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.")
|
||||
} else {
|
||||
if(!stored_device_id || stored_device_id !== device_id) {
|
||||
node.log(`Saving Device ID (old:${stored_device_id} new:${device_id})`);
|
||||
storeDeviceId(localStorage, device_id);
|
||||
}
|
||||
|
||||
// update device label
|
||||
if(node.deviceLabel) {
|
||||
node.matrixClient
|
||||
.getDevice(device_id)
|
||||
.then(
|
||||
function(response) {
|
||||
if(response.display_name !== node.deviceLabel) {
|
||||
node.matrixClient.setDeviceDetails(device_id, {
|
||||
display_name: node.deviceLabel
|
||||
}).then(
|
||||
function(response) {},
|
||||
function(error) {
|
||||
node.error("Failed to set device label: " + error);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
function(error) {
|
||||
node.error("Failed to fetch device: " + error);
|
||||
}
|
||||
},
|
||||
function(error) {
|
||||
node.error("Failed to fetch device: " + error);
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
initialSetup = true;
|
||||
@@ -110,10 +137,14 @@ 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
|
||||
deviceId: (this.deviceId || getStoredDeviceId(localStorage)) || undefined,
|
||||
request
|
||||
// verificationMethods: ["m.sas.v1"]
|
||||
});
|
||||
|
||||
// set globally if configured to do so
|
||||
@@ -141,7 +172,7 @@ module.exports = function(RED) {
|
||||
return node.connected;
|
||||
};
|
||||
|
||||
node.matrixClient.on("Room.timeline", async function(event, room, toStartOfTimeline, removed, data) {
|
||||
node.matrixClient.on(RoomEvent.Timeline, async function(event, room, toStartOfTimeline, removed, data) {
|
||||
if (toStartOfTimeline) {
|
||||
return; // ignore paginated results
|
||||
}
|
||||
@@ -162,16 +193,32 @@ module.exports = function(RED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isDmRoom = (room) => {
|
||||
// Find out if this is a direct message room.
|
||||
let isDM = !!room.getDMInviter();
|
||||
const allMembers = room.currentState.getMembers();
|
||||
if (!isDM && allMembers.length <= 2) {
|
||||
// if not a DM, but there are 2 users only
|
||||
// double check DM (needed because getDMInviter works only if you were invited, not if you invite)
|
||||
// hence why we check for each member
|
||||
if (allMembers.some((m) => m.getDMInviter())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return allMembers.length <= 2 && isDM;
|
||||
};
|
||||
|
||||
let msg = {
|
||||
encrypted : event.isEncrypted(),
|
||||
redacted : event.isRedacted(),
|
||||
content : event.getContent(),
|
||||
type : (event.getContent()['msgtype'] || event.getType()) || null,
|
||||
payload : (event.getContent()['body'] || event.getContent()) || null,
|
||||
isDM : isDmRoom(room),
|
||||
userId : event.getSender(),
|
||||
topic : event.getRoomId(),
|
||||
eventId : event.getId(),
|
||||
event : event,
|
||||
event : event
|
||||
};
|
||||
|
||||
node.log("Received" + (msg.encrypted ? ' encrypted' : '') +" timeline event [" + msg.type + "]: (" + room.name + ") " + event.getSender() + " :: " + msg.content.body + (toStartOfTimeline ? ' [PAGINATED]' : ''));
|
||||
@@ -184,9 +231,9 @@ module.exports = function(RED) {
|
||||
*
|
||||
* @event module:client~MatrixClient#"crypto.suggestKeyRestore"
|
||||
*/
|
||||
node.matrixClient.on("crypto.suggestKeyRestore", function(){
|
||||
|
||||
});
|
||||
// node.matrixClient.on("crypto.suggestKeyRestore", function(){
|
||||
//
|
||||
// });
|
||||
|
||||
// node.matrixClient.on("RoomMember.typing", async function(event, member) {
|
||||
// let isTyping = member.typing;
|
||||
@@ -205,21 +252,36 @@ module.exports = function(RED) {
|
||||
// });
|
||||
|
||||
// handle auto-joining rooms
|
||||
node.matrixClient.on("RoomMember.membership", async function(event, member) {
|
||||
|
||||
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);
|
||||
console.log(event);
|
||||
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(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
node.matrixClient.on("sync", async function(state, prevState, data) {
|
||||
node.matrixClient.on(ClientEvent.Sync, async function(state, prevState, data) {
|
||||
node.debug("SYNC [STATE=" + state + "] [PREVSTATE=" + prevState + "]");
|
||||
if(prevState === null && state === "PREPARED" ) {
|
||||
// Occurs when the initial sync is completed first time.
|
||||
@@ -287,7 +349,8 @@ module.exports = function(RED) {
|
||||
}
|
||||
});
|
||||
|
||||
node.matrixClient.on("Session.logged_out", async function(errorObj){
|
||||
|
||||
node.matrixClient.on(HttpApiEvent.SessionLoggedOut, async function(errorObj){
|
||||
// Example if user auth token incorrect:
|
||||
// {
|
||||
// errcode: 'M_UNKNOWN_TOKEN',
|
||||
@@ -326,9 +389,29 @@ module.exports = function(RED) {
|
||||
return;
|
||||
}
|
||||
|
||||
node.matrixClient.getAccountDataFromServer()
|
||||
/**
|
||||
* We do a /whoami request before starting for a few reasons:
|
||||
* - validate our auth token
|
||||
* - make sure auth token belongs to provided node.userId
|
||||
* - fetch device_id if possible (only available on Synapse >= v1.40.0 under MSC2033)
|
||||
*/
|
||||
node.matrixClient.whoami()
|
||||
.then(
|
||||
function() {
|
||||
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.");
|
||||
}
|
||||
if('device_id' in data && data['device_id'] && !node.deviceId) {
|
||||
// if we have no device_id configured lets use the one
|
||||
// returned by /whoami for this access_token
|
||||
node.matrixClient.deviceId = data['device_id'];
|
||||
}
|
||||
|
||||
// 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']}`);
|
||||
return;
|
||||
}
|
||||
run().catch((error) => node.error(error));
|
||||
},
|
||||
function(err) {
|
||||
@@ -366,7 +449,8 @@ module.exports = function(RED) {
|
||||
const matrixClient = sdk.createClient({
|
||||
baseUrl: baseUrl,
|
||||
deviceId: deviceId,
|
||||
localTimeoutMs: '30000'
|
||||
localTimeoutMs: '30000',
|
||||
request
|
||||
});
|
||||
|
||||
matrixClient.login(
|
||||
@@ -397,7 +481,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 {
|
||||
@@ -410,7 +494,7 @@ module.exports = function(RED) {
|
||||
fs.copySync(oldStorageDir, dir);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
node.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -419,7 +503,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)){
|
||||
@@ -436,10 +520,18 @@ module.exports = function(RED) {
|
||||
* If a device ID is stored we will use that for the client
|
||||
*/
|
||||
function getStoredDeviceId(localStorage) {
|
||||
return localStorage.getItem('my_device_id');
|
||||
let deviceId = localStorage.getItem('my_device_id');
|
||||
if(deviceId === "null" || !deviceId) {
|
||||
return null;
|
||||
}
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
function storeDeviceId(localStorage, deviceId) {
|
||||
if(!deviceId) {
|
||||
return false;
|
||||
}
|
||||
localStorage.setItem('my_device_id', deviceId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-synapse-create-edit-user">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ module.exports = function(RED) {
|
||||
|
||||
node.server.matrixClient.http
|
||||
.authedRequest(
|
||||
undefined,
|
||||
'PUT',
|
||||
node.encodeUri(
|
||||
"/_synapse/admin/v2/users/$userId",
|
||||
@@ -62,13 +61,13 @@ 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]);
|
||||
});
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("matrix-synapse-create-edit-user", MatrixSynapseCreateEditUser);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-synapse-deactivate-user">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ module.exports = function(RED) {
|
||||
);
|
||||
node.server.matrixClient.http
|
||||
.authedRequest(
|
||||
undefined,
|
||||
'POST',
|
||||
path,
|
||||
undefined,
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-synapse-join-room">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
|
||||
@@ -30,13 +30,25 @@
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-roomId"><i class="fa fa-user"></i> Room ID</label>
|
||||
<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-tips" style="margin-bottom: 12px;">
|
||||
User must be an admin to use this endpoint.
|
||||
</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-synapse-join-room">
|
||||
|
||||
@@ -60,7 +60,6 @@ module.exports = function(RED) {
|
||||
// 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",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-synapse-register">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-synapse-users">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<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">
|
||||
|
||||
@@ -48,7 +48,6 @@ module.exports = function(RED) {
|
||||
|
||||
node.server.matrixClient.http
|
||||
.authedRequest(
|
||||
undefined,
|
||||
'GET',
|
||||
"/_synapse/admin/v2/users",
|
||||
queryParams,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<script type="text/html" data-template-name="matrix-whois-user">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ module.exports = function(RED) {
|
||||
// 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",
|
||||
|
||||
Reference in New Issue
Block a user