[22] [Discord 机器人] 02.掷骰机器人

不知道大家知不知道 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,这部分该如何处理也是一个可以改进的目标


<<:  20 - Husky - Git Hooks 工具

>>:  day21: side effect

JavaScript Day15 - event(2)

event 查目前网页的 event,开启 Chrome 的开发者工具,点选 Elements,之後...

[Day18] - 在 React 中引用现成的 Web Component

当我们拿到一个现有的 Web Component 时 , 如何在 React 专案中引用呢 ? 利用...

万事真能从影片完整复制学习吗?(厨艺篇)

如果你正在为水电或3C故障而苦恼,也许照着影片操作,是可以完成修复,但网路影片并非每一样技术都可以直...

【D2】要下厨前需要准备锅具

简介厨房:Shioaji Shioaji是永丰证开发出来的Python API,用来给客户自行开发自...

[第二十八只羊] 迷雾森林顶尖对决 登入介面套版

天亮了 昨晚是平安夜 关於迷雾森林故事 香水 在场本来许多animal们牵着喜鹊儿的翅膀小手跳舞 都...