From fc2f6ed3c7dbbd689084c3f23608390db6678bc9 Mon Sep 17 00:00:00 2001
From: Andreas Martens
Date: Fri, 29 Jan 2021 16:46:15 +0000
Subject: [PATCH 01/69] join a MUC if we're already connected to server and
refactor error handling (#749)
---
social/xmpp/92-xmpp.js | 195 +++++++++++++++++++++--------------------
1 file changed, 100 insertions(+), 95 deletions(-)
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index 59e0ac36..69a0cd13 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -57,7 +57,7 @@ module.exports = function(RED) {
this.register = function(xmppThat) {
if (RED.settings.verbose || LOGITALL) {that.log("registering "+xmppThat.id); }
that.users[xmppThat.id] = xmppThat;
- // So we could start the connection here, but we already have the logic in the thats.
+ // So we could start the connection here, but we already have the logic in the nodes that takes care of that.
// if (Object.keys(that.users).length === 1) {
// this.client.start();
// }
@@ -211,6 +211,66 @@ module.exports = function(RED) {
}
});
+ function joinMUC(node, xmpp, name) {
+ // the presence with the muc x element signifies we want to join the muc
+ // if we want to support passwords, we need to add that as a child of the x element
+ // (third argument to the x/muc/children )
+ // We also turn off chat history (maxstanzas 0) because that's not what this node is about.
+ var stanza = xml('presence',
+ {"to": name},
+ xml("x",'http://jabber.org/protocol/muc'),
+ { maxstanzas:0, seconds:1 }
+ );
+ node.serverConfig.used(node);
+ xmpp.send(stanza);
+
+ }
+
+ // separated out since we want the same functionality from both in and out nodes
+ function errorHandler(node, err){
+ if (!node.quiet) {
+ node.quiet = true;
+ // if the error has a "stanza" then we've probably done something wrong and the
+ // server is unhappy with us
+ if (err.hasOwnProperty("stanza")) {
+ if (err.stanza.name === 'stream:error') { node.error("stream:error - bad login id/pwd ?",err); }
+ else { node.error(err.stanza.name,err); }
+ node.status({fill:"red",shape:"ring",text:"bad login"});
+ }
+ // The error might be a string
+ else if (err == "TimeoutError") {
+ // OK, this happens with OpenFire, suppress it.
+ node.status({fill:"grey",shape:"dot",text:"opening"});
+ node.log("Timed out! ",err);
+ // node.status({fill:"red",shape:"ring",text:"XMPP timeout"});
+ }
+ else if (err === "XMPP authentication failure") {
+ node.error(err,err);
+ node.status({fill:"red",shape:"ring",text:"XMPP authentication failure"});
+ }
+ // or it might have a name that tells us what's wrong
+ else if (err.name === "SASLError") {
+ node.error("Authorization error! "+err.condition,err);
+ node.status({fill:"red",shape:"ring",text:"XMPP authorization failure"});
+ }
+
+ // or it might have the errno set.
+ else if (err.errno === "ETIMEDOUT") {
+ node.error("Timeout connecting to server",err);
+ node.status({fill:"red",shape:"ring",text:"timeout"});
+ }
+ else if (err.errno === "ENOTFOUND") {
+ node.error("Server doesn't exist "+xmpp.options.service,err);
+ node.status({fill:"red",shape:"ring",text:"bad address"});
+ }
+ // nothing we've seen before!
+ else {
+ node.error("Unknown error: "+err,err);
+ node.status({fill:"red",shape:"ring",text:"node-red:common.status.error"});
+ }
+ }
+ }
+
function XmppInNode(n) {
RED.nodes.createNode(this,n);
this.server = n.server;
@@ -220,6 +280,9 @@ module.exports = function(RED) {
this.sendAll = n.sendObject;
// Yes, it's called "from", don't ask me why; I don't know why
this.from = n.to || "";
+ this.quiet = false;
+ // MUC == Multi-User-Chat == chatroom
+ this.muc = this.join && (this.from !== "")
var node = this;
var xmpp = this.serverConfig.client;
@@ -237,26 +300,27 @@ module.exports = function(RED) {
disconnect: Socket is disconnected
*/
- xmpp.on('online', async address => {
+ // if we're already connected, then do the actions now, otherwise register a callback
+ if(xmpp.status === "online") {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
- if ((node.join) && (node.from !== "")) {
- var to = node.from+'/'+node.nick;
- // the presence with the muc x element signifies we want to join the muc
- // if we want to support passwords, we need to add that as a child of the x element
- // (third argument to the x/muc/children )
- // We also turn off chat history (maxstanzas 0) because that's not what this node is about.
- var stanza = xml('presence',
- {"to": to},
- xml("x",'http://jabber.org/protocol/muc'),
- { maxstanzas:0, seconds:1 }
- );
- node.serverConfig.used(node);
- xmpp.send(stanza).catch(error => {node.warn("Got error when sending presence: "+error)});
+ if(node.muc) {
+ joinMUC(node, xmpp, node.from+'/'+node.nick);
+ }
+ }
+ // sod it, register it anyway, that way things will work better on a reconnect:
+ xmpp.on('online', async address => {
+ node.quiet = false;
+ node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
+ if (node.muc) {
+ // if we want to use a chatroom, we need to tell the server we want to join it
+ joinMUC(node, xmpp, node.from+'/'+node.nick);
}
});
xmpp.on('connecting', async address => {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ if(!node.quiet) {
+ node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ }
});
xmpp.on('connect', async address => {
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connected"});
@@ -281,39 +345,7 @@ module.exports = function(RED) {
// Should we listen on other's status (chatstate) or a chatroom state (groupbuddy)?
xmpp.on('error', err => {
if (RED.settings.verbose || LOGITALL) { node.log("XMPP Error: "+err); }
- if (err.hasOwnProperty("stanza")) {
- if (err.stanza.name === 'stream:error') { node.error("stream:error - bad login id/pwd ?",err); }
- else { node.error(err.stanza.name,err); }
- node.status({fill:"red",shape:"ring",text:"bad login"});
- }
- else {
- if (err.errno === "ETIMEDOUT") {
- node.error("Timeout connecting to server",err);
- node.status({fill:"red",shape:"ring",text:"timeout"});
- }
- if (err.errno === "ENOTFOUND") {
- node.error("Server doesn't exist "+xmpp.options.service,err);
- node.status({fill:"red",shape:"ring",text:"bad address"});
- }
- else if (err === "XMPP authentication failure") {
- node.error("Authentication failure! "+err,err);
- node.status({fill:"red",shape:"ring",text:"XMPP authentication failure"});
- }
- else if (err.name === "SASLError") {
- node.error("Authorization error! "+err.condition,err);
- node.status({fill:"red",shape:"ring",text:"XMPP authorization failure"});
- }
- else if (err == "TimeoutError") {
- // Suppress it!
- node.warn("Timed out! ");
- node.status({fill:"grey",shape:"dot",text:"opening"});
- //node.status({fill:"red",shape:"ring",text:"XMPP timeout"});
- }
- else {
- node.error(err,err);
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.error"});
- }
- }
+ errorHandler(node, err);
});
// Meat of it, a stanza object contains chat messages (and other things)
@@ -408,7 +440,6 @@ module.exports = function(RED) {
}
catch(e) {
node.error("Bad xmpp configuration; service: "+xmpp.options.service+" jid: "+node.serverConfig.jid);
- node.warn(e);
node.warn(e.stack);
node.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
}
@@ -429,6 +460,9 @@ module.exports = function(RED) {
this.join = n.join || false;
this.sendAll = n.sendObject;
this.to = n.to || "";
+ this.quiet = false;
+ // MUC == Multi-User-Chat == chatroom
+ this.muc = this.join && (this.to !== "")
var node = this;
var xmpp = this.serverConfig.client;
@@ -446,27 +480,26 @@ module.exports = function(RED) {
disconnect: Socket is disconnected
*/
- xmpp.on('online', function(data) {
+ // if we're already connected, then do the actions now, otherwise register a callback
+ if(xmpp.status === "online") {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
- if ((node.join) && (node.to !== "")) {
- // disable chat history
- var to = node.to+'/'+node.nick;
- // the presence with the muc x element signifies we want to join the muc
- // if we want to support passwords, we need to add that as a child of the x element
- // (third argument to the x/muc/children )
- // We also turn off chat history (maxstanzas 0) because that's not what this node is about.
- var stanza = xml('presence',
- {"to": to},
- xml("x",'http://jabber.org/protocol/muc'),
- { maxstanzas:0, seconds:1 }
- );
- node.serverConfig.used(node);
- xmpp.send(stanza);
+ if(node.muc){
+ joinMUC(node, xmpp, node.to+'/'+node.nick);
+ }
+ }
+ // sod it, register it anyway, that way things will work better on a reconnect:
+ xmpp.on('online', function(data) {
+ node.quiet = false;
+ node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
+ if (node.muc) {
+ joinMUC(node, xmpp,node.to+"/"+node.nick);
}
});
xmpp.on('connecting', async address => {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ if(!node.quiet) {
+ node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ }
});
xmpp.on('connect', async address => {
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connected"});
@@ -490,35 +523,7 @@ module.exports = function(RED) {
xmpp.on('error', function(err) {
if (RED.settings.verbose || LOGITALL) { node.log(err); }
- if (err.hasOwnProperty("stanza")) {
- if (err.stanza.name === 'stream:error') { node.error("stream:error - bad login id/pwd ?",err); }
- else { node.error(err.stanza.name,err); }
- node.status({fill:"red",shape:"ring",text:"bad login"});
- }
- else {
- if (err.errno === "ETIMEDOUT") {
- node.error("Timeout connecting to server",err);
- node.status({fill:"red",shape:"ring",text:"timeout"});
- }
- else if (err.errno === "ENOTFOUND") {
- node.error("Server doesn't exist "+xmpp.options.service,err);
- node.status({fill:"red",shape:"ring",text:"bad address"});
- }
- else if (err === "XMPP authentication failure") {
- node.error(err,err);
- node.status({fill:"red",shape:"ring",text:"XMPP authentication failure"});
- }
- else if (err == "TimeoutError") {
- // OK, this happens with OpenFire, suppress it.
- node.status({fill:"grey",shape:"dot",text:"opening"});
- node.log("Timed out! ",err);
- // node.status({fill:"red",shape:"ring",text:"XMPP timeout"});
- }
- else {
- node.error("Unknown error: "+err,err);
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.error"});
- }
- }
+ errorHandler(node, err)
});
//register with config
From 77fac0331980e8d20a73fa1b8a11ffe4d3d89fd8 Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Fri, 29 Jan 2021 16:48:54 +0000
Subject: [PATCH 02/69] bump xmpp
---
social/xmpp/package.json | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/social/xmpp/package.json b/social/xmpp/package.json
index 44381f89..09ed319b 100644
--- a/social/xmpp/package.json
+++ b/social/xmpp/package.json
@@ -1,10 +1,13 @@
{
"name": "node-red-node-xmpp",
- "version": "0.3.1",
+ "version": "0.3.2",
"description": "A Node-RED node to talk to an XMPP server",
"dependencies": {
- "@xmpp/client": "^0.11.1"
+ "@xmpp/client": "^0.12.0"
},
+ "bundledDependencies": [
+ "@xmpp/client"
+ ],
"repository": {
"type": "git",
"url": "https://github.com/node-red/node-red-nodes/tree/master/social/xmpp"
From 778fd683542c134ff16bf38a8fea68e9c772d642 Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Fri, 29 Jan 2021 16:53:50 +0000
Subject: [PATCH 03/69] add contributor
---
social/xmpp/package.json | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/social/xmpp/package.json b/social/xmpp/package.json
index 09ed319b..e9c9149b 100644
--- a/social/xmpp/package.json
+++ b/social/xmpp/package.json
@@ -26,5 +26,10 @@
"name": "Dave Conway-Jones",
"email": "ceejay@vnet.ibm.com",
"url": "http://nodered.org"
- }
+ },
+ "contributors": [
+ {
+ "name": "@tweek"
+ }
+ ]
}
From 886158485b4f9936f631804a228976324356c8fb Mon Sep 17 00:00:00 2001
From: Andreas Martens
Date: Tue, 2 Feb 2021 09:14:11 +0000
Subject: [PATCH 04/69] XMPP: ability to dynamically join chat rooms (#752)
* join a MUC if we're already connected to server and refactor error handling
* ability to dynamically join a MUC when provided as msg.topic
---
social/xmpp/92-xmpp.js | 35 ++++++++++++++++++++++++++---------
1 file changed, 26 insertions(+), 9 deletions(-)
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index 69a0cd13..c324d8d7 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -50,6 +50,8 @@ module.exports = function(RED) {
this.connected = false;
// store the nodes that have us as config so we know when to tear it all down.
this.users = {};
+ // Store the chatrooms (MUC) that we've joined (sent "presence" XML to) already
+ this.MUCs = {};
// helper variable, because "this" changes definition inside a callback
var that = this;
@@ -216,14 +218,23 @@ module.exports = function(RED) {
// if we want to support passwords, we need to add that as a child of the x element
// (third argument to the x/muc/children )
// We also turn off chat history (maxstanzas 0) because that's not what this node is about.
- var stanza = xml('presence',
- {"to": name},
- xml("x",'http://jabber.org/protocol/muc'),
- { maxstanzas:0, seconds:1 }
- );
- node.serverConfig.used(node);
- xmpp.send(stanza);
-
+ // Yes, there's a race condition, but it's not a huge problem to send two messages
+ // so we don't care.
+ if (name in node.serverConfig.MUCs) {
+ if (RED.settings.verbose || LOGITALL) {
+ node.log("already joined MUC "+name);
+ }
+ }
+ else {
+ var stanza = xml('presence',
+ {"to": name},
+ xml("x",'http://jabber.org/protocol/muc'),
+ { maxstanzas:0, seconds:1 }
+ );
+ node.serverConfig.used(node);
+ node.serverConfig.MUCs[name] = "joined";
+ xmpp.send(stanza);
+ }
}
// separated out since we want the same functionality from both in and out nodes
@@ -579,7 +590,13 @@ module.exports = function(RED) {
var to = node.to || msg.topic || "";
if (to !== "") {
var message;
- var type = node.join? "groupchat":"chat";
+ var type = "chat";
+ if (node.join) {
+ // we want to connect to groupchat / chatroom / MUC
+ type = "groupchat";
+ // joinMUC will do nothing if we're already joined
+ joinMUC(node, xmpp, to+'/'+node.nick);
+ }
if (node.sendAll) {
message = xml(
"message",
From f99e52d1fafc7ba30a65fa91288b8b5f6fa34e97 Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Tue, 2 Feb 2021 10:12:32 +0000
Subject: [PATCH 05/69] Merge branch 'master' of
https://github.com/node-red/node-red-nodes
merge upstream
---
social/xmpp/92-xmpp.js | 35 ++++++++++++++++++++++++++---------
social/xmpp/package.json | 2 +-
2 files changed, 27 insertions(+), 10 deletions(-)
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index 69a0cd13..c324d8d7 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -50,6 +50,8 @@ module.exports = function(RED) {
this.connected = false;
// store the nodes that have us as config so we know when to tear it all down.
this.users = {};
+ // Store the chatrooms (MUC) that we've joined (sent "presence" XML to) already
+ this.MUCs = {};
// helper variable, because "this" changes definition inside a callback
var that = this;
@@ -216,14 +218,23 @@ module.exports = function(RED) {
// if we want to support passwords, we need to add that as a child of the x element
// (third argument to the x/muc/children )
// We also turn off chat history (maxstanzas 0) because that's not what this node is about.
- var stanza = xml('presence',
- {"to": name},
- xml("x",'http://jabber.org/protocol/muc'),
- { maxstanzas:0, seconds:1 }
- );
- node.serverConfig.used(node);
- xmpp.send(stanza);
-
+ // Yes, there's a race condition, but it's not a huge problem to send two messages
+ // so we don't care.
+ if (name in node.serverConfig.MUCs) {
+ if (RED.settings.verbose || LOGITALL) {
+ node.log("already joined MUC "+name);
+ }
+ }
+ else {
+ var stanza = xml('presence',
+ {"to": name},
+ xml("x",'http://jabber.org/protocol/muc'),
+ { maxstanzas:0, seconds:1 }
+ );
+ node.serverConfig.used(node);
+ node.serverConfig.MUCs[name] = "joined";
+ xmpp.send(stanza);
+ }
}
// separated out since we want the same functionality from both in and out nodes
@@ -579,7 +590,13 @@ module.exports = function(RED) {
var to = node.to || msg.topic || "";
if (to !== "") {
var message;
- var type = node.join? "groupchat":"chat";
+ var type = "chat";
+ if (node.join) {
+ // we want to connect to groupchat / chatroom / MUC
+ type = "groupchat";
+ // joinMUC will do nothing if we're already joined
+ joinMUC(node, xmpp, to+'/'+node.nick);
+ }
if (node.sendAll) {
message = xml(
"message",
diff --git a/social/xmpp/package.json b/social/xmpp/package.json
index e9c9149b..4f65efd9 100644
--- a/social/xmpp/package.json
+++ b/social/xmpp/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-xmpp",
- "version": "0.3.2",
+ "version": "0.3.3",
"description": "A Node-RED node to talk to an XMPP server",
"dependencies": {
"@xmpp/client": "^0.12.0"
From 1c34e39d8a8f0fed6563e72f94fea119f4743e33 Mon Sep 17 00:00:00 2001
From: Andreas Martens
Date: Tue, 2 Feb 2021 16:55:55 +0000
Subject: [PATCH 06/69] XMPP: improve debugging and error handling (#753)
---
social/xmpp/92-xmpp.js | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index c324d8d7..4ba648ed 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -115,10 +115,14 @@ module.exports = function(RED) {
text = textObj.getText();
}
}
- if (RED.settings.verbose || LOGITALL) {that.log("Culprit: "+that.lastUsed); }
+ if (RED.settings.verbose || LOGITALL) {that.log("Culprit: "+that.lastUsed.id); }
if ("undefined" !== typeof that.lastUsed) {
that.lastUsed.status({fill:"red",shape:"ring",text:text});
that.lastUsed.warn(text);
+ if (that.lastUsed.join) {
+ // it was trying to MUC things up
+ clearMUC(that);
+ }
}
if (RED.settings.verbose || LOGITALL) {
that.log("We did wrong: "+text);
@@ -237,6 +241,13 @@ module.exports = function(RED) {
}
}
+ function clearMUC(config) {
+ //something has happened, so clear out our presence indicators
+ if (RED.settings.verbose || LOGITALL) {
+ config.log("cleared all MUC membership");
+ }
+ config.MUCs = {};
+ }
// separated out since we want the same functionality from both in and out nodes
function errorHandler(node, err){
if (!node.quiet) {
@@ -250,8 +261,9 @@ module.exports = function(RED) {
}
// The error might be a string
else if (err == "TimeoutError") {
- // OK, this happens with OpenFire, suppress it.
- node.status({fill:"grey",shape:"dot",text:"opening"});
+ // OK, this happens with OpenFire, suppress it, but invalidate MUC membership as it will need to be re-established.
+ clearMUC(node.serverConfig);
+ node.status({fill:"grey",shape:"dot",text:"TimeoutError"});
node.log("Timed out! ",err);
// node.status({fill:"red",shape:"ring",text:"XMPP timeout"});
}
@@ -271,7 +283,7 @@ module.exports = function(RED) {
node.status({fill:"red",shape:"ring",text:"timeout"});
}
else if (err.errno === "ENOTFOUND") {
- node.error("Server doesn't exist "+xmpp.options.service,err);
+ node.error("Server doesn't exist "+node.serverConfig.server,err);
node.status({fill:"red",shape:"ring",text:"bad address"});
}
// nothing we've seen before!
From bbf9a3b7e469732ef10bf38bfbd6bc72c07bb7e9 Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Tue, 2 Feb 2021 19:21:28 +0000
Subject: [PATCH 07/69] bump xmpp package
---
social/xmpp/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/social/xmpp/package.json b/social/xmpp/package.json
index 4f65efd9..ffd2b95e 100644
--- a/social/xmpp/package.json
+++ b/social/xmpp/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-xmpp",
- "version": "0.3.3",
+ "version": "0.3.4",
"description": "A Node-RED node to talk to an XMPP server",
"dependencies": {
"@xmpp/client": "^0.12.0"
From a57bf5383fad55d76709cb9ccdc6bd7384cb34b1 Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Fri, 5 Feb 2021 10:35:54 +0000
Subject: [PATCH 08/69] Daemon node handle parameters with spaces
to close #754
---
utility/daemon/daemon.js | 3 ++-
utility/daemon/package.json | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/utility/daemon/daemon.js b/utility/daemon/daemon.js
index 6d6546a6..bc231a5a 100644
--- a/utility/daemon/daemon.js
+++ b/utility/daemon/daemon.js
@@ -6,7 +6,8 @@ module.exports = function(RED) {
function DaemonNode(n) {
RED.nodes.createNode(this,n);
this.cmd = n.command;
- this.args = n.args.trim().split(" ") || [];
+ //this.args = n.args.trim().split(" ") || [];
+ this.args = n.args.trim().match(/("[^"]*")|[^ ]+/g);
this.cr = n.cr;
this.op = n.op;
this.redo = n.redo;
diff --git a/utility/daemon/package.json b/utility/daemon/package.json
index 308023c4..bfae623a 100644
--- a/utility/daemon/package.json
+++ b/utility/daemon/package.json
@@ -1,6 +1,6 @@
{
"name" : "node-red-node-daemon",
- "version" : "0.1.2",
+ "version" : "0.1.3",
"description" : "A Node-RED node that runs and monitors a long running system command.",
"dependencies" : {
},
From fbe2078324cd9e5dd1a338a7c455d53aae81aaaf Mon Sep 17 00:00:00 2001
From: Brian Orpin
Date: Sun, 7 Feb 2021 22:32:58 +0000
Subject: [PATCH 09/69] Return all feed data (#743)
---
io/emoncms/88-emoncms.html | 4 +++-
io/emoncms/88-emoncms.js | 9 ++++++---
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/io/emoncms/88-emoncms.html b/io/emoncms/88-emoncms.html
index 65789671..7fd323d7 100644
--- a/io/emoncms/88-emoncms.html
+++ b/io/emoncms/88-emoncms.html
@@ -83,7 +83,9 @@
-
@@ -66,7 +65,8 @@
});
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index 4ba648ed..60df52d0 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -15,13 +15,13 @@ module.exports = function(RED) {
if ("undefined" === typeof n.server || n.server === "") {
this.server = n.user.split('@')[1];
}
- else{
+ else {
this.server = n.server;
}
if ("undefined" === typeof n.port || n.port === "") {
this.port = 5222;
}
- else{
+ else {
this.port = parseInt(n.port);
}
@@ -50,7 +50,7 @@ module.exports = function(RED) {
this.connected = false;
// store the nodes that have us as config so we know when to tear it all down.
this.users = {};
- // Store the chatrooms (MUC) that we've joined (sent "presence" XML to) already
+ // store the chatrooms (MUC) that we've joined (sent "presence" XML to) already
this.MUCs = {};
// helper variable, because "this" changes definition inside a callback
var that = this;
@@ -75,7 +75,8 @@ module.exports = function(RED) {
if (Object.keys(that.users).length === 0) {
if (that.client && that.client.connected) {
return that.client.stop(done);
- } else {
+ }
+ else {
return done();
}
}
@@ -84,6 +85,7 @@ module.exports = function(RED) {
// store the last node to use us, in case we get an error back
this.lastUsed = undefined;
+
// function for a node to tell us it has just sent a message to our server
// so we know which node to blame if it all goes Pete Tong
this.used = function(xmppThat) {
@@ -91,11 +93,10 @@ module.exports = function(RED) {
that.lastUsed = xmppThat;
}
-
// Some errors come back as a message :-(
// this means we need to figure out which node might have sent it
// we also deal with subscriptions (i.e. presence information) here
- this.client.on('stanza', async (stanza) =>{
+ this.client.on('stanza', async (stanza) => {
if (stanza.is('message')) {
if (stanza.attrs.type == 'error') {
if (RED.settings.verbose || LOGITALL) {
@@ -106,17 +107,17 @@ module.exports = function(RED) {
if (err) {
var textObj = err.getChild('text');
var text = "node-red:common.status.error";
- if ("undefined" !== typeof textObj) {
+ if (typeof textObj !== "undefined") {
text = textObj.getText();
}
- else{
+ else {
textObj = err.getChild('code');
- if ("undefined" !== typeof textObj) {
+ if (typeof textObj !== "undefined") {
text = textObj.getText();
}
}
if (RED.settings.verbose || LOGITALL) {that.log("Culprit: "+that.lastUsed.id); }
- if ("undefined" !== typeof that.lastUsed) {
+ if (typeof that.lastUsed !== "undefined") {
that.lastUsed.status({fill:"red",shape:"ring",text:text});
that.lastUsed.warn(text);
if (that.lastUsed.join) {
@@ -165,12 +166,10 @@ module.exports = function(RED) {
var query = stanza.getChild('query');
if (RED.settings.verbose || LOGITALL) {that.log("result!"); }
if (RED.settings.verbose || LOGITALL) {that.log(query); }
-
}
}
});
-
// We shouldn't have any errors here that the input/output nodes can't handle
// if you need to see everything though; uncomment this block
// this.client.on('error', err => {
@@ -217,6 +216,24 @@ module.exports = function(RED) {
}
});
+ function getItems(thing,id,xmpp) {
+ // Now try to get a list of all items/conference rooms available on this server
+ var stanza = xml('iq',
+ {type:'get', id:id, to:thing},
+ xml('query', 'http://jabber.org/protocol/disco#items')
+ );
+ xmpp.send(stanza);
+ }
+
+ function getInfo(thing,id,xmpp) {
+ // Now try to get a list of all info about a thing
+ var stanza = xml('iq',
+ {type:'get', id:id, to:thing},
+ xml('query', 'http://jabber.org/protocol/disco#info')
+ );
+ xmpp.send(stanza);
+ }
+
function joinMUC(node, xmpp, name) {
// the presence with the muc x element signifies we want to join the muc
// if we want to support passwords, we need to add that as a child of the x element
@@ -231,13 +248,15 @@ module.exports = function(RED) {
}
else {
var stanza = xml('presence',
- {"to": name},
- xml("x",'http://jabber.org/protocol/muc'),
- { maxstanzas:0, seconds:1 }
- );
+ {"to":name},
+ xml("x",'http://jabber.org/protocol/muc',
+ xml("history", {maxstanzas:0, seconds:1}) // We don't want any history
+ )
+ );
node.serverConfig.used(node);
node.serverConfig.MUCs[name] = "joined";
xmpp.send(stanza);
+ if (RED.settings.verbose || LOGITALL) { node.log("JOINED",name); }
}
}
@@ -248,6 +267,7 @@ module.exports = function(RED) {
}
config.MUCs = {};
}
+
// separated out since we want the same functionality from both in and out nodes
function errorHandler(node, err){
if (!node.quiet) {
@@ -276,7 +296,6 @@ module.exports = function(RED) {
node.error("Authorization error! "+err.condition,err);
node.status({fill:"red",shape:"ring",text:"XMPP authorization failure"});
}
-
// or it might have the errno set.
else if (err.errno === "ETIMEDOUT") {
node.error("Timeout connecting to server",err);
@@ -293,7 +312,8 @@ module.exports = function(RED) {
}
}
}
-
+
+
function XmppInNode(n) {
RED.nodes.createNode(this,n);
this.server = n.server;
@@ -306,6 +326,8 @@ module.exports = function(RED) {
this.quiet = false;
// MUC == Multi-User-Chat == chatroom
this.muc = this.join && (this.from !== "")
+ // list of possible rooms - queried from server
+ this.roomsFound = {};
var node = this;
var xmpp = this.serverConfig.client;
@@ -324,24 +346,30 @@ module.exports = function(RED) {
*/
// if we're already connected, then do the actions now, otherwise register a callback
- if(xmpp.status === "online") {
- node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
- if(node.muc) {
- joinMUC(node, xmpp, node.from+'/'+node.nick);
- }
- }
+ // if (xmpp.status === "online") {
+ // node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
+ // if (node.muc) {
+ // joinMUC(node, xmpp, node.from+'/'+node.nick);
+ // }
+ // }
// sod it, register it anyway, that way things will work better on a reconnect:
xmpp.on('online', async address => {
node.quiet = false;
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
if (node.muc) {
- // if we want to use a chatroom, we need to tell the server we want to join it
- joinMUC(node, xmpp, node.from+'/'+node.nick);
+ if (node.from.toUpperCase() === "ALL_ROOMS") {
+ // try to get list of all rooms and join them all.
+ getItems(this.serverConfig.server,this.serverConfig.id,xmpp);
+ }
+ else {
+ // if we want to use a chatroom, we need to tell the server we want to join it
+ joinMUC(node, xmpp, node.from+'/'+node.nick);
+ }
}
});
xmpp.on('connecting', async address => {
- if(!node.quiet) {
+ if (!node.quiet) {
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
}
});
@@ -372,8 +400,7 @@ module.exports = function(RED) {
});
// Meat of it, a stanza object contains chat messages (and other things)
- xmpp.on('stanza', async (stanza) =>{
- // node.log("Received stanza");
+ xmpp.on('stanza', async (stanza) => {
if (RED.settings.verbose || LOGITALL) {node.log(stanza); }
if (stanza.is('message')) {
if (stanza.attrs.type == 'chat') {
@@ -385,7 +412,7 @@ module.exports = function(RED) {
msg.topic = stanza.attrs.from
}
else { msg.topic = ids[0]; }
- // if (RED.settings.verbose || LOGITALL) {node.log("Received a message from "+stanza.attrs.from); }
+ // if (RED.settings.verbose || LOGITALL) {node.log("Received a message from "+stanza.attrs.from); }
if (!node.join && ((node.from === "") || (node.from === stanza.attrs.to))) {
node.send([msg,null]);
}
@@ -397,12 +424,12 @@ module.exports = function(RED) {
var from = parts[1];
var body = stanza.getChild('body');
var payload = "";
- if ("undefined" !== typeof body) {
+ if (typeof body !== "undefined") {
payload = body.getText();
}
var msg = { topic:from, payload:payload, room:conference };
- if (stanza.attrs.from != node.nick) {
- if ((node.join) && (node.from === conference)) {
+ if (from && stanza.attrs.from != node.nick && from != node.nick) {
+ if (node.from.toUpperCase() === "ALL_ROOMS" || node.from === conference) {
node.send([msg,null]);
}
}
@@ -411,26 +438,28 @@ module.exports = function(RED) {
else if (stanza.is('presence')) {
if (['subscribe','subscribed','unsubscribe','unsubscribed'].indexOf(stanza.attrs.type) > -1) {
// this isn't for us, let the config node deal with it.
-
}
- else{
+ else {
+ var state = stanza.getChild('show');
+ if (state) { state = state.getText(); }
+ else { state = "available"; }
var statusText="";
if (stanza.attrs.type === 'unavailable') {
// the user might not exist, but the server doesn't tell us that!
statusText = "offline";
+ state = "offline";
}
var status = stanza.getChild('status');
- if ("undefined" !== typeof status) {
+ if (typeof status !== "undefined") {
statusText = status.getText();
}
// right, do we care if there's no status?
if (statusText !== "") {
var from = stanza.attrs.from;
- var state = stanza.attrs.show;
var msg = {topic:from, payload: {presence:state, status:statusText} };
node.send([null,msg]);
}
- else{
+ else {
if (RED.settings.verbose || LOGITALL) {
node.log("not propagating blank status");
node.log(stanza);
@@ -438,6 +467,32 @@ module.exports = function(RED) {
}
}
}
+ else if (stanza.attrs.type === 'result') {
+ // AM To-Do check for 'bind' result with our current jid
+ var query = stanza.getChild('query');
+ if (RED.settings.verbose || LOGITALL) {this.log("result!"); }
+ if (RED.settings.verbose || LOGITALL) {this.log(query); }
+
+ // handle query for list of rooms available
+ if (query && query.attrs.hasOwnProperty("xmlns") && query.attrs["xmlns"] === "http://jabber.org/protocol/disco#items") {
+ var _items = stanza.getChild('query').getChildren('item');
+ for (var i = 0; i<_items.length; i++) {
+ if ( _items[i].attrs.jid.indexOf('@') === -1 ) {
+ // if no @ in jid then it's probably the root or the room server so ask again
+ getItems(_items[i].attrs.jid,this.serverConfig.jid,xmpp);
+ }
+ else {
+ node.roomsFound[_items[i].attrs.name] = _items[i].attrs.jid;
+ var name = _items[i].attrs.jid+'/'+node.serverConfig.username;
+ if (!(name in node.serverConfig.MUCs)) {
+ if (RED.settings.verbose || LOGITALL) {this.log("Need to Join room:"+name); }
+ joinMUC(node, xmpp, name)
+ }
+ }
+ }
+ if (RED.settings.verbose || LOGITALL) {this.log("ROOMS:"+this.server+this.roomsFound); }
+ }
+ }
});
// xmpp.on('subscribe', from => {
@@ -451,7 +506,7 @@ module.exports = function(RED) {
if (xmpp.status === "online") {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
}
- else{
+ else {
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
if (xmpp.status === "offline") {
if (RED.settings.verbose || LOGITALL) {
@@ -504,23 +559,25 @@ module.exports = function(RED) {
*/
// if we're already connected, then do the actions now, otherwise register a callback
- if(xmpp.status === "online") {
- node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
- if(node.muc){
- joinMUC(node, xmpp, node.to+'/'+node.nick);
- }
- }
+ // if (xmpp.status === "online") {
+ // node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
+ // if (node.muc){
+ // // if we want to use a chatroom, we need to tell the server we want to join it
+ // joinMUC(node, xmpp, node.from+'/'+node.nick);
+ // }
+ // }
// sod it, register it anyway, that way things will work better on a reconnect:
xmpp.on('online', function(data) {
node.quiet = false;
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
if (node.muc) {
- joinMUC(node, xmpp,node.to+"/"+node.nick);
+ // if we want to use a chatroom, we need to tell the server we want to join it
+ joinMUC(node, xmpp, node.from+'/'+node.nick);
}
});
xmpp.on('connecting', async address => {
- if(!node.quiet) {
+ if (!node.quiet) {
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
}
});
@@ -555,7 +612,7 @@ module.exports = function(RED) {
if (xmpp.status === "online") {
node.status({fill:"green",shape:"dot",text:"online"});
}
- else{
+ else {
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
if (xmpp.status === "offline") {
xmpp.start().catch(error => {
@@ -571,9 +628,7 @@ module.exports = function(RED) {
node.on("input", function(msg) {
if (msg.presence) {
if (['away', 'dnd', 'xa', 'chat'].indexOf(msg.presence) > -1 ) {
- var stanza = xml('presence',
- {"show":msg.presence},
- xml('status',{},msg.payload));
+ var stanza = xml('presence', {"show":msg.presence}, xml('status', {}, msg.payload));
node.serverConfig.used(node);
xmpp.send(stanza);
}
@@ -581,22 +636,22 @@ module.exports = function(RED) {
}
else if (msg.command) {
if (msg.command === "subscribe") {
- var stanza = xml('presence',
- {type:'subscribe', to: msg.payload});
+ var stanza = xml('presence', {type:'subscribe', to:msg.payload});
node.serverConfig.used(node);
xmpp.send(stanza);
}
else if (msg.command === "get") {
var to = node.to || msg.topic || "";
var stanza = xml('iq',
- {type:'get', id:node.id, to: to},
+ {type:'get', id:node.id, to:to},
xml('query', 'http://jabber.org/protocol/muc#admin',
- xml('item',{affiliation:msg.payload})));
+ xml('item', {affiliation:msg.payload})
+ )
+ );
node.serverConfig.used(node);
if (RED.settings.verbose || LOGITALL) {node.log("sending stanza "+stanza.toString()); }
xmpp.send(stanza);
}
-
}
else {
var to = node.to || msg.topic || "";
diff --git a/social/xmpp/package.json b/social/xmpp/package.json
index ffd2b95e..7d1aa843 100644
--- a/social/xmpp/package.json
+++ b/social/xmpp/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-xmpp",
- "version": "0.3.4",
+ "version": "0.4.0",
"description": "A Node-RED node to talk to an XMPP server",
"dependencies": {
"@xmpp/client": "^0.12.0"
From 7485760db97a8e3d1d29150e50596eb8977050e8 Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Tue, 23 Feb 2021 11:37:53 +0000
Subject: [PATCH 15/69] xmpp node - use empty field to signify all rooms
---
social/xmpp/92-xmpp.html | 2 +-
social/xmpp/92-xmpp.js | 16 ++++++++--------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/social/xmpp/92-xmpp.html b/social/xmpp/92-xmpp.html
index e7659951..1b81a8ef 100644
--- a/social/xmpp/92-xmpp.html
+++ b/social/xmpp/92-xmpp.html
@@ -17,7 +17,7 @@
-
Note: By setting Buddy to "ALL_ROOMS" and ticking "Is a chat room",
+
Note: By leaving Buddy empty and ticking "Is a chat room",
the node will try to listen to all the rooms the user has access to.
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index 60df52d0..d056ef51 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -322,10 +322,10 @@ module.exports = function(RED) {
this.join = n.join || false;
this.sendAll = n.sendObject;
// Yes, it's called "from", don't ask me why; I don't know why
- this.from = n.to || "";
+ this.from = (n.to || "").trim();
this.quiet = false;
// MUC == Multi-User-Chat == chatroom
- this.muc = this.join && (this.from !== "")
+ //this.muc = this.join && (this.from !== "")
// list of possible rooms - queried from server
this.roomsFound = {};
var node = this;
@@ -356,8 +356,8 @@ module.exports = function(RED) {
xmpp.on('online', async address => {
node.quiet = false;
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
- if (node.muc) {
- if (node.from.toUpperCase() === "ALL_ROOMS") {
+ if (node.join) {
+ if (node.from === "") {
// try to get list of all rooms and join them all.
getItems(this.serverConfig.server,this.serverConfig.id,xmpp);
}
@@ -428,11 +428,11 @@ module.exports = function(RED) {
payload = body.getText();
}
var msg = { topic:from, payload:payload, room:conference };
- if (from && stanza.attrs.from != node.nick && from != node.nick) {
- if (node.from.toUpperCase() === "ALL_ROOMS" || node.from === conference) {
- node.send([msg,null]);
- }
+ //if (from && stanza.attrs.from != node.nick && from != node.nick) {
+ if (from && node.join && (node.from === "" || node.from === conference)) {
+ node.send([msg,null]);
}
+ //}
}
}
else if (stanza.is('presence')) {
From 40362ee985a52290883176acdde7c7def8cc844a Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Tue, 23 Feb 2021 12:45:55 +0000
Subject: [PATCH 16/69] Let xmpp in handle a list of rooms
---
social/xmpp/92-xmpp.html | 1 +
social/xmpp/92-xmpp.js | 13 ++++++++-----
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/social/xmpp/92-xmpp.html b/social/xmpp/92-xmpp.html
index 1b81a8ef..dbf3eb9a 100644
--- a/social/xmpp/92-xmpp.html
+++ b/social/xmpp/92-xmpp.html
@@ -19,6 +19,7 @@
Note: By leaving Buddy empty and ticking "Is a chat room",
the node will try to listen to all the rooms the user has access to.
+ You can specify multiple rooms by separating them by a :
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index d056ef51..ce6b90d9 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -322,7 +322,8 @@ module.exports = function(RED) {
this.join = n.join || false;
this.sendAll = n.sendObject;
// Yes, it's called "from", don't ask me why; I don't know why
- this.from = (n.to || "").trim();
+ // (because it's where you are asking to get messages from...)
+ this.from = ((n.to || "").split(':')).map(s => s.trim());
this.quiet = false;
// MUC == Multi-User-Chat == chatroom
//this.muc = this.join && (this.from !== "")
@@ -357,13 +358,15 @@ module.exports = function(RED) {
node.quiet = false;
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
if (node.join) {
- if (node.from === "") {
+ if (node.from[0] === "") {
// try to get list of all rooms and join them all.
getItems(this.serverConfig.server,this.serverConfig.id,xmpp);
}
else {
// if we want to use a chatroom, we need to tell the server we want to join it
- joinMUC(node, xmpp, node.from+'/'+node.nick);
+ for (var i=0; i
Date: Tue, 23 Feb 2021 22:15:57 +0000
Subject: [PATCH 17/69] add info command to xmpp nodes
---
social/xmpp/92-xmpp.js | 52 +++++++++++++++++++++++++-----------------
1 file changed, 31 insertions(+), 21 deletions(-)
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index ce6b90d9..e575ee90 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -161,12 +161,6 @@ module.exports = function(RED) {
that.lastUsed.warn(stanza.getChild('error'));
}
}
- else if (stanza.attrs.type === 'result') {
- // AM To-Do check for 'bind' result with our current jid
- var query = stanza.getChild('query');
- if (RED.settings.verbose || LOGITALL) {that.log("result!"); }
- if (RED.settings.verbose || LOGITALL) {that.log(query); }
- }
}
});
@@ -225,15 +219,6 @@ module.exports = function(RED) {
xmpp.send(stanza);
}
- function getInfo(thing,id,xmpp) {
- // Now try to get a list of all info about a thing
- var stanza = xml('iq',
- {type:'get', id:id, to:thing},
- xml('query', 'http://jabber.org/protocol/disco#info')
- );
- xmpp.send(stanza);
- }
-
function joinMUC(node, xmpp, name) {
// the presence with the muc x element signifies we want to join the muc
// if we want to support passwords, we need to add that as a child of the x element
@@ -404,7 +389,7 @@ module.exports = function(RED) {
// Meat of it, a stanza object contains chat messages (and other things)
xmpp.on('stanza', async (stanza) => {
- if (RED.settings.verbose || LOGITALL) {node.log(stanza); }
+ if (RED.settings.verbose || LOGITALL) { node.log(stanza); }
if (stanza.is('message')) {
if (stanza.attrs.type == 'chat') {
var body = stanza.getChild('body');
@@ -415,7 +400,7 @@ module.exports = function(RED) {
msg.topic = stanza.attrs.from
}
else { msg.topic = ids[0]; }
- // if (RED.settings.verbose || LOGITALL) {node.log("Received a message from "+stanza.attrs.from); }
+ // if (RED.settings.verbose || LOGITALL) { node.log("Received a message from "+stanza.attrs.from); }
if (!node.join && ((node.from[0] === "") || (node.from.includes(stanza.attrs.to)))) {
node.send([msg,null]);
}
@@ -473,8 +458,8 @@ module.exports = function(RED) {
else if (stanza.attrs.type === 'result') {
// AM To-Do check for 'bind' result with our current jid
var query = stanza.getChild('query');
- if (RED.settings.verbose || LOGITALL) {this.log("result!"); }
- if (RED.settings.verbose || LOGITALL) {this.log(query); }
+ if (RED.settings.verbose || LOGITALL) { this.log("result!"); }
+ if (RED.settings.verbose || LOGITALL) { this.log(query); }
// handle query for list of rooms available
if (query && query.attrs.hasOwnProperty("xmlns") && query.attrs["xmlns"] === "http://jabber.org/protocol/disco#items") {
@@ -495,6 +480,21 @@ module.exports = function(RED) {
}
if (RED.settings.verbose || LOGITALL) {this.log("ROOMS:"+this.server+this.roomsFound); }
}
+ if (query && query.attrs.hasOwnProperty("xmlns") && query.attrs["xmlns"] === "http://jabber.org/protocol/disco#info") {
+ var fe = [];
+ var _items = stanza.getChild('query').getChildren('feature');
+ for (var i = 0; i<_items.length; i++) {
+ fe.push(_items[i].attrs);
+ }
+ var id = []
+ var _idents = stanza.getChild('query').getChildren('identity');
+ for (var i = 0; i<_idents.length; i++) {
+ id.push(_idents[i].attrs);
+ }
+ var from = stanza.attrs.from;
+ var msg = {topic:from, payload: { identity:id, features:fe} };
+ node.send([null,msg]);
+ }
}
});
@@ -652,7 +652,17 @@ module.exports = function(RED) {
)
);
node.serverConfig.used(node);
- if (RED.settings.verbose || LOGITALL) {node.log("sending stanza "+stanza.toString()); }
+ if (RED.settings.verbose || LOGITALL) { node.log("sending stanza "+stanza.toString()); }
+ xmpp.send(stanza);
+ }
+ else if (msg.command === "info") {
+ var to = node.to || msg.topic || "";
+ var stanza = xml('iq',
+ {type:'get', id:node.id, to:to},
+ xml('query', 'http://jabber.org/protocol/disco#info')
+ );
+ node.serverConfig.used(node);
+ if (RED.settings.verbose || LOGITALL) { node.log("sending stanza "+stanza.toString()); }
xmpp.send(stanza);
}
}
@@ -698,7 +708,7 @@ module.exports = function(RED) {
});
node.on("close", function(removed, done) {
- if (RED.settings.verbose || LOGITALL) {node.log("Closing"); }
+ if (RED.settings.verbose || LOGITALL) { node.log("Closing"); }
node.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
node.serverConfig.deregister(node, done);
});
From c368e3bcd472a567885dda060c618cb94fa995dd Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Thu, 25 Feb 2021 09:05:11 +0000
Subject: [PATCH 18/69] xmpp auto create rooms correctly and auto join every 60
secs if required
---
social/xmpp/92-xmpp.js | 146 ++++++++++++++++++++++++---------------
social/xmpp/package.json | 2 +-
2 files changed, 91 insertions(+), 57 deletions(-)
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
index e575ee90..d6b2bb0c 100644
--- a/social/xmpp/92-xmpp.js
+++ b/social/xmpp/92-xmpp.js
@@ -97,6 +97,7 @@ module.exports = function(RED) {
// this means we need to figure out which node might have sent it
// we also deal with subscriptions (i.e. presence information) here
this.client.on('stanza', async (stanza) => {
+ //console.log("STAN",stanza.toString())
if (stanza.is('message')) {
if (stanza.attrs.type == 'error') {
if (RED.settings.verbose || LOGITALL) {
@@ -106,27 +107,27 @@ module.exports = function(RED) {
var err = stanza.getChild('error');
if (err) {
var textObj = err.getChild('text');
- var text = "node-red:common.status.error";
+ var text = "error";
if (typeof textObj !== "undefined") {
text = textObj.getText();
}
else {
- textObj = err.getChild('code');
+ textObj = err.getAttr('code');
if (typeof textObj !== "undefined") {
- text = textObj.getText();
+ text = textObj;
}
}
if (RED.settings.verbose || LOGITALL) {that.log("Culprit: "+that.lastUsed.id); }
if (typeof that.lastUsed !== "undefined") {
- that.lastUsed.status({fill:"red",shape:"ring",text:text});
- that.lastUsed.warn(text);
+ that.lastUsed.status({fill:"red",shape:"ring",text:"error "+text});
+ that.lastUsed.warn("Error "+text);
if (that.lastUsed.join) {
// it was trying to MUC things up
clearMUC(that);
}
}
if (RED.settings.verbose || LOGITALL) {
- that.log("We did wrong: "+text);
+ that.log("We did wrong: Error "+text);
that.log(stanza);
}
@@ -151,6 +152,24 @@ module.exports = function(RED) {
that.log("Was told we've "+stanza.attrs.type+" from "+stanza.attrs.from+" but we don't really care");
}
}
+ if (stanza.attrs.to.indexOf(that.jid) !== -1) {
+ var _x = stanza.getChild("x")
+ if (_x !== undefined) {
+ var _stat = _x.getChildren("status");
+ for (var i = 0; i<_stat.length; i++) {
+ if (_stat[i].attrs.code == 201) {
+ if (RED.settings.verbose || LOGITALL) {that.log("created new room"); }
+ var stanza = xml('iq',
+ {type:'set', id:that.id, from:that.jid, to:stanza.attrs.from.split('/')[0]},
+ xml('query', 'http://jabber.org/protocol/muc#owner',
+ xml('x', {xmlns:'jabber:x:data', type:'submit'})
+ )
+ );
+ that.client.send(stanza);
+ }
+ }
+ }
+ }
}
else if (stanza.is('iq')) {
if (RED.settings.verbose || LOGITALL) {that.log("got an iq query"); }
@@ -188,18 +207,22 @@ module.exports = function(RED) {
// gets called when the node is destroyed, e.g. if N-R is being stopped.
this.on("close", async done => {
- if (that.client.connected) {
- await that.client.send(xml('presence', {type: 'unavailable'}));
- try{
- if (RED.settings.verbose || LOGITALL) {
- that.log("Calling stop() after close, status is "+that.client.status);
- }
- await that.client.stop().then(that.log("XMPP client stopped")).catch(error=>{that.warn("Got an error whilst closing xmpp session: "+error)});
- }
- catch(e) {
- that.warn(e);
- }
+ const rooms = Object.keys(this.MUCs)
+ for (const room of rooms) {
+ await that.client.send(xml('presence', {to:room, type:'unavailable'}));
}
+ // if (that.client.connected) {
+ await that.client.send(xml('presence', {type:'unavailable'}));
+ try{
+ if (RED.settings.verbose || LOGITALL) {
+ that.log("Calling stop() after close, status is "+that.client.status);
+ }
+ await that.client.stop().then(that.log("XMPP client stopped")).catch(error=>{that.warn("Got an error whilst closing xmpp session: "+error)});
+ }
+ catch(e) {
+ that.warn(e);
+ }
+ // }
done();
});
}
@@ -227,9 +250,7 @@ module.exports = function(RED) {
// Yes, there's a race condition, but it's not a huge problem to send two messages
// so we don't care.
if (name in node.serverConfig.MUCs) {
- if (RED.settings.verbose || LOGITALL) {
- node.log("already joined MUC "+name);
- }
+ if (RED.settings.verbose || LOGITALL) { node.log("already joined MUC "+name); }
}
else {
var stanza = xml('presence',
@@ -240,8 +261,8 @@ module.exports = function(RED) {
);
node.serverConfig.used(node);
node.serverConfig.MUCs[name] = "joined";
+ if (RED.settings.verbose || LOGITALL) { node.log("JOINED "+name); }
xmpp.send(stanza);
- if (RED.settings.verbose || LOGITALL) { node.log("JOINED",name); }
}
}
@@ -293,7 +314,7 @@ module.exports = function(RED) {
// nothing we've seen before!
else {
node.error("Unknown error: "+err,err);
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.error"});
+ node.status({fill:"red",shape:"ring",text:"error"});
}
}
}
@@ -312,10 +333,21 @@ module.exports = function(RED) {
this.quiet = false;
// MUC == Multi-User-Chat == chatroom
//this.muc = this.join && (this.from !== "")
- // list of possible rooms - queried from server
- this.roomsFound = {};
var node = this;
+ var joinrooms = function() {
+ if (node.from[0] === "") {
+ // try to get list of all rooms and join them all.
+ getItems(node.serverConfig.server, node.serverConfig.id, xmpp);
+ }
+ else {
+ // if we want to use a chatroom, we need to tell the server we want to join it
+ for (var i=0; i {
node.quiet = false;
- node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
+ node.status({fill:"green",shape:"dot",text:"connected"});
if (node.join) {
- if (node.from[0] === "") {
- // try to get list of all rooms and join them all.
- getItems(this.serverConfig.server,this.serverConfig.id,xmpp);
- }
- else {
- // if we want to use a chatroom, we need to tell the server we want to join it
- for (var i=0; i {
if (!node.quiet) {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ node.status({fill:"grey",shape:"dot",text:"connecting"});
}
});
xmpp.on('connect', async address => {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connected"});
+ node.status({fill:"grey",shape:"dot",text:"connected"});
});
xmpp.on('opening', async address => {
node.status({fill:"grey",shape:"dot",text:"opening"});
@@ -374,7 +398,7 @@ module.exports = function(RED) {
node.status({fill:"grey",shape:"dot",text:"closing"});
});
xmpp.on('close', async address => {
- node.status({fill:"grey",shape:"dot",text:"closed"});
+ node.status({fill:"grey",shape:"ring",text:"closed"});
});
xmpp.on('disconnecting', async address => {
node.status({fill:"grey",shape:"dot",text:"disconnecting"});
@@ -391,6 +415,7 @@ module.exports = function(RED) {
xmpp.on('stanza', async (stanza) => {
if (RED.settings.verbose || LOGITALL) { node.log(stanza); }
if (stanza.is('message')) {
+ // console.log(stanza.toString())
if (stanza.attrs.type == 'chat') {
var body = stanza.getChild('body');
if (body) {
@@ -470,15 +495,16 @@ module.exports = function(RED) {
getItems(_items[i].attrs.jid,this.serverConfig.jid,xmpp);
}
else {
- node.roomsFound[_items[i].attrs.name] = _items[i].attrs.jid;
var name = _items[i].attrs.jid+'/'+node.serverConfig.username;
if (!(name in node.serverConfig.MUCs)) {
- if (RED.settings.verbose || LOGITALL) {this.log("Need to Join room:"+name); }
- joinMUC(node, xmpp, name)
+ if (RED.settings.verbose || LOGITALL) { node.log("Need to Join room:"+name); }
+ joinMUC(node, xmpp, name);
+ }
+ else {
+ if (RED.settings.verbose || LOGITALL) { node.log("Already joined:"+name); }
}
}
}
- if (RED.settings.verbose || LOGITALL) {this.log("ROOMS:"+this.server+this.roomsFound); }
}
if (query && query.attrs.hasOwnProperty("xmlns") && query.attrs["xmlns"] === "http://jabber.org/protocol/disco#info") {
var fe = [];
@@ -507,26 +533,30 @@ module.exports = function(RED) {
// Now actually make the connection
try {
if (xmpp.status === "online") {
- node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
+ node.status({fill:"green",shape:"dot",text:"connected"});
}
else {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ node.status({fill:"grey",shape:"dot",text:"connecting"});
if (xmpp.status === "offline") {
if (RED.settings.verbose || LOGITALL) {
node.log("starting xmpp client");
}
- xmpp.start().catch(error => {node.warn("Got error on start: "+error); node.warn("XMPP Status is now: "+xmpp.status)});
+ xmpp.start().catch(error => {
+ node.warn("Got error on start: "+error);
+ node.warn("XMPP Status is now: "+xmpp.status)
+ });
}
}
}
catch(e) {
node.error("Bad xmpp configuration; service: "+xmpp.options.service+" jid: "+node.serverConfig.jid);
node.warn(e.stack);
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
+ node.status({fill:"red",shape:"ring",text:"disconnected"});
}
node.on("close", function(removed, done) {
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
+ if (node.jointick) { clearInterval(node.jointick); }
+ node.status({fill:"grey",shape:"ring",text:"disconnected"});
node.serverConfig.deregister(node, done);
});
}
@@ -563,7 +593,7 @@ module.exports = function(RED) {
// if we're already connected, then do the actions now, otherwise register a callback
// if (xmpp.status === "online") {
- // node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
+ // node.status({fill:"green",shape:"dot",text:"connected"});
// if (node.muc){
// // if we want to use a chatroom, we need to tell the server we want to join it
// joinMUC(node, xmpp, node.from+'/'+node.nick);
@@ -572,7 +602,7 @@ module.exports = function(RED) {
// sod it, register it anyway, that way things will work better on a reconnect:
xmpp.on('online', function(data) {
node.quiet = false;
- node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
+ node.status({fill:"green",shape:"dot",text:"connected"});
if (node.muc) {
// if we want to use a chatroom, we need to tell the server we want to join it
joinMUC(node, xmpp, node.from+'/'+node.nick);
@@ -581,11 +611,11 @@ module.exports = function(RED) {
xmpp.on('connecting', async address => {
if (!node.quiet) {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ node.status({fill:"grey",shape:"dot",text:"connecting"});
}
});
xmpp.on('connect', async address => {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connected"});
+ node.status({fill:"grey",shape:"dot",text:"connected"});
});
xmpp.on('opening', async address => {
node.status({fill:"grey",shape:"dot",text:"opening"});
@@ -609,6 +639,11 @@ module.exports = function(RED) {
errorHandler(node, err)
});
+ xmpp.on('stanza', async (stanza) => {
+ // if (stanza.is('presence')) {
+ // }
+ });
+
//register with config
this.serverConfig.register(this);
// Now actually make the connection
@@ -616,13 +651,13 @@ module.exports = function(RED) {
node.status({fill:"green",shape:"dot",text:"online"});
}
else {
- node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
+ node.status({fill:"grey",shape:"dot",text:"connecting"});
if (xmpp.status === "offline") {
xmpp.start().catch(error => {
node.error("Bad xmpp configuration; service: "+xmpp.options.service+" jid: "+node.serverConfig.jid);
node.warn(error);
node.warn(error.stack);
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.error"});
+ node.status({fill:"red",shape:"ring",text:"error"});
});
}
}
@@ -683,7 +718,6 @@ module.exports = function(RED) {
{ type: type, to: to },
xml("body", {}, JSON.stringify(msg))
);
-
}
else if (msg.payload) {
if (typeof(msg.payload) === "object") {
@@ -709,7 +743,7 @@ module.exports = function(RED) {
node.on("close", function(removed, done) {
if (RED.settings.verbose || LOGITALL) { node.log("Closing"); }
- node.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
+ node.status({fill:"grey",shape:"ring",text:"disconnected"});
node.serverConfig.deregister(node, done);
});
}
diff --git a/social/xmpp/package.json b/social/xmpp/package.json
index 7d1aa843..c249eb03 100644
--- a/social/xmpp/package.json
+++ b/social/xmpp/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-xmpp",
- "version": "0.4.0",
+ "version": "0.4.1",
"description": "A Node-RED node to talk to an XMPP server",
"dependencies": {
"@xmpp/client": "^0.12.0"
From d69da748437b59fc5e8dc9298af087a1043cc761 Mon Sep 17 00:00:00 2001
From: Pablo Acosta-Serafini <63065092+pmacostapdi@users.noreply.github.com>
Date: Fri, 26 Feb 2021 09:38:28 -0500
Subject: [PATCH 19/69] timeswitch node: time zone support; do not mark as
misconfigured when sunrise/sunset not used and lat/lon not given (#757)
* timeswitch node: a) do not mark node as misconfigured if sunrise and
sunset are not used and latitude/longitude are not given. b) support for
specifying time zone when on and/or off times are not specified as
sunrise and/or sunset.
* Replaced moment dependency with spacetime
* Timezone defaults to UTC for compatibility with previous node version
---
time/timeswitch/package.json | 1 +
time/timeswitch/timeswitch.html | 646 +++++++++++++++++++++++++++++++-
time/timeswitch/timeswitch.js | 9 +-
3 files changed, 649 insertions(+), 7 deletions(-)
diff --git a/time/timeswitch/package.json b/time/timeswitch/package.json
index acaf2bf0..6d0fca3b 100644
--- a/time/timeswitch/package.json
+++ b/time/timeswitch/package.json
@@ -3,6 +3,7 @@
"version" : "0.0.8",
"description" : "A Node-RED node to provide a simple timeswitch to schedule daily on/off events.",
"dependencies" : {
+ "spacetime": "^6.12.5",
"suncalc": "^1.8.0"
},
"repository" : {
diff --git a/time/timeswitch/timeswitch.html b/time/timeswitch/timeswitch.html
index 910cca6a..52506229 100644
--- a/time/timeswitch/timeswitch.html
+++ b/time/timeswitch/timeswitch.html
@@ -214,6 +214,616 @@
+
Die Nachricht in msg.payload kann als HTML formatiert sein.
+
Die Nachricht in msg.payload kann als HTML formatiert sein.
+ Ein separater, davon abweichender Plaintext kann in msg.plaintext angegeben werden. Ansonsten wird auch msg.payload verwendet.
+ msg.plaintext wird ignoriert, wenn msg.payload kein HTML enthält.
Wenn msg.payload ein binärer Buffer ist, so wird sie in einen Nachrichten-Dateianhang (attachment) konvertiert.
Der Dateiname sollte mittels msg.filename angegeben werden.
Optional kann msg.description als Nachrichtentext hinzugefügt werden.
The payload can be html format. You may supply a separate plaintext version using msg.plaintext.
+ If you don't and msg.payload contains html, it will also be used for the plaintext.
+ msg.plaintext will be ignored if msg.payload doesn't contain html.
If the payload is a binary buffer then it will be converted to an attachment.
The filename should be set using msg.filename. Optionally msg.description can be added for the body text.
Alternatively you may provide msg.attachments which should contain an array of one or
From 1c256fd3b3038bcc53941b65e7c7f29408fee077 Mon Sep 17 00:00:00 2001
From: Dave Conway-Jones
Date: Thu, 1 Apr 2021 09:36:36 +0100
Subject: [PATCH 69/69] refix email attachments array
---
social/email/61-email.js | 7 ++++---
social/email/package.json | 2 +-
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/social/email/61-email.js b/social/email/61-email.js
index 3bb4e130..b4a51385 100644
--- a/social/email/61-email.js
+++ b/social/email/61-email.js
@@ -119,15 +119,16 @@ module.exports = function(RED) {
else {
var payload = RED.util.ensureString(msg.payload);
sendopts.text = payload; // plaintext body
- if (/<[a-z][\s\S]*>/i.test(payload)) {
+ if (/<[a-z][\s\S]*>/i.test(payload)) {
sendopts.html = payload; // html body
if (msg.hasOwnProperty("plaintext")) {
var plaintext = RED.util.ensureString(msg.plaintext);
sendopts.text = plaintext; // plaintext body - specific plaintext version
}
}
- if (msg.attachments && Array.isArray(msg.attachments)) {
- sendopts.attachments = msg.attachments;
+ if (msg.attachments) {
+ if (!Array.isArray(msg.attachments)) { sendopts.attachments = [ msg.attachments ]; }
+ else { sendopts.attachments = msg.attachments; }
for (var a=0; a < sendopts.attachments.length; a++) {
if (sendopts.attachments[a].hasOwnProperty("content")) {
if (typeof sendopts.attachments[a].content !== "string" && !Buffer.isBuffer(sendopts.attachments[a].content)) {
diff --git a/social/email/package.json b/social/email/package.json
index ed986d3e..e103ac71 100644
--- a/social/email/package.json
+++ b/social/email/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-email",
- "version": "1.10.1",
+ "version": "1.11.0",
"description": "Node-RED nodes to send and receive simple emails.",
"dependencies": {
"imap": "^0.8.19",