83 Commits

Author SHA1 Message Date
JinU Choi
1d23ac5121 Merge pull request #10 from dalbodeule/develop
BUG FIX: fix #9
2024-07-29 16:01:30 +09:00
dalbodeule
8f8d2f895a BUG FIX: fix #9 2024-07-29 15:58:50 +09:00
JinU Choi
47b4b8252f Merge pull request #8 from dalbodeule/develop
BUG FIX: daily counter with update updated_at column
2024-07-27 21:09:48 +09:00
dalbodeule
64880318e8 BUG FIX: daily counter with update updated_at column 2024-07-27 21:06:56 +09:00
JinU Choi
cec07f9859 Merge pull request #7 from dalbodeule/develop
add guild update, update document, update version to 1.0.1
2024-07-27 11:38:26 +09:00
dalbodeule
18ee27e567 add guild update, update document, update version to 1.0.1 2024-07-27 11:36:17 +09:00
JinU Choi
34ec0f29ff Merge pull request #6 from dalbodeule/develop
v1.0.0

- add managers (in discord)
- manager can add/remove/modify commands
- update some packages.
2024-07-27 10:23:50 +09:00
dalbodeule
2b999c5acb dockerfile update 2024-07-27 10:20:32 +09:00
dalbodeule
c669943f87 manager can add/update/delete commands 2024-07-27 10:13:33 +09:00
dalbodeule
efaaf5ae9f update library versions 2024-07-27 09:54:22 +09:00
JinU Choi
c8fca6995c Merge pull request #5 from dalbodeule/develop
Add discord managers.
2024-07-26 20:28:46 +09:00
dalbodeule
903f1f7f38 add Manager add/remove/list command, database tables. 2024-07-26 20:24:37 +09:00
dalbodeule
e4b6f819ca some improve MessageHandler.kt 2024-07-26 19:20:10 +09:00
JinU Choi
2c1f42e4af Merge pull request #4 from dalbodeule/develop
add days placeholder
2024-07-23 17:47:44 +09:00
dalbodeule
ff90e5fe7f add days placeholder
- days placeholder <days:yyyy-mm-dd> pattern added.
2024-07-23 17:44:28 +09:00
JinU Choi
a227dda930 Merge pull request #3 from dalbodeule/develop
fix README.md (with commands, placeholders, stacks)
2024-07-22 10:53:24 +09:00
dalbodeule
3e09989202 fix README.md (with commands, placeholders, stacks) 2024-07-22 10:45:52 +09:00
dalbodeule
eddca6dbab fix README.md 2024-07-19 13:30:45 +09:00
dalbodeule
9c48dd676e remove github ci/cd 2024-07-18 23:17:32 +09:00
dalbodeule
779604d21e update README.md 2024-07-18 23:00:36 +09:00
dalbodeule
83791fe59b remove useless prints. 2024-07-15 10:16:12 +09:00
dalbodeule
fe63a4af15 chzzk followdate fix 2024-07-14 19:47:12 +09:00
dalbodeule
7522ec6f9e chzzk followdate error handler add 2024-07-14 18:37:52 +09:00
dalbodeule
c35b3082cc log debug to info. level changed 2024-07-05 14:06:31 +09:00
dalbodeule
19d3f23cd7 chzzk livestream status handler fix 2024-07-03 22:33:25 +09:00
dalbodeule
80d777dad5 chzzkChat only connected on live 2024-07-02 10:04:23 +09:00
dalbodeule
d2071b323e register command in chat some fix (4x) 2024-06-29 21:58:50 +09:00
dalbodeule
f2b30c8b00 register command in chat some fix (3x) 2024-06-29 21:44:18 +09:00
dalbodeule
947e6d4bb3 register command in chat some fix (2x) 2024-06-29 21:38:19 +09:00
dalbodeule
43b6869100 register command in chat some fix 2024-06-29 21:11:19 +09:00
dalbodeule
ba12fd655b register discord command bug fix 2024-06-24 19:41:48 +09:00
dalbodeule
b1d69e90ef add command manage commands. 2024-06-24 19:06:24 +09:00
dalbodeule
a9aa5188f9 if stream end, send message and can't use commands.(live image url fix) 2024-06-17 19:43:19 +09:00
dalbodeule
e9a2e6b918 fix some bugs 6(live image url fix) 2024-06-17 18:21:47 +09:00
dalbodeule
035a6dc6cd fix some bugs 5(alert final fix) 2024-06-17 18:19:40 +09:00
dalbodeule
a774418259 fix some bugs 4(change useragent) 2024-06-17 17:53:27 +09:00
dalbodeule
e31efc0212 fix some bugs 3(streamInfo add errorhandler) 2024-06-17 17:35:48 +09:00
dalbodeule
eae675eaf6 fix some bugs 2(streamInfo get time fix) 2024-06-17 17:02:43 +09:00
dalbodeule
be84a73828 fix some bugs 2024-06-17 16:47:11 +09:00
dalbodeule
fbb0e50379 add alert commands 2024-06-17 16:21:34 +09:00
dalbodeule
3f60348ace stream info add 2024-06-17 16:10:34 +09:00
dalbodeule
09bb485a13 stream info(getStreamInfo fun) add 2024-06-16 22:07:15 +09:00
dalbodeule
c22c70398f follow period message fix done. 2024-06-16 13:25:05 +09:00
dalbodeule
8af0c3ac33 follow period message handler end. 2024-06-16 12:38:05 +09:00
dalbodeule
557600f812 follow period message handler add.
but not yet activated...
2024-06-14 16:03:09 +09:00
dalbodeule
e85561dd74 async connect... 2024-06-14 13:58:25 +09:00
dalbodeule
1a1b02506f arm7 give up 2024-06-14 13:02:33 +09:00
dalbodeule
5a3cdbd45a give up graalvm native image. 2024-06-14 12:40:30 +09:00
dalbodeule
ceb730a933 command error fix(enable http/https) 2024-06-14 10:28:08 +09:00
dalbodeule
f795e51845 dotenv error fix(ignoreIfMissing=true) 2024-06-14 01:44:54 +09:00
dalbodeule
9edf2d44ee fix build.yml (x22) 2024-06-14 01:33:38 +09:00
dalbodeule
0cd8a274e0 fix build.yml (x21) 2024-06-14 01:03:25 +09:00
dalbodeule
a98758532f fix build.yml (x20) 2024-06-14 00:46:43 +09:00
dalbodeule
22f47737df fix build.yml (x19) 2024-06-14 00:20:01 +09:00
dalbodeule
f5a97348be fix build.yml (x18) 2024-06-14 00:15:09 +09:00
dalbodeule
152752218b fix build.yml (x17) 2024-06-14 00:07:34 +09:00
dalbodeule
618a31c121 fix build.yml (x16) 2024-06-14 00:05:18 +09:00
dalbodeule
d85abcbe43 fix build.yml (x15) 2024-06-13 23:56:32 +09:00
dalbodeule
8675d0bd85 fix build.yml (x14) 2024-06-13 23:52:05 +09:00
dalbodeule
1c52466024 fix build.yml (x13) 2024-06-13 23:51:37 +09:00
dalbodeule
87f8cb0248 fix build.yml (x12) 2024-06-13 23:48:01 +09:00
dalbodeule
ea05474deb fix build.yml (x11) 2024-06-13 23:45:01 +09:00
dalbodeule
3c5ce73734 fix build.yml (x10) 2024-06-13 23:42:55 +09:00
dalbodeule
a65772f575 fix build.yml (x9) 2024-06-13 22:38:00 +09:00
dalbodeule
0481681dd4 fix build.yml (x8) 2024-06-13 22:35:30 +09:00
dalbodeule
d717b297f3 fix build.yml (x7) 2024-06-13 20:06:22 +09:00
dalbodeule
74645ee5da fix build.yml (x6) 2024-06-13 20:05:11 +09:00
dalbodeule
77545c3b74 fix build.yml (x5) 2024-06-13 20:02:23 +09:00
dalbodeule
9a2e14ad21 fix build.yml (x4) 2024-06-13 19:43:09 +09:00
dalbodeule
d9c7636ec0 fix build.yml (x3) 2024-06-13 19:40:57 +09:00
dalbodeule
be4b9ff3ec fix build.yml (x2) 2024-06-13 19:32:08 +09:00
dalbodeule
d4497b5a13 fix dockerfile again (x1) 2024-06-13 19:24:35 +09:00
dalbodeule
20f6d84040 add counter, counter handlers 2024-06-13 14:43:49 +09:00
dalbodeule
4da72f194e fix dockerfile again(x6) 2024-06-13 02:23:35 +09:00
dalbodeule
acfa2b4b02 fix dockerfile again(x5) 2024-06-13 02:11:30 +09:00
dalbodeule
3e398ba840 fix dockerfile again(x4) 2024-06-13 01:59:17 +09:00
dalbodeule
54edd46f71 fix dockerfile again(x3) 2024-06-13 01:55:32 +09:00
dalbodeule
7ad3013d47 fix dockerfile again(x2) 2024-06-13 01:53:09 +09:00
dalbodeule
94fa1eff61 fix dockerfile 2024-06-13 01:46:39 +09:00
dalbodeule
907b8e8d2a fix github workflows with remove arm7 2024-06-13 01:40:25 +09:00
dalbodeule
086e7f9392 fix github workflows with datetime 2024-06-13 01:38:19 +09:00
dalbodeule
5cc376f22f add github workflows 2024-06-13 01:33:30 +09:00
dalbodeule
639ed5faed add dockerfile, add get-metadata.sh, docker-build.sh 2024-06-13 01:33:11 +09:00
41 changed files with 1172 additions and 715 deletions

9
.idea/dataSources.xml generated
View File

@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true"> <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="@localhost" uuid="90f8ee11-600e-4155-a316-e8062c7c828b"> <data-source source="LOCAL" name="@prod" uuid="90f8ee11-600e-4155-a316-e8062c7c828b">
<driver-ref>mysql.8</driver-ref> <driver-ref>mariadb</driver-ref>
<synchronize>true</synchronize> <synchronize>true</synchronize>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver> <jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://localhost:3306</jdbc-url> <jdbc-url>jdbc:mariadb://localhost:3306</jdbc-url>
<jdbc-additional-properties> <jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" /> <property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" /> <property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.resource.type" value="Deployment" />
<property name="com.intellij.clouds.kubernetes.db.container.port" /> <property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties> </jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir> <working-dir>$ProjectFileDir$</working-dir>

