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 1 year has passed since last update.

discord.pyでcogをnestする

Last updated at Posted at 2020-01-06

注意

既にアナウンスされている通り、discord.pyは2021年8月下旬ころ、以降のサポートを停止する旨発表されております。
もし本記事を参考にして不具合が生じた場合、ライブラリのサポート終了による影響である可能性もあります。
詳しくは the_future_of_dpy.md をご覧ください。

前提

  • discord.py
  • python3.6

この記事中のコードはあくまで説明用の簡略化されたコードなので、ナンセンスなコードしか登場しません。
ご了承ください。

cogが複雑化してきた

discord.pyのコマンド拡張 (https://discordpy.readthedocs.io/ja/latest/ext/commands/index.html) には、cogといういくつかのコマンドやイベントをひとまとめにする機能があります。
このように

hoge.py
from discord.ext import commands as c

class CogHogeFuga(c.Cog):
    def __init__(self, bot, hoge):
        self._bot = bot
        self._hoge = hoge

    @c.command()
    async def hoge1(self, ctx):
        await ctx.send("hoge")
    
    @c.command()
    async def hoge2(self, ctx):
        foo = await hoge3()
        return foo

    async def hoge3(self):
        return self._hoge

書くと、CogHogeFugaを読み込むだけでhoge1hoge2も読み込まれるわけです。

ところが開発を進めるうちに以下のようになってきました。

hoge.py
from discord.ext import commands as c

class CogHogeFuga(c.Cog):
    def __init__(self, bot, hoge):
        self._bot = bot
        self._hoge = hoge
        self._fuga = None

    @c.command()
    async def hoge1(self, ctx):
        await ctx.send("hoge")
    
    @c.command()
    async def hoge2(self, ctx):
        foo = await hoge3()
        await ctx.send(foo)

    async def hoge3(self):
        return self._hoge

    """
    ここから下が追加したコマンドたち
    """
    
    @c.command()
    async def fuga1(self, ctx):
        return ctx.send("fuga")

    async def fuga2(self, ctx, new_):
        self._fuga = new_

    @c.command()
    async def fuga3(self, ctx):
        foo = await fuga4()
        await ctx.send(foo)

    async def fuga4(self):
        return self._fuga

なんとなく書き足してみましたが、hogeに関するコマンドとfugaに関するコマンドが混在していますね。
もしかすると今後はbarに関するコマンドも増えるかもしれないしできれば分離したい。

しかしコグを単に増やしてしまうと、コグのロードの手間が増えてしまいます。hogefugaもやっていることは似ているので、できればbot.add_cog(CogHogeFuga(bot))の一手間で済ませたいところです。

cogをnestする

そこで、cogが入れ子にできると嬉しくなれそうだなと思うわけです。
実際以下のように書くことで、コグのロード自体はbot.add_cog(CogHogeFuga(bot))のままで済ませられます。

hoge.py
from discord.ext import commands as c

class CogHogeFuga(c.Cog):
    def __init__(self, bot, hoge):
        self._bot = bot

        self._bot.add_cog(CogHoge(self._bot, hoge)) # ポイント
        self._bot.add_cog(CogFuga(self._bot)) # ポイント

class CogHoge(c.Cog):
    def __init__(self, bot, hoge):
        self._bot = bot
        self._hoge = hoge


    @c.command()
    async def hoge1(self, ctx):
        await ctx.send("hoge")
    
    @c.command()
    async def hoge2(self, ctx):
        foo = await hoge3()
        await ctx.send(foo)

    async def hoge3(self):
        return self._hoge
    

class CogFuga(c.Cog):
    def __init__(self, bot):
        self._bot = bot
        self._fuga = None

    @c.command()
    async def fuga1(self, ctx):
        return ctx.send("fuga")

    async def fuga2(self, ctx, new_):
        self._fuga = new_

    @c.command()
    async def fuga3(self, ctx):
        foo = await fuga4()
        await ctx.send(foo)

    async def fuga4(self):
        return self._fuga

上記のようにCogを追加する際にbot自身を渡すことで、初期化の際にほかのCogをbotに追加することができました。

終わりに

もっと簡単な方法があったら教えてください。

何がありがたいのか

チラ裏です。
上にも書いたように、cogはそれぞれbotにadd_cogする必要があります。Extensionというファイルまるごとロードする機能もありますが、こちらも結局ファイル内のsetup()メソッドを実行するので、ここにはadd_cogを書かねばなりません。

私はできるだけ

  • 関連性の高いものはまとめて書いておきたい、分かるようにしたい
  • 一つの改修で複数個所を編集しないで済ませたい

ので、Cogをネストすることで実現しています。
この書き方だと、例えばあるCogを分割したい場合、分割したいCogの書き換えだけで済ませられ、他の部分で新たにCogをロードするような処理を記載する必要もありません。

5
2
5

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?