From bbf08ba2d763394577708cba3a21f1c4c94ba64b Mon Sep 17 00:00:00 2001 From: limited_dev Date: Thu, 18 May 2023 20:50:43 +0200 Subject: [PATCH] feat: finished TimePlanner, the bot now can send a planning message mondays 4AM UTC Signed-off-by: limited_dev --- build.gradle.kts | 7 +- src/main/kotlin/net/moonleay/lilJudd/Bot.kt | 18 ++- .../extensions/FeatureManageExtension.kt | 2 +- .../lilJudd/extensions/VersionExtension.kt | 3 +- .../moonleay/lilJudd/features/TimePlanner.kt | 110 +++++++++++++++ .../net/moonleay/lilJudd/util/MessageUtil.kt | 16 ++- .../net/moonleay/lilJudd/util/TimeUtil.kt | 129 ++++++++++++++++++ .../moonleay/lilJudd/build/BuildConstants.kt | 3 +- 8 files changed, 279 insertions(+), 9 deletions(-) create mode 100644 src/main/kotlin/net/moonleay/lilJudd/features/TimePlanner.kt create mode 100644 src/main/kotlin/net/moonleay/lilJudd/util/TimeUtil.kt diff --git a/build.gradle.kts b/build.gradle.kts index ee67215..2776494 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,6 +39,7 @@ val coroutinesver = "1.1.0" val ktor_version = "2.3.0" val exposedver = "0.40.1" val postgresver = "42.3.8" +val krontabver = "1.0.0" val mavenArtifact = "lilJudd" project.base.archivesName.set(mavenArtifact) @@ -93,6 +94,9 @@ dependencies { shadow("org.jetbrains.exposed:exposed-dao:$exposedver") shadow("org.jetbrains.exposed:exposed-jdbc:$exposedver") shadow("org.postgresql:postgresql:$postgresver") + + //Korntab + shadow("dev.inmo:krontab:$krontabver") } @@ -106,7 +110,8 @@ val templateProps = mapOf( "coroutinesversion" to coroutinesver, "ktorversion" to ktor_version, "exposedversion" to exposedver, - "postgresversion" to postgresver + "postgresversion" to postgresver, + "krontabversion" to krontabver ) diff --git a/src/main/kotlin/net/moonleay/lilJudd/Bot.kt b/src/main/kotlin/net/moonleay/lilJudd/Bot.kt index 8b3ce9a..ec55b96 100644 --- a/src/main/kotlin/net/moonleay/lilJudd/Bot.kt +++ b/src/main/kotlin/net/moonleay/lilJudd/Bot.kt @@ -20,17 +20,20 @@ package net.moonleay.lilJudd import com.kotlindiscord.kord.extensions.ExtensibleBot import dev.kord.common.entity.PresenceStatus +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import net.moonleay.botendo.build.BuildConstants import net.moonleay.lilJudd.data.CredentialManager import net.moonleay.lilJudd.data.DB import net.moonleay.lilJudd.extensions.FeatureManageExtension import net.moonleay.lilJudd.extensions.VersionExtension +import net.moonleay.lilJudd.features.TimePlanner import net.moonleay.lilJudd.util.Logger import kotlin.system.exitProcess object Bot { //The kord object gets set at app launch - private lateinit var bot: ExtensibleBot + lateinit var bot: ExtensibleBot suspend fun start() { Logger.out("Starting Bot...") @@ -49,7 +52,7 @@ object Bot { exitProcess(3) } - //Connect to the Database + //Connect to the database DB.connect( CredentialManager.dbDomain, CredentialManager.dbName, @@ -57,6 +60,17 @@ object Bot { CredentialManager.dbPassword ) + //Register the TimePlanner thread + val coroutineJob = GlobalScope.launch { + TimePlanner.registerThread() + } + + // Add a shutdown hook to cancel the coroutine when the application is terminated + Runtime.getRuntime().addShutdownHook(Thread { + coroutineJob.cancel() + }) + + //Add Bot token to kord bot = ExtensibleBot(CredentialManager.token) { applicationCommands { diff --git a/src/main/kotlin/net/moonleay/lilJudd/extensions/FeatureManageExtension.kt b/src/main/kotlin/net/moonleay/lilJudd/extensions/FeatureManageExtension.kt index 61b598d..2bdd9e4 100644 --- a/src/main/kotlin/net/moonleay/lilJudd/extensions/FeatureManageExtension.kt +++ b/src/main/kotlin/net/moonleay/lilJudd/extensions/FeatureManageExtension.kt @@ -92,7 +92,7 @@ class FeatureManageExtension : Extension() { MessageUtil.getEmbed( Color(0x52E01A), "200: Success", - "The feature was enabled in channel ${args.channel.data.name}", + "The feature was enabled in channel ${args.channel.data.name.value}", u.asUser().username + "#" + u.asUser().discriminator ) ) diff --git a/src/main/kotlin/net/moonleay/lilJudd/extensions/VersionExtension.kt b/src/main/kotlin/net/moonleay/lilJudd/extensions/VersionExtension.kt index 949df89..c116c02 100644 --- a/src/main/kotlin/net/moonleay/lilJudd/extensions/VersionExtension.kt +++ b/src/main/kotlin/net/moonleay/lilJudd/extensions/VersionExtension.kt @@ -37,7 +37,8 @@ class VersionExtension : Extension() { "Lil' Judd", "Lil' Judd ***v." + BuildConstants.version + "***\n" + "Kord-Extensions ***v." + BuildConstants.kordVersion + "***\n" + - "Coroutines ***v." + BuildConstants.coroutinesVersion + "***" + "Coroutines ***v." + BuildConstants.coroutinesVersion + "***\n" + + "Krontab ***v." + BuildConstants.krontabVersion + "***" ) } } diff --git a/src/main/kotlin/net/moonleay/lilJudd/features/TimePlanner.kt b/src/main/kotlin/net/moonleay/lilJudd/features/TimePlanner.kt new file mode 100644 index 0000000..31775c1 --- /dev/null +++ b/src/main/kotlin/net/moonleay/lilJudd/features/TimePlanner.kt @@ -0,0 +1,110 @@ +/* + * lilJudd + * Copyright (C) 2023 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.lilJudd.features + +import com.kotlindiscord.kord.extensions.utils.addReaction +import dev.inmo.krontab.buildSchedule +import dev.inmo.krontab.doInfinity +import dev.kord.common.Color +import dev.kord.common.entity.Snowflake +import dev.kord.core.behavior.channel.createMessage +import dev.kord.core.entity.Message +import dev.kord.core.entity.channel.MessageChannel +import kotlinx.coroutines.delay +import net.moonleay.lilJudd.Bot +import net.moonleay.lilJudd.data.tables.TimePlanningChannels +import net.moonleay.lilJudd.util.Logger +import net.moonleay.lilJudd.util.MessageUtil +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.ZoneId +import java.time.ZonedDateTime + + +object TimePlanner { + /* /--------------- Seconds + | /------------- Minutes + | | /----------- Hours + | | | /--------- Days of months + | | | | /------- Months + | | | | | /----- (optional) Year + | | | | | | /--- (optional) Timezone offset + | | | | | | | / (optional) Week days + * * * * * * 0o *w*/ + + suspend fun registerThread() { + Logger.out("Adding scheduler...") + val scheduler = buildSchedule("0 0 4 * * * 0o 1") // 0 0 4 * * * 0o 1 // 0o is UTC + scheduler.doInfinity { + Logger.out("Starting to notify...") + val channelList = mutableListOf() + transaction { + for (tp in TimePlanningChannels.selectAll()) { + channelList.add(Snowflake(tp[TimePlanningChannels.channelid])) + Logger.out("Have to notify channel with ID ${tp[TimePlanningChannels.channelid]}.") + } + } + Logger.out("${channelList.count()} Channels to notify!") + for (ch in channelList) { + if (Bot.bot.kordRef.getChannel(ch)!!.asChannelOrNull() == null) + return@doInfinity + val c = Bot.bot.kordRef.getChannelOf(ch)!! + c.createMessage { + this.embeds.add( + MessageUtil.getEmbed( + Color(0X4C4645), + "Time Planning Feature", + "Do you have time on the following Days?", + "Automated Message" + ) + ) + } + delay(3000) + var then = ZonedDateTime.now(ZoneId.of("Europe/Berlin")).withHour(4).withMinute(0).withSecond(0) + repeat(6) { + val m = c.createMessage { + this.embeds.add( + MessageUtil.getEmbedSmall( + Color(0X4C4645), + "", + "${then.dayOfWeek.name}, ${then.dayOfMonth}.${then.monthValue}.${then.year} /${it + 1}. weekday" + ) + ) + } + then = then.plusDays(1).withHour(20).withMinute(24).withSecond(0) + delay(500) + addReactions(m) + delay(1000) + Logger.out("Finished sending day $it") + } + Logger.out("Finished with ${c.data.guildId.value}") + } + Logger.out("Done! Until next Monday! <3 ") + } + } + + private suspend fun addReactions(msg: Message) { + msg.addReaction("✅") + delay(300) + msg.addReaction("❌") + delay(300) + msg.addReaction("❓") + delay(300) + } +} diff --git a/src/main/kotlin/net/moonleay/lilJudd/util/MessageUtil.kt b/src/main/kotlin/net/moonleay/lilJudd/util/MessageUtil.kt index d299336..0544acb 100644 --- a/src/main/kotlin/net/moonleay/lilJudd/util/MessageUtil.kt +++ b/src/main/kotlin/net/moonleay/lilJudd/util/MessageUtil.kt @@ -70,6 +70,18 @@ object MessageUtil { } } + ///Get an embedded msg with title and description + fun getEmbedSmall( + color: Color, + title: String, + description: String + ): EmbedBuilder { + val ebb = EmbedBuilder() + ebb.title = title + ebb.description = description + return ebb + } + ///Get an embedded msg with title, description and a src fun getEmbed( color: Color, @@ -77,10 +89,8 @@ object MessageUtil { description: String, source: String ): EmbedBuilder { + val ebb = getEmbedSmall(color, title, description) val now: LocalDateTime = LocalDateTime.now() - val ebb = EmbedBuilder() - ebb.title = title - ebb.description = description ebb.footer = EmbedBuilder.Footer() ebb.footer!!.text = ">" + dtf.format(now) + " - $source" ebb.color = color diff --git a/src/main/kotlin/net/moonleay/lilJudd/util/TimeUtil.kt b/src/main/kotlin/net/moonleay/lilJudd/util/TimeUtil.kt new file mode 100644 index 0000000..1edfe17 --- /dev/null +++ b/src/main/kotlin/net/moonleay/lilJudd/util/TimeUtil.kt @@ -0,0 +1,129 @@ +/* + * lilJudd + * Copyright (C) 2023 moonleay + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.moonleay.lilJudd.util + +import java.time.Duration +import java.util.concurrent.TimeUnit + + +object TimeUtil { + + fun getTimeFormatedShortend(time2: Long): String { + var time = time2 + val days: Long = TimeUnit.MILLISECONDS + .toDays(time) + time -= TimeUnit.DAYS.toMillis(days) + val hours: Long = TimeUnit.MILLISECONDS + .toHours(time) + time -= TimeUnit.HOURS.toMillis(hours) + val minutes: Long = TimeUnit.MILLISECONDS + .toMinutes(time) + time -= TimeUnit.MINUTES.toMillis(minutes) + val seconds: Long = TimeUnit.MILLISECONDS + .toSeconds(time) + var s = "" + if (days >= 1) { + s += days.toString() + "d " + } + if (hours >= 1) { + s += hours.toString() + "h " + } + if (minutes >= 1) { + s += minutes.toString() + "m " + } + if (seconds >= 1 && hours < 1) { + s += seconds.toString() + "s" + } + if (s.isEmpty() || s.isBlank()) { + s = "None" + } + return s + } + + fun getTimeFormatedRaw(time2: Long): String { + var time = time2 + val days: Long = TimeUnit.MILLISECONDS + .toDays(time) + time -= TimeUnit.DAYS.toMillis(days) + val hours: Long = TimeUnit.MILLISECONDS + .toHours(time) + time -= TimeUnit.HOURS.toMillis(hours) + val minutes: Long = TimeUnit.MILLISECONDS + .toMinutes(time) + time -= TimeUnit.MINUTES.toMillis(minutes) + val seconds: Long = TimeUnit.MILLISECONDS + .toSeconds(time) + var s = "" + if (days >= 1) { + s += days.toString() + "d " + } + if (hours >= 1) { + s += hours.toString() + "h " + } + if (minutes >= 1) { + s += minutes.toString() + "m " + } + if (seconds >= 1) { + s += seconds.toString() + "s" + } + if (s.isEmpty() || s.isBlank()) { + s = "None" + } + return s + } + + + //This 100000%ly can be improved, I wrote this at 2am + fun getTimeUnformated(timeStr: String): Long { + var days: Long = 0 + var hours: Long = 0 + var minutes: Long = 0 + var seconds: Long = 0 + val timeArr = timeStr.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + for (s in timeArr) { + if (s.endsWith("d")) { + days = s.split("d".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0].toLong() + } else if (s.endsWith("h")) { + hours = s.split("h".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0].toLong() + } else if (s.endsWith("m")) { + minutes = s.split("m".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0].toLong() + } else if (s.endsWith("s")) { + seconds = s.split("s".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0].toLong() + } + } + return Duration.ofSeconds(seconds).plus(Duration.ofMinutes(minutes)).plus(Duration.ofHours(hours)) + .plus(Duration.ofDays(days)).toMillis() + } + + private val DaysUntilMonday: Map = object : HashMap() { + init { + put("MONDAY", 0) + put("TUESDAY", 6) + put("WEDNESDAY", 5) + put("THURSDAY", 4) + put("FRIDAY", 3) + put("SATURDAY", 2) + put("SUNDAY", 1) + } + } + + fun getDelay(day: String): Int { + return DaysUntilMonday[day]!! + } +} diff --git a/src/main/templates/net/moonleay/lilJudd/build/BuildConstants.kt b/src/main/templates/net/moonleay/lilJudd/build/BuildConstants.kt index 0d9f6cd..fdcf269 100644 --- a/src/main/templates/net/moonleay/lilJudd/build/BuildConstants.kt +++ b/src/main/templates/net/moonleay/lilJudd/build/BuildConstants.kt @@ -1,6 +1,6 @@ /* * lilJudd - * Copyright (C) 2023 limited_dev + * Copyright (C) 2023 moonleay * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,4 +26,5 @@ internal object BuildConstants { const val ktorVersion = "${ktorversion}" const val exposedVersion = "${exposedversion}" const val postgresVersion = "${postgresversion}" + const val krontabVersion = "${krontabversion}" }