mirror of
https://github.com/node-red/node-red-nodes.git
synced 2023-10-10 13:36:58 +02:00
Let xmpp input node join "ALL_ROOMS" automatically (if asked)
This commit is contained in:
parent
b1a2741fa0
commit
3ad829d75a
@ -17,6 +17,9 @@
|
|||||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||||
<input type="text" id="node-input-name" placeholder="Name">
|
<input type="text" id="node-input-name" placeholder="Name">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-tips"><b>Note:</b> By setting Buddy to "ALL_ROOMS" and ticking "Is a chat room",
|
||||||
|
the node will try to listen to all the rooms the user has access to.
|
||||||
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/html" data-help-name="xmpp in">
|
<script type="text/html" data-help-name="xmpp in">
|
||||||
@ -79,7 +82,6 @@
|
|||||||
<script type="text/html" data-help-name="xmpp out">
|
<script type="text/html" data-help-name="xmpp out">
|
||||||
<p>Connects to an XMPP server to send messages.</p>
|
<p>Connects to an XMPP server to send messages.</p>
|
||||||
<p>The <b>To</b> field is optional. If not set the <code>msg.topic</code> property of the message is used.</p>
|
<p>The <b>To</b> field is optional. If not set the <code>msg.topic</code> property of the message is used.</p>
|
||||||
<p>If you are joining a room then the <b>To</b> field must be supplied.</p>
|
|
||||||
<p>You may also send a msg with <code>msg.presence</code> to set your presence to one of <i>chat, away, dnd</i> or <i>xa</i>.
|
<p>You may also send a msg with <code>msg.presence</code> to set your presence to one of <i>chat, away, dnd</i> or <i>xa</i>.
|
||||||
If you do so then the <code>msg.payload</code> will set your status message.</p>
|
If you do so then the <code>msg.payload</code> will set your status message.</p>
|
||||||
</script>
|
</script>
|
||||||
|
@ -50,7 +50,7 @@ module.exports = function(RED) {
|
|||||||
this.connected = false;
|
this.connected = false;
|
||||||
// store the nodes that have us as config so we know when to tear it all down.
|
// store the nodes that have us as config so we know when to tear it all down.
|
||||||
this.users = {};
|
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 = {};
|
this.MUCs = {};
|
||||||
// helper variable, because "this" changes definition inside a callback
|
// helper variable, because "this" changes definition inside a callback
|
||||||
var that = this;
|
var that = this;
|
||||||
@ -75,7 +75,8 @@ module.exports = function(RED) {
|
|||||||
if (Object.keys(that.users).length === 0) {
|
if (Object.keys(that.users).length === 0) {
|
||||||
if (that.client && that.client.connected) {
|
if (that.client && that.client.connected) {
|
||||||
return that.client.stop(done);
|
return that.client.stop(done);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return done();
|
return done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,6 +85,7 @@ module.exports = function(RED) {
|
|||||||
|
|
||||||
// store the last node to use us, in case we get an error back
|
// store the last node to use us, in case we get an error back
|
||||||
this.lastUsed = undefined;
|
this.lastUsed = undefined;
|
||||||
|
|
||||||
// function for a node to tell us it has just sent a message to our server
|
// 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
|
// so we know which node to blame if it all goes Pete Tong
|
||||||
this.used = function(xmppThat) {
|
this.used = function(xmppThat) {
|
||||||
@ -91,7 +93,6 @@ module.exports = function(RED) {
|
|||||||
that.lastUsed = xmppThat;
|
that.lastUsed = xmppThat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Some errors come back as a message :-(
|
// Some errors come back as a message :-(
|
||||||
// this means we need to figure out which node might have sent it
|
// this means we need to figure out which node might have sent it
|
||||||
// we also deal with subscriptions (i.e. presence information) here
|
// we also deal with subscriptions (i.e. presence information) here
|
||||||
@ -106,17 +107,17 @@ module.exports = function(RED) {
|
|||||||
if (err) {
|
if (err) {
|
||||||
var textObj = err.getChild('text');
|
var textObj = err.getChild('text');
|
||||||
var text = "node-red:common.status.error";
|
var text = "node-red:common.status.error";
|
||||||
if ("undefined" !== typeof textObj) {
|
if (typeof textObj !== "undefined") {
|
||||||
text = textObj.getText();
|
text = textObj.getText();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
textObj = err.getChild('code');
|
textObj = err.getChild('code');
|
||||||
if ("undefined" !== typeof textObj) {
|
if (typeof textObj !== "undefined") {
|
||||||
text = textObj.getText();
|
text = textObj.getText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (RED.settings.verbose || LOGITALL) {that.log("Culprit: "+that.lastUsed.id); }
|
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.status({fill:"red",shape:"ring",text:text});
|
||||||
that.lastUsed.warn(text);
|
that.lastUsed.warn(text);
|
||||||
if (that.lastUsed.join) {
|
if (that.lastUsed.join) {
|
||||||
@ -165,12 +166,10 @@ module.exports = function(RED) {
|
|||||||
var query = stanza.getChild('query');
|
var query = stanza.getChild('query');
|
||||||
if (RED.settings.verbose || LOGITALL) {that.log("result!"); }
|
if (RED.settings.verbose || LOGITALL) {that.log("result!"); }
|
||||||
if (RED.settings.verbose || LOGITALL) {that.log(query); }
|
if (RED.settings.verbose || LOGITALL) {that.log(query); }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// We shouldn't have any errors here that the input/output nodes can't handle
|
// 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
|
// if you need to see everything though; uncomment this block
|
||||||
// this.client.on('error', err => {
|
// 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) {
|
function joinMUC(node, xmpp, name) {
|
||||||
// the presence with the muc x element signifies we want to join the muc
|
// 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
|
// if we want to support passwords, we need to add that as a child of the x element
|
||||||
@ -232,12 +249,14 @@ module.exports = function(RED) {
|
|||||||
else {
|
else {
|
||||||
var stanza = xml('presence',
|
var stanza = xml('presence',
|
||||||
{"to":name},
|
{"to":name},
|
||||||
xml("x",'http://jabber.org/protocol/muc'),
|
xml("x",'http://jabber.org/protocol/muc',
|
||||||
{ maxstanzas:0, seconds:1 }
|
xml("history", {maxstanzas:0, seconds:1}) // We don't want any history
|
||||||
|
)
|
||||||
);
|
);
|
||||||
node.serverConfig.used(node);
|
node.serverConfig.used(node);
|
||||||
node.serverConfig.MUCs[name] = "joined";
|
node.serverConfig.MUCs[name] = "joined";
|
||||||
xmpp.send(stanza);
|
xmpp.send(stanza);
|
||||||
|
if (RED.settings.verbose || LOGITALL) { node.log("JOINED",name); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,6 +267,7 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
config.MUCs = {};
|
config.MUCs = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// separated out since we want the same functionality from both in and out nodes
|
// separated out since we want the same functionality from both in and out nodes
|
||||||
function errorHandler(node, err){
|
function errorHandler(node, err){
|
||||||
if (!node.quiet) {
|
if (!node.quiet) {
|
||||||
@ -276,7 +296,6 @@ module.exports = function(RED) {
|
|||||||
node.error("Authorization error! "+err.condition,err);
|
node.error("Authorization error! "+err.condition,err);
|
||||||
node.status({fill:"red",shape:"ring",text:"XMPP authorization failure"});
|
node.status({fill:"red",shape:"ring",text:"XMPP authorization failure"});
|
||||||
}
|
}
|
||||||
|
|
||||||
// or it might have the errno set.
|
// or it might have the errno set.
|
||||||
else if (err.errno === "ETIMEDOUT") {
|
else if (err.errno === "ETIMEDOUT") {
|
||||||
node.error("Timeout connecting to server",err);
|
node.error("Timeout connecting to server",err);
|
||||||
@ -294,6 +313,7 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function XmppInNode(n) {
|
function XmppInNode(n) {
|
||||||
RED.nodes.createNode(this,n);
|
RED.nodes.createNode(this,n);
|
||||||
this.server = n.server;
|
this.server = n.server;
|
||||||
@ -306,6 +326,8 @@ module.exports = function(RED) {
|
|||||||
this.quiet = false;
|
this.quiet = false;
|
||||||
// MUC == Multi-User-Chat == chatroom
|
// 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;
|
var node = this;
|
||||||
|
|
||||||
var xmpp = this.serverConfig.client;
|
var xmpp = this.serverConfig.client;
|
||||||
@ -324,20 +346,26 @@ module.exports = function(RED) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// if we're already connected, then do the actions now, otherwise register a callback
|
// if we're already connected, then do the actions now, otherwise register a callback
|
||||||
if(xmpp.status === "online") {
|
// if (xmpp.status === "online") {
|
||||||
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
// node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
||||||
if(node.muc) {
|
// if (node.muc) {
|
||||||
joinMUC(node, xmpp, node.from+'/'+node.nick);
|
// joinMUC(node, xmpp, node.from+'/'+node.nick);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
// sod it, register it anyway, that way things will work better on a reconnect:
|
// sod it, register it anyway, that way things will work better on a reconnect:
|
||||||
xmpp.on('online', async address => {
|
xmpp.on('online', async address => {
|
||||||
node.quiet = false;
|
node.quiet = false;
|
||||||
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
||||||
if (node.muc) {
|
if (node.muc) {
|
||||||
|
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
|
// 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);
|
joinMUC(node, xmpp, node.from+'/'+node.nick);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
xmpp.on('connecting', async address => {
|
xmpp.on('connecting', async address => {
|
||||||
@ -373,7 +401,6 @@ module.exports = function(RED) {
|
|||||||
|
|
||||||
// Meat of it, a stanza object contains chat messages (and other things)
|
// Meat of it, a stanza object contains chat messages (and other things)
|
||||||
xmpp.on('stanza', async (stanza) => {
|
xmpp.on('stanza', async (stanza) => {
|
||||||
// node.log("Received stanza");
|
|
||||||
if (RED.settings.verbose || LOGITALL) {node.log(stanza); }
|
if (RED.settings.verbose || LOGITALL) {node.log(stanza); }
|
||||||
if (stanza.is('message')) {
|
if (stanza.is('message')) {
|
||||||
if (stanza.attrs.type == 'chat') {
|
if (stanza.attrs.type == 'chat') {
|
||||||
@ -397,12 +424,12 @@ module.exports = function(RED) {
|
|||||||
var from = parts[1];
|
var from = parts[1];
|
||||||
var body = stanza.getChild('body');
|
var body = stanza.getChild('body');
|
||||||
var payload = "";
|
var payload = "";
|
||||||
if ("undefined" !== typeof body) {
|
if (typeof body !== "undefined") {
|
||||||
payload = body.getText();
|
payload = body.getText();
|
||||||
}
|
}
|
||||||
var msg = { topic:from, payload:payload, room:conference };
|
var msg = { topic:from, payload:payload, room:conference };
|
||||||
if (stanza.attrs.from != node.nick) {
|
if (from && stanza.attrs.from != node.nick && from != node.nick) {
|
||||||
if ((node.join) && (node.from === conference)) {
|
if (node.from.toUpperCase() === "ALL_ROOMS" || node.from === conference) {
|
||||||
node.send([msg,null]);
|
node.send([msg,null]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -411,22 +438,24 @@ module.exports = function(RED) {
|
|||||||
else if (stanza.is('presence')) {
|
else if (stanza.is('presence')) {
|
||||||
if (['subscribe','subscribed','unsubscribe','unsubscribed'].indexOf(stanza.attrs.type) > -1) {
|
if (['subscribe','subscribed','unsubscribe','unsubscribed'].indexOf(stanza.attrs.type) > -1) {
|
||||||
// this isn't for us, let the config node deal with it.
|
// 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="";
|
var statusText="";
|
||||||
if (stanza.attrs.type === 'unavailable') {
|
if (stanza.attrs.type === 'unavailable') {
|
||||||
// the user might not exist, but the server doesn't tell us that!
|
// the user might not exist, but the server doesn't tell us that!
|
||||||
statusText = "offline";
|
statusText = "offline";
|
||||||
|
state = "offline";
|
||||||
}
|
}
|
||||||
var status = stanza.getChild('status');
|
var status = stanza.getChild('status');
|
||||||
if ("undefined" !== typeof status) {
|
if (typeof status !== "undefined") {
|
||||||
statusText = status.getText();
|
statusText = status.getText();
|
||||||
}
|
}
|
||||||
// right, do we care if there's no status?
|
// right, do we care if there's no status?
|
||||||
if (statusText !== "") {
|
if (statusText !== "") {
|
||||||
var from = stanza.attrs.from;
|
var from = stanza.attrs.from;
|
||||||
var state = stanza.attrs.show;
|
|
||||||
var msg = {topic:from, payload: {presence:state, status:statusText} };
|
var msg = {topic:from, payload: {presence:state, status:statusText} };
|
||||||
node.send([null,msg]);
|
node.send([null,msg]);
|
||||||
}
|
}
|
||||||
@ -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 => {
|
// xmpp.on('subscribe', from => {
|
||||||
@ -504,18 +559,20 @@ module.exports = function(RED) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// if we're already connected, then do the actions now, otherwise register a callback
|
// if we're already connected, then do the actions now, otherwise register a callback
|
||||||
if(xmpp.status === "online") {
|
// if (xmpp.status === "online") {
|
||||||
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
// node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
||||||
if(node.muc){
|
// 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);
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
// sod it, register it anyway, that way things will work better on a reconnect:
|
// sod it, register it anyway, that way things will work better on a reconnect:
|
||||||
xmpp.on('online', function(data) {
|
xmpp.on('online', function(data) {
|
||||||
node.quiet = false;
|
node.quiet = false;
|
||||||
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
||||||
if (node.muc) {
|
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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -571,9 +628,7 @@ module.exports = function(RED) {
|
|||||||
node.on("input", function(msg) {
|
node.on("input", function(msg) {
|
||||||
if (msg.presence) {
|
if (msg.presence) {
|
||||||
if (['away', 'dnd', 'xa', 'chat'].indexOf(msg.presence) > -1 ) {
|
if (['away', 'dnd', 'xa', 'chat'].indexOf(msg.presence) > -1 ) {
|
||||||
var stanza = xml('presence',
|
var stanza = xml('presence', {"show":msg.presence}, xml('status', {}, msg.payload));
|
||||||
{"show":msg.presence},
|
|
||||||
xml('status',{},msg.payload));
|
|
||||||
node.serverConfig.used(node);
|
node.serverConfig.used(node);
|
||||||
xmpp.send(stanza);
|
xmpp.send(stanza);
|
||||||
}
|
}
|
||||||
@ -581,8 +636,7 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
else if (msg.command) {
|
else if (msg.command) {
|
||||||
if (msg.command === "subscribe") {
|
if (msg.command === "subscribe") {
|
||||||
var stanza = xml('presence',
|
var stanza = xml('presence', {type:'subscribe', to:msg.payload});
|
||||||
{type:'subscribe', to: msg.payload});
|
|
||||||
node.serverConfig.used(node);
|
node.serverConfig.used(node);
|
||||||
xmpp.send(stanza);
|
xmpp.send(stanza);
|
||||||
}
|
}
|
||||||
@ -591,12 +645,13 @@ module.exports = function(RED) {
|
|||||||
var stanza = xml('iq',
|
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('query', 'http://jabber.org/protocol/muc#admin',
|
||||||
xml('item',{affiliation:msg.payload})));
|
xml('item', {affiliation:msg.payload})
|
||||||
|
)
|
||||||
|
);
|
||||||
node.serverConfig.used(node);
|
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);
|
xmpp.send(stanza);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var to = node.to || msg.topic || "";
|
var to = node.to || msg.topic || "";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red-node-xmpp",
|
"name": "node-red-node-xmpp",
|
||||||
"version": "0.3.4",
|
"version": "0.4.0",
|
||||||
"description": "A Node-RED node to talk to an XMPP server",
|
"description": "A Node-RED node to talk to an XMPP server",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xmpp/client": "^0.12.0"
|
"@xmpp/client": "^0.12.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user