Account Activity APIを使えるようにする

#Twitter開発者アカウント作成
作成は[こちら](https://developer.twitter.com/en/portal/petition/essential/basic-info)からできます
![スクリーンショット 2021-12-12 14.35.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/4e7ad236-4827-a07f-a089-0d2bc2be1234.png)

・ What's your name?
あなたの名前(山田太郎)
※ニックネームでもなんでも大丈夫

・ What country are you based in?
住んでる国(Japan)

・ What's your use case?
何に使うか(Making a bot)

・ Will you make Twitter content or derived information available to a government entity or a government affiliated entity?
政府機関に情報提供するか(No)

・ Want updates?
オプションなのでどちらでもいい


全て入力したら「**Next**」

![スクリーンショット 2021-12-12 14.44.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/85f93e61-c108-efc5-9d60-d2928989d32d.png)

規約に同意したらチェックを入れて「**Submit**」

**Submit**に成功するとアカウントに登録されているメールアドレスに確認メールが届きます。
メール内のリンクにアクセスしてください。

![スクリーンショット 2021-12-12 14.51.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/0f4dfe57-57e6-33e5-888b-8cc5842a4f7b.png)

Appに名前をつけます。
同じAppの名前は作れないので他の人と被らないような名前をつけましょう。

名前が決まったら「**Get keys**」

- API Key
- API Key Secret
- Bearer Token

上記の3つが表示されます。
安全な場所に保管してください。

保管できたら「**Skip to dashboard**」

アラートが出てくるので「**Yes, I Saved them**」

これでTwitter開発者アカウントの登録は完了です。

#Twitter開発者アカウントのアクセスレベルを上げる
https://developer.twitter.com/en/docs/twitter-api/getting-started/about-twitter-api#v2-access-level

上記リンクでアクセスレベル別の使用可能機能が確認できます。

現在の時点でのアクセスレベルは「**Essential**」です
Account Activity APIを使用するには「**Elevated**」にする必要があります。

![スクリーンショット 2021-12-12 15.19.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/b0714754-abd6-8e52-1e43-1ff1ca40205d.png)


左側のメニューの「**Project1**」を選択してください。

**Project1**の中の「**Apply for Elevated**」というボタンを押します。

①Basic info → ②Intended use → ③Review → ④Terms

**①Basic info**
・ What's your name?
あなたの名前(山田太郎)
※ニックネームでもなんでも大丈夫

・ What country are you based in?
住んでる国(Japan)

・ What's your current coding skill level?
コーディングはどのくらいのスキルか(Some experience)

・ Want updates?
オプションなのでどちらでもいい

全て入力したら「**Next**」

②Intended use
ここは全て自分の言葉で英文を作らないといけない箇所です
一番辛いところですが頑張ってください

自分は英語はできないので[DeepL翻訳](https://www.deepl.com/ja/translator)使いました。

③Review
自分の入力に間違えがないか確認します。
大丈夫なら「**Next**」

④Terms
規約とポリシーが表示されます。
同意したらチェックを入れて「**Submit**」

これでアクセスレベルを上げる申請は完了です。
申請が承認されるまで待ちましょう。

アカウントや提出した英文の内容によって審査時間が変わってくると思います。
4回ほど申請したことがありますが一瞬で承認された事もあれば3日後に承認された事もあります。

#App permissionsの変更
ここからはアクセスレベルが「**Elevated**」である事が前提です。
![スクリーンショット 2021-12-12 17.53.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/55525dc7-fd7d-bd79-f1fd-ea8bcc5fdeae.png)

設定マークを押してください

![スクリーンショット 2021-12-12 17.56.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/ad6337a1-aba8-1546-eb62-6f5c601556db.png)

「**Read**」を「**Read, Write, and Direct Messages**」に変更します。

#Access Token & Access Token Secretの取得
![スクリーンショット 2021-12-12 17.53.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/b0db5797-3b5e-18ea-3e94-18b0ce88ce77.png)

鍵マークを押してください

![スクリーンショット 2021-12-12 18.01.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/70479489-f07b-d753-7601-8a59ad25c1dd.png)

「**Access Token and Access Secret**」からAccess TokenとAccess Token Secretを取得します
「**Regenerate**」の表記は違うかもしれません


#Dev Environmentsの設定

![スクリーンショット 2021-12-12 15.50.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/610e9e5c-bb28-7961-6541-fc95648e5e45.png)

Dev Environmentsを選択します。
一番下にある「Account Activity API/Sandbox」の右にある「**Set up dev environment**」を選択してください。

- Dev environment label
- App

上記2つ入力することになります。
Dev environment labelは環境名でWebhookを登録する時に使います。
Appは先ほど作成したAppを選択してください。

![スクリーンショット 2021-12-12 16.32.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/6195ceb2-e0c9-a862-363e-90da43aafe14.png)

このようになれば成功です。

#Webhook登録用のURL作成
Webhookの登録時にCRCが送信されるので受け取れるように作成します

```app.py
#!/opt/alt/python36/bin/python3.6
import os
import base64
import hashlib
import hmac
from flask import Flask, request, jsonify

app = Flask(__name__)

consumer_secret = "XXXXXXXXXXXXXXXXXXXX"

@app.route('/webhook', methods=['GET'])
def webhook_challenge():
    params = request.args
    if 'crc_token' in params:
        sha256_hash_digest = hmac.new(consumer_secret.encode(), msg = params.get('crc_token').encode(), digestmod = hashlib.sha256).digest()
        response_token = 'sha256=' + base64.b64encode(sha256_hash_digest).decode()
        response = {'response_token': response_token}
        return json.dumps(response), 200, {'Content-Type': 'application/json'}
    else:
        return jsonify({"error":"No Content"})

if __name__ == '__main__':
    app.run()
```

これをConoHa Wingにアップロードします

https://qiita.com/_SNQ/items/a09320257621a6f39b08

ConoHa WingでFlaskを使用する方法はこちらで紹介しているので詳しい事は割愛します

アップロードが完了したらURLを確認してみましょう
https://あなたのドメイン/webhook?crc_token=test

jsonが表示されたら成功です
これでURLは完成です

#Webhookの登録
ここで大苦戦しました。
なんと！！Twiiterの公式ドキュメントが間違ってる！！

https://developer.twitter.com/en/docs/twitter-api/enterprise/account-activity-api/api-reference/aaa-enterprise#post-account-activity-webhooks

公式ドキュメントではこうなってます

```
$ curl --request POST 
   --url 'https://api.twitter.com/1.1/account_activity/webhooks.json?url=https%3A%2F%2Fyour_domain.com%2Fwebhooks%2Ftwitter%2F0' 
   --header 'authorization: OAuth oauth_consumer_key="CONSUMER_KEY", oauth_nonce="GENERATED", oauth_signature="GENERATED", oauth_signature_method="HMAC-SHA1", oauth_timestamp="GENERATED", oauth_token="ACCESS_TOKEN", oauth_version="1.0"'
```
POSTのパラメーターってURLに付けたらダメじゃなかったっけ？

解決には
url=https%3A%2F%2Fyour_domain.com%2Fwebhooks%2Ftwitter%2F0
をBodyに入れてPOSTする必要があります。

POSTは簡単でわかりやすい[Postman](https://www.postman.com/)を使います

![スクリーンショット 2021-12-12 17.21.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/15b71bb8-6923-ded7-46a6-3c3189984148.png)

Authorizationを選択して「Inherit auth from parent」を「OAuth 1.0」に変更します。

- Consumer Key
- Consumer Secret
- Access Token
- Token Secret

をそれぞれ入力します


❌ 公式ドキュメントver
![スクリーンショット 2021-12-12 17.20.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/b84765fb-d371-d747-f44a-d9d01f05c82a.png)


⭕️ 修正ver
![スクリーンショット 2021-12-12 17.16.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/c2767e10-cc11-6577-1091-9c75b27504fa.png)

**urlの「:ENV_NAME」は環境名で設定したものを入れてください。**

Sendを押して「Status: 200」なら登録成功です

エラーが出た場合

https://zenn.dev/noraworld/articles/twitter-account-activity-api-webhook-url#%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95

ここから該当するエラーを探してみてください

#subscriptionの登録
![スクリーンショット 2021-12-12 17.21.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/15b71bb8-6923-ded7-46a6-3c3189984148.png)

先程と同じようにAuthorizationを選択して「Inherit auth from parent」を「OAuth 1.0」に変更します。

- Consumer Key
- Consumer Secret
- Access Token
- Token Secret

をそれぞれ入力します

![スクリーンショット 2021-12-12 17.38.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/814be57b-3e50-9bd2-f437-c9490506fcaa.png)

**urlの「:ENV_NAME」は環境名で設定したものを入れてください。**

Sendを押して「Status: 204」なら登録成功です

お疲れ様です
以上で接続が完了しました。

#TwitterからのPOSTを受け取る
サンプルで簡単なDMbotを作ります

仕様
・ DMが来たらDMしたユーザーのIDを返信する
・ DM以外のActivityには反応しない
・ 自分の送ったDMには反応しない

```app.py
#!/opt/alt/python36/bin/python3.6
import os
import base64
import hashlib
import hmac
from flask import Flask, request, jsonify
import tweepy

app = Flask(__name__)

consumer_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
consumer_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
access_token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
access_token_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

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

@app.route('/webhook', methods=['GET'])
def webhook_challenge():
    params = request.args
    if 'crc_token' in params:
        sha256_hash_digest = hmac.new(consumer_secret.encode(), msg = params.get('crc_token').encode(), digestmod = hashlib.sha256).digest()
        response_token = 'sha256=' + base64.b64encode(sha256_hash_digest).decode()
        response = {'response_token': response_token}
        return json.dumps(response), 200, {'Content-Type': 'application/json'}
    else:
        return jsonify({"error":"No Content"})

@app.route('/webhook', methods=['POST'])
def webhook():
    data = request.get_data(as_text=True)
    data = json.loads(data)
    if "direct_message_events" in data:
        sender_id = data["direct_message_events"][0]["message_create"]["sender_id"]
        if not sender_id == "自分のTwitterアカウントのID":
            api.send_direct_message(recipient_id=sender_id, text=str(sender_id))
    return 'OK'

if __name__ == '__main__':
    app.run()
```

完成！！！

#追記
2022年1月22日〜
一部のユーザーからのアクティビティが取得できない問題が発生
原因: ConoHa WingのWAF設定により499が発生していた
解決策: WAF設定をOFFに変更

#おわり
質問があったら[@_SNQ](https://twitter.com/_SNQ?t=TS8J1OCmvZshvSkYnqQLjw&s=09)にDMください。

Account Activity APIを使ってTwitterBot作りました。
TwitterのShadowBanをチェックできるBotです。
DMに反応します。
[@Shadow_Ban_Bot](https://twitter.com/Shadow_Ban_Bot?t=TS8J1OCmvZshvSkYnqQLjw&s=09)