diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0b6a882 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +**.nils +/run +/run/ diff --git a/README.md b/README.md index 2b9d0bc..4e4c0f6 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,10 @@ A Discord Bot for Splatoon Teams - Features - Time Planner -- Make the bot send messages and reactions into a selected channel in order to make planning easier -## Maybe upcoming features +## (Maybe) upcoming features - Match Planner (Send Notifications some time before a match starts) +- Planning Notifier (Make it possible to ping people, who have time at a specific time) - Game Tracker (Save the results of the last matches) - Replay Saver (Maybe; will save the replay code to a database) diff --git a/build.gradle.kts b/build.gradle.kts index 53e1b78..4d4b3c5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,7 +32,7 @@ val ownerID = 372703841151614976L group = "net.moonleay.liljudd" version = System.getenv("CI_COMMIT_TAG")?.let { "$it-${System.getenv("CI_COMMIT_SHORT_SHA")}-prod" } ?: System.getenv("CI_COMMIT_SHORT_SHA")?.let { "$it-dev" } - ?: "2.2.0" + ?: "2.2.1" val kordver = "1.5.6" val coroutinesver = "1.1.0" @@ -88,8 +88,6 @@ dependencies { shadow("org.slf4j:slf4j-simple:2.0.3") //Database - shadow("io.ktor:ktor-client-core:$ktor_version") - shadow("io.ktor:ktor-client-cio:$ktor_version") shadow("org.jetbrains.exposed:exposed-core:$exposedver") shadow("org.jetbrains.exposed:exposed-dao:$exposedver") shadow("org.jetbrains.exposed:exposed-jdbc:$exposedver") @@ -97,6 +95,8 @@ dependencies { //Korntab shadow("dev.inmo:krontab:$krontabver") + "shadow"("io.ktor:ktor-client-core-jvm:2.3.1") + "shadow"("io.ktor:ktor-client-cio-jvm:2.3.1") } diff --git a/src/main/kotlin/net/moonleay/lilJudd/Bot.kt b/src/main/kotlin/net/moonleay/lilJudd/Bot.kt index 413e0bf..66208c7 100644 --- a/src/main/kotlin/net/moonleay/lilJudd/Bot.kt +++ b/src/main/kotlin/net/moonleay/lilJudd/Bot.kt @@ -33,7 +33,7 @@ import net.moonleay.lilJudd.data.DB import net.moonleay.lilJudd.extensions.FeatureManageExtension import net.moonleay.lilJudd.extensions.SendPlannerExtension import net.moonleay.lilJudd.extensions.VersionExtension -import net.moonleay.lilJudd.features.TimePlanner +import net.moonleay.lilJudd.features.TimeManager import net.moonleay.lilJudd.util.Logger import net.moonleay.lilJudd.util.MessageUtil import kotlin.system.exitProcess @@ -69,7 +69,7 @@ object Bot { // Register the TimePlanner thread val coroutineJob = GlobalScope.launch { - TimePlanner.registerThread() + TimeManager.registerThread() } // Add a shutdown hook to cancel the coroutine when the application is terminated diff --git a/src/main/kotlin/net/moonleay/lilJudd/data/entry/PlanningNotifierRolesData.kt b/src/main/kotlin/net/moonleay/lilJudd/data/entry/PlanningNotifierRolesData.kt new file mode 100644 index 0000000..0798ccc --- /dev/null +++ b/src/main/kotlin/net/moonleay/lilJudd/data/entry/PlanningNotifierRolesData.kt @@ -0,0 +1,26 @@ +/* + * 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.data.entry + +data class PlanningNotifierRolesData( + val serverID: String, + val channelId: String, + val hastimeroleid: String, + val wantstobenotifid: String +) diff --git a/src/main/kotlin/net/moonleay/lilJudd/data/tables/PlanningNotifyerRoles.kt b/src/main/kotlin/net/moonleay/lilJudd/data/tables/PlanningNotifierRoles.kt similarity index 91% rename from src/main/kotlin/net/moonleay/lilJudd/data/tables/PlanningNotifyerRoles.kt rename to src/main/kotlin/net/moonleay/lilJudd/data/tables/PlanningNotifierRoles.kt index 069bcc0..65e8b0e 100644 --- a/src/main/kotlin/net/moonleay/lilJudd/data/tables/PlanningNotifyerRoles.kt +++ b/src/main/kotlin/net/moonleay/lilJudd/data/tables/PlanningNotifierRoles.kt @@ -20,10 +20,9 @@ package net.moonleay.lilJudd.data.tables import org.jetbrains.exposed.dao.id.IntIdTable -object PlanningNotifyerRoles : IntIdTable() { +object PlanningNotifierRoles : IntIdTable() { var serverid = varchar("serverid", 50) var channelid = varchar("channelid", 50) var hastimeroleid = varchar("hastimeroleid", 50) - var hastovoteid = varchar("hastovoteid", 50) var wantstobenotifid = varchar("wantstobenotifid", 50) } diff --git a/src/main/kotlin/net/moonleay/lilJudd/extensions/FeatureManageExtension.kt b/src/main/kotlin/net/moonleay/lilJudd/extensions/FeatureManageExtension.kt index 373fcc7..7384c56 100644 --- a/src/main/kotlin/net/moonleay/lilJudd/extensions/FeatureManageExtension.kt +++ b/src/main/kotlin/net/moonleay/lilJudd/extensions/FeatureManageExtension.kt @@ -29,7 +29,7 @@ import dev.kord.common.Color import dev.kord.common.entity.Permission import dev.kord.common.entity.Snowflake import dev.kord.core.behavior.createRole -import net.moonleay.lilJudd.data.tables.PlanningNotifyerRoles +import net.moonleay.lilJudd.data.tables.PlanningNotifierRoles import net.moonleay.lilJudd.data.tables.TimePlanningChannels import net.moonleay.lilJudd.extensions.component.EnableOrDisable import net.moonleay.lilJudd.extensions.component.FeatureEnum @@ -47,7 +47,7 @@ class FeatureManageExtension : Extension() { /* * Note: This has to be rewritten at some point to better support more features - * and remove this mess of a class. + * and improve this mess of a class. * */ override suspend fun setup() { publicSlashCommand(::FeatureManagerArgs) { @@ -62,7 +62,7 @@ class FeatureManageExtension : Extension() { MessageUtil.getEmbed( Color(0xE0311A), "403: Forbidden", - "You cannot edit features, as you don't have the Administratior permission.", + "You cannot edit features, as you don't have the Administrator permission.", u.asUser().username + "#" + u.asUser().discriminator ) ) @@ -118,37 +118,31 @@ class FeatureManageExtension : Extension() { FeatureEnum.PLANNINGNOTIFIER -> { var alreadyExists = false transaction { - alreadyExists = PlanningNotifyerRoles.select { - (PlanningNotifyerRoles.serverid eq gID) and (PlanningNotifyerRoles.channelid eq cID) + alreadyExists = PlanningNotifierRoles.select { + (PlanningNotifierRoles.serverid eq gID) and (PlanningNotifierRoles.channelid eq cID) }.count() > 0 } if (!alreadyExists) { - val hasToVoteRole = this.guild!!.createRole { - this.name = "Hasn't voted [${channel.data.name.value}]" - this.mentionable = true - } - val htvr = hasToVoteRole.id.toString() val hasTimeRole = this.guild!!.createRole { - this.name = "Has time [${channel.data.name.value}]" + this.name = "available [${channel.data.name.value}]" this.mentionable = true } val htr = hasTimeRole.id.toString() val wantsNotifsRole = this.guild!!.createRole { - this.name = "Wants to be notified [${channel.data.name.value}]" + this.name = "notifications [${channel.data.name.value}]" this.mentionable = true } val wnr = wantsNotifsRole.id.toString() transaction { - PlanningNotifyerRoles.insert { - it[PlanningNotifyerRoles.serverid] = gID - it[PlanningNotifyerRoles.channelid] = cID - it[PlanningNotifyerRoles.hastovoteid] = htvr - it[PlanningNotifyerRoles.hastimeroleid] = htr - it[PlanningNotifyerRoles.wantstobenotifid] = wnr - } get PlanningNotifyerRoles.id + PlanningNotifierRoles.insert { + it[PlanningNotifierRoles.serverid] = gID + it[PlanningNotifierRoles.channelid] = cID + it[PlanningNotifierRoles.hastimeroleid] = htr + it[PlanningNotifierRoles.wantstobenotifid] = wnr + } get PlanningNotifierRoles.id } this.respond { @@ -156,7 +150,7 @@ class FeatureManageExtension : Extension() { MessageUtil.getEmbed( Color(0x52E01A), "200: Success", - "The feature was enabled in channel ${args.channel.data.name.value} with roles ${hasToVoteRole.mention}, ${hasTimeRole.mention} & ${wantsNotifsRole.mention}.", + "The feature was enabled in channel ${args.channel.data.name.value} with roles ${hasTimeRole.mention} & ${wantsNotifsRole.mention}.", u.asUser().username + "#" + u.asUser().discriminator ) ) @@ -225,28 +219,27 @@ class FeatureManageExtension : Extension() { FeatureEnum.PLANNINGNOTIFIER -> { var alreadyExists = false transaction { - alreadyExists = PlanningNotifyerRoles.select { - (PlanningNotifyerRoles.serverid eq gID) and (PlanningNotifyerRoles.channelid eq cID) + alreadyExists = PlanningNotifierRoles.select { + (PlanningNotifierRoles.serverid eq gID) and (PlanningNotifierRoles.channelid eq cID) }.count() > 0 } if (alreadyExists) { var matchingEntries: List = mutableListOf() transaction { - matchingEntries = PlanningNotifyerRoles.select { - (PlanningNotifyerRoles.serverid eq gID) and - (PlanningNotifyerRoles.channelid eq cID) + matchingEntries = PlanningNotifierRoles.select { + (PlanningNotifierRoles.serverid eq gID) and + (PlanningNotifierRoles.channelid eq cID) }.toList() } for (e in matchingEntries) { - this.guild!!.getRoleOrNull(Snowflake(e[PlanningNotifyerRoles.hastovoteid]))?.delete() - this.guild!!.getRoleOrNull(Snowflake(e[PlanningNotifyerRoles.hastimeroleid]))?.delete() - this.guild!!.getRoleOrNull(Snowflake(e[PlanningNotifyerRoles.wantstobenotifid])) + this.guild!!.getRoleOrNull(Snowflake(e[PlanningNotifierRoles.hastimeroleid]))?.delete() + this.guild!!.getRoleOrNull(Snowflake(e[PlanningNotifierRoles.wantstobenotifid])) ?.delete() } transaction { matchingEntries.forEach { entry -> - PlanningNotifyerRoles.deleteWhere { id eq entry[id] } + PlanningNotifierRoles.deleteWhere { id eq entry[id] } } } this.respond { diff --git a/src/main/kotlin/net/moonleay/lilJudd/extensions/SendPlannerExtension.kt b/src/main/kotlin/net/moonleay/lilJudd/extensions/SendPlannerExtension.kt index 4d3defb..2482bbf 100644 --- a/src/main/kotlin/net/moonleay/lilJudd/extensions/SendPlannerExtension.kt +++ b/src/main/kotlin/net/moonleay/lilJudd/extensions/SendPlannerExtension.kt @@ -18,8 +18,6 @@ package net.moonleay.lilJudd.extensions -import com.kotlindiscord.kord.extensions.commands.Arguments -import com.kotlindiscord.kord.extensions.commands.converters.impl.int import com.kotlindiscord.kord.extensions.extensions.Extension import com.kotlindiscord.kord.extensions.extensions.publicSlashCommand import com.kotlindiscord.kord.extensions.types.respond @@ -44,11 +42,11 @@ class SendPlannerExtension : Extension() { get() = false override suspend fun setup() { - publicSlashCommand(::PlannerArgs) { + publicSlashCommand() { name = "sendplanner" description = "Send the planner for the current and x next weeks" this.action { - if (this.arguments.weeks == 0 || !this.member!!.asMember(this.guild!!.id) + if (!this.member!!.asMember(this.guild!!.id) .hasPermission(Permission.Administrator) ) { val res = this.respond { @@ -65,42 +63,39 @@ class SendPlannerExtension : Extension() { var then = ZonedDateTime.now(ZoneId.of("Europe/Berlin")).withDayOfMonth(getMondayDayOfMonth()).withHour(4) .withMinute(0).withSecond(0) - repeat(this.arguments.weeks) { + c.createMessage { + this.embeds.add( + MessageUtil.getEmbed( + Color(0X4C4645), + "Time Planning Feature", + "Do you have time on the following Days?", + "Automated Message" + ) + ) + } + delay(1000) + repeat(7) { c.createMessage { this.embeds.add( - MessageUtil.getEmbed( + MessageUtil.getEmbedWithTable( Color(0X4C4645), - "Time Planning Feature", - "Do you have time on the following Days?", - "Automated Message" - ) - ) - } - delay(1000) - repeat(7) { - c.createMessage { - this.embeds.add( - MessageUtil.getEmbedWithTable( - Color(0X4C4645), - "", - "${then.dayOfWeek.name}, ${then.dayOfMonth}.${then.monthValue}.${then.year} /${it + 1}. weekday", - mapOf( - "Is available" to listOf(), - "May be available" to listOf(), - "Is not available" to listOf() - ) + "", + "${then.dayOfWeek.name}, ${then.dayOfMonth}.${then.monthValue}.${then.year} /${it + 1}. weekday", + mapOf( + "Is available" to listOf(), + "May be available" to listOf(), + "Is not available" to listOf() ) ) + ) - this.actionRow { - this.components.addAll(ButtonUtil.getTimePlannerButtons().components) - } + this.actionRow { + this.components.addAll(ButtonUtil.getTimePlannerButtons().components) } - then = then.plusDays(1).withHour(4).withMinute(0).withSecond(0) - Logger.out("Finished sending day $it") - delay(1000) } - Logger.out("Finished week $it") + then = then.plusDays(1).withHour(4).withMinute(0).withSecond(0) + Logger.out("Finished sending day $it") + delay(1000) } Logger.out("Finished with ${c.data.guildId.value}") } @@ -112,12 +107,4 @@ class SendPlannerExtension : Extension() { val monday = now.with(DayOfWeek.MONDAY) return monday.dayOfMonth } - - inner class PlannerArgs : Arguments() { - val weeks by int { - name = "weeks" - description = "Amount of weeks to send. Needs to be at least 1." - minValue = 1 - } - } } diff --git a/src/main/kotlin/net/moonleay/lilJudd/features/PlanningNotifier.kt b/src/main/kotlin/net/moonleay/lilJudd/features/PlanningNotifier.kt new file mode 100644 index 0000000..3aab7e5 --- /dev/null +++ b/src/main/kotlin/net/moonleay/lilJudd/features/PlanningNotifier.kt @@ -0,0 +1,36 @@ +/* + * 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 dev.inmo.krontab.buildSchedule +import dev.inmo.krontab.doInfinity +import net.moonleay.lilJudd.util.Logger + +object PlanningNotifier { + suspend fun registerThread() { + Logger.out("Adding ping scheduler...") + val scheduler = buildSchedule("0 00 23 * * * 0o *") // 0 0 4 * * * 0o 1w // 0o is UTC + scheduler.doInfinity { + Logger.out("Starting to update roles...") + + Logger.out("Done! Until tomorrow! <3 ") + } + + } +} diff --git a/src/main/kotlin/net/moonleay/lilJudd/features/TimePlanner.kt b/src/main/kotlin/net/moonleay/lilJudd/features/TimeManager.kt similarity index 73% rename from src/main/kotlin/net/moonleay/lilJudd/features/TimePlanner.kt rename to src/main/kotlin/net/moonleay/lilJudd/features/TimeManager.kt index 778673a..0da325b 100644 --- a/src/main/kotlin/net/moonleay/lilJudd/features/TimePlanner.kt +++ b/src/main/kotlin/net/moonleay/lilJudd/features/TimeManager.kt @@ -27,6 +27,8 @@ import dev.kord.core.entity.channel.MessageChannel import dev.kord.rest.builder.message.create.actionRow import kotlinx.coroutines.delay import net.moonleay.lilJudd.Bot +import net.moonleay.lilJudd.data.entry.PlanningNotifierRolesData +import net.moonleay.lilJudd.data.tables.PlanningNotifierRoles import net.moonleay.lilJudd.data.tables.TimePlanningChannels import net.moonleay.lilJudd.util.ButtonUtil import net.moonleay.lilJudd.util.Logger @@ -37,7 +39,7 @@ import java.time.ZoneId import java.time.ZonedDateTime -object TimePlanner { +object TimeManager { /* /--------------- Seconds | /------------- Minutes | | /----------- Hours @@ -49,23 +51,43 @@ object TimePlanner { * * * * * * 0o *w*/ suspend fun registerThread() { - Logger.out("Adding scheduler...") - val scheduler = buildSchedule("0 0 4 * * * 0o 1w") // 0 0 4 * * * 0o 1w // 0o is UTC + Logger.out("Adding message scheduler...") + val scheduler = buildSchedule("0 00 23 * * * 0o *") // 0 0 4 * * * 0o 1w // 0o is UTC scheduler.doInfinity { Logger.out("Starting to notify...") - val channelList = mutableListOf() + + + // ChannelID, ServerID + val channelList = mutableMapOf() + // ChannelID, Data + val roleList = mutableMapOf() + transaction { for (tp in TimePlanningChannels.selectAll()) { - channelList.add(Snowflake(tp[TimePlanningChannels.channelid])) + channelList[Snowflake(tp[TimePlanningChannels.channelid])] = + Snowflake(tp[TimePlanningChannels.serverid]) Logger.out("Have to notify channel with ID ${tp[TimePlanningChannels.channelid]}.") } + + for (pnr in PlanningNotifierRoles.selectAll()) { + roleList[Snowflake(pnr[PlanningNotifierRoles.channelid])] = PlanningNotifierRolesData( + pnr[PlanningNotifierRoles.serverid], + pnr[PlanningNotifierRoles.channelid], + pnr[PlanningNotifierRoles.hastimeroleid], + pnr[PlanningNotifierRoles.wantstobenotifid] + ) + Logger.out("Have to ping roles: ${pnr[PlanningNotifierRoles.wantstobenotifid]}}") + } } - Logger.out("${channelList.count()} Channels to notify!") - for (ch in channelList) { + Logger.out("${channelList.count()} Channels to notify with ${roleList.count()} Roles to ping!") + for (ch in channelList.keys) { if (Bot.bot.kordRef.getChannel(ch) == null) continue // TODO: Check if the channel is valid in another shard val c = Bot.bot.kordRef.getChannelOf(ch)!! c.createMessage { + if (roleList[ch] != null) { + this.content = "<@&${Snowflake(roleList[ch]?.wantstobenotifid!!)}>" + } this.embeds.add( MessageUtil.getEmbed( Color(0X4C4645), @@ -75,7 +97,7 @@ object TimePlanner { ) ) } - delay(3000) + delay(2000) var then = ZonedDateTime.now(ZoneId.of("Europe/Berlin")).withHour(4).withMinute(0).withSecond(0) repeat(7) { c.createMessage { @@ -98,6 +120,7 @@ object TimePlanner { } then = then.plusDays(1).withHour(4).withMinute(0).withSecond(0) Logger.out("Finished sending day $it") + delay(1000) } Logger.out("Finished with ${c.data.guildId.value}")