Closed14

PythonでDiscordボットの作成

YusukeYusuke
YusukeYusuke

cogs ディレクトリ下のファイルを動的に読み込む構成になっており、コマンドの追加が容易。

ボットに読み込ませたいファイルに以下のコードを書いておけば良さそう。
Templateのところは読み込ませるファイル内に実装したクラスに適宜変更する。

async def setup(bot):
    await bot.add_cog(Template(bot))
YusukeYusuke

コマンドの定義

  • テキストコマンドとして定義する場合(参考

    @bot.command()
    async def foo(ctx, arg):
       await ctx.send(arg)
    
  • テキストコマンド、および、スラッシュコマンドとして定義する場合 (参考)

    @bot.hybrid_command()
    async def test(ctx):
       await ctx.send("This is a hybrid command!")
    
YusukeYusuke

コマンドの引数を表示してくれる点でスラッシュコマンドのほうが便利なので、@bot.hybrid_command()を使えば良さそう。

YusukeYusuke

スラッシュコマンドの注意

スラッシュコマンドを使えるようにするには bot.tree.sync() を実行する必要がある。

デフォルトだとグローバルコマンドとして登録されてしまうため、テンプレートリポジトリでは有効とする設定としている場合のみ実行するようになっている。
bot.pyon_ready 関数内に上記コードが書かれている。)

YusukeYusuke

ギルドコマンド(特定のサーバでのみ有効なコマンド)とするには、コマンドに @discord.app_commands.guilds(<GUILD ID>) デコレータをつける。
グローバルコマンドは反映に時間がかかるが、ギルドコマンドは即時反映される。

自身の特定のサーバでのみ使用するコマンドはギルドコマンドとして登録すればよさそう。

YusukeYusuke

テンプレートリポジトリのREADMEにも上記デコレータに関して書かれているが、手元の環境での動作確認ではデコレータをつけるだけでは即時反映されなかった。

さらにbot.tree.sync(discord.Object(<GUILD ID>))とすることで即時反映されることを確認できた。

YusukeYusuke

コマンドに@discord.app_command.describe(msg="投稿したいメッセージ")というようなデコレータをつけることで、引数に説明文を追加できるとライブラリのドキュメントにかかれていた。

手元の環境にて、docstringに引数の説明を書くことでデコレータと同様の説明を追加できることを確認した。

@bot.hybrid_command()
async def test(ctx, msg: str):
    """
    テストコマンド

    Parameters
    ---------------
    msg : str
        投稿したいメッセージ
    """
   await ctx.send(f"{msg}")
YusukeYusuke

コマンド引数に型ヒントをつけることで値を変換してくれる。

@bot.hybrid_command()
async def test(ctx, number: int):
    # 関数内でint型として`number`を扱える (int型に変換できる値の場合)
    await ctx.send(f"{number}")

独自クラスを作ることで、より高度な変換も可能。

YusukeYusuke

Cogで定義したコマンドの定期実行

from discord.ext import commands, tasks

class ExampleCog(commands.Cog):
    def __init__(self, bot: commands.Bot) -> None:
        self.bot = bot

    @tasks.loop(seconds=5.0)
    def example_task(self) -> None:
        print("example task")

    @commands.Cog.listener()
    async def on_ready(self) -> None:
        self.example_task.start()

以下のように__init__startする方法も見受けられたが、自分の環境では動作しなかった。

from discord.ext import commands, tasks

class ExampleCog(commands.Cog):
    def __init__(self, bot: commands.Bot) -> None:
        self.bot = bot
        self.example_task.start()

    @tasks.loop(seconds=5.0)
    def example_task(self) -> None:
        print("example task")
YusukeYusuke

@tasks.loop(time=datetime.time(hour=8))とすることで、毎日指定時刻に実行することができる。
タイムゾーンを指定しない場合はUTCとして扱われる。
上記例の場合、毎日UTCの8時に実行される。

YusukeYusuke

任意のチャンネルの取得

bot.get_channel(<channel id>)でチャンネルを取得できない現象に遭遇した。
正しいチャンネルのIDであってもNoneが返ってくる。

この現象について調べたところ、下記のブログを見つけた。

【pycord】チャンネルが取得できない対処法【Python3.10】 - nekoy3`s room

上記ブログの通り、bot.get_partial_messageable(<channel id>)としたところ、チャンネルを取得できた。

このスクラップは2023/01/16にクローズされました