Discord BotをPythonで！（discord.py）

#最初に
この記事はpythonでのdiscord bot制作方法をまとめたものです。

ある程度のpython基礎知識が必要です。あとasyncioについての軽い知識があると便利です。

discord.pyを利用しbotを作成していきます。
botアカウントの作成はすでにできているという前提の元進めていきます。
#discord.pyのインストール
```python
$ python3 -m pip install -U "discord.py[voice]"
```
#Botのひな形
```python
import discord


#TOKENには動かすbotのトークンを入れてください
TOKEN='***************************'

#最新のdiscord.pyを利用している人は一部の機能が制限？されているのでdiscord.Intents.allしてあげます。詳しくは調べればわかる
intents=discord.Intents.all()

#オブジェクト作成
client=discord.Client(intents=intents)

#bot起動
client.run(TOKEN)
```
#イベントとは
イベントです。特定の処理が行われるとそのイベントが実行されます。
詳しくは[参考](https://discordpy.readthedocs.io/ja/latest/api.html#event-reference)を参照してください。一覧が乗っています。
#各種イベント
##起動イベント
```python
@client.event
async def on_ready():
    print('Discord Bot Start!!!')
    #ここにbot起動時にさせたい処理を書く
```
##メッセージ送信を検知するイベント
```python
@client.event
async def on_message(message):
    #メッセージ送信者がbotだったときに処理を行わないようにする
    if message.author.bot:
        return
    #ここに処理
    return
```
- message：送信されたmessage変数

##メッセージが削除されたことを検知するイベント
```python
@client.event
async def on_message_delete(message):
    #ここに処理
    return
```
- message：削除されたメッセージ

##ボイスチャンネルのステータスが変更されたときのイベント
```python
@client.event
async def on_voice_state_update(member,before,after):
    #ここに処理
    return
```
- member：ボイスチャンネルに参加/退出したユーザーのMember変数
- before：ステータスが変更される前に入っていたボイスチャンネルのChannel変数
- after：ステータスが変更された後に入っているボイスチャンネルのChannel変数（memberが今入っているボイスチャンネル）

##ユーザーがサーバーに入ったことを検知するイベント
```python
@client.event
async def on_member_join(member):
    #ここに処理
    return
```
- member：参加したユーザーのMember変数

##ユーザーがサーバーを抜けたイベント
```python
@client.event
async def on_member_remove(member):
    #ここに処理
    return
```
- member：退出したユーザーのMember変数

##リアクション付与イベント
```python
@client.event
async def on_reaction_add(reaction, user):
    #ここに処理
    return
```
##リアクション削除イベント
```python
@client.event
async def on_reaction_remove(reaction, user):
    #ここに処理
    return
```


#応用
##コマンド
discord.pyには便利なことに『discord.ext.commands』というものがあります。ですが個人的にこれ使い勝手が悪いので自分でコマンドを実装することにします。

```python
@client.event
async def on_message(message):
    #メッセージ送信者がbotだったときに処理を行わないようにする
    if message.author.bot:
        return

    #メッセージを分割
    args=message.content.split()
    #メインコマンドの判別
    if args[0]=='!main':
        #サブコマンドの判別
        if args[1]=='sub':
            await messgae.channel.send('!main subを実行しました')
    elif args[0]=='!test2':
        await message.channnel.send('!test2'を実行しました)
```

コマンドはスペースで区切られているので、スペースで文字を分割しリストに入れてあげることで引数を参照しやすくなります。
さらにリスト化することで引数が足りない場合のエラー処理の実装が容易になります。

##定期ループ処理
Botで定期処理を行う機会というものがあるでしょう。そんなときに使えるのが『discord.ext.tasks』というものがあります。
これを利用すれば非同期処理のasyncioを使うことなく実装可能です。

```python
import discord
from discord.ext import tasks

#ひな形は省略します

@tasks.loop(seconds=30)
async def loop():
    #ここに処理

loop.start()
```
引数に時間を入れることでその時間ごとに実行される関数が出来上がります。

##async/await
非同期処理をするうえで関数はコルーチン関数というものを使います。非同期処理は非常に難しく理解しにくいのですが、大まかになんとなく分かればいいと勝手に思ってます。
###関数定義の仕方
```python
async def test():
    #ここに処理
    return
```
defの前にasyncと書くだけです。
ここで重要なポイントなんですが、非同期関数や非同期のメソッドなどを呼び出すときは先頭に```await```を付けなければいけません。

```python
#上の非同期関数を呼ぶとき
await test()

#discord.pyに出てくるsendを使う時
#message引数があるものとする
await message.author.send('test')
```
discord.pyの[api公式リファレンス](https://discordpy.readthedocs.io/ja/latest/api.html)に非同期かどうか書いてあるのでおのおの見て判断しましょう。

#最後に
この記事はずっと未完成です。色々追加していきます。