discord.pyにおけるログイン時のイベントにおける処理の追加

- 想定環境
    - OS: linux (ubuntu)
    - pythonによる開発，つまりdiscord.pyでのdicord botの作成をする
- botのアカウントがある
- botがログインできる

要するに
```python: discordbot.py
import os
import discord
from dotenv import load_dotenv
load_dotenv()


intents = discord.Intents.default()
client = discord.Client(intents=intents)
client.run(os.getenv("TOKEN"))
```

といったプログラムが正しく動けば大丈夫です．

というわけで，この記事はdiscord.pyにおけるログイン回りの処理についてまとめたものです．

# botのログ

botをログインさせるには，`client.run()`にトークンを渡せばよいです．さらに[引数にloggingのhandlerを渡すことができます](https://discordpy.readthedocs.io/ja/latest/api.html#discord.Client.run)．つまり，botを利用したときのログをloggerを使って取得できます．

ところでリファレンスには，
> この関数は、初心者がライブラリの仕組みを知ることが簡単にできるよう logging ライブラリを設定します。

とありますが，私も，この記事の対象者も初心者ですのでなんの問題もないですね(？)．

loggingの設定をする方法は[リファレンス](https://discordpy.readthedocs.io/ja/latest/logging.html#setting-up-logging)にあります．

次にloggingのhandlerについて説明したいところですが，私自身にその知識がありません．そのため，[document](https://docs.python.org/ja/3/howto/logging.html)や他の記事を検索していただければと思います．とりあえず，わかりやすそうな記事を複数置いておきます．主に私のためですね．

- [すっきり分かる Python のログ](https://github.com/pistatium/about_python_logging#%E3%81%99%E3%81%A3%E3%81%8D%E3%82%8A%E5%88%86%E3%81%8B%E3%82%8B-python-%E3%81%AE%E3%83%AD%E3%82%B0)
- [とほほのPython入門 - ロギング](https://www.tohoho-web.com/python/logging.html)
- [pythonのlogger奮闘記 ～簡単な使い方から複数ファイルを跨る使い方まで～](https://qiita.com/mimitaro/items/9fa7e054d60290d13bfc)

続いて，botがログインした際に発生するイベントについて説明します．

# ログイン時のイベント

## event

まず，そもそもevent関連で処理を追加するには，`client.event`デコレータを利用するか，`client`を継承したクラスでオーバーライドする必要があります[^bot_event]．

[^bot_event]: [イベントリファレンス](https://discordpy.readthedocs.io/ja/latest/api.html#event-reference)

デコレータは，その下に記述した関数の前後に特定の処理をさせるものです．デザインパターンのデコレータパターンだと思います(違ったらごめんなさい．~~授業で習いたての言葉を使いたくなるやつです~~)．

デコレータの方とクラスの継承の方では，クラスの継承の方が良いと思います．毎回デコレータを記述するのも大変です．また，クラスのインスタンスを生成した後に`client.run()`だけすればいい状態にできるので，別ファイルにクラスを記述しそれをimportするようにすればコードの見通しもよくなります．


というわけで，具体的にログイン時のイベントについて見ていきます．

### on_ready()

[リファレンス](https://discordpy.readthedocs.io/ja/latest/api.html#discord.on_ready)によると，

> クライアントがDiscordから受信したデータの準備を完了した際に呼び出されます。通常はログインが成功したあと、 Client.guilds とそれに関連するものの準備が完了したときです。

とあります．要するにユーザーがdiscordにログインしてサーバーのチャットが見れたりする状態になることと一緒だと思います．また，警告のところにある，_このイベントは 一度だけ呼ばれるという保証もできません_ ということについてです．これはユーザーで考えると，PCのdiscordを閉じてもタスクを終了しない限り完全にdiscordは終了しないのと一緒です．単純に閉じた後，再度開くと`on_ready()`が再度実行されるというイメージです．

この`on_ready`でよく見かける処理は，ログインしたbotのアカウント名とIDを表示させるものです．

```diff_python: discordbot.py
import os
- import discord
+ from discord import Intents, Client
from dotenv import load_dotenv
load_dotenv()


+ class MyClient(Client):
+     def __init__(self, *, intents: Intents,) -> None:
+         super().__init__(intents=intents)
+
+     async def on_ready(self):
+         print(f"login: {self.user.name} [{self.user.id}]")


intents = Intents.default()
- client = discord.Client(intents=intents)
+ client = MyClient(intents=intents)
client.run(os.getenv("TOKEN"))
```

```bash: bash
$ python3 discordbot.py 
2022-12-02 00:45:49 INFO     discord.client logging in using static token
2022-12-02 00:45:50 INFO     discord.gateway Shard ID None has connected to Gateway (Session ID: 5cd6287476ea7274931b40d4a3f7e3e5).
login: itohal_manager [1030708397714198538]
```

~~これいつ書いてるかバレますね~~

このようになります．


### setup_hook()

`on_ready()`は複数回呼ばれる可能性がありますが，ログイン時の一度しか呼ばれないイベントがこの`setup_hook()`です．初心者の私にとってはやや`on_ready()`との区別が難しいですが，一回だけ呼ばれることが必要十分である処理をここでするのが良いとのだと思います．スラッシュコマンドの同期は`setup_hook()`が良いんでしょうね．


イベントはログイン時だけでなく，もちろん他の状況でもあります．そのため処理をしたいイベントがあれば[リファレンス](https://discordpy.readthedocs.io/ja/latest/api.html#event-reference)を読んでください．

以上がdiscord.pyにおけるログイン時のイベントにおける処理の追加の説明でした．
お疲れ様でした．