Truth or Dare is a room-scoped, opt-in, chat-only auction over shared economy credits. The runtime validates configuration and approved prompt records, pins their revisions in each session, reserves each participant’s current bid, and settles only persisted outcomes.
The feature is wired into the static plug-in host.
TRUTH_DARE_GAMEPLAY_ENABLED is true following the operator’s 2026-10-15
report that live bidding, settlement, decline, timeout, and restart-recovery
qualification succeeded and their explicit activation approval. This is an
operator-reported qualification, not a live run performed by the provisioning
tool. Room configuration remains disabled by default, and an auction cannot
open without at least two approved prompts in the configured locale.
Startup inserts the configuration below and ten approved English starter prompts only when absent. Existing rules, enabled state, timestamps, and prompt revisions are preserved. The starter catalog contains five playful fictional truths and five non-explicit, chat-only dares; none requests physical actions or private information.
The implementation adopts these bounded decisions:
/bot dare complete; the configured reward is granted once after that
attestation. The bot does not verify the challenge independently.Credits have no real-money value. Prompts must be approved, bounded text that is safe to complete in room chat. They must not ask for identifying or intimate information, physical or external actions, images or voice, or disclosure about non-consenting people. Moderation status is an operator assertion, not an automated semantic safety classifier.
All feedback is privately whispered; other players’ bids and wallet balances are never announced.
| Command | Behavior |
|---|---|
/bot dare rules |
Privately show configured rules and bounds. |
/bot dare join |
Show the rules and the explicit opt-in command. |
/bot dare join accept |
Join the pre-auction roster while present in the observed room. |
/bot dare leave |
Withdraw before an auction is opened. |
/bot dare open |
Room-admin only; freeze the roster and open bidding. |
/bot dare bid <credits> |
Place, raise, or lower a bid using one base-10 integer. |
/bot dare cancel |
Cancel a bid before the configured bid-change cutoff. |
/bot dare complete |
Explicitly attest the current winner or lowest-bid challenge. |
/bot dare decline |
Decline the current challenge without claiming completion. |
Global and scoped help, including /bot dare rules and admin-only commands,
is shown only while both the code gate and room configuration enable the game.
The commands remain registered while disabled.
Bid reductions and cancellation close bidChangeCloseMsBeforeDeadline before
the bidding deadline; raises remain available until the deadline. Accepted bids
must be within the configured bounds and follow bidIncrementCredits. Each
participant has one active bid; adjustments atomically reserve or release only
the difference. If a reservation fails, the prior bid remains unchanged.
At close, fewer than two valid bidders voids the round and refunds all reservations. Non-winner reservations are released before the winner challenge. A non-bidder is never selected as the low bidder or charged.
Configuration is stored in the selected runtime database’s
prototypePartyGameConfigs collection. There is one document per (roomKey,
gameId) with _id: "<roomKey>:truth-dare", gameId: "truth-dare", a positive
revision, timestamps, and a config matching this shape:
{
"enabled": false,
"locale": "en",
"minPlayers": 2,
"maxPlayers": 8,
"bidWindowMs": 45000,
"challengeWindowMs": 60000,
"bidChangeCloseMsBeforeDeadline": 5000,
"minBidCredits": 1,
"maxBidCredits": 100,
"bidIncrementCredits": 1,
"rewardCredits": 5,
"lowBidPolicy": "challenge_only",
"declinePolicy": "release",
"maxRoundsPerWindow": 2
}
The implementation caps the roster at 20, bid amounts at 1,000 credits,
rewards at 1,000 credits, each timed window at five minutes, and rounds at 10
per rolling 24-hour period. Invalid or absent settings fail closed. This config
is owned by this feature and is not managed through prototypeGameConfig or the
world-admin commands.
prototypeTruthDarePrompts stores immutable revisions. _id is
"<promptId>:r<revision>"; each record includes kind (truth or dare),
category, locale, text (10–500 characters), challenge level (1–5),
moderationStatus (pending, approved, or rejected), revision, and
timestamps. Operators should create a new record for each content revision
rather than editing an in-use revision. The runtime selects two distinct prompt
IDs in the configured locale and rechecks approval when recovering a challenge.
Use the party-game provisioning workflow to inspect, populate, and explicitly enable an approved room. New database defaults stay disabled; production activation is separate from test qualification.
prototypeTruthDareSessions stores the frozen member-number roster, pinned
configuration and prompt revisions, bounded bids, acceptance sequence, phase,
deadlines, attestation/decline state, and stable settlement identifiers. It
stores no challenge answer, chat transcript, or response text. Active sessions
hold a unique room key and have no TTL; terminal sessions expire after 30 days.
Concurrent bids and phase changes use revision-checked updates. A persisted
pending bid is replayed with the same economy operation ID after restart.
prototypeEconomyAccounts and prototypeEconomyLedger remain the only balance
authority. Truth/Dare uses the shared port’s atomic reservation adjustment for
bid changes; terminal operations use stable IDs for refunds, consumption, and
the one-time completion reward. The economic ledger remains separate from game
session state and BC action journaling. If an economic operation cannot be
reconciled, the session remains active for recovery/operator review; it is not
reported as settled.
The plug-in cancels its deadline timers and drains pending work on stop, leaving unresolved sessions and reservations recoverable. Deadline callbacks do not claim challenge completion. BC chat and whisper actions retain their existing dispatch-unconfirmed semantics.
flowchart LR
Root["start.ts"] --> Plugin["TruthDarePlugin"]
Plugin --> Commands["TruthDareCommandSuite"]
Plugin --> Service["TruthDareService"]
Commands --> Service
Service --> Repo["MongoTruthDareRepository"]
Service --> Economy["EconomyPort"]
Service --> Communication["Injected private-whisper capability"]
Repo --> Config[("prototypePartyGameConfigs")]
Repo --> Prompts[("prototypeTruthDarePrompts")]
Repo --> Sessions[("prototypeTruthDareSessions")]
stateDiagram-v2
[*] --> Bidding: gameplay gate + enabled room config
Bidding --> Bidding: reserve or adjust bid
Bidding --> Voided: fewer than two bidders; refund reservations
Bidding --> WinnerChallenge: close and persist high/low bidders
WinnerChallenge --> Settling: attested / declined / left / timed out
Settling --> LowChallenge: winner reservation resolved
LowChallenge --> Settling: low challenge attested / declined / left / timed out
Settling --> Settling: recovery retries stable operation IDs
Settling --> Complete: reservations and rewards reconciled
Voided --> Complete
Complete --> [*]
The winning stake and completion reward are applied only after explicit self-attestation. Recovery uses the persisted outcome and reservation IDs; it does not rerun the auction or choose new recipients.
src/features/truth-dare/prototypePartyGameConfigs,
prototypeTruthDarePrompts, prototypeTruthDareSessions