Skip to content

Commit 38e828e

Browse files
authored
feat!: add VoiceConnect/Disconnect events and update voice event semantics (#314)
* feat(events): add VoiceConnectEvent and VoiceDisconnectEvent * refactor(events)!: change VoiceLeaveEvent signature * refactor(events)!: update voice packet dispatching logic * chore(events): register VoiceConnectPacket and VoiceDisconnectPacket * feat(events): register VoiceConnect and VoiceDisconnect in Event enum * feat(events): add voiceConnect and voiceDisconnect to EventBucket
1 parent bd53e57 commit 38e828e

File tree

11 files changed

+148
-20
lines changed

11 files changed

+148
-20
lines changed

lib/events.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
library events;
33

44
export 'package:mineral/src/domains/events/contracts/common/ready_event.dart';
5+
export 'package:mineral/src/domains/events/contracts/common/voice_connect_event.dart';
6+
export 'package:mineral/src/domains/events/contracts/common/voice_disconnect_event.dart';
57
export 'package:mineral/src/domains/events/contracts/common/voice_join_event.dart';
68
export 'package:mineral/src/domains/events/contracts/common/voice_leave_event.dart';
79
export 'package:mineral/src/domains/events/contracts/common/voice_move_event.dart';
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import 'dart:async';
2+
3+
import 'package:mineral/api.dart';
4+
import 'package:mineral/src/domains/events/event.dart';
5+
import 'package:mineral/src/domains/events/types/listenable_event.dart';
6+
7+
typedef VoiceConnectEventHandler = FutureOr Function(VoiceState);
8+
9+
abstract class VoiceConnectEvent implements ListenableEvent {
10+
@override
11+
Event get event => Event.voiceConnect;
12+
13+
@override
14+
String? customId;
15+
16+
FutureOr<void> handle(VoiceState state);
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import 'dart:async';
2+
3+
import 'package:mineral/api.dart';
4+
import 'package:mineral/src/domains/events/event.dart';
5+
import 'package:mineral/src/domains/events/types/listenable_event.dart';
6+
7+
typedef VoiceDisconnectEventHandler = FutureOr Function(VoiceState);
8+
9+
abstract class VoiceDisconnectEvent implements ListenableEvent {
10+
@override
11+
Event get event => Event.voiceDisconnect;
12+
13+
@override
14+
String? customId;
15+
16+
FutureOr<void> handle(VoiceState state);
17+
}

lib/src/domains/events/contracts/common/voice_leave_event.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import 'package:mineral/src/api/server/voice_state.dart';
44
import 'package:mineral/src/domains/events/event.dart';
55
import 'package:mineral/src/domains/events/types/listenable_event.dart';
66

7-
typedef VoiceLeaveEventHandler = FutureOr Function(VoiceState?, VoiceState);
7+
typedef VoiceLeaveEventHandler = FutureOr Function(VoiceState);
88

99
abstract class VoiceLeaveEvent implements ListenableEvent {
1010
@override
@@ -13,5 +13,5 @@ abstract class VoiceLeaveEvent implements ListenableEvent {
1313
@override
1414
String? customId;
1515

16-
FutureOr<void> handle(VoiceState before, VoiceState after);
16+
FutureOr<void> handle(VoiceState state);
1717
}

lib/src/domains/events/event.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import 'package:mineral/src/domains/events/contracts/common/invite_create_event.
33
import 'package:mineral/src/domains/events/contracts/common/invite_delete_event.dart';
44
import 'package:mineral/src/domains/events/contracts/common/ready_event.dart';
55
import 'package:mineral/src/domains/events/contracts/common/typing_event.dart';
6+
import 'package:mineral/src/domains/events/contracts/common/voice_connect_event.dart';
7+
import 'package:mineral/src/domains/events/contracts/common/voice_disconnect_event.dart';
68
import 'package:mineral/src/domains/events/contracts/common/voice_join_event.dart';
79
import 'package:mineral/src/domains/events/contracts/common/voice_leave_event.dart';
810
import 'package:mineral/src/domains/events/contracts/common/voice_move_event.dart';
@@ -312,6 +314,12 @@ enum Event implements EnhancedEnum, EventType {
312314
['VoiceState', 'before'],
313315
['VoiceState', 'after']
314316
]),
317+
voiceConnect(VoiceConnectEvent, [
318+
['VoiceState', 'state']
319+
]),
320+
voiceDisconnect(VoiceDisconnectEvent, [
321+
['VoiceState', 'state']
322+
]),
315323
voiceJoin(VoiceJoinEvent, [
316324
['VoiceState', 'state']
317325
]),

lib/src/domains/events/event_bucket.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ final class EventBucket {
2525
void voiceStateUpdate(VoiceStateUpdateEventHandler handle) =>
2626
_registerEvent(event: Event.voiceStateUpdate, handle: handle);
2727

28+
void voiceConnect(VoiceConnectEventHandler handle) =>
29+
_registerEvent(event: Event.voiceConnect, handle: handle);
30+
31+
void voiceDisconnect(VoiceDisconnectEventHandler handle) =>
32+
_registerEvent(event: Event.voiceDisconnect, handle: handle);
33+
2834
void voiceJoin(VoiceJoinEventHandler handle) =>
2935
_registerEvent(event: Event.voiceJoin, handle: handle);
3036

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import 'package:mineral/contracts.dart';
2+
import 'package:mineral/src/domains/container/ioc_container.dart';
3+
import 'package:mineral/src/domains/events/event.dart';
4+
import 'package:mineral/src/infrastructure/internals/packets/listenable_packet.dart';
5+
import 'package:mineral/src/infrastructure/internals/packets/packet_type.dart';
6+
import 'package:mineral/src/infrastructure/internals/wss/shard_message.dart';
7+
8+
final class VoiceConnectPacket implements ListenablePacket {
9+
@override
10+
PacketType get packetType => PacketType.voiceStateUpdate;
11+
12+
MarshallerContract get _marshaller => ioc.resolve<MarshallerContract>();
13+
14+
@override
15+
Future<void> listen(ShardMessage message, DispatchEvent dispatch) async {
16+
final cacheKey = _marshaller.cacheKey.voiceState(
17+
message.payload['guild_id'],
18+
message.payload['user_id'],
19+
);
20+
final before = await _marshaller.cache?.get(cacheKey);
21+
22+
final rawVoiceState = await _marshaller.serializers.voice.normalize(
23+
message.payload,
24+
);
25+
final voiceState = await _marshaller.serializers.voice.serialize(
26+
rawVoiceState,
27+
);
28+
29+
if (before == null && message.payload['channel_id'] != null) {
30+
dispatch(event: Event.voiceConnect, params: [voiceState]);
31+
}
32+
}
33+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import 'package:mineral/contracts.dart';
2+
import 'package:mineral/src/domains/container/ioc_container.dart';
3+
import 'package:mineral/src/domains/events/event.dart';
4+
import 'package:mineral/src/infrastructure/internals/packets/listenable_packet.dart';
5+
import 'package:mineral/src/infrastructure/internals/packets/packet_type.dart';
6+
import 'package:mineral/src/infrastructure/internals/wss/shard_message.dart';
7+
8+
final class VoiceDisconnectPacket implements ListenablePacket {
9+
@override
10+
PacketType get packetType => PacketType.voiceStateUpdate;
11+
12+
MarshallerContract get _marshaller => ioc.resolve<MarshallerContract>();
13+
14+
@override
15+
Future<void> listen(ShardMessage message, DispatchEvent dispatch) async {
16+
if (message.payload['channel_id'] == null) {
17+
final cacheKey = _marshaller.cacheKey.voiceState(
18+
message.payload['guild_id'],
19+
message.payload['user_id'],
20+
);
21+
22+
final beforeRaw = await _marshaller.cache?.get(cacheKey);
23+
final before = beforeRaw != null
24+
? await _marshaller.serializers.voice.serialize(beforeRaw)
25+
: null;
26+
27+
await _marshaller.cache?.remove(cacheKey);
28+
29+
if (before != null) {
30+
dispatch(event: Event.voiceDisconnect, params: [before]);
31+
}
32+
}
33+
}
34+
}

lib/src/infrastructure/internals/packets/listeners/voice_join_packet.dart

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,23 @@ final class VoiceJoinPacket implements ListenablePacket {
1313

1414
@override
1515
Future<void> listen(ShardMessage message, DispatchEvent dispatch) async {
16-
final cacheKey = _marshaller.cacheKey
17-
.voiceState(message.payload['guild_id'], message.payload['user_id']);
16+
final cacheKey = _marshaller.cacheKey.voiceState(
17+
message.payload['guild_id'],
18+
message.payload['user_id'],
19+
);
1820
final before = await _marshaller.cache?.get(cacheKey);
1921

20-
final rawVoiceState =
21-
await _marshaller.serializers.voice.normalize(message.payload);
22-
final voiceState =
23-
await _marshaller.serializers.voice.serialize(rawVoiceState);
22+
final rawVoiceState = await _marshaller.serializers.voice.normalize(
23+
message.payload,
24+
);
25+
final voiceState = await _marshaller.serializers.voice.serialize(
26+
rawVoiceState,
27+
);
2428

25-
if (before == null && message.payload['channel_id'] != null) {
29+
// Trigger VoiceJoinEvent whenever a user joins ANY channel (including moves)
30+
if (message.payload['channel_id'] != null &&
31+
(before == null ||
32+
before['channel_id'] != message.payload['channel_id'])) {
2633
dispatch(event: Event.voiceJoin, params: [voiceState]);
2734
}
2835
}

lib/src/infrastructure/internals/packets/listeners/voice_leave_packet.dart

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,18 @@ final class VoiceLeavePacket implements ListenablePacket {
1313

1414
@override
1515
Future<void> listen(ShardMessage message, DispatchEvent dispatch) async {
16-
if (message.payload['channel_id'] == null) {
17-
final cacheKey = _marshaller.cacheKey.voiceState(message.payload['guild_id'], message.payload['user_id']);
16+
final cacheKey = _marshaller.cacheKey.voiceState(
17+
message.payload['guild_id'],
18+
message.payload['user_id'],
19+
);
20+
final beforeRaw = await _marshaller.cache?.get(cacheKey);
1821

19-
final beforeRaw = await _marshaller.cache?.get(cacheKey);
20-
final before = beforeRaw != null ? await _marshaller.serializers.voice.serialize(beforeRaw) : null;
21-
22-
final afterRaw = await _marshaller.serializers.voice.normalize(message.payload);
23-
final after = await _marshaller.serializers.voice.serialize(afterRaw);
24-
25-
await _marshaller.cache?.remove(cacheKey);
26-
27-
dispatch(event: Event.voiceLeave, params: [before, after]);
22+
// Trigger VoiceLeaveEvent whenever a user leaves ANY channel (including moves and disconnects)
23+
if (beforeRaw != null &&
24+
beforeRaw['channel_id'] != null &&
25+
beforeRaw['channel_id'] != message.payload['channel_id']) {
26+
final before = await _marshaller.serializers.voice.serialize(beforeRaw);
27+
dispatch(event: Event.voiceLeave, params: [before]);
2828
}
2929
}
3030
}

0 commit comments

Comments
 (0)