"Xのだるま"を「↑↑↓↓←→←→BA」せずに1クリックで回すChrome拡張を作ってみた #コナミコマンド

メリークリスマス :) **Xでダルマが回せる** のが[話題](https://www.itmedia.co.jp/news/articles/2412/24/news188.html)ですね。　（なぜダルマなのか。。。）

X（旧Twitter）で、コナミコマンド（上上下下左右左右BA）を入力すると、サイト上部のロゴが回転する機能が（この冬限定かな？）実装されています。これを自動的に実行できるChrome拡張機能を作ってみました。 **12/25に知って12/25に作ってるのでかなり勢い** ですね。

先に作ったものを載せておきます。

https://x.com/n0bisuke/status/1871843200392171994

"身の回りの困りごとを楽しく解決アドベントカレンダー"の記事です。

https://qiita.com/advent-calendar/2024/works-hi-1

## コナミコマンドでダルマとXロゴを回せる

まず基本ですが、"上上下下左右左右BA（↑↑↓↓←→←→BA）"をXの画面上で入力するとダルマが回転します。

https://www.itmedia.co.jp/news/articles/2412/24/news188.html

こんな感じで、コナミコマンドをうまく入力できるとダルマが...!

> ![](https://i.gyazo.com/c27785b0521abdd61e34663c74cddd80.gif)

### コナミコマンド?

ギネスにも載ってるらしい有名な裏技コマンドです。

https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%8A%E3%83%9F%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89

色々なサイトにギミックとして実装されてるっぽいです。PHP.netで試したら実装されてましたね。

https://dxo.co.jp/blog/archives/6626

"クレヨンしんちゃん 雲黒斎の野望"のラストのロボットバトルで似たようなギミックありましたね（世代）

## コナミコマンド難しいので半自動化

さて本題。

コナミコマンド結構難しいなと思ってまして、 **ゆっくり入力すぎるとコマンドとして認識されない** です。あと **上上下下だっけ下下上上だっけ、、、？と間違えることも多々** あります。

[困りごと解決アドベントカレンダー](https://qiita.com/advent-calendar/2024/works-hi-1)の記事ということで解決を試みます。

> ![](https://i.gyazo.com/7cbcb6a5daa3d4395a4bcbbdaf3ec3ae.gif)
> テストバージョン、ボタンを押すと回る。ここまで行くのもちょっとハマりました。

## 作ってみたログ

Chrome拡張機能でおそらくキー入力のエミュレートいけるだろうと思い作ってみました。

感情コードは記事の最後の方にGitHubに挙げておきます。

### 1. まずはキー入力をエミュレート

最初は単純なキーボードイベントのエミュレートからスタート

```js
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
```

しかし、これだけでは動作せず。

### 2. コナミコマンドのシーケンスを実装

次に、全てのキー入力を順番に実行するように改良：

```js
['ArrowUp','ArrowUp','ArrowDown','ArrowDown','ArrowLeft','ArrowRight','ArrowLeft','ArrowRight','b','a'].forEach((key, i) => setTimeout(() => document.dispatchEvent(new KeyboardEvent('keydown', {key})), i * 100));
```

この段階でも、まだうまく動作しません。

### 3. イベントの種類を増やす

割と全然うごかんなーと悩んでましたけど、キーボードイベントには keydown、keypress、keyup の3種類があることに気づき、全てのイベントを送信するように修正してみました。

割と諦めかけ（そこまで時間かけたわけではないけどこのネタにどこまで時間かけるのか問題）

```js
document.documentElement.dispatchEvent(createKeyEvent('keydown', key));
document.documentElement.dispatchEvent(createKeyEvent('keypress', key));
document.documentElement.dispatchEvent(createKeyEvent('keyup', key));
```

> この段階で一旦動きました。
> ![](https://i.gyazo.com/7cbcb6a5daa3d4395a4bcbbdaf3ec3ae.gif)

### 4. 複数回の回転に挑戦

1回の回転には成功したので、次は連続して3回回転させることに挑戦。しかし、ここでも問題が発生

- 2周目の途中でリセットされる
- アニメーションが重なって不自然
- 回転が途中で止まる

2周目いくのときの挙動が割と不安定だった感じです。

### 5. タイミングの最適化

だるまの要素を見たら回転しているときにclassが付与されてアニメーション指定がありました。

> ![スクリーンショット 2024-12-25 19.44.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/c54d9156-061c-1b12-5990-dad69d4378cb.png)

CSSを確認すると、`transform 1s`でアニメーション時間が1秒（1000ms）に設定されていることが判明。試行錯誤の結果、1024msのインターバルを設けることで滑らかな連続回転を実現できました。

### ということで一旦の完成コード 

https://github.com/n0bisuke/konamicommand 

動かんな、、、原因なんだろう、、、という試行錯誤はありましたが結果コード自体は95%くらいClaudeに書いてもらいました。

```js
document.addEventListener('DOMContentLoaded', () => {
  document.getElementById('konami').addEventListener('click', async () => {
    const rotations = parseInt(document.getElementById('rotations').value) || 3;
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    chrome.scripting.executeScript({
      target: { tabId: tab.id },
      args: [rotations],
      func: (totalRotations) => {
        const sequence = [
          'ArrowUp', 'ArrowUp',
          'ArrowDown', 'ArrowDown',
          'ArrowLeft', 'ArrowRight',
          'ArrowLeft', 'ArrowRight',
          'b', 'a'
        ];
        
        function createKeyEvent(type, key) {
          return new KeyboardEvent(type, {
            key: key,
            code: key.startsWith('Arrow') ? key : `Key${key.toUpperCase()}`,
            keyCode: key === 'a' ? 65 : key === 'b' ? 66 : 
                    key === 'ArrowUp' ? 38 : key === 'ArrowDown' ? 40 : 
                    key === 'ArrowLeft' ? 37 : key === 'ArrowRight' ? 39 : 0,
            bubbles: true,
            cancelable: true,
            view: window
          });
        }

        function runSequence(round) {
          if (round >= totalRotations) return;
          
          let currentKey = 0;
          function emitKey() {
            if (currentKey >= sequence.length) {
              console.log(`シーケンス ${round + 1}/${totalRotations} 完了`);
              setTimeout(() => runSequence(round + 1), 1024);
              return;
            }

            const key = sequence[currentKey];
            console.log(`ラウンド ${round + 1}/${totalRotations}: キー入力: ${key} (${currentKey + 1}/${sequence.length})`);

            document.documentElement.dispatchEvent(createKeyEvent('keydown', key));
            document.documentElement.dispatchEvent(createKeyEvent('keypress', key));
            document.documentElement.dispatchEvent(createKeyEvent('keyup', key));

            currentKey++;
            setTimeout(emitKey, 1);
          }

          emitKey();
        }

        runSequence(0);
      }
    });
  });
});
```

ログにも出しつつデバッグしてました。

> ![](https://i.gyazo.com/7f570b973a50edd34a5c6c362fca0f1f.gif)

## まとめ

**コナミコマンドをエミュレートすることで誰でもXのダルマを回せる拡張機能を作りました。**

ストアにも申請してみたので公開されたらリンク貼ります。リジェクトされないといいな。

個人的な身近な困りごと（コナミコマンド難しい）、解決できました★