屋根裏

コードを書いたりします

Astraeaで生成された惑星をツイートするbotを作る

前回の記事で紹介したAstraeaが生成した惑星の画像を共有するための、TwitterbotPythonで作りました。

Twitter APIの取得

開発者アカウントの登録

プログラムからツイートをするには、まずTwitterの開発者アカウントを登録する必要があります。

これについては既に多くの方が手順を紹介されているのであまり詳しく書きませんが、こちらから申請し、指示通りに情報を記入することになります。

私は下の記事を参考にしました。

私は2020年4月23日に英語で申請し、4月27日に詳細情報を求めるメールが日本語で届きました。翌日これに日本語で返信したところ、5月5日に承認されました。新型コロナウイルスやGWの影響で時間がかかったのでしょうか。

ちなみにこのとき登録するアカウントは、botとして運用するアカウントである必要はありません。

APIキーの取得

実際にプログラムからツイートをする際に必要となる、4つのキーを取得します。

こちらも上の記事で紹介されているように、「Create an app」から指示に従ってアプリを作成することになります。このとき設定したアプリ名は、他のユーザーからも見えるので注意しましょう。

アプリを作成すると「Keys and tokens」タブで、APIキーを取得できます。

このとき、上のConsumer API keysはアプリ固有のキーになりますが、下のアクセストークンはそのアプリを使用するアカウントごとのキーになります。

このページで確認できるのは、開発者アカウントとして申請したアカウントのものなので、botとして運用するアカウントが別にある場合は、そのアカウントのアクセストークンを取得しなくてはなりません。

アクセストークンの取得方法もいくつかあるようなのですが、今回はPythontweepyというライブラリで取得することにします。

tweepyをインストールした後、Pythonの対話モードで以下のスクリプトを動かします。consumer_keyとconsumer_secretには、上で確認したConsumer API keysを入れましょう。

import tweepy
consumer_key = "*"  # API key
consumer_secret = "*"  # API secret key
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
print(auth.get_authorization_url())

このとき出力されたURLにアクセスし、botにしたいアカウントで連携アプリを認証します。その後表示されるPINコードを使い、以下の行を実行します。

print(auth.get_access_token("*"))  # PINコード

このとき出力されるタプルの1つ目の要素がAccess token、2つ目の要素がAccess token secretとなります。

Pythonからツイート

astraea-bot/bot.py at master · yurkth/astraea-bot · GitHub

Astraeaで生成した画像を取得

AstraeaはJavaScriptで動作しているので、惑星の名前や画像を取得するには実際にブラウザ上で動作させないといけません。

ここで、Seleniumというライブラリを使い、Pythonからブラウザを操作することにします。

Seleniumを動かすには、別途ChromeDriverを用意する必要があります。Raspberry PiにChromeDriverを入れる際には、次のページを参考にしました。

実際のプログラムでは、以下のようにブラウザを立ち上げ、Astraeaのページにアクセスしています。

        options = Options()
        options.add_argument("--headless")
        options.add_argument("--no-sandbox")
        options.add_argument("--disable-gpu")
        driver = webdriver.Chrome(environ["CHROME_DRIVER_PATH"], options=options)
        driver.get(environ["ASTRAEA_URL"])

Seleniumでは、execute_scriptメソッド使うことでJavaScriptを実行できます。そこで、キャンバスが描画されるまで、つまりframeCountが1より大きくなるまで待機します。

その後、rng.seedで惑星の名前、canvas.toDataURL()でキャンバスの画像(Base64)を取得します。

        WebDriverWait(driver, 10).until(
            lambda d: d.execute_script("return frameCount > 1")
        )
        name = driver.execute_script("return rng.seed")
        b64 = driver.execute_script("return canvas.toDataURL()")

取得したキャンバスの画像は、Wandというライブラリでトリミングとリサイズを行います。

始めはPillowを使っていたのですが、Raspberry Pi上でうまく動かせなかったのでWandを使うことにしました。Wandを動かすにはImageMagickが必要になります。

    with Image(blob=b64decode(b64.replace("data:image/png;base64,", ""))) as img:
        img.crop(width=192, height=108, gravity="center")
        img.sample(*map(lambda x: x * 3, img.size))
        resized = BytesIO(img.make_blob())

画像を添付してツイート

いよいよ取得した画像をツイートします。

まず、先程確認した4つのキーを使い、APIの認証を済ませます。

プログラムをGitHubなどで公開する予定がある場合は、キーをそのまま書かずに環境変数から読み込むようにしましょう。以前も紹介したdirenvを使うと幸せになれます。

画像を添付してのツイートにはupdate_with_mediaメソッドを使います。

statusにツイート本文、fileにファイルオブジェクトを指定します。MIMEタイプを検出するためにfilenameも適当に指定したほうがいいようです。

def tweet(message, file):
    consumer_key = environ["CONSUMER_KEY"]
    consumer_secret = environ["CONSUMER_SECRET"]
    access_token = environ["ACCESS_TOKEN"]
    access_secret = environ["ACCESS_SECRET"]
    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_token, access_secret)
    api = tweepy.API(auth)
    api.update_with_media(status=message, filename="planet.png", file=file)

このtweet関数に惑星の名前と画像を渡すことでツイートが行われます。

定期的に実行させる

Raspberry Piでcronを使用して定期実行させます。あまり頻度が高くてもTLを汚染してしまうので、6時間毎にツイートするようにしました。

まとめ

無事Astraeaで作られた惑星をツイートできるようになりました。

単語をリプライしたら、その名前の惑星を返信してくれるような機能も追加したいですね。

後ほど記事にしますと言っていたのに忙しくて2ヶ月ほど経ってしまいました…

yurkth/astraea-bot - GitHub