シャニマスカレンダーを自動で壁紙に設定する（Linux版）

# この記事でやること
・Python + Twitter APIでシャニマスカレンダーをDLして、自動で壁紙に設定する仕組みを作ります。
・環境はLinuxです。WSLは対象外です。Windows版は今後作成する予定です。

# シャニマスカレンダー
毎月公式が出すシャニマスカレンダー

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">／<br>7月の <a href="https://twitter.com/hashtag/%E3%82%B7%E3%83%A3%E3%83%8B%E3%83%9E%E3%82%B9%E3%82%AB%E3%83%AC%E3%83%B3%E3%83%80%E3%83%BC?src=hash&amp;ref_src=twsrc%5Etfw">#シャニマスカレンダー</a><br>＼<br><br>7月は透さん、真乃さんのカレンダーです♪<br>夏らしい空や海の青さが目に心地いいですね～<br><br>プロデューサーさん、ぜひカレンダーから夏を感じてくださいね♪<a href="https://twitter.com/hashtag/%E3%82%B7%E3%83%A3%E3%83%8B%E3%83%9E%E3%82%B9?src=hash&amp;ref_src=twsrc%5Etfw">#シャニマス</a> <a href="https://t.co/H9wXdY2s8z">pic.twitter.com/H9wXdY2s8z</a></p>&mdash; アイドルマスター シャイニーカラーズ公式 (@imassc_official) <a href="https://twitter.com/imassc_official/status/1542704553950416896?ref_src=twsrc%5Etfw">July 1, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 


** **
**おしゃれ〜〜〜〜！！！！！**

**かわいい〜〜〜〜！！！！！**

これは壁紙として使うしかない。

でも毎月PCに画像をDLして、壁紙に設定するのは面倒、、、
普段スマホでしかツイッター見ないし、、、

ということで、これを自動化できないか考えました。
# 使うもの
・OSがLinuxのPC（WSLではない）
・python3
・シャニマス公式をフォローしているTwitterアカウント
・上記アカウントのTwitter API Key

なぜLinuxかというと、私がLinuxが好きで普段使いのPCがLinux(Ubuntu)だからです。
Windows版の記事は別途作ろうと思います。

また、未検証ですがpythonのコードはOSに依存しない作りになっているはずなので、python部分についてはWindowsやMacにそのまま流用できると思います。

# 参考にした記事
こちらの記事を参考にしました。
https://qiita.com/git-Ktu/items/b5971499efd59785915a

# 構成
構成は以下のとおりです。
```
/home/${user}/
├── .bash_profile
└── gscc
    ├── config.py
    ├── gscc.py
    └── gscc.sh
```

今回は.bash_profileを使います。
このファイルにシャニマスカレンダーを取得する処理を記載することで、ログインのたびにその処理を走らせるような作りとなっています。

.bash_profileはデフォルトだと存在しないことが多いようなので、その場合は新たにファイルを作成してください。

# 実装（全体像）
実装はpython部分とbash部分に分かれます。
pythonが画像を端末に保存し、bashがそれを背景に設定する役割を担っています。

最初にpythonの実装について説明します。

# 実装(python)
まずはconfigファイルを作成します。
ファイルにはTwitterAPIへのアクセスに必要なKeyを設定します
```python:config.py
CONFIG = {
    "CONSUMER_KEY":"XXXXXXXXXXX",
    "CONSUMER_SECRET":"XXXXXXXXXXXX",
    "ACCESS_TOKEN":"XXXXXXXXXXXXXXXXXXX",
    "ACCESS_SECRET":"XXXXXXXXXXXXXXXXX",
   }
```

今回は以下のKeyが必要になります。取得方法はリンク先を参考にしてください。
・API Key、API Secret Key
  https://wporz.com/twitterapi-apikey-accesstoken/#API_KeyAPI_secret_key
・Access token、Access token key
  https://wporz.com/twitterapi-apikey-accesstoken/#Access_tokenAccess_token_secret

次にメイン処理です。

