Skip to content

Instantly share code, notes, and snippets.

@zachlatta
Last active October 15, 2020 11:16
Show Gist options
  • Save zachlatta/d175ee9fde3447cca4ef to your computer and use it in GitHub Desktop.
Save zachlatta/d175ee9fde3447cca4ef to your computer and use it in GitHub Desktop.
Snaplive Workshop

Getting Started

Fire up a box on Nitrous.IO by right clicking on the following button and opening it in a new tab:

Hack zachlatta/snaplive on Nitrous

Here's a GIF of what should happen:

Example of setting up Nitrous.io

Once you're in, copy and paste the following into the terminal at the bottom and then hit enter:

$ cd workspace/snaplive && npm install

Here's a GIF of what should happen:

Example of installing NPM dependencies

When it's done, you should see the following:

NPM finishes installing dependencies

Replace both TODOs in var SNAPCHAT_USERNAME = 'TODO'; and var SNAPCHAT_PASSWORD = 'TODO'; with the actual username and password for the accounts you created. Then paste ./node_modules/nodemon/bin/nodemon.js into the terminal to start the web server and preview it by clicking Preview > Port 3000 (the one without SSL). A new tab should open with the running server, like as follows:

Setting up Snapchat account and viewing webpage served

Great! You should now be all set up.

Code Snippets

Making account public

// make sure we allow snaps from any account
client.privacy(false);

Code now looks like:

var express = require('express');
var app = express();
var fs = require('fs');
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Q =  require('q');
var snapchat = require('snapchat');

var client = new snapchat.Client();

var SNAPCHAT_USERNAME = 'YOURUSERNAME';
var SNAPCHAT_PASSWORD = 'YOURPASSWORD';

app.use('/images', express.static(__dirname + '/images'));

app.get('/', function (req, res) {
  res.sendFile('index.html', {root: __dirname});
})

client.login(SNAPCHAT_USERNAME, SNAPCHAT_PASSWORD)
  .then(function (data) {
    console.log('Logged into Snapchat as %s', SNAPCHAT_USERNAME);

    // make sure we allow snaps from any account
    client.privacy(false);

    // start our webserver on port 3000
    var server = http.listen(3000, function () {
      var host = server.address().address;
      var port = server.address().port;

      console.log('Snaplive listening on http://%s:%s', host, port);
    });
  }, function (err) {
    console.log(err);
  });

Create images directory if it doesn't already exist

function createDirIfNotExists(name) {
  if (!fs.existsSync(name)) {
    fs.mkdirSync(name);
  }
}

Code now looks like:

var express = require('express');
var app = express();
var fs = require('fs');
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Q =  require('q');
var snapchat = require('snapchat');

var client = new snapchat.Client();

var SNAPCHAT_USERNAME = 'YOURUSERNAME';
var SNAPCHAT_PASSWORD = 'YOURPASSWORD';

function createDirIfNotExists(name) {
  if (!fs.existsSync(name)) {
    fs.mkdirSync(name);
  }
}

app.use('/images', express.static(__dirname + '/images'));

app.get('/', function (req, res) {
  res.sendFile('index.html', {root: __dirname});
})

client.login(SNAPCHAT_USERNAME, SNAPCHAT_PASSWORD)
  .then(function (data) {
    console.log('Logged into Snapchat as %s', SNAPCHAT_USERNAME);

    createDirIfNotExists('./images');

    // make sure we allow snaps from any account
    client.privacy(false);

    // start our webserver on port 3000
    var server = http.listen(3000, function () {
      var host = server.address().address;
      var port = server.address().port;

      console.log('Snaplive listening on http://%s:%s', host, port);
    });
  }, function (err) {
    console.log(err);
  });

Polling

setInterval(function () {
  console.log('wow, such repeat');
}, 5000);
client.sync()
  .then(function (data) {
    console.log(data);
  })
  .fail(function (err) {
    console.error(err);
  });
