Skip to content

feature: chat message reactions - #445

Merged
lukepolo merged 3 commits into
mainfrom
feature/chat-reactions
Sep 29, 2026
Merged

lukepolo merged 3 commits into
mainfrom
feature/chat-reactions

Conversation

@lukepolo

@lukepolo lukepolo commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Players can react to any chat message with one of six reactions, and take a reaction back by sending it again.

  • lobby:react toggles the sender's reaction; the room gets the message's whole reaction state, always in ChatService.REACTIONS order
  • Same rules as sending (room access; a gag blocks adding but not taking back); no notifications, no game relay, at most 8 toggles a second per player
  • Rooms: a separate chat_reactions_<type>_<id> hash toggled by one Lua script with a per-toggle receipt, so a resent command can't undo itself
  • DMs: new direct_message_reactions table; toggles lock the message row, so racing toggles cancel out in pairs
  • New socket events: lobby:react {type, id, messageId, reaction, requestId?} in, lobby:<type>:<id>:reaction {id, reactions} out; new rate_limited error

Merge/deploy: stacked on #442; run yarn hasura:metadata after merge. Pairs with 5stackgg/web#624.

Tests: a test fails without each fix: the toggle resend receipt, gagged un-react, reaction order, and serialized DM toggles (that race test now runs 8 rounds; one round missed the bug about 1 run in 4). Based on DEAFCS f2c907512 b12c1c32a

Players can react to a chat message with one of six reactions (thumbsup,
heart, laugh, fire, wow, sad) and take it back by sending it again.

- lobby:react { type, id, messageId, reaction, requestId? } toggles the
  sender's reaction. The room gets lobby:<type>:<id>:reaction { id,
  reactions } with the message's whole reaction state, never a delta.
  chat:ack / chat:error carry action "react".
- Held to the same rules as sending: room access (canPostIn), and a gag
  blocks reacting in group rooms. Never a notification, never relayed to
  the game. At most 8 toggles a second per player (rate_limited), since
  every toggle is a broadcast to the room.
- Rooms: a separate chat_reactions_<type>_<id> hash, field per message, so
  reactions never contend with an edit's compare-and-set. One Lua script
  toggles, refuses a message that is gone, and gives the reactions the
  message's own expiry. Deleting a message clears its reactions, and the
  draft-to-match move carries them in the same step.
- DMs: direct_message_reactions, toggled in one statement scoped to the
  conversation; cascades with the message, the player and retention.
- History (room and DM) and live messages carry reactions per message.
…erialized DM toggles

- The room toggle script takes a receipt key per toggle: ioredis resends a
  command whose reply was lost to a reconnect, and a toggle run twice
  undid itself. A resend now answers with the current state instead.
- A gagged player can still take back a reaction they gave, the way #442
  lets them delete their own message; only adding one is refused.
- DM toggles lock the message row first (FOR NO KEY UPDATE) and toggle in a
  fresh statement, so racing toggles on an absent row cancel out as they
  do in rooms instead of both inserting, and a delete can no longer land
  between finding the message and writing the reaction.
- Reactions are always handed out in ChatService.REACTIONS order; the Lua
  table and jsonb each had their own.
- Index direct_message_reactions (steam_id) for the players cascade.
One round of the race lost it without the row lock only about three runs in
four, so the regression could pass. Eight rounds fail every time without the
lock.
@lukepolo
lukepolo force-pushed the feature/chat-reactions branch from 4580a4e to 8754dee Compare September 29, 2026 00:52
@lukepolo
lukepolo merged commit 29aa026 into main Sep 29, 2026
2 checks passed
@lukepolo
lukepolo deleted the feature/chat-reactions branch September 29, 2026 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant