5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Discord BotAdvent Calendar 2020

Day 8

discord.py commandsフレームワークの小ネタ: クールダウン

Last updated at Posted at 2020-12-27

皆さんこんにちは、daima3629と申します。
DiscordBotのTipsということで、8日目はDiscordAPIのPython用ラッパーライブラリであるdiscord.pyのcommandsフレームワークの小ネタを紹介していきたいと思います。
あれ...?8日目...?なにかおかしいnおっと誰か来たようだ
今回ご紹介致しますのは、その名も「クールダウン」!(商品紹介風)

クールダウンとは?

「一定時間内に指定した回数以上コマンドを入力するとそれ以上のコマンドは受け付けなくなる」です。
まあ言葉で書き連ねてもよくわかんないと思うので、GIF画像をご用意したのでどういう挙動をするのかご覧ください。output.gif
こんな感じです。
なんとなくわかったとこで実際にどのようなコードを書けばいいのかお教えしましょう!

今回使用するコード

# コマンドの実装部分のみ書いてあり、その他の部分は省略してあります

@bot.command()
@commands.cooldown(2, 10, type=discord.BucketType.user)
async def hello(ctx):
    await ctx.send("hello!")

@bot.event
async def on_command_error(ctx, err):
    if isinstance(err, commands.CommandOnCooldown):
        return await ctx.send("クールダウン中だよ!")

とりあえずクールダウンに直接関わる部分

とりあえずコードを2つのブロックに分けて、1つ目を見ていきましょう!

@bot.command()
@commands.cooldown(2, 10, type=discord.BucketType.user)
async def hello(ctx):
    await ctx.send("hello!")

commands.cooldown

今回の記事の本命です。
@commands.cooldown(rate, per, type)このようにして使います。
引数名の説明は以下の通り。

引数名 説明
rate 一定時間内に何回コマンドを打ったら
クールダウンに入るのか数字で指定
per 一定時間を指定
type クールダウンタイプを指定

type引数に指定するものはちょっと変わったものです。
これはリファレンスを見たほうがわかりやすいかもしれません。
今回のコード例ではBucketType.userを使用しているのでユーザーごとのクールダウンとなります。

これをもとに今回のクールダウンを見てみると、「10秒間の間に2回以上はコマンドを受け付けないユーザーごとのクールダウン」と読み取れますね!

on_command_error

もう一つ残っているコードを見てみましょう。

@bot.event
async def on_command_error(ctx, err):
    if isinstance(err, commands.CommandOnCooldown):
        return await ctx.send("クールダウン中だよ!")

ここで出ましたon_command_error
早速見ていきましょう。

クールダウン時はエラーが出る

クールダウンのときコマンドを実行するとCommandOnCooldownというエラー(例外)が発生します。
これを利用して、コードではクールダウン時にメッセージを発するようにしています。
on_command_errorは他にもコマンドが存在しないときのエラーもキャッチできますよね。
割と使う要素です。

エラーはオブジェクトの一種

Python自体の知識ですが、エラーはdiscord.Userとかと同じオブジェクトの一種です。
つまり、なにかしら情報が入っていると。
ではリファレンスを見てみましょう。

要素名 説明
retry_after 再びコマンドが使えるようになるまでにかかる秒数(float型)
cooldown クールダウンオブジェクト

retry_afterは整数(int型)ではなく小数(float型)であることに注意です。
またcooldowncooldown.rateのようにアクセスすることでデコレータで指定した情報を取得するために用意されています。

クールダウン応用編

これまで学んできたことを生かして、少し応用を効かせたコードを書いてみましょう!

クールダウンが終わるまでどのくらいかかるか表示する!

クールダウンの残り時間がわからないほどイライラすることはありません。
ユーザーのイライラを解消するためにも残り時間を表示してあげましょう。

小数点以下を切り捨てる

retry_afterを使うわけですが、先程も言ったようにこれは小数なので、そのまま表示すると「あと3.1548秒待ってね!」のように表示されてしまうので、切り捨てが必要です。
今回はint型に変換することで切り捨てます。

実装してみよう

今回は秒数が大きくなる可能性も考慮して、「~分…秒」の形で表示させることにします。

@bot.event
async def on_command_error(ctx, err):
    if isinstance(err, commands.CommandOnCooldown):
        retry_after_int = int(err.retry_after)
        retry_minute = retry_after_int // 60
        retry_second = retry_after_int % 60
        return await ctx.send(f"クールダウン中だよ!あと{retry_minute}{retry_second}秒待ってね!")

今回のポイントは先程説明した小数点切り捨てに加え、2つの演算子、//%です。
//は割り算したとき小数になった場合に小数点以下を切り捨てた数値を返す演算子で、
%は割り算したときの余りを返す演算子です。
これを組み合わせることで「~秒」を「~分...秒」の形にしています。

おまけ: Cogに組み込んでみる

もちろんクールダウンはCogの中のコマンドにも組み込めます。
使用例だけ記しておきます。
細かい解説は省きます。

class CooldownCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    @commands.command()
    @commands.cooldown(2, 10, type=discord.BucketType.user)
    async def hello(ctx):
        await ctx.send("hello!")

    @commands.Cog.listner()
    async def on_command_error(self, ctx, err):
        if isinstance(err, commands.CommandOnCooldown):
            return await ctx.send("クールダウン中だよ!")

まとめ

  • commands.cooldownを使うことでコマンドにクールダウンを実装させることができる!
  • on_command_errorを使うことでクールダウンに応じて文章を送ることができる!

というわけで、みなさんもクールダウンを使って一歩先のBot開発者になりませんか!?
この記事が役に立ったのなら幸いです。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?