client.sync()
  .then(function (data) {
    return downloadUnreadSnaps(client, data.snaps);
  })
  .fail(function (err) {
    console.error(err);
  });
// downloadUnreadSnaps downloads all of the unread snaps, writes them to the
// ./images directory, and returns promise for an array of all of the snaps
// downloaded. the snaps in the promised array have an additional attribute
// called 'filename' added to them that contains the name of the file that the
// snap is saved in.
function downloadUnreadSnaps(client, snaps) {
  // filter all of the snaps for unread snaps.
  //
  // the .filter function iterates over an array, runs a function on each item
  // in the array, then creates a new array with all of the elements that pass
  // the test in the function.
  //
  // examples:
  //
  // [5, 4, 3, 2, 1].filter(function (n) { return n > 2 })
  // > returns [5, 4, 3]
  //
  // ['a', 'b', 'c', 'd'].filter(function (l) { return l == 'a' })
  // > returns ['a']
  //
  var unreadSnaps = snaps.filter(function (snap) {
    return typeof snap.sn !== 'undefined' && typeof snap.t !== 'undefined' &&
      snap.st == 1
  })

  return Q.all(unreadSnaps.map(function (snap) {
    console.log('Saving snap from ' + snap.sn + '...');

    // save the image to ./images/{SENDER USERNAME}_{SNAP ID}.jpg
    snap.filename = snap.sn + '_' + snap.id + '.jpg';
    var stream = fs.createWriteStream('./images/' + snap.filename, {
      flags: 'w',
      encoding: null,
      mode: 0666
    });
    return client.getBlob(snap.id)
      .then(function (blob) {
        blob.pipe(stream);
      })
      .then(function () {
        return snap;
      });
  }))
  .then(function (downloadedSnaps) {
    var update = {};
    // add the snaps to the update we'll send to snapchat that'll it to mark
    // them as read
    downloadedSnaps.forEach(function (snap) {
      update[snap.id] = {
        c: 0, // mark snap as seen
        t: (new Date).getTime(), // set timestamp to the epoch
        replayed: 0 // we have not replayed the snap
      };
    })

    // send our update to snapchat, marking all snaps we just downloaded as
    // read
    return client.sync(update)
      .then(function () {
        return downloadedSnaps;
      });
  });
}

Code now looks like:

var express = require('express');
var app = express();
var fs = require('fs');
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Q =  require('q');
var snapchat = require('snapchat');

var client = new snapchat.Client();

var SNAPCHAT_USERNAME = 'YOURUSERNAME';
var SNAPCHAT_PASSWORD = 'YOURPASSWORD';

function createDirIfNotExists(name) {
  if (!fs.existsSync(name)) {
    fs.mkdirSync(name);
  }
}

// downloadUnreadSnaps downloads all of the unread snaps, writes them to the
// ./images directory, and returns promise for an array of all of the snaps
// downloaded. the snaps in the promised array have an additional attribute
// called 'filename' added to them that contains the name of the file that the
// snap is saved in.
function downloadUnreadSnaps(client, snaps) {
  // filter all of the snaps for unread snaps.
  //
  // the .filter function iterates over an array, runs a function on each item
  // in the array, then creates a new array with all of the elements that pass
  // the test in the function.
  //
  // examples:
  //
  // [5, 4, 3, 2, 1].filter(function (n) { return n > 2 })
  // > returns [5, 4, 3]
  //
  // ['a', 'b', 'c', 'd'].filter(function (l) { return l == 'a' })
  // > returns ['a']
  //
  var unreadSnaps = snaps.filter(function (snap) {
    return typeof snap.sn !== 'undefined' && typeof snap.t !== 'undefined' &&
      snap.st == 1
  })

  return Q.all(unreadSnaps.map(function (snap) {
    console.log('Saving snap from ' + snap.sn + '...');

    // save the image to ./images/{SENDER USERNAME}_{SNAP ID}.jpg
    snap.filename = snap.sn + '_' + snap.id + '.jpg';
    var stream = fs.createWriteStream('./images/' + snap.filename, {
      flags: 'w',
      encoding: null,
      mode: 0666
    });
    return client.getBlob(snap.id)
      .then(function (blob) {
        blob.pipe(stream);
      })
      .then(function () {
        return snap;
      });
  }))
  .then(function (downloadedSnaps) {
    var update = {};
    // add the snaps to the update we'll send to snapchat that'll it to mark
    // them as read
    downloadedSnaps.forEach(function (snap) {
      update[snap.id] = {
        c: 0, // mark snap as seen
        t: (new Date).getTime(), // set timestamp to the epoch
        replayed: 0 // we have not replayed the snap
      };
    })

    // send our update to snapchat, marking all snaps we just downloaded as
    // read
    return client.sync(update)
      .then(function () {
        return downloadedSnaps;
      });
  });
}

