From 3cf83e370eac87d2e71a6ae1200c11513ebe281b Mon Sep 17 00:00:00 2001 From: prosolis <5590409+prosolis@users.noreply.github.com> Date: Sat, 9 May 2026 21:37:29 -0700 Subject: [PATCH] Euro: add !eurogrant admin command Credits an arbitrary amount to any user, logged with the granting admin's ID (and optional free-form note) for audit. Reuses Credit so balance bootstrap and transaction history stay consistent. Co-Authored-By: Claude Opus 4.7 (1M context) --- internal/plugin/euro.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/internal/plugin/euro.go b/internal/plugin/euro.go index 5aad6d9..db2e507 100644 --- a/internal/plugin/euro.go +++ b/internal/plugin/euro.go @@ -124,6 +124,7 @@ func (p *EuroPlugin) Commands() []CommandDef { {Name: "baltop", Description: "Euro leaderboard", Usage: "!baltop", Category: "Economy"}, {Name: "baltransfer", Description: "Send euros to another player", Usage: "!baltransfer @user €amount", Category: "Economy"}, {Name: "combo", Description: "View your current activity combo streaks", Usage: "!combo", Category: "Economy"}, + {Name: "eurogrant", Description: "Grant euros to a user (admin)", Usage: "!eurogrant @user amount [reason]", Category: "Admin", AdminOnly: true}, } } @@ -146,6 +147,8 @@ func (p *EuroPlugin) OnMessage(ctx MessageContext) error { return p.handleTransfer(ctx) case p.IsCommand(ctx.Body, "combo"): return p.handleCombo(ctx) + case p.IsCommand(ctx.Body, "eurogrant"): + return p.handleGrant(ctx) } return nil } @@ -544,3 +547,40 @@ func (p *EuroPlugin) handleTransfer(ctx MessageContext) error { fmt.Sprintf("💸 **%s** sent €%d to **%s**.", senderName, int(amount), targetName)) } +// handleGrant lets an admin credit euros to any user. Logs the transaction +// with reason "admin grant by " plus any free-form note the admin +// supplied as the third argument. +func (p *EuroPlugin) handleGrant(ctx MessageContext) error { + if !p.IsAdmin(ctx.Sender) { + return nil + } + args := p.GetArgs(ctx.Body, "eurogrant") + parts := strings.Fields(args) + if len(parts) < 2 { + return p.SendReply(ctx.RoomID, ctx.EventID, "Usage: `!eurogrant @user amount [reason]`") + } + + targetID, ok := p.ResolveUser(parts[0], ctx.RoomID) + if !ok { + return p.SendReply(ctx.RoomID, ctx.EventID, "Could not resolve user.") + } + + amountStr := strings.TrimPrefix(parts[1], "€") + amount := 0.0 + fmt.Sscanf(amountStr, "%f", &amount) + if amount < 1 { + return p.SendReply(ctx.RoomID, ctx.EventID, "Amount must be at least €1.") + } + + reason := fmt.Sprintf("admin grant by %s", ctx.Sender) + if len(parts) > 2 { + reason = fmt.Sprintf("%s — %s", reason, strings.Join(parts[2:], " ")) + } + + p.Credit(targetID, amount, reason) + newBalance := p.GetBalance(targetID) + targetName := p.DisplayName(targetID) + return p.SendReply(ctx.RoomID, ctx.EventID, + fmt.Sprintf("✅ Granted €%d to **%s**. New balance: €%d.", int(amount), targetName, int(newBalance))) +} +