Custom prefix を導入しよう！！

#最初に
この記事書くとき、いろいろな作業が終わってからで、いろいろめんどくさがってるからいろいろと雑になってるよ！ごめんね！
※一応PEP8に準拠した書き方です。

#開発環境
・エディタ - Atom
・実行シェル - PlatformIO IDE Terminal(Atomの拡張機能)
｜ただのコマンドプロンプトです。
・python - Python 3.9.1
・discord.py - discord.py 1.5.1

# 本題
##コード

```py
# 必要なモジュールをインポート
import json
import discord
from discord.ext import commands


# 必須の変数の設定
default_prefix = 'ここにデフォルトとするprefixを入力'
TOKEN = 'ここにTOKENを入力'


# jsonファイルをロード
prefix_json = None
with open('prefix.json', encoding='UTF-8') as f:
    prefix_json = json.load(f)


def custom_prefix(bot: commands.Bot, msg: discord.Message):
    # コマンドが実行されたサーバーでカスタムprefixが設定されていれば対応するprefixを返す
    if str(msg.guild.id) in prefix_json.keys():
        return prefix_json[str(msg.guild.id)]

    # コマンドが実行されたサーバーでカスタムprefixが設定されていなければデフォルトprefixを返す
    else:
        return default_prefix


# botという変数に情報を格納
bot = commands.Bot(command_prefix=custom_prefix)
# intents は各自設定してください


# botが起動したら実行
@bot.event
async def on_ready():
    print('ready')


# testコマンド ※このコマンドは削除しても大丈夫です
@bot.command()
async def test(ctx):
    print('success')
    return


# prefixを変更するコマンド　※エイリアスとして cp が設定されています。
@bot.command(aliases=['cp'])
async def change_prefix(ctx, new_prefix: str):
    # コマンドが実行されたサーバーでカスタムprefixが設定されていれば実行
    if str(ctx.message.guild.id) in prefix_json.keys():

        # dictからコマンドを実行したサーバーのカスタムprefix情報を削除
        prefix_json.pop(str(ctx.message.guild.id))
        # dictにコマンドを実行したサーバーのカスタムprefix情報を追加
        prefix_json[str(ctx.message.guild.id)] = new_prefix

        # jsonファイルにdict情報を記入
        with open('prefix.json', 'w', encoding='UTF-8') as f:
            f.write(json.dumps(prefix_json))

        # 完了メッセージ
        print(f'{ctx.message.guild.name} のprefixが{prefix_json[str(ctx.message.guild.id)]}に変更されました')
        return

    else:
        # dictにコマンドを実行したサーバーのカスタムprefix情報を追加
        prefix_json[str(ctx.message.guild.id)] = new_prefix

        # jsonファイルにdict情報を記入
        with open('prefix.json', 'w', encoding='UTF-8') as f:
            f.write(json.dumps(prefix_json))

        # 完了メッセージ
        print(f'{ctx.message.guild.name} のprefixが{prefix_json[str(ctx.message.guild.id)]}に変更されました')
        return


bot.run(TOKEN)
```

##解説
コードのほうでもコメントで軽く解説してあるので大丈夫だと思いますが、念のためもうちょい詳しく解説。

###モジュールのインポート

```py
import json
import discord
from discord.ext import commands
```

こちら、上から
・jsonファイルに手を加えるために必要なモジュール
・discord.pyをやるうえで必須なモジュール
・コマンドフレームワークをやるうえで必須なモジュール
です。

###jsonファイル関連

```py
prefix_json = None
with open('prefix.json', encoding='UTF-8') as f:
    prefix_json = json.load(f)
```

・最初に`prefix_json = None`と記入しているのはグローバル変数にしたいからです。もしかしたら必要ないかも。
・`with`を使って`f`という変数に`prefix.json`というファイルの情報を格納しています。開発環境によっては`encoding`を指定しないとエラー吐きます。


###command_prefix

```py
def custom_prefix(bot: commands.Bot, msg: discord.Message):
    if str(msg.guild.id) in prefix_json.keys():
        return prefix_json[str(msg.guild.id)]

    else:
        return default_prefix
```

