discordpyを仮想環境(pipenv)で使う

## はじめに
discord.py と discord bot の基本的なことに関してはこちらをお読みください。
[Pythonで実用Discord bot(discord.py解説) - Qiita](https://qiita.com/1ntegrale9/items/9d570ef8175cf178468f)

pythonおよびdiscord.pyのバージョンを自由に切り替えたいという場合に、
「仮想環境」というものが便利です。
本記事ではその方法の1つ、pipenvの使い方を解説します。

## 動作環境

- Python 3.9.5
- pip 21.1.2
- pipenv 2021.5.29
- discord.py 1.7.3


## Pipenv のインストール

[Pipenv: 人間のためのPython開発ワークフロー — pipenv ドキュメント](https://pipenv-ja.readthedocs.io/ja/translate-ja/)

こちらはグローバル環境にインストールします。

```shell:pipenvのインストール
python3 -m pip install -U pipenv
```

`3` の部分は必要であれば適宜 `3.9` などに置き換えてください。

また、homebrewでもインストールが可能です。

```shell:homebrewでのpipenvのインストール
brew install pipenv
```

## 仮想環境の作成

こちらは仮想環境を利用したいディレクトリ内で行います。
（仮想環境はディレクトリ単位で利用します）

まずはディレクトリ直下で以下のコマンドを打って仮想環境を作成しましょう。
もし requirements.txt があれば、ライブラリのインストールも合わせて行われます。

```shell:仮想環境の作成
pipenv install
```

仮想環境を作成すると、ライブラリやバージョン情報などが記述された
`Pipfile` `Pipfile.lock` という2つのファイルが作成されます。
これらのファイルに関して中身などを気にする必要はあまり生じることはありません。

必要であれば利用するpythonのバージョンも指定しましょう。

```shell:pythonのバージョン指定
pipenv --python 3.9.5
```

実は以下に示す仮想環境へのライブラリのインストールを行うことで、
仮想環境の作成も合わせて行うことができるので、
特に必要がなければこれらの初期設定は不要です。

## discord.py を仮想環境にインストール

仮想環境を利用するディレクトリ直下で行ってください。

```shell:discord.pyのインストール
pipenv install discord.py
```

仮想環境でインストールされているライブラリは以下で確認することができます。

```shell:仮想環境のライブラリの確認
pipenv graph
```

```shell:pipenv_graphの例
$ pipenv graph
discord.py==1.7.3
  - aiohttp [required: >=3.6.0,<3.8.0, installed: 3.7.4.post0]
    - async-timeout [required: >=3.0,<4.0, installed: 3.0.1]
    - attrs [required: >=17.3.0, installed: 21.2.0]
    - chardet [required: >=2.0,<5.0, installed: 4.0.0]
    - multidict [required: >=4.5,<7.0, installed: 5.1.0]
    - typing-extensions [required: >=3.6.5, installed: 3.10.0.0]
    - yarl [required: >=1.0,<2.0, installed: 1.6.3]
      - idna [required: >=2.0, installed: 3.2]
      - multidict [required: >=4.0, installed: 5.1.0]
```


## botの作成と起動

以下は [公式のサンプルコード](https://discordpy.readthedocs.io/en/rewrite/intro.html#basic-concepts) を少し拡張したものです。
環境変数を追加するかコードを変更するかのどちらかが必要です。

```python:discordbot.py
import traceback
import discord
import os

client = discord.Client()

# 環境変数からBOTのTOKENと自分のユーザIDを取得
TOKEN = os.environ.get('DISCORD_BOT_TOKEN')


async def send2developer(text):
    """ 開発者にDMを送る """
    appinfo = await self.bot.application_info()
    await appinfo.owner.send(text)


@client.event
async def on_ready():
    """ 起動時のイベントハンドラ """
    text = f'Logged on as {client.user}!'
    await send2developer(text)


@client.event
async def on_message(message):
    """ メッセージ受信時のイベントハンドラ """
    try:
        if message.author != client.user:  # bot自身の発言には反応しない
            text = 'Message from {0.author}: {0.content}'.format(message)
            await send2developer(text)
    except Exception:  # エラー発生時にはトレースバックがDMで送られてくる
        await send2developer(traceback.format_exc())


client.run(TOKEN)
```

作成した仮想環境(Pipenv)にて上記のプログラムを以下のように実行します、

```shell:botの起動
pipenv run python discordbot.py
```

ログイン情報がDMで送られてきたら無事成功です。

## 関連リンク
- [discord.py documentation](https://discordpy.readthedocs.io/en/stable/)
- [discord.py 公式サーバー](https://discordapp.com/invite/r3sSKJJ)
- [Discord API 公式サーバー](https://discordapp.com/invite/discord-api)
- [Python.jp Discordサーバ案内](https://www.python.jp/pages/pythonjp_discord.html)