fix(social): robot blocks
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 17s
CI / ui (pull_request) Successful in 52s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m21s

Blocking an auto-match opponent who is secretly a pooled robot is recorded  instead in a separate `robot_blocks` table.

Now blocking behaves the same in that game (struck name, hidden composer) and lists the blocked opponent under the name you saw, but is recorded only against that game — the disguise holds, the shared robot is never globally blocked, and the matchmaker keeps pairing you with robots (so you can never block yourself out of opponents).

- the shared robot account is never put in `blocks`
- the matchmaker keeps it free and it is not blocked under its other per-game names
- the blocked list and the in-game card still show it by joining that table; an unblock deletes the row
This commit is contained in:
Ilia Denisov
2026-06-18 13:12:19 +02:00
parent 81b9e1529e
commit 64be0572b3
29 changed files with 700 additions and 68 deletions
+16 -4
View File
@@ -42,9 +42,20 @@ type RedeemResultResp struct {
Friend AccountRefResp `json:"friend"`
}
// BlockListResp is the accounts the caller has blocked.
// BlockListResp is the accounts the caller has blocked, plus the per-game disguised-robot
// blocks (not real accounts).
type BlockListResp struct {
Blocked []AccountRefResp `json:"blocked"`
Robots []RobotBlockResp `json:"robots"`
}
// RobotBlockResp is one per-game disguised-robot block: the row id (to unblock it), the game
// name the player saw, and the game + seat it was blocked in.
type RobotBlockResp struct {
ID string `json:"id"`
DisplayName string `json:"display_name"`
GameID string `json:"game_id"`
Seat int `json:"seat"`
}
// StatsResp is a durable account's lifetime statistics. BestMoves breaks the best move
@@ -188,10 +199,11 @@ func (c *Client) RedeemFriendCode(ctx context.Context, userID, code string) (Red
// --- blocks ---
// Block blocks an account.
func (c *Client) Block(ctx context.Context, userID, targetID string) error {
// Block blocks an account. A non-empty gameID marks an in-game block, so a disguised-robot
// opponent is recorded as a per-game block; it is empty for a settings-screen block.
func (c *Client) Block(ctx context.Context, userID, targetID, gameID string) error {
return c.do(ctx, http.MethodPost, "/api/v1/user/blocks", userID, "",
map[string]string{"account_id": targetID}, nil)
map[string]string{"account_id": targetID, "game_id": gameID}, nil)
}
// Unblock removes a block.