fix(voice): stop stale mute echoes from toggling mute (#3268)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-08 03:04:10 +02:00
1 parent e218ba21de
commit c51c222c47
2 files changed
+57 -3

No files matched your search

@@ -265,6 +265,7 @@ describe('shouldApplyGatewayVoiceStateEcho', () => {
it('applies the echo when it matches the pending local write', () => {
const snapshot = connectedSnapshot({
desiredVoiceState: desired({selfMute: false}),
desiredVoiceStateWrite: {
guildId: 'guild-1',
channelId: 'channel-1',
@@ -279,6 +280,7 @@ describe('shouldApplyGatewayVoiceStateEcho', () => {
it('rejects a stale echo that differs from the pending local write', () => {
const snapshot = connectedSnapshot({
desiredVoiceState: desired({selfMute: false}),
desiredVoiceStateWrite: {
guildId: 'guild-1',
channelId: 'channel-1',
@@ -290,4 +292,55 @@ describe('shouldApplyGatewayVoiceStateEcho', () => {
});
expect(shouldApplyGatewayVoiceStateEcho(snapshot, reported({selfMute: true}))).toBe(false);
});
it('rejects the echo of a pending write the user has already reverted', () => {
const snapshot = connectedSnapshot({
selfVoiceState: reported({selfMute: false}),
desiredVoiceState: desired({selfMute: false}),
desiredVoiceStateWrite: {
guildId: 'guild-1',
channelId: 'channel-1',
selfMute: true,
selfDeaf: false,
selfVideo: false,
selfStream: false,
},
});
expect(shouldApplyGatewayVoiceStateEcho(snapshot, reported({selfMute: true}))).toBe(false);
});
it('settles on the last intent after a mute and unmute faster than the echo', () => {
let snapshot = connectedSnapshot({selfVoiceState: reported({selfMute: false})});
const muted = transitionVoiceEngineV2(snapshot, {
type: 'gateway.desiredVoiceStateChanged',
desired: desired({selfMute: true}),
});
expect(muted.commands).toMatchObject([{type: 'gateway.voiceState.write', options: {selfMute: true}}]);
snapshot = transitionVoiceEngineV2(muted.snapshot, {
type: 'gateway.voiceStateWriteSucceeded',
operationId: muted.snapshot.gateway.operationId as number,
}).snapshot;
const unmuted = transitionVoiceEngineV2(snapshot, {
type: 'gateway.desiredVoiceStateChanged',
desired: desired({selfMute: false}),
});
expect(unmuted.commands).toEqual([]);
snapshot = unmuted.snapshot;
const staleEcho = reported({selfMute: true});
expect(shouldApplyGatewayVoiceStateEcho(snapshot, staleEcho)).toBe(false);
snapshot = transitionVoiceEngineV2(snapshot, {type: 'gateway.voiceStateUpdated', voiceState: staleEcho}).snapshot;
const corrective = transitionVoiceEngineV2(snapshot, {type: 'gateway.voiceStateReconcileRequested'});
expect(corrective.commands).toMatchObject([{type: 'gateway.voiceState.write', options: {selfMute: false}}]);
snapshot = transitionVoiceEngineV2(corrective.snapshot, {
type: 'gateway.voiceStateWriteSucceeded',
operationId: corrective.snapshot.gateway.operationId as number,
}).snapshot;
const finalEcho = reported({selfMute: false});
expect(shouldApplyGatewayVoiceStateEcho(snapshot, finalEcho)).toBe(true);
snapshot = transitionVoiceEngineV2(snapshot, {type: 'gateway.voiceStateUpdated', voiceState: finalEcho}).snapshot;
expect(snapshot.gateway.desiredVoiceStateWrite).toBeNull();
expect(transitionVoiceEngineV2(snapshot, {type: 'gateway.voiceStateReconcileRequested'}).commands).toEqual([]);
});
});
@@ -226,9 +226,10 @@ export function shouldApplyGatewayVoiceStateEcho(
): boolean {
assert.ok(snapshot != null, 'shouldApplyGatewayVoiceStateEcho snapshot must not be null');
assert.ok(echo != null, 'shouldApplyGatewayVoiceStateEcho echo must not be null');
const pending = snapshot.gateway.desiredVoiceStateWrite;
if (pending == null) return true;
return gatewayVoiceStateWriteMatchesReported(pending, echo);
if (snapshot.gateway.desiredVoiceStateWrite == null) return true;
const desired = deriveVoiceEngineV2DesiredGatewayVoiceState(snapshot);
if (desired == null) return false;
return gatewayVoiceStateWriteMatchesReported(desired, echo);
}
export function gatewayVoiceStateWriteMatchesReported(