From 102dedc1cbb1c16f4d823293c255ee0738423cdd Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Mon, 5 Aug 2024 20:48:34 +0200 Subject: [PATCH 01/19] refactor(interactions): move interaction to internals --- lib/domains/commands/command_interaction_dispatcher.dart | 2 +- lib/domains/commands/command_interaction_manager.dart | 2 +- lib/domains/commands/contexts/guild_command_context.dart | 4 ++-- .../internals/datastore/parts/interaction_part.dart | 2 +- .../internals}/interactions/interaction.dart | 4 ++-- .../interactions}/types/interaction_callback_type.dart | 0 .../internals/interactions}/types/interaction_contract.dart | 0 .../interactions}/types/interaction_dispatcher_contract.dart | 0 8 files changed, 7 insertions(+), 7 deletions(-) rename lib/{domains => infrastructure/internals}/interactions/interaction.dart (96%) rename lib/{domains => infrastructure/internals/interactions}/types/interaction_callback_type.dart (100%) rename lib/{domains => infrastructure/internals/interactions}/types/interaction_contract.dart (100%) rename lib/{domains => infrastructure/internals/interactions}/types/interaction_dispatcher_contract.dart (100%) diff --git a/lib/domains/commands/command_interaction_dispatcher.dart b/lib/domains/commands/command_interaction_dispatcher.dart index a8e087846..68a8eeeea 100644 --- a/lib/domains/commands/command_interaction_dispatcher.dart +++ b/lib/domains/commands/command_interaction_dispatcher.dart @@ -6,7 +6,7 @@ import 'package:mineral/api/common/commands/command_type.dart'; import 'package:mineral/api/common/types/interaction_type.dart'; import 'package:mineral/domains/commands/command_interaction_manager.dart'; import 'package:mineral/domains/commands/contexts/guild_command_context.dart'; -import 'package:mineral/domains/types/interaction_dispatcher_contract.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_dispatcher_contract.dart'; import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; final class CommandInteractionDispatcher implements InteractionDispatcherContract { diff --git a/lib/domains/commands/command_interaction_manager.dart b/lib/domains/commands/command_interaction_manager.dart index 267e571ca..8066735c3 100644 --- a/lib/domains/commands/command_interaction_manager.dart +++ b/lib/domains/commands/command_interaction_manager.dart @@ -7,7 +7,7 @@ import 'package:mineral/api/common/commands/command_context_type.dart'; import 'package:mineral/api/server/server.dart'; import 'package:mineral/domains/commands/command_builder.dart'; import 'package:mineral/domains/commands/command_interaction_dispatcher.dart'; -import 'package:mineral/domains/types/interaction_dispatcher_contract.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_dispatcher_contract.dart'; import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; import 'package:mineral/infrastructure/io/exceptions/missing_property_exception.dart'; diff --git a/lib/domains/commands/contexts/guild_command_context.dart b/lib/domains/commands/contexts/guild_command_context.dart index fc9f4449f..a9972e3fb 100644 --- a/lib/domains/commands/contexts/guild_command_context.dart +++ b/lib/domains/commands/contexts/guild_command_context.dart @@ -3,8 +3,8 @@ import 'package:mineral/api/common/snowflake.dart'; import 'package:mineral/api/private/user.dart'; import 'package:mineral/api/server/server.dart'; import 'package:mineral/domains/commands/command_context.dart'; -import 'package:mineral/domains/interactions/interaction.dart'; -import 'package:mineral/domains/types/interaction_contract.dart'; +import 'package:mineral/infrastructure/internals/interactions/interaction.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; final class ServerCommandContext implements CommandContext { @override diff --git a/lib/infrastructure/internals/datastore/parts/interaction_part.dart b/lib/infrastructure/internals/datastore/parts/interaction_part.dart index 0638bdd61..594e08af0 100644 --- a/lib/infrastructure/internals/datastore/parts/interaction_part.dart +++ b/lib/infrastructure/internals/datastore/parts/interaction_part.dart @@ -3,8 +3,8 @@ import 'dart:async'; import 'package:mineral/api/common/components/dialogs/dialog_builder.dart'; import 'package:mineral/api/common/snowflake.dart'; import 'package:mineral/api/common/types/message_flag_type.dart'; -import 'package:mineral/domains/types/interaction_callback_type.dart'; import 'package:mineral/infrastructure/internals/datastore/data_store_part.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_callback_type.dart'; import 'package:mineral/infrastructure/kernel/kernel.dart'; import 'package:mineral/infrastructure/services/http/http_client_status.dart'; diff --git a/lib/domains/interactions/interaction.dart b/lib/infrastructure/internals/interactions/interaction.dart similarity index 96% rename from lib/domains/interactions/interaction.dart rename to lib/infrastructure/internals/interactions/interaction.dart index d549e201e..dda812c5e 100644 --- a/lib/domains/interactions/interaction.dart +++ b/lib/infrastructure/internals/interactions/interaction.dart @@ -4,11 +4,11 @@ import 'package:mineral/api/common/components/message_component.dart'; import 'package:mineral/api/common/embed/message_embed.dart'; import 'package:mineral/api/common/snowflake.dart'; import 'package:mineral/api/common/types/message_flag_type.dart'; -import 'package:mineral/domains/types/interaction_callback_type.dart'; -import 'package:mineral/domains/types/interaction_contract.dart'; import 'package:mineral/infrastructure/commons/helper.dart'; import 'package:mineral/infrastructure/internals/container/ioc_container.dart'; import 'package:mineral/infrastructure/internals/datastore/data_store.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_callback_type.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; final class Interaction implements InteractionContract { diff --git a/lib/domains/types/interaction_callback_type.dart b/lib/infrastructure/internals/interactions/types/interaction_callback_type.dart similarity index 100% rename from lib/domains/types/interaction_callback_type.dart rename to lib/infrastructure/internals/interactions/types/interaction_callback_type.dart diff --git a/lib/domains/types/interaction_contract.dart b/lib/infrastructure/internals/interactions/types/interaction_contract.dart similarity index 100% rename from lib/domains/types/interaction_contract.dart rename to lib/infrastructure/internals/interactions/types/interaction_contract.dart diff --git a/lib/domains/types/interaction_dispatcher_contract.dart b/lib/infrastructure/internals/interactions/types/interaction_dispatcher_contract.dart similarity index 100% rename from lib/domains/types/interaction_dispatcher_contract.dart rename to lib/infrastructure/internals/interactions/types/interaction_dispatcher_contract.dart From af518fca34ec100e697ee7ccc1f412646363b380 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Tue, 6 Aug 2024 02:30:12 +0200 Subject: [PATCH 02/19] feat(interaction): implement server button interaction --- .../components/buttons/button_context.dart | 10 +++ .../contexts/server_button_context.dart | 38 +++++++++++ lib/domains/events/buckets/server_bucket.dart | 4 ++ .../server/server_button_click_event.dart | 14 ++++ lib/domains/events/event.dart | 2 + .../internals/datastore/data_store.dart | 7 ++ .../datastore/parts/channel_part.dart | 21 +++--- .../datastore/parts/message_part.dart | 39 +++++++++++ .../datastore/parts/server_part.dart | 5 +- .../internals/marshaller/cache_key.dart | 19 ++---- .../internals/marshaller/marshaller.dart | 2 +- .../marshaller/serializer_bucket.dart | 67 +++---------------- .../serializers/message_serializer.dart | 42 ------------ .../private_message_serializer.dart | 43 ++++++++++++ .../server_message_serializer.dart | 59 ++++++++++++++++ .../serializers/server_serializer.dart | 20 +++--- .../listeners/channel_create_packet.dart | 4 +- .../listeners/channel_delete_packet.dart | 2 +- .../listeners/channel_pins_update_packet.dart | 4 +- .../listeners/channel_update_packet.dart | 4 +- .../listeners/interaction_create_packet.dart | 60 +++++++++++++++-- .../listeners/message_create_packet.dart | 32 +++++---- 22 files changed, 336 insertions(+), 162 deletions(-) create mode 100644 lib/domains/components/buttons/button_context.dart create mode 100644 lib/domains/components/buttons/contexts/server_button_context.dart create mode 100644 lib/domains/events/contracts/server/server_button_click_event.dart create mode 100644 lib/infrastructure/internals/datastore/parts/message_part.dart delete mode 100644 lib/infrastructure/internals/marshaller/serializers/message_serializer.dart create mode 100644 lib/infrastructure/internals/marshaller/serializers/private_message_serializer.dart create mode 100644 lib/infrastructure/internals/marshaller/serializers/server_message_serializer.dart diff --git a/lib/domains/components/buttons/button_context.dart b/lib/domains/components/buttons/button_context.dart new file mode 100644 index 000000000..1e4de6efe --- /dev/null +++ b/lib/domains/components/buttons/button_context.dart @@ -0,0 +1,10 @@ +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/server/member.dart'; + +abstract interface class ButtonContext { + Snowflake get id; + Snowflake get applicationId; + String get token; + int get version; + Member get member; +} diff --git a/lib/domains/components/buttons/contexts/server_button_context.dart b/lib/domains/components/buttons/contexts/server_button_context.dart new file mode 100644 index 000000000..8ca75c160 --- /dev/null +++ b/lib/domains/components/buttons/contexts/server_button_context.dart @@ -0,0 +1,38 @@ +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/server/member.dart'; +import 'package:mineral/api/server/server_message.dart'; +import 'package:mineral/domains/components/buttons/button_context.dart'; +import 'package:mineral/infrastructure/internals/interactions/interaction.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; + +final class ServerButtonContext implements ButtonContext { + @override + final Snowflake id; + + @override + final Snowflake applicationId; + + @override + final String token; + + @override + final int version; + + @override + final Member member; + + final ServerMessage message; + + late final InteractionContract interaction; + + ServerButtonContext({ + required this.id, + required this.applicationId, + required this.token, + required this.version, + required this.message, + required this.member, + }) { + interaction = Interaction(token, id); + } +} diff --git a/lib/domains/events/buckets/server_bucket.dart b/lib/domains/events/buckets/server_bucket.dart index d68019a0c..c7b20b581 100644 --- a/lib/domains/events/buckets/server_bucket.dart +++ b/lib/domains/events/buckets/server_bucket.dart @@ -1,3 +1,4 @@ +import 'package:mineral/domains/events/contracts/server/server_button_click_event.dart'; import 'package:mineral/domains/events/event_bucket.dart'; import 'package:mineral/domains/events/contracts/server/server_ban_add_event.dart'; import 'package:mineral/domains/events/contracts/server/server_ban_remove_event.dart'; @@ -81,4 +82,7 @@ final class ServerBucket { void stickersUpdate(ServerStickersUpdateEventHandler handle) => _events.make(Event.serverStickersUpdate, handle); + + void serverButtonClick(ServerButtonClickEventHandler handle) => + _events.make(Event.serverButtonClick, handle); } diff --git a/lib/domains/events/contracts/server/server_button_click_event.dart b/lib/domains/events/contracts/server/server_button_click_event.dart new file mode 100644 index 000000000..d68551f68 --- /dev/null +++ b/lib/domains/events/contracts/server/server_button_click_event.dart @@ -0,0 +1,14 @@ +import 'dart:async'; + +import 'package:mineral/domains/components/buttons/contexts/server_button_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/domains/events/types/listenable_event.dart'; + +typedef ServerButtonClickEventHandler = FutureOr Function(ServerButtonContext); + +abstract class ServerButtonClickEvent implements ListenableEvent { + @override + Event get event => Event.serverButtonClick; + + FutureOr handle(ServerButtonContext ctx); +} diff --git a/lib/domains/events/event.dart b/lib/domains/events/event.dart index e67b2d62b..54c0e3b8d 100644 --- a/lib/domains/events/event.dart +++ b/lib/domains/events/event.dart @@ -7,6 +7,7 @@ import 'package:mineral/domains/events/contracts/private/private_channel_update_ import 'package:mineral/domains/events/contracts/private/private_message_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_ban_add_event.dart'; import 'package:mineral/domains/events/contracts/server/server_ban_remove_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_button_click_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_delete_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_pins_update_event.dart'; @@ -49,6 +50,7 @@ enum Event implements EnhancedEnum, EventType { serverRoleCreate(ServerRolesCreateEvent), serverRoleUpdate(ServerRolesUpdateEvent), serverRoleDelete(ServerRolesDeleteEvent), + serverButtonClick(ServerButtonClickEvent), // private privateMessageCreate(PrivateMessageCreateEvent), diff --git a/lib/infrastructure/internals/datastore/data_store.dart b/lib/infrastructure/internals/datastore/data_store.dart index 5c78083eb..113a5562b 100644 --- a/lib/infrastructure/internals/datastore/data_store.dart +++ b/lib/infrastructure/internals/datastore/data_store.dart @@ -1,6 +1,7 @@ import 'package:mineral/infrastructure/internals/datastore/parts/channel_part.dart'; import 'package:mineral/infrastructure/internals/datastore/parts/interaction_part.dart'; import 'package:mineral/infrastructure/internals/datastore/parts/member_part.dart'; +import 'package:mineral/infrastructure/internals/datastore/parts/message_part.dart'; import 'package:mineral/infrastructure/internals/datastore/parts/role_part.dart'; import 'package:mineral/infrastructure/internals/datastore/parts/server_part.dart'; import 'package:mineral/infrastructure/kernel/kernel.dart'; @@ -17,6 +18,8 @@ abstract class DataStoreContract { RolePart get role; + MessagePart get message; + InteractionPart get interaction; } @@ -38,6 +41,9 @@ final class DataStore implements DataStoreContract { @override late final RolePart role; + @override + late final MessagePart message; + @override late final InteractionPart interaction; @@ -48,6 +54,7 @@ final class DataStore implements DataStoreContract { server = ServerPart(kernel); member = MemberPart(kernel); role = RolePart(kernel); + message = MessagePart(kernel); interaction = InteractionPart(kernel); } } diff --git a/lib/infrastructure/internals/datastore/parts/channel_part.dart b/lib/infrastructure/internals/datastore/parts/channel_part.dart index 0ea079de6..788af69c9 100644 --- a/lib/infrastructure/internals/datastore/parts/channel_part.dart +++ b/lib/infrastructure/internals/datastore/parts/channel_part.dart @@ -12,6 +12,7 @@ import 'package:mineral/api/server/channels/server_channel.dart'; import 'package:mineral/infrastructure/commons/helper.dart'; import 'package:mineral/infrastructure/internals/datastore/data_store_part.dart'; import 'package:mineral/infrastructure/internals/http/discord_header.dart'; +import 'package:mineral/infrastructure/internals/marshaller/types/serializer.dart'; import 'package:mineral/infrastructure/kernel/kernel.dart'; import 'package:mineral/infrastructure/services/http/http_client_status.dart'; import 'package:mineral/infrastructure/services/http/http_request_option.dart'; @@ -24,10 +25,8 @@ final class ChannelPart implements DataStorePart { ChannelPart(this._kernel); - Future getChannel(Snowflake id, {Snowflake? serverId}) async { - final String key = serverId != null - ? _kernel.marshaller.cacheKey.serverChannel(serverId: serverId, channelId: id) - : _kernel.marshaller.cacheKey.privateChannel(id); + Future getChannel(Snowflake id) async { + final String key = _kernel.marshaller.cacheKey.channel(id); final cachedChannel = await _kernel.marshaller.cache.get(key); if (cachedChannel != null) { @@ -109,12 +108,14 @@ final class ChannelPart implements DataStorePart { 'components': components?.map((e) => e.toJson()).toList(), }); - final message = await switch (response.statusCode) { - int() when status.isSuccess(response.statusCode) => _kernel.marshaller.serializers.message - .serializeRemote({...response.body, 'guild_id': guildId}), - int() when status.isError(response.statusCode) => throw HttpException(response.bodyString), - _ => throw Exception('Unknown status code: ${response.statusCode}'), - }; + final channel = await _kernel.dataStore.channel.getChannel(channelId); + final serializer = switch (channel) { + ServerChannel() => _kernel.marshaller.serializers.serverMessage, + PrivateChannel() => _kernel.marshaller.serializers.privateMessage, + _ => throw Exception('Unknown channel type: $channel'), + } as SerializerContract; + + final Message message = await serializer.serializeRemote(response.body); await _kernel.marshaller.cache.put(message.id.value, {...response.body, 'guild_id': guildId}); diff --git a/lib/infrastructure/internals/datastore/parts/message_part.dart b/lib/infrastructure/internals/datastore/parts/message_part.dart new file mode 100644 index 000000000..f590fc0d5 --- /dev/null +++ b/lib/infrastructure/internals/datastore/parts/message_part.dart @@ -0,0 +1,39 @@ +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/server/channels/server_channel.dart'; +import 'package:mineral/api/server/server_message.dart'; +import 'package:mineral/infrastructure/internals/datastore/data_store_part.dart'; +import 'package:mineral/infrastructure/kernel/kernel.dart'; + +final class MessagePart implements DataStorePart { + final KernelContract _kernel; + + MessagePart(this._kernel); + + Future getServerMessage( + {required Snowflake messageId, required Snowflake channelId}) async { + final messageCacheKey = + _kernel.marshaller.cacheKey.serverMessage(messageId: messageId, channelId: channelId); + + final message = await _kernel.marshaller.cache.get(messageCacheKey); + if (message != null) { + return _kernel.marshaller.serializers.serverMessage.serializeCache(message); + } + + final response = await _kernel.dataStore.client.get('/channels/$channelId/messages/$messageId'); + + final serverMessage = + await _kernel.marshaller.serializers.serverMessage.serializeRemote(response.body); + + final channelCacheKey = _kernel.marshaller.cacheKey.channel(channelId); + final rawChannel = await _kernel.marshaller.cache.getOrFail(channelCacheKey); + serverMessage.channel = + await _kernel.marshaller.serializers.channels.serializeCache(rawChannel) as ServerChannel; + + final rawMessage = + await _kernel.marshaller.serializers.serverMessage.deserialize(serverMessage); + + await _kernel.marshaller.cache.put(messageCacheKey, rawMessage); + + return serverMessage; + } +} diff --git a/lib/infrastructure/internals/datastore/parts/server_part.dart b/lib/infrastructure/internals/datastore/parts/server_part.dart index b9b591f51..34a20a74a 100644 --- a/lib/infrastructure/internals/datastore/parts/server_part.dart +++ b/lib/infrastructure/internals/datastore/parts/server_part.dart @@ -41,7 +41,7 @@ final class ServerPart implements DataStorePart { await Future.wait([ _kernel.marshaller.cache.put(serverCacheKey, rawServer), ...server.channels.list.values.map((channel) { - final channelCacheKey = cacheKey.serverChannel(serverId: id, channelId: channel.id); + final channelCacheKey = cacheKey.channel(channel.id); final rawChannel = _kernel.marshaller.serializers.channels.deserialize(channel); return _kernel.marshaller.cache.put(channelCacheKey, rawChannel); @@ -80,7 +80,8 @@ final class ServerPart implements DataStorePart { final roles = await _serializeRolesResponse(response); for (final role in roles) { - final roleCacheKey = _kernel.marshaller.cacheKey.serverRole(serverId: guildId, roleId: role.id); + final roleCacheKey = + _kernel.marshaller.cacheKey.serverRole(serverId: guildId, roleId: role.id); final rawRole = await _kernel.marshaller.serializers.role.deserialize(role); await _kernel.marshaller.cache.put(roleCacheKey, rawRole); diff --git a/lib/infrastructure/internals/marshaller/cache_key.dart b/lib/infrastructure/internals/marshaller/cache_key.dart index 3df7831d0..4983e71ea 100644 --- a/lib/infrastructure/internals/marshaller/cache_key.dart +++ b/lib/infrastructure/internals/marshaller/cache_key.dart @@ -3,9 +3,7 @@ import 'package:mineral/api/common/snowflake.dart'; abstract interface class CacheKeyContract { String server(Snowflake id); - String privateChannel(Snowflake id); - - String serverChannel({required Snowflake serverId, required Snowflake channelId}); + String channel(Snowflake id); String serverRole({required Snowflake serverId, required Snowflake roleId}); @@ -13,7 +11,7 @@ abstract interface class CacheKeyContract { String serverEmoji({required Snowflake serverId, required Snowflake emojiId}); - String serverMessage({required Snowflake serverId, required Snowflake messageId}); + String serverMessage({required Snowflake channelId, required Snowflake messageId}); String privateMessage({required Snowflake channelId, required Snowflake messageId}); } @@ -23,11 +21,8 @@ final class CacheKey implements CacheKeyContract { String server(Snowflake id) => 'server-$id'; @override - String privateChannel(Snowflake id) => 'channel-$id'; - - @override - String serverChannel({required Snowflake serverId, required Snowflake channelId}) => - '${server(serverId)}/channel-$channelId'; + String channel(Snowflake channelId) => + 'channel-$channelId'; @override String serverRole({required Snowflake serverId, required Snowflake roleId}) => @@ -42,10 +37,10 @@ final class CacheKey implements CacheKeyContract { '${server(serverId)}/emoji-$emojiId'; @override - String serverMessage({required Snowflake serverId, required Snowflake messageId}) => - '${server(serverId)}/message-$messageId'; + String serverMessage({required Snowflake channelId, required Snowflake messageId}) => + '${channel(channelId)}/message-$messageId'; @override String privateMessage({required Snowflake channelId, required Snowflake messageId}) => - '${privateChannel(channelId)}/message-$messageId'; + '${channel(channelId)}/message-$messageId'; } diff --git a/lib/infrastructure/internals/marshaller/marshaller.dart b/lib/infrastructure/internals/marshaller/marshaller.dart index daa68e101..ceb335b4f 100644 --- a/lib/infrastructure/internals/marshaller/marshaller.dart +++ b/lib/infrastructure/internals/marshaller/marshaller.dart @@ -34,6 +34,6 @@ final class Marshaller implements MarshallerContract { final CacheKeyContract cacheKey = CacheKey(); Marshaller(this.logger, this.cache) { - serializers = SerializerBucketImpl(this); + serializers = SerializerBucket(this); } } diff --git a/lib/infrastructure/internals/marshaller/serializer_bucket.dart b/lib/infrastructure/internals/marshaller/serializer_bucket.dart index c940c0d55..b501a4988 100644 --- a/lib/infrastructure/internals/marshaller/serializer_bucket.dart +++ b/lib/infrastructure/internals/marshaller/serializer_bucket.dart @@ -2,14 +2,15 @@ import 'package:mineral/api/common/channel.dart'; import 'package:mineral/api/common/channel_permission_overwrite.dart'; import 'package:mineral/api/common/embed/message_embed.dart'; import 'package:mineral/api/common/emoji.dart'; -import 'package:mineral/api/common/message.dart'; import 'package:mineral/api/common/polls/poll.dart'; import 'package:mineral/api/common/sticker.dart'; +import 'package:mineral/api/private/private_message.dart'; import 'package:mineral/api/private/user.dart'; import 'package:mineral/api/server/member.dart'; import 'package:mineral/api/server/role.dart'; import 'package:mineral/api/server/server.dart'; import 'package:mineral/api/server/server_assets.dart'; +import 'package:mineral/api/server/server_message.dart'; import 'package:mineral/api/server/server_settings.dart'; import 'package:mineral/api/server/server_subscription.dart'; import 'package:mineral/domains/commands/contexts/global_command_context.dart'; @@ -21,11 +22,12 @@ import 'package:mineral/infrastructure/internals/marshaller/serializers/embed_se import 'package:mineral/infrastructure/internals/marshaller/serializers/emoji_serializer.dart'; import 'package:mineral/infrastructure/internals/marshaller/serializers/global_command_context_serializer.dart'; import 'package:mineral/infrastructure/internals/marshaller/serializers/member_serializer.dart'; -import 'package:mineral/infrastructure/internals/marshaller/serializers/message_serializer.dart'; import 'package:mineral/infrastructure/internals/marshaller/serializers/poll_serializer.dart'; +import 'package:mineral/infrastructure/internals/marshaller/serializers/private_message_serializer.dart'; import 'package:mineral/infrastructure/internals/marshaller/serializers/role_serializer.dart'; import 'package:mineral/infrastructure/internals/marshaller/serializers/server_assets_serializer.dart'; import 'package:mineral/infrastructure/internals/marshaller/serializers/server_command_context_serializer.dart'; +import 'package:mineral/infrastructure/internals/marshaller/serializers/server_message_serializer.dart'; import 'package:mineral/infrastructure/internals/marshaller/serializers/server_serializer.dart'; import 'package:mineral/infrastructure/internals/marshaller/serializers/server_settings_serializer.dart'; import 'package:mineral/infrastructure/internals/marshaller/serializers/server_subscription_serializer.dart'; @@ -33,90 +35,42 @@ import 'package:mineral/infrastructure/internals/marshaller/serializers/sticker_ import 'package:mineral/infrastructure/internals/marshaller/serializers/user_serializer.dart'; import 'package:mineral/infrastructure/internals/marshaller/types/serializer.dart'; -abstract interface class SerializerBucket { - SerializerContract get channels; - - SerializerContract get server; - - SerializerContract get member; - - SerializerContract get user; - - SerializerContract get role; - - SerializerContract get serverSubscription; - - SerializerContract get serverSettings; - - SerializerContract get serversAsset; - - SerializerContract get emojis; - - SerializerContract get sticker; - - SerializerContract get channelPermissionOverwrite; - - SerializerContract get message; - - SerializerContract get embed; - - SerializerContract get poll; - - SerializerContract get globalCommandContext; - - SerializerContract get guildCommandContext; -} - -final class SerializerBucketImpl implements SerializerBucket { - @override +final class SerializerBucket { final SerializerContract channels; - @override final SerializerContract server; - @override final SerializerContract member; - @override final SerializerContract user; - @override final SerializerContract role; - @override final SerializerContract serverSubscription; - @override final SerializerContract serverSettings; - @override final SerializerContract serversAsset; - @override final SerializerContract emojis; - @override final SerializerContract sticker; - @override final SerializerContract channelPermissionOverwrite; - @override - final SerializerContract message; + final SerializerContract serverMessage; + + final SerializerContract privateMessage; - @override final SerializerContract embed; - @override final SerializerContract poll; - @override final SerializerContract globalCommandContext; - @override final SerializerContract guildCommandContext; - SerializerBucketImpl(MarshallerContract marshaller) + SerializerBucket(MarshallerContract marshaller) : channels = ChannelSerializer(marshaller), server = ServerSerializer(marshaller), member = MemberSerializer(marshaller), @@ -128,7 +82,8 @@ final class SerializerBucketImpl implements SerializerBucket { emojis = EmojiSerializer(marshaller), sticker = StickerSerializer(marshaller), channelPermissionOverwrite = ChannelPermissionOverwriteSerializer(marshaller), - message = MessageSerializer(marshaller), + serverMessage = ServerMessageSerializer(marshaller), + privateMessage = PrivateMessageSerializer(marshaller), embed = EmbedSerializer(marshaller), globalCommandContext = GlobalCommandContextSerializer(marshaller), guildCommandContext = ServerCommandContextSerializer(marshaller), diff --git a/lib/infrastructure/internals/marshaller/serializers/message_serializer.dart b/lib/infrastructure/internals/marshaller/serializers/message_serializer.dart deleted file mode 100644 index 3ad032862..000000000 --- a/lib/infrastructure/internals/marshaller/serializers/message_serializer.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:mineral/api/common/message.dart'; -import 'package:mineral/api/common/snowflake.dart'; -import 'package:mineral/api/private/channels/private_channel.dart'; -import 'package:mineral/api/server/channels/server_channel.dart'; -import 'package:mineral/infrastructure/internals/marshaller/factories/messages/private_message_factory.dart'; -import 'package:mineral/infrastructure/internals/marshaller/factories/messages/server_message_factory.dart'; -import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; -import 'package:mineral/infrastructure/internals/marshaller/types/message_factory.dart'; -import 'package:mineral/infrastructure/internals/marshaller/types/serializer.dart'; - -final class MessageSerializer implements SerializerContract { - final MarshallerContract marshaller; - - final _serverMessageFactory = ServerMessageFactory(); - final _privateMessageFactory = PrivateMessageFactory(); - - MessageSerializer(this.marshaller); - - @override - Future serializeRemote(Map json) async { - final channel = await marshaller.dataStore.channel.getChannel(Snowflake(json['channel_id']), serverId: Snowflake(json['guild_id'])); - final factory = switch(channel) { - ServerChannel() => _serverMessageFactory, - PrivateChannel() => _privateMessageFactory, - _ => throw Exception('Channel type not found ${channel.runtimeType}'), - } as MessageFactory; - - return factory.serialize(marshaller, json); - } - - @override - Future serializeCache(Map json) { - throw UnimplementedError(); - } - - @override - Map deserialize(Message object) { - return { - 'id': object.id, - }; - } -} diff --git a/lib/infrastructure/internals/marshaller/serializers/private_message_serializer.dart b/lib/infrastructure/internals/marshaller/serializers/private_message_serializer.dart new file mode 100644 index 000000000..d59008799 --- /dev/null +++ b/lib/infrastructure/internals/marshaller/serializers/private_message_serializer.dart @@ -0,0 +1,43 @@ +import 'package:mineral/api/common/message_properties.dart'; +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/private/channels/private_channel.dart'; +import 'package:mineral/api/private/private_message.dart'; +import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; +import 'package:mineral/infrastructure/internals/marshaller/types/serializer.dart'; + +final class PrivateMessageSerializer implements SerializerContract { + final MarshallerContract marshaller; + + PrivateMessageSerializer(this.marshaller); + + @override + Future serializeRemote(Map json) async { + final channel = await marshaller.dataStore.channel.getChannel(Snowflake(json['channel_id'])); + + final messageProperties = MessageProperties.fromJson(channel as PrivateChannel, json); + final user = await marshaller.serializers.user.serializeRemote(json['author']); + + return PrivateMessage(messageProperties, userId: json['author']['id'], user: user); + } + + @override + Future serializeCache(Map json) { + throw UnimplementedError(); + } + + @override + Future> deserialize(PrivateMessage object) async { + final embeds = await Future.wait(object.embeds.map((message) async { + return marshaller.serializers.embed.deserialize(message); + })); + + return { + 'id': object.id, + 'content': object.content, + 'embeds': embeds, + 'channel_id': object.channel.id, + 'created_at': object.createdAt.toIso8601String(), + 'updated_at': object.updatedAt?.toIso8601String(), + }; + } +} diff --git a/lib/infrastructure/internals/marshaller/serializers/server_message_serializer.dart b/lib/infrastructure/internals/marshaller/serializers/server_message_serializer.dart new file mode 100644 index 000000000..bdb5b114f --- /dev/null +++ b/lib/infrastructure/internals/marshaller/serializers/server_message_serializer.dart @@ -0,0 +1,59 @@ +import 'package:mineral/api/common/message_properties.dart'; +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/server/channels/server_channel.dart'; +import 'package:mineral/api/server/server_message.dart'; +import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; +import 'package:mineral/infrastructure/internals/marshaller/types/serializer.dart'; + +final class ServerMessageSerializer implements SerializerContract { + final MarshallerContract marshaller; + + ServerMessageSerializer(this.marshaller); + + @override + Future serializeRemote(Map json) async { + final channel = await marshaller.dataStore.channel.getChannel(Snowflake(json['channel_id'])); + + if (channel == null) { + throw Exception('Channel not found'); + } + + final server = await marshaller.dataStore.server.getServer(channel.guildId); + final member = server.members.list[json['author']['id']]; + + final messageProperties = MessageProperties.fromJson(channel, json); + + return ServerMessage(messageProperties, author: member!); + } + + @override + Future serializeCache(Map json) async { + final channel = await marshaller.dataStore.channel + .getChannel(Snowflake(json['channel_id'])); + + final server = await marshaller.dataStore.server.getServer(json['guild_id']); + final member = server.members.list[json['author_id']]; + + final messageProperties = MessageProperties.fromJson(channel as ServerChannel, json); + + return ServerMessage(messageProperties, author: member!); + } + + @override + Future> deserialize(ServerMessage object) async { + final embeds = await Future.wait(object.embeds.map((message) async { + return marshaller.serializers.embed.deserialize(message); + })); + + return { + 'id': object.id, + 'content': object.content, + 'embeds': embeds, + 'author_id': object.author.id.value, + 'channel_id': object.channel.id.value, + 'guild_id': object.channel.guildId.value, + 'timestamp': object.createdAt.toIso8601String(), + 'edited_timestamp': object.updatedAt?.toIso8601String(), + }; + } +} diff --git a/lib/infrastructure/internals/marshaller/serializers/server_serializer.dart b/lib/infrastructure/internals/marshaller/serializers/server_serializer.dart index 045f9ac57..e0c577803 100644 --- a/lib/infrastructure/internals/marshaller/serializers/server_serializer.dart +++ b/lib/infrastructure/internals/marshaller/serializers/server_serializer.dart @@ -66,8 +66,7 @@ final class ServerSerializer implements SerializerContract { if (channel is ServerCategoryChannel) { channelManager.list.putIfAbsent(channel.id, () => channel); - final cacheKey = - _marshaller.cacheKey.serverChannel(serverId: json['id'], channelId: channel.id); + final cacheKey = _marshaller.cacheKey.channel(channel.id); final rawChannel = await _marshaller.serializers.channels.deserialize(channel); await _marshaller.cache.put(cacheKey, rawChannel); @@ -81,8 +80,7 @@ final class ServerSerializer implements SerializerContract { if (channel is ServerChannel) { channelManager.list.putIfAbsent(channel.id, () => channel); - final cacheKey = - _marshaller.cacheKey.serverChannel(serverId: json['id'], channelId: channel.id); + final cacheKey = _marshaller.cacheKey.channel(channel.id); final rawChannel = await _marshaller.serializers.channels.deserialize(channel); await _marshaller.cache.put(cacheKey, rawChannel); @@ -143,10 +141,11 @@ final class ServerSerializer implements SerializerContract { }); }).wait; - final channels = await _marshaller.cache.whereKeyStartsWithOrFail('$serverKey/channel-'); - await channels.entries.map((element) async { - final channel = await _marshaller.serializers.channels.serializeCache(element.value); - if (channel case final ServerChannel channel) { + await List.from(rawServer['channels']).map((key) async { + final rawChannel = await _marshaller.cache.getOrFail(key); + + final channel = await _marshaller.serializers.channels.serializeCache(rawChannel); + if (channel is ServerChannel) { channelManager.list.putIfAbsent(channel.id, () => channel); return channel; } @@ -193,11 +192,16 @@ final class ServerSerializer implements SerializerContract { return _marshaller.cacheKey.serverMember(serverId: server.id, memberId: member.id); }).toList(); + final channels = server.channels.list.values.map((channel) { + return _marshaller.cacheKey.channel(channel.id); + }).toList(); + return { 'id': server.id, 'owner_id': server.owner.id, 'name': server.name, 'members': members, + 'channels': channels, 'description': server.description, 'applicationId': server.applicationId, 'assets': await _marshaller.serializers.serversAsset.deserialize(server.assets), diff --git a/lib/infrastructure/internals/packets/listeners/channel_create_packet.dart b/lib/infrastructure/internals/packets/listeners/channel_create_packet.dart index 616553865..4e2e74e03 100644 --- a/lib/infrastructure/internals/packets/listeners/channel_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/channel_create_packet.dart @@ -30,7 +30,7 @@ final class ChannelCreatePacket implements ListenablePacket { Future registerServerChannel(ServerChannel channel, DispatchEvent dispatch) async { final server = await marshaller.dataStore.server.getServer(channel.guildId); final serverCacheKey = marshaller.cacheKey.server(server.id); - final channelCacheKey = marshaller.cacheKey.serverChannel(serverId: server.id, channelId: channel.id); + final channelCacheKey = marshaller.cacheKey.channel(channel.id); channel.server = server; server.channels.list.putIfAbsent(channel.id, () => channel); @@ -45,7 +45,7 @@ final class ChannelCreatePacket implements ListenablePacket { } Future registerPrivateChannel(PrivateChannel channel, DispatchEvent dispatch) async { - final cacheKey = marshaller.cacheKey.privateChannel(channel.id); + final cacheKey = marshaller.cacheKey.channel(channel.id); final rawChannel = await marshaller.serializers.channels.deserialize(channel); await marshaller.cache.put(cacheKey, rawChannel); diff --git a/lib/infrastructure/internals/packets/listeners/channel_delete_packet.dart b/lib/infrastructure/internals/packets/listeners/channel_delete_packet.dart index 26674f6e4..939ed54bc 100644 --- a/lib/infrastructure/internals/packets/listeners/channel_delete_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/channel_delete_packet.dart @@ -29,7 +29,7 @@ final class ChannelDeletePacket implements ListenablePacket { Future registerServerChannel(Snowflake guildId, ServerChannel channel, DispatchEvent dispatch) async { final server = await marshaller.dataStore.server.getServer(guildId); final serverCacheKey = marshaller.cacheKey.server(server.id); - final channelCacheKey = marshaller.cacheKey.serverChannel(serverId: server.id, channelId: channel.id); + final channelCacheKey = marshaller.cacheKey.channel(channel.id); channel.server = server; server.channels.list.remove(channel.id); diff --git a/lib/infrastructure/internals/packets/listeners/channel_pins_update_packet.dart b/lib/infrastructure/internals/packets/listeners/channel_pins_update_packet.dart index de642294c..64b3b1c2a 100644 --- a/lib/infrastructure/internals/packets/listeners/channel_pins_update_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/channel_pins_update_packet.dart @@ -30,7 +30,7 @@ final class ChannelPinsUpdatePacket implements ListenablePacket { Future registerServerChannelPins(ServerChannel channel, DispatchEvent dispatch) async { final server = await marshaller.dataStore.server.getServer(channel.guildId); final serverCacheKey = marshaller.cacheKey.server(server.id); - final channelCacheKey = marshaller.cacheKey.serverChannel(serverId: server.id, channelId: channel.id); + final channelCacheKey = marshaller.cacheKey.channel(channel.id); server.channels.list.update(channel.id, (_) => channel); @@ -46,7 +46,7 @@ final class ChannelPinsUpdatePacket implements ListenablePacket { } Future registerPrivateChannelPins(PrivateChannel channel, DispatchEvent dispatch) async { - final channelCacheKey = marshaller.cacheKey.privateChannel(channel.id); + final channelCacheKey = marshaller.cacheKey.channel(channel.id); final rawChannel = await marshaller.serializers.channels.deserialize(channel); await marshaller.cache.put(channelCacheKey, rawChannel); diff --git a/lib/infrastructure/internals/packets/listeners/channel_update_packet.dart b/lib/infrastructure/internals/packets/listeners/channel_update_packet.dart index 44553034b..8725386bc 100644 --- a/lib/infrastructure/internals/packets/listeners/channel_update_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/channel_update_packet.dart @@ -31,7 +31,7 @@ final class ChannelUpdatePacket implements ListenablePacket { final server = await marshaller.dataStore.server.getServer(channel.guildId); final serverCacheKey = marshaller.cacheKey.server(server.id); final channelCacheKey = - marshaller.cacheKey.serverChannel(serverId: server.id, channelId: channel.id); + marshaller.cacheKey.channel(channel.id); final before = server.channels.list[channel.id]; @@ -49,7 +49,7 @@ final class ChannelUpdatePacket implements ListenablePacket { } Future registerPrivateChannel(PrivateChannel channel, DispatchEvent dispatch) async { - final cacheKey = marshaller.cacheKey.privateChannel(channel.id); + final cacheKey = marshaller.cacheKey.channel(channel.id); final before = marshaller.dataStore.channel.getChannel(channel.id); final rawChannel = await marshaller.serializers.channels.deserialize(channel); diff --git a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart index 83b0d9462..bca1ca708 100644 --- a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart @@ -1,5 +1,11 @@ import 'package:collection/collection.dart'; +import 'package:mineral/api/common/components/buttons/button_type.dart'; +import 'package:mineral/api/common/components/component_type.dart'; +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/common/types/interaction_type.dart'; import 'package:mineral/domains/commands/command_interaction_manager.dart'; +import 'package:mineral/domains/components/buttons/contexts/server_button_context.dart'; +import 'package:mineral/domains/events/event.dart'; import 'package:mineral/infrastructure/internals/container/ioc_container.dart'; import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; import 'package:mineral/infrastructure/internals/packets/listenable_packet.dart'; @@ -18,16 +24,56 @@ final class InteractionCreatePacket implements ListenablePacket { @override Future listen(ShardMessage message, DispatchEvent dispatch) async { - final interactions = [ - ioc.resolve(), - ]; + final interactionManager = ioc.resolve(); + final type = InteractionType.values.firstWhereOrNull((e) => e.value == message.payload['type']); - final interaction = interactions.firstWhereOrNull((interaction) => interaction.dispatcher.type.value == message.payload['type']); - if (interaction == null) { - ioc.resolve().warn('Interaction type ${message.payload['type']} not found'); + return switch (type) { + InteractionType.applicationCommand => interactionManager.dispatcher.dispatch(message.payload), + InteractionType.messageComponent => dispatchMessageComponent(message.payload, dispatch), + _ => logger.warn('Interaction type ${message.payload['type']} not found') + }; + } + + Future dispatchMessageComponent(Map payload, DispatchEvent dispatch) async { + final String? serverId = payload['guild']?['id']; + + final metadata = payload['message']['interaction_metadata']; + final type = ComponentType.values.firstWhereOrNull((e) => e.value == metadata['type']); + + if (type == null) { + logger.warn('Component type ${metadata['type']} not found'); return; } - await interaction.dispatcher.dispatch(message.payload); + return switch (serverId) { + String() => _handleServerButton(payload, dispatch), + _ => _handlePrivateButton(payload), + }; } + + Future _handleServerButton(Map payload, DispatchEvent dispatch) async { + final message = await marshaller.dataStore.message.getServerMessage( + messageId: Snowflake(payload['message']['id']), channelId: Snowflake(payload['channel_id'])); + + final metadata = payload['message']['interaction_metadata']; + final type = ButtonType.values.firstWhereOrNull((e) => e.value == metadata['type']); + + if (type == null) { + logger.warn('Button type ${metadata['type']} not found'); + return; + } + + final ctx = ServerButtonContext( + id: Snowflake(payload['id']), + applicationId: Snowflake(payload['application_id']), + version: payload['version'], + token: payload['token'], + message: message, + member: message.author, + ); + + dispatch(event: Event.serverButtonClick, params: [ctx]); + } + + Future _handlePrivateButton(Map data) async {} } diff --git a/lib/infrastructure/internals/packets/listeners/message_create_packet.dart b/lib/infrastructure/internals/packets/listeners/message_create_packet.dart index 42ac9c6bf..25ac91183 100644 --- a/lib/infrastructure/internals/packets/listeners/message_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/message_create_packet.dart @@ -1,17 +1,15 @@ import 'package:mineral/api/common/message_type.dart'; import 'package:mineral/api/private/channels/private_channel.dart'; -import 'package:mineral/api/private/private_message.dart'; import 'package:mineral/api/server/channels/server_announcement_channel.dart'; import 'package:mineral/api/server/channels/server_channel.dart'; import 'package:mineral/api/server/channels/server_text_channel.dart'; import 'package:mineral/api/server/channels/server_voice_channel.dart'; -import 'package:mineral/api/server/server_message.dart'; -import 'package:mineral/infrastructure/services/logger/logger.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; import 'package:mineral/infrastructure/internals/packets/listenable_packet.dart'; import 'package:mineral/infrastructure/internals/packets/packet_type.dart'; -import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; -import 'package:mineral/domains/events/event.dart'; import 'package:mineral/infrastructure/internals/wss/shard_message.dart'; +import 'package:mineral/infrastructure/services/logger/logger.dart'; final class MessageCreatePacket implements ListenablePacket { @override @@ -28,33 +26,33 @@ final class MessageCreatePacket implements ListenablePacket { return; } - return switch (message.payload['guild_id']) { + return switch (message.payload['message_reference']?['guild_id']) { String() => sendServerMessage(dispatch, message.payload), _ => sendPrivateMessage(dispatch, message.payload), }; } Future sendServerMessage(DispatchEvent dispatch, Map json) async { - final server = await marshaller.dataStore.server.getServer(json['guild_id']); + final server = await marshaller.dataStore.server.getServer(json['message_reference']['guild_id']); final channel = server.channels.list[json['channel_id']]; - final message = await marshaller.serializers.message.serializeRemote(json); + final message = await marshaller.serializers.serverMessage.serializeRemote(json); if (channel is ServerChannel) { message.channel = channel; } switch (channel) { - case ServerTextChannel(): channel.messages.list.putIfAbsent(message.id, () => message as ServerMessage); - case ServerVoiceChannel(): channel.messages.list.putIfAbsent(message.id, () => message as ServerMessage); - case ServerAnnouncementChannel(): channel.messages.list.putIfAbsent(message.id, () => message as ServerMessage); + case ServerTextChannel(): channel.messages.list.putIfAbsent(message.id, () => message); + case ServerVoiceChannel(): channel.messages.list.putIfAbsent(message.id, () => message); + case ServerAnnouncementChannel(): channel.messages.list.putIfAbsent(message.id, () => message); } final serverCacheKey = marshaller.cacheKey.server(server.id); - final messageCacheKey = marshaller.cacheKey.serverMessage(serverId: server.id, messageId: message.id); + final messageCacheKey = marshaller.cacheKey.serverMessage(channelId: channel!.id, messageId: message.id); final rawServer = await marshaller.serializers.server.deserialize(server); - final rawMessage = await marshaller.serializers.message.deserialize(message); + final rawMessage = await marshaller.serializers.serverMessage.deserialize(message); await Future.wait([ marshaller.cache.put(serverCacheKey, rawServer), @@ -66,17 +64,17 @@ final class MessageCreatePacket implements ListenablePacket { Future sendPrivateMessage(DispatchEvent dispatch, Map json) async { final channel = await marshaller.dataStore.channel.getChannel(json['channel_id']); - final message = await marshaller.serializers.message.serializeRemote(json); + final message = await marshaller.serializers.privateMessage.serializeRemote(json); if (channel is PrivateChannel) { message.channel = channel; - channel.messages.list.putIfAbsent(message.id, () => message as PrivateMessage); + channel.messages.list.putIfAbsent(message.id, () => message); - final channelCacheKey = marshaller.cacheKey.privateChannel(channel.id); + final channelCacheKey = marshaller.cacheKey.channel(channel.id); final messageCacheKey = marshaller.cacheKey.privateMessage(channelId: channel.id, messageId: message.id); final rawChannel = await marshaller.serializers.channels.deserialize(channel); - final rawMessage = await marshaller.serializers.message.deserialize(message); + final rawMessage = await marshaller.serializers.privateMessage.deserialize(message); await Future.wait([ marshaller.cache.put(channelCacheKey, rawChannel), From bdb9551d8b70cdccc5d7f5d1689d070f84dff4a7 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Tue, 6 Aug 2024 02:53:21 +0200 Subject: [PATCH 03/19] feat(interaction): implement private button interaction --- lib/api/private/private_message.dart | 4 +- .../components/buttons/button_context.dart | 2 - .../contexts/private_button_context.dart | 39 +++++++++++++++++++ .../contexts/server_button_context.dart | 1 - .../events/buckets/private_bucket.dart | 4 ++ lib/domains/events/buckets/server_bucket.dart | 2 +- .../private/private_button_click_event.dart | 14 +++++++ lib/domains/events/event.dart | 4 +- .../datastore/parts/message_part.dart | 30 ++++++++++++++ .../messages/private_message_factory.dart | 2 +- .../private_message_serializer.dart | 2 +- .../listeners/interaction_create_packet.dart | 27 ++++++++++++- 12 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 lib/domains/components/buttons/contexts/private_button_context.dart create mode 100644 lib/domains/events/contracts/private/private_button_click_event.dart diff --git a/lib/api/private/private_message.dart b/lib/api/private/private_message.dart index 2f3c0b259..a96acd5c7 100644 --- a/lib/api/private/private_message.dart +++ b/lib/api/private/private_message.dart @@ -28,10 +28,10 @@ final class PrivateMessage implements Message { final String userId; - final User user; + final User author; PrivateMessage(this._properties, { required this.userId, - required this.user, + required this.author, }); } diff --git a/lib/domains/components/buttons/button_context.dart b/lib/domains/components/buttons/button_context.dart index 1e4de6efe..9121d6d09 100644 --- a/lib/domains/components/buttons/button_context.dart +++ b/lib/domains/components/buttons/button_context.dart @@ -1,10 +1,8 @@ import 'package:mineral/api/common/snowflake.dart'; -import 'package:mineral/api/server/member.dart'; abstract interface class ButtonContext { Snowflake get id; Snowflake get applicationId; String get token; int get version; - Member get member; } diff --git a/lib/domains/components/buttons/contexts/private_button_context.dart b/lib/domains/components/buttons/contexts/private_button_context.dart new file mode 100644 index 000000000..5f7845bc9 --- /dev/null +++ b/lib/domains/components/buttons/contexts/private_button_context.dart @@ -0,0 +1,39 @@ +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/private/private_message.dart'; +import 'package:mineral/api/private/user.dart'; +import 'package:mineral/api/server/member.dart'; +import 'package:mineral/api/server/server_message.dart'; +import 'package:mineral/domains/components/buttons/button_context.dart'; +import 'package:mineral/infrastructure/internals/interactions/interaction.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; + +final class PrivateButtonContext implements ButtonContext { + @override + final Snowflake id; + + @override + final Snowflake applicationId; + + @override + final String token; + + @override + final int version; + + final User user; + + final PrivateMessage message; + + late final InteractionContract interaction; + + PrivateButtonContext({ + required this.id, + required this.applicationId, + required this.token, + required this.version, + required this.message, + required this.user, + }) { + interaction = Interaction(token, id); + } +} diff --git a/lib/domains/components/buttons/contexts/server_button_context.dart b/lib/domains/components/buttons/contexts/server_button_context.dart index 8ca75c160..f8a4e9830 100644 --- a/lib/domains/components/buttons/contexts/server_button_context.dart +++ b/lib/domains/components/buttons/contexts/server_button_context.dart @@ -18,7 +18,6 @@ final class ServerButtonContext implements ButtonContext { @override final int version; - @override final Member member; final ServerMessage message; diff --git a/lib/domains/events/buckets/private_bucket.dart b/lib/domains/events/buckets/private_bucket.dart index 42e63480a..e1a9f1c58 100644 --- a/lib/domains/events/buckets/private_bucket.dart +++ b/lib/domains/events/buckets/private_bucket.dart @@ -1,3 +1,4 @@ +import 'package:mineral/domains/events/contracts/private/private_button_click_event.dart'; import 'package:mineral/domains/events/contracts/private/private_channel_create_event.dart'; import 'package:mineral/domains/events/contracts/private/private_channel_pins_update_event.dart'; import 'package:mineral/domains/events/contracts/private/private_message_create_event.dart'; @@ -17,4 +18,7 @@ final class PrivateBucket { void channelPinsUpdate(PrivateChannelPinsUpdateEventHandler handle) => _events.make(Event.privateChannelPinsUpdate, handle); + + void buttonClick(PrivateButtonClickEventHandler handle) => + _events.make(Event.privateButtonClick, handle); } diff --git a/lib/domains/events/buckets/server_bucket.dart b/lib/domains/events/buckets/server_bucket.dart index c7b20b581..ffbc40804 100644 --- a/lib/domains/events/buckets/server_bucket.dart +++ b/lib/domains/events/buckets/server_bucket.dart @@ -83,6 +83,6 @@ final class ServerBucket { void stickersUpdate(ServerStickersUpdateEventHandler handle) => _events.make(Event.serverStickersUpdate, handle); - void serverButtonClick(ServerButtonClickEventHandler handle) => + void buttonClick(ServerButtonClickEventHandler handle) => _events.make(Event.serverButtonClick, handle); } diff --git a/lib/domains/events/contracts/private/private_button_click_event.dart b/lib/domains/events/contracts/private/private_button_click_event.dart new file mode 100644 index 000000000..6fa09c24d --- /dev/null +++ b/lib/domains/events/contracts/private/private_button_click_event.dart @@ -0,0 +1,14 @@ +import 'dart:async'; + +import 'package:mineral/domains/components/buttons/contexts/private_button_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/domains/events/types/listenable_event.dart'; + +typedef PrivateButtonClickEventHandler = FutureOr Function(PrivateButtonContext); + +abstract class PrivateButtonClickEvent implements ListenableEvent { + @override + Event get event => Event.privateButtonClick; + + FutureOr handle(PrivateButtonContext ctx); +} diff --git a/lib/domains/events/event.dart b/lib/domains/events/event.dart index 54c0e3b8d..1206b0c03 100644 --- a/lib/domains/events/event.dart +++ b/lib/domains/events/event.dart @@ -1,5 +1,6 @@ import 'package:mineral/api/common/types/enhanced_enum.dart'; import 'package:mineral/domains/events/contracts/common/ready_event.dart'; +import 'package:mineral/domains/events/contracts/private/private_button_click_event.dart'; import 'package:mineral/domains/events/contracts/private/private_channel_create_event.dart'; import 'package:mineral/domains/events/contracts/private/private_channel_delete_event.dart'; import 'package:mineral/domains/events/contracts/private/private_channel_pins_update_event.dart'; @@ -56,7 +57,8 @@ enum Event implements EnhancedEnum, EventType { privateMessageCreate(PrivateMessageCreateEvent), privateChannelCreate(PrivateChannelCreateEvent), privateChannelUpdate(PrivateChannelUpdateEvent), - privateChannelDelete(PrivateChannelDeleteEvent); + privateChannelDelete(PrivateChannelDeleteEvent), + privateButtonClick(PrivateButtonClickEvent); @override final value; diff --git a/lib/infrastructure/internals/datastore/parts/message_part.dart b/lib/infrastructure/internals/datastore/parts/message_part.dart index f590fc0d5..219ae7eb9 100644 --- a/lib/infrastructure/internals/datastore/parts/message_part.dart +++ b/lib/infrastructure/internals/datastore/parts/message_part.dart @@ -1,4 +1,6 @@ import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/private/channels/private_channel.dart'; +import 'package:mineral/api/private/private_message.dart'; import 'package:mineral/api/server/channels/server_channel.dart'; import 'package:mineral/api/server/server_message.dart'; import 'package:mineral/infrastructure/internals/datastore/data_store_part.dart'; @@ -36,4 +38,32 @@ final class MessagePart implements DataStorePart { return serverMessage; } + + Future getPrivateMessage( + {required Snowflake messageId, required Snowflake channelId}) async { + final messageCacheKey = + _kernel.marshaller.cacheKey.privateMessage(messageId: messageId, channelId: channelId); + + final message = await _kernel.marshaller.cache.get(messageCacheKey); + if (message != null) { + return _kernel.marshaller.serializers.privateMessage.serializeCache(message); + } + + final response = await _kernel.dataStore.client.get('/channels/$channelId/messages/$messageId'); + + final privateMessage = + await _kernel.marshaller.serializers.privateMessage.serializeRemote(response.body); + + final channelCacheKey = _kernel.marshaller.cacheKey.channel(channelId); + final rawChannel = await _kernel.marshaller.cache.getOrFail(channelCacheKey); + privateMessage.channel = + await _kernel.marshaller.serializers.channels.serializeCache(rawChannel) as PrivateChannel; + + final rawMessage = + await _kernel.marshaller.serializers.privateMessage.deserialize(privateMessage); + + await _kernel.marshaller.cache.put(messageCacheKey, rawMessage); + + return privateMessage; + } } diff --git a/lib/infrastructure/internals/marshaller/factories/messages/private_message_factory.dart b/lib/infrastructure/internals/marshaller/factories/messages/private_message_factory.dart index befb023c6..269098b1c 100644 --- a/lib/infrastructure/internals/marshaller/factories/messages/private_message_factory.dart +++ b/lib/infrastructure/internals/marshaller/factories/messages/private_message_factory.dart @@ -13,7 +13,7 @@ final class PrivateMessageFactory implements MessageFactory { final user = await marshaller.serializers.user.serializeRemote(json['author']); - return PrivateMessage(messageProperties, userId: json['author']['id'], user: user); + return PrivateMessage(messageProperties, userId: json['author']['id'], author: user); } @override diff --git a/lib/infrastructure/internals/marshaller/serializers/private_message_serializer.dart b/lib/infrastructure/internals/marshaller/serializers/private_message_serializer.dart index d59008799..90108ad3e 100644 --- a/lib/infrastructure/internals/marshaller/serializers/private_message_serializer.dart +++ b/lib/infrastructure/internals/marshaller/serializers/private_message_serializer.dart @@ -17,7 +17,7 @@ final class PrivateMessageSerializer implements SerializerContract _handleServerButton(payload, dispatch), - _ => _handlePrivateButton(payload), + _ => _handlePrivateButton(payload, dispatch), }; } @@ -75,5 +76,27 @@ final class InteractionCreatePacket implements ListenablePacket { dispatch(event: Event.serverButtonClick, params: [ctx]); } - Future _handlePrivateButton(Map data) async {} + Future _handlePrivateButton(Map payload, DispatchEvent dispatch) async { + final message = await marshaller.dataStore.message.getPrivateMessage( + messageId: Snowflake(payload['message']['id']), channelId: Snowflake(payload['channel_id'])); + + final metadata = payload['message']['interaction_metadata']; + final type = ButtonType.values.firstWhereOrNull((e) => e.value == metadata['type']); + + if (type == null) { + logger.warn('Button type ${metadata['type']} not found'); + return; + } + + final ctx = PrivateButtonContext( + id: Snowflake(payload['id']), + applicationId: Snowflake(payload['application_id']), + version: payload['version'], + token: payload['token'], + message: message, + user: message.author, + ); + + dispatch(event: Event.serverButtonClick, params: [ctx]); + } } From 4201170f6dd04f6d029a53faa307f3d62ee5dcae Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Tue, 6 Aug 2024 09:02:10 +0200 Subject: [PATCH 04/19] feat(interaction): add missing customId --- lib/domains/components/buttons/button_context.dart | 1 + .../components/buttons/contexts/private_button_context.dart | 6 ++++-- .../components/buttons/contexts/server_button_context.dart | 4 ++++ .../packets/listeners/interaction_create_packet.dart | 2 ++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/domains/components/buttons/button_context.dart b/lib/domains/components/buttons/button_context.dart index 9121d6d09..a4a5aa018 100644 --- a/lib/domains/components/buttons/button_context.dart +++ b/lib/domains/components/buttons/button_context.dart @@ -5,4 +5,5 @@ abstract interface class ButtonContext { Snowflake get applicationId; String get token; int get version; + String get customId; } diff --git a/lib/domains/components/buttons/contexts/private_button_context.dart b/lib/domains/components/buttons/contexts/private_button_context.dart index 5f7845bc9..e8a5b9ba6 100644 --- a/lib/domains/components/buttons/contexts/private_button_context.dart +++ b/lib/domains/components/buttons/contexts/private_button_context.dart @@ -1,8 +1,6 @@ import 'package:mineral/api/common/snowflake.dart'; import 'package:mineral/api/private/private_message.dart'; import 'package:mineral/api/private/user.dart'; -import 'package:mineral/api/server/member.dart'; -import 'package:mineral/api/server/server_message.dart'; import 'package:mineral/domains/components/buttons/button_context.dart'; import 'package:mineral/infrastructure/internals/interactions/interaction.dart'; import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; @@ -20,6 +18,9 @@ final class PrivateButtonContext implements ButtonContext { @override final int version; + @override + final String customId; + final User user; final PrivateMessage message; @@ -31,6 +32,7 @@ final class PrivateButtonContext implements ButtonContext { required this.applicationId, required this.token, required this.version, + required this.customId, required this.message, required this.user, }) { diff --git a/lib/domains/components/buttons/contexts/server_button_context.dart b/lib/domains/components/buttons/contexts/server_button_context.dart index f8a4e9830..69398b4df 100644 --- a/lib/domains/components/buttons/contexts/server_button_context.dart +++ b/lib/domains/components/buttons/contexts/server_button_context.dart @@ -18,6 +18,9 @@ final class ServerButtonContext implements ButtonContext { @override final int version; + @override + final String customId; + final Member member; final ServerMessage message; @@ -29,6 +32,7 @@ final class ServerButtonContext implements ButtonContext { required this.applicationId, required this.token, required this.version, + required this.customId, required this.message, required this.member, }) { diff --git a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart index 943426b31..4c74c14ab 100644 --- a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart @@ -69,6 +69,7 @@ final class InteractionCreatePacket implements ListenablePacket { applicationId: Snowflake(payload['application_id']), version: payload['version'], token: payload['token'], + customId: payload['data']['custom_id'], message: message, member: message.author, ); @@ -93,6 +94,7 @@ final class InteractionCreatePacket implements ListenablePacket { applicationId: Snowflake(payload['application_id']), version: payload['version'], token: payload['token'], + customId: payload['data']['custom_id'], message: message, user: message.author, ); From 76f12efe7fc6f8d46b5aad67a5e9eb619509c069 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Wed, 7 Aug 2024 00:28:00 +0200 Subject: [PATCH 05/19] feat(interaction): implement dialog interactions --- .../contexts/private_dialog_context.dart | 37 +++++++++++++++++ .../contexts/server_dialog_context.dart | 37 +++++++++++++++++ .../components/dialog/dialog_context.dart | 9 ++++ .../events/buckets/private_bucket.dart | 4 ++ lib/domains/events/buckets/server_bucket.dart | 8 +++- .../private/private_dialog_submit_event.dart | 14 +++++++ .../server/server_dialog_submit_event.dart | 14 +++++++ lib/domains/events/event.dart | 8 +++- .../listeners/interaction_create_packet.dart | 41 +++++++++++++++++-- 9 files changed, 165 insertions(+), 7 deletions(-) create mode 100644 lib/domains/components/dialog/contexts/private_dialog_context.dart create mode 100644 lib/domains/components/dialog/contexts/server_dialog_context.dart create mode 100644 lib/domains/components/dialog/dialog_context.dart create mode 100644 lib/domains/events/contracts/private/private_dialog_submit_event.dart create mode 100644 lib/domains/events/contracts/server/server_dialog_submit_event.dart diff --git a/lib/domains/components/dialog/contexts/private_dialog_context.dart b/lib/domains/components/dialog/contexts/private_dialog_context.dart new file mode 100644 index 000000000..68835baef --- /dev/null +++ b/lib/domains/components/dialog/contexts/private_dialog_context.dart @@ -0,0 +1,37 @@ +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/private/user.dart'; +import 'package:mineral/domains/components/dialog/dialog_context.dart'; +import 'package:mineral/infrastructure/internals/interactions/interaction.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; + +final class PrivateDialogContext implements DialogContext { + @override + final Snowflake id; + + @override + final Snowflake applicationId; + + @override + final String token; + + @override + final int version; + + @override + final String customId; + + final User user; + + late final InteractionContract interaction; + + PrivateDialogContext({ + required this.id, + required this.applicationId, + required this.token, + required this.version, + required this.customId, + required this.user, + }) { + interaction = Interaction(token, id); + } +} diff --git a/lib/domains/components/dialog/contexts/server_dialog_context.dart b/lib/domains/components/dialog/contexts/server_dialog_context.dart new file mode 100644 index 000000000..6eb0dee4d --- /dev/null +++ b/lib/domains/components/dialog/contexts/server_dialog_context.dart @@ -0,0 +1,37 @@ +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/server/member.dart'; +import 'package:mineral/domains/components/dialog/dialog_context.dart'; +import 'package:mineral/infrastructure/internals/interactions/interaction.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; + +final class ServerDialogContext implements DialogContext { + @override + final Snowflake id; + + @override + final Snowflake applicationId; + + @override + final String token; + + @override + final int version; + + @override + final String customId; + + final Member member; + + late final InteractionContract interaction; + + ServerDialogContext({ + required this.id, + required this.applicationId, + required this.token, + required this.version, + required this.customId, + required this.member, + }) { + interaction = Interaction(token, id); + } +} diff --git a/lib/domains/components/dialog/dialog_context.dart b/lib/domains/components/dialog/dialog_context.dart new file mode 100644 index 000000000..aa0a26615 --- /dev/null +++ b/lib/domains/components/dialog/dialog_context.dart @@ -0,0 +1,9 @@ +import 'package:mineral/api/common/snowflake.dart'; + +abstract interface class DialogContext { + Snowflake get id; + Snowflake get applicationId; + String get token; + int get version; + String get customId; +} diff --git a/lib/domains/events/buckets/private_bucket.dart b/lib/domains/events/buckets/private_bucket.dart index e1a9f1c58..aaaf9996b 100644 --- a/lib/domains/events/buckets/private_bucket.dart +++ b/lib/domains/events/buckets/private_bucket.dart @@ -1,6 +1,7 @@ import 'package:mineral/domains/events/contracts/private/private_button_click_event.dart'; import 'package:mineral/domains/events/contracts/private/private_channel_create_event.dart'; import 'package:mineral/domains/events/contracts/private/private_channel_pins_update_event.dart'; +import 'package:mineral/domains/events/contracts/private/private_dialog_submit_event.dart'; import 'package:mineral/domains/events/contracts/private/private_message_create_event.dart'; import 'package:mineral/domains/events/event.dart'; import 'package:mineral/domains/events/event_bucket.dart'; @@ -21,4 +22,7 @@ final class PrivateBucket { void buttonClick(PrivateButtonClickEventHandler handle) => _events.make(Event.privateButtonClick, handle); + + void dialogSubmit(PrivateDialogSubmitEventHandler handle) => + _events.make(Event.privateDialogSubmit, handle); } diff --git a/lib/domains/events/buckets/server_bucket.dart b/lib/domains/events/buckets/server_bucket.dart index ffbc40804..537502a1b 100644 --- a/lib/domains/events/buckets/server_bucket.dart +++ b/lib/domains/events/buckets/server_bucket.dart @@ -1,13 +1,13 @@ -import 'package:mineral/domains/events/contracts/server/server_button_click_event.dart'; -import 'package:mineral/domains/events/event_bucket.dart'; import 'package:mineral/domains/events/contracts/server/server_ban_add_event.dart'; import 'package:mineral/domains/events/contracts/server/server_ban_remove_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_button_click_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_delete_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_pins_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_delete_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_dialog_submit_event.dart'; import 'package:mineral/domains/events/contracts/server/server_emojis_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_member_add_event.dart'; import 'package:mineral/domains/events/contracts/server/server_member_remove_event.dart'; @@ -20,6 +20,7 @@ import 'package:mineral/domains/events/contracts/server/server_roles_update_even import 'package:mineral/domains/events/contracts/server/server_stickers_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_update_event.dart'; import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/domains/events/event_bucket.dart'; final class ServerBucket { final EventBucket _events; @@ -85,4 +86,7 @@ final class ServerBucket { void buttonClick(ServerButtonClickEventHandler handle) => _events.make(Event.serverButtonClick, handle); + + void dialogSubmit(ServerDialogSubmitEventHandler handle) => + _events.make(Event.serverDialogSubmit, handle); } diff --git a/lib/domains/events/contracts/private/private_dialog_submit_event.dart b/lib/domains/events/contracts/private/private_dialog_submit_event.dart new file mode 100644 index 000000000..d35201dd9 --- /dev/null +++ b/lib/domains/events/contracts/private/private_dialog_submit_event.dart @@ -0,0 +1,14 @@ +import 'dart:async'; + +import 'package:mineral/domains/components/dialog/contexts/private_dialog_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/domains/events/types/listenable_event.dart'; + +typedef PrivateDialogSubmitEventHandler = FutureOr Function(PrivateDialogContext); + +abstract class PrivateDialogSubmitEvent implements ListenableEvent { + @override + Event get event => Event.privateDialogSubmit; + + FutureOr handle(PrivateDialogContext ctx); +} diff --git a/lib/domains/events/contracts/server/server_dialog_submit_event.dart b/lib/domains/events/contracts/server/server_dialog_submit_event.dart new file mode 100644 index 000000000..72e36b67e --- /dev/null +++ b/lib/domains/events/contracts/server/server_dialog_submit_event.dart @@ -0,0 +1,14 @@ +import 'dart:async'; + +import 'package:mineral/domains/components/dialog/contexts/server_dialog_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/domains/events/types/listenable_event.dart'; + +typedef ServerDialogSubmitEventHandler = FutureOr Function(ServerDialogContext); + +abstract class ServerDialogSubmitEvent implements ListenableEvent { + @override + Event get event => Event.serverDialogSubmit; + + FutureOr handle(ServerDialogContext ctx); +} diff --git a/lib/domains/events/event.dart b/lib/domains/events/event.dart index 1206b0c03..64396da69 100644 --- a/lib/domains/events/event.dart +++ b/lib/domains/events/event.dart @@ -5,6 +5,7 @@ import 'package:mineral/domains/events/contracts/private/private_channel_create_ import 'package:mineral/domains/events/contracts/private/private_channel_delete_event.dart'; import 'package:mineral/domains/events/contracts/private/private_channel_pins_update_event.dart'; import 'package:mineral/domains/events/contracts/private/private_channel_update_event.dart'; +import 'package:mineral/domains/events/contracts/private/private_dialog_submit_event.dart'; import 'package:mineral/domains/events/contracts/private/private_message_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_ban_add_event.dart'; import 'package:mineral/domains/events/contracts/server/server_ban_remove_event.dart'; @@ -15,6 +16,7 @@ import 'package:mineral/domains/events/contracts/server/server_channel_pins_upda import 'package:mineral/domains/events/contracts/server/server_channel_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_delete_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_dialog_submit_event.dart'; import 'package:mineral/domains/events/contracts/server/server_emojis_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_member_add_event.dart'; import 'package:mineral/domains/events/contracts/server/server_member_remove_event.dart'; @@ -52,16 +54,18 @@ enum Event implements EnhancedEnum, EventType { serverRoleUpdate(ServerRolesUpdateEvent), serverRoleDelete(ServerRolesDeleteEvent), serverButtonClick(ServerButtonClickEvent), + serverDialogSubmit(ServerDialogSubmitEvent), // private privateMessageCreate(PrivateMessageCreateEvent), privateChannelCreate(PrivateChannelCreateEvent), privateChannelUpdate(PrivateChannelUpdateEvent), privateChannelDelete(PrivateChannelDeleteEvent), - privateButtonClick(PrivateButtonClickEvent); + privateButtonClick(PrivateButtonClickEvent), + privateDialogSubmit(PrivateDialogSubmitEvent); @override - final value; + final Type value; const Event(this.value); } diff --git a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart index 4c74c14ab..4c7c69431 100644 --- a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart @@ -6,6 +6,8 @@ import 'package:mineral/api/common/types/interaction_type.dart'; import 'package:mineral/domains/commands/command_interaction_manager.dart'; import 'package:mineral/domains/components/buttons/contexts/private_button_context.dart'; import 'package:mineral/domains/components/buttons/contexts/server_button_context.dart'; +import 'package:mineral/domains/components/dialog/contexts/private_dialog_context.dart'; +import 'package:mineral/domains/components/dialog/contexts/server_dialog_context.dart'; import 'package:mineral/domains/events/event.dart'; import 'package:mineral/infrastructure/internals/container/ioc_container.dart'; import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; @@ -31,11 +33,13 @@ final class InteractionCreatePacket implements ListenablePacket { return switch (type) { InteractionType.applicationCommand => interactionManager.dispatcher.dispatch(message.payload), InteractionType.messageComponent => dispatchMessageComponent(message.payload, dispatch), + InteractionType.modal => dispatchModalComponent(message.payload, dispatch), _ => logger.warn('Interaction type ${message.payload['type']} not found') }; } - Future dispatchMessageComponent(Map payload, DispatchEvent dispatch) async { + Future dispatchMessageComponent( + Map payload, DispatchEvent dispatch) async { final String? serverId = payload['guild']?['id']; final metadata = payload['message']['interaction_metadata']; @@ -54,7 +58,8 @@ final class InteractionCreatePacket implements ListenablePacket { Future _handleServerButton(Map payload, DispatchEvent dispatch) async { final message = await marshaller.dataStore.message.getServerMessage( - messageId: Snowflake(payload['message']['id']), channelId: Snowflake(payload['channel_id'])); + messageId: Snowflake(payload['message']['id']), + channelId: Snowflake(payload['channel_id'])); final metadata = payload['message']['interaction_metadata']; final type = ButtonType.values.firstWhereOrNull((e) => e.value == metadata['type']); @@ -79,7 +84,8 @@ final class InteractionCreatePacket implements ListenablePacket { Future _handlePrivateButton(Map payload, DispatchEvent dispatch) async { final message = await marshaller.dataStore.message.getPrivateMessage( - messageId: Snowflake(payload['message']['id']), channelId: Snowflake(payload['channel_id'])); + messageId: Snowflake(payload['message']['id']), + channelId: Snowflake(payload['channel_id'])); final metadata = payload['message']['interaction_metadata']; final type = ButtonType.values.firstWhereOrNull((e) => e.value == metadata['type']); @@ -101,4 +107,33 @@ final class InteractionCreatePacket implements ListenablePacket { dispatch(event: Event.serverButtonClick, params: [ctx]); } + + Future dispatchModalComponent(Map payload, DispatchEvent dispatch) async { + switch (payload['guild_id']) { + case String(): + final context = ServerDialogContext( + customId: payload['data']['custom_id'], + id: Snowflake(payload['id']), + applicationId: Snowflake(payload['application_id']), + token: payload['token'], + version: payload['version'], + member: await marshaller.dataStore.member.getMember( + guildId: Snowflake(payload['guild_id']), + memberId: Snowflake(payload['member']['user']['id']), + ), + ); + dispatch(event: Event.serverDialogSubmit, params: [context]); + default: + final context = PrivateDialogContext( + customId: payload['data']['custom_id'], + id: Snowflake(payload['id']), + applicationId: Snowflake(payload['application_id']), + token: payload['token'], + version: payload['version'], + user: await marshaller.serializers.user.serializeRemote(payload['user']), + ); + + dispatch(event: Event.privateDialogSubmit, params: [context]); + } + } } From 520e64f71be3bc581bff1e4a03ad845a55c22188 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Fri, 9 Aug 2024 01:01:23 +0200 Subject: [PATCH 06/19] feat(interaction): implement dialog handler named parameters --- .../server/server_dialog_submit_event.dart | 2 +- lib/domains/events/event_dispatcher.dart | 6 +-- lib/domains/events/event_listener.dart | 2 +- lib/domains/events/internal_event_params.dart | 3 +- .../types/interaction_context_type.dart | 12 ++++++ .../internals/packets/listenable_packet.dart | 2 +- .../listeners/interaction_create_packet.dart | 40 ++++++++++++++----- 7 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 lib/infrastructure/internals/interactions/types/interaction_context_type.dart diff --git a/lib/domains/events/contracts/server/server_dialog_submit_event.dart b/lib/domains/events/contracts/server/server_dialog_submit_event.dart index 72e36b67e..0fe3050ef 100644 --- a/lib/domains/events/contracts/server/server_dialog_submit_event.dart +++ b/lib/domains/events/contracts/server/server_dialog_submit_event.dart @@ -4,7 +4,7 @@ import 'package:mineral/domains/components/dialog/contexts/server_dialog_context import 'package:mineral/domains/events/event.dart'; import 'package:mineral/domains/events/types/listenable_event.dart'; -typedef ServerDialogSubmitEventHandler = FutureOr Function(ServerDialogContext); +typedef ServerDialogSubmitEventHandler = Function; abstract class ServerDialogSubmitEvent implements ListenableEvent { @override diff --git a/lib/domains/events/event_dispatcher.dart b/lib/domains/events/event_dispatcher.dart index e2a38050d..090afce13 100644 --- a/lib/domains/events/event_dispatcher.dart +++ b/lib/domains/events/event_dispatcher.dart @@ -3,7 +3,7 @@ import 'package:mineral/domains/events/internal_event_params.dart'; import 'package:rxdart/rxdart.dart'; abstract interface class EventDispatcherContract { - void dispatch({required Event event, required List params}); + void dispatch({required Event event, required List params, Map? namedParams}); void dispose(); } @@ -14,8 +14,8 @@ final class EventDispatcher implements EventDispatcherContract { EventDispatcher(this._events); @override - void dispatch({required Event event, required List params}) { - _events.add(InternalEventParams(event, params)); + void dispatch({required Event event, required List params, Map? namedParams}) { + _events.add(InternalEventParams(event, params, namedParams)); } @override diff --git a/lib/domains/events/event_listener.dart b/lib/domains/events/event_listener.dart index 59b6b5c9a..8acfb29e1 100644 --- a/lib/domains/events/event_listener.dart +++ b/lib/domains/events/event_listener.dart @@ -31,6 +31,6 @@ final class EventListener implements EventListenerContract { StreamSubscription listen({required Event event, required T handle}) { return _events.stream .where((element) => element.event == event) - .listen((element) => Function.apply(handle, element.params)); + .listen((element) => Function.apply(handle, element.params, element.namedParams)); } } diff --git a/lib/domains/events/internal_event_params.dart b/lib/domains/events/internal_event_params.dart index eb7697ab2..cfd31f0a7 100644 --- a/lib/domains/events/internal_event_params.dart +++ b/lib/domains/events/internal_event_params.dart @@ -3,6 +3,7 @@ import 'package:mineral/domains/events/event.dart'; final class InternalEventParams { final Event event; final List params; + final Map? namedParams; - const InternalEventParams(this.event, this.params); + const InternalEventParams(this.event, this.params, this.namedParams); } diff --git a/lib/infrastructure/internals/interactions/types/interaction_context_type.dart b/lib/infrastructure/internals/interactions/types/interaction_context_type.dart new file mode 100644 index 000000000..63cac9cd0 --- /dev/null +++ b/lib/infrastructure/internals/interactions/types/interaction_context_type.dart @@ -0,0 +1,12 @@ +import 'package:mineral/api/common/types/enhanced_enum.dart'; + +enum InteractionContextType implements EnhancedEnum { + server(0), + botPrivate(1), + privateChannel(2); + + @override + final int value; + + const InteractionContextType(this.value); +} diff --git a/lib/infrastructure/internals/packets/listenable_packet.dart b/lib/infrastructure/internals/packets/listenable_packet.dart index 1515cc0b7..4fc53091b 100644 --- a/lib/infrastructure/internals/packets/listenable_packet.dart +++ b/lib/infrastructure/internals/packets/listenable_packet.dart @@ -4,7 +4,7 @@ import 'package:mineral/domains/events/event.dart'; import 'package:mineral/infrastructure/internals/packets/packet_type.dart'; import 'package:mineral/infrastructure/internals/wss/shard_message.dart'; -typedef DispatchEvent = Function({required Event event, required List params}); +typedef DispatchEvent = Function({required Event event, required List params, Map? namedParams}); abstract interface class ListenablePacket { PacketType get packetType; diff --git a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart index 4c7c69431..fa895259e 100644 --- a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart @@ -10,6 +10,7 @@ import 'package:mineral/domains/components/dialog/contexts/private_dialog_contex import 'package:mineral/domains/components/dialog/contexts/server_dialog_context.dart'; import 'package:mineral/domains/events/event.dart'; import 'package:mineral/infrastructure/internals/container/ioc_container.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_context_type.dart'; import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; import 'package:mineral/infrastructure/internals/packets/listenable_packet.dart'; import 'package:mineral/infrastructure/internals/packets/packet_type.dart'; @@ -109,9 +110,22 @@ final class InteractionCreatePacket implements ListenablePacket { } Future dispatchModalComponent(Map payload, DispatchEvent dispatch) async { - switch (payload['guild_id']) { - case String(): - final context = ServerDialogContext( + final interactionContext = InteractionContextType.values + .firstWhereOrNull((element) => element.value == payload['context']); + + final Map parameters = List.from(payload['data']['components']).map((row) { + final component = row['components'][0]; + return {Symbol(component['custom_id']): component['value']}; + }).fold({}, (prev, curr) => {...prev, ...curr}); + + final event = switch (interactionContext) { + InteractionContextType.server => Event.serverDialogSubmit, + InteractionContextType.privateChannel => Event.privateDialogSubmit, + _ => null + }; + + final ctx = switch (interactionContext) { + InteractionContextType.server => ServerDialogContext( customId: payload['data']['custom_id'], id: Snowflake(payload['id']), applicationId: Snowflake(payload['application_id']), @@ -121,19 +135,27 @@ final class InteractionCreatePacket implements ListenablePacket { guildId: Snowflake(payload['guild_id']), memberId: Snowflake(payload['member']['user']['id']), ), - ); - dispatch(event: Event.serverDialogSubmit, params: [context]); - default: - final context = PrivateDialogContext( + ), + InteractionContextType.privateChannel => PrivateDialogContext( customId: payload['data']['custom_id'], id: Snowflake(payload['id']), applicationId: Snowflake(payload['application_id']), token: payload['token'], version: payload['version'], user: await marshaller.serializers.user.serializeRemote(payload['user']), - ); + ), + _ => null + }; - dispatch(event: Event.privateDialogSubmit, params: [context]); + if ([event, ctx].contains(null)) { + logger.warn('Interaction context ${payload['context']} not found'); + return; } + + dispatch( + event: event!, + params: [ctx], + namedParams: parameters, + ); } } From 6acc96e953b02078d56daeb3d42d8c2d7684de8d Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Sun, 11 Aug 2024 15:31:11 +0200 Subject: [PATCH 07/19] feat(interaction): add event listening constraint --- lib/domains/events/buckets/server_bucket.dart | 22 +++++++------------ .../server/server_dialog_submit_event.dart | 4 +++- lib/domains/events/event_bucket.dart | 11 +++++----- lib/domains/events/event_dispatcher.dart | 6 ++--- lib/domains/events/event_listener.dart | 15 ++++++++----- lib/domains/events/internal_event_params.dart | 4 ++-- .../internals/packets/listenable_packet.dart | 6 ++--- .../listeners/interaction_create_packet.dart | 4 ++-- lib/infrastructure/kernel/mineral_client.dart | 5 +++++ 9 files changed, 41 insertions(+), 36 deletions(-) diff --git a/lib/domains/events/buckets/server_bucket.dart b/lib/domains/events/buckets/server_bucket.dart index 537502a1b..e879eb1b7 100644 --- a/lib/domains/events/buckets/server_bucket.dart +++ b/lib/domains/events/buckets/server_bucket.dart @@ -27,14 +27,11 @@ final class ServerBucket { ServerBucket(this._events); - void serverCreate(ServerCreateEventHandler handle) => - _events.make(Event.serverCreate, handle); + void serverCreate(ServerCreateEventHandler handle) => _events.make(Event.serverCreate, handle); - void serverUpdate(ServerUpdateEventHandler handle) => - _events.make(Event.serverUpdate, handle); + void serverUpdate(ServerUpdateEventHandler handle) => _events.make(Event.serverUpdate, handle); - void serverDelete(ServerDeleteEventHandler handle) => - _events.make(Event.serverDelete, handle); + void serverDelete(ServerDeleteEventHandler handle) => _events.make(Event.serverDelete, handle); void messageCreate(ServerMessageEventHandler handle) => _events.make(Event.serverMessageCreate, handle); @@ -51,8 +48,7 @@ final class ServerBucket { void channelPinsUpdate(ServerChannelPinsUpdateEventHandler handle) => _events.make(Event.serverChannelPinsUpdate, handle); - void memberAdd(ServerMemberAddEventHandler handle) => - _events.make(Event.serverMemberAdd, handle); + void memberAdd(ServerMemberAddEventHandler handle) => _events.make(Event.serverMemberAdd, handle); void memberRemove(ServerMemberRemoveEventHandler handle) => _events.make(Event.serverMemberRemove, handle); @@ -72,11 +68,9 @@ final class ServerBucket { void presenceUpdate(ServerPresenceUpdateEventHandler handle) => _events.make(Event.serverPresenceUpdate, handle); - void banAdd(ServerBanAddEventHandler handle) => - _events.make(Event.serverBanAdd, handle); + void banAdd(ServerBanAddEventHandler handle) => _events.make(Event.serverBanAdd, handle); - void banRemove(ServerBanRemoveEventHandler handle) => - _events.make(Event.serverBanRemove, handle); + void banRemove(ServerBanRemoveEventHandler handle) => _events.make(Event.serverBanRemove, handle); void emojisUpdate(ServerEmojisUpdateEventHandler handle) => _events.make(Event.serverEmojisUpdate, handle); @@ -87,6 +81,6 @@ final class ServerBucket { void buttonClick(ServerButtonClickEventHandler handle) => _events.make(Event.serverButtonClick, handle); - void dialogSubmit(ServerDialogSubmitEventHandler handle) => - _events.make(Event.serverDialogSubmit, handle); + void dialogSubmit(ServerDialogSubmitEventHandler handle, {String? customId}) => + _events.make(Event.serverDialogSubmit, handle, customId: customId); } diff --git a/lib/domains/events/contracts/server/server_dialog_submit_event.dart b/lib/domains/events/contracts/server/server_dialog_submit_event.dart index 0fe3050ef..a922a7489 100644 --- a/lib/domains/events/contracts/server/server_dialog_submit_event.dart +++ b/lib/domains/events/contracts/server/server_dialog_submit_event.dart @@ -4,11 +4,13 @@ import 'package:mineral/domains/components/dialog/contexts/server_dialog_context import 'package:mineral/domains/events/event.dart'; import 'package:mineral/domains/events/types/listenable_event.dart'; -typedef ServerDialogSubmitEventHandler = Function; +typedef ServerDialogSubmitEventHandler = FutureOr Function(ServerDialogContext ctx, T data); abstract class ServerDialogSubmitEvent implements ListenableEvent { @override Event get event => Event.serverDialogSubmit; + String get customId; + FutureOr handle(ServerDialogContext ctx); } diff --git a/lib/domains/events/event_bucket.dart b/lib/domains/events/event_bucket.dart index 733de34bd..3d28c5db4 100644 --- a/lib/domains/events/event_bucket.dart +++ b/lib/domains/events/event_bucket.dart @@ -15,13 +15,12 @@ final class EventBucket { private = PrivateBucket(this); } - void make(Event event, T handle) => _registerEvent(event: event, handle: handle); + void make(Event event, T handle, {String? customId}) => + _registerEvent(event: event, handle: handle, customId: customId); void ready(ReadyEventHandler handle) => _registerEvent(event: Event.ready, handle: handle); - void _registerEvent({required Event event, required T handle}) => - _kernel.eventListener.listen( - event: event, - handle: handle, - ); + void _registerEvent( + {required Event event, required T handle, String? customId}) => + _kernel.eventListener.listen(event: event, handle: handle, customId: customId); } diff --git a/lib/domains/events/event_dispatcher.dart b/lib/domains/events/event_dispatcher.dart index 090afce13..3b4f4dc15 100644 --- a/lib/domains/events/event_dispatcher.dart +++ b/lib/domains/events/event_dispatcher.dart @@ -3,7 +3,7 @@ import 'package:mineral/domains/events/internal_event_params.dart'; import 'package:rxdart/rxdart.dart'; abstract interface class EventDispatcherContract { - void dispatch({required Event event, required List params, Map? namedParams}); + void dispatch({required Event event, required List params}); void dispose(); } @@ -14,8 +14,8 @@ final class EventDispatcher implements EventDispatcherContract { EventDispatcher(this._events); @override - void dispatch({required Event event, required List params, Map? namedParams}) { - _events.add(InternalEventParams(event, params, namedParams)); + void dispatch({required Event event, required List params, bool Function(String?)? constraint}) { + _events.add(InternalEventParams(event, params, constraint)); } @override diff --git a/lib/domains/events/event_listener.dart b/lib/domains/events/event_listener.dart index 8acfb29e1..a00143b79 100644 --- a/lib/domains/events/event_listener.dart +++ b/lib/domains/events/event_listener.dart @@ -11,7 +11,8 @@ abstract interface class EventListenerContract { EventDispatcherContract get dispatcher; - StreamSubscription listen({required Event event, required T handle}); + StreamSubscription listen( + {required Event event, required T handle, required String? customId}); } final class EventListener implements EventListenerContract { @@ -28,9 +29,13 @@ final class EventListener implements EventListenerContract { } @override - StreamSubscription listen({required Event event, required T handle}) { - return _events.stream - .where((element) => element.event == event) - .listen((element) => Function.apply(handle, element.params, element.namedParams)); + StreamSubscription listen( + {required Event event, required T handle, required String? customId}) { + return _events.stream.where((element) => element.event == event).where((element) { + return switch (element.constraint) { + final bool Function(String?) constraint => constraint(customId), + _ => true + }; + }).listen((element) => Function.apply(handle, element.params)); } } diff --git a/lib/domains/events/internal_event_params.dart b/lib/domains/events/internal_event_params.dart index cfd31f0a7..b20cb78da 100644 --- a/lib/domains/events/internal_event_params.dart +++ b/lib/domains/events/internal_event_params.dart @@ -3,7 +3,7 @@ import 'package:mineral/domains/events/event.dart'; final class InternalEventParams { final Event event; final List params; - final Map? namedParams; + final bool Function(String?)? constraint; - const InternalEventParams(this.event, this.params, this.namedParams); + const InternalEventParams(this.event, this.params, this.constraint); } diff --git a/lib/infrastructure/internals/packets/listenable_packet.dart b/lib/infrastructure/internals/packets/listenable_packet.dart index 4fc53091b..e1d0031b0 100644 --- a/lib/infrastructure/internals/packets/listenable_packet.dart +++ b/lib/infrastructure/internals/packets/listenable_packet.dart @@ -4,11 +4,11 @@ import 'package:mineral/domains/events/event.dart'; import 'package:mineral/infrastructure/internals/packets/packet_type.dart'; import 'package:mineral/infrastructure/internals/wss/shard_message.dart'; -typedef DispatchEvent = Function({required Event event, required List params, Map? namedParams}); +typedef DispatchEvent = Function( + {required Event event, required List params, bool Function(String?)? constraint}); abstract interface class ListenablePacket { PacketType get packetType; - FutureOr listen( - ShardMessage message, DispatchEvent dispatch); + FutureOr listen(ShardMessage message, DispatchEvent dispatch); } diff --git a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart index fa895259e..9816e9890 100644 --- a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart @@ -154,8 +154,8 @@ final class InteractionCreatePacket implements ListenablePacket { dispatch( event: event!, - params: [ctx], - namedParams: parameters, + params: [ctx, parameters], + constraint: (String? customId) => customId == ctx!.customId ); } } diff --git a/lib/infrastructure/kernel/mineral_client.dart b/lib/infrastructure/kernel/mineral_client.dart index 253e38da2..68bfe89ae 100644 --- a/lib/infrastructure/kernel/mineral_client.dart +++ b/lib/infrastructure/kernel/mineral_client.dart @@ -1,5 +1,6 @@ import 'package:mineral/api/common/commands/command_contract.dart'; import 'package:mineral/domains/commands/command_declaration_bucket.dart'; +import 'package:mineral/domains/events/contracts/server/server_dialog_submit_event.dart'; import 'package:mineral/domains/events/event_bucket.dart'; import 'package:mineral/domains/events/types/listenable_event.dart'; import 'package:mineral/infrastructure/commons/listenable.dart'; @@ -46,6 +47,10 @@ final class MineralClient implements MineralClientContract { _kernel.eventListener.listen( event: instance.event, handle: (instance as dynamic).handle as Function, + customId: switch(instance) { + final ServerDialogSubmitEvent instance => instance.customId, + _ => null + } ); } } From 06672961a6a6493e9c5af1b6f9ce9dbe3e8ed538 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Sun, 11 Aug 2024 15:33:12 +0200 Subject: [PATCH 08/19] feat(interaction): add interaction dialog listening constraint --- lib/domains/events/buckets/private_bucket.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/domains/events/buckets/private_bucket.dart b/lib/domains/events/buckets/private_bucket.dart index aaaf9996b..442c22cfc 100644 --- a/lib/domains/events/buckets/private_bucket.dart +++ b/lib/domains/events/buckets/private_bucket.dart @@ -23,6 +23,6 @@ final class PrivateBucket { void buttonClick(PrivateButtonClickEventHandler handle) => _events.make(Event.privateButtonClick, handle); - void dialogSubmit(PrivateDialogSubmitEventHandler handle) => - _events.make(Event.privateDialogSubmit, handle); + void dialogSubmit(PrivateDialogSubmitEventHandler handle, {String? customId}) => + _events.make(Event.privateDialogSubmit, handle, customId: customId); } From 754eeefa6e1664f350d1f90bf212ccfe677e0265 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Sun, 11 Aug 2024 20:02:13 +0200 Subject: [PATCH 09/19] feat(interaction): implement channel select component --- lib/api/common/components/component_type.dart | 8 +++ .../components/selects/button_context.dart | 9 +++ .../contexts/server_select_context.dart | 42 +++++++++++++ .../server/server_channel_select_event.dart | 18 ++++++ lib/domains/events/event.dart | 2 + .../listeners/interaction_create_packet.dart | 63 +++++++++++++++++-- 6 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 lib/domains/components/selects/button_context.dart create mode 100644 lib/domains/components/selects/contexts/server_select_context.dart create mode 100644 lib/domains/events/contracts/server/server_channel_select_event.dart diff --git a/lib/api/common/components/component_type.dart b/lib/api/common/components/component_type.dart index 7859238d0..21890b554 100644 --- a/lib/api/common/components/component_type.dart +++ b/lib/api/common/components/component_type.dart @@ -10,6 +10,14 @@ enum ComponentType implements EnhancedEnum { mentionableSelectMenu(7), channelSelectMenu(8); + static const selectMenus = [ + textSelectMenu, + userSelectMenu, + roleSelectMenu, + mentionableSelectMenu, + channelSelectMenu, + ]; + @override final int value; diff --git a/lib/domains/components/selects/button_context.dart b/lib/domains/components/selects/button_context.dart new file mode 100644 index 000000000..aeb3c93f4 --- /dev/null +++ b/lib/domains/components/selects/button_context.dart @@ -0,0 +1,9 @@ +import 'package:mineral/api/common/snowflake.dart'; + +abstract interface class SelectContext { + Snowflake get id; + Snowflake get applicationId; + String get token; + int get version; + String get customId; +} diff --git a/lib/domains/components/selects/contexts/server_select_context.dart b/lib/domains/components/selects/contexts/server_select_context.dart new file mode 100644 index 000000000..d2230346c --- /dev/null +++ b/lib/domains/components/selects/contexts/server_select_context.dart @@ -0,0 +1,42 @@ +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/server/member.dart'; +import 'package:mineral/api/server/server_message.dart'; +import 'package:mineral/domains/components/buttons/button_context.dart'; +import 'package:mineral/domains/components/selects/button_context.dart'; +import 'package:mineral/infrastructure/internals/interactions/interaction.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; + +final class ServerSelectContext implements SelectContext { + @override + final Snowflake id; + + @override + final Snowflake applicationId; + + @override + final String token; + + @override + final int version; + + @override + final String customId; + + final Member member; + + final ServerMessage message; + + late final InteractionContract interaction; + + ServerSelectContext({ + required this.id, + required this.applicationId, + required this.token, + required this.version, + required this.customId, + required this.message, + required this.member, + }) { + interaction = Interaction(token, id); + } +} diff --git a/lib/domains/events/contracts/server/server_channel_select_event.dart b/lib/domains/events/contracts/server/server_channel_select_event.dart new file mode 100644 index 000000000..4748a5afe --- /dev/null +++ b/lib/domains/events/contracts/server/server_channel_select_event.dart @@ -0,0 +1,18 @@ +import 'dart:async'; + +import 'package:mineral/api/server/channels/server_channel.dart'; +import 'package:mineral/domains/components/dialog/contexts/server_dialog_context.dart'; +import 'package:mineral/domains/components/selects/contexts/server_select_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/domains/events/types/listenable_event.dart'; + +typedef ServerChannelSelectEventHandler = FutureOr Function(ServerSelectContext ctx, List channels); + +abstract class ServerChannelSelectEvent implements ListenableEvent { + @override + Event get event => Event.serverChannelSelect; + + String get customId; + + FutureOr handle(ServerSelectContext ctx, List channels); +} diff --git a/lib/domains/events/event.dart b/lib/domains/events/event.dart index 64396da69..9a7818ed8 100644 --- a/lib/domains/events/event.dart +++ b/lib/domains/events/event.dart @@ -13,6 +13,7 @@ import 'package:mineral/domains/events/contracts/server/server_button_click_even import 'package:mineral/domains/events/contracts/server/server_channel_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_delete_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_pins_update_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_channel_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_delete_event.dart'; @@ -55,6 +56,7 @@ enum Event implements EnhancedEnum, EventType { serverRoleDelete(ServerRolesDeleteEvent), serverButtonClick(ServerButtonClickEvent), serverDialogSubmit(ServerDialogSubmitEvent), + serverChannelSelect(ServerChannelSelectEvent), // private privateMessageCreate(PrivateMessageCreateEvent), diff --git a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart index 9816e9890..502c25f69 100644 --- a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:collection/collection.dart'; import 'package:mineral/api/common/components/buttons/button_type.dart'; import 'package:mineral/api/common/components/component_type.dart'; @@ -8,6 +10,7 @@ import 'package:mineral/domains/components/buttons/contexts/private_button_conte import 'package:mineral/domains/components/buttons/contexts/server_button_context.dart'; import 'package:mineral/domains/components/dialog/contexts/private_dialog_context.dart'; import 'package:mineral/domains/components/dialog/contexts/server_dialog_context.dart'; +import 'package:mineral/domains/components/selects/contexts/server_select_context.dart'; import 'package:mineral/domains/events/event.dart'; import 'package:mineral/infrastructure/internals/container/ioc_container.dart'; import 'package:mineral/infrastructure/internals/interactions/types/interaction_context_type.dart'; @@ -31,9 +34,15 @@ final class InteractionCreatePacket implements ListenablePacket { final interactionManager = ioc.resolve(); final type = InteractionType.values.firstWhereOrNull((e) => e.value == message.payload['type']); + final componentType = ComponentType.values + .firstWhereOrNull((e) => e.value == message.payload['data']['component_type']); + return switch (type) { InteractionType.applicationCommand => interactionManager.dispatcher.dispatch(message.payload), - InteractionType.messageComponent => dispatchMessageComponent(message.payload, dispatch), + InteractionType.messageComponent when componentType == ComponentType.button => + dispatchMessageComponent(message.payload, dispatch), + InteractionType.messageComponent when ComponentType.selectMenus.contains(componentType) => + dispatchSelectComponent(message.payload, dispatch), InteractionType.modal => dispatchModalComponent(message.payload, dispatch), _ => logger.warn('Interaction type ${message.payload['type']} not found') }; @@ -153,9 +162,55 @@ final class InteractionCreatePacket implements ListenablePacket { } dispatch( - event: event!, - params: [ctx, parameters], - constraint: (String? customId) => customId == ctx!.customId + event: event!, + params: [ctx, parameters], + constraint: (String? customId) => customId == ctx!.customId); + } + + Future dispatchSelectComponent(Map payload, DispatchEvent dispatch) async { + final selectMenuType = + ComponentType.values.firstWhereOrNull((e) => e.value == payload['data']['component_type']); + + print(selectMenuType); + + switch (selectMenuType) { + case ComponentType.channelSelectMenu: + _dispatchSelectMenu(payload, dispatch); + default: + logger.warn('Select menu type $selectMenuType not found'); + } + } + + Future _dispatchSelectMenu(Map payload, DispatchEvent dispatch) async { + final resolvedData = payload['data']['resolved']; + final channelIds = Map.from(resolvedData['channels']).keys; + + final ctx = ServerSelectContext( + id: Snowflake(payload['id']), + applicationId: Snowflake(payload['application_id']), + token: payload['token'], + version: payload['version'], + customId: payload['data']['custom_id'], + message: await marshaller.dataStore.message.getServerMessage( + messageId: Snowflake(payload['message']['id']), + channelId: Snowflake(payload['channel_id']), + ), + member: await marshaller.dataStore.member.getMember( + guildId: Snowflake(payload['guild_id']), + memberId: Snowflake(payload['member']['user']['id']), + ), ); + + final data = await Future.wait(channelIds.map((id) async { + final cacheKey = marshaller.cacheKey.channel(Snowflake(id)); + final rawChannel = await marshaller.cache.getOrFail(cacheKey); + + return marshaller.serializers.channels.serializeCache(rawChannel); + })); + + dispatch( + event: Event.serverChannelSelect, + params: [ctx, data], + constraint: (String? customId) => customId == ctx.customId); } } From 1f4c41aeab1b5e48b4e7cccb3d459954bf296ec1 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Sun, 11 Aug 2024 20:48:42 +0200 Subject: [PATCH 10/19] feat(interaction): implement server channel select handler --- lib/domains/events/buckets/server_bucket.dart | 4 ++++ .../contracts/server/server_channel_select_event.dart | 3 +-- .../contracts/server/server_dialog_submit_event.dart | 2 +- .../packets/listeners/interaction_create_packet.dart | 7 +++---- lib/infrastructure/kernel/mineral_client.dart | 2 ++ 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/domains/events/buckets/server_bucket.dart b/lib/domains/events/buckets/server_bucket.dart index e879eb1b7..f43e78818 100644 --- a/lib/domains/events/buckets/server_bucket.dart +++ b/lib/domains/events/buckets/server_bucket.dart @@ -4,6 +4,7 @@ import 'package:mineral/domains/events/contracts/server/server_button_click_even import 'package:mineral/domains/events/contracts/server/server_channel_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_delete_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_pins_update_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_channel_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_delete_event.dart'; @@ -83,4 +84,7 @@ final class ServerBucket { void dialogSubmit(ServerDialogSubmitEventHandler handle, {String? customId}) => _events.make(Event.serverDialogSubmit, handle, customId: customId); + + void selectChannel(ServerChannelSelectEventHandler handle, {String? customId}) => + _events.make(Event.serverChannelSelect, handle, customId: customId); } diff --git a/lib/domains/events/contracts/server/server_channel_select_event.dart b/lib/domains/events/contracts/server/server_channel_select_event.dart index 4748a5afe..386f9a154 100644 --- a/lib/domains/events/contracts/server/server_channel_select_event.dart +++ b/lib/domains/events/contracts/server/server_channel_select_event.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:mineral/api/server/channels/server_channel.dart'; -import 'package:mineral/domains/components/dialog/contexts/server_dialog_context.dart'; import 'package:mineral/domains/components/selects/contexts/server_select_context.dart'; import 'package:mineral/domains/events/event.dart'; import 'package:mineral/domains/events/types/listenable_event.dart'; @@ -12,7 +11,7 @@ abstract class ServerChannelSelectEvent implements ListenableEvent { @override Event get event => Event.serverChannelSelect; - String get customId; + String? get customId; FutureOr handle(ServerSelectContext ctx, List channels); } diff --git a/lib/domains/events/contracts/server/server_dialog_submit_event.dart b/lib/domains/events/contracts/server/server_dialog_submit_event.dart index a922a7489..93bdac433 100644 --- a/lib/domains/events/contracts/server/server_dialog_submit_event.dart +++ b/lib/domains/events/contracts/server/server_dialog_submit_event.dart @@ -10,7 +10,7 @@ abstract class ServerDialogSubmitEvent implements ListenableEvent { @override Event get event => Event.serverDialogSubmit; - String get customId; + String? get customId; FutureOr handle(ServerDialogContext ctx); } diff --git a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart index 502c25f69..e03ae70a6 100644 --- a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart @@ -1,10 +1,9 @@ -import 'dart:convert'; - import 'package:collection/collection.dart'; import 'package:mineral/api/common/components/buttons/button_type.dart'; import 'package:mineral/api/common/components/component_type.dart'; import 'package:mineral/api/common/snowflake.dart'; import 'package:mineral/api/common/types/interaction_type.dart'; +import 'package:mineral/api/server/channels/server_channel.dart'; import 'package:mineral/domains/commands/command_interaction_manager.dart'; import 'package:mineral/domains/components/buttons/contexts/private_button_context.dart'; import 'package:mineral/domains/components/buttons/contexts/server_button_context.dart'; @@ -201,11 +200,11 @@ final class InteractionCreatePacket implements ListenablePacket { ), ); - final data = await Future.wait(channelIds.map((id) async { + final List data = await Future.wait(channelIds.map((id) async { final cacheKey = marshaller.cacheKey.channel(Snowflake(id)); final rawChannel = await marshaller.cache.getOrFail(cacheKey); - return marshaller.serializers.channels.serializeCache(rawChannel); + return marshaller.serializers.channels.serializeCache(rawChannel) as Future; })); dispatch( diff --git a/lib/infrastructure/kernel/mineral_client.dart b/lib/infrastructure/kernel/mineral_client.dart index 68bfe89ae..02136e9a8 100644 --- a/lib/infrastructure/kernel/mineral_client.dart +++ b/lib/infrastructure/kernel/mineral_client.dart @@ -1,5 +1,6 @@ import 'package:mineral/api/common/commands/command_contract.dart'; import 'package:mineral/domains/commands/command_declaration_bucket.dart'; +import 'package:mineral/domains/events/contracts/server/server_channel_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_dialog_submit_event.dart'; import 'package:mineral/domains/events/event_bucket.dart'; import 'package:mineral/domains/events/types/listenable_event.dart'; @@ -49,6 +50,7 @@ final class MineralClient implements MineralClientContract { handle: (instance as dynamic).handle as Function, customId: switch(instance) { final ServerDialogSubmitEvent instance => instance.customId, + final ServerChannelSelectEvent instance => instance.customId, _ => null } ); From dda943457da0881804a6bd54aae817f1a267ff63 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Sun, 11 Aug 2024 21:20:23 +0200 Subject: [PATCH 11/19] feat(interaction): enhance packets interaction structures --- .../contexts/private_select_context.dart | 41 ++++ .../listeners/interaction_create_packet.dart | 215 ------------------ .../button_interaction_create_packet.dart | 100 ++++++++ .../command_interaction_create_packet.dart | 29 +++ .../dialog_interaction_create_packet.dart | 77 +++++++ .../interaction_create_packet.dart | 20 ++ .../select_interaction_create_packet.dart | 102 +++++++++ .../internals/packets/packet_listener.dart | 11 +- 8 files changed, 379 insertions(+), 216 deletions(-) create mode 100644 lib/domains/components/selects/contexts/private_select_context.dart delete mode 100644 lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart create mode 100644 lib/infrastructure/internals/packets/listeners/interactions/button_interaction_create_packet.dart create mode 100644 lib/infrastructure/internals/packets/listeners/interactions/command_interaction_create_packet.dart create mode 100644 lib/infrastructure/internals/packets/listeners/interactions/dialog_interaction_create_packet.dart create mode 100644 lib/infrastructure/internals/packets/listeners/interactions/interaction_create_packet.dart create mode 100644 lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart diff --git a/lib/domains/components/selects/contexts/private_select_context.dart b/lib/domains/components/selects/contexts/private_select_context.dart new file mode 100644 index 000000000..51a7e4515 --- /dev/null +++ b/lib/domains/components/selects/contexts/private_select_context.dart @@ -0,0 +1,41 @@ +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/private/private_message.dart'; +import 'package:mineral/api/private/user.dart'; +import 'package:mineral/domains/components/selects/button_context.dart'; +import 'package:mineral/infrastructure/internals/interactions/interaction.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; + +final class PrivateSelectContext implements SelectContext { + @override + final Snowflake id; + + @override + final Snowflake applicationId; + + @override + final String token; + + @override + final int version; + + @override + final String customId; + + final User user; + + final PrivateMessage message; + + late final InteractionContract interaction; + + PrivateSelectContext({ + required this.id, + required this.applicationId, + required this.token, + required this.version, + required this.customId, + required this.message, + required this.user, + }) { + interaction = Interaction(token, id); + } +} diff --git a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart deleted file mode 100644 index e03ae70a6..000000000 --- a/lib/infrastructure/internals/packets/listeners/interaction_create_packet.dart +++ /dev/null @@ -1,215 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:mineral/api/common/components/buttons/button_type.dart'; -import 'package:mineral/api/common/components/component_type.dart'; -import 'package:mineral/api/common/snowflake.dart'; -import 'package:mineral/api/common/types/interaction_type.dart'; -import 'package:mineral/api/server/channels/server_channel.dart'; -import 'package:mineral/domains/commands/command_interaction_manager.dart'; -import 'package:mineral/domains/components/buttons/contexts/private_button_context.dart'; -import 'package:mineral/domains/components/buttons/contexts/server_button_context.dart'; -import 'package:mineral/domains/components/dialog/contexts/private_dialog_context.dart'; -import 'package:mineral/domains/components/dialog/contexts/server_dialog_context.dart'; -import 'package:mineral/domains/components/selects/contexts/server_select_context.dart'; -import 'package:mineral/domains/events/event.dart'; -import 'package:mineral/infrastructure/internals/container/ioc_container.dart'; -import 'package:mineral/infrastructure/internals/interactions/types/interaction_context_type.dart'; -import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; -import 'package:mineral/infrastructure/internals/packets/listenable_packet.dart'; -import 'package:mineral/infrastructure/internals/packets/packet_type.dart'; -import 'package:mineral/infrastructure/internals/wss/shard_message.dart'; -import 'package:mineral/infrastructure/services/logger/logger.dart'; - -final class InteractionCreatePacket implements ListenablePacket { - @override - PacketType get packetType => PacketType.interactionCreate; - - final LoggerContract logger; - final MarshallerContract marshaller; - - InteractionCreatePacket(this.logger, this.marshaller); - - @override - Future listen(ShardMessage message, DispatchEvent dispatch) async { - final interactionManager = ioc.resolve(); - final type = InteractionType.values.firstWhereOrNull((e) => e.value == message.payload['type']); - - final componentType = ComponentType.values - .firstWhereOrNull((e) => e.value == message.payload['data']['component_type']); - - return switch (type) { - InteractionType.applicationCommand => interactionManager.dispatcher.dispatch(message.payload), - InteractionType.messageComponent when componentType == ComponentType.button => - dispatchMessageComponent(message.payload, dispatch), - InteractionType.messageComponent when ComponentType.selectMenus.contains(componentType) => - dispatchSelectComponent(message.payload, dispatch), - InteractionType.modal => dispatchModalComponent(message.payload, dispatch), - _ => logger.warn('Interaction type ${message.payload['type']} not found') - }; - } - - Future dispatchMessageComponent( - Map payload, DispatchEvent dispatch) async { - final String? serverId = payload['guild']?['id']; - - final metadata = payload['message']['interaction_metadata']; - final type = ComponentType.values.firstWhereOrNull((e) => e.value == metadata['type']); - - if (type == null) { - logger.warn('Component type ${metadata['type']} not found'); - return; - } - - return switch (serverId) { - String() => _handleServerButton(payload, dispatch), - _ => _handlePrivateButton(payload, dispatch), - }; - } - - Future _handleServerButton(Map payload, DispatchEvent dispatch) async { - final message = await marshaller.dataStore.message.getServerMessage( - messageId: Snowflake(payload['message']['id']), - channelId: Snowflake(payload['channel_id'])); - - final metadata = payload['message']['interaction_metadata']; - final type = ButtonType.values.firstWhereOrNull((e) => e.value == metadata['type']); - - if (type == null) { - logger.warn('Button type ${metadata['type']} not found'); - return; - } - - final ctx = ServerButtonContext( - id: Snowflake(payload['id']), - applicationId: Snowflake(payload['application_id']), - version: payload['version'], - token: payload['token'], - customId: payload['data']['custom_id'], - message: message, - member: message.author, - ); - - dispatch(event: Event.serverButtonClick, params: [ctx]); - } - - Future _handlePrivateButton(Map payload, DispatchEvent dispatch) async { - final message = await marshaller.dataStore.message.getPrivateMessage( - messageId: Snowflake(payload['message']['id']), - channelId: Snowflake(payload['channel_id'])); - - final metadata = payload['message']['interaction_metadata']; - final type = ButtonType.values.firstWhereOrNull((e) => e.value == metadata['type']); - - if (type == null) { - logger.warn('Button type ${metadata['type']} not found'); - return; - } - - final ctx = PrivateButtonContext( - id: Snowflake(payload['id']), - applicationId: Snowflake(payload['application_id']), - version: payload['version'], - token: payload['token'], - customId: payload['data']['custom_id'], - message: message, - user: message.author, - ); - - dispatch(event: Event.serverButtonClick, params: [ctx]); - } - - Future dispatchModalComponent(Map payload, DispatchEvent dispatch) async { - final interactionContext = InteractionContextType.values - .firstWhereOrNull((element) => element.value == payload['context']); - - final Map parameters = List.from(payload['data']['components']).map((row) { - final component = row['components'][0]; - return {Symbol(component['custom_id']): component['value']}; - }).fold({}, (prev, curr) => {...prev, ...curr}); - - final event = switch (interactionContext) { - InteractionContextType.server => Event.serverDialogSubmit, - InteractionContextType.privateChannel => Event.privateDialogSubmit, - _ => null - }; - - final ctx = switch (interactionContext) { - InteractionContextType.server => ServerDialogContext( - customId: payload['data']['custom_id'], - id: Snowflake(payload['id']), - applicationId: Snowflake(payload['application_id']), - token: payload['token'], - version: payload['version'], - member: await marshaller.dataStore.member.getMember( - guildId: Snowflake(payload['guild_id']), - memberId: Snowflake(payload['member']['user']['id']), - ), - ), - InteractionContextType.privateChannel => PrivateDialogContext( - customId: payload['data']['custom_id'], - id: Snowflake(payload['id']), - applicationId: Snowflake(payload['application_id']), - token: payload['token'], - version: payload['version'], - user: await marshaller.serializers.user.serializeRemote(payload['user']), - ), - _ => null - }; - - if ([event, ctx].contains(null)) { - logger.warn('Interaction context ${payload['context']} not found'); - return; - } - - dispatch( - event: event!, - params: [ctx, parameters], - constraint: (String? customId) => customId == ctx!.customId); - } - - Future dispatchSelectComponent(Map payload, DispatchEvent dispatch) async { - final selectMenuType = - ComponentType.values.firstWhereOrNull((e) => e.value == payload['data']['component_type']); - - print(selectMenuType); - - switch (selectMenuType) { - case ComponentType.channelSelectMenu: - _dispatchSelectMenu(payload, dispatch); - default: - logger.warn('Select menu type $selectMenuType not found'); - } - } - - Future _dispatchSelectMenu(Map payload, DispatchEvent dispatch) async { - final resolvedData = payload['data']['resolved']; - final channelIds = Map.from(resolvedData['channels']).keys; - - final ctx = ServerSelectContext( - id: Snowflake(payload['id']), - applicationId: Snowflake(payload['application_id']), - token: payload['token'], - version: payload['version'], - customId: payload['data']['custom_id'], - message: await marshaller.dataStore.message.getServerMessage( - messageId: Snowflake(payload['message']['id']), - channelId: Snowflake(payload['channel_id']), - ), - member: await marshaller.dataStore.member.getMember( - guildId: Snowflake(payload['guild_id']), - memberId: Snowflake(payload['member']['user']['id']), - ), - ); - - final List data = await Future.wait(channelIds.map((id) async { - final cacheKey = marshaller.cacheKey.channel(Snowflake(id)); - final rawChannel = await marshaller.cache.getOrFail(cacheKey); - - return marshaller.serializers.channels.serializeCache(rawChannel) as Future; - })); - - dispatch( - event: Event.serverChannelSelect, - params: [ctx, data], - constraint: (String? customId) => customId == ctx.customId); - } -} diff --git a/lib/infrastructure/internals/packets/listeners/interactions/button_interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interactions/button_interaction_create_packet.dart new file mode 100644 index 000000000..d98cb04f8 --- /dev/null +++ b/lib/infrastructure/internals/packets/listeners/interactions/button_interaction_create_packet.dart @@ -0,0 +1,100 @@ +import 'package:collection/collection.dart'; +import 'package:mineral/api/common/components/buttons/button_type.dart'; +import 'package:mineral/api/common/components/component_type.dart'; +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/common/types/interaction_type.dart'; +import 'package:mineral/domains/components/buttons/contexts/private_button_context.dart'; +import 'package:mineral/domains/components/buttons/contexts/server_button_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; +import 'package:mineral/infrastructure/internals/packets/listenable_packet.dart'; +import 'package:mineral/infrastructure/internals/packets/packet_type.dart'; +import 'package:mineral/infrastructure/internals/wss/shard_message.dart'; +import 'package:mineral/infrastructure/services/logger/logger.dart'; + +final class ButtonInteractionCreatePacket implements ListenablePacket { + @override + PacketType get packetType => PacketType.interactionCreate; + + final LoggerContract logger; + final MarshallerContract marshaller; + + ButtonInteractionCreatePacket(this.logger, this.marshaller); + + @override + Future listen(ShardMessage message, DispatchEvent dispatch) async { + final type = InteractionType.values.firstWhereOrNull((e) => e.value == message.payload['type']); + + final componentType = ComponentType.values + .firstWhereOrNull((e) => e.value == message.payload['data']['component_type']); + + if (type == InteractionType.messageComponent && componentType == ComponentType.button) { + final String? serverId = message.payload['guild']?['id']; + + final metadata = message.payload['message']['interaction_metadata']; + final type = ComponentType.values.firstWhereOrNull((e) => e.value == metadata['type']); + + if (type == null) { + logger.warn('Component type ${metadata['type']} not found'); + return; + } + + return switch (serverId) { + String() => _handleServerButton(message.payload, dispatch), + _ => _handlePrivateButton(message.payload, dispatch), + }; + } + } + + Future _handleServerButton(Map payload, DispatchEvent dispatch) async { + final message = await marshaller.dataStore.message.getServerMessage( + messageId: Snowflake(payload['message']['id']), + channelId: Snowflake(payload['channel_id'])); + + final metadata = payload['message']['interaction_metadata']; + final type = ButtonType.values.firstWhereOrNull((e) => e.value == metadata['type']); + + if (type == null) { + logger.warn('Button type ${metadata['type']} not found'); + return; + } + + final ctx = ServerButtonContext( + id: Snowflake(payload['id']), + applicationId: Snowflake(payload['application_id']), + version: payload['version'], + token: payload['token'], + customId: payload['data']['custom_id'], + message: message, + member: message.author, + ); + + dispatch(event: Event.serverButtonClick, params: [ctx]); + } + + Future _handlePrivateButton(Map payload, DispatchEvent dispatch) async { + final message = await marshaller.dataStore.message.getPrivateMessage( + messageId: Snowflake(payload['message']['id']), + channelId: Snowflake(payload['channel_id'])); + + final metadata = payload['message']['interaction_metadata']; + final type = ButtonType.values.firstWhereOrNull((e) => e.value == metadata['type']); + + if (type == null) { + logger.warn('Button type ${metadata['type']} not found'); + return; + } + + final ctx = PrivateButtonContext( + id: Snowflake(payload['id']), + applicationId: Snowflake(payload['application_id']), + version: payload['version'], + token: payload['token'], + customId: payload['data']['custom_id'], + message: message, + user: message.author, + ); + + dispatch(event: Event.serverButtonClick, params: [ctx]); + } +} diff --git a/lib/infrastructure/internals/packets/listeners/interactions/command_interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interactions/command_interaction_create_packet.dart new file mode 100644 index 000000000..24e5716ac --- /dev/null +++ b/lib/infrastructure/internals/packets/listeners/interactions/command_interaction_create_packet.dart @@ -0,0 +1,29 @@ +import 'package:collection/collection.dart'; +import 'package:mineral/api/common/types/interaction_type.dart'; +import 'package:mineral/domains/commands/command_interaction_manager.dart'; +import 'package:mineral/infrastructure/internals/container/ioc_container.dart'; +import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; +import 'package:mineral/infrastructure/internals/packets/listenable_packet.dart'; +import 'package:mineral/infrastructure/internals/packets/packet_type.dart'; +import 'package:mineral/infrastructure/internals/wss/shard_message.dart'; +import 'package:mineral/infrastructure/services/logger/logger.dart'; + +final class CommandInteractionCreatePacket implements ListenablePacket { + @override + PacketType get packetType => PacketType.interactionCreate; + + final LoggerContract logger; + final MarshallerContract marshaller; + + CommandInteractionCreatePacket(this.logger, this.marshaller); + + @override + Future listen(ShardMessage message, DispatchEvent dispatch) async { + final interactionManager = ioc.resolve(); + final type = InteractionType.values.firstWhereOrNull((e) => e.value == message.payload['type']); + + if (type == InteractionType.applicationCommand) { + await interactionManager.dispatcher.dispatch(message.payload); + } + } +} diff --git a/lib/infrastructure/internals/packets/listeners/interactions/dialog_interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interactions/dialog_interaction_create_packet.dart new file mode 100644 index 000000000..e8a3660fb --- /dev/null +++ b/lib/infrastructure/internals/packets/listeners/interactions/dialog_interaction_create_packet.dart @@ -0,0 +1,77 @@ +import 'package:collection/collection.dart'; +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/common/types/interaction_type.dart'; +import 'package:mineral/domains/components/dialog/contexts/private_dialog_context.dart'; +import 'package:mineral/domains/components/dialog/contexts/server_dialog_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/infrastructure/internals/interactions/types/interaction_context_type.dart'; +import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; +import 'package:mineral/infrastructure/internals/packets/listenable_packet.dart'; +import 'package:mineral/infrastructure/internals/packets/packet_type.dart'; +import 'package:mineral/infrastructure/internals/wss/shard_message.dart'; +import 'package:mineral/infrastructure/services/logger/logger.dart'; + +final class DialogInteractionCreatePacket implements ListenablePacket { + @override + PacketType get packetType => PacketType.interactionCreate; + + final LoggerContract logger; + final MarshallerContract marshaller; + + DialogInteractionCreatePacket(this.logger, this.marshaller); + + @override + Future listen(ShardMessage message, DispatchEvent dispatch) async { + final type = InteractionType.values.firstWhereOrNull((e) => e.value == message.payload['type']); + + if (type == InteractionType.modal) { + final interactionContext = InteractionContextType.values + .firstWhereOrNull((element) => element.value == message.payload['context']); + + final Map parameters = + List.from(message.payload['data']['components']).map((row) { + final component = row['components'][0]; + return {Symbol(component['custom_id']): component['value']}; + }).fold({}, (prev, curr) => {...prev, ...curr}); + + final event = switch (interactionContext) { + InteractionContextType.server => Event.serverDialogSubmit, + InteractionContextType.privateChannel => Event.privateDialogSubmit, + _ => null + }; + + final ctx = switch (interactionContext) { + InteractionContextType.server => ServerDialogContext( + customId: message.payload['data']['custom_id'], + id: Snowflake(message.payload['id']), + applicationId: Snowflake(message.payload['application_id']), + token: message.payload['token'], + version: message.payload['version'], + member: await marshaller.dataStore.member.getMember( + guildId: Snowflake(message.payload['guild_id']), + memberId: Snowflake(message.payload['member']['user']['id']), + ), + ), + InteractionContextType.privateChannel => PrivateDialogContext( + customId: message.payload['data']['custom_id'], + id: Snowflake(message.payload['id']), + applicationId: Snowflake(message.payload['application_id']), + token: message.payload['token'], + version: message.payload['version'], + user: await marshaller.serializers.user.serializeRemote(message.payload['user']), + ), + _ => null + }; + + if ([event, ctx].contains(null)) { + logger.warn('Interaction context ${message.payload['context']} not found'); + return; + } + + dispatch( + event: event!, + params: [ctx, parameters], + constraint: (String? customId) => customId == ctx!.customId); + } + } +} diff --git a/lib/infrastructure/internals/packets/listeners/interactions/interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interactions/interaction_create_packet.dart new file mode 100644 index 000000000..8353cc0ee --- /dev/null +++ b/lib/infrastructure/internals/packets/listeners/interactions/interaction_create_packet.dart @@ -0,0 +1,20 @@ +import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; +import 'package:mineral/infrastructure/internals/packets/listenable_packet.dart'; +import 'package:mineral/infrastructure/internals/packets/packet_type.dart'; +import 'package:mineral/infrastructure/internals/wss/shard_message.dart'; +import 'package:mineral/infrastructure/services/logger/logger.dart'; + +final class InteractionCreatePacket implements ListenablePacket { + @override + PacketType get packetType => PacketType.interactionCreate; + + final LoggerContract logger; + final MarshallerContract marshaller; + + InteractionCreatePacket(this.logger, this.marshaller); + + @override + Future listen(ShardMessage message, DispatchEvent dispatch) async { + // TODO: Implement global interaction handling + } +} diff --git a/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart new file mode 100644 index 000000000..e0fac17dc --- /dev/null +++ b/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart @@ -0,0 +1,102 @@ +import 'package:collection/collection.dart'; +import 'package:mineral/api/common/channel.dart'; +import 'package:mineral/api/common/components/component_type.dart'; +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/common/types/interaction_type.dart'; +import 'package:mineral/api/server/channels/server_channel.dart'; +import 'package:mineral/domains/components/selects/button_context.dart'; +import 'package:mineral/domains/components/selects/contexts/private_select_context.dart'; +import 'package:mineral/domains/components/selects/contexts/server_select_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; +import 'package:mineral/infrastructure/internals/packets/listenable_packet.dart'; +import 'package:mineral/infrastructure/internals/packets/packet_type.dart'; +import 'package:mineral/infrastructure/internals/wss/shard_message.dart'; +import 'package:mineral/infrastructure/services/logger/logger.dart'; + +final class SelectInteractionCreatePacket implements ListenablePacket { + @override + PacketType get packetType => PacketType.interactionCreate; + + final LoggerContract logger; + final MarshallerContract marshaller; + + SelectInteractionCreatePacket(this.logger, this.marshaller); + + @override + Future listen(ShardMessage message, DispatchEvent dispatch) async { + final type = InteractionType.values.firstWhereOrNull((e) => e.value == message.payload['type']); + + final componentType = ComponentType.values + .firstWhereOrNull((e) => e.value == message.payload['data']['component_type']); + + if (type == InteractionType.messageComponent && + ComponentType.selectMenus.contains(componentType)) { + final selectMenuType = ComponentType.values + .firstWhereOrNull((e) => e.value == message.payload['data']['component_type']); + + final ctx = switch (message.payload['guild_id']) { + String() => ServerSelectContext( + id: Snowflake(message.payload['id']), + applicationId: Snowflake(message.payload['application_id']), + token: message.payload['token'], + version: message.payload['version'], + customId: message.payload['data']['custom_id'], + message: await marshaller.dataStore.message.getServerMessage( + messageId: Snowflake(message.payload['message']['id']), + channelId: Snowflake(message.payload['channel_id']), + ), + member: await marshaller.dataStore.member.getMember( + guildId: Snowflake(message.payload['guild_id']), + memberId: Snowflake(message.payload['member']['user']['id']), + ), + ), + _ => PrivateSelectContext( + id: Snowflake(message.payload['id']), + applicationId: Snowflake(message.payload['application_id']), + token: message.payload['token'], + version: message.payload['version'], + customId: message.payload['data']['custom_id'], + message: await marshaller.dataStore.message.getPrivateMessage( + messageId: Snowflake(message.payload['message']['id']), + channelId: Snowflake(message.payload['channel_id']), + ), + user: await marshaller.serializers.user.serializeRemote(message.payload['user'])), + }; + + switch (selectMenuType) { + case ComponentType.channelSelectMenu: + _dispatchChannelSelectMenu(ctx, message.payload, dispatch); + default: + logger.warn('Select menu type $selectMenuType not found'); + } + } + } + + Future _dispatchChannelSelectMenu( + SelectContext ctx, Map payload, DispatchEvent dispatch) async { + final resolvedData = payload['data']['resolved']; + final channelIds = Map.from(resolvedData['channels']).keys; + + Future> resolveChannels() { + return Future.wait(channelIds.map((id) async { + final cacheKey = marshaller.cacheKey.channel(Snowflake(id)); + final rawChannel = await marshaller.cache.getOrFail(cacheKey); + + return marshaller.serializers.channels.serializeCache(rawChannel) as Future; + })); + } + + return switch (ctx) { + ServerSelectContext() => dispatch( + event: Event.serverChannelSelect, + params: [ctx, await resolveChannels()], + constraint: (String? customId) => customId == ctx.customId), + PrivateSelectContext() => dispatch( + event: Event.serverChannelSelect, + params: [ctx, await resolveChannels()], + constraint: (String? customId) => customId == ctx.customId), + _ => logger.warn('Select context $ctx not found'), + }; + } +} diff --git a/lib/infrastructure/internals/packets/packet_listener.dart b/lib/infrastructure/internals/packets/packet_listener.dart index 57c9bdf96..cb1d75f19 100644 --- a/lib/infrastructure/internals/packets/packet_listener.dart +++ b/lib/infrastructure/internals/packets/packet_listener.dart @@ -18,7 +18,11 @@ import 'package:mineral/infrastructure/internals/packets/listeners/guild_role_de import 'package:mineral/infrastructure/internals/packets/listeners/guild_role_update_packet.dart'; import 'package:mineral/infrastructure/internals/packets/listeners/guild_stickers_update_packet.dart'; import 'package:mineral/infrastructure/internals/packets/listeners/guild_update_packet.dart'; -import 'package:mineral/infrastructure/internals/packets/listeners/interaction_create_packet.dart'; +import 'package:mineral/infrastructure/internals/packets/listeners/interactions/button_interaction_create_packet.dart'; +import 'package:mineral/infrastructure/internals/packets/listeners/interactions/command_interaction_create_packet.dart'; +import 'package:mineral/infrastructure/internals/packets/listeners/interactions/dialog_interaction_create_packet.dart'; +import 'package:mineral/infrastructure/internals/packets/listeners/interactions/interaction_create_packet.dart'; +import 'package:mineral/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart'; import 'package:mineral/infrastructure/internals/packets/listeners/message_create_packet.dart'; import 'package:mineral/infrastructure/internals/packets/listeners/presence_update_packet.dart'; import 'package:mineral/infrastructure/internals/packets/listeners/ready_packet.dart'; @@ -72,6 +76,11 @@ final class PacketListener implements PacketListenerContract { subscribe(GuildBanRemovePacket.new); subscribe(GuildEmojisUpdatePacket.new); subscribe(GuildStickersUpdatePacket.new); + + subscribe(ButtonInteractionCreatePacket.new); subscribe(InteractionCreatePacket.new); + subscribe(CommandInteractionCreatePacket.new); + subscribe(SelectInteractionCreatePacket.new); + subscribe(DialogInteractionCreatePacket.new); } } From 7f737e6a475529466a6ffbabfd47f7e970131ec7 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Sun, 11 Aug 2024 23:29:44 +0200 Subject: [PATCH 12/19] feat(interaction): implement server role select handler --- lib/domains/events/buckets/server_bucket.dart | 4 ++++ .../server/server_role_select_event.dart | 18 ++++++++++++++ lib/domains/events/event.dart | 2 ++ .../select_interaction_create_packet.dart | 24 +++++++++++++++++++ lib/infrastructure/kernel/mineral_client.dart | 2 ++ 5 files changed, 50 insertions(+) create mode 100644 lib/domains/events/contracts/server/server_role_select_event.dart diff --git a/lib/domains/events/buckets/server_bucket.dart b/lib/domains/events/buckets/server_bucket.dart index f43e78818..204a81b56 100644 --- a/lib/domains/events/buckets/server_bucket.dart +++ b/lib/domains/events/buckets/server_bucket.dart @@ -15,6 +15,7 @@ import 'package:mineral/domains/events/contracts/server/server_member_remove_eve import 'package:mineral/domains/events/contracts/server/server_member_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_message_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_presence_update_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_role_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_roles_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_roles_remove_event.dart'; import 'package:mineral/domains/events/contracts/server/server_roles_update_event.dart'; @@ -87,4 +88,7 @@ final class ServerBucket { void selectChannel(ServerChannelSelectEventHandler handle, {String? customId}) => _events.make(Event.serverChannelSelect, handle, customId: customId); + + void selectRole(ServerRoleSelectEventHandler handle, {String? customId}) => + _events.make(Event.serverRoleSelect, handle, customId: customId); } diff --git a/lib/domains/events/contracts/server/server_role_select_event.dart b/lib/domains/events/contracts/server/server_role_select_event.dart new file mode 100644 index 000000000..70c673b90 --- /dev/null +++ b/lib/domains/events/contracts/server/server_role_select_event.dart @@ -0,0 +1,18 @@ +import 'dart:async'; + +import 'package:mineral/api/server/channels/server_channel.dart'; +import 'package:mineral/api/server/role.dart'; +import 'package:mineral/domains/components/selects/contexts/server_select_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/domains/events/types/listenable_event.dart'; + +typedef ServerRoleSelectEventHandler = FutureOr Function(ServerSelectContext, List); + +abstract class ServerRoleSelectEvent implements ListenableEvent { + @override + Event get event => Event.serverRoleSelect; + + String? get customId; + + FutureOr handle(ServerSelectContext ctx, List roles); +} diff --git a/lib/domains/events/event.dart b/lib/domains/events/event.dart index 9a7818ed8..cfb0e8613 100644 --- a/lib/domains/events/event.dart +++ b/lib/domains/events/event.dart @@ -24,6 +24,7 @@ import 'package:mineral/domains/events/contracts/server/server_member_remove_eve import 'package:mineral/domains/events/contracts/server/server_member_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_message_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_presence_update_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_role_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_roles_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_roles_remove_event.dart'; import 'package:mineral/domains/events/contracts/server/server_roles_update_event.dart'; @@ -57,6 +58,7 @@ enum Event implements EnhancedEnum, EventType { serverButtonClick(ServerButtonClickEvent), serverDialogSubmit(ServerDialogSubmitEvent), serverChannelSelect(ServerChannelSelectEvent), + serverRoleSelect(ServerRoleSelectEvent), // private privateMessageCreate(PrivateMessageCreateEvent), diff --git a/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart index e0fac17dc..95872a1d7 100644 --- a/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart @@ -1,9 +1,12 @@ +import 'dart:convert'; + import 'package:collection/collection.dart'; import 'package:mineral/api/common/channel.dart'; import 'package:mineral/api/common/components/component_type.dart'; import 'package:mineral/api/common/snowflake.dart'; import 'package:mineral/api/common/types/interaction_type.dart'; import 'package:mineral/api/server/channels/server_channel.dart'; +import 'package:mineral/api/server/role.dart'; import 'package:mineral/domains/components/selects/button_context.dart'; import 'package:mineral/domains/components/selects/contexts/private_select_context.dart'; import 'package:mineral/domains/components/selects/contexts/server_select_context.dart'; @@ -67,6 +70,8 @@ final class SelectInteractionCreatePacket implements ListenablePacket { switch (selectMenuType) { case ComponentType.channelSelectMenu: _dispatchChannelSelectMenu(ctx, message.payload, dispatch); + case ComponentType.roleSelectMenu: + _dispatchRoleSelectMenu(ctx, message.payload, dispatch); default: logger.warn('Select menu type $selectMenuType not found'); } @@ -99,4 +104,23 @@ final class SelectInteractionCreatePacket implements ListenablePacket { _ => logger.warn('Select context $ctx not found'), }; } + + Future _dispatchRoleSelectMenu( + SelectContext ctx, Map payload, DispatchEvent dispatch) async { + final resolvedData = payload['data']['resolved']; + final roleIds = Map.from(resolvedData['roles']).keys; + + final List resolvedRoles = await Future.wait(roleIds.map((id) async { + final cacheKey = + marshaller.cacheKey.serverRole(serverId: payload['guild_id'], roleId: Snowflake(id)); + final rawRole = await marshaller.cache.getOrFail(cacheKey); + + return marshaller.serializers.role.serializeCache(rawRole); + })); + + dispatch( + event: Event.serverRoleSelect, + params: [ctx, resolvedRoles], + constraint: (String? customId) => customId == ctx.customId); + } } diff --git a/lib/infrastructure/kernel/mineral_client.dart b/lib/infrastructure/kernel/mineral_client.dart index 02136e9a8..1b6760f2c 100644 --- a/lib/infrastructure/kernel/mineral_client.dart +++ b/lib/infrastructure/kernel/mineral_client.dart @@ -2,6 +2,7 @@ import 'package:mineral/api/common/commands/command_contract.dart'; import 'package:mineral/domains/commands/command_declaration_bucket.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_dialog_submit_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_role_select_event.dart'; import 'package:mineral/domains/events/event_bucket.dart'; import 'package:mineral/domains/events/types/listenable_event.dart'; import 'package:mineral/infrastructure/commons/listenable.dart'; @@ -51,6 +52,7 @@ final class MineralClient implements MineralClientContract { customId: switch(instance) { final ServerDialogSubmitEvent instance => instance.customId, final ServerChannelSelectEvent instance => instance.customId, + final ServerRoleSelectEvent instance => instance.customId, _ => null } ); From 96ab20e8d47e780a83810a7c2a1cca676e5a3843 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Sun, 11 Aug 2024 23:55:58 +0200 Subject: [PATCH 13/19] feat(interaction): implement server member/user select handler --- .../events/buckets/private_bucket.dart | 4 ++ lib/domains/events/buckets/server_bucket.dart | 4 ++ .../private/private_user_select_event.dart | 18 +++++++++ .../server/server_member_select_event.dart | 17 +++++++++ lib/domains/events/event.dart | 6 ++- .../internals/datastore/data_store.dart | 7 ++++ .../internals/datastore/parts/user_part.dart | 36 ++++++++++++++++++ .../select_interaction_create_packet.dart | 38 ++++++++++++++++++- 8 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 lib/domains/events/contracts/private/private_user_select_event.dart create mode 100644 lib/domains/events/contracts/server/server_member_select_event.dart create mode 100644 lib/infrastructure/internals/datastore/parts/user_part.dart diff --git a/lib/domains/events/buckets/private_bucket.dart b/lib/domains/events/buckets/private_bucket.dart index 442c22cfc..70fc84915 100644 --- a/lib/domains/events/buckets/private_bucket.dart +++ b/lib/domains/events/buckets/private_bucket.dart @@ -3,6 +3,7 @@ import 'package:mineral/domains/events/contracts/private/private_channel_create_ import 'package:mineral/domains/events/contracts/private/private_channel_pins_update_event.dart'; import 'package:mineral/domains/events/contracts/private/private_dialog_submit_event.dart'; import 'package:mineral/domains/events/contracts/private/private_message_create_event.dart'; +import 'package:mineral/domains/events/contracts/private/private_user_select_event.dart'; import 'package:mineral/domains/events/event.dart'; import 'package:mineral/domains/events/event_bucket.dart'; @@ -25,4 +26,7 @@ final class PrivateBucket { void dialogSubmit(PrivateDialogSubmitEventHandler handle, {String? customId}) => _events.make(Event.privateDialogSubmit, handle, customId: customId); + + void selectUser(PrivateUserSelectEventHandler handle, {String? customId}) => + _events.make(Event.privateUserSelect, handle, customId: customId); } diff --git a/lib/domains/events/buckets/server_bucket.dart b/lib/domains/events/buckets/server_bucket.dart index 204a81b56..4b8d04da9 100644 --- a/lib/domains/events/buckets/server_bucket.dart +++ b/lib/domains/events/buckets/server_bucket.dart @@ -12,6 +12,7 @@ import 'package:mineral/domains/events/contracts/server/server_dialog_submit_eve import 'package:mineral/domains/events/contracts/server/server_emojis_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_member_add_event.dart'; import 'package:mineral/domains/events/contracts/server/server_member_remove_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_member_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_member_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_message_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_presence_update_event.dart'; @@ -91,4 +92,7 @@ final class ServerBucket { void selectRole(ServerRoleSelectEventHandler handle, {String? customId}) => _events.make(Event.serverRoleSelect, handle, customId: customId); + + void selectMember(ServerMemberSelectEventHandler handle, {String? customId}) => + _events.make(Event.serverMemberSelect, handle, customId: customId); } diff --git a/lib/domains/events/contracts/private/private_user_select_event.dart b/lib/domains/events/contracts/private/private_user_select_event.dart new file mode 100644 index 000000000..ccbe4955e --- /dev/null +++ b/lib/domains/events/contracts/private/private_user_select_event.dart @@ -0,0 +1,18 @@ +import 'dart:async'; + +import 'package:mineral/api/private/user.dart'; +import 'package:mineral/domains/components/selects/contexts/private_select_context.dart'; +import 'package:mineral/domains/components/selects/contexts/server_select_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/domains/events/types/listenable_event.dart'; + +typedef PrivateUserSelectEventHandler = FutureOr Function(ServerSelectContext, List); + +abstract class PrivateUserSelectEvent implements ListenableEvent { + @override + Event get event => Event.privateUserSelect; + + String? get customId; + + FutureOr handle(PrivateSelectContext ctx, List roles); +} diff --git a/lib/domains/events/contracts/server/server_member_select_event.dart b/lib/domains/events/contracts/server/server_member_select_event.dart new file mode 100644 index 000000000..b115e345c --- /dev/null +++ b/lib/domains/events/contracts/server/server_member_select_event.dart @@ -0,0 +1,17 @@ +import 'dart:async'; + +import 'package:mineral/api/server/member.dart'; +import 'package:mineral/domains/components/selects/contexts/server_select_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/domains/events/types/listenable_event.dart'; + +typedef ServerMemberSelectEventHandler = FutureOr Function(ServerSelectContext, List); + +abstract class ServerMemberSelectEvent implements ListenableEvent { + @override + Event get event => Event.serverMemberSelect; + + String? get customId; + + FutureOr handle(ServerSelectContext ctx, List roles); +} diff --git a/lib/domains/events/event.dart b/lib/domains/events/event.dart index cfb0e8613..a0dab61a8 100644 --- a/lib/domains/events/event.dart +++ b/lib/domains/events/event.dart @@ -7,6 +7,7 @@ import 'package:mineral/domains/events/contracts/private/private_channel_pins_up import 'package:mineral/domains/events/contracts/private/private_channel_update_event.dart'; import 'package:mineral/domains/events/contracts/private/private_dialog_submit_event.dart'; import 'package:mineral/domains/events/contracts/private/private_message_create_event.dart'; +import 'package:mineral/domains/events/contracts/private/private_user_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_ban_add_event.dart'; import 'package:mineral/domains/events/contracts/server/server_ban_remove_event.dart'; import 'package:mineral/domains/events/contracts/server/server_button_click_event.dart'; @@ -21,6 +22,7 @@ import 'package:mineral/domains/events/contracts/server/server_dialog_submit_eve import 'package:mineral/domains/events/contracts/server/server_emojis_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_member_add_event.dart'; import 'package:mineral/domains/events/contracts/server/server_member_remove_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_member_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_member_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_message_create_event.dart'; import 'package:mineral/domains/events/contracts/server/server_presence_update_event.dart'; @@ -59,6 +61,7 @@ enum Event implements EnhancedEnum, EventType { serverDialogSubmit(ServerDialogSubmitEvent), serverChannelSelect(ServerChannelSelectEvent), serverRoleSelect(ServerRoleSelectEvent), + serverMemberSelect(ServerMemberSelectEvent), // private privateMessageCreate(PrivateMessageCreateEvent), @@ -66,7 +69,8 @@ enum Event implements EnhancedEnum, EventType { privateChannelUpdate(PrivateChannelUpdateEvent), privateChannelDelete(PrivateChannelDeleteEvent), privateButtonClick(PrivateButtonClickEvent), - privateDialogSubmit(PrivateDialogSubmitEvent); + privateDialogSubmit(PrivateDialogSubmitEvent), + privateUserSelect(PrivateUserSelectEvent); @override final Type value; diff --git a/lib/infrastructure/internals/datastore/data_store.dart b/lib/infrastructure/internals/datastore/data_store.dart index 113a5562b..6b77b0f74 100644 --- a/lib/infrastructure/internals/datastore/data_store.dart +++ b/lib/infrastructure/internals/datastore/data_store.dart @@ -4,6 +4,7 @@ import 'package:mineral/infrastructure/internals/datastore/parts/member_part.dar import 'package:mineral/infrastructure/internals/datastore/parts/message_part.dart'; import 'package:mineral/infrastructure/internals/datastore/parts/role_part.dart'; import 'package:mineral/infrastructure/internals/datastore/parts/server_part.dart'; +import 'package:mineral/infrastructure/internals/datastore/parts/user_part.dart'; import 'package:mineral/infrastructure/kernel/kernel.dart'; import 'package:mineral/infrastructure/services/http/http_client.dart'; @@ -16,6 +17,8 @@ abstract class DataStoreContract { MemberPart get member; + UserPart get user; + RolePart get role; MessagePart get message; @@ -38,6 +41,9 @@ final class DataStore implements DataStoreContract { @override late final MemberPart member; + @override + late final UserPart user; + @override late final RolePart role; @@ -53,6 +59,7 @@ final class DataStore implements DataStoreContract { channel = ChannelPart(kernel); server = ServerPart(kernel); member = MemberPart(kernel); + user = UserPart(kernel); role = RolePart(kernel); message = MessagePart(kernel); interaction = InteractionPart(kernel); diff --git a/lib/infrastructure/internals/datastore/parts/user_part.dart b/lib/infrastructure/internals/datastore/parts/user_part.dart new file mode 100644 index 000000000..a3863e857 --- /dev/null +++ b/lib/infrastructure/internals/datastore/parts/user_part.dart @@ -0,0 +1,36 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:mineral/api/common/snowflake.dart'; +import 'package:mineral/api/private/user.dart'; +import 'package:mineral/infrastructure/internals/datastore/data_store_part.dart'; +import 'package:mineral/infrastructure/kernel/kernel.dart'; +import 'package:mineral/infrastructure/services/http/http_client_status.dart'; + +final class UserPart implements DataStorePart { + final KernelContract _kernel; + + HttpClientStatus get status => _kernel.dataStore.client.status; + + UserPart(this._kernel); + + Future getUser(Snowflake userId) async { + final cachedRawUser = await _kernel.marshaller.cache.get(userId.value); + if (cachedRawUser != null) { + return _kernel.marshaller.serializers.user.serializeCache(cachedRawUser); + } + + final response = await _kernel.dataStore.client.get('/users/$userId'); + final user = await switch (response.statusCode) { + int() when status.isSuccess(response.statusCode) => + _kernel.marshaller.serializers.user.serializeRemote(response.body), + int() when status.isError(response.statusCode) => throw HttpException(response.body), + _ => throw Exception('Unknown status code: ${response.statusCode}'), + }; + + final rawUser = await _kernel.marshaller.serializers.user.deserialize(user); + await _kernel.marshaller.cache.put(userId.value, rawUser); + + return user; + } +} diff --git a/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart index 95872a1d7..b0e6d4c2f 100644 --- a/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:collection/collection.dart'; import 'package:mineral/api/common/channel.dart'; import 'package:mineral/api/common/components/component_type.dart'; @@ -72,6 +70,8 @@ final class SelectInteractionCreatePacket implements ListenablePacket { _dispatchChannelSelectMenu(ctx, message.payload, dispatch); case ComponentType.roleSelectMenu: _dispatchRoleSelectMenu(ctx, message.payload, dispatch); + case ComponentType.userSelectMenu: + _dispatchUserSelectMenu(ctx, message.payload, dispatch); default: logger.warn('Select menu type $selectMenuType not found'); } @@ -123,4 +123,38 @@ final class SelectInteractionCreatePacket implements ListenablePacket { params: [ctx, resolvedRoles], constraint: (String? customId) => customId == ctx.customId); } + + Future _dispatchUserSelectMenu( + SelectContext ctx, Map payload, DispatchEvent dispatch) async { + final resolvedData = payload['data']['resolved']; + final userIds = Map.from(resolvedData['users']).keys; + + final event = switch (ctx) { + ServerSelectContext() => Event.serverMemberSelect, + PrivateSelectContext() => Event.privateUserSelect, + _ => null, + }; + + if (event == null) { + logger.warn('Select context $ctx not found'); + return; + } + + final resolvedResource = await switch (ctx) { + ServerSelectContext() => Future.wait(userIds.map((id) { + return marshaller.dataStore.member.getMember( + guildId: Snowflake(payload['guild_id']), + memberId: Snowflake(id), + ); + })), + PrivateSelectContext() => + Future.wait(userIds.map((id) => marshaller.dataStore.user.getUser(id))), + _ => Future.value([]), + }; + + dispatch( + event: event, + params: [ctx, resolvedResource], + constraint: (String? customId) => customId == ctx.customId); + } } From b1bb6372a70f4f9752074ac01a941abec853bfc2 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Mon, 12 Aug 2024 00:12:04 +0200 Subject: [PATCH 14/19] feat(interaction): implement text select handler --- .../events/buckets/private_bucket.dart | 4 ++++ lib/domains/events/buckets/server_bucket.dart | 4 ++++ .../private/private_dialog_submit_event.dart | 2 ++ .../private/private_message_create_event.dart | 2 ++ .../private/private_text_select_event.dart | 16 ++++++++++++++++ .../private/private_user_select_event.dart | 3 +-- .../server/server_member_select_event.dart | 2 +- .../server/server_text_select_event.dart | 16 ++++++++++++++++ lib/domains/events/event.dart | 6 +++++- .../select_interaction_create_packet.dart | 19 +++++++++++++++++++ lib/infrastructure/kernel/mineral_client.dart | 10 ++++++++++ 11 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 lib/domains/events/contracts/private/private_text_select_event.dart create mode 100644 lib/domains/events/contracts/server/server_text_select_event.dart diff --git a/lib/domains/events/buckets/private_bucket.dart b/lib/domains/events/buckets/private_bucket.dart index 70fc84915..d15ba6496 100644 --- a/lib/domains/events/buckets/private_bucket.dart +++ b/lib/domains/events/buckets/private_bucket.dart @@ -3,6 +3,7 @@ import 'package:mineral/domains/events/contracts/private/private_channel_create_ import 'package:mineral/domains/events/contracts/private/private_channel_pins_update_event.dart'; import 'package:mineral/domains/events/contracts/private/private_dialog_submit_event.dart'; import 'package:mineral/domains/events/contracts/private/private_message_create_event.dart'; +import 'package:mineral/domains/events/contracts/private/private_text_select_event.dart'; import 'package:mineral/domains/events/contracts/private/private_user_select_event.dart'; import 'package:mineral/domains/events/event.dart'; import 'package:mineral/domains/events/event_bucket.dart'; @@ -29,4 +30,7 @@ final class PrivateBucket { void selectUser(PrivateUserSelectEventHandler handle, {String? customId}) => _events.make(Event.privateUserSelect, handle, customId: customId); + + void selectText(PrivateTextSelectEventHandler handle, {String? customId}) => + _events.make(Event.privateTextSelect, handle, customId: customId); } diff --git a/lib/domains/events/buckets/server_bucket.dart b/lib/domains/events/buckets/server_bucket.dart index 4b8d04da9..a52cffbbb 100644 --- a/lib/domains/events/buckets/server_bucket.dart +++ b/lib/domains/events/buckets/server_bucket.dart @@ -21,6 +21,7 @@ import 'package:mineral/domains/events/contracts/server/server_roles_create_even import 'package:mineral/domains/events/contracts/server/server_roles_remove_event.dart'; import 'package:mineral/domains/events/contracts/server/server_roles_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_stickers_update_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_text_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_update_event.dart'; import 'package:mineral/domains/events/event.dart'; import 'package:mineral/domains/events/event_bucket.dart'; @@ -95,4 +96,7 @@ final class ServerBucket { void selectMember(ServerMemberSelectEventHandler handle, {String? customId}) => _events.make(Event.serverMemberSelect, handle, customId: customId); + + void selectText(ServerTextSelectEventHandler handle, {String? customId}) => + _events.make(Event.serverTextSelect, handle, customId: customId); } diff --git a/lib/domains/events/contracts/private/private_dialog_submit_event.dart b/lib/domains/events/contracts/private/private_dialog_submit_event.dart index d35201dd9..c5e97a252 100644 --- a/lib/domains/events/contracts/private/private_dialog_submit_event.dart +++ b/lib/domains/events/contracts/private/private_dialog_submit_event.dart @@ -10,5 +10,7 @@ abstract class PrivateDialogSubmitEvent implements ListenableEvent { @override Event get event => Event.privateDialogSubmit; + String? get customId; + FutureOr handle(PrivateDialogContext ctx); } diff --git a/lib/domains/events/contracts/private/private_message_create_event.dart b/lib/domains/events/contracts/private/private_message_create_event.dart index 2dde744b2..82f8a98e5 100644 --- a/lib/domains/events/contracts/private/private_message_create_event.dart +++ b/lib/domains/events/contracts/private/private_message_create_event.dart @@ -10,5 +10,7 @@ abstract class PrivateMessageCreateEvent implements ListenableEvent { @override Event get event => Event.privateMessageCreate; + String? get customId; + FutureOr handle(PrivateMessage message); } diff --git a/lib/domains/events/contracts/private/private_text_select_event.dart b/lib/domains/events/contracts/private/private_text_select_event.dart new file mode 100644 index 000000000..e8684166f --- /dev/null +++ b/lib/domains/events/contracts/private/private_text_select_event.dart @@ -0,0 +1,16 @@ +import 'dart:async'; + +import 'package:mineral/domains/components/selects/contexts/private_select_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/domains/events/types/listenable_event.dart'; + +typedef PrivateTextSelectEventHandler = FutureOr Function(PrivateSelectContext, List); + +abstract class PrivateTextSelectEvent implements ListenableEvent { + @override + Event get event => Event.serverTextSelect; + + String? get customId; + + FutureOr handle(PrivateSelectContext ctx, List values); +} diff --git a/lib/domains/events/contracts/private/private_user_select_event.dart b/lib/domains/events/contracts/private/private_user_select_event.dart index ccbe4955e..a42d84936 100644 --- a/lib/domains/events/contracts/private/private_user_select_event.dart +++ b/lib/domains/events/contracts/private/private_user_select_event.dart @@ -2,11 +2,10 @@ import 'dart:async'; import 'package:mineral/api/private/user.dart'; import 'package:mineral/domains/components/selects/contexts/private_select_context.dart'; -import 'package:mineral/domains/components/selects/contexts/server_select_context.dart'; import 'package:mineral/domains/events/event.dart'; import 'package:mineral/domains/events/types/listenable_event.dart'; -typedef PrivateUserSelectEventHandler = FutureOr Function(ServerSelectContext, List); +typedef PrivateUserSelectEventHandler = FutureOr Function(PrivateSelectContext, List); abstract class PrivateUserSelectEvent implements ListenableEvent { @override diff --git a/lib/domains/events/contracts/server/server_member_select_event.dart b/lib/domains/events/contracts/server/server_member_select_event.dart index b115e345c..1b136d8d0 100644 --- a/lib/domains/events/contracts/server/server_member_select_event.dart +++ b/lib/domains/events/contracts/server/server_member_select_event.dart @@ -13,5 +13,5 @@ abstract class ServerMemberSelectEvent implements ListenableEvent { String? get customId; - FutureOr handle(ServerSelectContext ctx, List roles); + FutureOr handle(ServerSelectContext ctx, List members); } diff --git a/lib/domains/events/contracts/server/server_text_select_event.dart b/lib/domains/events/contracts/server/server_text_select_event.dart new file mode 100644 index 000000000..6aff90624 --- /dev/null +++ b/lib/domains/events/contracts/server/server_text_select_event.dart @@ -0,0 +1,16 @@ +import 'dart:async'; + +import 'package:mineral/domains/components/selects/contexts/server_select_context.dart'; +import 'package:mineral/domains/events/event.dart'; +import 'package:mineral/domains/events/types/listenable_event.dart'; + +typedef ServerTextSelectEventHandler = FutureOr Function(ServerSelectContext, List); + +abstract class ServerTextSelectEvent implements ListenableEvent { + @override + Event get event => Event.serverTextSelect; + + String? get customId; + + FutureOr handle(ServerSelectContext ctx, List values); +} diff --git a/lib/domains/events/event.dart b/lib/domains/events/event.dart index a0dab61a8..0f61d6aaa 100644 --- a/lib/domains/events/event.dart +++ b/lib/domains/events/event.dart @@ -7,6 +7,7 @@ import 'package:mineral/domains/events/contracts/private/private_channel_pins_up import 'package:mineral/domains/events/contracts/private/private_channel_update_event.dart'; import 'package:mineral/domains/events/contracts/private/private_dialog_submit_event.dart'; import 'package:mineral/domains/events/contracts/private/private_message_create_event.dart'; +import 'package:mineral/domains/events/contracts/private/private_text_select_event.dart'; import 'package:mineral/domains/events/contracts/private/private_user_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_ban_add_event.dart'; import 'package:mineral/domains/events/contracts/server/server_ban_remove_event.dart'; @@ -31,6 +32,7 @@ import 'package:mineral/domains/events/contracts/server/server_roles_create_even import 'package:mineral/domains/events/contracts/server/server_roles_remove_event.dart'; import 'package:mineral/domains/events/contracts/server/server_roles_update_event.dart'; import 'package:mineral/domains/events/contracts/server/server_stickers_update_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_text_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_update_event.dart'; interface class EventType {} @@ -62,6 +64,7 @@ enum Event implements EnhancedEnum, EventType { serverChannelSelect(ServerChannelSelectEvent), serverRoleSelect(ServerRoleSelectEvent), serverMemberSelect(ServerMemberSelectEvent), + serverTextSelect(ServerTextSelectEvent), // private privateMessageCreate(PrivateMessageCreateEvent), @@ -70,7 +73,8 @@ enum Event implements EnhancedEnum, EventType { privateChannelDelete(PrivateChannelDeleteEvent), privateButtonClick(PrivateButtonClickEvent), privateDialogSubmit(PrivateDialogSubmitEvent), - privateUserSelect(PrivateUserSelectEvent); + privateUserSelect(PrivateUserSelectEvent), + privateTextSelect(PrivateTextSelectEvent); @override final Type value; diff --git a/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart index b0e6d4c2f..d33ad3c13 100644 --- a/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart @@ -72,6 +72,8 @@ final class SelectInteractionCreatePacket implements ListenablePacket { _dispatchRoleSelectMenu(ctx, message.payload, dispatch); case ComponentType.userSelectMenu: _dispatchUserSelectMenu(ctx, message.payload, dispatch); + case ComponentType.textSelectMenu: + _dispatchTextSelectMenu(ctx, message.payload, dispatch); default: logger.warn('Select menu type $selectMenuType not found'); } @@ -157,4 +159,21 @@ final class SelectInteractionCreatePacket implements ListenablePacket { params: [ctx, resolvedResource], constraint: (String? customId) => customId == ctx.customId); } + + Future _dispatchTextSelectMenu( + SelectContext ctx, Map payload, DispatchEvent dispatch) async { + final List resolvedText = List.from(payload['data']['values']); + + return switch (ctx) { + ServerSelectContext() => dispatch( + event: Event.serverTextSelect, + params: [ctx, resolvedText], + constraint: (String? customId) => customId == ctx.customId), + PrivateSelectContext() => dispatch( + event: Event.privateTextSelect, + params: [ctx, resolvedText], + constraint: (String? customId) => customId == ctx.customId), + _ => logger.warn('Select context $ctx not found'), + }; + } } diff --git a/lib/infrastructure/kernel/mineral_client.dart b/lib/infrastructure/kernel/mineral_client.dart index 1b6760f2c..acebcc4f7 100644 --- a/lib/infrastructure/kernel/mineral_client.dart +++ b/lib/infrastructure/kernel/mineral_client.dart @@ -2,7 +2,12 @@ import 'package:mineral/api/common/commands/command_contract.dart'; import 'package:mineral/domains/commands/command_declaration_bucket.dart'; import 'package:mineral/domains/events/contracts/server/server_channel_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_dialog_submit_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_member_select_event.dart'; import 'package:mineral/domains/events/contracts/server/server_role_select_event.dart'; +import 'package:mineral/domains/events/contracts/server/server_text_select_event.dart'; +import 'package:mineral/domains/events/contracts/private/private_dialog_submit_event.dart'; +import 'package:mineral/domains/events/contracts/private/private_user_select_event.dart'; +import 'package:mineral/domains/events/contracts/private/private_text_select_event.dart'; import 'package:mineral/domains/events/event_bucket.dart'; import 'package:mineral/domains/events/types/listenable_event.dart'; import 'package:mineral/infrastructure/commons/listenable.dart'; @@ -53,6 +58,11 @@ final class MineralClient implements MineralClientContract { final ServerDialogSubmitEvent instance => instance.customId, final ServerChannelSelectEvent instance => instance.customId, final ServerRoleSelectEvent instance => instance.customId, + final ServerMemberSelectEvent instance => instance.customId, + final ServerTextSelectEvent instance => instance.customId, + final PrivateDialogSubmitEvent instance => instance.customId, + final PrivateUserSelectEvent instance => instance.customId, + final PrivateTextSelectEvent instance => instance.customId, _ => null } ); From 0d5a6beb5059e5d292289b44013a720b840b2980 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Mon, 12 Aug 2024 22:16:41 +0200 Subject: [PATCH 15/19] feat(component): remove customId and define dialog customId as null --- .../events/contracts/private/private_dialog_submit_event.dart | 2 +- .../events/contracts/private/private_message_create_event.dart | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/domains/events/contracts/private/private_dialog_submit_event.dart b/lib/domains/events/contracts/private/private_dialog_submit_event.dart index c5e97a252..fc261e8ad 100644 --- a/lib/domains/events/contracts/private/private_dialog_submit_event.dart +++ b/lib/domains/events/contracts/private/private_dialog_submit_event.dart @@ -10,7 +10,7 @@ abstract class PrivateDialogSubmitEvent implements ListenableEvent { @override Event get event => Event.privateDialogSubmit; - String? get customId; + String? customId; FutureOr handle(PrivateDialogContext ctx); } diff --git a/lib/domains/events/contracts/private/private_message_create_event.dart b/lib/domains/events/contracts/private/private_message_create_event.dart index 82f8a98e5..2dde744b2 100644 --- a/lib/domains/events/contracts/private/private_message_create_event.dart +++ b/lib/domains/events/contracts/private/private_message_create_event.dart @@ -10,7 +10,5 @@ abstract class PrivateMessageCreateEvent implements ListenableEvent { @override Event get event => Event.privateMessageCreate; - String? get customId; - FutureOr handle(PrivateMessage message); } From 58293650e90475f3aaf18a3f9e44d8b17fc3b4b0 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Tue, 13 Aug 2024 08:52:44 +0200 Subject: [PATCH 16/19] feat(component): add customId in button events --- lib/domains/events/buckets/server_bucket.dart | 4 ++-- .../contracts/server/server_button_click_event.dart | 2 ++ .../interactions/button_interaction_create_packet.dart | 10 ++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/domains/events/buckets/server_bucket.dart b/lib/domains/events/buckets/server_bucket.dart index a52cffbbb..0f638be68 100644 --- a/lib/domains/events/buckets/server_bucket.dart +++ b/lib/domains/events/buckets/server_bucket.dart @@ -82,8 +82,8 @@ final class ServerBucket { void stickersUpdate(ServerStickersUpdateEventHandler handle) => _events.make(Event.serverStickersUpdate, handle); - void buttonClick(ServerButtonClickEventHandler handle) => - _events.make(Event.serverButtonClick, handle); + void buttonClick(ServerButtonClickEventHandler handle, {String? customId}) => + _events.make(Event.serverButtonClick, handle, customId: customId); void dialogSubmit(ServerDialogSubmitEventHandler handle, {String? customId}) => _events.make(Event.serverDialogSubmit, handle, customId: customId); diff --git a/lib/domains/events/contracts/server/server_button_click_event.dart b/lib/domains/events/contracts/server/server_button_click_event.dart index d68551f68..cdfb0dea9 100644 --- a/lib/domains/events/contracts/server/server_button_click_event.dart +++ b/lib/domains/events/contracts/server/server_button_click_event.dart @@ -10,5 +10,7 @@ abstract class ServerButtonClickEvent implements ListenableEvent { @override Event get event => Event.serverButtonClick; + String? customId; + FutureOr handle(ServerButtonContext ctx); } diff --git a/lib/infrastructure/internals/packets/listeners/interactions/button_interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interactions/button_interaction_create_packet.dart index d98cb04f8..e3e44a8a8 100644 --- a/lib/infrastructure/internals/packets/listeners/interactions/button_interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interactions/button_interaction_create_packet.dart @@ -69,7 +69,10 @@ final class ButtonInteractionCreatePacket implements ListenablePacket { member: message.author, ); - dispatch(event: Event.serverButtonClick, params: [ctx]); + dispatch( + event: Event.serverButtonClick, + params: [ctx], + constraint: (String? customId) => customId == ctx.customId); } Future _handlePrivateButton(Map payload, DispatchEvent dispatch) async { @@ -95,6 +98,9 @@ final class ButtonInteractionCreatePacket implements ListenablePacket { user: message.author, ); - dispatch(event: Event.serverButtonClick, params: [ctx]); + dispatch( + event: Event.serverButtonClick, + params: [ctx], + constraint: (String? customId) => customId == ctx.customId); } } From 0bbcac53f4d3f86721c7a2d0318bf40095872127 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Tue, 13 Aug 2024 08:56:31 +0200 Subject: [PATCH 17/19] feat(component): write factories --- .../contexts/private_dialog_context.dart | 12 +++++++++ .../contexts/server_dialog_context.dart | 15 +++++++++++ .../dialog_interaction_create_packet.dart | 26 ++++--------------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/lib/domains/components/dialog/contexts/private_dialog_context.dart b/lib/domains/components/dialog/contexts/private_dialog_context.dart index 68835baef..c4a0f6e64 100644 --- a/lib/domains/components/dialog/contexts/private_dialog_context.dart +++ b/lib/domains/components/dialog/contexts/private_dialog_context.dart @@ -3,6 +3,7 @@ import 'package:mineral/api/private/user.dart'; import 'package:mineral/domains/components/dialog/dialog_context.dart'; import 'package:mineral/infrastructure/internals/interactions/interaction.dart'; import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; +import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; final class PrivateDialogContext implements DialogContext { @override @@ -34,4 +35,15 @@ final class PrivateDialogContext implements DialogContext { }) { interaction = Interaction(token, id); } + + static Future fromMap(MarshallerContract marshaller, Map payload) async{ + return PrivateDialogContext( + customId: payload['data']['custom_id'], + id: Snowflake(payload['id']), + applicationId: Snowflake(payload['application_id']), + token: payload['token'], + version: payload['version'], + user: await marshaller.serializers.user.serializeRemote(payload['user']), + ); + } } diff --git a/lib/domains/components/dialog/contexts/server_dialog_context.dart b/lib/domains/components/dialog/contexts/server_dialog_context.dart index 6eb0dee4d..733d6cdfa 100644 --- a/lib/domains/components/dialog/contexts/server_dialog_context.dart +++ b/lib/domains/components/dialog/contexts/server_dialog_context.dart @@ -3,6 +3,7 @@ import 'package:mineral/api/server/member.dart'; import 'package:mineral/domains/components/dialog/dialog_context.dart'; import 'package:mineral/infrastructure/internals/interactions/interaction.dart'; import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; +import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; final class ServerDialogContext implements DialogContext { @override @@ -34,4 +35,18 @@ final class ServerDialogContext implements DialogContext { }) { interaction = Interaction(token, id); } + + static Future fromMap(MarshallerContract marshaller, Map payload) async{ + return ServerDialogContext( + customId: payload['data']['custom_id'], + id: Snowflake(payload['id']), + applicationId: Snowflake(payload['application_id']), + token: payload['token'], + version: payload['version'], + member: await marshaller.dataStore.member.getMember( + guildId: Snowflake(payload['guild_id']), + memberId: Snowflake(payload['member']['user']['id']), + ), + ); + } } diff --git a/lib/infrastructure/internals/packets/listeners/interactions/dialog_interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interactions/dialog_interaction_create_packet.dart index e8a3660fb..e7d2f5ca3 100644 --- a/lib/infrastructure/internals/packets/listeners/interactions/dialog_interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interactions/dialog_interaction_create_packet.dart @@ -1,5 +1,4 @@ import 'package:collection/collection.dart'; -import 'package:mineral/api/common/snowflake.dart'; import 'package:mineral/api/common/types/interaction_type.dart'; import 'package:mineral/domains/components/dialog/contexts/private_dialog_context.dart'; import 'package:mineral/domains/components/dialog/contexts/server_dialog_context.dart'; @@ -40,26 +39,11 @@ final class DialogInteractionCreatePacket implements ListenablePacket { _ => null }; - final ctx = switch (interactionContext) { - InteractionContextType.server => ServerDialogContext( - customId: message.payload['data']['custom_id'], - id: Snowflake(message.payload['id']), - applicationId: Snowflake(message.payload['application_id']), - token: message.payload['token'], - version: message.payload['version'], - member: await marshaller.dataStore.member.getMember( - guildId: Snowflake(message.payload['guild_id']), - memberId: Snowflake(message.payload['member']['user']['id']), - ), - ), - InteractionContextType.privateChannel => PrivateDialogContext( - customId: message.payload['data']['custom_id'], - id: Snowflake(message.payload['id']), - applicationId: Snowflake(message.payload['application_id']), - token: message.payload['token'], - version: message.payload['version'], - user: await marshaller.serializers.user.serializeRemote(message.payload['user']), - ), + final ctx = await switch (interactionContext) { + InteractionContextType.server => + ServerDialogContext.fromMap(marshaller, message.payload['data']), + InteractionContextType.privateChannel => + PrivateDialogContext.fromMap(marshaller, message.payload['data']), _ => null }; From ec14f5a5d199a7e95c24951d6e0d1a3b9e95dfbb Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Tue, 13 Aug 2024 08:58:54 +0200 Subject: [PATCH 18/19] feat(component): write factories --- .../contexts/private_select_context.dart | 16 ++++++++++ .../contexts/server_select_context.dart | 19 ++++++++++++ .../select_interaction_create_packet.dart | 30 ++----------------- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/lib/domains/components/selects/contexts/private_select_context.dart b/lib/domains/components/selects/contexts/private_select_context.dart index 51a7e4515..b954c8bba 100644 --- a/lib/domains/components/selects/contexts/private_select_context.dart +++ b/lib/domains/components/selects/contexts/private_select_context.dart @@ -4,6 +4,7 @@ import 'package:mineral/api/private/user.dart'; import 'package:mineral/domains/components/selects/button_context.dart'; import 'package:mineral/infrastructure/internals/interactions/interaction.dart'; import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; +import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; final class PrivateSelectContext implements SelectContext { @override @@ -38,4 +39,19 @@ final class PrivateSelectContext implements SelectContext { }) { interaction = Interaction(token, id); } + + static Future fromMap(MarshallerContract marshaller, Map payload) async{ + return PrivateSelectContext( + customId: payload['data']['custom_id'], + id: Snowflake(payload['id']), + applicationId: Snowflake(payload['application_id']), + token: payload['token'], + version: payload['version'], + message: await marshaller.dataStore.message.getPrivateMessage( + messageId: Snowflake(payload['message']['id']), + channelId: Snowflake(payload['channel_id']), + ), + user: await marshaller.serializers.user.serializeRemote(payload['user']), + ); + } } diff --git a/lib/domains/components/selects/contexts/server_select_context.dart b/lib/domains/components/selects/contexts/server_select_context.dart index d2230346c..1a293e6f7 100644 --- a/lib/domains/components/selects/contexts/server_select_context.dart +++ b/lib/domains/components/selects/contexts/server_select_context.dart @@ -5,6 +5,7 @@ import 'package:mineral/domains/components/buttons/button_context.dart'; import 'package:mineral/domains/components/selects/button_context.dart'; import 'package:mineral/infrastructure/internals/interactions/interaction.dart'; import 'package:mineral/infrastructure/internals/interactions/types/interaction_contract.dart'; +import 'package:mineral/infrastructure/internals/marshaller/marshaller.dart'; final class ServerSelectContext implements SelectContext { @override @@ -39,4 +40,22 @@ final class ServerSelectContext implements SelectContext { }) { interaction = Interaction(token, id); } + + static Future fromMap(MarshallerContract marshaller, Map payload) async{ + return ServerSelectContext( + customId: payload['data']['custom_id'], + id: Snowflake(payload['id']), + applicationId: Snowflake(payload['application_id']), + token: payload['token'], + version: payload['version'], + message: await marshaller.dataStore.message.getServerMessage( + messageId: Snowflake(payload['message']['id']), + channelId: Snowflake(payload['channel_id']), + ), + member: await marshaller.dataStore.member.getMember( + guildId: Snowflake(payload['guild_id']), + memberId: Snowflake(payload['member']['user']['id']), + ), + ); + } } diff --git a/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart b/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart index d33ad3c13..a68c01f32 100644 --- a/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart +++ b/lib/infrastructure/internals/packets/listeners/interactions/select_interaction_create_packet.dart @@ -36,33 +36,9 @@ final class SelectInteractionCreatePacket implements ListenablePacket { final selectMenuType = ComponentType.values .firstWhereOrNull((e) => e.value == message.payload['data']['component_type']); - final ctx = switch (message.payload['guild_id']) { - String() => ServerSelectContext( - id: Snowflake(message.payload['id']), - applicationId: Snowflake(message.payload['application_id']), - token: message.payload['token'], - version: message.payload['version'], - customId: message.payload['data']['custom_id'], - message: await marshaller.dataStore.message.getServerMessage( - messageId: Snowflake(message.payload['message']['id']), - channelId: Snowflake(message.payload['channel_id']), - ), - member: await marshaller.dataStore.member.getMember( - guildId: Snowflake(message.payload['guild_id']), - memberId: Snowflake(message.payload['member']['user']['id']), - ), - ), - _ => PrivateSelectContext( - id: Snowflake(message.payload['id']), - applicationId: Snowflake(message.payload['application_id']), - token: message.payload['token'], - version: message.payload['version'], - customId: message.payload['data']['custom_id'], - message: await marshaller.dataStore.message.getPrivateMessage( - messageId: Snowflake(message.payload['message']['id']), - channelId: Snowflake(message.payload['channel_id']), - ), - user: await marshaller.serializers.user.serializeRemote(message.payload['user'])), + final ctx = await switch (message.payload['guild_id']) { + String() => ServerSelectContext.fromMap(marshaller, message.payload), + _ => PrivateSelectContext.fromMap(marshaller, message.payload), }; switch (selectMenuType) { From 759e810dba440c8f53ac79ef29670d4c4787bc12 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Wed, 14 Aug 2024 21:40:36 +0200 Subject: [PATCH 19/19] feat(component): add customId with default value as null --- .../events/contracts/common/ready_event.dart | 3 +++ .../private/private_button_click_event.dart | 3 +++ .../private/private_channel_create_event.dart | 3 +++ .../private/private_channel_delete_event.dart | 3 +++ .../private_channel_pins_update_event.dart | 3 +++ .../private/private_channel_update_event.dart | 3 +++ .../private/private_dialog_submit_event.dart | 1 + .../private/private_message_create_event.dart | 3 +++ .../private/private_text_select_event.dart | 3 ++- .../private/private_user_select_event.dart | 3 ++- .../server/server_ban_add_event.dart | 3 +++ .../server/server_ban_remove_event.dart | 3 +++ .../server/server_button_click_event.dart | 1 + .../server/server_channel_create_event.dart | 3 +++ .../server/server_channel_delete_event.dart | 3 +++ .../server_channel_pins_update_event.dart | 3 +++ .../server/server_channel_select_event.dart | 3 ++- .../server/server_channel_update_event.dart | 3 +++ .../contracts/server/server_create_event.dart | 3 +++ .../contracts/server/server_delete_event.dart | 3 +++ .../server/server_dialog_submit_event.dart | 3 ++- .../server/server_emojis_update_event.dart | 3 +++ .../server/server_member_add_event.dart | 3 +++ .../server/server_member_remove_event.dart | 3 +++ .../server/server_member_select_event.dart | 3 ++- .../server/server_member_update_event.dart | 3 +++ .../server/server_message_create_event.dart | 3 +++ .../server/server_presence_update_event.dart | 3 +++ .../server/server_role_select_event.dart | 4 ++-- .../server/server_roles_create_event.dart | 3 +++ .../server/server_roles_remove_event.dart | 3 +++ .../server/server_roles_update_event.dart | 3 +++ .../server/server_stickers_update_event.dart | 3 +++ .../server/server_text_select_event.dart | 3 ++- .../contracts/server/server_update_event.dart | 3 +++ .../events/types/listenable_event.dart | 1 + lib/infrastructure/kernel/mineral_client.dart | 24 +++---------------- 37 files changed, 98 insertions(+), 29 deletions(-) diff --git a/lib/domains/events/contracts/common/ready_event.dart b/lib/domains/events/contracts/common/ready_event.dart index ec8394030..c90dfa4f2 100644 --- a/lib/domains/events/contracts/common/ready_event.dart +++ b/lib/domains/events/contracts/common/ready_event.dart @@ -10,5 +10,8 @@ abstract class ReadyEvent implements ListenableEvent { @override Event get event => Event.ready; + @override + String? customId; + FutureOr handle(Bot bot); } diff --git a/lib/domains/events/contracts/private/private_button_click_event.dart b/lib/domains/events/contracts/private/private_button_click_event.dart index 6fa09c24d..675004ef5 100644 --- a/lib/domains/events/contracts/private/private_button_click_event.dart +++ b/lib/domains/events/contracts/private/private_button_click_event.dart @@ -10,5 +10,8 @@ abstract class PrivateButtonClickEvent implements ListenableEvent { @override Event get event => Event.privateButtonClick; + @override + String? customId; + FutureOr handle(PrivateButtonContext ctx); } diff --git a/lib/domains/events/contracts/private/private_channel_create_event.dart b/lib/domains/events/contracts/private/private_channel_create_event.dart index 25ceb9731..e86f44696 100644 --- a/lib/domains/events/contracts/private/private_channel_create_event.dart +++ b/lib/domains/events/contracts/private/private_channel_create_event.dart @@ -6,5 +6,8 @@ import 'package:mineral/domains/events/types/listenable_event.dart'; typedef PrivateChannelCreateEventHandler = FutureOr Function(PrivateChannel); abstract class PrivateChannelCreateEvent implements ListenableEvent { + @override + String? customId; + FutureOr handle(PrivateChannel channel); } diff --git a/lib/domains/events/contracts/private/private_channel_delete_event.dart b/lib/domains/events/contracts/private/private_channel_delete_event.dart index bbb6ff96b..1c45e7943 100644 --- a/lib/domains/events/contracts/private/private_channel_delete_event.dart +++ b/lib/domains/events/contracts/private/private_channel_delete_event.dart @@ -10,5 +10,8 @@ abstract class PrivateChannelDeleteEvent implements ListenableEvent { @override Event get event => Event.privateChannelDelete; + @override + String? customId; + FutureOr handle(PrivateChannel channel); } diff --git a/lib/domains/events/contracts/private/private_channel_pins_update_event.dart b/lib/domains/events/contracts/private/private_channel_pins_update_event.dart index ff0678a4a..9a30d61be 100644 --- a/lib/domains/events/contracts/private/private_channel_pins_update_event.dart +++ b/lib/domains/events/contracts/private/private_channel_pins_update_event.dart @@ -10,5 +10,8 @@ abstract class PrivateChannelPinsUpdateEvent implements ListenableEvent { @override Event get event => Event.privateChannelPinsUpdate; + @override + String? customId; + FutureOr handle(PrivateChannel channel); } diff --git a/lib/domains/events/contracts/private/private_channel_update_event.dart b/lib/domains/events/contracts/private/private_channel_update_event.dart index 38d7e998b..7de015661 100644 --- a/lib/domains/events/contracts/private/private_channel_update_event.dart +++ b/lib/domains/events/contracts/private/private_channel_update_event.dart @@ -10,5 +10,8 @@ abstract class PrivateChannelUpdateEvent implements ListenableEvent { @override Event get event => Event.privateChannelUpdate; + @override + String? customId; + FutureOr handle(PrivateChannel channel); } diff --git a/lib/domains/events/contracts/private/private_dialog_submit_event.dart b/lib/domains/events/contracts/private/private_dialog_submit_event.dart index fc261e8ad..50591ac22 100644 --- a/lib/domains/events/contracts/private/private_dialog_submit_event.dart +++ b/lib/domains/events/contracts/private/private_dialog_submit_event.dart @@ -10,6 +10,7 @@ abstract class PrivateDialogSubmitEvent implements ListenableEvent { @override Event get event => Event.privateDialogSubmit; + @override String? customId; FutureOr handle(PrivateDialogContext ctx); diff --git a/lib/domains/events/contracts/private/private_message_create_event.dart b/lib/domains/events/contracts/private/private_message_create_event.dart index 2dde744b2..2e546bfa0 100644 --- a/lib/domains/events/contracts/private/private_message_create_event.dart +++ b/lib/domains/events/contracts/private/private_message_create_event.dart @@ -10,5 +10,8 @@ abstract class PrivateMessageCreateEvent implements ListenableEvent { @override Event get event => Event.privateMessageCreate; + @override + String? customId; + FutureOr handle(PrivateMessage message); } diff --git a/lib/domains/events/contracts/private/private_text_select_event.dart b/lib/domains/events/contracts/private/private_text_select_event.dart index e8684166f..2e3fad005 100644 --- a/lib/domains/events/contracts/private/private_text_select_event.dart +++ b/lib/domains/events/contracts/private/private_text_select_event.dart @@ -10,7 +10,8 @@ abstract class PrivateTextSelectEvent implements ListenableEvent { @override Event get event => Event.serverTextSelect; - String? get customId; + @override + String? customId; FutureOr handle(PrivateSelectContext ctx, List values); } diff --git a/lib/domains/events/contracts/private/private_user_select_event.dart b/lib/domains/events/contracts/private/private_user_select_event.dart index a42d84936..d350b1f2e 100644 --- a/lib/domains/events/contracts/private/private_user_select_event.dart +++ b/lib/domains/events/contracts/private/private_user_select_event.dart @@ -11,7 +11,8 @@ abstract class PrivateUserSelectEvent implements ListenableEvent { @override Event get event => Event.privateUserSelect; - String? get customId; + @override + String? customId; FutureOr handle(PrivateSelectContext ctx, List roles); } diff --git a/lib/domains/events/contracts/server/server_ban_add_event.dart b/lib/domains/events/contracts/server/server_ban_add_event.dart index cadde37f7..e92fb1faa 100644 --- a/lib/domains/events/contracts/server/server_ban_add_event.dart +++ b/lib/domains/events/contracts/server/server_ban_add_event.dart @@ -12,5 +12,8 @@ abstract class ServerBanAddEvent implements ListenableEvent { @override Event get event => Event.serverBanAdd; + @override + String? customId; + FutureOr handle(Member? member, User user, Server server); } diff --git a/lib/domains/events/contracts/server/server_ban_remove_event.dart b/lib/domains/events/contracts/server/server_ban_remove_event.dart index a111f84d4..056b0730b 100644 --- a/lib/domains/events/contracts/server/server_ban_remove_event.dart +++ b/lib/domains/events/contracts/server/server_ban_remove_event.dart @@ -11,5 +11,8 @@ abstract class ServerBanRemoveEvent implements ListenableEvent { @override Event get event => Event.serverBanRemove; + @override + String? customId; + FutureOr handle(User user, Server server); } diff --git a/lib/domains/events/contracts/server/server_button_click_event.dart b/lib/domains/events/contracts/server/server_button_click_event.dart index cdfb0dea9..136f0799a 100644 --- a/lib/domains/events/contracts/server/server_button_click_event.dart +++ b/lib/domains/events/contracts/server/server_button_click_event.dart @@ -10,6 +10,7 @@ abstract class ServerButtonClickEvent implements ListenableEvent { @override Event get event => Event.serverButtonClick; + @override String? customId; FutureOr handle(ServerButtonContext ctx); diff --git a/lib/domains/events/contracts/server/server_channel_create_event.dart b/lib/domains/events/contracts/server/server_channel_create_event.dart index 84509f04d..7a7361b2e 100644 --- a/lib/domains/events/contracts/server/server_channel_create_event.dart +++ b/lib/domains/events/contracts/server/server_channel_create_event.dart @@ -10,5 +10,8 @@ abstract class ServerChannelCreateEvent implements ListenableEvent { @override Event get event => Event.serverChannelCreate; + @override + String? customId; + FutureOr handle(ServerChannel channel); } diff --git a/lib/domains/events/contracts/server/server_channel_delete_event.dart b/lib/domains/events/contracts/server/server_channel_delete_event.dart index ee76339c1..a7dacea61 100644 --- a/lib/domains/events/contracts/server/server_channel_delete_event.dart +++ b/lib/domains/events/contracts/server/server_channel_delete_event.dart @@ -10,5 +10,8 @@ abstract class ServerChannelDeleteEvent implements ListenableEvent { @override Event get event => Event.serverChannelDelete; + @override + String? customId; + FutureOr handle(ServerChannel? channel); } diff --git a/lib/domains/events/contracts/server/server_channel_pins_update_event.dart b/lib/domains/events/contracts/server/server_channel_pins_update_event.dart index 4792d6ebb..fec8148b2 100644 --- a/lib/domains/events/contracts/server/server_channel_pins_update_event.dart +++ b/lib/domains/events/contracts/server/server_channel_pins_update_event.dart @@ -11,5 +11,8 @@ abstract class ServerChannelPinsUpdateEvent implements ListenableEvent { @override Event get event => Event.serverChannelPinsUpdate; + @override + String? customId; + FutureOr handle(Server server, ServerChannel channel); } diff --git a/lib/domains/events/contracts/server/server_channel_select_event.dart b/lib/domains/events/contracts/server/server_channel_select_event.dart index 386f9a154..0c0eb8eb5 100644 --- a/lib/domains/events/contracts/server/server_channel_select_event.dart +++ b/lib/domains/events/contracts/server/server_channel_select_event.dart @@ -11,7 +11,8 @@ abstract class ServerChannelSelectEvent implements ListenableEvent { @override Event get event => Event.serverChannelSelect; - String? get customId; + @override + String? customId; FutureOr handle(ServerSelectContext ctx, List channels); } diff --git a/lib/domains/events/contracts/server/server_channel_update_event.dart b/lib/domains/events/contracts/server/server_channel_update_event.dart index 3cdeeffef..076da3477 100644 --- a/lib/domains/events/contracts/server/server_channel_update_event.dart +++ b/lib/domains/events/contracts/server/server_channel_update_event.dart @@ -10,5 +10,8 @@ abstract class ServerChannelUpdateEvent implements ListenableEvent { @override Event get event => Event.serverChannelUpdate; + @override + String? customId; + FutureOr handle(ServerChannel before, ServerChannel after); } diff --git a/lib/domains/events/contracts/server/server_create_event.dart b/lib/domains/events/contracts/server/server_create_event.dart index a31a1e831..a224047cc 100644 --- a/lib/domains/events/contracts/server/server_create_event.dart +++ b/lib/domains/events/contracts/server/server_create_event.dart @@ -10,5 +10,8 @@ abstract class ServerCreateEvent implements ListenableEvent { @override Event get event => Event.serverCreate; + @override + String? customId; + FutureOr handle(Server server); } diff --git a/lib/domains/events/contracts/server/server_delete_event.dart b/lib/domains/events/contracts/server/server_delete_event.dart index 54c3f3565..8d8a34b6a 100644 --- a/lib/domains/events/contracts/server/server_delete_event.dart +++ b/lib/domains/events/contracts/server/server_delete_event.dart @@ -10,5 +10,8 @@ abstract class ServerDeleteEvent implements ListenableEvent { @override Event get event => Event.serverDelete; + @override + String? customId; + FutureOr handle(Server server); } diff --git a/lib/domains/events/contracts/server/server_dialog_submit_event.dart b/lib/domains/events/contracts/server/server_dialog_submit_event.dart index 93bdac433..c7d362ebe 100644 --- a/lib/domains/events/contracts/server/server_dialog_submit_event.dart +++ b/lib/domains/events/contracts/server/server_dialog_submit_event.dart @@ -10,7 +10,8 @@ abstract class ServerDialogSubmitEvent implements ListenableEvent { @override Event get event => Event.serverDialogSubmit; - String? get customId; + @override + String? customId; FutureOr handle(ServerDialogContext ctx); } diff --git a/lib/domains/events/contracts/server/server_emojis_update_event.dart b/lib/domains/events/contracts/server/server_emojis_update_event.dart index 10c03ece8..55e7799bf 100644 --- a/lib/domains/events/contracts/server/server_emojis_update_event.dart +++ b/lib/domains/events/contracts/server/server_emojis_update_event.dart @@ -10,5 +10,8 @@ abstract class ServerEmojisUpdateEvent implements ListenableEvent { @override Event get event => Event.serverEmojisUpdate; + @override + String? customId; + FutureOr handle(EmojiManager emojisManager, Server server); } diff --git a/lib/domains/events/contracts/server/server_member_add_event.dart b/lib/domains/events/contracts/server/server_member_add_event.dart index e0dc9a1d8..e77b89612 100644 --- a/lib/domains/events/contracts/server/server_member_add_event.dart +++ b/lib/domains/events/contracts/server/server_member_add_event.dart @@ -11,5 +11,8 @@ abstract class ServerMemberAddEvent implements ListenableEvent { @override Event get event => Event.serverMemberAdd; + @override + String? customId; + FutureOr handle(Member member, Server server); } diff --git a/lib/domains/events/contracts/server/server_member_remove_event.dart b/lib/domains/events/contracts/server/server_member_remove_event.dart index b01de5f63..430b4197e 100644 --- a/lib/domains/events/contracts/server/server_member_remove_event.dart +++ b/lib/domains/events/contracts/server/server_member_remove_event.dart @@ -11,5 +11,8 @@ abstract class ServerMemberRemoveEvent implements ListenableEvent { @override Event get event => Event.serverMemberRemove; + @override + String? customId; + FutureOr handle(User? user, Server server); } diff --git a/lib/domains/events/contracts/server/server_member_select_event.dart b/lib/domains/events/contracts/server/server_member_select_event.dart index 1b136d8d0..e5eb28af6 100644 --- a/lib/domains/events/contracts/server/server_member_select_event.dart +++ b/lib/domains/events/contracts/server/server_member_select_event.dart @@ -11,7 +11,8 @@ abstract class ServerMemberSelectEvent implements ListenableEvent { @override Event get event => Event.serverMemberSelect; - String? get customId; + @override + String? customId; FutureOr handle(ServerSelectContext ctx, List members); } diff --git a/lib/domains/events/contracts/server/server_member_update_event.dart b/lib/domains/events/contracts/server/server_member_update_event.dart index 861a8bdef..648aa41d9 100644 --- a/lib/domains/events/contracts/server/server_member_update_event.dart +++ b/lib/domains/events/contracts/server/server_member_update_event.dart @@ -11,5 +11,8 @@ abstract class ServerMemberUpdateEvent implements ListenableEvent { @override Event get event => Event.serverMemberUpdate; + @override + String? customId; + FutureOr handle(Member after, Member before, Server server); } diff --git a/lib/domains/events/contracts/server/server_message_create_event.dart b/lib/domains/events/contracts/server/server_message_create_event.dart index f6135f44c..16ec7bb74 100644 --- a/lib/domains/events/contracts/server/server_message_create_event.dart +++ b/lib/domains/events/contracts/server/server_message_create_event.dart @@ -10,5 +10,8 @@ abstract class ServerMessageCreateEvent implements ListenableEvent { @override Event get event => Event.serverMessageCreate; + @override + String? customId; + FutureOr handle(ServerMessage message); } diff --git a/lib/domains/events/contracts/server/server_presence_update_event.dart b/lib/domains/events/contracts/server/server_presence_update_event.dart index ca5f7fa4c..759c56d95 100644 --- a/lib/domains/events/contracts/server/server_presence_update_event.dart +++ b/lib/domains/events/contracts/server/server_presence_update_event.dart @@ -12,5 +12,8 @@ abstract class ServerPresenceUpdateEvent implements ListenableEvent { @override Event get event => Event.serverPresenceUpdate; + @override + String? customId; + FutureOr handle(Member member, Server server, Presence presence); } diff --git a/lib/domains/events/contracts/server/server_role_select_event.dart b/lib/domains/events/contracts/server/server_role_select_event.dart index 70c673b90..3ae88bd4c 100644 --- a/lib/domains/events/contracts/server/server_role_select_event.dart +++ b/lib/domains/events/contracts/server/server_role_select_event.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:mineral/api/server/channels/server_channel.dart'; import 'package:mineral/api/server/role.dart'; import 'package:mineral/domains/components/selects/contexts/server_select_context.dart'; import 'package:mineral/domains/events/event.dart'; @@ -12,7 +11,8 @@ abstract class ServerRoleSelectEvent implements ListenableEvent { @override Event get event => Event.serverRoleSelect; - String? get customId; + @override + String? customId; FutureOr handle(ServerSelectContext ctx, List roles); } diff --git a/lib/domains/events/contracts/server/server_roles_create_event.dart b/lib/domains/events/contracts/server/server_roles_create_event.dart index 8e511c340..beab4be2b 100644 --- a/lib/domains/events/contracts/server/server_roles_create_event.dart +++ b/lib/domains/events/contracts/server/server_roles_create_event.dart @@ -11,5 +11,8 @@ abstract class ServerRolesCreateEvent implements ListenableEvent { @override Event get event => Event.serverRoleCreate; + @override + String? customId; + FutureOr handle(Role role, Server server); } diff --git a/lib/domains/events/contracts/server/server_roles_remove_event.dart b/lib/domains/events/contracts/server/server_roles_remove_event.dart index 8ca1b50a3..8643cad30 100644 --- a/lib/domains/events/contracts/server/server_roles_remove_event.dart +++ b/lib/domains/events/contracts/server/server_roles_remove_event.dart @@ -11,5 +11,8 @@ abstract class ServerRolesDeleteEvent implements ListenableEvent { @override Event get event => Event.serverRoleDelete; + @override + String? customId; + FutureOr handle(Role? role, Server server); } diff --git a/lib/domains/events/contracts/server/server_roles_update_event.dart b/lib/domains/events/contracts/server/server_roles_update_event.dart index 1c023a41f..e6632565e 100644 --- a/lib/domains/events/contracts/server/server_roles_update_event.dart +++ b/lib/domains/events/contracts/server/server_roles_update_event.dart @@ -11,5 +11,8 @@ abstract class ServerRolesUpdateEvent implements ListenableEvent { @override Event get event => Event.serverRoleUpdate; + @override + String? customId; + FutureOr handle(Role before, Role after, Server server); } diff --git a/lib/domains/events/contracts/server/server_stickers_update_event.dart b/lib/domains/events/contracts/server/server_stickers_update_event.dart index 39f21e345..ac03f3c44 100644 --- a/lib/domains/events/contracts/server/server_stickers_update_event.dart +++ b/lib/domains/events/contracts/server/server_stickers_update_event.dart @@ -10,5 +10,8 @@ abstract class ServerStickersUpdateEvent implements ListenableEvent { @override Event get event => Event.serverStickersUpdate; + @override + String? customId; + FutureOr handle(StickerManager stickerManager, Server server); } diff --git a/lib/domains/events/contracts/server/server_text_select_event.dart b/lib/domains/events/contracts/server/server_text_select_event.dart index 6aff90624..1c8c576cb 100644 --- a/lib/domains/events/contracts/server/server_text_select_event.dart +++ b/lib/domains/events/contracts/server/server_text_select_event.dart @@ -10,7 +10,8 @@ abstract class ServerTextSelectEvent implements ListenableEvent { @override Event get event => Event.serverTextSelect; - String? get customId; + @override + String? customId; FutureOr handle(ServerSelectContext ctx, List values); } diff --git a/lib/domains/events/contracts/server/server_update_event.dart b/lib/domains/events/contracts/server/server_update_event.dart index 830fd0038..5414064b1 100644 --- a/lib/domains/events/contracts/server/server_update_event.dart +++ b/lib/domains/events/contracts/server/server_update_event.dart @@ -10,5 +10,8 @@ abstract class ServerUpdateEvent implements ListenableEvent { @override Event get event => Event.serverUpdate; + @override + String? customId; + FutureOr handle(Server before, Server after); } diff --git a/lib/domains/events/types/listenable_event.dart b/lib/domains/events/types/listenable_event.dart index ca7d817b7..4ddc2377c 100644 --- a/lib/domains/events/types/listenable_event.dart +++ b/lib/domains/events/types/listenable_event.dart @@ -3,4 +3,5 @@ import 'package:mineral/infrastructure/commons/listenable.dart'; abstract interface class ListenableEvent implements Listenable { Event get event; + String? get customId; } diff --git a/lib/infrastructure/kernel/mineral_client.dart b/lib/infrastructure/kernel/mineral_client.dart index acebcc4f7..4bc36f531 100644 --- a/lib/infrastructure/kernel/mineral_client.dart +++ b/lib/infrastructure/kernel/mineral_client.dart @@ -1,13 +1,5 @@ import 'package:mineral/api/common/commands/command_contract.dart'; import 'package:mineral/domains/commands/command_declaration_bucket.dart'; -import 'package:mineral/domains/events/contracts/server/server_channel_select_event.dart'; -import 'package:mineral/domains/events/contracts/server/server_dialog_submit_event.dart'; -import 'package:mineral/domains/events/contracts/server/server_member_select_event.dart'; -import 'package:mineral/domains/events/contracts/server/server_role_select_event.dart'; -import 'package:mineral/domains/events/contracts/server/server_text_select_event.dart'; -import 'package:mineral/domains/events/contracts/private/private_dialog_submit_event.dart'; -import 'package:mineral/domains/events/contracts/private/private_user_select_event.dart'; -import 'package:mineral/domains/events/contracts/private/private_text_select_event.dart'; import 'package:mineral/domains/events/event_bucket.dart'; import 'package:mineral/domains/events/types/listenable_event.dart'; import 'package:mineral/infrastructure/commons/listenable.dart'; @@ -52,19 +44,9 @@ final class MineralClient implements MineralClientContract { switch (instance) { case ListenableEvent(): _kernel.eventListener.listen( - event: instance.event, - handle: (instance as dynamic).handle as Function, - customId: switch(instance) { - final ServerDialogSubmitEvent instance => instance.customId, - final ServerChannelSelectEvent instance => instance.customId, - final ServerRoleSelectEvent instance => instance.customId, - final ServerMemberSelectEvent instance => instance.customId, - final ServerTextSelectEvent instance => instance.customId, - final PrivateDialogSubmitEvent instance => instance.customId, - final PrivateUserSelectEvent instance => instance.customId, - final PrivateTextSelectEvent instance => instance.customId, - _ => null - } + event: instance.event, + handle: (instance as dynamic).handle as Function, + customId: instance.customId ); } }