diff --git a/common/src/main/kotlin/entity/AuditLog.kt b/common/src/main/kotlin/entity/AuditLog.kt index 4a11edf63989..3179439da593 100644 --- a/common/src/main/kotlin/entity/AuditLog.kt +++ b/common/src/main/kotlin/entity/AuditLog.kt @@ -143,11 +143,11 @@ public data class AuditLogChange( @Suppress("UNCHECKED_CAST") override fun serialize(encoder: Encoder, value: AuditLogChange) { - val value = value as AuditLogChange + val logChange = value as AuditLogChange encoder.encodeStructure(descriptor) { - encodeSerializableElement(descriptor, 0, value.key.serializer, value.new as Unit) - encodeSerializableElement(descriptor, 0, value.key.serializer, value.old as Unit) - encodeSerializableElement(descriptor, 0, AuditLogChangeKey.serializer(Unit.serializer()), value.key) + encodeSerializableElement(descriptor, 0, logChange.key.serializer, logChange.new as Unit) + encodeSerializableElement(descriptor, 0, logChange.key.serializer, logChange.old as Unit) + encodeSerializableElement(descriptor, 0, AuditLogChangeKey.serializer(Unit.serializer()), logChange.key) } } } diff --git a/common/src/main/kotlin/entity/DiscordGuild.kt b/common/src/main/kotlin/entity/DiscordGuild.kt index df088b926b3c..0fb73a6c9682 100644 --- a/common/src/main/kotlin/entity/DiscordGuild.kt +++ b/common/src/main/kotlin/entity/DiscordGuild.kt @@ -81,82 +81,61 @@ public data class DiscordGuild( val id: Snowflake, val name: String, val icon: String?, - @SerialName("icon_hash") - val iconHash: Optional = Optional.Missing(), + @SerialName("icon_hash") val iconHash: Optional = Optional.Missing(), val splash: Optional = Optional.Missing(), - @SerialName("discovery_splash") - val discoverySplash: Optional = Optional.Missing(), + @SerialName("discovery_splash") val discoverySplash: Optional = Optional.Missing(), val owner: OptionalBoolean = OptionalBoolean.Missing, - @SerialName("owner_id") - val ownerId: Snowflake, + @SerialName("owner_id") val ownerId: Snowflake, val permissions: Optional = Optional.Missing(), - @Deprecated("The region field has been moved to Channel#rtcRegion in Discord API v9", ReplaceWith("DiscordChannel#rtcRegion")) - val region: String, - @SerialName("afk_channel_id") - val afkChannelId: Snowflake?, - @SerialName("afk_timeout") - val afkTimeout: Int, - @SerialName("widget_enabled") - val widgetEnabled: OptionalBoolean = OptionalBoolean.Missing, - @SerialName("widget_channel_id") - val widgetChannelId: OptionalSnowflake? = OptionalSnowflake.Missing, - @SerialName("verification_level") - val verificationLevel: VerificationLevel, - @SerialName("default_message_notifications") - val defaultMessageNotifications: DefaultMessageNotificationLevel, - @SerialName("explicit_content_filter") - val explicitContentFilter: ExplicitContentFilter, + @Deprecated( + "The region field has been moved to Channel#rtcRegion in Discord API v9", + ReplaceWith("DiscordChannel#rtcRegion") + ) val region: String, + @SerialName("afk_channel_id") val afkChannelId: Snowflake?, + @SerialName("afk_timeout") val afkTimeout: Int, + @SerialName("widget_enabled") val widgetEnabled: OptionalBoolean = OptionalBoolean.Missing, + @SerialName("widget_channel_id") val widgetChannelId: OptionalSnowflake? = OptionalSnowflake.Missing, + @SerialName("verification_level") val verificationLevel: VerificationLevel, + @SerialName("default_message_notifications") val defaultMessageNotifications: DefaultMessageNotificationLevel, + @SerialName("explicit_content_filter") val explicitContentFilter: ExplicitContentFilter, val roles: List, val emojis: List, val features: List, - @SerialName("mfa_level") - val mfaLevel: MFALevel, - @SerialName("application_id") - val applicationId: Snowflake?, - @SerialName("system_channel_id") - val systemChannelId: Snowflake?, - @SerialName("system_channel_flags") - val systemChannelFlags: SystemChannelFlags, - @SerialName("rules_channel_id") - val rulesChannelId: Snowflake?, - @SerialName("joined_at") - val joinedAt: Optional = Optional.Missing(), + @SerialName("mfa_level") val mfaLevel: MFALevel, + @SerialName("application_id") val applicationId: Snowflake?, + @SerialName("system_channel_id") val systemChannelId: Snowflake?, + @SerialName("system_channel_flags") val systemChannelFlags: SystemChannelFlags, + @SerialName("rules_channel_id") val rulesChannelId: Snowflake?, + @SerialName("joined_at") val joinedAt: Optional = Optional.Missing(), val large: OptionalBoolean = OptionalBoolean.Missing, val unavailable: OptionalBoolean = OptionalBoolean.Missing, - @SerialName("member_count") - val memberCount: OptionalInt = OptionalInt.Missing, - @SerialName("voice_states") - val voiceStates: Optional> = Optional.Missing(), + @SerialName("member_count") val memberCount: OptionalInt = OptionalInt.Missing, + @SerialName("voice_states") val voiceStates: Optional> = Optional.Missing(), val members: Optional> = Optional.Missing(), val channels: Optional> = Optional.Missing(), val threads: Optional> = Optional.Missing(), val presences: Optional> = Optional.Missing(), - @SerialName("max_presences") - val maxPresences: OptionalInt? = OptionalInt.Missing, - @SerialName("max_members") - val maxMembers: OptionalInt = OptionalInt.Missing, - @SerialName("vanity_url_code") - val vanityUrlCode: String?, + @SerialName("max_presences") val maxPresences: OptionalInt? = OptionalInt.Missing, + @SerialName("max_members") val maxMembers: OptionalInt = OptionalInt.Missing, + @SerialName("vanity_url_code") val vanityUrlCode: String?, val description: String?, val banner: String?, - @SerialName("premium_tier") - val premiumTier: PremiumTier, - @SerialName("premium_subscription_count") - val premiumSubscriptionCount: OptionalInt = OptionalInt.Missing, - @SerialName("preferred_locale") - val preferredLocale: String, - @SerialName("public_updates_channel_id") - val publicUpdatesChannelId: Snowflake?, - @SerialName("max_video_channel_users") - val maxVideoChannelUsers: OptionalInt = OptionalInt.Missing, - @SerialName("approximate_member_count") - val approximateMemberCount: OptionalInt = OptionalInt.Missing, - @SerialName("approximate_presence_count") - val approximatePresenceCount: OptionalInt = OptionalInt.Missing, - @SerialName("welcome_screen") - val welcomeScreen: Optional = Optional.Missing(), - @SerialName("nsfw_level") - val nsfwLevel: NsfwLevel + @SerialName("premium_tier") val premiumTier: PremiumTier, + @SerialName("premium_subscription_count") val premiumSubscriptionCount: OptionalInt = OptionalInt.Missing, + @SerialName("preferred_locale") val preferredLocale: String, + @SerialName("public_updates_channel_id") val publicUpdatesChannelId: Snowflake?, + @SerialName("max_video_channel_users") val maxVideoChannelUsers: OptionalInt = OptionalInt.Missing, + @SerialName("approximate_member_count") val approximateMemberCount: OptionalInt = OptionalInt.Missing, + @SerialName("approximate_presence_count") val approximatePresenceCount: OptionalInt = OptionalInt.Missing, + @SerialName("welcome_screen") val welcomeScreen: Optional = Optional.Missing(), + @SerialName("nsfw_level") val nsfwLevel: NsfwLevel, + @SerialName("stage_instances") + val stageInstances: Optional> = Optional.Missing(), + val stickers: Optional> = Optional.Missing(), + @SerialName("guild_scheduled_events") + val guildScheduledEvents: Optional> = Optional.Missing(), + @SerialName("premium_progress_bar_enabled") + val premiumProgressBarEnabled: Boolean ) /** @@ -178,9 +157,23 @@ public data class DiscordPartialGuild( val owner: OptionalBoolean = OptionalBoolean.Missing, val permissions: Optional = Optional.Missing(), val features: List, - @SerialName("welcome_screen") - val welcomeScreen: Optional = Optional.Missing() -) + @SerialName("welcome_screen") val welcomeScreen: Optional = Optional.Missing(), + @SerialName("vanity_url_code") val vanityUrlCode: Optional = Optional.Missing(), + val description: Optional = Optional.Missing(), + val banner: Optional = Optional.Missing(), + val splash: Optional = Optional.Missing(), + @SerialName("nsfw_level") val nsfwLevel: Optional = Optional.Missing(), + @SerialName("verification_level") + val verificationLevel: Optional = Optional.Missing(), + @SerialName("stage_instances") + val stageInstances: Optional> = Optional.Missing(), + val stickers: Optional> = Optional.Missing(), + @SerialName("guild_scheduled_events") + val guildScheduledEvents: Optional> = Optional.Missing(), + @SerialName("premium_progress_bar_enabled") + val premiumProgressBarEnabled: OptionalBoolean = OptionalBoolean.Missing + + ) /** * A representation of a [Discord Guild Feature](https://discord.com/developers/docs/resources/guild#guild-object-guild-features). @@ -330,15 +323,13 @@ public enum class SystemChannelFlag(public val code: Int) { @Serializable public data class DiscordGuildBan( - @SerialName("guild_id") - val guildId: Snowflake, + @SerialName("guild_id") val guildId: Snowflake, val user: DiscordUser, ) @Serializable public data class DiscordGuildIntegrations( - @SerialName("guild_id") - val guildId: Snowflake, + @SerialName("guild_id") val guildId: Snowflake, ) @Serializable @@ -359,17 +350,14 @@ public data class DiscordIntegrationAccount( @Serializable public data class DiscordVoiceServerUpdateData( val token: String, - @SerialName("guild_id") - val guildId: Snowflake, + @SerialName("guild_id") val guildId: Snowflake, val endpoint: String?, ) @Serializable public data class DiscordWebhooksUpdateData( - @SerialName("guild_id") - val guildId: Snowflake, - @SerialName("channel_id") - val channelId: Snowflake, + @SerialName("guild_id") val guildId: Snowflake, + @SerialName("channel_id") val channelId: Snowflake, ) /** @@ -391,29 +379,19 @@ public data class DiscordWebhooksUpdateData( */ @Serializable public data class DiscordVoiceState( - @SerialName("guild_id") - val guildId: OptionalSnowflake = OptionalSnowflake.Missing, - @SerialName("channel_id") - val channelId: Snowflake?, - @SerialName("user_id") - val userId: Snowflake, - @SerialName("guild_member") - val member: Optional = Optional.Missing(), - @SerialName("session_id") - val sessionId: String, + @SerialName("guild_id") val guildId: OptionalSnowflake = OptionalSnowflake.Missing, + @SerialName("channel_id") val channelId: Snowflake?, + @SerialName("user_id") val userId: Snowflake, + @SerialName("guild_member") val member: Optional = Optional.Missing(), + @SerialName("session_id") val sessionId: String, val deaf: Boolean, val mute: Boolean, - @SerialName("self_deaf") - val selfDeaf: Boolean, - @SerialName("self_mute") - val selfMute: Boolean, - @SerialName("self_video") - val selfVideo: Boolean, - @SerialName("self_stream") - val selfStream: OptionalBoolean = OptionalBoolean.Missing, + @SerialName("self_deaf") val selfDeaf: Boolean, + @SerialName("self_mute") val selfMute: Boolean, + @SerialName("self_video") val selfVideo: Boolean, + @SerialName("self_stream") val selfStream: OptionalBoolean = OptionalBoolean.Missing, val suppress: Boolean, - @SerialName("request_to_speak_timestamp") - val requestToSpeakTimestamp: String? + @SerialName("request_to_speak_timestamp") val requestToSpeakTimestamp: String? ) /** @@ -618,18 +596,14 @@ public sealed class VerificationLevel(public val value: Int) { @Serializable public data class DiscordWelcomeScreenChannel( - @SerialName("channel_id") - val channelId: Snowflake, + @SerialName("channel_id") val channelId: Snowflake, val description: String, - @SerialName("emoji_id") - val emojiId: Snowflake?, - @SerialName("emoji_name") - val emojiName: String? + @SerialName("emoji_id") val emojiId: Snowflake?, + @SerialName("emoji_name") val emojiName: String? ) @Serializable public data class DiscordWelcomeScreen( val description: String?, - @SerialName("welcome_channels") - val welcomeChannels: List + @SerialName("welcome_channels") val welcomeChannels: List, ) diff --git a/common/src/main/kotlin/entity/DiscordInvite.kt b/common/src/main/kotlin/entity/DiscordInvite.kt index 555f8ac1381c..93cd678dda6a 100644 --- a/common/src/main/kotlin/entity/DiscordInvite.kt +++ b/common/src/main/kotlin/entity/DiscordInvite.kt @@ -10,7 +10,7 @@ import kotlinx.serialization.Serializable public data class DiscordInvite( val code: String, val guild: Optional = Optional.Missing(), - val channel: DiscordChannel, + val channel: DiscordChannel?, val inviter: Optional = Optional.Missing(), @SerialName("target_user") val targetUser: Optional = Optional.Missing(), diff --git a/common/src/test/resources/json/guild/guild.json b/common/src/test/resources/json/guild/guild.json index ed1cca39c4a1..5dd66f983c32 100644 --- a/common/src/test/resources/json/guild/guild.json +++ b/common/src/test/resources/json/guild/guild.json @@ -39,5 +39,7 @@ "preferred_locale": "en-US", "rules_channel_id": "441688182833020939", "public_updates_channel_id": "281283303326089216", - "nsfw_level": 0 + "nsfw_level": 0, + "premium_progress_bar_enabled": false + } \ No newline at end of file diff --git a/core/src/main/kotlin/Kord.kt b/core/src/main/kotlin/Kord.kt index 5d4e703571a0..1bd1ca782abf 100644 --- a/core/src/main/kotlin/Kord.kt +++ b/core/src/main/kotlin/Kord.kt @@ -351,17 +351,39 @@ public class Kord( strategy.supply(this).getUserOrNull(id) /** - * Requests to get the [Invite] with [code] through the [EntitySupplyStrategy.rest][rest]. + * Requests to get the [Invite] represented by the [code]. + * * The returned [Invite], if found, uses the default strategy used by Kord. * - * @throws [RequestException] if anything went wrong during the request. + * This is not resolvable through cache and will always use the [rest strategy][EntitySupplyStrategy.rest] instead. * + * @throws RestRequestException if anything went wrong during the request. + * @throws EntityNotFoundException if the [Invite] wasn't present. */ public suspend fun getInvite( code: String, - withCounts: Boolean, - ): Invite? = - EntitySupplyStrategy.rest.supply(this).getInviteOrNull(code, withCounts) + withCounts: Boolean = true, + withExpiration: Boolean = true, + scheduledEventId: Snowflake? = null, + ): Invite = with(EntitySupplyStrategy.rest).getInvite(code, withCounts, withExpiration, scheduledEventId) + + /** + * Requests to get the [Invite] represented by the [code], + * returns null if the [Invite] isn't present. + * + * The returned [Invite], if found, uses the default strategy used by Kord. + * + * This is not resolvable through cache and will always use the [rest strategy][EntitySupplyStrategy.rest] instead. + * + * @throws RestRequestException if anything went wrong during the request. + */ + public suspend fun getInviteOrNull( + code: String, + withCounts: Boolean = true, + withExpiration: Boolean = true, + scheduledEventId: Snowflake? = null, + ): Invite? = with(EntitySupplyStrategy.rest).getInviteOrNull(code, withCounts, withExpiration, scheduledEventId) + public suspend fun getSticker(id: Snowflake): Sticker = defaultSupplier.getSticker(id) diff --git a/core/src/main/kotlin/behavior/GuildBehavior.kt b/core/src/main/kotlin/behavior/GuildBehavior.kt index 4f13c2ffacc5..86558fa7ab35 100644 --- a/core/src/main/kotlin/behavior/GuildBehavior.kt +++ b/core/src/main/kotlin/behavior/GuildBehavior.kt @@ -358,25 +358,32 @@ public interface GuildBehavior : KordEntity, Strategizable { /** * Requests to get the [Invite] represented by the [code]. * + * This is not resolvable through cache and will always use the [rest strategy][EntitySupplyStrategy.rest] instead. * - * This property is not resolvable through cache and will always use the [RestClient] instead. - * - * @throws [RequestException] if anything went wrong during the request. - * @throws [EntityNotFoundException] if the [Invite] wasn't present. + * @throws RestRequestException if anything went wrong during the request. + * @throws EntityNotFoundException if the [Invite] wasn't present. */ - public suspend fun getInvite(code: String, withCounts: Boolean = true): Invite = - kord.with(rest).getInvite(code, withCounts) + public suspend fun getInvite( + code: String, + withCounts: Boolean = true, + withExpiration: Boolean = true, + scheduledEventId: Snowflake? = null, + ): Invite = kord.with(rest).getInvite(code, withCounts, withExpiration, scheduledEventId) /** * Requests to get the [Invite] represented by the [code], * returns null if the [Invite] isn't present. * - * This property is not resolvable through cache and will always use the [RestClient] instead. + * This is not resolvable through cache and will always use the [rest strategy][EntitySupplyStrategy.rest] instead. * - * @throws [RequestException] if anything went wrong during the request. + * @throws RestRequestException if anything went wrong during the request. */ - public suspend fun getInviteOrNull(code: String, withCounts: Boolean = true): Invite? = - kord.with(rest).getInviteOrNull(code, withCounts) + public suspend fun getInviteOrNull( + code: String, + withCounts: Boolean = true, + withExpiration: Boolean = true, + scheduledEventId: Snowflake? = null, + ): Invite? = kord.with(rest).getInviteOrNull(code, withCounts, withExpiration, scheduledEventId) /** diff --git a/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt b/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt index a441610fbd5f..fce841938db7 100644 --- a/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt +++ b/core/src/main/kotlin/behavior/GuildScheduledEventBehavior.kt @@ -4,19 +4,13 @@ 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.entity.* 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.InvocationKind import kotlin.contracts.contract @@ -26,17 +20,29 @@ 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)) - } - } + public val users: Flow + get() = supplier.getGuildScheduledEventUsers(guildId, id) + + public fun getUsersAfter(after: Snowflake, limit: Int? = null): Flow = + supplier.getGuildScheduledEventUsersAfter(guildId, id, after, limit) + + public fun getUsersBefore(before: Snowflake, limit: Int? = null): Flow = + supplier.getGuildScheduledEventUsersBefore(guildId, id, before, limit) + + public val members: Flow + get() = supplier.getGuildScheduledEventMembers(guildId, id) + + public fun getMembersAfter(after: Snowflake, limit: Int? = null): Flow = + supplier.getGuildScheduledEventMembersAfter(guildId, id, after, limit) + + public fun getMembersBefore(before: Snowflake, limit: Int? = null): Flow = + supplier.getGuildScheduledEventMembersBefore(guildId, id, before, limit) /** * Deletes this event. @@ -59,10 +65,11 @@ public interface GuildScheduledEventBehavior : KordEntity, Strategizable { * * @throws [RequestException] if anything went wrong during the request. */ - public suspend fun asGuildScheduledEventOrNull(): GuildScheduledEvent? = supplier.getGuildScheduledEventOrNull(guildId, id) + public suspend fun asGuildScheduledEventOrNull(): GuildScheduledEvent? = + supplier.getGuildScheduledEventOrNull(guildId, id) /** - * Fetches to get this behavior as a [GuildScheduledEvent]. + * Fetches this behavior as a [GuildScheduledEvent]. * * @throws [RequestException] if anything went wrong during the request. * @throws [EntityNotFoundException] if the event wasn't present. @@ -70,8 +77,7 @@ public interface GuildScheduledEventBehavior : KordEntity, Strategizable { public suspend fun fetchGuildScheduledEvent(): GuildScheduledEvent = supplier.getGuildScheduledEvent(guildId, id) /** - * Fetches to get the this behavior as a [Guild], - * returns null if the event isn't present. + * Fetches this behavior as a [GuildScheduledEvent], returns null if the event isn't present. * * @throws [RequestException] if anything went wrong during the request. */ diff --git a/core/src/main/kotlin/cache/data/GuildData.kt b/core/src/main/kotlin/cache/data/GuildData.kt index 5e68254490fd..54c89d11316b 100644 --- a/core/src/main/kotlin/cache/data/GuildData.kt +++ b/core/src/main/kotlin/cache/data/GuildData.kt @@ -58,7 +58,11 @@ public data class GuildData( val approximatePresenceCount: OptionalInt = OptionalInt.Missing, val welcomeScreen: Optional = Optional.Missing(), val nsfwLevel: NsfwLevel, - val threads: Optional> = Optional.Missing() + val threads: Optional> = Optional.Missing(), + val stageInstances: Optional> = Optional.Missing(), + val stickers: Optional> = Optional.Missing(), + val guildScheduledEvents: Optional> = Optional.Missing(), + val premiumProgressBarEnabled: Boolean ) { public companion object { @@ -120,7 +124,11 @@ public data class GuildData( approximatePresenceCount = approximatePresenceCount, welcomeScreen = welcomeScreen.map { WelcomeScreenData.from(it) }, nsfwLevel = nsfwLevel, - threads = threads.mapList { it.toData() } + threads = threads.mapList { it.toData() }, + stageInstances = stageInstances.mapList { StageInstanceData.from(it) }, + stickers = stickers.mapList { StickerData.from(it) }, + guildScheduledEvents = guildScheduledEvents.mapList { GuildScheduledEventData.from(it) }, + premiumProgressBarEnabled = premiumProgressBarEnabled ) } } diff --git a/core/src/main/kotlin/cache/data/InteractionData.kt b/core/src/main/kotlin/cache/data/InteractionData.kt index 39261aa7ae9d..a41a809a1d4c 100644 --- a/core/src/main/kotlin/cache/data/InteractionData.kt +++ b/core/src/main/kotlin/cache/data/InteractionData.kt @@ -117,6 +117,7 @@ public data class OptionData( val name: String, @OptIn(KordExperimental::class) val value: Optional> = Optional.Missing(), + @OptIn(KordExperimental::class) val values: Optional>> = Optional.Missing(), val subCommands: Optional> = Optional.Missing(), val focused: OptionalBoolean = OptionalBoolean.Missing diff --git a/core/src/main/kotlin/cache/data/InviteData.kt b/core/src/main/kotlin/cache/data/InviteData.kt index 7831ac3f5adc..66585f29fac0 100644 --- a/core/src/main/kotlin/cache/data/InviteData.kt +++ b/core/src/main/kotlin/cache/data/InviteData.kt @@ -10,7 +10,7 @@ import kotlinx.serialization.Serializable public data class InviteData( val code: String, val guild: Optional = Optional.Missing(), - val channelId: Snowflake, + val channelId: Snowflake?, val inviterId: OptionalSnowflake = OptionalSnowflake.Missing, val targetUserId: OptionalSnowflake = OptionalSnowflake.Missing, val targetUserType: Optional = Optional.Missing(), @@ -24,7 +24,7 @@ public data class InviteData( InviteData( code, guild = guild.map { PartialGuildData.from(it) }, - channelId = channel.id, + channelId = channel?.id, inviterId = inviter.mapSnowflake { it.id }, targetUserId = targetUser.mapSnowflake { it.id }, targetUserType, diff --git a/core/src/main/kotlin/cache/data/PartialGuildData.kt b/core/src/main/kotlin/cache/data/PartialGuildData.kt index 5c07b31a9859..092edd8acf98 100644 --- a/core/src/main/kotlin/cache/data/PartialGuildData.kt +++ b/core/src/main/kotlin/cache/data/PartialGuildData.kt @@ -1,12 +1,11 @@ package dev.kord.core.cache.data -import dev.kord.common.entity.DiscordPartialGuild -import dev.kord.common.entity.GuildFeature -import dev.kord.common.entity.Permissions -import dev.kord.common.entity.Snowflake +import dev.kord.common.entity.* import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.OptionalBoolean import dev.kord.common.entity.optional.map +import dev.kord.common.entity.optional.mapList +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable @@ -18,6 +17,18 @@ public class PartialGuildData( public val permissions: Optional = Optional.Missing(), public val features: List, public val welcomeScreen: Optional = Optional.Missing(), + @SerialName("vanity_url_code") public val vanityUrlCode: Optional = Optional.Missing(), + public val description: Optional = Optional.Missing(), + public val banner: Optional = Optional.Missing(), + public val splash: Optional = Optional.Missing(), + @SerialName("nsfw_level") public val nsfwLevel: Optional = Optional.Missing(), + @SerialName("verification_level") + public val verificationLevel: Optional = Optional.Missing(), + public val stageInstances: Optional> = Optional.Missing(), + public val stickers: Optional> = Optional.Missing(), + public val guildScheduledEvents: Optional> = Optional.Missing(), + public val premiumProgressBarEnabled: OptionalBoolean = OptionalBoolean.Missing + ) { public companion object { public fun from(partialGuild: DiscordPartialGuild): PartialGuildData = with(partialGuild) { @@ -28,7 +39,17 @@ public class PartialGuildData( owner, permissions, features, - welcomeScreen = welcomeScreen.map { WelcomeScreenData.from(it) }, + welcomeScreen.map { WelcomeScreenData.from(it) }, + vanityUrlCode, + description, + banner, + splash, + nsfwLevel, + verificationLevel, + stageInstances = stageInstances.mapList { StageInstanceData.from(it) }, + stickers = stickers.mapList { StickerData.from(it) }, + guildScheduledEvents = guildScheduledEvents.mapList { GuildScheduledEventData.from(it) }, + premiumProgressBarEnabled = premiumProgressBarEnabled ) } } diff --git a/core/src/main/kotlin/entity/Guild.kt b/core/src/main/kotlin/entity/Guild.kt index 7f44a8ebc73d..feb58997fca1 100644 --- a/core/src/main/kotlin/entity/Guild.kt +++ b/core/src/main/kotlin/entity/Guild.kt @@ -21,6 +21,7 @@ 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 dev.kord.core.switchIfEmpty import dev.kord.rest.Image import dev.kord.rest.service.RestClient import kotlinx.coroutines.flow.Flow @@ -56,8 +57,8 @@ public class Guild( public val threads: Flow get() = flow { data.threads.mapList { - val channel = Channel.from(it, kord) - if(channel is ThreadChannel) emit(channel) + val channel = Channel.from(it, kord) + if (channel is ThreadChannel) emit(channel) } } @@ -267,8 +268,12 @@ public class Guild( /** * The voice region id for the guild. */ - @Deprecated("The region field has been moved to Channel#rtcRegion in Discord API v9", ReplaceWith("Channel#rtcRegion")) - public val regionId: String get() = data.region + @Deprecated( + "The region field has been moved to Channel#rtcRegion in Discord API v9", + ReplaceWith("Channel#rtcRegion") + ) + public val regionId: String + get() = data.region /** * The id of the channel in which a discoverable server's rules should be found @@ -350,6 +355,17 @@ public class Guild( */ public val nsfw: NsfwLevel get() = data.nsfwLevel + public val premiumProgressBarEnabled: Boolean get() = data.premiumProgressBarEnabled + + public val stageInstances: Set + get() = data.stageInstances.orEmpty().map { StageInstance(it, kord) }.toSet() + + override val stickers: Flow + get() = flow { + for (sticker in data.stickers.orEmpty()) emit(GuildSticker(sticker, kord)) + }.switchIfEmpty(super.stickers) + + /** * Requests to get the [VoiceChannel] represented by the [afkChannelId], * returns null if the [afkChannelId] isn't present or the channel itself isn't present. @@ -421,7 +437,7 @@ public class Guild( * Gets the discovery splash url in the specified [format], if present. */ public fun getDiscoverySplashUrl(format: Image.Format): String? = - data.splash.value?.let { "discovery-splashes/$id/${it}.${format.extension}" } + splashHash?.let { "discovery-splashes/$id/${it}.${format.extension}" } /** * Requests to get the splash image in the specified [format], if present. diff --git a/core/src/main/kotlin/entity/Invite.kt b/core/src/main/kotlin/entity/Invite.kt index dc029eb17bbc..f26ce458d656 100644 --- a/core/src/main/kotlin/entity/Invite.kt +++ b/core/src/main/kotlin/entity/Invite.kt @@ -33,7 +33,7 @@ public data class Invite( /** * The id of the channel this invite is associated to. */ - public val channelId: Snowflake get() = data.channelId + public val channelId: Snowflake? get() = data.channelId /** * Returns [PartialGuild] if the invite was made in a guild, or null if not. @@ -53,7 +53,7 @@ public data class Invite( /** * The behavior of the channel this invite is associated to. */ - public val channel: ChannelBehavior get() = ChannelBehavior(channelId, kord) + public val channel: ChannelBehavior? get() = channelId?.let { ChannelBehavior(it, kord) } /** @@ -87,7 +87,7 @@ public data class Invite( * @throws [RequestException] if anything went wrong during the request. * @throws [EntityNotFoundException] if the [Channel] wasn't present. */ - public suspend fun getChannel(): Channel = supplier.getChannelOf(channelId) + public suspend fun getChannel(): Channel? = channelId?.let { supplier.getChannelOf(it) } /** * Requests to get the channel this invite is for, @@ -95,7 +95,7 @@ public data class Invite( * * @throws [RequestException] if anything went wrong during the request. */ - public suspend fun getChannelOrNull(): Channel? = supplier.getChannelOfOrNull(channelId) + public suspend fun getChannelOrNull(): Channel? = channelId?.let { supplier.getChannelOfOrNull(it) } /** * Requests to get the creator of the invite for, diff --git a/core/src/main/kotlin/entity/PartialGuild.kt b/core/src/main/kotlin/entity/PartialGuild.kt index 686e70042db9..a0f2fbabd739 100644 --- a/core/src/main/kotlin/entity/PartialGuild.kt +++ b/core/src/main/kotlin/entity/PartialGuild.kt @@ -1,7 +1,9 @@ package dev.kord.core.entity +import dev.kord.common.entity.NsfwLevel import dev.kord.common.entity.Permissions import dev.kord.common.entity.Snowflake +import dev.kord.common.entity.VerificationLevel import dev.kord.common.entity.optional.unwrap import dev.kord.common.entity.optional.value import dev.kord.common.exception.RequestException @@ -12,6 +14,7 @@ import dev.kord.core.exception.EntityNotFoundException import dev.kord.core.supplier.EntitySupplier import dev.kord.core.supplier.EntitySupplyStrategy import dev.kord.rest.Image +import dev.kord.rest.service.RestClient import java.util.* public class PartialGuild( @@ -53,11 +56,60 @@ public class PartialGuild( public val permissions: Permissions? get() = data.permissions.value + + /** + * The vanity code of this server used in the [vanityUrl], if present. + */ + public val vanityCode: String? get() = data.vanityUrlCode.value + + /** + * The vanity invite URL of this server, if present. + */ + public val vanityUrl: String? get() = vanityCode?.let { "https://discord.gg/$it" } + + /** + * The description of this guild, if present. + */ + public val description: String? get() = data.description.value + + + /** + * The [NSFW Level](https://discord.com/developers/docs/resources/guild#guild-object-guild-nsfw-level) of this Guild + */ + public val nsfw: NsfwLevel? get() = data.nsfwLevel.value + + /** + * The verification level required for the guild. + */ + public val verificationLevel: VerificationLevel? get() = data.verificationLevel.value + + public val splashHash: String? get() = data.splash.value + + + /** + * Gets the discovery splash url in the specified [format], if present. + */ + public fun getDiscoverySplashUrl(format: Image.Format): String? = + splashHash?.let { "discovery-splashes/$id/${it}.${format.extension}" } + + /** + * Requests to get the splash image in the specified [format], if present. + * + * This property is not resolvable through cache and will always use the [RestClient] instead. + */ + public suspend fun getDiscoverySplash(format: Image.Format): Image? { + val url = getDiscoverySplashUrl(format) ?: return null + + return Image.fromUrl(kord.resources.httpClient, url) + } + + /** * Gets the icon url, if present. */ public fun getIconUrl(format: Image.Format): String? = - data.icon?.let { "https://cdn.discordapp.com/icons/$id/$it.${format.extension}" } + iconHash?.let { "https://cdn.discordapp.com/icons/$id/$it.${format.extension}" } + /** * Requests to get the icon image in the specified [format], if present. @@ -68,6 +120,23 @@ public class PartialGuild( return Image.fromUrl(kord.resources.httpClient, url) } + + /** + * Gets the banner url in the specified format. + */ + public fun getBannerUrl(format: Image.Format): String? = + data.banner.value?.let { "https://cdn.discordapp.com/banners/$id/$it.${format.extension}" } + + /** + * Requests to get the banner image in the specified [format], if present. + */ + public suspend fun getBanner(format: Image.Format): Image? { + val url = getBannerUrl(format) ?: return null + + return Image.fromUrl(kord.resources.httpClient, url) + } + + /** * Requests to get the full [Guild] entity for this [PartialGuild]. * diff --git a/core/src/main/kotlin/entity/interaction/ComponentInteraction.kt b/core/src/main/kotlin/entity/interaction/ComponentInteraction.kt index af2df5ee7954..ddb696bb9e52 100644 --- a/core/src/main/kotlin/entity/interaction/ComponentInteraction.kt +++ b/core/src/main/kotlin/entity/interaction/ComponentInteraction.kt @@ -81,7 +81,7 @@ public fun ComponentInteraction( data: InteractionData, kord: Kord, supplier: EntitySupplier = kord.defaultSupplier, -): ComponentInteraction = when (val type = data.data.componentType.value) { +): ComponentInteraction = when (data.data.componentType.value) { ComponentType.Button -> if (data.guildId.value == null) GlobalButtonInteraction( data, kord, diff --git a/core/src/main/kotlin/gateway/handler/MessageEventHandler.kt b/core/src/main/kotlin/gateway/handler/MessageEventHandler.kt index 6472e124a3eb..cdcba1496961 100644 --- a/core/src/main/kotlin/gateway/handler/MessageEventHandler.kt +++ b/core/src/main/kotlin/gateway/handler/MessageEventHandler.kt @@ -65,8 +65,8 @@ internal class MessageEventHandler( //cache interaction user if present. if (interaction is Optional.Value) { - val userData = UserData.from(interaction.value!!.user) - cache.put(userData) + val interactionUserData = UserData.from(interaction.value!!.user) + cache.put(interactionUserData) } mentions.forEach { diff --git a/core/src/main/kotlin/supplier/CacheEntitySupplier.kt b/core/src/main/kotlin/supplier/CacheEntitySupplier.kt index 3ac108cde974..fdcbe2d4d490 100644 --- a/core/src/main/kotlin/supplier/CacheEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/CacheEntitySupplier.kt @@ -473,6 +473,62 @@ public class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { return GuildScheduledEvent(data, kord) } + override fun getGuildScheduledEventMembersBefore( + guildId: Snowflake, + eventId: Snowflake, + before: Snowflake, + limit: Int?, + ): Flow { + checkLimit(limit) + return cache + .query { + idLt(MemberData::userId, before) + idEq(MemberData::guildId, guildId) + } + .asFlow() + .mapNotNull { + val userData = cache.query { idEq(UserData::id, it.userId) }.singleOrNull() + ?: return@mapNotNull null + Member(it, userData, kord) + } + .limit(limit) + } + + override fun getGuildScheduledEventUsersBefore( + guildId: Snowflake, + eventId: Snowflake, + before: Snowflake, + limit: Int?, + ): Flow = getGuildScheduledEventMembersBefore(guildId, eventId, before, limit).map { it.asUser() } + + override fun getGuildScheduledEventMembersAfter( + guildId: Snowflake, + eventId: Snowflake, + after: Snowflake, + limit: Int?, + ): Flow { + checkLimit(limit) + return cache + .query { + idGt(MemberData::userId, after) + idEq(MemberData::guildId, guildId) + } + .asFlow() + .mapNotNull { + val userData = cache.query { idEq(UserData::id, it.userId) }.singleOrNull() + ?: return@mapNotNull null + Member(it, userData, kord) + } + .limit(limit) + } + + override fun getGuildScheduledEventUsersAfter( + guildId: Snowflake, + eventId: Snowflake, + after: Snowflake, + limit: Int?, + ): Flow = getGuildScheduledEventMembersAfter(guildId, eventId, after, limit).map { it.asUser() } + override suspend fun getStickerOrNull(id: Snowflake): Sticker? { val data = cache.query { idEq(StickerData::id, id) }.singleOrNull() ?: return null return Sticker(data, kord) diff --git a/core/src/main/kotlin/supplier/EntitySupplier.kt b/core/src/main/kotlin/supplier/EntitySupplier.kt index 06884f96c863..79114409c6fb 100644 --- a/core/src/main/kotlin/supplier/EntitySupplier.kt +++ b/core/src/main/kotlin/supplier/EntitySupplier.kt @@ -559,6 +559,43 @@ public interface EntitySupplier { public suspend fun getGuildScheduledEvent(guildId: Snowflake, eventId: Snowflake): GuildScheduledEvent = getGuildScheduledEventOrNull(guildId, eventId) ?: EntityNotFoundException.guildScheduledEventNotFound(eventId) + + public fun getGuildScheduledEventUsers(guildId: Snowflake, eventId: Snowflake, limit: Int? = null): Flow = + getGuildScheduledEventUsersAfter(guildId, eventId, after = Snowflake.min, limit) + + public fun getGuildScheduledEventUsersBefore( + guildId: Snowflake, + eventId: Snowflake, + before: Snowflake, + limit: Int? = null, + ): Flow + + public fun getGuildScheduledEventUsersAfter( + guildId: Snowflake, + eventId: Snowflake, + after: Snowflake, + limit: Int? = null, + ): Flow + + + public fun getGuildScheduledEventMembers(guildId: Snowflake, eventId: Snowflake, limit: Int? = null): Flow = + getGuildScheduledEventMembersAfter(guildId, eventId, after = Snowflake.min, limit) + + public fun getGuildScheduledEventMembersBefore( + guildId: Snowflake, + eventId: Snowflake, + before: Snowflake, + limit: Int? = null, + ): Flow + + public fun getGuildScheduledEventMembersAfter( + guildId: Snowflake, + eventId: Snowflake, + after: Snowflake, + limit: Int? = null, + ): Flow + + public suspend fun getStickerOrNull(id: Snowflake): Sticker? public suspend fun getSticker(id: Snowflake): Sticker = @@ -572,8 +609,6 @@ public interface EntitySupplier { public fun getNitroStickerPacks(): Flow public fun getGuildStickers(guildId: Snowflake): Flow - - } diff --git a/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt b/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt index 0c09536718dc..c6f45d3c6f6d 100644 --- a/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt @@ -217,6 +217,42 @@ private class FallbackEntitySupplier(val first: EntitySupplier, val second: Enti override suspend fun getGuildScheduledEventOrNull(guildId: Snowflake, eventId: Snowflake): GuildScheduledEvent? = first.getGuildScheduledEventOrNull(guildId, eventId) ?: second.getGuildScheduledEventOrNull(guildId, eventId) + override fun getGuildScheduledEventUsersBefore( + guildId: Snowflake, + eventId: Snowflake, + before: Snowflake, + limit: Int?, + ): Flow = + first.getGuildScheduledEventUsersBefore(guildId, eventId, before, limit) + .switchIfEmpty(second.getGuildScheduledEventUsersBefore(guildId, eventId, before, limit)) + + override fun getGuildScheduledEventUsersAfter( + guildId: Snowflake, + eventId: Snowflake, + after: Snowflake, + limit: Int?, + ): Flow = + first.getGuildScheduledEventUsersAfter(guildId, eventId, after, limit) + .switchIfEmpty(second.getGuildScheduledEventUsersAfter(guildId, eventId, after, limit)) + + override fun getGuildScheduledEventMembersBefore( + guildId: Snowflake, + eventId: Snowflake, + before: Snowflake, + limit: Int?, + ): Flow = + first.getGuildScheduledEventMembersBefore(guildId, eventId, before, limit) + .switchIfEmpty(second.getGuildScheduledEventMembersBefore(guildId, eventId, before, limit)) + + override fun getGuildScheduledEventMembersAfter( + guildId: Snowflake, + eventId: Snowflake, + after: Snowflake, + limit: Int?, + ): Flow = + first.getGuildScheduledEventMembersAfter(guildId, eventId, after, limit) + .switchIfEmpty(second.getGuildScheduledEventMembersAfter(guildId, eventId, after, limit)) + override suspend fun getStickerOrNull(id: Snowflake): Sticker? = first.getStickerOrNull(id) ?: second.getStickerOrNull(id) diff --git a/core/src/main/kotlin/supplier/RestEntitySupplier.kt b/core/src/main/kotlin/supplier/RestEntitySupplier.kt index b3ff4cb90cd8..0855a2f78ebc 100644 --- a/core/src/main/kotlin/supplier/RestEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/RestEntitySupplier.kt @@ -18,6 +18,7 @@ import dev.kord.core.entity.interaction.PublicFollowupMessage import dev.kord.core.exception.EntityNotFoundException import dev.kord.rest.builder.auditlog.AuditLogGetRequestBuilder import dev.kord.rest.json.request.AuditLogGetRequest +import dev.kord.rest.json.request.GuildScheduledEventUsersResponse import dev.kord.rest.json.request.ListThreadsBySnowflakeRequest import dev.kord.rest.json.request.ListThreadsByTimestampRequest import dev.kord.rest.request.RestRequestException @@ -252,13 +253,23 @@ public class RestEntitySupplier(public val kord: Kord) : EntitySupplier { Message(data, kord) } - public suspend fun getInviteOrNull(code: String, withCounts: Boolean): Invite? = catchNotFound { - val response = invite.getInvite(code, withCounts) + public suspend fun getInviteOrNull( + code: String, + withCounts: Boolean = true, + withExpiration: Boolean = true, + scheduledEventId: Snowflake? = null, + ): Invite? = catchNotFound { + val response = invite.getInvite(code, withCounts, withExpiration, scheduledEventId) Invite(InviteData.from(response), kord) } - public suspend fun getInvite(code: String, withCounts: Boolean = true): Invite = - getInviteOrNull(code, withCounts) ?: EntityNotFoundException.inviteNotFound(code) + public suspend fun getInvite( + code: String, + withCounts: Boolean = true, + withExpiration: Boolean = true, + scheduledEventId: Snowflake? = null, + ): Invite = getInviteOrNull(code, withCounts, withExpiration, scheduledEventId) + ?: EntityNotFoundException.inviteNotFound(code) /** * Requests to get the information of the current application. @@ -463,6 +474,74 @@ public class RestEntitySupplier(public val kord: Kord) : EntitySupplier { GuildScheduledEvent(data, kord) } + override fun getGuildScheduledEventUsersBefore( + guildId: Snowflake, + eventId: Snowflake, + before: Snowflake, + limit: Int?, + ): Flow = getGuildScheduledEventUsersBefore(guildId, eventId, before, withMember = false, limit).map { + val data = UserData.from(it.user) + User(data, kord) + } + + override fun getGuildScheduledEventUsersAfter( + guildId: Snowflake, + eventId: Snowflake, + after: Snowflake, + limit: Int?, + ): Flow = getGuildScheduledEventUsersAfter(guildId, eventId, after, withMember = false, limit).map { + val data = UserData.from(it.user) + User(data, kord) + } + + override fun getGuildScheduledEventMembersBefore( + guildId: Snowflake, + eventId: Snowflake, + before: Snowflake, + limit: Int?, + ): Flow = getGuildScheduledEventUsersBefore(guildId, eventId, before, withMember = true, limit).map { + val userData = UserData.from(it.user) + val memberData = it.member.value!!.toData(userData.id, guildId) + Member(memberData, userData, kord) + } + + override fun getGuildScheduledEventMembersAfter( + guildId: Snowflake, + eventId: Snowflake, + after: Snowflake, + limit: Int?, + ): Flow = getGuildScheduledEventUsersAfter(guildId, eventId, after, withMember = true, limit).map { + val userData = UserData.from(it.user) + val memberData = it.member.value!!.toData(userData.id, guildId) + Member(memberData, userData, kord) + } + + // maxBatchSize: see https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event-users + private fun getGuildScheduledEventUsersBefore( + guildId: Snowflake, + eventId: Snowflake, + before: Snowflake, + withMember: Boolean, + limit: Int?, + ): Flow = limitedPagination(limit, maxBatchSize = 100) { batchSize -> + paginateBackwards(batchSize, start = before, idSelector = { it.user.id }) { beforePosition -> + guild.getScheduledEventUsers(guildId, eventId, beforePosition, withMember, limit = batchSize) + } + } + + // maxBatchSize: see https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event-users + private fun getGuildScheduledEventUsersAfter( + guildId: Snowflake, + eventId: Snowflake, + after: Snowflake, + withMember: Boolean, + limit: Int?, + ): Flow = limitedPagination(limit, maxBatchSize = 100) { batchSize -> + paginateForwards(batchSize, start = after, idSelector = { it.user.id }) { afterPosition -> + guild.getScheduledEventUsers(guildId, eventId, afterPosition, withMember, limit = batchSize) + } + } + override suspend fun getStickerOrNull(id: Snowflake): Sticker? = catchNotFound { val response = sticker.getSticker(id) val data = StickerData.from(response) diff --git a/core/src/main/kotlin/supplier/StoreEntitySupplier.kt b/core/src/main/kotlin/supplier/StoreEntitySupplier.kt index 6a2e2c593c22..bb5e09ecc02f 100644 --- a/core/src/main/kotlin/supplier/StoreEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/StoreEntitySupplier.kt @@ -268,6 +268,37 @@ public class StoreEntitySupplier( } + override fun getGuildScheduledEventUsersBefore( + guildId: Snowflake, + eventId: Snowflake, + before: Snowflake, + limit: Int?, + ): Flow = storeOnEach(supplier.getGuildScheduledEventUsersBefore(guildId, eventId, before, limit)) { it.data } + + public override fun getGuildScheduledEventUsersAfter( + guildId: Snowflake, + eventId: Snowflake, + after: Snowflake, + limit: Int?, + ): Flow = storeOnEach(supplier.getGuildScheduledEventUsersAfter(guildId, eventId, after, limit)) { it.data } + + override fun getGuildScheduledEventMembersBefore( + guildId: Snowflake, + eventId: Snowflake, + before: Snowflake, + limit: Int?, + ): Flow = + storeOnEach(supplier.getGuildScheduledEventMembersBefore(guildId, eventId, before, limit)) { it.data } + + override fun getGuildScheduledEventMembersAfter( + guildId: Snowflake, + eventId: Snowflake, + after: Snowflake, + limit: Int?, + ): Flow = + storeOnEach(supplier.getGuildScheduledEventMembersAfter(guildId, eventId, after, limit)) { it.data } + + override fun getGuildApplicationCommandPermissions( applicationId: Snowflake, guildId: Snowflake diff --git a/core/src/test/kotlin/UtilKtTest.kt b/core/src/test/kotlin/UtilKtTest.kt index 0a87eb093321..d6880199f814 100644 --- a/core/src/test/kotlin/UtilKtTest.kt +++ b/core/src/test/kotlin/UtilKtTest.kt @@ -2,14 +2,14 @@ package dev.kord.core import dev.kord.common.entity.Snowflake import kotlinx.coroutines.flow.count -import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test internal class UtilKtTest { @Test - fun `paginate forwards selects the right id`() = runBlockingTest { + fun `paginate forwards selects the right id`() = runTest { val flow = paginateForwards(start = Snowflake(0u), batchSize = 100, idSelector = { it }) { var value = it.value.value @@ -27,7 +27,7 @@ internal class UtilKtTest { } @Test - fun `paginate backwards selects the right id`() = runBlockingTest { + fun `paginate backwards selects the right id`() = runTest { val flow = paginateBackwards(start = Snowflake(1000u), batchSize = 100, idSelector = { it }) { var value = it.value.value diff --git a/core/src/test/kotlin/live/LiveGuildTest.kt b/core/src/test/kotlin/live/LiveGuildTest.kt index 603df7cd0942..cfb6c78151fd 100644 --- a/core/src/test/kotlin/live/LiveGuildTest.kt +++ b/core/src/test/kotlin/live/LiveGuildTest.kt @@ -50,7 +50,8 @@ class LiveGuildTest : AbstractLiveEntityTest() { premiumTier = PremiumTier.None, preferredLocale = "", systemChannelFlags = SystemChannelFlags(0), - nsfwLevel = NsfwLevel.Default + nsfwLevel = NsfwLevel.Default, + premiumProgressBarEnabled = false ) ) ) @@ -714,7 +715,8 @@ class LiveGuildTest : AbstractLiveEntityTest() { premiumTier = PremiumTier.None, preferredLocale = "", publicUpdatesChannelId = null, - nsfwLevel = NsfwLevel.Default + nsfwLevel = NsfwLevel.Default, + premiumProgressBarEnabled = false ), 0 ) @@ -757,7 +759,8 @@ class LiveGuildTest : AbstractLiveEntityTest() { premiumTier = PremiumTier.None, preferredLocale = "", publicUpdatesChannelId = null, - nsfwLevel = NsfwLevel.Default + nsfwLevel = NsfwLevel.Default, + premiumProgressBarEnabled = false ), 0 ) diff --git a/core/src/test/kotlin/performance/KordEventDropTest.kt b/core/src/test/kotlin/performance/KordEventDropTest.kt index 9d6ce04a5bc1..6e2cd01fdf0d 100644 --- a/core/src/test/kotlin/performance/KordEventDropTest.kt +++ b/core/src/test/kotlin/performance/KordEventDropTest.kt @@ -61,33 +61,35 @@ class KordEventDropTest { val amount = 1_000 val event = GuildCreate( - DiscordGuild( - Snowflake("1337"), - "discord guild", - afkTimeout = 0, - defaultMessageNotifications = DefaultMessageNotificationLevel.AllMessages, - emojis = emptyList(), - explicitContentFilter = ExplicitContentFilter.AllMembers, - features = emptyList(), - mfaLevel = MFALevel.Elevated, - ownerId = Snowflake("123"), - preferredLocale = "en", - description = "A not really real guild", - premiumTier = PremiumTier.None, - region = "idk", - roles = emptyList(), - verificationLevel = VerificationLevel.High, - icon = null, - afkChannelId = null, - applicationId = null, - systemChannelFlags = SystemChannelFlags(0), - systemChannelId = null, - rulesChannelId = null, - vanityUrlCode = null, - banner = null, - publicUpdatesChannelId = null, - nsfwLevel = NsfwLevel.Default - ), 0) + DiscordGuild( + Snowflake("1337"), + "discord guild", + afkTimeout = 0, + defaultMessageNotifications = DefaultMessageNotificationLevel.AllMessages, + emojis = emptyList(), + explicitContentFilter = ExplicitContentFilter.AllMembers, + features = emptyList(), + mfaLevel = MFALevel.Elevated, + ownerId = Snowflake("123"), + preferredLocale = "en", + description = "A not really real guild", + premiumTier = PremiumTier.None, + region = "idk", + roles = emptyList(), + verificationLevel = VerificationLevel.High, + icon = null, + afkChannelId = null, + applicationId = null, + systemChannelFlags = SystemChannelFlags(0), + systemChannelId = null, + rulesChannelId = null, + vanityUrlCode = null, + banner = null, + publicUpdatesChannelId = null, + nsfwLevel = NsfwLevel.Default, + premiumProgressBarEnabled = false + ), 0 + ) val counter = AtomicInteger(0) val countdown = CountDownLatch(amount) diff --git a/core/src/test/kotlin/regression/CacheMissRegression.kt b/core/src/test/kotlin/regression/CacheMissRegression.kt index 05eb5a1d7227..c4b18eecc843 100644 --- a/core/src/test/kotlin/regression/CacheMissRegression.kt +++ b/core/src/test/kotlin/regression/CacheMissRegression.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.runTest import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.json.Json import org.junit.jupiter.api.Test @@ -124,7 +124,7 @@ class CacheMissingRegressions { lateinit var kord: Kord @BeforeTest - fun setup() = runBlockingTest { //TODO, move this over to entity supplier tests instead, eventually. + fun setup() = runTest { //TODO, move this over to entity supplier tests instead, eventually. val token = System.getenv("KORD_TEST_TOKEN") val resources = ClientResources( token, diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c9f906b81b11..d8d3f4bdd9e7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ #Tue Dec 31 15:02:18 CET 2019 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists diff --git a/rest/src/main/kotlin/json/request/GuildRequests.kt b/rest/src/main/kotlin/json/request/GuildRequests.kt index 0d7d3fe08b79..8a0bebabc76e 100644 --- a/rest/src/main/kotlin/json/request/GuildRequests.kt +++ b/rest/src/main/kotlin/json/request/GuildRequests.kt @@ -246,4 +246,9 @@ public data class GuildWelcomeScreenModifyRequest( @Serializable -public data class GuildScheduledEventUsersResponse(val users: List) +public data class GuildScheduledEventUsersResponse( + @SerialName("guild_scheduled_event_id") + val guildScheduledEventId: Snowflake, + val user: DiscordUser, + val member: Optional = Optional.Missing(), +) diff --git a/rest/src/main/kotlin/route/Route.kt b/rest/src/main/kotlin/route/Route.kt index 75adb2974cc2..1e9b979da182 100644 --- a/rest/src/main/kotlin/route/Route.kt +++ b/rest/src/main/kotlin/route/Route.kt @@ -837,10 +837,10 @@ public sealed class Route( ) public object GuildScheduledEventUsersGet : - Route( + Route>( HttpMethod.Get, "/guilds/$GuildId/scheduled-events/$ScheduledEventId/users", - GuildScheduledEventUsersResponse.serializer() + ListSerializer(GuildScheduledEventUsersResponse.serializer()), ) public object GuildScheduledEventPatch : diff --git a/rest/src/main/kotlin/service/GuildService.kt b/rest/src/main/kotlin/service/GuildService.kt index 9a4b863c069c..596489986b50 100644 --- a/rest/src/main/kotlin/service/GuildService.kt +++ b/rest/src/main/kotlin/service/GuildService.kt @@ -479,20 +479,45 @@ public class GuildService(requestHandler: RequestHandler) : RestService(requestH public suspend fun getScheduledEventUsers( guildId: Snowflake, eventId: Snowflake, - limit: Int? = null, + position: Position.BeforeOrAfter? = null, withMember: Boolean? = null, - ): GuildScheduledEventUsersResponse = call(Route.GuildScheduledEventUsersGet) { + limit: Int? = null, + ): List = call(Route.GuildScheduledEventUsersGet) { keys[Route.GuildId] = guildId keys[Route.ScheduledEventId] = eventId - if (limit != null) { - parameter("limit", limit) - } - - if (withMember != null) { - parameter("with_member", withMember) - } + limit?.let { parameter("limit", it) } + withMember?.let { parameter("with_member", it) } + position?.let { parameter(it.key, it.value) } } + + public suspend fun getScheduledEventUsersBefore( + guildId: Snowflake, + eventId: Snowflake, + before: Snowflake, + withMember: Boolean? = null, + limit: Int? = null, + ): List = getScheduledEventUsers( + guildId, + eventId, + Position.Before(before), + withMember, + limit, + ) + + public suspend fun getScheduledEventUsersAfter( + guildId: Snowflake, + eventId: Snowflake, + after: Snowflake, + withMember: Boolean? = null, + limit: Int? = null, + ): List = getScheduledEventUsers( + guildId, + eventId, + Position.After(after), + withMember, + limit, + ) } public suspend inline fun GuildService.modifyGuildWelcomeScreen( diff --git a/voice/src/main/kotlin/gateway/Command.kt b/voice/src/main/kotlin/gateway/Command.kt index 498f86c55a48..08746d6af7ef 100644 --- a/voice/src/main/kotlin/gateway/Command.kt +++ b/voice/src/main/kotlin/gateway/Command.kt @@ -73,7 +73,7 @@ public data class SendSpeaking( val ssrc: UInt ) : Command() -@OptIn(KordVoice::class) +@KordVoice @Serializable public data class SelectProtocol( val protocol: String,