這是一篇 Discord Bot 的進階教學文章,會教學如何將 Discord Bot 指令分門別類,以及如何在 Discord Bot 上線期間直接新增、移除、更新指令,讓 Discord Bot 未來能夠方便維護和擁有更好的可讀性。 如果還不了解 Discord Bot 的用途以及該如何創建一臺 Discord Bot 的讀者,建議可以參考這篇之前本作者撰寫的 [Python Discord Bot 基礎教學](https://hackmd.io/@smallshawn95/python_discord_bot_base)。 :::success :book: **更多 Python Discord Bot 教學系列和程式範例** https://github.com/smallshawn95/Python-Discord-Bot-Teach.git ::: --- [TOC] --- ## 一、Cog 架構簡介: 為了要讓未來 Discord Bot 方便維護以及增加可讀性,將各個指令分門別類會是一個不錯的方法,而 Cog 就是能夠實現這功能的架構,Cog 可以將程式碼分門別類,讓主程式只要負責執行加載檔案和卸載檔案的動作。 基礎教學的內容中,最後有提到使用關鍵字的 `on_message` 和前綴指令 `command` 如果放在同一個檔案會無法運作的問題,而使用 Cog 架構就可以來解決這個問題。 更多資訊可參考 [Discord.py Cog 官方文檔](https://discordpy.readthedocs.io/en/stable/ext/commands/cogs.html)。 ![Picture_1](https://i.imgur.com/64fwR9Z.png) ## 二、Discord Bot Cog 架構介紹: ![Picture_2](https://hackmd.io/_uploads/SkKRTOEah.png =50%x) * ### bot 主程式 負責執行載入(load)、卸載(unload)、重新載入(reload)程式檔案,可以讓 Discord Bot 上線期間就能更改指令。 * ### cogs 資料夾 放置分類過後的程式檔案,利用檔名就能知道此檔案的用途,增加撰寫程式碼的效率。 ## 三、Discord Bot Cog 主程式實作: :::warning :bulb:**小幫助** 使用「前綴指令help(例:$help)」可以了解目前有哪些指令可以執行。 ![Picture_3](https://i.imgur.com/J72CE0z.png) ::: * ### Load 載入 通常使用於撰寫好新的程式檔案要上線,或者要將之前載出的程式檔案再次上線。 執行語法:前綴符號load 檔案名稱(例:$load main) ```python= @bot.command() async def load(ctx, extension): await bot.load_extension(f"cogs.{extension}") await ctx.send(f"Loaded {extension} done.") ``` * 載入成功會回傳訊息讓使用者知道 ![Picture_4](https://i.imgur.com/EzgZGDf.png) * main檔案載入成功 ![Picture_5](https://i.imgur.com/1Moa4m4.png) * ### UnLoad 卸載 通常使用於程式檔案出 Bug 時,卸載來阻止其他使用者執行到錯誤指令。 執行語法:前綴符號unload 檔案名稱(例:$unload main) ```python= @bot.command() async def unload(ctx, extension): await bot.unload_extension(f"cogs.{extension}") await ctx.send(f"UnLoaded {extension} done.") ``` * 卸載成功會回傳訊息讓使用者知道 ![Picture_6](https://i.imgur.com/hyk7bZa.png) * main檔案卸載成功 ![Picture_7](https://i.imgur.com/GKmqtWs.png) * ### ReLoad 重新載入 通常使用於更改過程式檔案要直接更新,或者 Debug 時的測試。 執行語法:前綴符號reload 檔案名稱(例:$reload main) ```python= @bot.command() async def reload(ctx, extension): await bot.reload_extension(f"cogs.{extension}") await ctx.send(f"ReLoaded {extension} done.") ``` * 重新載入成功會回傳訊息讓使用者知道 ![Picture_8](https://i.imgur.com/kpSR19c.png) * main檔案重新載入成功 ![Picture_9](https://i.imgur.com/q8PkiMl.png) ![Picture_10](https://i.imgur.com/3cPOHE0.png) * ### 完整程式碼 ```python= import os import asyncio import discord from discord.ext import commands intents = discord.Intents.all() bot = commands.Bot(command_prefix = "$", intents = intents) # 當機器人完成啟動時 @bot.event async def on_ready(): print(f"目前登入身份 --> {bot.user}") # 載入指令程式檔案 @bot.command() async def load(ctx, extension): await bot.load_extension(f"cogs.{extension}") await ctx.send(f"Loaded {extension} done.") # 卸載指令檔案 @bot.command() async def unload(ctx, extension): await bot.unload_extension(f"cogs.{extension}") await ctx.send(f"UnLoaded {extension} done.") # 重新載入程式檔案 @bot.command() async def reload(ctx, extension): await bot.reload_extension(f"cogs.{extension}") await ctx.send(f"ReLoaded {extension} done.") # 一開始bot開機需載入全部程式檔案 async def load_extensions(): for filename in os.listdir("./cogs"): if filename.endswith(".py"): await bot.load_extension(f"cogs.{filename[:-3]}") async def main(): async with bot: await load_extensions() await bot.start("機器人的TOKEN") # 確定執行此py檔才會執行 if __name__ == "__main__": asyncio.run(main()) ``` ## 四、Discord Bot Cog cogs 資料夾實作: 只需要注意幾個地方,就能將基礎教學中的程式碼搬過來使用,而且可以將關鍵字觸發和前綴指令共同放在同一個檔案中。 1. `@bot.event` 要改成 `@commands.Cog.listener()` 2. `@bot.command()` 要改成 `@commands.command()` 3. class 的名稱跟檔名相同比較好管理,習慣會首字母大寫 4. class 中使用到 bot 物件時,要改成 `self.bot` 才能使用 5. 每個指令的第一個參數要是 `self`,否則程式碼會出錯 * ### 主要程式碼結構 ```python= import discord from discord.ext import commands class Main(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot ... async def setup(bot): await bot.add_cog(Main(bot)) ``` * ### 範例程式碼 ```python= import discord from discord.ext import commands # 定義名為 Main 的 Cog class Main(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot # 前綴指令 @commands.command() async def Hello(self, ctx: commands.Context): await ctx.send("Hello, world!") # 關鍵字觸發 @commands.Cog.listener() async def on_message(self, message: discord.Message): if message.author == self.bot.user: return if message.content == "Hello": await message.channel.send("Hello, world!") # Cog 載入 Bot 中 async def setup(bot: commands.Bot): await bot.add_cog(Main(bot)) ``` --- 本次 Discord Bot 進階教學 — Cog 篇到此介紹完了,希望大家有成功將指令分門別類,並且可以線上載入、卸載、重新加載指令。 本次教學中較難懂的點可能是 class 那部分,需要已經具備 class 的知識才能了解實際的運作,而筆者也沒有特別詳細描述,那是因為程式的資料過於龐大,如果每行程式碼都要釐清實際運作過程將會寸步難行,所以我們只要看得懂這行大概要做什麼以及如何應用,這樣就足以完成我們的 Discord Bot。 感謝各位觀看完整篇的教學,下篇教學將會介紹目前 Discord 推薦 Bot 應該都要使用的斜線指令該如何撰寫,敬請期待下一篇教學。 --- :::info 📢 **歡迎加入我的 Discord 伺服器** https://discord.gg/Jtd3eVrFJs ::: *Copyright © 2023 SmallShawn95. All rights reserved.*