npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

sneakemail

v0.0.16

Published

Simple email tracking, know when your emails are opened.

Downloads

45

Readme

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":"[email protected]",
    "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 protected]',
    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/.