注意
既にアナウンスされている通り、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といういくつかのコマンドやイベントをひとまとめにする機能があります。
このように
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
を読み込むだけでhoge1
もhoge2
も読み込まれるわけです。
ところが開発を進めるうちに以下のようになってきました。
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
に関するコマンドも増えるかもしれないしできれば分離したい。
しかしコグを単に増やしてしまうと、コグのロードの手間が増えてしまいます。hoge
もfuga
もやっていることは似ているので、できればbot.add_cog(CogHogeFuga(bot))
の一手間で済ませたいところです。
cogをnestする
そこで、cogが入れ子にできると嬉しくなれそうだなと思うわけです。
実際以下のように書くことで、コグのロード自体はbot.add_cog(CogHogeFuga(bot))
のままで済ませられます。
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をロードするような処理を記載する必要もありません。