mirror of
https://github.com/dalbodeule/chibot-chzzk-bot.git
synced 2025-06-09 07:18:22 +00:00
add chzzk chat handler, command handler
This commit is contained in:
parent
294bf04a50
commit
4b7fe25b21
3
.idea/sqldialects.xml
generated
3
.idea/sqldialects.xml
generated
@ -3,4 +3,7 @@
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="PROJECT" dialect="MariaDB" />
|
||||
</component>
|
||||
<component name="SqlResolveMappings">
|
||||
<file url="file://$PROJECT_DIR$/src/main/kotlin/space/mori/chzzk_bot/models/Command.kt" scope="{"node":{ "@negative":"1", "group":{ "@kind":"root", "node":{ "@negative":"1" } } }}" />
|
||||
</component>
|
||||
</project>
|
@ -58,6 +58,7 @@ dependencies {
|
||||
}
|
||||
// https://mvnrepository.com/artifact/io.github.R2turnTrue/chzzk4j
|
||||
implementation("io.github.R2turnTrue:chzzk4j:0.0.7")
|
||||
|
||||
implementation("ch.qos.logback:logback-classic:1.4.14")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-core
|
||||
|
@ -6,6 +6,8 @@ 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 org.slf4j.LoggerFactory
|
||||
import space.mori.chzzk_bot.models.Commands
|
||||
import space.mori.chzzk_bot.models.Users
|
||||
|
||||
object Connector {
|
||||
@ -22,9 +24,12 @@ object Connector {
|
||||
|
||||
init {
|
||||
Database.connect(dataSource)
|
||||
val tables = listOf(Users, Commands)
|
||||
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(Users)
|
||||
tables.forEach { table ->
|
||||
SchemaUtils.createMissingTablesAndColumns(table)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import space.mori.chzzk_bot.chzzk.ChzzkHandler
|
||||
import space.mori.chzzk_bot.discord.Discord
|
||||
import space.mori.chzzk_bot.chzzk.Connector as ChzzkConnector
|
||||
import java.util.concurrent.TimeUnit
|
||||
@ -15,10 +16,12 @@ val logger: Logger = LoggerFactory.getLogger("main")
|
||||
fun main(args: Array<String>) {
|
||||
val discord = Discord()
|
||||
|
||||
Connector
|
||||
ChzzkConnector
|
||||
val connector = Connector
|
||||
val chzzkConnector = ChzzkConnector
|
||||
val chzzkHandler = ChzzkHandler
|
||||
|
||||
discord.enable()
|
||||
chzzkHandler.enable()
|
||||
|
||||
if(dotenv.get("RUN_AGENT", "false").toBoolean()) {
|
||||
runBlocking {
|
||||
@ -26,4 +29,11 @@ fun main(args: Array<String>) {
|
||||
discord.disable()
|
||||
}
|
||||
}
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(Thread {
|
||||
logger.info("Shutting down...")
|
||||
chzzkHandler.disable()
|
||||
discord.disable()
|
||||
connector.dataSource.close()
|
||||
})
|
||||
}
|
69
src/main/kotlin/space/mori/chzzk_bot/chzzk/ChzzkHandler.kt
Normal file
69
src/main/kotlin/space/mori/chzzk_bot/chzzk/ChzzkHandler.kt
Normal file
@ -0,0 +1,69 @@
|
||||
package space.mori.chzzk_bot.chzzk
|
||||
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import space.mori.chzzk_bot.chzzk.Connector.chzzk
|
||||
import space.mori.chzzk_bot.services.UserService
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChatEventListener
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChatMessage
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChzzkChat
|
||||
import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel
|
||||
import java.lang.Exception
|
||||
|
||||
object ChzzkHandler {
|
||||
private val handlers = mutableListOf<UserHandler>()
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
|
||||
internal fun addUser(chzzkChannel: ChzzkChannel) {
|
||||
handlers.add(UserHandler(chzzkChannel, logger))
|
||||
}
|
||||
|
||||
internal fun enable() {
|
||||
UserService.getAllUsers().map {
|
||||
chzzk.getChannel(it.token)?.let { token -> addUser(token)}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun disable() {
|
||||
handlers.forEach { handler ->
|
||||
handler.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UserHandler(private val channel: ChzzkChannel, private val logger: Logger) {
|
||||
private lateinit var messageHandler: MessageHandler
|
||||
|
||||
private var listener: ChzzkChat = chzzk.chat(channel.channelId)
|
||||
.withAutoReconnect(true)
|
||||
.withChatListener(object : ChatEventListener {
|
||||
override fun onConnect(chat: ChzzkChat, isReconnecting: Boolean) {
|
||||
logger.info("ChzzkChat connected. ${channel.channelName} - ${channel.channelId} / reconnected: $isReconnecting")
|
||||
messageHandler = MessageHandler(channel, logger, chat)
|
||||
}
|
||||
|
||||
override fun onError(ex: Exception) {
|
||||
logger.info("ChzzkChat error. ${channel.channelName} - ${channel.channelId}")
|
||||
logger.debug(ex.stackTraceToString())
|
||||
}
|
||||
|
||||
override fun onChat(msg: ChatMessage) {
|
||||
messageHandler.handle(msg)
|
||||
}
|
||||
|
||||
override fun onConnectionClosed(code: Int, reason: String?, remote: Boolean, tryingToReconnect: Boolean) {
|
||||
logger.info("ChzzkChat closed. ${channel.channelName} - ${channel.channelId}")
|
||||
logger.info("Reason: $reason / $tryingToReconnect")
|
||||
}
|
||||
})
|
||||
.build()
|
||||
|
||||
init {
|
||||
logger.info("ChzzkChat connecting... ${channel.channelName} - ${channel.channelId}")
|
||||
listener.connectBlocking()
|
||||
}
|
||||
|
||||
internal fun disable() {
|
||||
listener.closeBlocking()
|
||||
}
|
||||
}
|
@ -1,14 +1,21 @@
|
||||
package space.mori.chzzk_bot.chzzk
|
||||
|
||||
import io.github.cdimascio.dotenv.dotenv
|
||||
import org.slf4j.LoggerFactory
|
||||
import xyz.r2turntrue.chzzk4j.Chzzk
|
||||
import xyz.r2turntrue.chzzk4j.ChzzkBuilder
|
||||
import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel
|
||||
|
||||
object Connector {
|
||||
private val dotenv = dotenv()
|
||||
val chzzk: Chzzk = ChzzkBuilder()
|
||||
.withAuthorization(dotenv["NID_AUT"], dotenv["NID_SES"])
|
||||
.build()
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
|
||||
fun getChannel(channelId: String) = chzzk.getChannel(channelId)
|
||||
fun getChannel(channelId: String): ChzzkChannel? = chzzk.getChannel(channelId)
|
||||
|
||||
init {
|
||||
logger.info("chzzk logged: ${chzzk.isLoggedIn} / ${chzzk.loggedUser?.nickname ?: "----"}")
|
||||
}
|
||||
}
|
34
src/main/kotlin/space/mori/chzzk_bot/chzzk/MessageHandler.kt
Normal file
34
src/main/kotlin/space/mori/chzzk_bot/chzzk/MessageHandler.kt
Normal file
@ -0,0 +1,34 @@
|
||||
package space.mori.chzzk_bot.chzzk
|
||||
|
||||
import org.slf4j.Logger
|
||||
import space.mori.chzzk_bot.services.CommandService
|
||||
import space.mori.chzzk_bot.services.UserService
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChatMessage
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChzzkChat
|
||||
import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel
|
||||
class MessageHandler(
|
||||
private val channel: ChzzkChannel,
|
||||
private val logger: Logger,
|
||||
private val listener: ChzzkChat
|
||||
) {
|
||||
private val commands = mutableMapOf<String, () -> Unit>()
|
||||
|
||||
init {
|
||||
val user = UserService.getUser(channel.channelId)
|
||||
?: throw RuntimeException("User not found. it's bug? ${channel.channelName} - ${channel.channelId}")
|
||||
val commands = CommandService.getCommands(user)
|
||||
|
||||
commands.map {
|
||||
this.commands.put(it.command.lowercase()) {
|
||||
logger.debug("${channel.channelName} - ${it.command} - ${it.content}")
|
||||
listener.sendChat(it.content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun handle(msg: ChatMessage) {
|
||||
val commandKey = msg.content.split(' ')[0]
|
||||
|
||||
commands[commandKey.lowercase()]?.let { it() }
|
||||
}
|
||||
}
|
@ -6,13 +6,13 @@ import net.dv8tion.jda.api.entities.Activity
|
||||
import net.dv8tion.jda.api.entities.Guild
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
||||
import org.slf4j.LoggerFactory
|
||||
import space.mori.chzzk_bot.dotenv
|
||||
import space.mori.chzzk_bot.logger
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class Discord: ListenerAdapter() {
|
||||
private lateinit var bot: JDA
|
||||
private var guild: Guild? = null
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
|
||||
private val commands = getCommands()
|
||||
|
||||
|
@ -3,13 +3,15 @@ package space.mori.chzzk_bot.discord.commands
|
||||
import net.dv8tion.jda.api.JDA
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands
|
||||
import org.slf4j.LoggerFactory
|
||||
import space.mori.chzzk_bot.discord.Command
|
||||
import space.mori.chzzk_bot.discord.CommandInterface
|
||||
|
||||
@Command()
|
||||
object Ping: CommandInterface {
|
||||
override val command = Commands.slash("ping", "봇이 살아있을까요?")
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
override val name = "ping"
|
||||
override val command = Commands.slash(name, "봇이 살아있을까요?")
|
||||
|
||||
override fun run(event: SlashCommandInteractionEvent, bot: JDA) {
|
||||
event.hook.sendMessage("${event.user.asMention} Pong!").queue()
|
||||
|
@ -0,0 +1,51 @@
|
||||
package space.mori.chzzk_bot.discord.commands
|
||||
|
||||
import net.dv8tion.jda.api.JDA
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
||||
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.OptionData
|
||||
import org.slf4j.LoggerFactory
|
||||
import space.mori.chzzk_bot.chzzk.ChzzkHandler
|
||||
import space.mori.chzzk_bot.chzzk.Connector
|
||||
import space.mori.chzzk_bot.discord.Command
|
||||
import space.mori.chzzk_bot.discord.CommandInterface
|
||||
import space.mori.chzzk_bot.services.UserService
|
||||
|
||||
@Command
|
||||
object Register: CommandInterface {
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
override val name = "register"
|
||||
override val command = Commands.slash(name, "치지직 계정을 등록합니다.")
|
||||
.addOptions(
|
||||
OptionData(
|
||||
OptionType.STRING,
|
||||
"chzzk_id",
|
||||
"36da10b7c35800f298e9c565a396bafd 형식으로 입력해주세요.",
|
||||
true
|
||||
)
|
||||
)
|
||||
|
||||
override fun run(event: SlashCommandInteractionEvent, bot: JDA) {
|
||||
val chzzkID = event.getOption("chzzk_id")?.asString
|
||||
if(chzzkID == null) {
|
||||
event.hook.sendMessage("치지직 계정은 필수 입력입니다.").queue()
|
||||
return
|
||||
}
|
||||
|
||||
val chzzkChannel = Connector.getChannel(chzzkID)
|
||||
if (chzzkChannel == null) {
|
||||
event.hook.sendMessage("치지직 계정을 찾을 수 없습니다.").queue()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
UserService.saveUser(chzzkChannel.channelName, chzzkChannel.channelId, event.user.idLong)
|
||||
ChzzkHandler.addUser(chzzkChannel)
|
||||
event.hook.sendMessage("등록이 완료되었습니다. ${chzzkChannel.channelId} - ${chzzkChannel.channelName}")
|
||||
} catch(e: Exception) {
|
||||
event.hook.sendMessage("에러가 발생했습니다.").queue()
|
||||
logger.debug(e.stackTraceToString())
|
||||
}
|
||||
}
|
||||
}
|
21
src/main/kotlin/space/mori/chzzk_bot/models/Command.kt
Normal file
21
src/main/kotlin/space/mori/chzzk_bot/models/Command.kt
Normal file
@ -0,0 +1,21 @@
|
||||
package space.mori.chzzk_bot.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")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package space.mori.chzzk_bot.services
|
||||
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import space.mori.chzzk_bot.models.Command
|
||||
import space.mori.chzzk_bot.models.Commands
|
||||
import space.mori.chzzk_bot.models.User
|
||||
|
||||
object CommandService {
|
||||
fun saveCommand(user: User, command: String, content: String): Command {
|
||||
return transaction {
|
||||
return@transaction Command.new {
|
||||
this.user = user
|
||||
this.command = command
|
||||
this.content = content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCommand(id: Int): Command? {
|
||||
return transaction {
|
||||
return@transaction Command.findById(id)
|
||||
}
|
||||
}
|
||||
|
||||
fun getCommands(user: User): List<Command> {
|
||||
return transaction {
|
||||
return@transaction Command.find(Commands.user eq user.id)
|
||||
.toList()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +1,46 @@
|
||||
package space.mori.chzzk_bot.services
|
||||
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import space.mori.chzzk_bot.models.User
|
||||
import space.mori.chzzk_bot.models.Users
|
||||
|
||||
class UserService {
|
||||
fun saveUser(user: User) {
|
||||
User.new {
|
||||
username = user.username
|
||||
token = user.token
|
||||
discord = user.discord
|
||||
object UserService {
|
||||
fun saveUser(username: String, token: String, discordID: Long): User {
|
||||
return transaction {
|
||||
return@transaction User.new {
|
||||
this.username = username
|
||||
this.token = token
|
||||
this.discord = discordID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getUser(id: Int): User? {
|
||||
return User.findById(id)
|
||||
return transaction {
|
||||
return@transaction User.findById(id)
|
||||
}
|
||||
}
|
||||
|
||||
fun getUser(discordID: Long): User? {
|
||||
val users = User.find(Users.discord eq discordID)
|
||||
return transaction {
|
||||
val users = User.find(Users.discord eq discordID)
|
||||
|
||||
return users.firstOrNull()
|
||||
return@transaction users.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
fun getUser(chzzkID: String): User? {
|
||||
return transaction {
|
||||
val users = User.find(Users.token eq chzzkID)
|
||||
|
||||
return@transaction users.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllUsers(): List<User> {
|
||||
return transaction {
|
||||
return@transaction User.all().toList()
|
||||
}
|
||||
}
|
||||
}
|
27
src/main/resources/logback.xml
Normal file
27
src/main/resources/logback.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<configuration>
|
||||
|
||||
<!-- 콘솔에 출력하는 기본 로그 설정 -->
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- HikariCP 로그 레벨 설정 -->
|
||||
<logger name="com.zaxxer.hikari" level="INFO" />
|
||||
<logger name="com.zaxxer.hikari.HikariConfig" level="WARN" />
|
||||
<logger name="com.zaxxer.hikari.pool.PoolBase" level="WARN" />
|
||||
<logger name="com.zaxxer.hikari.pool.HikariPool" level="WARN" />
|
||||
<logger name="com.zaxxer.hikari.util.DriverDataSource" level="WARN" />
|
||||
|
||||
<!-- Exposed 로그 레벨 설정 -->
|
||||
<logger name="org.jetbrains.exposed" level="INFO" />
|
||||
<logger name="org.jetbrains.exposed.sql" level="WARN" />
|
||||
<logger name="org.jetbrains.exposed.sql.transactions" level="WARN" />
|
||||
|
||||
<!-- 루트 로거 설정 -->
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
Loading…
x
Reference in New Issue
Block a user