[Discord 機器人] 02.擲骰機器人
建立擲骰子用的 Discord 聊天機器人,使用 Python3
不知道大家知不知道 TTRPG 呢?
比較有名的大概就是龍與地下城(D&D)吧
之前看 hololive EN 在玩,就萌生了「來寫個擲骰子 Bot 玩看看好了」的想法
需求討論
- 要可以擲多個骰子
- 骰子的面數要可以自訂
- 成功判定計算
指令格式
!roll 5D10>8
這代表:擲 5 個 10 面骰,成功判定是點數大於等於 8
程式碼
# 導入 Discord.py 套件
import discord
# 導入隨機數套件
import random
# 取得 Discord client 物件才能操作
client = discord.Client()
# 調用 event 函式庫
@client.event
# 當機器人完成啟動時在終端機顯示提示訊息
async def on_ready():
print(f'目前登入身份:{client.user}')
# 調用 event 函式庫
@client.event
# 當有訊息時
async def on_message(message):
# 排除機器人本身發出的訊息,避免機器人自問自答的無限迴圈
if message.author == client.user:
return
# 預設錯誤訊息
error = []
# 處理輸入文字
content = message.content.replace(' ', '').lower()
# 如果是「!roll」開頭的訊息
if message.content.startswith('!roll'):
content = content.replace('!roll', '')
# 骰子數量計算
dice_cont = content.split('d')[0]
try:
dice_cont = int(dice_cont)
except ValueError:
error.append('How many dice you roll must be an interger!')
# 骰子類型判斷
content = content.split('d')[1]
dice_type = content.split('>')[0]
try:
dice_type = int(dice_type)
except ValueError:
error.append('Dice type must be an interger!')
# 成功判斷
if '>' in content:
success = content.split('>')[1]
try:
success = int(success)
except ValueError:
error.append('Success condition must be an interger!')
else:
success = 0
if len(error) == 0:
success_count = 0
result_msg = ''
# 擲骰子
results = [random.randint(1, dice_type) for _ in range(dice_cont)]
for result in results:
if success > 0 and result >= success:
success_count += 1
result_msg += f'`{result}`, '
await message.channel.send(result_msg)
if success > 0:
await message.channel.send(f'Success: `{success_count}`')
else:
await message.channel.send(error)
# TOKEN 在 Discord Developer 的「BOT」頁取得
client.run('')
程式說明
基本的discord.py
使用方法之前寫過就不寫了
針對邏輯的部份做說明
輸入訊息偵測
首先透過
message.content.startswith('!roll')
只對!roll
開頭的訊息做反應
為了讓使用者不管輸入大小寫或是有沒有空格都可使用
所以透過
message.content.replace(' ', '').lower()
去除空白和全部轉小寫
如此一來,使用者不論輸入
- !Roll 5 D10
- !roll5d10
後續程式都是以!roll5d10
做處理
骰子數量計算
根據輸入訊息的格式,在!roll
後面、d
前面的「數字」代表骰子數量
使用
content.replace('!roll', '')
去除字串中的!roll
,因為這只是用來觸發機器人,不會影響擲骰子結果
使用
content.split('d')[0]
將字串從d
切分,取得列表第一個值,這就是骰子數量
使用強制轉型判斷取得的值是不是數字?如果不是則轉型出錯會被try except
捕獲
若是被捕獲了,就新增錯誤訊息字串
骰子類型判斷
和剛剛的數量計算很像,只是這次我們抓取的邏輯是介於d後,>符號前
如果沒有對應的>
符號,則不會分切回傳整個內容
再藉由跟數量計算一樣的強制轉型判斷取得的類型是否是數字
若是錯誤,一樣新增錯誤訊息
成功判斷
因為我們允許使用者在使用指令的時候不輸入成功判斷條件
所以透過'>' in content:
來判斷使用者是否有輸入判斷條件
如果有輸入的話條件會在>
後面,一樣取得值之後判斷是否是數字
擲骰
我們需要的參數都已經取得了,接下來就是根據條件擲骰並回傳
首先先檢查前面的錯誤訊息是不是有被建立,如果有代表不該擲骰!
這時候就直接輸出錯誤訊息到 Discord
如果錯誤訊息為空,就透過
results = [random.randint(1, dice_type) for _ in range(dice_cont)]
取得指定數量的隨機亂數執行結果,用這種方法寫和寫迴圈是一樣的!
接著遍歷所有結果,整理成輸出到 Discord 想要的格式
並且如果使用者有輸入擲骰成功的條件,也在這邊計算成功次數
最後就是輸出擲骰結果和成功次數到 Discord,結果如下
如此就完成了擲骰機器人的需求
改進方向
儘管機器人的需求是完成了,但是還有很多可以優化的部分
函式切分
現在太多邏輯全部寫在一起,應該拆成獨立函數來呼叫
這樣不論是寫測試或是未來要調整都會比較容易
輸入判斷邏輯優化
切分邏輯過於簡單,萬一使用者輸入了我們沒想到的內容
例如!roll6D10d5
,雖然程式執行起來會跟!roll6D10
一樣
但是使用者不應該多最後那個d5
,這部分該如何處理也是一個可以改進的目標