app.use('/images', express.static(__dirname + '/images'));

app.get('/', function (req, res) {
  res.sendFile('index.html', {root: __dirname});
})

client.login(SNAPCHAT_USERNAME, SNAPCHAT_PASSWORD)
  .then(function (data) {
    console.log('Logged into Snapchat as %s', SNAPCHAT_USERNAME);

    createDirIfNotExists('./images');
    
    // make sure we allow snaps from any account
    client.privacy(false);

    setInterval(function () {
      client.sync()
        .then(function (data) {
          return downloadUnreadSnaps(client, data.snaps);
        })
        .fail(function (err) {
          console.error(err);
        });
    }, 5000);
    
    // start our webserver on port 3000
    var server = http.listen(3000, function () {
      var host = server.address().address;
      var port = server.address().port;

      console.log('Snaplive listening on http://%s:%s', host, port);
    });
  }, function (err) {
    console.log(err);
  });

Replying

return downloadUnreadSnaps(client, data.snaps)
  .then(function (downloadedSnaps) {
    // if we downloaded any snaps, then emit an event to the client
    // and respond to them
    if (downloadedSnaps.length) {
      return replyToSnaps(client, downloadedSnaps);
    }
  });
function replyToSnaps(client, snaps) {
  // upload response.jpg to snapchat
  var blob = fs.createReadStream('response.jpg');
  return client.upload(blob, false)
    .then(function (mediaId) {
      // create an array of the senders of all of the downloaded
      // snaps.
      //
      // the .map function iterates over an array, runs a function
      // on each item in the array, then returns an array of all of
      // the results of all of the functions.
      //
      // examples:
      //
      // [5, 4, 3, 2].map(function (n) { return n + 1 })
      // > returns [6, 5, 4, 3]
      //
      // ['a', 'b', 'c'].map(function (l) { return 'hi' })
      // > returns ['hi', 'hi', 'hi']
      //
      var recipients = snaps.map(function (snap) { return snap.sn });

      console.log('Sending response to %s...', recipients.join(', '));

      return client.send(mediaId, recipients, 5);
    });
}

Code now looks like:

var express = require('express');
var app = express();
var fs = require('fs');
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Q =  require('q');
var snapchat = require('snapchat');

var client = new snapchat.Client();

var SNAPCHAT_USERNAME = 'YOURUSERNAME';
var SNAPCHAT_PASSWORD = 'YOURPASSWORD';

function createDirIfNotExists(name) {
  if (!fs.existsSync(name)) {
    fs.mkdirSync(name);
  }
}

