Google Cloud Functionでツイートを取得して画像をGoogleフォトに自動保存するようにする

# はじめに
仕事しながらTwitterの画像を一々ダウンロードしてGoogleフォトに持ってくの面倒だなぁという最近。
エンジニアなら自動化すればええやんということでした記録です。

なんでTwitter画像ダウンロードしたいかというと推しのアイドルの画像が欲しいだけです。
他に目的はありません。

# 最終的なゴール
定期的にtweetを取得→画像があったらダウンロード→ダウンロードした画像をGoogleフォトへアップロードです。

# 環境

## Google Cloud Functions
https://cloud.google.com/functions/?hl=ja

特定の関数を実行してくれるサービスです。
大きなプログラムを実行するというより、小さなプログラム用といった感じのようです。
今回は１２０行程度のプログラムなので、こちらを採用しました。

料金的はよほどじゃないと永久無料枠を超えません（いっぱい関数作ってても中々超えないと思います）。
今回のプログラムはGoogle API外の通信が発生するため(Tweetの取得と画像ダウンロード)、実行頻度を少なめにするという対策を念の為取っています。
ただ、ネットワーク使用量を見る場所が分かっていないため、実際どの程度の通信が発生しているかは運用始めた現在でも分からないのが辛いです。

今回関数に設定する言語はpythonにしています。
最近業務で使っているというのが理由です。
他にも使える言語があるので、確認の上開発をスタートしましょう（変わる可能性があるのでここには書きません）

料金表は[こちら](https://cloud.google.com/functions/pricing?hl=ja)です。

## Google Cloud Scheduler
https://cloud.google.com/scheduler/?hl=ja

Google Cloud Functionsとの繋ぎこみが楽なので、定期実行はこいつに任せてます。
ジョブの数で料金が決まりますが、無料枠を超えても安く利用出来て使いやすいサービスになっています。

料金表は[こちら](https://cloud.google.com/scheduler/pricing?hl=ja)です。

# 開発する前に
今回はGCPとGoogle API(Photos)とTwitter APIと色々APIを使うことになるので、各種設定を行なっていきます。

## GCP関連
https://cloud.google.com/?hl=ja

こちらは特に設定が面倒な訳ではないため、サクッと終わります。

## Google Photos API
https://developers.google.com/photos

上のURLあたりからAPIの有効化を行う必要があります。
有効化自体は簡単に出来ますが、APIをコールするためにはOAuthを使って認可をとる必要があります。
[こちら](https://qiita.com/shin1ogawa/items/49a076f62e5f17f18fe5
)をRefresh Tokenの取得までしておく必要がありますので、事前にやっておきましょう。

# Twitter API
https://developer.twitter.com/content/developer-twitter/ja.html

これが一番厄介でした。
操作は簡単なのですが、開発者登録時に英語でのアプリ説明が必要となるため、英語で説明文をある程度の長さ書かなければいけません。
アプリ作成時のポリシーは結構変わる印象があるので、作業する際はお気をつけください。

日本語でもいいよみたいな情報もありましたが、２０２０年1月現在は英語必須でした。

英語あんまりという人も簡単な日本語をGoogle翻訳かけながらやれば突破は可能なので、難しく考えすぎなくても大丈夫です。日本語で書くより面倒なことは確実ですが。

審査自体は一瞬で終わりました。多分、選択肢の選び方によって違うと思うので、人によっては違うかもしれません。

# 実装
プログラム自体は[github](https://github.com/tasogarei/twitter_image_uploader)にあげていますので完成形はそちらをご覧ください。

処理的には`python-twitter`使ってFirebaseで定義したユーザーのツイート取得して画像があったらダウンロードからのアップロードをやっているだけです。

Google Photo APIの仕様上、同じ画像をあげても問題ないため(多分ファイル名で判断してます)、取れたツイートに画像あったら同じ手順でアップロードしてしまっています。
無駄な通信ですが、とりあえず困らないという理由でそうしています。本当ならどこかにidを保存してそこから取得するというプログラムにした方が良いのですが、ちょっと時間がかかりそうという理由でリリース優先しました。

Firebaseに保存するのがGCP系使ってて無料でやるにはよくローカルからでも実行できるのでおすすめです。

Firebaseをpythonコードから使うやり方は[公式](https://firebase.google.com/docs/firestore/quickstart?hl=ja)がものすごく分かりやすいので、こちらを参考にしてください。
ローカルからテストする方法については別で[記事](https://qiita.com/tasogarei/items/87e3bc482bff539921ce)をあげていますので、そちらを参照ください。

Cloud SQLはAlways Freeがないので今回は諦めました。

2020/02/01 追記
Twitterの仕様上、tweet_mode="extended"としなければちょん切れたテキストが取得されます。
そうなったTweetはmediaが上手く設定されてくれないので、tweet_mode="extended"としなければいけないことにご注意ください。

Githubのソースはアップし直しました。

# 実装の注意点
イベントトリガーによって、開始の関数に引数が必要となる点は注意が必要です。

pub/sub => event, context
http => request, response

です。忘れて少しハマりました。
あと、MAIN.PYとREQUIREMENTS.TXTの２ファイルしか設定できないので、その範囲で実装しなければいけません。
複雑なプログラム書くと辛いです。

ログが必要であればprintで問題ないので、仕込んでおきましょう。

# テスト
全てローカル上で出来るようであればローカルで動くことを確認した方が楽だと思います。
Cloud Functions上でも出来るようですが、前述した通り謎のエラーに遭遇してできない状態です。

2020/04/17追記
Cloud Functions上でのテストはタイムアウト設定がデフォルトで行われてしまうためでした。
実行時間が長いプログラムはここでのテストはエラーになってしまいます。
しかし、実行自体は行われてるようなので、走りきるかはログで確認できます。

# Google Cloud Functionsの設定
メモリの設定とかはプログラムで変わりますが、このプログラムだと２５６MBで余裕でした。
トリガーはCloud Pub/Subで作成します。
タイムアウト設定は最大518秒が設定できるので、とりあえず最大を設定しておくと安心できます。
今回プログラムは２００秒くらいかかるので、適当に６０秒でやってタイムアウトくらったりしました。

# Google Cloud Schedulerの設定
トリガーをpub/sub、トピックをGoogle Cloud Functionsで設定した値にするだけです。
実行する時間はcron方式で書きます。たまーにしか設定しないので、書き方をしっかり覚える気はないのですが、[こういうサイト](http://cronbuilder.otchy.net/)があるので活用しましょう。

すぐ実行したい場合はボタンを押せば実行してくれるので、それで成功するのを確認しましょう。

# 最後に
小さいプログラムをサーバー上で定期的に動かすというのが簡単にできる構成なので、ちょっとしたことを自動化したい時にはすごく向いてますね。
今回は実装３時間、設定１時間くらいで終わったので、趣味と実益と勉強を短時間でできて凄く有意義でした。

Firebase周りとCloud Functionsのテスト周りに課題が残っていますが、そこは分かり次第別途作業していきます。終わったら記事化すると思います。