42 Commits
0.4.6 ... dev

Author SHA1 Message Date
Skylar Sadlier
ce8be4a30f Update readme 2022-12-06 22:10:26 -07:00
Skylar Sadlier
3e808cabec - Set version to 0.7.0 2022-12-06 21:53:30 -07:00
Skylar Sadlier
2fdc7482ce - Add back in persistent storage
- Receive node now accepts video files
2022-12-06 21:49:05 -07:00
Skylar Sadlier
c7f9d56df2 - Updated code for new authedRequest argument format
- matrix-js-sdk updated to 22.0.0
- fs-extra updated to 11.1.0
- minimum version set to 14.14
2022-12-06 18:47:41 -07:00
Skylar Sadlier
3c042ae47d - rename "Invite Room" node to "Invite to Room" to distinguish from new node
- new node "Invite Room" for receiving room invites
- new node "Leave Room" for leaving a room
- updated examples with new nodes
2022-12-05 23:40:05 -07:00
Skylar Sadlier
0a34870fa3 Merge pull request #71 from jacotec/master
Pass invites to the receive object
2022-12-05 21:20:11 -07:00
Skylar Sadlier
768a1c8ce0 Merge pull request #73 from WesleyE/master
Verify login on the relative matrix-chat/login URI
2022-12-05 21:19:03 -07:00
Skylar Sadlier
22dd9b4ca3 - matrix-js-sdk updated to v21.2.0
- added node-fetch polyfill for older nodejs versions
- added abort-controller polyfill for older nodejs versions
2022-12-05 21:18:16 -07:00
Skylar Sadlier
462f9670c2 - matrix-js-sdk updated to v20.0.0
- olm updated to v3.2.13 https://gitlab.matrix.org/matrix-org/olm/-/packages/136
- console messages now only appear if Node-RED's settings.js `logging.console.level` is info, debug, or trace
2022-12-05 19:49:55 -07:00
Wesley Elfring
e4b01c40c2 Verify login on the relative matrix-chat/login URI 2022-11-30 13:11:33 +01:00
Marco Jakobs
908d60835d Pass invites to the receive object
This commit passes a room invite to the receive node, so you can react to it.

Sample invitation:

