├── .gitignore ├── .php_cs.dist ├── LICENSE.md ├── README.md ├── composer.json └── src ├── Exceptions └── RedditAuthenticationException.php ├── Interfaces └── RedditApiInterface.php ├── RedditAPI.php ├── RedditAPIFacade.php ├── RedditAPIServiceProvider.php ├── RedditLive.php ├── RedditOAuth2.php ├── RedditRateLimiter.php └── config └── reddit-api.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | vendor 3 | .php_cs.cache 4 | .idea -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | in([ 8 | $project_path . '/src', 9 | ]) 10 | ->name('*.php') 11 | ->ignoreDotFiles(true) 12 | ->ignoreVCS(true); 13 | 14 | return JamesAusten\styles($finder); 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2017 CodeWizz 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel-Reddit-API 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/codewizz/laravel-reddit-api.svg?style=flat-square)](https://packagist.org/packages/codewizz/laravel-reddit-api) 4 | [![Total Downloads](https://img.shields.io/packagist/dt/codewizz/laravel-reddit-api.svg?style=flat-square)](https://packagist.org/packages/codewizz/laravel-reddit-api) 5 | 6 | Using this package you can easily retrieve data from Reddit API. 7 | 8 | Laravel wrapper for https://github.com/rotorcowboy/Phapper 9 | 10 | Supports Laravel 5.x - 8.x. 11 | 12 | Here are a few examples of the provided methods: 13 | ```php 14 | use RedditAPI; 15 | 16 | //fetch top Reddit posts 17 | RedditAPI::getTop(); 18 | 19 | //fetch top picture posts of Margot Robbie, limit to 100 20 | RedditAPI::search('Margot Robbie ', null, 'top', null, 'pics', 100); 21 | ``` 22 | 23 | ## Install 24 | 25 | This package can be installed through Composer. 26 | 27 | ``` bash 28 | composer require codewizz/laravel-reddit-api 29 | ``` 30 | 31 | If you are using Laravel 5.5+, the service provider and alias will be registered automatically. You can proceed to "[Publish config](#publish-config)". 32 | 33 | ### Manually register services 34 | 35 | You must install this service provider. 36 | 37 | ```php 38 | // config/app.php 39 | 'providers' => [ 40 | ... 41 | CodeWizz\RedditAPI\RedditAPIServiceProvider::class, 42 | ... 43 | ]; 44 | ``` 45 | 46 | This package also comes with a facade, which provides an easy way to call the the class. 47 | 48 | ```php 49 | // config/app.php 50 | 'aliases' => [ 51 | ... 52 | 'RedditAPI' => CodeWizz\RedditAPI\RedditAPIFacade::class, 53 | ... 54 | ]; 55 | ``` 56 | 57 | ### Publish config 58 | 59 | You should publish the config file of this package with this command: 60 | 61 | ``` bash 62 | php artisan vendor:publish --provider="CodeWizz\RedditAPI\RedditAPIServiceProvider" 63 | ``` 64 | 65 | The following config file will be published in `config/reddit-api.php` 66 | 67 | ```php 68 | return [ 69 | 'endpoint_standard' => 'https://www.reddit.com', 70 | 'endpoint_oauth' => 'https://oauth.reddit.com', 71 | 72 | 'username' => env('REDDIT_USERNAME', ''), 73 | 'password' => env('REDDIT_PASSWORD', ''), 74 | 'app_id' => env('REDDIT_APP_ID', ''), 75 | 'app_secret' => env('REDDIT_APP_SECRET', ''), 76 | 77 | 'response_format' => 'STD', // STD | ARRAY 78 | 79 | 'scopes' => 'save,modposts,identity,edit,flair,history,modconfig,modflair,modlog,modposts,modwiki,mysubreddits,privatemessages,read,report,submit,subscribe,vote,wikiedit,wikiread' 80 | ]; 81 | ``` 82 | 83 | 84 | ## About CodeWizz 85 | CodeWizz is a web development agency based in Lithuania. You'll find more information [on our website](https://codewizz.com). 86 | 87 | ## License 88 | 89 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 90 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codewizz/laravel-reddit-api", 3 | "description": "Laravel Reddit API wrapper", 4 | "keywords": [ 5 | "codewizz", 6 | "laravel-reddit-api", 7 | "laravel", 8 | "reddit", 9 | "api" 10 | ], 11 | "homepage": "https://github.com/codewizz/laravel-reddit-api", 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "Darius Šilkaitis", 16 | "email": "darius@codewizz.com", 17 | "homepage": "https://codewizz.com", 18 | "role": "Developer" 19 | } 20 | ], 21 | "require": { 22 | "php": "^7.2", 23 | "ext-curl": "*", 24 | "ext-json": "*", 25 | "illuminate/support": "^6.0|^7.0|^8.0|^9.0" 26 | }, 27 | "require-dev": { 28 | "friendsofphp/php-cs-fixer": "^2.16", 29 | "gtjamesa/php-standards": "^1.0", 30 | "orchestra/testbench": "^4.0|^5.0|^6.0", 31 | "phpunit/phpunit": "^8.4|^9.0" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "CodeWizz\\RedditAPI\\": "src" 36 | } 37 | }, 38 | "autoload-dev": { 39 | "psr-4": { 40 | "CodeWizz\\RedditAPI\\Test\\": "tests" 41 | } 42 | }, 43 | "extra": { 44 | "laravel": { 45 | "providers": [ 46 | "CodeWizz\\RedditAPI\\RedditAPIServiceProvider" 47 | ], 48 | "aliases": { 49 | "RedditAPI": "CodeWizz\\RedditAPI\\RedditAPIFacade" 50 | } 51 | } 52 | }, 53 | "config": { 54 | "sort-packages": true 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Exceptions/RedditAuthenticationException.php: -------------------------------------------------------------------------------- 1 | code}]: {$this->message}"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Interfaces/RedditApiInterface.php: -------------------------------------------------------------------------------- 1 | publishes([ 16 | __DIR__ . '/config/reddit-api.php' => config_path('reddit-api.php'), 17 | ], 'config'); 18 | } 19 | 20 | /** 21 | * Register the service provider. 22 | */ 23 | public function register() 24 | { 25 | $this->mergeConfigFrom(__DIR__ . '/config/reddit-api.php', 'reddit-api'); 26 | 27 | $redditAPIConfig = config('reddit-api'); 28 | 29 | $this->app->singleton('laravel-reddit-api', static function () use ($redditAPIConfig) { 30 | return new RedditAPI($redditAPIConfig['username'], $redditAPIConfig['password'], $redditAPIConfig['app_id'], $redditAPIConfig['app_secret'], $redditAPIConfig['endpoint_standard'], $redditAPIConfig['endpoint_oauth'], $redditAPIConfig['response_format']); 31 | }); 32 | 33 | $this->app->bind(RedditApiInterface::class, function () use ($redditAPIConfig) { 34 | return new RedditAPI( 35 | $redditAPIConfig['username'], 36 | $redditAPIConfig['password'], 37 | $redditAPIConfig['app_id'], 38 | $redditAPIConfig['app_secret'], 39 | $redditAPIConfig['endpoint_standard'], 40 | $redditAPIConfig['endpoint_oauth'], 41 | $redditAPIConfig['response_format'] 42 | ); 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/RedditLive.php: -------------------------------------------------------------------------------- 1 | redditAPI = $redditAPI; 14 | $this->thread_id = $thread_id; 15 | } 16 | 17 | /** 18 | * Returns the thread ID of the current thread. Useful for newly created threads. 19 | * 20 | * @return string Thread ID. 21 | */ 22 | public function getThreadId(): string 23 | { 24 | return $this->thread_id; 25 | } 26 | 27 | /** 28 | * Accepts a pending invitation to contribute to the live thread. 29 | * 30 | * @return object Response to API call. 31 | */ 32 | public function acceptContributorInvite() 33 | { 34 | $params = [ 35 | 'api_type' => 'json', 36 | ]; 37 | 38 | return $this->apiCall('/accept_contributor_invite', 'POST', $params); 39 | } 40 | 41 | /** 42 | * Permanently closes the live thread. 43 | * 44 | * @return object Response to API call. 45 | */ 46 | public function close() 47 | { 48 | $params = [ 49 | 'api_type' => 'json', 50 | ]; 51 | 52 | return $this->apiCall('/close_thread', 'POST', $params); 53 | } 54 | 55 | /** 56 | * Deletes the specified update. 57 | * 58 | * @param string $update_id ID (rather, name attribute) of update to delete. 59 | * 60 | * @return object Response to API call. 61 | */ 62 | public function deleteUpdate($update_id) 63 | { 64 | $params = [ 65 | 'api_type' => 'json', 66 | 'id' => $update_id, 67 | ]; 68 | 69 | return $this->apiCall('/delete_update', 'POST', $params); 70 | } 71 | 72 | /** 73 | * Edit the settings for the live thread. 74 | * 75 | * @param string|null $title The thread's title. 76 | * @param string|null $description The thread's description. 77 | * @param string|null $resources The thread's resources section in the sidebar. 78 | * @param bool|null $nsfw Whether or not the thread is NSFW. Prompts guests to continue when visiting. 79 | * 80 | * @return mixed|null Response to API call OR null if not all settings defined and getting current settings failed. 81 | */ 82 | public function editSettings($title = null, $description = null, $resources = null, $nsfw = null) 83 | { 84 | $current_settings = null; 85 | if ($title === null || $description === null || $resources === null || $nsfw === null) { 86 | $current_settings = $this->about(); 87 | if (!isset($current_settings->data)) { 88 | return null; 89 | } 90 | } 91 | $params = [ 92 | 'api_type' => 'json', 93 | 'description' => ($description === null) ? $current_settings->data->description : $description, 94 | 'nsfw' => ($nsfw === null) ? $current_settings->data->nsfw : (($nsfw) ? 'true' : 'false'), 95 | 'resources' => ($resources === null) ? $current_settings->data->resources : $resources, 96 | 'title' => ($title === null) ? $current_settings->data->title : $title, 97 | ]; 98 | 99 | return $this->apiCall('/edit', 'POST', $params); 100 | } 101 | 102 | /** 103 | * Invite a user as a contributor to the live thread. 104 | * 105 | * @param string $user Username of user to invite. 106 | * @param bool $perm_all If the user should have full permissions. 107 | * @param bool $perm_close If the user should have the 'close live thread' permission. User must have 'settings' too to close via the web UI. 108 | * @param bool $perm_edit If the user should have the 'edit' permission. 109 | * @param bool $perm_manage If the user should have the 'manage contributors' permission. 110 | * @param bool $perm_settings If the user should have the 'settings' permission. 111 | * @param bool $perm_update If the user should have the 'update' permission. 112 | * 113 | * @return object Response to API call. 114 | */ 115 | public function inviteContributor($user, $perm_all = true, $perm_close = false, $perm_edit = false, $perm_manage = false, $perm_settings = false, $perm_update = false) 116 | { 117 | $permissions = []; 118 | if ($perm_all) { 119 | $permissions[] = '+all'; 120 | } else { 121 | if ($perm_close) { 122 | $permissions[] = '+close'; 123 | } 124 | if ($perm_edit) { 125 | $permissions[] = '+edit'; 126 | } 127 | if ($perm_manage) { 128 | $permissions[] = '+manage'; 129 | } 130 | if ($perm_settings) { 131 | $permissions[] = '+settings'; 132 | } 133 | if ($perm_update) { 134 | $permissions[] = '+update'; 135 | } 136 | } 137 | if (count($permissions) == 0) { 138 | $permissions = ['-all', '-close', '-edit', '-manage', '-settings', '-update']; 139 | } 140 | $params = [ 141 | 'api_type' => 'json', 142 | 'name' => $user, 143 | 'permissions' => implode(',', $permissions), 144 | 'type' => 'liveupdate_contributor_invite', 145 | ]; 146 | 147 | return $this->apicall('/invite_contributor', 'POST', $params); 148 | } 149 | 150 | /** 151 | * Abdicate contributorship of the thread. 152 | * 153 | * @return object Response to API call. 154 | */ 155 | public function leaveContributor() 156 | { 157 | $params = [ 158 | 'api_type' => 'json', 159 | ]; 160 | 161 | return $this->apiCall('/leave_contributor', 'POST', $params); 162 | } 163 | 164 | /** 165 | * Report the thread to the reddit admins for breaking one of the site's rules. 166 | * 167 | * @param string $type One of 'spam', 'vote-manipulation', 'personal-information', 'sexualizing-minors', 'site-breaking'. 168 | * 169 | * @return object Response to API call. 170 | */ 171 | public function report($type) 172 | { 173 | $params = [ 174 | 'api_type' => 'json', 175 | 'type' => $type, 176 | ]; 177 | 178 | return $this->apiCall('/report', 'POST', $params); 179 | } 180 | 181 | /** 182 | * Remove a user as a contributor from the thread. To revoke a pending invitation, use uninviteContributor(). 183 | * 184 | * @param string $user Username of user to remove from thread's contributor list. 185 | * 186 | * @return object|null Response to API call OR null if the user does not exist. 187 | */ 188 | public function removeContributor($user) 189 | { 190 | $user_object = $this->redditAPI->getUser($user); 191 | if (!isset($user_object->data)) { 192 | return null; 193 | } 194 | $user_id = $user_object->kind . '_' . $user_object->data->id; 195 | $params = [ 196 | 'api_type' => 'json', 197 | 'id' => $user_id, 198 | ]; 199 | 200 | return $this->apiCall('/rm_contributor', 'POST', $params); 201 | } 202 | 203 | /** 204 | * Revoke a pending contributor invitation. To remove a current contributor, use removeContributor(). 205 | * 206 | * @param string $user Username of user to uninvite. 207 | * 208 | * @return object|null Response to API call OR null if the user does not exit. 209 | */ 210 | public function uninviteContributor($user) 211 | { 212 | $user_object = $this->redditAPI->getUser($user); 213 | if (!isset($user_object->data)) { 214 | return null; 215 | } 216 | $user_id = $user_object->kind . '_' . $user_object->data->id; 217 | $params = [ 218 | 'api_type' => 'json', 219 | 'id' => $user_id, 220 | ]; 221 | 222 | return $this->apiCall('/rm_contributor_invite', 'POST', $params); 223 | } 224 | 225 | /** 226 | * Modify a current contributor's permission set. To modify an invited contributor's permissions, use setInvitationPermissions(). 227 | * 228 | * @param string $user Username of user for which to edit permissions. 229 | * @param bool $perm_all If the user should have full permissions. 230 | * @param bool $perm_close If the user should have the 'close live thread' permission. User must have 'settings' too to close via the web UI. 231 | * @param bool $perm_edit If the user should have the 'edit' permission. 232 | * @param bool $perm_manage If the user should have the 'manage contributors' permission. 233 | * @param bool $perm_settings If the user should have the 'settings' permission. 234 | * @param bool $perm_update If the user should have the 'update' permission. 235 | * 236 | * @return object Response to API call. 237 | */ 238 | public function setContributorPermissions($user, $perm_all = true, $perm_close = false, $perm_edit = false, $perm_manage = false, $perm_settings = false, $perm_update = false) 239 | { 240 | $permissions = []; 241 | if ($perm_all) { 242 | $permissions[] = '+all'; 243 | } else { 244 | if ($perm_close) { 245 | $permissions[] = '+close'; 246 | } 247 | if ($perm_edit) { 248 | $permissions[] = '+edit'; 249 | } 250 | if ($perm_manage) { 251 | $permissions[] = '+manage'; 252 | } 253 | if ($perm_settings) { 254 | $permissions[] = '+settings'; 255 | } 256 | if ($perm_update) { 257 | $permissions[] = '+update'; 258 | } 259 | } 260 | if (count($permissions) == 0) { 261 | $permissions = ['-all', '-close', '-edit', '-manage', '-settings', '-update']; 262 | } 263 | $params = [ 264 | 'api_type' => 'json', 265 | 'name' => $user, 266 | 'permissions' => implode(',', $permissions), 267 | 'type' => 'liveupdate_contributor', 268 | ]; 269 | 270 | return $this->apicall('/set_contributor_permissions', 'POST', $params); 271 | } 272 | 273 | /** 274 | * Modify an invited contributor's permission set. To modify a current contributor's permissions, use setContributorPermissions(). 275 | * 276 | * @param string $user Username of user for which to edit permissions. 277 | * @param bool $perm_all If the user should have full permissions. 278 | * @param bool $perm_close If the user should have the 'close live thread' permission. User must have 'settings' too to close via the web UI. 279 | * @param bool $perm_edit If the user should have the 'edit' permission. 280 | * @param bool $perm_manage If the user should have the 'manage contributors' permission. 281 | * @param bool $perm_settings If the user should have the 'settings' permission. 282 | * @param bool $perm_update If the user should have the 'update' permission. 283 | * 284 | * @return object Response to API call. 285 | */ 286 | public function setInvitationPermissions($user, $perm_all = true, $perm_close = false, $perm_edit = false, $perm_manage = false, $perm_settings = false, $perm_update = false) 287 | { 288 | $permissions = []; 289 | if ($perm_all) { 290 | $permissions[] = '+all'; 291 | } else { 292 | if ($perm_close) { 293 | $permissions[] = '+close'; 294 | } 295 | if ($perm_edit) { 296 | $permissions[] = '+edit'; 297 | } 298 | if ($perm_manage) { 299 | $permissions[] = '+manage'; 300 | } 301 | if ($perm_settings) { 302 | $permissions[] = '+settings'; 303 | } 304 | if ($perm_update) { 305 | $permissions[] = '+update'; 306 | } 307 | } 308 | if (count($permissions) == 0) { 309 | $permissions = ['-all', '-close', '-edit', '-manage', '-settings', '-update']; 310 | } 311 | $params = [ 312 | 'api_type' => 'json', 313 | 'name' => $user, 314 | 'permissions' => implode(',', $permissions), 315 | 'type' => 'liveupdate_contributor_invite', 316 | ]; 317 | 318 | return $this->apiCall('/set_contributor_permissions', 'POST', $params); 319 | } 320 | 321 | /** 322 | * Strikes the specified update, which will show up as crossed out in the live thread. 323 | * 324 | * @param string $update_id ID (rather, name attribute) of update to strike. 325 | * 326 | * @return object Response to API call. 327 | */ 328 | public function strikeUpdate($update_id) 329 | { 330 | $params = [ 331 | 'api_type' => 'json', 332 | 'id' => $update_id, 333 | ]; 334 | 335 | return $this->apiCall('/strike_update', 'POST', $params); 336 | } 337 | 338 | /** 339 | * Makes an update on the live thread. 340 | * 341 | * @param string $body Body of update to post. 342 | * 343 | * @return object Response to API call. Unfortunately, no update ID is returned yet. You need to run getUpdates() to find this. 344 | */ 345 | public function update($body) 346 | { 347 | $params = [ 348 | 'api_type' => 'json', 349 | 'body' => $body, 350 | ]; 351 | 352 | return $this->apiCall('/update', 'POST', $params); 353 | } 354 | 355 | /** 356 | * Retrieves updates on a thread. 357 | * 358 | * @param int $limit Upper limit of the number of links to retrieve. Maximum is 100. 359 | * @param string|null $after Get items lower on list than this entry. Does not mean chronologically. Should be the *name* of an update: "LiveUpdate_..." 360 | * @param string|null $before Get items higher on list than this entry. Does not mean chronologically. Should be the *name* of an update: "LiveUpdate_..." 361 | * 362 | * @return object Listing of LiveUpdate objects. 363 | */ 364 | public function getUpdates($limit = 25, $after = null, $before = null) 365 | { 366 | $params = [ 367 | 'after' => $after, 368 | 'before' => $before, 369 | 'limit' => $limit, 370 | ]; 371 | 372 | return $this->liveCall('.json', 'GET', $params); 373 | } 374 | 375 | /** 376 | * Retrieves information about the live thread. 377 | * 378 | * @return object LiveUpdateEvent object. 379 | */ 380 | public function about() 381 | { 382 | return $this->liveCall('/about.json'); 383 | } 384 | 385 | /** 386 | * Retrieves a list of contributors for the thread. To see invitations, the current user must have the 'manage' permission. 387 | * 388 | * @return object|array UserList object OR array of two UserList objects if there are visible pending invitations. 389 | */ 390 | public function getContributors() 391 | { 392 | return $this->liveCall('/contributors.json'); 393 | } 394 | 395 | /** 396 | * Retrieves a list of discussions about the current thread. 397 | * 398 | * @param int $limit Upper limit of the number of links to retrieve. Maximum is 100. 399 | * @param string|null $after Get items lower on list than this entry. Does not mean chronologically. 400 | * @param string|null $before Get items higher on list than this entry. Does not mean chronologically. 401 | * 402 | * @return object Listing of posts. 403 | */ 404 | public function getDiscussions($limit = 25, $after = null, $before = null) 405 | { 406 | $params = [ 407 | 'after' => $after, 408 | 'before' => $before, 409 | 'limit' => $limit, 410 | 'show' => 'all', 411 | ]; 412 | 413 | return $this->liveCall('/discussions', 'GET', $params); 414 | } 415 | 416 | public function apiCall($path, $method = 'GET', $params = null) 417 | { 418 | return $this->redditAPI->apiCall("/api/live/{$this->thread_id}$path", $method, $params); 419 | } 420 | 421 | public function liveCall($path, $method = 'GET', $params = null) 422 | { 423 | return $this->redditAPI->apiCall("/live/{$this->thread_id}$path", $method, $params); 424 | } 425 | } 426 | -------------------------------------------------------------------------------- /src/RedditOAuth2.php: -------------------------------------------------------------------------------- 1 | username = $username; 23 | $this->password = $password; 24 | $this->app_id = $app_id; 25 | $this->app_secret = $app_secret; 26 | $this->user_agent = $user_agent; 27 | $this->endpoint = $endpoint; 28 | $this->requestAccessToken(); 29 | } 30 | 31 | public function getAccessToken(): array 32 | { 33 | if (!(isset($this->access_token, $this->token_type) && time() < $this->expiration)) { 34 | $this->requestAccessToken(); 35 | } 36 | 37 | return [ 38 | 'access_token' => $this->access_token, 39 | 'token_type' => $this->token_type, 40 | ]; 41 | } 42 | 43 | private function requestAccessToken(): void 44 | { 45 | $url = "{$this->endpoint}/api/v1/access_token"; 46 | $params = [ 47 | 'grant_type' => 'password', 48 | 'username' => $this->username, 49 | 'password' => $this->password, 50 | ]; 51 | $options[CURLOPT_USERAGENT] = $this->user_agent; 52 | $options[CURLOPT_USERPWD] = $this->app_id . ':' . $this->app_secret; 53 | $options[CURLOPT_RETURNTRANSFER] = true; 54 | $options[CURLOPT_CONNECTTIMEOUT] = 5; 55 | $options[CURLOPT_TIMEOUT] = 10; 56 | $options[CURLOPT_CUSTOMREQUEST] = 'POST'; 57 | $options[CURLOPT_POSTFIELDS] = $params; 58 | 59 | $response = null; 60 | $got_token = false; 61 | while (!$got_token) { 62 | $ch = curl_init($url); 63 | curl_setopt_array($ch, $options); 64 | $response_raw = curl_exec($ch); 65 | $response = json_decode($response_raw); 66 | curl_close($ch); 67 | if (isset($response->access_token)) { 68 | $got_token = true; 69 | } else { 70 | if (isset($response->error)) { 71 | if ($response->error === 'invalid_grant') { 72 | throw new RedditAuthenticationException('Supplied reddit username/password are invalid or the threshold for invalid logins has been exceeded.', 1); 73 | } 74 | 75 | if ($response->error === 401) { 76 | throw new RedditAuthenticationException('Supplied reddit app ID/secret are invalid.', 2); 77 | } 78 | } else { 79 | fwrite(STDERR, "WARNING: Request for reddit access token has failed. Check your connection.\n"); 80 | sleep(5); 81 | } 82 | } 83 | } 84 | 85 | $this->access_token = $response->access_token; 86 | $this->token_type = $response->token_type; 87 | $this->expiration = time() + $response->expires_in; 88 | $this->scope = $response->scope; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/RedditRateLimiter.php: -------------------------------------------------------------------------------- 1 | enabled = $enabled; 14 | $this->interval = $interval; 15 | $this->last_request = microtime(true) * 10000; 16 | } 17 | 18 | /** 19 | * Enable the rate limiter, on by default. 20 | */ 21 | public function enable(): void 22 | { 23 | $this->enabled = true; 24 | } 25 | 26 | /** 27 | * Disable the rate limiter. 28 | * This is meant to allow you to perform requests in bursts, but me mindful of reddit's rate limits and your program's structure. 29 | * https://github.com/reddit/reddit/wiki/API. 30 | */ 31 | public function disable(): void 32 | { 33 | $this->enabled = false; 34 | } 35 | 36 | /** 37 | * Set the rate limiter to wait the specified number of seconds past the previous API call to make the next one. 38 | * If this time has already elapsed during execution of other parts of the program, no wait is needed. 39 | * 40 | * @param int|float $interval Number of seconds that must elapse between each API call. 41 | */ 42 | public function setInterval($interval): void 43 | { 44 | $this->interval = $interval; 45 | } 46 | 47 | /** 48 | * Used by Phapper object to wait until another API call can be made. 49 | */ 50 | public function wait(): void 51 | { 52 | $now = microtime(true) * 10000; 53 | $wait_until = $this->last_request + ($this->interval * 10000); 54 | if ($this->enabled && $now < $wait_until) { 55 | usleep(($wait_until - $now) * 100); 56 | } 57 | $this->last_request = microtime(true) * 10000; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/config/reddit-api.php: -------------------------------------------------------------------------------- 1 | 'https://www.reddit.com', 5 | 'endpoint_oauth' => 'https://oauth.reddit.com', 6 | 7 | 'username' => env('REDDIT_USERNAME', ''), 8 | 'password' => env('REDDIT_PASSWORD', ''), 9 | 'app_id' => env('REDDIT_APP_ID', ''), 10 | 'app_secret' => env('REDDIT_APP_SECRET', ''), 11 | 12 | 'response_format' => 'STD', // STD | ARRAY 13 | 14 | 'scopes' => 'save,modposts,identity,edit,flair,history,modconfig,modflair,modlog,modposts,modwiki,mysubreddits,privatemessages,read,report,submit,subscribe,vote,wikiedit,wikiread', 15 | ]; 16 | --------------------------------------------------------------------------------