[Server] Let users fetch their own private chat history (#7506)

* [Server] Let users fetch their own private chat history

Players lose their private conversations on reconnect or on another
device. Servatrice can already log private messages, but only
moderators can read the logs.

Add Command_GetPrivateChatHistory / Response_PrivateChatHistory so a
registered user can page through their own logged conversation with one
other user. Operators opt in with logging/allow_user_chat_history
(default off, and log_user_msg_chat must be on). The caller is matched
by account id so reused guest or deleted-account names can't read older
messages; only time, sender name and text are returned. Pages hold
1..200 messages and never split a second, since log_time has 1-second
resolution.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* [Server] Harden private chat history identity matching

Match both participants by account id and name so a federated user's remote id cannot collide with a local account, and stop storing foreign ids in the local chat log. Extract the paging assembly into a pure helper and cover the whole-second boundary rules with gtests.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Lukas Brübach <44814898+BruebachL@users.noreply.github.com>
This commit is contained in:
SwitchOCE 2026-11-09 23:21:43 +11:00 • committed by GitHub
parent 3d8c729517
commit 7709184ecc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 395 additions and 4 deletions

View file

@ -33,6 +33,7 @@ set(PROTO_FILES
command_dump_zone.proto
command_flip_card.proto
command_game_say.proto
command_get_private_chat_history.proto
command_get_server_stats.proto
command_inc_card_counter.proto
command_inc_counter.proto
@ -164,6 +165,7 @@ set(PROTO_FILES
response_list_users.proto
response_login.proto
response_password_salt.proto
response_private_chat_history.proto
response_register.proto
response_replay_download.proto
response_replay_download_by_game_id.proto
@ -202,6 +204,7 @@ set(PROTO_FILES
serverinfo_player.proto
serverinfo_playerping.proto
serverinfo_playerproperties.proto
serverinfo_private_chat_message.proto
serverinfo_replay.proto
serverinfo_replay_match.proto
serverinfo_report.proto

View file

@ -0,0 +1,18 @@
syntax = "proto2";
import "session_commands.proto";
/**
* Fetch the caller's private conversation with user_name.
* user_name must be an active registered account; guest and deleted-account
* names have no stable account id, so they are rejected with RespNameNotFound.
* before is exclusive Unix seconds; zero or unset applies no upper time bound.
* limit is clamped to 1..200; a whole second may exceed it to preserve pagination.
*/
message Command_GetPrivateChatHistory {
extend SessionCommand {
optional Command_GetPrivateChatHistory ext = 1035;
}
optional string user_name = 1;
optional uint64 before = 2;
optional uint32 limit = 3 [default = 50];
}

View file

@ -78,6 +78,7 @@ message Response {
PASSWORD_SALT = 1017; // Response containing password salt
GET_ADMIN_NOTES = 1018; // Response with admin notes
GET_SERVER_STATS = 1019; // Response with server status statistics
PRIVATE_CHAT_HISTORY = 1020; // Response with the caller's private conversation
REPLAY_LIST = 1100; // Response listing replays
REPLAY_DOWNLOAD = 1101; // Response for replay download
REPLAY_GET_CODE = 1102; // Response containing replay code

View file

@ -0,0 +1,16 @@
syntax = "proto2";
import "response.proto";
import "serverinfo_private_chat_message.proto";
/**
* Private conversation messages, oldest first, without account IDs or IP addresses.
* When has_more is true, use the oldest returned time as before on the next request.
* Seconds are never split across pages; ordering within a second is unspecified.
*/
message Response_PrivateChatHistory {
extend Response {
optional Response_PrivateChatHistory ext = 1020;
}
repeated ServerInfo_PrivateChatMessage messages = 1;
optional bool has_more = 2;
}

View file

@ -0,0 +1,10 @@
syntax = "proto2";
/**
* A private conversation message with its time in Unix seconds.
*/
message ServerInfo_PrivateChatMessage {
optional uint64 time = 1;
optional string sender_name = 2;
optional string message = 3;
}

View file

@ -37,6 +37,7 @@ message SessionCommand {
DECK_SHARE_LIST_MINE = 1032;
DECK_SHARE_REMOVE = 1033;
SUBMIT_PASSWORD_VERIFIER = 1034;
GET_PRIVATE_CHAT_HISTORY = 1035;
REPLAY_LIST = 1100;
REPLAY_DOWNLOAD = 1101;
REPLAY_MODIFY_MATCH = 1102;