```python:gscc.py
import tweepy
from config import CONFIG
import urllib.request
import re
import datetime
import getpass

#config.pyの値を取得
CONSUMER_KEY = CONFIG["CONSUMER_KEY"]
CONSUMER_SECRET = CONFIG["CONSUMER_SECRET"]
ACCESS_TOKEN = CONFIG["ACCESS_TOKEN"]
ACCESS_SECRET = CONFIG["ACCESS_SECRET"]

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)

api = tweepy.API(auth)

#取得したURLを使って画像を保存
def save_image(url):
    count = 1
    user = getpass.getuser()
    dir = "/home/" + user + "/gscc/"

    for image_url in url:
        image_url = image_url + ":orig"
        date = datetime.date.today()
        file_path = dir + date.strftime('%Y-%m') + "-01_imassc_calender_" + str(count) + ".jpeg"
        data = urllib.request.urlopen(image_url, timeout=20).read()
        with open(file_path, mode="wb") as f:
            f.write(data)
        count += 1

#画像URLを取得
def get_image():
    result_url = []
    for tweet in tweepy.Cursor(api.user_timeline, id="imassc_official").items(500):
        if (list(tweet.text)[:2]!=['R', 'T']) & (list(tweet.text)[0]!='@'):
            if "#シャニマスカレンダー" in tweet.text:
                for media in tweet.extended_entities['media']:
                    result_url.append(media['media_url'])
                break
    return result_url

def start():
    url = get_image()
    save_image(url)

if __name__ == "__main__":
    start()
```

基本的な作りは参考元のコードと同じです。
簡単に内容を説明します。

**全体の流れ**
get_image()がシャニマスカレンダーの画像URLをTwitterから取得します。
save_image()は、上記で取得した画像URLを使って指定のフォルダに画像を保存します。

**get_image()**
APIを使ってシャニマス公式のツイートを最新のものから順に取得します。
さらにその中から「RTでも引用でもないもの」かつ「テキストに"#シャニマスカレンダー"が含まれるもの」を判別し、そのツイート内の画像URLを取得します。

取得ツイート数は500にしていますが、画像URLを取得できたらその時点で処理を完了するので、それ以降のツイートは取得されません。
加えて、詳細は後述しますが、既にシャニマスカレンダーを端末に保存済みであればgscc.pyそのものを実行しないような制御をgscc.shの中でしています。

**save_image()**
取得したURLを使ってシャニマスカレンダーをDLします。
保存先は${dir}変数で指定しています。

参考元とは違い、画像の保存に「urlopen」を使っています。
検証はしていませんが、以下の記事にあるように「urlretireve」だとゴミファイルが残ることがあるようです。
https://qiita.com/Kanahiro/items/dd585599f36a77540439

タイムアウト値は20秒としています。
この値は必要に応じて変えてもらって大丈夫です。

# 実装(Bash)

ここでは先程のgscc.pyの呼び出しと、取得した画像を壁紙に設定する処理を記述しています。

```bash:gscc.sh
#!/bin/bash

date=`date "+%Y-%m"`
dir="/home/`whoami`/gscc/"
image_file="${dir}${date}-01_imassc_calender_1.jpeg"

#インターネットにアクセス可能か確認
ping -c 4 8.8.8.8
if [ ! $? = 0 ];then
	exit 1
fi

#画像ファイルが存在していなければ新たに画像を取得し壁紙に設定する
if [ ! -f  ${image_file} ];then

	#画像取得処理
	python3 ${dir}/gscc.py

	for i in ${dir}${date}*
	do
		#画像がPC用かどうかをサイズから判断
		width=`file ${i} | cut -d, -f8 | awk -F'x' '{print $1}'`
		height=`file ${i} | cut -d, -f8 | awk -F'x' '{print $2}'`
		if [ ${width} -gt ${height} ];then
			#壁紙に設定
			gsettings set org.gnome.desktop.background picture-uri "file://${i}"
			break 2
		fi      
	done

fi
```

ディレクトリは${dir}で指定しています。画像の保存先を変える場合はここも修正してください。

処理の最初でインターネットにアクセスできることを確認しています。
pingがNGの場合はexitで処理を終了させます。

