猿でもわかる！discord.pyのIntentsについて！

※筆者も完全に理解してるわけではないので間違った情報が掲載されている可能性があることをご了承ください
記事の作成にあたり、公式ドキュメントの日本語訳をapple502j#6852様よりご提供頂きました。この場を借りて感謝申し上げます。

# Intentsとは？
Intentsはdiscord.py1.5の新機能です。
これを用いると、「一部のイベントを受け取り、一部のイベントは受け取らない」という選択ができ、
通信量やメモリの使用量を削減することができます。
※どの程度削減できるのかは未検証です

# How To Use
`Client`や`Bot`のオブジェクト生成時に引数として渡すことで適用されます。
ただし、**一部のIntent(Privileged Intents)には制限がかかっています**
これを有効にするには、コードへの記述に加え[Developer Portal](https://discord.com/developers/applications/ )へ行き、アプリケーションを選択してからBotのタブ(トークンを入手/リセットする場所)で手動で有効化する必要があります。
画像にある２つのボタンをクリックして有効化してください。(画像では既に有効化されています)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510912/229c1a32-ec95-48fe-e95d-27b5af6e33ad.png)

Privileged Intentsについての説明をdiscord.py 公式Discordサーバー #ニュース　より引用します。
> - Presence Intent(上): `Member.status`、`Member.activity`、`on_member_update`(statusとactivityに限る)を受け取るのに必要です。設定時は`presences=True`
- Server Members Intent(下): `on_member`で始まるイベントと、`on_user_update`を受け取るのに必要です。また、`Guild.get_member`などにも必要です。設定時は`members=True`

> 注意: 100サーバー以上に所属するボットでは、Privileged Intentsの使用はDiscord側の認証を得ないとできません。もしボットがすでに認証済みの場合は、Discordに連絡してください。


上記の設定が出来たら実際に使ってみましょう！
`on_typing`はあまり使われる機会がないので、`on_typing`を受け取らない場合の例を示します。

```py:main.py
import discord
intents = discord.Intents.default()  # デフォルトのIntentsオブジェクトを生成
intents.typing = False  # typingを受け取らないように
client = discord.Client(intents=intents)
# discord.extを用いる場合
# from discord.ext import commands
# bot = commands.Bot(command_prefix="/", intents=intents)
# or
# super().__init__(command_prefix="/", intents=intents)
```

2行目の`discord.Intents.default()`でデフォルトのIntentsオブジェクトを生成しています。
デフォルトでは`members`と`presences`が`False`でそれ以外は`True`に設定されています。
※デフォルトではメンバーの一覧がキャッシュされなくなるため、いくつかの属性やメソッドが使用不可となります。その場合は前述したDeveloper Portalでの設定は不要です。

`default()`以外には`all()`ですべてを`True`の、`none()`ですべてが`False`のIntentsオブジェクトを生成するメソッドがあります。
手動で`True` `False`を設定する際の、Intentsの属性については`discord.Intents`の[リファレンス](https://discordpy.readthedocs.io/ja/latest/api.html#discord.Intents )を参照してください。

## はいはい質問です！どのIntentsを使えばいいんですか？
人によりますが、小規模なbotの場合(従来のコードをそのまま使い続け、かつサーバー数が100未満の場合)はすべてのIntentを`True`にする`intents=discord.Intents.all()`で問題有りません！

上記の方法で殆どのユーザーは問題無いのですが、大規模なbotを運営しているユーザーは個別相談が必要だと思います。

# on_ready()がなかなか発火しません！どうしてですか？
Discord APIの変更により、メンバーの読み込みにも変更が加わりました。以前は75サーバー同時にリクエストでき、`Guild.large`が`True`である(=メンバーが250以上である)サーバーのみでよかったのが、現在はすべてのサーバーで必要になり、さらに1リクエストあたり1サーバーのみリクエスト出来るようになりました。
これにより、約75倍の速度低下が発生しています。

以下、公式ドキュメントの速度検証を引用します。
> 例: 840サーバー(うち95サーバーが250人以上所属(`Guild.large` == True))に所属するボット。

> 現在: 約60秒(75サーバー、20サーバー)
`Intents.members == True and Intents.presences == False`: 約7分 (840リクエスト、速度制限は120サーバー/分)
`Intents.members == True and Intents.presences == True`: 約100秒 (95リクエスト)

 [公式ドキュメント](https://discordpy.readthedocs.io/ja/latest/intents.html#why-does-on-ready-take-so-long-to-fire )にも書かれている解決方法を紹介します。

## リクエストの仕組みを戻す
まず、PresencesとServer Membersの両方のPrivileged Intentsを有効にする方法があります。これで、リクエストの仕組みは一部以前のものと同じとなるため、起動速度も以前と同じになります。
※前述したように100サーバー以上に所属するbotの場合はDiscord側の認証を得る必要があります。

## メンバーの読み込みをずらす
次に、`Client`や`Bot`の`chunk_guilds_at_startup`引数を`False`にすると、開始時にメンバーを読み込まなくなるため、起動が速くなります。
その後は(必要に応じて)`Guild.chunk`などを用いて、サーバーのメンバーを取得してください。
他の取得方法は[リファレンス](https://discordpy.readthedocs.io/ja/latest/intents.html#retrieving-members )に記載があります。

# この仕様が気に入りません！どうすればいいですか？
現時点では、古いAPIはまだ使用できるため、discord.py 1.4がv6ゲートウェイのサポート終了まで使用できますが、botの将来性のためにもコードを新しい方法にアップデートすることが得策です。
ダウングレードには

```sh
# Windows
py -3 -m pip install -U discord.py>=1.4, 1.5
# Linux Mac
python3 -m pip install -U "discord.py>=1.4,<1.5"
```

をターミナルで実行することで可能です。
しかし、v6ゲートウェイのサポート終了日時が不明なため、コードを更新することを推奨します。

重要: **10月7日以降は、discord.pyのバージョンに関わらず(使う場合は)Developer Portalでの設定が必要になります。**

Discord APIの変更や方向性を本当に嫌うなら[サポート](https://support.discord.com/hc/ja/requests/new )に連絡することも出来ます。


# 要約
Intentsを設定することで受け取るイベントを制限することが出来る。
殆どのユーザーは`intents=discord.Intents.all()`と設定し、Developer Portalで両方のIntentにチェックを入れればいい。