Byte Ebi's Logo

Byte Ebi 🍤

每天一小口,蝦米變鯨魚

[Discord 機器人] 02.擲骰機器人

建立擲骰子用的 Discord 聊天機器人,使用 Python3

Ray

不知道大家知不知道 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,結果如下

demo.png

如此就完成了擲骰機器人的需求

改進方向

儘管機器人的需求是完成了,但是還有很多可以優化的部分

函式切分

現在太多邏輯全部寫在一起,應該拆成獨立函數來呼叫
這樣不論是寫測試或是未來要調整都會比較容易

輸入判斷邏輯優化

切分邏輯過於簡單,萬一使用者輸入了我們沒想到的內容
例如!roll6D10d5,雖然程式執行起來會跟!roll6D10一樣
但是使用者不應該多最後那個d5,這部分該如何處理也是一個可以改進的目標

最新文章

Category

Tag