PythonでDiscordBotを書く方法

この記事は[Boostlog](https://boostlog.io/@junp1234/how-to-write-a-discord-bot-in-python-5a8e73aca7e5b7008ae1da8b)の翻訳記事です。

このチュートリアルでは、Pythonで簡単な[Discord](https://discordapp.com/)Botを作成する方法を学びます。
Discordは、ゲーマーがターゲットのSkypeのようなサービスです。

Discordでは、複数のサーバーに参加することができます。
これらのサーバーには多くのbotがあることに気がつきました。
これらのbotは、音楽を演奏したり、簡単なチャットのモデレートまで、多くのことを行うことができます。
そんなbotに魅了されたので、Pythonを使って書くことにしました。

## 設定
初めにbotのアカウントを作成します。
[DiscordApp](https://discordapp.com/developers/applications/me)に行き、新しいアプリを作成してください。
botに名前をつけ、プロフィール画像を貼ってください。
<img width="646" alt="スクリーンショット 2018-05-09 18.40.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/200848/e5fc2b60-65e9-79cf-4d1d-b95cd4399aa7.png">

下にスクロールして「Create a Bot User」をクリックします。
完了したら、「secret bot token」を取得できます。

<img width="646" alt="スクリーンショット 2018-05-09 18.41.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/200848/ad45f923-8ad8-97e6-0a79-b7ed45babd73.png">

クリックして　「bot token」を表示することができます。

<img width="647" alt="スクリーンショット 2018-05-09 18.43.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/200848/53591983-b68d-ed59-2ae2-73eab3fa925f.png">

botを乗っ取られるので、このtokenは誰にも共有しないでください。
(記事を書いた後、画像のtokenは変更します。)

## コード
### 前提条件
* [Python 3](https://www.python.org/downloads/)
* [pip](https://pypi.python.org/pypi/pip)

### Discord.py (rewrite)
discord.pyライブラリの書き換えバージョンをインストールします。
pipのdiscord.pyは管理されていないので、rewrite版のライブラリをインストールしてください。

```
$ python3 -m pip install -U https://github.com/Rapptz/discord.py/archive/rewrite.zip
```

使用しているdiscord.pyのバージョンの確認

```
>>> import discord
>>> discord.__version__
'1.0.0a'
```
botの作成に移りましょう。

```
import discord
from discord.ext import commands
```

ModuleNotFoundErrorまたはImportErrorを指定すると、discord.pyのインストールに問題が発生します。

```
bot = commands.Bot(command_prefix='$', description='A bot that greets the user back.')
```
command_prefixとは、コマンドを呼び出すために最初にメッセージの内容が含まなければならないものです。

```
@bot.event
async def on_ready():
    print('Logged in as')
    print(bot.user.name)
    print(bot.user.id)
    print('------')
```
```on_ready()``` はクライアントがDiscordから受信したデータの準備を完了したときに呼び出されます。
通常はbotのログインが成功した後に呼ばれます。

次に、botにいくつかの機能を追加しましょう。

```
@bot.command()
async def add(ctx, a: int, b: int):
    await ctx.send(a+b)

@bot.command()
async def multiply(ctx, a: int, b: int):
    await ctx.send(a*b)

@bot.command()
async def greet(ctx):
    await ctx.send(":smiley: :wave: Hello, there!")

@bot.cmmands()
async def cat(ctx):
    await ctx.send("https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif")
```

実行する前に、サーバーにbotを招待する必要があります。
このOAuth2のURLは、botの設定ページから生成できます。
[DiscordApp](https://discordapp.com/developers)に行き、botのプロフィールをクリックしてoAuth2のURLを生成してください。

<img width="650" alt="スクリーンショット 2018-05-10 14.13.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/200848/71a376e2-e738-9187-be67-193454cecd7b.png">


ここで、botに与える権限を決めます。
使用するために、メッセージを送信するための許可が必要です。

以下をコマンドラインで実行して、botを実行します。

```
$ python bot.py
```

<img width="567" alt="スクリーンショット 2018-05-10 14.13.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/200848/9d9daf49-f537-02a4-01be-ca9cd05ab778.png">


botを試して見ます。

<img width="519" alt="スクリーンショット 2018-05-10 14.14.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/200848/620238e6-eac6-302a-ee52-1f186c36ef00.png">

<img width="503" alt="スクリーンショット 2018-05-10 14.14.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/200848/0a70997a-c4b7-6432-ea51-6cd6ae9bc075.png">

DiscordBotを作る際に良いリファレンスがあります。
[こちら](https://github.com/meew0/discord-bot-best-practices)を読むことをお勧めします。

> Have an info command.
It should provide information about the bot such as what framework it is using and the used version, help command and, most importantly, who made it.  

```
@bot.command()
async def info(ctx):
    embed = discord.Embed(title="nice bot", description="Nicest bot there is ever.", color=0xeee657)
    
    # give info about you here
    embed.add_field(name="Author", value="<YOUR-USERNAME>")
    
    # Shows the number of servers the bot is member of.
    embed.add_field(name="Server count", value=f"{len(bot.guilds)}")

    # give users a link to invite thsi bot to their server
    embed.add_field(name="Invite", value="[Invite link](<insert your OAuth invitation link here>)")

    await ctx.send(embed=embed)
```

<img width="562" alt="スクリーンショット 2018-05-10 14.15.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/200848/acf51092-101f-d194-0f62-8bda27f03a00.png">


The discord.py generates an ```help``` command automatically.
So, to write our own, we would first have to remove the one given by it.

discord.pyは自動的に```help```コマンドを生成します。
自分自身で書くために、はじめにこれを削除します。

```
bot.remove_command('help')
```

これでhelpコマンドを書くことができます。 
ここではbotの使用法について説明します。

```
@bot.command()
async def help(ctx):
    embed = discord.Embed(title="nice bot", description="A Very Nice bot. List of commands are:", color=0xeee657)

    embed.add_field(name="$add X Y", value="Gives the addition of **X** and **Y**", inline=False)
    embed.add_field(name="$multiply X Y", value="Gives the multiplication of **X** and **Y**", inline=False)
    embed.add_field(name="$greet", value="Gives a nice greet message", inline=False)
    embed.add_field(name="$cat", value="Gives a cute cat gif to lighten up the mood.", inline=False)
    embed.add_field(name="$info", value="Gives a little info about the bot", inline=False)
    embed.add_field(name="$help", value="Gives this message", inline=False)

    await ctx.send(embed=embed)
```

<img width="589" alt="スクリーンショット 2018-05-10 14.15.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/200848/3186d785-2577-f7ce-7959-7c59e3699fe4.png">

Pythonで書かれたDiscordBotを作成できました。

## ホスティング

今のところは、botはスクリプトを実行するまでオンラインになっているだけです。
botを常に実行したい場合は、オンラインでホストする必要があります。
また、RaspberryPiなどでローカルにホストすることもできます。
ホスティングサービスは無料のもの（Heroku's Free Tier）から有料（Digital Ocean）のものもあります。
私は無料のHerokuでbotを走らせてますが、これまでどんな問題も発生してません。

## ソースコード
```
import discord
from discord.ext import commands

bot = commands.Bot(command_prefix='$')

@bot.event
async def on_ready():
    print('Logged in as')
    print(bot.user.name)
    print(bot.user.id)
    print('------')

@bot.command()
async def add(ctx, a: int, b: int):
    await ctx.send(a+b)

@bot.command()
async def multiply(ctx, a: int, b: int):
    await ctx.send(a*b)

@bot.command()
async def greet(ctx):
    await ctx.send(":smiley: :wave: Hello, there!")

@bot.command()
async def cat(ctx):
    await ctx.send("https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif")

@bot.command()
async def info(ctx):
    embed = discord.Embed(title="nice bot", description="Nicest bot there is ever.", color=0xeee657)
    
    # give info about you here
    embed.add_field(name="Author", value="<YOUR-USERNAME>")
    
    # Shows the number of servers the bot is member of.
    embed.add_field(name="Server count", value=f"{len(bot.guilds)}")

    # give users a link to invite thsi bot to their server
    embed.add_field(name="Invite", value="[Invite link](<insert your OAuth invitation link here>)")

    await ctx.send(embed=embed)

bot.remove_command('help')

@bot.command()
async def help(ctx):
    embed = discord.Embed(title="nice bot", description="A Very Nice bot. List of commands are:", color=0xeee657)

    embed.add_field(name="$add X Y", value="Gives the addition of **X** and **Y**", inline=False)
    embed.add_field(name="$multiply X Y", value="Gives the multiplication of **X** and **Y**", inline=False)
    embed.add_field(name="$greet", value="Gives a nice greet message", inline=False)
    embed.add_field(name="$cat", value="Gives a cute cat gif to lighten up the mood.", inline=False)
    embed.add_field(name="$info", value="Gives a little info about the bot", inline=False)
    embed.add_field(name="$help", value="Gives this message", inline=False)

    await ctx.send(embed=embed)

bot.run('NDE0MzIyMDQ1MzA0OTYzMDcy.DWl2qw.nTxSDf9wIcf42te4uSCMuk2VDa0')
```