mirror of
https://github.com/dalbodeule/chibot-chzzk-bot.git
synced 2025-08-07 12:51:13 +00:00
Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e8baa393cf | ||
|
8365fa1767 | ||
|
e16a8b6fc0 | ||
|
72f98b024b | ||
|
1d23ac5121 | ||
|
8f8d2f895a | ||
|
47b4b8252f | ||
|
64880318e8 | ||
|
cec07f9859 | ||
|
18ee27e567 | ||
|
34ec0f29ff | ||
|
2b999c5acb | ||
|
c669943f87 | ||
|
efaaf5ae9f | ||
|
c8fca6995c | ||
|
903f1f7f38 | ||
|
e4b6f819ca | ||
|
2c1f42e4af | ||
|
ff90e5fe7f | ||
|
a227dda930 | ||
|
3e09989202 | ||
|
eddca6dbab | ||
|
9c48dd676e | ||
|
779604d21e | ||
|
83791fe59b | ||
|
fe63a4af15 | ||
|
7522ec6f9e | ||
|
c35b3082cc | ||
|
19d3f23cd7 | ||
|
80d777dad5 | ||
|
d2071b323e | ||
|
f2b30c8b00 |
53
.github/workflows/build.yml
vendored
53
.github/workflows/build.yml
vendored
@@ -1,53 +0,0 @@
|
||||
name: build
|
||||
|
||||
# event trigger: main 브랜치에 push 이벤트 발생 시 jobs가 실행된다.
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
|
||||
# 권한 설정
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# jobs 정의
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: graalvm/setup-graalvm@v1
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'graalvm'
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Set datetime variable
|
||||
id: vars
|
||||
run: echo "DATETIME=$(date +'%Y%m%d%H%M')" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ secrets.DOCKER_USERNAME }}/chzzkbot:${{ env.DATETIME }}
|
||||
${{ secrets.DOCKER_USERNAME }}/chzzkbot:latest
|
9
.idea/dataSources.xml
generated
9
.idea/dataSources.xml
generated
@@ -1,14 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="@localhost" uuid="90f8ee11-600e-4155-a316-e8062c7c828b">
|
||||
<driver-ref>mysql.8</driver-ref>
|
||||
<data-source source="LOCAL" name="@prod" uuid="90f8ee11-600e-4155-a316-e8062c7c828b">
|
||||
<driver-ref>mariadb</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:mysql://localhost:3306</jdbc-url>
|
||||
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:mariadb://localhost:3306</jdbc-url>
|
||||
<jdbc-additional-properties>
|
||||
<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.resource.type" value="Deployment" />
|
||||
<property name="com.intellij.clouds.kubernetes.db.container.port" />
|
||||
</jdbc-additional-properties>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
|
6
.idea/ktor.xml
generated
Normal file
6
.idea/ktor.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KtorOptions">
|
||||
<option name="updateOpenAPI" value="true" />
|
||||
</component>
|
||||
</project>
|
3
.idea/sqldialects.xml
generated
3
.idea/sqldialects.xml
generated
@@ -3,7 +3,4 @@
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="PROJECT" dialect="MariaDB" />
|
||||
</component>
|
||||
<component name="SqlResolveMappings">
|
||||
<file url="file://$PROJECT_DIR$/src/main/kotlin/space/mori/chzzk_bot/models/Command.kt" scope="{"node":{ "@negative":"1", "group":{ "@kind":"root", "node":{ "@negative":"1" } } }}" />
|
||||
</component>
|
||||
</project>
|
19
Dockerfile
19
Dockerfile
@@ -1,23 +1,10 @@
|
||||
# Stage 1: Build the JAR file
|
||||
FROM gradle:jdk21 as build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the Gradle files and source code
|
||||
COPY build.gradle.kts settings.gradle.kts gradlew gradle.properties ./
|
||||
COPY gradle gradle
|
||||
COPY src src
|
||||
|
||||
# Build the project using Gradle
|
||||
RUN ./gradlew build
|
||||
|
||||
# Stage 2: Run the JAR file using JDK 21
|
||||
# Use a base image with JDK 21 for the final image
|
||||
FROM openjdk:21-jdk
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the JAR file from the build stage
|
||||
COPY --from=build /app/build/libs/*.jar app.jar
|
||||
# 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"]
|
73
README.md
73
README.md
@@ -1,3 +1,72 @@
|
||||
# maybe_chzzk_bot_kotlin
|
||||
# nabot_chzzk_bot
|
||||
|
||||
[discord](https://discord.gg/up8ANZegmy)
|
||||
[](https://discord.gg/up8ANZegmy) [](https://teamcity.mori.space/project/NabotChzzkBot) [](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/)
|
||||
|
@@ -5,7 +5,6 @@ plugins {
|
||||
id("application")
|
||||
kotlin("jvm") version kotlinVersion
|
||||
kotlin("plugin.jpa") version kotlinVersion
|
||||
id("org.graalvm.buildtools.native") version "0.10.2"
|
||||
}
|
||||
|
||||
group = "${project.group}"
|
||||
@@ -29,39 +28,29 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
// 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")
|
||||
}
|
||||
// https://mvnrepository.com/artifact/io.github.R2turnTrue/chzzk4j
|
||||
implementation("io.github.R2turnTrue:chzzk4j:0.0.8")
|
||||
|
||||
implementation("ch.qos.logback:logback-classic:1.4.14")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-core
|
||||
implementation("org.jetbrains.exposed:exposed-core:0.51.1")
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-dao
|
||||
implementation("org.jetbrains.exposed:exposed-dao:0.51.1")
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-jdbc
|
||||
implementation("org.jetbrains.exposed:exposed-jdbc:0.51.1")
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-kotlin-datetime
|
||||
implementation("org.jetbrains.exposed:exposed-java-time:0.51.1")
|
||||
|
||||
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
|
||||
implementation("com.zaxxer:HikariCP:5.1.0")
|
||||
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
|
||||
implementation("ch.qos.logback:logback-classic:1.5.6")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC")
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect:2.0.0")
|
||||
|
||||
// https://mvnrepository.com/artifact/com.google.code.gson/gson
|
||||
implementation("com.google.code.gson:gson:2.11.0")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client
|
||||
implementation("org.mariadb.jdbc:mariadb-java-client:3.4.0")
|
||||
|
||||
// https://mvnrepository.com/artifact/io.github.cdimascio/dotenv-kotlin
|
||||
implementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
|
||||
|
||||
kotlin("stdlib")
|
||||
|
||||
listOf(project(":common"), project(":chatbot"), project(":webserver")).forEach {
|
||||
implementation(it)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Jar> {
|
||||
@@ -77,4 +66,8 @@ tasks.withType<Jar> {
|
||||
})
|
||||
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
|
||||
tasks.named<JavaExec>("run") {
|
||||
systemProperty("logback.configurationFile", "logback-debug.xml")
|
||||
}
|
50
chatbot/build.gradle.kts
Normal file
50
chatbot/build.gradle.kts
Normal file
@@ -0,0 +1,50 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
group = project.rootProject.group
|
||||
version = project.rootProject.version
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// https://mvnrepository.com/artifact/net.dv8tion/JDA
|
||||
implementation("net.dv8tion:JDA:5.0.1") {
|
||||
exclude(module = "opus-java")
|
||||
}
|
||||
|
||||
// https://mvnrepository.com/artifact/io.github.R2turnTrue/chzzk4j
|
||||
implementation("io.github.R2turnTrue:chzzk4j:0.0.9")
|
||||
|
||||
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
|
||||
implementation("ch.qos.logback:logback-classic:1.5.6")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC")
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect:2.0.0")
|
||||
|
||||
// https://mvnrepository.com/artifact/com.google.code.gson/gson
|
||||
implementation("com.google.code.gson:gson:2.11.0")
|
||||
|
||||
// https://mvnrepository.com/artifact/io.github.cdimascio/dotenv-kotlin
|
||||
implementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
|
||||
|
||||
// https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
|
||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
|
||||
testImplementation(kotlin("test"))
|
||||
|
||||
listOf(project(":common")).forEach {
|
||||
implementation(it)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.chzzk
|
||||
package space.mori.chzzk_bot.chatbot.chzzk
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
@@ -21,12 +21,17 @@ data class IFollowContent(
|
||||
val badge: Badge? = null,
|
||||
val title: Title? = null,
|
||||
val verifiedMark: Boolean = false,
|
||||
val activityBadges: List<String> = emptyList(),
|
||||
val activityBadges: List<Badge> = emptyList(),
|
||||
val streamingProperty: StreamingProperty = StreamingProperty()
|
||||
)
|
||||
|
||||
data class Badge(
|
||||
val imageUrl: String = ""
|
||||
val badgeNo: Int?,
|
||||
val badgeId: String?,
|
||||
val imageUrl: String?,
|
||||
val title: String?,
|
||||
val description: String?,
|
||||
val activated: Boolean?
|
||||
)
|
||||
|
||||
data class Title(
|
||||
@@ -117,6 +122,7 @@ fun getFollowDate(chatID: String, userId: String) : IData<IFollowContent> {
|
||||
|
||||
return follow
|
||||
} catch(e: Exception) {
|
||||
println(e.stackTrace)
|
||||
throw e
|
||||
}
|
||||
}
|
@@ -1,37 +1,34 @@
|
||||
package space.mori.chzzk_bot.chzzk
|
||||
package space.mori.chzzk_bot.chatbot.chzzk
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
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.chatbot.chzzk.Connector.chzzk
|
||||
import space.mori.chzzk_bot.chatbot.discord.Discord
|
||||
import space.mori.chzzk_bot.common.models.User
|
||||
import space.mori.chzzk_bot.common.services.UserService
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChatEventListener
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChatMessage
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChzzkChat
|
||||
import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel
|
||||
import java.lang.Exception
|
||||
import java.net.SocketTimeoutException
|
||||
import java.time.Instant
|
||||
|
||||
object ChzzkHandler {
|
||||
private val handlers = mutableListOf<UserHandler>()
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
@Volatile private var running: Boolean = false
|
||||
|
||||
internal fun addUser(chzzkChannel: ChzzkChannel, user: User) {
|
||||
fun addUser(chzzkChannel: ChzzkChannel, user: User) {
|
||||
handlers.add(UserHandler(chzzkChannel, logger, user))
|
||||
}
|
||||
|
||||
internal fun enable() {
|
||||
fun enable() {
|
||||
UserService.getAllUsers().map {
|
||||
chzzk.getChannel(it.token)?.let { token -> addUser(token, it)}
|
||||
chzzk.getChannel(it.token)?.let { token -> addUser(token, it) }
|
||||
}
|
||||
}
|
||||
|
||||
internal fun disable() {
|
||||
fun disable() {
|
||||
handlers.forEach { handler ->
|
||||
handler.disable()
|
||||
}
|
||||
@@ -53,7 +50,7 @@ object ChzzkHandler {
|
||||
throw RuntimeException("${chzzkChannel.channelName} doesn't have handler")
|
||||
}
|
||||
|
||||
internal fun runStreamInfo() {
|
||||
fun runStreamInfo() {
|
||||
running = true
|
||||
val thread = Thread({
|
||||
while(running) {
|
||||
@@ -62,7 +59,7 @@ object ChzzkHandler {
|
||||
try {
|
||||
val streamInfo = getStreamInfo(it.channel.channelId)
|
||||
if (streamInfo.content.status == "OPEN" && !it.isActive) it.isActive(true, streamInfo)
|
||||
if (streamInfo.content.status == "CLOSED" && it.isActive) it.isActive(false, 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) {
|
||||
@@ -78,7 +75,7 @@ object ChzzkHandler {
|
||||
thread.start()
|
||||
}
|
||||
|
||||
internal fun stopStreamInfo() {
|
||||
fun stopStreamInfo() {
|
||||
running = false
|
||||
}
|
||||
}
|
||||
@@ -116,11 +113,6 @@ class UserHandler(
|
||||
})
|
||||
.build()
|
||||
|
||||
init {
|
||||
logger.info("ChzzkChat connecting... ${channel.channelName} - ${channel.channelId}")
|
||||
listener.connectAsync()
|
||||
}
|
||||
|
||||
internal fun disable() {
|
||||
listener.closeAsync()
|
||||
}
|
||||
@@ -140,32 +132,17 @@ class UserHandler(
|
||||
_isActive = value
|
||||
if(value) {
|
||||
logger.info("${user.username} is live.")
|
||||
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"))
|
||||
logger.info("ChzzkChat connecting... ${channel.channelName} - ${channel.channelId}")
|
||||
listener.connectBlocking()
|
||||
|
||||
channel.sendMessage(
|
||||
MessageCreateBuilder()
|
||||
.setContent(user.liveAlertMessage)
|
||||
.setEmbeds(embed.build())
|
||||
.build()
|
||||
).queue()
|
||||
Discord.sendDiscord(user, status)
|
||||
|
||||
listener.sendChat("${user.username} 님의 방송이 감지되었습니다.")
|
||||
|
||||
listener.sendChat("${user.username} 님의 방송이 감지되었습니다.")
|
||||
}
|
||||
} else {
|
||||
logger.info("${user.username} is offline.")
|
||||
|
||||
listener.sendChat("${user.username} 님! 방송 수고하셨습니다.")
|
||||
listener.closeAsync()
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,10 +1,14 @@
|
||||
package space.mori.chzzk_bot.chzzk
|
||||
package space.mori.chzzk_bot.chatbot.chzzk
|
||||
|
||||
import io.github.cdimascio.dotenv.dotenv
|
||||
import org.slf4j.LoggerFactory
|
||||
import xyz.r2turntrue.chzzk4j.Chzzk
|
||||
import xyz.r2turntrue.chzzk4j.ChzzkBuilder
|
||||
import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel
|
||||
import space.mori.chzzk_bot.dotenv
|
||||
|
||||
val dotenv = dotenv {
|
||||
ignoreIfMissing = true
|
||||
}
|
||||
|
||||
object Connector {
|
||||
val chzzk: Chzzk = ChzzkBuilder()
|
@@ -1,19 +1,16 @@
|
||||
package space.mori.chzzk_bot.chzzk
|
||||
package space.mori.chzzk_bot.chatbot.chzzk
|
||||
|
||||
import org.slf4j.Logger
|
||||
import space.mori.chzzk_bot.models.Command
|
||||
import space.mori.chzzk_bot.models.User
|
||||
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.common.models.User
|
||||
import space.mori.chzzk_bot.common.services.CommandService
|
||||
import space.mori.chzzk_bot.common.services.CounterService
|
||||
import space.mori.chzzk_bot.common.services.UserService
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChatMessage
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChzzkChat
|
||||
import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.LocalDate
|
||||
import java.time.Period
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
import java.time.temporal.ChronoUnit
|
||||
|
||||
|
||||
class MessageHandler(
|
||||
@@ -28,6 +25,7 @@ class MessageHandler(
|
||||
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 {
|
||||
reloadCommand()
|
||||
@@ -64,7 +62,7 @@ class MessageHandler(
|
||||
listener.sendChat("명령어 추가 형식은 '!명령어추가 명령어 내용'입니다.")
|
||||
return
|
||||
}
|
||||
if (commands.containsKey(parts[0])) {
|
||||
if (commands.containsKey(parts[1])) {
|
||||
listener.sendChat("${parts[1]} 명령어는 이미 있는 명령어입니다.")
|
||||
return
|
||||
}
|
||||
@@ -85,7 +83,7 @@ class MessageHandler(
|
||||
listener.sendChat("명령어 수정 형식은 '!명령어수정 명령어 내용'입니다.")
|
||||
return
|
||||
}
|
||||
if (!commands.containsKey(parts[0])) {
|
||||
if (!commands.containsKey(parts[1])) {
|
||||
listener.sendChat("${parts[1]} 명령어는 없는 명령어입니다.")
|
||||
return
|
||||
}
|
||||
@@ -121,58 +119,75 @@ class MessageHandler(
|
||||
var result = chat.first
|
||||
var isFail = false
|
||||
|
||||
result = counterPattern.replace(result) {
|
||||
val name = it.groupValues[1]
|
||||
CounterService.updateCounterValue(name, 1, user).toString()
|
||||
}
|
||||
|
||||
result = personalCounterPattern.replace(result) {
|
||||
val name = it.groupValues[1]
|
||||
CounterService.updatePersonalCounterValue(name, msg.userId, 1, user).toString()
|
||||
}
|
||||
|
||||
result = dailyCounterPattern.replace(result) {
|
||||
val name = it.groupValues[1]
|
||||
// Replace dailyCounterPattern
|
||||
result = dailyCounterPattern.replace(result) { matchResult ->
|
||||
val name = matchResult.groupValues[1]
|
||||
val dailyCounter = CounterService.getDailyCounterValue(name, msg.userId, user)
|
||||
|
||||
return@replace if(dailyCounter.second)
|
||||
if (dailyCounter.second) {
|
||||
CounterService.updateDailyCounterValue(name, msg.userId, 1, user).first.toString()
|
||||
else {
|
||||
} else {
|
||||
isFail = true
|
||||
dailyCounter.first.toString()
|
||||
}
|
||||
}
|
||||
|
||||
if(isFail && chat.second != "") {
|
||||
// Handle fail case
|
||||
if (isFail && chat.second.isNotEmpty()) {
|
||||
result = chat.second
|
||||
result = dailyCounterPattern.replace(result) {
|
||||
val name = it.groupValues[1]
|
||||
result = dailyCounterPattern.replace(result) { matchResult ->
|
||||
val name = matchResult.groupValues[1]
|
||||
val dailyCounter = CounterService.getDailyCounterValue(name, msg.userId, user)
|
||||
|
||||
dailyCounter.first.toString()
|
||||
}
|
||||
}
|
||||
|
||||
result = followPattern.replace(result) {
|
||||
val following = getFollowDate(listener.chatId, msg.userId)
|
||||
val dateString: String = following.content.streamingProperty.following?.followDate ?: SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(
|
||||
Date()
|
||||
)
|
||||
val today = LocalDate.now()
|
||||
// Replace followPattern
|
||||
result = followPattern.replace(result) { matchResult ->
|
||||
try {
|
||||
val followingDate = getFollowDate(listener.chatId, msg.userId)
|
||||
.content.streamingProperty.following?.followDate
|
||||
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||
// 문자열을 LocalDate 객체로 변환
|
||||
val pastDate = LocalDate.parse(dateString, formatter)
|
||||
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
|
||||
|
||||
val period = Period.between(pastDate, today)
|
||||
period.days.toString()
|
||||
}
|
||||
if(isFail) {
|
||||
return chat.second
|
||||
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
|
||||
}
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.discord
|
||||
package space.mori.chzzk_bot.chatbot.discord
|
||||
|
||||
import net.dv8tion.jda.api.JDA
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
@@ -0,0 +1,133 @@
|
||||
package space.mori.chzzk_bot.chatbot.discord
|
||||
|
||||
import io.github.cdimascio.dotenv.dotenv
|
||||
import net.dv8tion.jda.api.EmbedBuilder
|
||||
import net.dv8tion.jda.api.JDA
|
||||
import net.dv8tion.jda.api.JDABuilder
|
||||
import net.dv8tion.jda.api.entities.Activity
|
||||
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.hooks.ListenerAdapter
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder
|
||||
import org.slf4j.LoggerFactory
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.IData
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.IStreamInfo
|
||||
import space.mori.chzzk_bot.chatbot.discord.commands.*
|
||||
import space.mori.chzzk_bot.common.models.User
|
||||
import space.mori.chzzk_bot.common.services.ManagerService
|
||||
import java.time.Instant
|
||||
|
||||
val dotenv = dotenv {
|
||||
ignoreIfMissing = true
|
||||
}
|
||||
|
||||
class Discord: ListenerAdapter() {
|
||||
private var guild: Guild? = null
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
|
||||
companion object {
|
||||
lateinit var bot: JDA
|
||||
|
||||
internal fun getChannel(guildId: Long, channelId: Long) =
|
||||
bot.getGuildById(guildId)?.getTextChannelById(channelId)
|
||||
|
||||
fun sendDiscord(user: User, status: IData<IStreamInfo>) {
|
||||
if(user.liveAlertMessage != "" && user.liveAlertGuild != null && user.liveAlertChannel != null) {
|
||||
val channel = getChannel(user.liveAlertGuild!!, user.liveAlertChannel!!) ?: throw RuntimeException("${user.liveAlertChannel} is not valid.")
|
||||
|
||||
val embed = EmbedBuilder()
|
||||
embed.setTitle(status.content.liveTitle, "https://chzzk.naver.com/live/${user.token}")
|
||||
embed.setDescription("${user.username} 님이 방송을 시작했습니다.")
|
||||
embed.setUrl(status.content.channel.channelImageUrl)
|
||||
embed.setTimestamp(Instant.now())
|
||||
embed.setAuthor(user.username, "https://chzzk.naver.com/live/${user.token}", status.content.channel.channelImageUrl)
|
||||
embed.addField("카테고리", status.content.liveCategoryValue, true)
|
||||
embed.addField("태그", status.content.tags.joinToString(", "), true)
|
||||
embed.setImage(status.content.liveImageUrl.replace("{type}", "1080"))
|
||||
|
||||
channel.sendMessage(
|
||||
MessageCreateBuilder()
|
||||
.setContent(user.liveAlertMessage)
|
||||
.setEmbeds(embed.build())
|
||||
.build()
|
||||
).queue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val commands = listOf(
|
||||
AddCommand,
|
||||
AlertCommand,
|
||||
PingCommand,
|
||||
RegisterCommand,
|
||||
RemoveCommand,
|
||||
UpdateCommand,
|
||||
AddManagerCommand,
|
||||
ListManagerCommand,
|
||||
RemoveManagerCommand,
|
||||
)
|
||||
|
||||
override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) {
|
||||
event.deferReply().queue()
|
||||
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()
|
||||
}
|
||||
|
||||
fun enable() {
|
||||
val thread = Thread {
|
||||
try {
|
||||
bot = JDABuilder.createDefault(dotenv["DISCORD_TOKEN"])
|
||||
.setActivity(Activity.playing("치지직 보는중"))
|
||||
.addEventListeners(this)
|
||||
.build().awaitReady()
|
||||
|
||||
guild = bot.getGuildById(dotenv["GUILD_ID"])
|
||||
|
||||
commandUpdate(bot)
|
||||
bot.guilds.forEach {
|
||||
commandUpdate(it)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.info("Could not enable Discord!")
|
||||
logger.debug(e.stackTraceToString())
|
||||
}
|
||||
}
|
||||
thread.start()
|
||||
}
|
||||
|
||||
fun disable() {
|
||||
try {
|
||||
bot.shutdown()
|
||||
} catch(e: Exception) {
|
||||
logger.info("Error while shutting down Discord!")
|
||||
logger.debug(e.stackTraceToString())
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.discord.commands
|
||||
package space.mori.chzzk_bot.chatbot.discord.commands
|
||||
|
||||
import net.dv8tion.jda.api.JDA
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
||||
@@ -6,11 +6,12 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands
|
||||
import net.dv8tion.jda.api.interactions.commands.build.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.CommandService
|
||||
import space.mori.chzzk_bot.services.UserService
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.ChzzkHandler
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.Connector
|
||||
import space.mori.chzzk_bot.chatbot.discord.CommandInterface
|
||||
import space.mori.chzzk_bot.common.services.CommandService
|
||||
import space.mori.chzzk_bot.common.services.ManagerService
|
||||
import space.mori.chzzk_bot.common.services.UserService
|
||||
|
||||
object AddCommand : CommandInterface {
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
@@ -30,13 +31,19 @@ object AddCommand : CommandInterface {
|
||||
return
|
||||
}
|
||||
|
||||
val user = UserService.getUser(event.user.idLong)
|
||||
if(user == null) {
|
||||
event.hook.sendMessage("치지직 계정을 찾을 수 없습니다.").queue()
|
||||
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
|
||||
}
|
||||
|
||||
val commands = CommandService.getCommands(user)
|
||||
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
|
@@ -0,0 +1,47 @@
|
||||
package space.mori.chzzk_bot.chatbot.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.chatbot.discord.CommandInterface
|
||||
import space.mori.chzzk_bot.common.services.ManagerService
|
||||
import space.mori.chzzk_bot.common.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())
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.discord.commands
|
||||
package space.mori.chzzk_bot.chatbot.discord.commands
|
||||
|
||||
import net.dv8tion.jda.api.JDA
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
||||
@@ -6,10 +6,11 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands
|
||||
import net.dv8tion.jda.api.interactions.commands.build.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.UserService
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.ChzzkHandler
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.Connector
|
||||
import space.mori.chzzk_bot.chatbot.discord.CommandInterface
|
||||
import space.mori.chzzk_bot.common.services.ManagerService
|
||||
import space.mori.chzzk_bot.common.services.UserService
|
||||
|
||||
object AlertCommand : CommandInterface {
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
@@ -22,13 +23,19 @@ object AlertCommand : CommandInterface {
|
||||
val channel = event.getOption("channel")?.asChannel
|
||||
val content = event.getOption("content")?.asString
|
||||
|
||||
val user = UserService.getUser(event.user.idLong)
|
||||
if(user == null) {
|
||||
event.hook.sendMessage("치지직 계정을 찾을 수 없습니다.").queue()
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
val newUser = UserService.updateLiveAlert(user.id.value, channel?.guild?.idLong ?: 0L, channel?.idLong ?: 0L, content ?: "")
|
@@ -0,0 +1,44 @@
|
||||
package space.mori.chzzk_bot.chatbot.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.chatbot.discord.CommandInterface
|
||||
import space.mori.chzzk_bot.common.services.ManagerService
|
||||
import space.mori.chzzk_bot.common.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())
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,10 +1,10 @@
|
||||
package space.mori.chzzk_bot.discord.commands
|
||||
package space.mori.chzzk_bot.chatbot.discord.commands
|
||||
|
||||
import net.dv8tion.jda.api.JDA
|
||||
import net.dv8tion.jda.api.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.chatbot.discord.CommandInterface
|
||||
|
||||
object PingCommand: CommandInterface {
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
@@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.discord.commands
|
||||
package space.mori.chzzk_bot.chatbot.discord.commands
|
||||
|
||||
import net.dv8tion.jda.api.JDA
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
||||
@@ -6,10 +6,10 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands
|
||||
import net.dv8tion.jda.api.interactions.commands.build.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.UserService
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.ChzzkHandler
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.Connector
|
||||
import space.mori.chzzk_bot.chatbot.discord.CommandInterface
|
||||
import space.mori.chzzk_bot.common.services.UserService
|
||||
|
||||
object RegisterCommand: CommandInterface {
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
@@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.discord.commands
|
||||
package space.mori.chzzk_bot.chatbot.discord.commands
|
||||
|
||||
import net.dv8tion.jda.api.JDA
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
||||
@@ -6,11 +6,12 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands
|
||||
import net.dv8tion.jda.api.interactions.commands.build.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.CommandService
|
||||
import space.mori.chzzk_bot.services.UserService
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.ChzzkHandler
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.Connector
|
||||
import space.mori.chzzk_bot.chatbot.discord.CommandInterface
|
||||
import space.mori.chzzk_bot.common.services.CommandService
|
||||
import space.mori.chzzk_bot.common.services.ManagerService
|
||||
import space.mori.chzzk_bot.common.services.UserService
|
||||
|
||||
object RemoveCommand : CommandInterface {
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
@@ -26,12 +27,19 @@ object RemoveCommand : CommandInterface {
|
||||
return
|
||||
}
|
||||
|
||||
val user = UserService.getUser(event.user.idLong)
|
||||
if(user == null) {
|
||||
event.hook.sendMessage("치지직 계정을 찾을 수 없습니다.").queue()
|
||||
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
|
||||
}
|
||||
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 {
|
||||
CommandService.removeCommand(user, label)
|
@@ -0,0 +1,49 @@
|
||||
package space.mori.chzzk_bot.chatbot.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.chatbot.discord.CommandInterface
|
||||
import space.mori.chzzk_bot.common.services.ManagerService
|
||||
import space.mori.chzzk_bot.common.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())
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.discord.commands
|
||||
package space.mori.chzzk_bot.chatbot.discord.commands
|
||||
|
||||
import net.dv8tion.jda.api.JDA
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
||||
@@ -6,11 +6,12 @@ import net.dv8tion.jda.api.interactions.commands.OptionType
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands
|
||||
import net.dv8tion.jda.api.interactions.commands.build.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.CommandService
|
||||
import space.mori.chzzk_bot.services.UserService
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.ChzzkHandler
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.Connector
|
||||
import space.mori.chzzk_bot.chatbot.discord.CommandInterface
|
||||
import space.mori.chzzk_bot.common.services.CommandService
|
||||
import space.mori.chzzk_bot.common.services.ManagerService
|
||||
import space.mori.chzzk_bot.common.services.UserService
|
||||
|
||||
object UpdateCommand : CommandInterface {
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
@@ -30,12 +31,19 @@ object UpdateCommand : CommandInterface {
|
||||
return
|
||||
}
|
||||
|
||||
val user = UserService.getUser(event.user.idLong)
|
||||
if(user == null) {
|
||||
event.hook.sendMessage("치지직 계정을 찾을 수 없습니다.").queue()
|
||||
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
|
||||
}
|
||||
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 {
|
||||
CommandService.updateCommand(user, label, content, failContent ?: "")
|
42
common/build.gradle.kts
Normal file
42
common/build.gradle.kts
Normal file
@@ -0,0 +1,42 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
group = project.rootProject.group
|
||||
version = project.rootProject.version
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-core
|
||||
api("org.jetbrains.exposed:exposed-core:0.52.0")
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-dao
|
||||
api("org.jetbrains.exposed:exposed-dao:0.52.0")
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-jdbc
|
||||
api("org.jetbrains.exposed:exposed-jdbc:0.52.0")
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.exposed/exposed-kotlin-datetime
|
||||
api("org.jetbrains.exposed:exposed-java-time:0.52.0")
|
||||
|
||||
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
|
||||
api("com.zaxxer:HikariCP:5.1.0")
|
||||
|
||||
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
|
||||
implementation("ch.qos.logback:logback-classic:1.5.6")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client
|
||||
implementation("org.mariadb.jdbc:mariadb-java-client:3.4.1")
|
||||
|
||||
// https://mvnrepository.com/artifact/io.github.cdimascio/dotenv-kotlin
|
||||
implementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
|
||||
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
}
|
@@ -1,11 +1,16 @@
|
||||
package space.mori.chzzk_bot
|
||||
package space.mori.chzzk_bot.common
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig
|
||||
import com.zaxxer.hikari.HikariDataSource
|
||||
import io.github.cdimascio.dotenv.dotenv
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.SchemaUtils
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import space.mori.chzzk_bot.models.*
|
||||
import space.mori.chzzk_bot.common.models.*
|
||||
|
||||
val dotenv = dotenv {
|
||||
ignoreIfMissing = true
|
||||
}
|
||||
|
||||
object Connector {
|
||||
private val hikariConfig = HikariConfig().apply {
|
||||
@@ -19,7 +24,7 @@ object Connector {
|
||||
|
||||
init {
|
||||
Database.connect(dataSource)
|
||||
val tables = listOf(Users, Commands, Counters, DailyCounters, PersonalCounters)
|
||||
val tables = listOf(Users, Commands, Counters, DailyCounters, PersonalCounters, Managers)
|
||||
|
||||
transaction {
|
||||
SchemaUtils.createMissingTablesAndColumns(* tables.toTypedArray())
|
@@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.models
|
||||
package space.mori.chzzk_bot.common.models
|
||||
|
||||
import org.jetbrains.exposed.dao.IntEntity
|
||||
import org.jetbrains.exposed.dao.IntEntityClass
|
@@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.models
|
||||
package space.mori.chzzk_bot.common.models
|
||||
|
||||
import org.jetbrains.exposed.dao.IntEntity
|
||||
import org.jetbrains.exposed.dao.IntEntityClass
|
@@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.models
|
||||
package space.mori.chzzk_bot.common.models
|
||||
|
||||
import org.jetbrains.exposed.dao.IntEntity
|
||||
import org.jetbrains.exposed.dao.IntEntityClass
|
@@ -0,0 +1,22 @@
|
||||
package space.mori.chzzk_bot.common.models
|
||||
|
||||
import org.jetbrains.exposed.dao.IntEntity
|
||||
import org.jetbrains.exposed.dao.IntEntityClass
|
||||
import org.jetbrains.exposed.dao.id.EntityID
|
||||
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||
|
||||
object Managers: IntIdTable("manager") {
|
||||
val user = reference("user", Users)
|
||||
val managerId = long("manager_id")
|
||||
val discordGuildId = long("discord_guild_id")
|
||||
var lastUserName = varchar("last_user_name", 255).nullable()
|
||||
}
|
||||
|
||||
class Manager(id: EntityID<Int>) : IntEntity(id) {
|
||||
companion object : IntEntityClass<Manager>(Managers)
|
||||
|
||||
var user by User referencedOn Managers.user
|
||||
var managerId by Managers.managerId
|
||||
var discordGuildId by Managers.discordGuildId
|
||||
var lastUserName by Managers.lastUserName
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.models
|
||||
package space.mori.chzzk_bot.common.models
|
||||
|
||||
import org.jetbrains.exposed.dao.IntEntity
|
||||
import org.jetbrains.exposed.dao.IntEntityClass
|
@@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.models
|
||||
package space.mori.chzzk_bot.common.models
|
||||
|
||||
import org.jetbrains.exposed.dao.IntEntity
|
||||
import org.jetbrains.exposed.dao.IntEntityClass
|
@@ -1,12 +1,12 @@
|
||||
package space.mori.chzzk_bot.services
|
||||
package space.mori.chzzk_bot.common.services
|
||||
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.jetbrains.exposed.sql.update
|
||||
import space.mori.chzzk_bot.models.Command
|
||||
import space.mori.chzzk_bot.models.Commands
|
||||
import space.mori.chzzk_bot.models.User
|
||||
import space.mori.chzzk_bot.common.models.Command
|
||||
import space.mori.chzzk_bot.common.models.Commands
|
||||
import space.mori.chzzk_bot.common.models.User
|
||||
|
||||
object CommandService {
|
||||
fun saveCommand(user: User, command: String, content: String, failContent: String): Command {
|
@@ -1,8 +1,8 @@
|
||||
package space.mori.chzzk_bot.services
|
||||
package space.mori.chzzk_bot.common.services
|
||||
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import space.mori.chzzk_bot.models.*
|
||||
import space.mori.chzzk_bot.common.models.*
|
||||
import java.time.LocalDate
|
||||
|
||||
object CounterService {
|
||||
@@ -81,8 +81,6 @@ object CounterService {
|
||||
(DailyCounters.name eq name) and (DailyCounters.userId eq userId) and (DailyCounters.user eq user.id)
|
||||
}.singleOrNull()
|
||||
|
||||
println("$counter")
|
||||
|
||||
if(counter == null) {
|
||||
val newCounter = DailyCounter.new {
|
||||
this.name = name
|
||||
@@ -98,6 +96,7 @@ object CounterService {
|
||||
Pair(counter.value, false)
|
||||
else {
|
||||
counter.value += increment
|
||||
counter.updatedAt = today
|
||||
Pair(counter.value, true)
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
package space.mori.chzzk_bot.common.services
|
||||
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import space.mori.chzzk_bot.common.models.Manager
|
||||
import space.mori.chzzk_bot.common.models.Managers
|
||||
import space.mori.chzzk_bot.common.models.User
|
||||
|
||||
object ManagerService {
|
||||
fun saveManager(user: User, discordId: Long, name: String): Manager {
|
||||
if (user.liveAlertGuild == null)
|
||||
throw RuntimeException("${user.username} has no liveAlertGuild")
|
||||
|
||||
return transaction {
|
||||
Manager.new {
|
||||
this.user = user
|
||||
this.discordGuildId = user.liveAlertGuild!!
|
||||
this.managerId = discordId
|
||||
this.lastUserName = name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateManager(user: User, discordId: Long, name: String): Manager {
|
||||
if (user.liveAlertGuild == null)
|
||||
throw RuntimeException("${user.username} has no liveAlertGuild")
|
||||
|
||||
val manager = getUser(user.liveAlertGuild!!, discordId) ?: 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),
|
||||
)
|
||||
|
||||
val result = manager.firstOrNull()
|
||||
|
||||
result?.eagerLoad()
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllUsers(guildId: Long): List<Manager> {
|
||||
return transaction {
|
||||
val result = Manager.find(Managers.discordGuildId eq guildId).toList()
|
||||
|
||||
result.forEach { it.eagerLoad() }
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
fun Manager.eagerLoad() {
|
||||
this.user
|
||||
}
|
||||
}
|
@@ -1,10 +1,10 @@
|
||||
package space.mori.chzzk_bot.services
|
||||
package space.mori.chzzk_bot.common.services
|
||||
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.jetbrains.exposed.sql.update
|
||||
import space.mori.chzzk_bot.models.User
|
||||
import space.mori.chzzk_bot.models.Users
|
||||
import space.mori.chzzk_bot.common.models.User
|
||||
import space.mori.chzzk_bot.common.models.Users
|
||||
|
||||
object UserService {
|
||||
fun saveUser(username: String, token: String, discordID: Long): User {
|
||||
@@ -39,6 +39,14 @@ object UserService {
|
||||
}
|
||||
}
|
||||
|
||||
fun getUserWithGuildId(discordGuildId: Long): User? {
|
||||
return transaction {
|
||||
val users = User.find(Users.liveAlertGuild eq discordGuildId)
|
||||
|
||||
users.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllUsers(): List<User> {
|
||||
return transaction {
|
||||
User.all().toList()
|
@@ -1,6 +1,6 @@
|
||||
kotlin.code.style=official
|
||||
group = space.mori
|
||||
version = 1.0.0
|
||||
version = 1.1.1
|
||||
|
||||
org.gradle.jvmargs=-Dfile.encoding=UTF-8
|
||||
org.gradle.console=plain
|
||||
|
@@ -14,6 +14,13 @@ pluginManagement {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
maven { url = uri("https://maven.covers1624.net") }
|
||||
maven { url = uri("https://repo.spring.io/plugins-release/") }
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
|
||||
}
|
||||
}
|
||||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
||||
}
|
||||
include("common")
|
||||
include("chatbot")
|
||||
include("webserver")
|
||||
|
@@ -5,9 +5,12 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import space.mori.chzzk_bot.chzzk.ChzzkHandler
|
||||
import space.mori.chzzk_bot.discord.Discord
|
||||
import space.mori.chzzk_bot.chzzk.Connector as ChzzkConnector
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.ChzzkHandler
|
||||
import space.mori.chzzk_bot.chatbot.discord.Discord
|
||||
import space.mori.chzzk_bot.chatbot.chzzk.Connector as ChzzkConnector
|
||||
import space.mori.chzzk_bot.common.Connector
|
||||
import space.mori.chzzk_bot.webserver.start
|
||||
import space.mori.chzzk_bot.webserver.stop
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@@ -26,6 +29,7 @@ fun main(args: Array<String>) {
|
||||
discord.enable()
|
||||
chzzkHandler.enable()
|
||||
chzzkHandler.runStreamInfo()
|
||||
start()
|
||||
|
||||
if(dotenv.get("RUN_AGENT", "false").toBoolean()) {
|
||||
runBlocking {
|
||||
@@ -36,6 +40,8 @@ fun main(args: Array<String>) {
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(Thread {
|
||||
logger.info("Shutting down...")
|
||||
stop()
|
||||
|
||||
chzzkHandler.stopStreamInfo()
|
||||
chzzkHandler.disable()
|
||||
discord.disable()
|
||||
|
@@ -1,76 +0,0 @@
|
||||
package space.mori.chzzk_bot.discord
|
||||
|
||||
import net.dv8tion.jda.api.JDA
|
||||
import net.dv8tion.jda.api.JDABuilder
|
||||
import net.dv8tion.jda.api.entities.Activity
|
||||
import net.dv8tion.jda.api.entities.Guild
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
||||
import space.mori.chzzk_bot.dotenv
|
||||
import org.slf4j.LoggerFactory
|
||||
import space.mori.chzzk_bot.discord.commands.*
|
||||
|
||||
class Discord: ListenerAdapter() {
|
||||
private lateinit var bot: JDA
|
||||
private var guild: Guild? = null
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
|
||||
private val commands = listOf(
|
||||
AddCommand,
|
||||
AlertCommand,
|
||||
PingCommand,
|
||||
RegisterCommand,
|
||||
RemoveCommand,
|
||||
UpdateCommand,
|
||||
)
|
||||
|
||||
override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) {
|
||||
event.deferReply().queue()
|
||||
val handler = commands.find { it.name == event.name }
|
||||
logger.debug("Handler: ${handler?.name ?: "undefined"} command")
|
||||
handler?.run(event, bot)
|
||||
}
|
||||
|
||||
internal fun enable() {
|
||||
val thread = Thread {
|
||||
try {
|
||||
bot = JDABuilder.createDefault(dotenv["DISCORD_TOKEN"])
|
||||
.setActivity(Activity.playing("치지직 보는중"))
|
||||
.addEventListeners(this)
|
||||
.build().awaitReady()
|
||||
|
||||
guild = bot.getGuildById(dotenv["GUILD_ID"])
|
||||
|
||||
bot.updateCommands()
|
||||
.addCommands(* commands.map { it.command }.toTypedArray())
|
||||
.onSuccess {
|
||||
logger.info("Command update success!")
|
||||
logger.debug("Command list: ${commands.joinToString("/ ") { it.name }}")
|
||||
}
|
||||
.queue()
|
||||
|
||||
|
||||
if (guild == null) {
|
||||
logger.info("No guild found!")
|
||||
this.disable()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.info("Could not enable Discord!")
|
||||
logger.debug(e.stackTraceToString())
|
||||
}
|
||||
}
|
||||
thread.start()
|
||||
}
|
||||
|
||||
internal fun disable() {
|
||||
try {
|
||||
bot.shutdown()
|
||||
} catch(e: Exception) {
|
||||
logger.info("Error while shutting down Discord!")
|
||||
logger.debug(e.stackTraceToString())
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getChannel(guildId: Long, channelId: Long) =
|
||||
bot.getGuildById(guildId)?.getTextChannelById(channelId)
|
||||
}
|
35
src/main/resources/logback-debug.xml
Normal file
35
src/main/resources/logback-debug.xml
Normal 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>
|
@@ -3,7 +3,7 @@
|
||||
<!-- 콘솔에 출력하는 기본 로그 설정 -->
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>DEBUG</level>
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
|
45
webserver/build.gradle.kts
Normal file
45
webserver/build.gradle.kts
Normal file
@@ -0,0 +1,45 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization").version("2.0.0")
|
||||
}
|
||||
|
||||
group = project.rootProject.group
|
||||
version = project.rootProject.version
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
val ktorVersion = "2.3.12"
|
||||
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-server-core:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-netty:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-websockets:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-swagger:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-cors:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-swagger:$ktorVersion")
|
||||
|
||||
implementation("io.swagger.codegen.v3:swagger-codegen-generators:1.0.50")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0-RC")
|
||||
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect:2.0.0")
|
||||
|
||||
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
|
||||
implementation("ch.qos.logback:logback-classic:1.5.6")
|
||||
|
||||
implementation(project(":common"))
|
||||
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package space.mori.chzzk_bot.webserver
|
||||
|
||||
import io.ktor.http.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.netty.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import io.ktor.server.plugins.cors.routing.*
|
||||
import io.ktor.server.plugins.swagger.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.websocket.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import space.mori.chzzk_bot.webserver.routes.apiRoutes
|
||||
|
||||
val server = embeddedServer(Netty, port = 8080) {
|
||||
install(WebSockets)
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
prettyPrint = true
|
||||
isLenient = true
|
||||
})
|
||||
}
|
||||
install(CORS) {
|
||||
anyHost()
|
||||
allowHeader(HttpHeaders.ContentType)
|
||||
}
|
||||
routing {
|
||||
apiRoutes()
|
||||
swaggerUI("swagger-ui/index.html", "openapi/documentation.yaml") {
|
||||
options {
|
||||
version = "1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun start() {
|
||||
server.start(wait = true)
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
server.stop()
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package space.mori.chzzk_bot.webserver.routes
|
||||
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Routing.apiRoutes() {
|
||||
route("/") {
|
||||
get {
|
||||
call.respondText("Hello World!", status = HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
route("/health") {
|
||||
get {
|
||||
call.respondText("OK", status= HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
}
|
35
webserver/src/main/resources/openapi/documentation.yaml
Normal file
35
webserver/src/main/resources/openapi/documentation.yaml
Normal file
@@ -0,0 +1,35 @@
|
||||
openapi: "3.1.0"
|
||||
info:
|
||||
title: "chzzk_bot API"
|
||||
description: "chzzk_bot API"
|
||||
version: "1.0.0"
|
||||
servers:
|
||||
- url: "http://localhost:8080"
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
summary: "Webroot"
|
||||
description: "Main page of this api"
|
||||
responses:
|
||||
"200":
|
||||
description: "OK"
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: "string"
|
||||
examples:
|
||||
Example#1:
|
||||
value: "Hello World!"
|
||||
/health:
|
||||
get:
|
||||
description: "Health Check endpoint"
|
||||
responses:
|
||||
"200":
|
||||
description: "OK"
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: "string"
|
||||
examples:
|
||||
Example#1:
|
||||
value: "OK"
|
Reference in New Issue
Block a user