Merge branch 'feature/ping_system' into 'master'

fix: removed timeplanner args

See merge request moonleay/liljudd!2
This commit is contained in:
moonleay 2023-06-15 06:12:49 +00:00
commit 7295b39e92
10 changed files with 153 additions and 85 deletions

3
.dockerignore Normal file
View file

@ -0,0 +1,3 @@
**.nils
/run
/run/

View file

@ -25,9 +25,10 @@ A Discord Bot for Splatoon Teams
- Features - Features
- Time Planner -- Make the bot send messages and reactions into a selected channel in order to make planning easier - 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) - 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) - Game Tracker (Save the results of the last matches)
- Replay Saver (Maybe; will save the replay code to a database) - Replay Saver (Maybe; will save the replay code to a database)

View file

@ -32,7 +32,7 @@ val ownerID = 372703841151614976L
group = "net.moonleay.liljudd" group = "net.moonleay.liljudd"
version = System.getenv("CI_COMMIT_TAG")?.let { "$it-${System.getenv("CI_COMMIT_SHORT_SHA")}-prod" } version = System.getenv("CI_COMMIT_TAG")?.let { "$it-${System.getenv("CI_COMMIT_SHORT_SHA")}-prod" }
?: System.getenv("CI_COMMIT_SHORT_SHA")?.let { "$it-dev" } ?: System.getenv("CI_COMMIT_SHORT_SHA")?.let { "$it-dev" }
?: "2.2.0" ?: "2.2.1"
val kordver = "1.5.6" val kordver = "1.5.6"
val coroutinesver = "1.1.0" val coroutinesver = "1.1.0"
@ -88,8 +88,6 @@ dependencies {
shadow("org.slf4j:slf4j-simple:2.0.3") shadow("org.slf4j:slf4j-simple:2.0.3")
//Database //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-core:$exposedver")
shadow("org.jetbrains.exposed:exposed-dao:$exposedver") shadow("org.jetbrains.exposed:exposed-dao:$exposedver")
shadow("org.jetbrains.exposed:exposed-jdbc:$exposedver") shadow("org.jetbrains.exposed:exposed-jdbc:$exposedver")
@ -97,6 +95,8 @@ dependencies {
//Korntab //Korntab
shadow("dev.inmo:krontab:$krontabver") 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")
} }

View file

