Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement forums #684

Merged
merged 46 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
a613c08
add forum error codes
lukellmann Apr 11, 2022
c477d87
update docs of Permission.SendMessages for forums
lukellmann Apr 11, 2022
357bc5b
add ChannelType.GuildForum
lukellmann Apr 11, 2022
a901dae
add ChannelFlags
lukellmann Apr 11, 2022
a737cd0
add DiscordChannel.flags
lukellmann Apr 11, 2022
971ad90
add ChannelData.flags
lukellmann Apr 11, 2022
b97f35e
add Channel.flags
lukellmann Apr 11, 2022
082561d
add cache updates for lastMessageId of forum channels
lukellmann Apr 11, 2022
0feef32
Merge branch '0.8.x' into feature/forum-channels
lukellmann Aug 29, 2022
22b069b
Implmenet high level abstractions
HopeBaron Sep 6, 2022
3a9def4
Merge branch '0.8.x' into feature/forum-channels
lukellmann Sep 10, 2022
64bee62
Error 220003
lukellmann Sep 10, 2022
8bbda00
API dump
lukellmann Sep 10, 2022
3d88903
Fix compile error
lukellmann Sep 10, 2022
0ae3af5
More error codes
lukellmann Sep 10, 2022
37326ec
Add `ChannelFlag.RequireTag`
lukellmann Sep 10, 2022
abfd138
Add `thread_name` for execute webhook
lukellmann Sep 11, 2022
de698f8
Merge branch '0.8.x' into feature/forum-channels
lukellmann Sep 12, 2022
1bb3dc8
Merge branch '0.8.x' into feature/forum-channels
lukellmann Sep 18, 2022
56eaf09
Regenerate `ChannelType`
lukellmann Sep 18, 2022
c73e942
Merge branch 'feature/forum-channels' into forums
lukellmann Sep 18, 2022
d3d803d
Remove `nsfwLevel` from channel
lukellmann Sep 18, 2022
1d00503
Dump API
lukellmann Sep 18, 2022
0221cf1
Merge branch '0.8.x' into forums
lukellmann Oct 8, 2022
1b5605f
Add SortOrderType
lukellmann Oct 8, 2022
7bd0400
Add missing fields to DiscordChannel
lukellmann Oct 8, 2022
c747c2f
Imports
lukellmann Oct 8, 2022
5d151e1
Merge branch '0.8.x' into forums
lukellmann Oct 9, 2022
0332a1d
Merge branch '0.8.x' into forums
lukellmann Oct 13, 2022
0d2cdb5
Merge branch '0.8.x' into forums
lukellmann Oct 25, 2022
329592d
Add missing properties for channel builders (#712)
luisfbl Nov 16, 2022
5016f49
Merge branch '0.8.x' into forums
lukellmann Nov 16, 2022
7d0957a
TextChannelModifyBuilder.defaultThreadRateLimitPerUser
lukellmann Nov 16, 2022
9476e4a
Merge branch '0.8.x' into forums
lukellmann Nov 18, 2022
85b32a3
Update KDoc for ChannelType.PublicGuildThread
lukellmann Nov 18, 2022
eb214ea
Add ChannelData.defaultSortOrder (#727)
NoComment1105 Nov 21, 2022
d22f886
Merge branch '0.8.x' into forums
lukellmann Nov 26, 2022
28ffaaa
Add default forum layout (#749)
NoComment1105 Jan 24, 2023
bfc3387
Merge branch '0.8.x' into forums
lukellmann Jan 24, 2023
b7ea9ea
Fix nullability and docs
lukellmann Jan 24, 2023
bfb6d82
Add missing default layout to core type (#752)
NoComment1105 Jan 24, 2023
739abf8
Merge branch '0.8.x' into forums
lukellmann Feb 15, 2023
2b4f033
Message counts and position for channel and message (#726)
NoComment1105 Feb 16, 2023
e764284
Merge remote-tracking branch 'origin/0.8.x' into forums
DRSchlaubi Feb 24, 2023
f1234da
Merge branch '0.8.x' into forums
lukellmann Mar 8, 2023
365cc05
Forum posts (#709)
Mar 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 171 additions & 5 deletions common/api/common.api

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public sealed class ChannelType(
public object PublicNewsThread : ChannelType(10)

/**
* A temporary sub-channel within a [GuildText] channel.
* A temporary sub-channel within a [GuildText] or [GuildForum] channel.
*/
public object PublicGuildThread : ChannelType(11)

Expand All @@ -109,6 +109,11 @@ public sealed class ChannelType(
*/
public object GuildDirectory : ChannelType(14)

/**
* A channel that can only contain threads.
*/
public object GuildForum : ChannelType(15)

internal object Serializer : KSerializer<ChannelType> {
public override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("dev.kord.common.entity.ChannelType", PrimitiveKind.INT)
Expand All @@ -128,6 +133,7 @@ public sealed class ChannelType(
12 -> PrivateThread
13 -> GuildStageVoice
14 -> GuildDirectory
15 -> GuildForum
else -> Unknown(value)
}
}
Expand All @@ -149,6 +155,7 @@ public sealed class ChannelType(
PrivateThread,
GuildStageVoice,
GuildDirectory,
GuildForum,
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// THIS FILE IS AUTO-GENERATED BY KordEnumProcessor.kt, DO NOT EDIT!
@file:Suppress(names = arrayOf("RedundantVisibilityModifier", "IncorrectFormatting",
"ReplaceArrayOfWithLiteral", "SpellCheckingInspection", "GrazieInspection"))

package dev.kord.common.entity

import kotlin.Any
import kotlin.Boolean
import kotlin.Int
import kotlin.LazyThreadSafetyMode.PUBLICATION
import kotlin.String
import kotlin.Suppress
import kotlin.collections.List
import kotlinx.serialization.KSerializer
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

/**
* See [SortOrderType]s in the
* [Discord Developer Documentation](https://discord.com/developers/docs/resources/channel#channel-object-sort-order-types).
*/
@Serializable(with = SortOrderType.Serializer::class)
public sealed class SortOrderType(
/**
* The raw value used by Discord.
*/
public val `value`: Int,
) {
public final override fun equals(other: Any?): Boolean = this === other ||
(other is SortOrderType && this.value == other.value)

public final override fun hashCode(): Int = value.hashCode()

public final override fun toString(): String =
"SortOrderType.${this::class.simpleName}(value=$value)"

/**
* An unknown [SortOrderType].
*
* This is used as a fallback for [SortOrderType]s that haven't been added to Kord yet.
*/
public class Unknown(
`value`: Int,
) : SortOrderType(value)

/**
* Sort forum posts by activity.
*/
public object LatestActivity : SortOrderType(0)

/**
* Sort forum posts by creation time (from most recent to oldest).
*/
public object CreationDate : SortOrderType(1)

internal object Serializer : KSerializer<SortOrderType> {
public override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("dev.kord.common.entity.SortOrderType", PrimitiveKind.INT)

public override fun serialize(encoder: Encoder, `value`: SortOrderType) =
encoder.encodeInt(value.value)

public override fun deserialize(decoder: Decoder) = when (val value = decoder.decodeInt()) {
0 -> LatestActivity
1 -> CreationDate
else -> Unknown(value)
}
}

public companion object {
/**
* A [List] of all known [SortOrderType]s.
*/
public val entries: List<SortOrderType> by lazy(mode = PUBLICATION) {
listOf(
LatestActivity,
CreationDate,
)
}

}
}
168 changes: 143 additions & 25 deletions common/src/main/kotlin/entity/DiscordChannel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
"(https://support.discord.com/hc/en-us/articles/360032008192).",
),
Entry("PublicNewsThread", intValue = 10, kDoc = "A temporary sub-channel within a [GuildNews] channel."),
Entry("PublicGuildThread", intValue = 11, kDoc = "A temporary sub-channel within a [GuildText] channel."),
Entry(
"PublicGuildThread", intValue = 11,
kDoc = "A temporary sub-channel within a [GuildText] or [GuildForum] channel."
),
Entry(
"PrivateThread", intValue = 12,
kDoc = "A temporary sub-channel within a [GuildText] channel that is only viewable by those invited and " +
Expand All @@ -33,6 +36,7 @@
kDoc = "The channel in a [hub](https://support.discord.com/hc/en-us/articles/4406046651927-Discord-" +
"Student-Hubs-FAQ) containing the listed servers.",
),
Entry("GuildForum", intValue = 15, kDoc = "A channel that can only contain threads."),
],
)

Expand All @@ -45,6 +49,15 @@
],
)

@file:GenerateKordEnum(
name = "SortOrderType", valueType = INT,
docUrl = "https://discord.com/developers/docs/resources/channel#channel-object-sort-order-types",
entries = [
Entry("LatestActivity", intValue = 0, kDoc = "Sort forum posts by activity."),
Entry("CreationDate", intValue = 1, kDoc = "Sort forum posts by creation time (from most recent to oldest)."),
],
)

@file:GenerateKordEnum(
name = "OverwriteType", valueType = INT,
docUrl = "https://discord.com/developers/docs/resources/channel#overwrite-object-overwrite-structure",
Expand All @@ -53,6 +66,7 @@

package dev.kord.common.entity

import dev.kord.common.entity.ChannelType.GuildForum
import dev.kord.common.entity.optional.Optional
import dev.kord.common.entity.optional.OptionalBoolean
import dev.kord.common.entity.optional.OptionalInt
Expand All @@ -66,36 +80,18 @@ 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 kotlin.DeprecationLevel.ERROR
import kotlin.LazyThreadSafetyMode.PUBLICATION
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes

/**
* A representation of a [Discord Channel Structure](https://discord.com/developers/docs/resources/channel).
*
* @param id The id of the channel.
* @param type the Type of channel.
* @param guildId the id of the guild.
* @param position The sorting position of the channel.
* @param permissionOverwrites The explicit permission overwrite for members and roles.
* @param name The name of the channel.
* @param topic The channel topic.
* @param nsfw Whether the channel is nsfw.
* @param lastMessageId The id of the last message sent in this channel (may not point to an existing or valid message).
* @param bitrate The bitrate (in bits) of the voice channel.
* @param userLimit The user limit of the voice channel.
* @param rateLimitPerUser amount of time a user has to wait before sending another message; bots,
* as well as users with the permission [Permission.ManageMessages] or [Permission.ManageChannels] are unaffected.
* @param recipients The recipients of the DM.
* @param icon The icon hash.
* @param ownerId The id of DM creator.
* @param applicationId The application id of the group DM creator if it is bot-created.
* @param parentId The id of the parent category for a channel.
* @param lastPinTimestamp When the last pinned message was pinned.
*/
@Serializable
public data class DiscordChannel(
val id: Snowflake,
Expand Down Expand Up @@ -138,9 +134,112 @@ public data class DiscordChannel(
val threadMetadata: Optional<DiscordThreadMetadata> = Optional.Missing(),
@SerialName("default_auto_archive_duration")
val defaultAutoArchiveDuration: Optional<ArchiveDuration> = Optional.Missing(),
val member: Optional<DiscordThreadMember> = Optional.Missing()
val member: Optional<DiscordThreadMember> = Optional.Missing(),
val flags: Optional<ChannelFlags> = Optional.Missing(),
@SerialName("total_message_sent")
val totalMessageSent: OptionalInt = OptionalInt.Missing,
@SerialName("available_tags")
val availableTags: Optional<List<DiscordForumTag>> = Optional.Missing(),
@SerialName("applied_tags")
val appliedTags: Optional<List<Snowflake>> = Optional.Missing(),
@SerialName("default_reaction_emoji")
val defaultReactionEmoji: Optional<DiscordDefaultReaction?> = Optional.Missing(),
@SerialName("default_thread_rate_limit_per_user")
val defaultThreadRateLimitPerUser: Optional<DurationInSeconds> = Optional.Missing(),
@SerialName("default_sort_order")
val defaultSortOrder: Optional<SortOrderType?> = Optional.Missing(),
)

public enum class ChannelFlag(public val code: Int) {

/** This thread is pinned to the top of its parent [GuildForum] channel. */
Pinned(1 shl 1),

/** Whether a tag is required to be specified when creating a thread in a [GuildForum] channel. */
RequireTag(1 shl 4);


public operator fun plus(flag: ChannelFlag): ChannelFlags = ChannelFlags(this.code or flag.code)

public operator fun plus(flags: ChannelFlags): ChannelFlags = flags + this
}

@Serializable(with = ChannelFlags.Serializer::class)
public data class ChannelFlags internal constructor(public val code: Int) {

public val flags: List<ChannelFlag> get() = ChannelFlag.values().filter { it in this }

public operator fun contains(flag: ChannelFlag): Boolean = this.code and flag.code == flag.code

public operator fun contains(flags: ChannelFlags): Boolean = this.code and flags.code == flags.code

public operator fun plus(flag: ChannelFlag): ChannelFlags = ChannelFlags(this.code or flag.code)

public operator fun plus(flags: ChannelFlags): ChannelFlags = ChannelFlags(this.code or flags.code)

public operator fun minus(flag: ChannelFlag): ChannelFlags = ChannelFlags(this.code and flag.code.inv())

public operator fun minus(flags: ChannelFlags): ChannelFlags = ChannelFlags(this.code and flags.code.inv())


public inline fun copy(builder: Builder.() -> Unit): ChannelFlags {
contract { callsInPlace(builder, InvocationKind.EXACTLY_ONCE) }
return Builder(code).apply(builder).build()
}


internal object Serializer : KSerializer<ChannelFlags> {

override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("dev.kord.common.entity.ChannelFlags", PrimitiveKind.INT)

override fun deserialize(decoder: Decoder): ChannelFlags {
val code = decoder.decodeInt()
return ChannelFlags(code)
}

override fun serialize(encoder: Encoder, value: ChannelFlags) {
encoder.encodeInt(value.code)
}
}


public class Builder(private var code: Int = 0) {

public operator fun ChannelFlag.unaryPlus() {
[email protected] = [email protected] or this.code
}

public operator fun ChannelFlags.unaryPlus() {
[email protected] = [email protected] or this.code
}

public operator fun ChannelFlag.unaryMinus() {
[email protected] = [email protected] and this.code.inv()
}

public operator fun ChannelFlags.unaryMinus() {
[email protected] = [email protected] and this.code.inv()
}

public fun build(): ChannelFlags = ChannelFlags(code)
}
}

public inline fun ChannelFlags(builder: ChannelFlags.Builder.() -> Unit): ChannelFlags {
contract { callsInPlace(builder, InvocationKind.EXACTLY_ONCE) }
return ChannelFlags.Builder().apply(builder).build()
}

public fun ChannelFlags(vararg flags: ChannelFlag): ChannelFlags = ChannelFlags { flags.forEach { +it } }

public fun ChannelFlags(vararg flags: ChannelFlags): ChannelFlags = ChannelFlags { flags.forEach { +it } }

public fun ChannelFlags(flags: Iterable<ChannelFlag>): ChannelFlags = ChannelFlags { flags.forEach { +it } }

@JvmName("ChannelFlags0")
public fun ChannelFlags(flags: Iterable<ChannelFlags>): ChannelFlags = ChannelFlags { flags.forEach { +it } }

@Serializable
public data class Overwrite(
val id: Snowflake,
Expand Down Expand Up @@ -216,3 +315,22 @@ public sealed class ArchiveDuration(
// TODO rename internal `NewSerializer` to `Serializer` when this is removed
public object Serializer : KSerializer<ArchiveDuration> by NewSerializer
}

@Serializable
public data class DiscordDefaultReaction(
@SerialName("emoji_id")
val emojiId: Snowflake?,
@SerialName("emoji_name")
val emojiName: String?,
)

@Serializable
public data class DiscordForumTag(
val id: Snowflake,
val name: String,
val moderated: Boolean,
@SerialName("emoji_id")
val emojiId: Snowflake?,
@SerialName("emoji_name")
val emojiName: String?,
)
5 changes: 4 additions & 1 deletion common/src/main/kotlin/entity/Permission.kt
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,10 @@ public sealed class Permission(public val code: DiscordBitSet) {
*/
public object ViewChannel : Permission(1L shl 10)

/** Allows for sending messages in a channel (does not allow sending messages in threads). */
/**
* Allows for sending messages in a channel and creating threads in a forum (does not allow sending messages in
* threads).
*/
public object SendMessages : Permission(1L shl 11)

/** Allows for sending of `/tts` messages. */
Expand Down
Loading