discord.py入門

# 雛形
雛形とはプログラムコードを書く上で型となる部分のことを指します

discord.pyの雛形

```python
import discord
token = "" #ここにトークンを代入
client = discord.Client()

#Botの準備ができた時の処理
@client.event
async def on_ready():
    print("ready")

#ここから下にその他の処理を書く↓


# —-ここまで—-


client.run(token)

```

#イベントハンドラ
イベントハンドラとはdiscord上発生するイベントごとに処理を分けるための関数を指します。
![677FBF82-E001-466D-AAE7-4FF69604AD33.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/600026/3a44d08d-d278-271e-57d1-d7d9d07a8d50.jpeg)

イベントハンドラの記述方法は以下の通りです。

```python
@client.event
async def イベントハンドラ名(引数):
    処理
```

## 主なイベントハンドラ
| イベントハンドラ名 | 引数 | 説明 |
|:-:|:-:|:-:|
| `on_message` | message | メッセージが送信された時に発生するイベント|
| `add_reaction` | reaction,user | メッセージにリアクションが付与された時に発生する |
| `on_member_join` | member | メンバーがサーバーに参加した時に発生する。 |

**注意**
同じイベントハンドラを複数登録すると、動かなくなります。

ex)

```python
#ダメな例
@client.event
async def on_message(message):
    処理

@client.event
async def on_message(message):
    処理

```

