Twitter bot を作った時のメモ

Twitter bot を作ってみているのでその時のメモ。 あくまで個人的なメモであり、網羅的な解説などをするものではないです。

方針

なるべく新しいものを使いたいと思ったので、Twitter API v2 + OAuth 2.0 を使うことにした。

Twitter API v2 は 2021-11-15 にプライマリ API に指定された。まだ一部 1.1 にあって 2.0 にはない API もあるようだが、基本的には 2.0 を使っていくのがよさそうだ。

OAuth はどうも、App レベルのアクセスは 2.0 でユーザーレベルのアクセスは 1.0a みたいに使い分けることもできるらしい。 が、調べながらやった結果、その辺りをちゃんと把握せずに進めてしまい、今回は 2.0 だけでやるようにしてしまったので一旦その方向で進めている。

bot 用のアカウントを作る

bot 用に新しく Twitter アカウントを作る。 未ログインの状態で Twitter トップページから通常の手順で作れば問題ない。

色々挙動を確認したりするのに便利なので、運用に使うアカウントとは別にデバッグ用のアカウントを用意すると便利だ。一度 bot の運用を開始すると下手なテストができなくなる。

アカウントに電話番号を登録する

Twitter API を使うためには Twitter アカウントを開発者アカウントに登録する必要があるが、そのためには Twitter アカウントに電話番号を登録する必要がある。同じ電話番号を複数のアカウントに登録しても問題ないようなので、複数の bot を作る場合でも電話番号は 1 つで問題なさそうである。

一般的な手順通り、Twitter のログイン時の画面の左メニューから「もっと見る」「設定とプライバシー」「アカウント」「アカウント情報」でパスワードの確認が入るのでパスワードを入力し、「電話」「電話番号を追加」で行う。 番号を入力すると SMS で認証コードが送られてくるので入力すると登録できる。

なお、下記手順で開発者アカウントを有効にした後に試しにデバッグ用のアカウントに登録した電話番号の登録を消してみたが、今のところは問題なく API は使えている。もしかしたら何かのタイミングで使えなくなる可能性はあるかもしれない。

開発者アカウントを有効にする

Twitter アカウントを開発者アカウントに登録する。

開発者アカウントに登録したい Twitter アカウントにログインした状態で https://developer.twitter.com/ にアクセスし、右上の Sign up ボタンを押す。

名前、国、利用目的(選択式)、政府が使うものかどうか(?)、更新情報をメールで受け取るかを選んで下部の Next を押し、次の画面で Developer agreement & policy に同意するチェックボックスにチェックを入れて Submit を押す。

最後にメールアドレスの検証があるので、登録したメールアドレスに届いた検証メールを探して Confirm your email を押せば API の利用を開始できる。

開発者アカウントにはアクセスレベルがあり、一番簡単な(制限が強い) Essential access であればこれだけで使える。以前は申請理由について英作文をする必要があったようだが、今はここまでなら必要はない。

App を登録する

検証メールから Confirm your email を押すと、App の名前を入力するページに飛ばされる。 アプリ名はグローバルでユニークである必要があるが、後から変更できるので適当な名前でもよい。日本語でも大丈夫。 名前は OAuth の認可画面やツイート時の source 情報に使われる模様。

アプリ名を入力すると「API Key」「API Key Secret」「Bearer Token」が得られる。なくさないように保管する。

API を呼び出してみる

Bearer Token を使うと、ユーザーに紐付かない API を呼び出すことができる。

❯ curl -s -H "Authorization: bearer ${TWITTER_APP_BEARER_TOKEN}" 'https://api.twitter.com/2/users/by?usernames=thinca'
{"data":[{"id":"15676452","name":"thinca","username":"thinca"}]}

OAuth 2.0 を有効にする

API 経由で bot アカウントでツイート等を行うには、OAuth を使ってアカウントから App に認可を与える。 Developer Portal の左のメニューの Projects & Apps からプロジェクト内の Apps を選び、「User authentication settings」から「Set up」を押す。

OAuth 2.0 を有効にして、アプリのタイプを選ぶ。今回は bot を作るので「Automated App or bot」を選ぶ。

