「Twitter API リプライ」で検索した人に贈る記事

最初に言っておくと、この記事はAPIを駆使して特定のツイートにくっついたリプライを集めるものではない。
それをやりたければ[こちらの記事](https://qiita.com/h_tashiro/items/09b0bcf180dca7f91a3b)を参照してほしい。Twitter公式のコンシューマーキーが流出してるなんて初めて知ったぞ。Regenerateされないのか？？

## 概要
Pythonで[とあるシミュレーションゲーム](https://so2.mutoys.com/)のAPIを叩いて情報を流すTwitterのbotを作ってるときの話(というかその初期段階)。
シェル芸botよろしくハッシュタグに反応しようかなと思っていたので、テストをしていた。
疑似StreamingAPIならぬstatuses/filterを用いている。

## 実験
```main.py
if __name__ == "__main__":
    session = OAuth1Session(consumerKey, consumerSecret, accessToken, accessTokenSecret)

    # ハッシュタグ「#これはテスト用ですよ」のツイートを集める
    tracker = {"track":"#これはテスト用ですよ"}
    resGet = session.post(stream, params=tracker, stream=True)
    for line in resGet.iter_lines():
        print(line.decode("utf-8"))
        content = json.loads(line.decode("utf-8"))
        userName = content["user"]["name"]
        text = content["text"]
        tweetId = content["id"]
        print(userName + " said: " + text)

        # リプライする
        reply = "テストは成功です"
        mention = {"status":reply, "in_reply_to_status_id":tweetId}
        resPost = session.post(tweet, params=mention)
```

このコードを実行し、実際に「#これはテスト用ですよ」と呟いてみた。
その結果が以下の画像である。しゅうまい君には触れないであげてほしい。
![SnapCrab_NoName_2019-10-25_15-28-5_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310556/d17e09ae-7aba-843e-2c6a-71cc6dc5d9fe.png)
**何も成功していない。**ただそのツイートをタイムラインに流しただけだ。
ちなみに上記のコードを書くにあたって、私は「Twitter API リプライ」で検索した。
他の人のツイートについたリプライを集めるQiita記事が引っかかったりしたが、それは目的とは違ったのでスルー。
私が、そして記事を読んでいるあなたがやりたいことはリプライをすることであり集めることではないはずだ。
そうして見つけた記事が、多分CSSの整形とかでお世話になった人も多いであろう[Syncerのページ](https://syncer.jp/Web/API/Twitter/REST_API/POST/statuses/update/)だった。
「ツイートIDを指定すると、そのツイートに対するリプライになる」と書かれているものだから、私はすっかりそれを信じ込んでしまって、上のコードを書いたのだった。しかし、思ったとおりには動かない。

## 解決
その後、検索を繰り返してたどり着いたのが[この記事](https://www.x-tb.club/?p=63)だった。
ここに答えが書いてあった。

>2016年からの新仕様で、auto_populate_reply_metadata => true が無いと通常投稿になってしまう。
>
>色んな日本語サイトを参考にしてたんだけど、auto_populate_reply_metadata については書かれていなかった。

実際に、下から2行目のパラメータに`"auto_populate_reply_metadata":True`を追記してテストをしてみた。
![SnapCrab_NoName_2019-10-25_15-37-9_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310556/4a63535a-d76d-f653-3138-70a8bb5530fd.png)
無事成功した。

## 反省
上記の内容だけでは参照した記事のコピーのようになってしまうので、少し内容を追加しようと思う。
よく考えれば(よく考えなくても)当然ではあるが、このことは公式のAPIドキュメントにも記載されていた。解決した後なので見なくてもよかったが、記事を書くために見た。
そのドキュメントの、`in_reply_to_status_id`の説明を引用する。(引用元は[こちら](https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-update))

>The ID of an existing status that the update is in reply to. **Note:** This parameter will be ignored unless the author of the Tweet this parameter references is mentioned within the status text. Therefore, you must include `@username` , where `username` is the author of the referenced Tweet, within the update.

この文章の、わざわざ太字で**Note**と書かれた文章から先を読んでみてほしい。と言っても英語恐怖症の方も居るだろうから、ここに訳を記載する。なお訳は[みらい翻訳](https://miraitranslate.com/trial/)による。

>**注意:** このパラメータが参照するTweetの作成者がステータステキスト内に記述されていない場合、このパラメータは無視されます。したがって、更新には`@username` (`username`は参照されるつぶやきの作成者) を含める必要があります。

ここで言うステータステキストとは、要するにツイートするテキストそのものである。
そこに`@username`(要するにQmanEnobiktoとかみたいな@で始まるID)を入れておく必要がある、ということである。
というわけで、以下のようにコードを変更して試してみた。
ツイートする内容である変数`reply`に`screen_name`を埋め込んでいる。

```python

for line in resGet.iter_lines():
    print(line.decode("utf-8"))
    content = json.loads(line.decode("utf-8"))
    userName = content["user"]["name"]
    text = content["text"]
    screenName = content["user"]["screen_name"] # TwitterのユーザーID(@で始まるやつ)を取得している
    tweetId = content["id"]
    print(userName + " said: " + text)
    reply = f"@{screenName} テストは成功ですの"
    mention = {"status":reply, "in_reply_to_status_id":tweetId}
    resPost = session.post(tweet, params=mention)
```
その結果がこれである。
![SnapCrab_NoName_2019-10-25_16-16-54_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310556/bc6473e3-9b68-7fe7-7970-ebec1b1a6859.png)
**普通に成功した。**というわけでこの方法でも良さそうである。

次に、`auto_populate_reply_metadata`の説明を引用する。

>If set to `true` and used with `in_reply_to_status_id`, leading `@mentions` will be looked up from the original Tweet, and added to the new Tweet from there. This wil append `@mentions` into the metadata of an extended Tweet as a reply chain grows, until the limit on `@mentions` is reached. In cases where the original Tweet has been deleted, the reply will fail.

これはざっと訳せば、「`true`に設定して`in_reply_to_status_id`と一緒に使えば勝手に`@username`を追加してリプライにしてくれるよ。元ツイートが消えてると返信に失敗するよ」程度のものである。微妙に内容が抜けてはいるが理解に支障はない。
これを使うなら、返信に失敗したときのエラーでプログラムが落ちないようにすべきかもしれない。

## 総括
先に紹介した記事の著者も言っているように、この件については日本語の記事が本当になかった。

>auto_populate_reply_metadata さえ理解してしまえば問題は無かった話ではありますが、このパラメーターの存在が日本語のブログで書かれて居なかったのです。

この人が記事を書いてくれていなければ、私はまだインターネットの海を彷徨っていたかもしれない。
というわけで、この記事が少しでも日本語記事へのアクセスを楽にしてくれることを願って、総括とする。

ちなみに上に書いたコードをそのまま使うと、ハッシュタグの含まれたツイートをRTした人にもリプライが来るようになっているので注意してほしい。RTされたツイートの先頭には「RT」と書かれているのでそれを見つけて除外するとかの処理が必要だ。