HDsvcHGiTYFxxxxxxx:mydomain.de : msg : Object
object
type: "r.invite"
payload: "Invitation"
userId: "@relay:mydomain.de"
topic: "!HDsvcHGiTYFxxxxxxx:mydomain.de"
_msgid: "ef1fd7f3c8ae82e6"
2022-11-25 20:56:06 +01:00
Skylar Sadlier
4c17a21008 Merge pull request #63 from Skylar-Tech/dev
Version 0.6.1
2022-04-19 13:03:47 -06:00
Skylar Sadlier
bd4f6ea486 Update version to 0.6.1
- Fix: Error thrown in folder migration if new and old path are same
2022-04-19 13:01:00 -06:00
Skylar Sadlier
5ef0b6a11f Fix: Error thrown in folder migration if new and old path are same 2022-04-19 12:58:52 -06:00
Skylar Sadlier
97f27e61c6 Merge pull request #62 from Skylar-Tech/61-audio-messages
61 audio messages
2022-03-28 00:45:22 -06:00
Skylar Sadlier
7bdadc0fe9 Closes #209
- Can now receive audio files
- Can now receive locations
- Readme updated
2022-03-28 00:42:52 -06:00
Skylar Sadlier
5f129560aa Couple of little changes:
- Fix matrix-receive message types displaying in uppercase when it should be lowercase
- Readme updated with some extra things that are now possible since last release
- Link to matrix.org project in our README.md
2022-03-28 00:42:19 -06:00
Skylar Sadlier
00bc14e1c7 Merge pull request #59 from Skylar-Tech/dev
Version 0.5.8
2022-03-18 13:50:28 -06:00
Skylar Sadlier
9f41b67174 Version 0.5.8 2022-03-18 13:49:38 -06:00
Skylar Sadlier
4e93b7253e Revert examples 2022-03-18 13:37:24 -06:00
Skylar Sadlier
ecb4427217 - Revert device verification/e2ee stuff. It's not ready. 2022-03-18 13:34:46 -06:00
Skylar Sadlier
3e70369cae WIP 2022-03-18 00:23:28 -06:00
Skylar Sadlier
2ce80e2906 Update version to 0.5.5
- `fs-extra` and `got` dependencies updates
2022-03-17 21:06:15 -06:00
Skylar Sadlier
0c657caf5f Update version to 0.5.0 2022-03-17 20:05:06 -06:00
Skylar Sadlier
848fd0ec9d Merge pull request #58 from Skylar-Tech/48-key-sharing
48 key sharing
2022-03-17 20:03:56 -06:00
Skylar Sadlier
fef40f4ea9 - Update matrix-device-verification node description with super basic info on how to use it 2022-03-17 20:03:48 -06:00
Skylar Sadlier
595fbca3df - Update main readme with new verification notes 2022-03-17 19:58:25 -06:00
Skylar Sadlier
487f17a439 Update examples to move function example above verification ones. 2022-03-17 19:54:13 -06:00
Skylar Sadlier
68cb5a026e Update examples to include one for receiving and requesting device verification 2022-03-17 19:51:14 -06:00
Skylar Sadlier
d01733c647 closes #48
- Device verification can now be requested or received
2022-03-17 19:38:29 -06:00
Skylar Sadlier
ed146e98d8 Closes #56 Closes #50
- can now specify message in node's configuration for matrix-send-message
- can now specify reaction in node's configuration for matrix-react
- reason can now be configured on both matrix-room-kick and matrix-room-ban
- icons updated for various nodes
- fix tag for name configuration field for various nodes
- roomId input is now validated to ensure it starts with ! and if not shows an error
2022-03-17 17:21:44 -06:00
Skylar Sadlier
3dc6363a88 Closes #56
- can now specify message in node's configuration for matrix-send-message
- can now specify reaction in node's configuration for matrix-react
- reason can now be configured on both matrix-room-kick and matrix-room-ban
- icons updated for various nodes
2022-03-17 16:01:27 -06:00
Skylar Sadlier
380e548425 Closes #54
- New option for matrix-send-message node that allows replacing existing message if enabled and `msg.eventId` is passed in.
2022-03-17 15:37:10 -06:00
Skylar Sadlier
27dd4d81a2 name should default to null 2022-03-17 14:47:01 -06:00
Skylar Sadlier
de3c58044f Add example for delete-event node 2022-03-17 14:40:53 -06:00
Skylar Sadlier
db1901ed1e Closes #29
- msg.isDM is now returned on received messages to help determine if it originated from a direct message room
2022-03-17 14:30:10 -06:00
Skylar Sadlier
ac68060825 - matrix-crypt-file documentation updated (it cannot encrypt, only decrypts)
- matrix-js-sdk update from 15.5.0 to 16.0.0
- Fixed matrix-js-sdk breaking changes
- @matrix-org_olm-3.2.8.tgz added to package since there is no npm release for this. It fixes some errors so we need it.
2022-03-17 14:23:22 -06:00
Skylar Sadlier
d304cb95f6 Merge pull request #55 from Skylar-Tech/51-device_id-fix
- We now use /whoami to validate the auth token since it gives us the…
2022-03-17 13:29:48 -06:00
Skylar Sadlier
85764c08d3 Merge pull request #52 from aikitori/feature/delete-message
Add Delete Message Node
2022-03-17 12:26:40 -06:00
aikitori
050be29d64 rename node 2022-03-13 10:58:17 +01:00
Skylar Sadlier
33823dea25 - We now use /whoami to validate the auth token since it gives us the user_id and device_id for the given auth_token (device_id is only available on Synapse 1.40 onwards)
- Error is now thrown if device_id cannot be automatically detected
- Do not store `null` into my_device_id and ignore if it has already been set. Fixes #51
2022-03-12 16:23:43 -07:00
aikitori
d826c58b51 Add Delete Message Node 2022-03-12 16:48:52 +01:00
47 changed files with 10402 additions and 4885 deletions

BIN
@matrix-org_olm-3.2.8.tgz Normal file

Binary file not shown.

View File

@@ -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)

View File

@@ -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
![custom-redact-function-node.png](custom-redact-function-node.png)
### 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.
![accept-room-invites.png](accept-room-invites.png)
### Leave room when someone says bye
[View JSON](leave-room-bye.json)
Leave room when someone says "bye".
![leave-room-bye.png](leave-room-bye.png)
### Remove messages containing "delete"
[View JSON](delete-event.json)
Any messages containing "delete" will try to be removed by the client.
![respond-react-with-reaction.png](delete-event.png)
### Respond to "users" with full list of server users
[View JSON](respond-users-list.json)

View 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
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

13924
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -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",

View File

@@ -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>

View File

@@ -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">

View 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>

View 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);
}

View File

@@ -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">

View File

@@ -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">

View 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
View 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);
}

View File

@@ -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>

View File

@@ -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"
}
}

View File

@@ -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>

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;

View 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
View 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);
}

View File

@@ -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>

View File

@@ -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;

View File

@@ -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">

View File

@@ -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">

View 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">

View File

@@ -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>

View File

@@ -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]);
})

View File

@@ -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 &amp; About" tab (left side of the dialog).</li><li>Scroll to the bottom and click the <code>&lt;click to reveal&gt;</code> part of <code>Access Token: &lt;click to reveal&gt;</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>

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -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);

View File

@@ -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>

View File

@@ -56,7 +56,6 @@ module.exports = function(RED) {
);
node.server.matrixClient.http
.authedRequest(
undefined,
'POST',
path,
undefined,

View File

@@ -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">

View File

@@ -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",

View File

@@ -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>

View File

@@ -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">

View File

@@ -48,7 +48,6 @@ module.exports = function(RED) {
node.server.matrixClient.http
.authedRequest(
undefined,
'GET',
"/_synapse/admin/v2/users",
queryParams,

View File

@@ -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>

View File

@@ -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",