Discordの新機能「モーダルウィンドウ」をPythonから使ってみよう

# なんか面白そうな機能ｷﾀ――(ﾟ∀ﾟ)――!!
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107984/1e5ae8a8-583f-2fdc-f8fc-6b2b5b70f45b.png)
引用: [Discord Developer Portal](https://discord.com/developers/docs/interactions/message-components#text-inputs)

Qiita2回目の初心者です。
ということで、テキスト入力をさせるモーダルウィンドウを表示させることができるようになりました。
おもろいなぁって思ったので、メモ程度にQiita書かせていただきます。
また、環境はnextcordですが、Discord.pyとかpycordとかでも、**バージョン2.0**以上なら使えるので、`import`の名前とかをAPIリファレンス又は勘をもとにリファクタリングしてください。

# 百聞は一見に如かず
まずは、コードを見てみましょう。

```py:food.py
import nextcord
from nextcord.ext import commands
from nextcord import Interaction

bot = commands.Bot()


class FavoriteFoodModal(nextcord.ui.Modal):
    def __init__(self):
        super().__init__(
            "好きな食べ物は何ですか？",
            timeout=None,
        )

        self.food = nextcord.ui.TextInput(
            label="食べ物",
            style=nextcord.TextInputStyle.short,
            placeholder="おにぎり",
            required=True,
        )
        self.add_item(self.food)

    async def callback(self, interaction: Interaction) -> None:
        await interaction.response.send_message(f"好きな食べ物は{self.food.value}なんだね！")
        return


@bot.slash_command(name="food", description="好きな食べ物を聞きます")
async def food_slash(interaction: Interaction):
    modal = FavoriteFoodModal()
    await interaction.response.send_modal(modal=modal)

bot.run("abcdefghijklmnopqrstuvwxyz.1234567890.abcdefghijklmnopqrstu")
```

モーダルウィンドウは、`スラッシュコマンド`又は`ボタンなどのUI`のインタラクションにて表示させることができます。
上記コードの場合、スラッシュコマンドの`/food`を送信すると、`FavoriteFoodModal`のインスタンスを作成して、それを`interaction.response.send_modal`という関数に渡して実行されていって下のように表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107984/9fd393f4-99f6-734c-a171-9aaa31371662.png)
明らかに「レバニラ」とかを要求してそうなモーダルの完成です。
試しに「レバニラ」なんて入力してあげると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1107984/188c8abd-b9fd-9cbe-d7d5-203c56686b0d.png)
という風に、入力された値が返ってきます。

# ...ちょっとだけでいいから要約して。
まず、`nextcord.ui.Modal`クラスを継承したクラスを作ります。
`__init__`の中には、モーダルウィンドウのタイトルとタイムアウト時間を指定します。
次に`self`内に`nextcord.ui.TextInput`クラスを、ラベルなどのパラメーターの値を渡してインスタンス化します。
そしたら`self.add_item`関数に先ほどの`TextInput`を渡せば完了です。
モーダルで送信が行われた際に`callback`関数が実行されます。
値の取得は、`self`内の先ほどインスタンス化した`TextInput`の`value`というやつが値らしいので、そっから取れます。
で、あとはメッセージ送信をするなり煮るなり焼くなりしてあげてください。


# もうちょっとだけ詳しい仕様を解説するQ&A風コーナー
**Q.** モーダルウィンドウには複数個のテキストボックスをつけることができますか？
**A.** できます。

**Q.** ちなみに何個まで？
**A.** モーダルには最大で**5つ**のテキストボックスをつけることができます。

**Q.** さっき言ってた`ラベルなどのパラメーター`って実際には何があるの？
**A.** なんかたくさんあります。
パラメーター|変数型|説明
---|---|---
label|`str`|テキストボックスの上のラベル文
style|`nextcord.TextInputStyle`|テキストボックスのスタイル(後述)
*custom_id|`str`|インタラクションに関するカスタムID
*row|`int`|モーダルの中でのテキストボックスの位置(0-4)
*min_length|`int`|最小文字数
*max_length|`int`|最大文字数
*required|`bool`|必須項目にするかどうか
*default_value|`str`|最初から入力されてるデフォルトの値
*placeholder|`str`|後ろにうすーく見えるプレースホルダーの値

**Q.** テキストボックスのスタイルって？(`nextcord.TextInputStyle`って？)
**A.** 1行だけのテキストボックス(`nextcord.TextInputStyle.short`)か、複数行入力可能なテキストボックス(`nextcord.TextInputStyle.paragraph`)かの違いです。
HTMLやってる人にわかりやすく伝えるなら、`<input type="text">`か、`<textarea>`かの違いです。

**Q.** 必須項目じゃないところで、なにも入れずに送信したらどうなるの？
**A.** `value`に`""`(何もない文字列)が返ってきます。

**Q.** スマートフォン/タブレット版Discordには対応してるの？
**A.** なぜか画面全体を覆いますが、普通にモーダルが表示されます。

**Q.** 上のコードコピペしたけど動かない！
**A.** もし、あなたの使っているラッパーが`nextcord`でなければ、`nextcord`というところを`discord`に置き換えてみてください。(ごめんなさいあとは知りません)

**Q.** 自分で追加してみたけど`Command raised an exception: TypeError: Object of type TextInput is not JSON serializable`ってエラーが出て動かない！
**A.** 私も騙されましたが、`TextInput`クラスをインスタンス化する際に、`self.title`とかを使うと、そのエラーが発生します。
理由としては多分`nextcord.ui.Modal`のクラス内で使われてるパラメーターを指定すると、当然それを`TextInput`に置き換えて、それをサーバーに送信しようとしてなんか...こう...エラーが...ってことなんでしょう。
とりあえず、使いがちですが`title`などのパラメーターの名称は使わないようにしましょう。
パラメーターの名称などはAPIリファレンスとかを見れば大体わかります。
~~自分でDiscord Developer Portal眺めて頑張って解決したんです。褒めてくだs((~~


# 今後に期待
`ui.Modal`クラスと`ui.TextInput`クラスが分かれてるってことは、今後はラジオボタンとかチェックボックスとかをモーダルに配置できるようになるんでしょうかね...
そうなったら結構面白そうですよね～(個人の感想)
あと、テキストボックスって機能があるだけで、画像認証BOTなんかはさらなる機能向上とか出来ますよね？ね？(同業者からの圧)


# おしまい
まず、記事内にミスがあったらご容赦ください。コメントでなんか言ってもらえたら嬉しいです。
今後はDiscordBOTに関する内容とかを、後世に残すつもりでQiitaで書いていけたらなーって思ってたりしてます。
私自身の作成しているDiscordBOTは、[にらBOT](https://github.com/team-i2021/nira_bot)というもので、まぁ...地道に頑張ってるので...少しでも応援していただいたら嬉しいです...
もし出来るなら、皆さんも一緒にプログラミングをして、周りから「チョット出来る奴」みたいな目で見てもらえるようになりましょう！
では！