diff --git a/common/src/main/kotlin/entity/AuditLog.kt b/common/src/main/kotlin/entity/AuditLog.kt index 18d4c2a3dc6e..1903b93ef9b7 100644 --- a/common/src/main/kotlin/entity/AuditLog.kt +++ b/common/src/main/kotlin/entity/AuditLog.kt @@ -23,6 +23,7 @@ data class DiscordAuditLog( @SerialName("audit_log_entries") val auditLogEntries: List, val integrations: List, + val threads: List ) @Serializable @@ -301,6 +302,20 @@ sealed class AuditLogChangeKey(val name: String, val serializer: KSerializer< @SerialName("expire_grace_period") object ExpireGracePeriod : AuditLogChangeKey("expire_grace_period", serializer()) + @SerialName("user_limit") + object UserLimit : AuditLogChangeKey("user_limit", serializer()) + + @SerialName("archived") + object Archived : AuditLogChangeKey("archived", serializer()) + + @SerialName("locked") + object Locked : AuditLogChangeKey("locked", serializer()) + + @SerialName("auto_archive_duration") + object AutoArchiveDuration : AuditLogChangeKey("auto_archive_duration", serializer()) + + @SerialName("default_auto_archive_duration") + object DefaultAutoArchiveDuration : AuditLogChangeKey("default_auto_archive_duration", serializer()) internal class Serializer(val type: KSerializer) : KSerializer> { override val descriptor: SerialDescriptor @@ -361,6 +376,11 @@ sealed class AuditLogChangeKey(val name: String, val serializer: KSerializer< "enable_emoticons" -> EnableEmoticons "expire_behavior" -> ExpireBehavior "expire_grace_period" -> ExpireGracePeriod + "user_limit" -> UserLimit + "locked" -> Locked + "archived" -> Archived + "auto_archive_duration" -> AutoArchiveDuration + "default_auto_archive_duration" -> DefaultAutoArchiveDuration else -> Unknown(name) } as AuditLogChangeKey } @@ -405,6 +425,16 @@ sealed class AuditLogEvent(val value: Int) { object IntegrationCreate : AuditLogEvent(80) object IntegrationUpdate : AuditLogEvent(81) object IntegrationDelete : AuditLogEvent(82) + object StageInstanceCreate : AuditLogEvent(83) + object StageInstanceUpdate : AuditLogEvent(84) + object StageInstanceDelete : AuditLogEvent(85) + object StickerCreate : AuditLogEvent(90) + object StickerUpdate : AuditLogEvent(91) + object StickerDelete : AuditLogEvent(92) + object ThreadCreate : AuditLogEvent(110) + object ThreadUpdate : AuditLogEvent(111) + object ThreadDelete : AuditLogEvent(112) + internal object Serializer : KSerializer { override val descriptor: SerialDescriptor @@ -450,6 +480,15 @@ sealed class AuditLogEvent(val value: Int) { 80 -> IntegrationCreate 81 -> IntegrationUpdate 82 -> IntegrationDelete + 83 -> StageInstanceCreate + 84 -> StageInstanceUpdate + 85 -> StageInstanceDelete + 90 -> StickerCreate + 91 -> StickerUpdate + 92 -> StickerDelete + 110 -> ThreadCreate + 111 -> ThreadUpdate + 112 -> ThreadDelete else -> Unknown(value) } } diff --git a/core/src/main/kotlin/behavior/GuildBehavior.kt b/core/src/main/kotlin/behavior/GuildBehavior.kt index 64876710a6f4..cb488969dcf8 100644 --- a/core/src/main/kotlin/behavior/GuildBehavior.kt +++ b/core/src/main/kotlin/behavior/GuildBehavior.kt @@ -15,6 +15,7 @@ import dev.kord.core.cache.idEq import dev.kord.core.catchDiscordError import dev.kord.core.entity.* import dev.kord.core.entity.channel.* +import dev.kord.core.entity.channel.thread.ThreadChannel import dev.kord.core.entity.interaction.GuildApplicationCommand import dev.kord.core.event.guild.MembersChunkEvent import dev.kord.core.exception.EntityNotFoundException @@ -65,6 +66,17 @@ interface GuildBehavior : KordEntity, Strategizable { val bans: Flow get() = supplier.getGuildBans(id) + /** + * Returns all active public and private threads in this guild + * Threads are ordered by their id, in descending order. + * + * The returned flow is lazily executed, any [RequestException] will be thrown on + * [terminal operators](https://kotlinlang.org/docs/reference/coroutines/flow.html#terminal-flow-operators) instead. + + */ + val activeThreads: Flow + get() = supplier.getActiveThreads(id) + /** * Requests to get all present webhooks for this guild. * diff --git a/core/src/main/kotlin/behavior/channel/threads/ThreadParentChannelBehavior.kt b/core/src/main/kotlin/behavior/channel/threads/ThreadParentChannelBehavior.kt index c4b413225744..a67beea148da 100644 --- a/core/src/main/kotlin/behavior/channel/threads/ThreadParentChannelBehavior.kt +++ b/core/src/main/kotlin/behavior/channel/threads/ThreadParentChannelBehavior.kt @@ -17,6 +17,7 @@ import dev.kord.core.supplier.EntitySupplier import dev.kord.core.supplier.EntitySupplyStrategy import dev.kord.rest.json.request.StartThreadRequest import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter import kotlinx.datetime.Clock import kotlinx.datetime.Instant import java.util.* @@ -33,7 +34,7 @@ interface ThreadParentChannelBehavior : TopGuildMessageChannelBehavior { * [terminal operators](https://kotlinlang.org/docs/reference/coroutines/flow.html#terminal-flow-operators) instead. */ - val activeThreads: Flow get() = supplier.getActiveThreads(id) + val activeThreads: Flow get() = supplier.getActiveThreads(guildId).filter { it.parentId == id } /** * Returns archived threads in the channel that are public. diff --git a/core/src/main/kotlin/entity/AuditLog.kt b/core/src/main/kotlin/entity/AuditLog.kt index 8ce178edd68d..486b4f8ac3f7 100644 --- a/core/src/main/kotlin/entity/AuditLog.kt +++ b/core/src/main/kotlin/entity/AuditLog.kt @@ -4,9 +4,12 @@ import dev.kord.common.entity.* import dev.kord.common.entity.optional.orEmpty import dev.kord.core.Kord import dev.kord.core.KordObject +import dev.kord.core.cache.data.ChannelData import dev.kord.core.cache.data.IntegrationData import dev.kord.core.cache.data.UserData import dev.kord.core.cache.data.WebhookData +import dev.kord.core.entity.channel.Channel +import dev.kord.core.entity.channel.thread.ThreadChannel class AuditLog(val data: DiscordAuditLog, val guildId: Snowflake, override val kord: Kord) : KordObject { @@ -16,6 +19,11 @@ class AuditLog(val data: DiscordAuditLog, val guildId: Snowflake, override val k val integrations: List get() = data.integrations.map { it.id } + val threads: List get() = data.threads.map { + val data = ChannelData.from(it) + Channel.from(data, kord) + }.filterIsInstance() + val entries: List get() = data.auditLogEntries.map { AuditLogEntry(it, kord) } } diff --git a/core/src/main/kotlin/supplier/CacheAwareRestSupplier.kt b/core/src/main/kotlin/supplier/CacheAwareRestSupplier.kt index 605506cde475..be1944abc39c 100644 --- a/core/src/main/kotlin/supplier/CacheAwareRestSupplier.kt +++ b/core/src/main/kotlin/supplier/CacheAwareRestSupplier.kt @@ -156,8 +156,8 @@ class StoreEntitySupplier( return storeOnEach(supplier.getThreadMembers(channelId)) { it.data } } - override fun getActiveThreads(channelId: Snowflake): Flow { - return storeOnEach(supplier.getActiveThreads(channelId)) { it.data } + override fun getActiveThreads(guildId: Snowflake): Flow { + return storeOnEach(supplier.getActiveThreads(guildId)) { it.data } } override fun getPublicArchivedThreads(channelId: Snowflake, before: Instant, limit: Int): Flow { diff --git a/core/src/main/kotlin/supplier/CacheEntitySupplier.kt b/core/src/main/kotlin/supplier/CacheEntitySupplier.kt index fb398386b30d..1a5a404e68c9 100644 --- a/core/src/main/kotlin/supplier/CacheEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/CacheEntitySupplier.kt @@ -286,9 +286,9 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { }.asFlow().map { ThreadMember(it, kord) } } - override fun getActiveThreads(channelId: Snowflake): Flow { + override fun getActiveThreads(guildId: Snowflake): Flow { return cache.query { - idEq(ChannelData::parentId, channelId) + idEq(ChannelData::guildId, guildId) }.asFlow().filter { it.threadMetadata.value?.archived != true }.mapNotNull { diff --git a/core/src/main/kotlin/supplier/EntitySupplier.kt b/core/src/main/kotlin/supplier/EntitySupplier.kt index 9c9934f1fc64..73d7b443848c 100644 --- a/core/src/main/kotlin/supplier/EntitySupplier.kt +++ b/core/src/main/kotlin/supplier/EntitySupplier.kt @@ -400,7 +400,7 @@ interface EntitySupplier { fun getThreadMembers(channelId: Snowflake): Flow - fun getActiveThreads(channelId: Snowflake): Flow + fun getActiveThreads(guildId: Snowflake): Flow fun getPublicArchivedThreads(channelId: Snowflake, before: Instant, limit: Int): Flow diff --git a/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt b/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt index 5a0644da16f4..c30b1550a92e 100644 --- a/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/FallbackEntitySupplier.kt @@ -124,8 +124,8 @@ private class FallbackEntitySupplier(val first: EntitySupplier, val second: Enti return first.getThreadMembers(channelId).switchIfEmpty(second.getThreadMembers(channelId)) } - override fun getActiveThreads(channelId: Snowflake): Flow { - return first.getActiveThreads(channelId).switchIfEmpty(second.getActiveThreads(channelId)) + override fun getActiveThreads(guildId: Snowflake): Flow { + return first.getActiveThreads(guildId).switchIfEmpty(second.getActiveThreads(guildId)) } override fun getPublicArchivedThreads(channelId: Snowflake, before: Instant, limit: Int): Flow { diff --git a/core/src/main/kotlin/supplier/RestEntitySupplier.kt b/core/src/main/kotlin/supplier/RestEntitySupplier.kt index b88354bb19f5..c42a4730385e 100644 --- a/core/src/main/kotlin/supplier/RestEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/RestEntitySupplier.kt @@ -346,8 +346,8 @@ class RestEntitySupplier(val kord: Kord) : EntitySupplier { } } - override fun getActiveThreads(channelId: Snowflake): Flow = flow { - kord.rest.channel.listActiveThreads(channelId).threads.onEach { + override fun getActiveThreads(guildId: Snowflake): Flow = flow { + kord.rest.guild.listActiveThreads(guildId).threads.onEach { val data = ChannelData.from(it) val channel = Channel.from(data, kord) if (channel is ThreadChannel) emit(channel) diff --git a/rest/src/main/kotlin/route/Route.kt b/rest/src/main/kotlin/route/Route.kt index 9f844afed35a..de94b9a197e7 100644 --- a/rest/src/main/kotlin/route/Route.kt +++ b/rest/src/main/kotlin/route/Route.kt @@ -714,13 +714,6 @@ sealed class Route( ListSerializer(DiscordThreadMember.serializer()) ) - object ActiveThreadsGet : - Route( - HttpMethod.Get, - "/channels/${ChannelId}/threads/active", - ListThreadsResponse.serializer() - ) - object PrivateThreadsGet : Route( @@ -750,6 +743,11 @@ sealed class Route( "/channels/$ChannelId/users/@me/threads/archived/private", ListThreadsResponse.serializer() ) + object ActiveThreadsGet : Route( + HttpMethod.Get, + " /guilds/${GuildId}/threads/active", + ListThreadsResponse.serializer() + ) companion object { diff --git a/rest/src/main/kotlin/service/ChannelService.kt b/rest/src/main/kotlin/service/ChannelService.kt index 8425e8e4f60f..3457a4819490 100644 --- a/rest/src/main/kotlin/service/ChannelService.kt +++ b/rest/src/main/kotlin/service/ChannelService.kt @@ -298,12 +298,6 @@ class ChannelService(requestHandler: RequestHandler) : RestService(requestHandle } } - suspend fun listActiveThreads(channelId: Snowflake): ListThreadsResponse { - return call(Route.ActiveThreadsGet) { - keys[Route.ChannelId] = channelId - - } - } suspend fun listPublicArchivedThreads(channelId: Snowflake, request: ListThreadsByTimestampRequest): ListThreadsResponse { return call(Route.PublicArchivedThreadsGet) { diff --git a/rest/src/main/kotlin/service/GuildService.kt b/rest/src/main/kotlin/service/GuildService.kt index 5ff1f900c459..d60d8f443444 100644 --- a/rest/src/main/kotlin/service/GuildService.kt +++ b/rest/src/main/kotlin/service/GuildService.kt @@ -13,6 +13,7 @@ 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.response.ListThreadsResponse import dev.kord.rest.request.RequestHandler import dev.kord.rest.request.auditLogReason import dev.kord.rest.route.Position @@ -408,6 +409,11 @@ class GuildService(requestHandler: RequestHandler) : RestService(requestHandler) } + suspend fun listActiveThreads(guildId: Snowflake): ListThreadsResponse { + return call(Route.ActiveThreadsGet) { + keys[Route.GuildId] = guildId + } + } } @OptIn(ExperimentalContracts::class) diff --git a/rest/src/test/resources/json/auditlog.json b/rest/src/test/resources/json/auditlog.json index fe88fb7a6f43..ffb97baa9f12 100644 --- a/rest/src/test/resources/json/auditlog.json +++ b/rest/src/test/resources/json/auditlog.json @@ -119,5 +119,6 @@ "id": "1234567" } } - ] + ], + "threads" : [] } \ No newline at end of file