add webserver, etc...

- add ktor webserver
- package is subpackaged.
This commit is contained in:
dalbodeule
2024-07-30 14:28:43 +09:00
parent 8f8d2f895a
commit 72f98b024b
38 changed files with 409 additions and 148 deletions

42
common/build.gradle.kts Normal file
View File

@@ -0,0 +1,42 @@
plugins {
kotlin("jvm")
}
group = project.rootProject.group
version = project.rootProject.version
repositories {
mavenCentral()
}
dependencies {
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-core
api("org.jetbrains.exposed:exposed-core:0.52.0")
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-dao
api("org.jetbrains.exposed:exposed-dao:0.52.0")
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-jdbc
api("org.jetbrains.exposed:exposed-jdbc:0.52.0")
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-kotlin-datetime
api("org.jetbrains.exposed:exposed-java-time:0.52.0")
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
api("com.zaxxer:HikariCP:5.1.0")
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
implementation("ch.qos.logback:logback-classic:1.5.6")
// https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client
implementation("org.mariadb.jdbc:mariadb-java-client:3.4.1")
// https://mvnrepository.com/artifact/io.github.cdimascio/dotenv-kotlin
implementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
testImplementation(kotlin("test"))
}
tasks.test {
useJUnitPlatform()
}
kotlin {
jvmToolchain(21)
}

View File

@@ -0,0 +1,33 @@
package space.mori.chzzk_bot.common
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import io.github.cdimascio.dotenv.dotenv
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.transactions.transaction
import space.mori.chzzk_bot.common.models.*
val dotenv = dotenv {
ignoreIfMissing = true
}
object Connector {
private val hikariConfig = HikariConfig().apply {
jdbcUrl = dotenv["DB_URL"]
driverClassName = "org.mariadb.jdbc.Driver"
username = dotenv["DB_USER"]
password = dotenv["DB_PASS"]
maximumPoolSize = 10
}
val dataSource = HikariDataSource(hikariConfig)
init {
Database.connect(dataSource)
val tables = listOf(Users, Commands, Counters, DailyCounters, PersonalCounters, Managers)
transaction {
SchemaUtils.createMissingTablesAndColumns(* tables.toTypedArray())
}
}
}

View File

@@ -0,0 +1,23 @@
package space.mori.chzzk_bot.common.models
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.ReferenceOption
object Commands: IntIdTable("commands") {
val user = reference("user", Users, onDelete = ReferenceOption.CASCADE)
val command = varchar("command", 255)
val content = text("content")
val failContent = text("fail_content")
}
class Command(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Command>(Commands)
var user by User referencedOn Commands.user
var command by Commands.command
var content by Commands.content
var failContent by Commands.failContent
}

View File

@@ -0,0 +1,20 @@
package space.mori.chzzk_bot.common.models
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
object Counters: IntIdTable("counters") {
val name = varchar("name", 255)
val value = integer("value")
val user = reference("streamer", Users)
}
class Counter(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Counter>(Counters)
var name by Counters.name
var value by Counters.value
var user by User referencedOn Counters.user
}

View File

@@ -0,0 +1,25 @@
package space.mori.chzzk_bot.common.models
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.javatime.date
object DailyCounters: IntIdTable("daily_counters") {
val name = varchar("name", 255)
val userId = varchar("user_id", 64)
val value = integer("value")
val updatedAt = date("updated_at")
val user = reference("streamer", Users)
}
class DailyCounter(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<DailyCounter>(DailyCounters)
var name by DailyCounters.name
var userId by DailyCounters.userId
var value by DailyCounters.value
var updatedAt by DailyCounters.updatedAt
var user by User referencedOn DailyCounters.user
}

View File

@@ -0,0 +1,22 @@
package space.mori.chzzk_bot.common.models
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
object Managers: IntIdTable("manager") {
val user = reference("user", Users)
val managerId = long("manager_id")
val discordGuildId = long("discord_guild_id")
var lastUserName = varchar("last_user_name", 255).nullable()
}
class Manager(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Manager>(Managers)
var user by User referencedOn Managers.user
var managerId by Managers.managerId
var discordGuildId by Managers.discordGuildId
var lastUserName by Managers.lastUserName
}

View File

