自分のMisskeyサーバーにWebSocketで接続できないときの解決方法

Cloudflare Tunnelを使用して動かしているMisskeyサーバーに対しWebSocketで接続できず詰まったので解決方法を書いておきます。

# 起きたこと

[brominecore](https://pypi.org/project/brominecore/)に記載しているサンプルコードにおいて`INSTANCE`を自分のMisskeyサーバーのインスタンス名に設定したところ以下のエラーが出ました。

```
raceback (most recent call last):
  File "/tmp/test.py", line 33, in <module>
    asyncio.run(brm.main())
  File "/usr/local/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/local/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/usr/local/lib/python3.10/site-packages/brcore/core.py", line 126, in main
    await asyncio.create_task(self.__runner(backgrounds))
  File "/usr/local/lib/python3.10/site-packages/brcore/core.py", line 209, in __runner
    raise e
  File "/usr/local/lib/python3.10/site-packages/brcore/core.py", line 144, in __runner
    async with websockets.connect(self.__WS_URL) as ws:
  File "/usr/local/lib/python3.10/site-packages/websockets/asyncio/client.py", line 587, in __aenter__
    return await self
  File "/usr/local/lib/python3.10/site-packages/websockets/asyncio/client.py", line 543, in __await_impl__
    await self.connection.handshake(
  File "/usr/local/lib/python3.10/site-packages/websockets/asyncio/client.py", line 114, in handshake
    raise self.protocol.handshake_exc
  File "/usr/local/lib/python3.10/site-packages/websockets/client.py", line 325, in parse
    self.process_response(response)
  File "/usr/local/lib/python3.10/site-packages/websockets/client.py", line 142, in process_response
    raise InvalidStatus(response)
websockets.exceptions.InvalidStatus: server rejected WebSocket connection: HTTP 403
```

HTTP APIは使用できており、かつWebSocketポートがブロックされていないことも確認できていました。

そこで、以下のコードを挿入しデバッグモードでWebSocketの接続を詳細に確認することにしました。

```py
import logging
# WebSocketのデバッグログを有効化
logging.basicConfig(level=logging.DEBUG)
```

挿入後のコード全文は以下のとおりです。

<details>

```python
import asyncio
from brcore import Bromine, enum
import logging
# WebSocketのデバッグログを有効化
logging.basicConfig(level=logging.DEBUG)

INSTANCE = "jiskey.dev"
TL = enum.MisskeyChannelNames.GLOBAL_TIMELINE


def note_printer(note: dict) -> None:
    """ノートの情報を受け取って表示する関数"""
    NOBASIBOU_LENGTH = 20
    user = note["user"]
    username = user["name"] if user["name"] is not None else user["username"]
    print("-"*NOBASIBOU_LENGTH)
    if note.get("renoteId") and note["text"] is None:
        # リノートのときはリノート先だけ書く
        print(f"{username}がリノート")
        note_printer(note["renote"])
        # リノートはリアクション数とか書きたくないので
        # ここで返す
        print("-"*NOBASIBOU_LENGTH)
        return
    else:
        # 普通のノート
        print(f"{username}がノート ノートid: {note['id']}")
    if note.get("reply"):
        # リプライがある場合
        print("リプライ:")
        note_printer(note["reply"])
    if note.get("text"):
        print("テキスト:")
        print(note["text"])
    if note.get("renoteId"):
        # 引用
        print("引用:")
        note_printer(note["renote"])
    if len(note["files"]) != 0:
        # ファイルがある時
        print(f"ファイル数: {len(note['files'])}")
    # リアクションとかを書く
    print(f"リプライ数: {note['repliesCount']}, リノート数: {note['renoteCount']}, リアクション数: {note['reactionCount']}")
    reactions = []
    for reactionid, val in note["reactions"].items():
        if reactionid[-3:] == "@.:":
            # ローカルのカスタム絵文字のidはへんなのついてるので
            # それを消す
            reactionid = reactionid[:-3] + ":"
        reactions.append(f"({reactionid}, {val})")
    if len(reactions) != 0:
        print("リアクション達: ", ", ".join(reactions))
    print("-"*NOBASIBOU_LENGTH)


async def note_async(note: dict) -> None:
    """上のprinterの引数を調整するやつ

    asyncにするのはws_connectでは非同期関数が求められるので(見た目非同期っていう体にしているだけ)"""
    note_printer(note["body"])
    print()  # 空白をノート後に入れておく


async def main() -> None:
    brm = Bromine(instance=INSTANCE)
    brm.ws_connect(TL, note_async)
    print("start...")
    await brm.main()


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("fin")

```
</details>

結果以下のエラーが発生しており、Cloudflareでブロックしていることが確認できました。

```
<title>Attention Required! | Cloudflare</title>
<h1>Sorry, you have been blocked</h1>
<h2>You are unable to access jiskey.dev</h2>
```

# 解決方法

Cloudflareのダッシュボードにおいて「セキュリティ」->「ボットセキュリティ」にアクセスしボット設定を確認したところ、「AI ボットをブロック」が有効になっていたため、これを無効化したところWebSocketで接続できるようになりました。

![スクリーンショット 2025-03-13 21.19.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692201/59f02214-d28a-483f-a3b3-3c53cb341dee.png)