Categories: Development

Creating an automated blog link Reddit submitter

Creating an automated system to submit my blog posts to my subreddit using PHP and the Reddit API.

The idea is every couple of hours a check will be made to see if my latest blog post is the most recent post on /r/corbpie. If it isn’t then using the API class a post will be submitted using the blog post URL and its title.

The PHP class is a stripped-down and cleaned up version from the outdated reddit-php-sdk.

The main part of this task is authenticating the POST submit. By setting the client id and client secret with the redirect URL this is achieved.

Reddit API client id and client secret can be found at www.reddit.com/prefs/apps/

<?php
public function __construct($client_id, $client_secret, $endpoint_redirect)
    {
        $this->clientId = $client_id;
        $this->clientSecret = $client_secret;
        $endpoint_oauth = 'https://oauth.reddit.com';
        $endpoint_oauth_authorize = 'https://ssl.reddit.com/api/v1/authorize';
        $endpoint_oauth_token = 'https://ssl.reddit.com/api/v1/access_token';
        $scopes = 'save,modposts,identity,edit,flair,history,modconfig,modflair,modlog,modposts,modwiki,mysubreddits,privatemessages,read,report,submit,subscribe,vote,wikiedit,wikiread';
        if (isset($_COOKIE['reddit_token'])) {//Access token exists
            $token_info = explode(":", $_COOKIE['reddit_token']);
            $this->tokenType = $token_info[0];
            $this->accessToken = $token_info[1];
        } else {
            if (isset($_GET['code'])) {
                $code = $_GET["code"];
                $post_values = sprintf("code=%s&redirect_uri=%s&grant_type=authorization_code",
                    $code,
                    $endpoint_redirect
                );
                $token = $this->doCurl($endpoint_oauth_token, $post_values, null, true);
                if (isset($token->access_token)) {
                    $this->accessToken = $token->access_token;
                    $this->tokenType = $token->token_type;
                    $cookie_time = 3600 * 480 + time();//Token expires in 480hrs
                    setcookie('reddit_token', "" . $this->tokenType . ":" . $this->accessToken . "", $cookie_time);
                }
            } else {
                $state = rand();
                $urlAuth = sprintf("%s?response_type=code&client_id=%s&redirect_uri=%s&scope=%s&state=%s&duration=permanent",
                    $endpoint_oauth_authorize,
                    $client_id,
                    $endpoint_redirect,
                    $scopes,
                    $state
                );
                header("Location: $urlAuth");
            }
        }
        $this->apiMain = $endpoint_oauth;
        $this->authMode = 'oauth';
    }

The cURL function which does the GET and POST requests with the authentication

