docs(telegram): invert chat-gate strategy in docs; tune logs; i18n text
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 57s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m15s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 57s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m15s
- Bake the final default-allow + mute-the-ineligible strategy into docs/ARCHITECTURE.md, docs/FUNCTIONAL.md (+_ru), platform/telegram/README.md, the deploy compose comment and the PRERELEASE tracker. The live test proved a per-user grant cannot exceed a deny-by-default group (Telegram intersects the chat default with the per-user permission), so the chat allows sending by default and the bot restricts the ineligible instead of granting the eligible. - Lower the per-event chat_member trace and eligibility evaluation to Debug; keep the actual mute/unmute actions, the startup self-check and warnings at Info, so prod logs only what the bot did. - Update game.searchingForOpponent (Searching -> Waiting for opponent / Поиск -> Ждём соперника) and the quickmatch e2e assertions to match.
This commit is contained in:
@@ -43,15 +43,19 @@ Telegram identity to an account from a browser. Both map a rejection to gRPC
|
||||
code. This is **self-contained** — the bot never calls back into the game, so `/start`
|
||||
onboarding works even when the game is down.
|
||||
- **Moderated-chat gating.** When `TELEGRAM_CHAT_ID` names a channel's linked discussion
|
||||
group, the bot gates who may write there. The group is configured **default no-send**
|
||||
(a human setting); the bot grants write access to a user who **joins** when they are
|
||||
registered and neither admin-suspended nor `chat_muted`, asking the gateway over the
|
||||
bot-link (`ResolveChatEligibility`). When an operator blocks/unblocks an account or
|
||||
toggles its `chat_muted` role, the gateway pushes a `ChatGate` command and the bot
|
||||
applies it — but only to a member currently in the chat (it probes one user with
|
||||
`getChatMember`, since bots cannot list members). The bot must be an **administrator**
|
||||
there with the **"Ban users"** right (the Bot API `can_restrict_members`), and it
|
||||
subscribes to `chat_member` updates, which Telegram delivers only to a chat admin.
|
||||
group, the bot gates who may write there. The group **allows sending by default** (a
|
||||
human setting) and the bot only **restricts** — Telegram intersects the chat default with
|
||||
each user's permission, so a per-user grant cannot exceed a deny-by-default group, and the
|
||||
gate must mute the ineligible rather than grant the eligible. On a `chat_member` event the
|
||||
bot asks the gateway (`ResolveChatEligibility`) and **mutes** a member who is not registered
|
||||
or is admin-suspended or `chat_muted`, **un-mutes** an eligible one it had muted, and leaves
|
||||
an already-allowed eligible member untouched (it acts only when the state differs, so it is
|
||||
idempotent and skips its own change). When an operator blocks/unblocks an account, toggles
|
||||
its `chat_muted` role, or a user first registers, the gateway pushes a `ChatGate` command and
|
||||
the bot applies it — but only to a member currently in the chat (it probes one user with
|
||||
`getChatMember`, since bots cannot list members). The bot must be an **administrator** there
|
||||
with the **"Ban users"** right (the Bot API `can_restrict_members`), and it subscribes to
|
||||
`chat_member` updates, which Telegram delivers only to a chat admin.
|
||||
- **Promo bot (optional).** When `TELEGRAM_PROMO_BOT_TOKEN` is set, the container also
|
||||
runs a **second, standalone** bot whose only job is to answer `/start` with a localized
|
||||
message and a button that opens the **main** bot's Mini App. The button is a **URL** to
|
||||
|
||||
@@ -270,7 +270,7 @@ func (t *Bot) handleChatMember(ctx context.Context, cm *models.ChatMemberUpdated
|
||||
// Telegram delivers joins, for which chat, the transition, who performed it, and the
|
||||
// new member's send/membership state.
|
||||
canSend, isMember := restrictedSendState(cm.NewChatMember)
|
||||
t.log.Info("chat_member update",
|
||||
t.log.Debug("chat_member update",
|
||||
zap.Int64("chat_id", cm.Chat.ID),
|
||||
zap.Int64("configured_chat_id", t.chatID),
|
||||
zap.Int64("user_id", uid),
|
||||
@@ -318,7 +318,7 @@ func (t *Bot) handleChatMember(ctx context.Context, cm *models.ChatMemberUpdated
|
||||
t.log.Warn("chat access eligibility failed", zap.Int64("user_id", user.ID), zap.Error(err))
|
||||
return
|
||||
}
|
||||
t.log.Info("chat access evaluated",
|
||||
t.log.Debug("chat access evaluated",
|
||||
zap.Int64("user_id", user.ID), zap.Bool("eligible", eligible), zap.Bool("can_send", currentlyCanSend))
|
||||
// Desired: an eligible user may send, an ineligible one may not. Act only when the
|
||||
// current state differs — idempotent, a no-op for the common eligible member, and it
|
||||
@@ -356,7 +356,7 @@ func (t *Bot) ApplyChatGate(ctx context.Context, userID int64, allow bool) (bool
|
||||
t.log.Info("chat gate applied", zap.Int64("user_id", userID), zap.Bool("allow", allow))
|
||||
return true, nil
|
||||
default:
|
||||
t.log.Info("chat gate: user not in chat, skipped", zap.Int64("user_id", userID), zap.String("status", string(member.Type)))
|
||||
t.log.Debug("chat gate: user not in chat, skipped", zap.Int64("user_id", userID), zap.String("status", string(member.Type)))
|
||||
return false, nil // absent, or an admin/owner who cannot be restricted
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user