// downloadUnreadSnaps downloads all of the unread snaps, writes them to the
// ./images directory, and returns promise for an array of all of the snaps
// downloaded. the snaps in the promised array have an additional attribute
// called 'filename' added to them that contains the name of the file that the
// snap is saved in.
function downloadUnreadSnaps(client, snaps) {
  // filter all of the snaps for unread snaps.
  //
  // the .filter function iterates over an array, runs a function on each item
  // in the array, then creates a new array with all of the elements that pass
  // the test in the function.
  //
  // examples:
  //
  // [5, 4, 3, 2, 1].filter(function (n) { return n > 2 })
  // > returns [5, 4, 3]
  //
  // ['a', 'b', 'c', 'd'].filter(function (l) { return l == 'a' })
  // > returns ['a']
  //
  var unreadSnaps = snaps.filter(function (snap) {
    return typeof snap.sn !== 'undefined' && typeof snap.t !== 'undefined' &&
      snap.st == 1
  })

  return Q.all(unreadSnaps.map(function (snap) {
    console.log('Saving snap from ' + snap.sn + '...');

    // save the image to ./images/{SENDER USERNAME}_{SNAP ID}.jpg
    snap.filename = snap.sn + '_' + snap.id + '.jpg';
    var stream = fs.createWriteStream('./images/' + snap.filename, {
      flags: 'w',
      encoding: null,
      mode: 0666
    });
    return client.getBlob(snap.id)
      .then(function (blob) {
        blob.pipe(stream);
      })
      .then(function () {
        return snap;
      });
  }))
  .then(function (downloadedSnaps) {
    var update = {};
    // add the snaps to the update we'll send to snapchat that'll it to mark
    // them as read
    downloadedSnaps.forEach(function (snap) {
      update[snap.id] = {
        c: 0, // mark snap as seen
        t: (new Date).getTime(), // set timestamp to the epoch
        replayed: 0 // we have not replayed the snap
      };
    })

    // send our update to snapchat, marking all snaps we just downloaded as
    // read
    return client.sync(update)
      .then(function () {
        return downloadedSnaps;
      });
  });
}

function replyToSnaps(client, snaps) {
  // upload response.jpg to snapchat
  var blob = fs.createReadStream('response.jpg');
  return client.upload(blob, false)
    .then(function (mediaId) {
      // create an array of the senders of all of the downloaded
      // snaps.
      //
      // the .map function iterates over an array, runs a function
      // on each item in the array, then returns an array of all of
      // the results of all of the functions.
      //
      // examples:
      //
      // [5, 4, 3, 2].map(function (n) { return n + 1 })
      // > returns [6, 5, 4, 3]
      //
      // ['a', 'b', 'c'].map(function (l) { return 'hi' })
      // > returns ['hi', 'hi', 'hi']
      //
      var recipients = snaps.map(function (snap) { return snap.sn });

      console.log('Sending response to %s...', recipients.join(', '));

      return client.send(mediaId, recipients, 5);
    });
}

app.use('/images', express.static(__dirname + '/images'));

app.get('/', function (req, res) {
  res.sendFile('index.html', {root: __dirname});
})

client.login(SNAPCHAT_USERNAME, SNAPCHAT_PASSWORD)
  .then(function (data) {
    console.log('Logged into Snapchat as %s', SNAPCHAT_USERNAME);

    createDirIfNotExists('./images');
    
    // make sure we allow snaps from any account
    client.privacy(false);

    setInterval(function () {
      client.sync()
        .then(function (data) {
          return downloadUnreadSnaps(client, data.snaps)
            .then(function (downloadedSnaps) {
              // if we downloaded any snaps, then emit an event to the client
              // and respond to them
              if (downloadedSnaps.length) {
                return replyToSnaps(client, downloadedSnaps);
              }
            });
        })
        .fail(function (err) {
          console.error(err);
        });
    }, 5000);
    
    // start our webserver on port 3000
    var server = http.listen(3000, function () {
      var host = server.address().address;
      var port = server.address().port;

      console.log('Snaplive listening on http://%s:%s', host, port);
    });
  }, function (err) {
    console.log(err);
  });

Emitting event

io.emit('snap', downloadedSnaps[downloadedSnaps - 1]);

Code now looks like:

var express = require('express');
var app = express();
var fs = require('fs');
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Q =  require('q');
var snapchat = require('snapchat');

var client = new snapchat.Client();

var SNAPCHAT_USERNAME = 'YOURUSERNAME';
var SNAPCHAT_PASSWORD = 'YOURPASSWORD';

