平和にTwitterライフを送れるChrome拡張

## はじめに

こんにちは、[@rs_tukki](https://twitter.com/rs_tukki)です。
これは[クソアプリ Advent Calendar 2022](https://qiita.com/advent-calendar/2022/kuso-app)の8日目の記事です。

## 去年作ったクソ拡張

[推しに貢ぎたいChrome拡張](https://qiita.com/rs_tukki/items/9de350e6276c40f54fc4)

## 概要

10/27にイーロン･マスク氏によってTwitter社が買収されたのは記憶に新しいところかと思います。
最近激動続きのTwitterですが、そんな時だからこそツイート検索によって望まない情報が目に入ってしまうことも多いのではないでしょうか。
そこで今回は、Twitter検索によって不快な情報を目に入れることなく、心の平穏を保てるようなChrome拡張を作ってみました。

## 完成品

まずはこちらをご覧ください。

![cw61k-p0h6s.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308803/d49380b0-7f14-d273-12d9-a04303955f8e.gif)

なんと言うことでしょう。
たとえどんなワードで検索しようが、そこに表示されるのは自分のツイートのみ。
これで意図しない検索結果で心をかき乱されることはありません。Twitter万歳。

## 種明かし

Twitter検索にはいくつかのオプションが付いていて、その中に`from:Id`オプションというのがあります。
これは指定したIDのアカウントによるツイートのみを検索対象とするというオプションです。

https://www.howtonote.jp/twitter/helpful/index7.html#section6

本拡張機能は、検索結果に勝手に`from:自分のID`を付与することで、自分のツイートのみを検索できる拡張機能となっています。

## 実装

コードはGithubに上げているので、良かったらこちらからどうぞ。

https://github.com/rs-tukki/TwiSearchFromMe

とは言っても、特に凝った実装をしているわけではありません。
まずはコード全体をドン。

```typescript:script.ts
'use strict';

/**
 * Redirect with from me Param.
 */
const init: (() => void) = function() {
  const newUrl = new URL(location.href);
  if (newUrl.pathname !== '/search') {
    return;
  }

  const myId = getMyId();
  var query = newUrl.searchParams.get('q')?.toString();

  if (query?.includes('from:') || myId === '') {
    return;
  }

  query = query + ` from:${myId}`;
  newUrl.searchParams.set("q", query);
  location.href = newUrl.toString();
};

const getMyId = (): string => {
  const profileLink = document.querySelector('[data-testid="AppTabBar_Profile_Link"]');
  const myId = profileLink?.getAttribute('href')?.substring(1);
  return myId ? myId : '';
};

const mutationObserver = new MutationObserver(init)
const config = {
  childList: true,
  subtree: true
}
mutationObserver.observe(document, config)
```

内容は至極単純で、「検索ワードに`from:自分のID`を追加して再検索する」という処理をしています。

### URLで処理を分岐させる

```typescript:script.ts
'use strict';

/**
 * Redirect with from me Param.
 */
const init: (() => void) = function() {
  const newUrl = new URL(location.href);
  if (newUrl.pathname !== '/search') {
    return;
  }
```

まずは現在のURLを取得する処理です。
本来は最初から検索画面のURLでだけ動作するようにしたかったのですが、検索時に上手く動作してくれなかったので仕方なくTwitterドメイン全体を対象とした上で、  
URLパスが`/search`でなければ早期returnする形としました。

### 自分のアカウントのIDを取得する

```typescript:script.ts
const getMyId = (): string => {
  const profileLink = document.querySelector('[data-testid="AppTabBar_Profile_Link"]');
  const myId = profileLink?.getAttribute('href')?.substring(1);
  return myId ? myId : '';
};
```

自分のIDはどこから取得するか迷ったのですが、プロフィールボタン(↓)のパスがそのまま`/自分のID`を差していたので、ここから取得するのが楽そうです。
![スクリーンショット 2022-12-06 233055.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308803/1cd46b7e-ae76-b634-7c18-ec6ec3aeca64.png)
Id属性も楽に取得できそうなclass属性もなかったので明らかにテスト用の属性値から引っ張ってますがそこはご愛敬。

### 検索ワードを取得する

```typescript:script.ts
  const myId = getMyId();
  var query = newUrl.searchParams.get('q')?.toString();

  if (query?.includes('from:') || myId === '') {
    return;
  }
```

検索ワードはGETパラメータで渡されているので、簡単に取得することができます。  
最終的には同じ検索URLにリダイレクトさせることになるため、無限ループを防ぐ目的でパラメータに`from:`句が含まれていればその場で処理を終了させておきます。
また、自分のIDが取れていない場合も意味がないので早期returnしておくようにします。

### 検索操作を監視してリダイレクトする

```typescript:script.ts
const mutationObserver = new MutationObserver(init)
const config = {
  childList: true,
  subtree: true
}
mutationObserver.observe(document, config)
```

こちらも本当は検索画面に遷移した後一度だけ発火するようにしたかったのですが、
単純に実行させるだけだと上手く発火させられなかったため、`検索操作の実行`を`画面要素の変更`と解釈して無理矢理全体を監視させるようなやり方にしています。
無論無駄に処理が走りまくるのは言わずもがなですが...

この実装によって、任意の言葉で検索した際に自分のツイートのみを抽出してくれるようになります。

## 本拡張機能のクソな点

* `from:`句が付いていない限り勝手に変換し続けるため通常の検索ができない
* `MutationObserver`で無駄に監視し続けてしまっている
* 自分のIDをプロフィールページのパスから取得している
* TypeScriptなのでコンパイルしないと使えない
* というかそこまで凝った実装ではないので別にTypeScriptで書く必要もない
* ~~過去ツイを漁るのにはちょっと便利かもしれない~~

本拡張機能を使って適度に自衛をしつつ、快適なTwitterライフをお過ごしください！

## あとがき

実は最初は「TwitterからMastodonへ移行するユーザが増えている」みたいなニュースを見たので**升と丼ゲーム**とか作ろうかなとか思ってたんですが時間がなくて諦めたという経緯があります...

来年はもう少し凝ったクソアプリを作りたいなと思う年の瀬でした。

## 参考

https://r17n.page/2019/08/22/js-manipulate-query-params/

https://at.sachi-web.com/blog-entry-1516.html