10
Dockerfile Normal file
View File

@@ -0,0 +1,10 @@
# Use a base image with JDK 21 for the final image
FROM openjdk:21-jdk
WORKDIR /app
# Copy the JAR file from the TeamCity build artifacts
COPY build/libs/chzzk_bot-*.jar app.jar
# Set the entry point
ENTRYPOINT ["java", "-jar", "app.jar"]

View File

@@ -1,3 +1,72 @@
# maybe_chzzk_bot_kotlin # nabot_chzzk_bot
[discord](https://discord.gg/up8ANZegmy) [![Discord](https://img.shields.io/discord/1250093195870867577)](https://discord.gg/up8ANZegmy)&nbsp; &nbsp;[![Build Status](https://teamcity.mori.space/app/rest/builds/buildType:NabotChzzkBot_Build/statusIcon)](https://teamcity.mori.space/project/NabotChzzkBot)&nbsp; &nbsp;[![Docker Image Version](https://img.shields.io/docker/v/dalbodeule/chzzkbot)](https://hub.docker.com/repository/docker/dalbodeule/chzzkbot/general)
## Chzzk Chatbot with [JDA5](https://github.com/discord-jda/JDA), [chzzk4j](https://github.com/R2turnTrue/chzzk4j)
네이버 게임스트리밍 플랫폼 치지직의 챗봇입니다.
## 지원 기능
### Placeholders
- [x] \<name>
- [x] \<following>
- [x] \<counter:counter_name>
- [x] \<personal_counter:counter_name>
- [x] \<daily_counter:counter_name>
- [x] \<days:yyyy-mm-dd>
### 관리 명령어 (on Discord)
- [x] /register chzzk_id: \[치지직 고유ID]
- [x] /alert channel: \[디스코드 Channel ID] content: \[알림 내용]
- [x] /add label: \[명령어] content: \[내용]
- [ ] /list
- [x] /update label: \[명령어] content: \[내용]
- [x] /delete label: \[명령어]
### 매니저 명령어 (on Discord)
- [x] /addmanager user: \[Discord user]
- [x] /listmanager
- [x] /removemanager user: \[Discord user]
### 관리 명령어 (on Chzzk chat)
- [x] !명령어추가 \[명령어] \[내용]
- [x] !명령어수정 \[명령어] \[내용]
- [x] !명령어삭제 \[명령어]
### Envs
- DISCORD_TOKEN
- DB_URL
- DB_USER
- DB_PASS
- RUN_AGENT = `false`
- NID_AUT
- NID_SES
### 사용 예시
- 팔로우
- `/add label: !팔로우 content: <name>님은 오늘로 <following>일째 팔로우네요!`
- 출첵
- `/add label: !출첵 content: <name>님의 <daily_counter:attendance>번째 출석! fail_content: <name>님은 오늘 이미 출석했어요! <daily_counter:attendance>번 했네요?`
- `/add label: ? content: <name>님이 <counter:hook>개째 갈고리 수집`
- ㄱㅇㅇ
- `/add label: ㄱㅇㅇ content: <counter:cute>번째 ㄱㅇㅇ`
- `/add label: ㄱㅇㅇ content: 나누 귀여움 +<counter:cute>`
-
- `/add label: 풉 content: <counter:poop>번째 비웃음?`
- `/add label: 풉키풉키 content: <counter:poop>번째 비웃음?`
- 바보
- `/add label: 바보 content: 나 바보 아니다?`
- `/add label: 바보 content: <counter:fool> 번째 바보? 나 바보 아니다?`
- 첫방송
- `/add label: 첫방송 content: 24년 7월 23일부터 <days:2024-07-23>일 째 방송중!`
## 사용 기술스택
- [Exposed](https://github.com/JetBrains/Exposed)
- [Kotlin](https://github.com/JetBrains/kotlin)
- [JDA5](https://github.com/discord-jda/JDA)
- [chzzk4j](https://github.com/R2turnTrue/chzzk4j)
- [HikariCP](https://github.com/brettwooldridge/HikariCP)
- [gson](https://github.com/google/gson)
- [mariadb](https://mariadb.org/)
- [docker](https://www.docker.com/)
- [Teamcity](https://www.jetbrains.com/teamcity/)

View File

@@ -23,52 +23,29 @@ application {
mainClass.set("${"${project.group}.${project.name}".lowercase()}.MainKt") mainClass.set("${"${project.group}.${project.name}".lowercase()}.MainKt")
} }
graalvmNative {
agent {
trackReflectionMetadata.set(true)
metadataCopy {
outputDirectories.add("src/main/resources/META-INF/native-image")
mergeWithExisting.set(true)
}
}
binaries {
binaries.all {
resources.autodetect()
}
named("main") {
useFatJar.set(true)
sharedLibrary.set(false)
buildArgs.add("--initialize-at-build-time=org.hibernate.*")
}
}
metadataRepository {
enabled.set(true)
}
}
repositories { repositories {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
// https://mvnrepository.com/artifact/net.dv8tion/JDA // https://mvnrepository.com/artifact/net.dv8tion/JDA
implementation("net.dv8tion:JDA:5.0.0-beta.24") { implementation("net.dv8tion:JDA:5.0.1") {
exclude(module = "opus-java") exclude(module = "opus-java")
} }
// https://mvnrepository.com/artifact/io.github.R2turnTrue/chzzk4j // https://mvnrepository.com/artifact/io.github.R2turnTrue/chzzk4j
implementation("io.github.R2turnTrue:chzzk4j:0.0.7") implementation("io.github.R2turnTrue:chzzk4j:0.0.9")
implementation("ch.qos.logback:logback-classic:1.4.14") // https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
implementation("ch.qos.logback:logback-classic:1.5.6")
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-core // https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-core
implementation("org.jetbrains.exposed:exposed-core:0.51.1") implementation("org.jetbrains.exposed:exposed-core:0.52.0")
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-dao // https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-dao
implementation("org.jetbrains.exposed:exposed-dao:0.51.1") implementation("org.jetbrains.exposed:exposed-dao:0.52.0")
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-jdbc // https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-jdbc
runtimeOnly("org.jetbrains.exposed:exposed-jdbc:0.51.1") implementation("org.jetbrains.exposed:exposed-jdbc:0.52.0")
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-kotlin-datetime // https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-kotlin-datetime
runtimeOnly("org.jetbrains.exposed:exposed-kotlin-datetime:0.51.1") implementation("org.jetbrains.exposed:exposed-java-time:0.52.0")
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP // https://mvnrepository.com/artifact/com.zaxxer/HikariCP
implementation("com.zaxxer:HikariCP:5.1.0") implementation("com.zaxxer:HikariCP:5.1.0")
@@ -77,12 +54,14 @@ dependencies {
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
implementation("org.jetbrains.kotlin:kotlin-reflect:2.0.0") implementation("org.jetbrains.kotlin:kotlin-reflect:2.0.0")
// https://mvnrepository.com/artifact/org.reflections/reflections
implementation("org.reflections:reflections:0.10.2") // https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation("com.google.code.gson:gson:2.11.0")
// https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client // https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client
implementation("org.mariadb.jdbc:mariadb-java-client:3.4.0") 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") implementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
kotlin("stdlib") kotlin("stdlib")
@@ -102,3 +81,7 @@ tasks.withType<Jar> {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE duplicatesStrategy = DuplicatesStrategy.EXCLUDE
} }
tasks.named<JavaExec>("run") {
systemProperty("logback.configurationFile", "logback-debug.xml")
}

4
docker-build.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
current_time=$(date +"%Y%m%d%H%M")
docker build -t dalbodeule/chzzkbot:latest -t dalbodeule/chzzkbot:$current_time --push .

View File

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

View File

@@ -2,18 +2,13 @@ package space.mori.chzzk_bot
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 org.slf4j.LoggerFactory import space.mori.chzzk_bot.models.*
import space.mori.chzzk_bot.models.Commands
import space.mori.chzzk_bot.models.Users
object Connector { object Connector {
private val dotenv = dotenv() private val hikariConfig = HikariConfig().apply {
val hikariConfig = HikariConfig().apply {
jdbcUrl = dotenv["DB_URL"] jdbcUrl = dotenv["DB_URL"]
driverClassName = "org.mariadb.jdbc.Driver" driverClassName = "org.mariadb.jdbc.Driver"
username = dotenv["DB_USER"] username = dotenv["DB_USER"]
@@ -24,12 +19,10 @@ object Connector {
init { init {
Database.connect(dataSource) Database.connect(dataSource)
val tables = listOf(Users, Commands) val tables = listOf(Users, Commands, Counters, DailyCounters, PersonalCounters, Managers)
transaction { transaction {
tables.forEach { table -> SchemaUtils.createMissingTablesAndColumns(* tables.toTypedArray())
SchemaUtils.createMissingTablesAndColumns(table)
}
} }
} }
} }

View File

@@ -9,29 +9,34 @@ import space.mori.chzzk_bot.chzzk.ChzzkHandler
import space.mori.chzzk_bot.discord.Discord import space.mori.chzzk_bot.discord.Discord
import space.mori.chzzk_bot.chzzk.Connector as ChzzkConnector import space.mori.chzzk_bot.chzzk.Connector as ChzzkConnector
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.system.exitProcess
val dotenv = dotenv() val dotenv = dotenv {
ignoreIfMissing = true
}
val logger: Logger = LoggerFactory.getLogger("main") val logger: Logger = LoggerFactory.getLogger("main")
val discord = Discord()
val connector = Connector
val chzzkConnector = ChzzkConnector
val chzzkHandler = ChzzkHandler
fun main(args: Array<String>) { fun main(args: Array<String>) {
val discord = Discord()
val connector = Connector
val chzzkConnector = ChzzkConnector
val chzzkHandler = ChzzkHandler
discord.enable() discord.enable()
chzzkHandler.enable() chzzkHandler.enable()
chzzkHandler.runStreamInfo()
if(dotenv.get("RUN_AGENT", "false").toBoolean()) { if(dotenv.get("RUN_AGENT", "false").toBoolean()) {
runBlocking { runBlocking {
delay(TimeUnit.MINUTES.toMillis(1)) delay(TimeUnit.MINUTES.toMillis(1))
discord.disable() exitProcess(0)
} }
} }
Runtime.getRuntime().addShutdownHook(Thread { Runtime.getRuntime().addShutdownHook(Thread {
logger.info("Shutting down...") logger.info("Shutting down...")
chzzkHandler.stopStreamInfo()
chzzkHandler.disable() chzzkHandler.disable()
discord.disable() discord.disable()
connector.dataSource.close() connector.dataSource.close()

View File

@@ -0,0 +1,148 @@
package space.mori.chzzk_bot.chzzk
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.IOException
data class IData<T>(
val code: Int = 200,
val message: String? = null,
val content: T
)
// Follows
data class IFollowContent(
val userIdHash: String = "",
val nickname: String = "",
val profileImageUrl: String = "",
val userRoleCode: String = "",
val badge: Badge? = null,
val title: Title? = null,
val verifiedMark: Boolean = false,
val activityBadges: List<Badge> = emptyList(),
val streamingProperty: StreamingProperty = StreamingProperty()
)
data class Badge(
val badgeNo: Int?,
val badgeId: String?,
val imageUrl: String?,
val title: String?,
val description: String?,
val activated: Boolean?
)
data class Title(
val name: String = "",
val color: String = ""
)
data class StreamingProperty(
val following: Following? = Following(),
val nicknameColor: NicknameColor = NicknameColor()
)
data class Following(
val followDate: String? = null
)
data class NicknameColor(
val colorCode: String = ""
)
// Stream info
data class IStreamInfo(
val liveId: Int = 0,
val liveTitle: String = "",
val status: String = "",
val liveImageUrl: String = "",
val defaultThumbnailImageUrl: String? = null,
val concurrentUserCount: Int = 0,
val accumulateCount: Int = 0,
val openDate: String = "",
val closeDate: String = "",
val adult: Boolean = false,
val clipActive: Boolean = false,
val tags: List<String> = emptyList(),
val chatChannelId: String = "",
val categoryType: String = "",
val liveCategory: String = "",
val liveCategoryValue: String = "",
val chatActive: Boolean = true,
val chatAvailableGroup: String = "",
val paidPromotion: Boolean = false,
val chatAvailableCondition: String = "",
val minFollowerMinute: Int = 0,
val livePlaybackJson: String = "",
val p2pQuality: List<Any> = emptyList(),
val channel: Channel = Channel(),
val livePollingStatusJson: String = "",
val userAdultStatus: String? = null,
val chatDonationRankingExposure: Boolean = true,
val adParameter: AdParameter = AdParameter()
)
data class Channel(
val channelId: String = "",
val channelName: String = "",
val channelImageUrl: String = "",
val verifiedMark: Boolean = false
)
data class AdParameter(
val tag: String = ""
)
// OkHttpClient에 Interceptor 추가
val client = OkHttpClient.Builder()
.addNetworkInterceptor { chain ->
chain.proceed(
chain.request()
.newBuilder()
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
.build()
)
}
.build()
val gson = Gson()
fun getFollowDate(chatID: String, userId: String) : IData<IFollowContent> {
val url = "https://comm-api.game.naver.com/nng_main/v1/chats/$chatID/users/$userId/profile-card?chatType=STREAMING"
val request = Request.Builder()
.url(url)
.build()
client.newCall(request).execute().use { response ->
try {
if(!response.isSuccessful) throw IOException("Unexpected code ${response.code}")
val body = response.body?.string()
val follow = gson.fromJson(body, object: TypeToken<IData<IFollowContent>>() {})
return follow
} catch(e: Exception) {
println(e.stackTrace)
throw e
}
}
}
fun getStreamInfo(userId: String) : IData<IStreamInfo> {
val url = "https://api.chzzk.naver.com/service/v2/channels/${userId}/live-detail"
val request = Request.Builder()
.url(url)
.build()
client.newCall(request).execute().use { response ->
try {
if(!response.isSuccessful) throw IOException("Unexpected code ${response.code}")
val body = response.body?.string()
val follow = gson.fromJson(body, object: TypeToken<IData<IStreamInfo>>() {})
return follow
} catch(e: Exception) {
throw e
}
}
}

View File

@@ -1,26 +1,33 @@
package space.mori.chzzk_bot.chzzk package space.mori.chzzk_bot.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.chzzk.Connector.chzzk
import space.mori.chzzk_bot.discord
import space.mori.chzzk_bot.models.User
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.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.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
internal fun addUser(chzzkChannel: ChzzkChannel) { internal fun addUser(chzzkChannel: ChzzkChannel, user: User) {
handlers.add(UserHandler(chzzkChannel, logger)) handlers.add(UserHandler(chzzkChannel, logger, user))
} }
internal fun enable() { internal fun enable() {
UserService.getAllUsers().map { UserService.getAllUsers().map {
chzzk.getChannel(it.token)?.let { token -> addUser(token)} chzzk.getChannel(it.token)?.let { token -> addUser(token, it)}
} }
} }
@@ -31,15 +38,57 @@ object ChzzkHandler {
} }
internal fun reloadCommand(chzzkChannel: ChzzkChannel) { internal fun reloadCommand(chzzkChannel: ChzzkChannel) {
val handler = handlers.firstOrNull { it.channel == chzzkChannel } val handler = handlers.firstOrNull { it.channel.channelId == chzzkChannel.channelId }
if (handler != null) if (handler != null)
handler.reloadCommand() handler.reloadCommand()
else else
throw RuntimeException("${chzzkChannel.channelName} doesn't have handler") throw RuntimeException("${chzzkChannel.channelName} doesn't have handler")
} }
internal fun reloadUser(chzzkChannel: ChzzkChannel, user: User) {
val handler = handlers.firstOrNull { it.channel.channelId == chzzkChannel.channelId }
if (handler != null)
handler.reloadUser(user)
else
throw RuntimeException("${chzzkChannel.channelName} doesn't have handler")
}
internal fun runStreamInfo() {
running = true
val thread = Thread({
while(running) {
handlers.forEach {
if (!running) return@forEach
try {
val streamInfo = getStreamInfo(it.channel.channelId)
if (streamInfo.content.status == "OPEN" && !it.isActive) it.isActive(true, streamInfo)
if (streamInfo.content.status == "CLOSE" && it.isActive) it.isActive(false, streamInfo)
} catch(e: SocketTimeoutException) {
logger.info("Timeout: ${it.channel.channelName} / ${e.stackTraceToString()}")
} catch (e: Exception) {
logger.info("Exception: ${it.channel.channelName} / ${e.stackTraceToString()}")
} finally {
Thread.sleep(5000)
}
}
Thread.sleep(60000)
}
}, "Chzzk-StreamInfo")
thread.start()
}
internal fun stopStreamInfo() {
running = false
}
} }
class UserHandler(val channel: ChzzkChannel, private val logger: Logger) { class UserHandler(
val channel: ChzzkChannel,
private val logger: Logger,
private var user: User,
private var _isActive: Boolean = false
) {
private lateinit var messageHandler: MessageHandler private lateinit var messageHandler: MessageHandler
private var listener: ChzzkChat = chzzk.chat(channel.channelId) private var listener: ChzzkChat = chzzk.chat(channel.channelId)
@@ -56,7 +105,8 @@ class UserHandler(val channel: ChzzkChannel, private val logger: Logger) {
} }
override fun onChat(msg: ChatMessage) { override fun onChat(msg: ChatMessage) {
messageHandler.handle(msg) if(!_isActive) return
messageHandler.handle(msg, user)
} }
override fun onConnectionClosed(code: Int, reason: String?, remote: Boolean, tryingToReconnect: Boolean) { override fun onConnectionClosed(code: Int, reason: String?, remote: Boolean, tryingToReconnect: Boolean) {
@@ -66,16 +116,54 @@ class UserHandler(val channel: ChzzkChannel, private val logger: Logger) {
}) })
.build() .build()
init {
logger.info("ChzzkChat connecting... ${channel.channelName} - ${channel.channelId}")
listener.connectBlocking()
}
internal fun disable() { internal fun disable() {
listener.closeBlocking() listener.closeAsync()
} }
internal fun reloadCommand() { internal fun reloadCommand() {
messageHandler.reloadCommand() messageHandler.reloadCommand()
} }
internal fun reloadUser(user: User) {
this.user = user
}
internal val isActive: Boolean
get() = _isActive
internal fun isActive(value: Boolean, status: IData<IStreamInfo>) {
_isActive = value
if(value) {
logger.info("${user.username} is live.")
logger.info("ChzzkChat connecting... ${channel.channelName} - ${channel.channelId}")
listener.connectAsync()
if(user.liveAlertMessage != "" && user.liveAlertGuild != null && user.liveAlertChannel != null) {
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} 님의 방송이 감지되었습니다.")
}
} else {
logger.info("${user.username} is offline.")
listener.closeAsync()
}
}
} }

View File

@@ -1,13 +1,12 @@
package space.mori.chzzk_bot.chzzk package space.mori.chzzk_bot.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
object Connector { object Connector {
private val dotenv = dotenv()
val chzzk: Chzzk = ChzzkBuilder() val chzzk: Chzzk = ChzzkBuilder()
.withAuthorization(dotenv["NID_AUT"], dotenv["NID_SES"]) .withAuthorization(dotenv["NID_AUT"], dotenv["NID_SES"])
.build() .build()

View File

@@ -1,18 +1,31 @@
package space.mori.chzzk_bot.chzzk package space.mori.chzzk_bot.chzzk
import org.slf4j.Logger import org.slf4j.Logger
import space.mori.chzzk_bot.models.User
import space.mori.chzzk_bot.services.CommandService import space.mori.chzzk_bot.services.CommandService
import space.mori.chzzk_bot.services.CounterService
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.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
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
class MessageHandler( class MessageHandler(
private val channel: ChzzkChannel, private val channel: ChzzkChannel,
private val logger: Logger, private val logger: Logger,
private val listener: ChzzkChat private val listener: ChzzkChat
) { ) {
private val commands = mutableMapOf<String, () -> Unit>() private val commands = mutableMapOf<String, (msg: ChatMessage, user: User) -> Unit>()
private val counterPattern = Regex("<counter:([^>]+)>")
private val personalCounterPattern = Regex("<counter_personal:([^>]+)>")
private val dailyCounterPattern = Regex("<daily_counter:([^>]+)>")
private val namePattern = Regex("<name>")
private val followPattern = Regex("<following>")
private val daysPattern = """<days:(\d{4})-(\d{2})-(\d{2})>""".toRegex()
init { init {
reloadCommand() reloadCommand()
@@ -22,18 +35,159 @@ class MessageHandler(
val user = UserService.getUser(channel.channelId) val user = UserService.getUser(channel.channelId)
?: throw RuntimeException("User not found. it's bug? ${channel.channelName} - ${channel.channelId}") ?: throw RuntimeException("User not found. it's bug? ${channel.channelName} - ${channel.channelId}")
val commands = CommandService.getCommands(user) val commands = CommandService.getCommands(user)
val manageCommands = mapOf("!명령어추가" to this::manageAddCommand, "!명령어삭제" to this::manageRemoveCommand, "!명령어수정" to this::manageUpdateCommand)
manageCommands.forEach { (commandName, command) ->
this.commands[commandName] = command
}
commands.map { commands.map {
this.commands.put(it.command.lowercase()) { this.commands.put(it.command.lowercase()) { msg, user ->
logger.debug("${channel.channelName} - ${it.command} - ${it.content}") logger.debug("${channel.channelName} - ${it.command} - ${it.content}/${it.failContent}")
listener.sendChat(it.content)
val result = replaceCounters(Pair(it.content, it.failContent), user, msg, listener, msg.profile?.nickname ?: "")
listener.sendChat(result)
} }
} }
} }
internal fun handle(msg: ChatMessage) { private fun manageAddCommand(msg: ChatMessage, user: User) {
if (msg.profile?.userRoleCode == "common_user") {
listener.sendChat("매니저만 명령어를 추가할 수 있습니다.")
return
}
val parts = msg.content.split(" ", limit = 3)
if (parts.size < 3) {
listener.sendChat("명령어 추가 형식은 '!명령어추가 명령어 내용'입니다.")
return
}
if (commands.containsKey(parts[1])) {
listener.sendChat("${parts[1]} 명령어는 이미 있는 명령어입니다.")
return
}
val command = parts[1]
val content = parts[2]
CommandService.saveCommand(user, command, content, "")
listener.sendChat("명령어 '$command' 추가되었습니다.")
}
private fun manageUpdateCommand(msg: ChatMessage, user: User) {
if (msg.profile?.userRoleCode == "common_user") {
listener.sendChat("매니저만 명령어를 추가할 수 있습니다.")
return
}
val parts = msg.content.split(" ", limit = 3)
if (parts.size < 3) {
listener.sendChat("명령어 수정 형식은 '!명령어수정 명령어 내용'입니다.")
return
}
if (!commands.containsKey(parts[1])) {
listener.sendChat("${parts[1]} 명령어는 없는 명령어입니다.")
return
}
val command = parts[1]
val content = parts[2]
CommandService.updateCommand(user, command, content, "")
listener.sendChat("명령어 '$command' 수정되었습니다.")
}
private fun manageRemoveCommand(msg: ChatMessage, user: User) {
if (msg.profile?.userRoleCode == "common_user") {
listener.sendChat("매니저만 명령어를 삭제할 수 있습니다.")
return
}
val parts = msg.content.split(" ", limit = 2)
if (parts.size < 2) {
listener.sendChat("명령어 삭제 형식은 '!명령어삭제 명령어'입니다.")
return
}
val command = parts[1]
CommandService.removeCommand(user, command)
listener.sendChat("명령어 '$command' 삭제되었습니다.")
}
internal fun handle(msg: ChatMessage, user: User) {
val commandKey = msg.content.split(' ')[0] val commandKey = msg.content.split(' ')[0]
commands[commandKey.lowercase()]?.let { it() } commands[commandKey.lowercase()]?.let { it(msg, user) }
} }
private fun replaceCounters(chat: Pair<String, String>, user: User, msg: ChatMessage, listener: ChzzkChat, userName: String): String {
var result = chat.first
var isFail = false
// Replace dailyCounterPattern
result = dailyCounterPattern.replace(result) { matchResult ->
val name = matchResult.groupValues[1]
val dailyCounter = CounterService.getDailyCounterValue(name, msg.userId, user)
if (dailyCounter.second) {
CounterService.updateDailyCounterValue(name, msg.userId, 1, user).first.toString()
} else {
isFail = true
dailyCounter.first.toString()
}
}
// Handle fail case
if (isFail && chat.second.isNotEmpty()) {
result = chat.second
result = dailyCounterPattern.replace(result) { matchResult ->
val name = matchResult.groupValues[1]
val dailyCounter = CounterService.getDailyCounterValue(name, msg.userId, user)
dailyCounter.first.toString()
}
}
// Replace followPattern
result = followPattern.replace(result) { matchResult ->
try {
val followingDate = getFollowDate(listener.chatId, msg.userId)
.content.streamingProperty.following?.followDate
val period = followingDate?.let {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
val pastDate = LocalDateTime.parse(it, formatter)
val today = LocalDateTime.now()
ChronoUnit.DAYS.between(pastDate, today)
} ?: 0
period.toString()
} catch (e: Exception) {
logger.error(e.message)
"0"
}
}
// Replace daysPattern
result = daysPattern.replace(result) { matchResult ->
val (year, month, day) = matchResult.destructured
val pastDate = LocalDateTime.of(year.toInt(), month.toInt(), day.toInt(), 0, 0, 0)
val today = LocalDateTime.now()
val daysBetween = ChronoUnit.DAYS.between(pastDate, today)
daysBetween.toString()
}
// Replace counterPattern
result = counterPattern.replace(result) { matchResult ->
val name = matchResult.groupValues[1]
CounterService.updateCounterValue(name, 1, user).toString()
}
// Replace personalCounterPattern
result = personalCounterPattern.replace(result) { matchResult ->
val name = matchResult.groupValues[1]
CounterService.updatePersonalCounterValue(name, msg.userId, 1, user).toString()
}
// Replace namePattern
result = namePattern.replace(result, userName)
return result
}
} }

View File

@@ -3,33 +3,9 @@ package space.mori.chzzk_bot.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
import net.dv8tion.jda.api.interactions.commands.build.CommandData import net.dv8tion.jda.api.interactions.commands.build.CommandData
import org.reflections.Reflections
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Command
interface CommandInterface { interface CommandInterface {
val name: String val name: String
fun run(event: SlashCommandInteractionEvent, bot: JDA): Unit fun run(event: SlashCommandInteractionEvent, bot: JDA): Unit
val command: CommandData val command: CommandData
} }
fun getCommands(): List<CommandInterface> {
val commandList = mutableListOf<CommandInterface>()
val packageName = "space.mori.chzzk_bot.discord.commands"
val reflections = Reflections(packageName)
val annotatedClasses = reflections.getTypesAnnotatedWith(Command::class.java)
for(clazz in annotatedClasses) {
val obj = clazz.kotlin.objectInstance
if(obj is CommandInterface) {
commandList.add(obj)
} else {
throw IllegalStateException("${clazz.name} is not a CommandInterface")
}
}
return commandList.toList()
}

View File

@@ -4,21 +4,61 @@ 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
import net.dv8tion.jda.api.entities.Guild import net.dv8tion.jda.api.entities.Guild
import net.dv8tion.jda.api.events.guild.GuildJoinEvent
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 org.slf4j.LoggerFactory
import space.mori.chzzk_bot.dotenv import space.mori.chzzk_bot.dotenv
import org.slf4j.LoggerFactory
import space.mori.chzzk_bot.discord.commands.*
import space.mori.chzzk_bot.services.ManagerService
class Discord: ListenerAdapter() { class Discord: ListenerAdapter() {
private lateinit var bot: JDA 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)
private val commands = getCommands() private val commands = listOf(
AddCommand,
AlertCommand,
PingCommand,
RegisterCommand,
RemoveCommand,
UpdateCommand,
AddManagerCommand,
ListManagerCommand,
RemoveManagerCommand,
)
override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) { override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) {
event.deferReply().queue() event.deferReply().queue()
commands.find { it.name == event.name }?.run(event, bot) val handler = commands.find { it.name == event.name }
logger.debug("Handler: ${handler?.name ?: "undefined"} command")
handler?.run(event, bot)
}
override fun onGuildMemberRemove(event: GuildMemberRemoveEvent) {
event.member?.let { ManagerService.deleteManager(event.guild.idLong, it.idLong) }
}
override fun onGuildJoin(event: GuildJoinEvent) {
commandUpdate(event.guild)
}
private fun commandUpdate(guild: Guild) {
guild.updateCommands().addCommands(* commands.map { it.command}.toTypedArray())
.onSuccess {
logger.info("Command update on guild success!")
}
.queue()
}
private fun commandUpdate(bot: JDA) {
bot.updateCommands().addCommands(* commands.map { it.command}.toTypedArray())
.onSuccess {
logger.info("Command update bot boot success!")
}
.queue()
} }
internal fun enable() { internal fun enable() {
@@ -31,15 +71,9 @@ class Discord: ListenerAdapter() {
guild = bot.getGuildById(dotenv["GUILD_ID"]) guild = bot.getGuildById(dotenv["GUILD_ID"])
bot.updateCommands() commandUpdate(bot)
.addCommands(* commands.map { it.command }.toTypedArray()) bot.guilds.forEach {
.onSuccess { logger.info("Command update success!") } commandUpdate(it)
.queue()
if (guild == null) {
logger.info("No guild found!")
this.disable()
} }
} catch (e: Exception) { } catch (e: Exception) {
logger.info("Could not enable Discord!") logger.info("Could not enable Discord!")
@@ -57,4 +91,7 @@ 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

@@ -8,41 +8,55 @@ 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.chzzk.ChzzkHandler
import space.mori.chzzk_bot.chzzk.Connector 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.discord.CommandInterface
import space.mori.chzzk_bot.services.CommandService import space.mori.chzzk_bot.services.CommandService
import space.mori.chzzk_bot.services.ManagerService
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.services.UserService
@Command
object AddCommand : CommandInterface { object AddCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)
override val name: String = "add" override val name: String = "add"
override val command = Commands.slash(name, "명령어를 추가합니다.") override val command = Commands.slash(name, "명령어를 추가합니다.")
.addOptions(OptionData(OptionType.STRING, "label", "작동할 명령어를 입력하세요.", true)) .addOptions(OptionData(OptionType.STRING, "label", "작동할 명령어를 입력하세요.", true))
.addOptions(OptionData(OptionType.STRING, "content", "표시될 텍스트를 입력하세요.", true)) .addOptions(OptionData(OptionType.STRING, "content", "표시될 텍스트를 입력하세요.", true))
.addOptions(OptionData(OptionType.STRING, "fail_content", "카운터 업데이트 실패시 표시될 텍스트를 입력하세요.", false))
override fun run(event: SlashCommandInteractionEvent, bot: JDA) { override fun run(event: SlashCommandInteractionEvent, bot: JDA) {
val label = event.getOption("label")?.asString val label = event.getOption("label")?.asString
val content = event.getOption("content")?.asString val content = event.getOption("content")?.asString
val failContent = event.getOption("fail_content")?.asString
if(label == null || content == null) { if(label == null || content == null) {
event.hook.sendMessage("명령어와 텍스트는 필수 입력입니다.").queue() event.hook.sendMessage("명령어와 텍스트는 필수 입력입니다.").queue()
return return
} }
val user = UserService.getUser(event.user.idLong) var user = UserService.getUser(event.user.idLong)
if(user == null) { val manager = event.guild?.idLong?.let { ManagerService.getUser(it, event.user.idLong) }
event.hook.sendMessage("치지직 계정을 찾을 수 없습니다.").queue() if(user == null && manager == null) {
event.hook.sendMessage("당신은 이 명령어를 사용할 수 없습니다.").queue()
return return
} }
if (manager != null) {
user = manager.user
ManagerService.updateManager(user, event.user.idLong, event.user.effectiveName)
}
val commands = CommandService.getCommands(user!!)
if (commands.any { it.command == label }) {
event.hook.sendMessage("$label 명령어는 이미 있습니다! 업데이트 명령어를 써주세요.").queue()
return
}
val chzzkChannel = Connector.getChannel(user.token) val chzzkChannel = Connector.getChannel(user.token)
try { try {
CommandService.saveCommand(user, label, content) CommandService.saveCommand(user, label, content, failContent ?: "")
try { try {
ChzzkHandler.reloadCommand(chzzkChannel!!) ChzzkHandler.reloadCommand(chzzkChannel!!)
} catch (_: Exception) {} } catch (_: Exception) {}
event.hook.sendMessage("등록이 완료되었습니다. $label = $content").queue() event.hook.sendMessage("등록이 완료되었습니다. $label = $content/$failContent").queue()
} catch (e: Exception) { } catch (e: Exception) {
event.hook.sendMessage("에러가 발생했습니다.").queue() event.hook.sendMessage("에러가 발생했습니다.").queue()
logger.debug(e.stackTraceToString()) logger.debug(e.stackTraceToString())

View File

@@ -0,0 +1,47 @@
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.discord.CommandInterface
import space.mori.chzzk_bot.services.ManagerService
import space.mori.chzzk_bot.services.UserService
object AddManagerCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java)
override val name: String = "addmanager"
override val command = Commands.slash(name, "매니저를 추가합니다.")
.addOptions(OptionData(OptionType.USER, "user", "추가할 유저를 선택하세요.", true))
override fun run(event: SlashCommandInteractionEvent, bot: JDA) {
val manager = event.getOption("user")?.asUser
if(manager == null) {
event.hook.sendMessage("유저는 필수사항입니다.").queue()
return
}
if(manager.idLong == event.user.idLong) {
event.hook.sendMessage("자신은 매니저로 설정할 수 없습니다.").queue()
return
}
val user = UserService.getUser(event.user.idLong)
if(user == null) {
event.hook.sendMessage("치지직 계정을 찾을 수 없습니다.").queue()
return
}
try {
ManagerService.saveManager(user, manager.idLong, manager.effectiveName)
if(user.liveAlertGuild == null)
UserService.updateLiveAlert(user.id.value, event.guild!!.idLong, event.channelIdLong, "")
event.hook.sendMessage("등록이 완료되었습니다. ${manager.effectiveName}").queue()
} catch (e: Exception) {
event.hook.sendMessage("에러가 발생했습니다.").queue()
logger.debug(e.stackTraceToString())
}
}
}

View File

@@ -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.CommandInterface
import space.mori.chzzk_bot.services.ManagerService
import space.mori.chzzk_bot.services.UserService
object AlertCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java)
override val name: String = "alert"
override val command = Commands.slash(name, "명령어를 추가합니다.")
.addOptions(OptionData(OptionType.CHANNEL, "channel", "알림을 보낼 채널을 입력하세요."))
.addOptions(OptionData(OptionType.STRING, "content", "표시될 텍스트를 입력하세요. 비워두면 알람이 취소됩니다."))
override fun run(event: SlashCommandInteractionEvent, bot: JDA) {
val channel = event.getOption("channel")?.asChannel
val content = event.getOption("content")?.asString
var user = UserService.getUser(event.user.idLong)
val manager = event.guild?.idLong?.let { ManagerService.getUser(it, event.user.idLong) }
if(user == null && manager == null) {
event.hook.sendMessage("당신은 이 명령어를 사용할 수 없습니다.").queue()
return
}
if (manager != null) {
user = manager.user
ManagerService.updateManager(user, event.user.idLong, event.user.effectiveName)
}
val chzzkChannel = Connector.getChannel(user!!.token)
try {
val newUser = UserService.updateLiveAlert(user.id.value, channel?.guild?.idLong ?: 0L, channel?.idLong ?: 0L, content ?: "")
try {
ChzzkHandler.reloadUser(chzzkChannel!!, newUser)
} catch (_: Exception) {}
event.hook.sendMessage("업데이트가 완료되었습니다.").queue()
} catch (e: Exception) {
event.hook.sendMessage("에러가 발생했습니다.").queue()
logger.debug(e.stackTraceToString())
}
}
}

View File

@@ -0,0 +1,44 @@
package space.mori.chzzk_bot.discord.commands
import net.dv8tion.jda.api.EmbedBuilder
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.CommandInterface
import space.mori.chzzk_bot.services.ManagerService
import space.mori.chzzk_bot.services.UserService
object ListManagerCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java)
override val name: String = "listmanager"
override val command = Commands.slash(name, "매니저 목록을 확인합니다.")
override fun run(event: SlashCommandInteractionEvent, bot: JDA) {
try {
val managers = event.guild?.idLong?.let { ManagerService.getAllUsers(it) }
if(managers == null) {
event.channel.sendMessage("여기에서는 사용할 수 없습니다.")
return
}
val user = UserService.getUserWithGuildId(event.guild!!.idLong)
val embed = EmbedBuilder()
embed.setTitle("${user!!.username} 매니저 목록")
embed.setDescription("매니저 목록입니다.")
var idx = 1
managers.forEach {
embed.addField("${idx++}", it.lastUserName ?: it.managerId.toString(), true)
}
event.channel.sendMessageEmbeds(embed.build()).queue()
} catch (e: Exception) {
event.hook.sendMessage("에러가 발생했습니다.").queue()
logger.debug(e.stackTraceToString())
}
}
}

View File

@@ -4,11 +4,9 @@ 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.Command
import space.mori.chzzk_bot.discord.CommandInterface import space.mori.chzzk_bot.discord.CommandInterface
@Command() object PingCommand: CommandInterface {
object Ping: CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)
override val name = "ping" override val name = "ping"
override val command = Commands.slash(name, "봇이 살아있을까요?") override val command = Commands.slash(name, "봇이 살아있을까요?")

View File

@@ -8,12 +8,10 @@ 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.chzzk.ChzzkHandler
import space.mori.chzzk_bot.chzzk.Connector 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.discord.CommandInterface
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.services.UserService
@Command object RegisterCommand: CommandInterface {
object Register: CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)
override val name = "register" override val name = "register"
override val command = Commands.slash(name, "치지직 계정을 등록합니다.") override val command = Commands.slash(name, "치지직 계정을 등록합니다.")
@@ -40,9 +38,9 @@ object Register: CommandInterface {
} }
try { try {
UserService.saveUser(chzzkChannel.channelName, chzzkChannel.channelId, event.user.idLong) val user = UserService.saveUser(chzzkChannel.channelName, chzzkChannel.channelId, event.user.idLong)
ChzzkHandler.addUser(chzzkChannel)
event.hook.sendMessage("등록이 완료되었습니다. `${chzzkChannel.channelId}` - `${chzzkChannel.channelName}`") event.hook.sendMessage("등록이 완료되었습니다. `${chzzkChannel.channelId}` - `${chzzkChannel.channelName}`")
ChzzkHandler.addUser(chzzkChannel, user)
} catch(e: Exception) { } catch(e: Exception) {
event.hook.sendMessage("에러가 발생했습니다.").queue() event.hook.sendMessage("에러가 발생했습니다.").queue()
logger.debug(e.stackTraceToString()) logger.debug(e.stackTraceToString())

View File

@@ -8,12 +8,11 @@ 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.chzzk.ChzzkHandler
import space.mori.chzzk_bot.chzzk.Connector 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.discord.CommandInterface
import space.mori.chzzk_bot.services.CommandService import space.mori.chzzk_bot.services.CommandService
import space.mori.chzzk_bot.services.ManagerService
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.services.UserService
@Command
object RemoveCommand : CommandInterface { object RemoveCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)
override val name: String = "remove" override val name: String = "remove"
@@ -28,12 +27,19 @@ object RemoveCommand : CommandInterface {
return return
} }
val user = UserService.getUser(event.user.idLong) var user = UserService.getUser(event.user.idLong)
if(user == null) { val manager = event.guild?.idLong?.let { ManagerService.getUser(it, event.user.idLong) }
event.hook.sendMessage("치지직 계정을 찾을 수 없습니다.").queue() if(user == null && manager == null) {
event.hook.sendMessage("당신은 이 명령어를 사용할 수 없습니다.").queue()
return return
} }
val chzzkChannel = Connector.getChannel(user.token)
if (manager != null) {
user = manager.user
ManagerService.updateManager(user, event.user.idLong, event.user.effectiveName)
}
val chzzkChannel = Connector.getChannel(user!!.token)
try { try {
CommandService.removeCommand(user, label) CommandService.removeCommand(user, label)

View File

@@ -0,0 +1,49 @@
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.discord.CommandInterface
import space.mori.chzzk_bot.services.ManagerService
import space.mori.chzzk_bot.services.UserService
object RemoveManagerCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java)
override val name: String = "removemanager"
override val command = Commands.slash(name, "매니저를 삭제합니다.")
.addOptions(OptionData(OptionType.USER, "user", "삭제할 유저를 선택하세요.", true))
override fun run(event: SlashCommandInteractionEvent, bot: JDA) {
val manager = event.getOption("user")?.asUser
if(manager == null) {
event.hook.sendMessage("유저는 필수사항입니다.").queue()
return
}
if(manager.idLong == event.user.idLong) {
event.hook.sendMessage("자신은 매니저로 설정할 수 없습니다.").queue()
return
}
val user = UserService.getUser(event.user.idLong)
if(user == null) {
event.hook.sendMessage("치지직 계정을 찾을 수 없습니다.").queue()
return
}
if(ManagerService.getUser(user.liveAlertGuild ?: 0L, manager.idLong) == null) {
event.hook.sendMessage("${manager.name}은 매니저가 아닙니다.")
}
try {
ManagerService.deleteManager(user, manager.idLong)
event.hook.sendMessage("삭제가 완료되었습니다. ${manager.effectiveName}").queue()
} catch (e: Exception) {
event.hook.sendMessage("에러가 발생했습니다.").queue()
logger.debug(e.stackTraceToString())
}
}
}

View File

@@ -8,41 +8,46 @@ 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.chzzk.ChzzkHandler
import space.mori.chzzk_bot.chzzk.Connector 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.discord.CommandInterface
import space.mori.chzzk_bot.services.CommandService import space.mori.chzzk_bot.services.CommandService
import space.mori.chzzk_bot.services.ManagerService
import space.mori.chzzk_bot.services.UserService import space.mori.chzzk_bot.services.UserService
import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel
@Command
object UpdateCommand : CommandInterface { object UpdateCommand : CommandInterface {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)
override val name: String = "update" override val name: String = "update"
override val command = Commands.slash(name, "명령어를 수정합니다.") override val command = Commands.slash(name, "명령어를 수정합니다.")
.addOptions(OptionData(OptionType.STRING, "label", "수정할 명령어를 입력하세요.", true)) .addOptions(OptionData(OptionType.STRING, "label", "수정할 명령어를 입력하세요.", true))
.addOptions(OptionData(OptionType.STRING, "content", "표시될 텍스트를 입력하세요.", true)) .addOptions(OptionData(OptionType.STRING, "content", "표시될 텍스트를 입력하세요.", true))
.addOptions(OptionData(OptionType.STRING, "fail_content", "카운터 업데이트 실패시 표시될 텍스트를 입력하세요.", false))
override fun run(event: SlashCommandInteractionEvent, bot: JDA) { override fun run(event: SlashCommandInteractionEvent, bot: JDA) {
val label = event.getOption("label")?.asString val label = event.getOption("label")?.asString
val content = event.getOption("content")?.asString val content = event.getOption("content")?.asString
val failContent = event.getOption("fail_content")?.asString
if(label == null || content == null) { if(label == null || content == null) {
event.hook.sendMessage("명령어와 텍스트는 필수 입력입니다.").queue() event.hook.sendMessage("명령어와 텍스트는 필수 입력입니다.").queue()
return return
} }
val user = UserService.getUser(event.user.idLong) var user = UserService.getUser(event.user.idLong)
if(user == null) { val manager = event.guild?.idLong?.let { ManagerService.getUser(it, event.user.idLong) }
event.hook.sendMessage("치지직 계정을 찾을 수 없습니다.").queue() if(user == null && manager == null) {
event.hook.sendMessage("당신은 이 명령어를 사용할 수 없습니다.").queue()
return return
} }
val chzzkChannel = Connector.getChannel(user.token)
if (manager != null) {
user = manager.user
ManagerService.updateManager(user, event.user.idLong, event.user.effectiveName)
}
val chzzkChannel = Connector.getChannel(user!!.token)
try { try {
CommandService.updateCommand(user, label, content) CommandService.updateCommand(user, label, content, failContent ?: "")
try { chzzkChannel?.let { ChzzkHandler.reloadCommand(it) }
ChzzkHandler.reloadCommand(chzzkChannel!!)
} catch (_: Exception) {}
event.hook.sendMessage("등록이 완료되었습니다. $label = $content").queue() event.hook.sendMessage("등록이 완료되었습니다. $label = $content").queue()
} catch (e: Exception) { } catch (e: Exception) {
event.hook.sendMessage("에러가 발생했습니다.").queue() event.hook.sendMessage("에러가 발생했습니다.").queue()

View File

@@ -10,6 +10,7 @@ object Commands: IntIdTable("commands") {
val user = reference("user", Users, onDelete = ReferenceOption.CASCADE) val user = reference("user", Users, onDelete = ReferenceOption.CASCADE)
val command = varchar("command", 255) val command = varchar("command", 255)
val content = text("content") val content = text("content")
val failContent = text("fail_content")
} }
class Command(id: EntityID<Int>) : IntEntity(id) { class Command(id: EntityID<Int>) : IntEntity(id) {
@@ -18,4 +19,5 @@ class Command(id: EntityID<Int>) : IntEntity(id) {
var user by User referencedOn Commands.user var user by User referencedOn Commands.user
var command by Commands.command var command by Commands.command
var content by Commands.content var content by Commands.content
var failContent by Commands.failContent
} }

View File

@@ -0,0 +1,20 @@
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
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.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.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.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

@@ -10,6 +10,9 @@ object Users: IntIdTable("users") {
val username = varchar("username", 255) val username = varchar("username", 255)
val token = varchar("token", 64) val token = varchar("token", 64)
val discord = long("discord") 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) { class User(id: EntityID<Int>) : IntEntity(id) {
@@ -18,4 +21,7 @@ class User(id: EntityID<Int>) : IntEntity(id) {
var username by Users.username var username by Users.username
var token by Users.token var token by Users.token
var discord by Users.discord var discord by Users.discord
var liveAlertGuild by Users.liveAlertGuild
var liveAlertChannel by Users.liveAlertChannel
var liveAlertMessage by Users.liveAlertMessage
} }

View File

@@ -9,12 +9,13 @@ import space.mori.chzzk_bot.models.Commands
import space.mori.chzzk_bot.models.User import space.mori.chzzk_bot.models.User
object CommandService { object CommandService {
fun saveCommand(user: User, command: String, content: String): Command { fun saveCommand(user: User, command: String, content: String, failContent: String): Command {
return transaction { return transaction {
return@transaction Command.new { return@transaction Command.new {
this.user = user this.user = user
this.command = command this.command = command
this.content = content this.content = content
this.failContent = failContent
} }
} }
} }
@@ -26,31 +27,32 @@ object CommandService {
commandRow ?: throw RuntimeException("Command not found! $command") commandRow ?: throw RuntimeException("Command not found! $command")
commandRow.delete() commandRow.delete()
return@transaction commandRow commandRow
} }
} }
fun updateCommand(user: User, command: String, content: String): Command { fun updateCommand(user: User, command: String, content: String, failContent: String): Command {
return transaction { return transaction {
val updated = Commands.update({Commands.user eq user.id and(Commands.command eq command)}) { val updated = Commands.update({Commands.user eq user.id and(Commands.command eq command)}) {
it[Commands.content] = content it[Commands.content] = content
it[Commands.failContent] = failContent
} }
if(updated == 0) throw RuntimeException("Command not found! $command") if(updated == 0) throw RuntimeException("Command not found! $command")
return@transaction Command.find(Commands.user eq user.id and(Commands.command eq command)).first() Command.find(Commands.user eq user.id and(Commands.command eq command)).first()
} }
} }
fun getCommand(id: Int): Command? { fun getCommand(id: Int): Command? {
return transaction { return transaction {
return@transaction Command.findById(id) Command.findById(id)
} }
} }
fun getCommands(user: User): List<Command> { fun getCommands(user: User): List<Command> {
return transaction { return transaction {
return@transaction Command.find(Commands.user eq user.id) Command.find(Commands.user eq user.id)
.toList() .toList()
} }
} }

View File

@@ -0,0 +1,104 @@
package space.mori.chzzk_bot.services
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction
import space.mori.chzzk_bot.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.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.models.Manager
import space.mori.chzzk_bot.models.Managers
import space.mori.chzzk_bot.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

@@ -2,13 +2,14 @@ package space.mori.chzzk_bot.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 space.mori.chzzk_bot.models.User import space.mori.chzzk_bot.models.User
import space.mori.chzzk_bot.models.Users import space.mori.chzzk_bot.models.Users
object UserService { object UserService {
fun saveUser(username: String, token: String, discordID: Long): User { fun saveUser(username: String, token: String, discordID: Long): User {
return transaction { return transaction {
return@transaction User.new { User.new {
this.username = username this.username = username
this.token = token this.token = token
this.discord = discordID this.discord = discordID
@@ -18,7 +19,7 @@ object UserService {
fun getUser(id: Int): User? { fun getUser(id: Int): User? {
return transaction { return transaction {
return@transaction User.findById(id) User.findById(id)
} }
} }
@@ -26,7 +27,7 @@ object UserService {
return transaction { return transaction {
val users = User.find(Users.discord eq discordID) val users = User.find(Users.discord eq discordID)
return@transaction users.firstOrNull() users.firstOrNull()
} }
} }
@@ -34,13 +35,36 @@ object UserService {
return transaction { return transaction {
val users = User.find(Users.token eq chzzkID) val users = User.find(Users.token eq chzzkID)
return@transaction users.firstOrNull() users.firstOrNull()
}
}
fun getUserWithGuildId(discordGuildId: Long): User? {
return transaction {
val users = User.find(Users.liveAlertGuild eq discordGuildId)
users.firstOrNull()
} }
} }
fun getAllUsers(): List<User> { fun getAllUsers(): List<User> {
return transaction { return transaction {
return@transaction User.all().toList() 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()
} }
} }
} }

View File

@@ -1,18 +0,0 @@
[
{
"name":"java.lang.Boolean",
"methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]
},
{
"name":"java.lang.String",
"methods":[{"name":"lastIndexOf","parameterTypes":["int"] }, {"name":"substring","parameterTypes":["int"] }]
},
{
"name":"java.lang.System",
"methods":[{"name":"getProperty","parameterTypes":["java.lang.String"] }, {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }]
},
{
"name":"space.mori.chzzk_bot.MainKt",
"methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }]
}
]

View File

@@ -1,7 +0,0 @@
[
{
"type":"agent-extracted",
"classes":[
]
}
]

View File

@@ -1,5 +0,0 @@
[
{
"interfaces":["java.sql.Connection"]
}
]

View File

@@ -1,467 +0,0 @@
[
{
"name":"[B"
},
{
"name":"[Lcom.fasterxml.jackson.databind.AbstractTypeResolver;"
},
{
"name":"[Lcom.zaxxer.hikari.util.ConcurrentBag$IConcurrentBagEntry;"
},
{
"name":"[Ljava.lang.String;"
},
{
"name":"[Ljava.sql.Statement;"
},
{
"name":"[Lnet.dv8tion.jda.api.entities.Guild;"
},
{
"name":"[Lnet.dv8tion.jda.api.entities.Member;"
},
{
"name":"[Lnet.dv8tion.jda.api.entities.Role;"
},
{
"name":"[Lnet.dv8tion.jda.api.entities.ScheduledEvent;"
},
{
"name":"[Lnet.dv8tion.jda.api.entities.User;"
},
{
"name":"[Lnet.dv8tion.jda.api.entities.emoji.RichCustomEmoji;"
},
{
"name":"[Lnet.dv8tion.jda.api.entities.sticker.GuildSticker;"
},
{
"name":"[Lnet.dv8tion.jda.api.managers.AudioManager;"
},
{
"name":"[Lsun.security.pkcs.SignerInfo;"
},
{
"name":"android.os.Build$VERSION"
},
{
"name":"apple.security.AppleProvider",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"ch.qos.logback.classic.BasicConfigurator",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"ch.qos.logback.classic.joran.SerializedModelConfigurator",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"ch.qos.logback.classic.util.DefaultJoranConfigurator",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.sun.crypto.provider.AESCipher$General",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.sun.crypto.provider.ARCFOURCipher",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.sun.crypto.provider.DESCipher",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.sun.crypto.provider.DESedeCipher",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.sun.crypto.provider.DHParameters",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.sun.crypto.provider.HmacCore$HmacSHA384",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.sun.crypto.provider.TlsMasterSecretGenerator",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.zaxxer.hikari.HikariConfig",
"allDeclaredFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"getAllowPoolSuspension","parameterTypes":[] }, {"name":"getAutoCommit","parameterTypes":[] }, {"name":"getCatalog","parameterTypes":[] }, {"name":"getConnectionInitSql","parameterTypes":[] }, {"name":"getConnectionTestQuery","parameterTypes":[] }, {"name":"getConnectionTimeout","parameterTypes":[] }, {"name":"getDataSource","parameterTypes":[] }, {"name":"getDataSourceClassName","parameterTypes":[] }, {"name":"getDataSourceJNDI","parameterTypes":[] }, {"name":"getDataSourceProperties","parameterTypes":[] }, {"name":"getDriverClassName","parameterTypes":[] }, {"name":"getExceptionOverrideClassName","parameterTypes":[] }, {"name":"getHealthCheckProperties","parameterTypes":[] }, {"name":"getHealthCheckRegistry","parameterTypes":[] }, {"name":"getIdleTimeout","parameterTypes":[] }, {"name":"getInitializationFailTimeout","parameterTypes":[] }, {"name":"getIsolateInternalQueries","parameterTypes":[] }, {"name":"getJdbcUrl","parameterTypes":[] }, {"name":"getKeepaliveTime","parameterTypes":[] }, {"name":"getLeakDetectionThreshold","parameterTypes":[] }, {"name":"getMaxLifetime","parameterTypes":[] }, {"name":"getMaximumPoolSize","parameterTypes":[] }, {"name":"getMetricRegistry","parameterTypes":[] }, {"name":"getMetricsTrackerFactory","parameterTypes":[] }, {"name":"getMinimumIdle","parameterTypes":[] }, {"name":"getPassword","parameterTypes":[] }, {"name":"getPoolName","parameterTypes":[] }, {"name":"getReadOnly","parameterTypes":[] }, {"name":"getRegisterMbeans","parameterTypes":[] }, {"name":"getScheduledExecutor","parameterTypes":[] }, {"name":"getSchema","parameterTypes":[] }, {"name":"getThreadFactory","parameterTypes":[] }, {"name":"getTransactionIsolation","parameterTypes":[] }, {"name":"getUsername","parameterTypes":[] }, {"name":"getValidationTimeout","parameterTypes":[] }, {"name":"isAllowPoolSuspension","parameterTypes":[] }, {"name":"isAutoCommit","parameterTypes":[] }, {"name":"isIsolateInternalQueries","parameterTypes":[] }, {"name":"isReadOnly","parameterTypes":[] }, {"name":"isRegisterMbeans","parameterTypes":[] }, {"name":"setAllowPoolSuspension","parameterTypes":["boolean"] }, {"name":"setAutoCommit","parameterTypes":["boolean"] }, {"name":"setCatalog","parameterTypes":["java.lang.String"] }, {"name":"setClass","parameterTypes":["java.lang.Class"] }, {"name":"setConnectionInitSql","parameterTypes":["java.lang.String"] }, {"name":"setConnectionTestQuery","parameterTypes":["java.lang.String"] }, {"name":"setConnectionTimeout","parameterTypes":["long"] }, {"name":"setDataSource","parameterTypes":["javax.sql.DataSource"] }, {"name":"setDataSourceClassName","parameterTypes":["java.lang.String"] }, {"name":"setDataSourceJNDI","parameterTypes":["java.lang.String"] }, {"name":"setDataSourceProperties","parameterTypes":["java.util.Properties"] }, {"name":"setDriverClassName","parameterTypes":["java.lang.String"] }, {"name":"setExceptionOverrideClassName","parameterTypes":["java.lang.String"] }, {"name":"setHealthCheckProperties","parameterTypes":["java.util.Properties"] }, {"name":"setHealthCheckRegistry","parameterTypes":["java.lang.Object"] }, {"name":"setIdleTimeout","parameterTypes":["long"] }, {"name":"setInitializationFailTimeout","parameterTypes":["long"] }, {"name":"setIsolateInternalQueries","parameterTypes":["boolean"] }, {"name":"setJdbcUrl","parameterTypes":["java.lang.String"] }, {"name":"setKeepaliveTime","parameterTypes":["long"] }, {"name":"setLeakDetectionThreshold","parameterTypes":["long"] }, {"name":"setMaxLifetime","parameterTypes":["long"] }, {"name":"setMaximumPoolSize","parameterTypes":["int"] }, {"name":"setMetricRegistry","parameterTypes":["java.lang.Object"] }, {"name":"setMetricsTrackerFactory","parameterTypes":["com.zaxxer.hikari.metrics.MetricsTrackerFactory"] }, {"name":"setMinimumIdle","parameterTypes":["int"] }, {"name":"setPassword","parameterTypes":["java.lang.String"] }, {"name":"setPoolName","parameterTypes":["java.lang.String"] }, {"name":"setReadOnly","parameterTypes":["boolean"] }, {"name":"setRegisterMbeans","parameterTypes":["boolean"] }, {"name":"setScheduledExecutor","parameterTypes":["java.util.concurrent.ScheduledExecutorService"] }, {"name":"setSchema","parameterTypes":["java.lang.String"] }, {"name":"setThreadFactory","parameterTypes":["java.util.concurrent.ThreadFactory"] }, {"name":"setTransactionIsolation","parameterTypes":["java.lang.String"] }, {"name":"setUsername","parameterTypes":["java.lang.String"] }, {"name":"setValidationTimeout","parameterTypes":["long"] }]
},
{
"name":"com.zaxxer.hikari.pool.PoolBase",
"fields":[{"name":"catalog"}]
},
{
"name":"com.zaxxer.hikari.pool.PoolEntry",
"fields":[{"name":"state"}]
},
{
"name":"java.io.FilePermission"
},
{
"name":"java.lang.ClassValue"
},
{
"name":"java.lang.Module"
},
{
"name":"java.lang.RuntimePermission"
},
{
"name":"java.lang.String"
},
{
"name":"java.lang.StringBuilder"
},
{
"name":"java.lang.Thread",
"fields":[{"name":"threadLocalRandomProbe"}]
},
{
"name":"java.lang.invoke.CallSite"
},
{
"name":"java.net.NetPermission"
},
{
"name":"java.net.SocketPermission"
},
{
"name":"java.net.URLPermission",
"methods":[{"name":"<init>","parameterTypes":["java.lang.String","java.lang.String"] }]
},
{
"name":"java.nio.Buffer"
},
{
"name":"java.security.AlgorithmParametersSpi"
},
{
"name":"java.security.AllPermission"
},
{
"name":"java.security.KeyStoreSpi"
},
{
"name":"java.security.SecureRandomParameters"
},
{
"name":"java.security.SecurityPermission"
},
{
"name":"java.security.interfaces.ECPrivateKey"
},
{
"name":"java.security.interfaces.ECPublicKey"
},
{
"name":"java.security.interfaces.RSAPrivateKey"
},
{
"name":"java.security.interfaces.RSAPublicKey"
},
{
"name":"java.sql.Date"
},
{
"name":"java.util.Date"
},
{
"name":"java.util.List",
"methods":[{"name":"copyOf","parameterTypes":["java.util.Collection"] }]
},
{
"name":"java.util.Optional",
"methods":[{"name":"isEmpty","parameterTypes":[] }]
},
{
"name":"java.util.PropertyPermission"
},
{
"name":"java.util.concurrent.ForkJoinTask",
"fields":[{"name":"aux"}, {"name":"status"}]
},
{
"name":"java.util.concurrent.atomic.AtomicBoolean",
"fields":[{"name":"value"}]
},
{
"name":"java.util.concurrent.atomic.AtomicReference",
"fields":[{"name":"value"}]
},
{
"name":"java.util.concurrent.atomic.Striped64",
"fields":[{"name":"base"}, {"name":"cellsBusy"}]
},
{
"name":"java.util.function.Function"
},
{
"name":"java.util.zip.DeflaterInputStream"
},
{
"name":"javax.net.ssl.SNIHostName",
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
},
{
"name":"javax.net.ssl.SSLParameters",
"methods":[{"name":"setServerNames","parameterTypes":["java.util.List"] }]
},
{
"name":"javax.security.auth.x500.X500Principal",
"fields":[{"name":"thisX500Name"}],
"methods":[{"name":"<init>","parameterTypes":["sun.security.x509.X500Name"] }]
},
{
"name":"javax.smartcardio.CardPermission"
},
{
"name":"jdk.internal.misc.Unsafe"
},
{
"name":"kotlin.Metadata",
"queryAllDeclaredMethods":true,
"methods":[{"name":"bv","parameterTypes":[] }, {"name":"d1","parameterTypes":[] }, {"name":"d2","parameterTypes":[] }, {"name":"k","parameterTypes":[] }, {"name":"mv","parameterTypes":[] }, {"name":"pn","parameterTypes":[] }, {"name":"xi","parameterTypes":[] }, {"name":"xs","parameterTypes":[] }]
},
{
"name":"kotlin.SafePublicationLazyImpl",
"fields":[{"name":"_value"}]
},
{
"name":"kotlin.jvm.internal.DefaultConstructorMarker"
},
{
"name":"kotlin.reflect.jvm.internal.ReflectionFactoryImpl",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"kotlinx.coroutines.CancellableContinuationImpl",
"fields":[{"name":"_decisionAndIndex$volatile"}, {"name":"_parentHandle$volatile"}, {"name":"_state$volatile"}]
},
{
"name":"kotlinx.coroutines.EventLoopImplBase",
"fields":[{"name":"_delayed$volatile"}, {"name":"_isCompleted$volatile"}, {"name":"_queue$volatile"}]
},
{
"name":"kotlinx.coroutines.JobSupport",
"fields":[{"name":"_parentHandle$volatile"}, {"name":"_state$volatile"}]
},
{
"name":"kotlinx.coroutines.internal.DispatchedContinuation",
"fields":[{"name":"_reusableCancellableContinuation$volatile"}]
},
{
"name":"kotlinx.coroutines.internal.LimitedDispatcher",
"fields":[{"name":"runningWorkers$volatile"}]
},
{
"name":"kotlinx.coroutines.internal.LockFreeLinkedListNode",
"fields":[{"name":"_next$volatile"}, {"name":"_prev$volatile"}, {"name":"_removedRef$volatile"}]
},
{
"name":"kotlinx.coroutines.internal.LockFreeTaskQueue",
"fields":[{"name":"_cur$volatile"}]
},
{
"name":"kotlinx.coroutines.internal.LockFreeTaskQueueCore",
"fields":[{"name":"_next$volatile"}, {"name":"_state$volatile"}]
},
{
"name":"kotlinx.coroutines.internal.ThreadSafeHeap",
"fields":[{"name":"_size$volatile"}]
},
{
"name":"kotlinx.coroutines.scheduling.CoroutineScheduler",
"fields":[{"name":"_isTerminated$volatile"}, {"name":"controlState$volatile"}, {"name":"parkedWorkersStack$volatile"}]
},
{
"name":"net.dv8tion.jda.api.hooks.ListenerAdapter",
"methods":[{"name":"onGatewayPing","parameterTypes":["net.dv8tion.jda.api.events.GatewayPingEvent"] }, {"name":"onGenericGuild","parameterTypes":["net.dv8tion.jda.api.events.guild.GenericGuildEvent"] }, {"name":"onGenericSession","parameterTypes":["net.dv8tion.jda.api.events.session.GenericSessionEvent"] }, {"name":"onGuildReady","parameterTypes":["net.dv8tion.jda.api.events.guild.GuildReadyEvent"] }, {"name":"onHttpRequest","parameterTypes":["net.dv8tion.jda.api.events.http.HttpRequestEvent"] }, {"name":"onReady","parameterTypes":["net.dv8tion.jda.api.events.session.ReadyEvent"] }, {"name":"onShutdown","parameterTypes":["net.dv8tion.jda.api.events.session.ShutdownEvent"] }, {"name":"onStatusChange","parameterTypes":["net.dv8tion.jda.api.events.StatusChangeEvent"] }]
},
{
"name":"net.dv8tion.jda.internal.utils.FallbackLogger",
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
},
{
"name":"org.mariadb.jdbc.Configuration",
"allDeclaredFields":true
},
{
"name":"org.mariadb.jdbc.Configuration$Builder",
"allDeclaredFields":true
},
{
"name":"org.mariadb.jdbc.Driver",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.slf4j.LoggerFactory"
},
{
"name":"org.slf4j.spi.SLF4JServiceProvider"
},
{
"name":"space.mori.chzzk_bot.discord.CommandInterface"
},
{
"name":"space.mori.chzzk_bot.discord.commands.Ping",
"fields":[{"name":"INSTANCE"}]
},
{
"name":"sun.security.pkcs12.PKCS12KeyStore",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.provider.DSA$SHA224withDSA",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.provider.DSA$SHA256withDSA",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.provider.NativePRNG",
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }]
},
{
"name":"sun.security.provider.SHA",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.provider.SHA2$SHA224",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.provider.SHA2$SHA256",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.provider.SHA5$SHA384",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.provider.SHA5$SHA512",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.provider.X509Factory",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.provider.certpath.PKIXCertPathValidator",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.rsa.PSSParameters",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.rsa.RSAKeyFactory$Legacy",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.rsa.RSAPSSSignature",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.rsa.RSASignature$SHA224withRSA",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.rsa.RSASignature$SHA256withRSA",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.ssl.KeyManagerFactoryImpl$SunX509",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.ssl.SSLContextImpl$DefaultSSLContext",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.ssl.SSLContextImpl$TLSContext",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.util.ObjectIdentifier"
},
{
"name":"sun.security.x509.AuthorityInfoAccessExtension",
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
},
{
"name":"sun.security.x509.AuthorityKeyIdentifierExtension",
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
},
{
"name":"sun.security.x509.BasicConstraintsExtension",
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
},
{
"name":"sun.security.x509.CRLDistributionPointsExtension",
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
},
{
"name":"sun.security.x509.CertificateExtensions"
},
{
"name":"sun.security.x509.CertificatePoliciesExtension",
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
},
{
"name":"sun.security.x509.ExtendedKeyUsageExtension",
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
},
{
"name":"sun.security.x509.IssuerAlternativeNameExtension",
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
},
{
"name":"sun.security.x509.KeyUsageExtension",
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
},
{
"name":"sun.security.x509.NetscapeCertTypeExtension",
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
},
{
"name":"sun.security.x509.PrivateKeyUsageExtension",
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
},
{
"name":"sun.security.x509.SubjectAlternativeNameExtension",
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
},
{
"name":"sun.security.x509.SubjectKeyIdentifierExtension",
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
}
]

View File

@@ -1,49 +0,0 @@
{
"resources":{
"includes":[{
"pattern":"\\QMETA-INF/services/ch.qos.logback.classic.spi.Configurator\\E"
}, {
"pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E"
}, {
"pattern":"\\QMETA-INF/services/java.net.spi.InetAddressResolverProvider\\E"
}, {
"pattern":"\\QMETA-INF/services/java.net.spi.URLStreamHandlerProvider\\E"
}, {
"pattern":"\\QMETA-INF/services/java.sql.Driver\\E"
}, {
"pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E"
}, {
"pattern":"\\QMETA-INF/services/kotlin.reflect.jvm.internal.impl.resolve.ExternalOverridabilityCondition\\E"
}, {
"pattern":"\\QMETA-INF/services/kotlin.reflect.jvm.internal.impl.util.ModuleVisibilityHelper\\E"
}, {
"pattern":"\\QMETA-INF/services/org.jetbrains.exposed.sql.DatabaseConnectionAutoRegistration\\E"
}, {
"pattern":"\\QMETA-INF/services/org.jetbrains.exposed.sql.statements.GlobalStatementInterceptor\\E"
}, {
"pattern":"\\QMETA-INF/services/org.mariadb.jdbc.plugin.Codec\\E"
}, {
"pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E"
}, {
"pattern":"\\Qlogback-test.scmo\\E"
}, {
"pattern":"\\Qlogback-test.xml\\E"
}, {
"pattern":"\\Qlogback.scmo\\E"
}, {
"pattern":"\\Qlogback.xml\\E"
}, {
"pattern":"\\Qmariadb.properties\\E"
}, {
"pattern":"\\Qspace/mori/chzzk_bot/discord/commands\\E"
}, {
"pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt72b/nfc.nrm\\E"
}, {
"pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt72b/nfkc.nrm\\E"
}, {
"pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt72b/uprops.icu\\E"
}, {
"pattern":"java.base:\\Qsun/net/idn/uidna.spp\\E"
}]},
"bundles":[]
}

View File

@@ -1,8 +0,0 @@
{
"types":[
],
"lambdaCapturingTypes":[
],
"proxies":[
]
}

View File

@@ -0,0 +1,35 @@
<configuration>
<!-- 콘솔에 출력하는 기본 로그 설정 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<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="o.m.jdbc.client.impl.StandardClient" level="INFO" />
<logger name="o.m.jdbc.message.server.OkPacket" level="INFO" />
<logger name="o.m.jdbc.client.impl.StandardClient" 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="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>

View File

@@ -3,7 +3,7 @@
<!-- 콘솔에 출력하는 기본 로그 설정 --> <!-- 콘솔에 출력하는 기본 로그 설정 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level> <level>INFO</level>
</filter> </filter>
<encoder> <encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>