Discordで飯テロ自動返信BOTを作ってみよう

discordを運営していくにあたって、よく目にする飯テロに対して自動で返信するBOTを作ってみました。
discordBOTの導入手順などは色んな箇所で見かけるので割愛しますが、参考となる情報をまとめに記載しておきます。

最後にサンプルコード全文を記載しておきます。

# 各所の説明

ここにトークンのIDを設定します。
```
tokenID = 'ここにトークンIDを入れる'
```

自動返信する際に、画像付きでメッセージを返すための画像設定をここで行います。
画像ファイルは、imgフォルダを作成し、1.jpgとして準備しておきます。
**IMAGE_MAX**の値が2の場合は、1.jpgと2.jpgを作成します。
**sample_eat_message**は返答するメッセージをランダムで返します。
**IMAGE_MAX**と同じ数にしておく必要はありません。
```
IMAGE_MAX = 2
sample_eat_message = ['','','']
```

キーワードに応じて返答するメッセージは、特定のキーワードがヒットした場合、必ずメッセージを返します。
例）ぬるぽ－＞ガッ！！
※画像添付（飯テロ画像）がなかった場合のみ対象
```
text_action = {
    ''      : '',
    ''      : '',
    ''      : '',
}
```
飯テロ画像でもなく、特定キーワードでもない場合、ランダムで自動返信するメッセージの設定もします。
**RANDAM_WEIGHT**はキーワード返答の時に反応する重みを設定します。約3回に1回の場合は3を設定
通常のメッセージを返信する事が決定した場合、**sample_message**のメッセージをランダムで返します。
```
RANDAM_WEIGHT = 3
sample_message = ['','','']
```

各サーバーの情報は、config.iniで管理するため、１つ設定しておくだけで色んなサーバーで利用することができます。
ぜひお試しください。


# サンプルコード全文

```python:main.py
import discord
import discord.app_commands
import configparser
import random
import re

#########################
#   設定ファイル情報
#########################
CONF_PATH = './config.ini'
config = configparser.ConfigParser()
config.read(CONF_PATH, encoding='utf-8')
#########################
#   Discord設定情報
#########################
# ディスコードのトークンID
tokenID = 'ここにトークンIDを入れる'
# discord.Intents.all()を使用するとpresencesやmembersも有効な状態になります。
intents = discord.Intents.all()
# intents = discord.Intents.default()
# 用途に応じて以下のように無効化することもできます。設定できる項目は、以下のURLを参照してみてください。
# https://discordpy.readthedocs.io/ja/latest/api.html#discord.Intents
# discord_intents.bans = False # on_member_banやon_member_unbanのイベントを無効化
# intents.message_content = True
client = discord.Client(intents=intents)
tree = discord.app_commands.CommandTree(client)
#########################
#   個別設定情報
#########################
IMAGE_MAX = 2
# 画像を張り付けたら必ず返すメッセージ
sample_eat_message = ['','','']

# キーワードに応じて返答するメッセージ
text_action = {
    ''      : '',
    ''      : '',
    ''      : '',
}

# キーワード返答の時に反応する（3 = 0.3）
RANDAM_WEIGHT = 3
# 通常ランダムメッセージを返す
sample_message = ['','','']

#########################
#   スラッシュコマンド受信イベント
#########################
@tree.command(
    name="rice-terrorism",
    description="飯テロチャンネルを選択してください"
)
@discord.app_commands.default_permissions(
    administrator = True  #管理者のみ設定可能
)
async def hoge(ctx:discord.Interaction):
    try:
        guildid = str(ctx.guild_id)         # サーバーID
        channelid = str(ctx.channel_id)     # チャンネルID
        # サーバーセクションの存在チェック
        if not config.has_section(guildid):
            # 存在しない場合は作成する
            config.add_section(str(guildid))

        # チャンネルの設定
        config.set(guildid, 'channelid', channelid)
        # config_1.iniファイルに上書き
        with open(CONF_PATH, "w") as file:
            config.write(file)

        await ctx.response.send_message(f"飯テロチャンネルとして設定しました")
    except Exception as e:
        print(str(e))

#########################
#   開始イベント
#########################
@client.event
async def on_ready():
    print("on_ready MeshiTero")
    print(discord.__version__)
    await tree.sync()#スラッシュコマンドを同期

#########################
#   メッセージ受信イベント
#########################
@client.event
async def on_message(message):
    try:
        # bot判定>BOTは除外
        if message.author.bot:
            return

        # 受信ステータスの取得
        guildid = str(message.guild.id)                     # サーバーIDを取得
        channelid = str(message.channel.id)                 # チャンネルIDを取得
        channel = client.get_channel(message.channel.id)    # チャンネル名を取得

        # 送信処理
        try:
            # サーバーが存在しない場合は処理を抜ける
            if not config.has_section(guildid):
                return

            # チャンネルIDが存在しない場合は処理を抜ける
            if not config.has_option(guildid, 'channelid'):
                return

            # 設定ファイルからチャンネルIDの取得
            save_channelid = config.get(guildid, 'channelid')
            # 受信チャンネルIDが設定したチャンネルIDの場合
            if channelid == save_channelid:
                content = ''
                ########################
                #   ここに飯テロ処理を実行する前に行う処理を記述する
                ########################

                if content != '':
                    # 送信処理（content:送信メッセージ　reference:返信元）
                    await message.channel.send(content, reference = message)
                else:
                    if len(message.attachments) == 0:
                        # 添付ファイルが存在しない場合

                        # 特定のキーワードに応じて返答する処理
                        for action_msg in text_action:
                            if action_msg in message.content:
                                content = text_action[action_msg]

                        # ランダム処理(0:1 の重み ?:1)
                        if random.choices([0, 1], weights=[RANDAM_WEIGHT, 1])[0] == 1:
                            content += sample_message[random.randrange(len(sample_message))]

                        if content != '':
                            # 送信処理（content:送信メッセージ　reference:返信元）
                            await message.channel.send(content, reference = message)
                    else:
                        # 添付ファイルが存在する場合

                        # 画像をランダムで取得(1.jpg,2.jpg...)
                        fname = str(random.randrange(IMAGE_MAX)) + '.jpg'
                        # ローカル画像からFileオブジェクトを作成(imgファイル内)
                        file = discord.File(fp="img/" + fname,filename=fname,spoiler=False) 
                        # ランダムでメッセージを取得
                        content = sample_eat_message[random.randrange(len(sample_eat_message))]
                        # 送信処理（content:送信メッセージ　reference:返信元）
                        await message.channel.send(content, file=file, reference = message)

        except Exception as e:
            print(str(e))
    except Exception as e:
        print(str(e))

#########################
#   開始処理
#########################
if __name__ == '__main__':

    try:
        client.run(tokenID)
    except Exception as e:
        print(str(e))
        pass


```