function createDirIfNotExists(name) {
  if (!fs.existsSync(name)) {
    fs.mkdirSync(name);
  }
}

// downloadUnreadSnaps downloads all of the unread snaps, writes them to the
// ./images directory, and returns promise for an array of all of the snaps
// downloaded. the snaps in the promised array have an additional attribute
// called 'filename' added to them that contains the name of the file that the
// snap is saved in.
function downloadUnreadSnaps(client, snaps) {
  // filter all of the snaps for unread snaps.
  //
  // the .filter function iterates over an array, runs a function on each item
  // in the array, then creates a new array with all of the elements that pass
  // the test in the function.
  //
  // examples:
  //
  // [5, 4, 3, 2, 1].filter(function (n) { return n > 2 })
  // > returns [5, 4, 3]
  //
  // ['a', 'b', 'c', 'd'].filter(function (l) { return l == 'a' })
  // > returns ['a']
  //
  var unreadSnaps = snaps.filter(function (snap) {
    return typeof snap.sn !== 'undefined' && typeof snap.t !== 'undefined' &&
      snap.st == 1
  })

  return Q.all(unreadSnaps.map(function (snap) {
    console.log('Saving snap from ' + snap.sn + '...');

    // save the image to ./images/{SENDER USERNAME}_{SNAP ID}.jpg
    snap.filename = snap.sn + '_' + snap.id + '.jpg';
    var stream = fs.createWriteStream('./images/' + snap.filename, {
      flags: 'w',
      encoding: null,
      mode: 0666
    });
    return client.getBlob(snap.id)
      .then(function (blob) {
        blob.pipe(stream);
      })
      .then(function () {
        return snap;
      });
  }))
  .then(function (downloadedSnaps) {
    var update = {};
    // add the snaps to the update we'll send to snapchat that'll it to mark
    // them as read
    downloadedSnaps.forEach(function (snap) {
      update[snap.id] = {
        c: 0, // mark snap as seen
        t: (new Date).getTime(), // set timestamp to the epoch
        replayed: 0 // we have not replayed the snap
      };
    })

    // send our update to snapchat, marking all snaps we just downloaded as
    // read
    return client.sync(update)
      .then(function () {
        io.emit('snap', downloadedSnaps[downloadedSnaps - 1]);

        return downloadedSnaps;
      });
  });
}

function replyToSnaps(client, snaps) {
  // upload response.jpg to snapchat
  var blob = fs.createReadStream('response.jpg');
  return client.upload(blob, false)
    .then(function (mediaId) {
      // create an array of the senders of all of the downloaded
      // snaps.
      //
      // the .map function iterates over an array, runs a function
      // on each item in the array, then returns an array of all of
      // the results of all of the functions.
      //
      // examples:
      //
      // [5, 4, 3, 2].map(function (n) { return n + 1 })
      // > returns [6, 5, 4, 3]
      //
      // ['a', 'b', 'c'].map(function (l) { return 'hi' })
      // > returns ['hi', 'hi', 'hi']
      //
      var recipients = snaps.map(function (snap) { return snap.sn });

      console.log('Sending response to %s...', recipients.join(', '));

      return client.send(mediaId, recipients, 5);
    });
}

app.use('/images', express.static(__dirname + '/images'));

app.get('/', function (req, res) {
  res.sendFile('index.html', {root: __dirname});
})

