Skip to content

Commit

Permalink
Merge branch 'root' into module/voice-rooms
Browse files Browse the repository at this point in the history
  • Loading branch information
gdude2002 committed May 3, 2023
2 parents 60f0ecd + d5182ff commit c93c08f
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 7 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ dependencies {
implementation(libs.kordex.mappings)
implementation(libs.kordex.phishing)
implementation(libs.kordex.pluralkit)
implementation(libs.kordex.unsafe)

implementation(libs.commons.text)
implementation(libs.homoglyph)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,9 +586,9 @@ public class TagsExtension : Extension() {
}

var potentialTags = (
tagsData.getTagsByPartialKey(tagKey, this.data.guildId.value) +
tagsData.getTagsByPartialTitle(tagKey, this.data.guildId.value)
)
tagsData.getTagsByPartialKey(tagKey, this.data.guildId.value) +
tagsData.getTagsByPartialTitle(tagKey, this.data.guildId.value)
)
.toSet()
.toList()

Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/org/quiltmc/community/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ suspend fun setupQuilt() = ExtensibleBot(DISCORD_TOKEN) {
extensions {
add(::ApplicationsExtension)
add(::FilterExtension)
add(::ForumExtension)
add(::LookupExtension)
add(::MessageLogExtension)
add(::MinecraftExtension)
Expand Down
4 changes: 4 additions & 0 deletions src/main/kotlin/org/quiltmc/community/_Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ internal val TOOLCHAIN_COMMUNITY_TEAM_ROLE = envOrNull("TOOLCHAIN_COMMUNITY_TEAM
?.let { Snowflake(it) }
?: Snowflake(863765983890374656)

internal val COMMUNITY_DEVELOPER_CATEGORY = envOrNull("COMMUNITY_DEVELOPER_CATEGORY")
?.let { Snowflake(it) }
?: Snowflake(1102169914858541066)

internal val COMMUNITY_DEVELOPER_ROLE = envOrNull("COMMUNITY_DEVELOPER_ROLE")
?.let { Snowflake(it) }
?: Snowflake(972868531844710412)
Expand Down
8 changes: 6 additions & 2 deletions src/main/kotlin/org/quiltmc/community/_Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import dev.kord.common.entity.UserFlag
import dev.kord.core.Kord
import dev.kord.core.behavior.UserBehavior
import dev.kord.core.behavior.channel.MessageChannelBehavior
import dev.kord.core.behavior.channel.threads.ThreadChannelBehavior
import dev.kord.core.behavior.getChannelOf
import dev.kord.core.entity.Guild
import dev.kord.core.entity.Message
Expand Down Expand Up @@ -168,7 +169,7 @@ suspend fun ExtensibleBotBuilder.settings() {
fun Guild.getMaxArchiveDuration(): ArchiveDuration {
val features = features.filter {
it.value == "THREE_DAY_THREAD_ARCHIVE" ||
it.value == "SEVEN_DAY_THREAD_ARCHIVE"
it.value == "SEVEN_DAY_THREAD_ARCHIVE"
}.map { it.value }

return when {
Expand All @@ -182,7 +183,7 @@ fun Guild.getMaxArchiveDuration(): ArchiveDuration {
// Logging-related extensions

suspend fun <C : SlashCommandContext<C, A, M>, A : Arguments, M : ModalForm>
SlashCommandContext<C, A, M>.getGithubLogChannel(): GuildMessageChannel? {
SlashCommandContext<C, A, M>.getGithubLogChannel(): GuildMessageChannel? {
val channelId = getKoin().get<GlobalSettingsCollection>().get()?.githubLogChannel ?: return null

return event.kord.getChannelOf<GuildMessageChannel>(channelId)
Expand Down Expand Up @@ -283,3 +284,6 @@ fun String.replaceParams(vararg pairs: Pair<String, Any?>): String {
fun String.replaceParams(pairs: Map<String, Any>): String = this.replaceParams(
*pairs.entries.map { it.toPair() }.toTypedArray()
)

suspend fun ThreadChannelBehavior.getFirstMessage() =
getMessageOrNull(id)
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

@file:Suppress("MagicNumber")
@file:OptIn(UnsafeAPI::class, KordUnsafe::class)

package org.quiltmc.community.modes.quilt.extensions

import com.kotlindiscord.kord.extensions.checks.hasRole
import com.kotlindiscord.kord.extensions.checks.or
import com.kotlindiscord.kord.extensions.commands.Arguments
import com.kotlindiscord.kord.extensions.commands.converters.impl.channel
import com.kotlindiscord.kord.extensions.commands.converters.impl.optionalChannel
import com.kotlindiscord.kord.extensions.components.forms.ModalForm
import com.kotlindiscord.kord.extensions.extensions.Extension
import com.kotlindiscord.kord.extensions.extensions.ephemeralSlashCommand
import com.kotlindiscord.kord.extensions.modules.unsafe.annotations.UnsafeAPI
import com.kotlindiscord.kord.extensions.modules.unsafe.extensions.unsafeSubCommand
import com.kotlindiscord.kord.extensions.modules.unsafe.types.InitialSlashCommandResponse
import com.kotlindiscord.kord.extensions.modules.unsafe.types.ackEphemeral
import com.kotlindiscord.kord.extensions.utils.extraData
import dev.kord.common.annotation.KordUnsafe
import dev.kord.common.entity.ChannelType
import dev.kord.core.behavior.channel.asChannelOfOrNull
import dev.kord.core.behavior.channel.createMessage
import dev.kord.core.behavior.channel.threads.edit
import dev.kord.core.behavior.edit
import dev.kord.core.behavior.interaction.modal
import dev.kord.core.behavior.interaction.response.createEphemeralFollowup
import dev.kord.core.entity.channel.ForumChannel
import dev.kord.core.entity.channel.thread.ThreadChannel
import kotlinx.coroutines.delay
import org.quiltmc.community.*
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds

class ForumExtension : Extension() {
override val name: String = "forum"

override suspend fun setup() {
ephemeralSlashCommand {
name = "forum"
description = "Forum channel management commands"

check {
hasBaseModeratorRole(true)

or {
hasRole(COMMUNITY_DEVELOPER_ROLE)

if (passed) {
event.extraData["isDeveloper"] = true
}
}
}

unsafeSubCommand(::ForumChannelArgs) {
name = "create-post"
description = "Create a Cozy-managed forum post"

initialResponse = InitialSlashCommandResponse.None

action {
val isDeveloper: Boolean = event.extraData.getOrDefault("isDeveloper", false) as Boolean
val forum = arguments.channel.asChannelOfOrNull<ForumChannel>()

if (forum == null) {
ackEphemeral {
content = "Please provide a forum channel to create a post within."
}

return@action
}

if (isDeveloper && forum.categoryId != COMMUNITY_DEVELOPER_CATEGORY) {
ackEphemeral {
content = "Quilt Developers may only use this command to create posts in the developer " +
"forum channels."
}

return@action
}

val form = PostModal()

this@unsafeSubCommand.componentRegistry.register(form)

event.interaction.modal(
form.translateTitle(getLocale(), bundle),
form.id
) {
form.applyToBuilder(this, getLocale(), bundle)
}

val interactionResponse = form.awaitCompletion {
it?.deferEphemeralResponseUnsafe()
} ?: return@action

val text = form.postText.value
val title = form.postTitle.value

if (text == null || title == null) {
interactionResponse.createEphemeralFollowup {
content = "Please provide the post title and text."
}

return@action
}

val thread = forum.startPublicThread(title) {
// TODO: Tags?

message(text)
}

interactionResponse.createEphemeralFollowup {
content = "Post created."
}

delay(1.seconds)

val setupMessage = thread.createMessage {
content = "Just a moment while we finish setting up..."
}

val firstMessage = thread.getFirstMessage()!!

firstMessage.pin("First message in the forum post")

setupMessage.edit {
content = "Adding role: <@&$COMMUNITY_MODERATOR_ROLE>"
}

delay(1.seconds)

setupMessage.edit {
content = "Adding role: <@&$COMMUNITY_DEVELOPER_ROLE>"
}

delay(1.seconds)

setupMessage.delete("Removing initial setup message")
}
}

unsafeSubCommand(::ForumPostArgs) {
name = "edit-post"
description = "Edit an existing Cozy-managed forum post"

initialResponse = InitialSlashCommandResponse.None

action {
val isDeveloper: Boolean = event.extraData.getOrDefault("isDeveloper", false) as Boolean

val thread = (arguments.post ?: channel)
.asChannelOfOrNull<ThreadChannel>()

val parent = thread
?.parent
?.asChannelOfOrNull<ForumChannel>()

if (parent == null) {
ackEphemeral {
content = "Please provide a forum thread to edit the first post for, or run this " +
"command directly within the thread."
}

return@action
}

if (isDeveloper && parent.categoryId != COMMUNITY_DEVELOPER_CATEGORY) {
ackEphemeral {
content = "Quilt Developers may only use this command for threads in the developer " +
"forum channels."
}

return@action
}

val firstMessage = thread.getFirstMessage()

if (firstMessage == null) {
ackEphemeral {
content = "Unable to find the first message for this thread - is it a Cozy-managed thread?"
}

return@action
}

val form = PostModal(thread.name, firstMessage.content)

this@unsafeSubCommand.componentRegistry.register(form)

event.interaction.modal(
form.translateTitle(getLocale(), bundle),
form.id
) {
form.applyToBuilder(this, getLocale(), bundle)
}

val interactionResponse = form.awaitCompletion {
it?.deferEphemeralResponseUnsafe()
} ?: return@action

val text = form.postText.value
val title = form.postTitle.value

if (text == null || title == null) {
interactionResponse.createEphemeralFollowup {
content = "Please provide the post title and text."
}

return@action
}

if (title != thread.name) {
thread.edit { name = title }
}

if (text != firstMessage.content) {
firstMessage.edit { content = text }
}

interactionResponse.createEphemeralFollowup {
content = "Post edited."
}
}
}
}
}

inner class ForumChannelArgs : Arguments() {
val channel by channel {
name = "channel"
description = "Forum channel to post in"

requireChannelType(ChannelType.GuildForum)
}
}

inner class ForumPostArgs : Arguments() {
val post by optionalChannel {
name = "post"
description = "Thread to edit the first post for"

requireChannelType(ChannelType.PublicGuildThread)
}
}

inner class PostModal(
private val givenTitle: String? = null,
private val givenText: String? = null,
) : ModalForm() {
override var title: String = "Create/Edit Post"
override val timeout: Duration = 15.minutes

val postTitle = lineText {
label = "Post Text"

minLength = 1
required = true

if (givenTitle == null) {
placeholder = "Forum post title"
} else {
initialValue = givenTitle
}
}

val postText = paragraphText {
label = "Post Text"

maxLength = 4000
minLength = 1
required = true

if (givenText == null) {
placeholder = "Forum post text - supports all Discord Markdown"
} else {
initialValue = givenText
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ class UtilityExtension : Extension() {
}
}

(GUILDS + COLLAB_GUILD).forEach { guildId ->
(GUILDS + COLLAB_GUILD).toSet().forEach { guildId ->
ephemeralMessageCommand(::EventModal) {
name = "Log Event"
allowInDms = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class SettingsExtension : Extension() {
}
}

(GUILDS + COLLAB_GUILD).forEach { guildId ->
(GUILDS + COLLAB_GUILD).toSet().forEach { guildId ->
ephemeralSlashCommand {
name = "config"
description = "Manage your bot settings"
Expand Down

0 comments on commit c93c08f

Please sign in to comment.