如果你的機器人功能多到一個程度,把全部的程式碼全部塞在一個文件裡顯然不是好辦法。
不但看起來雜亂,也難以維護,就算只是其中一個功能出問題也必須要把整個bot關掉。
這時候就可以使用Cogs的架構來寫,
可以將commands和listener等東西分堆塞在各自文件裡並方便我們裝卸。

官方文件的說法,比較重要的有這幾點:

  • 每個cogs都是commands.Cog的子類別
  • 每個command要前加@commands.command()
  • 每個listener要前加@commands.Cog.listener()
  • Cogs可以用Bot.add_cog()來註冊
  • Cogs可以用Bot.remove_cog()來卸載
  • 通常會跟Extension功能配合

當然單純這樣條列出來是看不懂的,底下來看實例。
其檔案架構通常會長下面這樣

cogs資料夾是拿來放各個cogs的
core資料夾是拿來放一些你cogs可能會常用到的函數。
main.py是拿來運行的檔案,裡面也包含了載入和裝卸cogs的程式碼,會長的類似下面這樣:

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
46
47
from discord.ext import commands
import os

bot = commands.Bot('prefix')

#下面這塊從cogs資料夾中取出所有.py結尾的檔案並用load_extension載入cogs
for filename in os.listdir('./cogs'):
if filename.endswith('.py'):
bot.load_extension(f'cogs.{filename[:-3]}')

@bot.event
async def on_ready():
print('Test begin...')

#這裡建個指令讓你可以載入Cog
@bot.command()
async def load(ctx, cog_name):
try:
bot.load_extension(f'cogs.{cog_name}')
except:
await ctx.send('Failed.')
return
await ctx.send('load success!')

#這裡建個指令讓你可以卸載Cog
@bot.command()
async def unload(ctx, cog_name):
try:
bot.unload_extension(f'cogs.{cog_name}')
except:
await ctx.send('Failed.')
return
await ctx.send('unload success!')

#這裡建個指令讓你可以重新載入Cog
@bot.command()
async def reload(ctx, cog_name):
try:
bot.reload_extension(f'cogs.{cog_name}')
except:
await ctx.send('Failed.')
return
await ctx.send('reload success!')

if __name__ == "__main__":
bot.run('TOKEN')

接著在core底下加一個文檔,這裡我叫他init.py

1
2
3
4
5
6
import discord
from discord.ext import commands

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

接著來把一個指令用成cogs。
就拿上篇那個请不要随意地触碰我! 十分感谢!來舉例
他原本的程式區塊大概長這樣:

1
2
3
@bot.command(aliases=['touch'])
async def t(message):
await message.channel.send('请不要随意地触碰我! 十分感谢!')

把他用成cogs的話會需要把它塞進Class並加上我們剛剛寫的東西,所以會長得像這樣

1
2
3
4
5
6
7
from discord.ext import commands
from core.init import Cog_Extension

class touch(Cog_Extension): #建議class名稱跟檔案名稱一樣
@commands.command(aliases=['touch'])
async def t(self, message): #記得在參數列最前面加上self
await message.channel.send('请不要随意地触碰我! 十分感谢!')

記得要import需要的套件

接著在下面加上setup的函數

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

class touch(Cog_Extension):
@commands.command(aliases=['touch'])
async def t(self, message):
await message.channel.send('请不要随意地触碰我! 十分感谢!')

def setup(bot):
bot.add_cog(touch(bot))
#這裡的bot.add_cog(touch(bot))中的(bot)前面填上你class的名子

這樣運行後給機器人預設的help指令後就能看到結構了。

像這邊unload掉touch後可以發現輸t他也沒反應了,輸help也看不見了。

而load回來的話可以發現指令有反應了,help中也看的到了。


前述的的程式結構較簡單可能沒什麼感覺,但要是程式複雜到如下圖能夠先unload一塊調整完後再load回去還是挺方便的。