From 13be40cb14d778a0f0ad9af17fc5fb37f4cdec4c Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Thu, 14 Oct 2021 16:58:30 +0200 Subject: [PATCH 01/11] Add common entities --- common/src/main/kotlin/entity/DiscordGuild.kt | 2 +- .../entity/DiscordGuildScheduledEvent.kt | 127 ++++++++++++++++++ .../kotlin/entity/DiscordStageInstance.kt | 50 ++++++- common/src/main/kotlin/entity/Permission.kt | 2 + 4 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 common/src/main/kotlin/entity/DiscordGuildScheduledEvent.kt diff --git a/common/src/main/kotlin/entity/DiscordGuild.kt b/common/src/main/kotlin/entity/DiscordGuild.kt index 37064e10502c..326e53b4dd9c 100644 --- a/common/src/main/kotlin/entity/DiscordGuild.kt +++ b/common/src/main/kotlin/entity/DiscordGuild.kt @@ -26,7 +26,7 @@ data class DiscordUnavailableGuild( ) /** - * A representation of a [Discord Guild structure](https://discord.com/developers/docs/resources/guild#guild-object + * A representation of a [Discord Guild structure](https://discord.com/developers/docs/resources/guild#guild-object) * * @param id The guild id. * @param name The guild name (2-100 characters, excluding trailing and leading whitespace) diff --git a/common/src/main/kotlin/entity/DiscordGuildScheduledEvent.kt b/common/src/main/kotlin/entity/DiscordGuildScheduledEvent.kt new file mode 100644 index 000000000000..c22e98186010 --- /dev/null +++ b/common/src/main/kotlin/entity/DiscordGuildScheduledEvent.kt @@ -0,0 +1,127 @@ +package dev.kord.common.entity + +import dev.kord.common.entity.optional.Optional +import kotlinx.datetime.Instant +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonArray + +/** + * Representation of a [Guild Scheduled Event Structure](ADD LINK). + * + * @property id the id of the event + * @property guildId the id of the guild the event is on + * @property channelId the id of the channel the event is in + * @property name the name of the event + * @property description the description of the event + * @property image the image of the event + * @property scheduledStartTime the [Instant] in which the event will start + * @property scheduledEndTime the [Instant] in which the event wil stop, if any + * @property privacyLevel the [event privacy level][StageInstancePrivacyLevel] + * @property status the [event status][GuildScheduledEventStatus] + * @property entityType the [ScheduledEntityType] of the event + * @property entityId entity id + * @property entityMetadata [metadata][GuildScheduledEventEntityMetadata] for the event + * @property skuIds sku ids + * @property skus skus + * @property userCount users subscribed to the event + */ +@Serializable +data class DiscordGuildScheduledEvent( + val id: Snowflake, + @SerialName("guild_id") + val guildId: Snowflake, + val channelId: Snowflake?, + val name: String, + val description: Optional = Optional.Missing(), + val image: String?, + @SerialName("scheduled_start_time") + val scheduledStartTime: Instant, + @SerialName("scheduled_end_time") + val scheduledEndTime: Instant?, + @SerialName("privacy_level") + val privacyLevel: StageInstancePrivacyLevel, + val status: GuildScheduledEventStatus, + val type: ScheduledEntityType, + @SerialName("entity_id") + val entityId: Snowflake?, + @SerialName("entity_type") + val entityType: ScheduledEntityType, + @SerialName("entity_metadata") + val entityMetadata: GuildScheduledEventEntityMetadata, + @SerialName("sku_ids") + val skuIds: List, + val skus: JsonArray, + @SerialName("user_count") + val userCount: Int +) + +@Serializable(with = ScheduledEntityType.Serializer::class) +sealed class ScheduledEntityType(val value: Int) { + object None : ScheduledEntityType(0) + object StageInstance : ScheduledEntityType(1) + object Voice : ScheduledEntityType(2) + object Location : ScheduledEntityType(3) + class Unknown(value: Int) : ScheduledEntityType(value) + + companion object Serializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ScheduledEntityType", PrimitiveKind.INT) + + override fun deserialize(decoder: Decoder): ScheduledEntityType { + return when (val value = decoder.decodeInt()) { + 0 -> None + 1 -> StageInstance + 2 -> Voice + 3 -> Location + else -> Unknown(value) + } + } + + override fun serialize(encoder: Encoder, value: ScheduledEntityType) = encoder.encodeInt(value.value) + + } +} + +@Serializable(with = GuildScheduledEventStatus.Serializer::class) +sealed class GuildScheduledEventStatus(val value: Int) { + object Scheduled : GuildScheduledEventStatus(1) + object Active : GuildScheduledEventStatus(2) + object Completed : GuildScheduledEventStatus(3) + object Cancelled : GuildScheduledEventStatus(4) + class Unknown(value: Int) : GuildScheduledEventStatus(value) + + companion object Serializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("GuildScheduledEventStatus", PrimitiveKind.INT) + + override fun deserialize(decoder: Decoder): GuildScheduledEventStatus { + return when (val value = decoder.decodeInt()) { + 1 -> Scheduled + 2 -> Active + 3 -> Completed + 4 -> Cancelled + else -> Unknown(value) + } + } + + override fun serialize(encoder: Encoder, value: GuildScheduledEventStatus) = encoder.encodeInt(value.value) + + } +} + +/** + * Entity metadata for [DiscordGuildScheduledEvent]. + * + * @property speakerIds the speakers of the stage channel + * @property location location of the event + */ +@Serializable +data class GuildScheduledEventEntityMetadata( + val speakerIds: Optional> = Optional.Missing(), + val location: Optional = Optional.Missing() +) diff --git a/common/src/main/kotlin/entity/DiscordStageInstance.kt b/common/src/main/kotlin/entity/DiscordStageInstance.kt index 826eb9a4f79c..1ad3579ffcf1 100644 --- a/common/src/main/kotlin/entity/DiscordStageInstance.kt +++ b/common/src/main/kotlin/entity/DiscordStageInstance.kt @@ -1,7 +1,13 @@ package dev.kord.common.entity +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder /** @@ -11,6 +17,8 @@ import kotlinx.serialization.Serializable * @property guildId The guild id of the associated Stage channel * @property channelId The id of the associated Stage channel * @property topic The topic of the Stage instance (1-120 characters) + * @property privacyLevel The [privacy level][StageInstancePrivacyLevel] of the Stage instance + * @property discoverableDisabled Whether or not Stage Discovery is disabled */ @Serializable data class DiscordStageInstance( @@ -19,5 +27,45 @@ data class DiscordStageInstance( val guildId: Snowflake, @SerialName("channel_id") val channelId: Snowflake, - val topic: String + val topic: String, + @SerialName("privacy_level") + val privacyLevel: StageInstancePrivacyLevel, + @SerialName("discoverable_disabled") + val discoverableDisabled: Boolean ) + +/** + * Privacy level of a [DiscordStageInstance]. + */ +@Serializable(with = StageInstancePrivacyLevel.Serializer::class) +sealed class StageInstancePrivacyLevel(val value: Int) { + /** + * The Stage instance is visible publicly, such as on Stage Discovery. + */ + object Public : StageInstancePrivacyLevel(1) + + /** + * The Stage instance is visible to only guild members. + */ + object GuildOnly : StageInstancePrivacyLevel(2) + + /** + * An unknown privacy level. + */ + class Unknown(value: Int) : StageInstancePrivacyLevel(value) + + companion object Serializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("StageInstancePrivacyLevel", PrimitiveKind.INT) + + override fun deserialize(decoder: Decoder): StageInstancePrivacyLevel { + return when (val value = decoder.decodeInt()) { + 1 -> Public + 2 -> GuildOnly + else -> Unknown(value) + } + } + + override fun serialize(encoder: Encoder, value: StageInstancePrivacyLevel) = encoder.encodeInt(value.value) + + } +} diff --git a/common/src/main/kotlin/entity/Permission.kt b/common/src/main/kotlin/entity/Permission.kt index 995207814e9f..d3fc6df117d9 100644 --- a/common/src/main/kotlin/entity/Permission.kt +++ b/common/src/main/kotlin/entity/Permission.kt @@ -156,6 +156,7 @@ sealed class Permission(val code: DiscordBitSet) { object ManageEmojis : Permission(0x40000000) object UseSlashCommands : Permission(0x80000000) object RequestToSpeak : Permission(0x100000000) + object ManageEvents : Permission(0x0200000000) object ManageThreads : Permission(0x0400000000) object CreatePublicThreads : Permission(0x0800000000) object CreatePrivateThreads : Permission(0x1000000000) @@ -197,6 +198,7 @@ sealed class Permission(val code: DiscordBitSet) { ManageEmojis, UseSlashCommands, RequestToSpeak, + ManageEvents, ManageThreads, CreatePublicThreads, CreatePrivateThreads, From f683fb08980f8fcf08d6c4017d9aff1ca0a359f5 Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Thu, 14 Oct 2021 17:00:47 +0200 Subject: [PATCH 02/11] Add gateway events --- gateway/src/main/kotlin/Event.kt | 69 +++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/gateway/src/main/kotlin/Event.kt b/gateway/src/main/kotlin/Event.kt index a58bdac9901b..43ef62c75db1 100644 --- a/gateway/src/main/kotlin/Event.kt +++ b/gateway/src/main/kotlin/Event.kt @@ -1,14 +1,52 @@ package dev.kord.gateway -import dev.kord.common.annotation.KordPreview -import dev.kord.common.entity.* +import dev.kord.common.entity.AllRemovedMessageReactions +import dev.kord.common.entity.BulkDeleteData +import dev.kord.common.entity.DeletedMessage +import dev.kord.common.entity.DiscordAddedGuildMember +import dev.kord.common.entity.DiscordApplicationCommand +import dev.kord.common.entity.DiscordChannel +import dev.kord.common.entity.DiscordDeletedGuildRole +import dev.kord.common.entity.DiscordGuild +import dev.kord.common.entity.DiscordGuildBan +import dev.kord.common.entity.DiscordGuildIntegrations +import dev.kord.common.entity.DiscordGuildRole +import dev.kord.common.entity.DiscordGuildScheduledEvent +import dev.kord.common.entity.DiscordInteraction +import dev.kord.common.entity.DiscordMessage +import dev.kord.common.entity.DiscordPartialMessage +import dev.kord.common.entity.DiscordPinsUpdateData +import dev.kord.common.entity.DiscordPresenceUpdate +import dev.kord.common.entity.DiscordRemovedGuildMember +import dev.kord.common.entity.DiscordShard +import dev.kord.common.entity.DiscordThreadMember +import dev.kord.common.entity.DiscordTyping +import dev.kord.common.entity.DiscordUnavailableGuild +import dev.kord.common.entity.DiscordUpdatedEmojis +import dev.kord.common.entity.DiscordUpdatedGuildMember +import dev.kord.common.entity.DiscordUser +import dev.kord.common.entity.DiscordVoiceServerUpdateData +import dev.kord.common.entity.DiscordVoiceState +import dev.kord.common.entity.DiscordWebhooksUpdateData +import dev.kord.common.entity.MessageReactionAddData +import dev.kord.common.entity.MessageReactionRemoveData +import dev.kord.common.entity.Snowflake +import dev.kord.common.entity.TargetUserType +import dev.kord.common.entity.UserFlags import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalSnowflake -import kotlinx.serialization.* +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.nullable import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.descriptors.* +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.buildClassSerialDescriptor import kotlinx.serialization.encoding.CompositeDecoder import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder @@ -414,6 +452,18 @@ sealed class Event { sequence ) + "GUILD_SCHEDULED_EVENT_CREATE" -> GuildScheduledEventCreate( + decoder.decodeSerializableElement(descriptor, index, DiscordGuildScheduledEvent.serializer()), + sequence + ) + "GUILD_SCHEDULED_EVENT_UPDATE" -> GuildScheduledEventUpdate( + decoder.decodeSerializableElement(descriptor, index, DiscordGuildScheduledEvent.serializer()), + sequence + ) + "GUILD_SCHEDULED_EVENT_DELETE" -> GuildScheduledEventDelete( + decoder.decodeSerializableElement(descriptor, index, DiscordGuildScheduledEvent.serializer()), + sequence + ) else -> { @@ -680,6 +730,15 @@ data class ThreadListSync(val sync: DiscordThreadListSync, override val sequence data class ThreadMembersUpdate(val members: DiscordThreadMembersUpdate, override val sequence: Int?) : DispatchEvent() +data class GuildScheduledEventCreate(val event: DiscordGuildScheduledEvent, override val sequence: Int?) : + DispatchEvent() + +data class GuildScheduledEventUpdate(val event: DiscordGuildScheduledEvent, override val sequence: Int?) : + DispatchEvent() + +data class GuildScheduledEventDelete(val event: DiscordGuildScheduledEvent, override val sequence: Int?) : + DispatchEvent() + @Serializable data class DiscordThreadListSync( @SerialName("guild_id") @@ -701,4 +760,4 @@ data class DiscordThreadMembersUpdate( val addedMembers: Optional> = Optional.Missing(), @SerialName("removed_member_ids") val removedMemberIds: Optional> = Optional.Missing() -) \ No newline at end of file +) From 60091b1cd735d336bae239c77dcf79aa508acb72 Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Thu, 14 Oct 2021 17:22:07 +0200 Subject: [PATCH 03/11] Add low-level rest requests --- .../main/kotlin/json/request/GuildRequests.kt | 38 ++++++-- .../json/request/ScheduledEventRequests.kt | 23 +++++ rest/src/main/kotlin/route/Route.kt | 88 ++++++++++++++++--- rest/src/main/kotlin/service/GuildService.kt | 79 +++++++++++++++-- rest/src/main/kotlin/service/RestClient.kt | 3 +- .../kotlin/service/ScheduledEventService.kt | 23 +++++ 6 files changed, 227 insertions(+), 27 deletions(-) create mode 100644 rest/src/main/kotlin/json/request/ScheduledEventRequests.kt create mode 100644 rest/src/main/kotlin/service/ScheduledEventService.kt diff --git a/rest/src/main/kotlin/json/request/GuildRequests.kt b/rest/src/main/kotlin/json/request/GuildRequests.kt index 3592e13b2f6f..e4904c3977a7 100644 --- a/rest/src/main/kotlin/json/request/GuildRequests.kt +++ b/rest/src/main/kotlin/json/request/GuildRequests.kt @@ -3,15 +3,30 @@ package dev.kord.rest.json.request import dev.kord.common.Color import dev.kord.common.annotation.DeprecatedSinceKord import dev.kord.common.annotation.KordExperimental -import dev.kord.common.entity.* +import dev.kord.common.entity.ChannelType +import dev.kord.common.entity.DefaultMessageNotificationLevel +import dev.kord.common.entity.DiscordWelcomeScreenChannel +import dev.kord.common.entity.ExplicitContentFilter +import dev.kord.common.entity.IntegrationExpireBehavior +import dev.kord.common.entity.Overwrite +import dev.kord.common.entity.Permissions +import dev.kord.common.entity.ScheduledEntityType +import dev.kord.common.entity.Snowflake +import dev.kord.common.entity.StageInstancePrivacyLevel +import dev.kord.common.entity.VerificationLevel import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.OptionalInt import dev.kord.common.entity.optional.OptionalSnowflake -import dev.kord.rest.builder.guild.WelcomeScreenChannelBuilder -import kotlinx.serialization.* +import kotlinx.datetime.Instant +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.ListSerializer -import kotlinx.serialization.descriptors.* +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.listSerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder @@ -237,4 +252,17 @@ data class GuildWelcomeScreenModifyRequest( val enabled: OptionalBoolean = OptionalBoolean.Missing, val welcomeScreenChannels: Optional> = Optional.Missing(), val description: Optional = Optional.Missing() -) \ No newline at end of file +) + +@Serializable +data class GuildScheduledEventCreateRequest( + val channelId: OptionalSnowflake = OptionalSnowflake.Missing, + val name: String, + @SerialName("privacy_level") + val privacyLevel: StageInstancePrivacyLevel, + @SerialName("scheduled_start_time") + val scheduledStartTime: Instant, + val description: Optional = Optional.Missing(), + @SerialName("entity_type") + val entityType: ScheduledEntityType +) diff --git a/rest/src/main/kotlin/json/request/ScheduledEventRequests.kt b/rest/src/main/kotlin/json/request/ScheduledEventRequests.kt new file mode 100644 index 000000000000..8fb6e01600ed --- /dev/null +++ b/rest/src/main/kotlin/json/request/ScheduledEventRequests.kt @@ -0,0 +1,23 @@ +package dev.kord.rest.json.request + +import dev.kord.common.entity.ScheduledEntityType +import dev.kord.common.entity.StageInstancePrivacyLevel +import dev.kord.common.entity.optional.Optional +import dev.kord.common.entity.optional.OptionalSnowflake +import kotlinx.datetime.Instant +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ScheduledEventModifyRequest( + @SerialName("channel_id") + val channelId: OptionalSnowflake = OptionalSnowflake.Missing, + val name: Optional = Optional.Missing(), + @SerialName("privacy_level") + val privacyLevel: Optional = Optional.Missing(), + @SerialName("scheduled_start_time") + val scheduledStartTime: Optional = Optional.Missing(), + val description: Optional = Optional.Missing(), + @SerialName("entity_type") + val entityType: Optional +) diff --git a/rest/src/main/kotlin/route/Route.kt b/rest/src/main/kotlin/route/Route.kt index 483ff974e454..60ec29bbf38c 100644 --- a/rest/src/main/kotlin/route/Route.kt +++ b/rest/src/main/kotlin/route/Route.kt @@ -2,10 +2,40 @@ package dev.kord.rest.route import dev.kord.common.annotation.DeprecatedSinceKord import dev.kord.common.annotation.KordExperimental -import dev.kord.common.annotation.KordPreview -import dev.kord.common.entity.* -import dev.kord.rest.json.response.* -import io.ktor.http.* +import dev.kord.common.entity.DiscordApplicationCommand +import dev.kord.common.entity.DiscordAuditLog +import dev.kord.common.entity.DiscordChannel +import dev.kord.common.entity.DiscordGuild +import dev.kord.common.entity.DiscordGuildApplicationCommandPermissions +import dev.kord.common.entity.DiscordGuildMember +import dev.kord.common.entity.DiscordGuildPreview +import dev.kord.common.entity.DiscordGuildScheduledEvent +import dev.kord.common.entity.DiscordGuildWidget +import dev.kord.common.entity.DiscordIntegration +import dev.kord.common.entity.DiscordInvite +import dev.kord.common.entity.DiscordMessage +import dev.kord.common.entity.DiscordPartialGuild +import dev.kord.common.entity.DiscordPartialInvite +import dev.kord.common.entity.DiscordRole +import dev.kord.common.entity.DiscordStageInstance +import dev.kord.common.entity.DiscordTemplate +import dev.kord.common.entity.DiscordThreadMember +import dev.kord.common.entity.DiscordUser +import dev.kord.common.entity.DiscordVoiceRegion +import dev.kord.common.entity.DiscordWebhook +import dev.kord.common.entity.DiscordWelcomeScreen +import dev.kord.rest.json.response.ApplicationInfoResponse +import dev.kord.rest.json.response.BanResponse +import dev.kord.rest.json.response.BotGatewayResponse +import dev.kord.rest.json.response.Connection +import dev.kord.rest.json.response.CurrentUserNicknameModifyResponse +import dev.kord.rest.json.response.FollowedChannelResponse +import dev.kord.rest.json.response.GatewayResponse +import dev.kord.rest.json.response.GetPruneResponse +import dev.kord.rest.json.response.ListThreadsResponse +import dev.kord.rest.json.response.NothingSerializer +import dev.kord.rest.json.response.PruneResponse +import io.ktor.http.HttpMethod import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.InternalSerializationApi @@ -523,7 +553,6 @@ sealed class Route( ) - object GlobalApplicationCommandsCreate : Route>( HttpMethod.Put, "/applications/${ApplicationId}/commands", @@ -538,7 +567,6 @@ sealed class Route( ) - object GlobalApplicationCommandGet : Route( HttpMethod.Get, @@ -567,7 +595,6 @@ sealed class Route( ) - object GuildApplicationCommandsCreate : Route>( HttpMethod.Put, "/applications/${ApplicationId}/guilds/${GuildId}/commands", @@ -597,6 +624,18 @@ sealed class Route( NoStrategy ) + object GuildScheduledEventsGet : Route>( + HttpMethod.Get, + "/guilds/$GuildId/events", + ListSerializer(DiscordGuildScheduledEvent.serializer()) + ) + + object GuildScheduledEventsPost : Route( + HttpMethod.Post, + "/guilds/$GuildId/events", + DiscordGuildScheduledEvent.serializer() + ) + object InteractionResponseCreate : Route( HttpMethod.Post, "/interactions/${InteractionId}/${InteractionToken}/callback", @@ -604,11 +643,11 @@ sealed class Route( ) object OriginalInteractionResponseGet : - Route( - HttpMethod.Get, - "/webhooks/${ApplicationId}/${InteractionToken}/messages/@original", - DiscordMessage.serializer() - ) + Route( + HttpMethod.Get, + "/webhooks/${ApplicationId}/${InteractionToken}/messages/@original", + DiscordMessage.serializer() + ) object OriginalInteractionResponseModify : Route( @@ -672,11 +711,11 @@ sealed class Route( NoStrategy ) - object SelfVoiceStatePatch: + object SelfVoiceStatePatch : Route(HttpMethod.Patch, "/guilds/${GuildId}/voice-states/@me", NoStrategy) - object OthersVoiceStatePatch: + object OthersVoiceStatePatch : Route(HttpMethod.Patch, "/guilds/${GuildId}/voice-states/${UserId}", NoStrategy) object StageInstanceGet : @@ -750,6 +789,7 @@ sealed class Route( "/channels/$ChannelId/users/@me/threads/archived/private", ListThreadsResponse.serializer() ) + object ActiveThreadsGet : Route( HttpMethod.Get, "/guilds/${GuildId}/threads/active", @@ -757,6 +797,25 @@ sealed class Route( ) + object GuildEventsGet : Route( + HttpMethod.Get, + "/guild-events/$ScheduledEventId", + DiscordGuildScheduledEvent.serializer() + ) + + object GuildEventsDelete : Route( + HttpMethod.Get, + "/guild-events/$ScheduledEventId", + NoStrategy + ) + + object GuildEventsPatch : Route( + HttpMethod.Patch, + "/guild-events/$ScheduledEventId", + DiscordGuildScheduledEvent.serializer() + ) + + companion object { val baseUrl = "https://discord.com/api/$restVersion" } @@ -782,6 +841,7 @@ sealed class Route( object CommandId : Key("{command.id}", true) object InteractionId : Key("interaction.id", true) object InteractionToken : Key("{interaction.token}", true) + object ScheduledEventId : Key("{event.id}", true) } diff --git a/rest/src/main/kotlin/service/GuildService.kt b/rest/src/main/kotlin/service/GuildService.kt index d60d8f443444..efcb4e406b05 100644 --- a/rest/src/main/kotlin/service/GuildService.kt +++ b/rest/src/main/kotlin/service/GuildService.kt @@ -2,17 +2,50 @@ package dev.kord.rest.service import dev.kord.common.annotation.DeprecatedSinceKord import dev.kord.common.annotation.KordExperimental -import dev.kord.common.entity.* +import dev.kord.common.entity.DiscordChannel +import dev.kord.common.entity.DiscordGuild +import dev.kord.common.entity.DiscordGuildMember +import dev.kord.common.entity.DiscordGuildScheduledEvent +import dev.kord.common.entity.DiscordGuildWidget +import dev.kord.common.entity.DiscordRole +import dev.kord.common.entity.DiscordWelcomeScreen +import dev.kord.common.entity.Snowflake import dev.kord.rest.builder.ban.BanCreateBuilder -import dev.kord.rest.builder.channel.* -import dev.kord.rest.builder.guild.* +import dev.kord.rest.builder.channel.CategoryCreateBuilder +import dev.kord.rest.builder.channel.GuildChannelPositionModifyBuilder +import dev.kord.rest.builder.channel.NewsChannelCreateBuilder +import dev.kord.rest.builder.channel.TextChannelCreateBuilder +import dev.kord.rest.builder.channel.VoiceChannelCreateBuilder +import dev.kord.rest.builder.guild.CurrentVoiceStateModifyBuilder +import dev.kord.rest.builder.guild.GuildCreateBuilder +import dev.kord.rest.builder.guild.GuildModifyBuilder +import dev.kord.rest.builder.guild.GuildWidgetModifyBuilder +import dev.kord.rest.builder.guild.VoiceStateModifyBuilder +import dev.kord.rest.builder.guild.WelcomeScreenModifyBuilder import dev.kord.rest.builder.integration.IntegrationModifyBuilder import dev.kord.rest.builder.member.MemberAddBuilder import dev.kord.rest.builder.member.MemberModifyBuilder import dev.kord.rest.builder.role.RoleCreateBuilder import dev.kord.rest.builder.role.RoleModifyBuilder import dev.kord.rest.builder.role.RolePositionsModifyBuilder -import dev.kord.rest.json.request.* +import dev.kord.rest.json.request.CurrentUserNicknameModifyRequest +import dev.kord.rest.json.request.CurrentVoiceStateModifyRequest +import dev.kord.rest.json.request.GuildBanCreateRequest +import dev.kord.rest.json.request.GuildChannelCreateRequest +import dev.kord.rest.json.request.GuildChannelPositionModifyRequest +import dev.kord.rest.json.request.GuildCreateRequest +import dev.kord.rest.json.request.GuildIntegrationCreateRequest +import dev.kord.rest.json.request.GuildIntegrationModifyRequest +import dev.kord.rest.json.request.GuildMemberAddRequest +import dev.kord.rest.json.request.GuildMemberModifyRequest +import dev.kord.rest.json.request.GuildModifyRequest +import dev.kord.rest.json.request.GuildRoleCreateRequest +import dev.kord.rest.json.request.GuildRoleModifyRequest +import dev.kord.rest.json.request.GuildRolePositionModifyRequest +import dev.kord.rest.json.request.GuildScheduledEventCreateRequest +import dev.kord.rest.json.request.GuildWelcomeScreenModifyRequest +import dev.kord.rest.json.request.GuildWidgetModifyRequest +import dev.kord.rest.json.request.VoiceStateModifyRequest import dev.kord.rest.json.response.ListThreadsResponse import dev.kord.rest.request.RequestHandler import dev.kord.rest.request.auditLogReason @@ -353,7 +386,11 @@ class GuildService(requestHandler: RequestHandler) : RestService(requestHandler) keys[Route.GuildId] = guildId } - suspend fun modifyGuildWidget(guildId: Snowflake, widget: GuildWidgetModifyRequest, reason: String? = null): DiscordGuildWidget = + suspend fun modifyGuildWidget( + guildId: Snowflake, + widget: GuildWidgetModifyRequest, + reason: String? = null + ): DiscordGuildWidget = call(Route.GuildWidgetPatch) { keys[Route.GuildId] = guildId body(GuildWidgetModifyRequest.serializer(), widget) @@ -374,7 +411,11 @@ class GuildService(requestHandler: RequestHandler) : RestService(requestHandler) keys[Route.GuildId] = guildId } - suspend fun modifyCurrentUserNickname(guildId: Snowflake, nick: CurrentUserNicknameModifyRequest, reason: String? = null) = + suspend fun modifyCurrentUserNickname( + guildId: Snowflake, + nick: CurrentUserNicknameModifyRequest, + reason: String? = null + ) = call(Route.GuildCurrentUserNickPatch) { keys[Route.GuildId] = guildId body(CurrentUserNicknameModifyRequest.serializer(), nick) @@ -386,7 +427,11 @@ class GuildService(requestHandler: RequestHandler) : RestService(requestHandler) } - suspend fun modifyGuildWelcomeScreen(guildId: Snowflake, request: GuildWelcomeScreenModifyRequest, reason: String? = null) = + suspend fun modifyGuildWelcomeScreen( + guildId: Snowflake, + request: GuildWelcomeScreenModifyRequest, + reason: String? = null + ) = call(Route.GuildWelcomeScreenPatch) { keys[Route.GuildId] = guildId body(GuildWelcomeScreenModifyRequest.serializer(), request) @@ -414,6 +459,26 @@ class GuildService(requestHandler: RequestHandler) : RestService(requestHandler) keys[Route.GuildId] = guildId } } + + suspend fun listScheduledEvents( + guildId: Snowflake, + withUserCount: Boolean? = null + ): List = + call(Route.GuildScheduledEventsGet) { + keys[Route.GuildId] = guildId + if (withUserCount != null) { + parameter("with_user_count", withUserCount) + } + } + + suspend fun createScheduledEvent( + guildId: Snowflake, + request: GuildScheduledEventCreateRequest + ) = call(Route.GuildScheduledEventsPost) { + keys[Route.GuildId] = guildId + + body(GuildScheduledEventCreateRequest.serializer(), request) + } } @OptIn(ExperimentalContracts::class) diff --git a/rest/src/main/kotlin/service/RestClient.kt b/rest/src/main/kotlin/service/RestClient.kt index f3a68e551dbc..afeff7635f9d 100644 --- a/rest/src/main/kotlin/service/RestClient.kt +++ b/rest/src/main/kotlin/service/RestClient.kt @@ -3,8 +3,8 @@ package dev.kord.rest.service import dev.kord.common.annotation.KordExperimental import dev.kord.common.annotation.KordUnsafe import dev.kord.rest.request.KtorRequestHandler -import dev.kord.rest.request.RequestHandler import dev.kord.rest.request.RequestBuilder +import dev.kord.rest.request.RequestHandler import dev.kord.rest.route.Route import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind @@ -23,6 +23,7 @@ class RestClient(requestHandler: RequestHandler) : RestService(requestHandler) { val template: TemplateService = TemplateService(requestHandler) val interaction: InteractionService = InteractionService(requestHandler) val stageInstance: StageInstanceService = StageInstanceService(requestHandler) + val guildEvents: ScheduledEventService = ScheduledEventService(requestHandler) /** * Sends a request to the given [route]. This function exposes a direct call to the Discord api and allows diff --git a/rest/src/main/kotlin/service/ScheduledEventService.kt b/rest/src/main/kotlin/service/ScheduledEventService.kt new file mode 100644 index 000000000000..d0435d0e8321 --- /dev/null +++ b/rest/src/main/kotlin/service/ScheduledEventService.kt @@ -0,0 +1,23 @@ +package dev.kord.rest.service + +import dev.kord.common.entity.Snowflake +import dev.kord.rest.json.request.ScheduledEventModifyRequest +import dev.kord.rest.request.RequestHandler +import dev.kord.rest.route.Route + +class ScheduledEventService(requestHandler: RequestHandler) : RestService(requestHandler) { + suspend fun getScheduledEvent(eventId: Snowflake) = call(Route.GuildEventsGet) { + keys[Route.ScheduledEventId] = eventId + } + + suspend fun deleteScheduledEvent(eventId: Snowflake) = call(Route.GuildEventsDelete) { + keys[Route.ScheduledEventId] = eventId + } + + suspend fun modifyScheduledEvent(eventId: Snowflake, request: ScheduledEventModifyRequest) = + call(Route.GuildEventsPatch) { + keys[Route.ScheduledEventId] = eventId + + body(ScheduledEventModifyRequest.serializer(), request) + } +} From 376018c15e012d1f7ee92e68d928e564cbf1cea8 Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Thu, 14 Oct 2021 17:34:13 +0200 Subject: [PATCH 04/11] Add request builders --- .../guild/ScheduledEventCreateBuilder.kt | 28 +++++++++++++ .../ScheduledEventModifyBuilder.kt | 39 +++++++++++++++++++ rest/src/main/kotlin/service/GuildService.kt | 27 +++++++++++++ .../kotlin/service/ScheduledEventService.kt | 19 +++++++++ 4 files changed, 113 insertions(+) create mode 100644 rest/src/main/kotlin/builder/guild/ScheduledEventCreateBuilder.kt create mode 100644 rest/src/main/kotlin/builder/scheduled_events/ScheduledEventModifyBuilder.kt diff --git a/rest/src/main/kotlin/builder/guild/ScheduledEventCreateBuilder.kt b/rest/src/main/kotlin/builder/guild/ScheduledEventCreateBuilder.kt new file mode 100644 index 000000000000..bb1347c048eb --- /dev/null +++ b/rest/src/main/kotlin/builder/guild/ScheduledEventCreateBuilder.kt @@ -0,0 +1,28 @@ +package dev.kord.rest.builder.guild + +import dev.kord.common.entity.ScheduledEntityType +import dev.kord.common.entity.Snowflake +import dev.kord.common.entity.StageInstancePrivacyLevel +import dev.kord.common.entity.optional.Optional +import dev.kord.common.entity.optional.OptionalSnowflake +import dev.kord.common.entity.optional.delegate.delegate +import dev.kord.rest.builder.RequestBuilder +import dev.kord.rest.json.request.GuildScheduledEventCreateRequest +import kotlinx.datetime.Instant + +class ScheduledEventCreateBuilder( + val name: String, + val privacyLevel: StageInstancePrivacyLevel, + val scheduledStartTime: Instant, + val entityType: ScheduledEntityType +) : RequestBuilder { + private var _channelId: OptionalSnowflake = OptionalSnowflake.Missing + var channelId: Snowflake? by ::_channelId.delegate() + + private var _description: Optional = Optional.Missing() + var description: String? by ::_description.delegate() + + override fun toRequest(): GuildScheduledEventCreateRequest = GuildScheduledEventCreateRequest( + _channelId, name, privacyLevel, scheduledStartTime, _description, entityType + ) +} diff --git a/rest/src/main/kotlin/builder/scheduled_events/ScheduledEventModifyBuilder.kt b/rest/src/main/kotlin/builder/scheduled_events/ScheduledEventModifyBuilder.kt new file mode 100644 index 000000000000..9809578d8633 --- /dev/null +++ b/rest/src/main/kotlin/builder/scheduled_events/ScheduledEventModifyBuilder.kt @@ -0,0 +1,39 @@ +package dev.kord.rest.builder.scheduled_events + +import dev.kord.common.entity.ScheduledEntityType +import dev.kord.common.entity.StageInstancePrivacyLevel +import dev.kord.common.entity.optional.Optional +import dev.kord.common.entity.optional.OptionalSnowflake +import dev.kord.common.entity.optional.delegate.delegate +import dev.kord.rest.builder.RequestBuilder +import dev.kord.rest.json.request.ScheduledEventModifyRequest +import kotlinx.datetime.Instant + +class ScheduledEventModifyBuilder : RequestBuilder { + private var _channelId: OptionalSnowflake = OptionalSnowflake.Missing + var channelId by ::_channelId.delegate() + + private var _name: Optional = Optional.Missing() + var name by ::_name.delegate() + + private var _privacyLevel: Optional = Optional.Missing() + var privacyLevel by ::_privacyLevel.delegate() + + private var _scheduledStartTime: Optional = Optional.Missing() + var scheduledStartTime by ::_scheduledStartTime.delegate() + + private var _description: Optional = Optional.Missing() + var description by ::_description.delegate() + + private var _entityType: Optional = Optional.Missing() + var entityType by ::_entityType.delegate() + + override fun toRequest(): ScheduledEventModifyRequest = ScheduledEventModifyRequest( + _channelId, + _name, + _privacyLevel, + _scheduledStartTime, + _description, + _entityType + ) +} diff --git a/rest/src/main/kotlin/service/GuildService.kt b/rest/src/main/kotlin/service/GuildService.kt index efcb4e406b05..2b9cf02889ea 100644 --- a/rest/src/main/kotlin/service/GuildService.kt +++ b/rest/src/main/kotlin/service/GuildService.kt @@ -9,7 +9,9 @@ import dev.kord.common.entity.DiscordGuildScheduledEvent import dev.kord.common.entity.DiscordGuildWidget import dev.kord.common.entity.DiscordRole import dev.kord.common.entity.DiscordWelcomeScreen +import dev.kord.common.entity.ScheduledEntityType import dev.kord.common.entity.Snowflake +import dev.kord.common.entity.StageInstancePrivacyLevel import dev.kord.rest.builder.ban.BanCreateBuilder import dev.kord.rest.builder.channel.CategoryCreateBuilder import dev.kord.rest.builder.channel.GuildChannelPositionModifyBuilder @@ -20,6 +22,7 @@ import dev.kord.rest.builder.guild.CurrentVoiceStateModifyBuilder import dev.kord.rest.builder.guild.GuildCreateBuilder import dev.kord.rest.builder.guild.GuildModifyBuilder import dev.kord.rest.builder.guild.GuildWidgetModifyBuilder +import dev.kord.rest.builder.guild.ScheduledEventCreateBuilder import dev.kord.rest.builder.guild.VoiceStateModifyBuilder import dev.kord.rest.builder.guild.WelcomeScreenModifyBuilder import dev.kord.rest.builder.integration.IntegrationModifyBuilder @@ -51,6 +54,7 @@ import dev.kord.rest.request.RequestHandler import dev.kord.rest.request.auditLogReason import dev.kord.rest.route.Position import dev.kord.rest.route.Route +import kotlinx.datetime.Instant import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -479,6 +483,29 @@ class GuildService(requestHandler: RequestHandler) : RestService(requestHandler) body(GuildScheduledEventCreateRequest.serializer(), request) } + + @OptIn(ExperimentalContracts::class) + suspend fun createScheduledEvent( + guildId: Snowflake, + name: String, + privacyLevel: StageInstancePrivacyLevel, + scheduledStartTime: Instant, + entityType: ScheduledEntityType, + builder: ScheduledEventCreateBuilder.() -> Unit = {} + ): DiscordGuildScheduledEvent { + contract { + callsInPlace(builder, InvocationKind.EXACTLY_ONCE) + } + + val appliedBuilder = ScheduledEventCreateBuilder( + name, + privacyLevel, + scheduledStartTime, + entityType + ).apply(builder) + + return createScheduledEvent(guildId, appliedBuilder.toRequest()) + } } @OptIn(ExperimentalContracts::class) diff --git a/rest/src/main/kotlin/service/ScheduledEventService.kt b/rest/src/main/kotlin/service/ScheduledEventService.kt index d0435d0e8321..6a91a18297aa 100644 --- a/rest/src/main/kotlin/service/ScheduledEventService.kt +++ b/rest/src/main/kotlin/service/ScheduledEventService.kt @@ -1,9 +1,14 @@ package dev.kord.rest.service +import dev.kord.common.entity.DiscordGuildScheduledEvent import dev.kord.common.entity.Snowflake +import dev.kord.rest.builder.scheduled_events.ScheduledEventModifyBuilder import dev.kord.rest.json.request.ScheduledEventModifyRequest import dev.kord.rest.request.RequestHandler import dev.kord.rest.route.Route +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract class ScheduledEventService(requestHandler: RequestHandler) : RestService(requestHandler) { suspend fun getScheduledEvent(eventId: Snowflake) = call(Route.GuildEventsGet) { @@ -20,4 +25,18 @@ class ScheduledEventService(requestHandler: RequestHandler) : RestService(reques body(ScheduledEventModifyRequest.serializer(), request) } + + @OptIn(ExperimentalContracts::class) + suspend inline fun modifyScheduledEvent( + eventId: Snowflake, + builder: ScheduledEventModifyBuilder.() -> Unit + ): DiscordGuildScheduledEvent { + contract { + callsInPlace(builder, InvocationKind.EXACTLY_ONCE) + } + + val appliedBuilder = ScheduledEventModifyBuilder().apply(builder) + + return modifyScheduledEvent(eventId, appliedBuilder.toRequest()) + } } From 904a4ef7a4d48e9d8f9f45077236ac54c824d79d Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Thu, 14 Oct 2021 18:04:13 +0200 Subject: [PATCH 05/11] Update existing entities --- common/src/main/kotlin/entity/AuditLog.kt | 53 ++++++++++++++++--- .../src/main/kotlin/entity/DiscordInvite.kt | 9 +++- rest/src/main/kotlin/service/InviteService.kt | 18 ++++++- 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/common/src/main/kotlin/entity/AuditLog.kt b/common/src/main/kotlin/entity/AuditLog.kt index 1903b93ef9b7..09725c9ebc88 100644 --- a/common/src/main/kotlin/entity/AuditLog.kt +++ b/common/src/main/kotlin/entity/AuditLog.kt @@ -3,12 +3,25 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalSnowflake import dev.kord.common.entity.optional.orEmpty -import kotlinx.serialization.* +import kotlinx.serialization.Contextual +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.SerializationException import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.descriptors.* -import kotlinx.serialization.encoding.* +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.descriptors.element +import kotlinx.serialization.encoding.CompositeDecoder +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.encoding.decodeStructure +import kotlinx.serialization.encoding.encodeStructure import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.serializer import dev.kord.common.Color as CommonColor import dev.kord.common.entity.DefaultMessageNotificationLevel as CommonDefaultMessageNotificationLevel import dev.kord.common.entity.ExplicitContentFilter as CommonExplicitContentFilter @@ -92,7 +105,7 @@ data class AuditLogEntryOptionalInfo( 2020-11-12 field is described as present but is in fact optional */ @SerialName("role_name") - val roleName: Optional = Optional.Missing(), + val roleName: Optional = Optional.Missing() ) @Serializable(with = AuditLogChange.Serializer::class) @@ -315,7 +328,26 @@ sealed class AuditLogChangeKey(val name: String, val serializer: KSerializer< object AutoArchiveDuration : AuditLogChangeKey("auto_archive_duration", serializer()) @SerialName("default_auto_archive_duration") - object DefaultAutoArchiveDuration : AuditLogChangeKey("default_auto_archive_duration", serializer()) + object DefaultAutoArchiveDuration : + AuditLogChangeKey("default_auto_archive_duration", serializer()) + + @SerialName("entity_type") + object EntityType : AuditLogChangeKey( + "entity_type", + serializer() + ) + + @SerialName("status") + object Status : AuditLogChangeKey( + "status", + serializer() + ) + + @SerialName("sku_ids") + object SkuIds : AuditLogChangeKey>( + "sku_ids", + serializer() + ) internal class Serializer(val type: KSerializer) : KSerializer> { override val descriptor: SerialDescriptor @@ -381,6 +413,9 @@ sealed class AuditLogChangeKey(val name: String, val serializer: KSerializer< "archived" -> Archived "auto_archive_duration" -> AutoArchiveDuration "default_auto_archive_duration" -> DefaultAutoArchiveDuration + "entity_type" -> EntityType + "status" -> Status + "sku_ids" -> SkuIds else -> Unknown(name) } as AuditLogChangeKey } @@ -431,6 +466,9 @@ sealed class AuditLogEvent(val value: Int) { object StickerCreate : AuditLogEvent(90) object StickerUpdate : AuditLogEvent(91) object StickerDelete : AuditLogEvent(92) + object GuildScheduledEventCreate : AuditLogEvent(100) + object GuildScheduledEventUpdate : AuditLogEvent(101) + object GuildScheduledEventDelete : AuditLogEvent(102) object ThreadCreate : AuditLogEvent(110) object ThreadUpdate : AuditLogEvent(111) object ThreadDelete : AuditLogEvent(112) @@ -486,6 +524,9 @@ sealed class AuditLogEvent(val value: Int) { 90 -> StickerCreate 91 -> StickerUpdate 92 -> StickerDelete + 100 -> GuildScheduledEventCreate + 101 -> GuildScheduledEventUpdate + 102 -> GuildScheduledEventDelete 110 -> ThreadCreate 111 -> ThreadUpdate 112 -> ThreadDelete @@ -493,4 +534,4 @@ sealed class AuditLogEvent(val value: Int) { } } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/entity/DiscordInvite.kt b/common/src/main/kotlin/entity/DiscordInvite.kt index 80efe57c6370..08046fc7ac04 100644 --- a/common/src/main/kotlin/entity/DiscordInvite.kt +++ b/common/src/main/kotlin/entity/DiscordInvite.kt @@ -2,6 +2,7 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalInt +import kotlinx.datetime.Instant import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -19,6 +20,12 @@ data class DiscordInvite( val approximatePresenceCount: OptionalInt = OptionalInt.Missing, @SerialName("approximate_member_count") val approximateMemberCount: OptionalInt = OptionalInt.Missing, + @SerialName("expires_at") + val expiresAt: Optional = Optional.Missing(), + @SerialName("stage_instance") + val stageInstance: Optional = Optional.Missing(), + @SerialName("guild_scheduled_event") + val guildScheduledEvent: Optional = Optional.Missing(), ) @Serializable @@ -42,4 +49,4 @@ data class DiscordInviteMetadata( val temporary: Boolean, @SerialName("created_at") val createdAt: String, -) \ No newline at end of file +) diff --git a/rest/src/main/kotlin/service/InviteService.kt b/rest/src/main/kotlin/service/InviteService.kt index 5d67b61b41b6..0f52cc99a67e 100644 --- a/rest/src/main/kotlin/service/InviteService.kt +++ b/rest/src/main/kotlin/service/InviteService.kt @@ -1,13 +1,27 @@ package dev.kord.rest.service +import dev.kord.common.entity.Snowflake import dev.kord.rest.request.RequestHandler import dev.kord.rest.request.auditLogReason import dev.kord.rest.route.Route class InviteService(requestHandler: RequestHandler) : RestService(requestHandler) { - suspend fun getInvite(code: String, withCounts: Boolean) = call(Route.InviteGet) { + suspend fun getInvite( + code: String, + withCounts: Boolean? = null, + withExpiration: Boolean? = null, + guildScheduledEventId: Snowflake? = null + ) = call(Route.InviteGet) { keys[Route.InviteCode] = code - parameter("with_counts", "$withCounts") + if (withCounts != null) { + parameter("with_counts", withCounts) + } + if (withExpiration != null) { + parameter("with_expiration", withExpiration) + } + if (guildScheduledEventId != null) { + parameter("guild_scheduled_event_id", guildScheduledEventId) + } } suspend fun deleteInvite(code: String, reason: String? = null) = call(Route.InviteDelete) { From 94f8fa6335338d94b9aef8c9dc7ab33b6d4dcf1c Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Thu, 14 Oct 2021 18:58:38 +0200 Subject: [PATCH 06/11] Add core representation --- core/src/main/kotlin/Kord.kt | 84 ++++++++-- core/src/main/kotlin/Unsafe.kt | 30 +++- .../src/main/kotlin/behavior/GuildBehavior.kt | 129 ++++++++++++--- .../behavior/GuildScheduledEventBehavior.kt | 89 ++++++++++ .../cache/data/GuildScheduledEventData.kt | 56 +++++++ .../main/kotlin/entity/GuildScheduledEvent.kt | 153 ++++++++++++++++++ .../exception/EntityNotFoundException.kt | 3 + .../kotlin/supplier/CacheAwareRestSupplier.kt | 21 ++- .../kotlin/supplier/CacheEntitySupplier.kt | 61 ++++++- .../main/kotlin/supplier/EntitySupplier.kt | 9 +- .../kotlin/supplier/FallbackEntitySupplier.kt | 21 ++- .../kotlin/supplier/RestEntitySupplier.kt | 80 ++++++++- core/src/samples/kotlin/PingBot.kt | 2 +- 13 files changed, 687 insertions(+), 51 deletions(-) create mode 100644 core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt create mode 100644 core/src/main/kotlin/cache/data/GuildScheduledEventData.kt create mode 100644 core/src/main/kotlin/entity/GuildScheduledEvent.kt diff --git a/core/src/main/kotlin/Kord.kt b/core/src/main/kotlin/Kord.kt index 006075b44624..d6bb3c298dae 100644 --- a/core/src/main/kotlin/Kord.kt +++ b/core/src/main/kotlin/Kord.kt @@ -5,7 +5,6 @@ import dev.kord.common.annotation.DeprecatedSinceKord import dev.kord.common.annotation.KordExperimental import dev.kord.common.annotation.KordUnsafe import dev.kord.common.entity.DiscordShard -import dev.kord.common.entity.PresenceStatus import dev.kord.common.entity.Snowflake import dev.kord.common.exception.RequestException import dev.kord.core.builder.kord.KordBuilder @@ -13,8 +12,24 @@ import dev.kord.core.builder.kord.KordRestOnlyBuilder import dev.kord.core.cache.data.ApplicationCommandData import dev.kord.core.cache.data.GuildData import dev.kord.core.cache.data.UserData -import dev.kord.core.entity.* -import dev.kord.core.entity.application.* +import dev.kord.core.entity.ApplicationInfo +import dev.kord.core.entity.Guild +import dev.kord.core.entity.GuildPreview +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.Invite +import dev.kord.core.entity.KordEntity +import dev.kord.core.entity.Region +import dev.kord.core.entity.Strategizable +import dev.kord.core.entity.User +import dev.kord.core.entity.Webhook +import dev.kord.core.entity.application.GlobalApplicationCommand +import dev.kord.core.entity.application.GlobalChatInputCommand +import dev.kord.core.entity.application.GlobalMessageCommand +import dev.kord.core.entity.application.GlobalUserCommand +import dev.kord.core.entity.application.GuildApplicationCommand +import dev.kord.core.entity.application.GuildChatInputCommand +import dev.kord.core.entity.application.GuildMessageCommand +import dev.kord.core.entity.application.GuildUserCommand import dev.kord.core.entity.channel.Channel import dev.kord.core.event.Event import dev.kord.core.exception.EntityNotFoundException @@ -23,18 +38,40 @@ import dev.kord.core.gateway.MasterGateway import dev.kord.core.gateway.handler.DefaultGatewayEventInterceptor import dev.kord.core.gateway.handler.GatewayEventInterceptor import dev.kord.core.gateway.start -import dev.kord.core.supplier.* +import dev.kord.core.supplier.EntitySupplier +import dev.kord.core.supplier.EntitySupplyStrategy +import dev.kord.core.supplier.getChannelOfOrNull +import dev.kord.core.supplier.getGlobalApplicationCommandOf +import dev.kord.core.supplier.getGlobalApplicationCommandOfOrNull +import dev.kord.core.supplier.getGuildApplicationCommandOf +import dev.kord.core.supplier.getGuildApplicationCommandOfOrNull import dev.kord.gateway.Gateway import dev.kord.gateway.builder.LoginBuilder import dev.kord.gateway.builder.PresenceBuilder -import dev.kord.gateway.builder.Shards import dev.kord.rest.builder.guild.GuildCreateBuilder -import dev.kord.rest.builder.interaction.* +import dev.kord.rest.builder.interaction.ApplicationCommandPermissionsBulkModifyBuilder +import dev.kord.rest.builder.interaction.ApplicationCommandPermissionsModifyBuilder +import dev.kord.rest.builder.interaction.ChatInputCreateBuilder +import dev.kord.rest.builder.interaction.MessageCommandCreateBuilder +import dev.kord.rest.builder.interaction.MultiApplicationCommandBuilder +import dev.kord.rest.builder.interaction.UserCommandCreateBuilder import dev.kord.rest.builder.user.CurrentUserModifyBuilder import dev.kord.rest.request.RestRequestException import dev.kord.rest.service.RestClient -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.buffer +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import mu.KLogger import mu.KotlinLogging import kotlin.contracts.ExperimentalContracts @@ -292,7 +329,11 @@ public class Kord( * @throws [RestRequestException] if something went wrong during the request. */ - public suspend fun getWebhookWithTokenOrNull(id: Snowflake, token: String, strategy: EntitySupplyStrategy<*>): Webhook? = + public suspend fun getWebhookWithTokenOrNull( + id: Snowflake, + token: String, + strategy: EntitySupplyStrategy<*> + ): Webhook? = strategy.supply(this).getWebhookWithTokenOrNull(id, token) @@ -390,7 +431,10 @@ public class Kord( } - public suspend fun getGuildApplicationCommandOrNull(guildId: Snowflake, commandId: Snowflake): GuildApplicationCommand? { + public suspend fun getGuildApplicationCommandOrNull( + guildId: Snowflake, + commandId: Snowflake + ): GuildApplicationCommand? { return defaultSupplier.getGuildApplicationCommandOrNull(resources.applicationId, guildId, commandId) } @@ -582,6 +626,21 @@ public class Kord( rest.interaction.bulkEditApplicationCommandPermissions(resources.applicationId, guildId, builder) } + /** + * Requests a [GuildScheduledEvent] by its [id]. + * + * @throws [RequestException] if anything went wrong during the request. + */ + public suspend inline fun getGuildScheduledEvent(id: Snowflake): GuildScheduledEvent = + defaultSupplier.getGuildScheduledEvent(id) + + /** + * Requests a [GuildScheduledEvent] by its [id] returns `null` if none could be found. + * + * @throws [RequestException] if anything went wrong during the request. + */ + public suspend inline fun getGuildScheduledEventOrNull(id: Snowflake): GuildScheduledEvent? = + defaultSupplier.getGuildScheduledEventOrNull(id) } @@ -608,7 +667,10 @@ public suspend inline fun Kord(token: String, builder: KordBuilder.() -> Unit = * The returned [Job] is a reference to the created coroutine, call [Job.cancel] to cancel the processing of any further * events for this [consumer]. */ -public inline fun Kord.on(scope: CoroutineScope = this, noinline consumer: suspend T.() -> Unit): Job = +public inline fun Kord.on( + scope: CoroutineScope = this, + noinline consumer: suspend T.() -> Unit +): Job = events.buffer(CoroutineChannel.UNLIMITED).filterIsInstance() .onEach { event -> scope.launch(event.coroutineContext) { runCatching { consumer(event) }.onFailure { kordLogger.catching(it) } } diff --git a/core/src/main/kotlin/Unsafe.kt b/core/src/main/kotlin/Unsafe.kt index c62fee21b9c5..61a23a032907 100644 --- a/core/src/main/kotlin/Unsafe.kt +++ b/core/src/main/kotlin/Unsafe.kt @@ -3,14 +3,33 @@ package dev.kord.core import dev.kord.common.annotation.KordExperimental import dev.kord.common.annotation.KordUnsafe import dev.kord.common.entity.Snowflake -import dev.kord.core.behavior.* -import dev.kord.core.behavior.channel.* +import dev.kord.core.behavior.GlobalApplicationCommandBehavior +import dev.kord.core.behavior.GuildApplicationCommandBehavior +import dev.kord.core.behavior.GuildBehavior +import dev.kord.core.behavior.GuildEmojiBehavior +import dev.kord.core.behavior.GuildScheduledEventBehavior +import dev.kord.core.behavior.MemberBehavior +import dev.kord.core.behavior.MessageBehavior +import dev.kord.core.behavior.RoleBehavior +import dev.kord.core.behavior.StageInstanceBehavior +import dev.kord.core.behavior.ThreadMemberBehavior +import dev.kord.core.behavior.UserBehavior +import dev.kord.core.behavior.WebhookBehavior +import dev.kord.core.behavior.channel.ChannelBehavior +import dev.kord.core.behavior.channel.GuildChannelBehavior +import dev.kord.core.behavior.channel.GuildMessageChannelBehavior +import dev.kord.core.behavior.channel.MessageChannelBehavior +import dev.kord.core.behavior.channel.NewsChannelBehavior +import dev.kord.core.behavior.channel.StoreChannelBehavior +import dev.kord.core.behavior.channel.TextChannelBehavior +import dev.kord.core.behavior.channel.TopGuildChannelBehavior +import dev.kord.core.behavior.channel.TopGuildMessageChannelBehavior +import dev.kord.core.behavior.channel.VoiceChannelBehavior import dev.kord.core.behavior.channel.threads.PrivateThreadParentChannelBehavior import dev.kord.core.behavior.channel.threads.ThreadChannelBehavior import dev.kord.core.behavior.channel.threads.ThreadParentChannelBehavior import dev.kord.core.behavior.interaction.ApplicationCommandInteractionBehavior import dev.kord.core.behavior.interaction.ComponentInteractionBehavior -import dev.kord.core.entity.interaction.ApplicationCommandInteraction import dev.kord.rest.service.InteractionService /** @@ -155,4 +174,9 @@ public class Unsafe(private val kord: Kord) { id, channelId, token, applicationId, kord ) + public fun guildScheduledEvent(id: Snowflake): GuildScheduledEventBehavior = + GuildScheduledEventBehavior( + id, + kord + ) } diff --git a/core/src/main/kotlin/behavior/GuildBehavior.kt b/core/src/main/kotlin/behavior/GuildBehavior.kt index e8bbfab950ce..7248e863fbb5 100644 --- a/core/src/main/kotlin/behavior/GuildBehavior.kt +++ b/core/src/main/kotlin/behavior/GuildBehavior.kt @@ -4,23 +4,71 @@ import dev.kord.cache.api.query import dev.kord.common.annotation.DeprecatedSinceKord import dev.kord.common.annotation.KordExperimental import dev.kord.common.entity.DiscordUser +import dev.kord.common.entity.ScheduledEntityType import dev.kord.common.entity.Snowflake +import dev.kord.common.entity.StageInstancePrivacyLevel import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.unwrap import dev.kord.common.exception.RequestException import dev.kord.core.Kord -import dev.kord.core.cache.data.* +import dev.kord.core.cache.data.ChannelData +import dev.kord.core.cache.data.EmojiData +import dev.kord.core.cache.data.GuildData +import dev.kord.core.cache.data.GuildScheduledEventData +import dev.kord.core.cache.data.GuildWidgetData +import dev.kord.core.cache.data.IntegrationData +import dev.kord.core.cache.data.InviteData +import dev.kord.core.cache.data.MemberData +import dev.kord.core.cache.data.PresenceData +import dev.kord.core.cache.data.RoleData +import dev.kord.core.cache.data.UserData +import dev.kord.core.cache.data.VoiceStateData +import dev.kord.core.cache.data.WelcomeScreenData import dev.kord.core.cache.idEq import dev.kord.core.catchDiscordError -import dev.kord.core.entity.* -import dev.kord.core.entity.application.* -import dev.kord.core.entity.channel.* +import dev.kord.core.entity.AuditLogEntry +import dev.kord.core.entity.Ban +import dev.kord.core.entity.Guild +import dev.kord.core.entity.GuildEmoji +import dev.kord.core.entity.GuildPreview +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.GuildWidget +import dev.kord.core.entity.Integration +import dev.kord.core.entity.Invite +import dev.kord.core.entity.KordEntity +import dev.kord.core.entity.Member +import dev.kord.core.entity.PartialGuild +import dev.kord.core.entity.Presence +import dev.kord.core.entity.Region +import dev.kord.core.entity.Role +import dev.kord.core.entity.Strategizable +import dev.kord.core.entity.Template +import dev.kord.core.entity.User +import dev.kord.core.entity.VoiceState +import dev.kord.core.entity.Webhook +import dev.kord.core.entity.WelcomeScreen +import dev.kord.core.entity.application.GuildApplicationCommand +import dev.kord.core.entity.application.GuildChatInputCommand +import dev.kord.core.entity.application.GuildMessageCommand +import dev.kord.core.entity.application.GuildUserCommand +import dev.kord.core.entity.channel.Category +import dev.kord.core.entity.channel.Channel +import dev.kord.core.entity.channel.GuildChannel +import dev.kord.core.entity.channel.NewsChannel +import dev.kord.core.entity.channel.TextChannel +import dev.kord.core.entity.channel.TopGuildChannel +import dev.kord.core.entity.channel.VoiceChannel import dev.kord.core.entity.channel.thread.ThreadChannel import dev.kord.core.event.guild.MembersChunkEvent import dev.kord.core.exception.EntityNotFoundException import dev.kord.core.sorted -import dev.kord.core.supplier.* +import dev.kord.core.supplier.EntitySupplier +import dev.kord.core.supplier.EntitySupplyStrategy import dev.kord.core.supplier.EntitySupplyStrategy.Companion.rest +import dev.kord.core.supplier.getChannelOf +import dev.kord.core.supplier.getChannelOfOrNull +import dev.kord.core.supplier.getGuildApplicationCommandOf +import dev.kord.core.supplier.getGuildApplicationCommandOfOrNull import dev.kord.gateway.Gateway import dev.kord.gateway.PrivilegedIntent import dev.kord.gateway.RequestGuildMembers @@ -29,20 +77,45 @@ import dev.kord.gateway.start import dev.kord.rest.Image import dev.kord.rest.builder.auditlog.AuditLogGetRequestBuilder import dev.kord.rest.builder.ban.BanCreateBuilder -import dev.kord.rest.builder.channel.* +import dev.kord.rest.builder.channel.CategoryCreateBuilder +import dev.kord.rest.builder.channel.GuildChannelPositionModifyBuilder +import dev.kord.rest.builder.channel.NewsChannelCreateBuilder +import dev.kord.rest.builder.channel.TextChannelCreateBuilder +import dev.kord.rest.builder.channel.VoiceChannelCreateBuilder import dev.kord.rest.builder.guild.EmojiCreateBuilder import dev.kord.rest.builder.guild.GuildModifyBuilder import dev.kord.rest.builder.guild.GuildWidgetModifyBuilder +import dev.kord.rest.builder.guild.ScheduledEventCreateBuilder import dev.kord.rest.builder.guild.WelcomeScreenModifyBuilder -import dev.kord.rest.builder.interaction.* +import dev.kord.rest.builder.interaction.ApplicationCommandPermissionsBulkModifyBuilder +import dev.kord.rest.builder.interaction.ChatInputCreateBuilder +import dev.kord.rest.builder.interaction.MessageCommandCreateBuilder +import dev.kord.rest.builder.interaction.MultiApplicationCommandBuilder +import dev.kord.rest.builder.interaction.UserCommandCreateBuilder import dev.kord.rest.builder.role.RoleCreateBuilder import dev.kord.rest.builder.role.RolePositionsModifyBuilder import dev.kord.rest.json.JsonErrorCode import dev.kord.rest.json.request.CurrentUserNicknameModifyRequest import dev.kord.rest.request.RestRequestException -import dev.kord.rest.service.* -import kotlinx.coroutines.flow.* -import java.util.* +import dev.kord.rest.service.RestClient +import dev.kord.rest.service.createCategory +import dev.kord.rest.service.createNewsChannel +import dev.kord.rest.service.createTextChannel +import dev.kord.rest.service.createVoiceChannel +import dev.kord.rest.service.modifyGuildWelcomeScreen +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onSubscription +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.flow.takeWhile +import kotlinx.coroutines.flow.transformWhile +import kotlinx.datetime.Instant +import java.util.Objects import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -69,7 +142,7 @@ public interface GuildBehavior : KordEntity, Strategizable { */ public val activeThreads: Flow - get() = supplier.getActiveThreads(id) + get() = supplier.getActiveThreads(id) /** * Requests to get all present webhooks for this guild. @@ -204,6 +277,9 @@ public interface GuildBehavior : KordEntity, Strategizable { return kord.gateway.gateways[shard.toInt()] } + public val scheduledEvents: Flow + get() = supplier.getGuildScheduledEvents(id) + /** * Executes the [request] on this gateway, returning a flow of [MembersChunkEvent] responses. * @@ -545,13 +621,13 @@ public interface GuildBehavior : KordEntity, Strategizable { override fun withStrategy(strategy: EntitySupplyStrategy<*>): GuildBehavior = GuildBehavior(id, kord, strategy) } -public suspend inline fun GuildBehavior.getApplicationCommandOfOrNull(commandId: Snowflake): T? { - return supplier.getGuildApplicationCommandOfOrNull(kord.resources.applicationId,id, commandId) +public suspend inline fun GuildBehavior.getApplicationCommandOfOrNull(commandId: Snowflake): T? { + return supplier.getGuildApplicationCommandOfOrNull(kord.resources.applicationId, id, commandId) } -public suspend inline fun GuildBehavior.getApplicationCommandOf(commandId: Snowflake): T? { - return supplier.getGuildApplicationCommandOf(kord.resources.applicationId,id, commandId) +public suspend inline fun GuildBehavior.getApplicationCommandOf(commandId: Snowflake): T? { + return supplier.getGuildApplicationCommandOf(kord.resources.applicationId, id, commandId) } @@ -590,8 +666,6 @@ public suspend inline fun GuildBehavior.createChatInputCommand( } - - @OptIn(ExperimentalContracts::class) public suspend inline fun GuildBehavior.createMessageCommand( @@ -613,7 +687,6 @@ public suspend inline fun GuildBehavior.createUserCommand( } - @OptIn(ExperimentalContracts::class) public suspend inline fun GuildBehavior.createApplicationCommands( @@ -993,10 +1066,28 @@ public inline fun GuildBehavior.requestMembers(builder: RequestGuildMembersBuild } @OptIn(ExperimentalContracts::class) - public suspend inline fun GuildBehavior.bulkEditSlashCommandPermissions(noinline builder: ApplicationCommandPermissionsBulkModifyBuilder.() -> Unit) { contract { callsInPlace(builder, InvocationKind.EXACTLY_ONCE) } kord.bulkEditApplicationCommandPermissions(id, builder) } + +/** + * Creates a new [GuildScheduledEvent]. + */ +@OptIn(ExperimentalContracts::class) +public suspend fun GuildBehavior.createScheduledEvent( + name: String, + privacyLevel: StageInstancePrivacyLevel, + scheduledStartTime: Instant, + entityType: ScheduledEntityType, + builder: ScheduledEventCreateBuilder.() -> Unit +): GuildScheduledEvent { + contract { callsInPlace(builder, InvocationKind.EXACTLY_ONCE) } + + val event = kord.rest.guild.createScheduledEvent(id, name, privacyLevel, scheduledStartTime, entityType, builder) + val data = GuildScheduledEventData.from(event) + + return GuildScheduledEvent(data, kord, supplier) +} diff --git a/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt b/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt new file mode 100644 index 000000000000..4f4f3ef36aab --- /dev/null +++ b/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt @@ -0,0 +1,89 @@ +package dev.kord.core.behavior + +import dev.kord.common.entity.DiscordGuildScheduledEvent +import dev.kord.common.entity.Snowflake +import dev.kord.common.exception.RequestException +import dev.kord.core.Kord +import dev.kord.core.entity.Guild +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.KordEntity +import dev.kord.core.entity.Strategizable +import dev.kord.core.exception.EntityNotFoundException +import dev.kord.core.supplier.EntitySupplier +import dev.kord.core.supplier.EntitySupplyStrategy +import dev.kord.rest.builder.scheduled_events.ScheduledEventModifyBuilder +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +/** + * Behavior of a [Discord Scheduled Guild Event](ADD LINK). + */ +public interface GuildScheduledEventBehavior : KordEntity, Strategizable { + /** + * Deletes this event. + * + * @throws RequestException if anything goes wrong during the request + */ + public suspend fun delete(): Unit = kord.rest.guildEvents.deleteScheduledEvent(id) + + /** + * Requests to get this behavior as a [GuildScheduledEvent]. + * + * @throws [RequestException] if anything went wrong during the request. + * @throws [EntityNotFoundException] if the event wasn't present. + */ + public suspend fun asGuildScheduledEvent(): GuildScheduledEvent = supplier.getGuildScheduledEvent(id) + + /** + * Requests to get this behavior as a [Guild], + * returns null if the event isn't present. + * + * @throws [RequestException] if anything went wrong during the request. + */ + public suspend fun asGuildScheduledEventOrNull(): GuildScheduledEvent? = supplier.getGuildScheduledEventOrNull(id) + + /** + * Fetches to get this behavior as a [GuildScheduledEvent]. + * + * @throws [RequestException] if anything went wrong during the request. + * @throws [EntityNotFoundException] if the event wasn't present. + */ + public suspend fun fetchGuildScheduledEvent(): GuildScheduledEvent = supplier.getGuildScheduledEvent(id) + + /** + * Fetches to get the this behavior as a [Guild], + * returns null if the event isn't present. + * + * @throws [RequestException] if anything went wrong during the request. + */ + public suspend fun fetchGuildScheduledEventOrNull(): GuildScheduledEvent? = + supplier.getGuildScheduledEventOrNull(id) +} + +internal fun GuildScheduledEventBehavior( + id: Snowflake, + kord: Kord, + supplier: EntitySupplier = kord.defaultSupplier +): GuildScheduledEventBehavior = object : GuildScheduledEventBehavior { + override val kord: Kord = kord + override val id: Snowflake = id + override val supplier: EntitySupplier = supplier + + override fun withStrategy(strategy: EntitySupplyStrategy<*>): Strategizable = + GuildScheduledEventBehavior(id, kord, strategy.supply(kord)) +} + +/** + * Requests to modify this event according to the specified [builder]. + * + * @throws RequestException if anything goes wrong during the request + */ +@OptIn(ExperimentalContracts::class) +public suspend inline fun GuildScheduledEventBehavior.edit(builder: ScheduledEventModifyBuilder.() -> Unit): DiscordGuildScheduledEvent { + contract { + callsInPlace(builder, InvocationKind.EXACTLY_ONCE) + } + + return kord.rest.guildEvents.modifyScheduledEvent(id, builder) +} diff --git a/core/src/main/kotlin/cache/data/GuildScheduledEventData.kt b/core/src/main/kotlin/cache/data/GuildScheduledEventData.kt new file mode 100644 index 000000000000..9d0b4db4739f --- /dev/null +++ b/core/src/main/kotlin/cache/data/GuildScheduledEventData.kt @@ -0,0 +1,56 @@ +package dev.kord.core.cache.data + +import dev.kord.common.entity.DiscordGuildScheduledEvent +import dev.kord.common.entity.GuildScheduledEventEntityMetadata +import dev.kord.common.entity.GuildScheduledEventStatus +import dev.kord.common.entity.ScheduledEntityType +import dev.kord.common.entity.Snowflake +import dev.kord.common.entity.StageInstancePrivacyLevel +import dev.kord.common.entity.optional.Optional +import kotlinx.datetime.Instant +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonArray + +@Serializable +public data class GuildScheduledEventData( + val id: Snowflake, + val guildId: Snowflake, + val channelId: Snowflake?, + val name: String, + val description: Optional = Optional.Missing(), + val image: String?, + val scheduledStartTime: Instant, + val scheduledEndTime: Instant?, + val privacyLevel: StageInstancePrivacyLevel, + val status: GuildScheduledEventStatus, + val type: ScheduledEntityType, + val entityId: Snowflake?, + val entityType: ScheduledEntityType, + val entityMetadata: GuildScheduledEventEntityMetadata, + val skuIds: List, + val skus: JsonArray, + val userCount: Int +) { + public companion object { + public fun from(event: DiscordGuildScheduledEvent): GuildScheduledEventData = GuildScheduledEventData( + event.id, + event.guildId, + event.channelId, + event.name, + event.description, + event.image, + event.scheduledStartTime, + event.scheduledEndTime, + event.privacyLevel, + event.status, + event.type, + event.entityId, + event.entityType, + event.entityMetadata, + event.skuIds, + event.skus, + event.userCount + ) + } +} + diff --git a/core/src/main/kotlin/entity/GuildScheduledEvent.kt b/core/src/main/kotlin/entity/GuildScheduledEvent.kt new file mode 100644 index 000000000000..41efcef90764 --- /dev/null +++ b/core/src/main/kotlin/entity/GuildScheduledEvent.kt @@ -0,0 +1,153 @@ +package dev.kord.core.entity + +import dev.kord.common.entity.GuildScheduledEventEntityMetadata +import dev.kord.common.entity.GuildScheduledEventStatus +import dev.kord.common.entity.ScheduledEntityType +import dev.kord.common.entity.Snowflake +import dev.kord.common.entity.StageInstancePrivacyLevel +import dev.kord.common.exception.RequestException +import dev.kord.core.Kord +import dev.kord.core.behavior.GuildScheduledEventBehavior +import dev.kord.core.cache.data.GuildScheduledEventData +import dev.kord.core.entity.channel.TopGuildChannel +import dev.kord.core.exception.EntityNotFoundException +import dev.kord.core.supplier.EntitySupplier +import dev.kord.core.supplier.EntitySupplyStrategy +import dev.kord.core.supplier.getChannelOfOrNull +import kotlinx.datetime.Instant +import kotlinx.serialization.json.JsonArray + +/** + * An instance of a [Guild scheduled event](ADD LINK) belonging to a specific guild. + */ +public class GuildScheduledEvent( + public val data: GuildScheduledEventData, + override val kord: Kord, + override val supplier: EntitySupplier = kord.defaultSupplier +) : GuildScheduledEventBehavior { + + /** + * The id of this event. + */ + public override val id: Snowflake + get() = data.id + + /** + * The id of the guild this event is on. + */ + public val guildId: Snowflake + get() = data.guildId + + /** + * The id of the channel this event is in, if any. + */ + public val channelId: Snowflake? + get() = data.channelId + + /** + * The name of this event. + */ + public val name: String + get() = data.name + + /** + * The description of this event, if any. + */ + public val description: String? + get() = data.description.value + + public val image: String? + get() = data.image + + /** + * The [Instant] in which this event will start. + */ + public val scheduledStartTime: Instant + get() = data.scheduledStartTime + + /** + * The [Instant] in which this event will end, if any. + */ + public val scheduledEndTime: Instant? + get() = data.scheduledEndTime + + /** + * The [privacy level][StageInstancePrivacyLevel] of this event. + */ + public val privacyLevel: StageInstancePrivacyLevel + get() = data.privacyLevel + + /** + * The [status][GuildScheduledEventStatus] of this event. + */ + public val status: GuildScheduledEventStatus + get() = data.status + + /** + * The [type][ScheduledEntityType] of this event. + */ + public val type: ScheduledEntityType + get() = data.type + + public val entityId: Snowflake? + get() = data.entityId + + /** + * The [scheduled entity type][ScheduledEntityType] for this event. + */ + public val entityType: ScheduledEntityType + get() = data.entityType + + public val entityMetadata: GuildScheduledEventEntityMetadata + get() = data.entityMetadata + public val skuIds: List + get() = data.skuIds + public val skus: JsonArray + get() = data.skus + + /** + * The amount of users subscribed to this event. + */ + public val userCount: Int + get() = data.userCount + + /** + * Requests the [Guild] this event is on. + * + * @throws [RequestException] if anything went wrong during the request. + * @throws [EntityNotFoundException] if the [Guild] wasn't present. + */ + public suspend fun getGuild(): Guild = supplier.getGuild(guildId) + + /** + * Requests the [Guild] this event is on, + * returns null if the [Guild] isn't present. + * + * @throws [RequestException] if anything went wrong during the request. + */ + public suspend fun getGuildOrNull(): Guild? = supplier.getGuildOrNull(guildId) + + /** + * Requests the [TopGuildChannel] this event is in, + * returns null if the [TopGuildChannel] isn't present or not set. + * + * @throws [RequestException] if anything went wrong during the request. + */ + public suspend fun getChannelOrNull(): TopGuildChannel? = data.channelId?.let { supplier.getChannelOfOrNull(it) } + + /** + * Requests the channel this event is in, if it is of type [T], + * returns `null` if the channel is not set, not present or not of type [T] + * + * @throws [RequestException] if anything went wrong during the request. + */ + public suspend inline fun getChannelOfOrNull(): T? = + data.channelId?.let { supplier.getChannelOfOrNull(it) } + + override suspend fun asGuildScheduledEvent(): GuildScheduledEvent = this + override suspend fun asGuildScheduledEventOrNull(): GuildScheduledEvent = this + + override fun withStrategy(strategy: EntitySupplyStrategy<*>): Strategizable = GuildScheduledEvent( + data, kord, strategy.supply(kord) + ) +} diff --git a/core/src/main/kotlin/exception/EntityNotFoundException.kt b/core/src/main/kotlin/exception/EntityNotFoundException.kt index 9ba3a7063965..3cd48b711445 100644 --- a/core/src/main/kotlin/exception/EntityNotFoundException.kt +++ b/core/src/main/kotlin/exception/EntityNotFoundException.kt @@ -70,6 +70,9 @@ public class EntityNotFoundException : Exception { public inline fun applicationCommandPermissionsNotFound(commandId: Snowflake): Nothing = entityNotFound("ApplicationCommand", commandId) + public inline fun guildScheduledEventNotFound(eventId: Snowflake): Nothing = + entityNotFound("GuildScheduledEvent", eventId) + public inline fun applicationCommandNotFound(commandId: Snowflake): Nothing = entityNotFound(T::class.simpleName!!, commandId) diff --git a/core/src/main/kotlin/supplier/CacheAwareRestSupplier.kt b/core/src/main/kotlin/supplier/CacheAwareRestSupplier.kt index feccf818079c..99f6f505924b 100644 --- a/core/src/main/kotlin/supplier/CacheAwareRestSupplier.kt +++ b/core/src/main/kotlin/supplier/CacheAwareRestSupplier.kt @@ -4,7 +4,20 @@ import dev.kord.cache.api.DataCache import dev.kord.cache.api.put import dev.kord.common.entity.Snowflake import dev.kord.core.Kord -import dev.kord.core.entity.* +import dev.kord.core.entity.Ban +import dev.kord.core.entity.Guild +import dev.kord.core.entity.GuildEmoji +import dev.kord.core.entity.GuildPreview +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.GuildWidget +import dev.kord.core.entity.Member +import dev.kord.core.entity.Message +import dev.kord.core.entity.Region +import dev.kord.core.entity.Role +import dev.kord.core.entity.StageInstance +import dev.kord.core.entity.Template +import dev.kord.core.entity.User +import dev.kord.core.entity.Webhook import dev.kord.core.entity.application.ApplicationCommandPermissions import dev.kord.core.entity.application.GlobalApplicationCommand import dev.kord.core.entity.application.GuildApplicationCommand @@ -219,6 +232,12 @@ public class StoreEntitySupplier( ) { it.data } } + override fun getGuildScheduledEvents(guildId: Snowflake): Flow = + storeOnEach(supplier.getGuildScheduledEvents(guildId)) { it.data } + + override suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? = + storeAndReturn(supplier.getGuildScheduledEvent(eventId)) { it.data } + override fun getGuildApplicationCommandPermissions( applicationId: Snowflake, guildId: Snowflake diff --git a/core/src/main/kotlin/supplier/CacheEntitySupplier.kt b/core/src/main/kotlin/supplier/CacheEntitySupplier.kt index 468f369faede..0fe423a90a74 100644 --- a/core/src/main/kotlin/supplier/CacheEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/CacheEntitySupplier.kt @@ -8,10 +8,37 @@ import dev.kord.common.entity.optional.Optional import dev.kord.common.exception.RequestException import dev.kord.core.Kord import dev.kord.core.any -import dev.kord.core.cache.data.* +import dev.kord.core.cache.data.ApplicationCommandData +import dev.kord.core.cache.data.BanData +import dev.kord.core.cache.data.ChannelData +import dev.kord.core.cache.data.EmojiData +import dev.kord.core.cache.data.GuildApplicationCommandPermissionsData +import dev.kord.core.cache.data.GuildData +import dev.kord.core.cache.data.GuildPreviewData +import dev.kord.core.cache.data.MemberData +import dev.kord.core.cache.data.MessageData +import dev.kord.core.cache.data.RegionData +import dev.kord.core.cache.data.RoleData +import dev.kord.core.cache.data.TemplateData +import dev.kord.core.cache.data.ThreadMemberData +import dev.kord.core.cache.data.UserData +import dev.kord.core.cache.data.WebhookData import dev.kord.core.cache.idEq import dev.kord.core.cache.idGt -import dev.kord.core.entity.* +import dev.kord.core.entity.Ban +import dev.kord.core.entity.Guild +import dev.kord.core.entity.GuildEmoji +import dev.kord.core.entity.GuildPreview +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.GuildWidget +import dev.kord.core.entity.Member +import dev.kord.core.entity.Message +import dev.kord.core.entity.Region +import dev.kord.core.entity.Role +import dev.kord.core.entity.StageInstance +import dev.kord.core.entity.Template +import dev.kord.core.entity.User +import dev.kord.core.entity.Webhook import dev.kord.core.entity.application.ApplicationCommandPermissions import dev.kord.core.entity.application.GlobalApplicationCommand import dev.kord.core.entity.application.GuildApplicationCommand @@ -22,7 +49,15 @@ import dev.kord.core.entity.channel.thread.ThreadMember import dev.kord.core.exception.EntityNotFoundException import dev.kord.gateway.Gateway import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.take import kotlinx.datetime.Instant import kotlinx.datetime.toInstant @@ -289,16 +324,16 @@ public class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { } override fun getActiveThreads(guildId: Snowflake): Flow = flow { - val result = cache.query { + val result = cache.query { idEq(ChannelData::guildId, guildId) }.toCollection() .sortedByDescending { it.id } .asFlow() .filter { - it.threadMetadata.value?.archived != true - }.mapNotNull { - Channel.from(it, kord) as? ThreadChannel - } + it.threadMetadata.value?.archived != true + }.mapNotNull { + Channel.from(it, kord) as? ThreadChannel + } emitAll(result) } @@ -424,6 +459,16 @@ public class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { return ApplicationCommandPermissions(data) } + override suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? = + cache.query() { + idEq(GuildScheduledEvent::id, eventId) + }.singleOrNull() + + override fun getGuildScheduledEvents(guildId: Snowflake): Flow = + cache.query() { + idEq(GuildScheduledEvent::guildId, guildId) + }.asFlow() + override fun toString(): String { return "CacheEntitySupplier(cache=$cache)" } diff --git a/core/src/main/kotlin/supplier/EntitySupplier.kt b/core/src/main/kotlin/supplier/EntitySupplier.kt index c057e9fe9154..3a7aeaf69df8 100644 --- a/core/src/main/kotlin/supplier/EntitySupplier.kt +++ b/core/src/main/kotlin/supplier/EntitySupplier.kt @@ -4,13 +4,12 @@ import dev.kord.common.entity.ChannelType.Unknown import dev.kord.common.entity.Snowflake import dev.kord.common.exception.RequestException import dev.kord.core.entity.* -import dev.kord.core.entity.application.ApplicationCommand import dev.kord.core.entity.application.ApplicationCommandPermissions import dev.kord.core.entity.application.GlobalApplicationCommand import dev.kord.core.entity.application.GuildApplicationCommand import dev.kord.core.entity.channel.Channel -import dev.kord.core.entity.channel.TopGuildChannel import dev.kord.core.entity.channel.MessageChannel +import dev.kord.core.entity.channel.TopGuildChannel import dev.kord.core.entity.channel.thread.ThreadChannel import dev.kord.core.entity.channel.thread.ThreadMember import dev.kord.core.exception.EntityNotFoundException @@ -467,6 +466,12 @@ public interface EntitySupplier { guildId: Snowflake, ): Flow + public fun getGuildScheduledEvents(guildId: Snowflake): Flow + + public suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? + + public suspend fun getGuildScheduledEvent(eventId: Snowflake): GuildScheduledEvent = + getGuildScheduledEventOrNull(eventId) ?: EntityNotFoundException.guildScheduledEventNotFound(eventId) } diff --git a/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt b/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt index 430fe9c323a6..d8fb9ce4a269 100644 --- a/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt @@ -1,7 +1,20 @@ package dev.kord.core.supplier import dev.kord.common.entity.Snowflake -import dev.kord.core.entity.* +import dev.kord.core.entity.Ban +import dev.kord.core.entity.Guild +import dev.kord.core.entity.GuildEmoji +import dev.kord.core.entity.GuildPreview +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.GuildWidget +import dev.kord.core.entity.Member +import dev.kord.core.entity.Message +import dev.kord.core.entity.Region +import dev.kord.core.entity.Role +import dev.kord.core.entity.StageInstance +import dev.kord.core.entity.Template +import dev.kord.core.entity.User +import dev.kord.core.entity.Webhook import dev.kord.core.entity.application.ApplicationCommandPermissions import dev.kord.core.entity.application.GlobalApplicationCommand import dev.kord.core.entity.application.GuildApplicationCommand @@ -198,6 +211,12 @@ private class FallbackEntitySupplier(val first: EntitySupplier, val second: Enti first.getGuildApplicationCommandPermissions(applicationId, guildId) .switchIfEmpty(second.getGuildApplicationCommandPermissions(applicationId, guildId)) + override fun getGuildScheduledEvents(guildId: Snowflake): Flow = + first.getGuildScheduledEvents(guildId).switchIfEmpty(second.getGuildScheduledEvents(guildId)) + + override suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? = + first.getGuildScheduledEventOrNull(eventId) ?: second.getGuildScheduledEventOrNull(eventId) + override fun toString(): String { return "FallbackEntitySupplier(first=$first, second=$second)" diff --git a/core/src/main/kotlin/supplier/RestEntitySupplier.kt b/core/src/main/kotlin/supplier/RestEntitySupplier.kt index b8be3fc3cd05..13cdbfd3e271 100644 --- a/core/src/main/kotlin/supplier/RestEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/RestEntitySupplier.kt @@ -5,9 +5,46 @@ import dev.kord.common.entity.DiscordPartialGuild import dev.kord.common.entity.Snowflake import dev.kord.common.entity.optional.OptionalSnowflake import dev.kord.common.entity.optional.optionalSnowflake -import dev.kord.core.* -import dev.kord.core.cache.data.* -import dev.kord.core.entity.* +import dev.kord.core.Kord +import dev.kord.core.cache.data.ApplicationCommandData +import dev.kord.core.cache.data.ApplicationInfoData +import dev.kord.core.cache.data.BanData +import dev.kord.core.cache.data.ChannelData +import dev.kord.core.cache.data.EmojiData +import dev.kord.core.cache.data.GuildApplicationCommandPermissionsData +import dev.kord.core.cache.data.GuildData +import dev.kord.core.cache.data.GuildPreviewData +import dev.kord.core.cache.data.GuildScheduledEventData +import dev.kord.core.cache.data.GuildWidgetData +import dev.kord.core.cache.data.InviteData +import dev.kord.core.cache.data.MessageData +import dev.kord.core.cache.data.RegionData +import dev.kord.core.cache.data.RoleData +import dev.kord.core.cache.data.StageInstanceData +import dev.kord.core.cache.data.ThreadMemberData +import dev.kord.core.cache.data.UserData +import dev.kord.core.cache.data.WebhookData +import dev.kord.core.cache.data.WelcomeScreenData +import dev.kord.core.cache.data.toData +import dev.kord.core.catchNotFound +import dev.kord.core.entity.ApplicationInfo +import dev.kord.core.entity.Ban +import dev.kord.core.entity.Guild +import dev.kord.core.entity.GuildEmoji +import dev.kord.core.entity.GuildPreview +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.GuildWidget +import dev.kord.core.entity.Invite +import dev.kord.core.entity.Member +import dev.kord.core.entity.Message +import dev.kord.core.entity.ReactionEmoji +import dev.kord.core.entity.Region +import dev.kord.core.entity.Role +import dev.kord.core.entity.StageInstance +import dev.kord.core.entity.Template +import dev.kord.core.entity.User +import dev.kord.core.entity.Webhook +import dev.kord.core.entity.WelcomeScreen import dev.kord.core.entity.application.ApplicationCommandPermissions import dev.kord.core.entity.application.GlobalApplicationCommand import dev.kord.core.entity.application.GuildApplicationCommand @@ -16,14 +53,32 @@ import dev.kord.core.entity.channel.TopGuildChannel import dev.kord.core.entity.channel.thread.ThreadChannel import dev.kord.core.entity.channel.thread.ThreadMember import dev.kord.core.exception.EntityNotFoundException +import dev.kord.core.paginateBackwards +import dev.kord.core.paginateForwards +import dev.kord.core.paginateThreads import dev.kord.rest.builder.auditlog.AuditLogGetRequestBuilder import dev.kord.rest.json.request.AuditLogGetRequest import dev.kord.rest.json.request.ListThreadsBySnowflakeRequest import dev.kord.rest.json.request.ListThreadsByTimestampRequest import dev.kord.rest.request.RestRequestException import dev.kord.rest.route.Position -import dev.kord.rest.service.* -import kotlinx.coroutines.flow.* +import dev.kord.rest.service.ApplicationService +import dev.kord.rest.service.AuditLogService +import dev.kord.rest.service.ChannelService +import dev.kord.rest.service.EmojiService +import dev.kord.rest.service.GuildService +import dev.kord.rest.service.InteractionService +import dev.kord.rest.service.InviteService +import dev.kord.rest.service.RestClient +import dev.kord.rest.service.TemplateService +import dev.kord.rest.service.UserService +import dev.kord.rest.service.VoiceService +import dev.kord.rest.service.WebhookService +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.take import kotlinx.datetime.Instant import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind @@ -454,6 +509,21 @@ public class RestEntitySupplier(public val kord: Kord) : EntitySupplier { } } + override fun getGuildScheduledEvents(guildId: Snowflake): Flow = flow { + kord.rest.guild.listScheduledEvents(guildId).forEach { + val data = GuildScheduledEventData.from(it) + + emit(GuildScheduledEvent(data, kord)) + } + } + + override suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? = catchNotFound { + val event = kord.rest.guildEvents.getScheduledEvent(eventId) + val data = GuildScheduledEventData.from(event) + + GuildScheduledEvent(data, kord) + } + override suspend fun getApplicationCommandPermissionsOrNull( applicationId: Snowflake, guildId: Snowflake, diff --git a/core/src/samples/kotlin/PingBot.kt b/core/src/samples/kotlin/PingBot.kt index 048abf4e1b50..588a3d287316 100644 --- a/core/src/samples/kotlin/PingBot.kt +++ b/core/src/samples/kotlin/PingBot.kt @@ -10,5 +10,5 @@ suspend fun main(args: Array) { if (message.content == "!ping") message.channel.createMessage("pong") } - kord.login { playing("!ping to pong") } + kord.login { presence { playing("!ping to pong") } } } From a2530e75002484a8bb8c5fbc3b44d6c8c4661814 Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Mon, 18 Oct 2021 05:17:40 +0200 Subject: [PATCH 07/11] Add proper event handling - Fix caching --- .../guild/GuildScheduledEventCreateEvent.kt | 26 +++++ .../guild/GuildScheduledEventDeleteEvent.kt | 27 +++++ .../event/guild/GuildScheduledEventEvent.kt | 66 +++++++++++ .../guild/GuildScheduledEventUpdateEvent.kt | 51 ++++++++ .../gateway/handler/GuildEventHandler.kt | 109 +++++++++++++++++- .../kotlin/supplier/CacheEntitySupplier.kt | 18 +-- 6 files changed, 284 insertions(+), 13 deletions(-) create mode 100644 core/src/main/kotlin/event/guild/GuildScheduledEventCreateEvent.kt create mode 100644 core/src/main/kotlin/event/guild/GuildScheduledEventDeleteEvent.kt create mode 100644 core/src/main/kotlin/event/guild/GuildScheduledEventEvent.kt create mode 100644 core/src/main/kotlin/event/guild/GuildScheduledEventUpdateEvent.kt diff --git a/core/src/main/kotlin/event/guild/GuildScheduledEventCreateEvent.kt b/core/src/main/kotlin/event/guild/GuildScheduledEventCreateEvent.kt new file mode 100644 index 000000000000..46ca684c6f96 --- /dev/null +++ b/core/src/main/kotlin/event/guild/GuildScheduledEventCreateEvent.kt @@ -0,0 +1,26 @@ +package dev.kord.core.event.guild + +import dev.kord.core.Kord +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.Strategizable +import dev.kord.core.event.kordCoroutineScope +import dev.kord.core.supplier.EntitySupplier +import dev.kord.core.supplier.EntitySupplyStrategy +import kotlinx.coroutines.CoroutineScope + +/** + * Event fired when a scheduled event got created. + * + * @see GuildScheduledEvent + * @see GuildScheduledEventEvent + */ +public data class GuildScheduledEventCreateEvent( + override val scheduledEvent: GuildScheduledEvent, + override val kord: Kord, + override val shard: Int, + override val supplier: EntitySupplier = kord.defaultSupplier, + public val coroutineScope: CoroutineScope = kordCoroutineScope(kord) +) : GuildScheduledEventEvent, CoroutineScope by coroutineScope { + override fun withStrategy(strategy: EntitySupplyStrategy<*>): Strategizable = + copy(supplier = strategy.supply(kord)) +} diff --git a/core/src/main/kotlin/event/guild/GuildScheduledEventDeleteEvent.kt b/core/src/main/kotlin/event/guild/GuildScheduledEventDeleteEvent.kt new file mode 100644 index 000000000000..9c1871b81aba --- /dev/null +++ b/core/src/main/kotlin/event/guild/GuildScheduledEventDeleteEvent.kt @@ -0,0 +1,27 @@ +package dev.kord.core.event.guild + +import dev.kord.core.Kord +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.Strategizable +import dev.kord.core.event.kordCoroutineScope +import dev.kord.core.supplier.EntitySupplier +import dev.kord.core.supplier.EntitySupplyStrategy +import kotlinx.coroutines.CoroutineScope + +/** + * Event fired when a scheduled event got deleted. + * Use [GuildScheduledEvent.status] to know why the event got deleted. + * + * @see GuildScheduledEvent + * @see GuildScheduledEvent + */ +public data class GuildScheduledEventDeleteEvent( + public override val scheduledEvent: GuildScheduledEvent, + override val kord: Kord, + override val shard: Int, + override val supplier: EntitySupplier = kord.defaultSupplier, + public val coroutineScope: CoroutineScope = kordCoroutineScope(kord) +) : GuildScheduledEventEvent, CoroutineScope by coroutineScope { + override fun withStrategy(strategy: EntitySupplyStrategy<*>): Strategizable = + copy(supplier = strategy.supply(kord)) +} diff --git a/core/src/main/kotlin/event/guild/GuildScheduledEventEvent.kt b/core/src/main/kotlin/event/guild/GuildScheduledEventEvent.kt new file mode 100644 index 000000000000..0fad75f664cb --- /dev/null +++ b/core/src/main/kotlin/event/guild/GuildScheduledEventEvent.kt @@ -0,0 +1,66 @@ +package dev.kord.core.event.guild + +import dev.kord.common.entity.Snowflake +import dev.kord.common.exception.RequestException +import dev.kord.core.entity.Guild +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.Strategizable +import dev.kord.core.entity.channel.TopGuildChannel +import dev.kord.core.event.Event +import dev.kord.core.exception.EntityNotFoundException +import dev.kord.core.supplier.getChannelOfOrNull +import kotlinx.coroutines.CoroutineScope + +/** + * Interface of all events related to [GuildScheduledEvents][GuildScheduledEvent]. + * + * @see GuildScheduledEventCreateEvent + * @see GuildScheduledEventUpdateEvent + * @see GuildScheduledEventDeleteEvent + */ +public interface GuildScheduledEventEvent : Event, CoroutineScope, Strategizable { + + /** + * The [GuildScheduledEvent]. + */ + public val scheduledEvent: GuildScheduledEvent + + /** + * The id of [scheduledEvent]. + */ + public val scheduledEventId: Snowflake get() = scheduledEvent.id + + /** + * The id of the guild the event is on. + */ + public val guildId: Snowflake get() = scheduledEvent.guildId + + /** + * The id of the channel the event is in, if any. + */ + public val channelId: Snowflake? get() = scheduledEvent.channelId + + /** + * Requests the [Guild] this event is on. + * + * @throws [RequestException] if anything went wrong during the request. + * @throws [EntityNotFoundException] if the [Guild] wasn't present. + */ + public suspend fun getGuild(): Guild = supplier.getGuild(guildId) + + /** + * Requests the [Guild] this event is on, + * returns null if the [Guild] isn't present. + * + * @throws [RequestException] if anything went wrong during the request. + */ + public suspend fun getGuildOrNull(): Guild? = supplier.getGuildOrNull(guildId) + + /** + * Requests the [TopGuildChannel] this event is in, + * returns null if the [TopGuildChannel] isn't present or not set. + * + * @throws [RequestException] if anything went wrong during the request. + */ + public suspend fun getChannelOrNull(): TopGuildChannel? = channelId?.let { supplier.getChannelOfOrNull(it) } +} diff --git a/core/src/main/kotlin/event/guild/GuildScheduledEventUpdateEvent.kt b/core/src/main/kotlin/event/guild/GuildScheduledEventUpdateEvent.kt new file mode 100644 index 000000000000..fb10500df198 --- /dev/null +++ b/core/src/main/kotlin/event/guild/GuildScheduledEventUpdateEvent.kt @@ -0,0 +1,51 @@ +package dev.kord.core.event.guild + +import dev.kord.common.entity.Snowflake +import dev.kord.common.exception.RequestException +import dev.kord.core.Kord +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.Strategizable +import dev.kord.core.entity.channel.TopGuildChannel +import dev.kord.core.event.kordCoroutineScope +import dev.kord.core.supplier.EntitySupplier +import dev.kord.core.supplier.EntitySupplyStrategy +import kotlinx.coroutines.CoroutineScope + +/** + * Event fired if a [GuildScheduledEvent] gets updated. + * + * @property scheduledEvent the updated event, for the old event use [oldEvent] + * @property oldEvent the event that was in the cache before the update + * + * @see GuildScheduledEvent + * @see GuildScheduledEventEvent + */ +public data class GuildScheduledEventUpdateEvent( + override val scheduledEvent: GuildScheduledEvent, + public val oldEvent: GuildScheduledEvent?, + override val kord: Kord, + override val shard: Int, + override val supplier: EntitySupplier = kord.defaultSupplier, + public val coroutineScope: CoroutineScope = kordCoroutineScope(kord) +) : GuildScheduledEventEvent, CoroutineScope by coroutineScope { + + /** + * The channel id of the channel the event is in. + * + * **Note:** This is going to represent the channel of [scheduledEvent] for the previous channel use [oldEvent] + */ + override val channelId: Snowflake? + get() = super.channelId + + /** + * Requests the [TopGuildChannel] this event is in, + * returns null if the [TopGuildChannel] isn't present or not set. + * + * **Note:** This is going to represent the channel of [scheduledEvent] for the previous channel use [oldEvent] + * @throws [RequestException] if anything went wrong during the request. + */ + override suspend fun getChannelOrNull(): TopGuildChannel? = super.getChannelOrNull() + + override fun withStrategy(strategy: EntitySupplyStrategy<*>): Strategizable = + copy(supplier = strategy.supply(kord)) +} diff --git a/core/src/main/kotlin/gateway/handler/GuildEventHandler.kt b/core/src/main/kotlin/gateway/handler/GuildEventHandler.kt index 2e2185958bc6..814b1a65c7dc 100644 --- a/core/src/main/kotlin/gateway/handler/GuildEventHandler.kt +++ b/core/src/main/kotlin/gateway/handler/GuildEventHandler.kt @@ -7,17 +7,51 @@ import dev.kord.cache.api.query import dev.kord.common.entity.optional.optionalSnowflake import dev.kord.common.entity.optional.orEmpty import dev.kord.core.Kord -import dev.kord.core.cache.data.* +import dev.kord.core.cache.data.ChannelData +import dev.kord.core.cache.data.EmojiData +import dev.kord.core.cache.data.GuildData +import dev.kord.core.cache.data.GuildScheduledEventData +import dev.kord.core.cache.data.InviteCreateData +import dev.kord.core.cache.data.InviteDeleteData +import dev.kord.core.cache.data.MemberData +import dev.kord.core.cache.data.MembersChunkData +import dev.kord.core.cache.data.PresenceData +import dev.kord.core.cache.data.RoleData +import dev.kord.core.cache.data.UserData +import dev.kord.core.cache.data.VoiceStateData +import dev.kord.core.cache.data.id import dev.kord.core.cache.idEq -import dev.kord.core.entity.* -import dev.kord.core.event.guild.* +import dev.kord.core.entity.Guild +import dev.kord.core.entity.GuildEmoji +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.Member +import dev.kord.core.entity.Presence +import dev.kord.core.entity.Role +import dev.kord.core.entity.User +import dev.kord.core.event.guild.BanAddEvent +import dev.kord.core.event.guild.BanRemoveEvent +import dev.kord.core.event.guild.EmojisUpdateEvent +import dev.kord.core.event.guild.GuildCreateEvent +import dev.kord.core.event.guild.GuildDeleteEvent +import dev.kord.core.event.guild.GuildScheduledEventCreateEvent +import dev.kord.core.event.guild.GuildScheduledEventDeleteEvent +import dev.kord.core.event.guild.GuildScheduledEventUpdateEvent +import dev.kord.core.event.guild.GuildUpdateEvent +import dev.kord.core.event.guild.IntegrationsUpdateEvent +import dev.kord.core.event.guild.InviteCreateEvent +import dev.kord.core.event.guild.InviteDeleteEvent +import dev.kord.core.event.guild.MemberJoinEvent +import dev.kord.core.event.guild.MemberLeaveEvent +import dev.kord.core.event.guild.MemberUpdateEvent +import dev.kord.core.event.guild.MembersChunkEvent import dev.kord.core.event.role.RoleCreateEvent import dev.kord.core.event.role.RoleDeleteEvent import dev.kord.core.event.role.RoleUpdateEvent import dev.kord.core.event.user.PresenceUpdateEvent import dev.kord.gateway.* import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.singleOrNull import dev.kord.common.entity.DiscordGuild as GatewayGuild import dev.kord.core.event.Event as CoreEvent @@ -42,6 +76,9 @@ internal class GuildEventHandler( is GuildRoleUpdate -> handle(event, shard, kord, coroutineScope) is GuildRoleDelete -> handle(event, shard, kord, coroutineScope) is GuildMembersChunk -> handle(event, shard, kord, coroutineScope) + is GuildScheduledEventCreate -> handle(event, shard, kord, coroutineScope) + is GuildScheduledEventUpdate -> handle(event, shard, kord, coroutineScope) + is GuildScheduledEventDelete -> handle(event, shard, kord, coroutineScope) is PresenceUpdate -> handle(event, shard, kord, coroutineScope) is InviteCreate -> handle(event, shard, kord, coroutineScope) is InviteDelete -> handle(event, shard, kord, coroutineScope) @@ -115,7 +152,12 @@ internal class GuildEventHandler( return GuildDeleteEvent(id, unavailable.orElse(false), old, kord, shard, coroutineScope = coroutineScope) } - private suspend fun handle(event: GuildBanAdd, shard: Int, kord: Kord, coroutineScope: CoroutineScope): BanAddEvent = + private suspend fun handle( + event: GuildBanAdd, + shard: Int, + kord: Kord, + coroutineScope: CoroutineScope + ): BanAddEvent = with(event.ban) { val data = UserData.from(user) cache.put(user) @@ -280,6 +322,53 @@ internal class GuildEventHandler( return MembersChunkEvent(MembersChunkData.from(this), kord, shard, coroutineScope = coroutineScope) } + private suspend fun handle( + event: GuildScheduledEventCreate, + shard: Int, + kord: Kord, + coroutineScope: CoroutineScope + ): GuildScheduledEventCreateEvent { + val eventData = GuildScheduledEventData.from(event.event) + cache.put(eventData) + val scheduledEvent = GuildScheduledEvent(eventData, kord) + + return GuildScheduledEventCreateEvent(scheduledEvent, kord, shard, coroutineScope = coroutineScope) + } + + private suspend fun handle( + event: GuildScheduledEventUpdate, + shard: Int, + kord: Kord, + coroutineScope: CoroutineScope + ): GuildScheduledEventUpdateEvent { + val eventData = GuildScheduledEventData.from(event.event) + val oldData = cache.query { + idEq(GuildScheduledEventData::id, event.event.id) + }.singleOrNull() + val old = oldData?.let { GuildScheduledEvent(it, kord) } + cache.put(eventData) + val scheduledEvent = GuildScheduledEvent(eventData, kord) + + return GuildScheduledEventUpdateEvent(scheduledEvent, old, kord, shard, coroutineScope = coroutineScope) + } + + private suspend fun handle( + event: GuildScheduledEventDelete, + shard: Int, + kord: Kord, + coroutineScope: CoroutineScope + ): GuildScheduledEventDeleteEvent { + val query = cache.query { + idEq(GuildScheduledEvent::id, event.event.id) + } + query.remove() + + val eventData = GuildScheduledEventData.from(event.event) + val scheduledEvent = GuildScheduledEvent(eventData, kord) + + return GuildScheduledEventDeleteEvent(scheduledEvent, kord, shard, coroutineScope = coroutineScope) + } + private suspend fun handle( event: PresenceUpdate, shard: Int, @@ -300,7 +389,15 @@ internal class GuildEventHandler( .singleOrNull() ?.let { User(it, kord) } - return PresenceUpdateEvent(user, this.user, guildId.value!!, old, new, shard, coroutineScope = coroutineScope) + return PresenceUpdateEvent( + user, + this.user, + guildId.value!!, + old, + new, + shard, + coroutineScope = coroutineScope + ) } private suspend fun handle( diff --git a/core/src/main/kotlin/supplier/CacheEntitySupplier.kt b/core/src/main/kotlin/supplier/CacheEntitySupplier.kt index 0fe423a90a74..a913b3731d33 100644 --- a/core/src/main/kotlin/supplier/CacheEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/CacheEntitySupplier.kt @@ -15,6 +15,7 @@ import dev.kord.core.cache.data.EmojiData import dev.kord.core.cache.data.GuildApplicationCommandPermissionsData import dev.kord.core.cache.data.GuildData import dev.kord.core.cache.data.GuildPreviewData +import dev.kord.core.cache.data.GuildScheduledEventData import dev.kord.core.cache.data.MemberData import dev.kord.core.cache.data.MessageData import dev.kord.core.cache.data.RegionData @@ -459,15 +460,18 @@ public class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { return ApplicationCommandPermissions(data) } - override suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? = - cache.query() { - idEq(GuildScheduledEvent::id, eventId) - }.singleOrNull() + override suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? { + val data = cache.query { + idEq(GuildScheduledEventData::id, eventId) + }.singleOrNull() ?: return null + + return GuildScheduledEvent(data, kord) + } override fun getGuildScheduledEvents(guildId: Snowflake): Flow = - cache.query() { - idEq(GuildScheduledEvent::guildId, guildId) - }.asFlow() + cache.query { + idEq(GuildScheduledEventData::guildId, guildId) + }.asFlow().map { GuildScheduledEvent(it, kord) } override fun toString(): String { return "CacheEntitySupplier(cache=$cache)" From dbea5828d24184c8b2be6cb032d7c06a69591f30 Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Thu, 18 Nov 2021 16:07:52 +0100 Subject: [PATCH 08/11] Add new AuditLog changes --- common/src/main/kotlin/entity/AuditLog.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/src/main/kotlin/entity/AuditLog.kt b/common/src/main/kotlin/entity/AuditLog.kt index 09725c9ebc88..5aa69c029b7b 100644 --- a/common/src/main/kotlin/entity/AuditLog.kt +++ b/common/src/main/kotlin/entity/AuditLog.kt @@ -276,6 +276,9 @@ sealed class AuditLogChangeKey(val name: String, val serializer: KSerializer< @SerialName("inviter_id") object InviterId : AuditLogChangeKey("inviter_id", serializer()) + @SerialName("location") + object Location : AuditLogChangeKey("location", serializer()) + @SerialName("max_uses") object MaxUses : AuditLogChangeKey("max_uses", serializer()) @@ -395,6 +398,7 @@ sealed class AuditLogChangeKey(val name: String, val serializer: KSerializer< "code" -> Code "channel_id" -> ChannelId "inviter_id" -> InviterId + "location" -> Location "max_uses" -> MaxUses "uses" -> Uses "max_age" -> MaxAges From 957eaba397e0e290bf254998248a193d74efe62e Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Thu, 18 Nov 2021 16:17:04 +0100 Subject: [PATCH 09/11] Update GuildScheduledEvent models to new PR changes --- .../entity/DiscordGuildScheduledEvent.kt | 23 +++++++--------- .../cache/data/GuildScheduledEventData.kt | 15 +++++------ .../main/kotlin/entity/GuildScheduledEvent.kt | 27 +++++++++---------- 3 files changed, 29 insertions(+), 36 deletions(-) diff --git a/common/src/main/kotlin/entity/DiscordGuildScheduledEvent.kt b/common/src/main/kotlin/entity/DiscordGuildScheduledEvent.kt index c22e98186010..0bce62df8bf3 100644 --- a/common/src/main/kotlin/entity/DiscordGuildScheduledEvent.kt +++ b/common/src/main/kotlin/entity/DiscordGuildScheduledEvent.kt @@ -1,6 +1,7 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional +import dev.kord.common.entity.optional.OptionalSnowflake import kotlinx.datetime.Instant import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -10,7 +11,6 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.json.JsonArray /** * Representation of a [Guild Scheduled Event Structure](ADD LINK). @@ -18,9 +18,9 @@ import kotlinx.serialization.json.JsonArray * @property id the id of the event * @property guildId the id of the guild the event is on * @property channelId the id of the channel the event is in + * @property creatorId the id of the user that created the scheduled event * @property name the name of the event * @property description the description of the event - * @property image the image of the event * @property scheduledStartTime the [Instant] in which the event will start * @property scheduledEndTime the [Instant] in which the event wil stop, if any * @property privacyLevel the [event privacy level][StageInstancePrivacyLevel] @@ -28,8 +28,7 @@ import kotlinx.serialization.json.JsonArray * @property entityType the [ScheduledEntityType] of the event * @property entityId entity id * @property entityMetadata [metadata][GuildScheduledEventEntityMetadata] for the event - * @property skuIds sku ids - * @property skus skus + * @property creator the [user][DiscordUser] that created the scheduled event * @property userCount users subscribed to the event */ @Serializable @@ -38,9 +37,10 @@ data class DiscordGuildScheduledEvent( @SerialName("guild_id") val guildId: Snowflake, val channelId: Snowflake?, + @SerialName("creator_id") + val creatorId: OptionalSnowflake, val name: String, val description: Optional = Optional.Missing(), - val image: String?, @SerialName("scheduled_start_time") val scheduledStartTime: Instant, @SerialName("scheduled_end_time") @@ -48,16 +48,13 @@ data class DiscordGuildScheduledEvent( @SerialName("privacy_level") val privacyLevel: StageInstancePrivacyLevel, val status: GuildScheduledEventStatus, - val type: ScheduledEntityType, - @SerialName("entity_id") - val entityId: Snowflake?, @SerialName("entity_type") val entityType: ScheduledEntityType, + @SerialName("entity_id") + val entityId: Snowflake?, @SerialName("entity_metadata") val entityMetadata: GuildScheduledEventEntityMetadata, - @SerialName("sku_ids") - val skuIds: List, - val skus: JsonArray, + val creator: Optional, @SerialName("user_count") val userCount: Int ) @@ -67,7 +64,7 @@ sealed class ScheduledEntityType(val value: Int) { object None : ScheduledEntityType(0) object StageInstance : ScheduledEntityType(1) object Voice : ScheduledEntityType(2) - object Location : ScheduledEntityType(3) + object External : ScheduledEntityType(3) class Unknown(value: Int) : ScheduledEntityType(value) companion object Serializer : KSerializer { @@ -78,7 +75,7 @@ sealed class ScheduledEntityType(val value: Int) { 0 -> None 1 -> StageInstance 2 -> Voice - 3 -> Location + 3 -> External else -> Unknown(value) } } diff --git a/core/src/main/kotlin/cache/data/GuildScheduledEventData.kt b/core/src/main/kotlin/cache/data/GuildScheduledEventData.kt index 9d0b4db4739f..b862b9afbace 100644 --- a/core/src/main/kotlin/cache/data/GuildScheduledEventData.kt +++ b/core/src/main/kotlin/cache/data/GuildScheduledEventData.kt @@ -7,28 +7,27 @@ import dev.kord.common.entity.ScheduledEntityType import dev.kord.common.entity.Snowflake import dev.kord.common.entity.StageInstancePrivacyLevel import dev.kord.common.entity.optional.Optional +import dev.kord.common.entity.optional.OptionalSnowflake +import dev.kord.common.entity.optional.map import kotlinx.datetime.Instant import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonArray @Serializable public data class GuildScheduledEventData( val id: Snowflake, val guildId: Snowflake, val channelId: Snowflake?, + val creatorId: OptionalSnowflake = OptionalSnowflake.Missing, val name: String, val description: Optional = Optional.Missing(), - val image: String?, val scheduledStartTime: Instant, val scheduledEndTime: Instant?, val privacyLevel: StageInstancePrivacyLevel, val status: GuildScheduledEventStatus, - val type: ScheduledEntityType, val entityId: Snowflake?, val entityType: ScheduledEntityType, val entityMetadata: GuildScheduledEventEntityMetadata, - val skuIds: List, - val skus: JsonArray, + val creator: Optional = Optional.Missing(), val userCount: Int ) { public companion object { @@ -36,19 +35,17 @@ public data class GuildScheduledEventData( event.id, event.guildId, event.channelId, + event.creatorId, event.name, event.description, - event.image, event.scheduledStartTime, event.scheduledEndTime, event.privacyLevel, event.status, - event.type, event.entityId, event.entityType, event.entityMetadata, - event.skuIds, - event.skus, + event.creator.map { UserData.from(it) }, event.userCount ) } diff --git a/core/src/main/kotlin/entity/GuildScheduledEvent.kt b/core/src/main/kotlin/entity/GuildScheduledEvent.kt index 41efcef90764..10bc2fe2010f 100644 --- a/core/src/main/kotlin/entity/GuildScheduledEvent.kt +++ b/core/src/main/kotlin/entity/GuildScheduledEvent.kt @@ -5,6 +5,7 @@ import dev.kord.common.entity.GuildScheduledEventStatus import dev.kord.common.entity.ScheduledEntityType import dev.kord.common.entity.Snowflake import dev.kord.common.entity.StageInstancePrivacyLevel +import dev.kord.common.entity.optional.unwrap import dev.kord.common.exception.RequestException import dev.kord.core.Kord import dev.kord.core.behavior.GuildScheduledEventBehavior @@ -15,7 +16,6 @@ import dev.kord.core.supplier.EntitySupplier import dev.kord.core.supplier.EntitySupplyStrategy import dev.kord.core.supplier.getChannelOfOrNull import kotlinx.datetime.Instant -import kotlinx.serialization.json.JsonArray /** * An instance of a [Guild scheduled event](ADD LINK) belonging to a specific guild. @@ -44,6 +44,12 @@ public class GuildScheduledEvent( public val channelId: Snowflake? get() = data.channelId + /** + * The id of the user that created the scheduled event + */ + public val creatorId: Snowflake? + get() = data.creatorId.value + /** * The name of this event. */ @@ -56,9 +62,6 @@ public class GuildScheduledEvent( public val description: String? get() = data.description.value - public val image: String? - get() = data.image - /** * The [Instant] in which this event will start. */ @@ -83,12 +86,6 @@ public class GuildScheduledEvent( public val status: GuildScheduledEventStatus get() = data.status - /** - * The [type][ScheduledEntityType] of this event. - */ - public val type: ScheduledEntityType - get() = data.type - public val entityId: Snowflake? get() = data.entityId @@ -98,12 +95,14 @@ public class GuildScheduledEvent( public val entityType: ScheduledEntityType get() = data.entityType + /** + * The [entity metadata][GuildScheduledEventEntityMetadata] for the scheduled event + */ public val entityMetadata: GuildScheduledEventEntityMetadata get() = data.entityMetadata - public val skuIds: List - get() = data.skuIds - public val skus: JsonArray - get() = data.skus + + public val creator: User? + get() = data.creator.unwrap { User(it, kord, supplier) } /** * The amount of users subscribed to this event. From 7bf5c63f844e69bdb5016755e91da7802b9622cb Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Thu, 18 Nov 2021 17:02:07 +0100 Subject: [PATCH 10/11] Update rest endpoints --- core/src/main/kotlin/Kord.kt | 18 ---- core/src/main/kotlin/Unsafe.kt | 3 +- .../src/main/kotlin/behavior/GuildBehavior.kt | 18 ++++ .../behavior/GuildScheduledEventBehavior.kt | 18 ++-- .../main/kotlin/entity/GuildScheduledEvent.kt | 2 +- .../guild/GuildScheduledEventUserEvent.kt | 70 +++++++++++++ .../kotlin/supplier/CacheAwareRestSupplier.kt | 4 +- .../kotlin/supplier/CacheEntitySupplier.kt | 3 +- .../main/kotlin/supplier/EntitySupplier.kt | 6 +- .../kotlin/supplier/FallbackEntitySupplier.kt | 4 +- .../kotlin/supplier/RestEntitySupplier.kt | 4 +- gateway/src/main/kotlin/Event.kt | 22 +++++ gateway/src/main/kotlin/Intent.kt | 12 ++- .../guild/ScheduledEventCreateBuilder.kt | 16 ++- .../ScheduledEventModifyBuilder.kt | 17 +++- .../main/kotlin/json/request/GuildRequests.kt | 8 ++ .../json/request/ScheduledEventRequests.kt | 8 +- rest/src/main/kotlin/route/Route.kt | 44 +++++---- rest/src/main/kotlin/service/GuildService.kt | 99 +++++++++++++++---- rest/src/main/kotlin/service/RestClient.kt | 1 - .../kotlin/service/ScheduledEventService.kt | 42 -------- 21 files changed, 298 insertions(+), 121 deletions(-) create mode 100644 core/src/main/kotlin/event/guild/GuildScheduledEventUserEvent.kt delete mode 100644 rest/src/main/kotlin/service/ScheduledEventService.kt diff --git a/core/src/main/kotlin/Kord.kt b/core/src/main/kotlin/Kord.kt index d6bb3c298dae..6d0c9cb9ea05 100644 --- a/core/src/main/kotlin/Kord.kt +++ b/core/src/main/kotlin/Kord.kt @@ -15,7 +15,6 @@ import dev.kord.core.cache.data.UserData import dev.kord.core.entity.ApplicationInfo import dev.kord.core.entity.Guild import dev.kord.core.entity.GuildPreview -import dev.kord.core.entity.GuildScheduledEvent import dev.kord.core.entity.Invite import dev.kord.core.entity.KordEntity import dev.kord.core.entity.Region @@ -625,23 +624,6 @@ public class Kord( contract { callsInPlace(builder, InvocationKind.EXACTLY_ONCE) } rest.interaction.bulkEditApplicationCommandPermissions(resources.applicationId, guildId, builder) } - - /** - * Requests a [GuildScheduledEvent] by its [id]. - * - * @throws [RequestException] if anything went wrong during the request. - */ - public suspend inline fun getGuildScheduledEvent(id: Snowflake): GuildScheduledEvent = - defaultSupplier.getGuildScheduledEvent(id) - - /** - * Requests a [GuildScheduledEvent] by its [id] returns `null` if none could be found. - * - * @throws [RequestException] if anything went wrong during the request. - */ - public suspend inline fun getGuildScheduledEventOrNull(id: Snowflake): GuildScheduledEvent? = - defaultSupplier.getGuildScheduledEventOrNull(id) - } /** diff --git a/core/src/main/kotlin/Unsafe.kt b/core/src/main/kotlin/Unsafe.kt index 61a23a032907..b0b0045a5484 100644 --- a/core/src/main/kotlin/Unsafe.kt +++ b/core/src/main/kotlin/Unsafe.kt @@ -174,9 +174,10 @@ public class Unsafe(private val kord: Kord) { id, channelId, token, applicationId, kord ) - public fun guildScheduledEvent(id: Snowflake): GuildScheduledEventBehavior = + public fun guildScheduledEvent(id: Snowflake, guildId: Snowflake): GuildScheduledEventBehavior = GuildScheduledEventBehavior( id, + guildId, kord ) } diff --git a/core/src/main/kotlin/behavior/GuildBehavior.kt b/core/src/main/kotlin/behavior/GuildBehavior.kt index 665c2bd281e2..152ccec02471 100644 --- a/core/src/main/kotlin/behavior/GuildBehavior.kt +++ b/core/src/main/kotlin/behavior/GuildBehavior.kt @@ -100,6 +100,7 @@ import dev.kord.rest.request.RestRequestException import dev.kord.rest.service.RestClient import dev.kord.rest.service.createCategory import dev.kord.rest.service.createNewsChannel +import dev.kord.rest.service.createScheduledEvent import dev.kord.rest.service.createTextChannel import dev.kord.rest.service.createVoiceChannel import dev.kord.rest.service.modifyGuildWelcomeScreen @@ -607,6 +608,23 @@ public interface GuildBehavior : KordEntity, Strategizable { return "https://discord.gg/$identifier" } + + /** + * Requests a [GuildScheduledEvent] by its [id]. + * + * @throws [RequestException] if anything went wrong during the request. + */ + public suspend fun getGuildScheduledEvent(eventId: Snowflake): GuildScheduledEvent = + supplier.getGuildScheduledEvent(id, eventId) + + /** + * Requests a [GuildScheduledEvent] by its [id] returns `null` if none could be found. + * + * @throws [RequestException] if anything went wrong during the request. + */ + public suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? = + supplier.getGuildScheduledEventOrNull(id, eventId) + public suspend fun getWidget(): GuildWidget = supplier.getGuildWidget(id) public suspend fun getWidgetOrNull(): GuildWidget? = supplier.getGuildWidgetOrNull(id) diff --git a/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt b/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt index 4f4f3ef36aab..7588480401d2 100644 --- a/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt +++ b/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt @@ -12,6 +12,7 @@ import dev.kord.core.exception.EntityNotFoundException import dev.kord.core.supplier.EntitySupplier import dev.kord.core.supplier.EntitySupplyStrategy import dev.kord.rest.builder.scheduled_events.ScheduledEventModifyBuilder +import dev.kord.rest.service.modifyScheduledEvent import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -20,12 +21,13 @@ import kotlin.contracts.contract * Behavior of a [Discord Scheduled Guild Event](ADD LINK). */ public interface GuildScheduledEventBehavior : KordEntity, Strategizable { + public val guildId: Snowflake /** * Deletes this event. * * @throws RequestException if anything goes wrong during the request */ - public suspend fun delete(): Unit = kord.rest.guildEvents.deleteScheduledEvent(id) + public suspend fun delete(): Unit = kord.rest.guild.deleteScheduledEvent(guildId, id) /** * Requests to get this behavior as a [GuildScheduledEvent]. @@ -33,7 +35,7 @@ public interface GuildScheduledEventBehavior : KordEntity, Strategizable { * @throws [RequestException] if anything went wrong during the request. * @throws [EntityNotFoundException] if the event wasn't present. */ - public suspend fun asGuildScheduledEvent(): GuildScheduledEvent = supplier.getGuildScheduledEvent(id) + public suspend fun asGuildScheduledEvent(): GuildScheduledEvent = supplier.getGuildScheduledEvent(guildId, id) /** * Requests to get this behavior as a [Guild], @@ -41,7 +43,7 @@ public interface GuildScheduledEventBehavior : KordEntity, Strategizable { * * @throws [RequestException] if anything went wrong during the request. */ - public suspend fun asGuildScheduledEventOrNull(): GuildScheduledEvent? = supplier.getGuildScheduledEventOrNull(id) + public suspend fun asGuildScheduledEventOrNull(): GuildScheduledEvent? = supplier.getGuildScheduledEventOrNull(guildId, id) /** * Fetches to get this behavior as a [GuildScheduledEvent]. @@ -49,7 +51,7 @@ public interface GuildScheduledEventBehavior : KordEntity, Strategizable { * @throws [RequestException] if anything went wrong during the request. * @throws [EntityNotFoundException] if the event wasn't present. */ - public suspend fun fetchGuildScheduledEvent(): GuildScheduledEvent = supplier.getGuildScheduledEvent(id) + public suspend fun fetchGuildScheduledEvent(): GuildScheduledEvent = supplier.getGuildScheduledEvent(guildId, id) /** * Fetches to get the this behavior as a [Guild], @@ -58,20 +60,22 @@ public interface GuildScheduledEventBehavior : KordEntity, Strategizable { * @throws [RequestException] if anything went wrong during the request. */ public suspend fun fetchGuildScheduledEventOrNull(): GuildScheduledEvent? = - supplier.getGuildScheduledEventOrNull(id) + supplier.getGuildScheduledEventOrNull(guildId, id) } internal fun GuildScheduledEventBehavior( id: Snowflake, + guildId: Snowflake, kord: Kord, supplier: EntitySupplier = kord.defaultSupplier ): GuildScheduledEventBehavior = object : GuildScheduledEventBehavior { override val kord: Kord = kord override val id: Snowflake = id + override val guildId: Snowflake = guildId override val supplier: EntitySupplier = supplier override fun withStrategy(strategy: EntitySupplyStrategy<*>): Strategizable = - GuildScheduledEventBehavior(id, kord, strategy.supply(kord)) + GuildScheduledEventBehavior(id, guildId, kord, strategy.supply(kord)) } /** @@ -85,5 +89,5 @@ public suspend inline fun GuildScheduledEventBehavior.edit(builder: ScheduledEve callsInPlace(builder, InvocationKind.EXACTLY_ONCE) } - return kord.rest.guildEvents.modifyScheduledEvent(id, builder) + return kord.rest.guild.modifyScheduledEvent(guildId, id, builder) } diff --git a/core/src/main/kotlin/entity/GuildScheduledEvent.kt b/core/src/main/kotlin/entity/GuildScheduledEvent.kt index 10bc2fe2010f..bcd323f82782 100644 --- a/core/src/main/kotlin/entity/GuildScheduledEvent.kt +++ b/core/src/main/kotlin/entity/GuildScheduledEvent.kt @@ -35,7 +35,7 @@ public class GuildScheduledEvent( /** * The id of the guild this event is on. */ - public val guildId: Snowflake + public override val guildId: Snowflake get() = data.guildId /** diff --git a/core/src/main/kotlin/event/guild/GuildScheduledEventUserEvent.kt b/core/src/main/kotlin/event/guild/GuildScheduledEventUserEvent.kt new file mode 100644 index 000000000000..7ef2549ad43d --- /dev/null +++ b/core/src/main/kotlin/event/guild/GuildScheduledEventUserEvent.kt @@ -0,0 +1,70 @@ +package dev.kord.core.event.guild + +import dev.kord.common.annotation.KordPreview +import dev.kord.common.entity.Snowflake +import dev.kord.core.Kord +import dev.kord.core.entity.Guild +import dev.kord.core.entity.GuildScheduledEvent +import dev.kord.core.entity.Strategizable +import dev.kord.core.entity.User +import dev.kord.core.event.Event +import dev.kord.core.event.kordCoroutineScope +import dev.kord.core.supplier.EntitySupplier +import dev.kord.core.supplier.EntitySupplyStrategy +import kotlinx.coroutines.CoroutineScope + +/** + * NOTE: this event is currently experimental and not officially supported + */ +@KordPreview +public interface GuildScheduledEventUserEvent : Event, Strategizable { + public val scheduledEventId: Snowflake + public val userId: Snowflake + public val guildId: Snowflake + + public suspend fun getUser(): User = supplier.getUser(userId) + public suspend fun getUserOrNull(): User? = supplier.getUserOrNull(userId) + + public suspend fun getGuild(): Guild = supplier.getGuild(guildId) + public suspend fun getGuildOrNull(): Guild? = supplier.getGuildOrNull(guildId) + + public suspend fun getEvent(): GuildScheduledEvent = supplier.getGuildScheduledEvent(guildId, scheduledEventId) + public suspend fun getEventOrNull(): GuildScheduledEvent? = + supplier.getGuildScheduledEventOrNull(guildId, scheduledEventId) +} + +/** + * NOTE: this event is currently experimental and not officially supported + */ +@KordPreview +public data class GuildScheduledEventUserAddEvent( + override val scheduledEventId: Snowflake, + override val userId: Snowflake, + override val guildId: Snowflake, + override val kord: Kord, + override val shard: Int, + override val supplier: EntitySupplier = kord.defaultSupplier, + public val coroutineScope: CoroutineScope = kordCoroutineScope(kord), +) : GuildScheduledEventUserEvent, CoroutineScope by coroutineScope { + override fun withStrategy(strategy: EntitySupplyStrategy<*>): Strategizable = GuildScheduledEventUserAddEvent( + scheduledEventId, userId, guildId, kord, shard, strategy.supply(kord) + ) +} + +/** + * NOTE: this event is currently experimental and not officially supported + */ +@KordPreview +public data class GuildScheduledEventUserRemoveEvent( + override val scheduledEventId: Snowflake, + override val userId: Snowflake, + override val guildId: Snowflake, + override val kord: Kord, + override val shard: Int, + override val supplier: EntitySupplier = kord.defaultSupplier, + public val coroutineScope: CoroutineScope = kordCoroutineScope(kord), +) : GuildScheduledEventUserEvent, CoroutineScope by coroutineScope { + override fun withStrategy(strategy: EntitySupplyStrategy<*>): Strategizable = GuildScheduledEventUserAddEvent( + scheduledEventId, userId, guildId, kord, shard, strategy.supply(kord) + ) +} diff --git a/core/src/main/kotlin/supplier/CacheAwareRestSupplier.kt b/core/src/main/kotlin/supplier/CacheAwareRestSupplier.kt index 99f6f505924b..9fe83cb15b0e 100644 --- a/core/src/main/kotlin/supplier/CacheAwareRestSupplier.kt +++ b/core/src/main/kotlin/supplier/CacheAwareRestSupplier.kt @@ -235,8 +235,8 @@ public class StoreEntitySupplier( override fun getGuildScheduledEvents(guildId: Snowflake): Flow = storeOnEach(supplier.getGuildScheduledEvents(guildId)) { it.data } - override suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? = - storeAndReturn(supplier.getGuildScheduledEvent(eventId)) { it.data } + override suspend fun getGuildScheduledEventOrNull(guildId: Snowflake, eventId: Snowflake): GuildScheduledEvent? = + storeAndReturn(supplier.getGuildScheduledEvent(guildId, eventId)) { it.data } override fun getGuildApplicationCommandPermissions( applicationId: Snowflake, diff --git a/core/src/main/kotlin/supplier/CacheEntitySupplier.kt b/core/src/main/kotlin/supplier/CacheEntitySupplier.kt index a913b3731d33..f34f678a6d08 100644 --- a/core/src/main/kotlin/supplier/CacheEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/CacheEntitySupplier.kt @@ -460,8 +460,9 @@ public class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { return ApplicationCommandPermissions(data) } - override suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? { + override suspend fun getGuildScheduledEventOrNull(guildId: Snowflake, eventId: Snowflake): GuildScheduledEvent? { val data = cache.query { + idEq(GuildScheduledEventData::guildId, guildId) idEq(GuildScheduledEventData::id, eventId) }.singleOrNull() ?: return null diff --git a/core/src/main/kotlin/supplier/EntitySupplier.kt b/core/src/main/kotlin/supplier/EntitySupplier.kt index 3a7aeaf69df8..dc1006fc0dd2 100644 --- a/core/src/main/kotlin/supplier/EntitySupplier.kt +++ b/core/src/main/kotlin/supplier/EntitySupplier.kt @@ -468,10 +468,10 @@ public interface EntitySupplier { public fun getGuildScheduledEvents(guildId: Snowflake): Flow - public suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? + public suspend fun getGuildScheduledEventOrNull(guildId: Snowflake, eventId: Snowflake): GuildScheduledEvent? - public suspend fun getGuildScheduledEvent(eventId: Snowflake): GuildScheduledEvent = - getGuildScheduledEventOrNull(eventId) ?: EntityNotFoundException.guildScheduledEventNotFound(eventId) + public suspend fun getGuildScheduledEvent(guildId: Snowflake, eventId: Snowflake): GuildScheduledEvent = + getGuildScheduledEventOrNull(guildId, eventId) ?: EntityNotFoundException.guildScheduledEventNotFound(eventId) } diff --git a/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt b/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt index d8fb9ce4a269..f11c2c3206c9 100644 --- a/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt @@ -214,8 +214,8 @@ private class FallbackEntitySupplier(val first: EntitySupplier, val second: Enti override fun getGuildScheduledEvents(guildId: Snowflake): Flow = first.getGuildScheduledEvents(guildId).switchIfEmpty(second.getGuildScheduledEvents(guildId)) - override suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? = - first.getGuildScheduledEventOrNull(eventId) ?: second.getGuildScheduledEventOrNull(eventId) + override suspend fun getGuildScheduledEventOrNull(guildId: Snowflake, eventId: Snowflake): GuildScheduledEvent? = + first.getGuildScheduledEventOrNull(guildId, eventId) ?: second.getGuildScheduledEventOrNull(guildId, eventId) override fun toString(): String { diff --git a/core/src/main/kotlin/supplier/RestEntitySupplier.kt b/core/src/main/kotlin/supplier/RestEntitySupplier.kt index 13cdbfd3e271..fac706a4fa81 100644 --- a/core/src/main/kotlin/supplier/RestEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/RestEntitySupplier.kt @@ -517,8 +517,8 @@ public class RestEntitySupplier(public val kord: Kord) : EntitySupplier { } } - override suspend fun getGuildScheduledEventOrNull(eventId: Snowflake): GuildScheduledEvent? = catchNotFound { - val event = kord.rest.guildEvents.getScheduledEvent(eventId) + override suspend fun getGuildScheduledEventOrNull(guildId: Snowflake, eventId: Snowflake): GuildScheduledEvent? = catchNotFound { + val event = kord.rest.guild.getScheduledEvent(guildId, eventId) val data = GuildScheduledEventData.from(event) GuildScheduledEvent(data, kord) diff --git a/gateway/src/main/kotlin/Event.kt b/gateway/src/main/kotlin/Event.kt index 43ef62c75db1..3a51680b33a8 100644 --- a/gateway/src/main/kotlin/Event.kt +++ b/gateway/src/main/kotlin/Event.kt @@ -464,6 +464,18 @@ sealed class Event { decoder.decodeSerializableElement(descriptor, index, DiscordGuildScheduledEvent.serializer()), sequence ) + "GUILD_SCHEDULED_EVENT_USER_ADD" -> GuildScheduledEventUserAdd( + decoder.decodeSerializableElement(descriptor, index, Snowflake.serializer()), + decoder.decodeSerializableElement(descriptor, index, Snowflake.serializer()), + decoder.decodeSerializableElement(descriptor, index, Snowflake.serializer()), + sequence + ) + "GUILD_SCHEDULED_EVENT_USER_REMOVE" -> GuildScheduledEventUserRemove( + decoder.decodeSerializableElement(descriptor, index, Snowflake.serializer()), + decoder.decodeSerializableElement(descriptor, index, Snowflake.serializer()), + decoder.decodeSerializableElement(descriptor, index, Snowflake.serializer()), + sequence + ) else -> { @@ -739,6 +751,16 @@ data class GuildScheduledEventUpdate(val event: DiscordGuildScheduledEvent, over data class GuildScheduledEventDelete(val event: DiscordGuildScheduledEvent, override val sequence: Int?) : DispatchEvent() +data class GuildScheduledEventUserAdd( + val eventId: Snowflake, val userId: Snowflake, val guildId: Snowflake, + override val sequence: Int? +) : DispatchEvent() + +data class GuildScheduledEventUserRemove( + val eventId: Snowflake, val userId: Snowflake, val guildId: Snowflake, + override val sequence: Int? +) : DispatchEvent() + @Serializable data class DiscordThreadListSync( @SerialName("guild_id") diff --git a/gateway/src/main/kotlin/Intent.kt b/gateway/src/main/kotlin/Intent.kt index dd7b11532062..d9fd5a13253c 100644 --- a/gateway/src/main/kotlin/Intent.kt +++ b/gateway/src/main/kotlin/Intent.kt @@ -2,7 +2,6 @@ package dev.kord.gateway import dev.kord.common.DiscordBitSet import dev.kord.common.EmptyBitSet -import dev.kord.common.entity.Permission import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.PrimitiveKind @@ -152,6 +151,17 @@ sealed class Intent(val code: DiscordBitSet) { * - [TypingStart] */ object DirectMessageTyping : Intent(1 shl 14) + + /** + * Enables the following events: + * - [GuildScheduledEventCreate] + * - [GuildScheduledEventUpdate] + * - [GuildScheduledEventDelete] + * - [GuildScheduledEventUserAdd] + * - [GuildScheduledEventUserRemove] + */ + object GuildScheduledEvents : Intent(1 shl 16) + companion object { @OptIn(PrivilegedIntent::class) val values: Set diff --git a/rest/src/main/kotlin/builder/guild/ScheduledEventCreateBuilder.kt b/rest/src/main/kotlin/builder/guild/ScheduledEventCreateBuilder.kt index bb1347c048eb..18036c85d98f 100644 --- a/rest/src/main/kotlin/builder/guild/ScheduledEventCreateBuilder.kt +++ b/rest/src/main/kotlin/builder/guild/ScheduledEventCreateBuilder.kt @@ -1,5 +1,6 @@ package dev.kord.rest.builder.guild +import dev.kord.common.entity.GuildScheduledEventEntityMetadata import dev.kord.common.entity.ScheduledEntityType import dev.kord.common.entity.Snowflake import dev.kord.common.entity.StageInstancePrivacyLevel @@ -22,7 +23,20 @@ class ScheduledEventCreateBuilder( private var _description: Optional = Optional.Missing() var description: String? by ::_description.delegate() + private var _entityMetadata: Optional = Optional.Missing() + var entityMetadata: GuildScheduledEventEntityMetadata? by ::_entityMetadata.delegate() + + private var _scheduledEndTime: Optional = Optional.Missing() + var scheduledEndTime: Instant? by ::_scheduledEndTime.delegate() + override fun toRequest(): GuildScheduledEventCreateRequest = GuildScheduledEventCreateRequest( - _channelId, name, privacyLevel, scheduledStartTime, _description, entityType + _channelId, + _entityMetadata, + name, + privacyLevel, + scheduledStartTime, + _scheduledEndTime, + _description, + entityType ) } diff --git a/rest/src/main/kotlin/builder/scheduled_events/ScheduledEventModifyBuilder.kt b/rest/src/main/kotlin/builder/scheduled_events/ScheduledEventModifyBuilder.kt index 9809578d8633..bda9bd398a4e 100644 --- a/rest/src/main/kotlin/builder/scheduled_events/ScheduledEventModifyBuilder.kt +++ b/rest/src/main/kotlin/builder/scheduled_events/ScheduledEventModifyBuilder.kt @@ -1,5 +1,7 @@ package dev.kord.rest.builder.scheduled_events +import dev.kord.common.entity.GuildScheduledEventEntityMetadata +import dev.kord.common.entity.GuildScheduledEventStatus import dev.kord.common.entity.ScheduledEntityType import dev.kord.common.entity.StageInstancePrivacyLevel import dev.kord.common.entity.optional.Optional @@ -28,12 +30,25 @@ class ScheduledEventModifyBuilder : RequestBuilder private var _entityType: Optional = Optional.Missing() var entityType by ::_entityType.delegate() + private var _entityMetadata: Optional = Optional.Missing() + var entityMetadata: GuildScheduledEventEntityMetadata? by ::_entityMetadata.delegate() + + private var _scheduledEndTime: Optional = Optional.Missing() + var scheduledEndTime: Instant? by ::_scheduledEndTime.delegate() + + private var _status: Optional = Optional.Missing() + var status: GuildScheduledEventStatus? by ::_status.delegate() + + override fun toRequest(): ScheduledEventModifyRequest = ScheduledEventModifyRequest( _channelId, + _entityMetadata, _name, _privacyLevel, _scheduledStartTime, + _scheduledEndTime, _description, - _entityType + _entityType, + _status ) } diff --git a/rest/src/main/kotlin/json/request/GuildRequests.kt b/rest/src/main/kotlin/json/request/GuildRequests.kt index 17c492a5be2b..b983ef8f8636 100644 --- a/rest/src/main/kotlin/json/request/GuildRequests.kt +++ b/rest/src/main/kotlin/json/request/GuildRequests.kt @@ -7,6 +7,7 @@ import dev.kord.common.entity.ChannelType import dev.kord.common.entity.DefaultMessageNotificationLevel import dev.kord.common.entity.DiscordWelcomeScreenChannel import dev.kord.common.entity.ExplicitContentFilter +import dev.kord.common.entity.GuildScheduledEventEntityMetadata import dev.kord.common.entity.IntegrationExpireBehavior import dev.kord.common.entity.Overwrite import dev.kord.common.entity.Permissions @@ -257,12 +258,19 @@ data class GuildWelcomeScreenModifyRequest( @Serializable data class GuildScheduledEventCreateRequest( val channelId: OptionalSnowflake = OptionalSnowflake.Missing, + val entityMetadata: Optional = Optional.Missing(), val name: String, @SerialName("privacy_level") val privacyLevel: StageInstancePrivacyLevel, @SerialName("scheduled_start_time") val scheduledStartTime: Instant, + @SerialName("scheduled_end_time") + val scheduledEndTime: Optional, val description: Optional = Optional.Missing(), @SerialName("entity_type") val entityType: ScheduledEntityType ) + +@Serializable +data class GuildScheduledEventUsersResponse(val users: List) + diff --git a/rest/src/main/kotlin/json/request/ScheduledEventRequests.kt b/rest/src/main/kotlin/json/request/ScheduledEventRequests.kt index 8fb6e01600ed..3d3683976296 100644 --- a/rest/src/main/kotlin/json/request/ScheduledEventRequests.kt +++ b/rest/src/main/kotlin/json/request/ScheduledEventRequests.kt @@ -1,5 +1,7 @@ package dev.kord.rest.json.request +import dev.kord.common.entity.GuildScheduledEventEntityMetadata +import dev.kord.common.entity.GuildScheduledEventStatus import dev.kord.common.entity.ScheduledEntityType import dev.kord.common.entity.StageInstancePrivacyLevel import dev.kord.common.entity.optional.Optional @@ -12,12 +14,16 @@ import kotlinx.serialization.Serializable data class ScheduledEventModifyRequest( @SerialName("channel_id") val channelId: OptionalSnowflake = OptionalSnowflake.Missing, + val entityMetadata: Optional = Optional.Missing(), val name: Optional = Optional.Missing(), @SerialName("privacy_level") val privacyLevel: Optional = Optional.Missing(), @SerialName("scheduled_start_time") val scheduledStartTime: Optional = Optional.Missing(), + @SerialName("scheduled_end_time") + val scheduledEndTime: Optional = Optional.Missing(), val description: Optional = Optional.Missing(), @SerialName("entity_type") - val entityType: Optional + val entityType: Optional, + val status: Optional = Optional.Missing() ) diff --git a/rest/src/main/kotlin/route/Route.kt b/rest/src/main/kotlin/route/Route.kt index 60ec29bbf38c..d9dc6bcd1779 100644 --- a/rest/src/main/kotlin/route/Route.kt +++ b/rest/src/main/kotlin/route/Route.kt @@ -24,6 +24,7 @@ import dev.kord.common.entity.DiscordUser import dev.kord.common.entity.DiscordVoiceRegion import dev.kord.common.entity.DiscordWebhook import dev.kord.common.entity.DiscordWelcomeScreen +import dev.kord.rest.json.request.GuildScheduledEventUsersResponse import dev.kord.rest.json.response.ApplicationInfoResponse import dev.kord.rest.json.response.BanResponse import dev.kord.rest.json.response.BotGatewayResponse @@ -630,6 +631,30 @@ sealed class Route( ListSerializer(DiscordGuildScheduledEvent.serializer()) ) + object GuildScheduledEventGet : Route( + HttpMethod.Get, + "/guilds/$GuildId/events/$ScheduledEventId", + DiscordGuildScheduledEvent.serializer() + ) + + object GuildScheduledEventUsersGet : Route( + HttpMethod.Get, + "/guilds/$GuildId/events/$ScheduledEventId/users", + GuildScheduledEventUsersResponse.serializer() + ) + + object GuildScheduledEventPatch : Route( + HttpMethod.Patch, + "/guilds/$GuildId/events/$ScheduledEventId", + DiscordGuildScheduledEvent.serializer() + ) + + object GuildScheduledEventDelete : Route( + HttpMethod.Delete, + "/guilds/$GuildId/events/$ScheduledEventId", + NoStrategy + ) + object GuildScheduledEventsPost : Route( HttpMethod.Post, "/guilds/$GuildId/events", @@ -797,25 +822,6 @@ sealed class Route( ) - object GuildEventsGet : Route( - HttpMethod.Get, - "/guild-events/$ScheduledEventId", - DiscordGuildScheduledEvent.serializer() - ) - - object GuildEventsDelete : Route( - HttpMethod.Get, - "/guild-events/$ScheduledEventId", - NoStrategy - ) - - object GuildEventsPatch : Route( - HttpMethod.Patch, - "/guild-events/$ScheduledEventId", - DiscordGuildScheduledEvent.serializer() - ) - - companion object { val baseUrl = "https://discord.com/api/$restVersion" } diff --git a/rest/src/main/kotlin/service/GuildService.kt b/rest/src/main/kotlin/service/GuildService.kt index 2b9cf02889ea..26cb57b3b099 100644 --- a/rest/src/main/kotlin/service/GuildService.kt +++ b/rest/src/main/kotlin/service/GuildService.kt @@ -31,6 +31,7 @@ import dev.kord.rest.builder.member.MemberModifyBuilder import dev.kord.rest.builder.role.RoleCreateBuilder import dev.kord.rest.builder.role.RoleModifyBuilder import dev.kord.rest.builder.role.RolePositionsModifyBuilder +import dev.kord.rest.builder.scheduled_events.ScheduledEventModifyBuilder import dev.kord.rest.json.request.CurrentUserNicknameModifyRequest import dev.kord.rest.json.request.CurrentVoiceStateModifyRequest import dev.kord.rest.json.request.GuildBanCreateRequest @@ -48,6 +49,7 @@ import dev.kord.rest.json.request.GuildRolePositionModifyRequest import dev.kord.rest.json.request.GuildScheduledEventCreateRequest import dev.kord.rest.json.request.GuildWelcomeScreenModifyRequest import dev.kord.rest.json.request.GuildWidgetModifyRequest +import dev.kord.rest.json.request.ScheduledEventModifyRequest import dev.kord.rest.json.request.VoiceStateModifyRequest import dev.kord.rest.json.response.ListThreadsResponse import dev.kord.rest.request.RequestHandler @@ -475,6 +477,15 @@ class GuildService(requestHandler: RequestHandler) : RestService(requestHandler) } } + suspend fun getScheduledEvent( + guildId: Snowflake, + eventId: Snowflake + ): DiscordGuildScheduledEvent = + call(Route.GuildScheduledEventGet) { + keys[Route.GuildId] = guildId + keys[Route.ScheduledEventId] = eventId + } + suspend fun createScheduledEvent( guildId: Snowflake, request: GuildScheduledEventCreateRequest @@ -484,27 +495,41 @@ class GuildService(requestHandler: RequestHandler) : RestService(requestHandler) body(GuildScheduledEventCreateRequest.serializer(), request) } - @OptIn(ExperimentalContracts::class) - suspend fun createScheduledEvent( + suspend fun modifyScheduledEvent( guildId: Snowflake, - name: String, - privacyLevel: StageInstancePrivacyLevel, - scheduledStartTime: Instant, - entityType: ScheduledEntityType, - builder: ScheduledEventCreateBuilder.() -> Unit = {} - ): DiscordGuildScheduledEvent { - contract { - callsInPlace(builder, InvocationKind.EXACTLY_ONCE) - } + eventId: Snowflake, + request: ScheduledEventModifyRequest + ) = call(Route.GuildScheduledEventPatch) { + keys[Route.GuildId] = guildId + keys[Route.ScheduledEventId] = eventId - val appliedBuilder = ScheduledEventCreateBuilder( - name, - privacyLevel, - scheduledStartTime, - entityType - ).apply(builder) + body(ScheduledEventModifyRequest.serializer(), request) + } - return createScheduledEvent(guildId, appliedBuilder.toRequest()) + suspend fun deleteScheduledEvent( + guildId: Snowflake, + eventId: Snowflake + ) = call(Route.GuildScheduledEventDelete) { + keys[Route.GuildId] = guildId + keys[Route.ScheduledEventId] = eventId + } + + suspend fun getScheduledEventUsers( + guildId: Snowflake, + eventId: Snowflake, + limit: Int? = null, + withMember: Boolean? = null + ) = call(Route.GuildScheduledEventUsersGet) { + keys[Route.GuildId] = guildId + keys[Route.ScheduledEventId] = eventId + + if (limit != null) { + parameter("limit", limit) + } + + if (withMember != null) { + parameter("with_member", withMember) + } } } @@ -586,3 +611,41 @@ suspend inline fun GuildService.modifyVoiceState( val modifyBuilder = VoiceStateModifyBuilder(channelId).apply(builder) modifyVoiceState(guildId, userId, modifyBuilder.toRequest()) } + +@OptIn(ExperimentalContracts::class) +suspend inline fun GuildService.createScheduledEvent( + guildId: Snowflake, + name: String, + privacyLevel: StageInstancePrivacyLevel, + scheduledStartTime: Instant, + entityType: ScheduledEntityType, + builder: ScheduledEventCreateBuilder.() -> Unit = {} +): DiscordGuildScheduledEvent { + contract { + callsInPlace(builder, InvocationKind.EXACTLY_ONCE) + } + + val appliedBuilder = ScheduledEventCreateBuilder( + name, + privacyLevel, + scheduledStartTime, + entityType + ).apply(builder) + + return createScheduledEvent(guildId, appliedBuilder.toRequest()) +} + +@OptIn(ExperimentalContracts::class) +suspend inline fun GuildService.modifyScheduledEvent( + guildId: Snowflake, + eventId: Snowflake, + builder: ScheduledEventModifyBuilder.() -> Unit +): DiscordGuildScheduledEvent { + contract { + callsInPlace(builder, InvocationKind.EXACTLY_ONCE) + } + + val appliedBuilder = ScheduledEventModifyBuilder().apply(builder) + + return modifyScheduledEvent(guildId, eventId, appliedBuilder.toRequest()) +} diff --git a/rest/src/main/kotlin/service/RestClient.kt b/rest/src/main/kotlin/service/RestClient.kt index afeff7635f9d..d0de667e6944 100644 --- a/rest/src/main/kotlin/service/RestClient.kt +++ b/rest/src/main/kotlin/service/RestClient.kt @@ -23,7 +23,6 @@ class RestClient(requestHandler: RequestHandler) : RestService(requestHandler) { val template: TemplateService = TemplateService(requestHandler) val interaction: InteractionService = InteractionService(requestHandler) val stageInstance: StageInstanceService = StageInstanceService(requestHandler) - val guildEvents: ScheduledEventService = ScheduledEventService(requestHandler) /** * Sends a request to the given [route]. This function exposes a direct call to the Discord api and allows diff --git a/rest/src/main/kotlin/service/ScheduledEventService.kt b/rest/src/main/kotlin/service/ScheduledEventService.kt deleted file mode 100644 index 6a91a18297aa..000000000000 --- a/rest/src/main/kotlin/service/ScheduledEventService.kt +++ /dev/null @@ -1,42 +0,0 @@ -package dev.kord.rest.service - -import dev.kord.common.entity.DiscordGuildScheduledEvent -import dev.kord.common.entity.Snowflake -import dev.kord.rest.builder.scheduled_events.ScheduledEventModifyBuilder -import dev.kord.rest.json.request.ScheduledEventModifyRequest -import dev.kord.rest.request.RequestHandler -import dev.kord.rest.route.Route -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract - -class ScheduledEventService(requestHandler: RequestHandler) : RestService(requestHandler) { - suspend fun getScheduledEvent(eventId: Snowflake) = call(Route.GuildEventsGet) { - keys[Route.ScheduledEventId] = eventId - } - - suspend fun deleteScheduledEvent(eventId: Snowflake) = call(Route.GuildEventsDelete) { - keys[Route.ScheduledEventId] = eventId - } - - suspend fun modifyScheduledEvent(eventId: Snowflake, request: ScheduledEventModifyRequest) = - call(Route.GuildEventsPatch) { - keys[Route.ScheduledEventId] = eventId - - body(ScheduledEventModifyRequest.serializer(), request) - } - - @OptIn(ExperimentalContracts::class) - suspend inline fun modifyScheduledEvent( - eventId: Snowflake, - builder: ScheduledEventModifyBuilder.() -> Unit - ): DiscordGuildScheduledEvent { - contract { - callsInPlace(builder, InvocationKind.EXACTLY_ONCE) - } - - val appliedBuilder = ScheduledEventModifyBuilder().apply(builder) - - return modifyScheduledEvent(eventId, appliedBuilder.toRequest()) - } -} From a0f8a2f951fc05b9494bcf829a7dc4e8ae03f3df Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Sat, 20 Nov 2021 20:53:08 +0100 Subject: [PATCH 11/11] Add support for event members --- common/src/main/kotlin/entity/DiscordUser.kt | 6 +++++- .../behavior/GuildScheduledEventBehavior.kt | 17 +++++++++++++++++ .../main/kotlin/json/request/GuildRequests.kt | 3 ++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/entity/DiscordUser.kt b/common/src/main/kotlin/entity/DiscordUser.kt index 4df9557bd7a6..da0b557acaac 100644 --- a/common/src/main/kotlin/entity/DiscordUser.kt +++ b/common/src/main/kotlin/entity/DiscordUser.kt @@ -2,6 +2,7 @@ package dev.kord.common.entity import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -10,6 +11,7 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonNames import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -72,6 +74,7 @@ data class DiscordUser( * @param premiumType The type of Nitro subscription on a user's account. * @param publicFlags The public flags on a user's account. Unlike [flags], these **are** visible ot other users. */ +@OptIn(ExperimentalSerializationApi::class) @Serializable data class DiscordOptionallyMemberUser( val id: Snowflake, @@ -90,6 +93,7 @@ data class DiscordOptionallyMemberUser( val premiumType: Optional = Optional.Missing(), @SerialName("public_flags") val publicFlags: Optional = Optional.Missing(), + @JsonNames("member", "guild_member") val member: Optional = Optional.Missing(), ) @@ -207,4 +211,4 @@ sealed class UserPremium(val value: Int) { encoder.encodeInt(value.value) } } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt b/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt index 7588480401d2..c07401f44437 100644 --- a/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt +++ b/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt @@ -4,15 +4,19 @@ import dev.kord.common.entity.DiscordGuildScheduledEvent import dev.kord.common.entity.Snowflake import dev.kord.common.exception.RequestException import dev.kord.core.Kord +import dev.kord.core.cache.data.UserData import dev.kord.core.entity.Guild import dev.kord.core.entity.GuildScheduledEvent import dev.kord.core.entity.KordEntity import dev.kord.core.entity.Strategizable +import dev.kord.core.entity.User import dev.kord.core.exception.EntityNotFoundException import dev.kord.core.supplier.EntitySupplier import dev.kord.core.supplier.EntitySupplyStrategy import dev.kord.rest.builder.scheduled_events.ScheduledEventModifyBuilder import dev.kord.rest.service.modifyScheduledEvent +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -22,6 +26,19 @@ import kotlin.contracts.contract */ public interface GuildScheduledEventBehavior : KordEntity, Strategizable { public val guildId: Snowflake + + /** + * Requests all the users which are interested in this event. + * + * @throws RequestException if anything goes wrong during the request + */ + public val users: Flow get() = flow { + kord.rest.guild.getScheduledEventUsers(guildId, id).users.forEach { + val userData = UserData.from(it) + emit(User(userData, kord, supplier)) + } + } + /** * Deletes this event. * diff --git a/rest/src/main/kotlin/json/request/GuildRequests.kt b/rest/src/main/kotlin/json/request/GuildRequests.kt index b983ef8f8636..5a5e1ae306e0 100644 --- a/rest/src/main/kotlin/json/request/GuildRequests.kt +++ b/rest/src/main/kotlin/json/request/GuildRequests.kt @@ -5,6 +5,7 @@ import dev.kord.common.annotation.DeprecatedSinceKord import dev.kord.common.annotation.KordExperimental import dev.kord.common.entity.ChannelType import dev.kord.common.entity.DefaultMessageNotificationLevel +import dev.kord.common.entity.DiscordOptionallyMemberUser import dev.kord.common.entity.DiscordWelcomeScreenChannel import dev.kord.common.entity.ExplicitContentFilter import dev.kord.common.entity.GuildScheduledEventEntityMetadata @@ -272,5 +273,5 @@ data class GuildScheduledEventCreateRequest( ) @Serializable -data class GuildScheduledEventUsersResponse(val users: List) +data class GuildScheduledEventUsersResponse(val users: List)