「Callback URI / Redirect URL」と「Website URL」を設定する。 これは一般ユーザー向けに認可を行いたい場合はきちんと Web アプリを用意する必要があるが、今回は bot アカウントだけでよいので手動でがんばればなんとかなる。 Callback URI には適当に http://localhost/ を設定(この URL に token 等が渡るので外部の URL にしてはいけない)。Website URL には http://example.com/ などを適当に設定する。

すると「Client ID」と「Client Secret」が手に入るのでこれをなくさないように保管する。

OAuth 2.0 で bot に権限を与える

認可用の URL を作る。

  • type = code 固定
  • client_id = 先ほど生成した Client ID
  • redirect_uri = 先ほど Callback URI / Redirect URL に登録したもの
  • scope = 許可を与えるスコープのスペース(%20)区切りのリスト
    • API ごとに、その API を利用するのに必要なスコープが決められている
  • code_challenge = ちゃんとやる場合はランダムに生成する必要があるが、今回は固定で challenge
  • code_challenge_method = ちゃんとやる場合は S256 とかを指定するが、今回は plain
https://twitter.com/i/oauth2/authorize?response_type=code&client_id=${CLIENT_ID}&redirect_uri=http://localhost/&scope=tweet.read%20users.read%20tweet.write&state=state&code_challenge=challenge&code_challenge_method=plain

bot のアカウントでログインした状態で作った URL にアクセスすると、認可のための画面になる。 「アプリにアクセスを許可」を押すと、Redirect URI に指定した URI にパラメータ付きでリダイレクトされる。 本来はここで Web アプリがリクエストを受け取り、サーバで処理を行う。 が、今回はともかく bot の認可だけできればいいので、リダイレクトされた先(localhost だと当然サーバが動いていなければ接続エラーになるが、URL さえわかればよいので問題はない)の URL の code= の部分に現れたコードを使って以下のように HTTP リクエストを手動で発行する。

${CLIENT_ID}${CLIENT_SECRET} にはそれぞれ Client ID と Client Secret を、${CODE} にはリダイレクトされた URL から得たコードを入れる。code_verifier にはちゃんとやる場合は計算して出した verifier を入れる必要があるが、今回は plain で値を固定しているのでそのまま先ほどの challenge を入れる。 なお、この ${CODE} の寿命はとても短いので、今回のように手動で行う場合は急いで行う必要がある。ちゃんと計測していないが数分で切れているように思う。

curl --location --request POST 'https://api.twitter.com/2/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--basic -u "${CLIENT_ID}:${CLIENT_SECRET}" \
--data-urlencode "code=${CODE}" \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode "client_id=${CLIENT_ID}" \
--data-urlencode 'redirect_uri=http://localhost/' \
--data-urlencode 'code_verifier=challenge'

ちなみにここで Basic 認証をしているが、OAuth 2.0 を有効にした際に選んだアプリのタイプ(今回は Automated App or bot を選んでいる)によっては不要になるそうだが、今回は試していない。Confidential Client に分類されるタイプの場合は必要で、Public Client に分類されるタイプは不要らしい。

すると以下のように Access Token が得られる。Access Token は寿命が短く、7200 秒(2 時間)で切れる。

{"token_type":"bearer","expires_in":7200,"access_token":"dEVDdWkxQnVZT184RlhZM0RQdHp6SjA3S2ExQmVQWG5KOTNvWkdmdGdueTh5OjE2NDE4MjEwOTYwNDM6MTowOmF0OjE","scope":"tweet.write users.read tweet.read"}

この Access Token を以下のように Authorization ヘッダに渡すことでユーザーとして API を利用できる。 以下は認証しているユーザー自身の情報を返す API を利用する例。

curl -H 'Authorization: bearer dEVDdWkxQnVZT184RlhZM0RQdHp6SjA3S2ExQmVQWG5KOTNvWkdmdGdueTh5OjE2NDE4MjEwOTYwNDM6MTowOmF0OjE' https://api.twitter.com/2/users/me

Refresh Token を使って永続的にアカウントにアクセスする

