0.0.16 • Published 7 years ago

sneakemail v0.0.16

Weekly downloads
4
License
ISC
Repository
github
Last release
7 years ago

Sneakemail

Ever wonder how companies like Constant Contact or MailChimp track when somebody opens one of their emails? It's pretty neat, and Sneakemail effectively recreates it, although probably in a less robust way.

Nevertheless, Sneakemail will allow you to send emails and know when those emails have been opened.

Moving Parts

Sneakemail consists of an API and a soon to come UI.

API

The API uses Hapi, to handle the endpoints, and nodemailer, to send emails. During configuration you have direct access to both of these modules, so you can manipulate them however you lke.

By default, the Sneakemail API will create the two endpoints:

/email [POST]

This endpoint cab be used to send an email, and inject into that email the sneaky bit: that which allows you to track when an email has been opened. You, or your application, make an HTTP POST to this endpoint with a body like the following:

{
	"to":"recipient@domain.com",
    "fromName": "Frank Foo",
    "subject": "Super Important Meeting",
    "html": "<p>blah blah bar</p>",
    "passthrough": {
    	"username": "ffoo",
        "emailId": 1234567
    }
}

The Sneakemail API will then send an email to to and appends a <link rel="stylesheet" ... > tag to the html. The href attribute of the <link /> tag will be a URL that that is seemingly the location of a CSS file. But it's not a real CSS file, before the Sneakemail API sends the email it encrypts the passthrough object that was posted to /email and that encrypted string becomes the name of the CSS file referenced in the <link>'s href attribute (the name of the CSS file becomes the slug in the endpoint below.

/{slug}.css [GET]

This is how the tracking works. When an email client downloads the CSS file in the body of the email this endpoint decrypts the encrypted slug (name of the CSS file), and now has complete access to the passthrough object that was POSTed to /email.

UI

More info on the UI to come, it's pretty much done, and all it really does is just put a pretty face on posting to the API. Check out the working example below.

Usage

Install sneakemail

npm install --save sneakemail

Check out config/config.js for the default configuration. The options given to api.init() and ui.init() are merged with the defaults, at minimum your options should contain the transporter and sneakemail.fromAddress attributes (because that's specific to you).

'use strict'

const path = require('path')
const co = require('bluebird-co').co

const Sneakemail = require('sneakemail')
const api = Sneakemail.api
const ui = Sneakemail.ui

const options = {
  // the api object gets passed DIRECTLY to the Hapi `server.connection()` method
  server: {
    api: {
      port: 3000
    }
  },
  // these values need to be entered accodeing to `node-mailer`: https://nodemailer.com/smtp/well-known,
  // this object is passed DIRECTLY to nodemailer.createTransport(), so do whatever you like according to what
  // `node-mailer` does, you, of course, don't have to use a "well-known" service, as this example does
  transporter: {
    service: 'Mailjet',
    auth: {
      user: '',
      pass: ''
    }
  },
  // this becomes the from address in the email that is sent
  sneakemail: {
    fromAddress: 'email@gmail.com',
    endpoints: {
      api: {
        emailPost: '/email' //default
      },
      ui: {
        index: '/', //default
        emailPost: '/email' //default
      }
    }
  },
  // this is the function that gets called when somebody opens an email, its argument is an object that contains the
  // `to`, `subject`, and `passthrough` objects that were contained in the body of the HTTP POST to `/email`
  openCallback: function (obj) {
    console.log(obj)
  }
}

co(function *() {

  yield api.init(options)
  yield ui.init(options)

  // add some more routes if you so desire
  api.server.route({
    method: 'get',
    path: '/',
    handler: (req, reply) => {
      reply('Welcome to the Sneakemail API')
    }
  })

  yield api.start()
  yield ui.start()

})
  .then(() => {
    console.log('api started at ', api.server.info.uri)
    console.log('ui started at ', ui.server.info.uri)
    // lets look at the route table just to confirm that our routes are regisred
    console.log('api routes:')
    api.server.table()[0].table.map((t) => {
      console.log(' route %s [%s] is registered: %s%s', t.path, t.method, api.server.info.uri, t.path)
    })
    console.log('ui routes:')
    ui.server.table()[0].table.map((t) => {
      console.log(' route %s [%s] is registered: %s%s', t.path, t.method, ui.server.info.uri, t.path)
    })
  })
  .catch(console.error)

Will output something like

api started at  http://10.0.0.167:3000
ui started at  http://10.0.0.167:8080
api routes:
 route /email [post] is registered: http://10.0.0.167:3000/email
 route /{slug}.css [get] is registered: http://10.0.0.167:3000/{slug}.css
 route / [get] is registered: http://10.0.0.167:3000/
ui routes:
 route / [get] is registered: http://10.0.0.167:8080/
 route /{param*} [get] is registered: http://10.0.0.167:8080/{param*}
 route /static/{param*} [get] is registered: http://10.0.0.167:8080/static/{param*}
 route /email [post] is registered: http://10.0.0.167:8080/email

Now make a POST to /email, wait for the email to come, open it, and check your console!

Or browse to the / ui endpoint http://10.0.0.167:8080/.

0.0.16

7 years ago

0.0.15

7 years ago

0.0.14

7 years ago

0.0.13

7 years ago

0.0.12

7 years ago

0.0.11

7 years ago

0.0.10

7 years ago

0.0.9

7 years ago

0.0.8

7 years ago

0.0.7

7 years ago

0.0.6

7 years ago

0.0.5

7 years ago

0.0.4

7 years ago

0.0.3

7 years ago

0.0.2

7 years ago

0.0.1

7 years ago