介紹完兩種觸發條件,先暫停一下,來介紹一下一個好用的工具:Bot 指令框架。
在 Discord.py
,有內建擴充包 (extension library),對於開發常見的需求會很有幫助。而今天要介紹的這個擴充 ── Bot 指令框架,就是針對前幾天介紹的各種觸發條件 (事件、應用指令) 的擴充,可以幫助開發者加速開發各種指令。
雖然稱呼 Bot 指令框架 (commands framework) 是「指令框架」 (文件上寫的),但其實只是 Client
的 subclass,基本上能對 Client
做的事,對 Bot
也能做。不過,這兩者的差別是,Bot
已經幫忙把某些常見的設定都處理好了,同時,也提供更多在使用上的彈性。
不知道大家還記不記得之前的各版本 Quickstart (Day 04),那時候其實就有看到 bot 指令框架的寫法。這邊幫大家簡單複習一下,下面這兩種寫法其實效果一模一樣:
import discord
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
@client.event
async def on_message(message):
if message.author == client.user:
return
if message.content == 'ping':
await message.channel.send('pong')
client.run('token')
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='', intents=intents)
@bot.command()
async def ping(ctx):
await ctx.send('pong')
bot.run('token')
對照之後,是否覺得 沒有比較沒有傷害 下面這種寫法更簡潔了~
來看一下有哪些變化吧!
Client
變成 Bot
前面幾行的程式長得差不多,只是在這個框架中,從原本建立 Client
變成建立 Bot
。
建立 Bot
時,除了同樣要設定 intents
之外,還多了一個可以設定指令的前綴的參數 (這是必要參數)。
不能使用
/
作為前綴,不然會與斜線指令衝突。
在 bot 框架中,內建有 message.author
是否為 bot.user
(client.user
) 的檢查,就不必再寫一遍了。
Message
變成 Context
函數的第一個參數 (至少一個) 從原本的 Message
變成了 Context
(參數通常會簡寫為 ctx
)。
除了 @bot.command()
之外,也可以用 @commands.command()
搭配 add_command
來建立指令。
下面這兩段程式碼的效果完全一樣:
@bot.command()
async def test(ctx):
pass
@commands.command()
async def test(ctx):
pass
bot.add_command(test)
這個幾乎沒差別,一樣在後面加上 .event
就好了。
@client.event
async def on_ready():
print('We have logged in')
@bot.event
async def on_ready():
print('We have logged in')
接下來,來看一下如何用 Bot 來寫應用指令 (application command)。
import discord
from discord import app_commands
GUILD_ID = "your guild ID"
MY_GUILD = discord.Object(id=GUILD_ID)
class MyClient(discord.Client):
def __init__(self, *, intents: discord.Intents):
super().__init__(intents=intents)
self.tree = app_commands.CommandTree(self)
async def setup_hook(self):
self.tree.copy_global_to(guild=MY_GUILD)
await self.tree.sync(guild=MY_GUILD)
intents = discord.Intents.default()
client = MyClient(intents=intents)
@client.tree.command()
async def hello(interaction: discord.Interaction):
"""Says hello!"""
await interaction.response.send_message(f'Hi, {interaction.user.mention}')
client.run('token')
import discord
from discord.ext import commands
GUILD_ID = "your guild ID"
MY_GUILD = discord.Object(id=GUILD_ID)
class MyBot(commands.Bot):
def __init__(self, *, command_prefix: str, intents: discord.Intents):
super().__init__(command_prefix=command_prefix, intents=intents)
async def setup_hook(self):
self.tree.copy_global_to(guild=MY_GUILD)
await self.tree.sync(guild=MY_GUILD)
intents = discord.Intents.default()
bot = MyBot(command_prefix="", intents=intents)
@bot.tree.command()
async def hello(interaction: discord.Interaction):
"""Says hello!"""
await interaction.response.send_message(f'Hi, {interaction.user.mention}')
client.run('token')
這次只有一個地方值得講一下
在使用應用指令時,都需要一個 CommandTree
作為應用指令的容器。不過,在 Bot 指令框架中,就不用自己手動建立。
在查詢網路上其他人的範例或討論時,有一件事蠻多人提到的:不要在 on_ready
時 sync 應用指令。(例如:這篇和這篇)
主要的原因是,on_ready
可能會執行不只一次 (文件中也有警告這件事),平時如果只是紀錄個 log 倒也還好,但如果是 sync 應用指令的話,就有可能會有問題了。之所以會說「可能」,主要是因為 global 應用指令其實是有速度限制 (rate limit) 的 (參考官方文件)。
照理說,如果只是在自己的小小測試用伺服器的話,應該不至於會有問題,但還是要盡量養成好習慣XD
比較建議的時機 (事件) 是 setup_hook
,它只會觸發一次。
最後介紹一個有趣的東西,在 Bot 指令框架中,有一種東西叫做混合指令 (hybrid command)。聽起來很厲害,但其實是一般的文字指令和斜線指令的混合版:它能夠同時設定兩個指令。
因為有使用到應用指令,所以要記得 sync
@bot.hybrid_command()
async def test(ctx):
await ctx.send("This is a hybrid command!")
基本上只要向上面那樣設定好,就可以同時擁有兩種不同的指令 (但效果一樣)。
不過,我目前是沒有想到什麼特別適合使用混合指令的情況就是了...
今天介紹了第一個擴充套件:Bot 指令框架,讓大家了解如何用它來做到前面幾天所介紹的功能 (同時也快速複習了前面幾天的主題XD)。