認可用の URL を作る際に、scope に offline.access を入れておくと、Access Token 発行の際に同時に Refresh Token も発行される。 Refresh Token を使うと Access Token を再発行できる。これを使うことで再度ユーザーに認可画面を表示することなくアクセスが可能になる。

https://twitter.com/i/oauth2/authorize?response_type=code&client_id=${CLIENT_ID}&redirect_uri=http://localhost/&scope=tweet.read%20users.read%20tweet.write%20offline.access&state=state&code_challenge=challenge&code_challenge_method=plain

上記のように offline.access を足した画面から発行した code を先ほどと同様に https://api.twitter.com/2/oauth2/token に渡すと、以下のように Refresh Token も一緒に返ってくる。

{"token_type":"bearer","expires_in":7200,"access_token":"UEo2TDBIemlBUkdsVlRJNHpzMWxZekFkd24tcGFETGFmVXJOWEtKdXJJVUU0OjE2NDE4MjE3NTA4ODA6MToxOmF0OjE","scope":"tweet.write users.read tweet.read offline.access","refresh_token":"b0F1bXNtd0Q4QlpDSGg5dWVEeG82UHNIY3lGcC1RbVRUMzRZcVQ0bFpkSVhrOjE2NDE4MjE3NTA4ODA6MToxOnJ0OjE"}

Refresh Token を以下のように使えば、新しい Access Token と Refresh Token が手に入る。

curl --location --request POST 'https://api.twitter.com/2/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--basic -u "${CLIENT_ID}:${CLIENT_SECRET}" \
--data-urlencode 'refresh_token=b0F1bXNtd0Q4QlpDSGg5dWVEeG82UHNIY3lGcC1RbVRUMzRZcVQ0bFpkSVhrOjE2NDE4MjE3NTA4ODA6MToxOnJ0OjE \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'client_id=${CLIENT_ID}'
{"token_type":"bearer","expires_in":7200,"access_token":"aTl5MVNPMlhHSUltck5lOWFlM2lyQ3dLbGhMWnBEbUxEdl9Zdi1yWGdPMUtCOjE2NDE4MjgyMDg0ODU6MTowOmF0OjE","scope":"tweet.write users.read tweet.read offline.access","refresh_token":"TlZvNXNWT0wyMzhialRZdWloM0w5OENGc3JnZzVDYlphLTM3OXItMzVaUXgwOjE2NDE4MjgyMDg0ODU6MToxOnJ0OjE"}

新しい Access Token と Refresh Token が発行されると、古い Access Token と Refresh Token はどちらも無効になる。 実際の運用時には Access Token とその有効期限と Refresh Token を保存しておき、有効期限が切れていたら Access Token を再発行して使うことになる。

Optional: アクセスレベルを上げる

開発者アカウントに登録した直後は Essential Access になっている。申請を行うことで、Elevated Access にすることができる。 検索やユーザータイムライン取得などのいくつかの API は月当たりに取得できるツイート数の上限が決まっている。Essential は 50 万だが、Elevated は 200 万になる。また、作れるアプリの数や Filtered Stream に設定できるルールの数などいくつかの点で違いがある。

ログインした状態で以下にアクセスし、「Apply for Elevated」を押す。

https://developer.twitter.com/en/portal/products/elevated

Twitter アカウント、メールアドレス、個人開発者かどうか、名前、国、コーディングスキル(承認の可否には影響しない)、更新情報をメールで受け取るかを選んで下部の「Next」を押す。

次の画面で、申請したい理由を英語で書く必要がある。最低 200 文字。用途に応じて追加でいくつか理由を書く必要がある。ここは必要であれば機械翻訳等を駆使しつつ素直に書くのがよさそう。 私の場合は具体的に作りたい bot が決まっていたので、そのために制約の緩和があると助かる、みたいなことを書いたら 1 時間かかるかどうかの速度であっという間に承認された。

具体的な API の利用について

別記事で書くかもしれないし書かないかもしれない。とりあえず現状だと、API v2 ではできないけど v1.1 ではできること、v1.1 ではできないけど v2 ではできること、どちらもある。今後の v2 のアップデートに期待したい。