Secure email monitor

I segment my online accounts into two groups: valuable accounts and everything else. “Valuable” can vary a little by personal priorities, but for most of us, our most valuable accounts will be those with direct access to cash: banking and investments.

By transitivity, any account that can allow access to those accounts is also in the valuable and high-risk group. These include financial aggregators and any email address used for account recovery.

I would like to keep the only computer with access to the valuable accounts locked away in a dungeon guarded by trolls, but highly restricted access also makes it difficult to monitor activity. I want to be able to see notifications on all my devices while not actually allowing them access.

Enter Google Apps script.

Set up the script

You’ll need an everyday – normal – email address, one you access from anywhere. Then, you’ll need another that you only access from secured devices, the restricted – high risk – account.

Make a restricted Gmail account for yourself.

  • Don’t use an existing email address in for recovery
  • Do use a password manager and make sure you have a backup
  • Do set up two-factor auth

Go to https://script.google.com/

Untitled Google Apps script

Paste:

function digestAndArchive() {
  // CHANGE THIS TO YOUR NORMAL EMAIL ADDRESS:
  var monitor = "youreverydayemail@example.com"

  // Docs say that if you have many threads, for some unspecified value of "many", you
  // should use the paginated version of getInboxThreads, as the simple version will fail.
  //
  // It turns out that means "fail silently", returning at most some arbitrary number of
  // threads, and there is no obvious way to know there are more. I suspect the "correct"
  // way is to keep calling the paginated version with an increasing start index until
  // it returns nothing, but that seems ridiculous. For practical purposes, this function
  // returns more threads than you are likely to receive in a day.
  //
  // So, upon first installing this script on a long-ignored inbox, it might need to run
  // several times before it clears out the inbox, but that shouldn't hurt anyone.
  var threads = GmailApp.getInboxThreads()
  var bySender = {}
  for (var i = 0; i < threads.length; i++) {
    // I'm assuming this is a receive-only email address, so all messages in a thread
    // presumably have the same sender (or similar). Organizing by sender isn't
    // strictly necessary, but I think the final digest is more understandable.
    //
    // The docs don't say whether the first message is the most recent or not, but that
    // generally should not matter.
    var message = threads[i].getMessages()[0]
    var sender = message.getFrom()
    bySender[sender] = bySender[sender] || []
    bySender[sender].push(message.getSubject())
  }
  var body = ''
  var indent = '\n  - '
  for (var sender in bySender) {
    body += sender + indent + bySender[sender].join(indent) + '\n'
  }
  // Experimentally, it seems that GmailApp.sendEmail encodes the body as text/plain
  // so it should be safe to drop any old string in it. Would be nice to find
  // documentation to that effect. It munges astral plane characters, but for my
  // purposes here, I don't care.
  GmailApp.sendEmail(monitor, "Daily digest for " + new Date(), body)
  for (var i = 0; i < threads.length; i++) {
    // GmailApp.moveThreadsToArchive() can move multiple threads at once, but throws an
    // error and moves nothing for more than 100 threads. That's a pretty low limit when
    // you first run this on an inbox you haven't been regularly cleaning, so move one
    // by one.
    GmailApp.moveThreadToArchive(threads[i])
  }
}

Remember to change the email address in the script above to your normal, every day email. Save, as, for example “daily-digest”

To run by hand, go to the Run menu or just click the play button in the toolbar. At your normal address, you should see an email like this:

Subject: Daily digest for Fri Dec 02 2016 08:11:39 GMT-0500 (EST)
From: [your restricted address @gmail.com]

Andy from Google <andy-noreply@google.com>
– Josiah, welcome to your new Google Account

Run on a timer

To schedule, click Resources (menu) -> Current Project Triggers -> Click the link to add a trigger.

Set up to run daily on a timer. The time option is in your local time, as detected by Google.
Google apps time-driven trigger example

When you save the trigger, it will prompt you to authorize the trigger to run. Click “Review permissions”, which opens a new popup window, then allow.

Run the script by hand again after setting up the trigger. It should prompt for another required permission.

Daily summaries of your restricted account should now start to appear in your normal email.


