mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 00:52:40 +00:00
Coop: stack same-type gifts on the same day into a single vote post
Multiple baskets (or multiple mimics) sent during the same day now share one game-room post, one vote, one resolution. First-in becomes the stack "lead"; subsequent same-type sends become followers that inherit the lead's deadline and votes. Behavior: - Send a basket → new lead, new post, 6h timer starts - Send another basket within the window → silently joins the stack, edits lead's post to bump count - At stack size 2, TwinBee adds a "this gift looks REALLY special" line (one of 5 variants picked deterministically by lead id). No further escalation as the stack grows - One !coop giftvote on the lead's id covers the whole stack - Resolution applies the same outcome to every gift in the stack; modifier is N × ±6 - End-of-run gift log groups by stack with all senders attributed Schema: coop_dungeon_gifts.stack_lead_id INTEGER (NULL for lead/standalone, otherwise points at lead's id). Migration entry included. Why first-in deadline (vs extending on each follower): exploitability. Saboteurs could spam-extend a stack to delay resolution. Locked deadline keeps senders honest about timing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -217,22 +217,43 @@ func renderCoopStatus(p *AdventurePlugin, run *CoopRun, members []CoopMember) st
|
||||
sb.WriteString(renderCoopOddsLine(run, members, bets))
|
||||
}
|
||||
|
||||
// Pending gift list with per-gift countdowns. Voting windows expire
|
||||
// independently throughout the day, so this is the canonical place to
|
||||
// see what's still open.
|
||||
// Pending stacks with per-stack countdowns. Each game-room post is one
|
||||
// stack (lead + N followers); we show the lead's id and the stack size.
|
||||
if run.Status == "active" {
|
||||
all, _ := loadAllCoopGifts(run.ID)
|
||||
var pending []CoopGift
|
||||
for _, g := range all {
|
||||
if g.VoteResult == "" {
|
||||
pending = append(pending, g)
|
||||
}
|
||||
// Group unresolved gifts by lead. Followers contribute to the size
|
||||
// but use the lead's expiry/votes.
|
||||
type stackInfo struct {
|
||||
lead CoopGift
|
||||
size int
|
||||
}
|
||||
if len(pending) > 0 {
|
||||
stacks := map[int]*stackInfo{}
|
||||
order := []int{}
|
||||
for _, g := range all {
|
||||
if g.VoteResult != "" {
|
||||
continue
|
||||
}
|
||||
leadID := g.ID
|
||||
if g.StackLeadID != nil {
|
||||
leadID = *g.StackLeadID
|
||||
}
|
||||
s, ok := stacks[leadID]
|
||||
if !ok {
|
||||
s = &stackInfo{}
|
||||
stacks[leadID] = s
|
||||
order = append(order, leadID)
|
||||
}
|
||||
if g.StackLeadID == nil {
|
||||
s.lead = g
|
||||
}
|
||||
s.size++
|
||||
}
|
||||
if len(stacks) > 0 {
|
||||
sb.WriteString("\n\nPending gifts:\n")
|
||||
for _, g := range pending {
|
||||
for _, leadID := range order {
|
||||
s := stacks[leadID]
|
||||
opens, leaves := 0, 0
|
||||
for _, v := range g.Votes {
|
||||
for _, v := range s.lead.Votes {
|
||||
if v == "open" {
|
||||
opens++
|
||||
} else if v == "leave" {
|
||||
@@ -240,16 +261,20 @@ func renderCoopStatus(p *AdventurePlugin, run *CoopRun, members []CoopMember) st
|
||||
}
|
||||
}
|
||||
countdown := "—"
|
||||
if g.ExpiresAt != nil {
|
||||
remaining := time.Until(*g.ExpiresAt).Truncate(time.Minute)
|
||||
if s.lead.ExpiresAt != nil {
|
||||
remaining := time.Until(*s.lead.ExpiresAt).Truncate(time.Minute)
|
||||
if remaining > 0 {
|
||||
countdown = remaining.String()
|
||||
} else {
|
||||
countdown = "closing"
|
||||
}
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf(" #%d (day %d) — open %d / leave %d — closes in %s\n",
|
||||
g.ID, g.Day, opens, leaves, countdown))
|
||||
stackLabel := fmt.Sprintf("#%d", s.lead.ID)
|
||||
if s.size > 1 {
|
||||
stackLabel = fmt.Sprintf("#%d ×%d", s.lead.ID, s.size)
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf(" %s (day %d) — open %d / leave %d — closes in %s\n",
|
||||
stackLabel, s.lead.Day, opens, leaves, countdown))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user