Skip to content

Commit

Permalink
Refactored some old commands into new framework
Browse files Browse the repository at this point in the history
  • Loading branch information
blahblahbloopster committed Jan 5, 2024
1 parent 2c7a3f4 commit 7635ff3
Showing 1 changed file with 91 additions and 88 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.github.corruptedinc.corruptedmainframe.commands

import com.github.corruptedinc.corruptedmainframe.annotations.Command
import com.github.corruptedinc.corruptedmainframe.annotations.P
import com.github.corruptedinc.corruptedmainframe.commands.Commands.Companion.embed
import com.github.corruptedinc.corruptedmainframe.core.db.ExposedDatabase
import com.github.corruptedinc.corruptedmainframe.core.db.ExposedDatabase.Companion.m
Expand All @@ -16,14 +18,16 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
import net.dv8tion.jda.api.interactions.commands.build.Commands.slash
import net.dv8tion.jda.api.interactions.commands.build.Commands.user
import org.intellij.lang.annotations.Language
import org.jetbrains.exposed.sql.IColumnType
import org.jetbrains.exposed.sql.LongColumnType
import org.jetbrains.exposed.sql.and
import kotlin.math.*

@Suppress("MagicNumber")
class Leveling(private val bot: Bot) {
companion object {
const val POINTS_PER_MESSAGE = 2.0
private const val LEVEL_BAR_WIDTH = 24
const val LEVEL_BAR_WIDTH = 24

fun fightPoints(level: Double, zeroToOne: Double) = ((10 * (level + 2).pow(0.25) + 10) * zeroToOne) + 5

Expand Down Expand Up @@ -78,60 +82,102 @@ class Leveling(private val bot: Bot) {
}
}

fun registerCommands() {
bot.commands.register(
slash("level", "Gets your current level")
.addOption(OptionType.USER, "user", "The user to get the level of (optional)", false)
) { event ->
val user = event.getOption("user")?.asUser ?: event.user
val xp = bot.leveling.points(user, event.guild!!)
val level = bot.leveling.pointsToLevel(xp)
val levelStartXP = bot.leveling.levelToPoints(floor(level))
val levelEndXP = bot.leveling.levelToPoints(ceil(level))

val portion = (xp - levelStartXP) / (levelEndXP - levelStartXP)

val parts = " ▏▎▍▌▋▊▉█"
val blocks = LEVEL_BAR_WIDTH * portion
@Suppress("MagicNumber")
val out = (parts.last().toString().repeat(blocks.toInt()) +
parts[((blocks - blocks.toInt().toDouble()) * 8).toInt()]).padEnd(LEVEL_BAR_WIDTH, ' ')

val start = levelStartXP.toInt().coerceAtLeast(0).toString()
val end = levelEndXP.toInt().toString()

event.replyEmbeds(embed("Level ${level.toInt()}", description = "${user.asMention} has " +
"${xp.toInt()} points\nonly ${(levelEndXP - xp).roundToInt()} points to " +
"go until level ${level.toInt() + 1}!\n" +
"`" + start.padEnd(LEVEL_BAR_WIDTH + 2 - end.length, ' ') + end + "`\n" +
"`|$out|`",
@Command("level", "Gets your current level")
suspend inline fun CmdCtx.levelCommand(@P("user", "The user to get the level of") target: User?) {
val user = target ?: event.user
val xp = bot.leveling.points(user, event.guild!!)
val level = bot.leveling.pointsToLevel(xp)
val levelStartXP = bot.leveling.levelToPoints(floor(level))
val levelEndXP = bot.leveling.levelToPoints(ceil(level))

val portion = (xp - levelStartXP) / (levelEndXP - levelStartXP)

val parts = " ▏▎▍▌▋▊▉█"
val blocks = LEVEL_BAR_WIDTH * portion
@Suppress("MagicNumber")
val out = (parts.last().toString().repeat(blocks.toInt()) +
parts[((blocks - blocks.toInt().toDouble()) * 8).toInt()]).padEnd(LEVEL_BAR_WIDTH, ' ')

val start = levelStartXP.toInt().coerceAtLeast(0).toString()
val end = levelEndXP.toInt().toString()

event.replyEmbeds(
Commands.embed(
"Level ${level.toInt()}", description = "${user.asMention} has " +
"${xp.toInt()} points\nonly ${(levelEndXP - xp).roundToInt()} points to " +
"go until level ${level.toInt() + 1}!\n" +
"`" + start.padEnd(LEVEL_BAR_WIDTH + 2 - end.length, ' ') + end + "`\n" +
"`|$out|`",
thumbnail = user.effectiveAvatarUrl,
stripPings = false)).await()
}
stripPings = false
)
).await()
}

bot.commands.register(
slash("togglelevels", "Enable or disable level notifications for the whole server.")
.addOption(OptionType.BOOLEAN, "enabled", "If level up notifications should be shown.", true)
) { event ->
@Command("levelnotifs", "Enable or disable level notifications")
suspend inline fun CmdCtx.levelNotifs(@P("enabled", "If level notifications should be shown") enabled: Boolean, @P("guild", "If this is setting for the whole guild or just your user") guild: Boolean) {
if (guild) {
bot.commands.assertPermissions(event, Permission.ADMINISTRATOR)
val e = event.getOption("enabled")!!.asBoolean
bot.database.trnsctn {
event.guild!!.m.levelsEnabled = e
event.guild!!.m.levelsEnabled = enabled
}
event.replyEmbeds(embed("Successfully ${if (e) "enabled" else "disabled"} level notifications")).await()
event.replyEmbeds(Commands.embed("Successfully ${if (enabled) "enabled" else "disabled"} guild level notifications")).await()
} else {
bot.database.setPopups(event.user, event.guild ?: throw CommandException("This command must be run in a server!"), enabled)
event.replyEmbeds(Commands.embed("Set level popups to $enabled")).await()
}
}

bot.commands.register(slash("levelnotifs", "Toggle level notifications")
.addOption(OptionType.BOOLEAN, "enabled", "If you should be shown level notifications")) { event ->
val enabled = event.getOption("enabled")!!.asBoolean
bot.database.trnsctn {
event.user.m
val rankStatementPrepared = bot.database.trnsctn {
@Language("sql") val rankStatement = """
UPDATE points SET rank = RankTable.rank
FROM (SELECT id, DENSE_RANK() OVER(ORDER BY points DESC, id)
AS rank FROM points WHERE guild = ?) AS RankTable WHERE RankTable.id = points.id""".trimIndent()
connection.prepareStatement(rankStatement, false)
}

@Command("leaderboard", "Show the guild leaderboard")
suspend inline fun CmdCtx.leaderboard() {
// update rank column
bot.database.trnsctn {
val g = event.guild!!.m
rankStatementPrepared.fillParameters(listOf(LongColumnType() to g.id.value))
rankStatementPrepared.executeQuery()
}

val guildid = event.guild!!.idLong

val area = { start: Long, end: Long ->
val idsRanksPoints = bot.database.trnsctn {
val g = bot.database.guild(guildid).id
// TODO: add secondary sort column somehow
val items = ExposedDatabase.Point.find { (ExposedDatabase.Points.guild eq g) and (ExposedDatabase.Points.rank greaterEq start) and (ExposedDatabase.Points.rank less end) }.limit(30)
.sortedByDescending { it.points }

items.map { Triple(it.user.discordId, it.rank, it.points.roundToLong()) }
}
bot.database.setPopups(event.user, event.guild ?:
throw CommandException("This command must be run in a server!"), enabled)
event.replyEmbeds(embed("Set level popups to $enabled")).await()

val g = bot.jda.getGuildById(guildid)!!

val rankCol = Col(Align.RIGHT, *(arrayOf("Rank") + idsRanksPoints.map { it.second.toString() }))
val nameCol = Col(Align.LEFT, *(arrayOf("Name") + idsRanksPoints.map { g.getMemberById(it.first)?.effectiveName ?: "bug" }))
val pntsCol = Col(Align.LEFT, *(arrayOf("Points") + idsRanksPoints.map { it.third.toString() }))

Commands.embed(
"Leaderboard $start - $end",
description = "```" + table(rankCol, nameCol, pntsCol) + "```"
)
}

val perMessage = 15
val size = event.guild!!.memberCount / perMessage // todo: dejankify

event.replyLambdaPaginator(size.toLong()) { v ->
area(v * perMessage, (v + 1) * perMessage)
}.await()
}

fun registerCommands() {
bot.commands.registerUser(user("level")) { event ->
val user = event.targetMember!!.user
val xp = bot.leveling.points(user, event.guild!!)
Expand All @@ -158,48 +204,5 @@ class Leveling(private val bot: Bot) {
thumbnail = user.effectiveAvatarUrl,
stripPings = false)).ephemeral().await()
}

@Language("sql") val rankStatement = """
UPDATE points SET rank = RankTable.rank
FROM (SELECT id, DENSE_RANK() OVER(ORDER BY points DESC, id)
AS rank FROM points WHERE guild = ?) AS RankTable WHERE RankTable.id = points.id""".trimIndent()

bot.commands.register(slash("leaderboard", "Shows the server level leaderboard.")) { event ->
// update rank column
bot.database.trnsctn {
val g = event.guild!!.m
// yes, I'm aware this is awful, but because it must be an integer and isn't user provided it's safe
// if it was user provided I would have put more effort into a prepared statement
exec(rankStatement.replace("?", g.id.value.toString()))
}

val guildid = event.guild!!.idLong

fun area(start: Long, end: Long): MessageEmbed {
val idsRanksPoints = bot.database.trnsctn {
val g = bot.database.guild(guildid).id
// TODO: add secondary sort column somehow
val items = ExposedDatabase.Point.find { (ExposedDatabase.Points.guild eq g) and (ExposedDatabase.Points.rank greaterEq start) and (ExposedDatabase.Points.rank less end) }.limit(30)
.sortedByDescending { it.points }

items.map { Triple(it.user.discordId, it.rank, it.points.roundToLong()) }
}

val g = bot.jda.getGuildById(guildid)!!

val rankCol = Col(Align.RIGHT, *(arrayOf("Rank") + idsRanksPoints.map { it.second.toString() }))
val nameCol = Col(Align.LEFT, *(arrayOf("Name") + idsRanksPoints.map { g.getMemberById(it.first)?.effectiveName ?: "bug" }))
val pntsCol = Col(Align.LEFT, *(arrayOf("Points") + idsRanksPoints.map { it.third.toString() }))

return embed("Leaderboard $start - $end", description = "```" + table(rankCol, nameCol, pntsCol) + "```")
}

val perMessage = 15
val size = event.guild!!.memberCount / perMessage // todo: dejankify

event.replyLambdaPaginator(size.toLong()) { v ->
area(v * perMessage, (v + 1) * perMessage)
}.await()
}
}
}

0 comments on commit 7635ff3

Please sign in to comment.