@ -33,7 +33,7 @@ import net.moonleay.lilJudd.data.DB
import net.moonleay.lilJudd.extensions.FeatureManageExtension import net.moonleay.lilJudd.extensions.FeatureManageExtension
import net.moonleay.lilJudd.extensions.SendPlannerExtension import net.moonleay.lilJudd.extensions.SendPlannerExtension
import net.moonleay.lilJudd.extensions.VersionExtension 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.Logger
import net.moonleay.lilJudd.util.MessageUtil import net.moonleay.lilJudd.util.MessageUtil
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -69,7 +69,7 @@ object Bot {
// Register the TimePlanner thread // Register the TimePlanner thread
val coroutineJob = GlobalScope.launch { val coroutineJob = GlobalScope.launch {
TimePlanner.registerThread() TimeManager.registerThread()
} }
// Add a shutdown hook to cancel the coroutine when the application is terminated // Add a shutdown hook to cancel the coroutine when the application is terminated

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
package net.moonleay.lilJudd.data.entry
data class PlanningNotifierRolesData(
val serverID: String,
val channelId: String,
val hastimeroleid: String,
val wantstobenotifid: String
)

View file

@ -20,10 +20,9 @@ package net.moonleay.lilJudd.data.tables
import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.dao.id.IntIdTable
object PlanningNotifyerRoles : IntIdTable() { object PlanningNotifierRoles : IntIdTable() {
var serverid = varchar("serverid", 50) var serverid = varchar("serverid", 50)
var channelid = varchar("channelid", 50) var channelid = varchar("channelid", 50)
var hastimeroleid = varchar("hastimeroleid", 50) var hastimeroleid = varchar("hastimeroleid", 50)
var hastovoteid = varchar("hastovoteid", 50)
var wantstobenotifid = varchar("wantstobenotifid", 50) var wantstobenotifid = varchar("wantstobenotifid", 50)
} }

View file

@ -29,7 +29,7 @@ import dev.kord.common.Color
import dev.kord.common.entity.Permission import dev.kord.common.entity.Permission
import dev.kord.common.entity.Snowflake import dev.kord.common.entity.Snowflake
import dev.kord.core.behavior.createRole 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.data.tables.TimePlanningChannels
import net.moonleay.lilJudd.extensions.component.EnableOrDisable import net.moonleay.lilJudd.extensions.component.EnableOrDisable
import net.moonleay.lilJudd.extensions.component.FeatureEnum 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 * 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() { override suspend fun setup() {
publicSlashCommand(::FeatureManagerArgs) { publicSlashCommand(::FeatureManagerArgs) {
@ -62,7 +62,7 @@ class FeatureManageExtension : Extension() {
MessageUtil.getEmbed( MessageUtil.getEmbed(
Color(0xE0311A), Color(0xE0311A),
"403: Forbidden", "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 u.asUser().username + "#" + u.asUser().discriminator
) )
) )
@ -118,37 +118,31 @@ class FeatureManageExtension : Extension() {
FeatureEnum.PLANNINGNOTIFIER -> { FeatureEnum.PLANNINGNOTIFIER -> {
var alreadyExists = false var alreadyExists = false
transaction { transaction {
alreadyExists = PlanningNotifyerRoles.select { alreadyExists = PlanningNotifierRoles.select {
(PlanningNotifyerRoles.serverid eq gID) and (PlanningNotifyerRoles.channelid eq cID) (PlanningNotifierRoles.serverid eq gID) and (PlanningNotifierRoles.channelid eq cID)
}.count() > 0 }.count() > 0
} }
if (!alreadyExists) { 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 { val hasTimeRole = this.guild!!.createRole {
this.name = "Has time [${channel.data.name.value}]" this.name = "available [${channel.data.name.value}]"
this.mentionable = true this.mentionable = true
} }
val htr = hasTimeRole.id.toString() val htr = hasTimeRole.id.toString()
val wantsNotifsRole = this.guild!!.createRole { 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 this.mentionable = true
} }
val wnr = wantsNotifsRole.id.toString() val wnr = wantsNotifsRole.id.toString()
transaction { transaction {
PlanningNotifyerRoles.insert { PlanningNotifierRoles.insert {
it[PlanningNotifyerRoles.serverid] = gID it[PlanningNotifierRoles.serverid] = gID
it[PlanningNotifyerRoles.channelid] = cID it[PlanningNotifierRoles.channelid] = cID
it[PlanningNotifyerRoles.hastovoteid] = htvr it[PlanningNotifierRoles.hastimeroleid] = htr
it[PlanningNotifyerRoles.hastimeroleid] = htr it[PlanningNotifierRoles.wantstobenotifid] = wnr
it[PlanningNotifyerRoles.wantstobenotifid] = wnr } get PlanningNotifierRoles.id
} get PlanningNotifyerRoles.id
} }
this.respond { this.respond {
@ -156,7 +150,7 @@ class FeatureManageExtension : Extension() {
MessageUtil.getEmbed( MessageUtil.getEmbed(
Color(0x52E01A), Color(0x52E01A),
"200: Success", "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 u.asUser().username + "#" + u.asUser().discriminator
) )
) )
@ -225,28 +219,27 @@ class FeatureManageExtension : Extension() {
FeatureEnum.PLANNINGNOTIFIER -> { FeatureEnum.PLANNINGNOTIFIER -> {
var alreadyExists = false var alreadyExists = false
transaction { transaction {
alreadyExists = PlanningNotifyerRoles.select { alreadyExists = PlanningNotifierRoles.select {
(PlanningNotifyerRoles.serverid eq gID) and (PlanningNotifyerRoles.channelid eq cID) (PlanningNotifierRoles.serverid eq gID) and (PlanningNotifierRoles.channelid eq cID)
}.count() > 0 }.count() > 0
} }
if (alreadyExists) { if (alreadyExists) {
var matchingEntries: List<ResultRow> = mutableListOf() var matchingEntries: List<ResultRow> = mutableListOf()
transaction { transaction {
matchingEntries = PlanningNotifyerRoles.select { matchingEntries = PlanningNotifierRoles.select {
(PlanningNotifyerRoles.serverid eq gID) and (PlanningNotifierRoles.serverid eq gID) and
(PlanningNotifyerRoles.channelid eq cID) (PlanningNotifierRoles.channelid eq cID)
}.toList() }.toList()
} }
for (e in matchingEntries) { for (e in matchingEntries) {
this.guild!!.getRoleOrNull(Snowflake(e[PlanningNotifyerRoles.hastovoteid]))?.delete() this.guild!!.getRoleOrNull(Snowflake(e[PlanningNotifierRoles.hastimeroleid]))?.delete()
this.guild!!.getRoleOrNull(Snowflake(e[PlanningNotifyerRoles.hastimeroleid]))?.delete() this.guild!!.getRoleOrNull(Snowflake(e[PlanningNotifierRoles.wantstobenotifid]))
this.guild!!.getRoleOrNull(Snowflake(e[PlanningNotifyerRoles.wantstobenotifid]))
?.delete() ?.delete()
} }
transaction { transaction {
matchingEntries.forEach { entry -> matchingEntries.forEach { entry ->
PlanningNotifyerRoles.deleteWhere { id eq entry[id] } PlanningNotifierRoles.deleteWhere { id eq entry[id] }
} }
} }
this.respond { this.respond {

View file

@ -18,8 +18,6 @@
package net.moonleay.lilJudd.extensions 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.Extension
import com.kotlindiscord.kord.extensions.extensions.publicSlashCommand import com.kotlindiscord.kord.extensions.extensions.publicSlashCommand
import com.kotlindiscord.kord.extensions.types.respond import com.kotlindiscord.kord.extensions.types.respond
@ -44,11 +42,11 @@ class SendPlannerExtension : Extension() {
get() = false get() = false
override suspend fun setup() { override suspend fun setup() {
publicSlashCommand(::PlannerArgs) { publicSlashCommand() {
name = "sendplanner" name = "sendplanner"
description = "Send the planner for the current and x next weeks" description = "Send the planner for the current and x next weeks"
this.action { this.action {
if (this.arguments.weeks == 0 || !this.member!!.asMember(this.guild!!.id) if (!this.member!!.asMember(this.guild!!.id)
.hasPermission(Permission.Administrator) .hasPermission(Permission.Administrator)
) { ) {
val res = this.respond { val res = this.respond {
@ -65,42 +63,39 @@ class SendPlannerExtension : Extension() {
var then = var then =
ZonedDateTime.now(ZoneId.of("Europe/Berlin")).withDayOfMonth(getMondayDayOfMonth()).withHour(4) ZonedDateTime.now(ZoneId.of("Europe/Berlin")).withDayOfMonth(getMondayDayOfMonth()).withHour(4)
.withMinute(0).withSecond(0) .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 { c.createMessage {
this.embeds.add( this.embeds.add(
MessageUtil.getEmbed( MessageUtil.getEmbedWithTable(
Color(0X4C4645), Color(0X4C4645),
"Time Planning Feature", "",
"Do you have time on the following Days?", "${then.dayOfWeek.name}, ${then.dayOfMonth}.${then.monthValue}.${then.year} /${it + 1}. weekday",
"Automated Message" mapOf(
) "Is available" to listOf(),
) "May be available" to listOf(),
} "Is not available" to listOf()
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()
)
) )
) )
)
this.actionRow { this.actionRow {
this.components.addAll(ButtonUtil.getTimePlannerButtons().components) 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}") Logger.out("Finished with ${c.data.guildId.value}")
} }
@ -112,12 +107,4 @@ class SendPlannerExtension : Extension() {
val monday = now.with(DayOfWeek.MONDAY) val monday = now.with(DayOfWeek.MONDAY)
return monday.dayOfMonth 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
}
}
} }

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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 ")
}
}
}

View file

@ -27,6 +27,8 @@ import dev.kord.core.entity.channel.MessageChannel
import dev.kord.rest.builder.message.create.actionRow import dev.kord.rest.builder.message.create.actionRow
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import net.moonleay.lilJudd.Bot 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.data.tables.TimePlanningChannels
import net.moonleay.lilJudd.util.ButtonUtil import net.moonleay.lilJudd.util.ButtonUtil
import net.moonleay.lilJudd.util.Logger import net.moonleay.lilJudd.util.Logger
@ -37,7 +39,7 @@ import java.time.ZoneId
import java.time.ZonedDateTime import java.time.ZonedDateTime
object TimePlanner { object TimeManager {
/* /--------------- Seconds /* /--------------- Seconds
| /------------- Minutes | /------------- Minutes
| | /----------- Hours | | /----------- Hours
@ -49,23 +51,43 @@ object TimePlanner {
* * * * * * 0o *w*/ * * * * * * 0o *w*/
suspend fun registerThread() { suspend fun registerThread() {
Logger.out("Adding scheduler...") Logger.out("Adding message scheduler...")
val scheduler = buildSchedule("0 0 4 * * * 0o 1w") // 0 0 4 * * * 0o 1w // 0o is UTC val scheduler = buildSchedule("0 00 23 * * * 0o *") // 0 0 4 * * * 0o 1w // 0o is UTC
scheduler.doInfinity { scheduler.doInfinity {
Logger.out("Starting to notify...") Logger.out("Starting to notify...")
val channelList = mutableListOf<Snowflake>()
// ChannelID, ServerID
val channelList = mutableMapOf<Snowflake, Snowflake>()
// ChannelID, Data
val roleList = mutableMapOf<Snowflake, PlanningNotifierRolesData>()
transaction { transaction {
for (tp in TimePlanningChannels.selectAll()) { 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]}.") 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!") Logger.out("${channelList.count()} Channels to notify with ${roleList.count()} Roles to ping!")
for (ch in channelList) { for (ch in channelList.keys) {
if (Bot.bot.kordRef.getChannel(ch) == null) if (Bot.bot.kordRef.getChannel(ch) == null)
continue // TODO: Check if the channel is valid in another shard continue // TODO: Check if the channel is valid in another shard
val c = Bot.bot.kordRef.getChannelOf<MessageChannel>(ch)!! val c = Bot.bot.kordRef.getChannelOf<MessageChannel>(ch)!!
c.createMessage { c.createMessage {
if (roleList[ch] != null) {
this.content = "<@&${Snowflake(roleList[ch]?.wantstobenotifid!!)}>"
}
this.embeds.add( this.embeds.add(
MessageUtil.getEmbed( MessageUtil.getEmbed(
Color(0X4C4645), 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) var then = ZonedDateTime.now(ZoneId.of("Europe/Berlin")).withHour(4).withMinute(0).withSecond(0)
repeat(7) { repeat(7) {
c.createMessage { c.createMessage {
@ -98,6 +120,7 @@ object TimePlanner {
} }
then = then.plusDays(1).withHour(4).withMinute(0).withSecond(0) then = then.plusDays(1).withHour(4).withMinute(0).withSecond(0)
Logger.out("Finished sending day $it") Logger.out("Finished sending day $it")
delay(1000) delay(1000)
} }
Logger.out("Finished with ${c.data.guildId.value}") Logger.out("Finished with ${c.data.guildId.value}")