@@ -0,0 +1,22 @@
package space.mori.chzzk_bot.common.models
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
object PersonalCounters: IntIdTable("personal_counters") {
val name = varchar("name", 255)
val userId = varchar("user_id", 64)
val value = integer("value")
val user = reference("streamer", Users)
}
class PersonalCounter(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<PersonalCounter>(PersonalCounters)
var name by PersonalCounters.name
var userId by PersonalCounters.userId
var value by PersonalCounters.value
var user by User referencedOn PersonalCounters.user
}

View File

@@ -0,0 +1,27 @@
package space.mori.chzzk_bot.common.models
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
object Users: IntIdTable("users") {
val username = varchar("username", 255)
val token = varchar("token", 64)
val discord = long("discord")
val liveAlertGuild = long("live_alert_guild").nullable()
val liveAlertChannel = long("live_alert_channel").nullable()
val liveAlertMessage = text("live_alert_message").nullable()
}
class User(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<User>(Users)
var username by Users.username
var token by Users.token
var discord by Users.discord
var liveAlertGuild by Users.liveAlertGuild
var liveAlertChannel by Users.liveAlertChannel
var liveAlertMessage by Users.liveAlertMessage
}

View File

@@ -0,0 +1,59 @@
package space.mori.chzzk_bot.common.services
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import space.mori.chzzk_bot.common.models.Command
import space.mori.chzzk_bot.common.models.Commands
import space.mori.chzzk_bot.common.models.User
object CommandService {
fun saveCommand(user: User, command: String, content: String, failContent: String): Command {
return transaction {
return@transaction Command.new {
this.user = user
this.command = command
this.content = content
this.failContent = failContent
}
}
}
fun removeCommand(user: User, command: String): Command? {
return transaction {
val commandRow = Command.find(Commands.user eq user.id and(Commands.command eq command)).firstOrNull()
commandRow ?: throw RuntimeException("Command not found! $command")
commandRow.delete()
commandRow
}
}
fun updateCommand(user: User, command: String, content: String, failContent: String): Command {
return transaction {
val updated = Commands.update({Commands.user eq user.id and(Commands.command eq command)}) {
it[Commands.content] = content
it[Commands.failContent] = failContent
}
if(updated == 0) throw RuntimeException("Command not found! $command")
Command.find(Commands.user eq user.id and(Commands.command eq command)).first()
}
}
fun getCommand(id: Int): Command? {
return transaction {
Command.findById(id)
}
}
fun getCommands(user: User): List<Command> {
return transaction {
Command.find(Commands.user eq user.id)
.toList()
}
}
}

View File

@@ -0,0 +1,104 @@
package space.mori.chzzk_bot.common.services
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction
import space.mori.chzzk_bot.common.models.*
import java.time.LocalDate
object CounterService {
fun getCounterValue(name: String, user: User): Int {
return transaction {
Counter.find {
(Counters.name eq name) and (Counters.user eq user.id)
}.singleOrNull()?.value ?: 0
}
}
fun updateCounterValue(name: String, increment: Int, user: User): Int {
return transaction {
val counter = Counter.find {
(Counters.name eq name) and (Counters.user eq user.id) }.singleOrNull()
return@transaction if (counter != null) {
counter.value += increment
counter.value
} else {
val newCounter = Counter.new {
this.name = name
this.value = increment
this.user = user
}
newCounter.value
}
}
}
fun getPersonalCounterValue(name: String, userId: String, user: User): Int {
return transaction {
PersonalCounter.find {
(PersonalCounters.name eq name) and (PersonalCounters.userId eq userId) and (PersonalCounters.user eq user.id)
}.singleOrNull()?.value ?: 0
}
}
fun updatePersonalCounterValue(name: String, userId: String, increment: Int, user: User): Int {
return transaction {
val counter = PersonalCounter.find {
(PersonalCounters.name eq name) and (PersonalCounters.userId eq userId) and (PersonalCounters.user eq user.id)
}.singleOrNull()
return@transaction if (counter != null) {
counter.value += increment
counter.value
} else {
val newCounter = PersonalCounter.new {
this.name = name
this.value = increment
this.userId = userId
this.user = user
}
newCounter.value
}
}
}
fun getDailyCounterValue(name: String, userId: String, user: User): Pair<Int, Boolean> {
val today = LocalDate.now()
return transaction {
val counter = DailyCounter.find {
(DailyCounters.name eq name) and (DailyCounters.userId eq userId) and (DailyCounters.user eq user.id)
}.singleOrNull()
Pair(counter?.value ?: 0, counter?.updatedAt != today)
}
}
fun updateDailyCounterValue(name: String, userId: String, increment: Int, user: User): Pair<Int, Boolean> {
val today = LocalDate.now()
return transaction {
val counter = DailyCounter.find {
(DailyCounters.name eq name) and (DailyCounters.userId eq userId) and (DailyCounters.user eq user.id)
}.singleOrNull()
if(counter == null) {
val newCounter = DailyCounter.new {
this.name = name
this.value = increment
this.userId = userId
this.updatedAt = today
this.user = user
}
return@transaction Pair(newCounter.value, true)
}
return@transaction if(counter.updatedAt == today)
Pair(counter.value, false)
else {
counter.value += increment
counter.updatedAt = today
Pair(counter.value, true)
}
}
}
}

