Initial Commit
This commit is contained in:
17
src/main/kotlin/Main.kt
Normal file
17
src/main/kotlin/Main.kt
Normal file
@ -0,0 +1,17 @@
|
||||
package de.steamwar
|
||||
|
||||
import com.github.ajalt.clikt.core.main
|
||||
import com.github.ajalt.clikt.core.subcommands
|
||||
import de.steamwar.commands.SteamWar
|
||||
import de.steamwar.commands.database.DatabaseCommand
|
||||
import de.steamwar.commands.database.InfoCommand
|
||||
import de.steamwar.commands.database.ResetCommand
|
||||
import de.steamwar.commands.user.UserCommand
|
||||
import de.steamwar.commands.user.UsesrInfoCommand
|
||||
|
||||
fun main(args: Array<String>) = SteamWar()
|
||||
.subcommands(
|
||||
DatabaseCommand().subcommands(InfoCommand(), ResetCommand()),
|
||||
UserCommand().subcommands(UsesrInfoCommand())
|
||||
)
|
||||
.main(args)
|
||||
14
src/main/kotlin/commands/SteamWar.kt
Normal file
14
src/main/kotlin/commands/SteamWar.kt
Normal file
@ -0,0 +1,14 @@
|
||||
package de.steamwar.commands
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.core.findOrSetObject
|
||||
import com.github.ajalt.mordant.rendering.TextStyles
|
||||
import de.steamwar.db.Database
|
||||
|
||||
class SteamWar: CliktCommand(name = "sw") {
|
||||
val db by findOrSetObject { Database() }
|
||||
|
||||
override fun run() {
|
||||
echo("${TextStyles.bold("SteamWar-CLI")} (${db.database})")
|
||||
}
|
||||
}
|
||||
22
src/main/kotlin/commands/database/DatabaseCommand.kt
Normal file
22
src/main/kotlin/commands/database/DatabaseCommand.kt
Normal file
@ -0,0 +1,22 @@
|
||||
package de.steamwar.commands.database
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.core.CliktError
|
||||
import com.github.ajalt.clikt.core.Context
|
||||
import com.github.ajalt.clikt.core.requireObject
|
||||
import com.github.ajalt.clikt.parameters.options.flag
|
||||
import com.github.ajalt.clikt.parameters.options.option
|
||||
import de.steamwar.db.Database
|
||||
|
||||
class DatabaseCommand: CliktCommand(name = "db") {
|
||||
val useProduction by option().flag()
|
||||
val db by requireObject<Database>()
|
||||
|
||||
override fun help(context: Context): String = "Run database commands"
|
||||
|
||||
override fun run() {
|
||||
if (!useProduction && db.database == "production") {
|
||||
throw CliktError("You should not use the production database!")
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/main/kotlin/commands/database/InfoCommand.kt
Normal file
23
src/main/kotlin/commands/database/InfoCommand.kt
Normal file
@ -0,0 +1,23 @@
|
||||
package de.steamwar.commands.database
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.core.requireObject
|
||||
import com.github.ajalt.mordant.table.table
|
||||
import de.steamwar.db.Database
|
||||
|
||||
class InfoCommand: CliktCommand() {
|
||||
val db by requireObject<Database>()
|
||||
|
||||
override fun run() {
|
||||
val tables = db.execute("SHOW TABLES") { it.getString(1) }
|
||||
|
||||
echo(
|
||||
table {
|
||||
header { row("Name") }
|
||||
body {
|
||||
tables.map { row(it) }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
31
src/main/kotlin/commands/database/ResetCommand.kt
Normal file
31
src/main/kotlin/commands/database/ResetCommand.kt
Normal file
@ -0,0 +1,31 @@
|
||||
package de.steamwar.commands.database
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.core.CliktError
|
||||
import com.github.ajalt.clikt.core.requireObject
|
||||
import com.github.ajalt.mordant.rendering.TextColors
|
||||
import com.github.ajalt.mordant.rendering.TextStyles
|
||||
import de.steamwar.db.Database
|
||||
import java.io.File
|
||||
|
||||
class ResetCommand: CliktCommand() {
|
||||
val db by requireObject<Database>()
|
||||
|
||||
override fun run() {
|
||||
val schemaFile = File("/var/Schema.sql")
|
||||
if (!schemaFile.exists()) {
|
||||
throw CliktError("Schema file not found!")
|
||||
}
|
||||
|
||||
val schema = schemaFile.readText()
|
||||
|
||||
val tables = db.execute("SHOW TABLES;") { it.getString(1) }
|
||||
for (table in tables) {
|
||||
db.execute("DROP TABLE IF EXISTS $table;") { }
|
||||
}
|
||||
|
||||
db.execute(schema) { }
|
||||
|
||||
echo(TextColors.brightGreen(TextStyles.bold("Database reset!")))
|
||||
}
|
||||
}
|
||||
26
src/main/kotlin/commands/user/UserCommand.kt
Normal file
26
src/main/kotlin/commands/user/UserCommand.kt
Normal file
@ -0,0 +1,26 @@
|
||||
package de.steamwar.commands.user
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.core.CliktError
|
||||
import com.github.ajalt.clikt.core.findOrSetObject
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import de.steamwar.db.schema.SteamwarUser
|
||||
import de.steamwar.db.schema.SteamwarUserTable
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.core.or
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
|
||||
class UserCommand: CliktCommand("user") {
|
||||
val userId by argument()
|
||||
val user by findOrSetObject("user") {
|
||||
transaction {
|
||||
SteamwarUser.find { (SteamwarUserTable.id eq userId.toIntOrNull()) or (SteamwarUserTable.username eq userId) }
|
||||
.firstOrNull()
|
||||
?.let { return@transaction it } ?: throw CliktError("User not found!")
|
||||
}
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
user.id
|
||||
}
|
||||
}
|
||||
50
src/main/kotlin/commands/user/UsesrInfoCommand.kt
Normal file
50
src/main/kotlin/commands/user/UsesrInfoCommand.kt
Normal file
@ -0,0 +1,50 @@
|
||||
package de.steamwar.commands.user
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.core.requireObject
|
||||
import com.github.ajalt.mordant.table.table
|
||||
import de.steamwar.db.schema.Session
|
||||
import de.steamwar.db.schema.SteamwarUser
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
class UsesrInfoCommand: CliktCommand("info") {
|
||||
val user by requireObject<SteamwarUser>("user")
|
||||
|
||||
@OptIn(ExperimentalTime::class)
|
||||
override fun run() {
|
||||
transaction {
|
||||
val sessions = Session.selectAll().where { Session.user eq user.id.value }.map { it[Session.start] to it[Session.end] }
|
||||
|
||||
val totalPlayed = sessions.map { it.second - it.first }.sumOf { it.toDouble(DurationUnit.HOURS) }
|
||||
val firstJoin = sessions.minByOrNull { it.first }?.first
|
||||
val lastJoin = sessions.maxByOrNull { it.second }?.second
|
||||
|
||||
echo(
|
||||
table {
|
||||
body {
|
||||
row("Name", user.username)
|
||||
row("UUID", user.uuid)
|
||||
row("Team", user.team.name)
|
||||
row("Leader", user.leader)
|
||||
row("Locale", user.locale)
|
||||
row("Beigetreten am", firstJoin)
|
||||
row("Zuletzt gesehen am", lastJoin)
|
||||
row("Spielzeit", totalPlayed.toString() + "h")
|
||||
row("Punishments", table {
|
||||
header { row("Typ", "Ersteller", "Von", "Bis", "Grund") }
|
||||
body {
|
||||
user.punishments.map {
|
||||
row(it.type, it.punisher.username, it.starttime.toString(), if (it.perma) "Perma" else it.endtime.toString(), it.reason)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
83
src/main/kotlin/db/Database.kt
Normal file
83
src/main/kotlin/db/Database.kt
Normal file
@ -0,0 +1,83 @@
|
||||
package de.steamwar.db
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktError
|
||||
import org.jetbrains.exposed.v1.core.Expression
|
||||
import org.jetbrains.exposed.v1.core.ExpressionWithColumnType
|
||||
import org.jetbrains.exposed.v1.core.Function
|
||||
import org.jetbrains.exposed.v1.core.IColumnType
|
||||
import org.jetbrains.exposed.v1.core.LongColumnType
|
||||
import org.jetbrains.exposed.v1.core.QueryBuilder
|
||||
import org.jetbrains.exposed.v1.core.WindowFunction
|
||||
import org.jetbrains.exposed.v1.core.WindowFunctionDefinition
|
||||
import org.jetbrains.exposed.v1.core.append
|
||||
import org.jetbrains.exposed.v1.jdbc.Database
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import java.io.File
|
||||
import java.sql.ResultSet
|
||||
import java.util.Properties
|
||||
import kotlin.time.ExperimentalTime
|
||||
import kotlin.time.Instant
|
||||
|
||||
class Database {
|
||||
val host: String
|
||||
val port: String
|
||||
val database: String
|
||||
|
||||
init {
|
||||
val config = File(System.getProperty("user.home"), "mysql.properties")
|
||||
|
||||
if (!config.exists()) {
|
||||
throw CliktError("Config file not found!")
|
||||
}
|
||||
|
||||
val props = Properties();
|
||||
|
||||
props.load(config.inputStream())
|
||||
|
||||
host = props.getProperty("host")
|
||||
port = props.getProperty("port")
|
||||
database = props.getProperty("database")
|
||||
|
||||
val username = props.getProperty("user")
|
||||
val password = props.getProperty("password")
|
||||
|
||||
val url = "jdbc:mariadb://$host:$port/$database"
|
||||
|
||||
Database.Companion.connect(url, driver = "org.mariadb.jdbc.Driver", user = username, password = password)
|
||||
}
|
||||
|
||||
fun <T> execute(sql: String, transform: (ResultSet) -> T): List<T> = transaction {
|
||||
val result = mutableListOf<T>()
|
||||
exec(sql) { rs ->
|
||||
while (rs.next()) {
|
||||
result += transform(rs)
|
||||
}
|
||||
}
|
||||
return@transaction result
|
||||
}
|
||||
|
||||
fun <T> executeSingle(sql: String, transform: (ResultSet) -> T): T? {
|
||||
return execute(sql) { rs ->
|
||||
if (!rs.next()) {
|
||||
return@execute null
|
||||
}
|
||||
transform(rs)
|
||||
}.single()
|
||||
}
|
||||
}
|
||||
|
||||
class UnixTimestamp<T: Any, in S>(
|
||||
val expr: Expression<in S>,
|
||||
columnType: IColumnType<T>
|
||||
) : Function<T?>(columnType), WindowFunction<T?> {
|
||||
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
|
||||
append("UNIX_TIMESTAMP(", expr, ")")
|
||||
}
|
||||
|
||||
override fun over(): WindowFunctionDefinition<T?> {
|
||||
return WindowFunctionDefinition(columnType, this)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalTime::class)
|
||||
fun ExpressionWithColumnType<Instant>.unixTimestamp(): UnixTimestamp<Long, Instant> = UnixTimestamp(this, LongColumnType())
|
||||
32
src/main/kotlin/db/schema/Punishments.kt
Normal file
32
src/main/kotlin/db/schema/Punishments.kt
Normal file
@ -0,0 +1,32 @@
|
||||
package de.steamwar.db.schema
|
||||
|
||||
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.dao.IntEntity
|
||||
import org.jetbrains.exposed.v1.dao.IntEntityClass
|
||||
import org.jetbrains.exposed.v1.datetime.timestamp
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
@OptIn(ExperimentalTime::class)
|
||||
object Punishments: IntIdTable("Punishments", "PunishmentId") {
|
||||
val user = integer("userid").references(SteamwarUserTable.id)
|
||||
val punisher = integer("punisher").references(SteamwarUserTable.id)
|
||||
val type = varchar("type", 16)
|
||||
val reason = text("reason")
|
||||
val starttime = timestamp("starttime")
|
||||
val endtime = timestamp("endtime")
|
||||
val perma = bool("perma")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalTime::class)
|
||||
class Punishment(id: EntityID<Int>): IntEntity(id) {
|
||||
companion object : IntEntityClass<Punishment>(Punishments)
|
||||
|
||||
var user by SteamwarUser referencedOn Punishments.user
|
||||
var punisher by SteamwarUser referencedOn Punishments.punisher
|
||||
var type by Punishments.type
|
||||
var reason by Punishments.reason
|
||||
var starttime by Punishments.starttime
|
||||
var endtime by Punishments.endtime
|
||||
var perma by Punishments.perma
|
||||
}
|
||||
12
src/main/kotlin/db/schema/Session.kt
Normal file
12
src/main/kotlin/db/schema/Session.kt
Normal file
@ -0,0 +1,12 @@
|
||||
package de.steamwar.db.schema
|
||||
|
||||
import org.jetbrains.exposed.v1.core.Table
|
||||
import org.jetbrains.exposed.v1.datetime.timestamp
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
@OptIn(ExperimentalTime::class)
|
||||
object Session: Table("Session") {
|
||||
val user = integer("userid").references(SteamwarUserTable.id)
|
||||
val start = timestamp("starttime")
|
||||
val end = timestamp("endtime")
|
||||
}
|
||||
34
src/main/kotlin/db/schema/SteamwarUser.kt
Normal file
34
src/main/kotlin/db/schema/SteamwarUser.kt
Normal file
@ -0,0 +1,34 @@
|
||||
package de.steamwar.db.schema
|
||||
|
||||
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.dao.IntEntity
|
||||
import org.jetbrains.exposed.v1.dao.IntEntityClass
|
||||
|
||||
object SteamwarUserTable: IntIdTable("UserData") {
|
||||
val uuid = uuid("uuid")
|
||||
val username = varchar("username", 255)
|
||||
val team = integer("team").references(TeamTable.id)
|
||||
val leader = bool("leader")
|
||||
val locale = varchar("locale", 16)
|
||||
val manualLocale = bool("manuallocale")
|
||||
val bedrock = bool("bedrock")
|
||||
val password = varchar("password", 1024)
|
||||
val discordId = long("DiscordId")
|
||||
}
|
||||
|
||||
class SteamwarUser(id: EntityID<Int>): IntEntity(id) {
|
||||
companion object : IntEntityClass<SteamwarUser>(SteamwarUserTable)
|
||||
|
||||
var uuid by SteamwarUserTable.uuid
|
||||
var username by SteamwarUserTable.username
|
||||
var team by Team referencedOn SteamwarUserTable.team
|
||||
var leader by SteamwarUserTable.leader
|
||||
var locale by SteamwarUserTable.locale
|
||||
var manualLocale by SteamwarUserTable.manualLocale
|
||||
var bedrock by SteamwarUserTable.bedrock
|
||||
var password by SteamwarUserTable.password
|
||||
var discordId by SteamwarUserTable.discordId
|
||||
|
||||
val punishments by Punishment referrersOn Punishments.user
|
||||
}
|
||||
26
src/main/kotlin/db/schema/Team.kt
Normal file
26
src/main/kotlin/db/schema/Team.kt
Normal file
@ -0,0 +1,26 @@
|
||||
package de.steamwar.db.schema
|
||||
|
||||
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.dao.IntEntity
|
||||
import org.jetbrains.exposed.v1.dao.IntEntityClass
|
||||
|
||||
object TeamTable: IntIdTable("Team", "TeamId") {
|
||||
val kuerzel = varchar("TeamKuerzel", 16)
|
||||
val color = varchar("TeamColor", 16)
|
||||
val name = varchar("TeamName", 255)
|
||||
val deleted = bool("TeamDeleted")
|
||||
val address = varchar("Address", 16)
|
||||
val port = integer("Port")
|
||||
}
|
||||
|
||||
class Team(id: EntityID<Int>): IntEntity(id) {
|
||||
companion object : IntEntityClass<Team>(TeamTable)
|
||||
|
||||
var kuerzel by TeamTable.kuerzel
|
||||
var color by TeamTable.color
|
||||
var name by TeamTable.name
|
||||
var deleted by TeamTable.deleted
|
||||
var address by TeamTable.address
|
||||
var port by TeamTable.port
|
||||
}
|
||||
Reference in New Issue
Block a user