First commit
This commit is contained in:
parent
f37c6b1ca8
commit
8e379587af
84
docs/README.md
Normal file
84
docs/README.md
Normal file
@ -0,0 +1,84 @@
|
||||
# Visual bot creation using Node-RED
|
||||
|
||||
It's very easy to create simple interactions with a [Matrix](https://matrix.org) chatroom without programming. Discover [Node-RED](http://nodered.org/), a visual tool to wire together APIs. We have extended Node-RED with nodes to listen and talk in Matrix chatrooms.
|
||||
|
||||
![Sample Node-RED application](nodered-twitter.png)
|
||||
|
||||
## How to install Node-RED
|
||||
|
||||
Node-RED is an open-source web application that is easy to deploy as a Docker container. You can deploy it locally on your computer using Docker. Or you can use any of the available online Docker hosting offers, such as [sloppy.io](https://sloppy.io/) or [hyper_](https://hyper.sh/).
|
||||
|
||||
The custom Docker image which includes Node-RED plus additional nodes is available on our [DockerHub repository](https://hub.docker.com/r/maski/node-red-docker/)
|
||||
|
||||
### Install Node-RED on your local machine
|
||||
|
||||
First, [install Docker](http://www.docker.com/products/docker) if you haven't done it yet.
|
||||
|
||||
Then you just need to run:
|
||||
|
||||
docker run -it -p 1880:1880 --name mynodered maski/node-red-docker
|
||||
|
||||
Let's dissect that command...
|
||||
|
||||
docker run - run this container... and build locally if necessary first.
|
||||
-it - attach a terminal session so we can see what is going on
|
||||
-p 1880:1880 - connect local port 1880 to the exposed internal port 1880
|
||||
--name mynodered - give this machine a friendly local name
|
||||
maski/node-red-docker - the image to base it on
|
||||
|
||||
|
||||
Running that command should give a terminal window with a running instance of Node-RED
|
||||
|
||||
Welcome to Node-RED
|
||||
===================
|
||||
8 Apr 12:13:44 - [info] Node-RED version: v0.14.5
|
||||
8 Apr 12:13:44 - [info] Node.js version: v4.4.7
|
||||
.... etc
|
||||
|
||||
You can then browse to `http://{host-ip}:1880` to get the familiar Node-RED desktop.
|
||||
|
||||
|
||||
### Install Node-RED on an online Docker hosting
|
||||
|
||||
A quick and easy way to host it online is to use Sloppy.io's 1-month free trial.
|
||||
Once you have created an account, go to the Dashboard and create a new project, a new service inside it, and an app within it using `maski/node-red-docker` as image path and pick a domain URI, such as `my-node-red.sloppy.zone`. Deploy the project, and you will have your own Node-RED server up and running at the selected URI.
|
||||
|
||||
## How to use Node-RED
|
||||
|
||||
You can learn how to use Node-RED by following the [Node-RED getting started guide](http://nodered.org/docs/getting-started/first-flow). You can find additional tutorials [here](http://noderedguide.com/).
|
||||
|
||||
## How to use the Matrix nodes in Node-RED
|
||||
|
||||
The package `node-red-contrib-matrixbot`, included in the abovementioned Docker image, adds 3 node types to the Node-RED palette (Matrix section, at the bottom):
|
||||
* *Matrix sender*: sends messages from your Node-RED flow to the chatroom
|
||||
* *Matrix receiver*: listens to messages in a chatroom and sends them to your Node-RED flow
|
||||
* *Matrix command*: listens only to messages starting with a specific command and sends them to your Node-RED flow
|
||||
|
||||
All of these nodes require a Matrix Configuration with the following settings:
|
||||
|
||||
* *User ID*: the user ID in the matrix server, for instance @mybot:matrix.org
|
||||
* *Access token*: the access token of the user in the matrix server
|
||||
* *Server URL*: URL of the Matrix homeserver, e.g. https://matrix.org
|
||||
* *Room ID*: ID of the chatroom to join when starting. If no room is specified, it will automatically join any room where it is invited
|
||||
|
||||
## A simple application
|
||||
|
||||
We will create a simple application that will send messages to a chatroom whenever a RSS feed gets updated:
|
||||
|
||||
* First, invite the bot to the chatroom where it will be speaking and note the room ID.
|
||||
* In Node-RED, pick the *Feedparse* node from the palette (you can filter by typing the first letters at the top) and drop it on the canvas
|
||||
* Double-click on it to configure it and then enter a RSS feed, for instance `http://rss.nytimes.com/services/xml/rss/nyt/World.xml`. Give the node a name if you want and then click *Done*.
|
||||
* Pick the *Matrix sender* node and drop it on the canvas. Link the output of the previous node to its input.
|
||||
* Now double click on it and click on the pencil next to *Connection* to configure the Matrix settings. Follow the instructions described in the previous section.
|
||||
* Once you're done, click the *Deploy* button at the top right corner and you're done!
|
||||
* If you want to show also the link in the resulting message, try adding a *function* node in the middle with the following code:
|
||||
```
|
||||
var title = msg.article.title;
|
||||
var link = msg.topic;
|
||||
msg.payload = title + " \n" + link;
|
||||
return msg;
|
||||
```
|
||||
|
||||
![Sample Node-RED application](nodered-rss.png)
|
||||
|
||||
![ ](https://ga-beacon.appspot.com/UA-63227151-9/docs/README.md?pixel)
|
BIN
docs/nodered-rss.png
Normal file
BIN
docs/nodered-rss.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 118 KiB |
BIN
docs/nodered-twitter.png
Normal file
BIN
docs/nodered-twitter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
182
matrixbot/99-matrixbot.html
Normal file
182
matrixbot/99-matrixbot.html
Normal file
@ -0,0 +1,182 @@
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------------ -->
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('matrix bot',{
|
||||
category: 'config',
|
||||
color: '#a6bbcf',
|
||||
defaults: {
|
||||
name: {type:"text"},
|
||||
room: {type:"text"},
|
||||
userId: {type:"text", required:true},
|
||||
accessToken: {type:"text", required:true},
|
||||
matrixServerURL: {type:"text", required:true, value:"https://matrix.org"}
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "matrix.png",
|
||||
label: function() {
|
||||
return this.name||"matrix bot";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-template-name="matrix bot">
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-name"><i class="icon-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-user"></i> User ID</label>
|
||||
<input type="text" 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-row">
|
||||
<label for="node-config-input-matrixServerURL"><i class="fa fa-globe"></i> Server URL</label>
|
||||
<input type="text" id="node-config-input-matrixServerURL">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-room"><i class="fa fa-comments-o"></i> Room ID (optional)</label>
|
||||
<input type="text" id="node-config-input-room">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="matrix bot">
|
||||
<p>A configuration node that holds the details to connect to a Matrix chat server</p>
|
||||
<p>
|
||||
<ul>
|
||||
<li><code>User ID</code>: the user ID in the Matrix server, for instance @mybot:matrix.org</li>
|
||||
<li><code>Access Token</code>: the access token of the user (it can be retrieved from the HTTP request URLs when connecting to the server using a browser)</li>
|
||||
<li><code>Server URL</code>: URL of the Matrix homeserver, e.g. https://matrix.org</li>
|
||||
<li><code>Room ID</code>: ID of the chatroom to join when starting. If no room is specified, it will automatically join any room where it is invited</li>
|
||||
</ul>
|
||||
</p>
|
||||
</script>
|
||||
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------------ -->
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('matrix sender',{
|
||||
category: 'output',
|
||||
color: '#a6bbcf',
|
||||
defaults: {
|
||||
name: { value: "" },
|
||||
bot: { value:"", type: "matrix bot", required: true }
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: "matrix.png",
|
||||
label: function() {
|
||||
return this.name||"matrix sender";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-template-name="matrix sender">
|
||||
<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-bot"><i class="fa fa-globe"></i> Connection</label>
|
||||
<input type="text" id="node-input-bot" placeholder="Bot">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="matrix sender">
|
||||
<p>An output Matrix node that sends a message to a chatroom.</p>
|
||||
<p>Sends the <code>msg.payload</code> property as text to the chatroom is specified either by <code>msg.roomId</code> (which is filled by receiver nodes) or in the configuration node.</p>
|
||||
</script>
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------------ -->
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('matrix receiver',{
|
||||
category: 'input',
|
||||
color: '#a6bbcf',
|
||||
defaults: {
|
||||
name: { value: "" },
|
||||
bot: { value:"", type: "matrix bot", required: true }
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "matrix.png",
|
||||
label: function() {
|
||||
return this.name||"matrix receiver";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-template-name="matrix receiver">
|
||||
<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-bot"><i class="fa fa-globe"></i> Connection</label>
|
||||
<input type="text" id="node-input-bot" placeholder="Bot">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="matrix receiver">
|
||||
<p>An input Matrix node that outputs messages received in a chatroom.</p>
|
||||
<p>Outputs a message with the properties:
|
||||
<ul>
|
||||
<li><code>msg.payload</code>: text of the received message</li>
|
||||
<li><code>msg.sender</code>: ID of the sender</li>
|
||||
<li><code>msg.roomId</code>: ID of the room where it was sent</li>
|
||||
</ul>
|
||||
</p>
|
||||
</script>
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------------ -->
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('matrix command',{
|
||||
category: 'input',
|
||||
color: '#a6bbcf',
|
||||
defaults: {
|
||||
name: { value: "" },
|
||||
bot: { value:"", type: "matrix bot", required: true },
|
||||
command: { value: "", required: true }
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "matrix.png",
|
||||
label: function() {
|
||||
return this.name||"matrix command";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-template-name="matrix command">
|
||||
<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-command"><i class="fa fa-terminal"></i> Command</label>
|
||||
<input type="text" id="node-input-command" placeholder="Command">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-bot"><i class="fa fa-globe"></i> Connection</label>
|
||||
<input type="text" id="node-input-bot" placeholder="Bot">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="matrix command">
|
||||
<p>An input Matrix node that triggers the output when a command is received from the chatroom.</p>
|
||||
<p>Outputs a message with the properties:
|
||||
<ul>
|
||||
<li><code>msg.payload</code>: rest of the received text (excluding the command)</li>
|
||||
<li><code>msg.sender</code>: ID of the sender</li>
|
||||
<li><code>msg.roomId</code>: ID of the room where it was sent</li>
|
||||
<li><code>msg.originalMessage</code>: text sent in the chat (including the command)</li>
|
||||
</ul>
|
||||
</p>
|
||||
</script>
|
||||
|
303
matrixbot/99-matrixbot.js
Normal file
303
matrixbot/99-matrixbot.js
Normal file
@ -0,0 +1,303 @@
|
||||
module.exports = function(RED) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var sdk = require("matrix-js-sdk");
|
||||
var md = require("markdown-it")();
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// The configuration node holds the configuration and credentials for all nodes.
|
||||
|
||||
function MatrixBotNode(config) {
|
||||
RED.nodes.createNode(this, config);
|
||||
|
||||
// copy "this" object in case we need it in context of callbacks of other functions.
|
||||
var node = this;
|
||||
|
||||
node.log("Initializing Matrix Bot node");
|
||||
|
||||
// Configuration options passed by Node Red
|
||||
node.userId = config.userId;
|
||||
node.room = config.room;
|
||||
|
||||
// TODO: Switch from configuration to credentials and check with if (this.credentials)
|
||||
node.accessToken = config.accessToken;
|
||||
node.matrixServerURL = config.matrixServerURL;
|
||||
|
||||
node.matrixClient = sdk.createClient({
|
||||
baseUrl: node.matrixServerURL,
|
||||
accessToken: node.accessToken,
|
||||
userId: node.userId
|
||||
});
|
||||
|
||||
// If no room is specified, join any room where we are invited
|
||||
if (!node.room || node.room === "") {
|
||||
node.matrixClient.on("RoomMember.membership", function(event, member) {
|
||||
if (member.membership === "invite" && member.userId === node.userId) {
|
||||
node.log("Trying to join room " + member.roomId);
|
||||
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 (probably because I was kicked) " + member.roomId + ": " + e);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
node.matrixClient.on("sync", function(state, prevState, data) {
|
||||
switch (state) {
|
||||
case "ERROR":
|
||||
// update UI to say "Connection Lost"
|
||||
node.warn("Connection to Matrix server lost");
|
||||
node.updateConnectionState(false);
|
||||
break;
|
||||
case "SYNCING":
|
||||
// update UI to remove any "Connection Lost" message
|
||||
node.updateConnectionState(true);
|
||||
break;
|
||||
case "PREPARED":
|
||||
// the client instance is ready to be queried.
|
||||
node.log("Synchronized to Matrix server.");
|
||||
|
||||
if (node.room) {
|
||||
node.log("Trying to join room " + node.room);
|
||||
|
||||
node.matrixClient.joinRoom(node.room, {syncRoom:false})
|
||||
.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);
|
||||
});
|
||||
} else {
|
||||
node.log("No room configured. Will only join rooms where I'm invited");
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
node.log("Connecting to Matrix server...");
|
||||
|
||||
node.matrixClient.startClient();
|
||||
|
||||
// Called when the connection state may have changed
|
||||
this.updateConnectionState = function(connected){
|
||||
if (node.connected !== connected) {
|
||||
node.connected = connected;
|
||||
if (connected) {
|
||||
node.emit("connected");
|
||||
} else {
|
||||
node.emit("disconnected");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// When Node-RED updates nodes, disconnect from server to ensure a clean start
|
||||
node.on("close", function (done) {
|
||||
node.log("Matrix configuration node closing...");
|
||||
if (node.matrixClient) {
|
||||
node.log("Disconnecting from Matrix server...");
|
||||
node.matrixClient.stopClient();
|
||||
node.updateConnectionState(false);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
RED.nodes.registerType("matrix bot", MatrixBotNode);
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// The output node sends a message to the chat.
|
||||
|
||||
function MatrixOutNode(config) {
|
||||
RED.nodes.createNode(this, config);
|
||||
|
||||
// copy "this" object in case we need it in context of callbacks of other functions.
|
||||
var node = this;
|
||||
|
||||
// Configuration options passed by Node Red
|
||||
node.configNode = RED.nodes.getNode(config.bot);
|
||||
|
||||
node.configNode.on("connected", function(){
|
||||
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||||
});
|
||||
|
||||
node.configNode.on("disconnected", function(){
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
});
|
||||
|
||||
this.on("input", function (msg) {
|
||||
if (! node.configNode || ! node.configNode.matrixClient) {
|
||||
node.warn("No configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.payload) {
|
||||
node.log("Sending message " + msg.payload);
|
||||
|
||||
var destRoom = "";
|
||||
if (msg.roomId) {
|
||||
destRoom = msg.roomId;
|
||||
} else if (node.configNode.room) {
|
||||
destRoom = node.configNode.room;
|
||||
} else {
|
||||
node.warn("Room must be specified in msg.roomId or in configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
node.configNode.matrixClient.sendHtmlMessage(destRoom, msg.payload.toString(), md.render(msg.payload.toString()))
|
||||
.then(function() {
|
||||
node.log("Message sent: " + msg.payload);
|
||||
}).catch(function(e){
|
||||
node.warn("Error sending message " + e);
|
||||
});
|
||||
} else {
|
||||
node.warn("msg.payload is empty");
|
||||
}
|
||||
});
|
||||
|
||||
this.on("close", function(done) {
|
||||
node.log("Matrix out node closing...");
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("matrix sender", MatrixOutNode);
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// The input node receives messages from the chat.
|
||||
|
||||
function MatrixInNode(config) {
|
||||
RED.nodes.createNode(this, config);
|
||||
|
||||
// copy "this" object in case we need it in context of callbacks of other functions.
|
||||
var node = this;
|
||||
node.configNode = RED.nodes.getNode(config.bot);
|
||||
|
||||
node.log("MatrixInNode initializing...");
|
||||
|
||||
if (!node.configNode) {
|
||||
node.warn("No configuration node");
|
||||
return;
|
||||
}
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
node.configNode.on("disconnected", function(){
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
});
|
||||
|
||||
node.configNode.on("connected", function() {
|
||||
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||||
node.configNode.matrixClient.on("Room.timeline", function(event, room, toStartOfTimeline, data) {
|
||||
if (toStartOfTimeline) {
|
||||
return; // don't print paginated results
|
||||
}
|
||||
if (event.getType() !== "m.room.message") {
|
||||
return; // only keep messages
|
||||
}
|
||||
if (!event.getSender() || event.getSender() === node.configNode.userId) {
|
||||
return; // ignore our own messages
|
||||
}
|
||||
if (!event.getUnsigned() || event.getUnsigned().age > 1000) {
|
||||
return; // ignore old messages
|
||||
}
|
||||
// TODO process messages other than text
|
||||
node.log(
|
||||
// the room name will update with m.room.name events automatically
|
||||
"Received chat message: (" + room.name + ") " + event.getSender() + " :: " + event.getContent().body
|
||||
);
|
||||
var msg = {
|
||||
payload: event.getContent().body,
|
||||
sender: event.getSender(),
|
||||
roomId: room.roomId
|
||||
};
|
||||
node.send(msg);
|
||||
});
|
||||
});
|
||||
|
||||
this.on("close", function(done) {
|
||||
node.log("Matrix in node closing...");
|
||||
done();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
RED.nodes.registerType("matrix receiver", MatrixInNode);
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// The command node receives messages from the chat.
|
||||
|
||||
function MatrixCommandNode(config) {
|
||||
RED.nodes.createNode(this, config);
|
||||
|
||||
// copy "this" object in case we need it in context of callbacks of other functions.
|
||||
var node = this;
|
||||
node.command = config.command;
|
||||
node.configNode = RED.nodes.getNode(config.bot);
|
||||
|
||||
node.log("MatrixCommandNode initializing...");
|
||||
|
||||
if (!node.configNode) {
|
||||
node.warn("No configuration node");
|
||||
return;
|
||||
}
|
||||
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
|
||||
node.configNode.on("disconnected", function(){
|
||||
node.status({ fill: "red", shape: "ring", text: "disconnected" });
|
||||
});
|
||||
|
||||
node.configNode.on("connected", function() {
|
||||
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||||
node.configNode.matrixClient.on("Room.timeline", function(event, room, toStartOfTimeline, data) {
|
||||
if (toStartOfTimeline) {
|
||||
return; // don't print paginated results
|
||||
}
|
||||
if (event.getType() !== "m.room.message") {
|
||||
return; // only keep messages
|
||||
}
|
||||
if (!event.getSender() || event.getSender() === node.configNode.userId) {
|
||||
return; // ignore our own messages
|
||||
}
|
||||
if (!event.getUnsigned() || event.getUnsigned().age > 1000) {
|
||||
return; // ignore old messages
|
||||
}
|
||||
// TODO process messages other than text
|
||||
node.log(
|
||||
// the room name will update with m.room.name events automatically
|
||||
"Received chat message: (" + room.name + ") " + event.getSender() + " :: " + event.getContent().body
|
||||
);
|
||||
|
||||
var message = event.getContent().body;
|
||||
|
||||
var tokens = message.split(" ");
|
||||
|
||||
if (tokens[0] == node.command) {
|
||||
node.log("Recognized command " + node.command + " Processing...");
|
||||
var remainingText = message.replace(node.command, "");
|
||||
var msg = {
|
||||
payload: remainingText,
|
||||
sender: event.getSender(),
|
||||
roomId: room.roomId,
|
||||
originalMessage: message
|
||||
};
|
||||
node.send([msg, null]);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
this.on("close", function(done) {
|
||||
node.log("Matrix command node closing...");
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("matrix command", MatrixCommandNode);
|
||||
|
||||
}
|
BIN
matrixbot/icons/matrix.png
Normal file
BIN
matrixbot/icons/matrix.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 255 B |
22
package.json
Normal file
22
package.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "node-red-contrib-matrixbot",
|
||||
"version": "0.0.3",
|
||||
"description": "Matrix bot nodes for Node-RED",
|
||||
"dependencies": {
|
||||
"matrix-js-sdk": "^2.0.0",
|
||||
"markdown-it": "^8.4.1"
|
||||
},
|
||||
"node-red": {
|
||||
"nodes": {
|
||||
"matrixbot": "matrixbot/99-matrixbot.js"
|
||||
}
|
||||
},
|
||||
"keywords": [
|
||||
"node-red", "matrix", "bot"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mlopezr/node-red-contrib-matrixbot"
|
||||
},
|
||||
"author": "nobody@nowhere"
|
||||
}
|
Loading…
Reference in New Issue
Block a user