mirror of
https://github.com/node-red/node-red-nodes.git
synced 2023-10-10 13:36:58 +02:00
replace xmpp-simple with @xmpp/client (#664)
* replace xmpp-simple with @xmpp/client * indent at 4 * remove trailing commas
This commit is contained in:
parent
2d0c8f0fb0
commit
e334040dd7
@ -133,8 +133,8 @@
|
|||||||
RED.nodes.registerType('xmpp-server',{
|
RED.nodes.registerType('xmpp-server',{
|
||||||
category: 'config',
|
category: 'config',
|
||||||
defaults: {
|
defaults: {
|
||||||
// server: {required:true},
|
server: {required:false},
|
||||||
// port: {value:5222,required:true,validate:RED.validators.number()},
|
port: {value:5222,required:false,validate:RED.validators.number()},
|
||||||
user: {type:"text"},
|
user: {type:"text"},
|
||||||
nickname: {value:""}
|
nickname: {value:""}
|
||||||
},
|
},
|
||||||
|
@ -1,52 +1,163 @@
|
|||||||
|
|
||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var XMPP = require('simple-xmpp');
|
const {client, xml, jid} = require('@xmpp/client')
|
||||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
|
||||||
|
const LOGITALL=false;
|
||||||
|
|
||||||
function XMPPServerNode(n) {
|
function XMPPServerNode(n) {
|
||||||
RED.nodes.createNode(this,n);
|
RED.nodes.createNode(this,n);
|
||||||
this.server = n.server;
|
|
||||||
this.port = n.port;
|
|
||||||
this.nickname = n.nickname;
|
this.nickname = n.nickname;
|
||||||
this.username = n.user;
|
this.jid = n.user;
|
||||||
|
this.username = n.user.split('@')[0];
|
||||||
|
// The user may elect to just specify the jid in the settings,
|
||||||
|
// in which case extract the server from the jid and default the port
|
||||||
|
if("undefined" === typeof n.server || n.server === ""){
|
||||||
|
this.server = n.user.split('@')[1];
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
this.server = n.server;
|
||||||
|
}
|
||||||
|
if("undefined" === typeof n.port || n.port === ""){
|
||||||
|
this.port = 5222;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
this.port = n.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The password is obfuscated and stored in a separate location
|
||||||
var credentials = this.credentials;
|
var credentials = this.credentials;
|
||||||
if (credentials) {
|
if (credentials) {
|
||||||
this.password = credentials.password;
|
this.password = credentials.password;
|
||||||
}
|
}
|
||||||
this.client = new XMPP.SimpleXMPP();
|
// The basic xmpp client object, this will be referred to as "xmpp" in the nodes.
|
||||||
|
// note we're not actually connecting here.
|
||||||
|
this.client = client({
|
||||||
|
service: 'xmpp://' + this.server + ':' + this.port,
|
||||||
|
username: this.username,
|
||||||
|
password: this.password
|
||||||
|
});
|
||||||
|
|
||||||
|
// helper variable for checking against later, maybe we should be using the client
|
||||||
|
// object directly...
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
|
// store the nodes that have us as config so we know when to tear it all down.
|
||||||
|
this.users = {};
|
||||||
|
// helper variable, because "this" changes definition inside a callback
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
this.client.con = function() {
|
// function for a node to tell us it has us as config
|
||||||
if (that.connected === false ) {
|
this.register = function(xmppThat) {
|
||||||
that.connected = true;
|
if (RED.settings.verbose || LOGITALL) {that.log("registering "+xmppThat.id);}
|
||||||
that.client.connect({
|
that.users[xmppThat.id] = xmppThat;
|
||||||
jid : that.username,
|
// So we could start the connection here, but we already have the logic in the thats.
|
||||||
password : that.password,
|
// if (Object.keys(that.users).length === 1) {
|
||||||
host : that.server,
|
// this.client.start();
|
||||||
port : that.port,
|
// }
|
||||||
//skipPresence : true,
|
};
|
||||||
reconnect : true,
|
|
||||||
preferred : "PLAIN"
|
// function for a node to tell us it's not using us anymore
|
||||||
});
|
this.deregister = function(xmppThat,done) {
|
||||||
|
if (RED.settings.verbose || LOGITALL) {that.log("deregistering "+xmppThat.id);}
|
||||||
|
delete that.users[xmppThat.id];
|
||||||
|
if (that.closing) {
|
||||||
|
return done();
|
||||||
}
|
}
|
||||||
|
if (Object.keys(that.users).length === 0) {
|
||||||
|
if (that.client && that.client.connected) {
|
||||||
|
return that.client.stop(done);
|
||||||
|
} else {
|
||||||
|
that.client.stop();
|
||||||
|
return done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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){
|
||||||
|
if (RED.settings.verbose || LOGITALL) {that.log(xmppThat.id+" sent a message to the xmpp server");}
|
||||||
|
that.lastUsed = xmppThat;
|
||||||
}
|
}
|
||||||
|
|
||||||
that.client.on('online', function(data) {
|
|
||||||
|
// 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) =>{
|
||||||
|
if (stanza.is('message')) {
|
||||||
|
if (stanza.attrs.type == 'error') {
|
||||||
|
if (RED.settings.verbose || LOGITALL) {
|
||||||
|
that.log("Received error");
|
||||||
|
that.log(stanza);
|
||||||
|
}
|
||||||
|
var err = stanza.getChild('error');
|
||||||
|
if(err){
|
||||||
|
var textObj = err.getChild('text');
|
||||||
|
var text = "node-red:common.status.error";
|
||||||
|
if("undefined" !== typeof textObj){
|
||||||
|
text = textObj.getText();
|
||||||
|
}
|
||||||
|
if (RED.settings.verbose || LOGITALL) {that.log("Culprit: "+that.lastUsed);}
|
||||||
|
if("undefined" !== typeof that.lastUsed){
|
||||||
|
that.lastUsed.status({fill:"red",shape:"ring",text:text});
|
||||||
|
that.lastUsed.warn(text);
|
||||||
|
}
|
||||||
|
// maybe throw the message or summit
|
||||||
|
//that.error(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(stanza.is('presence')){
|
||||||
|
if(['subscribe','subscribed','unsubscribe','unsubscribed'].indexOf(stanza.attrs.type) > -1){
|
||||||
|
if (RED.settings.verbose || LOGITALL) {that.log("got a subscription based message");}
|
||||||
|
switch(stanza.attrs.type){
|
||||||
|
case 'subscribe':
|
||||||
|
// they're asking for permission let's just say yes
|
||||||
|
var response = xml('presence',
|
||||||
|
{type:'subscribed', to:stanza.attrs.from});
|
||||||
|
// if an error comes back we can't really blame anyone else
|
||||||
|
that.used(that);
|
||||||
|
that.client.send(response);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
that.log("Was told we've "+stanza.attrs.type+" from "+stanza.attrs.from+" but we don't really care");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 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 => {
|
||||||
|
// that.warn(err);
|
||||||
|
// that.warn(err.stack);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// this gets called when we've completed the connection
|
||||||
|
this.client.on('online', async address => {
|
||||||
|
// provide some presence so people can see we're online
|
||||||
that.connected = true;
|
that.connected = true;
|
||||||
that.client.setPresence('online', data.jid.user+' is online');
|
await that.client.send(xml('presence'));
|
||||||
that.log('connected as '+data.jid.user+' to '+data.jid._domain+":5222");
|
// await that.client.send(xml('presence', {type: 'available'},xml('status', {}, 'available')));
|
||||||
|
if (RED.settings.verbose || LOGITALL) {that.log('connected as '+that.username+' to ' +that.server+':'+that.port);}
|
||||||
});
|
});
|
||||||
that.client.on('close', function() {
|
|
||||||
|
// if the connection has gone away, not sure why!
|
||||||
|
this.client.on('offline', () => {
|
||||||
that.connected = false;
|
that.connected = false;
|
||||||
that.log('connection closed');
|
if (RED.settings.verbose || LOGITALL) {that.log('connection closed');}
|
||||||
});
|
});
|
||||||
this.on("close", function(done) {
|
|
||||||
that.client.setPresence('offline');
|
// gets called when the node is destroyed, e.g. if N-R is being stopped.
|
||||||
that.client.disconnect();
|
this.on("close", async done => {
|
||||||
if (that.client.conn) { that.client.conn.end(); }
|
if(that.connected){
|
||||||
that.client = null;
|
await that.client.send(xml('presence', {type: 'unavailable'}));
|
||||||
|
}
|
||||||
|
that.client.stop().catch(that.error);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -69,30 +180,94 @@ module.exports = function(RED) {
|
|||||||
|
|
||||||
var xmpp = this.serverConfig.client;
|
var xmpp = this.serverConfig.client;
|
||||||
|
|
||||||
xmpp.on('online', function(data) {
|
/* connection states
|
||||||
node.status({fill:"green",shape:"dot",text:"connected"});
|
online: We are connected
|
||||||
|
offline: disconnected and will not autoretry
|
||||||
|
connecting: Socket is connecting
|
||||||
|
connect: Socket is connected
|
||||||
|
opening: Stream is opening
|
||||||
|
open: Stream is open
|
||||||
|
closing: Stream is closing
|
||||||
|
close: Stream is closed
|
||||||
|
disconnecting: Socket is disconnecting
|
||||||
|
disconnect: Socket is disconnected
|
||||||
|
*/
|
||||||
|
|
||||||
|
xmpp.on('online', async address => {
|
||||||
|
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
||||||
if ((node.join) && (node.from !== "")) {
|
if ((node.join) && (node.from !== "")) {
|
||||||
// disable chat history
|
|
||||||
var to = node.from+'/'+node.nick;
|
var to = node.from+'/'+node.nick;
|
||||||
var stanza = new xmpp.Element('presence', {"to": to}).
|
// the presence with the muc x element signifies we want to join the muc
|
||||||
c('x', { xmlns: 'http://jabber.org/protocol/muc' }).
|
// if we want to support passwords, we need to add that as a child of the x element
|
||||||
c('history', { maxstanzas:0, seconds:1 });
|
// (third argument to the x/muc/children )
|
||||||
xmpp.conn.send(stanza);
|
// We also turn off chat history (maxstanzas 0) because that's not what this node is about.
|
||||||
xmpp.join(to);
|
var stanza = xml('presence',
|
||||||
|
{"to": to},
|
||||||
|
xml("x",'http://jabber.org/protocol/muc'),
|
||||||
|
{ maxstanzas:0, seconds:1 }
|
||||||
|
);
|
||||||
|
node.serverConfig.used(node);
|
||||||
|
xmpp.send(stanza);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// xmpp.on('chat', function(from, message) {
|
xmpp.on('connecting', async address => {
|
||||||
// var msg = { topic:from, payload:message };
|
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
|
||||||
// if (!node.join && ((node.from === "") || (node.from === from))) {
|
});
|
||||||
// node.send([msg,null]);
|
xmpp.on('connect', async address => {
|
||||||
// }
|
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connected"});
|
||||||
// });
|
});
|
||||||
|
xmpp.on('opening', async address => {
|
||||||
|
node.status({fill:"grey",shape:"dot",text:"opening"});
|
||||||
|
});
|
||||||
|
xmpp.on('open', async address => {
|
||||||
|
node.status({fill:"grey",shape:"dot",text:"open"});
|
||||||
|
});
|
||||||
|
xmpp.on('closing', async address => {
|
||||||
|
node.status({fill:"grey",shape:"dot",text:"closing"});
|
||||||
|
});
|
||||||
|
xmpp.on('close', async address => {
|
||||||
|
node.status({fill:"grey",shape:"dot",text:"closed"});
|
||||||
|
});
|
||||||
|
xmpp.on('disconnecting', async address => {
|
||||||
|
node.status({fill:"grey",shape:"dot",text:"disconnecting"});
|
||||||
|
});
|
||||||
|
// we'll not add a offline catcher, as the error catcher should populate the status for us
|
||||||
|
|
||||||
xmpp.on('stanza', function(stanza) {
|
// Should we listen on other's status (chatstate) or a chatroom state (groupbuddy)?
|
||||||
|
xmpp.on('error', 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"});
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
node.error(err.errno,err);
|
||||||
|
node.status({fill:"red",shape:"ring",text:"node-red:common.status.error"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Meat of it, a stanza object contains chat messages (and other things)
|
||||||
|
xmpp.on('stanza', async (stanza) =>{
|
||||||
|
// node.log("Received 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') {
|
||||||
//console.log(stanza);
|
|
||||||
var body = stanza.getChild('body');
|
var body = stanza.getChild('body');
|
||||||
if (body) {
|
if (body) {
|
||||||
var msg = { payload:body.getText() };
|
var msg = { payload:body.getText() };
|
||||||
@ -106,74 +281,85 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(stanza.attrs.type == 'groupchat'){
|
||||||
|
const parts = stanza.attrs.from.split("/");
|
||||||
|
var conference = parts[0];
|
||||||
|
var from = parts[1];
|
||||||
|
var body = stanza.getChild('body');
|
||||||
|
var payload = "";
|
||||||
|
if("undefined" !== typeof body){
|
||||||
|
payload = body.getText();
|
||||||
|
}
|
||||||
|
var msg = { topic:from, payload:payload, room:conference };
|
||||||
|
if (stanza.attrs.from != node.nick) {
|
||||||
|
if ((node.join) && (node.from === conference)) {
|
||||||
|
node.send([msg,null]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
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.
|
||||||
|
|
||||||
xmpp.on('groupchat', function(conference, from, message, stamp) {
|
}
|
||||||
var msg = { topic:from, payload:message, room:conference };
|
else{
|
||||||
if (from != node.nick) {
|
var statusText="";
|
||||||
if ((node.join) && (node.from === conference)) {
|
if(stanza.attrs.type === 'unavailable'){
|
||||||
node.send([msg,null]);
|
// the user might not exist, but the server doesn't tell us that!
|
||||||
|
statusText = "offline";
|
||||||
|
}
|
||||||
|
var status = stanza.getChild('status');
|
||||||
|
if("undefined" !== typeof status){
|
||||||
|
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{
|
||||||
|
if (RED.settings.verbose || LOGITALL) {
|
||||||
|
node.log("not propagating blank status");
|
||||||
|
node.log(stanza);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//xmpp.on('chatstate', function(from, state) {
|
|
||||||
//console.log('%s is currently %s', from, state);
|
|
||||||
//var msg = { topic:from, payload: {presence:state} };
|
|
||||||
//node.send([null,msg]);
|
|
||||||
//});
|
|
||||||
|
|
||||||
xmpp.on('buddy', function(jid, state, statusText) {
|
|
||||||
node.log(jid+" is "+state+" : "+statusText);
|
|
||||||
var msg = { topic:jid, payload: { presence:state, status:statusText} };
|
|
||||||
node.send([null,msg]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// xmpp.on('groupbuddy', function(conference, from, state, statusText) {
|
// xmpp.on('subscribe', from => {
|
||||||
// //console.log('%s: %s is in %s state - %s',conference, from, state, statusText);
|
// xmpp.acceptSubscription(from);
|
||||||
// var msg = { topic:from, payload: { presence:state, status:statusText}, room:conference };
|
|
||||||
// });
|
// });
|
||||||
|
|
||||||
xmpp.on('error', function(err) {
|
//register with config
|
||||||
if (RED.settings.verbose) { node.log(err); }
|
this.serverConfig.register(this);
|
||||||
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 === "XMPP authentication failure") {
|
|
||||||
node.error(err,err);
|
|
||||||
node.status({fill:"red",shape:"ring",text:"XMPP authentication failure"});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
node.error(err.errno,err);
|
|
||||||
node.status({fill:"red",shape:"ring",text:"error"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
xmpp.on('subscribe', function(from) {
|
|
||||||
xmpp.acceptSubscription(from);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Now actually make the connection
|
// Now actually make the connection
|
||||||
try {
|
try {
|
||||||
node.status({fill:"grey",shape:"dot",text:"connecting"});
|
if(xmpp.status === "online"){
|
||||||
xmpp.con();
|
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
|
||||||
|
if(xmpp.status === "offline"){
|
||||||
|
xmpp.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(e) {
|
catch(e) {
|
||||||
node.error("Bad xmpp configuration");
|
node.error("Bad xmpp configuration; service: "+xmpp.options.service+" jid: "+node.serverConfig.jid);
|
||||||
node.status({fill:"red",shape:"ring",text:"not connected"});
|
node.warn(e);
|
||||||
|
node.warn(e.stack);
|
||||||
|
node.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
|
||||||
}
|
}
|
||||||
|
|
||||||
node.on("close", function() {
|
node.on("close", function(removed, done) {
|
||||||
node.status({});
|
node.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
|
||||||
|
node.serverConfig.deregister(node, done);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
RED.nodes.registerType("xmpp in",XmppInNode);
|
RED.nodes.registerType("xmpp in",XmppInNode);
|
||||||
@ -191,21 +377,63 @@ module.exports = function(RED) {
|
|||||||
|
|
||||||
var xmpp = this.serverConfig.client;
|
var xmpp = this.serverConfig.client;
|
||||||
|
|
||||||
|
/* connection states
|
||||||
|
online: We are connected
|
||||||
|
offline: disconnected and will not autoretry
|
||||||
|
connecting: Socket is connecting
|
||||||
|
connect: Socket is connected
|
||||||
|
opening: Stream is opening
|
||||||
|
open: Stream is open
|
||||||
|
closing: Stream is closing
|
||||||
|
close: Stream is closed
|
||||||
|
disconnecting: Socket is disconnecting
|
||||||
|
disconnect: Socket is disconnected
|
||||||
|
*/
|
||||||
|
|
||||||
xmpp.on('online', function(data) {
|
xmpp.on('online', function(data) {
|
||||||
node.status({fill:"green",shape:"dot",text:"connected"});
|
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
||||||
if ((node.join) && (node.from !== "")) {
|
if ((node.join) && (node.from !== "")) {
|
||||||
// disable chat history
|
// disable chat history
|
||||||
var to = node.to+'/'+node.nick;
|
var to = node.to+'/'+node.nick;
|
||||||
var stanza = new xmpp.Element('presence', {"to": to}).
|
// the presence with the muc x element signifies we want to join the muc
|
||||||
c('x', { xmlns: 'http://jabber.org/protocol/muc' }).
|
// if we want to support passwords, we need to add that as a child of the x element
|
||||||
c('history', { maxstanzas:0, seconds:1 });
|
// (third argument to the x/muc/children )
|
||||||
xmpp.conn.send(stanza);
|
// We also turn off chat history (maxstanzas 0) because that's not what this node is about.
|
||||||
xmpp.join(to);
|
var stanza = xml('presence',
|
||||||
|
{"to": to},
|
||||||
|
xml("x",'http://jabber.org/protocol/muc'),
|
||||||
|
{ maxstanzas:0, seconds:1 }
|
||||||
|
);
|
||||||
|
node.serverConfig.used(node);
|
||||||
|
xmpp.send(stanza);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
xmpp.on('connecting', async address => {
|
||||||
|
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"});
|
||||||
|
});
|
||||||
|
xmpp.on('opening', async address => {
|
||||||
|
node.status({fill:"grey",shape:"dot",text:"opening"});
|
||||||
|
});
|
||||||
|
xmpp.on('open', async address => {
|
||||||
|
node.status({fill:"grey",shape:"dot",text:"open"});
|
||||||
|
});
|
||||||
|
xmpp.on('closing', async address => {
|
||||||
|
node.status({fill:"grey",shape:"dot",text:"closing"});
|
||||||
|
});
|
||||||
|
xmpp.on('close', async address => {
|
||||||
|
node.status({fill:"grey",shape:"dot",text:"closed"});
|
||||||
|
});
|
||||||
|
xmpp.on('disconnecting', async address => {
|
||||||
|
node.status({fill:"grey",shape:"dot",text:"disconnecting"});
|
||||||
|
});
|
||||||
|
// we'll not add a offline catcher, as the error catcher should populate the status for us
|
||||||
|
|
||||||
xmpp.on('error', function(err) {
|
xmpp.on('error', function(err) {
|
||||||
if (RED.settings.verbose) { node.log(err); }
|
if (RED.settings.verbose || LOGITALL) { node.log(err); }
|
||||||
if (err.hasOwnProperty("stanza")) {
|
if (err.hasOwnProperty("stanza")) {
|
||||||
if (err.stanza.name === 'stream:error') { node.error("stream:error - bad login id/pwd ?",err); }
|
if (err.stanza.name === 'stream:error') { node.error("stream:error - bad login id/pwd ?",err); }
|
||||||
else { node.error(err.stanza.name,err); }
|
else { node.error(err.stanza.name,err); }
|
||||||
@ -216,54 +444,98 @@ module.exports = function(RED) {
|
|||||||
node.error("Timeout connecting to server",err);
|
node.error("Timeout connecting to server",err);
|
||||||
node.status({fill:"red",shape:"ring",text:"timeout"});
|
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") {
|
else if (err === "XMPP authentication failure") {
|
||||||
node.error(err,err);
|
node.error(err,err);
|
||||||
node.status({fill:"red",shape:"ring",text:"XMPP authentication failure"});
|
node.status({fill:"red",shape:"ring",text:"XMPP authentication failure"});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
node.error(err.errno,err);
|
node.error(err.errno,err);
|
||||||
node.status({fill:"red",shape:"ring",text:"error"});
|
node.status({fill:"red",shape:"ring",text:"node-red:common.status.error"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//register with config
|
||||||
|
this.serverConfig.register(this);
|
||||||
// Now actually make the connection
|
// Now actually make the connection
|
||||||
try {
|
if(xmpp.status === "online"){
|
||||||
node.status({fill:"grey",shape:"dot",text:"connecting"});
|
node.status({fill:"green",shape:"dot",text:"online"});
|
||||||
xmpp.con();
|
|
||||||
}
|
}
|
||||||
catch(e) {
|
else{
|
||||||
node.error("Bad xmpp configuration");
|
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
|
||||||
node.status({fill:"red",shape:"ring",text:"not connected"});
|
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"});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let's get down to business and actually send a message
|
||||||
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 ) {
|
||||||
xmpp.setPresence(msg.presence, msg.payload);
|
var stanza = xml('presence',
|
||||||
|
{"show":msg.presence},
|
||||||
|
xml('status',{},msg.payload));
|
||||||
|
node.serverConfig.used(node);
|
||||||
|
xmpp.send(stanza);
|
||||||
}
|
}
|
||||||
else { node.warn("Can't set presence - invalid value: "+msg.presence); }
|
else { node.warn("Can't set presence - invalid value: "+msg.presence); }
|
||||||
}
|
}
|
||||||
|
else if(msg.command){
|
||||||
|
if(msg.command === "subscribe"){
|
||||||
|
var stanza = xml('presence',
|
||||||
|
{type:'subscribe', to: msg.payload});
|
||||||
|
node.serverConfig.used(node);
|
||||||
|
xmpp.send(stanza);
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
var to = node.to || msg.topic || "";
|
var to = node.to || msg.topic || "";
|
||||||
if (to !== "") {
|
if (to !== "") {
|
||||||
|
var message;
|
||||||
|
var type = node.join? "groupchat":"chat";
|
||||||
if (node.sendAll) {
|
if (node.sendAll) {
|
||||||
xmpp.send(to, JSON.stringify(msg), node.join);
|
message = xml(
|
||||||
|
"message",
|
||||||
|
{ type: type, to: to },
|
||||||
|
xml("body", {}, JSON.stringify(msg))
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (msg.payload) {
|
else if (msg.payload) {
|
||||||
if (typeof(msg.payload) === "object") {
|
if (typeof(msg.payload) === "object") {
|
||||||
xmpp.send(to, JSON.stringify(msg.payload), node.join);
|
message = xml(
|
||||||
|
"message",
|
||||||
|
{ type: type, to: to },
|
||||||
|
xml("body", {}, JSON.stringify(msg.payload))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
xmpp.send(to, msg.payload.toString(), node.join);
|
message = xml(
|
||||||
|
"message",
|
||||||
|
{ type: type, to: to },
|
||||||
|
xml("body", {}, msg.payload.toString())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
node.serverConfig.used(node);
|
||||||
|
xmpp.send(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
node.on("close", function() {
|
node.on("close", function(removed, done) {
|
||||||
node.status({});
|
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
RED.nodes.registerType("xmpp out",XmppOutNode);
|
RED.nodes.registerType("xmpp out",XmppOutNode);
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name" : "node-red-node-xmpp",
|
"name": "node-red-node-xmpp",
|
||||||
"version" : "0.2.4",
|
"version": "0.2.4",
|
||||||
"description" : "A Node-RED node to talk to an XMPP server",
|
"description": "A Node-RED node to talk to an XMPP server",
|
||||||
"dependencies" : {
|
"dependencies": {
|
||||||
"simple-xmpp" : "^1.3.1"
|
"@xmpp/client": "^0.11.1"
|
||||||
},
|
},
|
||||||
"repository" : {
|
"repository": {
|
||||||
"type":"git",
|
"type": "git",
|
||||||
"url":"https://github.com/node-red/node-red-nodes/tree/master/social/xmpp"
|
"url": "https://github.com/node-red/node-red-nodes/tree/master/social/xmpp"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"keywords": [ "node-red", "xmpp" ],
|
"keywords": [
|
||||||
"node-red" : {
|
"node-red",
|
||||||
"nodes" : {
|
"xmpp"
|
||||||
|
],
|
||||||
|
"node-red": {
|
||||||
|
"nodes": {
|
||||||
"xmpp": "92-xmpp.js"
|
"xmpp": "92-xmpp.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user