Twitterのプロフィール画像を自動で更新するツールをGitHub Actionsで作った話

# はじめに
こちらの記事は[GitHub Actions Advent Calendar 2022](https://qiita.com/advent-calendar/2022/github-actions) 25日目の記事です。
TwitterのAPIの審査が通ったので何か作ろうと思い、Twitterのプロフィール画像を時間帯に合わせて更新するツールをGitHub Actionsで作成しました。

# 作ったツール
以下のように時間帯に合わせて自動でプロフィール画像を更新してくれます。
（微妙にダサいですが目をつぶってください）

| 7:30~9:30 | 9:30~17:30 | 17:30~23:30 | 23:30~翌日7:30 | 
| --------- | ---------- | ----------- | -------------- | 
| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608343/e9d841e6-298d-437d-91a2-7788ec5efdee.png" width="150"> | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608343/7d9030a6-11e6-bed4-87aa-a8a9e2719507.png" width="150"> | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608343/a3e9f7a3-4784-9da3-b62d-e4e08e7bc4a8.png" width="150"> | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608343/80e2665c-bb16-1531-6893-1ef3aa3bbfbe.png" width="150"> | 

GitHub Actionsで以下の処理を行うスクリプトを1時間ごとに実行することで実現しています。
1. 日本の現在時刻を取得
1. 現在時刻に合わせたプロフィール画像を生成
1. Twitterのプロフィール画像を生成した画像に更新

# 使用したPython3ライブラリ
* [Pillow](https://pillow.readthedocs.io/en/stable/)
    * 背景画像とアイコンの合成に使用
* [Tweepy](https://www.tweepy.org/)
    * Twitterのプロフィール画像の更新に使用

# 1. Twitter APIのキー取得
まずTwitter APIの取得をします。
[こちらのサイト](https://wporz.com/twitterapi-apikey-accesstoken/)を参考にしてTwitter APIの利用申請、APIキーの取得を行なってください。**EssentialだとTweepyが利用できなかったため、Elevatedの利用申請まで行なってください。**
ここで取得したAPI Key、API Key Secret、Access Token、Access Token Secretはこの後の処理で必要なので必ずメモしてください。

# 2. 背景画像とアイコン画像の用意
昼の背景画像と夜の背景画像、起きているアイコンと寝ているアイコンを用意します。アイコンは背景が透過しているものを用意してください。
(背景もアイコンも天下の[いらすとや様](https://www.irasutoya.com/)からお借りしました。)

背景
| 昼 | 夜 |
| --------- | ---------- |
| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608343/cbf74b0c-d1ea-9e98-1bca-d92c90727c31.png" width="150"> | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608343/f6b321d8-180d-6199-519c-05951c65f005.png" width="150"> |

アイコン
| 起きている | 寝ている|
| --------- | ---------- |
| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608343/57491526-cc73-2b39-8dd9-360223eae26b.png" width="150"> | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608343/260edbff-04c3-1616-936e-7cb777213166.png" width="150"> |


# 3. 時刻に合わせてプロフィール画像を生成
まず必要なライブラリをインストールします。
```shell
pip3 install Pillow
pip3 install tweepy
```

以下の処理で日本の現在時刻を取得します。
```python: main.py
import datetime

# 日本のタイムゾーン
jp_tz = datetime.timezone(datetime.timedelta(hours=9))
# 日本の現在時刻
dt_now = datetime.datetime.now(jp_tz)
hour = dt_now.hour
```

次に、取得した時刻に合った背景、アイコンをPillowで読み込みます。
```python:main.py
from PIL import Image

# 背景
if(7 <= hour < 17):
    # 昼
    background = Image.open("img/background/hiru.png")
else:
    # 夜
    background = Image.open("img/background/yoru.png")

# アイコン
if(9 <= hour < 23):
    # 起きている
    icon = Image.open("img/icon/normal.png")
else:
    # 寝てる
    icon = Image.open("img/icon/suimin.png")
```

最後に背景とアイコンを合成し、保存します。
このとき、合成結果がいい感じになるように配置、トリミングしています。
```python:main.py
# 背景をアイコンサイズに合わせてリサイズ
background = background.resize((int(icon.width*1.1), int(icon.height*1.1)))
# アイコンを背景の中央下部に配置
background.paste(icon, (int(icon.width*0.05), int(icon.height*0.1)), icon)
# 周囲を削除
background = background.crop(
    (background.width*0.05, background.height*0.05, background.width*0.95, background.height*0.95))

dst = background
dst.save("out/out.png")
```

# 4. Twitterのプロフィール画像を更新
先ほど取得したAPI Key、API Key Secret、Access Token、Access Token Secretを用いてAPIの初期化を行います。

```python:main.py
import tweepy

# API Key
consumer_key = "xxxxxxxxxxxxxxxxxxx"
# API Key Secret
consumer_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Access Token
access_token = "00000000000000000000xxxxxxxxxxxxxxxxxx"
# Access Token Secret
access_token_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)

api = tweepy.API(auth)
```

そして、先ほど作成したプロフィール画像に更新を行います。

```python: main.py
api.update_profile_image("out/out.png")
```

# 5. `requirements.txt`の作成 
後のGitHub Actionsで必要になるため、`requirement.txt`を作成します。`requirement.txt`とは実行に必要なパッケージを記載しているファイルで、pipreqsというツールを使うと簡単に作成できます。
まずpipreqsをインストールします。
```shell
pip3 install pipreqs
```
プロジェクトのルートディレクトリでpipreqsを実行します。
```shell
pipreqs .
```
今回は以下のような`requirements.txt`が自動で生成されます。
```text: requirements.txt
Pillow==9.3.0
tweepy==4.12.1
```



# 6. GitHub Actionsで定期実行
GitHub Actionsでは`on.schedule`を使用することでワークフローを定期実行することができます。実行する時間の指定には[POSIX cron 構文](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html#tag_20_25_07)を用います。[POSIX cron 構文](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html#tag_20_25_07)は`分 時 日 月 曜日`を表し、この構文で表す時刻と現在時刻が一致した場合に実行されます。

例えば、以下の例では毎日UTCの5:30と17:30にトリガーされます。
```yml: CI.yml
on:
  schedule:
    - cron:  '30 5,17 * * *'
```

cron構文は[crontab guru](https://crontab.guru/)を使用することで実行時間を確認できます。cron構文に慣れていない方には特にオススメです。
![スクリーンショット 2022-12-20 12.22.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608343/d834b77c-eef7-4530-4200-86f102241b36.png)
今回のツールでは1時間に1回実行したいので以下のように記述しました。
mainブランチにpushされた場合にもトリガーするように設定しています。
```yml: .github/workflows/CI.yml
on:
  push:
    branches:
      - main
  schedule:
    - cron: '30 * * * *'
```
[公式ドキュメント](https://docs.github.com/ja/actions/using-workflows/events-that-trigger-workflows#scheduled-events)にある通り、毎時の開始時間は実行負荷が高く、遅延が発生する場合があるため、毎時30分になったら実行するように設定しました。
>Note: The schedule event can be delayed during periods of high loads of GitHub Actions workflow runs. High load times include the start of every hour. To decrease the chance of delay, schedule your workflow to run at a different time of the hour.
(注意：GitHub Actionsのワークフロー実行の負荷が高い時間帯は、スケジュールイベントが遅延することがあります。負荷の高い時間帯には、毎時の開始時刻が含まれます。遅延の可能性を減らすには、ワークフローを別の時間帯に実行するようスケジュールしてください。)

CI.ymlの全体はこんな感じです。Ubuntu上でPython3.8の環境を構築して実行しています。
```yml: .github/workflows/CI.yml
name: profile-update-actions
on:
  push:
    branches:
      - main
  schedule:
    - cron: '30 * * * *'
jobs:
  update:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
      - name: Setup Python3.8
        uses: actions/setup-python@v2
        with:
          python-version: "3.8"
          architecture: "x64"
      - name: Run
        run: |
          pip install -r requirements.txt
          python main.py
```

# まとめ
本記事ではTwitterのプロフィール画像を自動で更新するツールをGitHub Actionsで作成した話を紹介しました。GitHub Actionsを使うことで簡単に定期実行するツールが作成できました。
GitHub Actionsなんでもできますね！！！


# 参考文献
* [Twitter API v2を使うためのAPI Keyの取得方法：アプリケーションの利用申請からAPI登録まで](https://wporz.com/twitterapi-apikey-accesstoken/)
* [Github Actions で Twitter の Bot を稼働できる件 - Qiita](https://qiita.com/nuko-suke/items/26dd5988eeeee9c11bd4)
* [pipreqsを利用してrequirements.txtをスマートに作成 - Yotaro's BLOG](https://takazawa.github.io/hobby/pipreqs/)
* [GitHub Actions のワークフロー構文 - GitHub Docs](https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions)
* [GitHub Actions ワークフローで処理を定期実行する方法 | gotohayato.com](https://gotohayato.com/content/514/)