Auto submit your article link with the Reddit api

Because none of the current implementation of Reddit api in php doesnt work (probably out of date), i decided to write my own auto submiter. The main problem with this submitter is that Reddit requires to enter a captcha in order to submit a link or text, so its not fully automated.

To submit a link through the Reddit api we will need to do these steps:

  • 1.request captcha
  • 2.login
  • 3.submit link

Top of the class:

class Reddit {

        private $host = "http://www.reddit.com";
        private $mod_hash = null;
        private $cookie = null;
        private $iden = null;
        public $errors = array();
        public $data = array();
...

Request captcha

Because Reddit requires a captcha for link submition, we will need to send a request to /api/new_captcha. As response we will get an iden hash value.

public function new_iden(){
	$args = array('api_type' => 'json');
	$data = $this->request("/api/new_captcha", $args);
	$this->iden = $data['response']->json->data->iden;
}

Output:

...
[data] => stdClass Object
(
	[iden] => J6oawm2qijMkaG2ZvSkrkbYivul8fSdY
)
...

With this iden value, we can request a captcha image by sending a GET request to /captcha/[iden]. As response we will get a 120x50 PNG image. Be carefull not to send a POST request(e.g use CURLOPT_POSTFIELDS option in cURL) even without any args you will get a 404 response code.

public function captcha(){
	$data = $this->request("/captcha/" . $this->iden);
	return $data['response'];
}

To make things clear:

Reddit iden captcha explanation

Somewhere in the Reddit system, each iden is mapped to a captcha image, so when you supply the captcha answer, the system will know if the captcha anwer is corect or not.

Login through the Reddit API

To login, we will need to send a POST request to /api/login with the following arguments: username, password and api_type. The api_type is used for the response format, e.g in which format we would like to receive the response. In this case we choose to be in JSON.

public function login($username, $password){

	$args = array("api_type" => "json",
			"user" => $username,
			"passwd" => $password);

	$data = $this->request("/api/login", $args); 
	if(count($data['response']->json->errors) == 0){
		$this->mod_hash = $data['response']->json->data->modhash;
		$this->cookie = $data['response']->json->data->cookie;
	}else{
		foreach($data['response']->json->errors as $error){
			$this->errors[] = implode(" | ", $error);
		}
	}
}

If we supply a valid user/pass we get a response similar to this one:

...
[errors] => Array
(
)
[data] => stdClass Object
(
	[modhash] => btmrwlol7b08c4ea2d0391271eff0b38fee3531dbc045e3ee3
	[cookie] => 21349431,2013-10-27T16:57:35,98f30d223e633a25dbf9a2615673fdc6825b6a0b
)
...

As you can see, the Reddit api returns a modhash and cookie value if your login was successfull. As stated in the Reddit api documentation, a modhash is a token that the Reddit API requires to help prevent CSRF. We need these two values so we can send them along with the request when we are submiting a link. The modhash value goes in the header info e.g CURLOPT_HTTPHEADER option, and the cookie value in the CURLOPT_COOKIE option.

From the Reddit api documentation: The preferred way to send a modhash is to include an X-Modhash custom HTTP header with your requests.

Submit link with the Reddit Api

public function submit($title, $url, $subreddit, $captcha){
	$args = array(
		'api_type' => 'json',
		'captcha' => $captcha,
		'iden' => $this->iden,
		'kind' => 'link',
		'resubmit' => false,
		'save' => false,
		'sendreplies' => false,
		'sr' => $subreddit,
		'title' => $title,
		'uh / X-Modhash header' => $this->mod_hash,
		'url' => $url,
	);

	$data = $this->request("/api/submit", $args);
	if(count($data['response']->json->errors) != 0){
		$this->iden = $data['response']->json->captcha;
		foreach($data['response']->json->errors as $error){
			$this->errors[] = implode(" | ", $error);
		}
	}
}

Here are some of the important arguments explained:

  • captcha - letters from the captcha image
  • iden - the iden value which reffers to the captcha
  • kind - this arg accepts two values, link and text
  • resubmit - If a link with the same URL exist in the specified subreddit an error will be returned unless resubmit is true
  • save - if save is true, the link will be implicitly saved after submission
  • sr - name of the subreddit
  • title - title from the link page
  • uh / X-Modhash header - modhash value returned after successfull login
  • url - the link you want to submit

If we supply wrong captcha answer we will get an error and new captcha iden, so we dont need to send a new request to /api/new_captcha.

Output

...
[captcha] => 9VeLztScz5sFhCAxXXw2SoSWFVYjLkE5
[errors] => Array
(
	[0] => Array
	(
		[0] => BAD_CAPTCHA
		[1] => care to try these again?
		[2] => captcha
	)
)
...

Finaly, the request method

public function request($url, $args = array()){

	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $this->host . $url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
	curl_setopt($ch, CURLOPT_USERAGENT,'MyRedditBot v1.0');
	curl_setopt($ch, CURLOPT_TIMEOUT, 15);

	if($url == "/api/submit"){
		curl_setopt($ch, CURLOPT_HTTPHEADER, array("X-Modhash: " . $this->mod_hash));
		curl_setopt($ch, CURLOPT_COOKIE, "reddit_session=" . $this->cookie);
	}
	
	if($url != "/captcha/" . $this->iden){
		curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($args));
		$data['response'] = json_decode(curl_exec($ch));
	}else{
		$data['response'] = curl_exec($ch);
	}

	$data['http_code'] = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	$data['last_url'] = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
	curl_close($ch);
	$this->data[] = $data;
	return $data;
}

We log each error and response from Reddit in array along with the http status code and last url in case we are redirected.

Dont forget to respect the Reddit rules.

  • 1. Dont spam.
  • 2. No more than 30 requests per minute or an average one request every two seconds.
  • 3. Use a unique and descriptive name in the user agent, for example your Reddit username.
- Posted by rutix to Php
comments powered by Disqus