【Discord.py】Discord.py 1.5で追加されたIntentsを攻略する

#はじめに
###投稿者の自己紹介
どうもまぬおです！discord.pyでBotを開発しています。よろしくお願いします

###お知らせ
[Discord Bot Advent Calendar 2020](https://qiita.com/advent-calendar/2020/discordbot)
この記事はDiscord Bot Advent Calendarの1日目の記事です。ほかの方の投稿もぜひご覧になってください

###今回のテーマ
on_member_update()イベントを利用する際に、1.5で追加されたIntentsを設定する必要があったのでその経緯と解決について書きます
また、この記事はある程度Botを開発したことがある**中級者向け**の記事になっています。Botの開発を始める方法などは書きませんので注意してください
初心者の方には、別の方が書いていただいた初心者向けの記事があります
ボクもこの記事からDiscord Bot開発を勉強し始めましたのでおすすめです！

[PythonでDiscordBotを書く方法](https://qiita.com/1ntegrale9/items/9d570ef8175cf178468f)
##問題が起きた

###ある日
メンバーに特定の役職が付与された時に、メンバーのニックネームを変更するBotを開発していました
役職の付与は[Zira](https://top.gg/bot/275813801792634880)を使用しており、メンバーの役職が変更されたタイミングでニックネームを変更することができれば、仕様が満たされると考えました

###on_member_update()を使う
on_member_update()は、メンバーの情報が更新されたときに呼び出されるイベントです

[on_member_update()のAPIリファレンス](https://discordpy.readthedocs.io/ja/latest/api.html#discord.on_member_update)

>Member がプロフィールを編集したとき呼び出されます。
>これらのうちひとつ以上が変更されたとき呼び出されます

> * ステータス
> * activity
> * ニックネーム
> * roles

これらのうち、rolesの変更がされたときにニックネームの設定をしようと思いました

###書いたコード
これを踏まえて、イベントが呼ばれたらニックネームを変更するコードを書いてみます

~~~python3
import discord

TOKEN = "TOKEN"
ROLEID = ロールのID

client = discord.Client()

@client.event
async def on_member_update(before, after):
    Guild = before.guild
    Role = Guild.get_role(ROLEID)
    NickName = "ニックネーム"

    if not Role in before.roles and Role in after.roles:
        await after.edit(nick = NickName)

    if not Role in after.roles and Role in before.roles:
        await before.edit(nick = None) #Noneを代入することで初期化できます

client.run(TOKEN)
~~~

問題ないように見えますよね？
文法も間違っていませんし、実行してみるとエラーも出ません
しかし、このコードではこのイベントに入ることすらないのです
イベントの一番最初にprint()を書いてみましたが、出力はありませんでした

##原因

###何かが足りない
なにか見落としていると思い[API](https://discordpy.readthedocs.io/ja/latest/api.html#discord.on_member_update)を見直してみたところ、こんな一文が

> This requires Intents.members to be enabled.

明らかにこれが足りなさそう
でも、**Intents**とは何かを知りませんでした
以下[リファレンス](https://discordpy.readthedocs.io/ja/latest/api.html#intents)の引用です

>Wraps up a Discord gateway intent flag.
>Similar to Permissions, the properties provided are two way. You can set and retrieve individual bits using the properties as if they were regular bools.
>To construct an object you can pass keyword arguments denoting the flags to enable or disable.
>This is used to disable certain gateway features that are unnecessary to run your bot. To make use of this, it is passed to the intents keyword argument of Client.
>バージョン 1.5 で追加.

どうやら、最新のver.1.5で追加された要素のようです
このIntentsを設定しないとmemberの更新が検知できないようですね
[Intents.members](https://discordpy.readthedocs.io/ja/latest/api.html#discord.Intents.members)の項目の説明はこうなっています

> members
Whether guild member related events are enabled.
This corresponds to the following events:

>on_member_update() (nickname, roles)

Intentsのmembersの項目を有効にしたら行けそうです

###Intentsを有効にしたコード

~~~python3
import discord

TOKEN = "TOKEN"
ROLEID = ロールのID

Intents = discord.Intents.default()
Intents.members = True
client = discord.Client(intents=Intents)

# client = discord.Client()

@client.event
async def on_member_update(before, after):
    Guild = before.guild
    Role = Guild.get_role(ROLEID)
    NickName = "ニックネーム"

    if not Role in before.roles and Role in after.roles:
        await after.edit(nick = NickName)

    if not Role in after.roles and Role in before.roles:
        await before.edit(nick = None) #Noneを代入することで初期化できます

client.run(TOKEN)
~~~
これで完璧でしょ！
...
と思いますよね？
しかし、まだ落とし穴があります
Intentsを設定することで実際にこのイベントを実行することはできていますが、こういうエラーが出ます
`discord.errors.PrivilegedIntentsRequired: Shard ID None is requesting privileged intents that have not been explicitly enabled in the developer portal. `
つまり、Intentsを利用するには[developer portal](https://discord.com/developers/applications)で有効化する必要があるということです

##本当に解決

###Developer Portalで有効化する
そうと決まれば有効化です
Botの項目の**SERVER MEMBERS INTENT**を有効化します
![スクリーンショット 2020-12-01 031459.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/ada897fa-a43b-7d61-e0bf-4a4620130a1e.png)
ここを設定することで、特定のロールを付与した時にニックネームを変更する仕様を実装することができました
![result.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527954/ab1d315c-a91b-f5e8-9505-afcc85a35bf9.gif)
##おわりに
###Intentsが必要なイベントがまだまだある
今回はon_member_update()イベントでIntentsが必要でしたが、APIを見る限りまだまだIntentsの設定が必要なイベントはありそうです
もしイベントが反応しない場合に遭遇したら、Intentsを見直してみるとよいかもしれません
###おわりのあいさつ
ここまで読んでいただき、ありがとうございました
この記事がみなさんの開発に役立つと幸いです
それでは良いDiscord Bot開発ライフを！
###各種バージョン
Python3 : 3.6.9
Discord.py : 1.5.1