🍣

discord.py 入門 その5

2023/04/23に公開

おさらい

前回はミュート機能の実装をしました。今回は管理者限定の条件を付け、さらにミュート解除の機能も実装しましょう。

前回までのコード(再掲)

# bot.py

from discord.ext import commands
from discord.utils import get
import discord

from io import StringIO
from datetime import datetime, timedelta

intents = discord.Intents.default()
intents.members = True # メンバー管理の権限
intents.message_content = True # メッセージの内容を取得する権限

bot = commands.Bot(
    command_prefix="$", # $コマンド名 でコマンドを実行できるようになる
    case_insensitive=True, # 大文字小文字を区別しない ($hello も $Hello も同じ!)
    intents=intents # 権限を設定
)

@bot.event
async def on_ready():
    print("Bot is ready!")


@bot.command()
async def hello(ctx: commands.Context) -> None:
    """helloと返すコマンド"""
    await ctx.send(f"Hello {ctx.author.name}")

@bot.command(
    name="message",
    aliases=["msg", "m"],
)
async def get_message(ctx: commands.Context, channel: discord.TextChannel) -> None:
    """チャンネルのメッセージを取得し、テキストファイルに保存するコマンド"""

    stream = StringIO()

    async for message in channel.history(
        after=datetime.utcnow() -timedelta(hours=1),
        oldest_first=True,
    ):
        jst = message.created_at + timedelta(hours=9) # UTC -> JST
        msg = f"{message.author.name}: {jst.strftime('%Y/%m/%d %H:%M:%S')}\n{message.content}"
        stream.write(msg)
        stream.write("\n\n")

    stream.seek(0)
    await ctx.send(file=discord.File(stream, filename="messages.txt"))
    stream.close()


@bot.command(name="mute")
async def mute(ctx: commands.Context, member: discord.Member) -> None:

    if (role := get(member.roles, name="チャット制限")) is None: # ロールがサーバーに存在しない場合
        # ロールを作成
        role = await ctx.guild.create_role(
            name="チャット制限", # ロール名
            mentionable=True # メンションできるようにする
        )

    await member.add_roles(role) # メンバーにロールを付与
    await ctx.send(f"{member.mention} をチャット制限しました。")

@bot.event
async def on_message(message: discord.Message) -> None:

    if message.author.bot: # botからのメッセージは無視
        return

    # チャット制限ロールをメッセージ送信者が持っているか確認
    role = get(message.author.roles, name="チャット制限")

    if role is not None:
        await message.delete() # メッセージを削除
        return # ミュートされている場合はコマンドを実行しない

    await bot.process_commands(message) # コマンドを実行


bot.run("TOKEN")

Step6. 管理者限定の条件を付けよう

一番最初に思いつく方法としたら、以下のようにif文を用いて実装するものでしょう。

@bot.command(name="mute")
async def mute(ctx: commands.Context, member: discord.Member) -> None:

    if ctx.author.id !=  12345678: # 管理者のID
        return # 管理者以外は実行できないようにする

    # 以下同じ

この方法でも問題はありませんがdiscord.ext.commandsには、これを簡単に実装できる機能が用意されています。以下のように、もとのコードに1行足すだけで実装できます。

@bot.command(name="mute")
@commands.is_owner() # 管理者のみ実行可能にする
async def mute(ctx: commands.Context, member: discord.Member) -> None:

    # 以下同じ

@commands.is_owner()は、管理者のみ実行可能にする機能です。管理者以外が実行しようとすると、commands.NotOwnerというエラーが発生します。

Step7. ミュート解除の機能を実装しよう

これもStep6のときと同じように管理者限定のコマンドにします。今まで学んだことで実装できるので、まずはご自身で考えた後に以下の実装例を見てみましょう。ちなみにロールをメンバーから剥奪するメソッドはawait member.remove_roles(role)です。

メソッドのドキュメント

以下は実装例です。

@bot.command(name="unmute")
@commands.is_owner() # 管理者のみ実行可能にする
async def unmute(ctx: commands.Context, member: discord.Member) -> None:

    if (role := get(member.roles, name="チャット制限")) is None: # ロールがサーバーに存在しない場合
        await ctx.send(f"{member.mention} はミュートされていません。")
        return

    await member.remove_roles(role) # メンバーからロールを剥奪
    await ctx.send(f"{member.mention} のミュートを解除しました。")

まとめ

今回はcommands.is_ownerデコレータを用いると、管理者限定の機能を簡単に実装できることを学びました。
次回はエラー処理について学んでいきます。お疲れ様でした。

前回 | 次回

GitHubで編集を提案

Discussion