免責事項: これやってTwitterの垢凍結されてもしらん
投票APIを叩いていきますが、まずこのAPIは皆さんご存知の通り一般に公開されていません。 そのためこのAPIを使っている公式クライアントの通信を解析する必要があります。
公式クライアントで一般的なものといえば、通信を割りやすい順に
- Twitter Lite (mobile.twitter.com)
- Tweetdeck (tweetdeck.twitter.com)
- Twitter Web Client (twitter.com)
- Twitter for iPhone/iPad (以下Twitter for iOS)
- Twitter for Android
- その他
があるわけですが、私はAndroid実機を持っておらずまたAndroidでのmitmは面倒くさく(Android 7.0からは特に)、なおかつTwitter Web Clientは基本的にWeb専用APIなので通常のOAuth認証で使える可能性が低いこと(例外はありますが今回はその例外にはあてはまりませんでした)、またTwitter Lite/Tweetdeckにはそもそも投票をツイートする機能が実装されていなかったことから、今回はTwitter for iOSの通信を割る方針で行きます。
Twitterの通信は当然HTTPSで行なわれているので、まずmitmをしなければいけません。詳しくは説明しませんが、インターネットの海を数分漂えばmitmができるツールが複数あるので気になった方は探してみてください。
さてここで問題が発生しました。Twitter for iOSの通信はCertificate Pinningが行なわれており、mitm用ルート証明書をiOSに入れただけではmitmができなくなっているようです。 私は暇人だったのでTwitter.appの中身をいろいろ探してみたのですが、簡単(ここで言う簡単とはiOSデバイス上のファイルシステムにあるファイル差し替え程度の事を言う)にCertificate Pinningの迂回ができそうではありませんでした。 そこからも根気強く「Twitter for iPhone ceritifcate pinning」とかでずっと検索していたら、なんかSSL証明書のチェックを無効化する(Twitterも対象)tweakがあったので、これを使ってみたら、無事Twitter for iOSのmitmができました。めでたしめでたし。
ついでに言うとこれから紹介するAPIは基本的に公式のキーでないと使えません。
さて肝心の投票APIです。
まず、https://caps.twitter.com/v2/cards/create.json
にcard_data
という名前で以下のJSONを送り付けます。
{
"twitter:card": "poll2choice_text_only",
"twitter:string:choice1_label": "ここに選択肢1のアレが入る",
"twitter:string:choice2_label": "ここに選択肢2のアレが入る",
"twitter:long:duration_minutes": 1440,
"twitter:api:api:endpoint": 1
}
これが成功すると
{
"card_uri": "card://1145148101919893",
"status": "OK"
}
みたいなレスポンスが返ってくるんで、あとは https://api.twitter.com/v1/statuses/update.json
に
{
"card_uri": "さっきのcard_uri",
"status": "これを見たら投票しないとあなたは百年以内に死にます"
}
みたいなのを投げ付けると投票が発生します。
とりあえずhttps://api.twitter.com/1.1/statuses/show/:id.json?cards_platform=Web-12&include_cards=1
をたたくと、card
に以下のようなのが発生します。
{
"name": "poll4choice_text_only",
"url": "card://968924914810941440",
"card_type_url": "http://card-type-url-is-deprecated.invalid",
"binding_values": {
"choice1_label": {
"type": "STRING",
"string_value": "よい"
},
"choice2_label": {
"type": "STRING",
"string_value": "ふつう"
},
"end_datetime_utc": {
"type": "STRING",
"string_value": "2018-03-01T19:04:36Z"
},
"counts_are_final": {
"type": "BOOLEAN"
},
"choice2_count": {
"type": "STRING",
"string_value": "1"
},
"choice1_count": {
"type": "STRING",
"string_value": "0"
},
"choice4_label": {
"type": "STRING",
"string_value": "YEAH!"
},
"last_updated_datetime_utc": {
"type": "STRING",
"string_value": "2018-02-28T22:36:32Z"
},
"duration_minutes": {
"type": "STRING",
"string_value": "1440"
},
"choice3_count": {
"type": "STRING",
"string_value": "0"
},
"choice4_count": {
"type": "STRING",
"string_value": "5"
},
"choice3_label": {
"type": "STRING",
"string_value": "わるい"
},
"api": {
"type": "STRING",
"string_value": "capi://passthrough/1"
},
"card_url": {
"type": "STRING",
"string_value": "https://twitter.com",
"scribe_key": "card_url"
}
},
"card_platform": {
"platform": {
"device": {
"name": "Swift",
"version": "12"
},
"audience": {
"name": "production"
}
}
}
}