101861c7f7
Incident 2 of #109521: a Gateway socket can stay ESTABLISHED and keep ACKing heartbeats while zero DISPATCH events are parsed, so every transport-side liveness sample (ready/open/ack-age/latency) reads healthy for hours. The merged #109963 deliberately dropped the event_silence dimension: a raw-frame stamp is debug-gated (on_socket_raw_receive needs enable_debug_events) and, since heartbeat ACKs are frames, ack_stale always fires first by construction. This adds the dispatch-side signal that was requested instead: - stamp on on_socket_event_type, which discord.py 2.7.1 dispatches for every parsed DISPATCH frame with no debug gate (verified live against the real received_message path: 4/4 frames fired with enable_debug_events=False, on_socket_raw_receive 0/4) - new knob websocket_event_max_silence_seconds (default 4h, the incident report's field-proven operator bound); 0 opts out of this dimension ONLY — the #109782 review failure put the knob in _start_liveness_probe's all-or-nothing guard, killing the whole watchdog; it is gated strictly inside _read_websocket_health here - the stamp resets per connection (connect() clears it), and a None stamp (no event parsed yet on this connection) is not silence - docs (en + zh-Hans) cover the new knob and the per-dimension opt-out Fixes #109521 (cherry picked from commit b4baa97fc45794209711a45e052111d7d44d5f90)