這是一篇 Discord Bot 的進階教學,會使用 [Python Cog 架構](https://hackmd.io/@smallshawn95/python_discord_bot_cog)撰寫。 在之前的 View 教學中,曾經提到 Item 元素跟 View 息息相關,此次教學要來詳細介紹和實作 Discord 中的 Button,如果還沒看過 View 教學的讀者可以點此前往,[Python Discord Bot 進階教學 — View 篇](https://hackmd.io/@smallshawn95/python_discord_bot_view)。 :::success :book: **更多 Python Discord Bot 教學系列和程式範例** https://github.com/smallshawn95/Python-Discord-Bot-Teach.git ::: --- [TOC] --- ## 一、Button 簡介: Discord Button 於 2021 年 10 月推出,具有多種使用方式,可以傳送訊息、觸發特定事件、將使用者傳送到指定網址等等。 Discord Button 推出之前,開法者如果想要讓 Bot 可以利用 GUI 介面來實現投票、換頁、切歌等等功能,通常只能使用簡單的 Emoji,並且需要訊息輔助說明每個 Emoji 的用途,而現在的 Button 則在一開始就可以設定顯示文字、顏色等等,讓使用者可以直觀了解用途,也讓開發者可以根據自己需求創作。 更多詳細資訊可以參考 [Discord.py Button 官方文檔](https://discordpy.readthedocs.io/en/stable/interactions/api.html#id1)。 ## 二、Button 撰寫: ### Class 使用時機為函式中直接宣告時,設置 `url` 參數會變超連結按鈕,撰寫 Button 交互事件需要新增一個回呼函式或者搭配持續監聽事件。 ```python discord.ui.Button(label: str, style: discord.ButtonStyle, emoji: Union[PartialEmoji, Emoji, str], custom_id: str, disabled: bool, url: str, row: int) ``` * **label 標籤** Button 顯示的文字。 * **style 風格** Button 的風格,參考[註一](#註一)。 * **emoji 表情符號** Button 的表情符號。 * **custom_id 交互 ID** 交互期間收到的 Button ID,如果設置為 URL Button 則沒有自定義 ID。 * **disabled 禁用** Button 是否禁用。 * **url 網址** Button 導航使用者前往的網址。 * **row 相對行號** Discord 訊息允許 Item 元素最多 5 行,預設情況會自動排序,如果要控制相對位置,則行號 row = 1 會在 row = 3 之前,行號必須介於 0 到 4 之間。 ### 裝飾器 使用時機為添加在 View 類別中,撰寫 Button 交互事件只需要在裝飾器底下即可。 ```python @discord.ui.button(label: str, style: discord.ButtonStyle, emoji: Union[PartialEmoji, Emoji, str], custom_id: str, disabled: bool, row: int) ``` * **label 標籤** Button 顯示的文字。 * **style 風格** Button 的風格,參考[註一](#註一)。 * **emoji 表情符號** Button 的表情符號。 * **custom_id 交互 ID** 交互期間收到的 Button ID,如果設置為 URL Button 則沒有自定義 ID。 * **disabled 禁用** Button 是否禁用。 * **row 相對行號** Discord 訊息允許 Item 元素最多 5 行,預設情況會自動排序,如果要控制相對位置,則行號 row = 1 會在 row = 3 之前,行號必須介於 0 到 4 之間。 ## 三、Button 交互方式: ### 回呼函式(Callback) 額外撰寫一個交互函式,並讓 Button 呼叫此函式。 | 優點 | 缺點 | | :-: | :-: | | 多個按鈕可以共用同個函式 | 可讀性較差,查看按鈕功能需要自行對照函式 | * 回呼函式 ```python= async def button_callback(interaction: discord.Interaction): await interaction.response.edit_message(content = "Hello, world!") ``` * 主函式 ```python= @app_commands.command(name = "button_interaction_callback", description = "Button 回呼函式交互") async def button_interaction_callback(self, interaction: discord.Interaction): # 宣告 View view = discord.ui.View() # 使用 class 方式宣告 Button button = discord.ui.Button( label = "Click", style = discord.ButtonStyle.blurple ) # Button 連接回呼函式 button.callback = button_callback # 將 Button 添加到 View 中 view.add_item(button) await interaction.response.send_message(view = view) ``` ![](https://hackmd.io/_uploads/Bkp-JSGK2.png) ![](https://hackmd.io/_uploads/B11AkSMYh.png) ### 被裝飾函式(Decorator) 在 Button 裝飾器底下撰寫一個交互函式。 | 優點 | 缺點 | | :---: | :---: | | 按鈕與函式綁定,不用額外對照函式 | 多一個 `button: discord.ui.Button` 參數 | * 自定 View ```python= class ButtonView(discord.ui.View): def __init__(self, timeout: float | None = 180): super().__init__(timeout = timeout) # 使用裝飾器方式創建 Button,交互函式直接寫在裝飾器底下 @discord.ui.button( label = "Click", style = discord.ButtonStyle.blurple ) async def button_decorator(self, interaction: discord.Interaction, button: discord.ui.Button): await interaction.response.edit_message(content = "Hello, world!") ``` * 主函式 ```python= @app_commands.command(name = "button_interaction_decorator", description = "Button 被裝飾函式交互") async def button_interaction_decorator(self, interaction: discord.Interaction): # 宣告自定 View view = ButtonView() await interaction.response.send_message(view = view) ``` ![](https://hackmd.io/_uploads/B1rOX_fYn.png) ![](https://hackmd.io/_uploads/B1ncX_GY3.png) ### 持續監聽事件(Event Listener) `on_interaction()` 是一個交互持續監聽事件,幫 Button 自定義設置 `custom_id` 參數,比對交互時的 `custom_id` 即可執行相對應的操作。 | 優點 | 缺點 | | :---: | :---: | | 按鈕不會超時,可以製作永久按鈕 | 需要設置 `custom_id` 參數,按鈕操作塞在同一函式的可讀性較差 | * 持續監聽函式 ```python= @commands.Cog.listener() async def on_interaction(self, interaction: discord.Interaction): # interaction.data 是一個包含交互資訊的字典 # 有些交互不包含 custom_id,需要判斷式處理來防止出錯 if "custom_id" in interaction.data: if interaction.data["custom_id"] == "hello_world": await interaction.response.edit_message(content = "Hello, world!") ``` * 主函式 ```python= @app_commands.command(name = "button_interaction_on", description = "Button 持續監聽交互") async def button_interaction_on(self, interaction: discord.Interaction): # 宣告 View view = discord.ui.View() # 使用 class 方式宣告 Button 並設置 custom_id button = discord.ui.Button( label = "Hello, world!", style = discord.ButtonStyle.blurple, custom_id = "hello_world" ) # 將 Button 添加到 View 中 view.add_item(button) await interaction.response.send_message(view = view) ``` ![](https://hackmd.io/_uploads/rJJmskxcn.png) ![](https://hackmd.io/_uploads/SkTrokl9h.png) ## 四、文章註解: ### 註一 底下列出 Discord Button 的所有風格,詳細內容可參考 [Discord.py ButtonStyle 官方文檔](https://discordpy.readthedocs.io/en/stable/interactions/api.html#discord.ButtonStyle)。 | 名稱 | 樣式 | | :-----: | :-----: | | primary / blurple | 藍色 | | secondary / grey / gray | 灰色 | | success / green | 綠色 | | danger / red | 紅色 | | link / url | 超連結 | ![](https://hackmd.io/_uploads/SJ0zsVMY2.png) --- :::info 📢 **歡迎加入我的 Discord 伺服器** https://discord.gg/Jtd3eVrFJs ::: *Copyright © 2023 SmallShawn95. All rights reserved.*