118 lines
2.7 KiB
Go
118 lines
2.7 KiB
Go
package fun
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.nakama.town/fmartingr/butterrobot/internal/model"
|
|
"git.nakama.town/fmartingr/butterrobot/internal/plugin"
|
|
)
|
|
|
|
// DicePlugin rolls dice based on standard dice notation
|
|
type DicePlugin struct {
|
|
plugin.BasePlugin
|
|
rand *rand.Rand
|
|
}
|
|
|
|
// NewDice creates a new DicePlugin instance
|
|
func NewDice() *DicePlugin {
|
|
source := rand.NewSource(time.Now().UnixNano())
|
|
return &DicePlugin{
|
|
BasePlugin: plugin.BasePlugin{
|
|
ID: "fun.dice",
|
|
Name: "Dice Roller",
|
|
Help: "Rolls dice when you type '!dice [formula]' (default: 1d20)",
|
|
},
|
|
rand: rand.New(source),
|
|
}
|
|
}
|
|
|
|
// OnMessage handles incoming messages
|
|
func (p *DicePlugin) OnMessage(msg *model.Message, config map[string]interface{}) []*model.Message {
|
|
if !strings.HasPrefix(strings.TrimSpace(strings.ToLower(msg.Text)), "!dice") {
|
|
return nil
|
|
}
|
|
|
|
// Extract dice formula
|
|
formula := strings.TrimSpace(strings.TrimPrefix(msg.Text, "!dice"))
|
|
formula = strings.TrimSpace(strings.TrimPrefix(formula, "!dice"))
|
|
|
|
if formula == "" {
|
|
formula = "1d20" // Default formula
|
|
}
|
|
|
|
// Parse and roll the dice
|
|
result, err := p.rollDice(formula)
|
|
responseText := ""
|
|
|
|
if err != nil {
|
|
responseText = fmt.Sprintf("Error: %s", err.Error())
|
|
} else {
|
|
responseText = fmt.Sprintf("%d", result)
|
|
}
|
|
|
|
response := &model.Message{
|
|
Text: responseText,
|
|
Chat: msg.Chat,
|
|
ReplyTo: msg.ID,
|
|
Channel: msg.Channel,
|
|
}
|
|
|
|
return []*model.Message{response}
|
|
}
|
|
|
|
// rollDice parses a dice formula string and returns the result
|
|
func (p *DicePlugin) rollDice(formula string) (int, error) {
|
|
// Support basic dice notation like "2d6", "1d20+5", etc.
|
|
diceRegex := regexp.MustCompile(`^(\d+)d(\d+)(?:([+-])(\d+))?$`)
|
|
matches := diceRegex.FindStringSubmatch(formula)
|
|
|
|
if matches == nil {
|
|
return 0, fmt.Errorf("invalid dice formula: %s", formula)
|
|
}
|
|
|
|
// Parse number of dice
|
|
numDice, err := strconv.Atoi(matches[1])
|
|
if err != nil || numDice < 1 {
|
|
return 0, fmt.Errorf("invalid number of dice")
|
|
}
|
|
if numDice > 100 {
|
|
return 0, fmt.Errorf("too many dice (max 100)")
|
|
}
|
|
|
|
// Parse number of sides
|
|
sides, err := strconv.Atoi(matches[2])
|
|
if err != nil || sides < 1 {
|
|
return 0, fmt.Errorf("invalid number of sides")
|
|
}
|
|
if sides > 1000 {
|
|
return 0, fmt.Errorf("too many sides (max 1000)")
|
|
}
|
|
|
|
// Roll the dice
|
|
total := 0
|
|
for i := 0; i < numDice; i++ {
|
|
roll := p.rand.Intn(sides) + 1
|
|
total += roll
|
|
}
|
|
|
|
// Apply modifier if present
|
|
if len(matches) > 3 && matches[3] != "" {
|
|
modifier, err := strconv.Atoi(matches[4])
|
|
if err != nil {
|
|
return 0, fmt.Errorf("invalid modifier")
|
|
}
|
|
|
|
if matches[3] == "+" {
|
|
total += modifier
|
|
} else if matches[3] == "-" {
|
|
total -= modifier
|
|
}
|
|
}
|
|
|
|
return total, nil
|
|
}
|