Skip to content

Commit

Permalink
update pre-release
Browse files Browse the repository at this point in the history
  • Loading branch information
slickplaid committed Jul 13, 2015
1 parent 6dcbe91 commit edd412f
Show file tree
Hide file tree
Showing 9 changed files with 405 additions and 3 deletions.
31 changes: 31 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.*
!/.gitignore

# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# Commenting this out is preferred by some people, see
# https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules

# Users Environment Variables
.lock-wscript
66 changes: 65 additions & 1 deletion README.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1 +1,65 @@
# Slack Mailgun Notifications
# Slack Mailgun (SlackGun)

Slack Mailgun (SlackGun) is a simple webhook system to make it easy to route [Mailgun's](http://mailgun.com) [Webhooks](https://documentation.mailgun.com/user_manual.html#webhooks) to [Slack](https://slack.com/). This was built because there existed no simple way that I saw that could map Mailgun to Slack without [paying](https://zapier.com/app/pricing) an arm and a leg for it.

Why pay for it when it's easy enough to simply build?

## Install

You can integrate SlackGun into your existing web application or use it standalone from the command line.

### Using it with Express

`npm install node-slack-mailgun`

```javascript
var SlackGun = require('node-slack-mailgun');
var slack_hook_url = 'https://hooks.slack.com/services/aaa/bbb/ccc';
var mailgun_apikey = '123abc';
var express = require('express');

var app = express();

app.use('/api/mailgun', SlackGun({
slack: { hook: slack_hook_url },
mailgun: { apikey: mailgun_apikey }
}));
```

Now any requests from Mailgun to `/api/mailgun` will be routed to slack as a message.

### Using it with the build in `http/s` Node.js Module

#1

### Using it from the command line

#2 Not yet implemented

## Usage

`node-slack-mailgun` accepts a few options when initalizing.

```javascript
var SlackGun = require('node-slack-mailgun');

SlackGun({
slack: { // Options for slack
hook: 'url', // Required. The hook URL for posting messages to slack.
options: 'object' // Optional. Options to pass to https://github.com/xoxco/node-slack#install-slack
},
mailgun: { // Options for mailgun
apikey: 'string' // Optional. Used for verifying the HMAC token sent with a request.
}
})
```

## Customize Messages Sent To Slack

#3 Not yet implemented.

## Honey-Do List

- Utilize the real time notification API for Slack. Webhooks are soo 2010.
- Send emails from Slack through Mailgun to the user that received the email by responding to a message in Slack. That'd be cool.
- Expand upon security of replay attacks and other possible security issues using Mailgun's webhooks.
85 changes: 85 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
var gulp = require('gulp');
var runSequence = require('run-sequence');
var bump = require('gulp-bump');
var gutil = require('gulp-util');
var git = require('gulp-git');
var minimist = require('minimist');
var opts = minimist(process.argv.slice(2));

gulp.task('bump-version', function() {
var type = 'patch';

if (opts._.indexOf('minor') > -1) {
type = 'minor';
} else if (opts._.indexOf('major') > -1) {
type = 'major';
}

return gulp.src(['./bower.json', './package.json'])
.pipe(bump({
type: type
}).on('error', gutil.log))
.pipe(gulp.dest('./'));
});

gulp.task('commit-changes', function() {
return gulp.src('.')
.pipe(git.commit('[Prerelease] Bumped version number', {
args: '-a'
}));
});

gulp.task('push-changes', function(cb) {
git.push('origin', 'master', cb);
});

gulp.task('create-new-tag', function(cb) {
var version = getPackageJsonVersion();
git.tag(version, 'Created Tag for version: ' + version, function(error) {
if (error) {
return cb(error);
}
git.push('origin', 'master', {
args: '--tags'
}, cb);
});

function getPackageJsonVersion() {
//We parse the json file instead of using require because require caches multiple calls so the version number won't be updated
return JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
};
});

gulp.task('release', function(callback) {
runSequence(
// 'build', // buildall

'bump-version',
'commit-changes',
'push-changes',
'create-new-tag',
function(error) {
if (error) {
console.log(error.message);
} else {
console.log('RELEASE FINISHED SUCCESSFULLY');
}
callback(error);
});
});

gulp.task('minor', function() {
return true;
});

gulp.task('major', function() {
return true;
});

gulp.task('patch', function() {
return true;
});

gulp.task('default', /*['build'],*/ function() {
return true;
});
158 changes: 158 additions & 0 deletions index.js
100644 → 100755
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*!
* node-slack-mailgun
* Copyright(c) 2015 Slick Labs, LLC
* MIT Licensed
*/

'use strict';

/**
* Module dependencies.
* @private
*/

var Debug = require('debug');
var debug = Debug('node-slack-mailgun');
var Slack = require('node-slack');
var mustache = require('mustache');
var verifyMailgun = require('./lib/mailgun-verify');
var mustacheTemplates = require('./lib/templates');
var getTemplate = require('./lib/getTemplate');

/**
* Module exports.
*/

module.exports = SlackGun;

function SlackGun(options) {
var that = this;

// Normalize our options and log/throw any problems.
that.options = getOptions(options);

// Initalize node-slack
that.slack = new Slack(options.slack.hook, options.slack.options);



return function SlackGunMiddleware(req, res, next) {
var err = [];

// Default to trusting the message if we don't
// have an api key from Mailgun to work with.
var authentic = true;

if(that.options.mailgun.apikey) {
var apikey = that.options.mailgun.apikey;
var token = req.headers.token;
var timestamp = req.headers.timestamp;
var signature = req.headers.signature;

var verification = verifyMailgun(apikey, token, timestamp, signature);

if(!verification.valid) {
authentic = false;
err.push('Invalid token response. Mailgun message could not be authenticated.');
}
}

if(!req.body) {
err.push('Unable to get body from Mailgun request.');
} else {
var parsedMessage = getMessage(req.body);

if(parsedMessage) {
that.slack.send(parsedMessage);
}
}

if(err.length) {
// We've hit an error. Let mailgun know
// not to keep hitting us with requests
res.writeHead(406, 'Not Acceptable', { 'Content-Type': 'text/html' });
res.end();

err.forEach(function(e) {
debug(e);
});
} else {
// Great Success!
// Send response to let mailgun know
res.writeHead(200, 'Success', { 'Content-Type': 'text/html' });
res.end();
}

};
};

function getMessage(body, options) {
debug('Parsing Mailgun message.');

if(typeof body === 'undefined') {
debug('Unable to parse message from Mailgun.');
return false;
}

var defaultMessage = {
channel: '#general',
username: 'Mailgun',
icon_emoji: 'mailbox_with_mail',
unfurl_links: true,
attachments: []
};

if(!body.event) {
debug('Event type not sent by Mailgun.');
return false;
} else if(body.event === 'opened') {

} else if(body.event === 'clicked') {

} else if(body.event === 'unsubscribed') {

} else if(body.event === 'complained') {

} else if(body.event === 'bounced') {

} else if(body.event === 'dropped') {

} else if(body.event === 'delivered') {

} else {
debug('Unknown event type from Mailgun.');
return false;
}
};

function getOptions(options) {

debug('Setting options');

if(typeof options === 'undefined') {
options = {};
}

if(typeof options.slack === 'undefined') {
options.slack = {};
}

if(typeof options.mailgun === 'undefined') {
options.mailgun = {};
}

if(!options.slack.hook) {
throw new Error('You need to set a url for slack communication.');
}

if(!options.mailgun.apikey) {
debug('Not using Mailgun HMAC verification due to lack of an API key.');
}

if(!options.slack.options) {
debug('No options passed to node-slack.');
}

return options;

};
Empty file added lib/events.js
Empty file.
Empty file added lib/getTemplate.js
Empty file.
50 changes: 50 additions & 0 deletions lib/mailgun-verify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

/*!
* mailgun-verify
* Copyright(c) 2015 Slick Labs, LLC
* MIT Licensed
*/

'use strict';

/**
* Module dependencies.
* @private
*/

var crypto = require('crypto');

/**
* Module exports.
*/

module.exports = verifyMailgun;

/**
* Verify mailgun HMAC authentication token
*/

function verifyMailgun(apikey, token, timestamp, signature) {
var data = [token, timestamp].join();
var hmac = crypto.createHmac('sha256', apikey).update(data);
var ourSig = hmac.digest('hex');

var output = {
apikey: apikey,
token: token,
timestamp: timestamp,
signature: signature,
generatedSignature: ourSig,
valid: ourSig === signature
};

return output;
};

/**
* TODO - Reply attack cache check
*/

function replayAttackCache() {
this.tokens = [];
};
Empty file added lib/slack.js
Empty file.
Loading

0 comments on commit edd412f

Please sign in to comment.