// diplomail contains FlatBuffers payloads used by the authenticated // gateway boundary for the in-game diplomatic-mail subsystem. The // wire shapes here mirror the trusted internal // `/api/v1/user/games/{game_id}/mail/*` REST surface; gateway derives // the calling `user_id` from the verified session and forwards it as // `X-User-Id` to backend. include "common.fbs"; namespace diplomail; // MailMessage stores one inbox / sent-list / message-detail row. The // fields mirror `UserMailMessageDetail` in `backend/openapi.yaml` // with the following encoding rules: // // - `*_user_id` fields are RFC 4122 string UUIDs ("" means absent // for nullable fields such as `sender_user_id`). // - `*_at_ms` fields carry Unix milliseconds; `0` means the // timestamp is absent (e.g. an unread message has // `read_at_ms == 0`). // - `translated_*`, `translation_lang`, and `translator` are set // when the backend served a cached rendering into the caller's // preferred language; empty otherwise. // - `sender_race_name` is the snapshot of the sender's race name // in this game at send time. Present for `sender_kind="player"` // messages when the sender had an active membership; absent for // admin and system messages. The in-game UI keys per-race // threading on this field. table MailMessage { message_id:string; game_id:string; game_name:string; kind:string; sender_kind:string; sender_user_id:string; sender_username:string; sender_race_name:string; subject:string; body:string; body_lang:string; broadcast_scope:string; created_at_ms:int64; recipient_user_id:string; recipient_user_name:string; recipient_race_name:string; read_at_ms:int64; deleted_at_ms:int64; translated_subject:string; translated_body:string; translation_lang:string; translator:string; } // MailRecipientState mirrors the `UserMailRecipientState` payload // returned from mark-read and soft-delete endpoints. Same timestamp // conventions as `MailMessage`. table MailRecipientState { message_id:string; read_at_ms:int64; deleted_at_ms:int64; } // MailBroadcastReceipt mirrors `UserMailBroadcastReceipt`. Returned // from broadcast sends (paid-tier and admin); `recipient_count` is // the number of recipient rows the server materialised. table MailBroadcastReceipt { message_id:string; game_id:string; game_name:string; kind:string; sender_kind:string; subject:string; body:string; body_lang:string; broadcast_scope:string; created_at_ms:int64; recipient_count:int32; } // InboxRequest stores the read-side request for the caller's inbox // in `game_id`. Backend filters to messages with `available_at` set // (translation completed when the recipient's preferred language // differs from the body language). table InboxRequest { game_id:common.UUID (required); } // InboxResponse stores the resulting inbox list, newest first. // `items` is empty when the caller has no available messages in // this game. table InboxResponse { items:[MailMessage]; } // SentRequest stores the read-side request for the caller's sent // personal messages in `game_id`. Admin / system rows are not // included. table SentRequest { game_id:common.UUID (required); } // SentResponse stores the caller's outgoing personal-message list. // Each `MailMessage` carries the original recipient snapshot. table SentResponse { items:[MailMessage]; } // MessageGetRequest stores the read-side request for a single // message detail. The caller must be a recipient of the message. table MessageGetRequest { game_id:common.UUID (required); message_id:common.UUID (required); } // MessageGetResponse stores the fully decorated message detail // including any cached translation into the caller's preferred // language. table MessageGetResponse { message:MailMessage; } // SendRequest stores the write-side request for a single-recipient // personal send. Exactly one of `recipient_user_id` / // `recipient_race_name` must be supplied; the empty string means // "use the other field". table SendRequest { game_id:common.UUID (required); recipient_user_id:string; recipient_race_name:string; subject:string; body:string; } // SendResponse echoes the freshly inserted message detail. table SendResponse { message:MailMessage; } // BroadcastRequest stores the paid-tier player broadcast. The // recipient set is always "every other active member of the game". table BroadcastRequest { game_id:common.UUID (required); subject:string; body:string; } // BroadcastResponse stores the receipt returned by the server. table BroadcastResponse { receipt:MailBroadcastReceipt; } // AdminRequest stores the owner-only admin send. `target="user"` // requires exactly one of `recipient_user_id` / `recipient_race_name`; // `target="all"` accepts the optional `recipients` scope (default // `active`). table AdminRequest { game_id:common.UUID (required); target:string; recipient_user_id:string; recipient_race_name:string; recipients:string; subject:string; body:string; } // AdminResponse carries the result of an admin send. When the // request had `target="user"`, `message` is set; when `target="all"`, // `receipt` is set. Callers branch on which field is present. table AdminResponse { message:MailMessage; receipt:MailBroadcastReceipt; } // ReadRequest stores the mark-read intent for a single message. The // caller must be a recipient. Idempotent. table ReadRequest { game_id:common.UUID (required); message_id:common.UUID (required); } // ReadResponse echoes the recipient state after the operation. table ReadResponse { state:MailRecipientState; } // DeleteRequest stores the soft-delete intent for a single message. // The message must already be marked read (HTTP 409 otherwise). table DeleteRequest { game_id:common.UUID (required); message_id:common.UUID (required); } // DeleteResponse echoes the recipient state after the operation. table DeleteResponse { state:MailRecipientState; }