Wire VipGift dispatch for vote rewards via template clone#48
Merged
Conversation
The VipGift reward type now actually grants — earlier this was a stub
that threw NotImplementedException so admins selecting it got a clear
message rather than a silent no-op.
Approach: a "template" is just a vip_gift row whose player_id is the
sentinel "_template_". Admins build templates through the existing
VIP Gifts admin UI by typing that string as the player_id; the VoteRewards
feature looks them up by name and clones them per voter:
1. GetTemplateByName(name) — finds the template (lowest id wins on dups)
2. Read template's items + commands BEFORE insert so a partial failure
doesn't leave a half-built gift in the voter's pending list
3. Insert a new vip_gifts row keyed to the voter's player_id
4. Mirror the items + commands junctions into the new row
5. Voter claims with /vip on next login
Sentinel-prefix instead of an is_template column means no schema migration
and no junction-table scaffolding work — templates just don't show up in
any real player's pending-gift list because GetByPlayerId is exact-match.
Frontend changes:
- VIP gift template-name field is no longer disabled
- Dropdown label is plain "VIP Gift" (was "VIP Gift (not yet implemented)")
- New hint string explains the "_template_" sentinel convention so admins
know exactly what to put in the player_id field when building the template
CD Key dispatch still throws NotImplementedException — that's the next slot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8 tasks
AdaInTheLab
added a commit
that referenced
this pull request
Apr 30, 2026
Bug introduced (in spirit) by #46 and made very visible by #48: when DispatchReward throws AFTER the audit/idempotency row was inserted, the player is permanently locked out of ever claiming that vote. Future sweeps see HasGrantForDate=true and short-circuit before retrying, even though no reward was ever actually granted. This always could have happened (e.g. SQLite glitch on AdjustPoints), but #48's VipGift dispatch made it likely — a misspelled template name in the Vote Rewards settings would silently lock out every voter on that provider. Fix: narrow try/catch around the DispatchReward call. On exception, delete the audit row via the new IVoteGrantRepository.DeleteByKey so the next sweep can retry once the admin fixes the misconfiguration. The outer try/catch (network errors during GetClaimStatus / MarkClaimed) is unaffected — those failure modes don't leave an orphaned row, so they don't need rollback. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The VipGift reward type now actually grants. In #46 it was a stub throwing `NotImplementedException`; this PR wires up the template-clone path so admins can pick "VIP Gift" in the Vote Rewards settings and have voters get an actual loot bundle.
Design: sentinel player_id, no schema change
A "template" is just a `vip_gifts` row whose `player_id` is the sentinel string `template`. Admins build templates via the existing VIP Gifts admin tab — type `template` as the player_id, give it a name, attach items + commands. VoteRewards finds it by name and clones it per voter.
Why this over an `is_template` column:
Flow
Files
What's left
CD Key dispatch still throws `NotImplementedException` — that's the next slot if we end up wanting it. The CD Key system already has key generation + redemption; the missing piece is generating a fresh single-use key from a template on grant.
Test plan
🤖 Generated with Claude Code