這是一篇使用 discord.py 模組開發的 Discord Bot 進階教學文章。
首先介紹 Discord Bot 和 Cog 架構關係,此架構能將各個指令分門別類,以及上線期間能夠新增、移除、更新指令。藉由 Cog 架構能讓 Discord Bot 方便維護和擁有良好的可讀性。

歡迎加入筆者的 Discord 伺服器 點此加入

Python Discord Bot 教學文章和程式碼範例 點此前往


一、Cog 架構簡介:

為了要讓未來 Discord Bot 方便維護以及增加可讀性,將各個指令分門別類會是一個不錯的方法,而 Cog 就是能夠實現這功能的架構,Cog 可以將程式碼分門別類,讓主程式只要負責執行載入檔案和卸載檔案的動作。
基礎教學的內容中,最後有提到使用關鍵字的 on_message 和前綴指令 command 如果放在同一個檔案會無法運作的問題,而使用 Cog 架構就可以來解決這個問題。
更多資訊可參考 Discord.py Cog 官方文檔

00

二、Discord Bot Cog 架構介紹:

01

  • Bot 主程式

    負責執行載入(load)、卸載(unload)、重新載入(reload)程式檔案,可以讓 Discord Bot 上線期間就能更改指令。
  • Cog 資料夾

    放置分類過後的程式檔案,利用檔名就能知道此檔案的用途,增加撰寫程式碼的效率。

三、Discord Bot Cog 主程式實作:

🔔 小幫助
使用「前綴指令help(例:$help)」可以了解目前有哪些指令可以執行。
02

Load 載入

通常使用於撰寫好新的程式檔案要上線,或者要將之前載出的程式檔案再次上線。
執行語法:前綴符號load 檔案名稱(例:$load main)

1
2
3
4
@bot.command()
async def load(ctx: commands.Context, extension: str):
await bot.load_extension(f"cogs.{extension}")
await ctx.send(f"Loaded {extension} done.")
  • 載入成功會回傳訊息讓使用者知道
    03

  • main檔案載入成功
    04

UnLoad 卸載

通常使用於程式檔案出 Bug 時,卸載來阻止其他使用者執行到錯誤指令。
執行語法:前綴符號unload 檔案名稱(例:$unload main)

1
2
3
4
@bot.command()
async def unload(ctx: commands.Context, extension: str):
await bot.unload_extension(f"cogs.{extension}")
await ctx.send(f"UnLoaded {extension} done.")
  • 卸載成功會回傳訊息讓使用者知道
    05

  • main檔案卸載成功
    06

ReLoad 重新載入

通常使用於更改過程式檔案要直接更新,或者 Debug 時的測試。
執行語法:前綴符號reload 檔案名稱(例:$reload main)

1
2
3
4
@bot.command()
async def reload(ctx: commands.Context, extension: str):
await bot.reload_extension(f"cogs.{extension}")
await ctx.send(f"ReLoaded {extension} done.")
  • 重新載入成功會回傳訊息讓使用者知道
    07

  • main檔案重新載入成功
    08 09

完整主程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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: commands.Context, extension: str):
await bot.load_extension(f"cogs.{extension}")
await ctx.send(f"Loaded {extension} done.")

# 卸載指令檔案
@bot.command()
async def unload(ctx: commands.Context, extension: str):
await bot.unload_extension(f"cogs.{extension}")
await ctx.send(f"UnLoaded {extension} done.")

# 重新載入程式檔案
@bot.command()
async def reload(ctx: commands.Context, extension: str):
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 資料夾實作:

只需要注意幾個地方,就能將基礎教學中的程式碼搬過來使用,而且可以將關鍵字觸發和前綴指令共同放在同一個檔案中。

  1. @bot.event 要改成 @commands.Cog.listener()
  2. @bot.command() 要改成 @commands.command()
  3. class 的名稱跟檔名相同比較好管理,習慣會首字母大寫
  4. class 中使用到 bot 物件時,要改成 self.bot 才能使用
  5. 每個指令的第一個參數要是 self,否則程式碼會出錯
  • 程式碼結構

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import discord
    from discord.ext import commands

    class Main(commands.Cog):
    def __init__(self, bot: commands.Bot):
    self.bot = bot

    ...

    async def setup(bot: commands.Bot):
    await bot.add_cog(Main(bot))
  • 範例程式碼

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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 上線期間載入、卸載、重新載入指令!
筆者認為文章中較難懂的地方會是 class 的部分,通常 class 類別是程式設計中後期才會學習到的,對於程式新手學習的難度頗高,因此筆者沒有特別詳細描述運作原理。對於開發 Discord Bot 來說,我們通常只要大概理解程式碼架構該如何撰寫以及傳入參數意義,就足夠滿足開發需求,如果未來想要增進 Discord Bot 運作效率或者用法,此時再來學習也不遲。