Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Captcha and registration implementation #57

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 25 additions & 12 deletions README.md
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@ Snapchat for PHP
================
[![Build Status](https://travis-ci.org/JorgenPhi/php-snapchat.png)](https://travis-ci.org/JorgenPhi/php-snapchat)

This library is built to communicate with the Snapchat API. It was nearly
feature complete and lacks some newer functionality available in the latest
versions of the official apps (stories, text messages, and "chat").
This library is built to communicate with the Snapchat API. It is nearly
feature complete but still lacks some functionality available in the latest
versions of the official apps (namely Stories).

It's similar to the [excellent Snaphax library](http://github.com/tlack/snaphax)
built by Thomas Lackner <[@tlack](http://twitter.com/tlack)>, but the approach
is different enough that I figured it deserved its own repo.

We love it when developers add new features, fix bugs, and submit pull requests.
(We need more pull requests!)


Usage
-----
Expand All @@ -23,7 +20,12 @@ Include src/snapchat.php via require_once or Composer or whatever, then:
<?php

// Log in:
$snapchat = new Snapchat('username', 'password');
$snapchat = new Snapchat('username' 'password');

//Register an Account and username:
$snapchat = new Snapchat();
$snapchat->register('email','password','birthday');
$snapchat->registerUsername('email', 'username');

// Get your feed:
$snaps = $snapchat->getSnaps();
Expand All @@ -33,7 +35,7 @@ $snaps = $snapchat->getFriendStories();

// Download a specific snap:
$data = $snapchat->getMedia('122FAST2FURIOUS334r');
file_put_contents('/home/jorgen/snap.jpg', $data);
file_put_contents('/home/dan/snap.jpg', $data);

// Download a specific story:
$data = $snapchat->getStory('[story_media_id]', '[story_key]', '[story_iv]');
Expand All @@ -53,14 +55,14 @@ $snapchat->markSnapShot('122FAST2FURIOUS334r');
// Upload a snap and send it to me for 8 seconds:
$id = $snapchat->upload(
Snapchat::MEDIA_IMAGE,
file_get_contents('/home/jorgen/whatever.jpg')
file_get_contents('/home/dan/whatever.jpg')
);
$snapchat->send($id, array('jorgenphi'), 8);
$snapchat->send($id, array('stelljes'), 8);

// Upload a video story:
$id = $snapchat->upload(
Snapchat::MEDIA_VIDEO,
file_get_contents('/home/jorgen/whatever.mov')
file_get_contents('/home/dan/whatever.mov')
);
$snapchat->setStory($id, Snapchat::MEDIA_VIDEO);

Expand Down Expand Up @@ -101,14 +103,25 @@ $snapchat->deleteFriend('bart');
$snapchat->updatePrivacy(Snapchat::PRIVACY_FRIENDS);

// You want to change your email:
$snapchat->updateEmail('jorgen@example.com');
$snapchat->updateEmail('dan@example.com');

// Log out:
$snapchat->logout();

?>
```

Snaptcha
-----

For the methods on bypassing Snapchat's captcha system see this [gist](https://gist.github.com/hako/adb2ab9419eda2dca62b):
```php
$s->getCaptcha('username', true);
```

```php
$s->sendCaptcha('solution', 'captcha_id', 'username');
```

Documentation
------------
Expand Down
149 changes: 123 additions & 26 deletions src/snapchat.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,27 +157,26 @@ public function logout() {
* @todo
* Add better validation.
*
* @param string $username
* The desired username.
* @param string $password
* The password to associate with the account.
* @param string $email
* The email address to associate with the account.
* @param string $password
* The password to associate with the account.
* @param $birthday string
* The user's birthday (yyyy-mm-dd).
*
* @return mixed
* The data returned by the service or FALSE if registration failed.
* Generally, returns the same result as calling self::getUpdates().
* The data returned is TRUE if the registration succeeded or
* FALSE if the registration failed. Generally, returns the
* same result as calling self::getUpdates().
*/
public function register($username, $password, $email, $birthday) {
public function register($email, $password, $birthday) {
$timestamp = parent::timestamp();
$result = parent::post(
'/register',
array(
'birthday' => $birthday,
'password' => $password,
'email' => $email,
'password' => $password,
'birthday' => $birthday,
'timestamp' => $timestamp,
),
array(
Expand All @@ -186,39 +185,137 @@ public function register($username, $password, $email, $birthday) {
)
);

if (!isset($result->token)) {
if (!isset($result->auth_token)) {
return FALSE;
}

$timestamp = parent::timestamp();
$result = parent::post(
'/registeru',
array(
'email' => $email,
'username' => $username,
'timestamp' => $timestamp,
),
array(
parent::STATIC_TOKEN,
$timestamp,
)
);

// If registration is successful, set the username and auth_token.
if (isset($result->logged) && $result->logged) {
$this->auth_token = $result->auth_token;
$this->username = $result->username;

$this->cache = new SnapchatCache();
$this->cache->set('updates', $result);

return $result;
return TRUE;
}

else {
return FALSE;
}
}

/**
* Registers a username.
*
* @param string $email
* The email address to associate with the account.
* @param string $username
* The desired username for this account.
*
* @return mixed
* TRUE is returned if the username is accepted.
* status integer is returned if the username is either of the following:
*
* 69 - too short
* 70 - too long
* 71 - bad username
* 72 - taken
*/
public function registerUsername($email, $username) {
$timestamp = parent::timestamp();
$result = parent::post(
'/register_username',
array(
'username' => $email,
'selected_username' => $username,
'timestamp' => $timestamp
),
array(
$this->auth_token,
$timestamp,
)
);

if(!property_exists($result, "status"))
{
return TRUE;
}
else if($result->status == 69 || 70 || 71 || 72) {
return $result->status;
}
}

/**
* Gets the captcha associated to the username from snapchat's server.
*
* @param string $username
* The username to get the captcha puzzle, or in this case an email.
*
* @param bool $download
* Download the captcha archive from snapchat. (optional)
*
* @return string
* A string which is the captcha_id. FALSE on failure.
*
*/
public function getCaptcha($username, $download = NULL) {
$timestamp = parent::timestamp();
$result = parent::post(
'/get_captcha',
array(
'username' => $username,
'timestamp' => $timestamp,
'dl' => $download
),
array(
$this->auth_token,
$timestamp
)
);
return $result;
}

/**
* Sends the captcha solution to snapchat's server.
*
* @param $captcha_solution
* The binary solution of the captcha. must be 8 chars long.
*
* @param $captcha_id
* The ID of the captcha thats being solved.
*
* @param $username
* The username to be verified.
*
* @return mixed
* returns TRUE if captcha is solved, FALSE otherwise.
*
*/
public function sendCaptcha($captcha_solution, $captcha_id, $username) {
$timestamp = parent::timestamp();
$result = parent::post(
'/solve_captcha',
array(
'captcha_solution' => $captcha_solution,
'captcha_id' => $captcha_id,
'username' => $username,
'timestamp' => $timestamp,
),
array(
$this->auth_token,
$timestamp,

)
);
unlink(".headers.txt");
if(is_null($result)) {
return TRUE;
}
else if($result == FALSE) {
return FALSE;
}
}

/**
* Retrieves general user, friend, and snap updates.
*
Expand Down
53 changes: 47 additions & 6 deletions src/snapchat_agent.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,26 @@
abstract class SnapchatAgent {

/*
* App version (as of 2013-11-20). Before updating this value, confirm
* App version (as of 2014-12-5). Before updating this value, confirm
* that the library requests everything in the same way as the app.
*/
const VERSION = '4.1.07';
const VERSION = '8.0.1.3';

/*
* The API URL. We're using the /bq endpoint, the one that the iPhone
* The API URL. We're using the /loq endpoint, the one that the iPhone
* uses. Android clients still seem to be using the /ph endpoint.
* We still support the /bq endpoint for the captcha.
*
* @todo
* Make library capable of using different endpoints (some of the
* resource names are different, so they aren't interchangeable).
*/
const URL = 'https://feelinsonice-hrd.appspot.com/bq';
const URL = 'https://feelinsonice-hrd.appspot.com/loq';

/*
* The old API URL.
*/
const OLD_URL = "https://feelinsonice-hrd.appspot.com/bq";

/*
* The API secret. Used to create access tokens.
Expand Down Expand Up @@ -53,7 +59,7 @@ abstract class SnapchatAgent {
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_TIMEOUT => 10,
CURLOPT_USERAGENT => 'Snapchat/4.1.07 (Nexus 4; Android 18; gzip)',
CURLOPT_USERAGENT => 'Snapchat/8.0.1.3 (Nexus 4; Android 18; gzip)',
);

/**
Expand Down Expand Up @@ -286,20 +292,55 @@ public function post($endpoint, $data, $params, $multipart = FALSE) {

$data['req_token'] = self::hash($params[0], $params[1]);
$data['version'] = self::VERSION;

if(array_key_exists('dl', $data)) {
$download = $data['dl'];
}

if (!$multipart) {
$data = http_build_query($data);
}

if($endpoint == "/get_captcha" || $endpoint == "/solve_captcha" ) {

$options = self::$CURL_OPTIONS + array(
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $data,
CURLOPT_URL => self::OLD_URL . $endpoint,
);

curl_setopt($ch, CURLOPT_VERBOSE, true);
file_put_contents(".headers.txt", " ");
curl_setopt($ch, CURLOPT_STDERR, fopen(dirname(__DIR__) . "/.headers.txt", "r+"));

} else {

$options = self::$CURL_OPTIONS + array(
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $data,
CURLOPT_URL => self::URL . $endpoint,
);
curl_setopt_array($ch, $options);

}

curl_setopt_array($ch, $options);
$result = curl_exec($ch);

// upon registration, the captcha sends us a header to download.
if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) == "application/zip; charset=UTF-8") {
$filename = fopen(dirname(__DIR__) . "/.headers.txt", "r+");
$stream = stream_get_contents($filename);
$file = preg_match("/(=)(\S+).zip/", $stream, $match);
$group_matched = $match[2] . ".zip";
$captcha_id = str_replace(".zip", "", $group_matched);
if($download == 1) {
file_put_contents($group_matched, $result);
}
unlink(".headers.txt");
curl_close($ch);
return $captcha_id;
}

// If cURL doesn't have a bundle of root certificates handy, we provide
// ours (see http://curl.haxx.se/docs/sslcerts.html).
if (curl_errno($ch) == 60) {
Expand Down