feature: edit or delete your own chat messages for 10 minutes - #442
Merged
Merged
Conversation
This was referenced Sep 28, 2026
Contributor
Author
|
Edit ack now carries what the server stored: |
lukepolo
force-pushed
the
feature/chat-moderation
branch
from
September 29, 2026 00:49
60d2f76 to
af7a1ca
Compare
The author of a website message (source "web") can edit or delete it for
10 minutes (ChatService.SELF_SERVICE_WINDOW_MS, 5s of pod clock skew
allowed). Nobody edits another player's message, administrators included;
moderator deletes at any age stay as they were.
- lobby:edit { type, id, messageId, message, requestId? } broadcasts
lobby:<type>:<id>:edited { id, message, edited_at }, acks/errors with
action "edit", and is never relayed to the game server
- room edits are one compare-and-set Lua script against the value that was
read and checked: HSET drops a field's TTL, so the exact HPEXPIRETIME is
put back with HPEXPIREAT, and an expired or deleted field is never
written back
- a gag blocks editing in group rooms but not deleting your own message; an
author's own delete in a group room is audited like a moderator's
- DMs: direct_messages.edited_at (migration 1888000000200); edit and delete
are single statements guarded on author, room and created_at on the
database clock; not audited; history returns edited_at
- unread, live bell rows get the edited preview; rows a delete already
retracted stay blank, and an edit or delete that lands while a message's
rows are still being written is applied once they are
- new chat:error code window_closed
…c draft move - updateChatMessagePreview rewrites every row for the message except a retracted (blank) one: read rows stay in the bell for a week and a recipient can restore a collapsed one, so both kept the text the author took back. `message <> ''` still stops an edit racing a delete from writing text back, including when it waits on the retraction's row lock - migrateLobbyMessages moves a draft's messages in one Lua script, so an edit or a self-delete in the draft room can no longer land on a copy that is thrown away (edit lost) or written back (deleted message returns) - lobby:edit answers an empty or non-string edit with chat:error invalid instead of leaving the client waiting - deleting the only message of a DM conversation takes it off both rails, so the recipient is not left with an empty tab from the sender - tests: the edit/delete race asserts the refusal code, the DM catch-up test deletes through the service, a retraction holding the row lock mid-edit
An author could post abuse and edit it into something harmless within the 10 minute window, and nothing of the original survived. Editing and then deleting kept only the edited text in chat_message_deletions. - chat_message_edits (message, room, author, previous and new text, when the message was sent, when it was edited), one row per group-room edit. DMs are never audited, the same as deletions. - The row is written before the redis compare-and-set, the way a deletion is audited before its HDEL, so a failed audit write refuses the edit. A swap that finds the message gone or changed deletes its row again; a swap that fails outright keeps it, since it may have applied. - The swap leaves a short-lived receipt keyed on its audit row, so an ioredis resend after a lost reply answers 1 even if the message was deleted or edited again in between, instead of reading as a failed swap and discarding the audit of an edit that happened. - Hasura: moderator reads every row outside the organizers' room, match_organizer and up read everything; no insert/update/delete.
With the move made of separate calls, an author's delete in the draft room landed on a copy that was written back into the match, and an edit landed on one that was thrown away.
lukepolo
force-pushed
the
feature/chat-self-edit
branch
from
September 29, 2026 00:51
aa3e4cb to
5715f46
Compare
lukepolo
added a commit
that referenced
this pull request
Sep 29, 2026
…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.
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.
Players can edit or delete their own website messages for 10 minutes, and every group-room edit is audited so abuse can't be edited away.
lobby:editswaps the message in one compare-and-set Lua script that keeps its exact expiry and never brings back a deleted or expired messagechat_message_editsaudit (previous and new text), written before the swap; a resend after a lost redis reply counts as applieddirect_messages.edited_atand guarded single-statement edit/delete on the DB clock; deleting a conversation's only message takes it off both railslobby:edit {type, id, messageId, message, requestId?}in,:edited {id, message, edited_at}out; newwindow_closederrorMerge/deploy: stacked on #438; run
yarn hasura:metadataafter merge. Pairs with 5stackgg/web#622.Tests: a test fails without each fix: the edit ack carrying the stored
edited_at, an empty edit answeredinvalid, previews on read and collapsed rows, the DM only-message rail cleanup, kept TTL, no resurrection, the resend receipt, audit before swap, the discarded audit of a failed swap, the notification catch-up, and (new test) the atomic draft move. Based on DEAFCS ee3ce335e c76bba393