pythonでdiscordbotを作るライブラリに、pycord
というのがあります。
pycord
では、cogs
という機能があり、DiscordBotにおけるモジュール的な構成をすることができます。
モジュールやエクステンションとして知られるコグは、コマンドをグループにまとめるのに使われます。
これは、一般的な考え方が同じコマンドをグループ化するのに便利です(モデレーションコマンドなど)。
また、ボットのファイルが乱雑になるのを防ぐのにも役立ちます。
その中に、関数を定期的に実行する動作を書くことができるので
なかなかにマニアックなテクニックですが紹介します。
環境
py-cord >= 2.4
cogsファイルの読み込み
import discord
from discord.ext import commands
intents = discord.Intents.default()
bot = commands.AutoShardedBot(intents=intents)
path = "./cogs"
...
bot.load_extensions(
'cogs.hoge',
store=True
)
# store=Falseにすると、Cogでエラーが発生していた際にクリティカル警告となる
bot.run(TOKEN)
import
で読み込むのではなく、load_extensions
という関数で読み込みます。
...
bot.load_extensions(
'cogs.hoge',
'cogs.huga',
store=True
)
もちろん、cogsは増やせます。
また、コメントしていますが
store
というオプションがあり、エラーが発生した際にクリティカルにするか設定できます。
...
cogs_list = [
'greetings',
'moderation',
'fun',
'owner'
]
for cog in cogs_list:
bot.load_extension(f'cogs.{cog}')
cogsはリストでも読み込むことができます。
Cogsの書き方
import discord
from discord.ext import commands
from discord.commands import Option, OptionChoice, SlashCommandGroup
# クラス名にCogと付けたほうが分かりやすい
class HogeCog(commands.Cog):
def __init__(self, bot:commands.bot):
print(f"init -> {self.__class__}")
self.bot = bot
# nameは全て小文字
group = SlashCommandGroup(name="name", description="description")
# コマンド名は全て小文字。関数名の重複に注意。
@group.command(name="ping", description="通信テスト")
async def ping(self, ctx: discord.ApplicationContext):
await ctx.response.send_message(content="pong!")
def setup(bot: commands.bot):
bot.add_cog(HogeCog(bot))
気を付けること
discord.Botまたはdiscord.ext.commands.Botのインスタンスであるbot
を参照するには、
self.bot経由でアクセスする必要があります。
また、クラスの中にいるので、コマンドはすべてそのクラスのメソッドです。
このため、すべての関数は最初のパラメータとしてselfを持つ必要があります。
これがないと、ボットのインスタンスにアクセスできないのです。
例として、特定のチャンネルIDへメッセージを送信する場合、
async def send_channel_something(self): # 最初にselfを持つ
# self.botからbotにアクセスしてチャンネルを取得
channel = self.bot.get_partial_messageable(id=hoge_id)
# チャンネルにメッセージを送信
await channel.send(content=message, embed=embed)
となるわけです。
定期実行を作ってみる
class TaskCog(commands.Cog):
def __init__(self, bot: discord.AutoShardedBot):
print(f"init -> {self.__class__}")
self.bot = bot
self.something_loop_task.start()
@tasks.loop(seconds=20)
async def something_loop_task(self):
print("hello task!")
@something_loop_task.before_loop
async def before_something_loop_task(self):
print('waiting...')
await self.bot.wait_until_ready()
def setup(bot):
bot.add_cog(TaskCog(bot))
このCogsを読み込むと、20秒おきにhello task!
とプリントされます。
この方法だと、asyncio地獄や「インターネットが切れたらどうしよう」「再接続はどうしよう」といった頭を悩ませるようなことを気にすることなく、バックグラウンド・タスクを作ることができます。
最後に
このようにcogsで分割すると、それぞれのコマンドや動作を便利に管理できるようになります。
ぜひ試してみてください!