Coop: substitute {count} and {leader} placeholders in gift flavor

The TwinBeeGiftArrival pool entries each end with a templated footer
authored to the spec format ("Open: {count} · Leave it: {count}\nMajority
rules. Ties go to {leader}."), but the placeholders were never being
substituted — players saw the literal "{count}" and "{leader}" strings.

Substitute in renderCoopGiftPost: ordered Replace for the two {count}
positions (opens, then leaves), ReplaceAll for {leader}. Also dropped
the redundant "Current votes:" line my code was appending — the flavor's
own tally line covers it after substitution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-04-28 18:14:49 -07:00
parent 9f094549b7
commit b7077eeea1
2 changed files with 28 additions and 19 deletions

View File

@@ -412,7 +412,7 @@ func (p *AdventurePlugin) handleCoopAdmGift(ctx MessageContext, args string) err
gift, _ := loadCoopGift(giftID) gift, _ := loadCoopGift(giftID)
members, _ := loadCoopMembers(runID) members, _ := loadCoopMembers(runID)
if gift != nil { if gift != nil {
postID, perr := p.SendMessageID(gr, renderCoopGiftPost(run, gift, members)) postID, perr := p.SendMessageID(gr, renderCoopGiftPost(p, run, gift, members))
if perr == nil { if perr == nil {
_ = saveCoopGiftPostID(giftID, postID) _ = saveCoopGiftPostID(giftID, postID)
} }

View File

@@ -119,7 +119,7 @@ func (p *AdventurePlugin) handleCoopGift(ctx MessageContext, args string) error
if gr != "" { if gr != "" {
gift, _ := loadCoopGift(giftID) gift, _ := loadCoopGift(giftID)
members, _ := loadCoopMembers(runID) members, _ := loadCoopMembers(runID)
postID, err := p.SendMessageID(gr, renderCoopGiftPost(run, gift, members)) postID, err := p.SendMessageID(gr, renderCoopGiftPost(p, run, gift, members))
if err == nil { if err == nil {
_ = saveCoopGiftPostID(giftID, postID) _ = saveCoopGiftPostID(giftID, postID)
} }
@@ -182,7 +182,7 @@ func (p *AdventurePlugin) handleCoopGiftVote(ctx MessageContext, args string) er
gr := gamesRoom() gr := gamesRoom()
if gr != "" && gift.PostEventID != "" { if gr != "" && gift.PostEventID != "" {
members, _ := loadCoopMembers(run.ID) members, _ := loadCoopMembers(run.ID)
_ = p.EditMessage(gr, gift.PostEventID, renderCoopGiftPost(run, gift, members)) _ = p.EditMessage(gr, gift.PostEventID, renderCoopGiftPost(p, run, gift, members))
} }
return p.SendDM(ctx.Sender, fmt.Sprintf("Gift #%d vote recorded: %s.", gift.ID, choice)) return p.SendDM(ctx.Sender, fmt.Sprintf("Gift #%d vote recorded: %s.", gift.ID, choice))
} }
@@ -588,17 +588,34 @@ func markCoopGiftApplied(giftID int) (bool, error) {
// ── Render ────────────────────────────────────────────────────────────────── // ── Render ──────────────────────────────────────────────────────────────────
func renderCoopGiftPost(run *CoopRun, gift *CoopGift, members []CoopMember) string { func renderCoopGiftPost(p *AdventurePlugin, run *CoopRun, gift *CoopGift, members []CoopMember) string {
var sb strings.Builder var sb strings.Builder
flavor := ""
if len(TwinBeeGiftArrival) > 0 {
flavor = TwinBeeGiftArrival[rand.IntN(len(TwinBeeGiftArrival))]
}
sb.WriteString(fmt.Sprintf("📦 **Gift #%d arrived — Co-op #%d, Day %d**\n\n", gift.ID, run.ID, gift.Day)) sb.WriteString(fmt.Sprintf("📦 **Gift #%d arrived — Co-op #%d, Day %d**\n\n", gift.ID, run.ID, gift.Day))
if flavor != "" {
// Tally before substitution so the flavor's "Open: {count} · Leave it: {count}"
// renders with real numbers.
opens, leaves := 0, 0
for _, v := range gift.Votes {
if v == "open" {
opens++
} else if v == "leave" {
leaves++
}
}
leaderName := coopDisplayName(p, run.LeaderID)
// Pick a flavor entry and substitute its template fields. Two {count}
// placeholders are positional: first = opens, second = leaves.
if len(TwinBeeGiftArrival) > 0 {
flavor := TwinBeeGiftArrival[rand.IntN(len(TwinBeeGiftArrival))]
flavor = strings.Replace(flavor, "{count}", strconv.Itoa(opens), 1)
flavor = strings.Replace(flavor, "{count}", strconv.Itoa(leaves), 1)
flavor = strings.ReplaceAll(flavor, "{leader}", leaderName)
sb.WriteString(flavor) sb.WriteString(flavor)
sb.WriteString("\n\n") sb.WriteString("\n\n")
} }
// Actionable line — the flavor doesn't include the actual command.
closesIn := "" closesIn := ""
if gift.ExpiresAt != nil { if gift.ExpiresAt != nil {
remaining := time.Until(*gift.ExpiresAt).Truncate(time.Minute) remaining := time.Until(*gift.ExpiresAt).Truncate(time.Minute)
@@ -608,16 +625,8 @@ func renderCoopGiftPost(run *CoopRun, gift *CoopGift, members []CoopMember) stri
closesIn = " · voting closing" closesIn = " · voting closing"
} }
} }
sb.WriteString("Vote with `!coop giftvote " + strconv.Itoa(gift.ID) + " open|leave`" + closesIn + ".\n\n") sb.WriteString("Vote with `!coop giftvote " + strconv.Itoa(gift.ID) + " open|leave`" + closesIn + ".")
opens, leaves := 0, 0
for _, v := range gift.Votes {
if v == "open" {
opens++
} else if v == "leave" {
leaves++
}
}
sb.WriteString(fmt.Sprintf("Current votes: open (%d) · leave (%d)", opens, leaves))
awaiting := 0 awaiting := 0
for _, m := range members { for _, m := range members {
if _, ok := gift.Votes[m.UserID]; !ok { if _, ok := gift.Votes[m.UserID]; !ok {