diff --git a/Dockerfile b/Dockerfile index 3eceb63..e0b3d56 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,4 +18,4 @@ COPY --from=build --chown=user:user /home/gradle/src/build/libs/application.jar ENV DOCKER_LOGGING="true" ENV JVM_OPTS="-XX:MaxRAMPercentage=75.0" -ENTRYPOINT ["java", "$JVM_OPTS", "-jar", "/app/application.jar"] +ENTRYPOINT java $JVM_OPTS -jar /app/application.jar diff --git a/build.gradle.kts b/build.gradle.kts index 5b88128..0175622 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,7 @@ @file:Suppress("GradlePackageUpdate") +//import dev.kordex.gradle.plugins.docker.file.DockerfileCommand +//import dev.kordex.gradle.plugins.docker.file.add import dev.kordex.gradle.plugins.kordex.DataCollection @@ -9,25 +11,28 @@ plugins { id("com.google.devtools.ksp") id("com.github.johnrengelman.shadow") id("dev.kordex.gradle.kordex") +// id("dev.kordex.gradle.docker") version "1.4.2" } group = "moe.nikky" version = "1.0-SNAPSHOT" -repositories { -// google() -// mavenCentral() -} - +//docker { +// generateOnBuild = true +// target = file("test_DockerFile") +// commands { +//// add(DockerfileCommand) +// } +//} kordEx { bot { - mainClass.set("moe.nikky.MainKt") - processDotEnv.set(true) - voice.set(false) - dataCollection.set(DataCollection.None) + mainClass = "moe.nikky.MainKt" + processDotEnv = true + voice = false + dataCollection = DataCollection.None } - addDependencies.set(true) - addRepositories.set(true) + addDependencies = true + addRepositories = true // kordVersion.set("latest") // kordExVersion.set("latest") // module("extra-phishing") diff --git a/settings.gradle.kts b/settings.gradle.kts index ff82f35..382cdb9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,14 +7,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "3.17" -//// # available:"3.17.1" -//// # available:"3.17.2" -//// # available:"3.17.3" -//// # available:"3.17.4" -//// # available:"3.17.5" -//// # available:"3.17.6" -//// # available:"3.18" + id("com.gradle.develocity") version "3.18.1" id("de.fayard.refreshVersions") version "0.60.5" } diff --git a/src/main/kotlin/moe/nikky/SchedulingExtension.kt b/src/main/kotlin/moe/nikky/SchedulingExtension.kt index 3ca45a7..17787e4 100644 --- a/src/main/kotlin/moe/nikky/SchedulingExtension.kt +++ b/src/main/kotlin/moe/nikky/SchedulingExtension.kt @@ -27,14 +27,23 @@ import dev.kord.core.behavior.channel.asChannelOf import dev.kord.core.behavior.channel.createMessage import dev.kord.core.behavior.edit import dev.kord.core.behavior.interaction.followup.edit +import dev.kord.core.entity.GuildEmoji +import dev.kord.core.entity.ReactionEmoji import dev.kord.core.entity.channel.GuildMessageChannel import dev.kord.core.entity.interaction.followup.EphemeralFollowupMessage import dev.kord.rest.builder.message.actionRow +import dev.kord.x.emoji.DiscordEmoji +import dev.kord.x.emoji.from +import dev.kordex.core.utils.from import io.klogging.Klogging +import kotlinx.coroutines.runBlocking import kotlinx.datetime.Instant import kotlinx.serialization.Serializable +import net.fellbaum.jemoji.EmojiGroup +import net.fellbaum.jemoji.EmojiManager import org.koin.core.component.inject import org.koin.dsl.module +import kotlin.jvm.optionals.getOrNull import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes @@ -120,8 +129,81 @@ class SchedulingExtension() : Extension(), Klogging { Permission.SendMessages, Permission.ManageMessages, ) + + } + val emojiList = run { + listOf( + "🇦", "🇧", "🇨", "🇩", "🇪", + "🇫", "🇬", "🇭", "🇮", "🇯", + "🇰", "🇱", "🇲", "🇳", "🇴", + "🇵", "🇶", "🇷", "🇸", "🇹", + "🇺", "🇻", "🇼", "🇽", "🇾", + "🇿", + "0️⃣", "1️⃣", "2️⃣", + "3️⃣", "5️⃣", "6️⃣", + "7️⃣", "4️⃣", "8️⃣", + "9️⃣", "🔟" + ).map { + ReactionEmoji.from(it) + } + +// listOf( +// ('a'..'z').map { c -> +// ":regional_indicator_$c:" +// }.map { +// DiscordEmoji.Generic(it) +// }, +// (0..10).map { n -> +// ":number_$n:" +// }.map { +// DiscordEmoji.Generic(it) +// }, +// ).flatten().mapNotNull { +//// val r = ReactionEmoji.from(DiscordEmoji.Generic(it)) +//// runBlocking { logger.info { "${r}: ${r.mention}" } } +//// EmojiManager.getByAlias(it).getOrNull()?.let { emoji -> +//// runBlocking { logger.info { "${emoji.unicode}: $emoji" } } +//// ReactionEmoji.from(emoji.emoji) +//// } +// ReactionEmoji.from(it).also { +// runBlocking { logger.info { it.mention } } +// } +// } + } +// val emojiList2 = run { +// +// EmojiManager.getAllEmojisSubGrouped() +// .forEach { (subgroup, emojis) -> +// emojis.forEach { emoji -> +// runBlocking { +// logger.info { "${subgroup.group} ${subgroup.name} ${emoji.unicode}: $emoji" } +// } +// } +// +// } +//// } +// EmojiManager.getAllEmojisByGroup(EmojiGroup.SYMBOLS).filter { emoji -> +//// logger.info { "${emoji.unicode}: $emoji" } +// emoji.allAliases.any { +// it.startsWith(":number") || +// it.startsWith(":regional_indicator_") +// } +// }.also { +// runBlocking { +// logger.info { "filtered" } +// } +// it.forEach { emoji -> +// runBlocking { +// logger.info { "${emoji.unicode}: $emoji" } +// } +// } +// } +// }.map { +// ReactionEmoji.from(it.emoji) +// } + private suspend fun tryParseInstant(value: String): Instant? { try { return Instant.parse(value) @@ -293,6 +375,7 @@ class SchedulingExtension() : Extension(), Klogging { name = "create" description = "register a new event" + requireBotPermissions( Permission.SendMessages, ) @@ -336,6 +419,20 @@ class SchedulingExtension() : Extension(), Klogging { (config.get() ?: SchedulingData()).update(arguments.id, newEvent) ) + val timeslots = + newEvent.start.epochSeconds until newEvent.end.epochSeconds step newEvent.slotLength.inWholeSeconds + + val timeslotsMap = timeslots.mapIndexed { index, epochSeconds -> + val start = Instant.fromEpochSeconds(epochSeconds) + val end = start + newEvent.slotLength + val emoji = emojiList[index] + emoji to "${emoji.mention} ${TimestampType.LongDateTime.format(end.epochSeconds)} -> ${ + TimestampType.LongDateTime.format( + end.epochSeconds + ) + }" + }.toMap() + message.edit { content = """ id: `${newEvent.id}` @@ -344,6 +441,11 @@ class SchedulingExtension() : Extension(), Klogging { TimestampType.RelativeTime.format( newEvent.start.epochSeconds ) + } + end: ${TimestampType.LongDateTime.format(newEvent.end.epochSeconds)} ${ + TimestampType.RelativeTime.format( + newEvent.end.epochSeconds + ) } slots: ${newEvent.slotLength} @@ -353,8 +455,18 @@ class SchedulingExtension() : Extension(), Klogging { ``` /signup event:${newEvent.id} ``` - """.trimIndent() + """.trimIndent() + "\n" + timeslotsMap.values.joinToString("\n") } + + try { + timeslotsMap.keys.forEach { + message.addReaction(it) + } + } catch (e: Exception) { + logger.error(e) { e.message ?: "unknown" } + relayError(e.message ?: "unknown") + } + respond { content = "event created" } @@ -385,225 +497,225 @@ class SchedulingExtension() : Extension(), Klogging { } } - ephemeralSlashCommand(::SignupArgs) { - name = "signup" - description = "signup for events" - allowInDms = false - - action { - withLogContext(event, guild) { guild -> - val config = guild.config() - val schedulingData = config.get() ?: SchedulingData() - logger.info { "loading event ${arguments.event}" } - val scheduledEvent = schedulingData.events[arguments.event] - ?: relayError("could not find event for key ${arguments.event}") - -// val messageChannel = event.interaction.channel.asChannelOf() - try { - var selectedTimeslot: Instant? = null - var response: EphemeralFollowupMessage? = null -// var button: EphemeralInteractionButton? = null -// var timeslotSelection: EphemeralStringSelectMenu? = null - - suspend fun updateResponse( - buttonEnabled: Boolean = false, - ) { -// button = null - response?.edit { - content = "please select a timeslot and submit" - components { - removeAll() - ephemeralStringSelectMenu( - row = 0 - ) { - minimumChoices = 1 - maximumChoices = 1 // TODO: allow signing up for multiple timeslots ? - placeholder = "timeslot start" - - val timeslots = - scheduledEvent.start.epochSeconds..scheduledEvent.end.epochSeconds step scheduledEvent.slotLength.inWholeSeconds - timeslots.forEachIndexed() { index, epochSeconds -> - val instant = Instant.fromEpochSeconds(epochSeconds) - val label = TimestampType.LongDateTime.format(epochSeconds) - val value = instant.toString() - - option(label, value) { - if(instant == selectedTimeslot) { - default = true - } - val end = instant + scheduledEvent.slotLength - description = - "slot: $index, until ${TimestampType.LongDateTime.format(end.epochSeconds)}" - } - } - - - action { modal -> - selectedTimeslot = selected.firstOrNull() ?.let { - Instant.parse(it) - } - - -// validateValues() - val instant = selectedTimeslot - if (instant != null && instant >= scheduledEvent.start && instant < scheduledEvent.end) { - if(!buttonEnabled) { - updateResponse(buttonEnabled = true) - } - } else { - if(buttonEnabled) { - updateResponse(buttonEnabled = false) - } - } - } - } - ephemeralButton( - row = 1 - ) { - if(buttonEnabled) { - enable() - } else { - disable() - } -// disable() - style = ButtonStyle.Success - label = "Submit" - - action { modal -> - try { - val instant = selectedTimeslot ?: relayError("no timeslot was selected") - - val signup = Signup( - user = user.id, - slot = instant, - duration = scheduledEvent.slotLength - ) - - //TODO: check for duplication - - config.save( - (config.get() ?: SchedulingData()).update( - arguments.event - ) { event -> - event.addSignup(signup) - } - ) - - response?.edit { - content = "registered ${user.mention} for ${TimestampType.ShortDateTime.format(instant.epochSeconds)}" - - suppressEmbeds = true - logger.info { "components: ${components?.size}" } - logger.info { "embeds: ${embeds?.size}" } - components { - removeAll() - } - embeds?.clear() - components?.clear() - } ?: relayError("failed up update response") - } catch (e: Exception) { - logger.error(e) { "something exploded" } - relayError(e.message ?: "unknown error") - } - } - } - } - } ?: relayError("failed up update response") - } - - response = respond { - content = "please select a timeslot and submit" - /* components { - button = ephemeralButton( - row = 1 - ) { -// disable() - style = ButtonStyle.Success - label = "Submit" - - action { modal -> - try { - val instant = selectedTimeslot ?: relayError("no timeslot was selected") - - val signup = Signup( - user = user.id, - slot = instant, - duration = scheduledEvent.slotLength - ) - - //TODO: check for duplication - - config.save( - (config.get() ?: SchedulingData()).update( - arguments.event - ) { event -> - event.addSignup(signup) - } - ) - - response!!.edit { - content = " registered ${user.mention} for ${TimestampType.ShortDateTime.format(instant.epochSeconds)}" - } - } catch (e: Exception) { - logger.error(e) { "something exploded" } - relayError(e.message ?: "unknown error") - } - } - } - val timeslotSelection = ephemeralStringSelectMenu( - row = 0 - ) { - minimumChoices = 1 - maximumChoices = 1 // TODO: allow signing up for multiple timeslots ? - placeholder = "timeslot start" - -// body = { modal -> -// selectedTimeslot = selected.firstOrNull() +// ephemeralSlashCommand(::SignupArgs) { +// name = "signup" +// description = "signup for events" +// allowInDms = false +// +// action { +// withLogContext(event, guild) { guild -> +// val config = guild.config() +// val schedulingData = config.get() ?: SchedulingData() +// logger.info { "loading event ${arguments.event}" } +// val scheduledEvent = schedulingData.events[arguments.event] +// ?: relayError("could not find event for key ${arguments.event}") +// +//// val messageChannel = event.interaction.channel.asChannelOf() +// try { +// var selectedTimeslot: Instant? = null +// var response: EphemeralFollowupMessage? = null +//// var button: EphemeralInteractionButton? = null +//// var timeslotSelection: EphemeralStringSelectMenu? = null +// +// suspend fun updateResponse( +// buttonEnabled: Boolean = false, +// ) { +//// button = null +// response?.edit { +// content = "please select a timeslot and submit" +// components { +// removeAll() +// ephemeralStringSelectMenu( +// row = 0 +// ) { +// minimumChoices = 1 +// maximumChoices = 1 // TODO: allow signing up for multiple timeslots ? +// placeholder = "timeslot start" +// +// val timeslots = +// scheduledEvent.start.epochSeconds..scheduledEvent.end.epochSeconds step scheduledEvent.slotLength.inWholeSeconds +// timeslots.forEachIndexed() { index, epochSeconds -> +// val instant = Instant.fromEpochSeconds(epochSeconds) +// val label = TimestampType.LongDateTime.format(epochSeconds) +// val value = instant.toString() +// +// option(label, value) { +// if(instant == selectedTimeslot) { +// default = true +// } +// val end = instant + scheduledEvent.slotLength +// description = +// "slot: $index, until ${TimestampType.LongDateTime.format(end.epochSeconds)}" +// } +// } +// +// +// action { modal -> +// selectedTimeslot = selected.firstOrNull() ?.let { +// Instant.parse(it) +// } +// +// +//// validateValues() +// val instant = selectedTimeslot +// if (instant != null && instant >= scheduledEvent.start && instant < scheduledEvent.end) { +// if(!buttonEnabled) { +// updateResponse(buttonEnabled = true) +// } +// } else { +// if(buttonEnabled) { +// updateResponse(buttonEnabled = false) +// } +// } +// } // } - - - val timeslots = - scheduledEvent.start.epochSeconds..scheduledEvent.end.epochSeconds step scheduledEvent.slotLength.inWholeSeconds - timeslots.forEachIndexed() { index, epochSeconds -> - val instant = Instant.fromEpochSeconds(epochSeconds) - val label = TimestampType.LongDateTime.format(epochSeconds) - val value = instant.toString() - - option(label, value) { - val end = instant + scheduledEvent.slotLength - description = - "slot: $index, until ${TimestampType.LongDateTime.format(end.epochSeconds)}" - } - } - - - action { modal -> - selectedTimeslot = selected.firstOrNull() ?.let { - Instant.parse(it) - } -// validateValues() - val instant = selectedTimeslot?.let { - Instant.parse(it) - } - selectedTimeslot = instant -// if (instant != null && instant >= scheduledEvent.start && instant < scheduledEvent.end) { -// button.enable() +// ephemeralButton( +// row = 1 +// ) { +// if(buttonEnabled) { +// enable() // } else { -// button.disable() +// disable() // } - } - } - - - }*/ - } - updateResponse() - } catch (e: Exception) { - logger.error(e) { "something exploded" } - relayError(e.message ?: "unknown error") - } - } - } - } +//// disable() +// style = ButtonStyle.Success +// label = "Submit" +// +// action { modal -> +// try { +// val instant = selectedTimeslot ?: relayError("no timeslot was selected") +// +// val signup = Signup( +// user = user.id, +// slot = instant, +// duration = scheduledEvent.slotLength +// ) +// +// //TODO: check for duplication +// +// config.save( +// (config.get() ?: SchedulingData()).update( +// arguments.event +// ) { event -> +// event.addSignup(signup) +// } +// ) +// +// response?.edit { +// content = "registered ${user.mention} for ${TimestampType.ShortDateTime.format(instant.epochSeconds)}" +// +// suppressEmbeds = true +// logger.info { "components: ${components?.size}" } +// logger.info { "embeds: ${embeds?.size}" } +// components { +// removeAll() +// } +// embeds?.clear() +// components?.clear() +// } ?: relayError("failed up update response") +// } catch (e: Exception) { +// logger.error(e) { "something exploded" } +// relayError(e.message ?: "unknown error") +// } +// } +// } +// } +// } ?: relayError("failed up update response") +// } +// +// response = respond { +// content = "please select a timeslot and submit" +// /* components { +// button = ephemeralButton( +// row = 1 +// ) { +//// disable() +// style = ButtonStyle.Success +// label = "Submit" +// +// action { modal -> +// try { +// val instant = selectedTimeslot ?: relayError("no timeslot was selected") +// +// val signup = Signup( +// user = user.id, +// slot = instant, +// duration = scheduledEvent.slotLength +// ) +// +// //TODO: check for duplication +// +// config.save( +// (config.get() ?: SchedulingData()).update( +// arguments.event +// ) { event -> +// event.addSignup(signup) +// } +// ) +// +// response!!.edit { +// content = " registered ${user.mention} for ${TimestampType.ShortDateTime.format(instant.epochSeconds)}" +// } +// } catch (e: Exception) { +// logger.error(e) { "something exploded" } +// relayError(e.message ?: "unknown error") +// } +// } +// } +// val timeslotSelection = ephemeralStringSelectMenu( +// row = 0 +// ) { +// minimumChoices = 1 +// maximumChoices = 1 // TODO: allow signing up for multiple timeslots ? +// placeholder = "timeslot start" +// +//// body = { modal -> +//// selectedTimeslot = selected.firstOrNull() +//// } +// +// +// val timeslots = +// scheduledEvent.start.epochSeconds..scheduledEvent.end.epochSeconds step scheduledEvent.slotLength.inWholeSeconds +// timeslots.forEachIndexed() { index, epochSeconds -> +// val instant = Instant.fromEpochSeconds(epochSeconds) +// val label = TimestampType.LongDateTime.format(epochSeconds) +// val value = instant.toString() +// +// option(label, value) { +// val end = instant + scheduledEvent.slotLength +// description = +// "slot: $index, until ${TimestampType.LongDateTime.format(end.epochSeconds)}" +// } +// } +// +// +// action { modal -> +// selectedTimeslot = selected.firstOrNull() ?.let { +// Instant.parse(it) +// } +//// validateValues() +// val instant = selectedTimeslot?.let { +// Instant.parse(it) +// } +// selectedTimeslot = instant +//// if (instant != null && instant >= scheduledEvent.start && instant < scheduledEvent.end) { +//// button.enable() +//// } else { +//// button.disable() +//// } +// } +// } +// +// +// }*/ +// } +// updateResponse() +// } catch (e: Exception) { +// logger.error(e) { "something exploded" } +// relayError(e.message ?: "unknown error") +// } +// } +// } +// } } } \ No newline at end of file diff --git a/versions.properties b/versions.properties index c541cdf..20bb8c6 100644 --- a/versions.properties +++ b/versions.properties @@ -9,7 +9,8 @@ plugin.dev.kordex.gradle.kordex=1.4.2 -plugin.com.google.devtools.ksp=2.0.20-1.0.24 +plugin.com.google.devtools.ksp=2.0.20-1.0.25 +## # available=2.1.0-Beta1-1.0.25 plugin.com.github.johnrengelman.shadow=8.1.1 @@ -27,22 +28,19 @@ version.io.klogging..slf4j-klogging=0.7.2 version.kord.x.emoji=0.5.0 version.kotlin=2.0.20 +## # available=2.1.0-Beta1 -version.kotlinx.coroutines=1.8.1 -## # available=1.9.0-RC -## # available=1.9.0-RC.2 +version.kotlinx.coroutines=1.9.0 version.kotlinx.datetime=0.6.1 -version.kotlinx.serialization=1.7.2 +version.kotlinx.serialization=1.7.3 version.ktor=2.3.12 ### available=3.0.0-beta-1 ### available=3.0.0-beta-2 +### available=3.0.0-rc-1 -version.org.slf4j..slf4j-api=2.0.13 -## # available=2.0.14 -## # available=2.0.15 -## # available=2.0.16 +version.org.slf4j..slf4j-api=2.0.16 ## # available=2.1.0-alpha0 ## # available=2.1.0-alpha1