Skip to content

Commit

Permalink
Added Jabber outgoing service support
Browse files Browse the repository at this point in the history
  • Loading branch information
jc21 committed Mar 14, 2018
1 parent 0ad01e2 commit 2b91b4a
Show file tree
Hide file tree
Showing 32 changed files with 2,255 additions and 99 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Juxtapose

![Version](https://img.shields.io/badge/version-1.0.4-green.svg)
![Version](https://img.shields.io/badge/version-1.1.0-green.svg)
![Stars](https://img.shields.io/docker/stars/jc21/juxtapose.svg)
![Pulls](https://img.shields.io/docker/pulls/jc21/juxtapose.svg)

Expand All @@ -29,6 +29,7 @@ swamped in noise, thus improving productivity.
**Outgoing services**

- Slack (via Slack Bot)
- Jabber / XMPP / Google Hangouts


## Features
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "juxtapose",
"version": "1.0.4",
"version": "1.1.0",
"description": "Self hosted glue for internal tools",
"main": "src/backend/index.js",
"directories": {
Expand All @@ -27,6 +27,7 @@
"moment": "^2.21.0",
"mysql": "^2.14.1",
"node-rsa": "^0.4.2",
"node-xmpp": "^1.1.0",
"objection": "^0.8.9",
"path": "^0.12.7",
"slackbots": "git://github.com/jc21/slack-bot-api.git#proxy-support",
Expand Down
2 changes: 1 addition & 1 deletion src/backend/internal/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const internalService = {
.omit(omissions())
.patchAndFetchById(row_data.id, row_data)
.then(service => {
if (service.type === 'slack') {
if (service.type === 'slack' || service.type === 'jabber') {
const internalServiceWorker = require('./service_worker');
internalServiceWorker.restart();
}
Expand Down
157 changes: 136 additions & 21 deletions src/backend/internal/service_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const internalService = require('./service');
const batchflow = require('batchflow');
const slackBots = require('slackbots');
const notificationQueueModel = require('../models/notification_queue');
//const xmpp = require('simple-xmpp').SimpleXMPP;
const xmpp = require('../lib/xmpp');

const internalServiceWorker = {

Expand Down Expand Up @@ -37,6 +39,9 @@ const internalServiceWorker = {
if (typeof internalServiceWorker.services[idx].handler !== 'undefined') {
if (internalServiceWorker.services[idx].type === 'slack') {
internalServiceWorker.services[idx].handler.ws.close();

} else if (internalServiceWorker.services[idx].type === 'jabber') {
internalServiceWorker.services[idx].handler.disconnect();
}

internalServiceWorker.services[idx].handler = null;
Expand All @@ -56,7 +61,7 @@ const internalServiceWorker = {
let started = 0;

return internalService.getActiveServices()
.then((services) => {
.then(services => {
return new Promise((resolve, reject) => {
batchflow(services).sequential()
.each((i, service, next) => {
Expand All @@ -70,15 +75,28 @@ const internalServiceWorker = {
logger.service_worker('Service #' + service.id + ' ERROR: ' + err.message);
next(err);
});

} else if (service.type === 'jabber') {
internalServiceWorker.initJabber(service)
.then(() => {
started++;
next();
})
.catch(err => {
logger.service_worker('Service #' + service.id + ' ERROR: ' + err.message);
next(err);
});

} else if (service.type.match(/(.|\n)*-webhook$/im)) {
// can be ignored
next();

} else {
logger.service_worker('Service #' + service.id + ' of type "' + service.type + '" is not yet supported');
next();
}
})
.error((err) => {
.error(err => {
reject(err);
})
.end((/*results*/) => {
Expand All @@ -88,7 +106,7 @@ const internalServiceWorker = {
});
});
})
.catch((err) => {
.catch(err => {
logger.error(err);
});
},
Expand All @@ -97,8 +115,8 @@ const internalServiceWorker = {
* @param {Object} service
* @returns {Promise}
*/
initSlack: (service) => {
return new Promise((resolve, reject) => {
initSlack: service => {
return new Promise((resolve/*, reject*/) => {
logger.service_worker('Starting Slack Service #' + service.id + ': ' + service.name);

// Set global
Expand All @@ -115,16 +133,74 @@ const internalServiceWorker = {
obj.online = true;
});

obj.handler.on('close', function () {
obj.online = false;
});

resolve();
});
},

/**
* @param {Object} service
* @returns {Promise}
*/
initJabber: service => {
return new Promise((resolve, reject) => {
logger.service_worker('Starting Jabber Service #' + service.id + ': ' + service.name);

// Set global
let obj = internalServiceWorker.services['service-' + service.id] = _.clone(service);
obj.online = false;
obj.handler = new xmpp({
jid: service.data.jid,
password: service.data.password,
host: service.data.server,
port: service.data.port
});

/*
obj.handler.on('message', function () {
debug('#' + service.id + ' Slack Message:', arguments);
obj.handler.on('stanza', function(stanza) {
if (!stanza.is('presence') && !stanza.is('iq')) {
logger.info('STANZ:', stanza, stanza.toString());
}
});
*/

obj.handler.on('close', function () {
obj.handler.on('online', data => {
logger.service_worker('Service #' + service.id + ' Connected with JID: ' + data.jid.user + '@' + data.jid._domain);
obj.online = true;
});

obj.handler.on('close', () => {
logger.service_worker('Service #' + service.id + ' Closed');
obj.online = false;
});

obj.handler.on('chat', (from, message) => {
logger.service_worker('Service #' + service.id + ' Chat:', message, from);
obj.handler.send(from, 'Sorry dude, I\'m not the talkative type.');
});

obj.handler.on('error', err => {
//if (err.name !== 'presence') {
logger.error('Service #' + service.id + ' ERROR:', err);
//}
});

obj.handler.on('subscribe', from => {
logger.service_worker('Service #' + service.id + ' Accepting subscription from:', from);
obj.handler.acceptSubscription(from);
});

obj.handler.on('roster', roster => {
logger.service_worker('Service #' + service.id + ' Received Roster with ' + roster.length + ' people');
obj.roster = roster;
});


obj.handler.connect();

resolve();
});
},
Expand All @@ -133,7 +209,7 @@ const internalServiceWorker = {
* @param {Integer} service_id
* @returns {null}
*/
getService: (service_id) => {
getService: service_id => {
if (typeof internalServiceWorker.services['service-' + service_id] !== 'undefined') {
return internalServiceWorker.services['service-' + service_id];
}
Expand All @@ -145,7 +221,7 @@ const internalServiceWorker = {
* @param {Integer} service_id
* @returns {boolean}
*/
isOnline: (service_id) => {
isOnline: service_id => {
let service = internalServiceWorker.getService(service_id);
if (service && typeof service.online !== 'undefined') {
return service.online;
Expand All @@ -172,8 +248,6 @@ const internalServiceWorker = {
return new Promise((resolve, reject) => {
batchflow(notifications).sequential()
.each((i, notification, next) => {
//debug('notification:', notification);

// update row with processing
notificationQueueModel
.query()
Expand Down Expand Up @@ -205,7 +279,7 @@ const internalServiceWorker = {
})
.where('id', notification.id);
})
.catch((err) => {
.catch(err => {
// update row with error
return notificationQueueModel
.query()
Expand Down Expand Up @@ -262,14 +336,11 @@ const internalServiceWorker = {
return new Promise((resolve, reject) => {
let service = internalServiceWorker.getService(service_id);
if (service) {

let slack_options = {
icon_url: service.data.icon_url || 'https://public.jc21.com/jira-notify/apple-icon.png'
};

switch (service.type) {
case 'slack':
//debug('SLACK MESSAGE:', username, message, slack_options);
let slack_options = {
icon_url: service.data.icon_url || 'https://public.jc21.com/juxtapose/icons/default.png'
};

if (typeof message === 'object') {
slack_options = _.assign({}, slack_options, message);
Expand All @@ -278,13 +349,18 @@ const internalServiceWorker = {

service.handler.postMessageToUser(username, message, slack_options)
.fail(function (data) {
//data = { ok: false, error: 'user_not_found' }
reject(new Error(data.error));
})
.then(function (data) {
resolve(data || true);
});
break;

case 'jabber':
service.handler.send(username, message);
resolve(true);
break;

default:
reject(new Error('Service type "' + service.type + '" is not yet supported'));
break;
Expand All @@ -299,7 +375,7 @@ const internalServiceWorker = {
* @param {Integer} service_id
* @returns {Promise}
*/
getUsers: (service_id) => {
getUsers: service_id => {
return new Promise((resolve, reject) => {
let service = internalServiceWorker.getService(service_id);
if (service) {
Expand Down Expand Up @@ -332,6 +408,45 @@ const internalServiceWorker = {
}
});
break;

case 'jabber':
if (typeof service.roster !== 'undefined') {
resolve(_.sortBy(service.roster, ['name']));
} else {
reject(new Error('Roster is not set'));
}
break;

/*
service.handler.getUsers()
.fail(function (data) {
reject(new Error(data.error));
})
.then(function (data) {
if (typeof data.members !== 'undefined') {
let real_users = _.filter(data.members, function (m) {
return !m.is_bot && !m.deleted && m.id !== 'USLACKBOT';
});
let users = [];
_.map(real_users, real_user => {
users.push({
id: real_user.id,
name: real_user.name,
real_name: real_user.profile.real_name,
display_name: real_user.profile.display_name,
avatar: real_user.profile.image_24
});
});
resolve(_.sortBy(users, ['real_name', 'display_name', 'name']));
} else {
reject(new Error('Invalid response from service'));
}
});
break;
*/
default:
reject(new Error('Service type "' + service.type + '" is not yet supported'));
break;
Expand Down
Loading

0 comments on commit 2b91b4a

Please sign in to comment.