目次
背景
従来の方法
・公開されているGoogle Calendarでイベントを作成する→IFTTTでトリガー→IFTTTでTwitter(現在のX)に自動投稿する
→その時に方法をまとめた記事:https://www.nakamurayuji.com/archives/126
今回実装するに至った経緯
・TwitterのAPI有料化に伴い、IFTTTのTwitter自動投稿が有料プランに移行してしまった→ https://ifttt.com/explore/updates-to-twitter-2023
・そこで、IFTTTをあきらめ、自力でプログラム作成を行うものとした
今回の実現方法
概要
・Google Calendar APIで公開カレンダーイベントを取得し、Twitter APIによってTwitter(X)に投稿する。そのためのプログラム作成を行いました。
実装概要
・Mac Book Air M1,2020(macOS Ventura 13.4.1)において、Docker + Laravel Sail環境を構築し、バッチプログラムを手動実行する。
※Laravel Sail使わなくても単純にPHPが動作するDockerコンテナだけでも問題ないと思いますが、Sailの紹介も兼ねて。
開発
API事前準備
-
Google Calendar APIの準備を行います。
Google Cloud Platformにアクセスし、Google Calendar APIの準備を行います
・プロジェクト作成
・Google Calendar API有効化
・認証情報→APIキー作成
・サービスアカウント作成
・鍵を取得、JSONにてダウンロードしておく。
・サービスアカウントを該当カレンダーに追加し権限は、「予定の変更」にする
・また該当カレンダーのカレンダーIDも取得しておきます。 -
Twitter API v2の準備を行います。
開発者ポータルにアクセスし、Twitterアプリの登録を行い下記を取得しておきます。
・API key
・API Key Secret
・AccessToken
・AccessTokenSecret - Mac に Docker Desktop(4.20.1) をインストールします(Docker version 24.0.2)
- Laravel Framework 10.17.1 / Sail 8.2(PHP 8.2.8)をインストールします
公式ドキュメント:https://readouble.com/laravel/10.x/ja/installation.html#laravel-and-docker% cd {sailインストールフォルダ} % curl -s "https://laravel.build/calendar_twitter" | bash Thank you! We hope you build something incredible. Dive in with: % vi ~/.zshrc # .zshrcの中に下記を記述 alias sail='[ -f sail ] && bash sail || bash ./vendor/bin/sail' % sail up -d
-
http://localhost/
にてアクセスして下記のようなLaravel画面が表示されたらここまでOK
-
Laravelの.envファイルに、Google API、Twitter APIの情報などを定義しておきます。
TWITTER_CONSUMER_KEY=*********** TWITTER_CUSTOMER_SECRET=*********** TWITTER_ACCESS_TOKEN=*********** TWITTER_ACCESS_TOKEN_SECRET=*********** GOOGLE_CALENDAR_ID=***********
-
Google APIの準備で作成しておいた鍵ファイル(JSON)を下記に配置します。
storage/app/api-key/*********.json - Google Calendar APIの PHPライブラリ→googleapis/google-api-php-clientを導入します。
% sail composer require google/apiclient:^2.12.1
- Twitter APIの PHPライブラリ→TwitterOAuthを導入します。
% sail composer require abraham/twitteroauth
- プログラミングを行なっていきます。
-
(1)バッチプログラム作成
sail artisan make:command GoogleToTwitter
▼App\Console\Commands\GoogleToTwitter
<?php namespace App\Console\Commands; use Illuminate\Console\Command; use App\Services\TwitterService; use App\Services\GoogleCalendarService; class GoogleToTwitter extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'batch:google-to-twitter'; /** * The console command description. * * @var string */ protected $description = 'Google Calendar publish to Twitter'; /** * Execute the console command. */ public function handle() { $events = GoogleCalendarService::get(); foreach ($events as $event) { list($message, $summary) = TwitterService::makeTweets($event); //Twitterに投稿 $ret = TwitterService::tweet($message); //$ret正常だったらGoogleCalendarのタイトルから識別子削除 if ($ret) { echo 'Tweetしました:' . $message . "\n"; GoogleCalendarService::delete_hashtag($summary); } } } }
-
(2)Google API使っての取得、更新の実装
▼App\Services\GoogleCalendarService.php
<?php namespace App\Services; use Google_Client; use Google_Service_Calendar; use Google_Service_Calendar_Event; class GoogleCalendarService { /** * get from Google Calendar for tweets */ public static function get() { date_default_timezone_set('Asia/Tokyo'); $calendarId = env('GOOGLE_CALENDAR_ID'); //予定取得 $client = self::getClient(Google_Service_Calendar::CALENDAR_READONLY); $service = new Google_Service_Calendar($client); $optParams = array( 'orderBy' => 'updated', //最新更新日時昇順 'singleEvents' => true, //ひとつのインスタンス単位で取得する(繰り返しイベントは複数のインスタンスが取得される) 'q' => "#public_capture", //この文字列がある場合のみ投稿対象とする 'timeMin' => date('c',strtotime("now")),//現在以降の予定を取得対象 'timeMax' => date('c',strtotime("+6 month")),//半年後までの予定を取得対象 ); $results = $service->events->listEvents($calendarId, $optParams); $events = $results->getItems(); //取得したカレンダー情報から必要な情報を配列にしていく $event_lists = []; foreach ($events as $event) { $event_lists[] = [ 'id' => $event['id'], 'summary' => $event['summary'], 'link' => $event['htmlLink'], 'location' => $event['location'] ? $event['location'] : '', ]; } return $event_lists; } /** * update summary of Google Calendar event */ public static function delete_hashtag($summary) { date_default_timezone_set('Asia/Tokyo'); $calendarId = env('GOOGLE_CALENDAR_ID'); //予定取得 $client = self::getClient(Google_Service_Calendar::CALENDAR_EVENTS); $service = new Google_Service_Calendar($client); $event = $service->events->get($calendarId, $summary['event_id']); $event->setSummary($summary['summary']); //サマリ置き換え(#ハッシュタグ部分削除して文字列に置換) $updatedEvent = $service->events->update($calendarId, $event->getId(), $event); } /** * Get Google client * param $mode */ public static function getClient($mode = Google_Service_Calendar::CALENDAR_READONLY) { $client = new Google_Client(); //アプリケーション名 $client->setApplicationName('GoogleCalendarAPI get and update'); //権限の指定 $client->setScopes($mode); //JSONファイルの指定 $client->setAuthConfig(storage_path('app/api-key/calendarinfoug-8e5adc4b45a9.json')); return $client; } }
-
(3)Twitter API使っての取得、更新の実装
▼App\Services\TwitterService.php
<?php namespace App\Services; use Abraham\TwitterOAuth\TwitterOAuth; use Illuminate\Support\Facades\Log; class TwitterService { CONST MAX_STRING_NUMBERS = 140; CONST URL_STRING = 23; //22=URL文字数,改行コードが1文字 /** * Twitter投稿文字列生成. */ public static function makeTweets($event) { //URL以外の文字数 $rest_number = self::MAX_STRING_NUMBERS - self::URL_STRING; //投稿テキスト生成 $summary = trim(str_replace('#public_capture' , '' ,$event['summary'])); $message = $summary . "\n" . $event['location']; //規定文字数を超えた場合は後半を削除 if (($rest_number - strlen($message)) < 0) { $message = mb_substr($message, 0, $rest_number); } $message = $message . "\n" .$event['link']; $change_summary = [ 'event_id' => $event['id'], 'summary' => $summary, ]; return [$message, $change_summary]; } /** * Twitter投稿. */ public static function tweet($message) { $status = false; try { $twitter = new TwitterOAuth( config('twitter.key'), config('twitter.secret'), config('twitter.token'), config('twitter.token_secret'), ); $twitter->setApiVersion('2'); $ret = $twitter->post( 'tweets' , ['text' => $message] , true); $res = get_object_vars($ret); if (isset($res['data'])) {$status = true;} } catch (Exception $e) { Log::debug('例外エラー発生:' . print_r($e->getMessage(),true)); } return $status; } }
-
アプリケーション開発
実装後の運用の流れ
・公開のGoogle Calendarのタイトルに「#public_capture」という文字列を含めイベントを作成する
例:
・作成しておいたバッチプログラムを手動実行させる。すると下記処理の流れでイベントが自動投稿される
sail artisan batch:google-to-twitter
(1)Google Calendar APIにてタイトルに「#public_capture」を含むイベントを全件取得する
(2)取得したイベントをTwitter投稿文字数以内に整形、その際に「#public_capture」という文字列は削除する
(3)Twitter APIを用いてTwitter(X)に投稿
例:https://twitter.com/info_by_ug/status/1691064661771481088
(4)投稿成功したら、取得したGoogle Calendarイベントのタイトルから「#public_capture」を削除して、更新する(と重複投稿されない)
例: