discord.py - commandsフレームワークへの移行

公開:2021/02/18
更新:2021/02/19
6 min読了の目安(約5400字TECH技術記事

はじめに

この記事はdiscord.pyのcommandsフレームワークに移行する方法を書いたものです。
間違いを見つけたらどんどん指摘してください。

環境

- Python v3.8.3-final
- discord.py v1.6.0-final
- aiohttp v3.6.3
- system info: Windows 10 10.0.17134

python -m discord -vの情報)

最初のコード

以下のコードから始めるモノとします:

import discord

client = discord.Client()
RESPONSES = {
    "おは": "おはよう!",
    "おやすみ": "おやすみ!",
    "落ち": "おつかれ!"
}

@client.event
async def on_ready():
    print('We have logged in as {0.user}'.format(client))

@client.event
async def on_message(message):
    if message.author == client.user:
        return

    for rk, rv in RESPONSES.items():
        if rk in message.content:
            await message.reply(rv)
    
    if message.content.startswith('$ping'):
        await message.channel.send('Pong!')
    elif message.content.startswith('$greet'):
        await message.channel.send(f'Hello, `{message.content[7:]}`.')

client.run('Th1sIsN0tT0k3n.B3cause.1fiShowB0tWillG3tH4cked')

1. インポート

commandsフレームワークをインポートするには

import discord
+ from discord.ext import commands

のようにします。
普通のdiscordは残しておきます。(isinstanceでの判定やEmbedの作成に必要になります。が、今回は不必要です。)

なぜfrom〜importを使うのか?

実際、

import discord.ext.commands

でも動きはしますが、
毎回

discord.ext.commands.Bot(command_prefix="!")

@bot.command()
@discord.ext.commands.has_permissions(administrator=True)

と書く必要があり、面倒なためfrom~importを使っています。

2. ClientをBotにする

ClientをBotにしていきます。
移行にはそんな心配は要らず、discord.Clientをcommands.Botに置き換えるだけで移行できます。
Botを使うにはプレフィックス(/!など、コマンドの最初につける文字)を決める必要があります。
今回は$にしました。

- client = discord.Client()
+ bot = commands.Bot(command_prefix="$")

また、clientをbotにしたため、clientをbotに置き換えていきます。

- @client.event
+ @bot.event
async def on_ready():
-     print('We have logged in as {0.user}'.format(client))
+     print('We have logged in as {0.user}'.format(bot))

- @client.event
+ @bot.event
async def on_message(message):
-     if message.author == client.user:
+     if message.author == bot.user:
        return

    for rk, rv in RESPONSES.items():
        if rk in message.content:
            await message.channel.send(rv)
    
    if message.content.startswith('$ping'):
        await message.channel.send('Pong!')
    elif message.content.startswith('$greet'):
        await message.channel.send(f'Hello, `{message.content[7:]}`.')


- client.run('Th1sIsN0tT0k3n.B3cause.1fiShowB0tWillG3tH4cked')
+ bot.run('Th1sIsN0tT0k3n.B3cause.1fiShowB0tWillG3tH4cked')

3. コマンドを定義する

「commands」フレームワークの名の通り、このフレームワークはコマンドを簡単に作ることができます。
コマンドを作るには@bot.commandが一般的です[1]
関数名がそのままコマンド名になります。
また、@bot.commandには()が必要なので注意。

@bot.command()
async def ping(ctx):
    await ctx.reply("Pong!")

@bot.command()
async def greet(ctx, name):
    await ctx.reply(f"Hello, `{name}`.")
覚えておくと役に立つこと

listのように関数名がかぶりそうなときはname=を引数に指定します。

@bot.command(name="greet")
async def command_greet(ctx, name):
    await ctx.reply(f"Hello, `{name}`.")

エイリアスを追加するにはaliases=を指定します。

@bot.command(aliases=["hi", "hello"])
async def greet(ctx, name):
    await ctx.reply(f"Hello, `{name}`.")

空白を無視して受け取りたいときは*,をつけます。

@bot.command()
async def greet(ctx, *, name):
    await ctx.reply(f"Hello, `{name}`.")

これらは全て一緒に使うことが出来ます。

@bot.command(name="greet", aliases=["hi", "hello"])
async def command_greet(ctx, *, name):
    await ctx.reply(f"Hello, `{name}`.")

コマンドを定義したため、on_messageのコマンドを削除します。

@bot.event
async def on_message(message):
    if message.author == bot.user:
        return

    for rk, rv in RESPONSES.items():
        if rk in message.content:
            await message.channel.send(rv)

-     if message.content.startswith('$ping'):
-         await message.channel.send('Pong!')
-     elif message.content.startswith('$greet'):
-         await message.channel.send(f'Hello, `{message.content[7:]}`.')

4. コマンドを動作させる

on_messageを定義するとコマンドが実行されなくなります。
このようなときにはprocess_commandsを使います。

@bot.event
async def on_message(message):
    if message.author == bot.user:
        return
    
    for rk, rv in RESPONSES.items():
        if rk in message.content:
            await message.channel.send(rv)
   
+     await bot.process_commands(message)

これで移行は終了です。

最終コード

import discord
from discord.ext import commands

bot = commands.Bot(command_prefix="$")
RESPONSES = {
    "おは": "おはよう!",
    "おやすみ": "おやすみ!",
    "落ち": "おつかれ!"
}

@bot.event
async def on_ready():
    print('We have logged in as {0.user}'.format(bot))

@bot.command()
async def ping(ctx):
    await ctx.reply("Pong!")

@bot.command()
async def greet(ctx, name):
    await ctx.reply(f"Hello, `{name}`.")

@bot.event
async def on_message(message):
    if message.author == bot.user:
        return

    for rk, rv in RESPONSES.items():
        if rk in message.content:
            await message.reply(rv)
    
    await bot.process_commands(message)


bot.run('Th1sIsN0tT0k3n.B3cause.1fiShowB0tWillG3tH4cked')

終わりに

この記事が役に立てば幸いです。
読んでいただきありがとうございました。

脚注
  1. @commands.commandで定義後、bot.add_commandで追加する方法もありますがそれほど使わないので割愛。 ↩︎