Initial commit of an alpha version.
This commit is contained in:
parent
46a976e060
commit
61aa32e8a6
1126
package-lock.json
generated
Normal file
1126
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
34
package.json
Normal file
34
package.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "node-red-contrib-matrix-support",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Matrix chat server support for Node-RED",
|
||||||
|
"dependencies": {
|
||||||
|
"matrix-js-sdk": "^12.2.0"
|
||||||
|
},
|
||||||
|
"node-red": {
|
||||||
|
"nodes": {
|
||||||
|
"matrix-server-config": "src/matrix-server-config.js",
|
||||||
|
"matrix-send": "src/matrix-send.js",
|
||||||
|
"matrix-send-file": "src/matrix-send-file.js",
|
||||||
|
"matrix-send-image": "src/matrix-send-image.js",
|
||||||
|
"matrix-send-message": "src/matrix-send-message.js",
|
||||||
|
"matrix-receive": "src/matrix-receive.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"node-red",
|
||||||
|
"matrix",
|
||||||
|
"support",
|
||||||
|
"bot",
|
||||||
|
"chat"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/skylar-tech/node-red-contrib-matrix-support"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Skylar Sadlier",
|
||||||
|
"url": "https://skylar.tech"
|
||||||
|
},
|
||||||
|
"license": "SEE LICENSE FILE"
|
||||||
|
}
|
BIN
src/icons/matrix.png
Normal file
BIN
src/icons/matrix.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
99
src/matrix-receive.html
Normal file
99
src/matrix-receive.html
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<script type="text/javascript">
|
||||||
|
RED.nodes.registerType('matrix-receive',{
|
||||||
|
category: 'matrix',
|
||||||
|
color: '#00b7ca',
|
||||||
|
icon: "matrix.png",
|
||||||
|
outputLabels: ["message"],
|
||||||
|
inputs:0,
|
||||||
|
outputs:1,
|
||||||
|
defaults: {
|
||||||
|
name: { value: null },
|
||||||
|
server: { value: "", type: "matrix-server-config" },
|
||||||
|
roomId: {"value": null},
|
||||||
|
ignoreText: {"value": false},
|
||||||
|
ignoreReactions: {"value": false},
|
||||||
|
ignoreFiles: {"value": false},
|
||||||
|
ignoreImages: {"value": false},
|
||||||
|
},
|
||||||
|
label: function() {
|
||||||
|
return this.name || "Matrix Receive";
|
||||||
|
},
|
||||||
|
paletteLabel: 'Receive'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-template-name="matrix-receive">
|
||||||
|
<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-server"></i> Matrix Server</label>
|
||||||
|
<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>
|
||||||
|
<input type="text" id="node-input-roomId">
|
||||||
|
</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;">
|
||||||
|
Timeline event filters
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="node-config-input-ignoreText"
|
||||||
|
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||||
|
/>
|
||||||
|
<label for="node-config-input-ignoreText" style="width: auto">
|
||||||
|
Ignore text
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="node-config-input-ignoreReactions"
|
||||||
|
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||||
|
/>
|
||||||
|
<label for="node-config-input-ignoreReactions" style="width: auto">
|
||||||
|
Ignore reactions
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="node-config-input-ignoreFiles"
|
||||||
|
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||||
|
/>
|
||||||
|
<label for="node-config-input-ignoreFiles" style="width: auto">
|
||||||
|
Ignore files
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="node-config-input-ignoreImages"
|
||||||
|
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||||
|
/>
|
||||||
|
<label for="node-config-input-ignoreImages" style="width: auto">
|
||||||
|
Ignore images
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-help-name="matrix-receive">
|
||||||
|
<p>Receive messages from a matrix server on all rooms or a specified room.</p>
|
||||||
|
<h3>Outputs</h3>
|
||||||
|
<ol class="node-ports">
|
||||||
|
<li>Message
|
||||||
|
<dl class="message-properties">
|
||||||
|
<dt>topic <span class="property-type">string</span></dt>
|
||||||
|
<dd>the room the message originated from.</dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="message-properties">
|
||||||
|
<dt>payload <span class="property-type">string</span></dt>
|
||||||
|
<dd>the message from the server.</dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</script>
|
106
src/matrix-receive.js
Normal file
106
src/matrix-receive.js
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
module.exports = function(RED) {
|
||||||
|
function MatrixReceiveMessage(n) {
|
||||||
|
RED.nodes.createNode(this, n);
|
||||||
|
|
||||||
|
let node = this;
|
||||||
|
|
||||||
|
this.name = n.name;
|
||||||
|
this.server = RED.nodes.getNode(n.server);
|
||||||
|
this.ignoreText = n.ignoreText;
|
||||||
|
this.ignoreReactions = n.ignoreReactions;
|
||||||
|
this.ignoreFiles = n.ignoreFiles;
|
||||||
|
this.ignoreImages = n.ignoreImages;
|
||||||
|
|
||||||
|
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.server.matrixClient.on("Room.timeline", function(event, room, toStartOfTimeline, data) {
|
||||||
|
console.log("Room.timeline", [event, room]);
|
||||||
|
if (toStartOfTimeline) {
|
||||||
|
console.log("MESSAGED SKIPPED: toStartOfTimeline");
|
||||||
|
return; // ignore paginated results
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
event.getType() !== "m.room.message"
|
||||||
|
&& event.getType() !== "m.reaction"
|
||||||
|
) {
|
||||||
|
console.log("MESSAGED SKIPPED: TYPE");
|
||||||
|
return; // only keep messages
|
||||||
|
}
|
||||||
|
if (!event.getSender() || event.getSender() === node.server.userId) {
|
||||||
|
console.log("MESSAGED SKIPPED: SENDER");
|
||||||
|
return; // ignore our own messages
|
||||||
|
}
|
||||||
|
if (!event.getUnsigned() || event.getUnsigned().age > 1000) {
|
||||||
|
console.log("MESSAGED SKIPPED: UNSIGNED");
|
||||||
|
return; // ignore old messages
|
||||||
|
}
|
||||||
|
|
||||||
|
// if node has a room ID set we only listen on that room
|
||||||
|
if(node.roomId) {
|
||||||
|
let roomIds = node.roomId.split(',');
|
||||||
|
|
||||||
|
if(roomIds.indexOf(msg.roomId) === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = event.getContent(),
|
||||||
|
msg = {};
|
||||||
|
|
||||||
|
msg.type = (content.msgtype || event.getType()) || null;
|
||||||
|
msg.payload = event.getContent().body;
|
||||||
|
msg.sender = event.getSender();
|
||||||
|
msg.roomId = room.roomId;
|
||||||
|
msg.eventId = event.getId();
|
||||||
|
msg.event = event;
|
||||||
|
|
||||||
|
node.log("Received chat message [" + msg.type + "]: (" + room.name + ") " + event.getSender() + " :: " + event.getContent().body);
|
||||||
|
|
||||||
|
switch(msg.type) {
|
||||||
|
case 'm.text':
|
||||||
|
if(node.ignoreText) return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'm.reaction':
|
||||||
|
if(node.ignoreReactions) return;
|
||||||
|
msg.info = event.getContent()["m.relates_to"].info;
|
||||||
|
msg.eventId = event.getContent()["m.relates_to"].event_id;
|
||||||
|
msg.payload = event.getContent()["m.relates_to"].key;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'm.file':
|
||||||
|
if(node.ignoreFiles) return;
|
||||||
|
msg.file = {
|
||||||
|
info: event.getContent().info,
|
||||||
|
url: node.server.matrixClient.mxcUrlToHttp(event.getContent().url)
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'm.image':
|
||||||
|
if(node.ignoreImages) return;
|
||||||
|
msg.image = {
|
||||||
|
info: event.getContent().info,
|
||||||
|
url: node.server.matrixClient.mxcUrlToHttp(event.getContent().url),
|
||||||
|
thumbnail_url: node.server.matrixClient.mxcUrlToHttp(event.getContent().info.thumbnail_url)
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.send(msg);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
RED.nodes.registerType("matrix-receive", MatrixReceiveMessage);
|
||||||
|
}
|
97
src/matrix-send-file.html
Normal file
97
src/matrix-send-file.html
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<script type="text/javascript">
|
||||||
|
RED.nodes.registerType('matrix-send-file',{
|
||||||
|
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 },
|
||||||
|
contentType: { value: null }
|
||||||
|
},
|
||||||
|
label: function() {
|
||||||
|
return this.name || "Send File";
|
||||||
|
},
|
||||||
|
paletteLabel: 'Send File'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<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-user"></i> Room ID</label>
|
||||||
|
<input type="text" id="node-input-roomId">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-contentType"><i class="fa fa-user"></i> Content-Type</label>
|
||||||
|
<input type="text" id="node-input-contentType" placeholder="application/pdf">
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-help-name="matrix-send-file">
|
||||||
|
<p>Send a message to a matrix room.</p>
|
||||||
|
|
||||||
|
<h3>Inputs</h3>
|
||||||
|
<dl class="message-properties">
|
||||||
|
<dt>msg.payload
|
||||||
|
<span class="property-type">File | String | Buffer | ReadStream | Blob</span>
|
||||||
|
</dt>
|
||||||
|
<dd> the contents of the file to upload. </dd>
|
||||||
|
|
||||||
|
<dt>msg.roomId
|
||||||
|
<span class="property-type">String | Null</span>
|
||||||
|
</dt>
|
||||||
|
<dd> Room ID to send file to. Optional if configured on the node. Overrides node configuration if set.</dd>
|
||||||
|
|
||||||
|
<dt class="optional">msg.filename
|
||||||
|
<span class="property-type">String | Null</span>
|
||||||
|
</dt>
|
||||||
|
<dd> name of the file to upload (optional). Overrides node configuration.</dd>
|
||||||
|
|
||||||
|
<dt class="optional">msg.contentType
|
||||||
|
<span class="property-type">String | Null</span>
|
||||||
|
</dt>
|
||||||
|
<dd> Content <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types" target="_blank">MIME Type</a>. Optional if configured on the node. Overrides node configuration if set.</dd>
|
||||||
|
|
||||||
|
<dt class="optional">msg.body
|
||||||
|
<span class="property-type">String | Null</span>
|
||||||
|
</dt>
|
||||||
|
<dd> this will be the display name the client will see when rendered in their chat client. If this is left empty the it uses <code>msg.filename</code>. If <code>msg.filename</code> is also undefined it sets it to empty string</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Outputs</h3>
|
||||||
|
<ol class="node-ports">
|
||||||
|
<li>Success
|
||||||
|
<dl class="message-properties">
|
||||||
|
<dd>original msg object preserved.</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>
|
||||||
|
|
||||||
|
<h3>Details</h3>
|
||||||
|
<p>This node will send a file to a Matrix chat room. You can link this directly to a File In node.</p>
|
||||||
|
|
||||||
|
<h3>References</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types">MIME Types</a> - description of <code>msg.contentType</code> format</li>
|
||||||
|
</ul>
|
||||||
|
</script>
|
89
src/matrix-send-file.js
Normal file
89
src/matrix-send-file.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
module.exports = function(RED) {
|
||||||
|
function MatrixSendFile(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.contentType = n.contentType;
|
||||||
|
|
||||||
|
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 (! 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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.roomId = node.roomId || msg.roomId;
|
||||||
|
if(!msg.roomId) {
|
||||||
|
node.warn("Room must be specified in msg.roomId or in configuration");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!msg.payload) {
|
||||||
|
node.error('msg.payload is required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.contentType = msg.contentType || node.contentType;
|
||||||
|
if(!msg.contentType) {
|
||||||
|
node.error('msg.contentType is required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.log("Uploading file " + msg.filename);
|
||||||
|
node.server.matrixClient.uploadContent(
|
||||||
|
msg.payload, {
|
||||||
|
name: msg.filename || null, // Name to give the file on the server.
|
||||||
|
rawResponse: (msg.rawResponse || false), // Return the raw body, rather than parsing the JSON.
|
||||||
|
type: msg.contentType, // Content-type for the upload. Defaults to file.type, or applicaton/octet-stream.
|
||||||
|
onlyContentUri: false // Just return the content URI, rather than the whole body. Defaults to false. Ignored if opts.rawResponse is true.
|
||||||
|
})
|
||||||
|
.then(function(file){
|
||||||
|
const content = {
|
||||||
|
msgtype: 'm.file',
|
||||||
|
url: file.content_uri,
|
||||||
|
body: (msg.body || msg.filename) || "",
|
||||||
|
};
|
||||||
|
node.server.matrixClient
|
||||||
|
.sendMessage(msg.roomId, content)
|
||||||
|
.then(function(imgResp) {
|
||||||
|
node.log("File message sent: " + imgResp);
|
||||||
|
msg.eventId = e.eventId;
|
||||||
|
node.send([msg, null]);
|
||||||
|
})
|
||||||
|
.catch(function(e){
|
||||||
|
node.warn("Error sending file message " + e);
|
||||||
|
msg.matrixError = e;
|
||||||
|
node.send([null, msg]);
|
||||||
|
});
|
||||||
|
}).catch(function(e){
|
||||||
|
node.warn("Error uploading file message " + e);
|
||||||
|
msg.error = e;
|
||||||
|
node.send([null, msg]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
RED.nodes.registerType("matrix-send-file", MatrixSendFile);
|
||||||
|
}
|
97
src/matrix-send-image.html
Normal file
97
src/matrix-send-image.html
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<script type="text/javascript">
|
||||||
|
RED.nodes.registerType('matrix-send-image',{
|
||||||
|
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 },
|
||||||
|
contentType: { value: null }
|
||||||
|
},
|
||||||
|
label: function() {
|
||||||
|
return this.name || "Send Image";
|
||||||
|
},
|
||||||
|
paletteLabel: 'Send Image'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<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-user"></i> Room ID</label>
|
||||||
|
<input type="text" id="node-input-roomId">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-contentType"><i class="fa fa-user"></i> Content-Type</label>
|
||||||
|
<input type="text" id="node-input-contentType" placeholder="application/png">
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-help-name="matrix-send-image">
|
||||||
|
<p>Send an image to a matrix room.</p>
|
||||||
|
|
||||||
|
<h3>Inputs</h3>
|
||||||
|
<dl class="message-properties">
|
||||||
|
<dt>msg.payload
|
||||||
|
<span class="property-type">File | String | Buffer | ReadStream | Blob</span>
|
||||||
|
</dt>
|
||||||
|
<dd> the contents of the image to upload. </dd>
|
||||||
|
|
||||||
|
<dt>msg.roomId
|
||||||
|
<span class="property-type">String | Null</span>
|
||||||
|
</dt>
|
||||||
|
<dd> Room ID to send image to. Optional if configured on the node. Overrides node configuration if set.</dd>
|
||||||
|
|
||||||
|
<dt class="optional">msg.filename
|
||||||
|
<span class="property-type">String | Null</span>
|
||||||
|
</dt>
|
||||||
|
<dd> name of the image file to upload (optional). Overrides node configuration.</dd>
|
||||||
|
|
||||||
|
<dt class="optional">msg.contentType
|
||||||
|
<span class="property-type">String | Null</span>
|
||||||
|
</dt>
|
||||||
|
<dd> Content <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types" target="_blank">MIME Type</a>. Optional if configured on the node. Overrides node configuration if set.</dd>
|
||||||
|
|
||||||
|
<dt class="optional">msg.body
|
||||||
|
<span class="property-type">String | Null</span>
|
||||||
|
</dt>
|
||||||
|
<dd> this will be the display name the client will see when rendered in their chat client. If this is left empty the it uses <code>msg.filename</code>. If <code>msg.filename</code> is also undefined it sets it to empty string</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Outputs</h3>
|
||||||
|
<ol class="node-ports">
|
||||||
|
<li>Success
|
||||||
|
<dl class="message-properties">
|
||||||
|
<dd>original msg object preserved.</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>
|
||||||
|
|
||||||
|
<h3>Details</h3>
|
||||||
|
<p>This node will send an image to a Matrix chat room. You can link this directly to a File In node.</p>
|
||||||
|
|
||||||
|
<h3>References</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types">MIME Types</a> - description of <code>msg.contentType</code> format</li>
|
||||||
|
</ul>
|
||||||
|
</script>
|
83
src/matrix-send-image.js
Normal file
83
src/matrix-send-image.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
module.exports = function(RED) {
|
||||||
|
function MatrixSendImage(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.contentType = n.contentType;
|
||||||
|
|
||||||
|
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 (! 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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.roomId = node.roomId || msg.roomId;
|
||||||
|
if(!msg.roomId) {
|
||||||
|
node.warn("Room must be specified in msg.roomId or in configuration");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!msg.payload) {
|
||||||
|
node.error('msg.payload is required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.contentType = msg.contentType || node.contentType;
|
||||||
|
if(!msg.contentType) {
|
||||||
|
node.error('msg.contentType is required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.log("Uploading image " + msg.filename);
|
||||||
|
node.server.matrixClient.uploadContent(
|
||||||
|
msg.payload, {
|
||||||
|
name: msg.filename || null, // Name to give the file on the server.
|
||||||
|
rawResponse: (msg.rawResponse || false), // Return the raw body, rather than parsing the JSON.
|
||||||
|
type: msg.contentType, // Content-type for the upload. Defaults to file.type, or applicaton/octet-stream.
|
||||||
|
onlyContentUri: false // Just return the content URI, rather than the whole body. Defaults to false. Ignored if opts.rawResponse is true.
|
||||||
|
}).then(function(file){
|
||||||
|
node.server.matrixClient
|
||||||
|
.sendImageMessage(msg.roomId, file.content_uri, {}, (msg.body || msg.filename) || "")
|
||||||
|
.then(function(imgResp) {
|
||||||
|
node.log("Image message sent: " + imgResp);
|
||||||
|
msg.eventId = e.eventId;
|
||||||
|
node.send([msg, null]);
|
||||||
|
})
|
||||||
|
.catch(function(e){
|
||||||
|
node.warn("Error sending image message " + e);
|
||||||
|
msg.matrixError = e;
|
||||||
|
node.send([null, msg]);
|
||||||
|
});
|
||||||
|
}).catch(function(e){
|
||||||
|
node.warn("Error uploading image message " + e);
|
||||||
|
msg.matrixError = e;
|
||||||
|
node.send([null, msg]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
RED.nodes.registerType("matrix-send-image", MatrixSendImage);
|
||||||
|
}
|
103
src/matrix-send-message.html
Normal file
103
src/matrix-send-message.html
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
<script type="text/javascript">
|
||||||
|
RED.nodes.registerType('matrix-send-message',{
|
||||||
|
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 },
|
||||||
|
htmlMessage: { value: false }
|
||||||
|
},
|
||||||
|
label: function() {
|
||||||
|
return this.name || "Send Message";
|
||||||
|
},
|
||||||
|
paletteLabel: 'Send Message'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<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-user"></i> Room ID</label>
|
||||||
|
<input type="text" id="node-input-roomId">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="node-input-htmlMessage"
|
||||||
|
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||||
|
/>
|
||||||
|
<label for="node-input-htmlMessage" style="width: auto">
|
||||||
|
HTML/Markdown message
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-help-name="matrix-send-message">
|
||||||
|
<p>Send an image to a matrix room.</p>
|
||||||
|
|
||||||
|
<h3>Inputs</h3>
|
||||||
|
<dl class="message-properties">
|
||||||
|
<dt>msg.payload
|
||||||
|
<span class="property-type">File | String | Buffer | ReadStream | Blob</span>
|
||||||
|
</dt>
|
||||||
|
<dd> the contents of the image to upload. </dd>
|
||||||
|
|
||||||
|
<dt>msg.roomId
|
||||||
|
<span class="property-type">String | Null</span>
|
||||||
|
</dt>
|
||||||
|
<dd> Room ID to send image to. Optional if configured on the node. Overrides node configuration if set.</dd>
|
||||||
|
|
||||||
|
<dt class="optional">msg.filename
|
||||||
|
<span class="property-type">String | Null</span>
|
||||||
|
</dt>
|
||||||
|
<dd> name of the image file to upload (optional). Overrides node configuration.</dd>
|
||||||
|
|
||||||
|
<dt class="optional">msg.contentType
|
||||||
|
<span class="property-type">String | Null</span>
|
||||||
|
</dt>
|
||||||
|
<dd> Content <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types" target="_blank">MIME Type</a>. Optional if configured on the node. Overrides node configuration if set.</dd>
|
||||||
|
|
||||||
|
<dt class="optional">msg.body
|
||||||
|
<span class="property-type">String | Null</span>
|
||||||
|
</dt>
|
||||||
|
<dd> this will be the display name the client will see when rendered in their chat client. If this is left empty the it uses <code>msg.filename</code>. If <code>msg.filename</code> is also undefined it sets it to empty string</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Outputs</h3>
|
||||||
|
<ol class="node-ports">
|
||||||
|
<li>Success
|
||||||
|
<dl class="message-properties">
|
||||||
|
<dd>original msg object preserved.</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>
|
||||||
|
|
||||||
|
<h3>Details</h3>
|
||||||
|
<p>This node will send an image to a Matrix chat room. You can link this directly to a File In node.</p>
|
||||||
|
|
||||||
|
<h3>References</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types">MIME Types</a> - description of <code>msg.contentType</code> format</li>
|
||||||
|
</ul>
|
||||||
|
</script>
|
77
src/matrix-send-message.js
Normal file
77
src/matrix-send-message.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
module.exports = function(RED) {
|
||||||
|
function MatrixSendImage(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.htmlMessage = n.htmlMessage;
|
||||||
|
|
||||||
|
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 (! 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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.roomId = node.roomId || msg.roomId;
|
||||||
|
if(!msg.roomId) {
|
||||||
|
node.warn("Room must be specified in msg.roomId or in configuration");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!msg.payload) {
|
||||||
|
node.error('msg.payload is required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.htmlMessage) {
|
||||||
|
node.server.matrixClient.sendHtmlMessage(msg.roomId, msg.payload.toString(), msg.payload.toString())
|
||||||
|
.then(function(e) {
|
||||||
|
node.log("Message sent: " + msg.payload);
|
||||||
|
msg.eventId = e.eventId;
|
||||||
|
node.send([msg, null]);
|
||||||
|
})
|
||||||
|
.catch(function(e){
|
||||||
|
node.warn("Error sending message " + e);
|
||||||
|
msg.matrixError = e;
|
||||||
|
node.send([null, msg]);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
node.server.matrixClient.sendTextMessage(msg.roomId, msg.payload.toString())
|
||||||
|
.then(function(e) {
|
||||||
|
node.log("Message sent: " + msg.payload);
|
||||||
|
msg.eventId = e.eventId;
|
||||||
|
node.send([msg, null]);
|
||||||
|
})
|
||||||
|
.catch(function(e){
|
||||||
|
node.warn("Error sending message " + e);
|
||||||
|
msg.matrixError = e;
|
||||||
|
node.send([null, msg]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
RED.nodes.registerType("matrix-send-message", MatrixSendImage);
|
||||||
|
}
|
78
src/matrix-send.html
Normal file
78
src/matrix-send.html
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<script type="text/javascript">
|
||||||
|
RED.nodes.registerType('matrix-send',{
|
||||||
|
category: 'matrix',
|
||||||
|
color: '#00b7ca',
|
||||||
|
icon: "matrix.png",
|
||||||
|
outputLabels: ["success", "error"],
|
||||||
|
inputs:1,
|
||||||
|
outputs:2,
|
||||||
|
defaults: {
|
||||||
|
name: { value: null },
|
||||||
|
matrixServer: { value: "", type: "matrix-server-config" },
|
||||||
|
room: { value: null }
|
||||||
|
},
|
||||||
|
label: function() {
|
||||||
|
return this.name || "matrix-send";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-template-name="matrix-send">
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||||
|
<input type="text" id="node-input-name" placeholder="Name">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-matrixServer"><i class="fa fa-user"></i> Matrix Server Config</label>
|
||||||
|
<input type="text" id="node-input-matrixServer">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-roomId"><i class="fa fa-user"></i> Room ID</label>
|
||||||
|
<input type="text" id="node-input-roomId">
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-help-name="matrix-send">
|
||||||
|
<p>Send a message to a matrix room.</p>
|
||||||
|
|
||||||
|
<h3>Inputs</h3>
|
||||||
|
<dl class="message-properties">
|
||||||
|
<dt>payload
|
||||||
|
<span class="property-type">string</span>
|
||||||
|
</dt>
|
||||||
|
<dd> the payload of the message to publish. </dd>
|
||||||
|
<dt class="optional">topic <span class="property-type">string</span></dt>
|
||||||
|
<dd> the Matrix room ID to send the message to. Setting it will override the room ID configured on the node.</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Outputs</h3>
|
||||||
|
<ol class="node-ports">
|
||||||
|
<li>Standard output
|
||||||
|
<dl class="message-properties">
|
||||||
|
<dt>payload <span class="property-type">string</span></dt>
|
||||||
|
<dd>the standard output of the command.</dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
<li>Standard error
|
||||||
|
<dl class="message-properties">
|
||||||
|
<dt>payload <span class="property-type">string</span></dt>
|
||||||
|
<dd>the standard error of the command.</dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h3>Details</h3>
|
||||||
|
<p><code>msg.payload</code> is used as the payload of the published message.
|
||||||
|
If it contains an Object it will be converted to a JSON string before being sent.
|
||||||
|
If it contains a binary Buffer the message will be published as-is.</p>
|
||||||
|
<p>The topic used can be configured in the node or, if left blank, can be set
|
||||||
|
by <code>msg.topic</code>.</p>
|
||||||
|
<p>Likewise the QoS and retain values can be configured in the node or, if left
|
||||||
|
blank, set by <code>msg.qos</code> and <code>msg.retain</code> respectively.</p>
|
||||||
|
|
||||||
|
<h3>References</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a>Twitter API docs</a> - full description of <code>msg.tweet</code> property</li>
|
||||||
|
<li><a>GitHub</a> - the nodes github repository</li>
|
||||||
|
</ul>
|
||||||
|
</script>
|
175
src/matrix-send.js
Normal file
175
src/matrix-send.js
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
module.exports = function(RED) {
|
||||||
|
function MatrixSendMessage(n) {
|
||||||
|
RED.nodes.createNode(this, n);
|
||||||
|
|
||||||
|
var node = this;
|
||||||
|
|
||||||
|
this.name = n.name;
|
||||||
|
this.server = RED.nodes.getNode(n.matrixServer);
|
||||||
|
this.room = n.room;
|
||||||
|
|
||||||
|
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.matrixClient.joinRoom(node.room, {syncRoom:false}) // should we really skip syncing the room?
|
||||||
|
// .then(function(joinedRoom) {
|
||||||
|
// node.log("Joined " + node.room);
|
||||||
|
// node.room = joinedRoom.roomId;
|
||||||
|
// node.updateConnectionState(true);
|
||||||
|
// }).catch(function(e) {
|
||||||
|
// node.warn("Error joining " + node.room + ": " + e);
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
||||||
|
node.on("input", function (msg) {
|
||||||
|
if (! node.server || ! node.server.matrixClient) {
|
||||||
|
node.warn("No matrix server configuration");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!node.server.isConnected()) {
|
||||||
|
node.warn("Matrix server connection is currently closed");
|
||||||
|
node.send([null, msg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.payload) {
|
||||||
|
node.log("Sending message " + msg.payload);
|
||||||
|
|
||||||
|
if(!msg.roomId) {
|
||||||
|
msg.roomId = node.room;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!msg.roomId) {
|
||||||
|
node.warn("Room must be specified in msg.roomId or in configuration");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo add checks to make sure required properties are filled out instead of throwing an exception
|
||||||
|
switch(msg.type || null) {
|
||||||
|
case 'react':
|
||||||
|
/**
|
||||||
|
* React to another event (message)
|
||||||
|
* msg.roomId - required
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
node.server.matrixClient.sendCompleteEvent(
|
||||||
|
msg.roomId,
|
||||||
|
{
|
||||||
|
type: 'm.reaction',
|
||||||
|
content: {
|
||||||
|
"m.relates_to": {
|
||||||
|
event_id: msg.eventId,
|
||||||
|
"key": msg.payload,
|
||||||
|
"rel_type": "m.annotation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(function(e) {
|
||||||
|
msg.eventId = e.event_id;
|
||||||
|
node.send([msg, null]);
|
||||||
|
})
|
||||||
|
.catch(function(e){
|
||||||
|
msg.matrixError = e;
|
||||||
|
node.send([null, msg]);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'image':
|
||||||
|
node.server.matrixClient.uploadContent(
|
||||||
|
msg.image.content, {
|
||||||
|
name: msg.image.filename || null, // Name to give the file on the server.
|
||||||
|
rawResponse: (msg.rawResponse || false), // Return the raw body, rather than parsing the JSON.
|
||||||
|
type: msg.image.type, // Content-type for the upload. Defaults to file.type, or applicaton/octet-stream.
|
||||||
|
onlyContentUri: false // Just return the content URI, rather than the whole body. Defaults to false. Ignored if opts.rawResponse is true.
|
||||||
|
}).then(function(file){
|
||||||
|
node.server.matrixClient
|
||||||
|
.sendImageMessage(msg.roomId, file.content_uri, {}, msg.payload)
|
||||||
|
.then(function(imgResp) {
|
||||||
|
node.log("Image message sent: " + imgResp);
|
||||||
|
msg.eventId = e.eventId;
|
||||||
|
node.send([msg, null]);
|
||||||
|
})
|
||||||
|
.catch(function(e){
|
||||||
|
node.warn("Error sending image message " + e);
|
||||||
|
msg.matrixError = e;
|
||||||
|
node.send([null, msg]);
|
||||||
|
});
|
||||||
|
}).catch(function(e){
|
||||||
|
node.warn("Error uploading image message " + e);
|
||||||
|
msg.matrixError = e;
|
||||||
|
node.send([null, msg]);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'file':
|
||||||
|
if(!msg.file) {
|
||||||
|
node.error('msg.file must be defined to send a file');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!msg.file.type) {
|
||||||
|
node.error('msg.file.type must be set to a valid content-type header (i.e. application/pdf)');
|
||||||
|
}
|
||||||
|
|
||||||
|
node.server.matrixClient.uploadContent(
|
||||||
|
msg.file.content, {
|
||||||
|
name: msg.file.filename || null, // Name to give the file on the server.
|
||||||
|
rawResponse: (msg.rawResponse || false), // Return the raw body, rather than parsing the JSON.
|
||||||
|
type: msg.file.type, // Content-type for the upload. Defaults to file.type, or applicaton/octet-stream.
|
||||||
|
onlyContentUri: false // Just return the content URI, rather than the whole body. Defaults to false. Ignored if opts.rawResponse is true.
|
||||||
|
}).then(function(file){
|
||||||
|
const content = {
|
||||||
|
msgtype: 'm.file',
|
||||||
|
url: file.content_uri,
|
||||||
|
body: msg.payload,
|
||||||
|
};
|
||||||
|
node.server.matrixClient
|
||||||
|
.sendMessage(msg.roomId, content)
|
||||||
|
.then(function(imgResp) {
|
||||||
|
node.log("File message sent: " + imgResp);
|
||||||
|
msg.eventId = e.eventId;
|
||||||
|
node.send([msg, null]);
|
||||||
|
})
|
||||||
|
.catch(function(e){
|
||||||
|
node.warn("Error sending file message " + e);
|
||||||
|
msg.matrixError = e;
|
||||||
|
node.send([null, msg]);
|
||||||
|
});
|
||||||
|
}).catch(function(e){
|
||||||
|
node.warn("Error uploading file message " + e);
|
||||||
|
msg.matrixError = e;
|
||||||
|
node.send([null, msg]);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // default text message
|
||||||
|
node.server.matrixClient.sendTextMessage(msg.roomId, msg.payload.toString())
|
||||||
|
.then(function(e) {
|
||||||
|
node.log("Message sent: " + msg.payload);
|
||||||
|
msg.eventId = e.eventId;
|
||||||
|
node.send([msg, null]);
|
||||||
|
}).catch(function(e){
|
||||||
|
node.warn("Error sending message " + e);
|
||||||
|
msg.matrixError = e;
|
||||||
|
node.send([null, msg]);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node.warn("msg.payload is empty");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
RED.nodes.registerType("matrix-send", MatrixSendMessage);
|
||||||
|
}
|
63
src/matrix-server-config.html
Normal file
63
src/matrix-server-config.html
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<script type="text/javascript">
|
||||||
|
RED.nodes.registerType('matrix-server-config',{
|
||||||
|
category: 'config',
|
||||||
|
color: '#00b7ca',
|
||||||
|
credentials: {
|
||||||
|
userId: { type: "text", required: true },
|
||||||
|
accessToken: { type: "password", required: true },
|
||||||
|
url: { type: "text", required: true }
|
||||||
|
},
|
||||||
|
defaults: {
|
||||||
|
name: { value: null },
|
||||||
|
autoAcceptRoomInvites: { value: true }
|
||||||
|
},
|
||||||
|
icon: "matrix.png",
|
||||||
|
label: function() {
|
||||||
|
return this.name || undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-template-name="matrix-server-config">
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||||
|
<input type="text" id="node-config-input-name" placeholder="Name">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-config-input-userId"><i class="fa fa-server"></i> User ID</label>
|
||||||
|
<input type="text" placeholder="@example:matrix.org" id="node-config-input-userId">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-config-input-accessToken"><i class="fa fa-key"></i> Access Token</label>
|
||||||
|
<input type="text" id="node-config-input-accessToken">
|
||||||
|
</div>
|
||||||
|
<div class="form-tips" style="margin-bottom: 12px;">
|
||||||
|
View the <a href="javascript:$('#red-ui-tab-help-link-button').click();">node docs</a> to figure out how to generate an Access Token.
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-config-input-url"><i class="fa fa-globe"></i> Server URL</label>
|
||||||
|
<input type="text" placeholder="https://matrix.org" id="node-config-input-url">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="node-config-input-autoAcceptRoomInvites"
|
||||||
|
style="width: auto; margin-left: 125px; vertical-align: top"
|
||||||
|
/>
|
||||||
|
<label for="node-config-input-autoAcceptRoomInvites" style="width: auto">
|
||||||
|
Auto join invited rooms
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-help-name="matrix-server-config">
|
||||||
|
<p>Some useful help text to introduce the node.</p>
|
||||||
|
<h3>Outputs</h3>
|
||||||
|
<dl class="message-properties">
|
||||||
|
<dt>payload
|
||||||
|
<span class="property-type">string | buffer</span>
|
||||||
|
</dt>
|
||||||
|
<h3>Details</h3>
|
||||||
|
<p>Some more information about the node.</p>
|
||||||
|
</dl>
|
||||||
|
</script>
|
114
src/matrix-server-config.js
Normal file
114
src/matrix-server-config.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
module.exports = function(RED) {
|
||||||
|
let sdk = require("matrix-js-sdk");
|
||||||
|
|
||||||
|
function MatrixServerNode(n) {
|
||||||
|
// we should add support for getting access token automatically from username/password
|
||||||
|
// ref: https://matrix.org/docs/guides/usage-of-the-matrix-js-sdk#login-with-an-access-token
|
||||||
|
|
||||||
|
RED.nodes.createNode(this, n);
|
||||||
|
|
||||||
|
let node = this;
|
||||||
|
node.log("Initializing Matrix Server Config node");
|
||||||
|
|
||||||
|
if(!this.credentials) {
|
||||||
|
this.credentials = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connected = false;
|
||||||
|
this.name = n.name;
|
||||||
|
this.userId = this.credentials.userId;
|
||||||
|
this.url = this.credentials.url;
|
||||||
|
this.autoAcceptRoomInvites = n.autoAcceptRoomInvites;
|
||||||
|
|
||||||
|
if(!this.credentials.accessToken) {
|
||||||
|
node.log("Matrix connection failed: missing access token.");
|
||||||
|
} else if(!this.url) {
|
||||||
|
node.log("Matrix connection failed: missing server URL.");
|
||||||
|
} else if(!this.userId) {
|
||||||
|
node.log("Matrix connection failed: missing user ID.");
|
||||||
|
} else {
|
||||||
|
node.log("Initializing Matrix Server Config node");
|
||||||
|
|
||||||
|
node.matrixClient = sdk.createClient({
|
||||||
|
baseUrl: this.url,
|
||||||
|
accessToken: this.credentials.accessToken,
|
||||||
|
userId: this.userId
|
||||||
|
});
|
||||||
|
|
||||||
|
node.on('close', function(done) {
|
||||||
|
if(node.matrixClient) {
|
||||||
|
node.matrixClient.close();
|
||||||
|
node.matrixClient.stopClient();
|
||||||
|
node.setConnected(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
node.setConnected = function(connected) {
|
||||||
|
if (node.connected !== connected) {
|
||||||
|
node.connected = connected;
|
||||||
|
if (connected) {
|
||||||
|
node.emit("connected");
|
||||||
|
} else {
|
||||||
|
node.emit("disconnected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
node.isConnected = function() {
|
||||||
|
return node.connected;
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle auto-joining rooms
|
||||||
|
node.matrixClient.on("RoomMember.membership", function(event, member) {
|
||||||
|
if (member.membership === "invite" && member.userId === node.userId) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
node.matrixClient.on("sync", function(state, prevState, data) {
|
||||||
|
switch (state) {
|
||||||
|
case "ERROR":
|
||||||
|
node.error("Connection to Matrix server lost");
|
||||||
|
console.log(state, prevState, data);
|
||||||
|
node.setConnected(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "STOPPED":
|
||||||
|
node.setConnected(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "SYNCING":
|
||||||
|
node.setConnected(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "PREPARED":
|
||||||
|
// the client instance is ready to be queried.
|
||||||
|
node.log("Matrix server connection ready.");
|
||||||
|
node.setConnected(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
node.log("Connecting to Matrix server...");
|
||||||
|
node.matrixClient.startClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RED.nodes.registerType("matrix-server-config", MatrixServerNode, {
|
||||||
|
credentials: {
|
||||||
|
userId: { type:"text", required: true },
|
||||||
|
accessToken: { type:"text", required: true },
|
||||||
|
url: { type: "text", required: true },
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user