LoginSignup
0
1

More than 3 years have passed since last update.

ブラウザでdiscord.js使用時のCORSエラーを回避した

Posted at

ブラウザでdiscord.jsを使ってボットを動かそうとしたらエラーになってしまったので、その原因を調べて、エラーを回避するスクリプトを実装してみました。
※一部の機能しか使えません。

ライブラリ:discord.js(webpackブランチ)
ブラウザ:Microsoft Edge
Webサーバー:Visual Studio Code + Live Server
Node.jsは使用しません。

何が問題?

discord.jsのドキュメントのExampleソースコードを実行します。
https://discord.js.org/#/docs/main/stable/topics/web

すると早速ブラウザのコンソールに次のエラーが出てしまいます。

Access to fetch at 'https://discord.com/api/v7/gateway/bot' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

原因

Discordのドキュメントによると、HTTP APIには有効なUser-Agentを送らなければならないようです。
https://discord.com/developers/docs/reference#user-agent

Clients using the HTTP API must provide a valid User Agent which specifies information about the client library and version in the following format:

User Agent Example

User-Agent: DiscordBot ($url, $versionNumber)

よって、それ以外のUser-Agentの場合は未定義です。(User-AgentによってAPIが使えなかったり使えたりしました。)
ブラウザのUser-Agentでエラーになるのはバグではなく仕様ですね。

ぶっちゃけ、ブラウザの開発者ツールでUser-Agentを設定してしまえば直ります。

エラー回避方法

discord.jsを読み込む前に次のコードを実行します。

const FETCH = fetch;

window.fetch = async (input, init) => {
  if (input === 'https://discord.com/api/v7/gateway/bot') {
    const data = {
      url: 'wss://gateway.discord.gg',
      session_start_limit: {},
    };
    const headers = {
      'Content-Type': 'application/json',
    };
    return new Response(JSON.stringify(data), { headers });
  }

  return FETCH(input, init);
};

とりあえずこれでExampleソースコードが動いてメッセージを受信できるようになりました。

使えるのは一部の機能のみで、残念ながらエラー回避できない機能があります。

メッセージの送信はWebhookを代用します。
https://discord.js.org/#/docs/main/stable/examples/webhook

解説

DiscordのAPIは2つあります。

  • HTTP API
  • Gateway (WebSocket) API

discord.jsはまずHTTP APIにアクセスして、Gateway APIのURLを受け取ってからGatewayに接続しています。
Gateway APIにはUser-Agentの仕様がないのでブラウザからもアクセス可能です。

そこで上記のソースコードで細工して、最初のHTTP APIのアクセスでアクセスせずにGatewayのURLを受け取ったことにしています。
するとGatewayには接続できるので、Gatewayを使う一部機能のみが使えます。

ちなみに、discord.jsではGateway APIのURLを/gateway/botで取得していますが、/gatewayでも取得できます。
/gatewayはブラウザからもアクセスできました。認証不要なので例外なのかもしれません。
https://discord.com/developers/docs/topics/gateway#get-gateway
なので/gatewayから取得するようにしても良いと思います。
ただしエラーも想定した方が良いでしょう。

WebhookはHTTP APIに含まれそうな気がしますが、GitHubなどの連携サービスはおそらく有効なUser-Agent(DiscordBot)を送ってこないので、連携サービスからのアクセスを受け付けるために例外なのかもしれません。

JavaScriptでUser-Agentを変更できないの?

Edgeでは変更できませんでした。

仕様としてはUser-Agentを変更でき、Firefoxでは変更できるようです。
https://developer.mozilla.org/ja/docs/Glossary/Forbidden_header_name

注: User-Agent ヘッダーは仕様としては禁止ではなくなりました (Firefox 43 で実装された forbidden header name list を参照)。 Fetch の Headers オブジェクトや、XHR の setRequestHeader() などでこのヘッダーを設定することが可能です。

ということで、消してしまったFirefoxをインストールして検証してみます。

const FETCH = fetch;

window.fetch = (input, init) => {
  init.headers['User-Agent'] = 'DiscordBot';
  return FETCH(input, init);
};

コンソールに次のエラーが出ました。

クロスオリジン要求をブロックしました: 同一生成元ポリシーにより、https://discord.com/api/v7/gateway/bot にあるリモートリソースの読み込みは拒否されます (理由: CORS プリフライト応答からのヘッダー ‘Access-Control-Allow-Headers’ によりヘッダー ‘user-agent’ が許可されていない)。

Discord APIからのレスポンスヘッダーには下記のAccess-Control-Allow-Headersがありました。

access-control-allow-headers: Content-Type, Authorization, X-Track, X-Super-Properties, X-Context-Properties, X-Failed-Requests, X-Fingerprint, X-RPC-Proxy, X-Debug-Options, x-client-trace-id, If-None-Match, X-RateLimit-Precision

確かに、User-Agentは許可されているヘッダーに含まれていませんでした。
ダメみたいですね。

FAQ

discord.jsDiscord Official API DocumentationのIssuesで見つけたブラウザ関連の質問と答えを超簡単にまとめました。

Q. [discord.js]Discord APIへのアクセスで403レスポンスが来る

https://discord.com/api/v7/gateway/botへのアクセスで常に403レスポンスが来る。
2020/11/7に廃止されるdiscordapp.comに変更するしか接続方法がない。

A. discord.jsではどうにもできません。(超要約)

Q. [Discord]APIがCORSに対応していない

A. CORSに対応しています。何らかのエラーがあるとCORSエラーになります。

Q. [Discord]ブラウザのUser-Agentを送るとエラーになる

A. User-Agentのドキュメントを参照してください。 https://discord.com/developers/docs/reference#user-agent

まとめ

私(CORS)は許そう
だがこいつ(User-Agent)が許すかな!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1