コグ

Bot開発においてコマンドやリスナー、いくつかの状態を一つのクラスにまとめてしまいたい場合があるでしょう。コグはそれを実現したものです。

要旨:

コグは エクステンション とともに使用されるのが一般的であることを覚えておきましょう。

簡単な例

この例で紹介するコグは hello という名前の command と、 Event を受信するリスナーを実装した Greetings という名前のコマンドカテゴリを定義しています。

class Greetings(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self._last_member = None

    @commands.Cog.listener()
    async def on_member_join(self, member):
        channel = member.guild.system_channel
        if channel is not None:
            await channel.send(f'Welcome {member.mention}.')

    @commands.command()
    async def hello(self, ctx, *, member: discord.Member = None):
        """Says hello"""
        member = member or ctx.author
        if self._last_member is None or self._last_member.id != member.id:
            await ctx.send(f'Hello {member.name}~')
        else:
            await ctx.send(f'Hello {member.name}... This feels familiar.')
        self._last_member = member

考慮すべき二つのテクニカルノート:

  • すべてのリスナーは listener() で明示的に装飾する必要があります。

  • コグの名前は、自動的にクラスの名前が引用されますが、上書きも可能です。 メタオプション を参照してください。

  • すべてのコマンドは状態を保持するインスタンスの属性を使用するために self パラメータを持つ必要があります。

コグの登録

コグを定義したら、Botにコグを登録する処理が必要になります。 add_cog() メソッドを用いて登録ができます。

await bot.add_cog(Greetings(bot))

これはコグとボットを紐づけ、すべてのコマンドとリスナーを自動的にボットに追加します。

コグを名前で参照している点に注意してください。これは メタオプション で上書きが可能です。そのため、後でコグを除去したい場合は、次の処理を行う必要があります。

await bot.remove_cog('Greetings')

コグの使用

コグを名前で除去するのと同様に、名前でコグを検索することもできます。これによってコグをデータ共有のためのコマンド間通信プロトコルとして使うことができます。例えば:

class Economy(commands.Cog):
    ...

    async def withdraw_money(self, member, money):
        # implementation here
        ...

    async def deposit_money(self, member, money):
        # implementation here
        ...

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

    def coinflip(self):
        return random.randint(0, 1)

    @commands.command()
    async def gamble(self, ctx, money: int):
        """Gambles some money."""
        economy = self.bot.get_cog('Economy')
        if economy is not None:
            await economy.withdraw_money(ctx.author, money)
            if self.coinflip() == 1:
                await economy.deposit_money(ctx.author, money * 1.5)

特殊なメソッド

コグが複雑化し、多くのコマンドを持つようになるにつれ、コグあるいはBot全体の挙動をカスタマイズしたくなることがあります。

そのための特殊なメソッドは以下のとおりです:

詳細はリファレンスを参照してください。

メタオプション

コグの中核にはメタクラスである commands.CogMeta が存在します。これにはいくつかの挙動を変更ができる様々なオプションが用意されています。オプションを使用する際はキーワード引数をクラス定義の行で渡します。例えば、コグの名前を変更する場合はキーワード引数 name を次のように渡します。

class MyCog(commands.Cog, name='My Cog'):
    pass

設定可能な他のオプションについては commands.CogMeta のドキュメントを参照してください。

インスペクション

コグは究極的にはクラスのため、コグの特定のプロパティを調べるのに役立つツールがいくつか用意されています。

Cog.get_commands() を使うことで、コマンドの list を取得できます。

>>> cog = bot.get_cog('Greetings')
>>> commands = cog.get_commands()
>>> print([c.name for c in commands])

サブコマンドを取得したい場合は Cog.walk_commands() ジェネレータを使うことができます。

>>> print([c.qualified_name for c in cog.walk_commands()])

これと同様の処理をリスナーで行う場合は、 Cog.get_listeners() が使用できます。これはタプルのリストを返します -- 最初の要素がリスナーの名前で、二つ目の要素が関数そのものです。

>>> for name, func in cog.get_listeners():
...     print(name, '->', func)