Source: routes/snaps.js

module.exports = Snaps

var debug = require('debug')('snapchat:snaps')
var Promise = require('bluebird')

var constants = require('../lib/constants')
var StringUtils = require('../lib/string-utils')
var Request = require('../lib/request')

var SnapOptions = require('../models/snap-options')
var SKBlob = require('../models/blob')
var SKLocation = require('../models/location')

/**
 * Snapchat wrapper for Snap-related API calls.
 *
 * @class
 * @param {Object} opts
 */
function Snaps (client, opts) {
  var self = this
  if (!(self instanceof Snaps)) return new Snaps(client, opts)
  if (!opts) opts = {}

  self.client = client
}

/**
 * Sends a snap to everyone in recipients with text text for duration seconds.
 *
 * @param {SKBlob} blob The SKBlob object containing the image or video data to send. Can be created with any NSData object.
 * @param {Array<string>|string} recipients An array of username strings.
 * @param {string} text The text to label the snap with. This text is not superimposed upon the image; you must do that yourself.
 * @param {number} duration The length of the snap. It must be greater than 0 or an exception will be raised.
 * @param {function} cb
 */
Snaps.prototype.sendSnap = function (blob, recipients, text, duration, cb) {
  var self = this
  debug('Snaps.sendSnap')

  return self.sendSnapCustom(blob, new SnapOptions(recipients, text, duration), cb)
}

/**
 * Sends a snap with the given options.
 *
 * @param {SKBlob} blob The SKBlob object containing the image or video data to send. Can be created with any Buffer.
 * @param {SnapOptions} opts The options for the snap to be sent.
 * @param {function} cb
 */
Snaps.prototype.sendSnapCustom = function (blob, opts, cb) {
  var self = this

  return new Promise(function (resolve, reject) {
    debug('Snaps.sendSnapCustom')

    self._uploadSnap(blob, function (err, mediaID) {
      if (err) {
        return reject(err)
      }

      self.client.post(constants.endpoints.snaps.send, {
        'camera_front_facing': !!opts.cameraFrontFacing,
        'country_code': self.client.session.countryCode,
        'media_id': mediaID,
        'recipients': JSON.stringify(opts.recipients),
        'recipient_ids': JSON.stringify(opts.recipients),
        'reply': !!opts.isReply,
        'time': +opts.timer,
        'zipped': 0,
        'username': self.client.username
      }, function (err, result) {
        if (err) {
          return reject(err)
        }
        return resolve(result)
      })
    })
  }).nodeify(cb)
}

/**
 * Marks a snap as opened for secondsViewed seconds.
 *
 * @param {number} secondsViewed The number of seconds the snap was viewed for.
 * @param {function} cb
 */
Snaps.prototype.markSnapViewed = function (snap, secondsViewed, cb) {
  var self = this
  debug('Snaps.markSnapViewed')

  return self.markSnapsViewed([ snap ], [ new Date() ], [ secondsViewed ], cb)
}

/**
 * Marks a set of snaps as opened for the specified length at the given times.
 *
 * @param {Array<SKSnap>} snaps An array of SKSnap objects.
 * @param {Array<Date>} times An array of Date objects.
 * @param {Array<number>} secondsViewed An array of numbers.
 * @param {function} cb
 */
Snaps.prototype.markSnapsViewed = function (snaps, times, secondsViewed, cb) {
  var self = this
  debug('Snaps.markSnapsViewed')

  if (snaps.length !== times.length || times.length !== secondsViewed.length) {
    throw new Error('Snaps.markSnapsViewed all arrays must have the same length')
  }

  var json = { }

  snaps.forEach(function (snap, index) {
    json[snap.identifier] = {
      't': StringUtils.timestampFrom(times[index]),
      'sv': secondsViewed[index]
    }
  })

  return self.client.post(constants.endpoints.update.snaps, {
    'added_friends_timestamp': StringUtils.timestampFrom(self.session.addedFriendsTimestamp),
    'username': self.client.username,
    'json': JSON.stringify(json)
  }, cb)
}

/**
 * Marks a snap as screenshotted and viewed for secondsViewed seconds.
 *
 * @param {number} secondsViewed The number of seconds the snap was viewed for.
 * @param {function} cb
 */
Snaps.prototype.markSnapScreenshot = function (snap, secondsViewed, cb) {
  var self = this
  debug('Snaps.markSnapScreenshot')

  var timestamp = StringUtils.timestamp()
  var snapInfo = { }

  snapInfo[snap.identifier] = {
    't': timestamp | 0,
    'sv': secondsViewed,
    'c': constants.SnapStatus.Screenshot
  }

  var screenshot = {
    'eventName': 'SNAP_SCREENSHOT',
    'params': {
      'id': snap.identifier
    },
    'ts': StringUtils.timestamp() | 0
  }

  return self.client.sendEvents([ screenshot ], snapInfo, cb)
}

/**
 * Loads a snap.
 *
 * @param {Snap} snap
 * @param {function} cb
 */
Snaps.prototype.loadSnap = function (snap, cb) {
  var self = this
  debug('Snaps.loadSnap')

  return self._loadSnapWithIdentifier(snap.identifier, cb)
}

/**
 * Loads filters for a location.
 *
 * @param {Object} location { lat, lng }
 * @param {function} cb
 */
Snaps.prototype.loadFiltersForLocation = function (location, cb) {
  var self = this

  return new Promise(function (resolve, reject) {
    debug('Snaps.loadFiltersForLocation')

    self.client.post(constants.endpoints.misc.locationData, {
      'lat': location.lat,
      'lng': location.lng,
      'screen_width': self.client.screenSize.width,
      'screen_height': self.client.screenSize.height,
      'username': self.client.username
    }, function (err, result) {
      if (err) {
        return reject(err)
      } else if (result) {
        return resolve(new SKLocation(result))
      }

      return reject(new Error('Snaps.loadFiltersForLocation parse error'))
    })
  }).nodeify(cb)
}

/**
 * @private
 */
Snaps.prototype._loadSnapWithIdentifier = function (identifier, cb) {
  var self = this

  return new Promise(function (resolve, reject) {
    self.client.post(constants.endpoints.snaps.loadBlob, {
      'id': identifier,
      'username': self.client.username
    }, function (err, body) {
      if (err) {
        return reject(err)
      }

      SKBlob.initWithData(body, function (err, blob) {
        if (err) {
          return reject(err)
        }
        return resolve(blob)
      })
    })
  }).nodeify(cb)
}

/**
 * @private
 * @param {SKBlob} blob
 * @param {function} cb
 */
Snaps.prototype._uploadSnap = function (blob, cb) {
  var self = this
  var uuid = StringUtils.mediaIdentifier(self.client.username)

  if (!(blob instanceof SKBlob)) {
    throw new Error('Snap._uploadSnap invalid argument "blob" must be SKBlob instance')
  }

  var params = {
    'media_id': uuid,
    'type': blob.isImage ? constants.MediaKind.Image.value : constants.MediaKind.Video.value,
    'data': blob.data,
    'zipped': 0,
    'features_map': '{}',
    'username': self.client.username
  }

  var headers = { }
  headers[constants.headers.clientAuthToken] = 'Bearer ' + self.client.googleAuthToken
  headers[constants.headers.contentType] = 'multipart/form-data; boundary=' + constants.core.boundary

  Request.postCustom(constants.endpoints.snaps.upload, params, headers, self.client.authToken, function (err) {
    if (err) {
      return cb(err)
    } else {
      return cb(null, uuid)
    }
  })
}