Twitterのシャドウバンとセンシティブ判定をチェックする拡張機能を作った

## はじめに

![logo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420120/abecf6c0-3f2b-4a99-1287-dc140135051b.png)

Twitterのシャドウバンとセンシティブ判定を検出するブラウザー拡張機能「Shadowban Scanner」をリリースしました。この記事では、Shadowban Scannerの特長を説明したあと、技術的な仕組みについて解説します。

なお、Shadowban Scannerはユーザースクリプト版（[日本語版](https://raw.githubusercontent.com/Robot-Inventor/shadowban-scanner/main/userScript/ja.user.js) / [英語版](https://raw.githubusercontent.com/Robot-Inventor/shadowban-scanner/main/userScript/en.user.js)）も存在しますが、設定を変更できないので拡張機能版がおすすめです。

- [Chrome版インストール](https://chrome.google.com/webstore/detail/enlganfikppbjhabhkkilafmkhifadjd/)
- [Firefox版インストール](https://addons.mozilla.org/firefox/addon/shadowban-scanner/)
- [ソースコード（GitHub）](https://github.com/Robot-Inventor/shadowban-scanner/)

<blockquote class="twitter-tweet" data-dnt="true" data-theme="dark"><p lang="ja" dir="ltr">Twitterのシャドウバンを検出する拡張機能「Shadowban Scanner」を公開しました！<br><br>アカウント単位のシャドウバンとツイート単位のセンシティブ判定を、外部サイトに飛ばずに確認できます。ぜひインストールしてください！<br><br>Chrome版：<a href="https://t.co/33XTpVLF8G">https://t.co/33XTpVLF8G</a><br>Firefox版：<a href="https://t.co/jNaxV8pVYw">https://t.co/jNaxV8pVYw</a> <a href="https://t.co/wV3IWAlJww">pic.twitter.com/wV3IWAlJww</a></p>&mdash; ろぼいん@一般人 (@keita_roboin) <a href="https://twitter.com/keita_roboin/status/1667365975937757185?ref_src=twsrc%5Etfw">June 10, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

Shadowban Scannerを使うと、アカウント単位のシャドウバンとツイート単位のセンシティブ判定を検出できます。シャドウバンやセンシティブ判定を検知すると、画像のように、ツイートやプロフィールにメッセージを表示します。

![screenshot2_ja.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420120/44256d76-daae-59d8-bc4b-70fb3157aa7b.png)

![screenshot1_ja.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420120/ef0c3443-2dbb-602a-48d4-6263b8a80417.png)

シャドウバンの検知については、他の多くのツールと同様に、偽陽性や偽陰性が発生することがあります。一方で、センシティブ判定についてはほとんど正確に検出できます。

## 類似のツールとの違い

Twitterのシャドウバンを検知するツールは複数ありますが、それらとShadowban Scannerの違いには次のようなものがあります。

|類似のツール|Shadowban Scanner|
|:--|:--|
|専用のサイトを開き、スクリーンネームを入力する必要がある|✅普通にTwitterを使っているだけでツイートやプロフィールに結果を表示する|
|アカウント単位のシャドウバンを検出できる|✅アカウント単位のシャドウバンに加えて、ツイート単位のセンシティブ判定を軽度・重度のどちらも検出できる|
|✅シャドウバンの他にSearch Suggestion Banも検出できる|Search Suggestion Banは検出できない|
|✅PCとスマホの両方で利用できる|PCでのみ利用できる|

ここからは、この拡張機能の仕組みなどの技術的な情報について解説します。

## シャドウバンについて

いわゆる「シャドウバン」には、Twitter公式の定義と、一般的な定義の2つがあります。

[Twitter公式の定義](https://blog.twitter.com/en_us/topics/company/2018/Setting-the-record-straight-on-shadow-banning)では、「元の投稿者に知られずに、意図的に誰かのコンテンツを投稿者以外の誰もが発見できないようにすること」とされています。一方で、一般的には「ツイートが検索結果に表示されなくなること」をシャドウバンと呼ぶことが多いです。

ここでは、後者の一般的な定義を用います。

:::note info
ときどき「Twitterはシャドウバンをしていないと主張しているのに実際にはシャドウバンをしている」と批判されることがありますが、これは定義の違いに由来するものです。Twitterは、Twitter公式の定義のシャドウバンを実施していません。

一般的な定義のシャドウバンでは、検索結果に表示されなくても、アカウントのプロフィールからツイートを閲覧できます。これは、Twitter公式の定義の「投稿者以外の**誰もが**発見できない」には該当しません。

また、シャドウバンをしていないと主張しているTwitter公式の[ブログ記事](https://blog.twitter.com/en_us/topics/company/2018/Setting-the-record-straight-on-shadow-banning)には、（Twitter公式の定義の）シャドウバンはしていないが「プロフィールに直接アクセスするなど、それらを見つけるためにさらに多くの作業が必要になる場合があります」と書かれています。

したがって、Twitterのシャドウバンをしていない、という主張は実態と矛盾しません。
:::

## シャドウバンの検出方法

TwitterのAPIにアクセスするか、WebクライアントのReact Propsを参照すると、アカウントの情報に``possibly_sensitive``というフラグの存在を確認できます。

このフラグが``true``になっている場合、シャドウバンされている可能性が高いです。たまに、``false``なのにシャドウバンされていたり、``true``なのにシャドウバンされていなかったりすることもありますが、概ねこのフラグで判断できます。

## センシティブ判定の検出方法

Twitterにはアカウント単位のシャドウバンとは別に、ツイート単位のセンシティブ判定が存在しています。

センシティブ判定には軽度のものと重度のものがあります。

軽度のものでは、閲覧者側の設定で［センシティブな内容を含むものを表示しない］がオンになっている場合に、ツイートが検索結果に表示されなくなります。重度のものでは、設定にかかわらずツイートが検索結果に表示されなくなり、年齢制限がかかります。年齢制限がかかった状態では、未成年者のアカウントや非ログイン状態で閲覧できなくなります。

ツイートの``possibly_sensitive``フラグが``true``になっており、かつ``possibly_sensitive_editable``フラグが``true``になっているのが軽度のセンシティブ判定です。重度のセンシティブ判定では、これらがそれぞれ``true``と``false``になっています。

## React Propsの取得方法

ここまでで説明したような、アカウントやツイートに関するフラグを取得するには、WebクライアントのReact Propsを参照する必要があります。

React Propsを参照する方法の他にも、Twitterの通常のAPIにアクセスする方法と、内部向けAPIにアクセスする方法があります。

しかし、通常のAPIは有料化されてしまいましたし、内部向けAPIを使うにはCookieからユーザーの認証情報を（勝手に）引っこ抜く必要があります。後者の方法は、一部の人気なTwitter向け拡張機能で実際に使われていますが、セキュリティー的にあまりよくありません。

そこで、React Propsに格納されている情報を参照するわけです。React Propsは、対象となるHTML要素の、``__reactProps$``から始まる名前のプロパティーに格納されています。変数``targetElement``に対象となるHTML要素が格納されているとき、次のコードでReact Propsを取得できます。

```javascript
const reactPropsName = Object.getOwnPropertyNames(targetElement).filter((n) => n.startsWith("__reactProps$"))[0];
const reactProps = targetElement[reactPropsName];
```

## 工夫した点

コンテンツスクリプトとページスクリプトの連携を工夫しました。

というのも、拡張機能の多言語対応や設定の読み書きのためにコンテンツスクリプトが必要ですが、React Propsはページスクリプトからでないと参照できません。この拡張機能を動作させるには、コンテンツスクリプトとページスクリプトの両方が必要であり、それらの間でデータをやり取りする必要があります。

参考：[コンテンツスクリプトからはHTML要素の一部のプロパティーにアクセスできない - Qiita](https://qiita.com/Robot-Inventor/items/34806e9a42e6789fd30f)

そこでまず、コンテンツスクリプトを使ってページにページスクリプトを挿入します。次に、ページスクリプトを使い、コンテンツスクリプトと情報のやり取りをするためのHTML要素を作成します。コンテンツスクリプトは、このHTML要素に対してカスタムイベントを監視します。ページスクリプトからコンテンツスクリプトに情報を送るときは、カスタムイベントを発火させます。

このようにすることで、コンテンツスクリプトとページスクリプトの間でデータをやり取りできます。ただし、Chromeではコンテンツスクリプトからカスタムイベントのdetailプロパティーを参照できません。

そのため、データの送受信のタイミングを知らせるためにのみカスタムイベントを使い、実際のデータのやり取りにはクラスやデータ属性を駆使する必要があります。

## まとめ

この記事では、Twitterのシャドウバンとセンシティブ判定を検出する拡張機能「Shadowban Scanner」の仕組みについて解説しました。ソースコードは[GitHub](https://github.com/Robot-Inventor/shadowban-scanner/)で公開しているので、興味のある方は覗いてみてください。

今のところ、表示されるメッセージは日本語と英語に対応していますが、言語の追加や翻訳の改善なども受け付けています。翻訳も含めて、開発に興味のある方はIssueやPull Requestなどをお待ちしています！

## 宣伝

Twitterのインプレッション数（閲覧数）を非表示にする拡張機能「Hide View Count」も作っています。こちらもぜひチェックしてみてください。

- [Chrome版](https://chrome.google.com/webstore/detail/hide-view-count/mmefeobjcchickbjelledfhagoddcklp)
- [Firefox版](https://addons.mozilla.org/firefox/addon/hide-view-count/)
- [ソースコード（GitHub）](https://github.com/Robot-Inventor/hide-view-count)

<blockquote class="twitter-tweet" data-dnt="true" data-theme="dark"><p lang="ja" dir="ltr">ツイートの左下に表示されるインプレッション数を非表示にして、承認欲求モンスターが生まれてしまうのを防ぐ拡張機能を作りました。インプレッション数の表示を一瞬たりとも許しません！<br><br>使い方は簡単。PCのChromeまたはFirefoxにインストールするだけです<br><br>Chrome版:<a href="https://t.co/ysn2JWXCEV">https://t.co/ysn2JWXCEV</a><br>(続く) <a href="https://t.co/JLhged2hpx">pic.twitter.com/JLhged2hpx</a></p>&mdash; ろぼいん@一般人 (@keita_roboin) <a href="https://twitter.com/keita_roboin/status/1606621187723132929?ref_src=twsrc%5Etfw">December 24, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>