# まとめ
参考情報

## ■ 全体資料
    ・Python Discord ボットのチュートリアル – Discord ボットをコーディングして無料でホストする
        https://www.freecodecamp.org/japanese/news/create-a-discord-bot-with-python/
## ■ プログラミング資料
    ・指定したチャンネルにメッセージを送信する方法まとめ
        https://scrapbox.io/discordjs-japan/%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%9F%E3%83%81%E3%83%A3%E3%83%B3%E3%83%8D%E3%83%AB%E3%81%AB%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%82%92%E9%80%81%E4%BF%A1%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%81%BE%E3%81%A8%E3%82%81

    ・discord.pyでスラッシュコマンドを実装する。
        https://qiita.com/beatbox4108/items/0d28619bb49e86f58ace

    ・discord.py V2のスラッシュコマンドを使えるようにする
        https://qiita.com/Luapy/items/3abff9575e132e2955ec

## ■ 設定資料
    ・常駐プログラム (Python) をWindowsにサービス登録
        https://nuxtblog.work/blog/fgj5szar4gjs/

    ・Discord DeveloperのPermissions 日本語まとめ【開発者向け】
        https://narikakun.net/technology/discord-developer-permissions/

            権限名                  権限名（説明）
            CREATE_INSTANT_INVITE   招待を作成（この権限を持つメンバーは招待を作成できます。）
            KICK_MEMBERS	        メンバーをキック（この権限を持つメンバーはメンバーをキックする権限を持ちます。）
            BAN_MEMBERS	            メンバーをBAN（この権限を持つメンバーはメンバーをBANする権限を持ちます。）
            ADMINISTRATOR	        管理者（この権限を持つメンバーはすべての権限を持ち、チャンネル固有の権限もバイパスします。この権限を付与することは 危険です。）
            MANAGE_CHANNELS	        チャンネルの管理（この権限を持つメンバーは、新しいチャンネルを作成、編集またはチャンネルを削除できます。）
            MANAGE_GUILD	        サーバー管理（この権限を持つメンバーは、サーバー名の変更と地域の変更ができます。）
            ADD_REACTIONS	        リアクションの追加（この権限を持つメンバーは、メッセージへの新たなリアクションを加えることができます。この権限がなくても、メンバーは メッセージにすでに付いている選択肢から、リアクションをすることが可能です。）
            VIEW_AUDIT_LOG	        監視ログの表示（この権限が与えられたメンバーは、サーバーの監査ログへのアクセス権を保有します。）
            PRIORITY_SPEAKER	    優先スピーカー（この権限を持つユーザーは、話しが聞き取られやすくなります。有効にすると、 この権限のない他の人の音量は自動的に下がります。優先スピーカーは、プッシュツートーク（優先）キーバインドを使用して有効化されます。）
            STREAM	                動画（権限を持つメンバーは、このサーバーで配信できます。）
            VIEW_CHANNEL	        テキストチャンネルの閲覧＆ボイスチャンネルの表示（この権限を持つメンバーは、テキストチャンネルの閲覧とボイスチャンネルの表示ができます。）
            SEND_MESSAGES	        メッセージを送信（この権限を持つメンバーは、メッセージを送信することができます。）
            SEND_TTS_MESSAGES	    TTSメッセージを送信（この権限を持つメンバーは、/ttsをメッセージの始めに付けることで、テキスト読み上げ機能のメッセージを送ることができます。このメッセージはチャンネル内の全員が聞くことが 可能です。）
            MANAGE_MESSAGES	        メッセージの管理（この権限を持つメンバーは、他のメンバーのメッセージを削除したり、任意のメッセージをピン留めしたりできます。）
            EMBED_LINKS	            埋め込みリンク（この権限を持つメンバーは、埋め込みリンクを送信することが可能です。）
            ATTACH_FILES	        ファイルを添付（この権限を持つメンバーは、ファイルを添付することが可能です。）
            READ_MESSAGE_HISTORY	メッセージ履歴を読む（この権限を持つメンバーは、メッセージの履歴を読むことができます。）
            MENTION_EVERYONE	    @everyone、@here、全てのロールにメンション（この権限のあるメンバーは、@everyone または @here を使って、このチャンネルの全メンバーにpingできます。また、全てのロールに @mention できます。これはロールの「全員がこのロールにメンションすることを許可する」が無効化されている場合でも有効です。）
            USE_EXTERNAL_EMOJIS	    外部の絵文字の使用（この権限を持つメンバーは、このサーバー上で他のサーバーの絵文字を使うことができます。）
            VIEW_GUILD_INSIGHTS	    不明（この権限を持つメンバーは、サーバーのインサイトにアクセスすることができます。）
            CONNECT	                接続（この権限を持つメンバーは、音声チャネルに接続することができます。）
            SPEAK	                発言（この権限を持つメンバーは、音声チャネルで発言することができます。）
            MUTE_MEMBERS	        メンバーをミュート（この権限を持つメンバーは、音声チャネルでメンバーをミュートとできます。）
            DEAFEN_MEMBERS	        メンバーのスピーカーをミュート（この権限を持つメンバーは、音声チャンネルのメンバーのスピーカーをオフにできます。）
            MOVE_MEMBERS	        メンバーの移動（この権限を持つメンバーは、他のメンバーをこのチャンネルから移動させることができます。移動が可能となるチャンネルは、この権限を持つメンバー、そして移動させられるメンバー両方がアクセス権を持つ チャンネルのみです。）
            USE_VAD	                音声検出を使用（この権限が無効の場合、メンバーはプッシュツートークをチャンネル内で使用しなければいけなくなります。）
            CHANGE_NICKNAME	        ニックネームの変更（この権限を持つユーザーは、自分のニックネームを変更することができます。）
            MANAGE_NICKNAMES	    ニックネームの管理（この権限を持つユーザーは、他のメンバーのニックネームを変更することができます。）
            MANAGE_ROLES	        ロールの管理（この権限を持つメンバーは、新しくロールを作成したり、自身よりも下位の権限を編集または削除することができます。）
            MANAGE_WEBHOOKS	        ウェブフックの管理（この権限を持つメンバーは、ウェブフックを作成、編集および削除することができます。）
            MANAGE_EMOJIS	        絵文字の管理（この権限を持つユーザーは、絵文字を管理することができます。）
'''