~~command_prefixって関数指定できるんですね。知らなかった。~~
・jsonファイルをロードしたときに取得したdict型変数(以下`prefix_json`)のkeysにコマンドを実行したサーバーのIDが入ってれば、それに対応したprefixを返します。入ってなければ上のほうで設定された`default_prefix`を返します。
｜jsonはkeyにint型の要素を指定できないのでstr型に変換してます。

###Botが起動したら

```py
@bot.event
async def on_ready():
    print('ready')
```

これはほとんどのD.pyプログラムに入ってますね。Botの準備が完了すると実行されるやつです。実行するプログラムは任意のものに変えてもらってOkです。

###うまく動作できてるか確認

```py
@bot.command()
async def test(ctx):
    print('success')
    return
```

ちゃんとうまいこといってるかどうかを確認するためにこのコマンドを実装しています。

###本体

```py
@bot.command(aliases=['cp'])
async def change_prefix(ctx, new_prefix: str):
    if str(ctx.message.guild.id) in prefix_json.keys():

        prefix_json.pop(str(ctx.message.guild.id))
        prefix_json[str(ctx.message.guild.id)] = new_prefix

        with open('prefix.json', 'w', encoding='UTF-8') as f:
            f.write(json.dumps(prefix_json))

        print(f'{ctx.message.guild.name} のprefixが{prefix_json[str(ctx.message.guild.id)]}に変更されました')
        return

    else:
        prefix_json[str(ctx.message.guild.id)] = new_prefix

        with open('prefix.json', 'w', encoding='UTF-8') as f:
            f.write(json.dumps(prefix_json))

        print(f'{ctx.message.guild.name} のprefixが{prefix_json[str(ctx.message.guild.id)]}に変更されました')
        return
```

＿人人人人＿
＞急に長え＜
￣^Y^Y^Y^Y￣　powerd by `double_alt_death`

上から順番に解説してくよー

`if str(ctx.message.guild.id) in prefix_json.keys()`は、コマンドを実行したサーバーでカスタムprefixが設定されたらTrue返すやつですね。判断方法として、prefix_jsonっていうdictのkeysに鯖のID（str型）があったら、カスタムprefixが設定されてるってことになるから、それで判断するってだけですね。Too easy.

`prefix_json.pop(str(ctx.message.guild.id))`は、prefix_jsonっていうdictからpop関数を使って要素を削除するっていうやつですね。更新しないといけないからね。うん。
`prefix_json[str(ctx.message.guild.id)] = new_prefix`は、prefix_jsonに要素を追加するってやつ。dictの使い方知ってる人なら何してるか理解できると思う。

`with open('prefix.json', 'w', encoding='UTF-8') as f:
　　f.write(json.dumps(prefix_json))`は、更新されたprefix_jsonをバックアップのためにjsonファイルに書き込むだけです。これがないとBotを再起動したときに全部リセットされます。
これの下のprintはprefixが変わったっていうのをお知らせするだけだからなんでもいいね。

じゃあ次`else:`以下のやつ。
`prefix_json[str(ctx.message.guild.id)] = new_prefix`は、prefix_jsonに要素を追加するだけ。上のやつと違ってまだprefix_jsonに要素がないから削除する必要ないね。
それよりしたのwith ~とかは上と全く同じなのでパス。

はい！解説終わり！！！

#注意事項
もしHerokuでBotを動かしている場合、確かHerokuはread onlyなので、読み込むことはできますが書き込むことができません。そのため、カスタムprefixを保存することができないので、ご注意ください。
もしかしたらなんかやる方法あるかもですが。

#後書き
前に一回くそみたいなカスタムprefixのコードかいてとあるサーバーの方たちに怒られたので下のほうにかいてある記事を参考にしていい感じにやってみたらある程度認めてもらえました。最初は書き方がPEP8に準拠してないって怒られましたが。これから普通にプログラミングしてくときに気を付けたい。
一応Githubにも同じようなの公開してるからよかったら見てね
[Githubへはここから！](https://github.com/Req-kun/custom_prefix)

#参考にした記事
[discord.pyでカスタムprefixを実装しよう！](https://qiita.com/disneyresidents/items/341b12be26efbb8a1a90)
[【初心者必見！】PythonでJSONを扱う方法をわかりやすく解説！](https://www.sejuku.net/blog/41293)