<?php
    private function doCurl($url, $post_values = null, $headers = null, $auth = false)
    {
        $ch = curl_init($url);
        $options = array(
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CONNECTTIMEOUT => 6,
            CURLOPT_TIMEOUT => 8,
            CURLOPT_USERAGENT => 'write.corbpie blog poster'
        );
        if ($post_values != null) {
            $options[CURLOPT_POSTFIELDS] = $post_values;
            $options[CURLOPT_CUSTOMREQUEST] = "POST";
        }
        if ($this->authMode == 'oauth') {
            $headers = array("Authorization: " . $this->tokenType . " " . $this->accessToken . "");
            $options[CURLOPT_HEADER] = false;
            $options[CURLINFO_HEADER_OUT] = false;
            $options[CURLOPT_HTTPHEADER] = $headers;
        }
        if ($auth) {
            $options[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC;
            $options[CURLOPT_USERPWD] = $this->clientId . ":" . $this->clientSecret;
            $options[CURLOPT_SSLVERSION] = 4;
            $options[CURLOPT_SSL_VERIFYPEER] = false;
            $options[CURLOPT_SSL_VERIFYHOST] = 2;
        }
        curl_setopt_array($ch, $options);
        $apiResponse = curl_exec($ch);
        $response = json_decode($apiResponse);
        curl_close($ch);
        return $response;
    }

The following functions are the Reddit workers, starting with the post create function. This needs to have a defined title, URL and subreddit to submit to.

Next is a function that will approve the post since /r/corbpie is locked down, this goes with the getName function which returns the posts name (its id) which is needed to approve it.

There are functions to get the most recent post URL and title from /r/corbpie, this is used when comparing to the most recent blog post.

getListing function is used by these past few stated functions, it returns data about the most recent posts from a chosen sub.

<?php
    public function createPost($title = null, $url = null, $subreddit = null)
    {
        $urlSubmit = "" . $this->apiMain . "/api/submit";
        if ($title == null || $subreddit == null) {
            return "Please set a title and URL";
        }
        //Check if post is URL or self post (text)
        $kind = ($url == null) ? "self" : "link";
        //Build post data
        $postData = sprintf("kind=%s&sr=%s&title=%s&r=%s",
            $kind,
            $subreddit,
            urlencode($title),
            $subreddit
        );
        //if link exists add to POST data
        if ($url != null) {
            $postData .= "&url=" . urlencode($url);
        }
        $response = $this->doCurl($urlSubmit, $postData);
    }

    public function approvePost($id)
    {
        $url = "" . $this->apiMain . "/api/approve";
        $postData = "id=$id";
        return $this->doCurl($url, $postData);
    }

    public function getLastUrl($sub)
    {
        $data = $this->getListing($sub);
        return $data->data->children[0]->data->url;
    }

    public function getName($sub)
    {
        $data = $this->getListing($sub);
        return $data->data->children[0]->data->name;
    }

    public function getTitle($sub)
    {
        $data = $this->getListing($sub);
        return $data->data->children[0]->data->title;
    }

    public function getListing($sr, $limit = 5)
    {
        return json_decode(file_get_contents("https://www.reddit.com/r/$sr/new.json?&limit=$limit"));
    }

Finally, the functions that interact with the WordPress REST API, these get the title and URL from the most recent published blog post at write.corbpie.com

<?php
    public function getLatestBlogData()
    {
        return json_decode(file_get_contents("https://write.corbpie.com/wp-json/wp/v2/posts"), true);
    }

    public function getLatestBlogUrl($data)
    {
        return $data[0]['link'];
    }

    public function getLatestBlogTitle($data)
    {
        return $data[0]['title']['rendered'];
    }

These function wrapped in the class corbpieBlogPoster are called upon:

<?php
$cbp = new corbpieBlogPoster('CLIENTID', 'CLIENTSECRET', 'https://domain.com/dir/file.php');

$last_sub_url = $cbp->getLastUrl('corbpie');//Get latest post url on sub corbpie

$blog_data = $cbp->getLatestBlogData();//Get latest write.corbpie.com posts data
$latest_blog_url = $cbp->getLatestBlogUrl($blog_data);//Latest blog post url

if ($latest_blog_url === $last_sub_url) {
    echo "Same";//Most recent blog url and recent sub url are the same
} else {
    $title = $cbp->getLatestBlogTitle($blog_data);//Latest blog title
    $new_post = $cbp->createPost($title, $latest_blog_url, 'corbpie');//Create new reddit post at r/corbpie
    sleep(4);//Gives time for post to show up
    $approve_post = $cbp->approvePost($cbp->getName('corbpie'));//Approve the new post
    echo "New[$title][$latest_blog_url]";
}

The full class can be found here.

Share

Recent Posts

Kennington reservoir drained drone images

A drained and empty Kennington reservoir images from a drone in early July 2024. The…

2 years ago

Merrimu Reservoir drone images

Merrimu Reservoir from drone. Click images to view larger.

2 years ago

Early September spring shots

Some early September spring shots from a bushwalk, It is still too early for wildflowers.

3 years ago

Cloning an NVMe to SK Hynix Platinum P41 NVMe

I recently needed to clone over a smaller NVMe to a 2TB SK Hynix Platinum…

3 years ago

Using Laravel’s deleteDirectory to delete folders and their contents

If you ever needed a simple solution in Laravel to delete an entire directory and…

3 years ago

How to allow only 1 user to register in a Laravel application

How to enforce only one user to register or be registered in a Laravel web…

3 years ago