Merge pull request #11 from dalbodeule/develop

add webserver, etc...
This commit is contained in:
JinU Choi 2024-07-30 14:32:09 +09:00 committed by GitHub
commit 55ec35832e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 409 additions and 148 deletions

6
.idea/ktor.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KtorOptions">
<option name="updateOpenAPI" value="true" />
</component>
</project>

3
.idea/sqldialects.xml generated
View File

@ -3,7 +3,4 @@
<component name="SqlDialectMappings"> <component name="SqlDialectMappings">
<file url="PROJECT" dialect="MariaDB" /> <file url="PROJECT" dialect="MariaDB" />
</component> </component>
<component name="SqlResolveMappings">
<file url="file://$PROJECT_DIR$/src/main/kotlin/space/mori/chzzk_bot/models/Command.kt" scope="{&quot;node&quot;:{ &quot;@negative&quot;:&quot;1&quot;, &quot;group&quot;:{ &quot;@kind&quot;:&quot;root&quot;, &quot;node&quot;:{ &quot;@negative&quot;:&quot;1&quot; } } }}" />
</component>
</project> </project>

View File

@ -5,7 +5,6 @@ plugins {
id("application") id("application")
kotlin("jvm") version kotlinVersion kotlin("jvm") version kotlinVersion
kotlin("plugin.jpa") version kotlinVersion kotlin("plugin.jpa") version kotlinVersion
id("org.graalvm.buildtools.native") version "0.10.2"
} }
group = "${project.group}" group = "${project.group}"
@ -32,24 +31,10 @@ dependencies {
implementation("net.dv8tion:JDA:5.0.1") { implementation("net.dv8tion:JDA:5.0.1") {
exclude(module = "opus-java") exclude(module = "opus-java")
} }
// https://mvnrepository.com/artifact/io.github.R2turnTrue/chzzk4j
implementation("io.github.R2turnTrue:chzzk4j:0.0.9")
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic // https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
implementation("ch.qos.logback:logback-classic:1.5.6") implementation("ch.qos.logback:logback-classic:1.5.6")
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-core
implementation("org.jetbrains.exposed:exposed-core:0.52.0")
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-dao
implementation("org.jetbrains.exposed:exposed-dao:0.52.0")
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-jdbc
implementation("org.jetbrains.exposed:exposed-jdbc:0.52.0")
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-kotlin-datetime
implementation("org.jetbrains.exposed:exposed-java-time:0.52.0")
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
implementation("com.zaxxer:HikariCP:5.1.0")
// https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core // https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC")
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect // https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect
@ -58,13 +43,14 @@ dependencies {
// https://mvnrepository.com/artifact/com.google.code.gson/gson // https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation("com.google.code.gson:gson:2.11.0") implementation("com.google.code.gson:gson:2.11.0")
// 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 // https://mvnrepository.com/artifact/io.github.cdimascio/dotenv-kotlin
implementation("io.github.cdimascio:dotenv-kotlin:6.4.1") implementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
kotlin("stdlib") kotlin("stdlib")
listOf(project(":common"), project(":chatbot"), project(":webserver")).forEach {
implementation(it)
}
} }
tasks.withType<Jar> { tasks.withType<Jar> {

50
chatbot/build.gradle.kts Normal file
View File

@ -0,0 +1,50 @@
plugins {
kotlin("jvm")
}
group = project.rootProject.group
version = project.rootProject.version
repositories {
mavenCentral()
}
dependencies {
// https://mvnrepository.com/artifact/net.dv8tion/JDA
implementation("net.dv8tion:JDA:5.0.1") {
exclude(module = "opus-java")
}
// https://mvnrepository.com/artifact/io.github.R2turnTrue/chzzk4j
implementation("io.github.R2turnTrue:chzzk4j:0.0.9")
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
implementation("ch.qos.logback:logback-classic:1.5.6")
// https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC")
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect
implementation("org.jetbrains.kotlin:kotlin-reflect:2.0.0")
// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation("com.google.code.gson:gson:2.11.0")
// https://mvnrepository.com/artifact/io.github.cdimascio/dotenv-kotlin
implementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
// https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
implementation("com.squareup.okhttp3:okhttp:4.12.0")
testImplementation(kotlin("test"))
listOf(project(":common")).forEach {
implementation(it)
}
}
tasks.test {
useJUnitPlatform()
}
kotlin {
jvmToolchain(21)
}

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.chzzk package space.mori.chzzk_bot.chatbot.chzzk
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken

View File

@ -1,37 +1,34 @@
package space.mori.chzzk_bot.chzzk package space.mori.chzzk_bot.chatbot.chzzk
import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.chzzk.Connector.chzzk import space.mori.chzzk_bot.chatbot.chzzk.Connector.chzzk
import space.mori.chzzk_bot.discord import space.mori.chzzk_bot.chatbot.discord.Discord
import space.mori.chzzk_bot.models.User import space.mori.chzzk_bot.common.models.User
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.common.services.UserService
import xyz.r2turntrue.chzzk4j.chat.ChatEventListener import xyz.r2turntrue.chzzk4j.chat.ChatEventListener
import xyz.r2turntrue.chzzk4j.chat.ChatMessage import xyz.r2turntrue.chzzk4j.chat.ChatMessage
import xyz.r2turntrue.chzzk4j.chat.ChzzkChat import xyz.r2turntrue.chzzk4j.chat.ChzzkChat
import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel
import java.lang.Exception import java.lang.Exception
import java.net.SocketTimeoutException import java.net.SocketTimeoutException
import java.time.Instant
object ChzzkHandler { object ChzzkHandler {
private val handlers = mutableListOf<UserHandler>() private val handlers = mutableListOf<UserHandler>()
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)
@Volatile private var running: Boolean = false @Volatile private var running: Boolean = false
internal fun addUser(chzzkChannel: ChzzkChannel, user: User) { fun addUser(chzzkChannel: ChzzkChannel, user: User) {
handlers.add(UserHandler(chzzkChannel, logger, user)) handlers.add(UserHandler(chzzkChannel, logger, user))
} }
internal fun enable() { fun enable() {
UserService.getAllUsers().map { UserService.getAllUsers().map {
chzzk.getChannel(it.token)?.let { token -> addUser(token, it)} chzzk.getChannel(it.token)?.let { token -> addUser(token, it) }
} }
} }
internal fun disable() { fun disable() {
handlers.forEach { handler -> handlers.forEach { handler ->
handler.disable() handler.disable()
} }
@ -53,7 +50,7 @@ object ChzzkHandler {
throw RuntimeException("${chzzkChannel.channelName} doesn't have handler") throw RuntimeException("${chzzkChannel.channelName} doesn't have handler")
} }
internal fun runStreamInfo() { fun runStreamInfo() {
running = true running = true
val thread = Thread({ val thread = Thread({
while(running) { while(running) {
@ -78,7 +75,7 @@ object ChzzkHandler {
thread.start() thread.start()
} }
internal fun stopStreamInfo() { fun stopStreamInfo() {
running = false running = false
} }
} }
@ -137,30 +134,12 @@ class UserHandler(
logger.info("${user.username} is live.") logger.info("${user.username} is live.")
logger.info("ChzzkChat connecting... ${channel.channelName} - ${channel.channelId}") logger.info("ChzzkChat connecting... ${channel.channelName} - ${channel.channelId}")
listener.connectAsync() listener.connectBlocking()
if(user.liveAlertMessage != "" && user.liveAlertGuild != null && user.liveAlertChannel != null) { Discord.sendDiscord(user, status)
val channel = discord.getChannel(user.liveAlertGuild!!, user.liveAlertChannel!!) ?: throw RuntimeException("${user.liveAlertChannel} is not valid.")
val embed = EmbedBuilder()
embed.setTitle(status.content.liveTitle, "https://chzzk.naver.com/live/${user.token}")
embed.setDescription("${user.username} 님이 방송을 시작했습니다.")
embed.setUrl(status.content.channel.channelImageUrl)
embed.setTimestamp(Instant.now())
embed.setAuthor(user.username, "https://chzzk.naver.com/live/${user.token}", status.content.channel.channelImageUrl)
embed.addField("카테고리", status.content.liveCategoryValue, true)
embed.addField("태그", status.content.tags.joinToString(", "), true)
embed.setImage(status.content.liveImageUrl.replace("{type}", "1080"))
channel.sendMessage(
MessageCreateBuilder()
.setContent(user.liveAlertMessage)
.setEmbeds(embed.build())
.build()
).queue()
listener.sendChat("${user.username} 님의 방송이 감지되었습니다.") listener.sendChat("${user.username} 님의 방송이 감지되었습니다.")
}
} else { } else {
logger.info("${user.username} is offline.") logger.info("${user.username} is offline.")
listener.closeAsync() listener.closeAsync()

View File

@ -1,10 +1,14 @@
package space.mori.chzzk_bot.chzzk package space.mori.chzzk_bot.chatbot.chzzk
import io.github.cdimascio.dotenv.dotenv
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import xyz.r2turntrue.chzzk4j.Chzzk import xyz.r2turntrue.chzzk4j.Chzzk
import xyz.r2turntrue.chzzk4j.ChzzkBuilder import xyz.r2turntrue.chzzk4j.ChzzkBuilder
import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel
import space.mori.chzzk_bot.dotenv
val dotenv = dotenv {
ignoreIfMissing = true
}
object Connector { object Connector {
val chzzk: Chzzk = ChzzkBuilder() val chzzk: Chzzk = ChzzkBuilder()

View File

@ -1,10 +1,10 @@
package space.mori.chzzk_bot.chzzk package space.mori.chzzk_bot.chatbot.chzzk
import org.slf4j.Logger import org.slf4j.Logger
import space.mori.chzzk_bot.models.User import space.mori.chzzk_bot.common.models.User
import space.mori.chzzk_bot.services.CommandService import space.mori.chzzk_bot.common.services.CommandService
import space.mori.chzzk_bot.services.CounterService import space.mori.chzzk_bot.common.services.CounterService
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.common.services.UserService
import xyz.r2turntrue.chzzk4j.chat.ChatMessage import xyz.r2turntrue.chzzk4j.chat.ChatMessage
import xyz.r2turntrue.chzzk4j.chat.ChzzkChat import xyz.r2turntrue.chzzk4j.chat.ChzzkChat
import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.discord package space.mori.chzzk_bot.chatbot.discord
import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent

View File

@ -1,5 +1,7 @@
package space.mori.chzzk_bot.discord package space.mori.chzzk_bot.chatbot.discord
import io.github.cdimascio.dotenv.dotenv
import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.JDABuilder import net.dv8tion.jda.api.JDABuilder
import net.dv8tion.jda.api.entities.Activity import net.dv8tion.jda.api.entities.Activity
@ -8,16 +10,53 @@ import net.dv8tion.jda.api.events.guild.GuildJoinEvent
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.hooks.ListenerAdapter import net.dv8tion.jda.api.hooks.ListenerAdapter
import space.mori.chzzk_bot.dotenv import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.discord.commands.* import space.mori.chzzk_bot.chatbot.chzzk.IData
import space.mori.chzzk_bot.services.ManagerService import space.mori.chzzk_bot.chatbot.chzzk.IStreamInfo
import space.mori.chzzk_bot.chatbot.discord.commands.*
import space.mori.chzzk_bot.common.models.User
import space.mori.chzzk_bot.common.services.ManagerService
import java.time.Instant
val dotenv = dotenv {
ignoreIfMissing = true
}
class Discord: ListenerAdapter() { class Discord: ListenerAdapter() {
private lateinit var bot: JDA
private var guild: Guild? = null private var guild: Guild? = null
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)
companion object {
lateinit var bot: JDA
internal fun getChannel(guildId: Long, channelId: Long) =
bot.getGuildById(guildId)?.getTextChannelById(channelId)
fun sendDiscord(user: User, status: IData<IStreamInfo>) {
if(user.liveAlertMessage != "" && user.liveAlertGuild != null && user.liveAlertChannel != null) {
val channel = getChannel(user.liveAlertGuild!!, user.liveAlertChannel!!) ?: throw RuntimeException("${user.liveAlertChannel} is not valid.")
val embed = EmbedBuilder()
embed.setTitle(status.content.liveTitle, "https://chzzk.naver.com/live/${user.token}")
embed.setDescription("${user.username} 님이 방송을 시작했습니다.")
embed.setUrl(status.content.channel.channelImageUrl)
embed.setTimestamp(Instant.now())
embed.setAuthor(user.username, "https://chzzk.naver.com/live/${user.token}", status.content.channel.channelImageUrl)
embed.addField("카테고리", status.content.liveCategoryValue, true)
embed.addField("태그", status.content.tags.joinToString(", "), true)
embed.setImage(status.content.liveImageUrl.replace("{type}", "1080"))
channel.sendMessage(
MessageCreateBuilder()
.setContent(user.liveAlertMessage)
.setEmbeds(embed.build())
.build()
).queue()
}
}
}
private val commands = listOf( private val commands = listOf(
AddCommand, AddCommand,
AlertCommand, AlertCommand,
@ -61,7 +100,7 @@ class Discord: ListenerAdapter() {
.queue() .queue()
} }
internal fun enable() { fun enable() {
val thread = Thread { val thread = Thread {
try { try {
bot = JDABuilder.createDefault(dotenv["DISCORD_TOKEN"]) bot = JDABuilder.createDefault(dotenv["DISCORD_TOKEN"])
@ -83,7 +122,7 @@ class Discord: ListenerAdapter() {
thread.start() thread.start()
} }
internal fun disable() { fun disable() {
try { try {
bot.shutdown() bot.shutdown()
} catch(e: Exception) { } catch(e: Exception) {
@ -91,7 +130,4 @@ class Discord: ListenerAdapter() {
logger.debug(e.stackTraceToString()) logger.debug(e.stackTraceToString())
} }
} }
internal fun getChannel(guildId: Long, channelId: Long) =
bot.getGuildById(guildId)?.getTextChannelById(channelId)
} }

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.discord.commands package space.mori.chzzk_bot.chatbot.discord.commands
import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
@ -6,12 +6,12 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
import net.dv8tion.jda.api.interactions.commands.build.Commands import net.dv8tion.jda.api.interactions.commands.build.Commands
import net.dv8tion.jda.api.interactions.commands.build.OptionData import net.dv8tion.jda.api.interactions.commands.build.OptionData
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.chzzk.ChzzkHandler import space.mori.chzzk_bot.chatbot.chzzk.ChzzkHandler
import space.mori.chzzk_bot.chzzk.Connector import space.mori.chzzk_bot.chatbot.chzzk.Connector
import space.mori.chzzk_bot.discord.CommandInterface import space.mori.chzzk_bot.chatbot.discord.CommandInterface
import space.mori.chzzk_bot.services.CommandService import space.mori.chzzk_bot.common.services.CommandService
import space.mori.chzzk_bot.services.ManagerService import space.mori.chzzk_bot.common.services.ManagerService
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.common.services.UserService
object AddCommand : CommandInterface { object AddCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.discord.commands package space.mori.chzzk_bot.chatbot.discord.commands
import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
@ -6,9 +6,9 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
import net.dv8tion.jda.api.interactions.commands.build.Commands import net.dv8tion.jda.api.interactions.commands.build.Commands
import net.dv8tion.jda.api.interactions.commands.build.OptionData import net.dv8tion.jda.api.interactions.commands.build.OptionData
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.discord.CommandInterface import space.mori.chzzk_bot.chatbot.discord.CommandInterface
import space.mori.chzzk_bot.services.ManagerService import space.mori.chzzk_bot.common.services.ManagerService
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.common.services.UserService
object AddManagerCommand : CommandInterface { object AddManagerCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.discord.commands package space.mori.chzzk_bot.chatbot.discord.commands
import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
@ -6,11 +6,11 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
import net.dv8tion.jda.api.interactions.commands.build.Commands import net.dv8tion.jda.api.interactions.commands.build.Commands
import net.dv8tion.jda.api.interactions.commands.build.OptionData import net.dv8tion.jda.api.interactions.commands.build.OptionData
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.chzzk.ChzzkHandler import space.mori.chzzk_bot.chatbot.chzzk.ChzzkHandler
import space.mori.chzzk_bot.chzzk.Connector import space.mori.chzzk_bot.chatbot.chzzk.Connector
import space.mori.chzzk_bot.discord.CommandInterface import space.mori.chzzk_bot.chatbot.discord.CommandInterface
import space.mori.chzzk_bot.services.ManagerService import space.mori.chzzk_bot.common.services.ManagerService
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.common.services.UserService
object AlertCommand : CommandInterface { object AlertCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)

View File

@ -1,13 +1,13 @@
package space.mori.chzzk_bot.discord.commands package space.mori.chzzk_bot.chatbot.discord.commands
import net.dv8tion.jda.api.EmbedBuilder import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.Commands import net.dv8tion.jda.api.interactions.commands.build.Commands
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.discord.CommandInterface import space.mori.chzzk_bot.chatbot.discord.CommandInterface
import space.mori.chzzk_bot.services.ManagerService import space.mori.chzzk_bot.common.services.ManagerService
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.common.services.UserService
object ListManagerCommand : CommandInterface { object ListManagerCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)

View File

@ -1,10 +1,10 @@
package space.mori.chzzk_bot.discord.commands package space.mori.chzzk_bot.chatbot.discord.commands
import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.Commands import net.dv8tion.jda.api.interactions.commands.build.Commands
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.discord.CommandInterface import space.mori.chzzk_bot.chatbot.discord.CommandInterface
object PingCommand: CommandInterface { object PingCommand: CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.discord.commands package space.mori.chzzk_bot.chatbot.discord.commands
import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
@ -6,10 +6,10 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
import net.dv8tion.jda.api.interactions.commands.build.Commands import net.dv8tion.jda.api.interactions.commands.build.Commands
import net.dv8tion.jda.api.interactions.commands.build.OptionData import net.dv8tion.jda.api.interactions.commands.build.OptionData
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.chzzk.ChzzkHandler import space.mori.chzzk_bot.chatbot.chzzk.ChzzkHandler
import space.mori.chzzk_bot.chzzk.Connector import space.mori.chzzk_bot.chatbot.chzzk.Connector
import space.mori.chzzk_bot.discord.CommandInterface import space.mori.chzzk_bot.chatbot.discord.CommandInterface
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.common.services.UserService
object RegisterCommand: CommandInterface { object RegisterCommand: CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.discord.commands package space.mori.chzzk_bot.chatbot.discord.commands
import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
@ -6,12 +6,12 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
import net.dv8tion.jda.api.interactions.commands.build.Commands import net.dv8tion.jda.api.interactions.commands.build.Commands
import net.dv8tion.jda.api.interactions.commands.build.OptionData import net.dv8tion.jda.api.interactions.commands.build.OptionData
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.chzzk.ChzzkHandler import space.mori.chzzk_bot.chatbot.chzzk.ChzzkHandler
import space.mori.chzzk_bot.chzzk.Connector import space.mori.chzzk_bot.chatbot.chzzk.Connector
import space.mori.chzzk_bot.discord.CommandInterface import space.mori.chzzk_bot.chatbot.discord.CommandInterface
import space.mori.chzzk_bot.services.CommandService import space.mori.chzzk_bot.common.services.CommandService
import space.mori.chzzk_bot.services.ManagerService import space.mori.chzzk_bot.common.services.ManagerService
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.common.services.UserService
object RemoveCommand : CommandInterface { object RemoveCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.discord.commands package space.mori.chzzk_bot.chatbot.discord.commands
import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
@ -6,9 +6,9 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
import net.dv8tion.jda.api.interactions.commands.build.Commands import net.dv8tion.jda.api.interactions.commands.build.Commands
import net.dv8tion.jda.api.interactions.commands.build.OptionData import net.dv8tion.jda.api.interactions.commands.build.OptionData
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.discord.CommandInterface import space.mori.chzzk_bot.chatbot.discord.CommandInterface
import space.mori.chzzk_bot.services.ManagerService import space.mori.chzzk_bot.common.services.ManagerService
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.common.services.UserService
object RemoveManagerCommand : CommandInterface { object RemoveManagerCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.discord.commands package space.mori.chzzk_bot.chatbot.discord.commands
import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
@ -6,12 +6,12 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
import net.dv8tion.jda.api.interactions.commands.build.Commands import net.dv8tion.jda.api.interactions.commands.build.Commands
import net.dv8tion.jda.api.interactions.commands.build.OptionData import net.dv8tion.jda.api.interactions.commands.build.OptionData
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.chzzk.ChzzkHandler import space.mori.chzzk_bot.chatbot.chzzk.ChzzkHandler
import space.mori.chzzk_bot.chzzk.Connector import space.mori.chzzk_bot.chatbot.chzzk.Connector
import space.mori.chzzk_bot.discord.CommandInterface import space.mori.chzzk_bot.chatbot.discord.CommandInterface
import space.mori.chzzk_bot.services.CommandService import space.mori.chzzk_bot.common.services.CommandService
import space.mori.chzzk_bot.services.ManagerService import space.mori.chzzk_bot.common.services.ManagerService
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.common.services.UserService
object UpdateCommand : CommandInterface { object UpdateCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)

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

@ -1,11 +1,16 @@
package space.mori.chzzk_bot package space.mori.chzzk_bot.common
import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource import com.zaxxer.hikari.HikariDataSource
import io.github.cdimascio.dotenv.dotenv
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import space.mori.chzzk_bot.models.* import space.mori.chzzk_bot.common.models.*
val dotenv = dotenv {
ignoreIfMissing = true
}
object Connector { object Connector {
private val hikariConfig = HikariConfig().apply { private val hikariConfig = HikariConfig().apply {

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.models package space.mori.chzzk_bot.common.models
import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass import org.jetbrains.exposed.dao.IntEntityClass

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.models package space.mori.chzzk_bot.common.models
import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass import org.jetbrains.exposed.dao.IntEntityClass

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.models package space.mori.chzzk_bot.common.models
import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass import org.jetbrains.exposed.dao.IntEntityClass

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.models package space.mori.chzzk_bot.common.models
import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass import org.jetbrains.exposed.dao.IntEntityClass

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.models package space.mori.chzzk_bot.common.models
import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass import org.jetbrains.exposed.dao.IntEntityClass

View File

@ -1,4 +1,4 @@
package space.mori.chzzk_bot.models package space.mori.chzzk_bot.common.models
import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass import org.jetbrains.exposed.dao.IntEntityClass

View File

@ -1,12 +1,12 @@
package space.mori.chzzk_bot.services package space.mori.chzzk_bot.common.services
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import space.mori.chzzk_bot.models.Command import space.mori.chzzk_bot.common.models.Command
import space.mori.chzzk_bot.models.Commands import space.mori.chzzk_bot.common.models.Commands
import space.mori.chzzk_bot.models.User import space.mori.chzzk_bot.common.models.User
object CommandService { object CommandService {
fun saveCommand(user: User, command: String, content: String, failContent: String): Command { fun saveCommand(user: User, command: String, content: String, failContent: String): Command {

View File

@ -1,8 +1,8 @@
package space.mori.chzzk_bot.services package space.mori.chzzk_bot.common.services
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import space.mori.chzzk_bot.models.* import space.mori.chzzk_bot.common.models.*
import java.time.LocalDate import java.time.LocalDate
object CounterService { object CounterService {

View File

@ -1,11 +1,11 @@
package space.mori.chzzk_bot.services package space.mori.chzzk_bot.common.services
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import space.mori.chzzk_bot.models.Manager import space.mori.chzzk_bot.common.models.Manager
import space.mori.chzzk_bot.models.Managers import space.mori.chzzk_bot.common.models.Managers
import space.mori.chzzk_bot.models.User import space.mori.chzzk_bot.common.models.User
object ManagerService { object ManagerService {
fun saveManager(user: User, discordId: Long, name: String): Manager { fun saveManager(user: User, discordId: Long, name: String): Manager {

View File

@ -1,10 +1,10 @@
package space.mori.chzzk_bot.services package space.mori.chzzk_bot.common.services
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import space.mori.chzzk_bot.models.User import space.mori.chzzk_bot.common.models.User
import space.mori.chzzk_bot.models.Users import space.mori.chzzk_bot.common.models.Users
object UserService { object UserService {
fun saveUser(username: String, token: String, discordID: Long): User { fun saveUser(username: String, token: String, discordID: Long): User {

View File

@ -1,6 +1,6 @@
kotlin.code.style=official kotlin.code.style=official
group = space.mori group = space.mori
version = 1.0.3 version = 1.1.0
org.gradle.jvmargs=-Dfile.encoding=UTF-8 org.gradle.jvmargs=-Dfile.encoding=UTF-8
org.gradle.console=plain org.gradle.console=plain

View File

@ -14,6 +14,13 @@ pluginManagement {
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
maven { url = uri("https://maven.covers1624.net") } maven { url = uri("https://maven.covers1624.net") }
maven { url = uri("https://repo.spring.io/plugins-release/") } maven { url = uri("https://jitpack.io") }
} }
} }
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
}
include("common")
include("chatbot")
include("webserver")

View File

@ -5,9 +5,12 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.chzzk.ChzzkHandler import space.mori.chzzk_bot.chatbot.chzzk.ChzzkHandler
import space.mori.chzzk_bot.discord.Discord import space.mori.chzzk_bot.chatbot.discord.Discord
import space.mori.chzzk_bot.chzzk.Connector as ChzzkConnector import space.mori.chzzk_bot.chatbot.chzzk.Connector as ChzzkConnector
import space.mori.chzzk_bot.common.Connector
import space.mori.chzzk_bot.webserver.start
import space.mori.chzzk_bot.webserver.stop
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -26,6 +29,7 @@ fun main(args: Array<String>) {
discord.enable() discord.enable()
chzzkHandler.enable() chzzkHandler.enable()
chzzkHandler.runStreamInfo() chzzkHandler.runStreamInfo()
start()
if(dotenv.get("RUN_AGENT", "false").toBoolean()) { if(dotenv.get("RUN_AGENT", "false").toBoolean()) {
runBlocking { runBlocking {
@ -36,6 +40,8 @@ fun main(args: Array<String>) {
Runtime.getRuntime().addShutdownHook(Thread { Runtime.getRuntime().addShutdownHook(Thread {
logger.info("Shutting down...") logger.info("Shutting down...")
stop()
chzzkHandler.stopStreamInfo() chzzkHandler.stopStreamInfo()
chzzkHandler.disable() chzzkHandler.disable()
discord.disable() discord.disable()

View File

@ -0,0 +1,45 @@
plugins {
kotlin("jvm")
kotlin("plugin.serialization").version("2.0.0")
}
group = project.rootProject.group
version = project.rootProject.version
repositories {
mavenCentral()
}
val ktorVersion = "2.3.12"
dependencies {
implementation("io.ktor:ktor-server-core:$ktorVersion")
implementation("io.ktor:ktor-server-netty:$ktorVersion")
implementation("io.ktor:ktor-server-websockets:$ktorVersion")
implementation("io.ktor:ktor-server-swagger:$ktorVersion")
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
implementation("io.ktor:ktor-server-cors:$ktorVersion")
implementation("io.ktor:ktor-server-swagger:$ktorVersion")
implementation("io.swagger.codegen.v3:swagger-codegen-generators:1.0.50")
// https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC")
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect
implementation("org.jetbrains.kotlin:kotlin-reflect:2.0.0")
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
implementation("ch.qos.logback:logback-classic:1.5.6")
implementation(project(":common"))
testImplementation(kotlin("test"))
}
tasks.test {
useJUnitPlatform()
}
kotlin {
jvmToolchain(21)
}

View File

@ -0,0 +1,44 @@
package space.mori.chzzk_bot.webserver
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.plugins.swagger.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import kotlinx.serialization.json.Json
import space.mori.chzzk_bot.webserver.routes.apiRoutes
val server = embeddedServer(Netty, port = 8080) {
install(WebSockets)
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
})
}
install(CORS) {
anyHost()
allowHeader(HttpHeaders.ContentType)
}
routing {
apiRoutes()
swaggerUI("swagger-ui/index.html", "openapi/documentation.yaml") {
options {
version = "1.1.0"
}
}
}
}
fun start() {
server.start(wait = true)
}
fun stop() {
server.stop()
}

View File

@ -0,0 +1,19 @@
package space.mori.chzzk_bot.webserver.routes
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Routing.apiRoutes() {
route("/") {
get {
call.respondText("Hello World!", status = HttpStatusCode.OK)
}
}
route("/health") {
get {
call.respondText("OK", status= HttpStatusCode.OK)
}
}
}

View File

@ -0,0 +1,35 @@
openapi: "3.1.0"
info:
title: "chzzk_bot API"
description: "chzzk_bot API"
version: "1.0.0"
servers:
- url: "http://localhost:8080"
paths:
/:
get:
summary: "Webroot"
description: "Main page of this api"
responses:
"200":
description: "OK"
content:
text/plain:
schema:
type: "string"
examples:
Example#1:
value: "Hello World!"
/health:
get:
description: "Health Check endpoint"
responses:
"200":
description: "OK"
content:
text/plain:
schema:
type: "string"
examples:
Example#1:
value: "OK"