最後に.bash_profileです

```bash:.bash_profile
/home/`whoami`/gscc/gscc.sh > /dev/null 2>&1
```

ここではgscc.shを呼び出しているだけです。
.bash_profileに処理を書くことで、ログイン時に必ずこの処理が動いてツイッターからシャニマスカレンダーを取得してくれます。

ディレクトリの指定にwhoamiコマンドを使っていますが、"~/"でホームディレクトリを指定しても問題なく動くと思います。

ログを取得したい場合は、リダイレクト先を/dev/nullから任意のファイルに変更してください。
# 実際に動かしてみる
まずは実行前のデスクトップ画面。
![Screenshot from 2022-07-07 01-43-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729405/6cea15aa-6f03-0d11-2bed-a30a7c2e69c3.png)

ここから端末再起動もしくはログオフをして、再度ログインすると、、、

![Screenshot from 2022-07-07 01-44-22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729405/6272613e-d22c-8443-1a59-09127d8fbaa6.png)
**かわいい〜〜〜〜！！！！！**

ご覧の通り！自動で壁紙を設定してくれました！

ログイン前に画像のDLを行うことになるので、ログインに時間がかかるかと懸念していましたが体感ほとんど気になりませんでした。
遅くなるにしても月に一度だけなので致命的な欠点にはならなさそうです。

# まとめ
やっぱりアイドルがデスクトップ上にいるとテンションが上がりますね！
みなさんもLinuxをお使いであれば、こちらのスクリプトを使ってみてください。

また、Windows版は今後作成予定ですが、Macに関しては門外漢なのでどなたか詳しい方が作成してくださるのをお待ちしています。

# エンジニア向けの雑記
技術的な面で躓いたポイント・感想などをいくつか

**・cron**
最初はcronを使って実装しようと思っていたのですが、いくつか落とし穴がありました。
ひとつは環境変数。cronでの実行はユーザでの実行と異なり、環境変数が最低限しか設定されていません。（参考：https://rcmdnk.com/blog/2020/01/12/computer-linux-mac/）
特にX Window Systemで使用されるDISPLAY変数が存在していないのが致命的でした。

この問題はcronに実行させるスクリプト内でexportコマンドを使い環境変数を設定する、などの解決策があるのですが、その先にも問題がありました。
cronだとgsettingコマンドで壁紙を変更しても端末を再起動するまで変更が反映されないのです。それどころか、場合によっては現在設定している壁紙がはずれてデスクトップ背景が真っ黒になってしまうこともありました。理由はよくわかりませんでしたが、これは問題です。

一方で、手動でgscc.shを動かした場合は問題なく壁紙が変更できたので、「原因はよくわからないが、現在デスクトップを動かしているユーザのプロセスから実行すれば再起動せずともリアルタイムで壁紙を変更できるのでは？」と思い、.bash_profileを使ったところドンピシャでした。

.bash_profileなんてLPICの勉強をしたとき以来まったく意識したことがなかったので、使い道があることにちょっと感動しました。

**・シェルの展開順序について**
シェルに展開順序があることを知りました。（参考：https://qiita.com/emasaka/items/a59335c74220b3641639）

シェルスクリプトは処理が実行される前に、変数展開、コマンド置換、パス名展開などを行います。
例えば、私の場合は当初以下のような形で処理を記述していました。
```bash:gscc.sh
#画像取得処理(画像が${dir}配下に保存される)
python3 ${dir}/gscc.py

#gscc.pyで保存した画像をfor文で回す
for i in `ls ${dir}*`
do
　〜省略〜
done
```
gscc.pyで取得した画像をfor文で扱いたかったのですが、これだとgscc.pyが実行される前に「ls ${dir}*」が展開されます。
その場合、当然ですがまだ画像は存在しないのでlsコマンドがエラーを吐きます。for文にはそのエラーの結果が渡されることになるため、全く想定通りの動きをせず、解決にはかなりの時間を要しました。

シェルの展開順序については、きちんと抑えた上でスクリプトを作成する必要がありますね。