View File

@@ -0,0 +1,70 @@
package space.mori.chzzk_bot.common.services
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction
import space.mori.chzzk_bot.common.models.Manager
import space.mori.chzzk_bot.common.models.Managers
import space.mori.chzzk_bot.common.models.User
object ManagerService {
fun saveManager(user: User, discordId: Long, name: String): Manager {
if (user.liveAlertGuild == null)
throw RuntimeException("${user.username} has no liveAlertGuild")
return transaction {
Manager.new {
this.user = user
this.discordGuildId = user.liveAlertGuild!!
this.managerId = discordId
this.lastUserName = name
}
}
}
fun updateManager(user: User, discordId: Long, name: String): Manager {
if (user.liveAlertGuild == null)
throw RuntimeException("${user.username} has no liveAlertGuild")
val manager = getUser(user.liveAlertGuild!!, discordId)
if (manager == null)
throw RuntimeException("$name isn't manager.")
manager.lastUserName = name
return manager
}
fun getUser(guildId: Long, discordId: Long): Manager? {
return transaction {
val manager = Manager.find(
(Managers.discordGuildId eq guildId) and (Managers.managerId eq discordId)
)
manager.firstOrNull()
}
}
fun getAllUsers(guildId: Long): List<Manager> {
return transaction {
Manager.find(Managers.discordGuildId eq guildId).toList()
}
}
fun deleteManager(user: User, discordId: Long): Manager {
if (user.liveAlertGuild == null)
throw RuntimeException("${user.username} has no liveAlertGuild")
return deleteManager(user.liveAlertGuild!!, discordId)
}
fun deleteManager(guildId: Long, discordId: Long): Manager {
return transaction {
val managerRow = Manager.find((Managers.discordGuildId eq guildId) and (Managers.managerId eq discordId)).firstOrNull()
managerRow ?: throw RuntimeException("Manager not found! $discordId")
managerRow.delete()
managerRow
}
}
}

View File

@@ -0,0 +1,70 @@
package space.mori.chzzk_bot.common.services
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import space.mori.chzzk_bot.common.models.User
import space.mori.chzzk_bot.common.models.Users
object UserService {
fun saveUser(username: String, token: String, discordID: Long): User {
return transaction {
User.new {
this.username = username
this.token = token
this.discord = discordID
}
}
}
fun getUser(id: Int): User? {
return transaction {
User.findById(id)
}
}
fun getUser(discordID: Long): User? {
return transaction {
val users = User.find(Users.discord eq discordID)
users.firstOrNull()
}
}
fun getUser(chzzkID: String): User? {
return transaction {
val users = User.find(Users.token eq chzzkID)
users.firstOrNull()
}
}
fun getUserWithGuildId(discordGuildId: Long): User? {
return transaction {
val users = User.find(Users.liveAlertGuild eq discordGuildId)
users.firstOrNull()
}
}
fun getAllUsers(): List<User> {
return transaction {
User.all().toList()
}
}
fun updateLiveAlert(id: Int, guildId: Long, channelId: Long, alertMessage: String?): User {
return transaction {
val updated = Users.update({ Users.id eq id }) {
it[liveAlertGuild] = guildId
it[liveAlertChannel] = channelId
it[liveAlertMessage] = alertMessage ?: ""
}
if(updated == 0) throw RuntimeException("User not found! $id")
val users = User.find { Users.id eq id }
return@transaction users.first()
}
}
}