Laravel Command line post article to Google Blogger

純屬娛樂,寫一支 Laravel Console Command,可以呼叫 Browser 開一個連結向 Google 驗證後,自動發文到你所擁有的 Blogger。
Environment:
1. PHP >= 7.2
2. Laravel Framework
Composer Package install:
composer require pulkitjalan/google-apiclient詳細參考:https://github.com/pulkitjalan/google-apiclient
Google Cloud Console:
1. 去 Google Console 申請 Api Key, OAuth2.0 Access
2. OAuth2.0 設定的 Redirect Url 填:http://127.0.0.1:8000/authorization-code/callback
執行:
php artisan oauth:blogger
以下是範例程式:
php artisan oauth:blogger
以下是範例程式:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Console\Commands; | |
use Illuminate\Console\Command; | |
use PulkitJalan\Google\Facades\Google; | |
use Illuminate\Support\Facades\Cache; | |
class OAuthLogin extends Command | |
{ | |
/** | |
* The name and signature of the console command. | |
* | |
* @var string | |
*/ | |
protected $signature = 'oauth:blogger'; | |
/** | |
* The console command description. | |
* | |
* @var string | |
*/ | |
protected $description = 'oauth login blogger'; | |
/** | |
* Create a new command instance. | |
* | |
* @return void | |
*/ | |
public function __construct() | |
{ | |
parent::__construct(); | |
} | |
/** | |
* Execute the console command. | |
* | |
* @return mixed | |
*/ | |
public function handle() | |
{ | |
// | |
if ( | |
!Cache::get('expiredAt') or | |
!Cache::get('refreshToken') or | |
!Cache::get('accessToken') | |
) { | |
$ip = '127.0.0.1'; | |
$port = '8000'; | |
$socket_str = 'tcp://' . $ip . ':' . $port; | |
$client = Google::getClient(); | |
$authorize_url = $client->createAuthUrl(); | |
$this->info('Auto Open Browser connect to: ' . $authorize_url . PHP_EOL); | |
shell_exec("open '" . $authorize_url . "'"); | |
$response = $this->startHttpServer($socket_str); | |
$auth = $client->authenticate($response['code']); | |
$accessToken = $client->getAccessToken(); | |
$refreshToken = $client->getRefreshToken(); | |
$expiredAt = $auth['created'] + $auth['expires_in']; | |
Cache::store('file')->put('refreshToken', $refreshToken); | |
Cache::store('file')->put('accessToken', $accessToken); | |
Cache::store('file')->put('expiredAt', $expiredAt); | |
} | |
if (time() > Cache::get('expiredAt')) { | |
$client = Google::getClient(); | |
$auth = $client->fetchAccessTokenWithRefreshToken(Cache::get('refreshToken')); | |
$client->setAccessToken($auth['access_token']); | |
Cache::forget('accessToken'); | |
Cache::forget('refreshToken'); | |
Cache::forget('expiredAt'); | |
$refreshToken = $client->getRefreshToken(); | |
$expiredAt = $auth['created'] + $auth['expires_in']; | |
Cache::store('file')->put('refreshToken', $refreshToken); | |
Cache::store('file')->put('accessToken', $auth['access_token']); | |
Cache::store('file')->put('expiredAt', $expiredAt); | |
} | |
$client = Google::getClient(); | |
$client->setAccessToken(Cache::get('accessToken')); | |
$googlService = Google::make('blogger', $client); | |
$ownblogs = $googlService->blogs->listByUser('self'); | |
$bloglist = []; | |
foreach ($ownblogs as $key => $own) { | |
$bloglist[] = [ | |
'choice' => $key + 1, | |
'id' => $own->id, | |
'name' => $own->name, | |
]; | |
} | |
$this->table(['select', 'blogId', 'blogName'], $bloglist); | |
$choice = $this->ask('Select one to Post'); | |
$blogId = (isset($bloglist[$choice - 1])) ? $bloglist[$choice - 1]['id'] : $this->ask('Select one to Post'); | |
// Write a post | |
$post = Google::make('blogger_post'); | |
$post->setTitle('test'); | |
$post->setContent('<h1>yes</h1>'); | |
$post->setLabels(['news', 'k12']); | |
$options = [ | |
// 'isDraft' => true, // if want direct publish, comment this line. | |
]; | |
$result = $googlService->posts->insert( | |
$blogId, | |
$post, | |
$options | |
); | |
$this->info( | |
sprintf( | |
'Uploaded: { title: %s, status: %s, url: %s, timestamp: %s }%s', | |
$result->title, | |
$result->status, | |
$result->url, | |
$result->updated, | |
PHP_EOL | |
) | |
); | |
} | |
// From: https://developer.okta.com/blog/2018/07/16/oauth-2-command-line | |
private function startHttpServer($socketStr) | |
{ | |
// Adapted from http://cweiske.de/shpub.htm | |
$responseOk = "HTTP/1.0 200 OK\r\n" | |
. "Content-Type: text/plain\r\n" | |
. "\r\n" | |
. "Ok. You may close this tab and return to the shell.\r\n"; | |
$responseErr = "HTTP/1.0 400 Bad Request\r\n" | |
. "Content-Type: text/plain\r\n" | |
. "\r\n" | |
. "Bad Request\r\n"; | |
ini_set('default_socket_timeout', 60 * 5); | |
$server = stream_socket_server($socketStr, $errno, $errstr); | |
if (!$server) { | |
Log::err('Error starting HTTP server'); | |
return false; | |
} | |
do { | |
$sock = stream_socket_accept($server); | |
if (!$sock) { | |
Log::err('Error accepting socket connection'); | |
exit(1); | |
} | |
$headers = []; | |
$body = null; | |
$content_length = 0; | |
//read request headers | |
while (false !== ($line = trim(fgets($sock)))) { | |
if ('' === $line) { | |
break; | |
} | |
$regex = '#^Content-Length:\s*([[:digit:]]+)\s*$#i'; | |
if (preg_match($regex, $line, $matches)) { | |
$content_length = (int) $matches[1]; | |
} | |
$headers[] = $line; | |
} | |
// read content/body | |
if ($content_length > 0) { | |
$body = fread($sock, $content_length); | |
} | |
// send response | |
list($method, $url, $httpver) = explode(' ', $headers[0]); | |
if ($method == 'GET') { | |
#echo "Redirected to $url\n"; | |
$parts = parse_url($url); | |
#print_r($parts); | |
if ( | |
isset($parts['path']) && $parts['path'] == '/authorization-code/callback' | |
&& isset($parts['query']) | |
) { | |
parse_str($parts['query'], $query); | |
if (isset($query['code']) && isset($query['scope'])) { | |
fwrite($sock, $responseOk); | |
fclose($sock); | |
return $query; | |
} | |
} | |
} | |
fwrite($sock, $responseErr); | |
fclose($sock); | |
} while (true); | |
} | |
} |
留言
張貼留言
回應不用錢,請多多益善!懶得寫字按個讚也是相當感謝!