# Discord bot 教學筆記(修改日期:2023/09/17): ## 0. 前置作業: * ### **Discord bot 建置:** 1. 點選此 [連結](https://discord.com/developers/applications),並登入 2. 點選畫面右上角的**New Application**建立新的app(截圖時間:2022/11/22):![](https://i.imgur.com/GaigSpl.png) 3. 切換至Bot頁面,移動至下面將3個選項都打開(截圖時間:2022/11/22): ![](https://cdn.discordapp.com/attachments/1152777088814825592/1152777217525420043/Screenshot_2023-09-17_092137.png) ![](https://i.imgur.com/9JuOtJH.png) 4. 移動至上面,點擊下面的Reset token取得密鑰(token)(截圖時間:2022/11/22) ![](https://i.imgur.com/7A6QVSJ.png) 5. 下面那一串就是密鑰(token),先另外保存起來,等等會用到(截圖時間:2022/11/22): (請不要外流出去,擁有密鑰(token)就等於擁有bot) ![](https://i.imgur.com/8IH9pNy.png) 6. 切換至**OAuth2 -> URL Generator**,並勾選以下3個選項(截圖時間:2022/11/22): ![](https://cdn.discordapp.com/attachments/1152777088814825592/1152787852275490816/1.png) ![](https://i.imgur.com/yaJKwt3.png) 7. 按下**右下角的按鈕並前往該網址**(截圖時間:2022/11/22): ![](https://i.imgur.com/ZXcxs2O.png) 8. 選擇要將Bot邀請至哪一個伺服器,並按下**繼續**(截圖時間:2022/11/22): **(必須要有伺服器的管理權限才能夠邀請Bot)** ![](https://i.imgur.com/DSbQlNd.png) --- * ### **開啟Discord 開發者模式:** 1. 點擊畫面左下角的**使用者設定**(截圖時間:2023/09/17): ![](https://cdn.discordapp.com/attachments/1152777088814825592/1152779166186479636/1.png) 2. 點擊**進階**(截圖時間:2023/09/17): ![](https://cdn.discordapp.com/attachments/1152777088814825592/1152780639939072160/1.png) 4. 將**開發者模式打開**(截圖時間:2023/09/17): ![](https://cdn.discordapp.com/attachments/1152777088814825592/1152780842440077322/1.png) --- * ### **取得Discord Bot Application ID(需開啟開發者模式):** 1. 邀請至伺服器後,對Bot按**右鍵->複製使用者ID**(截圖時間:2023/09/17): ![](https://cdn.discordapp.com/attachments/1152777088814825592/1152782395100123156/1.png) 2. 或者是在上面**Discord bot 建置**時的**General Information**頁面也能夠取得: (截圖時間:2023/09/17) ![](https://cdn.discordapp.com/attachments/1152777088814825592/1152783159843360828/1.png) --- * ### **取得Discord文字頻道ID(需開啟開發者模式):** 1. 在已建立的文字頻道上按**右鍵->複製頻道ID**(截圖時間:2023/09/17): ![](https://cdn.discordapp.com/attachments/1152777088814825592/1152783894370865303/1.png) --- * ### **取得Discord 使用者ID(需開啟開發者模式):** 1. 對自己按**右鍵->複製使用者ID**(截圖時間:2023/09/17): ![](https://cdn.discordapp.com/attachments/1152777088814825592/1152786703573065831/1.png) --- * ### **開發環境建置:** 建議使用VS Code來開發 或者是其他能夠執行 python(**3.8或以上**) 的IDE都可以,並執行以下指令來安裝discord py:(以下來自於官方文檔) ``` To install the library without full voice support, you can just run the following command: # Linux/macOS python3 -m pip install -U discord.py # Windows py -3 -m pip install -U discord.py --------------------------------------------------------------------- Otherwise to get voice support you should run the following command: # Linux/macOS python3 -m pip install -U "discord.py[voice]" # Windows py -3 -m pip install -U discord.py[voice] --------------------------------------------------------------------- To install the development version, do the following: $ git clone https://github.com/Rapptz/discord.py $ cd discord.py $ python3 -m pip install -U .[voice] ``` ### 1.第一次使用bot: ```python= #main.py import aiohttp import os from discord.ext import commands from discord import Intents class client(commands.Bot): def __init__(self,**options): super().__init__( command_prefix="-", intents = Intents.all(), application_id = '請輸入bot的id(數字)', **options ) async def setup_hook(self): self.session = aiohttp.ClientSession() #載入資料夾commands裡面的py檔案 for filename in os.listdir('./commands'): if filename.endswith('.py'): await bot.load_extension(f'commands.{filename[:-3]}') #載入插件 async def on_ready(self): #當bot準備好時執行(不保證是第一個執行) print('bot已上線') if __name__ == "__main__": bot = client() bot.run('請輸入bot的token') ``` ### 2.Cog: 有點類似於模組的概念,將監聽器、指令、參數打包在一個class裡面,以下範例建立了Apple的cog: ```python= #Apple.py from discord.ext import commands class Apple(commands.Cog): def __init__(self,bot:commands.Bot): self.bot = bot async def setup(bot:commands.Bot): #一定要有setup函數 await bot.add_cog(Apple(bot)) ``` ### 3.基本指令(以斜槓指令為主): 顧名思義就是以 斜槓(/) 來觸發指令,而不是用前綴符號來觸發指令 可參考 [連結](https://gist.github.com/AbstractUmbra/a9c188797ae194e592efe05fa129c57f) 關於斜槓指令的同步 ```python= #Apple.py from discord.ext import commands from discord import app_commands,Object class Apple(commands.Cog): def __init__(self,bot:commands.Bot): self.bot = bot @app_commands.command(name = "hi", description="說你好") async def hi(self,interaction:Interaction): await interaction.response.send_message("你好") #如果只想要讓打指令的人看到可以加上 ephemeral=True await interaction.response.send_message("你好",ephemeral=True) #interaction 僅有效3秒鐘,且每一個interacion僅能回覆一次 @app_commands.command(name = "say", description="說話") async def say(self,interaction:Interaction): await interaction.response.defer() #可將interaction有效時間延長為15分鐘 await interaction.followup.send_message("你好") async def setup(bot:commands.Bot): #如需指定特定的伺服器擁有此指令的話,需添加guild/guilds參數 await bot.add_cog(Apple(bot),guild = Object(id = '伺服器ID(數字)')) ``` ### 4.基本UI使用範例(按鈕、下拉式選單): 此範例演示了如何直接繼承View來實作UI畫面: 僅能置入Button、Select(2022.11.22截稿前) ```python= #Apple.py #使用直接繼承View from discord import ButtonStyle from discord.ui import Button,View class AppleView(View): def __init__(self): super().__init__(timeout = None) self.add_base_button() def add(self,item,callback = None): self.add_item(item) #將ui物件加入至View裡面 if callback is not None: item.callback = callback #對ui物件添加回呼函數(當使用者跟ui物件互動時觸發) return item async def next(self,interaction:Interaction): await interaction.response.defer(ephemeral=True) self.start += int(interaction.data.get('custom_id')) self.end += int(interaction.data.get('custom_id')) self.ui_control() await interaction.followup.edit_message(interaction.message.id,embeds = self.embed_list[self.start:self.end],view=self) def add_base_button(self): self.add(Button(style = ButtonStyle.green,label = "前十首",emoji="⏮️",custom_id="-10"),self.next) self.add(Button(style = ButtonStyle.green,label = "下十首",emoji="⏭️",custom_id="10"),self.next) self.ui_control() def ui_control(self): #透過self.children來抓取已添加的ui物件 disabled來修改物件是否啟用 False啟用 True不啟用 self.children[0].disabled = False self.children[1].disabled = False if self.start == 0: self.children[0].disabled = True if len(self.embed_list) <= self.end: self.children[1].disabled = True ``` 此範例演示了如何使用自訂class來間接繼承View實作UI畫面: ```python= #Apple.py #透過自訂class間接繼承 from discord import ButtonStyle,Interaction from discord.ui import Button,View,Modal,TextInput,Select import abc class CustomView(View,metaclass=abc.ABCMeta): #自訂class def __init__(self,timeout): super().__init__(timeout = timeout) def add(self,item,callback = None): self.add_item(item) #將ui物件加入至View裡面 if callback is not None: item.callback = callback #對ui物件添加回呼函數(當使用者跟ui物件互動時觸發) return item @abc.abstractmethod def add_base_button(self): return NotImplemented @abc.abstractmethod def ui_control(self): return NotImplemented class AppleView(CustomView): #繼承此自訂class def __init__(self,embed_list): super().__init__(timeout = None) self.embed_list = embed_list self.start = 0 self.end = 10 self.add_base_button() async def next(self,interaction:Interaction): await interaction.response.defer(ephemeral=True) self.start += int(interaction.data.get('custom_id')) self.end += int(interaction.data.get('custom_id')) self.ui_control() await interaction.followup.edit_message(interaction.message.id,embeds = self.embed_list[self.start:self.end],view=self) def add_base_button(self): self.add(Button(style = ButtonStyle.green,label = "上十項",emoji="⏮️",custom_id="-10"),self.next) self.add(Button(style = ButtonStyle.green,label = "下十項",emoji="⏭️",custom_id="10"),self.next) self.ui_control() def ui_control(self): #透過self.children來抓取已添加的ui物件 disabled來修改物件是否啟用 False啟用 True不啟用 self.children[0].disabled = False self.children[1].disabled = False if self.start == 0: self.children[0].disabled = True if len(self.embed_list) <= self.end: self.children[1].disabled = True ``` 將View送出: ```python= @app_commands.command(name = "send",description = "送出View") async def send(self,interaction:Interaction): embed_list = [] await interaction.response.send_message(view = AppleView(embed_list)) ``` 下拉式選單Select: 依照官方文件上所寫僅能添加25個選項(2022.11.22截稿前) ```python= from discord import Interaction,SelectOption,app_commands from discord.ui import View,Select,Item from discord.ext import commands class FruitSelectView(View): def __init__(self): self.fruit = Select(placeholder="選擇你最喜歡的水果?",options=[SelectOption( label="香蕉", value="Banana", emoji="🍌" ),SelectOption( label="蘋果", value="Apple", emoji="🍎" )]) self.add(self.fruit,self.select_callback) async def select_callback(self,interaction:Interaction): fruit = self.fruit.values[0] #所選的選項都會在values的list裡面 await interaction.respose.send_message(f"你選的水果是{fruit}") def add(self,item:Item,callback): self.add_item(item) item.callback=callback class Apple(commands.Cog): def __init__(self): pass @app_commands.command(name = "fruit",description = "選取喜歡的水果") async def fruit(self,interaction:Interaction): await interaction.response.send_message(view = FruitSelectView()) ``` Modal(彈出視窗): 僅支援置入TextInput(2022.11.22截稿前) ```python= from discord.ui import Modal,TextInput,TextStyle from discord import Interaction class NameModal(Modal): def __init__(self): super().__init__(title = "報名表單") self.name = TextInput(label='姓名:', style = TextStyle.short,default=f"XXX") #輸入的值會存在變數中 self.phone = TextInput(label='電話:', style = TextStyle.short,default=f"0900123123") self.add_item(self.name) self.add_item(self.phone) async def on_submit(self, interaction:Interaction): #視窗提交時觸發 await interaction.response.send_message(f"姓名:{self.name} ,電話:{self.phone}") ``` 送出Modal: ```python= @app_commands.command(name = "Sign_up",description = "報名") async def Sign_up(self,interaction:Interaction): await interaction.response.send_modal(NameModal()) ``` ### 5.案例說明: 1. Q:如何使用時間戳(timestamp)來自動倒數計時? A:透過與現在時間的相對時間,讓Discord來自動維護,達成自動倒數計時的目的,將其當作字串放入即可 [連結](https://github.com/discord/discord-api-docs/blob/ff4d9d8ea6493405a8823492338880c47fb02996/docs/Reference.md#timestamp-styles) ```txt= <t:1624385691:t> 02:14 <t:1624385691:T> 02:14:51 <t:1624385691:d> 2021/06/23 <t:1624385691:D> 2021年6月23日 <t:1624385691:f> 2021年6月23日 02:14 <t:1624385691:F> 2021年6月23日星期三 02:14 <t:1624385691:R> 1 年前 ``` --- 2. Q:我想做一個四選一的單選題,我如何知道使用者按了哪一個按鈕? A:將按鈕設定custom_id,再透過interaction.data.get('custom_id')來確定按了哪一個按鈕 ```python= async def next(self,interaction:Interaction): self.start += int(interaction.data.get('custom_id')) self.end += int(interaction.data.get('custom_id')) def add_base_button(self): self.add(Button(style = ButtonStyle.green,label = "上十項",emoji="⏮️",custom_id="-10"),self.next) self.add(Button(style = ButtonStyle.green,label = "下十項",emoji="⏭️",custom_id="10"),self.next) ``` --- 3. Q:如果我需要物件初始化時透過異步函數讀取資料,我該如何處理? A:可以先建立一個異步函數,透過異步函數建立物件,再回傳回來 ```python= #Apple.py class Banana(): def __init__(self): pass async def _init(interaction): pass async def Create_Banana(interaction:Interaction): banana = Banana(interaction) await banana._init(interaction) return banana class Apple(commands.Cog): def __init__(self): pass @app_commands.commands(name = "create",description = "建立Banana物件") async def create(self,interaction:Interaction): banana = Create_Banana(interaction) ``` 4.