イベントハンドラのリファレンス
[discord.py API refarence](https://discordpy.readthedocs.io/en/latest/api.html#event-reference)

#discord.pyの型

discord.pyには`str`や`int`のようにdiscord.py独自の型があります

## 主な型
### discord.Message
メッセージの情報がまとまっている、
主な属性

| 属性 | 説明 | 返り値の型 |
|:-:|:-:|:-:|
| content | メッセージの内容を返します | str|
| author | メッセージの送信者を返します |discord.User|
| channel | メッセージを送信したチャンネルを返します。| discord.TextChannel |
| id | メッセージのIDを返します。 | int |
| guild | メッセージが送信されたサーバーを返します | discord.Guild |

[Reference](https://discordpy.readthedocs.io/en/latest/api.html#message)

### discord.TextChannel
テキストチャンネルの情報がまとまっています
主な属性

| 属性 | 説明 | 返り値の型 |
|:-:|:-:|:-:|
| name | チャンネル名を返します。 | str |
| id | チャンネルのIDを返します。 | int |
| category | チャンネルの存在するカテゴリを返します | discord.CtegoryChannel |
| guild | チャンネルが存在するサーバーを返します。 | discord.Guild |
| members | チャンネルのメンバーリストを返します。 | list[discrd.Members] |

[Reference](https://discordpy.readthedocs.io/en/latest/api.html#textchannel)

###discord.Role
ロールの情報がまとまっています。

主な属性

| 属性 | 説明 | 返り値の型 |
|:-:|:-:|:-:|
| name | ロール名を返します | str |
| id | ロールのIDを返します | int |
| members | ロールが付与されているメンバーリストを返します | list[discord.Member] |
| color | ロールの色を返します | discord.Colour |
| permissions | ロールの権限を返します。 | discord.Permissions |

[Reference](https://discordpy.readthedocs.io/en/latest/api.html#role)

### discord.Member
メンバーの情報がまとまっています。

| 属性 | 説明 | 返り値の型 |
|:-:|:-:|:-:|
| name | ユーザーの名前を返します | str |
| id | ユーザーのIDを返します | int |
| avatar_url | ユーザーのアバターのURLを返します | discord.Asset |
| roles | ユーザーが所持しているロールリストを返します | list[discord.Role] |
| guild | メンバーが存在してるサーバーを返します | discord.Guild |
| status | メンバーのステータスを返します。 | discord.Status |

[Reference](https://discordpy.readthedocs.io/en/latest/api.html#member)

#discordを操作
それぞれの型には`属性(Attribute)`だけではなく、メッセージを送ったり、ロール付与を行うための`メソッド`があります。
以下からはメソッドの説明と、よく使うメソッドを挙げていきます。

## awaitについて
`discord.TextChannel` の型にはメッセージを送信するメソッドである`send()`があります。
また、`discord.Guild`  の型にはチャンネルを取得するメソッドである`get_channel(チャンネルのID)`があります。

一応それぞれのメソッドの概要をまとめておきます

| メソッドを持つ型 | メソッド | メソッドの引数 | メソッドの返り値 |
|:-:|:-:|:-:|:-:|
| discord.TextChannel | send | content(str)... | discord.Message |
| discord.Guild | get_channel | チャンネルのID(int) | abc.GuildChannel |


```python
@client.event
async def on_message(message):
    send_message = "メッセージを受け取りました。" #メッセージ内容
    guild = message.guild #メッセージが送信されたサーバー
    channel = guild.get_channel(00000000000) #チャンネルを取得
    await send_channel.send(send_message)#メッセージを送信

```

6行目で使用した `send()` メソッドには`await`がついています。
一方5行目で使用した`get_channel`メソッドには`await`はついていません。

ここからはこのawaitについて解説していきますが、
難しければ、多少語弊はあるかもしれないですが、discord上で目に見える形で操作をする
`ex( メッセージを送信,リアクションをつける,チャンネルを作る`

ものには`await`が必要であると思ってください。




逆に、チャンネルを取得したり、ユーザーがフレンドか確認するといった,
情報を取得するような処理をするmethodには`await`がつかないと解釈してください

##awaitの解説

awaitは実行する関数がコルーチンである際に先頭につけるものです
これをasync/await構文と呼びます。

async が付く関数はコルーチン関数と呼ばれます、

### コルーチン関数とは何か？
通常の関数は同期処理として実行されます。
つまり、関数Aを実行中に、関数Bを実行した場合、関数Aの処理は途中で中断され、関数Bの処理が始まります。関数Bが終了次第、関数Aの処理は再開します。
![D06E1A4F-22BD-47E9-A2D7-F7F76502950A.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/600026/801ff5d8-452e-e97f-bc86-246a6f670f6b.jpeg)


ですが、コルーチン関数は非同期処理として実行され、
別の処理と並行してタスクを実行できます。
![CFD37752-AC43-4E8C-9DA5-9001EF27737F.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/600026/cddfa8ab-3a4f-b707-a1c4-87b0e5e18aed.jpeg)





```python
import time

def wait_A():
    print("処理Aを開始")
    time.sleep(1)
    print("処理Aを終了")

def wait_B():
    print("処理Bを開始")
    time.sleep(2)
    print("処理Bを終了")

def wait_C():
    print("処理Cを開始")
    time.sleep(3)
    print("処理Cを終了")

if __name__ == "__main__":
    wait_A()
    wait_B()
    wait_C()
```
こちらのプログラムを実行します。

```
~出力~
処理Aを開始
処理Aを終了
処理Bを開始
処理Bを終了
処理Cを開始
処理Cを終了
```

となります。

これをコルーチン関数として定義します

```python
import asyncio
async def wait_A():
    print("処理Aを開始")
    asyncio.sleep(3)
    print("処理Aを終了")

async def wait_B():
    print("処理Bを開始")
    asyncio.sleep(2)
    print("処理Bを終了")

async def wait_C():
    print("処理Cを開始")
    asyncio.sleep(1)
    print("処理Cを終了")

if __name__ == "__main__":
    await wait_A()
    await wait_B()
    await wait_C()

```

```
出力
処理Aを開始
処理Bを開始
処理Cを開始
処理Cを終了
処理Bを終了
処理Aを終了

```

となります。

複数の処理を並行して実行していることがわかるでしょう。

つまり、discord.pyのモジュールにおいて、
型のメソッドがコルーチン関数として定義されていた場合に`await`をつける必要があります。

型のメソッドが、コルーチン関数として定義されているか、されていないかの確認をするには[discord.pyのドキュメント](https://discordpy.readthedocs.io/en/latest/api.html)を確認する必要があり、
![BD2BC61E-B3D0-4CEC-A665-494593BE6A3B.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/600026/72247376-1f8c-4f21-2295-c58752f43ccf.png)

各型の説明の先頭にMethodの項目があります。
その一覧の先頭に`async`がついているものはコルーチン関数メソッドです。

# 色々なメッセージを送る

##埋め込みメッセージ

```python
@client.event
async def on_message(message):
    if message.content == "/埋め込み":
        send_embed = discord.Embed(title="埋め込みのタイトル",description="埋め込みの説明",colour=discord.Colour.red())
        send_embed.add_field(name="フィールドの名前",value="フィールドの説明書き")
        await message.channel.send(embed=embed)
```

discord.Embedはdiscord.pyのデータクラスです。
4行目でデータクラスのインスタンス化を行い、その際に、タイトル,説明,色を指定できます。
`add_field`はEmbedデータクラスのメソッドでコルーチン関数ではありません。
`add_field`には`name`と`value`の引数の他に,`inline`という引数があり、これにはbool値を指定できます。`inline`のデフォルト値は`True`で、
他のフィールドと横に並列して表示させます。
`inline=False`にした場合
フィールドを縦に表示させます。


###画像を送信する。

```py
await message.channel.send(File=discord.File("sample.png"))
```
sendの引数にはFileを指定することができ、
Fileの引数はdiscord.Fileのデータクラスを指定します。
Fileの引数をインスタンス化する際に直接,ファイルのパスを文字列型で指定することで画像を送信することができます。

1/8更新済み