├── .npmignore ├── CONTRIBUTORS ├── README.md ├── .gitignore ├── package.json ├── example ├── download_pending_snaps.js └── upload.js ├── client.js └── snapchat.js /.npmignore: -------------------------------------------------------------------------------- 1 | #*# 2 | *~ 3 | .* -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Lucas Dohring 2 | Kenan Yildirim 3 | Jacob Duval 4 | kaushal 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-snapchat 2 | ============= 3 | 4 | A SnapChat client in Node.js 5 | 6 | EUPL-1.1 License 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snapchat", 3 | "version": "1.0.4", 4 | "description": "A snapchat client for node.js", 5 | "main": "snapchat.js", 6 | "scripts": {}, 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/nykac/node-snapchat" 10 | }, 11 | "keywords": [ 12 | "snapchat", 13 | "api" 14 | ], 15 | "author": { 16 | "name": "Lucas A. Dohring", 17 | "email": "lucas@dohring.tk" 18 | }, 19 | "license": "EUPL-1.1", 20 | "dependencies": { 21 | "uuid-v4": "*", 22 | "multipart-form-stream": "*", 23 | "stream-sink": "*", 24 | "q": "*" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/download_pending_snaps.js: -------------------------------------------------------------------------------- 1 | var snapchat = require('../snapchat'), 2 | client = new snapchat.Client(), 3 | fs = require('fs'); 4 | 5 | // Make sure the images folder exists 6 | if(!fs.existsSync('./images')) { 7 | fs.mkdirSync('./images'); 8 | } 9 | 10 | client.login('USERNAME', 'PASSWORD').then(function(data) { 11 | // Handle any problems, such as wrong password 12 | if (typeof data.snaps === 'undefined') { 13 | console.log(data); 14 | return; 15 | } 16 | 17 | // Loop through the latest snaps 18 | data.snaps.forEach(function(snap) { 19 | // Make sure the snap item is unopened and sent to you (not sent by you) 20 | if (typeof snap.sn !== 'undefined' && typeof snap.t !== 'undefined' && snap.st == 1) { 21 | console.log('Saving snap from ' + snap.sn + '...'); 22 | 23 | // Save the image to ./images/{SENDER USERNAME}_{SNAP ID}.jpg 24 | var stream = fs.createWriteStream('./images/' + snap.sn + '_' + snap.id + '.jpg', { flags: 'w', encoding: null, mode: 0666 }); 25 | client.getBlob(snap.id).then(function(blob) { 26 | blob.pipe(stream); 27 | blob.resume(); 28 | }); 29 | } 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /example/upload.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var Q = require('q') 3 | , sc = require('../snapchat') 4 | , fs = require('fs') 5 | ,util = require('util'); 6 | 7 | (function main(argc, argv) { 8 | // Usage: ./upload [recipient2] ... [recipient n] 9 | if(argc < 6) { 10 | var path = require('path'); 11 | var name = path.basename(argv.shift()); 12 | util.puts(util.format("Usage:")) 13 | util.puts(util.format("\t%s username password timeout filename.jpg first_recipient [more_recipents]", name)); 14 | util.puts(util.format("\t%s username password 0 filename.mp4 first_recipient [more_recipients]", name)); 15 | return false; 16 | } 17 | argv.shift(); 18 | 19 | var username = argv.shift() 20 | , password = argv.shift() 21 | , time = argv.shift() 22 | , filename = argv.shift() 23 | , recipients = argv; 24 | 25 | 26 | var isVideo = time === 0; 27 | 28 | var c = new sc.Client(); 29 | c.login(username, password) 30 | .then(function() { 31 | var blob = fs.createReadStream(filename); 32 | return c.upload(blob, isVideo); 33 | }, function(err) { 34 | console.error("Failed to login"); 35 | console.error(err) 36 | }) 37 | .then(function(mediaId) { 38 | return Q.allSettled(recipients.map(function(recipient) { 39 | if(isVideo) 40 | return c.send(mediaId, recipient).catch(function(err) { 41 | console.error("Failed to send snap to", recipient); 42 | console.error(err); 43 | }); 44 | else 45 | return c.send(mediaId, recipient, time).catch(function(err) { 46 | console.error("Failed to send snap to", recipient); 47 | console.error(err); 48 | }); 49 | })); 50 | }, function(error) { 51 | console.error("Unable to upload file", filename); 52 | console.error(error); 53 | }) 54 | .then(function(statuses) { 55 | console.log("All done"); 56 | }, function(err) { 57 | console.error("There was an error") 58 | console.error(err); 59 | }); 60 | })(process.argv.length-1, process.argv.slice(1)) 61 | 62 | -------------------------------------------------------------------------------- /client.js: -------------------------------------------------------------------------------- 1 | /** 2 | *@license 3 | * Copyright 2013 Lucas A. Dohring 4 | * 5 | * Licensed under the EUPL, Version 1.1 or – as soon they 6 | * will be approved by the European Commission - subsequent 7 | * versions of the EUPL (the "Licence"); 8 | * 9 | * You may not use this work except in compliance with the 10 | * Licence. 11 | * 12 | * You may obtain a copy of the Licence at: 13 | * http://ec.europa.eu/idabc/eupl 14 | * 15 | * Unless required by applicable law or agreed to in 16 | * writing, software distributed under the Licence is 17 | * distributed on an "AS IS" basis, 18 | * 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 20 | * express or implied. 21 | * 22 | * See the Licence for the specific language governing 23 | * permissions and limitations under the Licence. 24 | */ 25 | 26 | var sc = require('./snapchat'); 27 | 28 | /** 29 | * An oop wraper api 30 | * @constructor 31 | */ 32 | 33 | var Client = module.exports = function() { 34 | if(!(this instanceof Client)) return new Client(); 35 | }; 36 | 37 | /** 38 | * @param {String} username 39 | * @param {String} password 40 | * @return {Promise} sync data 41 | */ 42 | 43 | Client.prototype.login = function(username, password, cb) { 44 | var self = this; 45 | return sc.login(username, password).then(function(data) { 46 | self.username = data.username; 47 | self.auth_token = data.auth_token; 48 | self.lastSync = { 49 | time: Date.now(), 50 | data: data 51 | }; 52 | return data; 53 | }).nodeify(cb); 54 | }; 55 | 56 | /** 57 | * Sign up 58 | * @param {String} email 59 | * @param {String} password 60 | * @param {String} username 61 | * @return {Promise} sync data 62 | */ 63 | Client.prototype.register = function register(email, password, username, cb) { 64 | var self = this; 65 | return sc.register(email, password, username) 66 | .then(function(syncData) { 67 | console.log(typeof syncData); 68 | self.username = username; 69 | self.auth_token = syncData.auth_token; 70 | return syncData; 71 | }).nodeify(cb); 72 | }; 73 | 74 | /** 75 | * Removes the auth_token property. 76 | * AFIK there is no way to invalidate it unless you request a new one. 77 | */ 78 | Client.prototype.logout = function(cb) { 79 | delete this.auth_token; 80 | return Q(true).nodeify(cb); 81 | }; 82 | 83 | /** 84 | * Sync the data. You need to have an auth_token set first. 85 | * @return {Promise} The sync data. 86 | */ 87 | Client.prototype.sync = function(jsonOrCb, cb) { 88 | var self = this; 89 | var json = {}; 90 | if(typeof jsonOrCb == 'object') 91 | json = jsonOrCb; 92 | else if(typeof jsonOrCb == 'function') 93 | cb = jsonOrCb; 94 | return sc.sync(this.username, this.auth_token, json).then(function(data) { 95 | self.auth_token = data.auth_token; 96 | self.lastSync = { 97 | time: Date.now(), 98 | data: data 99 | }; 100 | return data; 101 | }).nodeify(cb); 102 | }; 103 | 104 | /** 105 | * Fetches a blob (image or video) 106 | * @param {String} id The blob id. 107 | * @return {Promise} A stream (decrypted if necessary) 108 | */ 109 | Client.prototype.getBlob = function(id, cb) { 110 | if (typeof this.auth_token === "undefined") return Q.reject("Not signed in").nodeify(cb); 111 | return sc.getBlob(this.username, this.auth_token, id).nodeify(cb); 112 | }; 113 | 114 | /** 115 | * Uploads an blob 116 | * @param {Stream} stream A readable stream to upload 117 | * @param {Boolean} isVideo True if the stream is a video. 118 | * @return {Promise} The blob id 119 | */ 120 | Client.prototype.upload = function(stream, isVideo, cb) { 121 | return sc.upload(this.username, this.auth_token, stream, isVideo).nodeify(cb); 122 | }; 123 | 124 | /** Send the snap to people 125 | * @param {String} mediaId the snap to send. 126 | * @param {Array|String} friends An array, or comma-seperated list of friends to send the snap to. 127 | * @param {Number} time How long (in seconds) a snap should be visible. This should only be set if the snap is a picture. 128 | */ 129 | Client.prototype.send = function(mediaId,friends,timeOrCb,cb) { 130 | var time; 131 | if(typeof timeOrCb === 'function') { 132 | cb = timeOrCb; 133 | } else { 134 | time = timeOrCb; 135 | } 136 | return sc.send(this.username,this.auth_token,mediaId,friends,time).nodeify(cb); 137 | }; 138 | 139 | /** 140 | * Add a friend 141 | * @param {String} friend Your friend's username 142 | * @return {Promise} 143 | */ 144 | Client.prototype.addFriend = function(friend, cb) { 145 | if (typeof this.auth_token === "undefined") return; 146 | var self = this; 147 | return sc.addFriend(this.username, this.auth_token, friend).nodeify(cb); 148 | }; 149 | 150 | /** 151 | * Set a friend's display name 152 | * @param {String} friend Your friend's username. 153 | * @param {String} newName Then new display name. 154 | * @return {Promise} 155 | */ 156 | Client.prototype.rename = function(friend, newName, cb) { 157 | if (typeof this.auth_token === "undefined") return; 158 | var self = this; 159 | return sc.rename(this.username, this.auth_token, friend, newName).nodeify(cb); 160 | }; 161 | 162 | /** 163 | * Unfriend someone 164 | * @param {String} friend The friend to remove 165 | * @return {Promise} 166 | */ 167 | Client.prototype.unfriend = function(friend, cb) { 168 | if (typeof this.auth_token === "undefined") return; 169 | return sc.unfriend(this.username, this.auth_token, friend).nodeify(cb); 170 | }; 171 | 172 | /** 173 | * Clear your feed 174 | * @return {Promise} 175 | */ 176 | Client.prototype.clear = function(cb) { 177 | return sc.clear(this.username, this.auth_token).nodeify(cb); 178 | }; 179 | 180 | /** 181 | * Set your privacy settings 182 | * @param {Boolean} only_friends If true, only recieve snaps from friends. 183 | * @return {Promise} 184 | */ 185 | Client.prototype.privacy = function(only_friends, cb) { 186 | return sc.privacy(this.username, this.auth_token, only_friends).nodeify(cb); 187 | }; 188 | 189 | /** 190 | * Update your email 191 | * @param {String} email Your new email. 192 | * @return {Promise} 193 | */ 194 | Client.prototype.updateEmail = function(email, cb) { 195 | return sc.updateEmail(this.username, this.auth_token, email).nodeify(cb); 196 | }; 197 | 198 | /** 199 | * Get a list of users who you have added as a friend 200 | * @return {Promise} 201 | */ 202 | Client.prototype.getFriends = function(cb) { 203 | return sc.getUpdates(this.username, this.auth_token).then(JSON.parse).then(function(data) { 204 | return data.updates_response.friends; 205 | }).nodeify(cb); 206 | }; 207 | 208 | /** 209 | * Get a list of users who have added you as a friend 210 | * @return {Promise} 211 | */ 212 | Client.prototype.getFriendRequests = function(cb) { 213 | return sc.getUpdates(this.username, this.auth_token).then(JSON.parse).then(function(data) { 214 | return data.updates_response.added_friends; 215 | }).nodeify(cb); 216 | }; 217 | -------------------------------------------------------------------------------- /snapchat.js: -------------------------------------------------------------------------------- 1 | /** 2 | *@license 3 | * Copyright 2013 Lucas A. Dohring 4 | * 5 | * Licensed under the EUPL, Version 1.1 or – as soon they 6 | * will be approved by the European Commission - subsequent 7 | * versions of the EUPL (the "Licence"); 8 | * 9 | * You may not use this work except in compliance with the 10 | * Licence. 11 | * 12 | * You may obtain a copy of the Licence at: 13 | * http://ec.europa.eu/idabc/eupl 14 | * 15 | * Unless required by applicable law or agreed to in 16 | * writing, software distributed under the Licence is 17 | * distributed on an "AS IS" basis, 18 | * 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 20 | * express or implied. 21 | * 22 | * See the Licence for the specific language governing 23 | * permissions and limitations under the Licence. 24 | */ 25 | 26 | var FormStream = require('multipart-form-stream'), 27 | crypto = require('crypto'), 28 | https = require('https'), 29 | util = require('util'), 30 | spawn = require("child_process").spawn, 31 | uuid = require("uuid-v4"), 32 | qs = require('querystring'), 33 | Q = require('q'); 34 | 35 | var e = module.exports; 36 | /** @const */ var blob_enc_key = e.blob_enc_key = Buffer('4d3032636e5135314a69393776775434', 'hex'); 37 | /** @const */ var pattern = e.hash_pattern = "0001110111101110001111010101111011010001001110011000110001000110"; 38 | /** @const */ var secret = e.secret = "iEk21fuwZApXlz93750dmW22pw389dPwOk"; 39 | /** @const */ var static_token = e.static_token = "m198sOkJEn37DjqZ32lpRu76xmw288xSQ9"; 40 | /** @const */ var hostname = e.hostname = "feelinsonice.appspot.com"; 41 | /** @const */ var user_agent = e.user_agent = "Snapchat/8.1.1 (Nexus 5; Android 21; gzip)"; 42 | 43 | var sink = require("stream-sink"); 44 | 45 | e.hash = function hash(param1, param2) { 46 | var s1 = secret + param1; 47 | var s2 = param2 + secret; 48 | 49 | var hash = crypto.createHash('sha256'); 50 | hash.update(s1, 'binary'); 51 | var h1 = hash.digest('hex'); 52 | 53 | var hash = crypto.createHash('sha256'); 54 | hash.update(s2, 'binary'); 55 | var h2 = hash.digest('hex'); 56 | 57 | var out = ''; 58 | for (var i = 0, len = pattern.length; i < len; ++i) { 59 | if (pattern[i] == '0') out += h1[i]; 60 | else out += h2[i]; 61 | } 62 | return out; 63 | }; 64 | 65 | 66 | 67 | /** @const */ e.MEDIA_IMAGE = 0; 68 | /** @const */ e.MEDIA_VIDEO = 1; 69 | 70 | /** 71 | * Make a post call and sign it with a req_token. 72 | * @param {String} endpoint The endpoint to call 73 | * @param {Object} post_data Data 74 | * @param {String} param1 Usually the auth_token 75 | * @param {String} param2 Usually the timestamp 76 | * @param {Boolean=false} raw If true, return a stream instead of a string. The stream will be paused to avoid data loss. 77 | * @return {Promise} 78 | */ 79 | e.postCall = function postCall(endpoint, post_data, param1, param2, raw, cb) { 80 | if(typeof raw === 'function') { 81 | cb = raw; 82 | raw = false; 83 | } 84 | post_data.req_token = e.hash(param1, param2); 85 | var data = qs.stringify(post_data); 86 | var opts = { 87 | host: hostname, 88 | method: 'POST', 89 | path: endpoint, 90 | headers: { 91 | 'User-Agent': e.user_agent, 92 | 'Accept-Language': 'en', 93 | 'Content-Type': 'application/x-www-form-urlencoded', 94 | 'Content-Length': data.length 95 | } 96 | }; 97 | return Q.promise(function(resolve, reject) { 98 | var req = https.request(opts, function(res) { 99 | if(raw) { 100 | res.pause(); 101 | return resolve(res); 102 | } 103 | res.pipe(sink().on('data', function(resp) { 104 | if(res.statusCode==200) 105 | resolve(resp); 106 | else 107 | reject(resp); 108 | })); 109 | }); 110 | req.end(data); 111 | }).nodeify(cb); 112 | }; 113 | 114 | /** 115 | * Login and get auth_token 116 | * @param {String} username 117 | * @param {String} password 118 | * @return {Promise} sync data 119 | */ 120 | e.login = function login(username, password, cb) { 121 | var ts = '' + Date.now(); 122 | return e.postCall('/ph/login', { 123 | username: username, 124 | password: password, 125 | timestamp: ts 126 | }, static_token, ts) 127 | .then(function(data) { 128 | var resp = JSON.parse(data); 129 | if(resp.auth_token) return(resp); 130 | else throw(resp); 131 | }).nodeify(cb); 132 | }; 133 | 134 | /** 135 | * Get current state and optionally update it 136 | * @param {String} username 137 | * @param {String} auth_token 138 | * @param {Object} json An object countaining fields to update. 139 | * @return {Promise} The current state 140 | */ 141 | e.sync = function(username, auth_token, json, cb) { 142 | var ts = Date.now().toString(); 143 | return e.postCall('/ph/sync', { 144 | username: username, 145 | timestamp: ts, 146 | json: JSON.stringify(json||{}), 147 | auth_token: auth_token 148 | }, auth_token, ts) 149 | .then(function(data) { 150 | return JSON.parse(data); 151 | }).nodeify(cb); 152 | }; 153 | 154 | /** 155 | * Fetch blob 156 | * @param {String} username 157 | * @param {String} auth_token 158 | * @param {String} id 159 | * @return {Promise} Readable stream 160 | */ 161 | e.getBlob = function(username, auth_token, id, cb) { 162 | var ts = Date.now().toString(); 163 | return e.postCall('/ph/blob', { 164 | id: id, 165 | timestamp: ts, 166 | username: username, 167 | }, auth_token, ts, true) 168 | .then(function(stream) { 169 | if(stream.statusCode != 200) 170 | return Q.promise(function(resolve, reject) { 171 | stream.setEncoding('ascii'); 172 | stream.pipe(sink().on('data', function(resp) { 173 | reject(resp); 174 | })); 175 | stream.resume(); 176 | }); 177 | if (stream.headers['content-type'] !== 'application/octet-stream') 178 | return stream; 179 | 180 | /*var decrypt = crypto.createDecipheriv('aes-128-ecb', blob_enc_key, ''); 181 | stream.on('data', function(data) { 182 | if(data !== undefined) 183 | decrypt.update(data); 184 | }).on('end', function() { 185 | var final = decrypt.final(); 186 | });*/ 187 | var decrypt = spawn('openssl', ['enc', '-d', '-K', '4d3032636e5135314a69393776775434', '-aes-128-ecb']); 188 | stream.pipe(decrypt.stdin); 189 | stream.resume(); 190 | return decrypt.stdout; 191 | }).nodeify(cb); 192 | }; 193 | 194 | /** 195 | * Upload a snap 196 | * @param {String} username 197 | * @param {String} auth_token 198 | * @param {Stream} stream A readable stream for the snap. 199 | * @param {Boolean} isVideo 200 | * @return {Promise} The blob's mediaId. 201 | */ 202 | e.upload = function upload(username, auth_token, stream, isVideo, cb) { 203 | var ts = ''+Date.now(); 204 | isVideo = Number(!!isVideo); 205 | 206 | var mediaId = (username + uuid()).toUpperCase(); 207 | var encrypt = spawn('openssl', ['enc', '-K', '4d3032636e5135314a69393776775434', '-aes-128-ecb']); 208 | encrypt.stdout.pause(); 209 | stream.pipe(encrypt.stdin); 210 | 211 | var form = new FormStream(); 212 | var req_token = e.hash(auth_token, ts); 213 | form.addField('req_token', req_token); 214 | form.addField('timestamp', ts); 215 | form.addStream('data', 'media', 'application/octet-stream', encrypt.stdout); 216 | form.addField('username', username); 217 | form.addField('media_id', mediaId); 218 | form.addField('type', isVideo); 219 | 220 | return Q.promise(function(resolve,reject) { 221 | var req = https.request({ 222 | host: hostname, 223 | method: 'POST', 224 | path: '/ph/upload', 225 | headers: { 226 | 'Content-type': 'multipart/form-data; boundary=' + form.getBoundary(), 227 | 'User-Agent': user_agent, 228 | } 229 | }, function(res) { 230 | res.setEncoding('ascii'); 231 | res.pipe(sink().on('data', function(data) { 232 | if (res.statusCode != 200) return reject(data); 233 | resolve(mediaId); 234 | })); 235 | }); 236 | form.on('data', function(data) { 237 | req.write(data); 238 | }).on('end', function(end) { 239 | req.end(end); 240 | }); 241 | }).nodeify(cb);; 242 | }; 243 | 244 | /** 245 | * Send a blob to a friend. 246 | * @param {String} username 247 | * @param {String} auth_token 248 | * @param {String} mediaId A unique identifyer for the blob generated by @link upload 249 | * @param {Array} friends An array of friends to send the snap to. 250 | * @return {Promise} 251 | */ 252 | e.send = function send(username, auth_token, mediaId, friends, time, cb) { 253 | var ts = Date.now()+''; 254 | var postData = { 255 | username: username, 256 | auth_token: auth_token, 257 | recipient: friends, 258 | media_id: mediaId, 259 | timestamp:ts 260 | }; 261 | if(typeof time != 'undefined') postData.time = time; 262 | return e.postCall('/ph/send', postData, auth_token,ts).nodeify(cb); 263 | }; 264 | 265 | /** 266 | * Add a friend 267 | * @param {String} username 268 | * @param {String} auth_token 269 | * @param {String} friend Your soon to be friends 270 | * @return {Promise} 271 | */ 272 | e.addFriend = function addFriend(username, auth_token, friend) { 273 | var ts = Date.now().toString(); 274 | return e.postCall('/ph/friend', { 275 | username: username, 276 | timestamp: ts, 277 | action: 'add', 278 | friend: friend 279 | }, auth_token, ts) 280 | .then(function(data) { 281 | return JSON.parse(data); 282 | }); 283 | }; 284 | 285 | /** 286 | * Change a friend's display name 287 | * @param {String} username 288 | * @param {String} auth_token 289 | * @param {String} friend The friend to modify 290 | * @param {String} newName Their new display name 291 | * @return {Promise} 292 | */ 293 | e.rename = function rename(username, auth_token, friend, newName, cb) { 294 | var ts = Date.now().toString(); 295 | return e.postCall('/ph/friend', { 296 | username: username, 297 | timestamp: ts, 298 | action: 'display', 299 | friend: friend, 300 | display: newName 301 | }, auth_token, ts) 302 | .then(function(data) { 303 | return JSON.parse(data); 304 | }).nodeify(cb); 305 | }; 306 | 307 | /** 308 | * Remove a friend 309 | * @param {String} username 310 | * @param {String} auth_token 311 | * @param {String} friend The friend to remove 312 | * @return {Promise} 313 | */ 314 | e.unfriend = function(username, auth_token, friend, cb) { 315 | var ts = Date.now().toString(); 316 | return e.postCall('/ph/friend', { 317 | username: username, 318 | timestamp: ts, 319 | action: 'delete', 320 | friend: friend, 321 | }, auth_token, ts) 322 | .then(function(data) { 323 | return JSON.parse(data); 324 | }).nodeify(cb); 325 | }; 326 | 327 | /** 328 | * Sign up 329 | * @param {String} email 330 | * @param {String} password 331 | * @param {String} username 332 | * @return {Promise} sync data 333 | */ 334 | e.register = function register(email, password, username, cb) { 335 | var ts = Date.now().toString(); 336 | return e.postCall('/ph/register', { 337 | timestamp: ts, 338 | password: password, 339 | email: email 340 | }, static_token, ts) 341 | .then(function(data) { 342 | var resp = JSON.parse(JSON.parse(data)); 343 | var token = resp.token; 344 | if(typeof token === 'undefined') 345 | throw resp; 346 | 347 | var ts = Date.now().toString(); 348 | return e.postCall('/ph/registeru', { 349 | timestamp: ts, 350 | email: email, 351 | username: username, 352 | }, static_token, ts) 353 | .then(function(data) { 354 | var resp = JSON.parse(data); 355 | if(data.auth_token === 'undefined') 356 | throw resp; 357 | return resp; 358 | }); 359 | }).nodeify(cb); 360 | }; 361 | 362 | /** 363 | * Clear your feed 364 | * @param {String} username 365 | * @param {String} auth_token 366 | * @return {Promise} 367 | */ 368 | e.clear = function clear(username, auth_token, cb) { 369 | var ts = Date.now().toString(); 370 | return e.postCall('/ph/clear', { 371 | timestamp: ts, 372 | username: username 373 | }, auth_token, ts).nodeify(cb); 374 | }; 375 | 376 | /** 377 | * Update your email 378 | * @param {String} username 379 | * @param {String} auth_token 380 | * @param {String} email Your new email. 381 | * @return {Promise} 382 | */ 383 | e.updateEmail = function updateEmail(username, auth_token, email, cb) { 384 | var ts = Date.now().toString(); 385 | return e.postCall('/ph/settings', { 386 | timestamp: ts, 387 | action: 'updateEmail', 388 | email: email, 389 | username: username 390 | }, auth_token, ts).nodeify(cb); 391 | }; 392 | 393 | /** 394 | * Update your privacy settings 395 | * @param {String} username 396 | * @param {String} auth_token 397 | * @param {Boolean} only_friends 398 | * @return {Promise} 399 | */ 400 | e.privacy = function privacy(username, auth_token, only_friends, cb) { 401 | only_friends = !!only_friends; 402 | var ts = Date.now().toString(); 403 | return e.postCall('/ph/settings', { 404 | timestamp: ts, 405 | action: 'updatePrivacy', 406 | privacySetting: +only_friends, 407 | username: username 408 | }, auth_token, ts).nodeify(cb); 409 | }; 410 | 411 | /** 412 | * Get updates from the SnapChat server 413 | * @param {String} auth_token 414 | * @return {Promise} 415 | */ 416 | e.getUpdates = function(username, auth_token, cb) { 417 | var ts = Date.now().toString(); 418 | return e.postCall('/bq/all_updates', { 419 | timestamp: ts, 420 | username: username 421 | }, auth_token, ts).nodeify(cb); 422 | }; 423 | 424 | e.Client = require('./client'); 425 | --------------------------------------------------------------------------------