client.login(SNAPCHAT_USERNAME, SNAPCHAT_PASSWORD)
  .then(function (data) {
    console.log('Logged into Snapchat as %s', SNAPCHAT_USERNAME);

    createDirIfNotExists('./images');
    
    // make sure we allow snaps from any account
    client.privacy(false);

    setInterval(function () {
      client.sync()
        .then(function (data) {
          return downloadUnreadSnaps(client, data.snaps)
            .then(function (downloadedSnaps) {
              // if we downloaded any snaps, then emit an event to the client
              // and respond to them
              if (downloadedSnaps.length) {
                return replyToSnaps(client, downloadedSnaps);
              }
            });
        })
        .fail(function (err) {
          console.error(err);
        });
    }, 5000);
    
    // start our webserver on port 3000
    var server = http.listen(3000, function () {
      var host = server.address().address;
      var port = server.address().port;

      console.log('Snaplive listening on http://%s:%s', host, port);
    });
  }, function (err) {
    console.log(err);
  });
{
"enable_video_transcoding_android":false,
"birthday":"1997-10-22",
"bests":[
"jujujuhsshshsj",
"fgdutdtdutdidut"
],
"score":227,
"number_of_best_friends":3,
"received":136,
"logged":true,
"added_friends":[
],
"requests":[
],
"sent":91,
"story_privacy":"FRIENDS",
"username":"eshs-snaplive",
"snaps":[
{
"sn":"jujujuhsshshsj",
"id":"995873417495874691r",
"st":2,
"m":0,
"ts":1417495874691,
"sts":1417495874691
},
{
"sn":"jujujuhsshshsj",
"id":"445384417495863446r",
"st":2,
"m":0,
"ts":1417495863446,
"sts":1417495863446
}
],
"friends":[
{
"can_see_custom_stories":true,
"source":"SEARCHED_BY_USERNAME",
"direction":"OUTGOING",
"name":"zachlatta",
"display":"",
"type":0,
"new_link":"true"
},
{
"can_see_custom_stories":true,
"source":"SEARCHED_BY_USERNAME",
"direction":"OUTGOING",
"name":"eshs-snaplive",
"display":"",
"type":0,
"new_link":"true"
},
{
"can_see_custom_stories":true,
"source":"SEARCHED_BY_USERNAME",
"direction":"OUTGOING",
"name":"eshs-snaplive2",
"display":"",
"type":0,
"new_link":"true"
},
{
"can_see_custom_stories":true,
"source":"SEARCHED_BY_USERNAME",
"direction":"OUTGOING",
"name":"summerr06",
"display":"",
"type":0,
"new_link":"true"
},
{
"can_see_custom_stories":true,
"source":"SEARCHED_BY_USERNAME",
"direction":"OUTGOING",
"name":"katielatta_13",
"display":"",
"type":0,
"new_link":"true"
},
{
"can_see_custom_stories":true,
"source":"SEARCHED_BY_USERNAME",
"direction":"OUTGOING",
"name":"teamsnapchat",
"display":"Team Snapchat",
"type":0,
"new_link":"true"
},
{
"can_see_custom_stories":true,
"source":"SEARCHED_BY_USERNAME",
"direction":"OUTGOING",
"name":"jujujuhsshshsj",
"display":"",
"type":0,
"new_link":"true"
},
{
"can_see_custom_stories":true,
"source":"SEARCHED_BY_USERNAME",
"direction":"OUTGOING",
"name":"fgdutdtdutdidut",
"display":"",
"type":0,
"new_link":"true"
}
],
"device_token":"",
"feature_settings":{
"front_facing_flash":false,
"replay_snaps":false,
"special_text":false,
"visual_filters":false,
"smart_filters":false,
"power_save_mode":false
},
"user_id":"467b3ad2-44b2-425b-bfab-6346f320d092",
"snap_p":0,
"mobile_verification_key":"ODQ5MDplc2hzLXNuYXBsaXZl",
"recents":[
"zachlatta"
],
"notification_sound_setting":"OFF",
"added_friends_timestamp":1417157218928,
"searchable_by_phone_number":false,
"snapchat_phone_number":"+17864088263",
"auth_token":"742d6d8f55922bdc5b1e3e397151a446",
"image_caption":false,
"blocked_from_using_our_story":false,
"current_timestamp":1417511234295,
"can_view_mature_content":false,
"email":"zach+snaplive@zachlatta.com",
"should_send_text_to_verify_number":false,
"should_call_to_verify_number":false,
"mobile":""
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment