mirror of
				https://github.com/node-red/node-red-nodes.git
				synced 2025-03-01 10:37:43 +00:00 
			
		
		
		
	Let xmpp input node join "ALL_ROOMS" automatically (if asked)
This commit is contained in:
		| @@ -17,6 +17,9 @@ | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> | ||||
|         <input type="text" id="node-input-name" placeholder="Name"> | ||||
|     </div> | ||||
|     <div class="form-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 type="text/html" data-help-name="xmpp in"> | ||||
| @@ -79,7 +82,6 @@ | ||||
| <script type="text/html" data-help-name="xmpp out"> | ||||
|     <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>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>. | ||||
|     If you do so then the <code>msg.payload</code> will set your status message.</p> | ||||
| </script> | ||||
|   | ||||
| @@ -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 || ""; | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user