If you’re paranoid – and if you’ve gotten this far, you probably are – delete the digest emails from time to time. Deleting the digests removes a possible attack vector on your high-value account because Google’s account recovery process asks the date you created an account as a form of identity verification.

Qwerty War: days 2 and 3

For day 2 of the Qwerty War project, I wanted to get all the visuals roughed in, though without final sprites for the player and enemies. It turns out this actually took two days.

I considered doing the whole thing by moving divs about the screen, or even svg everywhere, but the obvious choice is canvas, so best not to overthink it.

User controls – word input, health meter and score – are still regular html elements overlaid on the canvas. At first, I thought I would do the same with the enemy words: reposition the table cells from day one’s text-only version to follow sprites around, but soon decided drawing the text with canvas api would be simpler.

More structural changes came from the rework necessary to allow for delay while bullets reached the targets. This may not have been strictly necessary: originally, I planned to keep reactions instantaneous. When the player shoots, for example, the enemy would explode immediately. I would then animate the bullet motion toward the enemy. Since the bullet would whiz over in less than a tenth of second, the delay would not be noticeable.

As I was starting, though, this felt wrong. The code made more sense if I associated the explosion with a bullet impact, rather than the shot. If I’m inclined in the future, this should allow features like slow motion.

Bullets are the only effect not built with sprites: they are just lines with gradients to fade out toward the shooter. The “collision detection”, if you can call it that, is easy. Since every moving element is arranged radially from the player, it’s a simple matter of checking whether the bullet traveled far enough to hit its target, which causes the target to explode.

Explosions, of course, are the fun part. The nifty http://explosiongenerator.com/ gives me the look I want. There are three levels of enemies in the game, from words length three up to five, so I just need an explosion per enemy type. Stopping the explosion a few frames from the end gives a nice debris and blood splatter to paint on the background. By randomizing the rotation of each explosion and its ending frame, just three explosions look pretty organic.

Explosion generator gives you a download full of .png images; I used ImageMagick to combine them into a single image suitable for animation:

montage images/explosion0{000..191..4}.png -geometry +0+0 -tile 12x4 -background none explosion1.png

That squirly brace stuff is a handy shell trick to pick every fourth image from a desired range. From there I just use JavaScript to animate these frames.

So far, the game on day 3 looks pretty good, but a few details bother me:

  • Enemies run over each other, sometimes obscuring the words you need to see. This also happens in the original Qwerty Warriors. It’s not too much of a problem – you could even say it makes you think a little more strategically about who to shoot – but it seems unpolished. Would be nice to put in collision detection so enemies stay don’t run each other over.
  • The end is abrupt. It might be a neat effect to finish animating all bullets in flight and explosions after the player dies.
  • Bullet animations are not entirely satisfying. They are functional, but not exciting.
  • As I was testing, I realized muzzle flashes are a nice feature to show you who is shooting at you. I put them in as just one extra sprite for each of the player and enemy types, flash on or off, but there probably should be a few frames to that animation.
  • The bullet trail extends through the shooter. I mentally justify that by saying it’s the stuff coming out of the rear of a rocket or something, but actually it just looks odd.

Nonetheless, it looks decent and is becoming fun to play. Day four will be for sound effects.

Qwerty War: day 1

“Qwerty War” is what I’m calling a small typing game inspired by Qwerty Warriors.

The concept is that you are a keyboard-slinging warrior surrounded by never-ending waves of enemies. There is no escape, but you’ll take as many of them down with you as you can. Qwerty Warriors is my favorite typing game; since it surrounds you with enemies, it has an intensity that other typing games lack. Its remake, Qwerty Warriors 2, by contrast, only sends enemies in from the top, typical for typing games, and boring.

The original is built in Flash and often syndicated on shady sites who make their money via ads for scareware and other garbage. So my own Qwerty War will be an open source JavaScript only game.

On day one of this project, my goal is to get a text-only version working with all components. In the text only version, a table of “word” and “range to enemy” replaces the visual of enemies encircling the player, but it contains all the actual gameplay components. There is no reason for a typing game not to be accessible to the blind, so the text only version will remain as a fallback to the final graphical version, build on html canvas.

The final version will include a server-side component for saving the leaderboard, but the game itself runs entirely in the front end. After day 1, it is playable, if a bland. Source is on Bitbucket.